From nobody Fri Oct 23 22:16:12 2026 Received: from mail-dl1-f69.google.com (mail-dl1-f69.google.com [74.125.82.69]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E6A595304D6 for ; Thu, 15 Oct 2026 16:39:18 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.69 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1792082373; cv=none; b=M8HC0bAMGrBP3YMhE6nue1OVcxTDR541soZSo9Ee99XRnzupt5z455Q8cAPwjNZU8ok3/NzYyK01YwK1/b7q88H6TKjG8z5/Q9oc/QGncQ4KRi6APA5pzHLB3zG3oCBTt7cExfLzjQTI5srzmf0lkhtv/eYOCF4z599T+oOEhrI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1792082373; c=relaxed/simple; bh=i+Pn5IlHj0Z3YezfY+shDPHuT5rvRYKBt677cP7O1Ig=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=E+uhT2DIVBIFckiSmS2Aokr6Hz+Ax3rYTbTVmPaB07EhkCkVp67TEq1kWlwrFp2LL28EM6j5PQoyiBmuS3FsuAJl1nBKfR0eqRCYwu7e14va6dduKk51wPStMASh852rWb1ywW0tofj+IEJkmEkiXMgoSd52oM0irm1C1tlwLJI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--almasrymina.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=rNHpNi75; arc=none smtp.client-ip=74.125.82.69 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--almasrymina.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="rNHpNi75" Received: by mail-dl1-f69.google.com with SMTP id a92af1059eb24-147be78cd56so21501205c88.1 for ; Thu, 15 Oct 2026 09:39:18 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1792082358; x=1792687158; darn=vger.kernel.org; h=content-type:cc:to:from:subject:message-id:references:mime-version :in-reply-to:date:from:to:cc:subject:date:message-id:reply-to :content-type; bh=ng4zxpw+boV8S1ELPw0fhKdUN4Zin1qbWJGc8Tx1FlY=; b=rNHpNi75oGjCinxKpFtsfOyIl7KEyqgMpXlVRPkrf84PpDyHaiiL84dz+coPK27qUO p19wCpa0oicqJec64+mLPJKP7BfXArv6FW4rSHB8ev0j2Cx2ZGMVgriELHzxO+6tDCNV iXaaUaK4SivfyrYGCpzIfLYPNk9EsgY/j+2LI2gaURPeseg/hY2lj6T1LMHgXIa3al/X 5/SmLubWxDVYf0ZT40m3IJIKKdszwjavoA18U0Z0fvhCmDS4BkqkHfx+jBmtjPJsX5cT FlK6j0s4aglykfd7v952uZfI+BLsQ0CeSDHRjKiaU8XMCzScepP0PfBur988xXYSudhn jcSw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1792082358; x=1792687158; h=content-type:cc:to:from:subject:message-id:references:mime-version :in-reply-to:date:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to:content-type; bh=ng4zxpw+boV8S1ELPw0fhKdUN4Zin1qbWJGc8Tx1FlY=; b=IRGpNONCcEIxZc3jm43SyOkbcJ5eO3PYQHzhW0Xsfs2d7FK4TMnq06JWzMAW7VrFmi D+w3BcGbdNhn9Mh9/Jv5AM1f1FB8yLFCBhRBI/ZKwY5RwF1lvTYLiM3fcuRavTwUtC89 /gYgHjGyZN3AVmeKYoPq3CmddmNB5LiBatErzb6zHKn7utarFdyQaXwClCW29qBbnbEf b+mECD891huF7k7QuJWy/DpvP7WWRDyWnz9fBsttkyagPuaV+wOgsfBIEhmT1OBjezaF z561BHH+zAobQUWNuwBAQwrM0a3HILd4/kvMbXw1TnGVKA+18Koy0Gft+900pzDSeFCo DVCw== X-Forwarded-Encrypted: i=1; AKwUvBznNaxAc3iZBbnxNJ6HZ5o980kqRwxz1obuoEWD/UUHZ0wxB/UPHgiQ9VCahUMDHr7/Wp3gqP3TGMZWScI=@vger.kernel.org X-Gm-Message-State: AFq9FYKAUeWiayrTcLP+43ODgHtFL0fMIsHOyv5E2xmXVEvxCBtH2ZmZ A7HQ6GYQibVaoOR4bKoX843aheOeelm6KsYQXOA1H4YzJKDQzQV7CFmHRnSImJvudGlnw909chl +C22ceV6ADFe1ZrH3Q/m8+DASrA== X-Received: from dlbrx16.prod.google.com ([2002:a05:7022:1710:b0:14e:353:712f]) (user=almasrymina job=prod-delivery.src-stubby-dispatcher) by 2002:a05:701b:2094:20b0:163:a197:722d with SMTP id a92af1059eb24-18069ea6621mr3696080c88.4.1792082357554; Thu, 15 Oct 2026 09:39:17 -0700 (PDT) Date: Thu, 15 Oct 2026 16:39:12 +0000 In-Reply-To: <20261015163915.1166043-1-almasrymina@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20261015163915.1166043-1-almasrymina@google.com> X-Mailer: git-send-email 2.56.0.176.g3575e65682-goog Message-ID: <20261015163915.1166043-2-almasrymina@google.com> Subject: [PATCH net v4 1/4] net: devmem: unmap dma_buf synchronously on queue uninstall From: Mina Almasry To: Jakub Kicinski , Mina Almasry , Pavel Begunkov , David Wei , Stanislav Fomichev , Taehee Yoo , Paolo Abeni , Kaiyuan Zhang , Bobby Eshleman , netdev@vger.kernel.org, linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-media@vger.kernel.org, dri-devel@lists.freedesktop.org Cc: Andrew Lunn , "David S. Miller" , Eric Dumazet , Jesper Dangaard Brouer , Ilias Apalodimas , Simon Horman , Shuah Khan , Sumit Semwal , "=?UTF-8?q?Christian=20K=C3=B6nig?=" , Dragos Tatulea , Nikolay Aleksandrov , Daniel Borkmann , Tariq Toukan , Kaifeng Wang Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" When a bound net_device is unregistered while userspace still holds a netlink or TCP socket reference on a devmem binding, mp_dmabuf_devmem_uninstall() clears binding->dev to NULL so netdev_nl_sock_priv_destroy() will not touch the unregistered net_device, but leaves the dma_buf attachment mapped until __net_devmem_dmabuf_binding_free() runs later on socket close. Because dma_buf_attach() does not take a reference on dma_dev, device removal frees dma_dev while the attachment is still mapped. Closing the netlink socket afterwards triggers a slab use-after-free in dma_unmap_sg_attrs(): BUG: KASAN: slab-use-after-free in dma_unmap_sg_attrs+0x76/0x2b0 Read of size 8 at addr ffff88810fae64b0 by task kworker/3:1/53 Workqueue: events __net_devmem_dmabuf_binding_free Call Trace: dma_unmap_sg_attrs+0x76/0x2b0 unmap_udmabuf+0x8e/0x100 dma_buf_unmap_attachment+0x145/0x330 dma_buf_unmap_attachment_unlocked+0x96/0x130 __net_devmem_dmabuf_binding_free+0x156/0x2e0 Freed by task 9: kfree+0x1b8/0x550 nsim_bus_dev_release+0xe/0x60 device_release+0xcf/0x250 Add net_devmem_dmabuf_binding_unmap() serialized under binding->lock and null out binding->sgt and binding->attachment after unmapping so unmapping is idempotent across queue uninstall and deferred binding destruction. Call it synchronously when the last bound RX queue is uninstalled in mp_dmabuf_devmem_uninstall(), except when a leased queue is unleased: the physical device is still registered and, if restarting its queue failed, may still be using the binding. The same use-after-free happens when the netlink socket is closed before the device is removed but RX tokens keep the binding alive. Once its queues are closed, nothing DMAs into an RX binding anymore and the outstanding tokens only need the net_iovs, so unmap it in net_devmem_unbind_dmabuf(). Make netif_mp_close_rxq() return an error when a queue failed to restart and may still be using the binding. Such a binding stays mapped and keeps binding->dev. Record a queue in bound_rxqs before opening it, so that a failed bind never leaves a queue using the binding without unbind knowing about it. Keep bindings in net_devmem_dmabuf_bindings until they are freed and hide unbound ones from TX lookups with an xarray mark, so that on NETDEV_UNREGISTER the remaining unbound RX bindings of the device can be found, detached and unmapped, unless the device is a virtual device leasing queues, for the same reason as above. The walk runs under the device's instance lock and is skipped when the device only moves to another netns. Like netdev_nl_sock_priv_destroy(), __net_devmem_dmabuf_binding_free() takes the instance lock of binding->dev before unmapping and erasing the binding, so a binding the walk finds under the xarray lock stays alive while it is detached. Fixes: f8350a4358fc ("net: page_pool: add a mp hook to unregister_netdevice= *") Fixes: 0afc44d8cdf6 ("net: devmem: fix kernel panic when netlink socket clo= se after module unload") Suggested-by: Stanislav Fomichev Cc: Taehee Yoo Cc: Tariq Toukan Cc: Kaifeng Wang Assisted-by: LLM Signed-off-by: Mina Almasry Reviewed-by: Dragos Tatulea Acked-by: Stanislav Fomichev --- v4: - Unmap RX bindings in net_devmem_unbind_dmabuf() once their queues are closed, and make netif_mp_close_rxq() report queue restart failures. The NETDEV_UNREGISTER walk only handles bindings whose queue failed to restart (Stanislav). - Drop net_devmem_lock. Walk the bindings under the xarray lock, and take the instance lock of binding->dev in the final free (Stanislav). - Record a queue in bound_rxqs before opening it, so unbind knows about every queue using the binding (Sashiko). v3: - Run the NETDEV_UNREGISTER walk under the netdev instance lock so it cannot race with net_devmem_unbind_dmabuf() (Sashiko). - Skip the walk when the device only moves to another netns (Sashiko). v2: - Don't unmap when a leased queue is unleased (Sashiko). - Unmap unbound RX bindings on NETDEV_UNREGISTER; keep bindings in net_devmem_dmabuf_bindings until freed, hidden from TX lookups by NET_DEVMEM_BINDING_LIVE (Sashiko). - Update the binding->lock and binding->ref comments (Sashiko). --- include/net/page_pool/memory_provider.h | 4 +- net/core/devmem.c | 137 +++++++++++++++++++----- net/core/devmem.h | 10 +- net/core/netdev-genl.c | 1 + net/core/netdev_rx_queue.c | 26 +++-- 5 files changed, 140 insertions(+), 38 deletions(-) diff --git a/include/net/page_pool/memory_provider.h b/include/net/page_poo= l/memory_provider.h index 255ce4cfd9755..a999a9b28bc70 100644 --- a/include/net/page_pool/memory_provider.h +++ b/include/net/page_pool/memory_provider.h @@ -26,8 +26,8 @@ void net_mp_niov_clear_page_pool(struct net_iov *niov); int netif_mp_open_rxq(struct net_device *dev, unsigned int rxq_idx, const struct pp_memory_provider_params *p, struct netlink_ext_ack *extack); -void netif_mp_close_rxq(struct net_device *dev, unsigned int rxq_idx, - const struct pp_memory_provider_params *old_p); +int netif_mp_close_rxq(struct net_device *dev, unsigned int rxq_idx, + const struct pp_memory_provider_params *old_p); =20 /** * net_mp_netmem_place_in_cache() - give a netmem to a page pool diff --git a/net/core/devmem.c b/net/core/devmem.c index f4d60654ce7fd..20d95b24d5951 100644 --- a/net/core/devmem.c +++ b/net/core/devmem.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -20,6 +21,7 @@ #include #include =20 +#include "dev.h" #include "devmem.h" #include "mp_dmabuf_devmem.h" #include "page_pool_priv.h" @@ -27,6 +29,7 @@ /* Device memory support */ =20 static DEFINE_XARRAY_FLAGS(net_devmem_dmabuf_bindings, XA_FLAGS_ALLOC1); +#define NET_DEVMEM_BINDING_LIVE XA_MARK_1 =20 static const struct memory_provider_ops dmabuf_devmem_ops; =20 @@ -58,9 +61,26 @@ static void net_devmem_dmabuf_binding_release(struct per= cpu_ref *ref) schedule_work(&binding->unbind_w); } =20 +static void +net_devmem_dmabuf_binding_unmap(struct net_devmem_dmabuf_binding *binding) +{ + lockdep_assert_held(&binding->lock); + + if (!binding->sgt) + return; + + dma_buf_unmap_attachment_unlocked(binding->attachment, binding->sgt, + binding->direction); + dma_buf_detach(binding->dmabuf, binding->attachment); + binding->sgt =3D NULL; + binding->attachment =3D NULL; +} + void __net_devmem_dmabuf_binding_free(struct work_struct *wq) { struct net_devmem_dmabuf_binding *binding =3D container_of(wq, typeof(*bi= nding), unbind_w); + netdevice_tracker dev_tracker; + struct net_device *dev; =20 size_t size, avail; =20 @@ -74,9 +94,23 @@ void __net_devmem_dmabuf_binding_free(struct work_struct= *wq) size, avail)) gen_pool_destroy(binding->chunk_pool); =20 - dma_buf_unmap_attachment_unlocked(binding->attachment, binding->sgt, - binding->direction); - dma_buf_detach(binding->dmabuf, binding->attachment); + mutex_lock(&binding->lock); + dev =3D binding->dev; + if (dev) + netdev_hold(dev, &dev_tracker, GFP_KERNEL); + mutex_unlock(&binding->lock); + + if (dev) + netdev_lock(dev); + mutex_lock(&binding->lock); + net_devmem_dmabuf_binding_unmap(binding); + mutex_unlock(&binding->lock); + xa_erase(&net_devmem_dmabuf_bindings, binding->id); + if (dev) { + netdev_unlock(dev); + netdev_put(dev, &dev_tracker); + } + dma_buf_put(binding->dmabuf); xa_destroy(&binding->bound_rxqs); percpu_ref_exit(&binding->ref); @@ -128,11 +162,13 @@ void net_devmem_unbind_dmabuf(struct net_devmem_dmabu= f_binding *binding) struct netdev_rx_queue *rxq; unsigned long xa_idx; unsigned int rxq_idx; + bool busy =3D false; =20 - xa_erase(&net_devmem_dmabuf_bindings, binding->id); + xa_clear_mark(&net_devmem_dmabuf_bindings, binding->id, + NET_DEVMEM_BINDING_LIVE); =20 /* Ensure no tx net_devmem_lookup_dmabuf() are in flight after the - * erase. + * mark is cleared. */ synchronize_net(); =20 @@ -147,12 +183,62 @@ void net_devmem_unbind_dmabuf(struct net_devmem_dmabu= f_binding *binding) =20 rxq_idx =3D get_netdev_rx_queue_index(rxq); =20 - netif_mp_close_rxq(binding->dev, rxq_idx, &mp_params); + if (netif_mp_close_rxq(binding->dev, rxq_idx, &mp_params)) + busy =3D true; + } + + if (binding->direction =3D=3D DMA_FROM_DEVICE && binding->dev && !busy) { + mutex_lock(&binding->lock); + WRITE_ONCE(binding->dev, NULL); + net_devmem_dmabuf_binding_unmap(binding); + mutex_unlock(&binding->lock); } =20 percpu_ref_kill(&binding->ref); } =20 +static bool +net_devmem_dev_unregister_rx(struct net_devmem_dmabuf_binding *binding, + unsigned long xa_idx, struct net_device *dev) +{ + return binding->direction =3D=3D DMA_FROM_DEVICE && + READ_ONCE(binding->dev) =3D=3D dev && + !xa_get_mark(&net_devmem_dmabuf_bindings, xa_idx, + NET_DEVMEM_BINDING_LIVE); +} + +static void net_devmem_dev_detach(struct net_devmem_dmabuf_binding *bindin= g, + struct net_device *dev) +{ + mutex_lock(&binding->lock); + WRITE_ONCE(binding->dev, NULL); + if (!netif_is_queue_leasee(dev)) + net_devmem_dmabuf_binding_unmap(binding); + mutex_unlock(&binding->lock); +} + +void net_devmem_dev_unregister(struct net_device *dev) +{ + struct net_devmem_dmabuf_binding *binding; + unsigned long xa_idx; + + netdev_assert_locked(dev); + + if (dev->reg_state !=3D NETREG_UNREGISTERING) + return; + + xa_lock(&net_devmem_dmabuf_bindings); + xa_for_each(&net_devmem_dmabuf_bindings, xa_idx, binding) { + if (!net_devmem_dev_unregister_rx(binding, xa_idx, dev)) + continue; + + xa_unlock(&net_devmem_dmabuf_bindings); + net_devmem_dev_detach(binding, dev); + xa_lock(&net_devmem_dmabuf_bindings); + } + xa_unlock(&net_devmem_dmabuf_bindings); +} + int net_devmem_bind_dmabuf_to_queue(struct net_device *dev, u32 rxq_idx, struct net_devmem_dmabuf_binding *binding, struct netlink_ext_ack *extack) @@ -168,20 +254,16 @@ int net_devmem_bind_dmabuf_to_queue(struct net_device= *dev, u32 rxq_idx, if (binding->niov_shift !=3D PAGE_SHIFT) mp_params.rx_page_size =3D 1U << binding->niov_shift; =20 - err =3D netif_mp_open_rxq(dev, rxq_idx, &mp_params, extack); - if (err) - return err; - rxq =3D __netif_get_rx_queue(dev, rxq_idx); err =3D xa_alloc(&binding->bound_rxqs, &xa_idx, rxq, xa_limit_32b, GFP_KERNEL); if (err) - goto err_close_rxq; + return err; =20 - return 0; + err =3D netif_mp_open_rxq(dev, rxq_idx, &mp_params, extack); + if (err) + xa_erase(&binding->bound_rxqs, xa_idx); =20 -err_close_rxq: - netif_mp_close_rxq(dev, rxq_idx, &mp_params); return err; } =20 @@ -331,6 +413,8 @@ net_devmem_bind_dmabuf(struct net_device *dev, void *vd= ev, GFP_KERNEL); if (err < 0) goto err_free_chunks; + xa_set_mark(&net_devmem_dmabuf_bindings, binding->id, + NET_DEVMEM_BINDING_LIVE); =20 list_add(&binding->list, &priv->bindings); =20 @@ -359,9 +443,11 @@ net_devmem_bind_dmabuf(struct net_device *dev, void *v= dev, struct net_devmem_dmabuf_binding *net_devmem_lookup_dmabuf(u32 id) { struct net_devmem_dmabuf_binding *binding; + unsigned long xa_idx =3D id; =20 rcu_read_lock(); - binding =3D xa_load(&net_devmem_dmabuf_bindings, id); + binding =3D xa_find(&net_devmem_dmabuf_bindings, &xa_idx, id, + NET_DEVMEM_BINDING_LIVE); if (binding) { if (!net_devmem_dmabuf_binding_get(binding)) binding =3D NULL; @@ -529,16 +615,19 @@ static void mp_dmabuf_devmem_uninstall(void *mp_priv, unsigned long xa_idx; =20 xa_for_each(&binding->bound_rxqs, xa_idx, bound_rxq) { - if (bound_rxq =3D=3D rxq) { - xa_erase(&binding->bound_rxqs, xa_idx); - if (xa_empty(&binding->bound_rxqs)) { - mutex_lock(&binding->lock); - ASSERT_EXCLUSIVE_WRITER(binding->dev); - WRITE_ONCE(binding->dev, NULL); - mutex_unlock(&binding->lock); - } - break; + if (bound_rxq !=3D rxq) + continue; + + xa_erase(&binding->bound_rxqs, xa_idx); + if (xa_empty(&binding->bound_rxqs)) { + mutex_lock(&binding->lock); + ASSERT_EXCLUSIVE_WRITER(binding->dev); + WRITE_ONCE(binding->dev, NULL); + if (!rxq->lease) + net_devmem_dmabuf_binding_unmap(binding); + mutex_unlock(&binding->lock); } + break; } } =20 diff --git a/net/core/devmem.h b/net/core/devmem.h index 4a293a7d1149c..a834e0f698c7e 100644 --- a/net/core/devmem.h +++ b/net/core/devmem.h @@ -27,7 +27,7 @@ struct net_devmem_dmabuf_binding { */ void *vdev; struct gen_pool *chunk_pool; - /* Protect dev */ + /* Protect dev, attachment and sgt */ struct mutex lock; =20 /* The user holds a ref (via the netlink API) for as long as they want @@ -38,7 +38,8 @@ struct net_devmem_dmabuf_binding { * * The binding undos itself and unmaps the underlying dmabuf once all * those refs are dropped and the binding is no longer desired or in - * use. + * use. RX bindings are unmapped earlier, once they are no longer + * installed on any queue. * * net_devmem_get_net_iov() on dmabuf net_iovs will increment this * reference, making sure that the binding remains alive until all the @@ -100,6 +101,7 @@ net_devmem_bind_dmabuf(struct net_device *dev, void *vd= ev, struct netlink_ext_ack *extack); struct net_devmem_dmabuf_binding *net_devmem_lookup_dmabuf(u32 id); void net_devmem_unbind_dmabuf(struct net_devmem_dmabuf_binding *binding); +void net_devmem_dev_unregister(struct net_device *dev); int net_devmem_bind_dmabuf_to_queue(struct net_device *dev, u32 rxq_idx, struct net_devmem_dmabuf_binding *binding, struct netlink_ext_ack *extack); @@ -196,6 +198,10 @@ net_devmem_unbind_dmabuf(struct net_devmem_dmabuf_bind= ing *binding) { } =20 +static inline void net_devmem_dev_unregister(struct net_device *dev) +{ +} + static inline int net_devmem_bind_dmabuf_to_queue(struct net_device *dev, u32 rxq_idx, struct net_devmem_dmabuf_binding *binding, diff --git a/net/core/netdev-genl.c b/net/core/netdev-genl.c index 33b9f4eb9565a..b04e22937b789 100644 --- a/net/core/netdev-genl.c +++ b/net/core/netdev-genl.c @@ -1488,6 +1488,7 @@ static int netdev_genl_netdevice_event(struct notifie= r_block *nb, break; case NETDEV_UNREGISTER: netdev_lock(netdev); + net_devmem_dev_unregister(netdev); netdev_genl_dev_notify(netdev, NETDEV_CMD_DEV_DEL_NTF); netdev_unlock(netdev); break; diff --git a/net/core/netdev_rx_queue.c b/net/core/netdev_rx_queue.c index 00a7011eb4d57..a7261fedd5e76 100644 --- a/net/core/netdev_rx_queue.c +++ b/net/core/netdev_rx_queue.c @@ -275,8 +275,8 @@ int netif_mp_open_rxq(struct net_device *dev, unsigned = int rxq_idx, return ret; } =20 -static void __netif_mp_close_rxq(struct net_device *dev, unsigned int rxq_= idx, - const struct pp_memory_provider_params *old_p) +static int __netif_mp_close_rxq(struct net_device *dev, unsigned int rxq_i= dx, + const struct pp_memory_provider_params *old_p) { struct netdev_queue_config qcfg[2]; struct netdev_rx_queue *rxq; @@ -289,35 +289,41 @@ static void __netif_mp_close_rxq(struct net_device *d= ev, unsigned int rxq_idx, */ if (dev->reg_state > NETREG_REGISTERED && !rxq->mp_params.mp_ops) - return; + return 0; =20 if (WARN_ON_ONCE(rxq->mp_params.mp_ops !=3D old_p->mp_ops || rxq->mp_params.mp_priv !=3D old_p->mp_priv)) - return; + return -EINVAL; =20 netdev_queue_config(dev, rxq_idx, &qcfg[0]); memset(&rxq->mp_params, 0, sizeof(rxq->mp_params)); netdev_queue_config(dev, rxq_idx, &qcfg[1]); =20 err =3D netdev_rx_queue_reconfig(dev, rxq_idx, &qcfg[0], &qcfg[1]); - WARN_ON(err && err !=3D -ENETDOWN); + if (err =3D=3D -ENETDOWN) + return 0; + WARN_ON(err); + return err; } =20 -void netif_mp_close_rxq(struct net_device *dev, unsigned int rxq_idx, - const struct pp_memory_provider_params *old_p) +int netif_mp_close_rxq(struct net_device *dev, unsigned int rxq_idx, + const struct pp_memory_provider_params *old_p) { + int ret; + if (WARN_ON_ONCE(rxq_idx >=3D dev->real_num_rx_queues)) - return; + return -EINVAL; if (!netif_rxq_is_leased(dev, rxq_idx)) return __netif_mp_close_rxq(dev, rxq_idx, old_p); =20 if (!__netif_get_rx_queue_lease(&dev, &rxq_idx, NETIF_VIRT_TO_PHYS)) { WARN_ON_ONCE(1); - return; + return -EINVAL; } netdev_lock(dev); - __netif_mp_close_rxq(dev, rxq_idx, old_p); + ret =3D __netif_mp_close_rxq(dev, rxq_idx, old_p); netdev_unlock(dev); + return ret; } =20 void __netif_mp_uninstall_rxq(struct netdev_rx_queue *rxq, --=20 2.56.0.176.g3575e65682-goog From nobody Fri Oct 23 22:16:12 2026 Received: from mail-dy1-f197.google.com (mail-dy1-f197.google.com [74.125.82.197]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 93F0F524B14 for ; Thu, 15 Oct 2026 16:39:20 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.197 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1792082373; cv=none; b=XleYTO3+S2030ZMNrBiIozuIXJMF7Fskmeu9wtV5h1LIaPKpRT2JLmseFWYzgNiViS4qR3UkhtwV6tNpHluvryqwB3OUATdQDHrojrbn8xWeVHQOOMUMyXGK8eF88fC91t5mhSCHwbPRcqrmAsxQUDicjdADbib77PZja9rUyNQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1792082373; c=relaxed/simple; bh=Txx+SO8uwxuJ3Ktm0Hsppbh1+qsaoXJIVwNfp/iA60c=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=sdcpuZhRNWeBy30VvMYFabrDWM1FGVRc24hzfo91sjdGUgOldRLFzKD9aLGaYszcADLN/tQJyAzPU2hSg+W5Ew1/Yj5fIJUwXrg5ieAZAhyGA6qa0z9gNBQoVSyPfDxkpvNy7VtBqq0DOVxVShE8Yc8gKFrAgOmEy/Qf2GpHzMw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--almasrymina.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=dloQYJ5G; arc=none smtp.client-ip=74.125.82.197 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--almasrymina.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="dloQYJ5G" Received: by mail-dy1-f197.google.com with SMTP id 5a478bee46e88-35c5aef2a74so4226681eec.0 for ; Thu, 15 Oct 2026 09:39:20 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1792082359; x=1792687159; darn=vger.kernel.org; h=content-type:cc:to:from:subject:message-id:references:mime-version :in-reply-to:date:from:to:cc:subject:date:message-id:reply-to :content-type; bh=7vrwp4HC01Z0kE3Wy37HFrMt6VtKQXq+YFsCUGH+2uM=; b=dloQYJ5GNV0bEqh9RgMaDW7gbbP4NocC2aINiEXJ9dxt2CfEfR6V/aSfqruRWKpcXw jJP+vHmCEtjmyNnMj+ZgtznwQGpzFnRkCExnGXkifJusM4Ja+0bDJ6f3Z0gNZZsfweGe 318zRti4x0BNUjPcr5J0HdWg17o7eR79TaF3j28/UKaH9f5KGLJWtCQ3IwvHWm86wmVN NkvsTJ1SIDuUdo6N88W0EcbOc/cD1C+X1Z4fzyZcLPmOJOu1OWOnz3pn1ECjjc8E1+No 6xmnswuPfqGN6zZ/fg4MfVl7ssih7dhOEFWv1ocPZDUtnqXAK5Soms0IqfYMpX6JwYtQ BWbQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1792082359; x=1792687159; h=content-type:cc:to:from:subject:message-id:references:mime-version :in-reply-to:date:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to:content-type; bh=7vrwp4HC01Z0kE3Wy37HFrMt6VtKQXq+YFsCUGH+2uM=; b=HBZKKMq+gSKaeYPa0dJBdOP0Xa3SMfLolXi7+RSnvQ/FwUpLRst7sNKp4XT9MYU4MN kKK+tkC1bg0UCPczhd70nffMbRc4a2UB1y2QKV34rcLtyEVNRZaRo41iIlXdgTRMTLdU pGQYBLi+R6sE3hWAVJz8dE7KyEjTX6+YErd1HA05EtP98kROTaximTbCfo+GuQDEP4M5 Sa+ooNDVVPCm+qfm5Q5K3eW7hSZe5TCr+IhxJ28jrIcfCIBgAej8H287m98AqNdy0+Y5 G9LLKiDk9/+8VZ1ywFfnrgttfaInSDD1+ah3TXY5DoqlIA3hcDsKvdqXZREuYizsaB7S HHHQ== X-Forwarded-Encrypted: i=1; AKwUvBz+Xf3DceY7VUM7FUtSR9M7QEFL6FRbL4o29m6XS83rrHAWSkUoZby0bp4y3jcmzywHiEOUhKJW20gBiq0=@vger.kernel.org X-Gm-Message-State: AFq9FYKu2u0H7LQciSNNvEcvvnUSOygdoBBJZuiHkzkMy7tRqbwF7ch8 hlJ5PQGuiKZGnbsoOqgKw9+Ikl8IYIdjswiwZVVK4ARNhZm8Ugvc5hPTgzL11ilmgJr0fIcQj8g PmjbEYSi2PbS9mJ/lHynADltOTw== X-Received: from dybtm1.prod.google.com ([2002:a05:7300:f0c1:b0:358:cd5e:4e5c]) (user=almasrymina job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7300:e002:b0:35d:5a2c:2321 with SMTP id 5a478bee46e88-35d5a2c2980mr8679042eec.20.1792082358745; Thu, 15 Oct 2026 09:39:18 -0700 (PDT) Date: Thu, 15 Oct 2026 16:39:13 +0000 In-Reply-To: <20261015163915.1166043-1-almasrymina@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20261015163915.1166043-1-almasrymina@google.com> X-Mailer: git-send-email 2.56.0.176.g3575e65682-goog Message-ID: <20261015163915.1166043-3-almasrymina@google.com> Subject: [PATCH net v4 2/4] net: devmem: detach TX bindings on NETDEV_UNREGISTER From: Mina Almasry To: Jakub Kicinski , Mina Almasry , Pavel Begunkov , David Wei , Stanislav Fomichev , Taehee Yoo , Paolo Abeni , Kaiyuan Zhang , Bobby Eshleman , netdev@vger.kernel.org, linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-media@vger.kernel.org, dri-devel@lists.freedesktop.org Cc: Andrew Lunn , "David S. Miller" , Eric Dumazet , Jesper Dangaard Brouer , Ilias Apalodimas , Simon Horman , Shuah Khan , Sumit Semwal , "=?UTF-8?q?Christian=20K=C3=B6nig?=" , Dragos Tatulea , Nikolay Aleksandrov , Daniel Borkmann , Tariq Toukan , Kaifeng Wang Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" TX devmem bindings (NETDEV_CMD_BIND_TX) have no bound RX queues, so mp_dmabuf_devmem_uninstall() never runs when binding->dev or binding->vdev unregisters. As a result, binding->dev and binding->vdev dangle pointing to freed struct net_device memory, and the dma_buf remains attached and mapped after device removal. Closing the netlink socket after netdev unregistration dereferences the freed struct net_device in netdev_nl_sock_priv_destroy(): BUG: unable to handle page fault for address: ffff88824ca9b000 Oops: Oops: 0000 [#1] SMP KASAN NOPTI RIP: 0010:netdev_nl_sock_priv_destroy+0xbd/0x1e0 Call Trace: genl_release+0xf5/0x190 netlink_release+0xd01/0x18d0 __sock_release+0xb0/0x270 sock_close+0x18/0x20 Extend net_devmem_dev_unregister() to TX bindings, including ones whose netlink socket is already closed but which TCP retransmits keep alive: - When binding->dev (the physical DMA-capable device) unregisters, clear the binding's NET_DEVMEM_BINDING_LIVE mark and both binding->dev and binding->vdev, and unmap the dma_buf under binding->lock. No grace period is needed: the device is already closed, so nothing hands the DMA addresses to the hardware anymore, and concurrent senders only compare against binding->dev. - When only binding->vdev (virtual device) unregisters while binding->dev lives, only clear binding->vdev so no new TX sends match the virtual device while allowing in-flight TX packets on binding->dev to finish DMA safely. Fixes: bd61848900bf ("net: devmem: Implement TX path") Fixes: 1abe839b34ae ("net: devmem: support TX over NETMEM_TX_NO_DMA devices= ") Suggested-by: Stanislav Fomichev Cc: Kaiyuan Zhang Cc: Bobby Eshleman Cc: Tariq Toukan Cc: Kaifeng Wang Assisted-by: LLM Signed-off-by: Mina Almasry Reviewed-by: Dragos Tatulea Acked-by: Stanislav Fomichev --- v4: - Detach and unmap TX bindings directly from the xarray walk, without a grace period or a second pass (Stanislav). v3: - Detach all matching TX bindings first, then call synchronize_net() once before unmapping them, instead of once per binding. Don't wait for a grace period when only binding->vdev is cleared, since vdev is never dereferenced (Sashiko). - Don't detach TX bindings when the device only moves to another netns (Sashiko). v2: - Build on the net_devmem_dev_unregister() walk from the previous patch, so unbound TX bindings are also detached (Sashiko). Clear the binding's LIVE mark instead of erasing its ID. - The walk holds net_devmem_lock, which the final free also takes before erasing the binding, instead of using RCU and percpu_ref_tryget(), so it no longer takes a binding reference (Stanislav). --- net/core/devmem.c | 22 +++++++++++++++++++++- net/core/devmem.h | 3 ++- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/net/core/devmem.c b/net/core/devmem.c index 20d95b24d5951..d33a092c6e4bb 100644 --- a/net/core/devmem.c +++ b/net/core/devmem.c @@ -207,11 +207,30 @@ net_devmem_dev_unregister_rx(struct net_devmem_dmabuf= _binding *binding, NET_DEVMEM_BINDING_LIVE); } =20 +static bool +net_devmem_dev_unregister_tx(struct net_devmem_dmabuf_binding *binding, + unsigned long xa_idx, struct net_device *dev) +{ + if (binding->direction !=3D DMA_TO_DEVICE) + return false; + + if (READ_ONCE(binding->vdev) =3D=3D dev) + WRITE_ONCE(binding->vdev, NULL); + + if (READ_ONCE(binding->dev) !=3D dev) + return false; + + __xa_clear_mark(&net_devmem_dmabuf_bindings, xa_idx, + NET_DEVMEM_BINDING_LIVE); + return true; +} + static void net_devmem_dev_detach(struct net_devmem_dmabuf_binding *bindin= g, struct net_device *dev) { mutex_lock(&binding->lock); WRITE_ONCE(binding->dev, NULL); + WRITE_ONCE(binding->vdev, NULL); if (!netif_is_queue_leasee(dev)) net_devmem_dmabuf_binding_unmap(binding); mutex_unlock(&binding->lock); @@ -229,7 +248,8 @@ void net_devmem_dev_unregister(struct net_device *dev) =20 xa_lock(&net_devmem_dmabuf_bindings); xa_for_each(&net_devmem_dmabuf_bindings, xa_idx, binding) { - if (!net_devmem_dev_unregister_rx(binding, xa_idx, dev)) + if (!net_devmem_dev_unregister_rx(binding, xa_idx, dev) && + !net_devmem_dev_unregister_tx(binding, xa_idx, dev)) continue; =20 xa_unlock(&net_devmem_dmabuf_bindings); diff --git a/net/core/devmem.h b/net/core/devmem.h index a834e0f698c7e..ce069e1736416 100644 --- a/net/core/devmem.h +++ b/net/core/devmem.h @@ -39,7 +39,8 @@ struct net_devmem_dmabuf_binding { * The binding undos itself and unmaps the underlying dmabuf once all * those refs are dropped and the binding is no longer desired or in * use. RX bindings are unmapped earlier, once they are no longer - * installed on any queue. + * installed on any queue. TX bindings are unmapped earlier if their + * device is unregistered. * * net_devmem_get_net_iov() on dmabuf net_iovs will increment this * reference, making sure that the binding remains alive until all the --=20 2.56.0.176.g3575e65682-goog From nobody Fri Oct 23 22:16:12 2026 Received: from mail-dl1-f71.google.com (mail-dl1-f71.google.com [74.125.82.71]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C0213513555 for ; Thu, 15 Oct 2026 16:39:21 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.71 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1792082375; cv=none; b=QrbxWS4dWn1r2J7MwScDGKTrPF0GkLj6cWmI6nolNrbqW3BiIWRFXYzhZLf3zVDRQpnB/aQtgahOAz/1q1MMR5ICwp+g83DGT2maKTgMTWAIubRlMm/26/aHkDFxk1AK/QGgD1VHWBEpHsYdtw7hiIqZF5aDV9i+UOcj1/L+tzg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1792082375; c=relaxed/simple; bh=vO6YEQIMsSYDbMWgTeXfd0RkPWpXF5baIQZyZXdCBFU=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=Kg1Wj9+7FmX2ORtJ5vi/1i/nf9LTNutcq6w1bH+CEkBezMAQE2VYpOs8xxFMcvpPQ41Iea0P4QTXojUU2slVJxY/lgeMu8uHpdTwjgstg8/lZg3hBh+dZoTGj+Zvpz8srrqOLHGLt2lIt1nMDjLIt2s4DDvUaU0CK++b2NlfU9k= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--almasrymina.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=bRJsHChG; arc=none smtp.client-ip=74.125.82.71 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--almasrymina.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="bRJsHChG" Received: by mail-dl1-f71.google.com with SMTP id a92af1059eb24-151ba226e1bso6653260c88.0 for ; Thu, 15 Oct 2026 09:39:21 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1792082361; x=1792687161; darn=vger.kernel.org; h=content-type:cc:to:from:subject:message-id:references:mime-version :in-reply-to:date:from:to:cc:subject:date:message-id:reply-to :content-type; bh=01xy5PseRVpPPje1JnCly3s3YIplU78ERdCkYH+3Kds=; b=bRJsHChG3DrA2JmAqi7FcqG0MVg1r6kxt044hD9wtVlIdbMpnkUxKkAblAiN4r1flV xij0PTsh5Eh6D+q0waI+U4nY6IHaHvzsWMofjue2FjDjRJBPu5GkkwggJOrBOicw8nEn M0VYywW1HI82zr8Q741/o6akGR+h/YOvLGZ814yhIQhkv4rFJeMnwI5Y9FJHtnccZdsO tqEVCYSkWDwOmuEHxrEKu+AdA3Z72dKlVIuxvBLFtiQAMmMCaDAmHJdokqjZWiwxgnAU 1+rNiKMqUuMAwO2zHXIUUR1FFaLdM3i8xTtY5yYW/pF8URP1u1pzuO1ooeKe4ryud1Q9 xT8A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1792082361; x=1792687161; h=content-type:cc:to:from:subject:message-id:references:mime-version :in-reply-to:date:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to:content-type; bh=01xy5PseRVpPPje1JnCly3s3YIplU78ERdCkYH+3Kds=; b=OKohVjCNX1E1gP/PR5S0gkIpDqmXn/s+pnaSeZmQnh9Uf1Ls8qxVQQbo9BVTPnKUAp SjGdkC9OwCjOf1zMAih3Xsz6eIFDHzL/sRfTwTxeli/BI/hBszyxEf5qlWE4saemtlIz P6bcVmFx//I8S99kcI7N/BvOuoIScmZo1bL+bCy4ZNY1CpHKlQlqG9XOn4HvOLY53HqV ei3/Wz1lUEu0cT97tYZxhBNsH11ZE46GwnLsK41Y47lknZ4JyI8rGn82n2qDaENch2WM yWGwc0L1cXV8U6CY8b8JiizzKHDA34HDJevgqDA+QBEdcVeY3Au9FBDWM3CJSk9mm8IW rDwA== X-Forwarded-Encrypted: i=1; AKwUvBw1pdtOZ4wYQQDZXlpElmTqvMspqWh3UAPINCwlxtmwGbUUhZvyBECxPHwjhDK/LbLakQqOLgJKaFRKxXQ=@vger.kernel.org X-Gm-Message-State: AFq9FYLshcjYuZyvKWFvpMWaTzci/yKY+s4dEtJY+NR0mjwa7HX9zwU5 H2+kDHOQZ14E4E0MNmeimqw6UbKpbhuVobEA7qMTqhIn2Wg8AaxBW7NJvvSueFNa1+fx3ukxNpO /sQ4T9S3FcFGEqPMhyHe+7Fh2Lg== X-Received: from dlbts20-n1.prod.google.com ([2002:a05:7022:b054:10b0:17e:d816:7855]) (user=almasrymina job=prod-delivery.src-stubby-dispatcher) by 2002:a05:701b:2088:20b0:143:2719:566e with SMTP id a92af1059eb24-16a61a6e5f3mr27037111c88.42.1792082360355; Thu, 15 Oct 2026 09:39:20 -0700 (PDT) Date: Thu, 15 Oct 2026 16:39:14 +0000 In-Reply-To: <20261015163915.1166043-1-almasrymina@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20261015163915.1166043-1-almasrymina@google.com> X-Mailer: git-send-email 2.56.0.176.g3575e65682-goog Message-ID: <20261015163915.1166043-4-almasrymina@google.com> Subject: [PATCH net v4 3/4] netdevsim: support devmem RX and TX bindings From: Mina Almasry To: Jakub Kicinski , Mina Almasry , Pavel Begunkov , David Wei , Stanislav Fomichev , Taehee Yoo , Paolo Abeni , Kaiyuan Zhang , Bobby Eshleman , netdev@vger.kernel.org, linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-media@vger.kernel.org, dri-devel@lists.freedesktop.org Cc: Andrew Lunn , "David S. Miller" , Eric Dumazet , Jesper Dangaard Brouer , Ilias Apalodimas , Simon Horman , Shuah Khan , Sumit Semwal , "=?UTF-8?q?Christian=20K=C3=B6nig?=" , Dragos Tatulea , Nikolay Aleksandrov , Daniel Borkmann , Tariq Toukan , Kaifeng Wang Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Set a 64-bit DMA mask on nsim_bus_dev, enable NETMEM_TX_DMA on the netdevsim net_device, and configure page_pool instances with PP_FLAG_ALLOW_UNREADABLE_NETMEM and queue_idx, plus PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV when CONFIG_HAS_DMA is set, so devmem bind-rx and bind-tx netlink operations can be tested against netdevsim in software selftests. The pp_hold debugfs file allocates pages from the queue 0 page pool, so refuse it when that pool is backed by a memory provider, and take the netdev instance lock there so a concurrent bind-rx cannot swap the pool in between. The software PSP path checksums the payload when the peer does not decapsulate, so drop unreadable skbs there. Cc: Tariq Toukan Cc: Kaifeng Wang Assisted-by: LLM Signed-off-by: Mina Almasry Reviewed-by: Dragos Tatulea Acked-by: Stanislav Fomichev --- v3: - Only request page pool DMA mapping when CONFIG_HAS_DMA is set (Sashiko). - Refuse pp_hold on a page pool backed by a memory provider, under the netdev instance lock (Sashiko). - Drop unreadable skbs in the software PSP path when it would have to checksum the payload (Sashiko). --- drivers/net/netdevsim/bus.c | 1 + drivers/net/netdevsim/netdev.c | 27 +++++++++++++++++++++------ drivers/net/netdevsim/psp.c | 5 +++++ 3 files changed, 27 insertions(+), 6 deletions(-) diff --git a/drivers/net/netdevsim/bus.c b/drivers/net/netdevsim/bus.c index 5c55c308487b4..f87f5e037b6fb 100644 --- a/drivers/net/netdevsim/bus.c +++ b/drivers/net/netdevsim/bus.c @@ -462,6 +462,7 @@ nsim_bus_dev_new(unsigned int id, unsigned int port_cou= nt, unsigned int num_queu nsim_bus_dev->port_count =3D port_count; nsim_bus_dev->num_queues =3D num_queues; nsim_bus_dev->initial_net =3D current->nsproxy->net_ns; + dma_coerce_mask_and_coherent(&nsim_bus_dev->dev, DMA_BIT_MASK(64)); /* Disallow using nsim_bus_dev */ smp_store_release(&nsim_bus_dev->init, false); =20 diff --git a/drivers/net/netdevsim/netdev.c b/drivers/net/netdevsim/netdev.c index b4a99f3ceac60..357ea9e5623b3 100644 --- a/drivers/net/netdevsim/netdev.c +++ b/drivers/net/netdevsim/netdev.c @@ -414,19 +414,27 @@ static int nsim_poll(struct napi_struct *napi, int bu= dget) return done; } =20 -static int nsim_create_page_pool(struct page_pool **p, struct napi_struct = *napi) +static int nsim_create_page_pool(struct net_device *dev, struct page_pool = **p, + struct napi_struct *napi, int idx) { + struct netdevsim *ns =3D netdev_priv(dev); struct page_pool_params params =3D { .order =3D 0, .pool_size =3D NSIM_RING_SIZE, + .flags =3D PP_FLAG_ALLOW_UNREADABLE_NETMEM, + .max_len =3D PAGE_SIZE, .nid =3D NUMA_NO_NODE, - .dev =3D &napi->dev->dev, + .dev =3D &ns->nsim_bus_dev->dev, .napi =3D napi, .dma_dir =3D DMA_BIDIRECTIONAL, - .netdev =3D napi->dev, + .netdev =3D dev, + .queue_idx =3D idx, }; struct page_pool *pool; =20 + if (IS_ENABLED(CONFIG_HAS_DMA)) + params.flags |=3D PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV; + pool =3D page_pool_create(¶ms); if (IS_ERR(pool)) return PTR_ERR(pool); @@ -450,7 +458,7 @@ static int nsim_init_napi(struct netdevsim *ns) for (i =3D 0; i < dev->num_rx_queues; i++) { rq =3D ns->rq[i]; =20 - err =3D nsim_create_page_pool(&rq->page_pool, &rq->napi); + err =3D nsim_create_page_pool(dev, &rq->page_pool, &rq->napi, i); if (err) goto err_pp_destroy; } @@ -762,14 +770,16 @@ nsim_queue_mem_alloc(struct net_device *dev, if (ns->rq_reset_mode =3D=3D 1) { if (!netif_running(ns->netdev)) return -ENETDOWN; - return nsim_create_page_pool(&qmem->pp, &ns->rq[idx]->napi); + return nsim_create_page_pool(dev, &qmem->pp, + &ns->rq[idx]->napi, idx); } =20 qmem->rq =3D nsim_queue_alloc(); if (!qmem->rq) return -ENOMEM; =20 - err =3D nsim_create_page_pool(&qmem->rq->page_pool, &qmem->rq->napi); + err =3D nsim_create_page_pool(dev, &qmem->rq->page_pool, &qmem->rq->napi, + idx); if (err) goto err_free; =20 @@ -929,12 +939,15 @@ nsim_pp_hold_write(struct file *file, const char __us= er *data, return ret; =20 rtnl_lock(); + netdev_lock(ns->netdev); ret =3D count; if (val =3D=3D !!ns->page) goto exit; =20 if (!netif_running(ns->netdev) && val) { ret =3D -ENETDOWN; + } else if (val && page_pool_is_unreadable(ns->rq[0]->page_pool)) { + ret =3D -EOPNOTSUPP; } else if (val) { ns->page =3D page_pool_dev_alloc_pages(ns->rq[0]->page_pool); if (!ns->page) @@ -946,6 +959,7 @@ nsim_pp_hold_write(struct file *file, const char __user= *data, } =20 exit: + netdev_unlock(ns->netdev); rtnl_unlock(); return ret; } @@ -1051,6 +1065,7 @@ static int nsim_init_netdevsim(struct netdevsim *ns) ns->netdev->netdev_ops =3D &nsim_netdev_ops; ns->netdev->stat_ops =3D &nsim_stat_ops; ns->netdev->queue_mgmt_ops =3D &nsim_queue_mgmt_ops; + ns->netdev->netmem_tx =3D NETMEM_TX_DMA; netdev_lockdep_set_classes(ns->netdev); =20 err =3D nsim_udp_tunnels_info_create(ns->nsim_dev, ns->netdev); diff --git a/drivers/net/netdevsim/psp.c b/drivers/net/netdevsim/psp.c index 6b3532b5e360d..908cc0d391dc6 100644 --- a/drivers/net/netdevsim/psp.c +++ b/drivers/net/netdevsim/psp.c @@ -86,6 +86,11 @@ nsim_do_psp(struct sk_buff *skb, struct netdevsim *ns, __wsum csum; int udplen; =20 + if (!skb_frags_readable(skb)) { + rc =3D SKB_DROP_REASON_PSP_OUTPUT; + goto out_unlock; + } + /* Do not decapsulate. Receive the skb with the udp and psp * headers still there as if this is a normal udp packet. * psp_dev_encapsulate() sets udp checksum to 0, so we need to --=20 2.56.0.176.g3575e65682-goog From nobody Fri Oct 23 22:16:12 2026 Received: from mail-dl1-f69.google.com (mail-dl1-f69.google.com [74.125.82.69]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A14BA5304C9 for ; Thu, 15 Oct 2026 16:39:23 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.69 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1792082379; cv=none; b=VCEsjolTSAhzkwLoBcmpdEfE2Cnzol1Cv8eueuvdmdJl7p9CicAfcSh/yMCJSBOxSetuo0uoFHvfsqdm5/k38g5AHDAUK0mUiW92KL0xbYqtRvqErTQPRbTSXajQvbiPoYrZHQDGUhradm6w86SnOmpifsF8NhFiLeRzZ05F7OE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1792082379; c=relaxed/simple; bh=7+ZuMQGm1WqmYa/SemsnkHIbC/OIMDMcJFeU+26oqOg=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=jvHM+nUeaZMU0oKPNOiDzXUf7JL3uJc9zDmwbXwu8oVT3wgW+jt8QL4dYblgEopfqTvWcGVguSqA1tj2b+8Rcd2AJA9Z8MbRU11nioGglnnf7bVAlOEPiNkUJp8dBAbjjbUaM6mjGBXowbRprxfxMoSEdMUg7Jg+MycBkoXGGMc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--almasrymina.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=v/3NrS3t; arc=none smtp.client-ip=74.125.82.69 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--almasrymina.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="v/3NrS3t" Received: by mail-dl1-f69.google.com with SMTP id a92af1059eb24-149e8e8ad71so6292930c88.1 for ; Thu, 15 Oct 2026 09:39:23 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1792082362; x=1792687162; darn=vger.kernel.org; h=content-type:cc:to:from:subject:message-id:references:mime-version :in-reply-to:date:from:to:cc:subject:date:message-id:reply-to :content-type; bh=wON/Y5KE9qpxqraW/pAdp0YZ1NlG3ckfd93a0Oe+XQc=; b=v/3NrS3tAXlYq5GGohccUKrdWvYYYa5ZPCL3pwov1dZr3Sn46aASVdM/LkM8erPWcP xyUKgdaSj7Cnfb20/ihUatWKcAnX4ajUr4rl2D1DghOzwNiRl3Vuitp6oZbDaT2Zhmkv MRRR2LxKloftFxrDulx7MPNWPG/g7FHXuY2nZhJNF68ulo51AUBI56FntfJGBXQin1hJ FunBwnWWw17cLauAXiD86BGF79dN2uE4vLidEyJcrvTUnUm5NRGLEcALoE1ac1q+HuR/ +4aHdcge51bpIo9KAaqwIPMxLXJC/BZdtxoq/nCFsCBZQZ/5lM4jQx6AjPr1qlOaRSxP he/g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1792082362; x=1792687162; h=content-type:cc:to:from:subject:message-id:references:mime-version :in-reply-to:date:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to:content-type; bh=wON/Y5KE9qpxqraW/pAdp0YZ1NlG3ckfd93a0Oe+XQc=; b=kv4IUHLnvycuJ3DIc1Ya8PSImJqesFRBVmonWVhC2EVx2g1bXn/bdsAuISCT+uST8r bQJ+QusNpAXHm1CYXyqmMgHz9gtIMLIw3ksXcovuyPyGdqIES/tHwhh1HLyU9XZSN6Wh BNMytCwYCVkpVOr9Z5EEIbBQcm+Rr0pAB3qyj82QHzPBrPK8HDhwbz9BEtLyKAg2KVbQ hve1iiqgUnlpg0bA5ln8aO25Z8WpzbdH6ca7v/hXSat+ETj0OJbvfoy3xgBxLyHWixko ocjWJ+yCLDfuM4bnLz8n6P0QeotRFyK8kOQjU8Ybl817gY3b1V7MY0m9VSYOeV9/v5C7 3Bkw== X-Forwarded-Encrypted: i=1; AKwUvByirOCgSh0mHmhQNBCYQLPChcuJybw/VB2M39uaI473gAWPgZV23AQQO+5sWYP3ouuIYMS0587vcgOostE=@vger.kernel.org X-Gm-Message-State: AFq9FYJruZ4BmrE8pIbtpmFB/OGEclnsvAI5hijjDDCG4ZemFw07b5RN BMyvJbFvI8/VODtfQzf3qEgCNOqyymLtx1L7X+Ls9xqcfl8qzA6lX/kZID9C8cu6GulIwXHT/Ne JBYtuibtbc+nQmXezhsYJJPwj6w== X-Received: from dldyq4-n2.prod.google.com ([2002:a05:701b:4544:20b0:15f:4035:64c7]) (user=almasrymina job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7023:c00a:b0:149:1feb:6da4 with SMTP id a92af1059eb24-18111a09757mr2320444c88.32.1792082361807; Thu, 15 Oct 2026 09:39:21 -0700 (PDT) Date: Thu, 15 Oct 2026 16:39:15 +0000 In-Reply-To: <20261015163915.1166043-1-almasrymina@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20261015163915.1166043-1-almasrymina@google.com> X-Mailer: git-send-email 2.56.0.176.g3575e65682-goog Message-ID: <20261015163915.1166043-5-almasrymina@google.com> Subject: [PATCH net v4 4/4] selftests: net: add devmem RX and TX netdev unregister tests From: Mina Almasry To: Jakub Kicinski , Mina Almasry , Pavel Begunkov , David Wei , Stanislav Fomichev , Taehee Yoo , Paolo Abeni , Kaiyuan Zhang , Bobby Eshleman , netdev@vger.kernel.org, linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-media@vger.kernel.org, dri-devel@lists.freedesktop.org Cc: Andrew Lunn , "David S. Miller" , Eric Dumazet , Jesper Dangaard Brouer , Ilias Apalodimas , Simon Horman , Shuah Khan , Sumit Semwal , "=?UTF-8?q?Christian=20K=C3=B6nig?=" , Dragos Tatulea , Nikolay Aleksandrov , Daniel Borkmann , Tariq Toukan , Kaifeng Wang Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add devmem_bind_rx_unregister_check and devmem_bind_tx_unregister_check to nl_netdev.py to verify that unregistering a netdevsim device while a netlink socket holds an active RX or TX devmem binding synchronously detaches the dma_buf attachment and cleanly closes the netlink socket without use-after-free or page faults. Add the dma-buf, udmabuf and devmem options the tests need to tools/testing/selftests/net/config. Cc: Tariq Toukan Cc: Kaifeng Wang Assisted-by: LLM Signed-off-by: Mina Almasry Reviewed-by: Dragos Tatulea Acked-by: Stanislav Fomichev --- v3: - Count attachments of the test's own dma-buf only, and skip instead of passing when debugfs bufinfo is not readable (Sashiko). - Close the netlink socket after removing the device, clean up with defer(), and skip on EOPNOTSUPP from bind-rx/bind-tx (Sashiko). - Replace the hardcoded UDMABUF_CREATE value with a helper that computes it for the running architecture (Sashiko, Dragos), and close the memfd and /dev/udmabuf fds on errors (Sashiko). - Add the needed options to tools/testing/selftests/net/config (Sashiko). --- tools/testing/selftests/net/config | 4 + tools/testing/selftests/net/nl_netdev.py | 125 ++++++++++++++++++++++- 2 files changed, 126 insertions(+), 3 deletions(-) diff --git a/tools/testing/selftests/net/config b/tools/testing/selftests/n= et/config index 737e7e6327b37..1e21f0055a2c0 100644 --- a/tools/testing/selftests/net/config +++ b/tools/testing/selftests/net/config @@ -11,6 +11,7 @@ CONFIG_CRYPTO_ARIA=3Dy CONFIG_CRYPTO_CHACHA20POLY1305=3Dm CONFIG_CRYPTO_SHA1=3Dy CONFIG_CRYPTO_SM4_GENERIC=3Dy +CONFIG_DEBUG_FS=3Dy CONFIG_DEBUG_INFO_BTF=3Dy CONFIG_DEBUG_INFO_BTF_MODULES=3Dn CONFIG_DUMMY=3Dy @@ -76,6 +77,7 @@ CONFIG_NET_CLS_BPF=3Dm CONFIG_NET_CLS_FLOWER=3Dm CONFIG_NET_CLS_MATCHALL=3Dm CONFIG_NET_CLS_U32=3Dm +CONFIG_NET_DEVMEM=3Dy CONFIG_NETDEVSIM=3Dm CONFIG_NET_DROP_MONITOR=3Dm CONFIG_NETFILTER=3Dy @@ -120,6 +122,7 @@ CONFIG_OPENVSWITCH=3Dm CONFIG_PAGE_POOL_STATS=3Dy CONFIG_PSAMPLE=3Dm CONFIG_RPS=3Dy +CONFIG_SYNC_FILE=3Dy CONFIG_SYN_COOKIES=3Dy CONFIG_SYSCTL=3Dy CONFIG_SYSFS=3Dy @@ -131,6 +134,7 @@ CONFIG_TEST_BPF=3Dm CONFIG_TLS=3Dm CONFIG_TRACEPOINTS=3Dy CONFIG_TUN=3Dy +CONFIG_UDMABUF=3Dy CONFIG_USER_NS=3Dy CONFIG_VETH=3Dy CONFIG_VLAN_8021Q=3Dy diff --git a/tools/testing/selftests/net/nl_netdev.py b/tools/testing/selft= ests/net/nl_netdev.py index ceb44c8e1fec5..988878b71358c 100755 --- a/tools/testing/selftests/net/nl_netdev.py +++ b/tools/testing/selftests/net/nl_netdev.py @@ -6,10 +6,14 @@ Tests for the netdev netlink family. """ =20 import errno +import fcntl +import mmap +import os +import struct from os import system -from lib.py import ksft_run, ksft_exit +from lib.py import ksft_run, ksft_exit, KsftFailEx, KsftSkipEx from lib.py import ksft_eq, ksft_ge, ksft_ne, ksft_raises, ksft_busy_wait -from lib.py import NetdevFamily, NetdevSimDev, NlError, defer, ip +from lib.py import EthtoolFamily, NetdevFamily, NetdevSimDev, NlError, def= er, ip =20 =20 def empty_check(nf) -> None: @@ -366,6 +370,119 @@ def page_pool_stats_ifindex_check(nf) -> None: ksft_eq(cm.exception.nl_msg.extack['bad-attr'], '.info.id') =20 =20 +def _udmabuf_create_ioctl() -> int: + """Return UDMABUF_CREATE, _IOW('u', 0x42, struct udmabuf_create).""" + machine =3D os.uname().machine + if machine.startswith(("alpha", "mips", "ppc", "powerpc", "sparc")): + ioc_write =3D 4 << 29 + elif machine.startswith("parisc"): + ioc_write =3D 2 << 30 + else: + ioc_write =3D 1 << 30 + return ioc_write | 24 << 16 | ord("u") << 8 | 0x42 + + +def _create_udmabuf(num_pages=3D64) -> int: + """Create a sealed memfd-backed udmabuf fd for devmem tests.""" + if not os.path.exists("/dev/udmabuf"): + raise KsftSkipEx("/dev/udmabuf is not available") + + size =3D num_pages * mmap.PAGESIZE + memfd =3D os.memfd_create("devmem-ksft", os.MFD_ALLOW_SEALING) + try: + os.ftruncate(memfd, size) + fcntl.fcntl(memfd, fcntl.F_ADD_SEALS, fcntl.F_SEAL_SHRINK) + devfd =3D os.open("/dev/udmabuf", os.O_RDWR) + try: + req =3D bytearray(struct.pack("IIQQ", memfd, 0, 0, size)) + return fcntl.ioctl(devfd, _udmabuf_create_ioctl(), req) + finally: + os.close(devfd) + finally: + os.close(memfd) + + +def _dmabuf_attach_count(dmabuf_fd) -> int: + """Return the number of devices attached to dmabuf_fd, from debugfs.""" + path =3D "/sys/kernel/debug/dma_buf/bufinfo" + try: + with open(path, "r", encoding=3D"utf-8") as f: + lines =3D f.read().splitlines() + except OSError as e: + raise KsftSkipEx(f"{path} is not readable: {e}") from e + + ino =3D os.fstat(dmabuf_fd).st_ino + found =3D False + for line in lines: + fields =3D line.split("\t") + if not found: + found =3D (len(fields) =3D=3D 7 and fields[5].isdigit() and + int(fields[5]) =3D=3D ino) + elif line.startswith("Total "): + return int(line.split()[1]) + raise KsftFailEx(f"dma-buf {ino} not found in {path}") + + +def _devmem_bind(bind, attrs) -> None: + """Run a devmem bind op, skip the test if the device can't do devmem."= "" + try: + bind(attrs) + except NlError as e: + if e.error =3D=3D errno.EOPNOTSUPP: + raise KsftSkipEx(f"devmem binding not supported: {e}") from e + raise + + +def devmem_bind_rx_unregister_check(_nf) -> None: + """Verify RX devmem bindings detach dma_buf synchronously on netdev un= register.""" + dmabuf_fd =3D _create_udmabuf() + defer(os.close, dmabuf_fd) + nf_priv =3D NetdevFamily() + defer(nf_priv.close) + nsimdev =3D NetdevSimDev(queue_count=3D2) + defer(nsimdev.remove) + nsim =3D nsimdev.nsims[0] + + ip(f"link set dev {nsim.ifname} up") + with EthtoolFamily() as ef: + ef.rings_set({"header": {"dev-index": nsim.ifindex}, + "tcp-data-split": "enabled"}) + _devmem_bind(nf_priv.bind_rx, { + "ifindex": nsim.ifindex, + "fd": dmabuf_fd, + "queues": [{"id": 1, "type": "rx"}], + }) + ksft_ge(_dmabuf_attach_count(dmabuf_fd), 1) + + nsimdev.remove() + ksft_eq(_dmabuf_attach_count(dmabuf_fd), 0) + + nf_priv.close() + + +def devmem_bind_tx_unregister_check(_nf) -> None: + """Verify TX devmem bindings detach cleanly on netdev unregister witho= ut UAF.""" + dmabuf_fd =3D _create_udmabuf() + defer(os.close, dmabuf_fd) + nf_priv =3D NetdevFamily() + defer(nf_priv.close) + nsimdev =3D NetdevSimDev(queue_count=3D2) + defer(nsimdev.remove) + nsim =3D nsimdev.nsims[0] + + ip(f"link set dev {nsim.ifname} up") + _devmem_bind(nf_priv.bind_tx, { + "ifindex": nsim.ifindex, + "fd": dmabuf_fd, + }) + ksft_ge(_dmabuf_attach_count(dmabuf_fd), 1) + + nsimdev.remove() + ksft_eq(_dmabuf_attach_count(dmabuf_fd), 0) + + nf_priv.close() + + def main() -> None: """ Ksft boiler plate main """ nf =3D NetdevFamily() @@ -379,7 +496,9 @@ def main() -> None: page_pool_check, page_pool_dump_ifindex, page_pool_ifindex_leak_check, - page_pool_stats_ifindex_check + page_pool_stats_ifindex_check, + devmem_bind_rx_unregister_check, + devmem_bind_tx_unregister_check ], args=3D(nf, )) ksft_exit() --=20 2.56.0.176.g3575e65682-goog