Netdev List
 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 01/13] net: mana: add queue-set allocation and teardown helpers
Date: Fri, 04 Sep 2026 04:46:37 +0000	[thread overview]
Message-ID: <178849719763.4131868.4917085699227078071@kernel.org> (raw)
In-Reply-To: <20260901014442.2945689-2-longli@microsoft.com>

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

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

- [High] mana_drain_txqs() refuses to unmap/free pending TX SKBs when the
  fallback pci_try_reset_function() fails ("leaking pending TX buffers…
- [High] The drain's fallback reset was changed from an unconditional
  pcie_flr() to pci_try_reset_function(), which starts with…
- [Medium] The commit message claims "No functional change otherwise:
  nothing calls the new helpers yet", but the TX-drain rewrite changes…
- [Medium] After a successful function reset invalidates every port's
  queues, the sibling recovery this patch newly relies on can exit…
- [Medium] The drain fallback issues a PCI function-level reset and, on
  success, treats the function as usable: it bumps reset_gen, continues…
- [Low] include/net/mana/mana.h documents mana_context.reset_gen as
  "Bumped on every PCI function reset", and mana_drain_txqs() uses a…
- [Low] The commit message presents "That tears the vport down, so RDMA
  can claim it while released" as applying to all of the named…
- [Low] The live-path change from a bare pcie_flr() to a PCI-core-managed
  function reset is a user-visible correctness fix (the bare FLR wipes…

--

net: mana: add queue-set allocation and teardown helpers

This adds a mana_qset data model plus mana_qset_scratch_alloc(),
mana_alloc_qset() and mana_free_qset() so a queue set can be built and
retired against a scratch port context rather than by tearing the vport
down. It also moves the TX drain out of mana_dealloc_queues() into a new
mana_drain_txqs() and replaces the fallback pcie_flr() with
pci_try_reset_function().

No Link: tags are present in the commit message.

A few questions on the commit message first, then some on the diff.

> The ethtool reconfiguration paths (channel count, ring size, private
> flags), mana_change_mtu() and mana_xdp_set() rebuild the queues with
> mana_detach() then mana_attach(). That tears the vport down, so RDMA can
> claim it while released, and a failed mana_attach() leaves the port down
> with no way back but manual intervention.

[Severity: Low]
This isn't a bug, but does the RDMA part of that sentence hold for all of
the listed paths?

mana_set_channels() and mana_set_priv_flags() in mana_ethtool.c already
set apc->channel_changing = true across their detach/attach window:

	/* Block RDMA from grabbing the vport during detach/attach */
	apc->channel_changing = true;

and mana_cfg_vport() returns -EBUSY while that flag is set. The RDMA
exposure argument seems to apply to mana_set_ringparam(),
mana_change_mtu() and mana_xdp_set() only, while the failed-attach
argument applies to all of them. Could the wording be narrowed?

> The TX drain moves out of mana_dealloc_queues() so the new teardown path
> gets it too, and its fallback reset becomes pci_try_reset_function()
> rather than an open-coded pcie_flr(), which does not save and restore
> config space. Trylock because this runs under RTNL while removal takes
> the device lock first.

[Severity: Low]
This part reads as a correctness fix to the pre-existing fallback reset in
mana_dealloc_queues(), but the patch carries no Fixes: tag and declares no
functional change. Would it be better as a separate commit so it can be
considered for stable on its own?

> No functional change otherwise: nothing calls the new helpers yet.

[Severity: Medium]
Is the "no functional change otherwise" claim accurate for the live
teardown path (mana_close()/mana_detach()/mana_remove())? Three behaviour
changes seem to ride along:

- On a failed fallback reset, mana_drain_txqs() returns before the
  skb_dequeue() loop, so pending TX SKBs and their DMA mappings are now
  deliberately leaked. The baseline code ran that loop unconditionally
  after the pcie_flr() attempt.

- On a successful reset, mana_dealloc_queues() now walks ac->ports[] and
  queues sib->queue_reset_work for every other port, so unrelated netdevs
  get their queues torn down and rebuilt.

- mana_remove() now clears ac->ports[i], which is only justified by a code
  comment.

Could these be called out in the changelog?

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

[ ... ]

> @@ -3679,15 +3684,115 @@ int mana_attach(struct net_device *ndev)
>  	return 0;
>  }
>  
> -static int mana_dealloc_queues(struct net_device *ndev)
> +/* Drain a set about to be destroyed: nothing new can reach it, so wait for the
> + * hardware to finish what it owns, then release every mapped SKB.
> + *
> + * The 120s budget is shared across all queues. On timeout the device is reset,
> + * since its buffers are about to be freed while it may still DMA into them; if
> + * that fails too they are leaked.
> + *
> + * Returns true only if a reset happened, taking every queue on the function
> + * down with it.
> + */
> +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;
> +
> +		/* The function was reset after this queue was created, so the
> +		 * device has stopped touching its buffers and the completions
> +		 * waited for below can never arrive. Without this the port
> +		 * would burn the full timeout under RTNL, then reset the
> +		 * function again on the way out.
> +		 */
> +		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)) {
> +			/* The device still owns these buffers, so reset it
> +			 * before they are freed. pci_try_reset_function()
> +			 * rather than pcie_flr(): it saves and restores config
> +			 * space, which a bare FLR wipes behind the PCI core's
> +			 * back. Trylock because RTNL is held here while the
> +			 * remove path takes the device lock first.
> +			 */
> +			err = pci_try_reset_function(to_pci_dev(gc->dev));

[Severity: High]
Can this reset ever happen on the remove path? mana_gd_remove() is the PCI
.remove callback, and the driver core invokes it with device_lock held:

mana_gd_remove() -> mana_remove() -> mana_detach() -> mana_dealloc_queues()
  -> mana_drain_txqs()

and pci_try_reset_function() begins with:

	if (!pci_dev_trylock(dev))
		return -EAGAIN;

on a mutex the calling thread already owns, so it looks like it can only
return -EAGAIN there. The old pcie_flr() had no lock dependency and did
stop the device, so on unbind with un-drained TX the device now appears to
never be reset while its queues are still destroyed below. Would
pci_reset_function_locked() (or __pci_reset_function_locked()) be the right
call for a caller that is already under device_lock?

> +			if (err) {
> +				netdev_err(apc->ndev,
> +					   "function reset failed: %d, %d pkts pending in txq %u\n",
> +					   err,
> +					   atomic_read(&txq->pending_sends),
> +					   txq->gdma_txq_id);
> +				quiesced = false;
> +			} else {
> +				/* Every queue on the function is dead now,
> +				 * including the ones this loop has not reached
> +				 * and those of the other ports.
> +				 */
> +				WRITE_ONCE(apc->ac->reset_gen,
> +					   apc->ac->reset_gen + 1);
> +
> +				/* Only a reset that actually happened takes the
> +				 * other ports down with it; reporting a failed
> +				 * one would rebuild them for nothing.
> +				 */
> +				reset = true;
> +			}
> +			break;
> +		}
> +	}
> +
> +	/* Only a reset that actually happened makes freeing these safe; without
> +	 * one the device still owns them. Leak instead, bounded at one SQ ring
> +	 * of skbs per queue.
> +	 */
> +	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;
> +	}

