From: Dragos Tatulea <dtatulea@nvidia.com>
To: netdev-bot+sashiko@kernel.org, tariqt@nvidia.com
Cc: andrew+netdev@lunn.ch, davem@davemloft.net, edumazet@kernel.org,
kuba@kernel.org, netdev@vger.kernel.org, pabeni@redhat.com,
bobbyeshleman@meta.com, cjubran@nvidia.com, cratiu@nvidia.com,
daniel@iogearbox.net, dw@davidwei.uk, erfansh@meta.com,
gal@nvidia.com, Jacob.e.keller@intel.com, kaifengw@google.com,
leon@kernel.org, linux-kernel@vger.kernel.org,
linux-rdma@vger.kernel.org, mbloch@nvidia.com,
mfleming@cloudflare.com, almasrymina@google.com,
razor@blackwall.org, saeedm@nvidia.com, horms@kernel.org,
sdf@fomichev.me, sdf.kernel@gmail.com, witu@nvidia.com
Subject: Re: [PATCH net-next V2 11/13] net: add netdev_unbind_dma_dev() helper
Date: Sat, 14 Nov 2026 11:54:23 +0200 [thread overview]
Message-ID: <7b464808-2c49-4b30-83d5-5997bd22aa4f@nvidia.com> (raw)
In-Reply-To: <179447641496.2199510.9115038837644205059@kernel.org>
On 12.11.26 10:40, netdev-bot+sashiko@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 7 potential
> issue(s) to consider.
>
> Critical: 0 · High: 1 · Medium: 2 · Low: 4
>
> 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] netdev_unbind_dma_dev() (net/core/netdev_queues.c:84) refuses
> only when the netdev is both running and present (`netif_running(dev)…
> - [Medium] The two halves of netdev_unbind_dma_dev() use different rules
> for which mappings belong to @dma_dev.
> - [Medium] __netif_mp_close_rxq() (net/core/netdev_rx_queue.c:291)
> quietly accepts an already-uninstalled provider only when…
> - [Low] The commit message says "A detached netdev also works because ...
> new bindings can't be added", but the core does not enforce this for…
> - [Low] Several in-tree comments that list binding lifecycle rules were
> not updated for the new site that clears and unmaps bindings, and the…
> - [Low] netdev_unbind_dma_dev() treats a NULL result from
> __netdev_queue_get_dma_dev() as "this queue maps against the departing…
> - [Low] net_devmem_unbind_dma_dev() (devmem.c:575-596) clears
> NET_DEVMEM_BINDING_LIVE to "hide the binding from TX lookups while it…
>
> --
>
> net: add netdev_unbind_dma_dev() helper
>
> This adds an exported helper for a closed or detached netdev. It
> uninstalls the memory providers of RX queues mapped against a departing
> DMA device and unmaps the devmem bindings that use that device. It is
> meant for mlx5 data direct, where the DMA device is not the netdev's own
> PCI device.
>
>> diff --git a/net/core/devmem.c b/net/core/devmem.c
>> index dd3987bbd9f6c..bc8ae12b2318a 100644
>> --- a/net/core/devmem.c
>> +++ b/net/core/devmem.c
>> @@ -556,6 +556,48 @@ bool netdev_has_dmabuf_binding(struct net_device *dev, struct device *dma_dev)
>> }
>> EXPORT_SYMBOL_GPL(netdev_has_dmabuf_binding);
>>
>> +/* Unmap the bindings of a closed @dev that use @dma_dev, the way netdev
>> + * unregister does. Senders that raced the detach drop their skbs at xmit.
>> + */
>> +void net_devmem_unbind_dma_dev(struct net_device *dev, struct device *dma_dev)
>> +{
>> + struct net_devmem_dmabuf_binding *binding;
>> + unsigned long xa_idx;
>> + bool match, live;
>> +
>> + netdev_assert_locked(dev);
>> +
>> + xa_lock(&net_devmem_dmabuf_bindings);
>> + xa_for_each(&net_devmem_dmabuf_bindings, xa_idx, binding) {
>> + if (READ_ONCE(binding->dev) != dev)
>> + continue;
>> +
>> + /* Hide the binding from TX lookups while it is checked. */
>> + live = xa_get_mark(&net_devmem_dmabuf_bindings, xa_idx,
>> + NET_DEVMEM_BINDING_LIVE);
>> + if (live)
>> + __xa_clear_mark(&net_devmem_dmabuf_bindings, xa_idx,
>> + NET_DEVMEM_BINDING_LIVE);
>> + xa_unlock(&net_devmem_dmabuf_bindings);
>
> [Severity: Low]
> Does clearing the mark here actually hide the binding from TX lookups?
> net_devmem_lookup_dmabuf() runs under rcu_read_lock() and does:
>
> binding = xa_find(&net_devmem_dmabuf_bindings, &xa_idx, id,
> NET_DEVMEM_BINDING_LIVE);
>
> net_devmem_unbind_dmabuf() needs a grace period after clearing the mark
> for exactly this reason:
>
> /* Ensure no tx net_devmem_lookup_dmabuf() are in flight after the
> * mark is cleared.
> */
> synchronize_net();
>
> There is no synchronize_net() here. A lookup already in flight can still
> find a matching binding and take a reference. The safety of matching
> bindings then rests only on clearing binding->dev and the check at xmit
> time.
>
> There is also a side effect for bindings that don't match. A binding on
> @dev that doesn't use @dma_dev is invisible while xa_lock is dropped. A
> concurrent SO_DEVMEM_TX sendmsg on it could get a spurious -EINVAL.
>
> The mark is cleared on entry and restored on exit to guard this region,
> rather than to express state. Is the clear and restore needed at all?
>
v3 doesn't to the mark restore trick anymore.
>> +
>> + mutex_lock(&binding->lock);
>> + match = net_devmem_binding_mapped_to(binding, dma_dev);
>> + mutex_unlock(&binding->lock);
>> + if (match)
>> + net_devmem_dmabuf_binding_unmap(binding);
>
> [Severity: Medium]
> The commit message calls this half "devmem TX", and the comment says it
> works "the way netdev unregister does". However, nothing here filters on
> binding->direction or LIVE. net_devmem_dev_unregister() skips LIVE RX
> bindings:
>
> if (binding->direction == DMA_FROM_DEVICE) {
> if (READ_ONCE(binding->dev) != dev ||
> xa_get_mark(..., NET_DEVMEM_BINDING_LIVE))
> continue;
>
> The two halves of netdev_unbind_dma_dev() also pick mappings by
> different rules. The RX loop uses the driver's current per-queue report
> and skips leased queues. Here, net_devmem_binding_mapped_to() treats a
> NULL @dma_dev as a wildcard:
>
> return binding->attachment &&
> (!dma_dev || binding->attachment->dev == dma_dev);
>
> Take an RX binding whose queue the RX loop skipped but which matches
> here. That can happen with a NULL @dma_dev, with a driver that already
> reports a different device, or on the leased-queue path. Such a binding
> is unmapped and gets binding->dev cleared while rxq->mp_params and
> binding->bound_rxqs still point to it. mp_dmabuf_devmem_uninstall()
> avoids this by clearing dev only once bound_rxqs is empty.
>
> A later netlink socket close then reaches net_devmem_unbind_dmabuf()
> with a NULL binding->dev:
>
> if (netif_mp_close_rxq(binding->dev, rxq_idx, &mp_params))
>
> Wouldn't netif_mp_close_rxq() then dereference dev->real_num_rx_queues
> through NULL? If the netdev is reopened first, the page pool would be
> built from the provider that is still installed, over an sgt that has
> already been unmapped.
>
> This doesn't look reachable from the in-tree mlx5 caller. But the helper
> is exported, and netdev_has_dmabuf_binding() documents a NULL dma_dev as
> "any". Should this half filter on DMA_TO_DEVICE, or should a NULL
> @dma_dev be rejected?
>
Ack. Will fix in v3 to filter by DMA_TO_DEVICE and rename
net_devmem_unbind_dma_dev() to net_devmem_unbind_tx_dma_dev().
>> +
>> + xa_lock(&net_devmem_dmabuf_bindings);
>> + if (match) {
>> + WRITE_ONCE(binding->vdev, NULL);
>> + WRITE_ONCE(binding->dev, NULL);
>
> [Severity: Low]
> This isn't a bug, but some comments about the binding lifecycle don't
> cover this new site.
>
> The net_devmem_lock_dev() kernel-doc lists where binding->dev is
> cleared:
>
> * NULL while holding netdev_lock(dev) (in mp_dmabuf_devmem_uninstall(),
> * net_devmem_unbind_dmabuf(), or net_devmem_dev_unregister() during
>
> In struct net_devmem_dmabuf_binding, the vdev comment says "cleared with
> WRITE_ONCE() on unregistration". The ref comment says "TX bindings are
> also unmapped earlier if their device is unregistered". Both of these now
> also happen when the DMA device goes away.
>
> The new netdev_unbind_dma_dev() kernel-doc says it uninstalls queues
> "that map against @dma_dev". It also uninstalls queues for which the
> driver reports no DMA device at all.
>
> Should these comments be updated?
>
Ok. Will update.
>> + } else if (live) {
>> + __xa_set_mark(&net_devmem_dmabuf_bindings, xa_idx,
>> + NET_DEVMEM_BINDING_LIVE);
>> + }
>> + }
>> + xa_unlock(&net_devmem_dmabuf_bindings);
>> +}
>
> [ ... ]
>
>> diff --git a/net/core/netdev_queues.c b/net/core/netdev_queues.c
>> index 27cb6397bdd4e..71cc7bfc087dd 100644
>> --- a/net/core/netdev_queues.c
>> +++ b/net/core/netdev_queues.c
>
> [ ... ]
>
>> @@ -58,6 +60,55 @@ struct device *netdev_queue_get_dma_dev(struct net_device *dev,
>> return dma_dev;
>> }
>>
>> +/**
>> + * netdev_unbind_dma_dev() - drop the memory providers of a departing DMA device
>> + * @dev: net_device, closed or detached by the caller
>> + * @dma_dev: DMA device the driver is about to lose
>
> [Severity: Low]
> The commit message says:
>
> A detached netdev also works because the driver has already torn down
> the queues and new bindings can't be added.
>
> Is that enforced for io_uring zcrx? The devmem netlink bind paths check
> netif_device_present(). zcrx_register_netdev() only needs
> netdev_queue_get_dma_dev() to return non-NULL before it calls
> netif_mp_open_rxq(). Neither netif_mp_open_rxq() nor
> __netif_mp_open_rxq() checks whether the device is present.
>
> So once this returns and the instance lock is dropped, the only thing
> stopping a new zcrx area from being mapped against the departing device
> is the driver's ndo_queue_get_dma_dev() no longer reporting it. mlx5
> does stop reporting it: channels->num is 0 when closed, and dd.dev is
> cleared before the notifier runs.
>
> The kernel-doc only says "net_device, closed or detached by the caller".
> Should it also state that the driver must stop reporting @dma_dev?
>
Driver does have to stop reporting the unbound dma_dev. Will add
to doc.
>> + *
>> + * Uninstalls the memory provider of every RX queue of @dev that maps
>> + * against @dma_dev. Unmaps the devmem bindings of @dev that use @dma_dev.
>> + * The bindings stay allocated until their users release them.
>
> [ ... ]
>
>> + * Return: 0 on success, -EBUSY if @dev is running and present.
>> + */
>> +int netdev_unbind_dma_dev(struct net_device *dev, struct device *dma_dev)
>> +{
>> + unsigned int i;
>> +
>> + netdev_assert_locked(dev);
>> +
>> + if (netif_running(dev) && netif_device_present(dev))
>> + return -EBUSY;
>
> [Severity: High]
> Can a netdev that is running but detached get past this check while
> validated devmem TX skbs are still sitting in a qdisc?
>
> The commit message says:
>
> The netdev must be closed or detached so that the hardware no longer
> accesses the mappings and the qdiscs hold no validated skbs.
>
> netif_device_detach() only clears __LINK_STATE_PRESENT and stops the TX
> queues. Only dev_close()->dev_deactivate()->qdisc_reset() empties the
> qdiscs.
>
> The mlx5 caller later in this series seems to reach exactly this state
> in mlx5e_nic_disable():
>
> if (netif_running(priv->netdev))
> mlx5e_close(priv->netdev);
> netif_device_detach(priv->netdev);
> ...
> mlx5e_data_direct_unbind_dev(priv);
>
> mlx5e_close() is the ndo_stop, so __LINK_STATE_START stays set. The
> check above then passes because the device is not present.
>
> Consider this sequence:
>
> sch_direct_xmit()
> validate_xmit_skb_list()
> validate_xmit_unreadable_skb() <- binding->dev still set, passes
> mlx5e_close() stops the txq
> dev_requeue_skb(skb, q) <- skb parked in q->gso_skb
>
> netdev_unbind_dma_dev()
> net_devmem_unbind_dma_dev()
> net_devmem_dmabuf_binding_unmap()
> dma_buf_unmap_attachment()
> dma_buf_unpin()
>
> On resume, mlx5e_open() and netif_device_attach() wake the queue.
> dequeue_skb() then returns the gso_skb with:
>
> /* skb in gso_skb were already validated */
> *validate = false;
>
> The binding->dev check in validate_xmit_unreadable_skb() never runs
> again for that skb. Would the NIC then be given net_iov DMA addresses
> that are no longer mapped for the DMA device?
>
> The comment above net_devmem_unbind_dma_dev() says "Senders that raced
> the detach drop their skbs at xmit". That doesn't seem to hold for these
> requeued skbs.
>
> For RX, the check relies on an unenforced convention that a detached
> driver has already torn down its RX rings. mlx5 meets it because it
> closes before detaching. A caller that only detaches would have its
> dmabuf and zcrx mappings revoked while RX descriptors still point at
> them. Should the exported helper enforce or document this?
>
I did this to prevent another issue in mlx5e: mlx5e_suspend() disables
the data direct notifier so if the data direct device is unbound after
suspend, the DMA dev ptr would be left dangling. So I wanted to call
netdev_unbind_dma_dev() during suspend. Seems like this is not
the right way.
I will add one more mlx5 patch that closes the netdev on suspend
if it is in use. It will be ugly, but I can't see a better way.
>> +
>> + for (i = 0; i < dev->real_num_rx_queues; i++) {
>> + struct netdev_rx_queue *rxq = __netif_get_rx_queue(dev, i);
>> + struct pp_memory_provider_params p = rxq->mp_params;
>> + struct device *queue_dev;
>> +
>> + if (!p.mp_ops)
>> + continue;
>> +
>> + queue_dev = __netdev_queue_get_dma_dev(dev, i);
>> + if (queue_dev && queue_dev != dma_dev)
>> + continue;
>
> [Severity: Low]
> Here a NULL from __netdev_queue_get_dma_dev() counts as a match. Is that
> intended for a closed netdev?
>
> mlx5e_queue_get_dma_dev() returns NULL whenever:
>
> /* Allow only opened channels. */
> if (queue_index < 0 || queue_index >= channels->num)
> goto out;
>
> mlx5e_close_channels() sets chs->num = 0. So in the closed state this
> helper requires, the @dma_dev filter appears to do nothing for the only
> in-tree caller. Every RX queue with a provider is uninstalled, whichever
> device it is actually mapped to.
>
> If a driver's queues can map against more than one device, wouldn't
> providers on a device that isn't going away be torn down as well?
Yes, that is the point.
> A leased queue with a provider would also get past this filter and hit
> the WARN_ON_ONCE(rxq->lease). The TX half filters on
> binding->attachment->dev instead.
>
I chose to keep leased queues as unsupported to keep this series
reasonably sized.
>> +
>> + if (WARN_ON_ONCE(rxq->lease))
>> + continue;
>> +
>> + memset(&rxq->mp_params, 0, sizeof(rxq->mp_params));
>> + __netif_mp_uninstall_rxq(rxq, &p);
>
> [Severity: Medium]
> Can this race with io_close_queue() in io_uring/zcrx.c and trigger the
> WARN_ON_ONCE() in __netif_mp_close_rxq()?
>
> __netif_mp_close_rxq() only accepts an already-uninstalled provider on
> the unregister path:
>
> if (dev->reg_state > NETREG_REGISTERED &&
> !rxq->mp_params.mp_ops)
> return 0;
>
> if (WARN_ON_ONCE(rxq->mp_params.mp_ops != old_p->mp_ops ||
>
> Here mp_params is cleared on a device that is still registered. One
> possible sequence:
>
> io_close_queue()
> copies ifq->netdev, sets ifq->netdev = NULL under pp_lock
> netdev_lock(netdev) <- blocks, held by the unbind caller
> netdev_unbind_dma_dev()
> memset(&rxq->mp_params, 0, ...)
> __netif_mp_uninstall_rxq()
> io_pp_uninstall()
> io_zcrx_unmap_areas()
> io_zcrx_drop_netdev()
> (ifq->if_rxq left unchanged)
> if (ifq->if_rxq != -1)
> netif_mp_close_rxq(netdev, ifq->if_rxq, &p);
> __netif_mp_close_rxq()
> reg_state == NETREG_REGISTERED, mp_ops == NULL
> WARN_ON_ONCE(...) -> -EINVAL
>
> The areas are already unmapped, so cleanup still completes. Would a
> zcrx teardown that races with a data direct unbind or a suspend produce
> a WARN splat (fatal with panic_on_warn) plus a spurious -EINVAL?
>
Ack. I will add a patch in io_pp_uninstall() to set ifq->if_rxq to -1.
Thanks,
Dragos
next prev parent reply other threads:[~2026-11-14 9:54 UTC|newest]
Thread overview: 43+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-10 18:38 [PATCH net-next V2 00/13] net/mlx5e: Add netdev support for data direct Tariq Toukan
2026-11-10 18:38 ` [PATCH net-next V2 01/13] net/mlx5: Log the data direct to PF device mapping Tariq Toukan
2026-11-11 18:40 ` sashiko-bot
2026-11-10 18:38 ` [PATCH net-next V2 02/13] net/mlx5: Detach auxiliary devices on attach failure during load Tariq Toukan
2026-11-11 18:40 ` sashiko-bot
2026-11-12 9:40 ` netdev-bot+sashiko
2026-11-13 13:58 ` Dragos Tatulea
2026-11-10 18:38 ` [PATCH net-next V2 03/13] net/mlx5e: Register supported netdevs as data direct users Tariq Toukan
2026-11-11 18:40 ` sashiko-bot
2026-11-10 18:38 ` [PATCH net-next V2 04/13] net/mlx5e: Pre-calculate UMR padding and entry size Tariq Toukan
2026-11-11 18:40 ` sashiko-bot
2026-11-10 18:38 ` [PATCH net-next V2 05/13] net/mlx5e: Add data direct ethtool private flag Tariq Toukan
2026-11-11 18:40 ` sashiko-bot
2026-11-10 18:38 ` [PATCH net-next V2 06/13] net/mlx5e: Add data direct RX infrastructure Tariq Toukan
2026-11-11 18:40 ` sashiko-bot
2026-11-12 9:40 ` netdev-bot+sashiko
2026-11-13 14:28 ` Dragos Tatulea
2026-11-10 18:38 ` [PATCH net-next V2 07/13] net/mlx5e: SHAMPO, Use page_pool DMA sync helper for SKB frags Tariq Toukan
2026-11-11 18:40 ` sashiko-bot
2026-11-12 9:40 ` netdev-bot+sashiko
2026-11-13 14:36 ` Dragos Tatulea
2026-11-10 18:38 ` [PATCH net-next V2 08/13] net/mlx5e: Add data direct TX infrastructure Tariq Toukan
2026-11-11 18:40 ` sashiko-bot
2026-11-10 18:38 ` [PATCH net-next V2 09/13] net/mlx5e: Use the correct DMA dev when data_direct is enabled Tariq Toukan
2026-11-11 18:40 ` sashiko-bot
2026-11-12 9:40 ` netdev-bot+sashiko
2026-11-13 14:43 ` Dragos Tatulea
2026-11-10 18:38 ` [PATCH net-next V2 10/13] net: devmem: add netdev_has_dmabuf_binding() helper Tariq Toukan
2026-11-11 18:40 ` sashiko-bot
2026-11-11 21:46 ` Stanislav Fomichev
2026-11-13 15:00 ` Dragos Tatulea
2026-11-12 9:40 ` netdev-bot+sashiko
2026-11-13 15:02 ` Dragos Tatulea
2026-11-10 18:38 ` [PATCH net-next V2 11/13] net: add netdev_unbind_dma_dev() helper Tariq Toukan
2026-11-11 18:40 ` sashiko-bot
2026-11-12 9:40 ` netdev-bot+sashiko
2026-11-14 9:54 ` Dragos Tatulea [this message]
2026-11-10 18:38 ` [PATCH net-next V2 12/13] net/mlx5e: Disable data direct on data direct device unbind Tariq Toukan
2026-11-11 18:40 ` sashiko-bot
2026-11-12 9:40 ` netdev-bot+sashiko
2026-11-14 9:54 ` Dragos Tatulea
2026-11-10 18:38 ` [PATCH net-next V2 13/13] net/mlx5e: Enable the data direct netdev feature Tariq Toukan
2026-11-11 18:40 ` sashiko-bot
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=7b464808-2c49-4b30-83d5-5997bd22aa4f@nvidia.com \
--to=dtatulea@nvidia.com \
--cc=Jacob.e.keller@intel.com \
--cc=almasrymina@google.com \
--cc=andrew+netdev@lunn.ch \
--cc=bobbyeshleman@meta.com \
--cc=cjubran@nvidia.com \
--cc=cratiu@nvidia.com \
--cc=daniel@iogearbox.net \
--cc=davem@davemloft.net \
--cc=dw@davidwei.uk \
--cc=edumazet@kernel.org \
--cc=erfansh@meta.com \
--cc=gal@nvidia.com \
--cc=horms@kernel.org \
--cc=kaifengw@google.com \
--cc=kuba@kernel.org \
--cc=leon@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-rdma@vger.kernel.org \
--cc=mbloch@nvidia.com \
--cc=mfleming@cloudflare.com \
--cc=netdev-bot+sashiko@kernel.org \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=razor@blackwall.org \
--cc=saeedm@nvidia.com \
--cc=sdf.kernel@gmail.com \
--cc=sdf@fomichev.me \
--cc=tariqt@nvidia.com \
--cc=witu@nvidia.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