From: "Gupta, Suraj" <suraj.gupta2@amd.com>
To: netdev-bot+sashiko@kernel.org
Cc: radhey.shyam.pandey@amd.com, andrew+netdev@lunn.ch,
davem@davemloft.net, edumazet@google.com, kuba@kernel.org,
pabeni@redhat.com, michal.simek@amd.com, netdev@vger.kernel.org,
linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org
Subject: Re: [PATCH net v2] net: xilinx: axienet: Free outstanding DMA buffers on dmaengine stop
Date: Tue, 22 Sep 2026 23:41:25 +0530 [thread overview]
Message-ID: <408b7740-58a8-4fb6-be78-352773a7db13@amd.com> (raw)
In-Reply-To: <178998604072.2160803.17018942361014137390@kernel.org>
On 9/21/2026 3:50 PM, netdev-bot+sashiko@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 1 potential
> issue(s) to consider.
>
> Critical: 0 · High: 1 (1) · Medium: 0 · Low: 0
>
> If you believe none of the issues are legitimate, please reply to each
> issue of Medium severity or higher and explain why the AI is wrong.
> If any of them are legitimate and you plan to respin, please let patchwork
> know by including "pw-bot: cr" as a separate line at the end of your reply
> (one such reply per series is enough).
>
> - [High] The new @stopping check in axienet_dma_rx_cb() does not actually
> fence RX resubmission against the terminate in axienet_stop(), so the…
>
> Pre-existing issues:
> - [High] TX ring head/tail desynchronization causes a NULL pointer
> dereference in axienet_dma_tx_cb().
>
> --
>
> net: xilinx: axienet: Free outstanding DMA buffers on dmaengine stop
>
> In the dmaengine path the driver clears the ring slot's skb pointer in the
> TX and RX completion callbacks, so a non-NULL skb marks a slot that still
> owns a live DMA-mapped buffer, and axienet_stop() unmaps and frees every
> such buffer instead of only kfree()ing the ring shells. It also sets
> @stopping before dmaengine_terminate_sync() and checks it in
> axienet_dma_rx_cb() to fence RX resubmission, and releases the channels
> before freeing the rings.
>
>> diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
>> index 1722b7038f340..fc314f67f157c 100644
>> --- a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
>> +++ b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
>> @@ -881,6 +881,7 @@ static void axienet_dma_tx_cb(void *data, const struct dmaengine_result *result)
>> u64_stats_update_end(&lp->tx_stat_sync);
>> dma_unmap_sg(lp->dev, skbuf_dma->sgl, skbuf_dma->sg_len, DMA_TO_DEVICE);
>> dev_consume_skb_any(skbuf_dma->skb);
>> + skbuf_dma->skb = NULL;
>
> [Severity: High]
> This isn't a bug introduced by this patch, but can clearing the slot's skb
> here turn an existing TX ring desynchronization into a NULL pointer
> dereference?
>
> axienet_start_xmit_dmaengine() advances the head before the fallible steps:
>
> lp->tx_ring_head++;
> sg_init_table(skbuf_dma->sgl, sg_len);
> ret = skb_to_sgvec(skb, skbuf_dma->sgl, 0, skb->len);
> if (ret < 0)
> goto xmit_error_drop_skb;
>
> ret = dma_map_sg(lp->dev, skbuf_dma->sgl, sg_len, DMA_TO_DEVICE);
> if (!ret)
> goto xmit_error_drop_skb;
>
> None of the xmit_error_* labels rewind lp->tx_ring_head, and they return
> NETDEV_TX_OK without ever calling dmaengine_submit(), so that slot is
> consumed with no descriptor behind it and skbuf_dma->skb is never assigned.
>
> axienet_dma_tx_cb() pairs completions with slots positionally:
>
> skbuf_dma = axienet_get_tx_desc(lp, lp->tx_ring_tail++);
> len = skbuf_dma->skb->len;
> txq = skb_get_tx_queue(lp->ndev, skbuf_dma->skb);
>
> so after one skipped slot the head/tail pairing is off by one for the life of
> the interface and the next completion lands on the skipped slot.
>
> Before this patch that read a stale, already consumed skb pointer. With
> skbuf_dma->skb = NULL set on every completion, the skipped slot's skb is
> deterministically NULL, so the dereference above runs on NULL from the DMA
> channel tasklet.
>
> The triggering condition is a single dma_map_sg() failure, i.e. the same
> IOMMU aperture exhaustion the commit message describes as motivation. Would
> it be reasonable to advance lp->tx_ring_head only after
> device_prep_slave_sg() succeeds, or unwind it on the pre-submit error paths,
> as part of this series?
>
This is a pre-existing issue, will address it separately.
>> @@ -1171,6 +1172,7 @@ static void axienet_dma_rx_cb(void *data, const struct dmaengine_result *result)
>> &meta_max_len);
>> dma_unmap_single(lp->dev, skbuf_dma->dma_address, lp->max_frm_size,
>> DMA_FROM_DEVICE);
>> + skbuf_dma->skb = NULL;
>>
>> if (IS_ERR(app_metadata)) {
>> if (net_ratelimit())
>> @@ -1193,6 +1195,12 @@ static void axienet_dma_rx_cb(void *data, const struct dmaengine_result *result)
>> u64_stats_update_end(&lp->rx_stat_sync);
>>
>> rx_submit:
>> + /* Do not re-arm the RX ring while a stop is in progress, or the
>> + * teardown could free a buffer still handed to the engine.
>> + */
>> + if (READ_ONCE(lp->stopping))
>> + return;
>> +
>> for (i = 0; i < CIRC_SPACE(lp->rx_ring_head, lp->rx_ring_tail,
>> RX_BUF_NUM_DEFAULT); i++)
>> axienet_rx_submit_desc(lp->ndev);
>
> [Severity: High]
> Does this check actually fence resubmission against the terminate?
>
> axienet_dma_rx_cb() runs from the xilinx DMA channel tasklet, which drops
> chan->lock around dmaengine_desc_get_callback_invoke() in
> xilinx_dma_chan_desc_cleanup(), concurrently with axienet_stop() in process
> context. READ_ONCE()/WRITE_ONCE() only prevent tearing, they do not make the
> check-then-submit sequence atomic:
>
> CPU0 (DMA tasklet) CPU1 (axienet_stop)
> axienet_dma_rx_cb()
> READ_ONCE(lp->stopping) == false
> WRITE_ONCE(lp->stopping, true);
> dmaengine_terminate_sync(rx_chan);
> axienet_rx_submit_desc()
> skbuf_dma->skb = skb;
> dmaengine_submit(dma_rx_desc);
> dma_async_issue_pending(lp->rx_chan);
>
Agreed, will add a spinlock to guard the callback's re-arm and the
stopping set in axienet_stop()
Regards,
Suraj
> Note that dmaengine_terminate_sync() ends in dmaengine_synchronize(), which
> for this provider is xilinx_dma_synchronize() -> tasklet_kill(): it waits for
> the running tasklet to finish rather than discarding what that tasklet
> submits.
>
> Does the late submit re-arm the hardware? xilinx_dma_tx_submit() queues the
> descriptor and clears the provider's terminate state:
>
> append_desc_queue(chan, desc);
> ...
> chan->terminating = false;
>
> and xilinx_dma_terminate_all() left the channel idle:
>
> chan->terminating = true;
> xilinx_dma_free_descriptors(chan);
> chan->idle = true;
>
> so the racing dma_async_issue_pending() reaches start_transfer() and
> reprograms CURDESC/TAILDESC with DMACR.RUNSTOP set.
>
> Is anything after that point stopping the channel again? dma_release_channel()
> -> dma_chan_put() only does:
>
> if (!chan->client_count && chan->device->device_free_chan_resources) {
> /* Make sure all operations have completed */
> dmaengine_synchronize(chan);
> chan->device->device_free_chan_resources(chan);
> }
>
> and xilinx_dma_free_chan_resources() dma_free_coherent()s chan->seg_v without
> a stop_transfer() or reset, so moving the release ahead of the ring free does
> not appear to close the window.
>
> The new teardown loop then sees the resubmitted slot:
>
>> + for (i = 0; i < RX_BUF_NUM_DEFAULT; i++) {
>> + skbuf_dma = lp->rx_skb_ring[i];
>> + if (skbuf_dma && skbuf_dma->skb) {
>> + dma_unmap_single(lp->dev, skbuf_dma->dma_address,
>> + lp->max_frm_size, DMA_FROM_DEVICE);
>> + dev_kfree_skb_any(skbuf_dma->skb);
>> + }
>
> Can this unmap and free a buffer the engine is still writing into, turning
> the former mapping leak into DMA write-after-free?
>
> Documentation/driver-api/dmaengine/client.rst also states:
>
> The behavior of this function is undefined if dma_async_issue_pending() has
> been called between dmaengine_terminate_async() and this function.
>
> which is exactly what the racing callback does inside the window, and this
> patch removes the extra dmaengine_synchronize() calls that followed the
> terminates.
>
> There is a second placement question: the ring is dereferenced before the new
> check,
>
> skbuf_dma = axienet_get_rx_desc(lp, lp->rx_ring_tail++);
> skb = skbuf_dma->skb;
>
> so a completion from a re-armed channel arriving after tasklet_kill() and
> after kfree(lp->rx_skb_ring) touches freed memory before the @stopping test is
> reached.
>
> The commit message says "Set @stopping before terminating and check it in the
> callback to fence resubmission, and release the channels before freeing the
> rings so a late completion cannot touch a freed ring." Given the above, does
> the check-submit-issue sequence need real serialization against the stop
> transition, or a second dmaengine_terminate_sync() once all callbacks are
> known to be drained?
>
>> @@ -1752,20 +1761,43 @@ static int axienet_stop(struct net_device *ndev)
>> free_irq(lp->rx_irq, ndev);
>> axienet_dma_bd_release(ndev);
>> } else {
>> + struct skbuf_dma_descriptor *skbuf_dma;
>> +
>> + WRITE_ONCE(lp->stopping, true);
>> dmaengine_terminate_sync(lp->tx_chan);
>> - dmaengine_synchronize(lp->tx_chan);
>> dmaengine_terminate_sync(lp->rx_chan);
>> - dmaengine_synchronize(lp->rx_chan);
>> -
>> - for (i = 0; i < TX_BD_NUM_MAX; i++)
>> - kfree(lp->tx_skb_ring[i]);
>> - kfree(lp->tx_skb_ring);
>> - for (i = 0; i < RX_BUF_NUM_DEFAULT; i++)
>> - kfree(lp->rx_skb_ring[i]);
>> - kfree(lp->rx_skb_ring);
>>
>> + /* Release the channels before freeing the rings, so the DMA is
>> + * fully torn down before the memory its descriptors reference is
>> + * freed.
>> + */
>> dma_release_channel(lp->rx_chan);
>> dma_release_channel(lp->tx_chan);
>
> [ ... ]
>
prev parent reply other threads:[~2026-09-22 18:11 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-17 10:05 Suraj Gupta
2026-09-21 10:20 ` netdev-bot+sashiko
2026-09-22 18:11 ` Gupta, Suraj [this message]
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=408b7740-58a8-4fb6-be78-352773a7db13@amd.com \
--to=suraj.gupta2@amd.com \
--cc=andrew+netdev@lunn.ch \
--cc=davem@davemloft.net \
--cc=edumazet@google.com \
--cc=kuba@kernel.org \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-kernel@vger.kernel.org \
--cc=michal.simek@amd.com \
--cc=netdev-bot+sashiko@kernel.org \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=radhey.shyam.pandey@amd.com \
/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
all inboxes | Powered by JetHome®