Linux RDMA and InfiniBand development
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Sahil Chandna" <sahilchandna@linux.microsoft.com>,
	linux-hyperv@vger.kernel.org, linux-rdma@vger.kernel.org
Subject: Re: [PATCH net] net: mana: Avoid waiting on dead HWC after device reset
Date: Mon, 26 Oct 2026 07:29:06 +0000	[thread overview]
Message-ID: <sashiko-outbox-184659@kernel.org> (raw)
In-Reply-To: <20261025072838.1991785-1-sahilchandna@linux.microsoft.com>

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] net: mana: non-waiting CQ poll in mana_dealloc_queues() triggers false FLR
- [Medium] net: mana: mana_hwc_send_request() blocks on hwc->sema before checking hwc_dead
--

commit 7823c203253d16c1fb640cce922ae30200b9f486
Author: Sahil Chandna <sahilchandna@linux.microsoft.com>

net: mana: Avoid waiting on dead HWC after device reset

This commit replaces the sleep-based TX queue drain in mana_dealloc_queues()
with synchronous CQE polling and marks the hardware channel dead via hwc_dead
after a device reset so subsequent HWC commands fail fast with -ETIMEDOUT.

> diff --git a/drivers/net/ethernet/microsoft/mana/hw_channel.c b/drivers/net/ethernet/microsoft/mana/hw_channel.c
> index 3bca4b683134b..c9337f49fbe15 100644
> --- a/drivers/net/ethernet/microsoft/mana/hw_channel.c
> +++ b/drivers/net/ethernet/microsoft/mana/hw_channel.c

[ ... ]

