mirror of https://github.com/torvalds/linux.git
504 lines
12 KiB
C
504 lines
12 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/* Copyright (C) 2025 Intel Corporation */
|
|
|
|
#include <linux/export.h>
|
|
|
|
#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);
|
|
}
|