Linux RDMA and InfiniBand development
 help / color / mirror / Atom feed
From: netdev-bot+sashiko@kernel.org
To: longli@microsoft.com
Cc: kotaranov@microsoft.com, kuba@kernel.org, davem@davemloft.net,
	pabeni@redhat.com, edumazet@google.com, andrew+netdev@lunn.ch,
	jgg@ziepe.ca, leon@kernel.org, haiyangz@microsoft.com,
	kys@microsoft.com, wei.liu@kernel.org, decui@microsoft.com,
	shradhagupta@linux.microsoft.com, horms@kernel.org,
	ernis@linux.microsoft.com, stephen@networkplumber.org,
	shirazsaleem@microsoft.com, netdev@vger.kernel.org,
	linux-rdma@vger.kernel.org, linux-hyperv@vger.kernel.org,
	linux-kernel@vger.kernel.org
Subject: Re: [PATCH net-next v3 02/13] net: mana: share the EQ pool across a queue-set swap
Date: Fri, 04 Sep 2026 04:46:39 +0000	[thread overview]
Message-ID: <178849719939.4131868.8782384593868319521@kernel.org> (raw)
In-Reply-To: <20260901014442.2945689-3-longli@microsoft.com>

Thank you for your contribution! Sashiko AI review found 3 potential
issue(s) to consider:

Critical: 0 · High: 0 · Medium: 2 · Low: 1

- [Medium] The patch makes the EQ pool port-owned and grows it against
  the *live* port context from inside the allocation phase, while the…
- [Medium] mana_grow_eqs()
  (drivers/net/ethernet/microsoft/mana/mana_en.c:1835) is the first NIC…
- [Low] mana_qset_scratch_alloc()'s kernel-doc
  (drivers/net/ethernet/microsoft/mana/mana_en.c:3983-3985) still…

--

net: mana: share the EQ pool across a queue-set swap

The EQ array moves from the queue set to the port, so a queue-set swap no
longer needs old + new MSI-X vectors. mana_create_eq() now sizes the array
to apc->max_queues and tracks how many EQs exist in apc->num_eqs, and a new
mana_grow_eqs() adds EQs on demand from mana_alloc_qset().

> diff --git a/drivers/net/ethernet/microsoft/mana/mana_en.c b/drivers/net/ethernet/microsoft/mana/mana_en.c
> index b9d9543d1a6dc..bd80658cf3efc 100644
> --- a/drivers/net/ethernet/microsoft/mana/mana_en.c
> +++ b/drivers/net/ethernet/microsoft/mana/mana_en.c

[ ... ]

> @@ -1814,6 +1821,72 @@ int mana_create_eq(struct mana_port_context *apc)
>  }
>  EXPORT_SYMBOL_NS(mana_create_eq, "NET_MANA");
>  
> +/**
> + * mana_grow_eqs - make sure the port has at least @need EQs
> + * @apc:  port context
> + * @need: number of EQs the new queue set requires
> + *
> + * EQs are bound to MSI-X vectors, so the pool is port-owned and shared across
> + * a swap: peak usage is max(old, new), not the sum. Grow-only, up to
> + * apc->max_queues.
> + *
> + * Return: 0 on success, negative error code on failure.
> + */
> +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);

[Severity: Medium]
This looks like the first NIC path that creates an EQ while the port is up
and its MSI-X vectors are already delivering interrupts.  Can a vector
interrupt land on the EQ before it is initialized?

mana_gd_create_mana_eq() -> mana_gd_create_eq() publishes the queue to the
handler list first:

gdma_main.c:mana_gd_create_eq() {
	...
	queue->id = INVALID_QUEUE_ID;
	...
	err = mana_gd_register_irq(queue, spec);
	...
	queue->eq.callback = spec->eq.callback;
	queue->eq.context = spec->eq.context;
	queue->head |= INITIALIZED_OWNER_BIT(log2_num_entries);
	...
}

gdma_main.c:mana_gd_register_irq() {
	...
	spin_lock_irqsave(&gic->lock, flags);
	list_add_rcu(&queue->entry, &gic->eq_list);
	spin_unlock_irqrestore(&gic->lock, flags);
}

and the hardirq handler walks that list under RCU only:

gdma_main.c:mana_gd_intr() {
	rcu_read_lock();
	list_for_each_entry_rcu(eq, eq_list, entry) {
		gic->handler(eq);
	}
	rcu_read_unlock();
}

With head still 0 and a zeroed ring, does mana_gd_process_eq_events() miss
its early return?