[Severity: High]
Does this leak-instead-of-unmap decision reach the callers?

The return value is reset, which is false both for a healthy drain and for
a failed reset, so neither mana_dealloc_queues() nor mana_free_qset() can
tell the two apart. Both then run unconditionally:

	mana_fence_rqs(apc);
	mana_destroy_rxqs(apc);
	mana_destroy_txq(apc);
	mana_destroy_eq(apc);

and mana_gd_destroy_queue() in gdma_main.c releases the SQ/CQ/EQ ring
memory regardless of whether the firmware-side destroy succeeded:

	mana_gd_destroy_dma_region(gc, gmi->dma_region_handle);
	mana_gd_free_memory(gmi);
	kfree(queue);

while mana_destroy_rxq() unmaps and frees the RX pages. So the pending SKB
mappings are kept, but the descriptor rings and RX buffers of the same
un-quiesced device go back to the page allocator. Can the device DMA into
that memory after it is reused?

> +
> +	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;
> +}

[ ... ]

> @@ -3705,41 +3810,27 @@ static int mana_dealloc_queues(struct net_device *ndev)
>  	 * 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.
> +	 *
> +	 * If the drain had to reset the function to get there, every other
> +	 * port on the adapter lost its queues too, so schedule them for a
> +	 * rebuild. This port is being torn down here and needs no such
> +	 * treatment, and a down port stays down: with port_st_save false,
> +	 * detach and attach both skip the queue work.
>  	 */
> +	if (mana_drain_txqs(apc)) {
> +		struct mana_context *ac = apc->ac;
> +		unsigned int i;

[ ... ]

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

[Severity: Medium]
Can this rebuild silently not happen? mana_per_port_queue_reset_work_handler()
gives up before detach/attach when the RX pre-allocation fails:

	err = mana_pre_alloc_rxbufs(apc, ndev->mtu, apc->num_queues);
	if (err) {
		netdev_err(ndev, "Insufficient memory for reset post tx stall detection\n");
		goto clear_flag;
	}

There is no retry and no state change on that branch, so under memory
pressure the sibling netdev stays with port_is_up true and
apc->tx_qp[]/apc->rxqs[] pointing at queues the function reset destroyed,
until someone does a manual ifdown/ifup. Should this branch requeue the
work or mark the port down?

> @@ -3763,6 +3854,278 @@ static int mana_dealloc_queues(struct net_device *ndev)
>  	return 0;
>  }
>  

