net: mana: Query device capabilities and configure MSI-X sharing for EQs

When querying the device, adjust the max number of queues to allow
dedicated MSI-X vectors for each vPort. The per-vPort queue count is
clamped towards MANA_DEF_NUM_QUEUES but will not exceed the hardware
maximum reported by the device.

MSI-X sharing among vPorts is enabled when there are not enough MSI-X
vectors for dedicated allocation, or when the platform does not support
dynamic MSI-X allocation (in which case all vectors are pre-allocated
at probe time and sharing is always used). The msi_sharing flag is
reset at the top of mana_gd_query_max_resources() so it is recomputed
from current hardware state on each probe or resume cycle.

Clamp apc->max_queues to gc->max_num_queues_vport in mana_init_port()
so that on resume, if max_num_queues_vport has decreased due to fewer
MSI-X vectors, num_queues is reduced accordingly before EQ allocation.

A device reporting zero ports now results in a fatal probe error since
the per-vPort MSI-X math requires at least one port.

Rename mana_query_device_cfg() to mana_gd_query_device_cfg() as it is
used at GDMA device probe time for querying device capabilities.

Signed-off-by: Long Li <longli@microsoft.com>
Link: https://patch.msgid.link/20260605005717.2059954-3-longli@microsoft.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Long Li 2026-06-04 17:57:11 -07:00 committed by Jakub Kicinski
parent fa1a3b7bcd
commit d7c253d614
3 changed files with 114 additions and 22 deletions

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;
}
@ -1901,6 +1960,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);
@ -1979,6 +2039,8 @@ static void mana_gd_remove_irqs(struct pci_dev *pdev)
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 +2080,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 +2094,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;

View File

@ -1018,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;
@ -1055,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)
@ -1188,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;
@ -1207,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;
@ -1241,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;
}
@ -3208,6 +3213,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;
@ -3478,7 +3485,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;
@ -3487,9 +3494,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;
@ -3753,13 +3760,18 @@ int mana_probe(struct gdma_dev *gd, bool resuming)
INIT_DELAYED_WORK(&ac->gf_stats_work, mana_gf_stats_work_handler);
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 {
@ -3773,9 +3785,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;

View File

@ -399,8 +399,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 +449,12 @@ struct gdma_context {
struct workqueue_struct *service_wq;
unsigned long flags;
/* 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)
@ -1019,4 +1027,7 @@ int mana_gd_resume(struct pci_dev *pdev);
bool mana_need_log(struct gdma_context *gc, int err);
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 */