gdma_main.c:mana_gd_process_eq_events() {
	...
		old_bits = (eq->head / num_eqe - 1) & GDMA_EQE_OWNER_MASK;
		/* No more entries */
		if (owner_bits == old_bits) {
			if (i == 0)
				return;
	...
}

old_bits computes as 7 while owner_bits reads 0, so the handler would
consume zeroed EQEs, advance eq->head and ring the doorbell while
queue->id is still INVALID_QUEUE_ID.  The window spans the
mana_gd_create_hw_eq() and mana_gd_test_eq() HWC round trip.

The sharing precondition holds when gc->msi_sharing is set, and also when
an RDMA EQ already sits on the same index, since that path takes the vector
without setting the NIC bitmap bit:

drivers/infiniband/hw/mana/main.c:mana_ib_create_eqs() {
	...
		gic = mana_gd_get_gic(gc, false, &msi);
	...
}

Would this show up as an intermittent EQ creation failure in
mana_gd_test_eq(), or as missed events on the new queue set?  gdma_main.c is
unchanged across this series, and mana_grow_eqs() is still called on a live
port by mana_grow_qset() at the end of it.

> +		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:
> +	/* Keep whatever was created: the running queue set still needs its
> +	 * own EQs, and the extras are reused by the next attempt.
> +	 */
> +	return err;
> +}
> +

[ ... ]

> @@ -3926,14 +3996,14 @@ struct mana_port_context *mana_qset_scratch_alloc(struct mana_port_context *apc)
>  
>  	*scratch = *apc;
>  
> -	/* Owns no queues yet. */
> -	scratch->eqs		= NULL;
> +	/* EQs stay shared with the live port: they are a vector-backed
> +	 * resource and must not be duplicated for the new set.
> +	 */
>  	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;

[Severity: Low]
With these two assignments gone, the kernel-doc just above
mana_qset_scratch_alloc() no longer matches the code:

 * Builds a heap copy of @apc that shares its vport identity but owns no
 * queues, so the existing allocators and destroyers can run against it
 * without touching the live context.

After *scratch = *apc the scratch aliases apc->eqs, apc->num_eqs and the
live apc->mana_eqs_debugfs dentry.  mana_destroy_eq() is an exported
NET_MANA symbol taking any struct mana_port_context *, and it does
debugfs_remove_recursive(apc->mana_eqs_debugfs) plus destroy and
kfree(apc->eqs), so running it against a scratch as the doc invites would
tear down the live port's EQs and EQ debugfs tree.

No caller does that today, either here or at the end of the series, so this
is a stale comment rather than a live bug.  Should the wording call out
that EQs and the EQ debugfs dentry are the exception?