[ ... ]

> +void mana_free_qset(struct mana_port_context *scratch, struct mana_qset *qset)
> +{

[ ... ]

> +	if (mana_drain_txqs(scratch)) {
> +		/* The drain had to reset the function to stop the device
> +		 * touching those buffers. A function reset takes down every
> +		 * port on the adapter, not just this one, so rebuild them all
> +		 * - the same recovery mana_tx_timeout() relies on. A port that
> +		 * is already down has nothing to rebuild and its handler
> +		 * leaves it down.
> +		 */

[Severity: Medium]
After the function reset, what re-establishes the GDMA/HWC state that the
scheduled rebuilds need?

The driver registers no pci_error_handlers, so there is no reset_prepare or
reset_done callback, and pci_try_reset_function() only saves and restores
config space. The only path that re-bootstraps the device is:

mana_serv_reset() -> mana_gd_suspend()/mana_gd_resume()
  -> mana_gd_cleanup_device()/mana_gd_setup()

and that is not invoked here. Each queued queue_reset_work then re-enters
mana_attach() and issues HWC requests over a channel the reset destroyed,
while gc->cq_table[], the EQ/MSI-X bookkeeping and the cached vport and
queue handles still describe objects that no longer exist. The mana_ib
auxiliary device on the same function is also not told.

Is the comment's "the same recovery mana_tx_timeout() relies on" accurate?
That path never resets the function, so its HWC is still alive when the
rebuild runs.

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

[ ... ]

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

[ ... ]

> @@ -537,6 +547,14 @@ struct mana_context {
>  	u8 bm_hostmode;
>  
>  	struct mana_ethtool_hc_stats hc_stats;
> +
> +	/* Bumped on every PCI function reset. A queue created before the
> +	 * current value can no longer be reached by the device, so its buffers
> +	 * need no drain. All access is under RTNL: the bump is a non-atomic
> +	 * read-modify-write, so a second writer would lose an increment.
> +	 */
> +	u32 reset_gen;

[Severity: Low]
Is "bumped on every PCI function reset" true? The only writer is
mana_drain_txqs(), right after its own successful pci_try_reset_function().

Resets initiated elsewhere leave it unchanged, for example:

  echo 1 > /sys/bus/pci/devices/<bdf>/reset

as well as secondary-bus/slot resets, EEH/AER recovery, and the driver's
own mana_serv_reset() -> mana_gd_suspend()/mana_gd_resume() cycle. There
is no reset_done callback anywhere under drivers/net/ethernet/microsoft/mana/
that would observe them, so mana_drain_txqs() still burns the full 120s
under RTNL and then resets the function again.

The effect is conservative rather than unsafe, so would either adding a
reset_done handler or narrowing the comment to resets issued by
mana_drain_txqs() be preferable?

-- 
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: 28+ 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 [this message]
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
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
2026-09-05  0:57 ` [PATCH net-next v3 00/13] net: mana: reconfigure by replacing the queue set Jakub Kicinski

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=178849719763.4131868.4917085699227078071@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