RE: [EXTERNAL] Re: [PATCH net-next v6 2/6] net: mana: Query device capabilities and configure MSI-X sharing for EQs

Long Li
2026-05-04T22:30:21Z

> From: 'Simon Horman' <horms@kernel.org>
>
> This is an AI-generated review of your patch. The human sending this email has
> considered the AI review valid, or at least plausible.
> Full review at:
> https://nam06.safelinks.protection.outlook.com/?url=https%3A%2F%2Fnetdev-
> ai.bots.linux.dev%2Fsashiko%2F&data=05%7C02%7Clongli%40microsoft.com%7
> Ca5d48bdebd0240e5d91308dea85ff3a8%7C72f988bf86f141af91ab2d7cd011db4
> 7%7C1%7C0%7C639133327234427106%7CUnknown%7CTWFpbGZsb3d8eyJFbX
> B0eU1hcGkiOnRydWUsIlYiOiIwLjAuMDAwMCIsIlAiOiJXaW4zMiIsIkFOIjoiTWFpbC
> IsIldUIjoyfQ%3D%3D%7C0%7C%7C%7C&sdata=4doWStoTQ4w7CbmhN3I294b%
> 2B9c3NNjDGySSmISHGZk4%3D&reserved=0
>
> I apologise for any overlap with this and the review of this patchset available at
> sashiko.dev. They both seem relevant and I'm unsure how to cleanly pass them
> both on.
>
> Both Sashiko instances also flagged concerns in the remaining patches of this
> patchset. But I am not forwarding them as they seem to be lower priority, and I
> think I've already created enough noise here.
>
> ---
> net: mana: Query device capabilities and configure MSI-X sharing for EQs
>
> This patch queries device capabilities early in GDMA probe and chooses between
> dedicated MSI-X per vPort or a shared-MSI-X mode based on the available vector
> count, introducing gc->max_num_queues_vport,
> gc->msi_sharing and gc->msi_bitmap, and renaming mana_query_device_cfg()
> to mana_gd_query_device_cfg() so it can be called from the GDMA layer.
>
> > diff --git a/drivers/net/ethernet/microsoft/mana/gdma_main.c
> > b/drivers/net/ethernet/microsoft/mana/gdma_main.c
> > index 098fbda0d128..b96859e0aec9 100644
> > --- a/drivers/net/ethernet/microsoft/mana/gdma_main.c
> > +++ b/drivers/net/ethernet/microsoft/mana/gdma_main.c
> > @@ -149,6 +149,9 @@ 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;
> >
> > mana_gd_init_req_hdr(&req.hdr, GDMA_QUERY_MAX_RESOURCES,
> @@ -194,6
> > +197,40 @@ static int mana_gd_query_max_resources(struct pci_dev *pdev)
> > if (gc->max_num_queues > gc->num_msix_usable - 1)
> > gc->max_num_queues = gc->num_msix_usable - 1;
> >
> > + err = mana_gd_query_device_cfg(gc, MANA_MAJOR_VERSION,
> MANA_MINOR_VERSION,
> > + MANA_MICRO_VERSION, &num_ports,
> &bm_hostmode);
> > + if (err)
> > + return err;
>
> Is this the same MANA_QUERY_DEV_CONFIG that mana_probe() issues shortly
> after? mana_probe() still contains:
>
> err = mana_gd_query_device_cfg(gc, MANA_MAJOR_VERSION,
> MANA_MINOR_VERSION,
> MANA_MICRO_VERSION, &num_ports,
> &bm_hostmode);
>
> so the same HWC round-trip runs twice on every probe with identical arguments,
> both populating gc->adapter_mtu.
>
> The commit message frames this rename as consolidation ("used at GDMA device
> probe time"), but the mana_probe() call-site is not removed. Was one of the two
> call-sites meant to go away?
>
> The relocation of debugfs_create_u16("adapter-MTU", ...) from
> mana_gd_query_device_cfg() into mana_probe() only makes sense if the function
> is now invoked more than once, which hints at the same duplication.

The two calls are intentional and serve different layers. The call in mana_gd_query_max_resources() runs at the GDMA layer during device setup - it needs num_ports to compute per-vPort MSI-X
allocation before any MANA-level structures exist. The call in mana_probe() runs at the MANA layer and consumes the results differently: it populates ac->num_ports, ac->bm_hostmode, and on
resume validates that the port count hasn't changed.

The HWC round-trip is lightweight (a single command/response exchange), and the function is renamed to mana_gd_ precisely because it now has callers at both layers. The debugfs move is the same
reason - the debugfs node should only be created once, so it moves to mana_probe() which runs once, rather than staying in the function that is now called from both sites.

>
> > +
> > + if (!num_ports)
> > + return -EINVAL;
> > +
> > + /*
> > + * Adjust gc->max_num_queues returned from the SOC to allow
> dedicated
> > + * MSIx for each vPort. Clamp to no less than
> MANA_DEF_NUM_QUEUES.
> > + */
> > + max_num_queues = (gc->num_msix_usable - 1) / num_ports;
> > + max_num_queues = roundup_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;
>
> Is gc->msi_sharing ever reset to false? The only two writers are this line and
> mana_gd_setup_hwc_irqs() (the !pci_msix_can_alloc_dyn branch), and both only
> set it to true. mana_gd_remove_irqs() frees msi_bitmap and zeros
> max_num_msix / num_msix_usable, but does not clear msi_sharing, and the
> gdma_context survives mana_gd_suspend() / mana_gd_resume().
>
> Once true is latched in any setup cycle, mana_gd_setup() will always take the
> sharing branch on resume even if the recomputed max_num_queues * num_ports
> fits within num_msix_usable - 1. Is that consistent with the commit message
> saying sharing "is only enabled when there are not enough MSI-X vectors for
> dedicated allocation"?

msi_sharing is one-directional by design, but this is not a problem in practice. The inputs to the sharing computation - num_msix_usable, num_ports, and hardware queue limits - are properties of
the hardware and do not change across suspend/resume cycles. The gdma_context is kzalloc'd at probe time (so msi_sharing starts as false), and on any given hardware configuration the computation
in mana_gd_query_max_resources() will always produce the same result.

On resume, mana_gd_setup_hwc_irqs() re-evaluates dynamic MSI-X support and mana_gd_query_max_resources() recomputes the sharing check from the same hardware parameters. Since the hardware
doesn't change, the flag converges to the same value. Adding an explicit reset would be defensive but not functionally necessary.

>
> > +
> > + /* 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 %d max queues %d\n",
> > + gc->msi_sharing, gc->max_num_queues);
>
> Should this print gc->max_num_queues_vport rather than
> gc->max_num_queues? The block immediately above computes
> gc->max_num_queues_vport and leaves gc->max_num_queues unchanged, and
> mana_probe_port() sizes alloc_etherdev_mq() and apc->max_queues from
> gc->max_num_queues_vport:
>
> ndev = alloc_etherdev_mq(sizeof(struct mana_port_context),
> gc->max_num_queues_vport);
> ...
> apc->max_queues = gc->max_num_queues_vport;
> apc->num_queues = min(gc->max_num_queues_vport,
> MANA_DEF_NUM_QUEUES);
>
> so the logged value does not match the per-vPort queue count the driver actually
> exposes.

The printed value is intentional. gc->max_num_queues is the GDMA device-level maximum, which is consumed by both the Ethernet and RDMA drivers - the RDMA driver uses it to size its queue
resources. Printing max_num_queues_vport here would only reflect the Ethernet per-vPort limit and would be misleading in a device-level log message. The per-vPort value is already visible
through ethtool channel reporting.

>
> > +
> > return 0;
> > }
> >
> > @@ -1856,6 +1893,7 @@ static int mana_gd_setup_hwc_irqs(struct pci_dev
> > *pdev)
>
> [ ... ]
>
> > @@ -1968,20 +2008,30 @@ static int mana_gd_setup(struct pci_dev *pdev)
> > if (err)
> > goto destroy_hwc;
> >
> > - err = mana_gd_query_max_resources(pdev);
> > + err = mana_gd_detect_devices(pdev);
> > if (err)
> > goto destroy_hwc;
> >
> > - err = mana_gd_setup_remaining_irqs(pdev);
> > - if (err) {
> > - dev_err(gc->dev, "Failed to setup remaining IRQs: %d", err);
> > - goto destroy_hwc;
> > - }
> > -
> > - err = mana_gd_detect_devices(pdev);
> > + err = mana_gd_query_max_resources(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);
> > + } else {
> > + err = mana_gd_setup_remaining_irqs(pdev);
> > + if (err) {
> > + dev_err(gc->dev, "Failed to setup remaining IRQs: %d",
> err);
> > + goto destroy_hwc;
> > + }
> > + }
> > +
>
> Can the driver bring up any vPort after this change when the !msi_sharing branch
> is taken?
>
> In the dedicated branch, only gc->msi_bitmap is allocated and bit 0 is set for
> HWC. mana_gd_setup_remaining_irqs() is skipped, so no gdma_irq_context is
> inserted into gc->irq_contexts for indices 1..
> num_msix_usable-1.
>
> Later, mana_create_eq() still assigns
>
> spec.eq.msix_index = (i + 1) % gc->num_msix_usable;
>
> and mana_gd_register_irq() does:
>
> gic = xa_load(&gc->irq_contexts, msi_index);
> if (WARN_ON(!gic))
> return -EINVAL;
>
> On a typical cloud SKU with, for example, num_msix_usable=32,
> num_ports=1 and num_online_cpus=16, the new math keeps msi_sharing=false
> (16 * 1 <= 31), so every EQ-create goes down this path and hits the WARN_ON.
> Doesn't that make every vPort open and every resume fail for the common
> dedicated-MSI-X case?
>
> The msi_bitmap allocated here is not consumed anywhere in this commit; the on-
> demand allocation via mana_gd_get_gic() appears in the later commit "net:
> mana: Allocate interrupt context for each EQ when creating vPort"
> (dbbdf40a8974). Should the bitmap and the new branch be introduced in the
> same commit that actually uses them, so each commit in the series is
> independently bootable?

You're right that the non-sharing EQ creation path is not fully functional until patch 5 wires mana_gd_get_gic() into mana_create_eq(). However, this is a new capability being built
incrementally: patch 2 introduces the decision framework and bitmap, patch 3 adds the GIC infrastructure, patch 4 converts global IRQ setup to use it, and patch 5 integrates it into per-vPort EQ
creation.

The intermediate state between patches 2 and 5 results in a clean error (-EINVAL from mana_gd_register_irq) - not a crash or data corruption. The dedicated MSI-X mode is a new feature that did
not exist before this series, so there is no regression from the pre-patch baseline. Restructuring to make it functional at each intermediate commit would require squashing the GIC
infrastructure into this patch, producing a significantly larger and harder-to-review change. I'd prefer to keep the logical separation as-is.