Merge branch 'mana-per-vport-eq'

Long Li says:

====================
net: mana: Per-vPort EQ and MSI-X management

This series moves EQ ownership from the shared mana_context to per-vPort
mana_port_context, enabling each vPort to have dedicated MSI-X vectors
when the hardware provides enough vectors. When vectors are limited, the
driver falls back to sharing MSI-X among vPorts.

The series introduces a GDMA IRQ Context (GIC) abstraction with reference
counting to manage interrupt context lifecycle. This allows both Ethernet
and RDMA EQs to dynamically acquire dedicated or shared MSI-X vectors at
vPort creation time rather than pre-allocating all vectors at probe time.
====================

Link: https://patch.msgid.link/20260605005717.2059954-1-longli@microsoft.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski 2026-06-09 17:23:42 -07:00
commit 81a699ccd3
8 changed files with 607 additions and 192 deletions

View File

@ -20,8 +20,10 @@ void mana_ib_uncfg_vport(struct mana_ib_dev *dev, struct mana_ib_pd *pd,
pd->vport_use_count--;
WARN_ON(pd->vport_use_count < 0);
if (!pd->vport_use_count)
if (!pd->vport_use_count) {
mana_destroy_eq(mpc);
mana_uncfg_vport(mpc);
}
mutex_unlock(&pd->vport_mutex);
}
@ -40,13 +42,27 @@ int mana_ib_cfg_vport(struct mana_ib_dev *dev, u32 port, struct mana_ib_pd *pd,
pd->vport_use_count++;
if (pd->vport_use_count > 1) {
/* Reject cross-port PD sharing. EQs and vport config
* are per-port, so the PD must stay bound to the port
* that was configured on the first raw QP creation.
*/
if (pd->vport_port != port) {
pd->vport_use_count--;
mutex_unlock(&pd->vport_mutex);
ibdev_dbg(&dev->ib_dev,
"PD already bound to port %u\n",
pd->vport_port);
return -EINVAL;
}
ibdev_dbg(&dev->ib_dev,
"Skip as this PD is already configured vport\n");
mutex_unlock(&pd->vport_mutex);
return 0;
}
err = mana_cfg_vport(mpc, pd->pdn, doorbell_id);
pd->vport_port = port;
err = mana_cfg_vport(mpc, pd->pdn, doorbell_id, true);
if (err) {
pd->vport_use_count--;
mutex_unlock(&pd->vport_mutex);
@ -55,15 +71,23 @@ int mana_ib_cfg_vport(struct mana_ib_dev *dev, u32 port, struct mana_ib_pd *pd,
return err;
}
err = mana_create_eq(mpc);
if (err) {
mana_uncfg_vport(mpc);
pd->vport_use_count--;
} else {
pd->tx_shortform_allowed = mpc->tx_shortform_allowed;
pd->tx_vp_offset = mpc->tx_vp_offset;
}
mutex_unlock(&pd->vport_mutex);
pd->tx_shortform_allowed = mpc->tx_shortform_allowed;
pd->tx_vp_offset = mpc->tx_vp_offset;
if (!err)
ibdev_dbg(&dev->ib_dev, "vport handle %llx pdid %x doorbell_id %x\n",
mpc->port_handle, pd->pdn, doorbell_id);
ibdev_dbg(&dev->ib_dev, "vport handle %llx pdid %x doorbell_id %x\n",
mpc->port_handle, pd->pdn, doorbell_id);
return 0;
return err;
}
int mana_ib_alloc_pd(struct ib_pd *ibpd, struct ib_udata *udata)
@ -741,7 +765,8 @@ int mana_ib_create_eqs(struct mana_ib_dev *mdev)
{
struct gdma_context *gc = mdev_to_gc(mdev);
struct gdma_queue_spec spec = {};
int err, i;
struct gdma_irq_context *gic;
int err, i, msi;
spec.type = GDMA_EQ;
spec.monitor_avl_buf = false;
@ -749,11 +774,19 @@ int mana_ib_create_eqs(struct mana_ib_dev *mdev)
spec.eq.callback = mana_ib_event_handler;
spec.eq.context = mdev;
spec.eq.log2_throttle_limit = LOG2_EQ_THROTTLE;
spec.eq.msix_index = 0;
msi = 0;
gic = mana_gd_get_gic(gc, false, &msi);
if (IS_ERR(gic))
return PTR_ERR(gic);
spec.eq.msix_index = msi;
err = mana_gd_create_mana_eq(mdev->gdma_dev, &spec, &mdev->fatal_err_eq);
if (err)
if (err) {
mana_gd_put_gic(gc, false, 0);
return err;
}
mdev->fatal_err_eq->eq.irq = gic->irq;
mdev->eqs = kzalloc_objs(struct gdma_queue *,
mdev->ib_dev.num_comp_vectors);
@ -763,32 +796,50 @@ int mana_ib_create_eqs(struct mana_ib_dev *mdev)
}
spec.eq.callback = NULL;
for (i = 0; i < mdev->ib_dev.num_comp_vectors; i++) {
spec.eq.msix_index = (i + 1) % gc->num_msix_usable;
err = mana_gd_create_mana_eq(mdev->gdma_dev, &spec, &mdev->eqs[i]);
if (err)
msi = (i + 1) % gc->num_msix_usable;
gic = mana_gd_get_gic(gc, false, &msi);
if (IS_ERR(gic)) {
err = PTR_ERR(gic);
goto destroy_eqs;
}
spec.eq.msix_index = msi;
err = mana_gd_create_mana_eq(mdev->gdma_dev, &spec, &mdev->eqs[i]);
if (err) {
mana_gd_put_gic(gc, false, msi);
goto destroy_eqs;
}
mdev->eqs[i]->eq.irq = gic->irq;
}
return 0;
destroy_eqs:
while (i-- > 0)
while (i-- > 0) {
mana_gd_destroy_queue(gc, mdev->eqs[i]);
mana_gd_put_gic(gc, false, (i + 1) % gc->num_msix_usable);
}
kfree(mdev->eqs);
destroy_fatal_eq:
mana_gd_destroy_queue(gc, mdev->fatal_err_eq);
mana_gd_put_gic(gc, false, 0);
return err;
}
void mana_ib_destroy_eqs(struct mana_ib_dev *mdev)
{
struct gdma_context *gc = mdev_to_gc(mdev);
int i;
int i, msi;
mana_gd_destroy_queue(gc, mdev->fatal_err_eq);
mana_gd_put_gic(gc, false, 0);
for (i = 0; i < mdev->ib_dev.num_comp_vectors; i++)
for (i = 0; i < mdev->ib_dev.num_comp_vectors; i++) {
mana_gd_destroy_queue(gc, mdev->eqs[i]);
msi = (i + 1) % gc->num_msix_usable;
mana_gd_put_gic(gc, false, msi);
}
kfree(mdev->eqs);
}

View File

@ -102,6 +102,20 @@ struct mana_ib_pd {
struct mutex vport_mutex;
int vport_use_count;
/* Port bound to this PD for raw QP usage. Only valid when
* vport_use_count > 0. A PD can only be associated with a
* single physical port because per-port EQs and vport
* configuration are tied to the PD's refcount.
*/
u32 vport_port;
/* Only one RSS QP is allowed per vport because each RSS QP
* overwrites the vport steering config (indirection table /
* hash key) and mana_disable_vport_rx() on destroy would
* blackhole traffic for any other RSS QP on the same vport.
*/
bool has_rss_qp;
bool tx_shortform_allowed;
u32 tx_vp_offset;
};

