1 // SPDX-License-Identifier: GPL-2.0-only
2 /* Copyright (C) 2025 Intel Corporation */
3
4 #include <linux/export.h>
5
6 #include "idpf.h"
7 #include "idpf_virtchnl.h"
8
9 static DEFINE_IDA(idpf_idc_ida);
10
11 #define IDPF_IDC_MAX_ADEV_NAME_LEN 15
12
13 /**
14 * idpf_idc_init - Called to initialize IDC
15 * @adapter: driver private data structure
16 *
17 * Return: 0 on success or cap not enabled, error code on failure.
18 */
idpf_idc_init(struct idpf_adapter * adapter)19 int idpf_idc_init(struct idpf_adapter *adapter)
20 {
21 int err;
22
23 if (!idpf_is_rdma_cap_ena(adapter) ||
24 !adapter->dev_ops.idc_init)
25 return 0;
26
27 err = adapter->dev_ops.idc_init(adapter);
28 if (err)
29 dev_err(&adapter->pdev->dev, "failed to initialize idc: %d\n",
30 err);
31
32 return err;
33 }
34
35 /**
36 * idpf_vport_adev_release - function to be mapped to aux dev's release op
37 * @dev: pointer to device to free
38 */
idpf_vport_adev_release(struct device * dev)39 static void idpf_vport_adev_release(struct device *dev)
40 {
41 struct iidc_rdma_vport_auxiliary_dev *iadev;
42
43 iadev = container_of(dev, struct iidc_rdma_vport_auxiliary_dev, adev.dev);
44 kfree(iadev);
45 iadev = NULL;
46 }
47
48 /**
49 * idpf_plug_vport_aux_dev - allocate and register a vport Auxiliary device
50 * @cdev_info: IDC core device info pointer
51 * @vdev_info: IDC vport device info pointer
52 *
53 * Return: 0 on success or error code on failure.
54 */
idpf_plug_vport_aux_dev(struct iidc_rdma_core_dev_info * cdev_info,struct iidc_rdma_vport_dev_info * vdev_info)55 static int idpf_plug_vport_aux_dev(struct iidc_rdma_core_dev_info *cdev_info,
56 struct iidc_rdma_vport_dev_info *vdev_info)
57 {
58 struct iidc_rdma_vport_auxiliary_dev *iadev;
59 char name[IDPF_IDC_MAX_ADEV_NAME_LEN];
60 struct auxiliary_device *adev;
61 int ret;
62
63 iadev = kzalloc(sizeof(*iadev), GFP_KERNEL);
64 if (!iadev)
65 return -ENOMEM;
66
67 adev = &iadev->adev;
68 vdev_info->adev = &iadev->adev;
69 iadev->vdev_info = vdev_info;
70
71 ret = ida_alloc(&idpf_idc_ida, GFP_KERNEL);
72 if (ret < 0) {
73 pr_err("failed to allocate unique device ID for Auxiliary driver\n");
74 goto err_ida_alloc;
75 }
76 adev->id = ret;
77 adev->dev.release = idpf_vport_adev_release;
78 adev->dev.parent = &cdev_info->pdev->dev;
79 sprintf(name, "%04x.rdma.vdev", cdev_info->pdev->vendor);
80 adev->name = name;
81
82 ret = auxiliary_device_init(adev);
83 if (ret)
84 goto err_aux_dev_init;
85
86 ret = auxiliary_device_add(adev);
87 if (ret)
88 goto err_aux_dev_add;
89
90 return 0;
91
92 err_aux_dev_add:
93 auxiliary_device_uninit(adev);
94 err_aux_dev_init:
95 ida_free(&idpf_idc_ida, adev->id);
96 err_ida_alloc:
97 vdev_info->adev = NULL;
98 kfree(iadev);
99
100 return ret;
101 }
102
103 /**
104 * idpf_idc_init_aux_vport_dev - initialize vport Auxiliary Device(s)
105 * @vport: virtual port data struct
106 *
107 * Return: 0 on success or error code on failure.
108 */
idpf_idc_init_aux_vport_dev(struct idpf_vport * vport)109 static int idpf_idc_init_aux_vport_dev(struct idpf_vport *vport)
110 {
111 struct idpf_adapter *adapter = vport->adapter;
112 struct iidc_rdma_vport_dev_info *vdev_info;
113 struct iidc_rdma_core_dev_info *cdev_info;
114 struct virtchnl2_create_vport *vport_msg;
115 int err;
116
117 vport_msg = (struct virtchnl2_create_vport *)
118 adapter->vport_params_recvd[vport->idx];
119
120 if (!(le16_to_cpu(vport_msg->vport_flags) & VIRTCHNL2_VPORT_ENABLE_RDMA))
121 return 0;
122
123 vport->vdev_info = kzalloc(sizeof(*vdev_info), GFP_KERNEL);
124 if (!vport->vdev_info)
125 return -ENOMEM;
126
127 cdev_info = vport->adapter->cdev_info;
128
129 vdev_info = vport->vdev_info;
130 vdev_info->vport_id = vport->vport_id;
131 vdev_info->netdev = vport->netdev;
132 vdev_info->core_adev = cdev_info->adev;
133
134 err = idpf_plug_vport_aux_dev(cdev_info, vdev_info);
135 if (err) {
136 vport->vdev_info = NULL;
137 kfree(vdev_info);
138 return err;
139 }
140
141 return 0;
142 }
143
144 /**
145 * idpf_idc_vdev_mtu_event - Function to handle IDC vport mtu change events
146 * @vdev_info: IDC vport device info pointer
147 * @event_type: type of event to pass to handler
148 */
idpf_idc_vdev_mtu_event(struct iidc_rdma_vport_dev_info * vdev_info,enum iidc_rdma_event_type event_type)149 void idpf_idc_vdev_mtu_event(struct iidc_rdma_vport_dev_info *vdev_info,
150 enum iidc_rdma_event_type event_type)
151 {
152 struct iidc_rdma_vport_auxiliary_drv *iadrv;
153 struct iidc_rdma_event event = { };
154 struct auxiliary_device *adev;
155
156 if (!vdev_info)
157 /* RDMA is not enabled */
158 return;
159
160 set_bit(event_type, event.type);
161
162 device_lock(&vdev_info->adev->dev);
163 adev = vdev_info->adev;
164 if (!adev || !adev->dev.driver)
165 goto unlock;
166 iadrv = container_of(adev->dev.driver,
167 struct iidc_rdma_vport_auxiliary_drv,
168 adrv.driver);
169 if (iadrv->event_handler)
170 iadrv->event_handler(vdev_info, &event);
171 unlock:
172 device_unlock(&vdev_info->adev->dev);
173 }
174
175 /**
176 * idpf_core_adev_release - function to be mapped to aux dev's release op
177 * @dev: pointer to device to free
178 */
idpf_core_adev_release(struct device * dev)179 static void idpf_core_adev_release(struct device *dev)
180 {
181 struct iidc_rdma_core_auxiliary_dev *iadev;
182
183 iadev = container_of(dev, struct iidc_rdma_core_auxiliary_dev, adev.dev);
184 kfree(iadev);
185 iadev = NULL;
186 }
187
188 /**
189 * idpf_plug_core_aux_dev - allocate and register an Auxiliary device
190 * @cdev_info: IDC core device info pointer
191 *
192 * Return: 0 on success or error code on failure.
193 */
idpf_plug_core_aux_dev(struct iidc_rdma_core_dev_info * cdev_info)194 static int idpf_plug_core_aux_dev(struct iidc_rdma_core_dev_info *cdev_info)
195 {
196 struct iidc_rdma_core_auxiliary_dev *iadev;
197 char name[IDPF_IDC_MAX_ADEV_NAME_LEN];
198 struct auxiliary_device *adev;
199 int ret;
200
201 iadev = kzalloc(sizeof(*iadev), GFP_KERNEL);
202 if (!iadev)
203 return -ENOMEM;
204
205 adev = &iadev->adev;
206 cdev_info->adev = adev;
207 iadev->cdev_info = cdev_info;
208
209 ret = ida_alloc(&idpf_idc_ida, GFP_KERNEL);
210 if (ret < 0) {
211 pr_err("failed to allocate unique device ID for Auxiliary driver\n");
212 goto err_ida_alloc;
213 }
214 adev->id = ret;
215 adev->dev.release = idpf_core_adev_release;
216 adev->dev.parent = &cdev_info->pdev->dev;
217 sprintf(name, "%04x.rdma.core", cdev_info->pdev->vendor);
218 adev->name = name;
219
220 ret = auxiliary_device_init(adev);
221 if (ret)
222 goto err_aux_dev_init;
223
224 ret = auxiliary_device_add(adev);
225 if (ret)
226 goto err_aux_dev_add;
227
228 return 0;
229
230 err_aux_dev_add:
231 auxiliary_device_uninit(adev);
232 err_aux_dev_init:
233 ida_free(&idpf_idc_ida, adev->id);
234 err_ida_alloc:
235 cdev_info->adev = NULL;
236 kfree(iadev);
237
238 return ret;
239 }
240
241 /**
242 * idpf_unplug_aux_dev - unregister and free an Auxiliary device
243 * @adev: auxiliary device struct
244 */
idpf_unplug_aux_dev(struct auxiliary_device * adev)245 static void idpf_unplug_aux_dev(struct auxiliary_device *adev)
246 {
247 if (!adev)
248 return;
249
250 ida_free(&idpf_idc_ida, adev->id);
251
252 auxiliary_device_delete(adev);
253 auxiliary_device_uninit(adev);
254 }
255
256 /**
257 * idpf_idc_issue_reset_event - Function to handle reset IDC event
258 * @cdev_info: IDC core device info pointer
259 */
idpf_idc_issue_reset_event(struct iidc_rdma_core_dev_info * cdev_info)260 void idpf_idc_issue_reset_event(struct iidc_rdma_core_dev_info *cdev_info)
261 {
262 enum iidc_rdma_event_type event_type = IIDC_RDMA_EVENT_WARN_RESET;
263 struct iidc_rdma_core_auxiliary_drv *iadrv;
264 struct iidc_rdma_event event = { };
265 struct auxiliary_device *adev;
266
267 if (!cdev_info)
268 /* RDMA is not enabled */
269 return;
270
271 set_bit(event_type, event.type);
272
273 device_lock(&cdev_info->adev->dev);
274
275 adev = cdev_info->adev;
276 if (!adev || !adev->dev.driver)
277 goto unlock;
278
279 iadrv = container_of(adev->dev.driver,
280 struct iidc_rdma_core_auxiliary_drv,
281 adrv.driver);
282 if (iadrv->event_handler)
283 iadrv->event_handler(cdev_info, &event);
284 unlock:
285 device_unlock(&cdev_info->adev->dev);
286 }
287
288 /**
289 * idpf_idc_vport_dev_up - called when CORE is ready for vport aux devs
290 * @adapter: private data struct
291 *
292 * Return: 0 on success or error code on failure.
293 */
idpf_idc_vport_dev_up(struct idpf_adapter * adapter)294 static int idpf_idc_vport_dev_up(struct idpf_adapter *adapter)
295 {
296 int i, err = 0;
297
298 for (i = 0; i < adapter->num_alloc_vports; i++) {
299 struct idpf_vport *vport = adapter->vports[i];
300
301 if (!vport)
302 continue;
303
304 if (!vport->vdev_info)
305 err = idpf_idc_init_aux_vport_dev(vport);
306 else
307 err = idpf_plug_vport_aux_dev(vport->adapter->cdev_info,
308 vport->vdev_info);
309 }
310
311 return err;
312 }
313
314 /**
315 * idpf_idc_vport_dev_down - called CORE is leaving vport aux dev support state
316 * @adapter: private data struct
317 */
idpf_idc_vport_dev_down(struct idpf_adapter * adapter)318 static void idpf_idc_vport_dev_down(struct idpf_adapter *adapter)
319 {
320 int i;
321
322 for (i = 0; i < adapter->num_alloc_vports; i++) {
323 struct idpf_vport *vport = adapter->vports[i];
324
325 if (!vport)
326 continue;
327
328 idpf_unplug_aux_dev(vport->vdev_info->adev);
329 vport->vdev_info->adev = NULL;
330 }
331 }
332
333 /**
334 * idpf_idc_vport_dev_ctrl - Called by an Auxiliary Driver
335 * @cdev_info: IDC core device info pointer
336 * @up: RDMA core driver status
337 *
338 * This callback function is accessed by an Auxiliary Driver to indicate
339 * whether core driver is ready to support vport driver load or if vport
340 * drivers need to be taken down.
341 *
342 * Return: 0 on success or error code on failure.
343 */
idpf_idc_vport_dev_ctrl(struct iidc_rdma_core_dev_info * cdev_info,bool up)344 int idpf_idc_vport_dev_ctrl(struct iidc_rdma_core_dev_info *cdev_info, bool up)
345 {
346 struct idpf_adapter *adapter = pci_get_drvdata(cdev_info->pdev);
347
348 if (up)
349 return idpf_idc_vport_dev_up(adapter);
350
351 idpf_idc_vport_dev_down(adapter);
352
353 return 0;
354 }
355 EXPORT_SYMBOL_GPL(idpf_idc_vport_dev_ctrl);
356
357 /**
358 * idpf_idc_request_reset - Called by an Auxiliary Driver
359 * @cdev_info: IDC core device info pointer
360 * @reset_type: function, core or other
361 *
362 * This callback function is accessed by an Auxiliary Driver to request a reset
363 * on the Auxiliary Device.
364 *
365 * Return: 0 on success or error code on failure.
366 */
idpf_idc_request_reset(struct iidc_rdma_core_dev_info * cdev_info,enum iidc_rdma_reset_type __always_unused reset_type)367 int idpf_idc_request_reset(struct iidc_rdma_core_dev_info *cdev_info,
368 enum iidc_rdma_reset_type __always_unused reset_type)
369 {
370 struct idpf_adapter *adapter = pci_get_drvdata(cdev_info->pdev);
371
372 if (!idpf_is_reset_in_prog(adapter)) {
373 set_bit(IDPF_HR_FUNC_RESET, adapter->flags);
374 queue_delayed_work(adapter->vc_event_wq,
375 &adapter->vc_event_task,
376 msecs_to_jiffies(10));
377 }
378
379 return 0;
380 }
381 EXPORT_SYMBOL_GPL(idpf_idc_request_reset);
382
383 /**
384 * idpf_idc_init_msix_data - initialize MSIX data for the cdev_info structure
385 * @adapter: driver private data structure
386 */
387 static void
idpf_idc_init_msix_data(struct idpf_adapter * adapter)388 idpf_idc_init_msix_data(struct idpf_adapter *adapter)
389 {
390 struct iidc_rdma_core_dev_info *cdev_info;
391 struct iidc_rdma_priv_dev_info *privd;
392
393 if (!adapter->rdma_msix_entries)
394 return;
395
396 cdev_info = adapter->cdev_info;
397 privd = cdev_info->iidc_priv;
398
399 privd->msix_entries = adapter->rdma_msix_entries;
400 privd->msix_count = adapter->num_rdma_msix_entries;
401 }
402
403 /**
404 * idpf_idc_init_aux_core_dev - initialize Auxiliary Device(s)
405 * @adapter: driver private data structure
406 * @ftype: PF or VF
407 *
408 * Return: 0 on success or error code on failure.
409 */
idpf_idc_init_aux_core_dev(struct idpf_adapter * adapter,enum iidc_function_type ftype)410 int idpf_idc_init_aux_core_dev(struct idpf_adapter *adapter,
411 enum iidc_function_type ftype)
412 {
413 struct iidc_rdma_core_dev_info *cdev_info;
414 struct iidc_rdma_priv_dev_info *privd;
415 int err, i;
416
417 adapter->cdev_info = kzalloc(sizeof(*cdev_info), GFP_KERNEL);
418 if (!adapter->cdev_info)
419 return -ENOMEM;
420 cdev_info = adapter->cdev_info;
421
422 privd = kzalloc(sizeof(*privd), GFP_KERNEL);
423 if (!privd) {
424 err = -ENOMEM;
425 goto err_privd_alloc;
426 }
427
428 cdev_info->iidc_priv = privd;
429 cdev_info->pdev = adapter->pdev;
430 cdev_info->rdma_protocol = IIDC_RDMA_PROTOCOL_ROCEV2;
431 privd->ftype = ftype;
432
433 privd->mapped_mem_regions =
434 kcalloc(adapter->hw.num_lan_regs,
435 sizeof(struct iidc_rdma_lan_mapped_mem_region),
436 GFP_KERNEL);
437 if (!privd->mapped_mem_regions) {
438 err = -ENOMEM;
439 goto err_plug_aux_dev;
440 }
441
442 privd->num_memory_regions = cpu_to_le16(adapter->hw.num_lan_regs);
443 for (i = 0; i < adapter->hw.num_lan_regs; i++) {
444 privd->mapped_mem_regions[i].region_addr =
445 adapter->hw.lan_regs[i].vaddr;
446 privd->mapped_mem_regions[i].size =
447 cpu_to_le64(adapter->hw.lan_regs[i].addr_len);
448 privd->mapped_mem_regions[i].start_offset =
449 cpu_to_le64(adapter->hw.lan_regs[i].addr_start);
450 }
451
452 idpf_idc_init_msix_data(adapter);
453
454 err = idpf_plug_core_aux_dev(cdev_info);
455 if (err)
456 goto err_free_mem_regions;
457
458 return 0;
459
460 err_free_mem_regions:
461 kfree(privd->mapped_mem_regions);
462 privd->mapped_mem_regions = NULL;
463 err_plug_aux_dev:
464 kfree(privd);
465 err_privd_alloc:
466 kfree(cdev_info);
467 adapter->cdev_info = NULL;
468
469 return err;
470 }
471
472 /**
473 * idpf_idc_deinit_core_aux_device - de-initialize Auxiliary Device(s)
474 * @cdev_info: IDC core device info pointer
475 */
idpf_idc_deinit_core_aux_device(struct iidc_rdma_core_dev_info * cdev_info)476 void idpf_idc_deinit_core_aux_device(struct iidc_rdma_core_dev_info *cdev_info)
477 {
478 struct iidc_rdma_priv_dev_info *privd;
479
480 if (!cdev_info)
481 return;
482
483 idpf_unplug_aux_dev(cdev_info->adev);
484
485 privd = cdev_info->iidc_priv;
486 kfree(privd->mapped_mem_regions);
487 kfree(privd);
488 kfree(cdev_info);
489 }
490
491 /**
492 * idpf_idc_deinit_vport_aux_device - de-initialize Auxiliary Device(s)
493 * @vdev_info: IDC vport device info pointer
494 */
idpf_idc_deinit_vport_aux_device(struct iidc_rdma_vport_dev_info * vdev_info)495 void idpf_idc_deinit_vport_aux_device(struct iidc_rdma_vport_dev_info *vdev_info)
496 {
497 if (!vdev_info)
498 return;
499
500 idpf_unplug_aux_dev(vdev_info->adev);
501
502 kfree(vdev_info);
503 }
504