| // SPDX-License-Identifier: GPL-2.0 |
| /* Copyright (C) 2021, Intel Corporation. */ |
| |
| /* Inter-Driver Communication */ |
| #include "ice.h" |
| #include "ice_lib.h" |
| #include "ice_dcb_lib.h" |
| |
| static DEFINE_XARRAY_ALLOC1(ice_aux_id); |
| |
| /** |
| * ice_get_auxiliary_drv - retrieve iidc_rdma_core_auxiliary_drv struct |
| * @cdev: pointer to iidc_rdma_core_dev_info struct |
| * |
| * This function has to be called with a device_lock on the |
| * cdev->adev.dev to avoid race conditions. |
| * |
| * Return: pointer to the matched auxiliary driver struct |
| */ |
| static struct iidc_rdma_core_auxiliary_drv * |
| ice_get_auxiliary_drv(struct iidc_rdma_core_dev_info *cdev) |
| { |
| struct auxiliary_device *adev; |
| |
| adev = cdev->adev; |
| if (!adev || !adev->dev.driver) |
| return NULL; |
| |
| return container_of(adev->dev.driver, |
| struct iidc_rdma_core_auxiliary_drv, adrv.driver); |
| } |
| |
| /** |
| * ice_send_event_to_aux - send event to RDMA AUX driver |
| * @pf: pointer to PF struct |
| * @event: event struct |
| */ |
| void ice_send_event_to_aux(struct ice_pf *pf, struct iidc_rdma_event *event) |
| { |
| struct iidc_rdma_core_auxiliary_drv *iadrv; |
| struct iidc_rdma_core_dev_info *cdev; |
| |
| if (WARN_ON_ONCE(!in_task())) |
| return; |
| |
| cdev = pf->cdev_info; |
| if (!cdev) |
| return; |
| |
| mutex_lock(&pf->adev_mutex); |
| if (!cdev->adev) |
| goto finish; |
| |
| device_lock(&cdev->adev->dev); |
| iadrv = ice_get_auxiliary_drv(cdev); |
| if (iadrv && iadrv->event_handler) |
| iadrv->event_handler(cdev, event); |
| device_unlock(&cdev->adev->dev); |
| finish: |
| mutex_unlock(&pf->adev_mutex); |
| } |
| |
| /** |
| * ice_add_rdma_qset - Add Leaf Node for RDMA Qset |
| * @cdev: pointer to iidc_rdma_core_dev_info struct |
| * @qset: Resource to be allocated |
| * |
| * Return: Zero on success or error code encountered |
| */ |
| int ice_add_rdma_qset(struct iidc_rdma_core_dev_info *cdev, |
| struct iidc_rdma_qset_params *qset) |
| { |
| u16 max_rdmaqs[ICE_MAX_TRAFFIC_CLASS]; |
| struct ice_vsi *vsi; |
| struct device *dev; |
| struct ice_pf *pf; |
| u32 qset_teid; |
| u16 qs_handle; |
| int status; |
| int i; |
| |
| if (WARN_ON(!cdev || !qset)) |
| return -EINVAL; |
| |
| pf = pci_get_drvdata(cdev->pdev); |
| dev = ice_pf_to_dev(pf); |
| |
| if (!ice_is_rdma_ena(pf)) |
| return -EINVAL; |
| |
| vsi = ice_get_main_vsi(pf); |
| if (!vsi) { |
| dev_err(dev, "RDMA QSet invalid VSI\n"); |
| return -EINVAL; |
| } |
| |
| ice_for_each_traffic_class(i) |
| max_rdmaqs[i] = 0; |
| |
| max_rdmaqs[qset->tc]++; |
| qs_handle = qset->qs_handle; |
| |
| status = ice_cfg_vsi_rdma(vsi->port_info, vsi->idx, vsi->tc_cfg.ena_tc, |
| max_rdmaqs); |
| if (status) { |
| dev_err(dev, "Failed VSI RDMA Qset config\n"); |
| return status; |
| } |
| |
| status = ice_ena_vsi_rdma_qset(vsi->port_info, vsi->idx, qset->tc, |
| &qs_handle, 1, &qset_teid); |
| if (status) { |
| dev_err(dev, "Failed VSI RDMA Qset enable\n"); |
| return status; |
| } |
| qset->teid = qset_teid; |
| |
| return 0; |
| } |
| EXPORT_SYMBOL_GPL(ice_add_rdma_qset); |
| |
| /** |
| * ice_del_rdma_qset - Delete leaf node for RDMA Qset |
| * @cdev: pointer to iidc_rdma_core_dev_info struct |
| * @qset: Resource to be freed |
| * |
| * Return: Zero on success, error code on failure |
| */ |
| int ice_del_rdma_qset(struct iidc_rdma_core_dev_info *cdev, |
| struct iidc_rdma_qset_params *qset) |
| { |
| struct ice_vsi *vsi; |
| struct ice_pf *pf; |
| u32 teid; |
| u16 q_id; |
| |
| if (WARN_ON(!cdev || !qset)) |
| return -EINVAL; |
| |
| pf = pci_get_drvdata(cdev->pdev); |
| vsi = ice_find_vsi(pf, qset->vport_id); |
| if (!vsi) { |
| dev_err(ice_pf_to_dev(pf), "RDMA Invalid VSI\n"); |
| return -EINVAL; |
| } |
| |
| q_id = qset->qs_handle; |
| teid = qset->teid; |
| |
| return ice_dis_vsi_rdma_qset(vsi->port_info, 1, &teid, &q_id); |
| } |
| EXPORT_SYMBOL_GPL(ice_del_rdma_qset); |
| |
| /** |
| * ice_rdma_request_reset - accept request from RDMA to perform a reset |
| * @cdev: pointer to iidc_rdma_core_dev_info struct |
| * @reset_type: type of reset |
| * |
| * Return: Zero on success, error code on failure |
| */ |
| int ice_rdma_request_reset(struct iidc_rdma_core_dev_info *cdev, |
| enum iidc_rdma_reset_type reset_type) |
| { |
| enum ice_reset_req reset; |
| struct ice_pf *pf; |
| |
| if (WARN_ON(!cdev)) |
| return -EINVAL; |
| |
| pf = pci_get_drvdata(cdev->pdev); |
| |
| switch (reset_type) { |
| case IIDC_FUNC_RESET: |
| reset = ICE_RESET_PFR; |
| break; |
| case IIDC_DEV_RESET: |
| reset = ICE_RESET_CORER; |
| break; |
| default: |
| return -EINVAL; |
| } |
| |
| return ice_schedule_reset(pf, reset); |
| } |
| EXPORT_SYMBOL_GPL(ice_rdma_request_reset); |
| |
| /** |
| * ice_rdma_update_vsi_filter - update main VSI filters for RDMA |
| * @cdev: pointer to iidc_rdma_core_dev_info struct |
| * @vsi_id: VSI HW idx to update filter on |
| * @enable: bool whether to enable or disable filters |
| * |
| * Return: Zero on success, error code on failure |
| */ |
| int ice_rdma_update_vsi_filter(struct iidc_rdma_core_dev_info *cdev, |
| u16 vsi_id, bool enable) |
| { |
| struct ice_vsi *vsi; |
| struct ice_pf *pf; |
| int status; |
| |
| if (WARN_ON(!cdev)) |
| return -EINVAL; |
| |
| pf = pci_get_drvdata(cdev->pdev); |
| vsi = ice_find_vsi(pf, vsi_id); |
| if (!vsi) |
| return -EINVAL; |
| |
| status = ice_cfg_rdma_fltr(&pf->hw, vsi->idx, enable); |
| if (status) { |
| dev_err(ice_pf_to_dev(pf), "Failed to %sable RDMA filtering\n", |
| enable ? "en" : "dis"); |
| } else { |
| if (enable) |
| vsi->info.q_opt_flags |= ICE_AQ_VSI_Q_OPT_PE_FLTR_EN; |
| else |
| vsi->info.q_opt_flags &= ~ICE_AQ_VSI_Q_OPT_PE_FLTR_EN; |
| } |
| |
| return status; |
| } |
| EXPORT_SYMBOL_GPL(ice_rdma_update_vsi_filter); |
| |
| /** |
| * ice_alloc_rdma_qvector - alloc vector resources reserved for RDMA driver |
| * @cdev: pointer to iidc_rdma_core_dev_info struct |
| * @entry: MSI-X entry to be removed |
| * |
| * Return: Zero on success, error code on failure |
| */ |
| int ice_alloc_rdma_qvector(struct iidc_rdma_core_dev_info *cdev, |
| struct msix_entry *entry) |
| { |
| struct msi_map map; |
| struct ice_pf *pf; |
| |
| if (WARN_ON(!cdev)) |
| return -EINVAL; |
| |
| pf = pci_get_drvdata(cdev->pdev); |
| map = ice_alloc_irq(pf, true); |
| if (map.index < 0) |
| return -ENOMEM; |
| |
| entry->entry = map.index; |
| entry->vector = map.virq; |
| |
| return 0; |
| } |
| EXPORT_SYMBOL_GPL(ice_alloc_rdma_qvector); |
| |
| /** |
| * ice_free_rdma_qvector - free vector resources reserved for RDMA driver |
| * @cdev: pointer to iidc_rdma_core_dev_info struct |
| * @entry: MSI-X entry to be removed |
| */ |
| void ice_free_rdma_qvector(struct iidc_rdma_core_dev_info *cdev, |
| struct msix_entry *entry) |
| { |
| struct msi_map map; |
| struct ice_pf *pf; |
| |
| if (WARN_ON(!cdev || !entry)) |
| return; |
| |
| pf = pci_get_drvdata(cdev->pdev); |
| |
| map.index = entry->entry; |
| map.virq = entry->vector; |
| ice_free_irq(pf, map); |
| } |
| EXPORT_SYMBOL_GPL(ice_free_rdma_qvector); |
| |
| /** |
| * ice_adev_release - function to be mapped to AUX dev's release op |
| * @dev: pointer to device to free |
| */ |
| static void ice_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); |
| } |
| |
| /** |
| * ice_plug_aux_dev - allocate and register AUX device |
| * @pf: pointer to pf struct |
| * |
| * Return: Zero on success, error code on failure |
| */ |
| int ice_plug_aux_dev(struct ice_pf *pf) |
| { |
| struct iidc_rdma_core_auxiliary_dev *iadev; |
| struct iidc_rdma_core_dev_info *cdev; |
| struct auxiliary_device *adev; |
| int ret; |
| |
| /* if this PF doesn't support a technology that requires auxiliary |
| * devices, then gracefully exit |
| */ |
| if (!ice_is_rdma_ena(pf)) |
| return 0; |
| |
| cdev = pf->cdev_info; |
| if (!cdev) |
| return -ENODEV; |
| |
| iadev = kzalloc(sizeof(*iadev), GFP_KERNEL); |
| if (!iadev) |
| return -ENOMEM; |
| |
| adev = &iadev->adev; |
| iadev->cdev_info = cdev; |
| |
| adev->id = pf->aux_idx; |
| adev->dev.release = ice_adev_release; |
| adev->dev.parent = &pf->pdev->dev; |
| adev->name = cdev->rdma_protocol & IIDC_RDMA_PROTOCOL_ROCEV2 ? |
| "roce" : "iwarp"; |
| |
| ret = auxiliary_device_init(adev); |
| if (ret) { |
| kfree(iadev); |
| return ret; |
| } |
| |
| ret = auxiliary_device_add(adev); |
| if (ret) { |
| auxiliary_device_uninit(adev); |
| return ret; |
| } |
| |
| mutex_lock(&pf->adev_mutex); |
| cdev->adev = adev; |
| mutex_unlock(&pf->adev_mutex); |
| |
| return 0; |
| } |
| |
| /* ice_unplug_aux_dev - unregister and free AUX device |
| * @pf: pointer to pf struct |
| */ |
| void ice_unplug_aux_dev(struct ice_pf *pf) |
| { |
| struct auxiliary_device *adev; |
| |
| mutex_lock(&pf->adev_mutex); |
| adev = pf->cdev_info->adev; |
| pf->cdev_info->adev = NULL; |
| mutex_unlock(&pf->adev_mutex); |
| |
| if (adev) { |
| auxiliary_device_delete(adev); |
| auxiliary_device_uninit(adev); |
| } |
| } |
| |
| /** |
| * ice_init_rdma - initializes PF for RDMA use |
| * @pf: ptr to ice_pf |
| */ |
| int ice_init_rdma(struct ice_pf *pf) |
| { |
| struct iidc_rdma_priv_dev_info *privd; |
| struct device *dev = &pf->pdev->dev; |
| struct iidc_rdma_core_dev_info *cdev; |
| int ret; |
| |
| if (!ice_is_rdma_ena(pf)) { |
| dev_warn(dev, "RDMA is not supported on this device\n"); |
| return 0; |
| } |
| |
| cdev = kzalloc(sizeof(*cdev), GFP_KERNEL); |
| if (!cdev) |
| return -ENOMEM; |
| |
| pf->cdev_info = cdev; |
| |
| privd = kzalloc(sizeof(*privd), GFP_KERNEL); |
| if (!privd) { |
| ret = -ENOMEM; |
| goto err_privd_alloc; |
| } |
| |
| privd->pf_id = pf->hw.pf_id; |
| ret = xa_alloc(&ice_aux_id, &pf->aux_idx, NULL, XA_LIMIT(1, INT_MAX), |
| GFP_KERNEL); |
| if (ret) { |
| dev_err(dev, "Failed to allocate device ID for AUX driver\n"); |
| ret = -ENOMEM; |
| goto err_alloc_xa; |
| } |
| |
| cdev->iidc_priv = privd; |
| privd->netdev = pf->vsi[0]->netdev; |
| |
| privd->hw_addr = (u8 __iomem *)pf->hw.hw_addr; |
| cdev->pdev = pf->pdev; |
| privd->vport_id = pf->vsi[0]->vsi_num; |
| |
| pf->cdev_info->rdma_protocol |= IIDC_RDMA_PROTOCOL_ROCEV2; |
| ice_setup_dcb_qos_info(pf, &privd->qos_info); |
| ret = ice_plug_aux_dev(pf); |
| if (ret) |
| goto err_plug_aux_dev; |
| return 0; |
| |
| err_plug_aux_dev: |
| pf->cdev_info->adev = NULL; |
| xa_erase(&ice_aux_id, pf->aux_idx); |
| err_alloc_xa: |
| kfree(privd); |
| err_privd_alloc: |
| kfree(cdev); |
| pf->cdev_info = NULL; |
| |
| return ret; |
| } |
| |
| /** |
| * ice_deinit_rdma - deinitialize RDMA on PF |
| * @pf: ptr to ice_pf |
| */ |
| void ice_deinit_rdma(struct ice_pf *pf) |
| { |
| if (!ice_is_rdma_ena(pf)) |
| return; |
| |
| ice_unplug_aux_dev(pf); |
| xa_erase(&ice_aux_id, pf->aux_idx); |
| kfree(pf->cdev_info->iidc_priv); |
| kfree(pf->cdev_info); |
| pf->cdev_info = NULL; |
| } |