View File

@ -79,6 +79,7 @@ static int mana_ib_create_qp_rss(struct ib_qp *ibqp, struct ib_pd *pd,
struct ib_qp_init_attr *attr,
struct ib_udata *udata)
{
struct mana_ib_pd *mana_pd = container_of(pd, struct mana_ib_pd, ibpd);
struct mana_ib_qp *qp = container_of(ibqp, struct mana_ib_qp, ibqp);
struct mana_ib_dev *mdev =
container_of(pd->device, struct mana_ib_dev, ib_dev);
@ -155,6 +156,30 @@ static int mana_ib_create_qp_rss(struct ib_qp *ibqp, struct ib_pd *pd,
qp->port = port;
/* Take a reference on the vport to ensure EQs outlive this QP.
* The vport must already be configured by a raw QP on the
* same port cross-port PD sharing is not supported.
* Only one RSS QP per vport is allowed because each one
* overwrites the steering config and destroy disables RX
* globally.
*/
mutex_lock(&mana_pd->vport_mutex);
if (!mana_pd->vport_use_count || mana_pd->vport_port != port) {
mutex_unlock(&mana_pd->vport_mutex);
ret = -EINVAL;
goto fail;
}
if (mana_pd->has_rss_qp) {
mutex_unlock(&mana_pd->vport_mutex);
ibdev_dbg(&mdev->ib_dev,
"Only one RSS QP per vport is supported\n");
ret = -EBUSY;
goto fail;
}
mana_pd->vport_use_count++;
mana_pd->has_rss_qp = true;
mutex_unlock(&mana_pd->vport_mutex);
for (i = 0; i < ind_tbl_size; i++) {
struct mana_obj_spec wq_spec = {};
struct mana_obj_spec cq_spec = {};
@ -171,13 +196,19 @@ static int mana_ib_create_qp_rss(struct ib_qp *ibqp, struct ib_pd *pd,
cq_spec.gdma_region = cq->queue.gdma_region;
cq_spec.queue_size = cq->cqe * COMP_ENTRY_SIZE;
cq_spec.modr_ctx_id = 0;
eq = &mpc->ac->eqs[cq->comp_vector];
/* Map comp_vector to a per-vPort EQ. The modulo handles
* the case where the RDMA-advertised num_comp_vectors
* exceeds this port's num_queues (e.g. after ethtool -L
* reduces it), remapping to an available EQ rather than
* failing the QP creation.
*/
eq = &mpc->eqs[cq->comp_vector % mpc->num_queues];
cq_spec.attached_eq = eq->eq->id;
ret = mana_create_wq_obj(mpc, mpc->port_handle, GDMA_RQ,
&wq_spec, &cq_spec, &wq->rx_object);
if (ret)
goto fail;
goto free_vport;
/* The GDMA regions are now owned by the WQ object */
wq->queue.gdma_region = GDMA_INVALID_DMA_REGION;
@ -199,7 +230,7 @@ static int mana_ib_create_qp_rss(struct ib_qp *ibqp, struct ib_pd *pd,
ret = mana_ib_install_cq_cb(mdev, cq);
if (ret) {
mana_destroy_wq_obj(mpc, GDMA_RQ, wq->rx_object);
goto fail;
goto free_vport;
}
}
resp.num_entries = i;
@ -210,7 +241,7 @@ static int mana_ib_create_qp_rss(struct ib_qp *ibqp, struct ib_pd *pd,
ucmd.rx_hash_key_len,
ucmd.rx_hash_key);
if (ret)
goto fail;
goto free_vport;
ret = ib_copy_to_udata(udata, &resp, sizeof(resp));
if (ret) {
@ -226,7 +257,7 @@ static int mana_ib_create_qp_rss(struct ib_qp *ibqp, struct ib_pd *pd,
err_disable_vport_rx:
mana_disable_vport_rx(mpc);
fail:
free_vport:
while (i-- > 0) {
ibwq = ind_tbl->ind_tbl[i];
ibcq = ibwq->cq;
@ -237,6 +268,13 @@ static int mana_ib_create_qp_rss(struct ib_qp *ibqp, struct ib_pd *pd,
mana_destroy_wq_obj(mpc, GDMA_RQ, wq->rx_object);
}
mutex_lock(&mana_pd->vport_mutex);
mana_pd->has_rss_qp = false;
mutex_unlock(&mana_pd->vport_mutex);
mana_ib_uncfg_vport(mdev, mana_pd, port);
fail:
kfree(mana_ind_table);
return ret;
@ -299,7 +337,7 @@ static int mana_ib_create_qp_raw(struct ib_qp *ibqp, struct ib_pd *ibpd,
err = mana_ib_cfg_vport(mdev, port, pd, mana_ucontext->doorbell);
if (err)
return -ENODEV;
return err;
qp->port = port;
@ -321,7 +359,14 @@ static int mana_ib_create_qp_raw(struct ib_qp *ibqp, struct ib_pd *ibpd,
cq_spec.queue_size = send_cq->cqe * COMP_ENTRY_SIZE;
cq_spec.modr_ctx_id = 0;
eq_vec = send_cq->comp_vector;
eq = &mpc->ac->eqs[eq_vec];
if (!mpc->eqs) {
err = -EINVAL;
goto err_destroy_queue;
}
/* Map comp_vector to a per-vPort EQ. See comment in
* mana_ib_create_qp_rss() for the modulo rationale.
*/
eq = &mpc->eqs[eq_vec % mpc->num_queues];
cq_spec.attached_eq = eq->eq->id;
err = mana_create_wq_obj(mpc, mpc->port_handle, GDMA_SQ, &wq_spec,
@ -785,14 +830,17 @@ static int mana_ib_destroy_qp_rss(struct mana_ib_qp *qp,
{
struct mana_ib_dev *mdev =
container_of(qp->ibqp.device, struct mana_ib_dev, ib_dev);
struct ib_pd *ibpd = qp->ibqp.pd;
struct mana_port_context *mpc;
struct net_device *ndev;
struct mana_ib_pd *pd;
struct mana_ib_wq *wq;
struct ib_wq *ibwq;
int i;
ndev = mana_ib_get_netdev(qp->ibqp.device, qp->port);
mpc = netdev_priv(ndev);
pd = container_of(ibpd, struct mana_ib_pd, ibpd);
/* Disable vPort RX steering before destroying RX WQ objects.
* Otherwise firmware still routes traffic to the destroyed queues,
@ -817,6 +865,12 @@ static int mana_ib_destroy_qp_rss(struct mana_ib_qp *qp,
mana_destroy_wq_obj(mpc, GDMA_RQ, wq->rx_object);
}
mutex_lock(&pd->vport_mutex);
pd->has_rss_qp = false;
mutex_unlock(&pd->vport_mutex);
mana_ib_uncfg_vport(mdev, pd, qp->port);
return 0;
}

View File

@ -179,8 +179,21 @@ static int mana_gd_query_max_resources(struct pci_dev *pdev)
struct gdma_context *gc = pci_get_drvdata(pdev);
struct gdma_query_max_resources_resp resp = {};
struct gdma_general_req req = {};
unsigned int max_num_queues;
u8 bm_hostmode;
u16 num_ports;
int err;
/* Reset msi_sharing so it is recomputed from current hardware
* state. On resume, num_online_cpus() or num_msix_usable may
* have changed, making dedicated MSI-X feasible where it was
* not before. Only reset on platforms that support dynamic
* MSI-X allocation; on non-dyn platforms msi_sharing is
* unconditionally true (set in mana_gd_setup_hwc_irqs).
*/
if (pci_msix_can_alloc_dyn(to_pci_dev(gc->dev)))
gc->msi_sharing = false;
mana_gd_init_req_hdr(&req.hdr, GDMA_QUERY_MAX_RESOURCES,
sizeof(req), sizeof(resp));
@ -232,6 +245,52 @@ static int mana_gd_query_max_resources(struct pci_dev *pdev)
debugfs_create_u32("max_num_queues", 0400, gc->mana_pci_debugfs,
&gc->max_num_queues);
err = mana_gd_query_device_cfg(gc, MANA_MAJOR_VERSION,
MANA_MINOR_VERSION,
MANA_MICRO_VERSION,
&num_ports, &bm_hostmode);
if (err)
return err;
if (!num_ports) {
dev_err(gc->dev, "Failed to detect any vPort\n");
return -EINVAL;
}
/* Cap to the same limit used by mana_probe() for port instantiation,
* so MSI-X and queue budgeting matches the actual port count.
*/
if (num_ports > MAX_PORTS_IN_MANA_DEV)
num_ports = MAX_PORTS_IN_MANA_DEV;
/*
* Adjust the per-vPort max queue count to allow dedicated
* MSIx for each vPort. Prefer at least MANA_DEF_NUM_QUEUES,
* but the hardware max (gc->max_num_queues) takes precedence.
*/
max_num_queues = (gc->num_msix_usable - 1) / num_ports;
max_num_queues = rounddown_pow_of_two(max(max_num_queues, 1U));
if (max_num_queues < MANA_DEF_NUM_QUEUES)
max_num_queues = MANA_DEF_NUM_QUEUES;
/*
* Use dedicated MSIx for EQs whenever possible, use MSIx sharing for
* Ethernet EQs when (max_num_queues * num_ports > num_msix_usable - 1).
*/
max_num_queues = min(gc->max_num_queues, max_num_queues);
if (max_num_queues * num_ports > gc->num_msix_usable - 1)
gc->msi_sharing = true;
/* If MSI is shared, use max allowed value */
if (gc->msi_sharing)
gc->max_num_queues_vport = min(gc->num_msix_usable - 1,
gc->max_num_queues);
else
gc->max_num_queues_vport = max_num_queues;
dev_info(gc->dev, "MSI sharing mode %u max queues %u\n",
gc->msi_sharing, gc->max_num_queues_vport);
return 0;
}
@ -812,6 +871,10 @@ static int mana_gd_register_irq(struct gdma_queue *queue,
}
queue->eq.msix_index = msi_index;
/* The caller acquired a GIC reference via mana_gd_get_gic().
* That refcount prevents mana_gd_put_gic() from erasing this
* irq_contexts entry concurrently.
*/
gic = xa_load(&gc->irq_contexts, msi_index);
if (WARN_ON(!gic))
return -EINVAL;
@ -839,6 +902,10 @@ static void mana_gd_deregister_irq(struct gdma_queue *queue)
if (WARN_ON(msix_index >= gc->num_msix_usable))
return;
/* The caller releases the GIC reference via mana_gd_put_gic()
* after this function returns. The refcount guarantees this
* irq_contexts entry is still valid.
*/
gic = xa_load(&gc->irq_contexts, msix_index);
if (WARN_ON(!gic))
return;
@ -852,7 +919,6 @@ static void mana_gd_deregister_irq(struct gdma_queue *queue)
}
spin_unlock_irqrestore(&gic->lock, flags);
queue->eq.msix_index = INVALID_PCI_MSIX_INDEX;
synchronize_rcu();
}
@ -967,6 +1033,7 @@ static int mana_gd_create_eq(struct gdma_dev *gd,
out:
dev_err(dev, "Failed to create EQ: %d\n", err);
mana_gd_destroy_eq(gc, false, queue);
queue->eq.msix_index = INVALID_PCI_MSIX_INDEX;
return err;
}
@ -1620,6 +1687,166 @@ static irqreturn_t mana_gd_intr(int irq, void *arg)
return IRQ_HANDLED;
}
void mana_gd_put_gic(struct gdma_context *gc, bool use_msi_bitmap, int msi)
{
struct pci_dev *dev = to_pci_dev(gc->dev);
struct gdma_irq_context *gic;
struct msi_map irq_map;
int irq;
mutex_lock(&gc->gic_mutex);
gic = xa_load(&gc->irq_contexts, msi);
if (WARN_ON(!gic)) {
mutex_unlock(&gc->gic_mutex);
return;
}
if (use_msi_bitmap)
gic->bitmap_refs--;
if (use_msi_bitmap && gic->bitmap_refs == 0)
clear_bit(msi, gc->msi_bitmap);
if (!refcount_dec_and_test(&gic->refcount))
goto out;
irq = gic->irq;
irq_update_affinity_hint(irq, NULL);
free_irq(irq, gic);
if (gic->dyn_msix) {
irq_map.virq = irq;
irq_map.index = msi;
pci_msix_free_irq(dev, irq_map);
}
xa_erase(&gc->irq_contexts, msi);
kfree(gic);
out:
mutex_unlock(&gc->gic_mutex);
}
EXPORT_SYMBOL_NS(mana_gd_put_gic, "NET_MANA");
/*
* Get a GIC (GDMA IRQ Context) on a MSI vector
* a MSI can be shared between different EQs, this function supports setting
* up separate MSIs using a bitmap, or directly using the MSI index
*
* @use_msi_bitmap:
* True if MSI is assigned by this function on available slots from bitmap.
* False if MSI is passed from *msi_requested
*/
struct gdma_irq_context *mana_gd_get_gic(struct gdma_context *gc,
bool use_msi_bitmap,
int *msi_requested)
{
struct pci_dev *dev = to_pci_dev(gc->dev);
struct gdma_irq_context *gic;
struct msi_map irq_map = { };
int irq;
int msi;
int err;
mutex_lock(&gc->gic_mutex);
if (use_msi_bitmap) {
msi = find_first_zero_bit(gc->msi_bitmap, gc->num_msix_usable);
if (msi >= gc->num_msix_usable) {
dev_err(gc->dev, "No free MSI vectors available\n");
gic = ERR_PTR(-ENOSPC);
goto out;
}
*msi_requested = msi;
} else {
msi = *msi_requested;
}
gic = xa_load(&gc->irq_contexts, msi);
if (gic) {
refcount_inc(&gic->refcount);
if (use_msi_bitmap) {
gic->bitmap_refs++;
set_bit(msi, gc->msi_bitmap);
}
goto out;
}
irq = pci_irq_vector(dev, msi);
if (irq == -EINVAL) {
irq_map = pci_msix_alloc_irq_at(dev, msi, NULL);
if (!irq_map.virq) {
err = irq_map.index;
dev_err(gc->dev,
"Failed to alloc irq_map msi %d err %d\n",
msi, err);
gic = ERR_PTR(err);
goto out;
}
irq = irq_map.virq;
msi = irq_map.index;
*msi_requested = msi;
}
gic = kzalloc(sizeof(*gic), GFP_KERNEL);
if (!gic) {
gic = ERR_PTR(-ENOMEM);
if (irq_map.virq)
pci_msix_free_irq(dev, irq_map);
goto out;
}
gic->handler = mana_gd_process_eq_events;
gic->msi = msi;
gic->irq = irq;
INIT_LIST_HEAD(&gic->eq_list);
spin_lock_init(&gic->lock);
if (!gic->msi)
snprintf(gic->name, MANA_IRQ_NAME_SZ, "mana_hwc@pci:%s",
pci_name(dev));
else
snprintf(gic->name, MANA_IRQ_NAME_SZ, "mana_msi%d@pci:%s",
gic->msi, pci_name(dev));
err = request_irq(irq, mana_gd_intr, 0, gic->name, gic);
if (err) {
dev_err(gc->dev, "Failed to request irq %d %s\n",
irq, gic->name);
kfree(gic);
gic = ERR_PTR(err);
if (irq_map.virq)
pci_msix_free_irq(dev, irq_map);
goto out;
}
gic->dyn_msix = !!irq_map.virq;
refcount_set(&gic->refcount, 1);
gic->bitmap_refs = use_msi_bitmap ? 1 : 0;
err = xa_err(xa_store(&gc->irq_contexts, msi, gic, GFP_KERNEL));
if (err) {
dev_err(gc->dev, "Failed to store irq context for msi %d: %d\n",
msi, err);
free_irq(irq, gic);
kfree(gic);
gic = ERR_PTR(err);
if (irq_map.virq)
pci_msix_free_irq(dev, irq_map);
goto out;
}
if (use_msi_bitmap)
set_bit(msi, gc->msi_bitmap);
out:
mutex_unlock(&gc->gic_mutex);
return gic;
}
EXPORT_SYMBOL_NS(mana_gd_get_gic, "NET_MANA");
int mana_gd_alloc_res_map(u32 res_avail, struct gdma_resource *r)
{
r->map = bitmap_zalloc(res_avail, GFP_KERNEL);
@ -1722,7 +1949,7 @@ static int mana_gd_setup_dyn_irqs(struct pci_dev *pdev, int nvec)
struct gdma_context *gc = pci_get_drvdata(pdev);
struct gdma_irq_context *gic;
bool skip_first_cpu = false;
int *irqs, irq, err, i;
int *irqs, err, i, msi;
irqs = kmalloc_objs(int, nvec);
if (!irqs)
@ -1735,30 +1962,14 @@ static int mana_gd_setup_dyn_irqs(struct pci_dev *pdev, int nvec)
* further used in irq_setup()
*/
for (i = 1; i <= nvec; i++) {
gic = kzalloc_obj(*gic);
if (!gic) {
err = -ENOMEM;
msi = i;
gic = mana_gd_get_gic(gc, false, &msi);
if (IS_ERR(gic)) {
err = PTR_ERR(gic);
goto free_irq;
}
gic->handler = mana_gd_process_eq_events;
INIT_LIST_HEAD(&gic->eq_list);
spin_lock_init(&gic->lock);
snprintf(gic->name, MANA_IRQ_NAME_SZ, "mana_q%d@pci:%s",
i - 1, pci_name(pdev));
/* one pci vector is already allocated for HWC */
irqs[i - 1] = pci_irq_vector(pdev, i);
if (irqs[i - 1] < 0) {
err = irqs[i - 1];
goto free_current_gic;
}
err = request_irq(irqs[i - 1], mana_gd_intr, 0, gic->name, gic);
if (err)
goto free_current_gic;
xa_store(&gc->irq_contexts, i, gic, GFP_KERNEL);
irqs[i - 1] = gic->irq;
}
/*
@ -1780,20 +1991,9 @@ static int mana_gd_setup_dyn_irqs(struct pci_dev *pdev, int nvec)
kfree(irqs);
return 0;
free_current_gic:
kfree(gic);
free_irq:
for (i -= 1; i > 0; i--) {
irq = pci_irq_vector(pdev, i);
gic = xa_load(&gc->irq_contexts, i);
if (WARN_ON(!gic))
continue;
irq_update_affinity_hint(irq, NULL);
free_irq(irq, gic);
xa_erase(&gc->irq_contexts, i);
kfree(gic);
}
for (i -= 1; i > 0; i--)
mana_gd_put_gic(gc, false, i);
kfree(irqs);
return err;
}
@ -1802,9 +2002,9 @@ static int mana_gd_setup_irqs(struct pci_dev *pdev, int nvec)
{
struct gdma_context *gc = pci_get_drvdata(pdev);
struct gdma_irq_context *gic;
int *irqs, *start_irqs, irq;
int *irqs, *start_irqs;
unsigned int cpu;
int err, i;
int err, i, msi;
irqs = kmalloc_objs(int, nvec);
if (!irqs)
@ -1813,34 +2013,14 @@ static int mana_gd_setup_irqs(struct pci_dev *pdev, int nvec)
start_irqs = irqs;
for (i = 0; i < nvec; i++) {
gic = kzalloc_obj(*gic);
if (!gic) {
err = -ENOMEM;
msi = i;
gic = mana_gd_get_gic(gc, false, &msi);
if (IS_ERR(gic)) {
err = PTR_ERR(gic);
goto free_irq;
}
gic->handler = mana_gd_process_eq_events;
INIT_LIST_HEAD(&gic->eq_list);
spin_lock_init(&gic->lock);
if (!i)
snprintf(gic->name, MANA_IRQ_NAME_SZ, "mana_hwc@pci:%s",
pci_name(pdev));
else
snprintf(gic->name, MANA_IRQ_NAME_SZ, "mana_q%d@pci:%s",
i - 1, pci_name(pdev));
irqs[i] = pci_irq_vector(pdev, i);
if (irqs[i] < 0) {
err = irqs[i];
goto free_current_gic;
}
err = request_irq(irqs[i], mana_gd_intr, 0, gic->name, gic);
if (err)
goto free_current_gic;
xa_store(&gc->irq_contexts, i, gic, GFP_KERNEL);
irqs[i] = gic->irq;
}
/* If number of IRQ is one extra than number of online CPUs,
@ -1869,20 +2049,9 @@ static int mana_gd_setup_irqs(struct pci_dev *pdev, int nvec)
kfree(start_irqs);
return 0;
free_current_gic:
kfree(gic);
free_irq:
for (i -= 1; i >= 0; i--) {
irq = pci_irq_vector(pdev, i);
gic = xa_load(&gc->irq_contexts, i);
if (WARN_ON(!gic))
continue;
irq_update_affinity_hint(irq, NULL);
free_irq(irq, gic);
xa_erase(&gc->irq_contexts, i);
kfree(gic);
}
for (i -= 1; i >= 0; i--)
mana_gd_put_gic(gc, false, i);
kfree(start_irqs);
return err;
@ -1901,6 +2070,7 @@ static int mana_gd_setup_hwc_irqs(struct pci_dev *pdev)
/* Need 1 interrupt for HWC */
max_irqs = min(num_online_cpus(), MANA_MAX_NUM_QUEUES) + 1;
min_irqs = 2;
gc->msi_sharing = true;
}
nvec = pci_alloc_irq_vectors(pdev, min_irqs, max_irqs, PCI_IRQ_MSIX);
@ -1955,30 +2125,24 @@ static int mana_gd_setup_remaining_irqs(struct pci_dev *pdev)
static void mana_gd_remove_irqs(struct pci_dev *pdev)
{
struct gdma_context *gc = pci_get_drvdata(pdev);
struct gdma_irq_context *gic;
int irq, i;
int i;
if (gc->max_num_msix < 1)
return;
for (i = 0; i < gc->max_num_msix; i++) {
irq = pci_irq_vector(pdev, i);
if (irq < 0)
if (!xa_load(&gc->irq_contexts, i))
continue;
gic = xa_load(&gc->irq_contexts, i);
if (WARN_ON(!gic))
continue;
/* Need to clear the hint before free_irq */
irq_update_affinity_hint(irq, NULL);
free_irq(irq, gic);
xa_erase(&gc->irq_contexts, i);
kfree(gic);
mana_gd_put_gic(gc, false, i);
}
WARN_ON(!xa_empty(&gc->irq_contexts));
pci_free_irq_vectors(pdev);
bitmap_free(gc->msi_bitmap);
gc->msi_bitmap = NULL;
gc->max_num_msix = 0;
gc->num_msix_usable = 0;
}
@ -2018,6 +2182,10 @@ static int mana_gd_setup(struct pci_dev *pdev)
if (err)
goto destroy_hwc;
err = mana_gd_detect_devices(pdev);
if (err)
goto destroy_hwc;
err = mana_gd_query_max_resources(pdev);
if (err)
goto destroy_hwc;
@ -2028,9 +2196,15 @@ static int mana_gd_setup(struct pci_dev *pdev)
goto destroy_hwc;
}
err = mana_gd_detect_devices(pdev);
if (err)
goto destroy_hwc;
if (!gc->msi_sharing) {
gc->msi_bitmap = bitmap_zalloc(gc->num_msix_usable, GFP_KERNEL);
if (!gc->msi_bitmap) {
err = -ENOMEM;
goto destroy_hwc;
}
/* Set bit for HWC */
set_bit(0, gc->msi_bitmap);
}
dev_dbg(&pdev->dev, "mana gdma setup successful\n");
return 0;
@ -2108,6 +2282,7 @@ static int mana_gd_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
goto release_region;
mutex_init(&gc->eq_test_event_mutex);
mutex_init(&gc->gic_mutex);
pci_set_drvdata(pdev, gc);
gc->bar0_pa = pci_resource_start(pdev, 0);
gc->bar0_size = pci_resource_len(pdev, 0);

View File

@ -309,11 +309,18 @@ static void mana_per_port_queue_reset_work_handler(struct work_struct *work)
rtnl_lock();
/* Block RDMA from grabbing the vport during the detach/attach
* window, same as mana_set_channels().
*/
mutex_lock(&apc->vport_mutex);
apc->channel_changing = true;
mutex_unlock(&apc->vport_mutex);
/* Pre-allocate buffers to prevent failure in mana_attach later */
err = mana_pre_alloc_rxbufs(apc, ndev->mtu, apc->num_queues);
if (err) {
netdev_err(ndev, "Insufficient memory for reset post tx stall detection\n");
goto out;
goto clear_flag;
}
err = mana_detach(ndev, false);
@ -328,7 +335,11 @@ static void mana_per_port_queue_reset_work_handler(struct work_struct *work)
dealloc_pre_rxbufs:
mana_pre_dealloc_rxbufs(apc);
out:
clear_flag:
mutex_lock(&apc->vport_mutex);
apc->channel_changing = false;
mutex_unlock(&apc->vport_mutex);
rtnl_unlock();
}
@ -1007,10 +1018,9 @@ static int mana_init_port_context(struct mana_port_context *apc)
return !apc->rxqs ? -ENOMEM : 0;
}
static int mana_send_request(struct mana_context *ac, void *in_buf,
u32 in_len, void *out_buf, u32 out_len)
static int gdma_mana_send_request(struct gdma_context *gc, void *in_buf,
u32 in_len, void *out_buf, u32 out_len)
{
struct gdma_context *gc = ac->gdma_dev->gdma_context;
struct gdma_resp_hdr *resp = out_buf;
struct gdma_req_hdr *req = in_buf;
struct device *dev = gc->dev;
@ -1044,6 +1054,14 @@ static int mana_send_request(struct mana_context *ac, void *in_buf,
return 0;
}
static int mana_send_request(struct mana_context *ac, void *in_buf,
u32 in_len, void *out_buf, u32 out_len)
{
struct gdma_context *gc = ac->gdma_dev->gdma_context;
return gdma_mana_send_request(gc, in_buf, in_len, out_buf, out_len);
}
static int mana_verify_resp_hdr(const struct gdma_resp_hdr *resp_hdr,
const enum mana_command_code expected_code,
const u32 min_size)
@ -1177,11 +1195,10 @@ static void mana_pf_deregister_filter(struct mana_port_context *apc)
err, resp.hdr.status);
}
static int mana_query_device_cfg(struct mana_context *ac, u32 proto_major_ver,
u32 proto_minor_ver, u32 proto_micro_ver,
u16 *max_num_vports, u8 *bm_hostmode)
int mana_gd_query_device_cfg(struct gdma_context *gc, u32 proto_major_ver,
u32 proto_minor_ver, u32 proto_micro_ver,
u16 *max_num_vports, u8 *bm_hostmode)
{
struct gdma_context *gc = ac->gdma_dev->gdma_context;
struct mana_query_device_cfg_resp resp = {};
struct mana_query_device_cfg_req req = {};
struct device *dev = gc->dev;
@ -1196,7 +1213,8 @@ static int mana_query_device_cfg(struct mana_context *ac, u32 proto_major_ver,
req.proto_minor_ver = proto_minor_ver;
req.proto_micro_ver = proto_micro_ver;
err = mana_send_request(ac, &req, sizeof(req), &resp, sizeof(resp));
err = gdma_mana_send_request(gc, &req, sizeof(req),
&resp, sizeof(resp));
if (err) {
dev_err(dev, "Failed to query config: %d", err);
return err;
@ -1230,8 +1248,6 @@ static int mana_query_device_cfg(struct mana_context *ac, u32 proto_major_ver,
else
*bm_hostmode = 0;
debugfs_create_u16("adapter-MTU", 0400, gc->mana_pci_debugfs, &gc->adapter_mtu);
return 0;
}
@ -1298,7 +1314,7 @@ void mana_uncfg_vport(struct mana_port_context *apc)
EXPORT_SYMBOL_NS(mana_uncfg_vport, "NET_MANA");
int mana_cfg_vport(struct mana_port_context *apc, u32 protection_dom_id,
u32 doorbell_pg_id)
u32 doorbell_pg_id, bool check_channel_changing)
{
struct mana_config_vport_resp resp = {};
struct mana_config_vport_req req = {};
@ -1323,7 +1339,8 @@ int mana_cfg_vport(struct mana_port_context *apc, u32 protection_dom_id,
* Ethernet usage on the same port.
*/
mutex_lock(&apc->vport_mutex);
if (apc->vport_use_count > 0) {
if (apc->vport_use_count > 0 ||
(check_channel_changing && apc->channel_changing)) {
mutex_unlock(&apc->vport_mutex);
return -EBUSY;
}
@ -1623,78 +1640,100 @@ void mana_destroy_wq_obj(struct mana_port_context *apc, u32 wq_type,
}
EXPORT_SYMBOL_NS(mana_destroy_wq_obj, "NET_MANA");
static void mana_destroy_eq(struct mana_context *ac)
void mana_destroy_eq(struct mana_port_context *apc)
{
struct mana_context *ac = apc->ac;
struct gdma_context *gc = ac->gdma_dev->gdma_context;
struct gdma_queue *eq;
unsigned int msi;
int i;
if (!ac->eqs)
if (!apc->eqs)
return;
debugfs_remove_recursive(ac->mana_eqs_debugfs);
ac->mana_eqs_debugfs = NULL;
debugfs_remove_recursive(apc->mana_eqs_debugfs);
apc->mana_eqs_debugfs = NULL;
for (i = 0; i < gc->max_num_queues; i++) {
eq = ac->eqs[i].eq;
for (i = 0; i < apc->num_queues; i++) {
eq = apc->eqs[i].eq;
if (!eq)
continue;
msi = eq->eq.msix_index;
mana_gd_destroy_queue(gc, eq);
mana_gd_put_gic(gc, !gc->msi_sharing, msi);
}
kfree(ac->eqs);
ac->eqs = NULL;
kfree(apc->eqs);
apc->eqs = NULL;
}
EXPORT_SYMBOL_NS(mana_destroy_eq, "NET_MANA");
static void mana_create_eq_debugfs(struct mana_context *ac, int i)
static void mana_create_eq_debugfs(struct mana_port_context *apc, int i)
{
struct mana_eq eq = ac->eqs[i];
struct mana_eq eq = apc->eqs[i];
char eqnum[32];
sprintf(eqnum, "eq%d", i);
eq.mana_eq_debugfs = debugfs_create_dir(eqnum, ac->mana_eqs_debugfs);
eq.mana_eq_debugfs = debugfs_create_dir(eqnum, apc->mana_eqs_debugfs);
debugfs_create_u32("head", 0400, eq.mana_eq_debugfs, &eq.eq->head);
debugfs_create_u32("tail", 0400, eq.mana_eq_debugfs, &eq.eq->tail);
debugfs_create_u32("irq", 0400, eq.mana_eq_debugfs, &eq.eq->eq.irq);
debugfs_create_file("eq_dump", 0400, eq.mana_eq_debugfs, eq.eq, &mana_dbg_q_fops);
}
static int mana_create_eq(struct mana_context *ac)
int mana_create_eq(struct mana_port_context *apc)
{
struct gdma_dev *gd = ac->gdma_dev;
struct gdma_dev *gd = apc->ac->gdma_dev;
struct gdma_context *gc = gd->gdma_context;
struct gdma_queue_spec spec = {};
struct gdma_irq_context *gic;
int err;
int msi;
int i;
ac->eqs = kzalloc_objs(struct mana_eq, gc->max_num_queues);
if (!ac->eqs)
if (WARN_ON(apc->eqs))
return -EEXIST;
apc->eqs = kzalloc_objs(struct mana_eq, apc->num_queues);
if (!apc->eqs)
return -ENOMEM;
spec.type = GDMA_EQ;
spec.monitor_avl_buf = false;
spec.queue_size = EQ_SIZE;
spec.eq.callback = NULL;
spec.eq.context = ac->eqs;
spec.eq.context = apc->eqs;
spec.eq.log2_throttle_limit = LOG2_EQ_THROTTLE;
ac->mana_eqs_debugfs = debugfs_create_dir("EQs", gc->mana_pci_debugfs);
apc->mana_eqs_debugfs =
debugfs_create_dir("EQs", apc->mana_port_debugfs);
for (i = 0; i < gc->max_num_queues; i++) {
spec.eq.msix_index = (i + 1) % gc->num_msix_usable;
err = mana_gd_create_mana_eq(gd, &spec, &ac->eqs[i].eq);
if (err) {
dev_err(gc->dev, "Failed to create EQ %d : %d\n", i, err);
for (i = 0; i < apc->num_queues; i++) {
msi = (i + 1) % gc->num_msix_usable;
gic = mana_gd_get_gic(gc, !gc->msi_sharing, &msi);
if (IS_ERR(gic)) {
err = PTR_ERR(gic);
goto out;
}
mana_create_eq_debugfs(ac, i);
spec.eq.msix_index = msi;
err = mana_gd_create_mana_eq(gd, &spec, &apc->eqs[i].eq);
if (err) {
dev_err(gc->dev, "Failed to create EQ %d : %d\n", i, err);
mana_gd_put_gic(gc, !gc->msi_sharing, msi);
goto out;
}
apc->eqs[i].eq->eq.irq = gic->irq;
mana_create_eq_debugfs(apc, i);
}
return 0;
out:
mana_destroy_eq(ac);
mana_destroy_eq(apc);
return err;
}
EXPORT_SYMBOL_NS(mana_create_eq, "NET_MANA");
static int mana_fence_rq(struct mana_port_context *apc, struct mana_rxq *rxq)
{
@ -2462,7 +2501,7 @@ static int mana_create_txq(struct mana_port_context *apc,
spec.monitor_avl_buf = false;
spec.queue_size = cq_size;
spec.cq.callback = mana_schedule_napi;
spec.cq.parent_eq = ac->eqs[i].eq;
spec.cq.parent_eq = apc->eqs[i].eq;
spec.cq.context = cq;
err = mana_gd_create_mana_wq_cq(gd, &spec, &cq->gdma_cq);
if (err)
@ -2855,13 +2894,12 @@ static void mana_create_rxq_debugfs(struct mana_port_context *apc, int idx)
static int mana_add_rx_queues(struct mana_port_context *apc,
struct net_device *ndev)
{
struct mana_context *ac = apc->ac;
struct mana_rxq *rxq;
int err = 0;
int i;
for (i = 0; i < apc->num_queues; i++) {
rxq = mana_create_rxq(apc, i, &ac->eqs[i], ndev);
rxq = mana_create_rxq(apc, i, &apc->eqs[i], ndev);
if (!rxq) {
err = -ENOMEM;
netdev_err(ndev, "Failed to create rxq %d : %d\n", i, err);
@ -2880,9 +2918,8 @@ static int mana_add_rx_queues(struct mana_port_context *apc,
return err;
}
static void mana_destroy_vport(struct mana_port_context *apc)
static void mana_destroy_rxqs(struct mana_port_context *apc)
{
struct gdma_dev *gd = apc->ac->gdma_dev;
struct mana_rxq *rxq;
u32 rxq_idx;
@ -2897,8 +2934,12 @@ static void mana_destroy_vport(struct mana_port_context *apc)
apc->rxqs[rxq_idx] = NULL;
}
}
}
static void mana_destroy_vport(struct mana_port_context *apc)
{
struct gdma_dev *gd = apc->ac->gdma_dev;
mana_destroy_txq(apc);
mana_uncfg_vport(apc);
if (gd->gdma_context->is_pf && !apc->ac->bm_hostmode)
@ -2919,11 +2960,14 @@ static int mana_create_vport(struct mana_port_context *apc,
return err;
}
err = mana_cfg_vport(apc, gd->pdid, gd->doorbell);
if (err)
err = mana_cfg_vport(apc, gd->pdid, gd->doorbell, false);
if (err) {
if (gd->gdma_context->is_pf && !apc->ac->bm_hostmode)
mana_pf_deregister_hw_vport(apc);
return err;
}
return mana_create_txq(apc, net);
return 0;
}
static int mana_rss_table_alloc(struct mana_port_context *apc)
@ -3185,6 +3229,8 @@ static int mana_init_port(struct net_device *ndev)
max_queues = min_t(u32, max_txq, max_rxq);
if (apc->max_queues > max_queues)
apc->max_queues = max_queues;
if (apc->max_queues > gc->max_num_queues_vport)
apc->max_queues = gc->max_num_queues_vport;
if (apc->num_queues > apc->max_queues)
apc->num_queues = apc->max_queues;
@ -3226,21 +3272,36 @@ int mana_alloc_queues(struct net_device *ndev)
err = mana_create_vport(apc, ndev);
if (err) {
netdev_err(ndev, "Failed to create vPort %u : %d\n", apc->port_idx, err);
netdev_err(ndev, "Failed to create vPort %u : %d\n",
apc->port_idx, err);
return err;
}
err = mana_create_eq(apc);
if (err) {
netdev_err(ndev, "Failed to create EQ on vPort %u: %d\n",
apc->port_idx, err);
goto destroy_vport;
}
err = mana_create_txq(apc, ndev);
if (err) {
netdev_err(ndev, "Failed to create TXQ on vPort %u: %d\n",
apc->port_idx, err);
goto destroy_eq;
}
err = netif_set_real_num_tx_queues(ndev, apc->num_queues);
if (err) {
netdev_err(ndev,
"netif_set_real_num_tx_queues () failed for ndev with num_queues %u : %d\n",
apc->num_queues, err);
goto destroy_vport;
goto destroy_txq;
}
err = mana_add_rx_queues(apc, ndev);
if (err)
goto destroy_vport;
goto destroy_rxq;
apc->rss_state = apc->num_queues > 1 ? TRI_STATE_TRUE : TRI_STATE_FALSE;
@ -3249,7 +3310,7 @@ int mana_alloc_queues(struct net_device *ndev)
netdev_err(ndev,
"netif_set_real_num_rx_queues () failed for ndev with num_queues %u : %d\n",
apc->num_queues, err);
goto destroy_vport;
goto destroy_rxq;
}
mana_rss_table_init(apc);
@ -3257,19 +3318,25 @@ int mana_alloc_queues(struct net_device *ndev)
err = mana_config_rss(apc, TRI_STATE_TRUE, true, true);
if (err) {
netdev_err(ndev, "Failed to configure RSS table: %d\n", err);
goto destroy_vport;
goto destroy_rxq;
}
if (gd->gdma_context->is_pf && !apc->ac->bm_hostmode) {
err = mana_pf_register_filter(apc);
if (err)
goto destroy_vport;
goto destroy_rxq;
}
mana_chn_setxdp(apc, mana_xdp_get(apc));
return 0;
destroy_rxq:
mana_destroy_rxqs(apc);
destroy_txq:
mana_destroy_txq(apc);
destroy_eq:
mana_destroy_eq(apc);
destroy_vport:
mana_destroy_vport(apc);
return err;
@ -3380,6 +3447,9 @@ static int mana_dealloc_queues(struct net_device *ndev)
mana_fence_rqs(apc);
/* Even in err case, still need to cleanup the vPort */
mana_destroy_rxqs(apc);
mana_destroy_txq(apc);
mana_destroy_eq(apc);
mana_destroy_vport(apc);
return 0;
@ -3431,7 +3501,7 @@ static int mana_probe_port(struct mana_context *ac, int port_idx,
int err;
ndev = alloc_etherdev_mq(sizeof(struct mana_port_context),
gc->max_num_queues);
gc->max_num_queues_vport);
if (!ndev)
return -ENOMEM;
@ -3440,9 +3510,9 @@ static int mana_probe_port(struct mana_context *ac, int port_idx,
apc = netdev_priv(ndev);
apc->ac = ac;
apc->ndev = ndev;
apc->max_queues = gc->max_num_queues;
apc->max_queues = gc->max_num_queues_vport;
/* Use MANA_DEF_NUM_QUEUES as default, still honoring the HW limit */
apc->num_queues = min(gc->max_num_queues, MANA_DEF_NUM_QUEUES);
apc->num_queues = min(gc->max_num_queues_vport, MANA_DEF_NUM_QUEUES);
apc->tx_queue_size = DEF_TX_BUFFERS_PER_QUEUE;
apc->rx_queue_size = DEF_RX_BUFFERS_PER_QUEUE;
apc->port_handle = INVALID_MANA_HANDLE;
@ -3706,19 +3776,18 @@ int mana_probe(struct gdma_dev *gd, bool resuming)
INIT_DELAYED_WORK(&ac->gf_stats_work, mana_gf_stats_work_handler);
err = mana_create_eq(ac);
if (err) {
dev_err(dev, "Failed to create EQs: %d\n", err);
goto out;
}
err = mana_query_device_cfg(ac, MANA_MAJOR_VERSION, MANA_MINOR_VERSION,
MANA_MICRO_VERSION, &num_ports, &bm_hostmode);
err = mana_gd_query_device_cfg(gc, MANA_MAJOR_VERSION,
MANA_MINOR_VERSION,
MANA_MICRO_VERSION,
&num_ports, &bm_hostmode);
if (err)
goto out;
ac->bm_hostmode = bm_hostmode;
debugfs_create_u16("adapter-MTU", 0400,
gc->mana_pci_debugfs, &gc->adapter_mtu);
if (!resuming) {
ac->num_ports = num_ports;
} else {
@ -3732,9 +3801,6 @@ int mana_probe(struct gdma_dev *gd, bool resuming)
enable_work(&ac->link_change_work);
}
if (ac->num_ports == 0)
dev_err(dev, "Failed to detect any vPort\n");
if (ac->num_ports > MAX_PORTS_IN_MANA_DEV)
ac->num_ports = MAX_PORTS_IN_MANA_DEV;
@ -3856,8 +3922,6 @@ void mana_remove(struct gdma_dev *gd, bool suspending)
free_netdev(ndev);
}
mana_destroy_eq(ac);
if (ac->per_port_queue_reset_wq) {
destroy_workqueue(ac->per_port_queue_reset_wq);
ac->per_port_queue_reset_wq = NULL;

View File

@ -454,6 +454,11 @@ static int mana_set_coalesce(struct net_device *ndev,
return err;
}
/* mana_set_channels - change the number of queues on a port
*
* Returns -EBUSY if RDMA holds the vport with EQs sized to the
* current num_queues.
*/
static int mana_set_channels(struct net_device *ndev,
struct ethtool_channels *channels)
{
@ -462,10 +467,22 @@ static int mana_set_channels(struct net_device *ndev,
unsigned int old_count = apc->num_queues;
int err;
/* Set channel_changing to block RDMA from grabbing the vport
* during the detach/attach window. mana_cfg_vport() checks
* this flag under vport_mutex and returns -EBUSY if set.
*/
mutex_lock(&apc->vport_mutex);
if (!apc->port_is_up && apc->vport_use_count) {
mutex_unlock(&apc->vport_mutex);
return -EBUSY;
}
apc->channel_changing = true;
mutex_unlock(&apc->vport_mutex);
err = mana_pre_alloc_rxbufs(apc, ndev->mtu, new_count);
if (err) {
netdev_err(ndev, "Insufficient memory for new allocations");
return err;
goto clear_flag;
}
err = mana_detach(ndev, false);
@ -483,6 +500,10 @@ static int mana_set_channels(struct net_device *ndev,
out:
mana_pre_dealloc_rxbufs(apc);
clear_flag:
mutex_lock(&apc->vport_mutex);
apc->channel_changing = false;
mutex_unlock(&apc->vport_mutex);
return err;
}

View File

@ -342,6 +342,7 @@ struct gdma_queue {
void *context;
unsigned int msix_index;
unsigned int irq;
u32 log2_throttle_limit;
} eq;
@ -388,6 +389,11 @@ struct gdma_irq_context {
spinlock_t lock;
struct list_head eq_list;
char name[MANA_IRQ_NAME_SZ];
unsigned int msi;
unsigned int irq;
refcount_t refcount;
unsigned int bitmap_refs;
bool dyn_msix;
};
enum gdma_context_flags {
@ -399,8 +405,10 @@ struct gdma_context {
struct device *dev;
struct dentry *mana_pci_debugfs;
/* Per-vPort max number of queues */
/* Hardware max number of queues */
unsigned int max_num_queues;
/* Per-vPort max number of queues */
unsigned int max_num_queues_vport;
unsigned int max_num_msix;
unsigned int num_msix_usable;
struct xarray irq_contexts;
@ -447,6 +455,15 @@ struct gdma_context {
struct workqueue_struct *service_wq;
unsigned long flags;
/* Protect access to GIC context */
struct mutex gic_mutex;
/* Indicate if this device is sharing MSI for EQs on MANA */
bool msi_sharing;
/* Bitmap tracks where MSI is allocated when it is not shared for EQs */
unsigned long *msi_bitmap;
};
static inline bool mana_gd_is_mana(struct gdma_dev *gd)
@ -599,6 +616,7 @@ enum {
#define GDMA_DRV_CAP_FLAG_1_HWC_TIMEOUT_RECONFIG BIT(3)
#define GDMA_DRV_CAP_FLAG_1_GDMA_PAGES_4MB_1GB_2GB BIT(4)
#define GDMA_DRV_CAP_FLAG_1_VARIABLE_INDIRECTION_TABLE_SUPPORT BIT(5)
#define GDMA_DRV_CAP_FLAG_1_HW_VPORT_LINK_AWARE BIT(6)
/* Driver can handle holes (zeros) in the device list */
#define GDMA_DRV_CAP_FLAG_1_DEV_LIST_HOLES_SUP BIT(11)
@ -615,7 +633,8 @@ enum {
/* Driver detects stalled send queues and recovers them */
#define GDMA_DRV_CAP_FLAG_1_HANDLE_STALL_SQ_RECOVERY BIT(18)
#define GDMA_DRV_CAP_FLAG_1_HW_VPORT_LINK_AWARE BIT(6)
/* Driver supports separate EQ/MSIs for each vPort */
#define GDMA_DRV_CAP_FLAG_1_EQ_MSI_UNSHARE_MULTI_VPORT BIT(19)
/* Driver supports linearizing the skb when num_sge exceeds hardware limit */
#define GDMA_DRV_CAP_FLAG_1_SKB_LINEARIZE BIT(20)
@ -643,7 +662,8 @@ enum {
GDMA_DRV_CAP_FLAG_1_SKB_LINEARIZE | \
GDMA_DRV_CAP_FLAG_1_PROBE_RECOVERY | \
GDMA_DRV_CAP_FLAG_1_HANDLE_STALL_SQ_RECOVERY | \
GDMA_DRV_CAP_FLAG_1_HWC_TIMEOUT_RECOVERY)
GDMA_DRV_CAP_FLAG_1_HWC_TIMEOUT_RECOVERY | \
GDMA_DRV_CAP_FLAG_1_EQ_MSI_UNSHARE_MULTI_VPORT)
#define GDMA_DRV_CAP_FLAGS2 0
@ -1019,4 +1039,11 @@ int mana_gd_resume(struct pci_dev *pdev);
bool mana_need_log(struct gdma_context *gc, int err);
struct gdma_irq_context *mana_gd_get_gic(struct gdma_context *gc,
bool use_msi_bitmap,
int *msi_requested);
void mana_gd_put_gic(struct gdma_context *gc, bool use_msi_bitmap, int msi);
int mana_gd_query_device_cfg(struct gdma_context *gc, u32 proto_major_ver,
u32 proto_minor_ver, u32 proto_micro_ver,
u16 *max_num_vports, u8 *bm_hostmode);
#endif /* _GDMA_H */

View File

@ -480,8 +480,6 @@ struct mana_context {
u8 bm_hostmode;
struct mana_ethtool_hc_stats hc_stats;
struct mana_eq *eqs;
struct dentry *mana_eqs_debugfs;
struct workqueue_struct *per_port_queue_reset_wq;
/* Workqueue for querying hardware stats */
struct delayed_work gf_stats_work;
@ -501,6 +499,9 @@ struct mana_port_context {
u8 mac_addr[ETH_ALEN];
struct mana_eq *eqs;
struct dentry *mana_eqs_debugfs;
enum TRI_STATE rss_state;
mana_handle_t default_rxobj;
@ -547,6 +548,12 @@ struct mana_port_context {
struct mutex vport_mutex;
int vport_use_count;
/* Set by mana_set_channels() under vport_mutex to block RDMA
* from grabbing the vport during the detach/attach window.
* Checked by mana_cfg_vport() when called from the RDMA path.
*/
bool channel_changing;
/* Net shaper handle*/
struct net_shaper_handle handle;
@ -1040,8 +1047,10 @@ void mana_destroy_wq_obj(struct mana_port_context *apc, u32 wq_type,
mana_handle_t wq_obj);
int mana_cfg_vport(struct mana_port_context *apc, u32 protection_dom_id,
u32 doorbell_pg_id);
u32 doorbell_pg_id, bool check_channel_changing);
void mana_uncfg_vport(struct mana_port_context *apc);
int mana_create_eq(struct mana_port_context *apc);
void mana_destroy_eq(struct mana_port_context *apc);
struct net_device *mana_get_primary_netdev(struct mana_context *ac,
u32 port_index,