> @@ -862,6 +867,7 @@ int mana_hwc_send_request(struct hw_channel_context *hwc, u32 req_len,
>  	u32 dest_vrcq;
>  	u32 dest_vrq;
>  	u32 command;
> +	u32 timeout = 0;
>  	u16 msg_id;
>  	int err;
>  
> @@ -897,6 +903,17 @@ int mana_hwc_send_request(struct hw_channel_context *hwc, u32 req_len,
>  	dest_vrq = hwc->dest_vrq_id;
>  	dest_vrcq = hwc->dest_vrcq_id;
>  
> +	/* If the HWC is unavailable (e.g. hwc_timeout was set to 0 or
> +	 * hwc_dead set after device reset), do not post the WQE: the
> +	 * hardware cannot respond, so fail fast instead of waiting out
> +	 * the full timeout.
> +	 */
> +	timeout = READ_ONCE(hwc->hwc_timeout);
> +	if (timeout == 0 || READ_ONCE(hwc->hwc_dead)) {
> +		err = -ETIMEDOUT;
> +		goto out;
> +	}

[Severity: Medium]
Can mana_hwc_send_request() still block for the full hwc_timeout on
hwc->sema before reaching this hwc_dead check?

At the start of mana_hwc_send_request(), mana_hwc_get_msg_index() is called:

    mana_hwc_send_request()
      -> mana_hwc_get_msg_index()
           -> down(&hwc->sema);

Because hwc->sema is initialized to 1 (HW_CHANNEL_VF_BOOTSTRAP_QUEUE_DEPTH),
only one HWC command can be in flight at a time.

While the device is active, periodic work such as
mana_gf_stats_work_handler() (running every 2 seconds without holding
rtnl_lock) or auxiliary RDMA (mana_ib) callers can enter
mana_hwc_send_request(), acquire hwc->sema, and sleep in
wait_for_completion_timeout(&ctx->comp_event, ...) for the full 60s
hwc_timeout when the hardware stops responding.

When mana_dealloc_queues() resets the device and sets hwc->hwc_dead = true,
it does not complete any in-flight ctx->comp_event, so the subsequent
mana_disable_vport_rx() -> mana_hwc_send_request() call in
mana_dealloc_queues() will still block in down(&hwc->sema) while holding
rtnl_lock until the concurrent caller's timeout expires.

Should hwc_timeout == 0 || hwc_dead also be checked before acquiring
hwc->sema (or should in-flight waiters be completed when marking HWC dead)?

> diff --git a/drivers/net/ethernet/microsoft/mana/mana_en.c b/drivers/net/ethernet/microsoft/mana/mana_en.c
> index 6b7c027e497ed..00885d21a9af0 100644
> --- a/drivers/net/ethernet/microsoft/mana/mana_en.c
> +++ b/drivers/net/ethernet/microsoft/mana/mana_en.c
> @@ -3753,16 +3753,19 @@ int mana_attach(struct net_device *ndev)
>  static int mana_dealloc_queues(struct net_device *ndev)
>  {
>  	struct mana_port_context *apc = netdev_priv(ndev);
> -	unsigned long timeout = jiffies + 120 * HZ;
>  	struct gdma_dev *gd = apc->ac->gdma_dev;
> +	struct hw_channel_context *hwc;
> +	bool trigger_flr = false;
> +	int flr_pending, i, err;
>  	struct mana_txq *txq;
>  	struct sk_buff *skb;
> -	int i, err;
> -	u32 tsleep;
> +	u32 flr_txq_id = 0;
>  
>  	if (apc->port_is_up)
>  		return -EINVAL;
>  
> +	hwc = gd->gdma_context->hwc.driver_data;
> +
>  	if (apc->rxqs)
>  		mana_chn_setxdp(apc, NULL);
>  
> @@ -3774,42 +3777,92 @@ static int mana_dealloc_queues(struct net_device *ndev)
>  	 * 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.
> +	 * Drain the in-flight TX completions for each queue. TX is already
> +	 * stopped, so pending_sends can no longer grow. For each queue,
> +	 * disable NAPI so it cannot run at the same time as this drain,
> +	 * then process all completions still in the CQ. If some are still
> +	 * missing after that, the hardware is not responding: reset device
> +	 * and stop waiting on the now-unavailable HWC.
>  	 */
>  
>  	if (apc->tx_qp) {
>  		for (i = 0; i < apc->num_queues; i++) {
> +			struct mana_cq *tx_cq;
> +			struct napi_struct *napi;
> +			int pending;
> +
> +			if (!apc->tx_qp[i])
> +				continue;
> +
> +			tx_cq = &apc->tx_qp[i]->tx_cq;
>  			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;
> +			napi = &tx_cq->napi;
> +
> +			if (txq->napi_initialized) {
> +				/* Stop the NAPI poll so it cannot run
> +				 * concurrently with the synchronous drain.
> +				 */
> +				napi_synchronize(napi);
> +				napi_disable_locked(napi);
> +				cancel_work_sync(&tx_cq->dim.work);
> +
> +				/* Reap all TX completions currently in the CQ.
> +				 * A zero budget frees skbs via the non-NAPI
> +				 * path, which is required outside softirq.
> +				 */
> +				tx_cq->budget = 0;
> +				do {
> +					tx_cq->work_done = 0;
> +					mana_poll_tx_cq(tx_cq);
> +				} while (tx_cq->work_done);
>  			}
> -			if (atomic_read(&txq->pending_sends)) {
> -				err = mana_gd_dev_reset(gd->gdma_context);
> -				if (err) {
> -					netdev_err(ndev, "device reset failed %d with %d pkts pending in txq %u\n",
> -						   err,
> -					    atomic_read(&txq->pending_sends),
> -					    txq->gdma_txq_id);
> -				}
> -				break;
> +
> +			pending = atomic_read(&txq->pending_sends);
> +			if (!trigger_flr && pending > 0) {
> +				trigger_flr = true;
> +				flr_txq_id = txq->gdma_txq_id;
> +				flr_pending = pending;
>  			}
>  		}
>  
> +		if (trigger_flr && !(hwc && hwc->hwc_dead)) {
> +			/* Completions never arrived: the device is
> +			 * unresponsive. Issue device reset and
> +			 * mark the HWC dead so subsequent HWC commands
> +			 * fail fast instead of each waiting the full
> +			 * hwc_timeout.
> +			 */
> +			err = mana_gd_dev_reset(gd->gdma_context);
> +			if (err)
> +				netdev_err(ndev, "device reset failed %d with %d pkts pending in txq %u\n",
> +					   err,
> +					   flr_pending,
> +					   flr_txq_id);
> +			else if (hwc)
> +				WRITE_ONCE(hwc->hwc_dead, true);
> +		}

[Severity: High]
Won't this non-waiting CQ poll trigger a premature Function Level Reset and
permanently mark the HWC dead whenever TX packets are still in flight in the
hardware during a normal detach or per-port queue reset?

In mana_detach(), setting apc->port_is_up = false and calling
netif_tx_disable(ndev) prevents new packets from being queued in software,
but packets posted to txq->gdma_sq right before netif_tx_disable() are still
asynchronously in flight in the NIC hardware (DMA read, shaping, wire
transmission, and CQE DMA write-back).

mana_poll_tx_cq() is non-blocking:

    mana_poll_tx_cq()
      comp_read = mana_gd_poll_cq(cq->gdma_cq, completions, ...);
      if (comp_read < 1)
              return;

As soon as mana_gd_poll_cq() reaches a CQE slot that the hardware has not
yet written back to host memory, it returns 0, mana_poll_tx_cq() returns
early with tx_cq->work_done == 0, and the do-while loop exits immediately
without waiting for the in-flight SQ entries to complete.

If any packet is still in flight on the NIC when the loop exits,
atomic_read(&txq->pending_sends) > 0 sets trigger_flr = true, which calls
mana_gd_dev_reset() and sets hwc->hwc_dead = true on healthy hardware.

Because hwc->hwc_dead is never cleared without destroying and recreating the
HWC channel, any operation that detaches and reattaches queues under traffic
(such as mana_close() followed by mana_open(), mana_change_mtu(),
mana_set_channels(), mana_set_ringparam(), mana_set_priv_flags(), or
mana_xdp_set()) will cause subsequent mana_attach() or mana_open() calls and
any other vPorts or RDMA instances on the function to fail permanently with
-ETIMEDOUT.

Similarly, single-port TX timeout recovery in
mana_per_port_queue_reset_work_handler() runs when a TX queue is stalled
(so pending_sends > 0 holds by definition):

    mana_per_port_queue_reset_work_handler()
      -> mana_detach(ndev, false)
           -> mana_dealloc_queues(ndev)
      -> mana_attach(ndev)

Because mana_dealloc_queues() now resets the entire PCIe function and sets
hwc->hwc_dead = true, the recovery call to mana_attach(ndev) immediately
fails with -ETIMEDOUT instead of recovering the port.

Furthermore, if mana_gd_dev_reset() fails or is skipped because hwc_dead was
already set, the cleanup loop immediately below unmaps and frees the
in-flight TX SKBs via mana_unmap_skb() and dev_kfree_skb_any() without
having waited for DMA completion or reset the device.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261025072838.1991785-1-sahilchandna@linux.microsoft.com?part=1

  reply	other threads:[~2026-10-26  7:29 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-25  7:28 [PATCH net] net: mana: Avoid waiting on dead HWC after device reset Sahil Chandna
2026-10-26  7:29 ` sashiko-bot [this message]
2026-10-28  8:18   ` Sahil Chandna
2026-10-26  8:21 ` netdev-bot+sashiko
2026-10-28  8:22   ` Sahil Chandna

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=sashiko-outbox-184659@kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=linux-hyperv@vger.kernel.org \
    --cc=linux-rdma@vger.kernel.org \
    --cc=sahilchandna@linux.microsoft.com \
    --cc=sashiko-reviews@lists.linux.dev \
    /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