>  
>  	/* Never consume the live set's pre-allocated RX buffers; the swap path
>  	 * has no post-teardown allocation to de-risk.
> @@ -3959,7 +4029,8 @@ void mana_qset_scratch_free(struct mana_port_context *scratch)
>   * installed set keeps serving traffic meanwhile. On error nothing is left
>   * allocated.
>   */
> -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)
>  {
> @@ -3981,13 +4052,20 @@ int mana_alloc_qset(struct mana_port_context *scratch, unsigned int num_queues,
>  	if (err)
>  		goto cleanup_rxq_array;
>  
> -	err = mana_create_eq(scratch);
> +	/* Grow the port's shared EQ pool if this set needs more. The pool
> +	 * belongs to @apc, not to either queue set, so both sets can be
> +	 * live at once without double-booking MSI-X vectors.
> +	 */
> +	err = mana_grow_eqs(apc, num_queues);
>  	if (err)
>  		goto cleanup_rss;
>  
> +	scratch->eqs = apc->eqs;
> +	scratch->num_eqs = apc->num_eqs;
> +

[Severity: Medium]
Two documented contracts appear to be contradicted by this call.

The comment retained right above mana_alloc_qset() still says:

 * installed set keeps serving traffic meanwhile. On error nothing is left
 * allocated.

but the cleanup_eq label is deleted here and mana_grow_eqs() ends with:

out:
	/* Keep whatever was created: the running queue set still needs its
	 * own EQs, and the extras are reused by the next attempt.
	 */
	return err;

so a failed rebuild permanently raises apc->num_eqs and the port's MSI-X
footprint.  Should the "on error nothing is left allocated" wording be
adjusted, or the growth unwound?

The second one is the header comment this patch rewrites (see the
include/net/mana/mana.h hunk below): mana_grow_eqs(apc, num_queues) writes
apc->eqs[i].eq and apc->num_eqs and creates children under the live
apc->mana_eqs_debugfs while the old set is installed and TX is enabled, not
inside mana_publish_qset().

For what it is worth, the "nothing is left allocated" half is repaired
later in this series: mana_alloc_qset() stops growing EQs and
mana_grow_qset() unwinds with mana_shrink_eqs(apc, apc->num_queues) on
failure.  The header claim about where the live context is mutated still
reads the same at the end of the series, while mana_grow_qset() keeps
calling mana_grow_eqs() during allocation.

>  	err = mana_create_txq(scratch, ndev);
>  	if (err)
> -		goto cleanup_eq;
> +		goto cleanup_rss;
>  
>  	err = mana_add_rx_queues(scratch, ndev);
>  	if (err)
> @@ -4005,8 +4083,6 @@ int mana_alloc_qset(struct mana_port_context *scratch, unsigned int num_queues,
>  	 */
>  	mana_destroy_rxqs(scratch);
>  	mana_destroy_txq(scratch);
> -cleanup_eq:
> -	mana_destroy_eq(scratch);
>  cleanup_rss:
>  	mana_cleanup_indir_table(scratch);
>  cleanup_rxq_array:

[ ... ]

> diff --git a/include/net/mana/mana.h b/include/net/mana/mana.h
> index 356aaa652fa6f..2117a30116c26 100644
> --- a/include/net/mana/mana.h
> +++ b/include/net/mana/mana.h

[ ... ]

> @@ -710,13 +714,14 @@ int mana_attach(struct net_device *ndev);
>  int mana_detach(struct net_device *ndev, bool from_close);
>  
>  /* Pre-allocate + swap reconfiguration. Allocation and teardown run against a
> - * scratch context, so the live port context is only ever mutated with TX
> - * disabled.
> + * scratch context, so the live port context is mutated only inside
> + * mana_publish_qset() with TX disabled. Both sets share a port-owned EQ pool.
>   */

Is this claim accurate once mana_grow_eqs() exists?  The allocation phase
mutates the live @apc: apc->eqs[i].eq, apc->num_eqs and new debugfs
children under apc->mana_eqs_debugfs are all written from
mana_alloc_qset() -> mana_grow_eqs(), with the old set still installed and
TX enabled, so the mutation is not confined to mana_publish_qset().

>  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);

-- 
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260901014442.2945689-1-longli%40microsoft.com

  reply	other threads:[~2026-09-04  4:46 UTC|newest]

Thread overview: 27+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-01  1:44 [PATCH net-next v3 00/13] net: mana: reconfigure by replacing the queue set Long Li
2026-09-01  1:44 ` [PATCH net-next v3 01/13] net: mana: add queue-set allocation and teardown helpers Long Li
2026-09-04  4:46   ` netdev-bot+sashiko
2026-09-01  1:44 ` [PATCH net-next v3 02/13] net: mana: share the EQ pool across a queue-set swap Long Li
2026-09-04  4:46   ` netdev-bot+sashiko [this message]
2026-09-01  1:44 ` [PATCH net-next v3 03/13] net: mana: swap queue sets in mana_set_channels Long Li
2026-09-04  4:46   ` netdev-bot+sashiko
2026-09-01  1:44 ` [PATCH net-next v3 04/13] net: mana: swap queue sets in mana_set_ringparam Long Li
2026-09-04  4:46   ` netdev-bot+sashiko
2026-09-01  1:44 ` [PATCH net-next v3 05/13] net: mana: swap queue sets in mana_set_priv_flags Long Li
2026-09-04  4:46   ` netdev-bot+sashiko
2026-09-01  1:44 ` [PATCH net-next v3 06/13] net: mana: swap queue sets in mana_change_mtu Long Li
2026-09-04  4:46   ` netdev-bot+sashiko
2026-09-01  1:44 ` [PATCH net-next v3 07/13] net: mana: swap queue sets in mana_xdp_set Long Li
2026-09-04  4:46   ` netdev-bot+sashiko
2026-09-01  1:44 ` [PATCH net-next v3 08/13] net: mana: do not bail out of mana_detach on dealloc failure Long Li
2026-09-04  4:46   ` netdev-bot+sashiko
2026-09-01  1:44 ` [PATCH net-next v3 09/13] net: mana: keep per-queue statistics in the port context Long Li
2026-09-04  4:46   ` netdev-bot+sashiko
2026-09-01  1:44 ` [PATCH net-next v3 10/13] net: mana: release EQs left idle by a channel-count reduction Long Li
2026-09-04  4:46   ` netdev-bot+sashiko
2026-09-01  1:44 ` [PATCH net-next v3 11/13] net: mana: keep a user-configured RSS table across a queue rebuild Long Li
2026-09-04  4:46   ` netdev-bot+sashiko
2026-09-01  1:44 ` [PATCH net-next v3 12/13] net: mana: keep the surviving queues when the channel count is reduced Long Li
2026-09-04  4:46   ` netdev-bot+sashiko
2026-09-01  1:44 ` [PATCH net-next v3 13/13] net: mana: keep the existing queues when the channel count is raised Long Li
2026-09-04  4:46   ` netdev-bot+sashiko

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=178849719939.4131868.8782384593868319521@kernel.org \
    --to=netdev-bot+sashiko@kernel.org \
    --cc=andrew+netdev@lunn.ch \
    --cc=davem@davemloft.net \
    --cc=decui@microsoft.com \
    --cc=edumazet@google.com \
    --cc=ernis@linux.microsoft.com \
    --cc=haiyangz@microsoft.com \
    --cc=horms@kernel.org \
    --cc=jgg@ziepe.ca \
    --cc=kotaranov@microsoft.com \
    --cc=kuba@kernel.org \
    --cc=kys@microsoft.com \
    --cc=leon@kernel.org \
    --cc=linux-hyperv@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-rdma@vger.kernel.org \
    --cc=longli@microsoft.com \
    --cc=netdev@vger.kernel.org \
    --cc=pabeni@redhat.com \
    --cc=shirazsaleem@microsoft.com \
    --cc=shradhagupta@linux.microsoft.com \
    --cc=stephen@networkplumber.org \
    --cc=wei.liu@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox