965 lines
23 KiB
C
965 lines
23 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/* Copyright (C) 2018-2021, Intel Corporation. */
|
|
|
|
/* Inter-Driver Communication */
|
|
#include "ice.h"
|
|
#include "ice_lib.h"
|
|
#include "ice_fltr.h"
|
|
#include "ice_dcb_lib.h"
|
|
#include "ice_ptp.h"
|
|
#include "ice_ieps.h"
|
|
|
|
static DEFINE_IDA(ice_cdev_info_ida);
|
|
|
|
static struct cdev_info_id ice_cdev_ids[] = ASSIGN_IIDC_INFO;
|
|
|
|
/**
|
|
* ice_get_auxiliary_drv - retrieve iidc_auxiliary_drv struct
|
|
* @cdev_info: pointer to iidc_core_dev_info struct
|
|
*
|
|
* This function has to be called with a device_lock on the
|
|
* cdev_info->adev.dev to avoid race conditions for auxiliary
|
|
* driver unload, and the mutex pf->adev_mutex locked to avoid
|
|
* plug/unplug race conditions..
|
|
*/
|
|
struct iidc_auxiliary_drv
|
|
*ice_get_auxiliary_drv(struct iidc_core_dev_info *cdev_info)
|
|
{
|
|
struct auxiliary_device *adev;
|
|
struct ice_pf *pf;
|
|
|
|
if (!cdev_info)
|
|
return NULL;
|
|
pf = pci_get_drvdata(cdev_info->pdev);
|
|
|
|
lockdep_assert_held(&pf->adev_mutex);
|
|
|
|
adev = cdev_info->adev;
|
|
if (!adev || !adev->dev.driver)
|
|
return NULL;
|
|
|
|
return container_of(adev->dev.driver, struct iidc_auxiliary_drv,
|
|
adrv.driver);
|
|
}
|
|
|
|
/**
|
|
* ice_for_each_aux - iterate across and call function for each aux driver
|
|
* @pf: pointer to private board struct
|
|
* @data: data to pass to function on each call
|
|
* @fn: pointer to function to call for each peer
|
|
*/
|
|
int
|
|
ice_for_each_aux(struct ice_pf *pf, void *data,
|
|
int (*fn)(struct iidc_core_dev_info *, void *))
|
|
{
|
|
unsigned int i;
|
|
|
|
if (!pf->cdev_infos)
|
|
return 0;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(ice_cdev_ids); i++) {
|
|
struct iidc_core_dev_info *cdev_info;
|
|
|
|
cdev_info = pf->cdev_infos[i];
|
|
if (cdev_info) {
|
|
int ret = fn(cdev_info, data);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* ice_send_event_to_aux - send event to a specific aux driver
|
|
* @cdev_info: pointer to iidc_core_dev_info struct for this aux
|
|
* @data: opaque pointer used to pass event struct
|
|
*/
|
|
static int
|
|
ice_send_event_to_aux(struct iidc_core_dev_info *cdev_info, void *data)
|
|
{
|
|
struct iidc_event *event = (struct iidc_event *)data;
|
|
struct iidc_auxiliary_drv *iadrv;
|
|
struct ice_pf *pf;
|
|
|
|
if (WARN_ON_ONCE(!in_task()))
|
|
return -EINVAL;
|
|
|
|
if (!cdev_info)
|
|
return -EINVAL;
|
|
pf = pci_get_drvdata(cdev_info->pdev);
|
|
|
|
if (!pf)
|
|
return -EINVAL;
|
|
mutex_lock(&pf->adev_mutex);
|
|
|
|
if (!cdev_info->adev || !event) {
|
|
mutex_unlock(&pf->adev_mutex);
|
|
return 0;
|
|
}
|
|
|
|
device_lock(&cdev_info->adev->dev);
|
|
iadrv = ice_get_auxiliary_drv(cdev_info);
|
|
if (iadrv && iadrv->event_handler)
|
|
iadrv->event_handler(cdev_info, event);
|
|
device_unlock(&cdev_info->adev->dev);
|
|
mutex_unlock(&pf->adev_mutex);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* ice_send_event_to_aux_no_lock - send event to aux dev without taking dev_lock
|
|
* @cdev: pointer to iidc_core_dev_info struct
|
|
* @data: opaque poiner used to pass event struct
|
|
*/
|
|
void ice_send_event_to_aux_no_lock(struct iidc_core_dev_info *cdev, void *data)
|
|
{
|
|
struct iidc_event *event = (struct iidc_event *)data;
|
|
struct iidc_auxiliary_drv *iadrv;
|
|
|
|
iadrv = ice_get_auxiliary_drv(cdev);
|
|
if (iadrv && iadrv->event_handler)
|
|
iadrv->event_handler(cdev, event);
|
|
}
|
|
|
|
/**
|
|
* ice_send_event_to_auxs - send event to all auxiliary drivers
|
|
* @pf: pointer to PF struct
|
|
* @event: pointer to iidc_event to propagate
|
|
*
|
|
* event struct to be populated by caller
|
|
*/
|
|
void ice_send_event_to_auxs(struct ice_pf *pf, struct iidc_event *event)
|
|
{
|
|
if (!event || !pf)
|
|
return;
|
|
|
|
if (bitmap_weight(event->type, IIDC_EVENT_NBITS) != 1) {
|
|
dev_warn(ice_pf_to_dev(pf), "Event with not exactly one type bit set\n");
|
|
return;
|
|
}
|
|
|
|
ice_for_each_aux(pf, event, ice_send_event_to_aux);
|
|
}
|
|
|
|
/**
|
|
* ice_unroll_cdev_info - destroy cdev_info resources
|
|
* @cdev_info: ptr to cdev_info struct
|
|
* @data: ptr to opaque data
|
|
*
|
|
* This function releases resources for cdev_info objects.
|
|
* Meant to be called from a ice_for_each_aux invocation
|
|
*/
|
|
int ice_unroll_cdev_info(struct iidc_core_dev_info *cdev_info,
|
|
void __always_unused *data)
|
|
{
|
|
if (!cdev_info)
|
|
return 0;
|
|
|
|
kfree(cdev_info);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef CONFIG_PM
|
|
/**
|
|
* ice_cdev_info_refresh_msix - load new values into iidc_core_dev_info structs
|
|
* @pf: pointer to private board struct
|
|
*/
|
|
void ice_cdev_info_refresh_msix(struct ice_pf *pf)
|
|
{
|
|
struct iidc_core_dev_info *cdev_info;
|
|
unsigned int i;
|
|
|
|
if (!pf->cdev_infos)
|
|
return;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(ice_cdev_ids); i++) {
|
|
if (!pf->cdev_infos[i])
|
|
continue;
|
|
|
|
cdev_info = pf->cdev_infos[i];
|
|
|
|
switch (cdev_info->cdev_info_id) {
|
|
case IIDC_RDMA_ID:
|
|
cdev_info->msix_count = pf->num_rdma_msix;
|
|
cdev_info->msix_entries =
|
|
&pf->msix_entries[pf->rdma_base_vector];
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif /* CONFIG_PM */
|
|
/**
|
|
* ice_alloc_rdma_qsets - Allocate Leaf Nodes for RDMA Qset
|
|
* @cdev_info: aux driver that is requesting the Leaf Nodes
|
|
* @qset: Resource to be allocated
|
|
*
|
|
* This function allocates Leaf Nodes for given RDMA Qset resources
|
|
* for the peer object.
|
|
*/
|
|
static int
|
|
ice_alloc_rdma_qsets(struct iidc_core_dev_info *cdev_info,
|
|
struct iidc_rdma_qset_params *qset)
|
|
{
|
|
u16 max_rdmaqs[ICE_MAX_TRAFFIC_CLASS];
|
|
#ifdef HAVE_NETDEV_UPPER_INFO
|
|
struct ice_lag *lag;
|
|
#endif /* HAVE_NETDEV_UPPER_INFO */
|
|
struct ice_vsi *vsi;
|
|
struct device *dev;
|
|
struct ice_pf *pf;
|
|
u32 qset_teid;
|
|
u16 qs_handle;
|
|
int i, status;
|
|
|
|
if (!cdev_info || !qset)
|
|
return -EINVAL;
|
|
|
|
pf = pci_get_drvdata(cdev_info->pdev);
|
|
dev = ice_pf_to_dev(pf);
|
|
|
|
if (!ice_chk_rdma_cap(pf))
|
|
return -EINVAL;
|
|
|
|
ice_for_each_traffic_class(i)
|
|
max_rdmaqs[i] = 0;
|
|
|
|
max_rdmaqs[qset->tc]++;
|
|
qs_handle = qset->qs_handle;
|
|
|
|
vsi = ice_find_vsi(pf, qset->vport_id);
|
|
if (!vsi) {
|
|
dev_err(dev, "RDMA QSet invalid VSI\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
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;
|
|
}
|
|
vsi->qset_handle[qset->tc] = qset->qs_handle;
|
|
qset->teid = qset_teid;
|
|
|
|
#ifdef HAVE_NETDEV_UPPER_INFO
|
|
lag = pf->lag;
|
|
if (lag && lag->bonded) {
|
|
mutex_lock(&pf->lag_mutex);
|
|
lag->rdma_qset[qset->tc] = *qset;
|
|
|
|
if (cdev_info->rdma_active_port != pf->hw.port_info->lport &&
|
|
cdev_info->rdma_active_port != ICE_LAG_INVALID_PORT) {
|
|
struct net_device *tmp_nd;
|
|
|
|
rcu_read_lock();
|
|
for_each_netdev_rcu(&init_net, tmp_nd) {
|
|
struct ice_netdev_priv *tmp_ndp;
|
|
struct ice_lag *tmp_lag;
|
|
struct ice_vsi *tmp_vsi;
|
|
struct ice_hw *tmp_hw;
|
|
|
|
if (!netif_is_ice(tmp_nd))
|
|
continue;
|
|
|
|
tmp_ndp = netdev_priv(tmp_nd);
|
|
tmp_vsi = tmp_ndp->vsi;
|
|
tmp_lag = tmp_vsi->back->lag;
|
|
|
|
if (!tmp_lag->bonded ||
|
|
tmp_lag->bond_id != lag->bond_id)
|
|
continue;
|
|
|
|
tmp_hw = &tmp_vsi->back->hw;
|
|
|
|
if (cdev_info->rdma_active_port ==
|
|
tmp_hw->port_info->lport)
|
|
status = ice_lag_move_node_sync(&pf->hw,
|
|
tmp_hw,
|
|
tmp_vsi,
|
|
qset);
|
|
}
|
|
rcu_read_unlock();
|
|
}
|
|
mutex_unlock(&pf->lag_mutex);
|
|
}
|
|
|
|
#endif /* HAVE_NETDEV_UPPER_INFO */
|
|
return status;
|
|
}
|
|
|
|
/**
|
|
* ice_free_rdma_qsets - Free leaf nodes for RDMA Qset
|
|
* @cdev_info: aux driver that requested qsets to be freed
|
|
* @qset: Resource to be freed
|
|
*/
|
|
static int
|
|
ice_free_rdma_qsets(struct iidc_core_dev_info *cdev_info,
|
|
struct iidc_rdma_qset_params *qset)
|
|
{
|
|
struct ice_vsi *vsi;
|
|
struct device *dev;
|
|
struct ice_pf *pf;
|
|
u16 vsi_id;
|
|
int status;
|
|
u32 teid;
|
|
u16 q_id;
|
|
|
|
if (!cdev_info || !qset)
|
|
return -EINVAL;
|
|
|
|
pf = pci_get_drvdata(cdev_info->pdev);
|
|
dev = ice_pf_to_dev(pf);
|
|
|
|
vsi_id = qset->vport_id;
|
|
vsi = ice_find_vsi(pf, vsi_id);
|
|
if (!vsi) {
|
|
dev_err(dev, "RDMA Invalid VSI\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (qset->vport_id != vsi_id) {
|
|
dev_err(dev, "RDMA Invalid VSI ID\n");
|
|
return -EINVAL;
|
|
}
|
|
q_id = qset->qs_handle;
|
|
teid = qset->teid;
|
|
|
|
vsi->qset_handle[qset->tc] = 0;
|
|
|
|
#ifdef HAVE_NETDEV_UPPER_INFO
|
|
if (pf->lag && pf->lag->bonded) {
|
|
mutex_lock(&pf->lag_mutex);
|
|
|
|
if (cdev_info->rdma_active_port != pf->hw.port_info->lport &&
|
|
cdev_info->rdma_active_port != ICE_LAG_INVALID_PORT) {
|
|
struct net_device *tmp_nd;
|
|
|
|
rcu_read_lock();
|
|
for_each_netdev_rcu(&init_net, tmp_nd) {
|
|
struct ice_netdev_priv *tmp_ndp;
|
|
struct ice_lag *tmp_lag;
|
|
struct ice_vsi *tmp_vsi;
|
|
struct ice_hw *tmp_hw;
|
|
|
|
if (!netif_is_ice(tmp_nd))
|
|
continue;
|
|
|
|
tmp_ndp = netdev_priv(tmp_nd);
|
|
tmp_vsi = tmp_ndp->vsi;
|
|
tmp_lag = tmp_vsi->back->lag;
|
|
tmp_hw = &tmp_vsi->back->hw;
|
|
|
|
if (!tmp_lag->bonded ||
|
|
tmp_lag->bond_id != pf->lag->bond_id)
|
|
continue;
|
|
|
|
if (cdev_info->rdma_active_port ==
|
|
tmp_hw->port_info->lport)
|
|
ice_lag_move_node_sync(tmp_hw, &pf->hw,
|
|
pf->vsi[0],
|
|
qset);
|
|
}
|
|
rcu_read_unlock();
|
|
}
|
|
mutex_unlock(&pf->lag_mutex);
|
|
}
|
|
|
|
#endif /* HAVE_NETDEV_UPPER_INFO */
|
|
status = ice_dis_vsi_rdma_qset(vsi->port_info, 1, &teid, &q_id);
|
|
if (status)
|
|
return -EINVAL;
|
|
|
|
#ifdef HAVE_NETDEV_UPPER_INFO
|
|
memset(&pf->lag->rdma_qset[qset->tc], 0, sizeof(*qset));
|
|
#endif /* HAVE_NETDEV_UPPER_INFO */
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* ice_cdev_info_alloc_res - Allocate requested resources for aux driver
|
|
* @cdev_info: struct for aux driver that is requesting resources
|
|
* @qset: Resource to be allocated
|
|
*/
|
|
static int
|
|
ice_cdev_info_alloc_res(struct iidc_core_dev_info *cdev_info,
|
|
struct iidc_rdma_qset_params *qset)
|
|
{
|
|
struct ice_pf *pf;
|
|
|
|
if (!cdev_info || !qset)
|
|
return -EINVAL;
|
|
|
|
pf = pci_get_drvdata(cdev_info->pdev);
|
|
if (!ice_pf_state_is_nominal(pf))
|
|
return -EBUSY;
|
|
|
|
return ice_alloc_rdma_qsets(cdev_info, qset);
|
|
}
|
|
|
|
/**
|
|
* ice_cdev_info_free_res - Free resources associated with aux driver
|
|
* @cdev_info: struct for aux driver that is requesting freeing of resources
|
|
* @qset: Resource to be freed
|
|
*/
|
|
static int
|
|
ice_cdev_info_free_res(struct iidc_core_dev_info *cdev_info,
|
|
struct iidc_rdma_qset_params *qset)
|
|
{
|
|
if (!cdev_info || !qset)
|
|
return -EINVAL;
|
|
|
|
return ice_free_rdma_qsets(cdev_info, qset);
|
|
}
|
|
|
|
/**
|
|
* ice_cdev_info_request_reset - accept request from peer to perform a reset
|
|
* @cdev_info: struct for aux driver that is requesting a reset
|
|
* @reset_type: type of reset the peer is requesting
|
|
*/
|
|
static int
|
|
ice_cdev_info_request_reset(struct iidc_core_dev_info *cdev_info,
|
|
enum iidc_reset_type reset_type)
|
|
{
|
|
enum ice_reset_req reset;
|
|
struct ice_pf *pf;
|
|
|
|
if (!cdev_info)
|
|
return -EINVAL;
|
|
|
|
pf = pci_get_drvdata(cdev_info->pdev);
|
|
|
|
switch (reset_type) {
|
|
case IIDC_PFR:
|
|
reset = ICE_RESET_PFR;
|
|
break;
|
|
case IIDC_CORER:
|
|
reset = ICE_RESET_CORER;
|
|
break;
|
|
case IIDC_GLOBR:
|
|
reset = ICE_RESET_GLOBR;
|
|
break;
|
|
default:
|
|
dev_err(ice_pf_to_dev(pf), "incorrect reset request from peer\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
return ice_schedule_reset(pf, reset);
|
|
}
|
|
|
|
/**
|
|
* ice_cdev_info_update_vsi_filter - update main VSI filters for RDMA
|
|
* @cdev_info: pointer to struct for aux device updating filters
|
|
* @vsi_id: vsi HW idx to update filter on
|
|
* @enable: bool whether to enable or disable filters
|
|
*/
|
|
static int
|
|
ice_cdev_info_update_vsi_filter(struct iidc_core_dev_info *cdev_info,
|
|
u16 vsi_id, bool enable)
|
|
{
|
|
struct ice_vsi *vsi;
|
|
struct ice_pf *pf;
|
|
int ret;
|
|
|
|
if (!cdev_info)
|
|
return -EINVAL;
|
|
|
|
pf = pci_get_drvdata(cdev_info->pdev);
|
|
|
|
vsi = ice_find_vsi(pf, vsi_id);
|
|
if (!vsi)
|
|
return -EINVAL;
|
|
|
|
ret = ice_cfg_iwarp_fltr(&pf->hw, vsi->idx, enable);
|
|
|
|
if (ret) {
|
|
dev_err(ice_pf_to_dev(pf), "Failed to %sable iWARP 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 ret;
|
|
}
|
|
|
|
/**
|
|
* ice_cdev_info_vc_send - send a virt channel message from an aux driver
|
|
* @cdev_info: pointer to cdev_info struct for aux driver
|
|
* @vf_id: the VF ID of recipient of message
|
|
* @msg: pointer to message contents
|
|
* @len: len of message
|
|
*
|
|
* Note that the VF ID is absolute for RDMA operations, but a relative ID for
|
|
* IPSEC operations.
|
|
*/
|
|
static int
|
|
ice_cdev_info_vc_send(struct iidc_core_dev_info *cdev_info, u32 vf_id,
|
|
u8 *msg, u16 len)
|
|
{
|
|
struct ice_pf *pf;
|
|
u32 rel_vf_id;
|
|
int status;
|
|
|
|
if (!cdev_info)
|
|
return -EINVAL;
|
|
if (!msg || !len)
|
|
return -ENOMEM;
|
|
|
|
pf = pci_get_drvdata(cdev_info->pdev);
|
|
if (len > ICE_AQ_MAX_BUF_LEN)
|
|
return -EINVAL;
|
|
|
|
if (ice_is_reset_in_progress(pf->state))
|
|
return -EBUSY;
|
|
|
|
switch (cdev_info->cdev_info_id) {
|
|
case IIDC_RDMA_ID:
|
|
/* The ID is absolute so it must be converted first */
|
|
rel_vf_id = ice_rel_vf_id(&pf->hw, vf_id);
|
|
|
|
if (!ice_is_valid_vf_id(pf, rel_vf_id))
|
|
return -ENODEV;
|
|
|
|
/* VIRTCHNL_OP_RDMA is being used for RoCEv2 msg also */
|
|
status = ice_aq_send_msg_to_vf(&pf->hw, rel_vf_id,
|
|
VIRTCHNL_OP_RDMA, 0, msg, len,
|
|
NULL);
|
|
break;
|
|
default:
|
|
dev_err(ice_pf_to_dev(pf), "Can't send message to VF, Aux not supported, %u\n",
|
|
(u32)cdev_info->cdev_info_id);
|
|
return -ENODEV;
|
|
}
|
|
|
|
if (status)
|
|
dev_err(ice_pf_to_dev(pf), "Unable to send msg to VF, error %d\n",
|
|
status);
|
|
return status;
|
|
}
|
|
|
|
/**
|
|
* ice_reserve_cdev_info_qvector - Reserve vector resources for aux drivers
|
|
* @pf: board private structure to initialize
|
|
*/
|
|
static int ice_reserve_cdev_info_qvector(struct ice_pf *pf)
|
|
{
|
|
if (ice_chk_rdma_cap(pf)) {
|
|
int index;
|
|
|
|
index = ice_get_res(pf, pf->irq_tracker, pf->num_rdma_msix, ICE_RES_RDMA_VEC_ID);
|
|
if (index < 0)
|
|
return index;
|
|
pf->num_avail_sw_msix -= pf->num_rdma_msix;
|
|
pf->rdma_base_vector = (u16)index;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* ice_send_vf_reset_to_aux - send a VF reset notification to the aux driver
|
|
* @cdev_info: pointer to the cdev_info object
|
|
* @vf_id: VF ID to query
|
|
*
|
|
* Tell the RDMA auxiliary driver that a VF is resetting
|
|
*/
|
|
void ice_send_vf_reset_to_aux(struct iidc_core_dev_info *cdev_info, u16 vf_id)
|
|
{
|
|
struct iidc_event *event;
|
|
|
|
event = kzalloc(sizeof(*event), GFP_KERNEL);
|
|
if (!event)
|
|
return;
|
|
set_bit(IIDC_EVENT_VF_RESET, event->type);
|
|
event->info.vf_id = (u32)vf_id;
|
|
ice_send_event_to_aux(cdev_info, event);
|
|
kfree(event);
|
|
}
|
|
|
|
/**
|
|
* ice_cdev_info_get_vf_port_info - get a VF's information
|
|
* @cdev_info: pointer to the cdev_info object
|
|
* @abs_vf_id: Absolute VF ID to query
|
|
* @vf_port_info: structure to populate for the caller
|
|
*
|
|
* Allow the RDMA auxiliary driver to query a VF's information
|
|
*/
|
|
static int
|
|
ice_cdev_info_get_vf_port_info(struct iidc_core_dev_info *cdev_info,
|
|
u16 abs_vf_id,
|
|
struct iidc_vf_port_info *vf_port_info)
|
|
{
|
|
struct ice_pf *pf;
|
|
|
|
if (!cdev_info || !vf_port_info)
|
|
return -EINVAL;
|
|
|
|
pf = pci_get_drvdata(cdev_info->pdev);
|
|
|
|
return ice_get_vf_port_info(pf, ice_rel_vf_id(&pf->hw, abs_vf_id),
|
|
vf_port_info);
|
|
}
|
|
|
|
/**
|
|
* ice_find_cdev_info_by_id - find cdev_info instance by its id
|
|
* @pf: pointer to private board struct
|
|
* @cdev_info_id: peer driver ID
|
|
*/
|
|
struct iidc_core_dev_info
|
|
*ice_find_cdev_info_by_id(struct ice_pf *pf, int cdev_info_id)
|
|
{
|
|
struct iidc_core_dev_info *cdev_info = NULL;
|
|
unsigned int i;
|
|
|
|
if (!pf->cdev_infos)
|
|
return NULL;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(ice_cdev_ids); i++) {
|
|
cdev_info = pf->cdev_infos[i];
|
|
if (cdev_info &&
|
|
cdev_info->cdev_info_id == cdev_info_id)
|
|
break;
|
|
cdev_info = NULL;
|
|
}
|
|
return cdev_info;
|
|
}
|
|
|
|
/**
|
|
* ice_cdev_info_update_vsi - update the pf_vsi info in cdev_info struct
|
|
* @cdev_info: pointer to cdev_info struct
|
|
* @vsi: VSI to be updated
|
|
*/
|
|
void ice_cdev_info_update_vsi(struct iidc_core_dev_info *cdev_info,
|
|
struct ice_vsi *vsi)
|
|
{
|
|
if (!cdev_info)
|
|
return;
|
|
|
|
cdev_info->vport_id = vsi->vsi_num;
|
|
}
|
|
|
|
/* Initialize the ice_ops struct, which is used in 'ice_init_aux_devices' */
|
|
static const struct iidc_core_ops iidc_ops = {
|
|
.alloc_res = ice_cdev_info_alloc_res,
|
|
.free_res = ice_cdev_info_free_res,
|
|
.request_reset = ice_cdev_info_request_reset,
|
|
.update_vport_filter = ice_cdev_info_update_vsi_filter,
|
|
.get_vf_info = ice_cdev_info_get_vf_port_info,
|
|
.vc_send = ice_cdev_info_vc_send,
|
|
.ieps_entry = ice_ieps_entry,
|
|
|
|
};
|
|
|
|
/**
|
|
* ice_cdev_info_adev_release - function to be mapped to aux dev's release op
|
|
* @dev: pointer to device to free
|
|
*/
|
|
static void ice_cdev_info_adev_release(struct device *dev)
|
|
{
|
|
struct iidc_auxiliary_dev *iadev;
|
|
|
|
iadev = container_of(dev, struct iidc_auxiliary_dev, adev.dev);
|
|
kfree(iadev);
|
|
}
|
|
|
|
/* ice_plug_aux_dev - allocate and register aux dev for cdev_info
|
|
* @cdev_info: pointer to cdev_info struct
|
|
* @name: name of peer_aux_dev
|
|
*
|
|
* The cdev_info must be setup before calling this function
|
|
*/
|
|
int ice_plug_aux_dev(struct iidc_core_dev_info *cdev_info, const char *name)
|
|
{
|
|
struct iidc_auxiliary_dev *iadev;
|
|
struct auxiliary_device *adev;
|
|
struct ice_pf *pf;
|
|
int ret = 0;
|
|
|
|
if (!cdev_info || !name)
|
|
return -EINVAL;
|
|
|
|
pf = pci_get_drvdata(cdev_info->pdev);
|
|
if (!pf)
|
|
return -EINVAL;
|
|
|
|
/* if this PF does not support a technology that requires auxiliary
|
|
* devices, then exit gracefully
|
|
*/
|
|
if (!ice_is_aux_ena(pf))
|
|
return ret;
|
|
mutex_lock(&pf->adev_mutex);
|
|
if (cdev_info->adev)
|
|
goto aux_plug_out;
|
|
|
|
if (cdev_info->cdev_info_id == IIDC_RDMA_ID && !ice_chk_rdma_cap(pf))
|
|
goto aux_plug_out;
|
|
|
|
iadev = kzalloc(sizeof(*iadev), GFP_KERNEL);
|
|
if (!iadev) {
|
|
ret = -ENOMEM;
|
|
goto aux_plug_out;
|
|
}
|
|
|
|
adev = &iadev->adev;
|
|
cdev_info->adev = adev;
|
|
iadev->cdev_info = cdev_info;
|
|
|
|
adev->id = pf->aux_idx;
|
|
adev->dev.release = ice_cdev_info_adev_release;
|
|
adev->dev.parent = &cdev_info->pdev->dev;
|
|
adev->name = name;
|
|
|
|
ret = auxiliary_device_init(adev);
|
|
if (ret) {
|
|
cdev_info->adev = NULL;
|
|
kfree(iadev);
|
|
goto aux_plug_out;
|
|
}
|
|
|
|
ret = auxiliary_device_add(adev);
|
|
if (ret) {
|
|
cdev_info->adev = NULL;
|
|
auxiliary_device_uninit(adev);
|
|
}
|
|
|
|
aux_plug_out:
|
|
mutex_unlock(&pf->adev_mutex);
|
|
return ret;
|
|
}
|
|
|
|
/* ice_unplug_aux_dev - unregister and free aux dev
|
|
* @cdev_info: pointer cdev_info struct
|
|
*/
|
|
void ice_unplug_aux_dev(struct iidc_core_dev_info *cdev_info)
|
|
{
|
|
struct ice_pf *pf;
|
|
|
|
if (!cdev_info)
|
|
return;
|
|
pf = pci_get_drvdata(cdev_info->pdev);
|
|
|
|
/* if this aux dev has already been unplugged move on */
|
|
mutex_lock(&pf->adev_mutex);
|
|
if (!cdev_info->adev) {
|
|
mutex_unlock(&pf->adev_mutex);
|
|
return;
|
|
}
|
|
|
|
auxiliary_device_delete(cdev_info->adev);
|
|
auxiliary_device_uninit(cdev_info->adev);
|
|
cdev_info->adev = NULL;
|
|
mutex_unlock(&pf->adev_mutex);
|
|
}
|
|
|
|
/* ice_plug_aux_devs - allocate and register aux dev for cdev_info
|
|
* @pf: pointer to pf struct
|
|
*
|
|
* The PFs cdev_infos array must be setup before calling this function
|
|
*/
|
|
int ice_plug_aux_devs(struct ice_pf *pf)
|
|
{
|
|
int ret;
|
|
u8 i;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(ice_cdev_ids); i++) {
|
|
const char *name;
|
|
|
|
if (!pf->cdev_infos[i])
|
|
continue;
|
|
|
|
if (pf->cdev_infos[i]->cdev_info_id == IIDC_RDMA_ID) {
|
|
if (pf->cdev_infos[i]->rdma_protocol ==
|
|
IIDC_RDMA_PROTOCOL_IWARP)
|
|
name = IIDC_RDMA_IWARP_NAME;
|
|
else
|
|
name = IIDC_RDMA_ROCE_NAME;
|
|
} else {
|
|
name = ice_cdev_ids[i].name;
|
|
}
|
|
|
|
ret = ice_plug_aux_dev(pf->cdev_infos[i], name);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* ice_unplug_aux_devs - unregister and free aux devs
|
|
* @pf: pointer to pf struct
|
|
*/
|
|
void ice_unplug_aux_devs(struct ice_pf *pf)
|
|
{
|
|
u8 i;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(ice_cdev_ids); i++) {
|
|
ice_unplug_aux_dev(pf->cdev_infos[i]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* ice_cdev_init_rdma_qos_info - initialize qos_info for RDMA peer
|
|
* @pf: pointer to ice_pf
|
|
* @qos_info: pointer to qos_info struct
|
|
*/
|
|
static void ice_cdev_init_rdma_qos_info(struct ice_pf *pf,
|
|
struct iidc_qos_params *qos_info)
|
|
{
|
|
int j;
|
|
|
|
/* setup qos_info fields with defaults */
|
|
qos_info->num_apps = 0;
|
|
qos_info->num_tc = 1;
|
|
|
|
for (j = 0; j < IIDC_MAX_USER_PRIORITY; j++)
|
|
qos_info->up2tc[j] = 0;
|
|
|
|
qos_info->tc_info[0].rel_bw = 100;
|
|
for (j = 1; j < IEEE_8021QAZ_MAX_TCS; j++)
|
|
qos_info->tc_info[j].rel_bw = 0;
|
|
/* for DCB, override the qos_info defaults. */
|
|
ice_setup_dcb_qos_info(pf, qos_info);
|
|
|
|
}
|
|
|
|
/**
|
|
* ice_init_aux_devices - initializes cdev_info objects and aux devices
|
|
* @pf: ptr to ice_pf
|
|
*/
|
|
int ice_init_aux_devices(struct ice_pf *pf)
|
|
{
|
|
struct ice_vsi *vsi = pf->vsi[0];
|
|
struct pci_dev *pdev = pf->pdev;
|
|
struct device *dev = &pdev->dev;
|
|
int err;
|
|
unsigned int i;
|
|
|
|
/* Reserve vector resources */
|
|
err = ice_reserve_cdev_info_qvector(pf);
|
|
if (err < 0) {
|
|
dev_err(dev, "failed to reserve vectors for aux drivers\n");
|
|
return err;
|
|
}
|
|
|
|
/* This PFs auxiliary id value */
|
|
pf->aux_idx = ida_alloc(&ice_cdev_info_ida, GFP_KERNEL);
|
|
if (pf->aux_idx < 0) {
|
|
dev_err(dev, "failed to allocate device ID for aux drvs\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
for (i = 0; i < ARRAY_SIZE(ice_cdev_ids); i++) {
|
|
struct msix_entry *entry = NULL;
|
|
struct iidc_core_dev_info *cdev_info;
|
|
|
|
/* structure layout needed for container_of's looks like:
|
|
* iidc_auxiliary_dev (container_of super-struct for adev)
|
|
* |--> auxiliary_device
|
|
* |--> *iidc_core_dev_info (pointer from cdev_info struct)
|
|
*
|
|
* The iidc_auxiliary_device has a lifespan as long as it
|
|
* is on the bus. Once removed it will be freed and a new
|
|
* one allocated if needed to re-add.
|
|
*
|
|
* The iidc_core_dev_info is tied to the life of the PF, and
|
|
* will exist as long as the PF driver is loaded. It will be
|
|
* freed in the remove flow for the PF driver.
|
|
*/
|
|
cdev_info = kzalloc(sizeof(*cdev_info), GFP_KERNEL);
|
|
if (!cdev_info) {
|
|
ida_simple_remove(&ice_cdev_info_ida, pf->aux_idx);
|
|
pf->aux_idx = -1;
|
|
return -ENOMEM;
|
|
}
|
|
|
|
pf->cdev_infos[i] = cdev_info;
|
|
|
|
cdev_info->hw_addr = (u8 __iomem *)pf->hw.hw_addr;
|
|
cdev_info->ver.major = IIDC_MAJOR_VER;
|
|
cdev_info->ver.minor = IIDC_MINOR_VER;
|
|
cdev_info->cdev_info_id = ice_cdev_ids[i].id;
|
|
cdev_info->pdev = pdev;
|
|
/* Initialize ice_ops */
|
|
cdev_info->ops = &iidc_ops;
|
|
/* make sure peer specific resources such as msix_count and
|
|
* msix_entries are initialized
|
|
*/
|
|
switch (ice_cdev_ids[i].id) {
|
|
|
|
case IIDC_RDMA_ID:
|
|
if (!ice_chk_rdma_cap(pf)) {
|
|
pf->cdev_infos[i] = NULL;
|
|
kfree(cdev_info);
|
|
continue;
|
|
}
|
|
cdev_info->vport_id = vsi->vsi_num;
|
|
cdev_info->netdev = vsi->netdev;
|
|
cdev_info->rdma_protocol = IIDC_RDMA_PROTOCOL_ROCEV2;
|
|
cdev_info->rdma_caps.gen = IIDC_RDMA_GEN_2;
|
|
cdev_info->ftype = IIDC_FUNCTION_TYPE_PF;
|
|
cdev_info->cdev_info_id = IIDC_RDMA_ID;
|
|
cdev_info->pf_id = pf->hw.pf_id;
|
|
#ifdef HAVE_NETDEV_UPPER_INFO
|
|
cdev_info->rdma_active_port = ICE_LAG_INVALID_PORT;
|
|
cdev_info->main_pf_port = pf->hw.port_info->lport;
|
|
#endif /* HAVE_NETDEV_UPPER_INFO */
|
|
ice_cdev_init_rdma_qos_info(pf, &cdev_info->qos_info);
|
|
/* make sure peer specific resources such as msix_count
|
|
* and msix_entries are initialized
|
|
*/
|
|
cdev_info->msix_count = pf->num_rdma_msix;
|
|
entry = &pf->msix_entries[pf->rdma_base_vector];
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
cdev_info->msix_entries = entry;
|
|
}
|
|
set_bit(ICE_FLAG_PLUG_AUX_DEV, pf->flags);
|
|
|
|
return err;
|
|
}
|
|
|
|
/**
|
|
* ice_is_rdma_aux_loaded - check if RDMA auxiliary driver is loaded
|
|
* @pf: ptr to ice_pf
|
|
*/
|
|
bool ice_is_rdma_aux_loaded(struct ice_pf *pf)
|
|
{
|
|
struct iidc_core_dev_info *rcdi;
|
|
struct iidc_auxiliary_drv *iadrv;
|
|
bool loaded;
|
|
|
|
rcdi = ice_find_cdev_info_by_id(pf, IIDC_RDMA_ID);
|
|
if (!rcdi)
|
|
return false;
|
|
|
|
mutex_lock(&pf->adev_mutex);
|
|
device_lock(&rcdi->adev->dev);
|
|
iadrv = ice_get_auxiliary_drv(rcdi);
|
|
loaded = iadrv ? true : false;
|
|
device_unlock(&rcdi->adev->dev);
|
|
mutex_unlock(&pf->adev_mutex);
|
|
|
|
dev_dbg(ice_pf_to_dev(pf), "RDMA Auxiliary Driver status: %s\n",
|
|
loaded ? "loaded" : "not loaded");
|
|
|
|
return loaded;
|
|
}
|
|
|