From: Sahil Chandna <sahilchandna@linux.microsoft.com>
To: sashiko-reviews@lists.linux.dev, 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: Wed, 28 Oct 2026 13:48:44 +0530 [thread overview]
Message-ID: <10979583-c3db-4ebe-bdec-f1848b27c96b@linux.microsoft.com> (raw)
In-Reply-To: <sashiko-outbox-184659@kernel.org>
On 26-10-2026 12:59, sashiko-bot@kernel.org wrote:
> 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)?
>
Ack, this is valid, I will fix this in v2.>> 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.
>
Ack, this would need a bounded wait to verify queue is really stuck
rather than depend only on pending_sends, I will fix this in v2.
next prev parent reply other threads:[~2026-10-28 8:18 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
2026-10-28 8:18 ` Sahil Chandna [this message]
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=10979583-c3db-4ebe-bdec-f1848b27c96b@linux.microsoft.com \
--to=sahilchandna@linux.microsoft.com \
--cc=linux-hyperv@vger.kernel.org \
--cc=linux-rdma@vger.kernel.org \
--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