* [PATCH net-next v4 00/13] net: mana: reconfigure by replacing the queue set
@ 2026-09-08 3:28 Long Li
2026-09-08 3:28 ` [PATCH net-next v4 01/13] net: mana: add queue-set allocation and teardown helpers Long Li
` (12 more replies)
0 siblings, 13 replies; 28+ messages in thread
From: Long Li @ 2026-09-08 3:28 UTC (permalink / raw)
To: Long Li, Long Li, Konstantin Taranov, Jakub Kicinski,
David S . Miller, Paolo Abeni, Eric Dumazet, Andrew Lunn,
Jason Gunthorpe, Leon Romanovsky, Haiyang Zhang,
K . Y . Srinivasan, Wei Liu, Dexuan Cui, shradhagupta,
Simon Horman, ernis, stephen, shirazsaleem
Cc: netdev, linux-rdma, linux-hyperv, linux-kernel
Replace detach/attach reconfiguration with pre-allocation and queue-set
replacement. Allocation failure leaves the running configuration intact.
Publication failure attempts rollback; if rollback also fails, the port
is stopped and requires administrative recovery.
EQs and statistics belong to the port, and valid user RSS tables survive
queue rebuilds. Channel-count changes reuse surviving queues: reductions
retire only the tail, and increases allocate only the added queues. Ring,
MTU, private-flag and XDP changes that rebuild queues still require both
sets temporarily and can fail at hardware resource limits.
Patch layout:
1-2: Queue-set helpers and the shared EQ pool.
3-7: Convert channel, ring, private-flag, MTU and XDP reconfiguration.
8: Remove an unreachable early return from mana_detach().
9-11: Preserve statistics, release unused EQs and preserve RSS tables.
12-13: Reuse queues across channel-count reductions and increases.
Overlap with submitted net fixes:
Two independent fixes have been submitted to net:
[1] net: mana: restore the XDP program pointer when pre-allocation fails
https://lore.kernel.org/all/20260904202640.3900685-1-longli@microsoft.com/
[2] net: mana: do not discard an ethtool-configured RSS table on a queue
rebuild
https://lore.kernel.org/all/20260905004401.3937066-1-longli@microsoft.com/
Neither is a prerequisite for this series, and they are not resent here
as standalone patches. Their handling is incorporated into patches 7
and 11, respectively. Merging the trees can conflict in these blocks:
- Patch 7, mana_bpf.c: retain the queue-set version of mana_xdp_set().
It leaves the live program unchanged during allocation, replacing
the pre-allocation failure path repaired by [1].
- Patch 11, mana_en.c: retain the three-argument mana_rss_table_keep()
and its callers, which validate the prospective queue count and
defer swap-path loss notifications until publication succeeds.
ETHTOOL_OP_NEEDS_RTNL_RSS in mana_ethtool.c is identical in [2] and
this series.
These resolutions apply to the overlapping blocks, not unrelated net
changes. The two patch mails also carry the corresponding merge notes.
Changes in v4:
- Retain pcie_flr() instead of pci_try_reset_function(), avoiding the
device-lock conflict in the remove path.
- Delay lowering real_num_rx_queues until retiring RQs are destroyed,
including after rollback.
- Add the full barrier between reopening port_is_up and reading TX
ring state, preventing a missed queue wakeup.
- Report persistent statistics while down or swapping; keep only the
hardware PHY query gated on port_is_up.
- Derive the live port inside mana_free_qset() instead of passing a
redundant port argument.
- Require RTNL for RSS setters. Remove loss notification from the
non-swap allocation path, whose callers do not consistently hold
the required netdev instance lock.
- Wait for TX-selection readers before freeing the old RSS table in
both channel-count paths. Clear TX slots after partial teardown.
Changes in v3:
- Move shared EQ ownership before the first converted caller.
- Separate retiring RX statistics and fold them on teardown or rollback.
- Preserve carrier through successful swaps and rollback, and track
software-forced carrier shutdown separately from hardware link events.
- Correct kernel-doc and EQ/vector lifetime descriptions.
- Rebase onto net-next and move the resize-recovery capability to BIT(31).
Changes in v2:
- Replace post-failure retries with pre-allocated queue-set replacement.
Previous versions:
v3: https://lore.kernel.org/all/20260901014442.2945689-1-longli@microsoft.com/
v2: https://patchwork.kernel.org/project/netdevbpf/list/?series=1145125
v1: https://lore.kernel.org/netdev/20260711041415.3008868-1-dipayanroy@linux.microsoft.com/
Dipayaan Roy (1):
net: mana: do not bail out of mana_detach on dealloc failure
Long Li (12):
net: mana: add queue-set allocation and teardown helpers
net: mana: share the EQ pool across a queue-set swap
net: mana: swap queue sets in mana_set_channels
net: mana: swap queue sets in mana_set_ringparam
net: mana: swap queue sets in mana_set_priv_flags
net: mana: swap queue sets in mana_change_mtu
net: mana: swap queue sets in mana_xdp_set
net: mana: keep per-queue statistics in the port context
net: mana: release EQs left idle by a channel-count reduction
net: mana: keep a user-configured RSS table across a queue rebuild
net: mana: keep the surviving queues when the channel count is reduced
net: mana: keep the existing queues when the channel count is raised
.../net/ethernet/microsoft/mana/mana_bpf.c | 94 +-
drivers/net/ethernet/microsoft/mana/mana_en.c | 1256 +++++++++++++++--
.../ethernet/microsoft/mana/mana_ethtool.c | 312 ++--
include/net/mana/gdma.h | 8 +-
include/net/mana/mana.h | 105 +-
5 files changed, 1524 insertions(+), 251 deletions(-)
base-commit: 1bb784eb6e38fd73143f021608e4ef3095d0c0d7
--
2.43.0
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH net-next v4 01/13] net: mana: add queue-set allocation and teardown helpers
2026-09-08 3:28 [PATCH net-next v4 00/13] net: mana: reconfigure by replacing the queue set Long Li
@ 2026-09-08 3:28 ` Long Li
2026-09-09 3:29 ` sashiko-bot
2026-09-08 3:28 ` [PATCH net-next v4 02/13] net: mana: share the EQ pool across a queue-set swap Long Li
` (11 subsequent siblings)
12 siblings, 1 reply; 28+ messages in thread
From: Long Li @ 2026-09-08 3:28 UTC (permalink / raw)
To: Long Li, Long Li, Konstantin Taranov, Jakub Kicinski,
David S . Miller, Paolo Abeni, Eric Dumazet, Andrew Lunn,
Jason Gunthorpe, Leon Romanovsky, Haiyang Zhang,
K . Y . Srinivasan, Wei Liu, Dexuan Cui, shradhagupta,
Simon Horman, ernis, stephen, shirazsaleem
Cc: netdev, linux-rdma, linux-hyperv, linux-kernel
Add queue-set allocation and teardown helpers using a scratch port
context, without releasing the vport. These prepare the reconfiguration
paths to retain their running queues if replacement allocation fails.
Extract the TX drain, retaining pcie_flr(). On reset failure, leave
pending TX SKBs mapped; this does not prevent subsequent ring and RX
teardown. Track successful drain-initiated resets and request best-effort
sibling rebuilds, without reinitializing the HWC. Clear removed ports
from ac->ports[] before those walks can reach them.
The new queue-set helpers have no callers yet.
Signed-off-by: Long Li <longli@microsoft.com>
---
Changes in v4:
- Retain pcie_flr() and its existing failure log instead of the
trylock-based PCI reset helper.
- Clarify drain/reset limitations and shorten lifecycle comments.
.../net/ethernet/microsoft/mana/mana_bpf.c | 24 ++
drivers/net/ethernet/microsoft/mana/mana_en.c | 338 +++++++++++++++---
include/net/mana/mana.h | 38 ++
3 files changed, 353 insertions(+), 47 deletions(-)
diff --git a/drivers/net/ethernet/microsoft/mana/mana_bpf.c b/drivers/net/ethernet/microsoft/mana/mana_bpf.c
index 53308e139cbe917b074dd381c83546fc74d7b79f..1c5ac7e2d884ed90bb12993a86718e2ebc0937c9 100644
--- a/drivers/net/ethernet/microsoft/mana/mana_bpf.c
+++ b/drivers/net/ethernet/microsoft/mana/mana_bpf.c
@@ -265,3 +265,27 @@ int mana_bpf(struct net_device *ndev, struct netdev_bpf *bpf)
return ret;
}
+
+struct bpf_prog *mana_chn_xdp_peek(struct mana_port_context *apc)
+{
+ ASSERT_RTNL();
+
+ if (!apc->rxqs || !apc->rxqs[0])
+ return NULL;
+
+ return rtnl_dereference(apc->rxqs[0]->bpf_prog);
+}
+
+/* Keep the per-queue program pointers until RX polling stops. */
+void mana_chn_xdp_release(struct bpf_prog *prog, unsigned int num_queues)
+{
+ unsigned int i;
+
+ ASSERT_RTNL();
+
+ if (!prog)
+ return;
+
+ for (i = 0; i < num_queues; i++)
+ bpf_prog_put(prog);
+}
diff --git a/drivers/net/ethernet/microsoft/mana/mana_en.c b/drivers/net/ethernet/microsoft/mana/mana_en.c
index 7a1ac853e3abcd28c4a1e5c6987ec631a18ad840..5ac3ae9fd7ea2f786dddbd21d340884c008e1020 100644
--- a/drivers/net/ethernet/microsoft/mana/mana_en.c
+++ b/drivers/net/ethernet/microsoft/mana/mana_en.c
@@ -2018,7 +2018,8 @@ static void mana_poll_tx_cq(struct mana_cq *cq)
/* Ensure checking txq_stopped before apc->port_is_up. */
smp_rmb();
- if (txq_stopped && apc->port_is_up && avail_space >= MAX_TX_WQE_SIZE) {
+ if (txq_stopped && !READ_ONCE(txq->retiring) && apc->port_is_up &&
+ avail_space >= MAX_TX_WQE_SIZE) {
netif_tx_wake_queue(net_txq);
apc->eth_stats.wake_queue++;
}
@@ -2754,6 +2755,7 @@ static int mana_create_txq(struct mana_port_context *apc,
u64_stats_init(&txq->stats.syncp);
txq->ndev = net;
txq->net_txq = netdev_get_tx_queue(net, i);
+ txq->reset_gen = READ_ONCE(apc->ac->reset_gen);
txq->vp_offset = apc->tx_vp_offset;
txq->napi_initialized = false;
skb_queue_head_init(&txq->pending_skbs);
@@ -3009,11 +3011,11 @@ static int mana_push_wqe(struct mana_rxq *rxq)
static int mana_create_page_pool(struct mana_rxq *rxq, struct gdma_context *gc)
{
- struct mana_port_context *mpc = netdev_priv(rxq->ndev);
struct page_pool_params pprm = {};
int ret;
- pprm.pool_size = mpc->rx_queue_size / rxq->frag_count + 1;
+ /* Size the pool for this RX queue, not the live configuration. */
+ pprm.pool_size = rxq->num_rx_buf / rxq->frag_count + 1;
pprm.nid = gc->numa_node;
pprm.napi = &rxq->rx_cq.napi;
pprm.netdev = rxq->ndev;
@@ -3679,15 +3681,88 @@ int mana_attach(struct net_device *ndev)
return 0;
}
-static int mana_dealloc_queues(struct net_device *ndev)
+/* Drain with a per-set timeout; return true only for a successful FLR. A false
+ * return does not guarantee DMA quiescence.
+ */
+static bool mana_drain_txqs(struct mana_port_context *apc)
{
- struct mana_port_context *apc = netdev_priv(ndev);
unsigned long timeout = jiffies + 120 * HZ;
- struct gdma_dev *gd = apc->ac->gdma_dev;
+ struct gdma_context *gc = apc->ac->gdma_dev->gdma_context;
+ bool quiesced = true;
+ bool reset = false;
struct mana_txq *txq;
struct sk_buff *skb;
- int i, err;
u32 tsleep;
+ int i, err;
+
+ if (!apc->tx_qp)
+ return false;
+
+ for (i = 0; i < apc->num_queues; i++) {
+ if (!apc->tx_qp[i])
+ continue;
+
+ txq = &apc->tx_qp[i]->txq;
+
+ /* A previous function reset invalidated these queues. */
+ if (READ_ONCE(apc->ac->reset_gen) != txq->reset_gen)
+ continue;
+
+ tsleep = 1000;
+ while (atomic_read(&txq->pending_sends) > 0 &&
+ time_before(jiffies, timeout)) {
+ usleep_range(tsleep, tsleep + 1000);
+ tsleep <<= 1;
+ }
+ if (atomic_read(&txq->pending_sends)) {
+ err = pcie_flr(to_pci_dev(gc->dev));
+ if (err) {
+ netdev_err(apc->ndev,
+ "flr failed %d with %d pkts pending in txq %u\n",
+ err,
+ atomic_read(&txq->pending_sends),
+ txq->gdma_txq_id);
+ quiesced = false;
+ } else {
+ /* Invalidate queues on every port after the
+ * function reset.
+ */
+ WRITE_ONCE(apc->ac->reset_gen,
+ apc->ac->reset_gen + 1);
+
+ reset = true;
+ }
+ break;
+ }
+ }
+
+ /* A failed FLR cannot justify unmapping pending TX buffers. */
+ if (!quiesced) {
+ netdev_err(apc->ndev,
+ "device not quiesced, leaking pending TX buffers instead of unmapping memory it can still DMA from\n");
+ return reset;
+ }
+
+ for (i = 0; i < apc->num_queues; i++) {
+ if (!apc->tx_qp[i])
+ continue;
+
+ txq = &apc->tx_qp[i]->txq;
+ while ((skb = skb_dequeue(&txq->pending_skbs))) {
+ mana_unmap_skb(skb, apc);
+ dev_kfree_skb_any(skb);
+ }
+ atomic_set(&txq->pending_sends, 0);
+ }
+
+ return reset;
+}
+
+static int mana_dealloc_queues(struct net_device *ndev)
+{
+ struct mana_port_context *apc = netdev_priv(ndev);
+ struct gdma_dev *gd = apc->ac->gdma_dev;
+ int err;
if (apc->port_is_up)
return -EINVAL;
@@ -3698,48 +3773,21 @@ static int mana_dealloc_queues(struct net_device *ndev)
if (gd->gdma_context->is_pf && !apc->ac->bm_hostmode)
mana_pf_deregister_filter(apc);
- /* No packet can be transmitted now since apc->port_is_up is false.
- * There is still a tiny chance that mana_poll_tx_cq() can re-enable
- * a txq because it may not timely see apc->port_is_up being cleared
- * to false, but it doesn't matter since mana_start_xmit() drops any
- * new packets due to apc->port_is_up being false.
- *
- * Drain all the in-flight TX packets.
- * A timeout of 120 seconds for all the queues is used.
- * This will break the while loop when h/w is not responding.
- * This value of 120 has been decided here considering max
- * number of queues.
- */
+ /* After FLR, schedule a best-effort sibling-port rebuild. */
+ if (mana_drain_txqs(apc)) {
+ struct mana_context *ac = apc->ac;
+ unsigned int i;
- if (apc->tx_qp) {
- for (i = 0; i < apc->num_queues; i++) {
- txq = &apc->tx_qp[i]->txq;
- tsleep = 1000;
- while (atomic_read(&txq->pending_sends) > 0 &&
- time_before(jiffies, timeout)) {
- usleep_range(tsleep, tsleep + 1000);
- tsleep <<= 1;
- }
- if (atomic_read(&txq->pending_sends)) {
- err =
- pcie_flr(to_pci_dev(gd->gdma_context->dev));
- if (err) {
- netdev_err(ndev, "flr failed %d with %d pkts pending in txq %u\n",
- err,
- atomic_read(&txq->pending_sends),
- txq->gdma_txq_id);
- }
- break;
- }
- }
+ for (i = 0; i < ac->num_ports; i++) {
+ struct mana_port_context *sib;
- for (i = 0; i < apc->num_queues; i++) {
- txq = &apc->tx_qp[i]->txq;
- while ((skb = skb_dequeue(&txq->pending_skbs))) {
- mana_unmap_skb(skb, apc);
- dev_kfree_skb_any(skb);
- }
- atomic_set(&txq->pending_sends, 0);
+ if (!ac->ports[i] || ac->ports[i] == ndev)
+ continue;
+ sib = netdev_priv(ac->ports[i]);
+ netdev_err(ac->ports[i],
+ "queues reset by a sibling port, scheduling rebuild\n");
+ queue_work(ac->per_port_queue_reset_wq,
+ &sib->queue_reset_work);
}
}
@@ -3763,6 +3811,198 @@ static int mana_dealloc_queues(struct net_device *ndev)
return 0;
}
+static void mana_qset_snapshot(const struct mana_port_context *ctx,
+ struct mana_qset *out)
+{
+ out->eqs = ctx->eqs;
+ out->tx_qp = ctx->tx_qp;
+ out->rxqs = ctx->rxqs;
+ out->indir_table = ctx->indir_table;
+ out->indir_table_sz = ctx->indir_table_sz;
+ out->rxobj_table = ctx->rxobj_table;
+ out->default_rxobj = ctx->default_rxobj;
+ out->num_queues = ctx->num_queues;
+ out->rx_queue_size = ctx->rx_queue_size;
+ out->tx_queue_size = ctx->tx_queue_size;
+ out->priv_flags = ctx->priv_flags;
+}
+
+/* Vport identity and port debugfs outlive queue sets. */
+static void mana_qset_install(struct mana_port_context *ctx,
+ const struct mana_qset *qset)
+{
+ ctx->eqs = qset->eqs;
+ ctx->tx_qp = qset->tx_qp;
+ ctx->rxqs = qset->rxqs;
+ ctx->indir_table = qset->indir_table;
+ ctx->indir_table_sz = qset->indir_table_sz;
+ ctx->rxobj_table = qset->rxobj_table;
+ ctx->default_rxobj = qset->default_rxobj;
+ ctx->num_queues = qset->num_queues;
+ ctx->rx_queue_size = qset->rx_queue_size;
+ ctx->tx_queue_size = qset->tx_queue_size;
+ ctx->priv_flags = qset->priv_flags;
+}
+
+/* Copy the vport identity without borrowing the live queues. */
+struct mana_port_context *mana_qset_scratch_alloc(struct mana_port_context *apc)
+{
+ struct mana_port_context *scratch;
+
+ scratch = kvzalloc_obj(*scratch, GFP_KERNEL);
+ if (!scratch)
+ return NULL;
+
+ *scratch = *apc;
+
+ scratch->eqs = NULL;
+ scratch->tx_qp = NULL;
+ scratch->rxqs = NULL;
+ scratch->indir_table = NULL;
+ scratch->rxobj_table = NULL;
+ scratch->default_rxobj = INVALID_MANA_HANDLE;
+ scratch->mana_eqs_debugfs = NULL;
+
+ /* Do not consume the live set's pre-allocated RX buffers. */
+ scratch->rxbufs_pre = NULL;
+ scratch->das_pre = NULL;
+ scratch->rxbpre_total = 0;
+
+ /* Suppress debugfs names that would collide with the live set. */
+ scratch->mana_port_debugfs = ERR_PTR(-ENODEV);
+
+ return scratch;
+}
+
+void mana_qset_scratch_free(struct mana_port_context *scratch)
+{
+ kvfree(scratch);
+}
+
+int mana_alloc_qset(struct mana_port_context *scratch, unsigned int num_queues,
+ unsigned int rx_queue_size, unsigned int tx_queue_size,
+ u32 priv_flags, struct mana_qset *out)
+{
+ struct net_device *ndev = scratch->ndev;
+ int err;
+
+ ASSERT_RTNL();
+
+ scratch->num_queues = num_queues;
+ scratch->rx_queue_size = rx_queue_size;
+ scratch->tx_queue_size = tx_queue_size;
+ scratch->priv_flags = priv_flags;
+
+ err = mana_init_port_context(scratch);
+ if (err)
+ goto out_err;
+
+ err = mana_rss_table_alloc(scratch);
+ if (err)
+ goto cleanup_rxq_array;
+
+ err = mana_create_eq(scratch);
+ if (err)
+ goto cleanup_rss;
+
+ err = mana_create_txq(scratch, ndev);
+ if (err)
+ goto cleanup_eq;
+
+ err = mana_add_rx_queues(scratch, ndev);
+ if (err)
+ goto cleanup_rxq;
+
+ mana_rss_table_init(scratch);
+
+ mana_qset_snapshot(scratch, out);
+ return 0;
+
+cleanup_rxq:
+ mana_destroy_rxqs(scratch);
+ mana_destroy_txq(scratch);
+cleanup_eq:
+ mana_destroy_eq(scratch);
+cleanup_rss:
+ mana_cleanup_indir_table(scratch);
+cleanup_rxq_array:
+ kfree(scratch->rxqs);
+ scratch->rxqs = NULL;
+out_err:
+ netdev_err(ndev, "%s(num_queues=%u) failed: %d\n", __func__,
+ num_queues, err);
+ return err;
+}
+
+/* Under RTNL, free only queues no longer shared with the installed set. */
+void mana_free_qset(struct mana_port_context *scratch, struct mana_qset *qset)
+{
+ struct bpf_prog *retiring_prog;
+ unsigned int retiring_queues;
+
+ ASSERT_RTNL();
+
+ if (!qset->rxqs && !qset->tx_qp && !qset->eqs)
+ return;
+
+ if (qset->tx_qp) {
+ unsigned int q;
+
+ for (q = 0; q < qset->num_queues; q++) {
+ if (qset->tx_qp[q])
+ WRITE_ONCE(qset->tx_qp[q]->txq.retiring, true);
+ }
+ }
+
+ /* Keep retired queues and arrays through this grace period; local NAPI
+ * synchronization does not drain other devices' XDP.
+ */
+ synchronize_net();
+
+ mana_qset_install(scratch, qset);
+
+ /* Keep retiring RXQs' XDP programs and references until RX teardown. */
+ retiring_prog = mana_chn_xdp_peek(scratch);
+ retiring_queues = scratch->num_queues;
+
+ /* Drain TX before unmapping RX buffers. */
+ if (mana_drain_txqs(scratch)) {
+ /* FLR also destroys the HWC; rebuilding ports is best-effort.
+ * This path does not reinitialize the device.
+ */
+ struct mana_port_context *apc = netdev_priv(scratch->ndev);
+ struct mana_context *ac = apc->ac;
+ struct mana_port_context *sib;
+ unsigned int i;
+
+ netdev_err(scratch->ndev,
+ "device reset while retiring a queue set, scheduling port reset\n");
+
+ for (i = 0; i < ac->num_ports; i++) {
+ if (!ac->ports[i])
+ continue;
+ sib = netdev_priv(ac->ports[i]);
+ queue_work(ac->per_port_queue_reset_wq,
+ &sib->queue_reset_work);
+ }
+ }
+
+ /* Fence RQs before unmapping, but teardown proceeds on errors. */
+ mana_fence_rqs(scratch);
+
+ mana_destroy_rxqs(scratch);
+
+ mana_chn_xdp_release(retiring_prog, retiring_queues);
+
+ mana_destroy_txq(scratch);
+ mana_destroy_eq(scratch);
+ mana_cleanup_indir_table(scratch);
+ kfree(scratch->rxqs);
+ scratch->rxqs = NULL;
+
+ memset(qset, 0, sizeof(*qset));
+}
+
int mana_detach(struct net_device *ndev, bool from_close)
{
struct mana_port_context *apc = netdev_priv(ndev);
@@ -4240,6 +4480,10 @@ void mana_remove(struct gdma_dev *gd, bool suspending)
unregister_netdevice(ndev);
mana_cleanup_indir_table(apc);
+ /* Remove the port from reset walks before freeing its netdev.
+ */
+ ac->ports[i] = NULL;
+
rtnl_unlock();
free_netdev(ndev);
diff --git a/include/net/mana/mana.h b/include/net/mana/mana.h
index 83b7eff4646ead7aef1382c6ce565a573a940af4..4c00a98430262b6c8c25bc37c9d1c8b48d928629 100644
--- a/include/net/mana/mana.h
+++ b/include/net/mana/mana.h
@@ -143,6 +143,11 @@ struct mana_txq {
bool napi_initialized;
+ u32 reset_gen;
+
+ /* Suppress completion wakeups on the replacement's netdev queue. */
+ bool retiring;
+
struct mana_stats_tx stats;
};
@@ -537,6 +542,12 @@ struct mana_context {
u8 bm_hostmode;
struct mana_ethtool_hc_stats hc_stats;
+
+ /* Generation of successful resets issued by mana_drain_txqs(), under
+ * RTNL.
+ */
+ u32 reset_gen;
+
struct workqueue_struct *per_port_queue_reset_wq;
/* Workqueue for querying hardware stats */
struct delayed_work gf_stats_work;
@@ -661,6 +672,23 @@ struct mana_port_context {
u32 steer_cqe_coalescing;
};
+struct mana_qset {
+ struct mana_eq *eqs;
+ struct mana_tx_qp **tx_qp;
+ struct mana_rxq **rxqs;
+
+ u32 *indir_table;
+ u32 indir_table_sz;
+ mana_handle_t *rxobj_table;
+ mana_handle_t default_rxobj;
+
+ unsigned int num_queues;
+ unsigned int rx_queue_size;
+ unsigned int tx_queue_size;
+ u32 priv_flags;
+
+};
+
netdev_tx_t mana_start_xmit(struct sk_buff *skb, struct net_device *ndev);
int mana_config_rss(struct mana_port_context *ac, enum TRI_STATE rx,
bool update_hash, bool update_tab);
@@ -670,6 +698,14 @@ int mana_alloc_queues(struct net_device *ndev);
int mana_attach(struct net_device *ndev);
int mana_detach(struct net_device *ndev, bool from_close);
+struct mana_port_context *
+mana_qset_scratch_alloc(struct mana_port_context *apc);
+void mana_qset_scratch_free(struct mana_port_context *scratch);
+int mana_alloc_qset(struct mana_port_context *scratch, unsigned int num_queues,
+ unsigned int rx_queue_size, unsigned int tx_queue_size,
+ u32 priv_flags, struct mana_qset *out);
+void mana_free_qset(struct mana_port_context *scratch, struct mana_qset *qset);
+
void mana_dim_change(struct mana_cq *cq, bool enable);
int mana_probe(struct gdma_dev *gd, bool resuming);
@@ -685,6 +721,8 @@ u32 mana_run_xdp(struct net_device *ndev, struct mana_rxq *rxq,
struct xdp_buff *xdp, void *buf_va, uint pkt_len);
struct bpf_prog *mana_xdp_get(struct mana_port_context *apc);
void mana_chn_setxdp(struct mana_port_context *apc, struct bpf_prog *prog);
+struct bpf_prog *mana_chn_xdp_peek(struct mana_port_context *apc);
+void mana_chn_xdp_release(struct bpf_prog *prog, unsigned int num_queues);
int mana_bpf(struct net_device *ndev, struct netdev_bpf *bpf);
int mana_query_gf_stats(struct mana_context *ac);
int mana_query_link_cfg(struct mana_port_context *apc);
--
2.43.0
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH net-next v4 02/13] net: mana: share the EQ pool across a queue-set swap
2026-09-08 3:28 [PATCH net-next v4 00/13] net: mana: reconfigure by replacing the queue set Long Li
2026-09-08 3:28 ` [PATCH net-next v4 01/13] net: mana: add queue-set allocation and teardown helpers Long Li
@ 2026-09-08 3:28 ` Long Li
2026-09-09 3:29 ` sashiko-bot
2026-09-08 3:28 ` [PATCH net-next v4 03/13] net: mana: swap queue sets in mana_set_channels Long Li
` (10 subsequent siblings)
12 siblings, 1 reply; 28+ messages in thread
From: Long Li @ 2026-09-08 3:28 UTC (permalink / raw)
To: Long Li, Long Li, Konstantin Taranov, Jakub Kicinski,
David S . Miller, Paolo Abeni, Eric Dumazet, Andrew Lunn,
Jason Gunthorpe, Leon Romanovsky, Haiyang Zhang,
K . Y . Srinivasan, Wei Liu, Dexuan Cui, shradhagupta,
Simon Horman, ernis, stephen, shirazsaleem
Cc: netdev, linux-rdma, linux-hyperv, linux-kernel
Make the EQ pool port-owned so overlapping queue sets share EQs instead
of requiring old + new vector allocations. Allocate max_queues slots and
track populated entries with num_eqs.
Grow the pool before creating replacement CQs. Additional EQs survive
allocation failure in this patch and are released at port teardown.
Signed-off-by: Long Li <longli@microsoft.com>
---
Changes in v4:
- Clarify that allocation can grow the live, port-owned EQ pool and
that partial growth is retained at this stage of the series.
- Shorten the scratch-context ownership comments.
drivers/net/ethernet/microsoft/mana/mana_en.c | 86 ++++++++++++++++---
include/net/mana/mana.h | 6 +-
2 files changed, 76 insertions(+), 16 deletions(-)
diff --git a/drivers/net/ethernet/microsoft/mana/mana_en.c b/drivers/net/ethernet/microsoft/mana/mana_en.c
index 5ac3ae9fd7ea2f786dddbd21d340884c008e1020..fc80d4bcde6c453d61222371d7763cff90b2a26a 100644
--- a/drivers/net/ethernet/microsoft/mana/mana_en.c
+++ b/drivers/net/ethernet/microsoft/mana/mana_en.c
@@ -1733,7 +1733,7 @@ void mana_destroy_eq(struct mana_port_context *apc)
debugfs_remove_recursive(apc->mana_eqs_debugfs);
apc->mana_eqs_debugfs = NULL;
- for (i = 0; i < apc->num_queues; i++) {
+ for (i = 0; i < apc->num_eqs; i++) {
eq = apc->eqs[i].eq;
if (!eq)
continue;
@@ -1745,6 +1745,7 @@ void mana_destroy_eq(struct mana_port_context *apc)
kfree(apc->eqs);
apc->eqs = NULL;
+ apc->num_eqs = 0;
}
EXPORT_SYMBOL_NS(mana_destroy_eq, "NET_MANA");
@@ -1773,9 +1774,11 @@ int mana_create_eq(struct mana_port_context *apc)
if (WARN_ON(apc->eqs))
return -EEXIST;
- apc->eqs = kzalloc_objs(struct mana_eq, apc->num_queues);
+ /* Keep EQ array addresses stable while CQs reference them. */
+ apc->eqs = kzalloc_objs(struct mana_eq, apc->max_queues);
if (!apc->eqs)
return -ENOMEM;
+ apc->num_eqs = 0;
spec.type = GDMA_EQ;
spec.monitor_avl_buf = false;
@@ -1805,6 +1808,7 @@ int mana_create_eq(struct mana_port_context *apc)
}
apc->eqs[i].eq->eq.irq = gic->irq;
mana_create_eq_debugfs(apc, i);
+ apc->num_eqs = i + 1;
}
return 0;
@@ -1814,6 +1818,61 @@ int mana_create_eq(struct mana_port_context *apc)
}
EXPORT_SYMBOL_NS(mana_create_eq, "NET_MANA");
+/* Grow the shared EQ pool without replacing live entries. */
+static int mana_grow_eqs(struct mana_port_context *apc, unsigned int need)
+{
+ 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;
+ unsigned int i;
+ int err;
+ int msi;
+
+ if (WARN_ON(!apc->eqs))
+ return -EINVAL;
+
+ if (need > apc->max_queues)
+ return -EINVAL;
+
+ if (need <= apc->num_eqs)
+ return 0;
+
+ spec.type = GDMA_EQ;
+ spec.monitor_avl_buf = false;
+ spec.queue_size = EQ_SIZE;
+ spec.eq.callback = NULL;
+ spec.eq.context = apc->eqs;
+ spec.eq.log2_throttle_limit = LOG2_EQ_THROTTLE;
+
+ for (i = apc->num_eqs; i < need; 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;
+ }
+ 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 grow EQ %u : %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);
+ apc->num_eqs = i + 1;
+ }
+
+ return 0;
+out:
+ /* Retain partial growth for reuse; the live set still needs this pool.
+ */
+ return err;
+}
+
static int mana_fence_rq(struct mana_port_context *apc, struct mana_rxq *rxq)
{
struct mana_fence_rq_resp resp = {};
@@ -3814,7 +3873,6 @@ static int mana_dealloc_queues(struct net_device *ndev)
static void mana_qset_snapshot(const struct mana_port_context *ctx,
struct mana_qset *out)
{
- out->eqs = ctx->eqs;
out->tx_qp = ctx->tx_qp;
out->rxqs = ctx->rxqs;
out->indir_table = ctx->indir_table;
@@ -3831,7 +3889,6 @@ static void mana_qset_snapshot(const struct mana_port_context *ctx,
static void mana_qset_install(struct mana_port_context *ctx,
const struct mana_qset *qset)
{
- ctx->eqs = qset->eqs;
ctx->tx_qp = qset->tx_qp;
ctx->rxqs = qset->rxqs;
ctx->indir_table = qset->indir_table;
@@ -3844,7 +3901,9 @@ static void mana_qset_install(struct mana_port_context *ctx,
ctx->priv_flags = qset->priv_flags;
}
-/* Copy the vport identity without borrowing the live queues. */
+/* Scratch starts without SQs/RQs and borrows the port's EQ pool. Never call
+ * mana_destroy_eq() on it.
+ */
struct mana_port_context *mana_qset_scratch_alloc(struct mana_port_context *apc)
{
struct mana_port_context *scratch;
@@ -3855,13 +3914,11 @@ struct mana_port_context *mana_qset_scratch_alloc(struct mana_port_context *apc)
*scratch = *apc;
- scratch->eqs = NULL;
scratch->tx_qp = NULL;
scratch->rxqs = NULL;
scratch->indir_table = NULL;
scratch->rxobj_table = NULL;
scratch->default_rxobj = INVALID_MANA_HANDLE;
- scratch->mana_eqs_debugfs = NULL;
/* Do not consume the live set's pre-allocated RX buffers. */
scratch->rxbufs_pre = NULL;
@@ -3879,7 +3936,8 @@ void mana_qset_scratch_free(struct mana_port_context *scratch)
kvfree(scratch);
}
-int mana_alloc_qset(struct mana_port_context *scratch, unsigned int num_queues,
+int mana_alloc_qset(struct mana_port_context *apc,
+ struct mana_port_context *scratch, unsigned int num_queues,
unsigned int rx_queue_size, unsigned int tx_queue_size,
u32 priv_flags, struct mana_qset *out)
{
@@ -3901,13 +3959,16 @@ int mana_alloc_qset(struct mana_port_context *scratch, unsigned int num_queues,
if (err)
goto cleanup_rxq_array;
- err = mana_create_eq(scratch);
+ err = mana_grow_eqs(apc, num_queues);
if (err)
goto cleanup_rss;
+ scratch->eqs = apc->eqs;
+ scratch->num_eqs = apc->num_eqs;
+
err = mana_create_txq(scratch, ndev);
if (err)
- goto cleanup_eq;
+ goto cleanup_rss;
err = mana_add_rx_queues(scratch, ndev);
if (err)
@@ -3921,8 +3982,6 @@ int mana_alloc_qset(struct mana_port_context *scratch, unsigned int num_queues,
cleanup_rxq:
mana_destroy_rxqs(scratch);
mana_destroy_txq(scratch);
-cleanup_eq:
- mana_destroy_eq(scratch);
cleanup_rss:
mana_cleanup_indir_table(scratch);
cleanup_rxq_array:
@@ -3942,7 +4001,7 @@ void mana_free_qset(struct mana_port_context *scratch, struct mana_qset *qset)
ASSERT_RTNL();
- if (!qset->rxqs && !qset->tx_qp && !qset->eqs)
+ if (!qset->rxqs && !qset->tx_qp)
return;
if (qset->tx_qp) {
@@ -3995,7 +4054,6 @@ void mana_free_qset(struct mana_port_context *scratch, struct mana_qset *qset)
mana_chn_xdp_release(retiring_prog, retiring_queues);
mana_destroy_txq(scratch);
- mana_destroy_eq(scratch);
mana_cleanup_indir_table(scratch);
kfree(scratch->rxqs);
scratch->rxqs = NULL;
diff --git a/include/net/mana/mana.h b/include/net/mana/mana.h
index 4c00a98430262b6c8c25bc37c9d1c8b48d928629..de026eeb8fc25f80d3e0f9613fb277eae9ae19a9 100644
--- a/include/net/mana/mana.h
+++ b/include/net/mana/mana.h
@@ -570,7 +570,9 @@ struct mana_port_context {
u8 mac_addr[ETH_ALEN];
+ /* Port-owned EQ pool: max_queues slots, num_eqs populated. */
struct mana_eq *eqs;
+ unsigned int num_eqs;
struct dentry *mana_eqs_debugfs;
enum TRI_STATE rss_state;
@@ -673,7 +675,6 @@ struct mana_port_context {
};
struct mana_qset {
- struct mana_eq *eqs;
struct mana_tx_qp **tx_qp;
struct mana_rxq **rxqs;
@@ -701,7 +702,8 @@ int mana_detach(struct net_device *ndev, bool from_close);
struct mana_port_context *
mana_qset_scratch_alloc(struct mana_port_context *apc);
void mana_qset_scratch_free(struct mana_port_context *scratch);
-int mana_alloc_qset(struct mana_port_context *scratch, unsigned int num_queues,
+int mana_alloc_qset(struct mana_port_context *apc,
+ struct mana_port_context *scratch, unsigned int num_queues,
unsigned int rx_queue_size, unsigned int tx_queue_size,
u32 priv_flags, struct mana_qset *out);
void mana_free_qset(struct mana_port_context *scratch, struct mana_qset *qset);
--
2.43.0
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH net-next v4 03/13] net: mana: swap queue sets in mana_set_channels
2026-09-08 3:28 [PATCH net-next v4 00/13] net: mana: reconfigure by replacing the queue set Long Li
2026-09-08 3:28 ` [PATCH net-next v4 01/13] net: mana: add queue-set allocation and teardown helpers Long Li
2026-09-08 3:28 ` [PATCH net-next v4 02/13] net: mana: share the EQ pool across a queue-set swap Long Li
@ 2026-09-08 3:28 ` Long Li
2026-09-09 3:29 ` sashiko-bot
2026-09-08 3:28 ` [PATCH net-next v4 04/13] net: mana: swap queue sets in mana_set_ringparam Long Li
` (9 subsequent siblings)
12 siblings, 1 reply; 28+ messages in thread
From: Long Li @ 2026-09-08 3:28 UTC (permalink / raw)
To: Long Li, Long Li, Konstantin Taranov, Jakub Kicinski,
David S . Miller, Paolo Abeni, Eric Dumazet, Andrew Lunn,
Jason Gunthorpe, Leon Romanovsky, Haiyang Zhang,
K . Y . Srinivasan, Wei Liu, Dexuan Cui, shradhagupta,
Simon Horman, ernis, stephen, shirazsaleem
Cc: netdev, linux-rdma, linux-hyperv, linux-kernel
Build a replacement queue set before quiescing TX, then publish it and
retire the old set. Allocation failure preserves the running queues;
publication failure attempts rollback. If rollback also fails, close the
port and lower carrier, allowing a later administrative reopen.
Keep RX queue indices valid until retiring queues stop delivering. Order
the port-up store before TX ring reads to avoid a missed queue wakeup.
The temporary SQ/RQ peak is old + new. Later patches remove that peak for
channel-count changes; full per-queue rebuilds still require it.
Signed-off-by: Long Li <longli@microsoft.com>
---
Changes in v4:
- Raise the RX queue count during publication, but defer lowering it
until retiring RQs are destroyed, including after rollback.
- Order the port-up store before TX ring reads with a full barrier.
- Qualify rollback recovery and document the temporary queue peak;
shorten comments.
.../net/ethernet/microsoft/mana/mana_bpf.c | 5 +
drivers/net/ethernet/microsoft/mana/mana_en.c | 246 +++++++++++++++++-
.../ethernet/microsoft/mana/mana_ethtool.c | 78 ++++--
include/net/mana/mana.h | 16 +-
4 files changed, 313 insertions(+), 32 deletions(-)
diff --git a/drivers/net/ethernet/microsoft/mana/mana_bpf.c b/drivers/net/ethernet/microsoft/mana/mana_bpf.c
index 1c5ac7e2d884ed90bb12993a86718e2ebc0937c9..4365e26cc73874a727ea3cae93ec959956d2d42a 100644
--- a/drivers/net/ethernet/microsoft/mana/mana_bpf.c
+++ b/drivers/net/ethernet/microsoft/mana/mana_bpf.c
@@ -59,6 +59,11 @@ int mana_xdp_xmit(struct net_device *ndev, int n, struct xdp_frame **frames,
if (unlikely(!apc->port_is_up))
return 0;
+ /* Pair with the smp_wmb() in mana_publish_qset() before reading queue
+ * state.
+ */
+ smp_rmb();
+
q_idx = smp_processor_id() % ndev->real_num_tx_queues;
for (i = 0; i < n; i++) {
diff --git a/drivers/net/ethernet/microsoft/mana/mana_en.c b/drivers/net/ethernet/microsoft/mana/mana_en.c
index fc80d4bcde6c453d61222371d7763cff90b2a26a..bb9ef4e634a6edaf097bc85f2248c2ceea88fc05 100644
--- a/drivers/net/ethernet/microsoft/mana/mana_en.c
+++ b/drivers/net/ethernet/microsoft/mana/mana_en.c
@@ -90,6 +90,17 @@ static int mana_open(struct net_device *ndev)
smp_wmb();
netif_tx_wake_all_queues(ndev);
+
+ /* Undo a forced carrier-off unless a disconnect is pending behind RTNL.
+ */
+ if (apc->carrier_forced_off) {
+ u32 ev = READ_ONCE(apc->ac->link_event);
+
+ apc->carrier_forced_off = false;
+ if (ev != HWC_DATA_HW_LINK_DISCONNECT)
+ netif_carrier_on(ndev);
+ }
+
netdev_dbg(ndev, "%s successful\n", __func__);
return 0;
}
@@ -106,6 +117,7 @@ static int mana_close(struct net_device *ndev)
static void mana_link_state_handle(struct work_struct *w)
{
+ struct mana_port_context *apc;
struct mana_context *ac;
struct net_device *ndev;
u32 link_event;
@@ -131,6 +143,9 @@ static void mana_link_state_handle(struct work_struct *w)
if (!ndev)
continue;
+ apc = netdev_priv(ndev);
+ apc->carrier_forced_off = false;
+
if (link_up) {
netif_carrier_on(ndev);
@@ -312,8 +327,8 @@ 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().
+ /* Exclude RDMA across detach/attach; RTNL serializes channel_changing
+ * writers.
*/
mutex_lock(&apc->vport_mutex);
apc->channel_changing = true;
@@ -366,6 +381,15 @@ netdev_tx_t mana_start_xmit(struct sk_buff *skb, struct net_device *ndev)
if (unlikely(!apc->port_is_up))
goto tx_drop;
+ /* Pair with mana_publish_qset()'s pre-gate smp_wmb(): observe queue
+ * fields after reading port_is_up.
+ */
+ smp_rmb();
+
+ /* Retiring RXQs may use indices beyond the live queue count. */
+ if (unlikely(txq_idx >= apc->num_queues))
+ goto tx_drop_count;
+
if (skb_cow_head(skb, MANA_HEADROOM))
goto tx_drop_count;
@@ -1045,6 +1069,7 @@ static void mana_cleanup_indir_table(struct mana_port_context *apc)
static int mana_init_port_context(struct mana_port_context *apc)
{
+ kfree(apc->rxqs);
apc->rxqs = kzalloc_objs(struct mana_rxq *, apc->num_queues);
return !apc->rxqs ? -ENOMEM : 0;
@@ -2077,6 +2102,7 @@ static void mana_poll_tx_cq(struct mana_cq *cq)
/* Ensure checking txq_stopped before apc->port_is_up. */
smp_rmb();
+ /* Order the stopped-state read before the retiring read. */
if (txq_stopped && !READ_ONCE(txq->retiring) && apc->port_is_up &&
avail_space >= MAX_TX_WQE_SIZE) {
netif_tx_wake_queue(net_txq);
@@ -3993,9 +4019,213 @@ int mana_alloc_qset(struct mana_port_context *apc,
return err;
}
+/* Destroy caller-owned CQs before closing this dead-end port: closing also
+ * frees the shared EQ pool. Requires RTNL.
+ */
+void mana_publish_close_if_needed(struct mana_port_context *apc)
+{
+ ASSERT_RTNL();
+
+ if (!apc->publish_dead_end)
+ return;
+
+ apc->publish_dead_end = false;
+
+ if (mana_dealloc_queues(apc->ndev))
+ netdev_err(apc->ndev,
+ "failed to close the port after a failed rollback\n");
+}
+
+/* Carried-over queues may still have full rings. */
+static void mana_start_txqs(struct mana_port_context *apc)
+{
+ struct net_device *ndev = apc->ndev;
+ unsigned int i;
+
+ if (!apc->tx_qp)
+ return;
+
+ /* Order port_is_up=true before ring reads to avoid a missed wakeup.
+ * Pair with mana_poll_tx_cq()'s full barrier after its tail update.
+ */
+ smp_mb();
+
+ for (i = 0; i < apc->num_queues; i++) {
+ if (!apc->tx_qp[i])
+ continue;
+
+ if (mana_can_tx(apc->tx_qp[i]->txq.gdma_sq))
+ netif_tx_wake_queue(netdev_get_tx_queue(ndev, i));
+ }
+}
+
+/* Retiring completions must not wake replacement queues. Mark the leaving set
+ * before unmarking the incoming set.
+ */
+static void mana_qset_set_retiring(struct mana_qset *qset, bool retiring)
+{
+ unsigned int q;
+
+ if (!qset->tx_qp)
+ return;
+
+ for (q = 0; q < qset->num_queues; q++) {
+ if (qset->tx_qp[q])
+ WRITE_ONCE(qset->tx_qp[q]->txq.retiring, retiring);
+ }
+}
+
+/* Leave TX stopped and request RX disable; steering may be unrecoverable. */
+static void mana_publish_give_up(struct mana_port_context *apc)
+{
+ int err;
+
+ apc->rss_state = TRI_STATE_FALSE;
+
+ err = mana_disable_vport_rx(apc);
+ if (err && mana_en_need_log(apc, err))
+ netdev_err(apc->ndev, "failed to disable vPort RX: %d\n", err);
+
+ apc->carrier_forced_off = netif_carrier_ok(apc->ndev);
+ netif_carrier_off(apc->ndev);
+ apc->publish_dead_end = true;
+}
+
+/* Keep the RX count high until retiring RQs stop delivering their indices. */
+static int mana_raise_real_num_rx(struct net_device *ndev, unsigned int count)
+{
+ if (count <= ndev->real_num_rx_queues)
+ return 0;
+
+ return netif_set_real_num_rx_queues(ndev, count);
+}
+
+/* Publish under RTNL with TX gated. An error restores old pointers, not
+ * necessarily service. Free only owned queues.
+ */
+int mana_publish_qset(struct mana_port_context *apc, struct mana_qset *newq,
+ struct mana_qset *out_old)
+{
+ struct net_device *ndev = apc->ndev;
+ int err;
+
+ ASSERT_RTNL();
+
+ /* Close the XDP gate before stopping TX queues. Pair with
+ * mana_poll_tx_cq()'s smp_rmb() to prevent mid-swap wakeups.
+ */
+ WRITE_ONCE(apc->port_is_up, false);
+
+ /* Ensure port state updated before txq state */
+ smp_wmb();
+
+ netif_tx_disable(ndev);
+
+ mana_qset_snapshot(apc, out_old);
+
+ /* Mark before the grace period so old completions cannot wake the
+ * replacement's stopped queue.
+ */
+ mana_qset_set_retiring(out_old, true);
+
+ /* Drain TX/XDP readers past the gate and polls missing retiring. */
+ synchronize_net();
+
+ mana_qset_set_retiring(newq, false);
+
+ mana_qset_install(apc, newq);
+ apc->rss_state = apc->num_queues > 1 ? TRI_STATE_TRUE : TRI_STATE_FALSE;
+
+ err = netif_set_real_num_tx_queues(ndev, apc->num_queues);
+ if (err)
+ goto rollback;
+
+ err = mana_raise_real_num_rx(ndev, apc->num_queues);
+ if (err)
+ goto rollback;
+
+ /* Install XDP and per-RXQ references before steering reaches new
+ * queues.
+ */
+ mana_chn_setxdp(apc, mana_xdp_get(apc));
+
+ err = mana_config_rss(apc, TRI_STATE_TRUE, true, true);
+ if (err)
+ goto rollback;
+
+ /* Publish fields before opening the gate; pair with TX/XDP read
+ * barriers. The post-gate full barrier cannot replace this.
+ */
+ smp_wmb();
+
+ WRITE_ONCE(apc->port_is_up, true);
+ mana_start_txqs(apc);
+
+ return 0;
+
+rollback:
+ netdev_err(ndev, "%s failed: %d, restoring previous queue set\n",
+ __func__, err);
+
+ mana_qset_set_retiring(newq, true);
+ mana_qset_set_retiring(out_old, false);
+
+ mana_qset_install(apc, out_old);
+ apc->rss_state = apc->num_queues > 1 ? TRI_STATE_TRUE : TRI_STATE_FALSE;
+
+ if (netif_set_real_num_tx_queues(ndev, apc->num_queues) ||
+ mana_raise_real_num_rx(ndev, apc->num_queues)) {
+ /* Inconsistent restored queue counts prohibit TX; leave the
+ * port stopped.
+ */
+ netdev_err(ndev, "failed to restore queue counts, closing the port\n");
+ mana_publish_give_up(apc);
+ return err;
+ }
+
+ if (mana_config_rss(apc, TRI_STATE_TRUE, true, true)) {
+ /* Do not reopen TX with mismatched steering; RX disable is
+ * best-effort.
+ */
+ netdev_err(ndev, "failed to restore RSS steering, closing the port\n");
+ mana_publish_give_up(apc);
+ return err;
+ }
+
+ /* Publish restored fields before reopening the gate, as on success. */
+ smp_wmb();
+
+ WRITE_ONCE(apc->port_is_up, true);
+ mana_start_txqs(apc);
+
+ return err;
+}
+
+/* Create missing debugfs nodes once retiring names are gone. */
+static void mana_qset_debugfs_publish(struct mana_port_context *apc)
+{
+ unsigned int i;
+
+ ASSERT_RTNL();
+
+ if (IS_ERR_OR_NULL(apc->mana_port_debugfs))
+ return;
+
+ for (i = 0; i < apc->num_queues; i++) {
+ if (apc->tx_qp && apc->tx_qp[i] &&
+ IS_ERR_OR_NULL(apc->tx_qp[i]->mana_tx_debugfs))
+ mana_create_txq_debugfs(apc, i);
+
+ if (apc->rxqs && apc->rxqs[i] &&
+ IS_ERR_OR_NULL(apc->rxqs[i]->mana_rx_debugfs))
+ mana_create_rxq_debugfs(apc, i);
+ }
+}
+
/* Under RTNL, free only queues no longer shared with the installed set. */
void mana_free_qset(struct mana_port_context *scratch, struct mana_qset *qset)
{
+ struct mana_port_context *apc = netdev_priv(scratch->ndev);
struct bpf_prog *retiring_prog;
unsigned int retiring_queues;
@@ -4020,7 +4250,9 @@ void mana_free_qset(struct mana_port_context *scratch, struct mana_qset *qset)
mana_qset_install(scratch, qset);
- /* Keep retiring RXQs' XDP programs and references until RX teardown. */
+ /* Keep retiring RXQs' XDP programs and references until RX teardown.
+ * Read the program from the queues, not queue-set metadata.
+ */
retiring_prog = mana_chn_xdp_peek(scratch);
retiring_queues = scratch->num_queues;
@@ -4029,7 +4261,6 @@ void mana_free_qset(struct mana_port_context *scratch, struct mana_qset *qset)
/* FLR also destroys the HWC; rebuilding ports is best-effort.
* This path does not reinitialize the device.
*/
- struct mana_port_context *apc = netdev_priv(scratch->ndev);
struct mana_context *ac = apc->ac;
struct mana_port_context *sib;
unsigned int i;
@@ -4059,6 +4290,13 @@ void mana_free_qset(struct mana_port_context *scratch, struct mana_qset *qset)
scratch->rxqs = NULL;
memset(qset, 0, sizeof(*qset));
+
+ /* Retiring RQs can no longer deliver indices beyond the live queue
+ * count.
+ */
+ netif_set_real_num_rx_queues(apc->ndev, apc->num_queues);
+
+ mana_qset_debugfs_publish(apc);
}
int mana_detach(struct net_device *ndev, bool from_close)
diff --git a/drivers/net/ethernet/microsoft/mana/mana_ethtool.c b/drivers/net/ethernet/microsoft/mana/mana_ethtool.c
index ece7ff9cc409a806b6a6de70a85b44874bfa6dad..45031ca1254e327a9b129bd77c7a08fb8a240838 100644
--- a/drivers/net/ethernet/microsoft/mana/mana_ethtool.c
+++ b/drivers/net/ethernet/microsoft/mana/mana_ethtool.c
@@ -648,52 +648,82 @@ static int mana_set_coalesce(struct net_device *ndev,
return 0;
}
-/* 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)
{
struct mana_port_context *apc = netdev_priv(ndev);
unsigned int new_count = channels->combined_count;
- unsigned int old_count = apc->num_queues;
+ struct mana_port_context *scratch;
+ struct mana_qset newq, oldq;
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.
+ if (new_count < 1 || new_count > apc->max_queues) {
+ netdev_err(ndev, "Invalid combined_count %u (max %u)\n",
+ new_count, apc->max_queues);
+ return -EINVAL;
+ }
+
+ if (new_count == apc->num_queues)
+ return 0;
+
+ /* Resize rxqs while down: mana_open() does not recreate the port
+ * context. RDMA must not own the vport while num_queues changes.
*/
mutex_lock(&apc->vport_mutex);
- if (!apc->port_is_up && apc->vport_use_count) {
+ if (!apc->port_is_up) {
+ struct mana_rxq **rxqs;
+
+ if (apc->vport_use_count) {
+ mutex_unlock(&apc->vport_mutex);
+ return -EBUSY;
+ }
+
+ rxqs = kzalloc_objs(struct mana_rxq *, new_count);
+ if (!rxqs) {
+ mutex_unlock(&apc->vport_mutex);
+ return -ENOMEM;
+ }
+
+ kfree(apc->rxqs);
+ apc->rxqs = rxqs;
+ apc->num_queues = new_count;
+ mutex_unlock(&apc->vport_mutex);
+ return 0;
+ }
+
+ /* The Ethernet port already holds a vport reference; exclude RDMA
+ * through failure cleanup.
+ */
+ if (apc->channel_changing) {
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");
+ scratch = mana_qset_scratch_alloc(apc);
+ if (!scratch) {
+ err = -ENOMEM;
goto clear_flag;
}
- err = mana_detach(ndev, false);
- if (err) {
- netdev_err(ndev, "mana_detach failed: %d\n", err);
- goto out;
- }
+ err = mana_alloc_qset(apc, scratch, new_count, apc->rx_queue_size,
+ apc->tx_queue_size, apc->priv_flags, &newq);
+ if (err)
+ goto free_scratch;
- apc->num_queues = new_count;
- err = mana_attach(ndev);
+ err = mana_publish_qset(apc, &newq, &oldq);
if (err) {
- apc->num_queues = old_count;
- netdev_err(ndev, "mana_attach failed: %d\n", err);
+ mana_free_qset(scratch, &newq);
+ goto free_scratch;
}
-out:
- mana_pre_dealloc_rxbufs(apc);
+ mana_free_qset(scratch, &oldq);
+
+free_scratch:
+ /* Release unpublished queues before closing their shared EQ pool. */
+ mana_publish_close_if_needed(apc);
+ mana_qset_scratch_free(scratch);
clear_flag:
mutex_lock(&apc->vport_mutex);
apc->channel_changing = false;
diff --git a/include/net/mana/mana.h b/include/net/mana/mana.h
index de026eeb8fc25f80d3e0f9613fb277eae9ae19a9..e965d86b4d8502408f175bdbee5d77fb2e406df3 100644
--- a/include/net/mana/mana.h
+++ b/include/net/mana/mana.h
@@ -623,12 +623,17 @@ 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.
- */
+ /* Exclude RDMA during reconfiguration; protected by vport_mutex. */
bool channel_changing;
+ /* Caller must close the port after releasing the unpublished set. */
+ bool publish_dead_end;
+
+ /* Carrier lowered by failed rollback; cleared on reopen or a link
+ * event.
+ */
+ bool carrier_forced_off;
+
/* Net shaper handle*/
struct net_shaper_handle handle;
@@ -706,6 +711,9 @@ int mana_alloc_qset(struct mana_port_context *apc,
struct mana_port_context *scratch, unsigned int num_queues,
unsigned int rx_queue_size, unsigned int tx_queue_size,
u32 priv_flags, struct mana_qset *out);
+int mana_publish_qset(struct mana_port_context *apc, struct mana_qset *newq,
+ struct mana_qset *out_old);
+void mana_publish_close_if_needed(struct mana_port_context *apc);
void mana_free_qset(struct mana_port_context *scratch, struct mana_qset *qset);
void mana_dim_change(struct mana_cq *cq, bool enable);
--
2.43.0
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH net-next v4 04/13] net: mana: swap queue sets in mana_set_ringparam
2026-09-08 3:28 [PATCH net-next v4 00/13] net: mana: reconfigure by replacing the queue set Long Li
` (2 preceding siblings ...)
2026-09-08 3:28 ` [PATCH net-next v4 03/13] net: mana: swap queue sets in mana_set_channels Long Li
@ 2026-09-08 3:28 ` Long Li
2026-09-09 3:29 ` sashiko-bot
2026-09-08 3:28 ` [PATCH net-next v4 05/13] net: mana: swap queue sets in mana_set_priv_flags Long Li
` (8 subsequent siblings)
12 siblings, 1 reply; 28+ messages in thread
From: Long Li @ 2026-09-08 3:28 UTC (permalink / raw)
To: Long Li, Long Li, Konstantin Taranov, Jakub Kicinski,
David S . Miller, Paolo Abeni, Eric Dumazet, Andrew Lunn,
Jason Gunthorpe, Leon Romanovsky, Haiyang Zhang,
K . Y . Srinivasan, Wei Liu, Dexuan Cui, shradhagupta,
Simon Horman, ernis, stephen, shirazsaleem
Cc: netdev, linux-rdma, linux-hyperv, linux-kernel
Replace detach/attach with queue-set allocation and publication. Failed
allocation now leaves the running queues and ring sizes unchanged,
rather than risking a detached port after attach failure.
Skip requests whose rounded sizes already match. Keep RDMA excluded
through failure cleanup, which can release the vport.
Signed-off-by: Long Li <longli@microsoft.com>
---
Changes in v4:
- Describe the old failed-attach behaviour without the nonexistent
retry chain, and shorten comments.
.../ethernet/microsoft/mana/mana_ethtool.c | 68 ++++++++++++-------
1 file changed, 45 insertions(+), 23 deletions(-)
diff --git a/drivers/net/ethernet/microsoft/mana/mana_ethtool.c b/drivers/net/ethernet/microsoft/mana/mana_ethtool.c
index 45031ca1254e327a9b129bd77c7a08fb8a240838..96f355de9779cc865a896f4834f780a158cdc058 100644
--- a/drivers/net/ethernet/microsoft/mana/mana_ethtool.c
+++ b/drivers/net/ethernet/microsoft/mana/mana_ethtool.c
@@ -750,13 +750,11 @@ static int mana_set_ringparam(struct net_device *ndev,
struct netlink_ext_ack *extack)
{
struct mana_port_context *apc = netdev_priv(ndev);
+ struct mana_port_context *scratch;
+ struct mana_qset newq, oldq;
u32 new_tx, new_rx;
- u32 old_tx, old_rx;
int err;
- old_tx = apc->tx_queue_size;
- old_rx = apc->rx_queue_size;
-
if (ring->tx_pending < MIN_TX_BUFFERS_PER_QUEUE) {
NL_SET_ERR_MSG_FMT(extack, "tx:%d less than the min:%d", ring->tx_pending,
MIN_TX_BUFFERS_PER_QUEUE);
@@ -774,32 +772,56 @@ static int mana_set_ringparam(struct net_device *ndev,
netdev_info(ndev, "Using nearest power of 2 values for Txq:%d Rxq:%d\n",
new_tx, new_rx);
- /* pre-allocating new buffers to prevent failures in mana_attach() later */
- apc->rx_queue_size = new_rx;
- err = mana_pre_alloc_rxbufs(apc, ndev->mtu, apc->num_queues);
- apc->rx_queue_size = old_rx;
- if (err) {
- netdev_err(ndev, "Insufficient memory for new allocations\n");
- return err;
+ if (new_rx == apc->rx_queue_size && new_tx == apc->tx_queue_size)
+ return 0;
+
+ if (!apc->port_is_up) {
+ apc->rx_queue_size = new_rx;
+ apc->tx_queue_size = new_tx;
+ return 0;
}
- err = mana_detach(ndev, false);
- if (err) {
- netdev_err(ndev, "mana_detach failed: %d\n", err);
- goto out;
+ /* Exclude RDMA through failure cleanup, which may release the vport. */
+ mutex_lock(&apc->vport_mutex);
+ if (apc->channel_changing) {
+ mutex_unlock(&apc->vport_mutex);
+ return -EBUSY;
+ }
+ apc->channel_changing = true;
+ mutex_unlock(&apc->vport_mutex);
+
+ scratch = mana_qset_scratch_alloc(apc);
+ if (!scratch) {
+ err = -ENOMEM;
+ goto clear_flag;
}
- apc->tx_queue_size = new_tx;
- apc->rx_queue_size = new_rx;
+ err = mana_alloc_qset(apc, scratch, apc->num_queues, new_rx, new_tx,
+ apc->priv_flags, &newq);
+ if (err) {
+ NL_SET_ERR_MSG_FMT(extack, "failed to change ring params: %d",
+ err);
+ goto free_scratch;
+ }
- err = mana_attach(ndev);
+ err = mana_publish_qset(apc, &newq, &oldq);
if (err) {
- netdev_err(ndev, "mana_attach failed: %d\n", err);
- apc->tx_queue_size = old_tx;
- apc->rx_queue_size = old_rx;
+ NL_SET_ERR_MSG_FMT(extack, "failed to change ring params: %d",
+ err);
+ mana_free_qset(scratch, &newq);
+ goto free_scratch;
}
-out:
- mana_pre_dealloc_rxbufs(apc);
+
+ mana_free_qset(scratch, &oldq);
+
+free_scratch:
+ /* Release unpublished queues before closing their shared EQ pool. */
+ mana_publish_close_if_needed(apc);
+ mana_qset_scratch_free(scratch);
+clear_flag:
+ mutex_lock(&apc->vport_mutex);
+ apc->channel_changing = false;
+ mutex_unlock(&apc->vport_mutex);
return err;
}
--
2.43.0
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH net-next v4 05/13] net: mana: swap queue sets in mana_set_priv_flags
2026-09-08 3:28 [PATCH net-next v4 00/13] net: mana: reconfigure by replacing the queue set Long Li
` (3 preceding siblings ...)
2026-09-08 3:28 ` [PATCH net-next v4 04/13] net: mana: swap queue sets in mana_set_ringparam Long Li
@ 2026-09-08 3:28 ` Long Li
2026-09-09 3:29 ` sashiko-bot
2026-09-08 3:28 ` [PATCH net-next v4 06/13] net: mana: swap queue sets in mana_change_mtu Long Li
` (7 subsequent siblings)
12 siblings, 1 reply; 28+ messages in thread
From: Long Li @ 2026-09-08 3:28 UTC (permalink / raw)
To: Long Li, Long Li, Konstantin Taranov, Jakub Kicinski,
David S . Miller, Paolo Abeni, Eric Dumazet, Andrew Lunn,
Jason Gunthorpe, Leon Romanovsky, Haiyang Zhang,
K . Y . Srinivasan, Wei Liu, Dexuan Cui, shradhagupta,
Simon Horman, ernis, stephen, shirazsaleem
Cc: netdev, linux-rdma, linux-hyperv, linux-kernel
Rebuild queues through the swap path when USE_FULL_PAGE_RXBUF changes
the RX layout. Carry priv_flags with the queue set so allocation failure
leaves the live configuration unchanged and rollback restores the flags.
Retain the shortcuts for a down port or a configuration that already
requires full-page RX. A failed rollback closes the port.
Signed-off-by: Long Li <longli@microsoft.com>
---
Changes in v4:
- Distinguish allocation failure from failed rollback; drop the
incorrect claim about removing queue_reset_work scheduling.
- Shorten comments and the commit message.
.../ethernet/microsoft/mana/mana_ethtool.c | 72 +++++++++----------
1 file changed, 34 insertions(+), 38 deletions(-)
diff --git a/drivers/net/ethernet/microsoft/mana/mana_ethtool.c b/drivers/net/ethernet/microsoft/mana/mana_ethtool.c
index 96f355de9779cc865a896f4834f780a158cdc058..adc65b9146c7e74703db587a619678439ff0d84b 100644
--- a/drivers/net/ethernet/microsoft/mana/mana_ethtool.c
+++ b/drivers/net/ethernet/microsoft/mana/mana_ethtool.c
@@ -851,7 +851,8 @@ static int mana_set_priv_flags(struct net_device *ndev, u32 priv_flags)
{
struct mana_port_context *apc = netdev_priv(ndev);
u32 changed = apc->priv_flags ^ priv_flags;
- u32 old_priv_flags = apc->priv_flags;
+ struct mana_port_context *scratch;
+ struct mana_qset newq, oldq;
int err = 0;
if (!changed)
@@ -861,54 +862,49 @@ static int mana_set_priv_flags(struct net_device *ndev, u32 priv_flags)
if (priv_flags & ~GENMASK(MANA_PRIV_FLAG_MAX - 1, 0))
return -EINVAL;
- apc->priv_flags = priv_flags;
-
- if (changed & BIT(MANA_PRIV_FLAG_USE_FULL_PAGE_RXBUF)) {
- if (!apc->port_is_up)
- return 0;
-
- /* If XDP is attached or MTU is jumbo, single-buffer-per-page
- * is already forced regardless of this flag. Skip the
- * expensive detach/attach cycle since nothing changes.
- */
- if (ndev->mtu + MANA_RXBUF_PAD > PAGE_SIZE / 2 ||
- mana_xdp_get(apc))
- return 0;
+ /* Skip rebuilding when full-page RX is already required. */
+ if (!(changed & BIT(MANA_PRIV_FLAG_USE_FULL_PAGE_RXBUF)) ||
+ !apc->port_is_up ||
+ ndev->mtu + MANA_RXBUF_PAD > PAGE_SIZE / 2 ||
+ mana_xdp_get(apc)) {
+ apc->priv_flags = priv_flags;
+ return 0;
+ }
- /* Block RDMA from grabbing the vport during detach/attach */
- mutex_lock(&apc->vport_mutex);
- apc->channel_changing = true;
+ mutex_lock(&apc->vport_mutex);
+ if (apc->channel_changing) {
mutex_unlock(&apc->vport_mutex);
+ return -EBUSY;
+ }
+ apc->channel_changing = true;
+ mutex_unlock(&apc->vport_mutex);
- err = mana_pre_alloc_rxbufs(apc, ndev->mtu, apc->num_queues);
- if (err) {
- netdev_err(ndev,
- "Insufficient memory for new allocations\n");
- apc->priv_flags = old_priv_flags;
- goto clear_flag;
- }
+ scratch = mana_qset_scratch_alloc(apc);
+ if (!scratch) {
+ err = -ENOMEM;
+ goto clear_flag;
+ }
- err = mana_detach(ndev, false);
- if (err) {
- netdev_err(ndev, "mana_detach failed: %d\n", err);
- apc->priv_flags = old_priv_flags;
- goto out;
- }
+ err = mana_alloc_qset(apc, scratch, apc->num_queues, apc->rx_queue_size,
+ apc->tx_queue_size, priv_flags, &newq);
+ if (err)
+ goto free_scratch;
- err = mana_attach(ndev);
- if (err) {
- netdev_err(ndev, "mana_attach failed: %d\n", err);
- apc->priv_flags = old_priv_flags;
- }
+ err = mana_publish_qset(apc, &newq, &oldq);
+ if (err) {
+ mana_free_qset(scratch, &newq);
+ goto free_scratch;
}
-out:
- mana_pre_dealloc_rxbufs(apc);
+ mana_free_qset(scratch, &oldq);
+
+free_scratch:
+ mana_publish_close_if_needed(apc);
+ mana_qset_scratch_free(scratch);
clear_flag:
mutex_lock(&apc->vport_mutex);
apc->channel_changing = false;
mutex_unlock(&apc->vport_mutex);
-
return err;
}
--
2.43.0
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH net-next v4 06/13] net: mana: swap queue sets in mana_change_mtu
2026-09-08 3:28 [PATCH net-next v4 00/13] net: mana: reconfigure by replacing the queue set Long Li
` (4 preceding siblings ...)
2026-09-08 3:28 ` [PATCH net-next v4 05/13] net: mana: swap queue sets in mana_set_priv_flags Long Li
@ 2026-09-08 3:28 ` Long Li
2026-09-09 3:29 ` sashiko-bot
2026-09-08 3:28 ` [PATCH net-next v4 07/13] net: mana: swap queue sets in mana_xdp_set Long Li
` (6 subsequent siblings)
12 siblings, 1 reply; 28+ messages in thread
From: Long Li @ 2026-09-08 3:28 UTC (permalink / raw)
To: Long Li, Long Li, Konstantin Taranov, Jakub Kicinski,
David S . Miller, Paolo Abeni, Eric Dumazet, Andrew Lunn,
Jason Gunthorpe, Leon Romanovsky, Haiyang Zhang,
K . Y . Srinivasan, Wei Liu, Dexuan Cui, shradhagupta,
Simon Horman, ernis, stephen, shirazsaleem
Cc: netdev, linux-rdma, linux-hyperv, linux-kernel
Carry the MTU in the queue set and size replacement RX buffers from it.
Publish ndev->mtu after RSS configuration succeeds; allocation failure
leaves the live queues and advertised MTU unchanged.
This still rebuilds all queues, requiring both sets' SQ/RQ/CQ objects
and RX buffers temporarily. Resource limits can therefore reject an MTU
change that detach/attach previously accepted.
Signed-off-by: Long Li <longli@microsoft.com>
---
Changes in v4:
- Document the two-set resource peak and possible allocation refusal
at high queue counts; shorten comments.
drivers/net/ethernet/microsoft/mana/mana_en.c | 53 ++++++++++++-------
.../ethernet/microsoft/mana/mana_ethtool.c | 10 ++--
include/net/mana/mana.h | 7 ++-
3 files changed, 46 insertions(+), 24 deletions(-)
diff --git a/drivers/net/ethernet/microsoft/mana/mana_en.c b/drivers/net/ethernet/microsoft/mana/mana_en.c
index bb9ef4e634a6edaf097bc85f2248c2ceea88fc05..7f7833f3e1aad43d250b15ddb35f59907d01929e 100644
--- a/drivers/net/ethernet/microsoft/mana/mana_en.c
+++ b/drivers/net/ethernet/microsoft/mana/mana_en.c
@@ -902,32 +902,37 @@ int mana_pre_alloc_rxbufs(struct mana_port_context *mpc, int new_mtu, int num_qu
static int mana_change_mtu(struct net_device *ndev, int new_mtu)
{
struct mana_port_context *mpc = netdev_priv(ndev);
- unsigned int old_mtu = ndev->mtu;
+ struct mana_port_context *scratch;
+ struct mana_qset newq, oldq;
int err;
- /* Pre-allocate buffers to prevent failure in mana_attach later */
- err = mana_pre_alloc_rxbufs(mpc, new_mtu, mpc->num_queues);
- if (err) {
- netdev_err(ndev, "Insufficient memory for new MTU\n");
- return err;
+ if (!mpc->port_is_up) {
+ mpc->configured_mtu = new_mtu;
+ WRITE_ONCE(ndev->mtu, new_mtu);
+ return 0;
}
- err = mana_detach(ndev, false);
- if (err) {
- netdev_err(ndev, "mana_detach failed: %d\n", err);
- goto out;
- }
+ scratch = mana_qset_scratch_alloc(mpc);
+ if (!scratch)
+ return -ENOMEM;
- WRITE_ONCE(ndev->mtu, new_mtu);
+ err = mana_alloc_qset(mpc, scratch, mpc->num_queues,
+ mpc->rx_queue_size, mpc->tx_queue_size,
+ mpc->priv_flags, new_mtu, &newq);
+ if (err)
+ goto free_scratch;
- err = mana_attach(ndev);
+ err = mana_publish_qset(mpc, &newq, &oldq);
if (err) {
- netdev_err(ndev, "mana_attach failed: %d\n", err);
- WRITE_ONCE(ndev->mtu, old_mtu);
+ mana_free_qset(scratch, &newq);
+ goto free_scratch;
}
-out:
- mana_pre_dealloc_rxbufs(mpc);
+ mana_free_qset(scratch, &oldq);
+
+free_scratch:
+ mana_publish_close_if_needed(mpc);
+ mana_qset_scratch_free(scratch);
return err;
}
@@ -3153,7 +3158,8 @@ static struct mana_rxq *mana_create_rxq(struct mana_port_context *apc,
rxq->rxq_idx = rxq_idx;
rxq->rxobj = INVALID_MANA_HANDLE;
- mana_get_rxbuf_cfg(apc, ndev->mtu, &rxq->datasize, &rxq->alloc_size,
+ mana_get_rxbuf_cfg(apc, apc->configured_mtu, &rxq->datasize,
+ &rxq->alloc_size,
&rxq->headroom, &rxq->frag_count);
/* Create page pool for RX queue */
err = mana_create_page_pool(rxq, gc);
@@ -3909,6 +3915,7 @@ static void mana_qset_snapshot(const struct mana_port_context *ctx,
out->rx_queue_size = ctx->rx_queue_size;
out->tx_queue_size = ctx->tx_queue_size;
out->priv_flags = ctx->priv_flags;
+ out->mtu = ctx->configured_mtu;
}
/* Vport identity and port debugfs outlive queue sets. */
@@ -3925,6 +3932,7 @@ static void mana_qset_install(struct mana_port_context *ctx,
ctx->rx_queue_size = qset->rx_queue_size;
ctx->tx_queue_size = qset->tx_queue_size;
ctx->priv_flags = qset->priv_flags;
+ ctx->configured_mtu = qset->mtu;
}
/* Scratch starts without SQs/RQs and borrows the port's EQ pool. Never call
@@ -3965,7 +3973,7 @@ void mana_qset_scratch_free(struct mana_port_context *scratch)
int mana_alloc_qset(struct mana_port_context *apc,
struct mana_port_context *scratch, unsigned int num_queues,
unsigned int rx_queue_size, unsigned int tx_queue_size,
- u32 priv_flags, struct mana_qset *out)
+ u32 priv_flags, int mtu, struct mana_qset *out)
{
struct net_device *ndev = scratch->ndev;
int err;
@@ -3977,6 +3985,8 @@ int mana_alloc_qset(struct mana_port_context *apc,
scratch->tx_queue_size = tx_queue_size;
scratch->priv_flags = priv_flags;
+ scratch->configured_mtu = mtu;
+
err = mana_init_port_context(scratch);
if (err)
goto out_err;
@@ -4153,6 +4163,8 @@ int mana_publish_qset(struct mana_port_context *apc, struct mana_qset *newq,
if (err)
goto rollback;
+ WRITE_ONCE(ndev->mtu, apc->configured_mtu);
+
/* Publish fields before opening the gate; pair with TX/XDP read
* barriers. The post-gate full barrier cannot replace this.
*/
@@ -4192,6 +4204,8 @@ int mana_publish_qset(struct mana_port_context *apc, struct mana_qset *newq,
return err;
}
+ WRITE_ONCE(ndev->mtu, apc->configured_mtu);
+
/* Publish restored fields before reopening the gate, as on success. */
smp_wmb();
@@ -4362,6 +4376,7 @@ static int mana_probe_port(struct mana_context *ac, int port_idx,
apc->port_handle = INVALID_MANA_HANDLE;
apc->pf_filter_handle = INVALID_MANA_HANDLE;
apc->port_idx = port_idx;
+ apc->configured_mtu = ndev->mtu;
apc->link_cfg_error = 1;
apc->cqe_coalescing_enable = 0;
apc->cqe8_coalescing_enable = 0;
diff --git a/drivers/net/ethernet/microsoft/mana/mana_ethtool.c b/drivers/net/ethernet/microsoft/mana/mana_ethtool.c
index adc65b9146c7e74703db587a619678439ff0d84b..c03944f95cf84dcbaa866e92f6c7d67100215b7b 100644
--- a/drivers/net/ethernet/microsoft/mana/mana_ethtool.c
+++ b/drivers/net/ethernet/microsoft/mana/mana_ethtool.c
@@ -708,7 +708,8 @@ static int mana_set_channels(struct net_device *ndev,
}
err = mana_alloc_qset(apc, scratch, new_count, apc->rx_queue_size,
- apc->tx_queue_size, apc->priv_flags, &newq);
+ apc->tx_queue_size, apc->priv_flags,
+ apc->configured_mtu, &newq);
if (err)
goto free_scratch;
@@ -797,7 +798,7 @@ static int mana_set_ringparam(struct net_device *ndev,
}
err = mana_alloc_qset(apc, scratch, apc->num_queues, new_rx, new_tx,
- apc->priv_flags, &newq);
+ apc->priv_flags, apc->configured_mtu, &newq);
if (err) {
NL_SET_ERR_MSG_FMT(extack, "failed to change ring params: %d",
err);
@@ -885,8 +886,9 @@ static int mana_set_priv_flags(struct net_device *ndev, u32 priv_flags)
goto clear_flag;
}
- err = mana_alloc_qset(apc, scratch, apc->num_queues, apc->rx_queue_size,
- apc->tx_queue_size, priv_flags, &newq);
+ err = mana_alloc_qset(apc, scratch, apc->num_queues,
+ apc->rx_queue_size, apc->tx_queue_size,
+ priv_flags, apc->configured_mtu, &newq);
if (err)
goto free_scratch;
diff --git a/include/net/mana/mana.h b/include/net/mana/mana.h
index e965d86b4d8502408f175bdbee5d77fb2e406df3..02d60a6b7be423ae55a93fa21e53717e40918eb7 100644
--- a/include/net/mana/mana.h
+++ b/include/net/mana/mana.h
@@ -616,6 +616,10 @@ struct mana_port_context {
unsigned int rx_queue_size;
unsigned int tx_queue_size;
+ /* MTU used to size RX buffers, independent of ndev->mtu during a swap.
+ */
+ int configured_mtu;
+
mana_handle_t port_handle;
mana_handle_t pf_filter_handle;
@@ -693,6 +697,7 @@ struct mana_qset {
unsigned int tx_queue_size;
u32 priv_flags;
+ int mtu;
};
netdev_tx_t mana_start_xmit(struct sk_buff *skb, struct net_device *ndev);
@@ -710,7 +715,7 @@ void mana_qset_scratch_free(struct mana_port_context *scratch);
int mana_alloc_qset(struct mana_port_context *apc,
struct mana_port_context *scratch, unsigned int num_queues,
unsigned int rx_queue_size, unsigned int tx_queue_size,
- u32 priv_flags, struct mana_qset *out);
+ u32 priv_flags, int mtu, struct mana_qset *out);
int mana_publish_qset(struct mana_port_context *apc, struct mana_qset *newq,
struct mana_qset *out_old);
void mana_publish_close_if_needed(struct mana_port_context *apc);
--
2.43.0
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH net-next v4 07/13] net: mana: swap queue sets in mana_xdp_set
2026-09-08 3:28 [PATCH net-next v4 00/13] net: mana: reconfigure by replacing the queue set Long Li
` (5 preceding siblings ...)
2026-09-08 3:28 ` [PATCH net-next v4 06/13] net: mana: swap queue sets in mana_change_mtu Long Li
@ 2026-09-08 3:28 ` Long Li
2026-09-09 3:29 ` sashiko-bot
2026-09-08 3:28 ` [PATCH net-next v4 08/13] net: mana: do not bail out of mana_detach on dealloc failure Long Li
` (5 subsequent siblings)
12 siblings, 1 reply; 28+ messages in thread
From: Long Li @ 2026-09-08 3:28 UTC (permalink / raw)
To: Long Li, Long Li, Konstantin Taranov, Jakub Kicinski,
David S . Miller, Paolo Abeni, Eric Dumazet, Andrew Lunn,
Jason Gunthorpe, Leon Romanovsky, Haiyang Zhang,
K . Y . Srinivasan, Wei Liu, Dexuan Cui, shradhagupta,
Simon Horman, ernis, stephen, shirazsaleem
Cc: netdev, linux-rdma, linux-hyperv, linux-kernel
Carry the XDP program with the queue set and install its per-queue
references before redirecting RSS. This keeps the RX buffer layout and
program together during publication and rollback.
Do not replace the live program during allocation. This also avoids the
pre-existing failed-preallocation stale-pointer bug; its standalone net
fix is linked below.
Link: https://lore.kernel.org/all/20260904202640.3900685-1-longli@microsoft.com/
Signed-off-by: Long Li <longli@microsoft.com>
---
Changes in v4:
- Reference the separately submitted net fix and shorten the XDP
ownership explanation.
Merge note:
This overlaps the net submission:
https://lore.kernel.org/all/20260904202640.3900685-1-longli@microsoft.com/
Retain this patch's queue-set version of mana_xdp_set() when resolving
the overlapping block. It does not assign the live program before
allocation and removes the failure path where the net fix restores
apc->bpf_prog.
.../net/ethernet/microsoft/mana/mana_bpf.c | 61 +++++++++----------
drivers/net/ethernet/microsoft/mana/mana_en.c | 12 ++--
.../ethernet/microsoft/mana/mana_ethtool.c | 11 ++--
include/net/mana/mana.h | 5 +-
4 files changed, 46 insertions(+), 43 deletions(-)
diff --git a/drivers/net/ethernet/microsoft/mana/mana_bpf.c b/drivers/net/ethernet/microsoft/mana/mana_bpf.c
index 4365e26cc73874a727ea3cae93ec959956d2d42a..2dc2624128a9141012ab59b723dbfc9617ae6237 100644
--- a/drivers/net/ethernet/microsoft/mana/mana_bpf.c
+++ b/drivers/net/ethernet/microsoft/mana/mana_bpf.c
@@ -177,6 +177,8 @@ static int mana_xdp_set(struct net_device *ndev, struct bpf_prog *prog,
struct netlink_ext_ack *extack)
{
struct mana_port_context *apc = netdev_priv(ndev);
+ struct mana_port_context *scratch;
+ struct mana_qset newq, oldq;
struct bpf_prog *old_prog;
struct gdma_context *gc;
int err;
@@ -196,46 +198,44 @@ static int mana_xdp_set(struct net_device *ndev, struct bpf_prog *prog,
return -EOPNOTSUPP;
}
- /* One refcnt of the prog is hold by the caller already, so
- * don't increase refcnt for this one.
- */
- apc->bpf_prog = prog;
-
if (apc->port_is_up) {
- /* Re-create rxq's after xdp prog was loaded or unloaded.
- * Ex: re create rxq's to switch from full pages to smaller
- * size page fragments when xdp prog is unloaded and
- * vice-versa.
- */
-
- /* Pre-allocate buffers to prevent failure in mana_attach */
- err = mana_pre_alloc_rxbufs(apc, ndev->mtu, apc->num_queues);
- if (err) {
+ scratch = mana_qset_scratch_alloc(apc);
+ if (!scratch) {
NL_SET_ERR_MSG_MOD(extack,
- "XDP: Insufficient memory for tx/rx re-config");
- return err;
+ "XDP: Insufficient memory for re-config");
+ return -ENOMEM;
}
- err = mana_detach(ndev, false);
+ err = mana_alloc_qset(apc, scratch, apc->num_queues,
+ apc->rx_queue_size, apc->tx_queue_size,
+ apc->priv_flags, apc->configured_mtu,
+ prog, &newq);
if (err) {
- netdev_err(ndev,
- "mana_detach failed at xdp set: %d\n", err);
NL_SET_ERR_MSG_MOD(extack,
- "XDP: Re-config failed at detach");
- goto err_dealloc_rxbuffs;
+ "XDP: Re-config failed at alloc");
+ mana_qset_scratch_free(scratch);
+ return err;
}
- err = mana_attach(ndev);
+ err = mana_publish_qset(apc, &newq, &oldq);
if (err) {
- netdev_err(ndev,
- "mana_attach failed at xdp set: %d\n", err);
NL_SET_ERR_MSG_MOD(extack,
- "XDP: Re-config failed at attach");
- goto err_dealloc_rxbuffs;
+ "XDP: Re-config failed at publish");
+ mana_free_qset(scratch, &newq);
+ /* Free the queues before closing their shared EQ pool.
+ */
+ mana_publish_close_if_needed(apc);
+ mana_qset_scratch_free(scratch);
+ return err;
}
- mana_chn_setxdp(apc, prog);
- mana_pre_dealloc_rxbufs(apc);
+ mana_free_qset(scratch, &oldq);
+ mana_qset_scratch_free(scratch);
+ } else {
+ /* Use the caller's program reference; mana_open() installs it
+ * on queues.
+ */
+ apc->bpf_prog = prog;
}
if (old_prog)
@@ -248,11 +248,6 @@ static int mana_xdp_set(struct net_device *ndev, struct bpf_prog *prog,
ndev->max_mtu = gc->adapter_mtu - ETH_HLEN;
return 0;
-
-err_dealloc_rxbuffs:
- apc->bpf_prog = old_prog;
- mana_pre_dealloc_rxbufs(apc);
- return err;
}
int mana_bpf(struct net_device *ndev, struct netdev_bpf *bpf)
diff --git a/drivers/net/ethernet/microsoft/mana/mana_en.c b/drivers/net/ethernet/microsoft/mana/mana_en.c
index 7f7833f3e1aad43d250b15ddb35f59907d01929e..62c11af2fc422206f0ab8e144a4d082d62f7737d 100644
--- a/drivers/net/ethernet/microsoft/mana/mana_en.c
+++ b/drivers/net/ethernet/microsoft/mana/mana_en.c
@@ -916,9 +916,9 @@ static int mana_change_mtu(struct net_device *ndev, int new_mtu)
if (!scratch)
return -ENOMEM;
- err = mana_alloc_qset(mpc, scratch, mpc->num_queues,
- mpc->rx_queue_size, mpc->tx_queue_size,
- mpc->priv_flags, new_mtu, &newq);
+ err = mana_alloc_qset(mpc, scratch, mpc->num_queues, mpc->rx_queue_size,
+ mpc->tx_queue_size, mpc->priv_flags, new_mtu,
+ mpc->bpf_prog, &newq);
if (err)
goto free_scratch;
@@ -3916,6 +3916,7 @@ static void mana_qset_snapshot(const struct mana_port_context *ctx,
out->tx_queue_size = ctx->tx_queue_size;
out->priv_flags = ctx->priv_flags;
out->mtu = ctx->configured_mtu;
+ out->bpf_prog = ctx->bpf_prog;
}
/* Vport identity and port debugfs outlive queue sets. */
@@ -3933,6 +3934,7 @@ static void mana_qset_install(struct mana_port_context *ctx,
ctx->tx_queue_size = qset->tx_queue_size;
ctx->priv_flags = qset->priv_flags;
ctx->configured_mtu = qset->mtu;
+ ctx->bpf_prog = qset->bpf_prog;
}
/* Scratch starts without SQs/RQs and borrows the port's EQ pool. Never call
@@ -3973,7 +3975,8 @@ void mana_qset_scratch_free(struct mana_port_context *scratch)
int mana_alloc_qset(struct mana_port_context *apc,
struct mana_port_context *scratch, unsigned int num_queues,
unsigned int rx_queue_size, unsigned int tx_queue_size,
- u32 priv_flags, int mtu, struct mana_qset *out)
+ u32 priv_flags, int mtu, struct bpf_prog *bpf_prog,
+ struct mana_qset *out)
{
struct net_device *ndev = scratch->ndev;
int err;
@@ -3986,6 +3989,7 @@ int mana_alloc_qset(struct mana_port_context *apc,
scratch->priv_flags = priv_flags;
scratch->configured_mtu = mtu;
+ scratch->bpf_prog = bpf_prog;
err = mana_init_port_context(scratch);
if (err)
diff --git a/drivers/net/ethernet/microsoft/mana/mana_ethtool.c b/drivers/net/ethernet/microsoft/mana/mana_ethtool.c
index c03944f95cf84dcbaa866e92f6c7d67100215b7b..32f40f5cd850e6c67ea0dd57cc45a51b37c43b93 100644
--- a/drivers/net/ethernet/microsoft/mana/mana_ethtool.c
+++ b/drivers/net/ethernet/microsoft/mana/mana_ethtool.c
@@ -709,7 +709,7 @@ static int mana_set_channels(struct net_device *ndev,
err = mana_alloc_qset(apc, scratch, new_count, apc->rx_queue_size,
apc->tx_queue_size, apc->priv_flags,
- apc->configured_mtu, &newq);
+ apc->configured_mtu, apc->bpf_prog, &newq);
if (err)
goto free_scratch;
@@ -798,7 +798,8 @@ static int mana_set_ringparam(struct net_device *ndev,
}
err = mana_alloc_qset(apc, scratch, apc->num_queues, new_rx, new_tx,
- apc->priv_flags, apc->configured_mtu, &newq);
+ apc->priv_flags, apc->configured_mtu,
+ apc->bpf_prog, &newq);
if (err) {
NL_SET_ERR_MSG_FMT(extack, "failed to change ring params: %d",
err);
@@ -886,9 +887,9 @@ static int mana_set_priv_flags(struct net_device *ndev, u32 priv_flags)
goto clear_flag;
}
- err = mana_alloc_qset(apc, scratch, apc->num_queues,
- apc->rx_queue_size, apc->tx_queue_size,
- priv_flags, apc->configured_mtu, &newq);
+ err = mana_alloc_qset(apc, scratch, apc->num_queues, apc->rx_queue_size,
+ apc->tx_queue_size, priv_flags,
+ apc->configured_mtu, apc->bpf_prog, &newq);
if (err)
goto free_scratch;
diff --git a/include/net/mana/mana.h b/include/net/mana/mana.h
index 02d60a6b7be423ae55a93fa21e53717e40918eb7..9b8c038042b4f90b0b5958fc33cdad36e169863c 100644
--- a/include/net/mana/mana.h
+++ b/include/net/mana/mana.h
@@ -698,6 +698,8 @@ struct mana_qset {
u32 priv_flags;
int mtu;
+ struct bpf_prog *bpf_prog;
+
};
netdev_tx_t mana_start_xmit(struct sk_buff *skb, struct net_device *ndev);
@@ -715,7 +717,8 @@ void mana_qset_scratch_free(struct mana_port_context *scratch);
int mana_alloc_qset(struct mana_port_context *apc,
struct mana_port_context *scratch, unsigned int num_queues,
unsigned int rx_queue_size, unsigned int tx_queue_size,
- u32 priv_flags, int mtu, struct mana_qset *out);
+ u32 priv_flags, int mtu, struct bpf_prog *bpf_prog,
+ struct mana_qset *out);
int mana_publish_qset(struct mana_port_context *apc, struct mana_qset *newq,
struct mana_qset *out_old);
void mana_publish_close_if_needed(struct mana_port_context *apc);
--
2.43.0
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH net-next v4 08/13] net: mana: do not bail out of mana_detach on dealloc failure
2026-09-08 3:28 [PATCH net-next v4 00/13] net: mana: reconfigure by replacing the queue set Long Li
` (6 preceding siblings ...)
2026-09-08 3:28 ` [PATCH net-next v4 07/13] net: mana: swap queue sets in mana_xdp_set Long Li
@ 2026-09-08 3:28 ` Long Li
2026-09-09 3:29 ` sashiko-bot
2026-09-08 3:28 ` [PATCH net-next v4 09/13] net: mana: keep per-queue statistics in the port context Long Li
` (4 subsequent siblings)
12 siblings, 1 reply; 28+ messages in thread
From: Long Li @ 2026-09-08 3:28 UTC (permalink / raw)
To: Long Li, Long Li, Konstantin Taranov, Jakub Kicinski,
David S . Miller, Paolo Abeni, Eric Dumazet, Andrew Lunn,
Jason Gunthorpe, Leon Romanovsky, Haiyang Zhang,
K . Y . Srinivasan, Wei Liu, Dexuan Cui, shradhagupta,
Simon Horman, ernis, stephen, shirazsaleem
Cc: netdev, linux-rdma, linux-hyperv, linux-kernel, Dipayaan Roy
From: Dipayaan Roy <dipayanroy@linux.microsoft.com>
Remove the early return after mana_dealloc_queues() so detach continues
its device and port-context cleanup.
The return is currently unreachable: mana_dealloc_queues() only rejects
an up port, and mana_detach() clears port_is_up before calling it. This
is a robustness cleanup, not a fix for a reachable reset failure.
Signed-off-by: Dipayaan Roy <dipayanroy@linux.microsoft.com>
Signed-off-by: Long Li <longli@microsoft.com>
---
Changes in v4:
- Frame this as removal of an unreachable error return, not a fix
for a reachable fatal reset failure.
drivers/net/ethernet/microsoft/mana/mana_en.c | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/drivers/net/ethernet/microsoft/mana/mana_en.c b/drivers/net/ethernet/microsoft/mana/mana_en.c
index 62c11af2fc422206f0ab8e144a4d082d62f7737d..5752e3745ccc6ba26bdd8f9be3108e4f3de69be0 100644
--- a/drivers/net/ethernet/microsoft/mana/mana_en.c
+++ b/drivers/net/ethernet/microsoft/mana/mana_en.c
@@ -4340,10 +4340,8 @@ int mana_detach(struct net_device *ndev, bool from_close)
if (apc->port_st_save) {
err = mana_dealloc_queues(ndev);
- if (err) {
+ if (err)
netdev_err(ndev, "%s failed to deallocate queues: %d\n", __func__, err);
- return err;
- }
}
if (!from_close) {
--
2.43.0
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH net-next v4 09/13] net: mana: keep per-queue statistics in the port context
2026-09-08 3:28 [PATCH net-next v4 00/13] net: mana: reconfigure by replacing the queue set Long Li
` (7 preceding siblings ...)
2026-09-08 3:28 ` [PATCH net-next v4 08/13] net: mana: do not bail out of mana_detach on dealloc failure Long Li
@ 2026-09-08 3:28 ` Long Li
2026-09-09 3:29 ` sashiko-bot
2026-09-08 3:28 ` [PATCH net-next v4 10/13] net: mana: release EQs left idle by a channel-count reduction Long Li
` (3 subsequent siblings)
12 siblings, 1 reply; 28+ messages in thread
From: Long Li @ 2026-09-08 3:28 UTC (permalink / raw)
To: Long Li, Long Li, Konstantin Taranov, Jakub Kicinski,
David S . Miller, Paolo Abeni, Eric Dumazet, Andrew Lunn,
Jason Gunthorpe, Leon Romanovsky, Haiyang Zhang,
K . Y . Srinivasan, Wei Liu, Dexuan Cui, shradhagupta,
Simon Horman, ernis, stephen, shirazsaleem
Cc: netdev, linux-rdma, linux-hyperv, linux-kernel
Move RX/TX counters into port-lifetime arrays so queue rebuilds do not
reset them. Sum all max_queues slots for ndo_get_stats64(); keep the
current queue count for ethtool's per-queue output.
Retiring RX queues count into private drain_stats. Destruction and
rollback fold these into separate port slots after writer quiescence.
Report counters while down too, but gate the PHY query on port_is_up.
DIM now samples the persistent live counters and skips retiring queues.
Signed-off-by: Long Li <longli@microsoft.com>
---
Changes in v4:
- Remove the port-up early returns from both statistics readers;
retain that condition only for the hardware PHY query.
- Clarify drain-stat folding and DIM input changes; shorten comments.
.../net/ethernet/microsoft/mana/mana_bpf.c | 4 +-
drivers/net/ethernet/microsoft/mana/mana_en.c | 204 +++++++++++++++---
.../ethernet/microsoft/mana/mana_ethtool.c | 48 ++++-
include/net/mana/mana.h | 30 ++-
4 files changed, 242 insertions(+), 44 deletions(-)
diff --git a/drivers/net/ethernet/microsoft/mana/mana_bpf.c b/drivers/net/ethernet/microsoft/mana/mana_bpf.c
index 2dc2624128a9141012ab59b723dbfc9617ae6237..8daeb65cc647bb4cf4391f6fadcd04793630c3b6 100644
--- a/drivers/net/ethernet/microsoft/mana/mana_bpf.c
+++ b/drivers/net/ethernet/microsoft/mana/mana_bpf.c
@@ -73,7 +73,7 @@ int mana_xdp_xmit(struct net_device *ndev, int n, struct xdp_frame **frames,
count++;
}
- tx_stats = &apc->tx_qp[q_idx]->txq.stats;
+ tx_stats = apc->tx_qp[q_idx]->txq.stats;
u64_stats_update_begin(&tx_stats->syncp);
tx_stats->xdp_xmit += count;
@@ -100,7 +100,7 @@ u32 mana_run_xdp(struct net_device *ndev, struct mana_rxq *rxq,
act = bpf_prog_run_xdp(prog, xdp);
- rx_stats = &rxq->stats;
+ rx_stats = mana_rxq_stats(rxq);
switch (act) {
case XDP_PASS:
diff --git a/drivers/net/ethernet/microsoft/mana/mana_en.c b/drivers/net/ethernet/microsoft/mana/mana_en.c
index 5752e3745ccc6ba26bdd8f9be3108e4f3de69be0..98007bb3ef7caa974849c3f4998854f15c925321 100644
--- a/drivers/net/ethernet/microsoft/mana/mana_en.c
+++ b/drivers/net/ethernet/microsoft/mana/mana_en.c
@@ -396,7 +396,7 @@ netdev_tx_t mana_start_xmit(struct sk_buff *skb, struct net_device *ndev)
txq = &apc->tx_qp[txq_idx]->txq;
gdma_sq = txq->gdma_sq;
cq = &apc->tx_qp[txq_idx]->tx_cq;
- tx_stats = &txq->stats;
+ tx_stats = txq->stats;
BUILD_BUG_ON(MAX_TX_WQE_SGL_ENTRIES != MANA_MAX_TX_WQE_SGL_ENTRIES);
if (MAX_SKB_FRAGS + 2 > MAX_TX_WQE_SGL_ENTRIES &&
@@ -575,7 +575,7 @@ netdev_tx_t mana_start_xmit(struct sk_buff *skb, struct net_device *ndev)
/* Populated the packet and bytes counters based on post GSO packet
* calculations
*/
- tx_stats = &txq->stats;
+ tx_stats = txq->stats;
u64_stats_update_begin(&tx_stats->syncp);
tx_stats->packets += num_gso_seg;
tx_stats->bytes += len + ((num_gso_seg - 1) * gso_hs);
@@ -621,15 +621,15 @@ static void mana_get_stats64(struct net_device *ndev,
struct rtnl_link_stats64 *st)
{
struct mana_port_context *apc = netdev_priv(ndev);
- unsigned int num_queues = apc->num_queues;
struct mana_stats_rx *rx_stats;
struct mana_stats_tx *tx_stats;
+ unsigned int num_queues;
unsigned int start;
u64 packets, bytes;
int q;
- if (!apc->port_is_up)
- return;
+ /* Report even while down; dev_get_stats() zeroes its output. */
+ num_queues = apc->max_queues;
netdev_stats_to_stats64(st, &ndev->stats);
@@ -639,7 +639,18 @@ static void mana_get_stats64(struct net_device *ndev,
st->rx_missed_errors = apc->ac->hc_stats.hc_rx_discards_no_wqe;
for (q = 0; q < num_queues; q++) {
- rx_stats = &apc->rxqs[q]->stats;
+ rx_stats = &apc->rxq_stats[q];
+
+ do {
+ start = u64_stats_fetch_begin(&rx_stats->syncp);
+ packets = rx_stats->packets;
+ bytes = rx_stats->bytes;
+ } while (u64_stats_fetch_retry(&rx_stats->syncp, start));
+
+ st->rx_packets += packets;
+ st->rx_bytes += bytes;
+
+ rx_stats = &apc->rxq_stats_ret[q];
do {
start = u64_stats_fetch_begin(&rx_stats->syncp);
@@ -652,7 +663,7 @@ static void mana_get_stats64(struct net_device *ndev,
}
for (q = 0; q < num_queues; q++) {
- tx_stats = &apc->tx_qp[q]->txq.stats;
+ tx_stats = &apc->txq_stats[q];
do {
start = u64_stats_fetch_begin(&tx_stats->syncp);
@@ -1065,6 +1076,105 @@ static void mana_cleanup_port_context(struct mana_port_context *apc)
apc->rxqs = NULL;
}
+/* Port lifetime preserves counters across queue replacement. */
+static int mana_alloc_queue_stats(struct mana_port_context *apc)
+{
+ unsigned int i;
+
+ apc->rxq_stats = kcalloc(apc->max_queues, sizeof(*apc->rxq_stats),
+ GFP_KERNEL);
+ if (!apc->rxq_stats)
+ return -ENOMEM;
+
+ apc->rxq_stats_ret = kcalloc(apc->max_queues,
+ sizeof(*apc->rxq_stats_ret), GFP_KERNEL);
+ if (!apc->rxq_stats_ret)
+ goto free_rxq_stats;
+
+ apc->txq_stats = kcalloc(apc->max_queues, sizeof(*apc->txq_stats),
+ GFP_KERNEL);
+ if (!apc->txq_stats)
+ goto free_rxq_stats_ret;
+
+ for (i = 0; i < apc->max_queues; i++) {
+ u64_stats_init(&apc->rxq_stats[i].syncp);
+ u64_stats_init(&apc->rxq_stats_ret[i].syncp);
+ u64_stats_init(&apc->txq_stats[i].syncp);
+ }
+
+ return 0;
+
+free_rxq_stats_ret:
+ kfree(apc->rxq_stats_ret);
+ apc->rxq_stats_ret = NULL;
+free_rxq_stats:
+ kfree(apc->rxq_stats);
+ apc->rxq_stats = NULL;
+ return -ENOMEM;
+}
+
+static void mana_free_queue_stats(struct mana_port_context *apc)
+{
+ kfree(apc->rxq_stats);
+ apc->rxq_stats = NULL;
+ kfree(apc->rxq_stats_ret);
+ apc->rxq_stats_ret = NULL;
+ kfree(apc->txq_stats);
+ apc->txq_stats = NULL;
+}
+
+/* Fold under RTNL after drain_stats writers quiesce. Clear drain_stats to
+ * prevent double counting on rollback.
+ */
+static void mana_fold_rxq_stats(struct mana_port_context *apc,
+ struct mana_rxq *rxq)
+{
+ struct mana_stats_rx *src = &rxq->drain_stats;
+ struct mana_stats_rx *dst;
+ unsigned int i;
+
+ ASSERT_RTNL();
+
+ if (!apc->rxq_stats_ret || rxq->rxq_idx >= apc->max_queues)
+ return;
+
+ dst = &apc->rxq_stats_ret[rxq->rxq_idx];
+
+ u64_stats_update_begin(&dst->syncp);
+ dst->packets += src->packets;
+ dst->bytes += src->bytes;
+ dst->xdp_drop += src->xdp_drop;
+ dst->xdp_tx += src->xdp_tx;
+ dst->xdp_redirect += src->xdp_redirect;
+ dst->pkt_len0_err += src->pkt_len0_err;
+ for (i = 0; i < ARRAY_SIZE(dst->coalesced_cqe); i++)
+ dst->coalesced_cqe[i] += src->coalesced_cqe[i];
+ u64_stats_update_end(&dst->syncp);
+
+ src->packets = 0;
+ src->bytes = 0;
+ src->xdp_drop = 0;
+ src->xdp_tx = 0;
+ src->xdp_redirect = 0;
+ src->pkt_len0_err = 0;
+ for (i = 0; i < ARRAY_SIZE(src->coalesced_cqe); i++)
+ src->coalesced_cqe[i] = 0;
+}
+
+static void mana_fold_qset_rx_stats(struct mana_port_context *apc,
+ struct mana_qset *qset)
+{
+ unsigned int q;
+
+ if (!qset->rxqs)
+ return;
+
+ for (q = 0; q < qset->num_queues; q++) {
+ if (qset->rxqs[q])
+ mana_fold_rxq_stats(apc, qset->rxqs[q]);
+ }
+}
+
static void mana_cleanup_indir_table(struct mana_port_context *apc)
{
apc->indir_table_sz = 0;
@@ -2172,7 +2282,7 @@ static void mana_rx_skb(void *buf_va, bool from_pool,
struct mana_rxcomp_oob *cqe, struct mana_rxq *rxq,
u32 pkt_len, u32 pkt_hash)
{
- struct mana_stats_rx *rx_stats = &rxq->stats;
+ struct mana_stats_rx *rx_stats = mana_rxq_stats(rxq);
struct net_device *ndev = rxq->ndev;
u16 rxq_idx = rxq->rxq_idx;
struct napi_struct *napi;
@@ -2405,6 +2515,7 @@ static void mana_process_rx_cqe(struct mana_rxq *rxq, struct mana_cq *cq,
struct net_device *ndev = rxq->ndev;
struct mana_recv_buf_oob *rxbuf_oob;
struct mana_port_context *apc;
+ struct mana_stats_rx *rx_stats;
struct device *dev = gc->dev;
bool coalesced_8 = false;
bool coalesced = false;
@@ -2486,13 +2597,15 @@ static void mana_process_rx_cqe(struct mana_rxq *rxq, struct mana_cq *cq,
* Coalesced CQEs have at least 2 packets, so index is pkt_i - 2.
*/
if (pkt_i > 1) {
- u64_stats_update_begin(&rxq->stats.syncp);
- rxq->stats.coalesced_cqe[pkt_i - 2]++;
- u64_stats_update_end(&rxq->stats.syncp);
+ rx_stats = mana_rxq_stats(rxq);
+ u64_stats_update_begin(&rx_stats->syncp);
+ rx_stats->coalesced_cqe[pkt_i - 2]++;
+ u64_stats_update_end(&rx_stats->syncp);
} else if (!pkt_i && !pktlen) {
- u64_stats_update_begin(&rxq->stats.syncp);
- rxq->stats.pkt_len0_err++;
- u64_stats_update_end(&rxq->stats.syncp);
+ rx_stats = mana_rxq_stats(rxq);
+ u64_stats_update_begin(&rx_stats->syncp);
+ rx_stats->pkt_len0_err++;
+ u64_stats_update_end(&rx_stats->syncp);
netdev_err_once(ndev,
"RX pkt len=0, rq=%u, cq=%u, rxobj=0x%llx\n",
rxq->gdma_id, cq->gdma_id, rxq->rxobj);
@@ -2624,8 +2737,12 @@ static void mana_update_rx_dim(struct mana_cq *cq)
if (!smp_load_acquire(&apc->rx_dim_enabled))
return;
- dim_update_sample(READ_ONCE(cq->dim_event_ctr), rxq->stats.packets,
- rxq->stats.bytes, &dim_sample);
+ /* Skip retiring RXQs; DIM reads shared per-index counters. */
+ if (READ_ONCE(rxq->retiring))
+ return;
+
+ dim_update_sample(READ_ONCE(cq->dim_event_ctr), rxq->stats->packets,
+ rxq->stats->bytes, &dim_sample);
net_dim(&cq->dim, &dim_sample);
}
@@ -2842,7 +2959,7 @@ static int mana_create_txq(struct mana_port_context *apc,
/* Create SQ */
txq = &apc->tx_qp[i]->txq;
- u64_stats_init(&txq->stats.syncp);
+ txq->stats = &apc->txq_stats[i];
txq->ndev = net;
txq->net_txq = netdev_get_tx_queue(net, i);
txq->reset_gen = READ_ONCE(apc->ac->reset_gen);
@@ -2969,6 +3086,9 @@ static void mana_destroy_rxq(struct mana_port_context *apc,
netif_napi_del_locked(napi);
}
+ /* NAPI is quiesced, so drain_stats has no remaining writer. */
+ mana_fold_rxq_stats(apc, rxq);
+
if (xdp_rxq_info_is_reg(&rxq->xdp_rxq))
xdp_rxq_info_unreg(&rxq->xdp_rxq);
@@ -3154,6 +3274,8 @@ static struct mana_rxq *mana_create_rxq(struct mana_port_context *apc,
return ERR_PTR(-ENOMEM);
rxq->ndev = ndev;
+ rxq->stats = &apc->rxq_stats[rxq_idx];
+ u64_stats_init(&rxq->drain_stats.syncp);
rxq->num_rx_buf = apc->rx_queue_size;
rxq->rxq_idx = rxq_idx;
rxq->rxobj = INVALID_MANA_HANDLE;
@@ -3304,8 +3426,6 @@ static int mana_add_rx_queues(struct mana_port_context *apc,
goto out;
}
- u64_stats_init(&rxq->stats.syncp);
-
apc->rxqs[i] = rxq;
mana_create_rxq_debugfs(apc, i);
@@ -4076,16 +4196,27 @@ static void mana_start_txqs(struct mana_port_context *apc)
/* Retiring completions must not wake replacement queues. Mark the leaving set
* before unmarking the incoming set.
*/
-static void mana_qset_set_retiring(struct mana_qset *qset, bool retiring)
+static void mana_qset_set_retiring(struct mana_qset *qset,
+ const struct mana_qset *keep, bool retiring)
{
unsigned int q;
- if (!qset->tx_qp)
- return;
-
for (q = 0; q < qset->num_queues; q++) {
- if (qset->tx_qp[q])
+ if (qset->tx_qp && qset->tx_qp[q])
WRITE_ONCE(qset->tx_qp[q]->txq.retiring, retiring);
+
+ if (!qset->rxqs || !qset->rxqs[q])
+ continue;
+
+ /* Carried RXQs remain the sole poll writers of shared slots. */
+ if (retiring && keep && q < keep->num_queues &&
+ keep->rxqs && keep->rxqs[q] == qset->rxqs[q])
+ continue;
+
+ /* Switch to drain_stats; hand off shared slots after a grace
+ * period.
+ */
+ WRITE_ONCE(qset->rxqs[q]->retiring, retiring);
}
}
@@ -4140,12 +4271,12 @@ int mana_publish_qset(struct mana_port_context *apc, struct mana_qset *newq,
/* Mark before the grace period so old completions cannot wake the
* replacement's stopped queue.
*/
- mana_qset_set_retiring(out_old, true);
+ mana_qset_set_retiring(out_old, newq, true);
/* Drain TX/XDP readers past the gate and polls missing retiring. */
synchronize_net();
- mana_qset_set_retiring(newq, false);
+ mana_qset_set_retiring(newq, NULL, false);
mana_qset_install(apc, newq);
apc->rss_state = apc->num_queues > 1 ? TRI_STATE_TRUE : TRI_STATE_FALSE;
@@ -4183,8 +4314,16 @@ int mana_publish_qset(struct mana_port_context *apc, struct mana_qset *newq,
netdev_err(ndev, "%s failed: %d, restoring previous queue set\n",
__func__, err);
- mana_qset_set_retiring(newq, true);
- mana_qset_set_retiring(out_old, false);
+ mana_qset_set_retiring(newq, out_old, true);
+
+ /* Quiesce new shared-slot writers before restoring old ones. */
+ synchronize_net();
+
+ mana_qset_set_retiring(out_old, NULL, false);
+
+ /* Quiesce old drain_stats writers before folding. */
+ synchronize_net();
+ mana_fold_qset_rx_stats(apc, out_old);
mana_qset_install(apc, out_old);
apc->rss_state = apc->num_queues > 1 ? TRI_STATE_TRUE : TRI_STATE_FALSE;
@@ -4393,6 +4532,10 @@ static int mana_probe_port(struct mana_context *ac, int port_idx,
apc->tx_dim_enabled = MANA_ADAPTIVE_TX_DEF;
}
+ err = mana_alloc_queue_stats(apc);
+ if (err)
+ goto free_net;
+
mutex_init(&apc->vport_mutex);
apc->vport_use_count = 0;
@@ -4415,7 +4558,7 @@ static int mana_probe_port(struct mana_context *ac, int port_idx,
err = mana_init_port(ndev);
if (err)
- goto free_net;
+ goto free_stats;
err = mana_rss_table_alloc(apc);
if (err)
@@ -4452,6 +4595,8 @@ static int mana_probe_port(struct mana_context *ac, int port_idx,
mana_cleanup_indir_table(apc);
reset_apc:
mana_cleanup_port_context(apc);
+free_stats:
+ mana_free_queue_stats(apc);
free_net:
*ndev_storage = NULL;
netdev_err(ndev, "Failed to probe vPort %d: %d\n", port_idx, err);
@@ -4792,6 +4937,7 @@ void mana_remove(struct gdma_dev *gd, bool suspending)
unregister_netdevice(ndev);
mana_cleanup_indir_table(apc);
+ mana_free_queue_stats(apc);
/* Remove the port from reset walks before freeing its netdev.
*/
diff --git a/drivers/net/ethernet/microsoft/mana/mana_ethtool.c b/drivers/net/ethernet/microsoft/mana/mana_ethtool.c
index 32f40f5cd850e6c67ea0dd57cc45a51b37c43b93..77dcbd01a1f3da692c341f17d8f2a8301c02bd6b 100644
--- a/drivers/net/ethernet/microsoft/mana/mana_ethtool.c
+++ b/drivers/net/ethernet/microsoft/mana/mana_ethtool.c
@@ -242,6 +242,12 @@ static void mana_get_ethtool_stats(struct net_device *ndev,
u64 xdp_tx;
u64 pkt_len0_err;
u64 coalesced_cqe[MANA_CQE_COAL_PKTS_8 - 1];
+ u64 ret_coalesced_cqe[MANA_CQE_COAL_PKTS_8 - 1];
+ u64 ret_packets, ret_bytes;
+ u64 ret_xdp_redirect;
+ u64 ret_pkt_len0_err;
+ u64 ret_xdp_drop;
+ u64 ret_xdp_tx;
u64 tso_packets;
u64 tso_bytes;
u64 tso_inner_packets;
@@ -252,14 +258,11 @@ static void mana_get_ethtool_stats(struct net_device *ndev,
u64 mana_map_err;
int q, i = 0, j;
- if (!apc->port_is_up)
- return;
-
- /* We call this mana function to get the phy stats from GDMA and includes
- * aggregate tx/rx drop counters, Per-TC(Traffic Channel) tx/rx and pause
- * counters.
+ /* Counters outlive the queues, but suspend can destroy the HW channel
+ * while the netdev remains registered. Gate only the PHY query.
*/
- mana_query_phy_stats(apc);
+ if (apc->port_is_up)
+ mana_query_phy_stats(apc);
for (q = 0; q < ARRAY_SIZE(mana_eth_stats); q++)
data[i++] = *(u64 *)(eth_stats + mana_eth_stats[q].offset);
@@ -271,7 +274,7 @@ static void mana_get_ethtool_stats(struct net_device *ndev,
data[i++] = *(u64 *)(phy_stats + mana_phy_stats[q].offset);
for (q = 0; q < num_queues; q++) {
- rx_stats = &apc->rxqs[q]->stats;
+ rx_stats = &apc->rxq_stats[q];
do {
start = u64_stats_fetch_begin(&rx_stats->syncp);
@@ -285,6 +288,33 @@ static void mana_get_ethtool_stats(struct net_device *ndev,
coalesced_cqe[j] = rx_stats->coalesced_cqe[j];
} while (u64_stats_fetch_retry(&rx_stats->syncp, start));
+ /* Snapshot separately so a retry cannot add retired counters
+ * twice.
+ */
+ rx_stats = &apc->rxq_stats_ret[q];
+
+ do {
+ start = u64_stats_fetch_begin(&rx_stats->syncp);
+ ret_packets = rx_stats->packets;
+ ret_bytes = rx_stats->bytes;
+ ret_xdp_drop = rx_stats->xdp_drop;
+ ret_xdp_tx = rx_stats->xdp_tx;
+ ret_xdp_redirect = rx_stats->xdp_redirect;
+ ret_pkt_len0_err = rx_stats->pkt_len0_err;
+ for (j = 0; j < MANA_CQE_COAL_PKTS_8 - 1; j++)
+ ret_coalesced_cqe[j] =
+ rx_stats->coalesced_cqe[j];
+ } while (u64_stats_fetch_retry(&rx_stats->syncp, start));
+
+ packets += ret_packets;
+ bytes += ret_bytes;
+ xdp_drop += ret_xdp_drop;
+ xdp_tx += ret_xdp_tx;
+ xdp_redirect += ret_xdp_redirect;
+ pkt_len0_err += ret_pkt_len0_err;
+ for (j = 0; j < MANA_CQE_COAL_PKTS_8 - 1; j++)
+ coalesced_cqe[j] += ret_coalesced_cqe[j];
+
data[i++] = packets;
data[i++] = bytes;
data[i++] = xdp_drop;
@@ -296,7 +326,7 @@ static void mana_get_ethtool_stats(struct net_device *ndev,
}
for (q = 0; q < num_queues; q++) {
- tx_stats = &apc->tx_qp[q]->txq.stats;
+ tx_stats = &apc->txq_stats[q];
do {
start = u64_stats_fetch_begin(&tx_stats->syncp);
diff --git a/include/net/mana/mana.h b/include/net/mana/mana.h
index 9b8c038042b4f90b0b5958fc33cdad36e169863c..6c6d906db207304cd2d32c474cf2e9e5f32f4738 100644
--- a/include/net/mana/mana.h
+++ b/include/net/mana/mana.h
@@ -102,7 +102,7 @@ struct mana_stats_rx {
u64 pkt_len0_err;
u64 coalesced_cqe[MANA_CQE_COAL_PKTS_8 - 1];
struct u64_stats_sync syncp;
-};
+} ____cacheline_aligned_in_smp;
struct mana_stats_tx {
u64 packets;
@@ -117,7 +117,7 @@ struct mana_stats_tx {
u64 csum_partial;
u64 mana_map_err;
struct u64_stats_sync syncp;
-};
+} ____cacheline_aligned_in_smp;
struct mana_txq {
struct gdma_queue *gdma_sq;
@@ -148,7 +148,7 @@ struct mana_txq {
/* Suppress completion wakeups on the replacement's netdev queue. */
bool retiring;
- struct mana_stats_tx stats;
+ struct mana_stats_tx *stats;
};
/* skb data and frags dma mappings */
@@ -410,7 +410,16 @@ struct mana_rxq {
u32 buf_index;
- struct mana_stats_rx stats;
+ /* Port-owned live slot; use mana_rxq_stats() to select the writer's
+ * slot.
+ */
+ struct mana_stats_rx *stats;
+
+ /* Set under RTNL before another queue takes over this index. */
+ bool retiring;
+
+ /* Folded under RTNL after drain-stat writers quiesce. */
+ struct mana_stats_rx drain_stats;
struct bpf_prog __rcu *bpf_prog;
struct xdp_rxq_info xdp_rxq;
@@ -613,6 +622,14 @@ struct mana_port_context {
unsigned int max_queues;
unsigned int num_queues;
+ /* Port-lifetime arrays with max_queues slots. Live RX queues write
+ * rxq_stats[]; teardown and rollback fold drain_stats into
+ * rxq_stats_ret[] under RTNL. Readers sum both.
+ */
+ struct mana_stats_rx *rxq_stats;
+ struct mana_stats_rx *rxq_stats_ret;
+ struct mana_stats_tx *txq_stats;
+
unsigned int rx_queue_size;
unsigned int tx_queue_size;
@@ -714,6 +731,11 @@ int mana_detach(struct net_device *ndev, bool from_close);
struct mana_port_context *
mana_qset_scratch_alloc(struct mana_port_context *apc);
void mana_qset_scratch_free(struct mana_port_context *scratch);
+static inline struct mana_stats_rx *mana_rxq_stats(struct mana_rxq *rxq)
+{
+ return READ_ONCE(rxq->retiring) ? &rxq->drain_stats : rxq->stats;
+}
+
int mana_alloc_qset(struct mana_port_context *apc,
struct mana_port_context *scratch, unsigned int num_queues,
unsigned int rx_queue_size, unsigned int tx_queue_size,
--
2.43.0
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH net-next v4 10/13] net: mana: release EQs left idle by a channel-count reduction
2026-09-08 3:28 [PATCH net-next v4 00/13] net: mana: reconfigure by replacing the queue set Long Li
` (8 preceding siblings ...)
2026-09-08 3:28 ` [PATCH net-next v4 09/13] net: mana: keep per-queue statistics in the port context Long Li
@ 2026-09-08 3:28 ` Long Li
2026-09-09 3:29 ` sashiko-bot
2026-09-08 3:28 ` [PATCH net-next v4 11/13] net: mana: keep a user-configured RSS table across a queue rebuild Long Li
` (2 subsequent siblings)
12 siblings, 1 reply; 28+ messages in thread
From: Long Li @ 2026-09-08 3:28 UTC (permalink / raw)
To: Long Li, Long Li, Konstantin Taranov, Jakub Kicinski,
David S . Miller, Paolo Abeni, Eric Dumazet, Andrew Lunn,
Jason Gunthorpe, Leon Romanovsky, Haiyang Zhang,
K . Y . Srinivasan, Wei Liu, Dexuan Cui, shradhagupta,
Simon Horman, ernis, stephen, shirazsaleem
Cc: netdev, linux-rdma, linux-hyperv, linux-kernel
Release EQs above the live queue count after retiring queues are freed
or replacement allocation fails. All CQs using those EQs must be gone.
Return their vector allocations to the pool; IRQ registrations remain.
Store each EQ's debugfs dentry in apc->eqs[] rather than a stack copy so
shrinking can remove individual EQ directories.
Signed-off-by: Long Li <longli@microsoft.com>
---
Changes in v4:
- Derive the live port from scratch->ndev inside mana_free_qset()
instead of adding a redundant parameter and shadowing local.
- State the CQ-lifetime requirement for shrinking and shorten comments.
drivers/net/ethernet/microsoft/mana/mana_en.c | 49 ++++++++++++++++---
.../ethernet/microsoft/mana/mana_ethtool.c | 1 -
2 files changed, 41 insertions(+), 9 deletions(-)
diff --git a/drivers/net/ethernet/microsoft/mana/mana_en.c b/drivers/net/ethernet/microsoft/mana/mana_en.c
index 98007bb3ef7caa974849c3f4998854f15c925321..fc96837d69a00f97c090474ca69bf42cb4805353 100644
--- a/drivers/net/ethernet/microsoft/mana/mana_en.c
+++ b/drivers/net/ethernet/microsoft/mana/mana_en.c
@@ -1881,6 +1881,8 @@ void mana_destroy_eq(struct mana_port_context *apc)
msi = eq->eq.msix_index;
mana_gd_destroy_queue(gc, eq);
mana_gd_put_gic(gc, !gc->msi_sharing, msi);
+ apc->eqs[i].eq = NULL;
+ apc->eqs[i].mana_eq_debugfs = NULL;
}
kfree(apc->eqs);
@@ -1891,15 +1893,16 @@ EXPORT_SYMBOL_NS(mana_destroy_eq, "NET_MANA");
static void mana_create_eq_debugfs(struct mana_port_context *apc, int i)
{
- struct mana_eq eq = apc->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, 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);
+ 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);
}
int mana_create_eq(struct mana_port_context *apc)
@@ -2008,11 +2011,37 @@ static int mana_grow_eqs(struct mana_port_context *apc, unsigned int need)
return 0;
out:
- /* Retain partial growth for reuse; the live set still needs this pool.
- */
return err;
}
+/* All CQs referencing EQs at or above @keep must be destroyed first. */
+static void mana_shrink_eqs(struct mana_port_context *apc, unsigned int keep)
+{
+ struct gdma_context *gc = apc->ac->gdma_dev->gdma_context;
+ struct gdma_queue *eq;
+ unsigned int msi;
+ unsigned int i;
+
+ if (!apc->eqs || keep >= apc->num_eqs)
+ return;
+
+ for (i = keep; i < apc->num_eqs; i++) {
+ eq = apc->eqs[i].eq;
+ if (!eq)
+ continue;
+
+ debugfs_remove_recursive(apc->eqs[i].mana_eq_debugfs);
+ apc->eqs[i].mana_eq_debugfs = NULL;
+
+ msi = eq->eq.msix_index;
+ mana_gd_destroy_queue(gc, eq);
+ mana_gd_put_gic(gc, !gc->msi_sharing, msi);
+ apc->eqs[i].eq = NULL;
+ }
+
+ apc->num_eqs = keep;
+}
+
static int mana_fence_rq(struct mana_port_context *apc, struct mana_rxq *rxq)
{
struct mana_fence_rq_resp resp = {};
@@ -4148,6 +4177,8 @@ int mana_alloc_qset(struct mana_port_context *apc,
kfree(scratch->rxqs);
scratch->rxqs = NULL;
out_err:
+ mana_shrink_eqs(apc, apc->num_queues);
+
netdev_err(ndev, "%s(num_queues=%u) failed: %d\n", __func__,
num_queues, err);
return err;
@@ -4453,6 +4484,8 @@ void mana_free_qset(struct mana_port_context *scratch, struct mana_qset *qset)
*/
netif_set_real_num_rx_queues(apc->ndev, apc->num_queues);
+ mana_shrink_eqs(apc, apc->num_queues);
+
mana_qset_debugfs_publish(apc);
}
diff --git a/drivers/net/ethernet/microsoft/mana/mana_ethtool.c b/drivers/net/ethernet/microsoft/mana/mana_ethtool.c
index 77dcbd01a1f3da692c341f17d8f2a8301c02bd6b..53a76ae42333f46e8791c3c493887ede5c6bb410 100644
--- a/drivers/net/ethernet/microsoft/mana/mana_ethtool.c
+++ b/drivers/net/ethernet/microsoft/mana/mana_ethtool.c
@@ -752,7 +752,6 @@ static int mana_set_channels(struct net_device *ndev,
mana_free_qset(scratch, &oldq);
free_scratch:
- /* Release unpublished queues before closing their shared EQ pool. */
mana_publish_close_if_needed(apc);
mana_qset_scratch_free(scratch);
clear_flag:
--
2.43.0
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH net-next v4 11/13] net: mana: keep a user-configured RSS table across a queue rebuild
2026-09-08 3:28 [PATCH net-next v4 00/13] net: mana: reconfigure by replacing the queue set Long Li
` (9 preceding siblings ...)
2026-09-08 3:28 ` [PATCH net-next v4 10/13] net: mana: release EQs left idle by a channel-count reduction Long Li
@ 2026-09-08 3:28 ` Long Li
2026-09-09 3:29 ` sashiko-bot
2026-09-08 3:28 ` [PATCH net-next v4 12/13] net: mana: keep the surviving queues when the channel count is reduced Long Li
2026-09-08 3:28 ` [PATCH net-next v4 13/13] net: mana: keep the existing queues when the channel count is raised Long Li
12 siblings, 1 reply; 28+ messages in thread
From: Long Li @ 2026-09-08 3:28 UTC (permalink / raw)
To: Long Li, Long Li, Konstantin Taranov, Jakub Kicinski,
David S . Miller, Paolo Abeni, Eric Dumazet, Andrew Lunn,
Jason Gunthorpe, Leon Romanovsky, Haiyang Zhang,
K . Y . Srinivasan, Wei Liu, Dexuan Cui, shradhagupta,
Simon Horman, ernis, stephen, shirazsaleem
Cc: netdev, linux-rdma, linux-hyperv, linux-kernel
Preserve a user RSS table whenever all entries fit the requested queue
count. Regenerate driver defaults. On growth, a retained user table does
not steer RSS traffic to the added queues until the user updates it.
Report table loss only after successful queue-set publication. The
non-swap allocation path still replaces invalid tables silently because
its callers do not consistently hold the notification's netdev lock.
Require RTNL for RSS setters to serialize them with reset/resume rebuilds.
Signed-off-by: Long Li <longli@microsoft.com>
---
Changes in v4:
- Require RTNL for RSS setters to serialize against reset/resume.
- Drop loss notification from mana_alloc_queues(), where the netdev
instance lock is not held by every caller.
- Describe preservation by entry bounds, including channel growth;
shorten comments.
Merge note:
This overlaps the net submission:
https://lore.kernel.org/all/20260905004401.3937066-1-longli@microsoft.com/
Keep this patch's three-argument mana_rss_table_keep() and its callers
when resolving the overlapping mana_en.c blocks. The explicit queue
count is needed to validate an unpublished set; the loss flag defers
swap-path notification. ETHTOOL_OP_NEEDS_RTNL_RSS is identical in both
trees. This series does not depend on the net fix landing first.
drivers/net/ethernet/microsoft/mana/mana_en.c | 41 +++++++++++++++++--
.../ethernet/microsoft/mana/mana_ethtool.c | 3 +-
include/net/mana/mana.h | 2 +
3 files changed, 42 insertions(+), 4 deletions(-)
diff --git a/drivers/net/ethernet/microsoft/mana/mana_en.c b/drivers/net/ethernet/microsoft/mana/mana_en.c
index fc96837d69a00f97c090474ca69bf42cb4805353..78be88b29c99ba2a349df6b43e13487db8c69be1 100644
--- a/drivers/net/ethernet/microsoft/mana/mana_en.c
+++ b/drivers/net/ethernet/microsoft/mana/mana_en.c
@@ -3548,6 +3548,27 @@ static void mana_rss_table_init(struct mana_port_context *apc)
ethtool_rxfh_indir_default(i, apc->num_queues);
}
+/* Keep user tables with valid indices; defer loss notification. */
+static bool mana_rss_table_keep(struct mana_port_context *apc,
+ unsigned int num_queues, bool *lost)
+{
+ u32 i;
+
+ *lost = false;
+
+ if (!netif_is_rxfh_configured(apc->ndev))
+ return false;
+
+ for (i = 0; i < apc->indir_table_sz; i++) {
+ if (apc->indir_table[i] >= num_queues) {
+ *lost = true;
+ return false;
+ }
+ }
+
+ return true;
+}
+
int mana_disable_vport_rx(struct mana_port_context *apc)
{
return mana_cfg_vport_steering(apc, TRI_STATE_FALSE, false, false,
@@ -3818,6 +3839,7 @@ int mana_alloc_queues(struct net_device *ndev)
{
struct mana_port_context *apc = netdev_priv(ndev);
struct gdma_dev *gd = apc->ac->gdma_dev;
+ bool indir_lost;
int err;
err = mana_create_vport(apc, ndev);
@@ -3863,7 +3885,9 @@ int mana_alloc_queues(struct net_device *ndev)
goto destroy_rxq;
}
- mana_rss_table_init(apc);
+ /* Loss notification needs a netdev instance lock we may lack. */
+ if (!mana_rss_table_keep(apc, apc->num_queues, &indir_lost))
+ mana_rss_table_init(apc);
err = mana_config_rss(apc, TRI_STATE_TRUE, true, true);
if (err) {
@@ -4066,9 +4090,10 @@ static void mana_qset_snapshot(const struct mana_port_context *ctx,
out->priv_flags = ctx->priv_flags;
out->mtu = ctx->configured_mtu;
out->bpf_prog = ctx->bpf_prog;
+
+ out->rxfh_indir_lost = false;
}
-/* Vport identity and port debugfs outlive queue sets. */
static void mana_qset_install(struct mana_port_context *ctx,
const struct mana_qset *qset)
{
@@ -4128,6 +4153,7 @@ int mana_alloc_qset(struct mana_port_context *apc,
struct mana_qset *out)
{
struct net_device *ndev = scratch->ndev;
+ bool indir_lost;
int err;
ASSERT_RTNL();
@@ -4163,9 +4189,14 @@ int mana_alloc_qset(struct mana_port_context *apc,
if (err)
goto cleanup_rxq;
- mana_rss_table_init(scratch);
+ if (mana_rss_table_keep(apc, num_queues, &indir_lost))
+ memcpy(scratch->indir_table, apc->indir_table,
+ apc->indir_table_sz * sizeof(*apc->indir_table));
+ else
+ mana_rss_table_init(scratch);
mana_qset_snapshot(scratch, out);
+ out->rxfh_indir_lost = indir_lost;
return 0;
cleanup_rxq:
@@ -4339,6 +4370,10 @@ int mana_publish_qset(struct mana_port_context *apc, struct mana_qset *newq,
WRITE_ONCE(apc->port_is_up, true);
mana_start_txqs(apc);
+ /* Report a lost user table only after successful publication. */
+ if (newq->rxfh_indir_lost)
+ ethtool_rxfh_indir_lost(ndev);
+
return 0;
rollback:
diff --git a/drivers/net/ethernet/microsoft/mana/mana_ethtool.c b/drivers/net/ethernet/microsoft/mana/mana_ethtool.c
index 53a76ae42333f46e8791c3c493887ede5c6bb410..acc82fa9f0057120920b5a09bbb2bc7185707e65 100644
--- a/drivers/net/ethernet/microsoft/mana/mana_ethtool.c
+++ b/drivers/net/ethernet/microsoft/mana/mana_ethtool.c
@@ -951,7 +951,8 @@ const struct ethtool_ops mana_ethtool_ops = {
.op_needs_rtnl = ETHTOOL_OP_NEEDS_RTNL_SCHANNELS |
ETHTOOL_OP_NEEDS_RTNL_SRINGPARAM |
ETHTOOL_OP_NEEDS_RTNL_SPFLAGS |
- ETHTOOL_OP_NEEDS_RTNL_GLINK,
+ ETHTOOL_OP_NEEDS_RTNL_GLINK |
+ ETHTOOL_OP_NEEDS_RTNL_RSS,
.get_ethtool_stats = mana_get_ethtool_stats,
.get_sset_count = mana_get_sset_count,
.get_strings = mana_get_strings,
diff --git a/include/net/mana/mana.h b/include/net/mana/mana.h
index 6c6d906db207304cd2d32c474cf2e9e5f32f4738..84b8151f0693c7e7ef06a558c69e2d207b445921 100644
--- a/include/net/mana/mana.h
+++ b/include/net/mana/mana.h
@@ -717,6 +717,8 @@ struct mana_qset {
int mtu;
struct bpf_prog *bpf_prog;
+ /* Notify the core only after this set is published. */
+ bool rxfh_indir_lost;
};
netdev_tx_t mana_start_xmit(struct sk_buff *skb, struct net_device *ndev);
--
2.43.0
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH net-next v4 12/13] net: mana: keep the surviving queues when the channel count is reduced
2026-09-08 3:28 [PATCH net-next v4 00/13] net: mana: reconfigure by replacing the queue set Long Li
` (10 preceding siblings ...)
2026-09-08 3:28 ` [PATCH net-next v4 11/13] net: mana: keep a user-configured RSS table across a queue rebuild Long Li
@ 2026-09-08 3:28 ` Long Li
2026-09-09 3:29 ` sashiko-bot
2026-09-08 3:28 ` [PATCH net-next v4 13/13] net: mana: keep the existing queues when the channel count is raised Long Li
12 siblings, 1 reply; 28+ messages in thread
From: Long Li @ 2026-09-08 3:28 UTC (permalink / raw)
To: Long Li, Long Li, Konstantin Taranov, Jakub Kicinski,
David S . Miller, Paolo Abeni, Eric Dumazet, Andrew Lunn,
Jason Gunthorpe, Leon Romanovsky, Haiyang Zhang,
K . Y . Srinivasan, Wei Liu, Dexuan Cui, shradhagupta,
Simon Horman, ernis, stephen, shirazsaleem
Cc: netdev, linux-rdma, linux-hyperv, linux-kernel
Split the live set into a kept prefix and a retiring tail. Reductions
allocate only pointer arrays and steering tables, retaining the kept
queues' page pools, buffers, NAPI state and XDP references.
After publication, wait for TX-selection readers before freeing the old
containers, then retire only the tail. Failed publication discards the
new containers without freeing shared queues.
Signed-off-by: Long Li <longli@microsoft.com>
---
Changes in v4:
- Wait for TX-selection readers before freeing old containers.
- Correct the allocation and shared-queue ownership descriptions;
shorten comments and the commit message.
drivers/net/ethernet/microsoft/mana/mana_en.c | 112 +++++++++++++++++-
.../ethernet/microsoft/mana/mana_ethtool.c | 30 +++++
include/net/mana/mana.h | 4 +
3 files changed, 143 insertions(+), 3 deletions(-)
diff --git a/drivers/net/ethernet/microsoft/mana/mana_en.c b/drivers/net/ethernet/microsoft/mana/mana_en.c
index 78be88b29c99ba2a349df6b43e13487db8c69be1..158f9a6ce42157ad7afd45ec642405fbd81f0bac 100644
--- a/drivers/net/ethernet/microsoft/mana/mana_en.c
+++ b/drivers/net/ethernet/microsoft/mana/mana_en.c
@@ -4146,6 +4146,114 @@ void mana_qset_scratch_free(struct mana_port_context *scratch)
kvfree(scratch);
}
+/* Split into kept queues and a retiring tail without changing live ownership.
+ * Queue i retains EQ i.
+ */
+int mana_split_qset(struct mana_port_context *apc,
+ struct mana_port_context *scratch, unsigned int new_count,
+ struct mana_qset *out_new, struct mana_qset *out_tail)
+{
+ unsigned int old_count = apc->num_queues;
+ struct mana_tx_qp **new_tx, **tail_tx;
+ struct mana_rxq **new_rx, **tail_rx;
+ unsigned int tail_count;
+ bool indir_lost;
+ unsigned int i;
+ int err;
+
+ ASSERT_RTNL();
+
+ if (WARN_ON(new_count == 0 || new_count >= old_count))
+ return -EINVAL;
+ if (WARN_ON(!apc->tx_qp || !apc->rxqs))
+ return -EINVAL;
+
+ tail_count = old_count - new_count;
+
+ /* Build steering separately so it cannot index beyond the shortened RX
+ * array.
+ */
+ scratch->num_queues = new_count;
+ err = mana_rss_table_alloc(scratch);
+ if (err)
+ return err;
+
+ if (mana_rss_table_keep(apc, new_count, &indir_lost))
+ memcpy(scratch->indir_table, apc->indir_table,
+ apc->indir_table_sz * sizeof(*apc->indir_table));
+ else
+ mana_rss_table_init(scratch);
+
+ new_tx = kzalloc_objs(struct mana_tx_qp *, new_count);
+ new_rx = kzalloc_objs(struct mana_rxq *, new_count);
+ tail_tx = kzalloc_objs(struct mana_tx_qp *, tail_count);
+ tail_rx = kzalloc_objs(struct mana_rxq *, tail_count);
+ if (!new_tx || !new_rx || !tail_tx || !tail_rx) {
+ err = -ENOMEM;
+ goto free_arrays;
+ }
+
+ for (i = 0; i < new_count; i++) {
+ new_tx[i] = apc->tx_qp[i];
+ new_rx[i] = apc->rxqs[i];
+ }
+ for (i = 0; i < tail_count; i++) {
+ tail_tx[i] = apc->tx_qp[new_count + i];
+ tail_rx[i] = apc->rxqs[new_count + i];
+ }
+
+ out_new->tx_qp = new_tx;
+ out_new->rxqs = new_rx;
+ out_new->indir_table = scratch->indir_table;
+ out_new->indir_table_sz = scratch->indir_table_sz;
+ out_new->rxobj_table = scratch->rxobj_table;
+ out_new->default_rxobj = apc->rxqs[0]->rxobj;
+ out_new->num_queues = new_count;
+ out_new->rx_queue_size = apc->rx_queue_size;
+ out_new->tx_queue_size = apc->tx_queue_size;
+ out_new->priv_flags = apc->priv_flags;
+ out_new->mtu = apc->configured_mtu;
+ out_new->bpf_prog = apc->bpf_prog;
+ out_new->rxfh_indir_lost = indir_lost;
+
+ scratch->indir_table = NULL;
+ scratch->rxobj_table = NULL;
+
+ memset(out_tail, 0, sizeof(*out_tail));
+ out_tail->tx_qp = tail_tx;
+ out_tail->rxqs = tail_rx;
+ out_tail->default_rxobj = INVALID_MANA_HANDLE;
+ out_tail->num_queues = tail_count;
+ out_tail->rx_queue_size = apc->rx_queue_size;
+ out_tail->tx_queue_size = apc->tx_queue_size;
+ out_tail->priv_flags = apc->priv_flags;
+ out_tail->mtu = apc->configured_mtu;
+ out_tail->bpf_prog = apc->bpf_prog;
+
+ return 0;
+
+free_arrays:
+ kfree(new_tx);
+ kfree(new_rx);
+ kfree(tail_tx);
+ kfree(tail_rx);
+ mana_cleanup_indir_table(scratch);
+ return err;
+}
+
+/* Free containers only; the live port still owns the queues. */
+void mana_discard_split(struct mana_qset *newq, struct mana_qset *tailq)
+{
+ kfree(newq->tx_qp);
+ kfree(newq->rxqs);
+ kfree(newq->indir_table);
+ kfree(newq->rxobj_table);
+ kfree(tailq->tx_qp);
+ kfree(tailq->rxqs);
+ memset(newq, 0, sizeof(*newq));
+ memset(tailq, 0, sizeof(*tailq));
+}
+
int mana_alloc_qset(struct mana_port_context *apc,
struct mana_port_context *scratch, unsigned int num_queues,
unsigned int rx_queue_size, unsigned int tx_queue_size,
@@ -4351,9 +4459,7 @@ int mana_publish_qset(struct mana_port_context *apc, struct mana_qset *newq,
if (err)
goto rollback;
- /* Install XDP and per-RXQ references before steering reaches new
- * queues.
- */
+ /* Install XDP before steering reaches the incoming RXQs. */
mana_chn_setxdp(apc, mana_xdp_get(apc));
err = mana_config_rss(apc, TRI_STATE_TRUE, true, true);
diff --git a/drivers/net/ethernet/microsoft/mana/mana_ethtool.c b/drivers/net/ethernet/microsoft/mana/mana_ethtool.c
index acc82fa9f0057120920b5a09bbb2bc7185707e65..0b8c2f61d6263c65a8119b94426f794b218ad55f 100644
--- a/drivers/net/ethernet/microsoft/mana/mana_ethtool.c
+++ b/drivers/net/ethernet/microsoft/mana/mana_ethtool.c
@@ -737,6 +737,36 @@ static int mana_set_channels(struct net_device *ndev,
goto clear_flag;
}
+ if (new_count < apc->num_queues) {
+ struct mana_qset tailq;
+
+ err = mana_split_qset(apc, scratch, new_count, &newq, &tailq);
+ if (err)
+ goto free_scratch;
+
+ err = mana_publish_qset(apc, &newq, &oldq);
+ if (err) {
+ /* Discard containers only; their queues still belong to
+ * the old set.
+ */
+ mana_discard_split(&newq, &tailq);
+ goto free_scratch;
+ }
+
+ /* Wait for ndo_select_queue() readers of oldq.indir_table. Free
+ * only containers; the queues belong to the kept set or tail.
+ */
+ synchronize_net();
+
+ kfree(oldq.tx_qp);
+ kfree(oldq.rxqs);
+ kfree(oldq.indir_table);
+ kfree(oldq.rxobj_table);
+
+ mana_free_qset(scratch, &tailq);
+ goto free_scratch;
+ }
+
err = mana_alloc_qset(apc, scratch, new_count, apc->rx_queue_size,
apc->tx_queue_size, apc->priv_flags,
apc->configured_mtu, apc->bpf_prog, &newq);
diff --git a/include/net/mana/mana.h b/include/net/mana/mana.h
index 84b8151f0693c7e7ef06a558c69e2d207b445921..5ec75de0b644569acdf697e997c262d999295a06 100644
--- a/include/net/mana/mana.h
+++ b/include/net/mana/mana.h
@@ -743,6 +743,10 @@ int mana_alloc_qset(struct mana_port_context *apc,
unsigned int rx_queue_size, unsigned int tx_queue_size,
u32 priv_flags, int mtu, struct bpf_prog *bpf_prog,
struct mana_qset *out);
+int mana_split_qset(struct mana_port_context *apc,
+ struct mana_port_context *scratch, unsigned int new_count,
+ struct mana_qset *out_new, struct mana_qset *out_tail);
+void mana_discard_split(struct mana_qset *newq, struct mana_qset *tailq);
int mana_publish_qset(struct mana_port_context *apc, struct mana_qset *newq,
struct mana_qset *out_old);
void mana_publish_close_if_needed(struct mana_port_context *apc);
--
2.43.0
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH net-next v4 13/13] net: mana: keep the existing queues when the channel count is raised
2026-09-08 3:28 [PATCH net-next v4 00/13] net: mana: reconfigure by replacing the queue set Long Li
` (11 preceding siblings ...)
2026-09-08 3:28 ` [PATCH net-next v4 12/13] net: mana: keep the surviving queues when the channel count is reduced Long Li
@ 2026-09-08 3:28 ` Long Li
2026-09-09 3:29 ` sashiko-bot
12 siblings, 1 reply; 28+ messages in thread
From: Long Li @ 2026-09-08 3:28 UTC (permalink / raw)
To: Long Li, Long Li, Konstantin Taranov, Jakub Kicinski,
David S . Miller, Paolo Abeni, Eric Dumazet, Andrew Lunn,
Jason Gunthorpe, Leon Romanovsky, Haiyang Zhang,
K . Y . Srinivasan, Wei Liu, Dexuan Cui, shradhagupta,
Simon Horman, ernis, stephen, shirazsaleem
Cc: netdev, linux-rdma, linux-hyperv, linux-kernel
Keep existing queues and allocate only the added tail. Growing N to M
now needs M SQ/RQ pairs at peak, rather than N + M.
Track the fresh queues separately for failure cleanup and XDP references.
Wait for TX-selection readers before freeing old containers, and clear
slots during partial teardown. Full rebuilds now keep the current count.
Advertise in-driver resize recovery after converting the live resize
paths. Failed rollback still requires recovery.
Signed-off-by: Long Li <longli@microsoft.com>
---
Changes in v4:
- Wait for TX-selection readers before freeing old containers.
- Clear TX pointer-array slots after partial teardown.
- Shorten comments and qualify the resize-recovery description.
.../net/ethernet/microsoft/mana/mana_bpf.c | 2 +-
drivers/net/ethernet/microsoft/mana/mana_en.c | 209 +++++++++++++++---
.../ethernet/microsoft/mana/mana_ethtool.c | 31 ++-
include/net/mana/gdma.h | 8 +-
include/net/mana/mana.h | 7 +-
5 files changed, 217 insertions(+), 40 deletions(-)
diff --git a/drivers/net/ethernet/microsoft/mana/mana_bpf.c b/drivers/net/ethernet/microsoft/mana/mana_bpf.c
index 8daeb65cc647bb4cf4391f6fadcd04793630c3b6..fc7bb0016d5dd8e112c544baa83b3efafb6eeb5c 100644
--- a/drivers/net/ethernet/microsoft/mana/mana_bpf.c
+++ b/drivers/net/ethernet/microsoft/mana/mana_bpf.c
@@ -206,7 +206,7 @@ static int mana_xdp_set(struct net_device *ndev, struct bpf_prog *prog,
return -ENOMEM;
}
- err = mana_alloc_qset(apc, scratch, apc->num_queues,
+ err = mana_alloc_qset(apc, scratch,
apc->rx_queue_size, apc->tx_queue_size,
apc->priv_flags, apc->configured_mtu,
prog, &newq);
diff --git a/drivers/net/ethernet/microsoft/mana/mana_en.c b/drivers/net/ethernet/microsoft/mana/mana_en.c
index 158f9a6ce42157ad7afd45ec642405fbd81f0bac..29f54f91cb0094f65cdd09d82cf4f29f3d296437 100644
--- a/drivers/net/ethernet/microsoft/mana/mana_en.c
+++ b/drivers/net/ethernet/microsoft/mana/mana_en.c
@@ -927,7 +927,7 @@ static int mana_change_mtu(struct net_device *ndev, int new_mtu)
if (!scratch)
return -ENOMEM;
- err = mana_alloc_qset(mpc, scratch, mpc->num_queues, mpc->rx_queue_size,
+ err = mana_alloc_qset(mpc, scratch, mpc->rx_queue_size,
mpc->tx_queue_size, mpc->priv_flags, new_mtu,
mpc->bpf_prog, &newq);
if (err)
@@ -2878,7 +2878,9 @@ static void mana_deinit_txq(struct mana_port_context *apc, struct mana_txq *txq)
mana_gd_destroy_queue(gd->gdma_context, txq->gdma_sq);
}
-static void mana_destroy_txq(struct mana_port_context *apc)
+/* Keep the array and queues below @first; clear freed slots. */
+static void mana_destroy_txq_from(struct mana_port_context *apc,
+ unsigned int first)
{
struct napi_struct *napi;
int i;
@@ -2886,7 +2888,7 @@ static void mana_destroy_txq(struct mana_port_context *apc)
if (!apc->tx_qp)
return;
- for (i = 0; i < apc->num_queues; i++) {
+ for (i = first; i < apc->num_queues; i++) {
if (!apc->tx_qp[i])
continue;
@@ -2910,7 +2912,16 @@ static void mana_destroy_txq(struct mana_port_context *apc)
mana_deinit_txq(apc, &apc->tx_qp[i]->txq);
kvfree(apc->tx_qp[i]);
+ apc->tx_qp[i] = NULL;
}
+}
+
+static void mana_destroy_txq(struct mana_port_context *apc)
+{
+ if (!apc->tx_qp)
+ return;
+
+ mana_destroy_txq_from(apc, 0);
kfree(apc->tx_qp);
apc->tx_qp = NULL;
@@ -2941,8 +2952,11 @@ static void mana_create_txq_debugfs(struct mana_port_context *apc, int idx)
tx_qp->tx_cq.gdma_cq, &mana_dbg_q_fops);
}
+/* With @first nonzero, use the existing array and unwind only new queues on
+ * failure.
+ */
static int mana_create_txq(struct mana_port_context *apc,
- struct net_device *net)
+ struct net_device *net, unsigned int first)
{
struct mana_context *ac = apc->ac;
struct gdma_dev *gd = ac->gdma_dev;
@@ -2957,9 +2971,14 @@ static int mana_create_txq(struct mana_port_context *apc,
int err;
int i;
- apc->tx_qp = kzalloc_objs(struct mana_tx_qp *, apc->num_queues);
- if (!apc->tx_qp)
- return -ENOMEM;
+ if (first) {
+ if (WARN_ON(!apc->tx_qp))
+ return -EINVAL;
+ } else {
+ apc->tx_qp = kzalloc_objs(struct mana_tx_qp *, apc->num_queues);
+ if (!apc->tx_qp)
+ return -ENOMEM;
+ }
/* The minimum size of the WQE is 32 bytes, hence
* apc->tx_queue_size represents the maximum number of WQEs
@@ -2976,7 +2995,7 @@ static int mana_create_txq(struct mana_port_context *apc,
gc = gd->gdma_context;
- for (i = 0; i < apc->num_queues; i++) {
+ for (i = first; i < apc->num_queues; i++) {
apc->tx_qp[i] = kvzalloc_obj(*apc->tx_qp[i]);
if (!apc->tx_qp[i]) {
err = -ENOMEM;
@@ -3084,7 +3103,10 @@ static int mana_create_txq(struct mana_port_context *apc,
out:
netdev_err(net, "Failed to create %d TX queues, %d\n",
apc->num_queues, err);
- mana_destroy_txq(apc);
+ if (first)
+ mana_destroy_txq_from(apc, first);
+ else
+ mana_destroy_txq(apc);
return err;
}
@@ -3440,14 +3462,15 @@ static void mana_create_rxq_debugfs(struct mana_port_context *apc, int idx)
&mana_dbg_q_fops);
}
+/* The caller must destroy queues added before a failure. */
static int mana_add_rx_queues(struct mana_port_context *apc,
- struct net_device *ndev)
+ struct net_device *ndev, unsigned int first)
{
struct mana_rxq *rxq;
int err = 0;
int i;
- for (i = 0; i < apc->num_queues; i++) {
+ for (i = first; i < apc->num_queues; i++) {
rxq = mana_create_rxq(apc, i, &apc->eqs[i], ndev);
if (IS_ERR(rxq)) {
err = PTR_ERR(rxq);
@@ -3465,14 +3488,15 @@ static int mana_add_rx_queues(struct mana_port_context *apc,
return err;
}
-static void mana_destroy_rxqs(struct mana_port_context *apc)
+static void mana_destroy_rxqs_from(struct mana_port_context *apc,
+ unsigned int first)
{
struct mana_rxq *rxq;
u32 rxq_idx;
if (apc->rxqs) {
- for (rxq_idx = 0; rxq_idx < apc->num_queues; rxq_idx++) {
+ for (rxq_idx = first; rxq_idx < apc->num_queues; rxq_idx++) {
rxq = apc->rxqs[rxq_idx];
if (!rxq)
continue;
@@ -3483,6 +3507,11 @@ static void mana_destroy_rxqs(struct mana_port_context *apc)
}
}
+static void mana_destroy_rxqs(struct mana_port_context *apc)
+{
+ mana_destroy_rxqs_from(apc, 0);
+}
+
static void mana_destroy_vport(struct mana_port_context *apc)
{
struct gdma_dev *gd = apc->ac->gdma_dev;
@@ -3856,7 +3885,7 @@ int mana_alloc_queues(struct net_device *ndev)
goto destroy_vport;
}
- err = mana_create_txq(apc, ndev);
+ err = mana_create_txq(apc, ndev, 0);
if (err) {
netdev_err(ndev, "Failed to create TXQ on vPort %u: %d\n",
apc->port_idx, err);
@@ -3871,7 +3900,7 @@ int mana_alloc_queues(struct net_device *ndev)
goto destroy_txq;
}
- err = mana_add_rx_queues(apc, ndev);
+ err = mana_add_rx_queues(apc, ndev, 0);
if (err)
goto destroy_rxq;
@@ -4254,8 +4283,137 @@ void mana_discard_split(struct mana_qset *newq, struct mana_qset *tailq)
memset(tailq, 0, sizeof(*tailq));
}
+/* Carry existing queues into @out_new; allocate only the tail. @out_fresh
+ * isolates new queues for cleanup after a failed publish.
+ */
+int mana_grow_qset(struct mana_port_context *apc,
+ struct mana_port_context *scratch, unsigned int new_count,
+ struct mana_qset *out_new, struct mana_qset *out_fresh)
+{
+ unsigned int old_count = apc->num_queues;
+ struct mana_tx_qp **new_tx, **fresh_tx;
+ struct mana_rxq **new_rx, **fresh_rx;
+ struct net_device *ndev = apc->ndev;
+ unsigned int fresh_count;
+ bool indir_lost;
+ unsigned int i;
+ int err;
+
+ ASSERT_RTNL();
+
+ if (WARN_ON(new_count <= old_count))
+ return -EINVAL;
+ if (WARN_ON(!apc->tx_qp || !apc->rxqs))
+ return -EINVAL;
+
+ fresh_count = new_count - old_count;
+
+ new_tx = kzalloc_objs(struct mana_tx_qp *, new_count);
+ new_rx = kzalloc_objs(struct mana_rxq *, new_count);
+ fresh_tx = kzalloc_objs(struct mana_tx_qp *, fresh_count);
+ fresh_rx = kzalloc_objs(struct mana_rxq *, fresh_count);
+ if (!new_tx || !new_rx || !fresh_tx || !fresh_rx) {
+ err = -ENOMEM;
+ goto free_arrays;
+ }
+
+ for (i = 0; i < old_count; i++) {
+ new_tx[i] = apc->tx_qp[i];
+ new_rx[i] = apc->rxqs[i];
+ }
+
+ scratch->num_queues = new_count;
+ scratch->tx_qp = new_tx;
+ scratch->rxqs = new_rx;
+
+ err = mana_rss_table_alloc(scratch);
+ if (err)
+ goto free_arrays;
+
+ err = mana_grow_eqs(apc, new_count);
+ if (err)
+ goto cleanup_rss;
+
+ scratch->eqs = apc->eqs;
+ scratch->num_eqs = apc->num_eqs;
+
+ err = mana_create_txq(scratch, ndev, old_count);
+ if (err)
+ goto cleanup_rss;
+
+ err = mana_add_rx_queues(scratch, ndev, old_count);
+ if (err)
+ goto cleanup_rxq;
+
+ if (mana_rss_table_keep(apc, new_count, &indir_lost))
+ memcpy(scratch->indir_table, apc->indir_table,
+ apc->indir_table_sz * sizeof(*apc->indir_table));
+ else
+ mana_rss_table_init(scratch);
+
+ mana_qset_snapshot(scratch, out_new);
+ out_new->rxfh_indir_lost = indir_lost;
+
+ for (i = 0; i < fresh_count; i++) {
+ fresh_tx[i] = new_tx[old_count + i];
+ fresh_rx[i] = new_rx[old_count + i];
+ }
+
+ memset(out_fresh, 0, sizeof(*out_fresh));
+ out_fresh->tx_qp = fresh_tx;
+ out_fresh->rxqs = fresh_rx;
+ out_fresh->default_rxobj = INVALID_MANA_HANDLE;
+ out_fresh->num_queues = fresh_count;
+ out_fresh->rx_queue_size = apc->rx_queue_size;
+ out_fresh->tx_queue_size = apc->tx_queue_size;
+ out_fresh->priv_flags = apc->priv_flags;
+ out_fresh->mtu = apc->configured_mtu;
+ out_fresh->bpf_prog = apc->bpf_prog;
+
+ /* Take XDP refs on fresh RXQs only. On the merged set,
+ * mana_chn_setxdp() returns early on the carried rxqs[0].
+ */
+ mana_qset_install(scratch, out_fresh);
+ mana_chn_setxdp(scratch, mana_xdp_get(apc));
+
+ return 0;
+
+cleanup_rxq:
+ mana_destroy_rxqs_from(scratch, old_count);
+ mana_destroy_txq_from(scratch, old_count);
+cleanup_rss:
+ mana_cleanup_indir_table(scratch);
+free_arrays:
+ /* Free containers only; carried queues remain live. */
+ scratch->tx_qp = NULL;
+ scratch->rxqs = NULL;
+ kfree(new_tx);
+ kfree(new_rx);
+ kfree(fresh_tx);
+ kfree(fresh_rx);
+
+ mana_shrink_eqs(apc, apc->num_queues);
+
+ netdev_err(ndev, "%s(num_queues=%u) failed: %d\n", __func__,
+ new_count, err);
+ return err;
+}
+
+/* Free merged containers only, not carried queues. The caller must retire fresh
+ * queues separately.
+ */
+void mana_discard_grow(struct mana_qset *newq)
+{
+ kfree(newq->tx_qp);
+ kfree(newq->rxqs);
+ kfree(newq->indir_table);
+ kfree(newq->rxobj_table);
+ memset(newq, 0, sizeof(*newq));
+}
+
+/* Rebuild at the current count; resize uses split/grow. */
int mana_alloc_qset(struct mana_port_context *apc,
- struct mana_port_context *scratch, unsigned int num_queues,
+ struct mana_port_context *scratch,
unsigned int rx_queue_size, unsigned int tx_queue_size,
u32 priv_flags, int mtu, struct bpf_prog *bpf_prog,
struct mana_qset *out)
@@ -4266,7 +4424,7 @@ int mana_alloc_qset(struct mana_port_context *apc,
ASSERT_RTNL();
- scratch->num_queues = num_queues;
+ scratch->num_queues = apc->num_queues;
scratch->rx_queue_size = rx_queue_size;
scratch->tx_queue_size = tx_queue_size;
scratch->priv_flags = priv_flags;
@@ -4282,22 +4440,19 @@ int mana_alloc_qset(struct mana_port_context *apc,
if (err)
goto cleanup_rxq_array;
- err = mana_grow_eqs(apc, num_queues);
- if (err)
- goto cleanup_rss;
-
+ /* Reuse the existing EQ pool; the queue count is unchanged. */
scratch->eqs = apc->eqs;
scratch->num_eqs = apc->num_eqs;
- err = mana_create_txq(scratch, ndev);
+ err = mana_create_txq(scratch, ndev, 0);
if (err)
goto cleanup_rss;
- err = mana_add_rx_queues(scratch, ndev);
+ err = mana_add_rx_queues(scratch, ndev, 0);
if (err)
goto cleanup_rxq;
- if (mana_rss_table_keep(apc, num_queues, &indir_lost))
+ if (mana_rss_table_keep(apc, scratch->num_queues, &indir_lost))
memcpy(scratch->indir_table, apc->indir_table,
apc->indir_table_sz * sizeof(*apc->indir_table));
else
@@ -4316,10 +4471,8 @@ int mana_alloc_qset(struct mana_port_context *apc,
kfree(scratch->rxqs);
scratch->rxqs = NULL;
out_err:
- mana_shrink_eqs(apc, apc->num_queues);
-
netdev_err(ndev, "%s(num_queues=%u) failed: %d\n", __func__,
- num_queues, err);
+ apc->num_queues, err);
return err;
}
@@ -4531,7 +4684,7 @@ int mana_publish_qset(struct mana_port_context *apc, struct mana_qset *newq,
}
/* Create missing debugfs nodes once retiring names are gone. */
-static void mana_qset_debugfs_publish(struct mana_port_context *apc)
+void mana_qset_debugfs_publish(struct mana_port_context *apc)
{
unsigned int i;
diff --git a/drivers/net/ethernet/microsoft/mana/mana_ethtool.c b/drivers/net/ethernet/microsoft/mana/mana_ethtool.c
index 0b8c2f61d6263c65a8119b94426f794b218ad55f..6586a9d2a13438bd85d0aa55c55c08a0b3301f78 100644
--- a/drivers/net/ethernet/microsoft/mana/mana_ethtool.c
+++ b/drivers/net/ethernet/microsoft/mana/mana_ethtool.c
@@ -684,7 +684,7 @@ static int mana_set_channels(struct net_device *ndev,
struct mana_port_context *apc = netdev_priv(ndev);
unsigned int new_count = channels->combined_count;
struct mana_port_context *scratch;
- struct mana_qset newq, oldq;
+ struct mana_qset newq, oldq, freshq;
int err;
if (new_count < 1 || new_count > apc->max_queues) {
@@ -767,19 +767,33 @@ static int mana_set_channels(struct net_device *ndev,
goto free_scratch;
}
- err = mana_alloc_qset(apc, scratch, new_count, apc->rx_queue_size,
- apc->tx_queue_size, apc->priv_flags,
- apc->configured_mtu, apc->bpf_prog, &newq);
+ err = mana_grow_qset(apc, scratch, new_count, &newq, &freshq);
if (err)
goto free_scratch;
err = mana_publish_qset(apc, &newq, &oldq);
if (err) {
- mana_free_qset(scratch, &newq);
+ /* Free only the new queues, then discard the merged containers.
+ */
+ mana_free_qset(scratch, &freshq);
+ mana_discard_grow(&newq);
goto free_scratch;
}
- mana_free_qset(scratch, &oldq);
+ /* Wait for ndo_select_queue() readers of oldq.indir_table. All queues
+ * are now live in newq; free only the old and fresh containers.
+ */
+ synchronize_net();
+
+ kfree(oldq.tx_qp);
+ kfree(oldq.rxqs);
+ kfree(oldq.indir_table);
+ kfree(oldq.rxobj_table);
+ kfree(freshq.tx_qp);
+ kfree(freshq.rxqs);
+
+ /* No retirement runs to publish the new queues' debugfs nodes. */
+ mana_qset_debugfs_publish(apc);
free_scratch:
mana_publish_close_if_needed(apc);
@@ -856,7 +870,7 @@ static int mana_set_ringparam(struct net_device *ndev,
goto clear_flag;
}
- err = mana_alloc_qset(apc, scratch, apc->num_queues, new_rx, new_tx,
+ err = mana_alloc_qset(apc, scratch, new_rx, new_tx,
apc->priv_flags, apc->configured_mtu,
apc->bpf_prog, &newq);
if (err) {
@@ -876,7 +890,6 @@ static int mana_set_ringparam(struct net_device *ndev,
mana_free_qset(scratch, &oldq);
free_scratch:
- /* Release unpublished queues before closing their shared EQ pool. */
mana_publish_close_if_needed(apc);
mana_qset_scratch_free(scratch);
clear_flag:
@@ -946,7 +959,7 @@ static int mana_set_priv_flags(struct net_device *ndev, u32 priv_flags)
goto clear_flag;
}
- err = mana_alloc_qset(apc, scratch, apc->num_queues, apc->rx_queue_size,
+ err = mana_alloc_qset(apc, scratch, apc->rx_queue_size,
apc->tx_queue_size, priv_flags,
apc->configured_mtu, apc->bpf_prog, &newq);
if (err)
diff --git a/include/net/mana/gdma.h b/include/net/mana/gdma.h
index 308950f9b54b0485bac66b80d63e257eaf5f787e..666565ffb26aad6b77044ce1604568aa208a5b79 100644
--- a/include/net/mana/gdma.h
+++ b/include/net/mana/gdma.h
@@ -686,6 +686,11 @@ enum {
/* Driver supports non-contiguous queue buffers */
#define GDMA_DRV_CAP_FLAG_1_NON_CONTIGUOUS_BUFFERS BIT(30)
+/* Resize failures are handled in-driver; a failed rollback still needs
+ * recovery.
+ */
+#define GDMA_DRV_CAP_FLAG_1_SELF_RECOVERY_ON_QUEUE_RESIZE_FAILURE BIT(31)
+
#define GDMA_DRV_CAP_FLAGS1 \
(GDMA_DRV_CAP_FLAG_1_EQ_SHARING_MULTI_VPORT | \
GDMA_DRV_CAP_FLAG_1_NAPI_WKDONE_FIX | \
@@ -703,7 +708,8 @@ enum {
GDMA_DRV_CAP_FLAG_1_HWC_TIMEOUT_RECOVERY | \
GDMA_DRV_CAP_FLAG_1_EQ_MSI_UNSHARE_MULTI_VPORT | \
GDMA_DRV_CAP_FLAG_1_DYN_INTERRUPT_MODERATION | \
- GDMA_DRV_CAP_FLAG_1_NON_CONTIGUOUS_BUFFERS)
+ GDMA_DRV_CAP_FLAG_1_NON_CONTIGUOUS_BUFFERS | \
+ GDMA_DRV_CAP_FLAG_1_SELF_RECOVERY_ON_QUEUE_RESIZE_FAILURE)
#define GDMA_DRV_CAP_FLAGS2 0
diff --git a/include/net/mana/mana.h b/include/net/mana/mana.h
index 5ec75de0b644569acdf697e997c262d999295a06..cc936ae97a0318620c011ae55925595858e0fc3d 100644
--- a/include/net/mana/mana.h
+++ b/include/net/mana/mana.h
@@ -739,7 +739,7 @@ static inline struct mana_stats_rx *mana_rxq_stats(struct mana_rxq *rxq)
}
int mana_alloc_qset(struct mana_port_context *apc,
- struct mana_port_context *scratch, unsigned int num_queues,
+ struct mana_port_context *scratch,
unsigned int rx_queue_size, unsigned int tx_queue_size,
u32 priv_flags, int mtu, struct bpf_prog *bpf_prog,
struct mana_qset *out);
@@ -747,10 +747,15 @@ int mana_split_qset(struct mana_port_context *apc,
struct mana_port_context *scratch, unsigned int new_count,
struct mana_qset *out_new, struct mana_qset *out_tail);
void mana_discard_split(struct mana_qset *newq, struct mana_qset *tailq);
+int mana_grow_qset(struct mana_port_context *apc,
+ struct mana_port_context *scratch, unsigned int new_count,
+ struct mana_qset *out_new, struct mana_qset *out_fresh);
+void mana_discard_grow(struct mana_qset *newq);
int mana_publish_qset(struct mana_port_context *apc, struct mana_qset *newq,
struct mana_qset *out_old);
void mana_publish_close_if_needed(struct mana_port_context *apc);
void mana_free_qset(struct mana_port_context *scratch, struct mana_qset *qset);
+void mana_qset_debugfs_publish(struct mana_port_context *apc);
void mana_dim_change(struct mana_cq *cq, bool enable);
--
2.43.0
^ permalink raw reply related [flat|nested] 28+ messages in thread
* Re: [PATCH net-next v4 06/13] net: mana: swap queue sets in mana_change_mtu
2026-09-08 3:28 ` [PATCH net-next v4 06/13] net: mana: swap queue sets in mana_change_mtu Long Li
@ 2026-09-09 3:29 ` sashiko-bot
0 siblings, 0 replies; 28+ messages in thread
From: sashiko-bot @ 2026-09-09 3:29 UTC (permalink / raw)
To: Long Li; +Cc: linux-hyperv, linux-rdma
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] mana_change_mtu lacks channel_changing protection, causing a race condition with RDMA vport configuration on failure.
--
commit 1868db24408cc9d4a118e97d242371346d9b0c67
Author: Long Li <longli@microsoft.com>
net: mana: swap queue sets in mana_change_mtu
This commit updates mana_change_mtu to carry the MTU in the queue set,
replacing RX buffers and rebuilding all queues using a temporary queue set
swap instead of just detaching and attaching.
> diff --git a/drivers/net/ethernet/microsoft/mana/mana_en.c b/drivers/net/ethernet/microsoft/mana/mana_en.c
> index bb9ef4e634a6e..7f7833f3e1aad 100644
> --- a/drivers/net/ethernet/microsoft/mana/mana_en.c
> +++ b/drivers/net/ethernet/microsoft/mana/mana_en.c
> @@ -902,32 +902,37 @@ int mana_pre_alloc_rxbufs(struct mana_port_context *mpc, int new_mtu, int num_qu
> static int mana_change_mtu(struct net_device *ndev, int new_mtu)
> {
> struct mana_port_context *mpc = netdev_priv(ndev);
> - unsigned int old_mtu = ndev->mtu;
> + struct mana_port_context *scratch;
> + struct mana_qset newq, oldq;
> int err;
>
> - /* Pre-allocate buffers to prevent failure in mana_attach later */
> - err = mana_pre_alloc_rxbufs(mpc, new_mtu, mpc->num_queues);
> - if (err) {
> - netdev_err(ndev, "Insufficient memory for new MTU\n");
> - return err;
> + if (!mpc->port_is_up) {
> + mpc->configured_mtu = new_mtu;
> + WRITE_ONCE(ndev->mtu, new_mtu);
> + return 0;
> }
>
> - err = mana_detach(ndev, false);
> - if (err) {
> - netdev_err(ndev, "mana_detach failed: %d\n", err);
> - goto out;
> - }
> + scratch = mana_qset_scratch_alloc(mpc);
> + if (!scratch)
> + return -ENOMEM;
>
> - WRITE_ONCE(ndev->mtu, new_mtu);
> + err = mana_alloc_qset(mpc, scratch, mpc->num_queues,
> + mpc->rx_queue_size, mpc->tx_queue_size,
> + mpc->priv_flags, new_mtu, &newq);
> + if (err)
> + goto free_scratch;
[Severity: High]
This is a pre-existing issue, but does mana_change_mtu() need to set the
channel_changing flag to protect against concurrent RDMA operations? Unlike
the ethtool queue configuration paths, this function initiates a queue set
swap without taking vport_mutex to set apc->channel_changing to true.
>
> - err = mana_attach(ndev);
> + err = mana_publish_qset(mpc, &newq, &oldq);
> if (err) {
> - netdev_err(ndev, "mana_attach failed: %d\n", err);
> - WRITE_ONCE(ndev->mtu, old_mtu);
> + mana_free_qset(scratch, &newq);
> + goto free_scratch;
> }
>
> -out:
> - mana_pre_dealloc_rxbufs(mpc);
> + mana_free_qset(scratch, &oldq);
> +
> +free_scratch:
> + mana_publish_close_if_needed(mpc);
> + mana_qset_scratch_free(scratch);
> return err;
> }
[Severity: High]
This isn't a bug introduced by this patch, but can this failure path race
with RDMA vport configuration?
On failure, mana_publish_close_if_needed() triggers a vport teardown via
mana_dealloc_queues() and mana_destroy_vport(). Because the
channel_changing flag is not set, a concurrent RDMA operation (such as
creating an RDMA RAW QP from userspace) could grab the vport mid-teardown,
specifically between mana_uncfg_vport() and mana_pf_deregister_hw_vport().
If this race occurs, the Ethernet driver might send a hardware deregister
command for a vport that RDMA just configured, potentially causing hardware
state corruption or PCIe errors.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260908032843.397667-1-longli@microsoft.com?part=6
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH net-next v4 05/13] net: mana: swap queue sets in mana_set_priv_flags
2026-09-08 3:28 ` [PATCH net-next v4 05/13] net: mana: swap queue sets in mana_set_priv_flags Long Li
@ 2026-09-09 3:29 ` sashiko-bot
0 siblings, 0 replies; 28+ messages in thread
From: sashiko-bot @ 2026-09-09 3:29 UTC (permalink / raw)
To: Long Li; +Cc: linux-hyperv, linux-rdma
> Rebuild queues through the swap path when USE_FULL_PAGE_RXBUF changes
> the RX layout. Carry priv_flags with the queue set so allocation failure
> leaves the live configuration unchanged and rollback restores the flags.
>
> Retain the shortcuts for a down port or a configuration that already
> requires full-page RX. A failed rollback closes the port.
>
> Signed-off-by: Long Li <longli@microsoft.com>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260908032843.397667-1-longli@microsoft.com?part=5
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH net-next v4 02/13] net: mana: share the EQ pool across a queue-set swap
2026-09-08 3:28 ` [PATCH net-next v4 02/13] net: mana: share the EQ pool across a queue-set swap Long Li
@ 2026-09-09 3:29 ` sashiko-bot
0 siblings, 0 replies; 28+ messages in thread
From: sashiko-bot @ 2026-09-09 3:29 UTC (permalink / raw)
To: Long Li; +Cc: linux-rdma, linux-hyperv
> Make the EQ pool port-owned so overlapping queue sets share EQs instead
> of requiring old + new vector allocations. Allocate max_queues slots and
> track populated entries with num_eqs.
>
> Grow the pool before creating replacement CQs. Additional EQs survive
> allocation failure in this patch and are released at port teardown.
>
> Signed-off-by: Long Li <longli@microsoft.com>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260908032843.397667-1-longli@microsoft.com?part=2
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH net-next v4 04/13] net: mana: swap queue sets in mana_set_ringparam
2026-09-08 3:28 ` [PATCH net-next v4 04/13] net: mana: swap queue sets in mana_set_ringparam Long Li
@ 2026-09-09 3:29 ` sashiko-bot
2026-09-09 23:25 ` [EXTERNAL] " Long Li
0 siblings, 1 reply; 28+ messages in thread
From: sashiko-bot @ 2026-09-09 3:29 UTC (permalink / raw)
To: Long Li; +Cc: linux-hyperv, linux-rdma
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] The patch introduces an ad-hoc locking pattern using a boolean flag (`apc->channel_changing`) to guard the reconfiguration critical section in `mana_set_ringparam`.
--
commit 40668ebf41a83bf44835ffb33803930c039ce405
Author: Long Li <longli@microsoft.com>
net: mana: swap queue sets in mana_set_ringparam
This commit replaces detach/attach operations in mana_set_ringparam() with
queue-set allocation and publication. Failed allocations now leave the
running queues and ring sizes unchanged instead of risking a detached port.
> diff --git a/drivers/net/ethernet/microsoft/mana/mana_ethtool.c b/drivers/net/ethernet/microsoft/mana/mana_ethtool.c
> --- a/drivers/net/ethernet/microsoft/mana/mana_ethtool.c
> +++ b/drivers/net/ethernet/microsoft/mana/mana_ethtool.c
[ ... ]
> @@ -774,32 +772,56 @@ static int mana_set_ringparam(struct net_device *ndev,
> netdev_info(ndev, "Using nearest power of 2 values for Txq:%d Rxq:%d\n",
> new_tx, new_rx);
>
> - /* pre-allocating new buffers to prevent failures in mana_attach() later */
> - apc->rx_queue_size = new_rx;
> - err = mana_pre_alloc_rxbufs(apc, ndev->mtu, apc->num_queues);
> - apc->rx_queue_size = old_rx;
> - if (err) {
> - netdev_err(ndev, "Insufficient memory for new allocations\n");
> - return err;
> + if (new_rx == apc->rx_queue_size && new_tx == apc->tx_queue_size)
> + return 0;
> +
> + if (!apc->port_is_up) {
> + apc->rx_queue_size = new_rx;
> + apc->tx_queue_size = new_tx;
> + return 0;
> }
>
> - err = mana_detach(ndev, false);
> - if (err) {
> - netdev_err(ndev, "mana_detach failed: %d\n", err);
> - goto out;
> + /* Exclude RDMA through failure cleanup, which may release the vport. */
> + mutex_lock(&apc->vport_mutex);
> + if (apc->channel_changing) {
> + mutex_unlock(&apc->vport_mutex);
> + return -EBUSY;
> + }
> + apc->channel_changing = true;
> + mutex_unlock(&apc->vport_mutex);
[Severity: Medium]
Does this code introduce an ad-hoc locking pattern using the
apc->channel_changing boolean flag?
The networking drivers subsystem guidelines indicate that using a boolean
flag set on entry and cleared on exit to guard a reconfiguration critical
section bypasses lockdep and provides weaker synchronization guarantees than
using a proper lock.
Could a standard kernel synchronization primitive be used for this critical
section in mana_set_ringparam() instead of relying on the
apc->channel_changing flag?
[ ... ]
> +clear_flag:
> + mutex_lock(&apc->vport_mutex);
> + apc->channel_changing = false;
> + mutex_unlock(&apc->vport_mutex);
> return err;
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260908032843.397667-1-longli@microsoft.com?part=4
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH net-next v4 08/13] net: mana: do not bail out of mana_detach on dealloc failure
2026-09-08 3:28 ` [PATCH net-next v4 08/13] net: mana: do not bail out of mana_detach on dealloc failure Long Li
@ 2026-09-09 3:29 ` sashiko-bot
0 siblings, 0 replies; 28+ messages in thread
From: sashiko-bot @ 2026-09-09 3:29 UTC (permalink / raw)
To: Long Li; +Cc: linux-rdma, linux-hyperv
> From: Dipayaan Roy <dipayanroy@linux.microsoft.com>
>
> Remove the early return after mana_dealloc_queues() so detach continues
> its device and port-context cleanup.
>
> The return is currently unreachable: mana_dealloc_queues() only rejects
> an up port, and mana_detach() clears port_is_up before calling it. This
> is a robustness cleanup, not a fix for a reachable reset failure.
>
> Signed-off-by: Dipayaan Roy <dipayanroy@linux.microsoft.com>
> Signed-off-by: Long Li <longli@microsoft.com>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260908032843.397667-1-longli@microsoft.com?part=8
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH net-next v4 03/13] net: mana: swap queue sets in mana_set_channels
2026-09-08 3:28 ` [PATCH net-next v4 03/13] net: mana: swap queue sets in mana_set_channels Long Li
@ 2026-09-09 3:29 ` sashiko-bot
0 siblings, 0 replies; 28+ messages in thread
From: sashiko-bot @ 2026-09-09 3:29 UTC (permalink / raw)
To: Long Li; +Cc: linux-rdma, linux-hyperv
> Build a replacement queue set before quiescing TX, then publish it and
> retire the old set. Allocation failure preserves the running queues;
> publication failure attempts rollback. If rollback also fails, close the
> port and lower carrier, allowing a later administrative reopen.
>
> Keep RX queue indices valid until retiring queues stop delivering. Order
> the port-up store before TX ring reads to avoid a missed queue wakeup.
>
> The temporary SQ/RQ peak is old + new. Later patches remove that peak for
> channel-count changes; full per-queue rebuilds still require it.
>
> Signed-off-by: Long Li <longli@microsoft.com>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260908032843.397667-1-longli@microsoft.com?part=3
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH net-next v4 01/13] net: mana: add queue-set allocation and teardown helpers
2026-09-08 3:28 ` [PATCH net-next v4 01/13] net: mana: add queue-set allocation and teardown helpers Long Li
@ 2026-09-09 3:29 ` sashiko-bot
0 siblings, 0 replies; 28+ messages in thread
From: sashiko-bot @ 2026-09-09 3:29 UTC (permalink / raw)
To: Long Li; +Cc: linux-hyperv, linux-rdma
> Add queue-set allocation and teardown helpers using a scratch port
> context, without releasing the vport. These prepare the reconfiguration
> paths to retain their running queues if replacement allocation fails.
>
> Extract the TX drain, retaining pcie_flr(). On reset failure, leave
> pending TX SKBs mapped; this does not prevent subsequent ring and RX
> teardown. Track successful drain-initiated resets and request best-effort
> sibling rebuilds, without reinitializing the HWC. Clear removed ports
> from ac->ports[] before those walks can reach them.
>
> The new queue-set helpers have no callers yet.
>
> Signed-off-by: Long Li <longli@microsoft.com>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260908032843.397667-1-longli@microsoft.com?part=1
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH net-next v4 09/13] net: mana: keep per-queue statistics in the port context
2026-09-08 3:28 ` [PATCH net-next v4 09/13] net: mana: keep per-queue statistics in the port context Long Li
@ 2026-09-09 3:29 ` sashiko-bot
0 siblings, 0 replies; 28+ messages in thread
From: sashiko-bot @ 2026-09-09 3:29 UTC (permalink / raw)
To: Long Li; +Cc: linux-hyperv, linux-rdma
> Move RX/TX counters into port-lifetime arrays so queue rebuilds do not
> reset them. Sum all max_queues slots for ndo_get_stats64(); keep the
> current queue count for ethtool's per-queue output.
>
> Retiring RX queues count into private drain_stats. Destruction and
> rollback fold these into separate port slots after writer quiescence.
> Report counters while down too, but gate the PHY query on port_is_up.
>
> DIM now samples the persistent live counters and skips retiring queues.
>
> Signed-off-by: Long Li <longli@microsoft.com>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260908032843.397667-1-longli@microsoft.com?part=9
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH net-next v4 10/13] net: mana: release EQs left idle by a channel-count reduction
2026-09-08 3:28 ` [PATCH net-next v4 10/13] net: mana: release EQs left idle by a channel-count reduction Long Li
@ 2026-09-09 3:29 ` sashiko-bot
0 siblings, 0 replies; 28+ messages in thread
From: sashiko-bot @ 2026-09-09 3:29 UTC (permalink / raw)
To: Long Li; +Cc: linux-rdma, linux-hyperv
> Release EQs above the live queue count after retiring queues are freed
> or replacement allocation fails. All CQs using those EQs must be gone.
> Return their vector allocations to the pool; IRQ registrations remain.
>
> Store each EQ's debugfs dentry in apc->eqs[] rather than a stack copy so
> shrinking can remove individual EQ directories.
>
> Signed-off-by: Long Li <longli@microsoft.com>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260908032843.397667-1-longli@microsoft.com?part=10
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH net-next v4 11/13] net: mana: keep a user-configured RSS table across a queue rebuild
2026-09-08 3:28 ` [PATCH net-next v4 11/13] net: mana: keep a user-configured RSS table across a queue rebuild Long Li
@ 2026-09-09 3:29 ` sashiko-bot
0 siblings, 0 replies; 28+ messages in thread
From: sashiko-bot @ 2026-09-09 3:29 UTC (permalink / raw)
To: Long Li; +Cc: linux-rdma, linux-hyperv
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
New issues:
- [High] Silent replacement of an invalid RSS table in `mana_alloc_queues` without notifying the ethtool core causes the table to be falsely treated as user-configured, permanently breaking default table auto-scaling.
Pre-existing issues:
- [High] Heap out-of-bounds read and write due to untrusted hardware configuration changing across resets.
--
commit 935252e88b1186422d52dcb7eb47455372ea3ed9
Author: Long Li <longli@microsoft.com>
Subject: net: mana: keep a user-configured RSS table across a queue rebuild
Preserve a user RSS table whenever all entries fit the requested queue
count. Regenerate driver defaults. On growth, a retained user table does
not steer RSS traffic to the added queues until the user updates it.
> diff --git a/drivers/net/ethernet/microsoft/mana/mana_en.c b/drivers/net/ethernet/microsoft/mana/mana_en.c
> index fc96837d69a00..78be88b29c99b 100644
> --- a/drivers/net/ethernet/microsoft/mana/mana_en.c
> +++ b/drivers/net/ethernet/microsoft/mana/mana_en.c
> @@ -3548,6 +3548,27 @@ static void mana_rss_table_init(struct mana_port_context *apc)
> ethtool_rxfh_indir_default(i, apc->num_queues);
> }
>
> +/* Keep user tables with valid indices; defer loss notification. */
> +static bool mana_rss_table_keep(struct mana_port_context *apc,
> + unsigned int num_queues, bool *lost)
> +{
> + u32 i;
> +
> + *lost = false;
> +
> + if (!netif_is_rxfh_configured(apc->ndev))
> + return false;
> +
> + for (i = 0; i < apc->indir_table_sz; i++) {
> + if (apc->indir_table[i] >= num_queues) {
[Severity: High]
This is a pre-existing issue, but can this out-of-bounds read occur if the
hardware configuration changes across resets?
If the hardware/firmware returns a larger num_indirection_ent during a
device reset (e.g., timeout recovery triggering mana_attach()),
mana_init_port() updates apc->indir_table_sz via mana_query_vport_cfg().
However, mana_attach() fails to reallocate apc->indir_table to accommodate
this potentially new size.
When mana_alloc_queues() is subsequently called, mana_rss_table_keep() and
mana_rss_table_init() iterate up to the new, larger apc->indir_table_sz,
causing an out-of-bounds read and write on the statically allocated buffer.
> + *lost = true;
> + return false;
> + }
> + }
> +
> + return true;
> +}
> +
[ ... ]
> @@ -3863,7 +3885,9 @@ int mana_alloc_queues(struct net_device *ndev)
> goto destroy_rxq;
> }
>
> - mana_rss_table_init(apc);
> + /* Loss notification needs a netdev instance lock we may lack. */
> + if (!mana_rss_table_keep(apc, apc->num_queues, &indir_lost))
> + mana_rss_table_init(apc);
[Severity: High]
Does this silent replacement of an invalid RSS table without notifying the
ethtool core permanently break default table auto-scaling?
If a user configures a custom RSS table, lowers the queue count while the
port is down, brings the port up, and then increases the queue count while
the port is up, the newly added queues might permanently receive no RSS
traffic.
Because the driver silently replaces the invalid custom table with a
smaller default table during the down/up cycle but fails to notify the
ethtool core (by intentionally omitting a call to ethtool_rxfh_indir_lost()),
the core remains desynchronized.
Later, when the queue count is increased, mana_grow_qset() falsely assumes
the small default table is the user's custom table and preserves it,
failing to scale the RSS indirection table to include the new queues.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260908032843.397667-1-longli@microsoft.com?part=11
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH net-next v4 12/13] net: mana: keep the surviving queues when the channel count is reduced
2026-09-08 3:28 ` [PATCH net-next v4 12/13] net: mana: keep the surviving queues when the channel count is reduced Long Li
@ 2026-09-09 3:29 ` sashiko-bot
0 siblings, 0 replies; 28+ messages in thread
From: sashiko-bot @ 2026-09-09 3:29 UTC (permalink / raw)
To: Long Li; +Cc: linux-rdma, linux-hyperv
> Split the live set into a kept prefix and a retiring tail. Reductions
> allocate only pointer arrays and steering tables, retaining the kept
> queues' page pools, buffers, NAPI state and XDP references.
>
> After publication, wait for TX-selection readers before freeing the old
> containers, then retire only the tail. Failed publication discards the
> new containers without freeing shared queues.
>
> Signed-off-by: Long Li <longli@microsoft.com>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260908032843.397667-1-longli@microsoft.com?part=12
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH net-next v4 13/13] net: mana: keep the existing queues when the channel count is raised
2026-09-08 3:28 ` [PATCH net-next v4 13/13] net: mana: keep the existing queues when the channel count is raised Long Li
@ 2026-09-09 3:29 ` sashiko-bot
0 siblings, 0 replies; 28+ messages in thread
From: sashiko-bot @ 2026-09-09 3:29 UTC (permalink / raw)
To: Long Li; +Cc: linux-rdma, linux-hyperv
> Keep existing queues and allocate only the added tail. Growing N to M
> now needs M SQ/RQ pairs at peak, rather than N + M.
>
> Track the fresh queues separately for failure cleanup and XDP references.
> Wait for TX-selection readers before freeing old containers, and clear
> slots during partial teardown. Full rebuilds now keep the current count.
>
> Advertise in-driver resize recovery after converting the live resize
> paths. Failed rollback still requires recovery.
>
> Signed-off-by: Long Li <longli@microsoft.com>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260908032843.397667-1-longli@microsoft.com?part=13
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH net-next v4 07/13] net: mana: swap queue sets in mana_xdp_set
2026-09-08 3:28 ` [PATCH net-next v4 07/13] net: mana: swap queue sets in mana_xdp_set Long Li
@ 2026-09-09 3:29 ` sashiko-bot
0 siblings, 0 replies; 28+ messages in thread
From: sashiko-bot @ 2026-09-09 3:29 UTC (permalink / raw)
To: Long Li; +Cc: linux-rdma, linux-hyperv
> Carry the XDP program with the queue set and install its per-queue
> references before redirecting RSS. This keeps the RX buffer layout and
> program together during publication and rollback.
>
> Do not replace the live program during allocation. This also avoids the
> pre-existing failed-preallocation stale-pointer bug; its standalone net
> fix is linked below.
>
> Link: https://lore.kernel.org/all/20260904202640.3900685-1-longli@microsoft.com/
> Signed-off-by: Long Li <longli@microsoft.com>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260908032843.397667-1-longli@microsoft.com?part=7
^ permalink raw reply [flat|nested] 28+ messages in thread
* RE: [EXTERNAL] Re: [PATCH net-next v4 04/13] net: mana: swap queue sets in mana_set_ringparam
2026-09-09 3:29 ` sashiko-bot
@ 2026-09-09 23:25 ` Long Li
0 siblings, 0 replies; 28+ messages in thread
From: Long Li @ 2026-09-09 23:25 UTC (permalink / raw)
To: sashiko-reviews@lists.linux.dev
Cc: linux-hyperv@vger.kernel.org, linux-rdma@vger.kernel.org
>
> Thank you for your contribution! Sashiko AI review found 1 potential issue(s)
> to consider:
> - [Medium] The patch introduces an ad-hoc locking pattern using a boolean
> flag (`apc->channel_changing`) to guard the reconfiguration critical section in
> `mana_set_ringparam`.
> --
>
> commit 40668ebf41a83bf44835ffb33803930c039ce405
> Author: Long Li <longli@microsoft.com>
>
> net: mana: swap queue sets in mana_set_ringparam
>
> This commit replaces detach/attach operations in mana_set_ringparam() with
> queue-set allocation and publication. Failed allocations now leave the
> running queues and ring sizes unchanged instead of risking a detached port.
>
> > diff --git a/drivers/net/ethernet/microsoft/mana/mana_ethtool.c
> > b/drivers/net/ethernet/microsoft/mana/mana_ethtool.c
> > --- a/drivers/net/ethernet/microsoft/mana/mana_ethtool.c
> > +++ b/drivers/net/ethernet/microsoft/mana/mana_ethtool.c
> [ ... ]
> > @@ -774,32 +772,56 @@ static int mana_set_ringparam(struct net_device
> *ndev,
> > netdev_info(ndev, "Using nearest power of 2 values for Txq:%d
> Rxq:%d\n",
> > new_tx, new_rx);
> >
> > - /* pre-allocating new buffers to prevent failures in mana_attach()
> later */
> > - apc->rx_queue_size = new_rx;
> > - err = mana_pre_alloc_rxbufs(apc, ndev->mtu, apc->num_queues);
> > - apc->rx_queue_size = old_rx;
> > - if (err) {
> > - netdev_err(ndev, "Insufficient memory for new
> allocations\n");
> > - return err;
> > + if (new_rx == apc->rx_queue_size && new_tx == apc->tx_queue_size)
> > + return 0;
> > +
> > + if (!apc->port_is_up) {
> > + apc->rx_queue_size = new_rx;
> > + apc->tx_queue_size = new_tx;
> > + return 0;
> > }
> >
> > - err = mana_detach(ndev, false);
> > - if (err) {
> > - netdev_err(ndev, "mana_detach failed: %d\n", err);
> > - goto out;
> > + /* Exclude RDMA through failure cleanup, which may release the
> vport. */
> > + mutex_lock(&apc->vport_mutex);
> > + if (apc->channel_changing) {
> > + mutex_unlock(&apc->vport_mutex);
> > + return -EBUSY;
> > + }
> > + apc->channel_changing = true;
> > + mutex_unlock(&apc->vport_mutex);
>
> [Severity: Medium]
> Does this code introduce an ad-hoc locking pattern using the
> apc->channel_changing boolean flag?
>
> The networking drivers subsystem guidelines indicate that using a boolean
> flag set on entry and cleared on exit to guard a reconfiguration critical section
> bypasses lockdep and provides weaker synchronization guarantees than using
> a proper lock.
>
> Could a standard kernel synchronization primitive be used for this critical
> section in mana_set_ringparam() instead of relying on the
> apc->channel_changing flag?
This locking pattern has existed in the base code for a long time; this is not introduced by this patch.
This patch just uses this locking pattern for the vport changing states.
>
> [ ... ]
> > +clear_flag:
> > + mutex_lock(&apc->vport_mutex);
> > + apc->channel_changing = false;
> > + mutex_unlock(&apc->vport_mutex);
> > return err;
> > }
>
> --
> Sashiko AI
> review * https://nam06.safelinks.protection.outlook.com/?url=https%3A%2F%
> 2Fsashiko.dev%2F%23%2Fpatchset%2F20260908032843.397667-1-
> longli%40microsoft.com%3Fpart%3D4&data=05%7C02%7Clongli%40microsoft.
> com%7Cc67f289fbf6d44697aa108df0e229871%7C72f988bf86f141af91ab2d7cd
> 011db47%7C1%7C0%7C639245213896893836%7CUnknown%7CTWFpbGZsb3
> d8eyJFbXB0eU1hcGkiOnRydWUsIlYiOiIwLjAuMDAwMCIsIlAiOiJXaW4zMiIsIkFOI
> joiTWFpbCIsIldUIjoyfQ%3D%3D%7C0%7C%7C%7C&sdata=bb9IsrgM5js%2BYCF
> uaioyc5OMJmhNDee3ut65iShCesk%3D&reserved=0
^ permalink raw reply [flat|nested] 28+ messages in thread
end of thread, other threads:[~2026-09-09 23:25 UTC | newest]
Thread overview: 28+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-08 3:28 [PATCH net-next v4 00/13] net: mana: reconfigure by replacing the queue set Long Li
2026-09-08 3:28 ` [PATCH net-next v4 01/13] net: mana: add queue-set allocation and teardown helpers Long Li
2026-09-09 3:29 ` sashiko-bot
2026-09-08 3:28 ` [PATCH net-next v4 02/13] net: mana: share the EQ pool across a queue-set swap Long Li
2026-09-09 3:29 ` sashiko-bot
2026-09-08 3:28 ` [PATCH net-next v4 03/13] net: mana: swap queue sets in mana_set_channels Long Li
2026-09-09 3:29 ` sashiko-bot
2026-09-08 3:28 ` [PATCH net-next v4 04/13] net: mana: swap queue sets in mana_set_ringparam Long Li
2026-09-09 3:29 ` sashiko-bot
2026-09-09 23:25 ` [EXTERNAL] " Long Li
2026-09-08 3:28 ` [PATCH net-next v4 05/13] net: mana: swap queue sets in mana_set_priv_flags Long Li
2026-09-09 3:29 ` sashiko-bot
2026-09-08 3:28 ` [PATCH net-next v4 06/13] net: mana: swap queue sets in mana_change_mtu Long Li
2026-09-09 3:29 ` sashiko-bot
2026-09-08 3:28 ` [PATCH net-next v4 07/13] net: mana: swap queue sets in mana_xdp_set Long Li
2026-09-09 3:29 ` sashiko-bot
2026-09-08 3:28 ` [PATCH net-next v4 08/13] net: mana: do not bail out of mana_detach on dealloc failure Long Li
2026-09-09 3:29 ` sashiko-bot
2026-09-08 3:28 ` [PATCH net-next v4 09/13] net: mana: keep per-queue statistics in the port context Long Li
2026-09-09 3:29 ` sashiko-bot
2026-09-08 3:28 ` [PATCH net-next v4 10/13] net: mana: release EQs left idle by a channel-count reduction Long Li
2026-09-09 3:29 ` sashiko-bot
2026-09-08 3:28 ` [PATCH net-next v4 11/13] net: mana: keep a user-configured RSS table across a queue rebuild Long Li
2026-09-09 3:29 ` sashiko-bot
2026-09-08 3:28 ` [PATCH net-next v4 12/13] net: mana: keep the surviving queues when the channel count is reduced Long Li
2026-09-09 3:29 ` sashiko-bot
2026-09-08 3:28 ` [PATCH net-next v4 13/13] net: mana: keep the existing queues when the channel count is raised Long Li
2026-09-09 3:29 ` sashiko-bot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox