// SPDX-License-Identifier: GPL-2.0-only /* Copyright (C) 2025 Intel Corporation */ #include #include "idpf.h" #include "idpf_virtchnl.h" static DEFINE_IDA(idpf_idc_ida); #define IDPF_IDC_MAX_ADEV_NAME_LEN 15 /** * idpf_idc_init - Called to initialize IDC * @adapter: driver private data structure * * Return: 0 on success or cap not enabled, error code on failure. */ int idpf_idc_init(struct idpf_adapter *adapter) { int err; if (!idpf_is_rdma_cap_ena(adapter) || !adapter->dev_ops.idc_init) return 0; err = adapter->dev_ops.idc_init(adapter); if (err) dev_err(&adapter->pdev->dev, "failed to initialize idc: %d\n", err); return err; } /** * idpf_vport_adev_release - function to be mapped to aux dev's release op * @dev: pointer to device to free */ static void idpf_vport_adev_release(struct device *dev) { struct iidc_rdma_vport_auxiliary_dev *iadev; iadev = container_of(dev, struct iidc_rdma_vport_auxiliary_dev, adev.dev); kfree(iadev); iadev = NULL; } /** * idpf_plug_vport_aux_dev - allocate and register a vport Auxiliary device * @cdev_info: IDC core device info pointer * @vdev_info: IDC vport device info pointer * * Return: 0 on success or error code on failure. */ static int idpf_plug_vport_aux_dev(struct iidc_rdma_core_dev_info *cdev_info, struct iidc_rdma_vport_dev_info *vdev_info) { struct iidc_rdma_vport_auxiliary_dev *iadev; char name[IDPF_IDC_MAX_ADEV_NAME_LEN]; struct auxiliary_device *adev; int ret; iadev = kzalloc(sizeof(*iadev), GFP_KERNEL); if (!iadev) return -ENOMEM; adev = &iadev->adev; vdev_info->adev = &iadev->adev; iadev->vdev_info = vdev_info; ret = ida_alloc(&idpf_idc_ida, GFP_KERNEL); if (ret < 0) { pr_err("failed to allocate unique device ID for Auxiliary driver\n"); goto err_ida_alloc; } adev->id = ret; adev->dev.release = idpf_vport_adev_release; adev->dev.parent = &cdev_info->pdev->dev; sprintf(name, "%04x.rdma.vdev", cdev_info->pdev->vendor); adev->name = name; ret = auxiliary_device_init(adev); if (ret) goto err_aux_dev_init; ret = auxiliary_device_add(adev); if (ret) goto err_aux_dev_add; return 0; err_aux_dev_add: auxiliary_device_uninit(adev); err_aux_dev_init: ida_free(&idpf_idc_ida, adev->id); err_ida_alloc: vdev_info->adev = NULL; kfree(iadev); return ret; } /** * idpf_idc_init_aux_vport_dev - initialize vport Auxiliary Device(s) * @vport: virtual port data struct * * Return: 0 on success or error code on failure. */ static int idpf_idc_init_aux_vport_dev(struct idpf_vport *vport) { struct idpf_adapter *adapter = vport->adapter; struct iidc_rdma_vport_dev_info *vdev_info; struct iidc_rdma_core_dev_info *cdev_info; struct virtchnl2_create_vport *vport_msg; int err; vport_msg = (struct virtchnl2_create_vport *) adapter->vport_params_recvd[vport->idx]; if (!(le16_to_cpu(vport_msg->vport_flags) & VIRTCHNL2_VPORT_ENABLE_RDMA)) return 0; vport->vdev_info = kzalloc(sizeof(*vdev_info), GFP_KERNEL); if (!vport->vdev_info) return -ENOMEM; cdev_info = vport->adapter->cdev_info; vdev_info = vport->vdev_info; vdev_info->vport_id = vport->vport_id; vdev_info->netdev = vport->netdev; vdev_info->core_adev = cdev_info->adev; err = idpf_plug_vport_aux_dev(cdev_info, vdev_info); if (err) { vport->vdev_info = NULL; kfree(vdev_info); return err; } return 0; } /** * idpf_idc_vdev_mtu_event - Function to handle IDC vport mtu change events * @vdev_info: IDC vport device info pointer * @event_type: type of event to pass to handler */ void idpf_idc_vdev_mtu_event(struct iidc_rdma_vport_dev_info *vdev_info, enum iidc_rdma_event_type event_type) { struct iidc_rdma_vport_auxiliary_drv *iadrv; struct iidc_rdma_event event = { }; struct auxiliary_device *adev; if (!vdev_info) /* RDMA is not enabled */ return; set_bit(event_type, event.type); device_lock(&vdev_info->adev->dev); adev = vdev_info->adev; if (!adev || !adev->dev.driver) goto unlock; iadrv = container_of(adev->dev.driver, struct iidc_rdma_vport_auxiliary_drv, adrv.driver); if (iadrv->event_handler) iadrv->event_handler(vdev_info, &event); unlock: device_unlock(&vdev_info->adev->dev); } /** * idpf_core_adev_release - function to be mapped to aux dev's release op * @dev: pointer to device to free */ static void idpf_core_adev_release(struct device *dev) { struct iidc_rdma_core_auxiliary_dev *iadev; iadev = container_of(dev, struct iidc_rdma_core_auxiliary_dev, adev.dev); kfree(iadev); iadev = NULL; } /** * idpf_plug_core_aux_dev - allocate and register an Auxiliary device * @cdev_info: IDC core device info pointer * * Return: 0 on success or error code on failure. */ static int idpf_plug_core_aux_dev(struct iidc_rdma_core_dev_info *cdev_info) { struct iidc_rdma_core_auxiliary_dev *iadev; char name[IDPF_IDC_MAX_ADEV_NAME_LEN]; struct auxiliary_device *adev; int ret; iadev = kzalloc(sizeof(*iadev), GFP_KERNEL); if (!iadev) return -ENOMEM; adev = &iadev->adev; cdev_info->adev = adev; iadev->cdev_info = cdev_info; ret = ida_alloc(&idpf_idc_ida, GFP_KERNEL); if (ret < 0) { pr_err("failed to allocate unique device ID for Auxiliary driver\n"); goto err_ida_alloc; } adev->id = ret; adev->dev.release = idpf_core_adev_release; adev->dev.parent = &cdev_info->pdev->dev; sprintf(name, "%04x.rdma.core", cdev_info->pdev->vendor); adev->name = name; ret = auxiliary_device_init(adev); if (ret) goto err_aux_dev_init; ret = auxiliary_device_add(adev); if (ret) goto err_aux_dev_add; return 0; err_aux_dev_add: auxiliary_device_uninit(adev); err_aux_dev_init: ida_free(&idpf_idc_ida, adev->id); err_ida_alloc: cdev_info->adev = NULL; kfree(iadev); return ret; } /** * idpf_unplug_aux_dev - unregister and free an Auxiliary device * @adev: auxiliary device struct */ static void idpf_unplug_aux_dev(struct auxiliary_device *adev) { if (!adev) return; auxiliary_device_delete(adev); auxiliary_device_uninit(adev); ida_free(&idpf_idc_ida, adev->id); } /** * idpf_idc_issue_reset_event - Function to handle reset IDC event * @cdev_info: IDC core device info pointer */ void idpf_idc_issue_reset_event(struct iidc_rdma_core_dev_info *cdev_info) { enum iidc_rdma_event_type event_type = IIDC_RDMA_EVENT_WARN_RESET; struct iidc_rdma_core_auxiliary_drv *iadrv; struct iidc_rdma_event event = { }; struct auxiliary_device *adev; if (!cdev_info) /* RDMA is not enabled */ return; set_bit(event_type, event.type); device_lock(&cdev_info->adev->dev); adev = cdev_info->adev; if (!adev || !adev->dev.driver) goto unlock; iadrv = container_of(adev->dev.driver, struct iidc_rdma_core_auxiliary_drv, adrv.driver); if (iadrv->event_handler) iadrv->event_handler(cdev_info, &event); unlock: device_unlock(&cdev_info->adev->dev); } /** * idpf_idc_vport_dev_up - called when CORE is ready for vport aux devs * @adapter: private data struct * * Return: 0 on success or error code on failure. */ static int idpf_idc_vport_dev_up(struct idpf_adapter *adapter) { int i, err = 0; for (i = 0; i < adapter->num_alloc_vports; i++) { struct idpf_vport *vport = adapter->vports[i]; if (!vport) continue; if (!vport->vdev_info) err = idpf_idc_init_aux_vport_dev(vport); else err = idpf_plug_vport_aux_dev(vport->adapter->cdev_info, vport->vdev_info); } return err; } /** * idpf_idc_vport_dev_down - called CORE is leaving vport aux dev support state * @adapter: private data struct */ static void idpf_idc_vport_dev_down(struct idpf_adapter *adapter) { int i; for (i = 0; i < adapter->num_alloc_vports; i++) { struct idpf_vport *vport = adapter->vports[i]; if (!vport) continue; idpf_unplug_aux_dev(vport->vdev_info->adev); vport->vdev_info->adev = NULL; } } /** * idpf_idc_vport_dev_ctrl - Called by an Auxiliary Driver * @cdev_info: IDC core device info pointer * @up: RDMA core driver status * * This callback function is accessed by an Auxiliary Driver to indicate * whether core driver is ready to support vport driver load or if vport * drivers need to be taken down. * * Return: 0 on success or error code on failure. */ int idpf_idc_vport_dev_ctrl(struct iidc_rdma_core_dev_info *cdev_info, bool up) { struct idpf_adapter *adapter = pci_get_drvdata(cdev_info->pdev); if (up) return idpf_idc_vport_dev_up(adapter); idpf_idc_vport_dev_down(adapter); return 0; } EXPORT_SYMBOL_GPL(idpf_idc_vport_dev_ctrl); /** * idpf_idc_request_reset - Called by an Auxiliary Driver * @cdev_info: IDC core device info pointer * @reset_type: function, core or other * * This callback function is accessed by an Auxiliary Driver to request a reset * on the Auxiliary Device. * * Return: 0 on success or error code on failure. */ int idpf_idc_request_reset(struct iidc_rdma_core_dev_info *cdev_info, enum iidc_rdma_reset_type __always_unused reset_type) { struct idpf_adapter *adapter = pci_get_drvdata(cdev_info->pdev); if (!idpf_is_reset_in_prog(adapter)) { set_bit(IDPF_HR_FUNC_RESET, adapter->flags); queue_delayed_work(adapter->vc_event_wq, &adapter->vc_event_task, msecs_to_jiffies(10)); } return 0; } EXPORT_SYMBOL_GPL(idpf_idc_request_reset); /** * idpf_idc_init_msix_data - initialize MSIX data for the cdev_info structure * @adapter: driver private data structure */ static void idpf_idc_init_msix_data(struct idpf_adapter *adapter) { struct iidc_rdma_core_dev_info *cdev_info; struct iidc_rdma_priv_dev_info *privd; if (!adapter->rdma_msix_entries) return; cdev_info = adapter->cdev_info; privd = cdev_info->iidc_priv; privd->msix_entries = adapter->rdma_msix_entries; privd->msix_count = adapter->num_rdma_msix_entries; } /** * idpf_idc_init_aux_core_dev - initialize Auxiliary Device(s) * @adapter: driver private data structure * @ftype: PF or VF * * Return: 0 on success or error code on failure. */ int idpf_idc_init_aux_core_dev(struct idpf_adapter *adapter, enum iidc_function_type ftype) { struct iidc_rdma_core_dev_info *cdev_info; struct iidc_rdma_priv_dev_info *privd; int err, i; adapter->cdev_info = kzalloc(sizeof(*cdev_info), GFP_KERNEL); if (!adapter->cdev_info) return -ENOMEM; cdev_info = adapter->cdev_info; privd = kzalloc(sizeof(*privd), GFP_KERNEL); if (!privd) { err = -ENOMEM; goto err_privd_alloc; } cdev_info->iidc_priv = privd; cdev_info->pdev = adapter->pdev; cdev_info->rdma_protocol = IIDC_RDMA_PROTOCOL_ROCEV2; privd->ftype = ftype; privd->mapped_mem_regions = kcalloc(adapter->hw.num_lan_regs, sizeof(struct iidc_rdma_lan_mapped_mem_region), GFP_KERNEL); if (!privd->mapped_mem_regions) { err = -ENOMEM; goto err_plug_aux_dev; } privd->num_memory_regions = cpu_to_le16(adapter->hw.num_lan_regs); for (i = 0; i < adapter->hw.num_lan_regs; i++) { privd->mapped_mem_regions[i].region_addr = adapter->hw.lan_regs[i].vaddr; privd->mapped_mem_regions[i].size = cpu_to_le64(adapter->hw.lan_regs[i].addr_len); privd->mapped_mem_regions[i].start_offset = cpu_to_le64(adapter->hw.lan_regs[i].addr_start); } idpf_idc_init_msix_data(adapter); err = idpf_plug_core_aux_dev(cdev_info); if (err) goto err_free_mem_regions; return 0; err_free_mem_regions: kfree(privd->mapped_mem_regions); privd->mapped_mem_regions = NULL; err_plug_aux_dev: kfree(privd); err_privd_alloc: kfree(cdev_info); adapter->cdev_info = NULL; return err; } /** * idpf_idc_deinit_core_aux_device - de-initialize Auxiliary Device(s) * @cdev_info: IDC core device info pointer */ void idpf_idc_deinit_core_aux_device(struct iidc_rdma_core_dev_info *cdev_info) { struct iidc_rdma_priv_dev_info *privd; if (!cdev_info) return; idpf_unplug_aux_dev(cdev_info->adev); privd = cdev_info->iidc_priv; kfree(privd->mapped_mem_regions); kfree(privd); kfree(cdev_info); } /** * idpf_idc_deinit_vport_aux_device - de-initialize Auxiliary Device(s) * @vdev_info: IDC vport device info pointer */ void idpf_idc_deinit_vport_aux_device(struct iidc_rdma_vport_dev_info *vdev_info) { if (!vdev_info) return; idpf_unplug_aux_dev(vdev_info->adev); kfree(vdev_info); }