* Question: prestera: clarify event handler RCU-list contract
From: Runyu Xiao @ 2026-06-27 13:50 UTC (permalink / raw)
To: Taras Chornyi
Cc: Runyu Xiao, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, netdev, linux-kernel, jianhao.xu
Hi Prestera maintainers,
This question comes from a candidate found by our static analysis tool
and then manually reviewed against the current tree. The audit used
CONFIG_PROVE_RCU_LIST as target-matched triage evidence; I am asking
for maintainer guidance because the source-level review did not prove
a use-after-free.
A CONFIG_PROVE_RCU_LIST audit flags the shared event-handler lookup
helper:
drivers/net/ethernet/marvell/prestera/prestera_hw.c:923 __find_event_handler()
The receive-side paths use prestera_find_event_handler(), which wraps
the helper in rcu_read_lock(), copies the handler record, and drops RCU
before invoking the callback.
The registration paths call __find_event_handler() directly:
drivers/net/ethernet/marvell/prestera/prestera_hw.c:2255 prestera_hw_event_handler_register()
drivers/net/ethernet/marvell/prestera/prestera_hw.c:2280 prestera_hw_event_handler_unregister()
Registration adds entries with list_add_rcu(). Unregistration removes
entries with list_del_rcu() and frees them with kfree_rcu(). The call
sites look init/fini-style, and I am not claiming a use-after-free here.
The question is only how the non-receive-side lookup contract is meant
to be expressed.
Would you prefer one of these directions?
1. split the helper into an RCU-reader lookup and a registration-time
lookup;
2. wrap the direct register/unregister lookups in a local RCU read-side
section;
3. add an explicit event-handler registration lock if concurrent
register/unregister is possible;
4. document that handler registration is init/fini ordered and leave
the direct helper calls unchanged.
I am intentionally sending this as a maintainer question rather than a
patch because the right shape depends on the intended registration and
teardown model.
Thanks.
^ permalink raw reply
* Question: bridge: clarify MST VLAN list RCU traversal contract
From: Runyu Xiao @ 2026-06-27 13:25 UTC (permalink / raw)
To: Nikolay Aleksandrov, Ido Schimmel
Cc: Runyu Xiao, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, bridge, netdev, linux-kernel,
jianhao.xu
Hi bridge maintainers,
This question comes from a candidate found by our static analysis tool
and then manually reviewed against the current tree. The audit used
CONFIG_PROVE_RCU_LIST as target-matched triage evidence; I am asking
for maintainer guidance because the source-level review did not prove
a use-after-free.
A CONFIG_PROVE_RCU_LIST audit flags the VLAN-list traversal in
br_mst_info_size():
net/bridge/br_mst.c:251 br_mst_info_size()
The helper walks vg->vlan_list with list_for_each_entry_rcu(). In the
direct local context, br_get_link_af_size_filtered() first enters an
RCU read-side section, resolves the bridge port or bridge VLAN group,
and calls br_get_num_vlan_infos(vg, filter_mask). That local RCU
read-side section is then dropped before the later MST sizing call:
net/bridge/br_netlink.c:104 rcu_read_lock()
net/bridge/br_netlink.c:113 br_get_num_vlan_infos(vg, filter_mask)
net/bridge/br_netlink.c:114 rcu_read_unlock()
net/bridge/br_netlink.c:123 br_mst_info_size(vg)
The helper is registered through rtnl_af_ops.get_link_af_size, and
bridge VLAN updates appear RTNL-centered, so the broader rtnetlink
sizing path may already provide the intended serialization. I am not
claiming a use-after-free here. The question is only whether the
RCU-list traversal contract around br_mst_info_size() should be made
explicit enough for CONFIG_PROVE_RCU_LIST to see it.
Would you prefer one of these directions?
1. keep the MST sizing loop inside an explicit rcu_read_lock() in
br_get_link_af_size_filtered();
2. pass a confirmed RTNL lockdep condition to the iterator in
br_mst_info_size();
3. document that the outer rtnetlink sizing path is the required
protection and leave the helper unchanged;
4. use a different bridge-specific pattern.
I am intentionally sending this as a maintainer question rather than a
patch because the right contract seems to depend on the bridge/rtnetlink
caller semantics.
Thanks.
^ permalink raw reply
* [PATCH net] nfc: pn533: fix use-after-free in pn533_recv_frame
From: Yinhao Hu @ 2026-06-27 13:13 UTC (permalink / raw)
To: netdev
Cc: David Heidelberg, Dan Carpenter, Krzysztof Kozlowski, Kees Cook,
Jakub Kicinski, Samuel Ortiz, Michael Thalmeier, dzm91,
hust-os-kernel-patches, Yinhao Hu
pn533_recv_frame() checks dev->cmd and then dereferences it several
times to store the receive status and the response skb. In parallel the
command completion worker runs pn533_send_async_complete(), which frees
the same struct pn533_cmd and only clears dev->cmd after the free. With
fast receive/completion timing the receive path can therefore write to
an already freed command object.
Serialize all accesses to dev->cmd with a new cmd_state_lock spinlock:
pn533_recv_frame() now inspects and updates the current command while
holding the lock, the send paths publish and clear the pointer through
pn533_set_current_cmd(), and the completion worker detaches the command
under the lock before freeing it. Once the worker has detached the command,
a racing receive observes a NULL dev->cmd and no longer touches the freed
object.
BUG: KASAN: slab-use-after-free in pn533_recv_frame+0x54f/0x670
Write of size 4 at addr ffff888105566a14 by task kworker/1:3/126
CPU: 1 UID: 0 PID: 126 Comm: kworker/1:3 Not tainted 7.1.0+ #8 PREEMPT
Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.16.3-0-ga6ed6b701f0a-prebuilt.qemu.org 04/01/2014
Workqueue: events nfc_urelease_event_work
Call Trace:
<TASK>
dump_stack_lvl+0x8c/0xb0
print_report+0xd0/0x630
kasan_report+0xce/0x100
pn533_recv_frame+0x54f/0x670
pn533_stop_poll+0xdc/0x150
nfc_stop_poll+0xf2/0x1c0
nfc_urelease_event_work+0x194/0x270
process_one_work+0x701/0x1160
worker_thread+0x5e6/0xd50
kthread+0x373/0x4c0
ret_from_fork+0x2e9/0x750
ret_from_fork_asm+0x1a/0x30
</TASK>
Allocated by task 1744:
kasan_save_stack+0x33/0x60
kasan_save_track+0x14/0x30
__kasan_kmalloc+0x8f/0xa0
__kmalloc_cache_noprof+0x194/0x430
__pn533_send_async+0x5c/0x410
pn533_wq_rf+0xc6/0x160
process_one_work+0x701/0x1160
worker_thread+0x5e6/0xd50
kthread+0x373/0x4c0
ret_from_fork+0x2e9/0x750
ret_from_fork_asm+0x1a/0x30
Freed by task 1744:
kasan_save_stack+0x33/0x60
kasan_save_track+0x14/0x30
kasan_save_free_info+0x3b/0x60
__kasan_slab_free+0x43/0x70
kfree+0x17b/0x440
pn533_wq_cmd_complete+0x24d/0x450
process_one_work+0x701/0x1160
worker_thread+0x5e6/0xd50
kthread+0x373/0x4c0
ret_from_fork+0x2e9/0x750
ret_from_fork_asm+0x1a/0x30
Fixes: 9815c7cf22da ("NFC: pn533: Separate physical layer from the core implementation")
Signed-off-by: Yinhao Hu <dddddd@hust.edu.cn>
---
drivers/nfc/pn533/pn533.c | 47 ++++++++++++++++++++++++++++++++-------
drivers/nfc/pn533/pn533.h | 1 +
2 files changed, 40 insertions(+), 8 deletions(-)
diff --git a/drivers/nfc/pn533/pn533.c b/drivers/nfc/pn533/pn533.c
index d7bdbc82e2ba..921e93a5f16f 100644
--- a/drivers/nfc/pn533/pn533.c
+++ b/drivers/nfc/pn533/pn533.c
@@ -394,12 +394,32 @@ static void pn533_build_cmd_frame(struct pn533 *dev, u8 cmd_code,
ops->tx_frame_finish(skb->data);
}
+static void pn533_set_current_cmd(struct pn533 *dev, struct pn533_cmd *cmd)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->cmd_state_lock, flags);
+ dev->cmd = cmd;
+ spin_unlock_irqrestore(&dev->cmd_state_lock, flags);
+}
+
static int pn533_send_async_complete(struct pn533 *dev)
{
- struct pn533_cmd *cmd = dev->cmd;
+ struct pn533_cmd *cmd;
struct sk_buff *resp;
+ unsigned long flags;
int status, rc = 0;
+ /*
+ * Detach the current command before freeing it, so a concurrent
+ * pn533_recv_frame() either observes a valid command under the lock
+ * or a NULL dev->cmd and stops touching the freed object.
+ */
+ spin_lock_irqsave(&dev->cmd_state_lock, flags);
+ cmd = dev->cmd;
+ dev->cmd = NULL;
+ spin_unlock_irqrestore(&dev->cmd_state_lock, flags);
+
if (!cmd) {
dev_dbg(dev->dev, "%s: cmd not set\n", __func__);
goto done;
@@ -430,7 +450,6 @@ static int pn533_send_async_complete(struct pn533 *dev)
done:
kfree(cmd);
- dev->cmd = NULL;
return rc;
}
@@ -458,10 +477,10 @@ static int __pn533_send_async(struct pn533 *dev, u8 cmd_code,
mutex_lock(&dev->cmd_lock);
if (!dev->cmd_pending) {
- dev->cmd = cmd;
+ pn533_set_current_cmd(dev, cmd);
rc = dev->phy_ops->send_frame(dev, req);
if (rc) {
- dev->cmd = NULL;
+ pn533_set_current_cmd(dev, NULL);
goto error;
}
@@ -529,10 +548,10 @@ static int pn533_send_cmd_direct_async(struct pn533 *dev, u8 cmd_code,
pn533_build_cmd_frame(dev, cmd_code, req);
- dev->cmd = cmd;
+ pn533_set_current_cmd(dev, cmd);
rc = dev->phy_ops->send_frame(dev, req);
if (rc < 0) {
- dev->cmd = NULL;
+ pn533_set_current_cmd(dev, NULL);
kfree(cmd);
}
@@ -569,10 +588,10 @@ static void pn533_wq_cmd(struct work_struct *work)
mutex_unlock(&dev->cmd_lock);
- dev->cmd = cmd;
+ pn533_set_current_cmd(dev, cmd);
rc = dev->phy_ops->send_frame(dev, cmd->req);
if (rc < 0) {
- dev->cmd = NULL;
+ pn533_set_current_cmd(dev, NULL);
dev_kfree_skb(cmd->req);
kfree(cmd);
return;
@@ -2165,6 +2184,15 @@ static int pn533_data_exchange_complete(struct pn533 *dev, void *_arg,
*/
void pn533_recv_frame(struct pn533 *dev, struct sk_buff *skb, int status)
{
+ unsigned long flags;
+
+ /*
+ * Hold cmd_state_lock across the whole receive path so the current
+ * command cannot be freed by pn533_send_async_complete() between the
+ * dev->cmd check and the stores into it.
+ */
+ spin_lock_irqsave(&dev->cmd_state_lock, flags);
+
if (!dev->cmd)
goto sched_wq;
@@ -2182,6 +2210,7 @@ void pn533_recv_frame(struct pn533 *dev, struct sk_buff *skb, int status)
if (pn533_rx_frame_is_ack(skb->data)) {
dev_dbg(dev->dev, "%s: Received ACK frame\n", __func__);
+ spin_unlock_irqrestore(&dev->cmd_state_lock, flags);
dev_kfree_skb(skb);
return;
}
@@ -2200,6 +2229,7 @@ void pn533_recv_frame(struct pn533 *dev, struct sk_buff *skb, int status)
dev->cmd->resp = skb;
sched_wq:
+ spin_unlock_irqrestore(&dev->cmd_state_lock, flags);
queue_work(dev->wq, &dev->cmd_complete_work);
}
EXPORT_SYMBOL(pn533_recv_frame);
@@ -2760,6 +2790,7 @@ struct pn533 *pn53x_common_init(u32 device_type,
priv->device_type = device_type;
mutex_init(&priv->cmd_lock);
+ spin_lock_init(&priv->cmd_state_lock);
INIT_WORK(&priv->cmd_work, pn533_wq_cmd);
INIT_WORK(&priv->cmd_complete_work, pn533_wq_cmd_complete);
diff --git a/drivers/nfc/pn533/pn533.h b/drivers/nfc/pn533/pn533.h
index 09e35b8693f5..8b009b2318d0 100644
--- a/drivers/nfc/pn533/pn533.h
+++ b/drivers/nfc/pn533/pn533.h
@@ -153,6 +153,7 @@ struct pn533 {
struct pn533_cmd *cmd;
u8 cmd_pending;
struct mutex cmd_lock; /* protects cmd queue */
+ spinlock_t cmd_state_lock; /* protects dev->cmd lifetime */
void *cmd_complete_mi_arg;
void *cmd_complete_dep_arg;
--
2.43.0
^ permalink raw reply related
* Re: [PATCH v6 07/10] rust: configfs: use `LocalModule` for `THIS_MODULE`
From: Alvin Sun @ 2026-06-27 13:10 UTC (permalink / raw)
To: Gary Guo, Miguel Ojeda, Boqun Feng, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Luis Chamberlain, Petr Pavlu, Daniel Gomez,
Sami Tolvanen, Aaron Tomlin, Greg Kroah-Hartman,
Rafael J. Wysocki, David Airlie, Simona Vetter, Daniel Almeida,
Arnd Bergmann, Brendan Higgins, David Gow, Rae Moar, Breno Leitao,
Jens Axboe, Dave Ertman, Leon Romanovsky, Igor Korotin,
FUJITA Tomonori, Bjorn Helgaas, Krzysztof Wilczyński,
Arve Hjønnevåg, Todd Kjos, Christian Brauner,
Carlos Llamas
Cc: rust-for-linux, linux-modules, driver-core, dri-devel, nova-gpu,
linux-kselftest, kunit-dev, linux-block, linux-kernel, netdev,
linux-pci
In-Reply-To: <DJJ25JJ3TDO9.1UFOP60U0IPX5@garyguo.net>
On 6/26/26 22:41, Gary Guo wrote:
> On Fri Jun 26, 2026 at 3:35 AM BST, Alvin Sun wrote:
>> On 6/25/26 22:40, Gary Guo wrote:
>>> On Wed Jun 24, 2026 at 4:00 PM BST, Alvin Sun wrote:
>>>> Replace the `THIS_MODULE` static reference in the `configfs_attrs!`
>>>> macro with `this_module::<LocalModule>()`, and update
>>>> rnull to import `LocalModule` instead of `THIS_MODULE`, consistent
>>>> with the move of `THIS_MODULE` into the `ModuleMetadata` trait.
>>>>
>>>> Assisted-by: opencode:glm-5.2
>>>> Reviewed-by: Andreas Hindborg <a.hindborg@kernel.org>
>>>> Acked-by: Danilo Krummrich <dakr@kernel.org>
>>>> Signed-off-by: Alvin Sun <alvin.sun@linux.dev>
>>>> ---
>>>> drivers/block/rnull/configfs.rs | 6 ++----
>>>> rust/kernel/configfs.rs | 8 +++++---
>>>> 2 files changed, 7 insertions(+), 7 deletions(-)
>>>>
>>>> diff --git a/drivers/block/rnull/configfs.rs b/drivers/block/rnull/configfs.rs
>>>> index c10a55fc58948..b2547ad1e5ddd 100644
>>>> --- a/drivers/block/rnull/configfs.rs
>>>> +++ b/drivers/block/rnull/configfs.rs
>>>> @@ -1,9 +1,7 @@
>>>> // SPDX-License-Identifier: GPL-2.0
>>>>
>>>> -use super::{
>>>> - NullBlkDevice,
>>>> - THIS_MODULE, //
>>>> -};
>>>> +use super::NullBlkDevice;
>>>> +use crate::LocalModule;
>>>> use kernel::{
>>>> block::mq::gen_disk::{
>>>> GenDisk,
>>>> diff --git a/rust/kernel/configfs.rs b/rust/kernel/configfs.rs
>>>> index 2339c6467325d..c31d7882e216d 100644
>>>> --- a/rust/kernel/configfs.rs
>>>> +++ b/rust/kernel/configfs.rs
>>>> @@ -875,7 +875,7 @@ fn as_ptr(&self) -> *const bindings::config_item_type {
>>>> /// configfs::Subsystem<Configuration>,
>>>> /// Configuration
>>>> /// >::new_with_child_ctor::<N,Child>(
>>>> -/// &THIS_MODULE,
>>>> +/// ::kernel::module::this_module::<crate::LocalModule>(),
>>>> /// &CONFIGURATION_ATTRS
>>>> /// );
>>>> ///
>>>> @@ -1021,7 +1021,8 @@ macro_rules! configfs_attrs {
>>>>
>>>> static [< $data:upper _TPE >] : $crate::configfs::ItemType<$container, $data> =
>>>> $crate::configfs::ItemType::<$container, $data>::new::<N>(
>>>> - &THIS_MODULE, &[<$ data:upper _ATTRS >]
>>>> + $crate::module::this_module::<LocalModule>(),
>>> ^ You only changed one single place. This is still plain `LocalModule`.
>> Initially I wrote it as `crate::LocalModule`, but clippy warned about it. So
>> instead of putting the crate path in the macro body, I added `use
>> crate::LocalModule` in the calling file.
>>
>> ```
>> warning: `crate` references the macro call's crate
>> --> rust/kernel/configfs.rs:1024:59
>> |
>> 1024 | ... $crate::module::this_module::<crate::LocalModule>(),
>> | ^^^^^ help:
>> to reference the macro definition's crate, use: `$crate`
>> |
>> = help: for further information visit
>> https://rust-lang.github.io/rust-clippy/rust-1.94.0/index.html#crate_in_macro_def
>> = note: `-W clippy::crate-in-macro-def` implied by `-W clippy::all`
>> = help: to override `-W clippy::all` add
>> `#[allow(clippy::crate_in_macro_def)]`
>>
>> warning: 1 warning emitted
>> ```
> Clippy has a point about `crate::` being usually wrong in macros, but it is what
> we actually want here, so obviously you should allow the warning.
>
> It is the exact same case in `vtable` macro, just that Clippy is unable to check
> proc macros!
Thanks for the detailed explanation.
I specifically searched for issues related to `crate_in_macro_def`, and
this information is very useful to me.
Best regards,
Alvin
>
> Best,
> Gary
>
>> Alternatively, `#[allow(clippy::crate_in_macro_def)]` could be added on
>> the macro
>> definition. Would you suggest that approach?
^ permalink raw reply
* Re: [RFC PATCH net-next v8 03/12] net: phylink: add phylink_release_pcs() to externally release a PCS
From: Christian Marangi @ 2026-06-27 12:33 UTC (permalink / raw)
To: Maxime Chevallier
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Simon Horman, Jonathan Corbet, Shuah Khan, Lorenzo Bianconi,
Heiner Kallweit, Russell King, Saravana Kannan, Philipp Zabel,
Nathan Chancellor, Nick Desaulniers, Bill Wendling, Justin Stitt,
netdev, devicetree, linux-kernel, linux-doc, linux-arm-kernel,
linux-mediatek, llvm
In-Reply-To: <a271385e-302d-45c7-a1df-aebd380b427b@bootlin.com>
On Thu, Jun 25, 2026 at 04:13:14PM +0200, Maxime Chevallier wrote:
> Hello Christian,
>
> On 6/18/26 14:57, Christian Marangi wrote:
> > Add phylink_release_pcs() to externally release a PCS from a phylink
> > instance. This can be used to handle case when a single PCS needs to be
> > removed and the phylink instance needs to be refreshed.
> >
> > On calling phylink_release_pcs(), the PCS will be removed from the
> > phylink internal PCS list and the phylink supported_interfaces value is
> > reparsed with the remaining PCS interfaces.
> >
> > Also a phylink resolve is triggered to handle the PCS removal.
> >
> > The flag force_major_config is set to make phylink resolve reconfigure
> > the interface (even if it didn't change).
> > This is needed to handle the special case when the current PCS used
> > by phylink is removed and a major_config is needed to propagae the
> > configuration change. With this option enabled we also force mac_config
> > even if the PHY link is not up for the in-band case.
> >
> > Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
> > ---
> > drivers/net/phy/phylink.c | 56 +++++++++++++++++++++++++++++++++++++++
> > include/linux/phylink.h | 2 ++
> > 2 files changed, 58 insertions(+)
> >
> > diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c
> > index c38bcd43b8c8..064d6f5a06da 100644
> > --- a/drivers/net/phy/phylink.c
> > +++ b/drivers/net/phy/phylink.c
> > @@ -158,6 +158,8 @@ static const phy_interface_t phylink_sfp_interface_preference[] = {
> > static DECLARE_PHY_INTERFACE_MASK(phylink_sfp_interfaces);
> >
> > static void phylink_run_resolve(struct phylink *pl);
> > +static void phylink_link_down(struct phylink *pl);
> > +static void phylink_pcs_disable(struct phylink_pcs *pcs);
> >
> > /**
> > * phylink_set_port_modes() - set the port type modes in the ethtool mask
> > @@ -918,6 +920,60 @@ static void phylink_resolve_an_pause(struct phylink_link_state *state)
> > }
> > }
> >
> > +/**
> > + * phylink_release_pcs - Removes a PCS from the phylink PCS available list
> > + * @pcs: a pointer to the phylink_pcs struct to be released
> > + *
> > + * This function release a PCS from the phylink PCS available list if
> > + * actually in use. It also refreshes the supported interfaces of the
> > + * phylink instance by copying the supported interfaces from the phylink
> > + * conf and merging the supported interfaces of the remaining available PCS
> > + * in the list and trigger a resolve.
> > + */
> > +void phylink_release_pcs(struct phylink_pcs *pcs)
> > +{
> > + struct phylink *pl;
> > +
> > + ASSERT_RTNL();
> > +
> > + pl = pcs->phylink;
> > + if (!pl)
> > + return;
> > +
> > + mutex_lock(&pl->state_mutex);
> > +
> > + list_del(&pcs->list);
> > + pcs->phylink = NULL;
> > +
> > + /*
> > + * Check if we are removing the PCS currently
> > + * in use by phylink. If this is the case, tear down
> > + * the link, force phylink resolve to reconfigure the
> > + * interface mode, disable the current PCS and set the
> > + * phylink PCS to NULL.
> > + */
> > + if (pl->pcs == pcs) {
> > + phylink_link_down(pl);
> > + phylink_pcs_disable(pl->pcs);
> > +
> > + pl->force_major_config = true;
> > + pl->pcs = NULL;
> > + }
> > +
> > + mutex_unlock(&pl->state_mutex);
> > +
> > + /* Refresh supported interfaces */
> > + phy_interface_copy(pl->supported_interfaces,
> > + pl->config->supported_interfaces);
> > + list_for_each_entry(pcs, &pl->pcs_list, list)
> > + phy_interface_or(pl->supported_interfaces,
> > + pl->supported_interfaces,
> > + pcs->supported_interfaces);
>
> I've given more thought to that 'supported_interfaces' thing. This
> patchset redefines the meaning of
>
> pl->config->supported_interfaces
>
> Currently, it's filled by the MAC driver and means "Every interface
> we can support, including the ones provided by PCSs that we can use
> with this MAC".
>
> It now becomes "Every interface we support without needing a PCS", at
> least the way I understand that.
>
Wait but with the current code using the OR logic, it still follows
"Every interface we can support...". The modes that needs a PCS are
specificed with the pcs_interfaces mask in phylink_config.
The late add and release operates on the phylink supported_interfaces ONLY
when the MAC didn't specify support for it (by removing it as only the PCS
will declare support for it)
The confusion is present because everything is validated later on
major_config so those supported_interfaces are just an HINT that are later
verified with get_caps and with the pcs_validate OPs.
Adding the supported_interfaces to phylink is really to keep an original
reference of the value. This is to address a pattern I have notice where
the MAC driver always OR the interfaces with the one supported by the PCS.
(I remember it was pointed out by Russell)
But I'm more than open to discussion as this is something marginal to the
whole implementation, I'm also questioning if this OR is actually useful to
anything on the nth tought on this.
One thing that I notice is that parsing this early with AND might be
problematic at phylink_create, but I still have to evaluate that.
My take is that would be good to have some review also on the other logic
as I think I reached a point where Sashiko starts to comments on more or
less unreal problem.
> It's not an error in your code, but I think this is worth documenting
> somewhere as this changes one the things that's already fairly
> error-prone in new drivers.
>
> I don't know to what extent people use that, be we have a porting guide
> that explains how to use phylink in a MAC driver, maybe an update in there
> would be nice as well :
>
> https://docs.kernel.org/networking/sfp-phylink.html#rough-guide-to-converting-a-network-driver-to-sfp-phylink
>
> Maxime
>
>
--
Ansuel
^ permalink raw reply
* [PATCH v4] net: stmmac: fix fatal bus error on resume by reinitializing RX buffers
From: Ding Hui @ 2026-06-27 12:25 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Maxime Coquelin, Alexandre Torgue,
Russell King (Oracle), Maxime Chevallier, Ding Hui,
open list:STMMAC ETHERNET DRIVER,
moderated list:ARM/STM32 ARCHITECTURE,
moderated list:ARM/STM32 ARCHITECTURE, open list
Cc: xiasanbo, yangchen11, liuxuanjun
From: Ding Hui <dinghui@lixiang.com>
On suspend, stmmac_suspend() calls stmmac_disable_all_queues() which
stops the RX NAPI, but the RX DMA engine may still be running for a
short window before stmmac_stop_all_dma() takes effect. During that
window the hardware can write incoming frames into the buffers pointed
to by the RX descriptors and write back the descriptors (clearing the
OWN bit and overwriting RDES0/1/2 with status/timestamp data). Because
NAPI is already disabled, the driver never refills these descriptors,
so the RX ring is left in a "consumed but not refilled" state with
stale content in the descriptor buffer-address fields.
On resume, stmmac_clear_descriptors() only re-arms the OWN bit and
does not repopulate the RX buffer address fields. When the DMA is
restarted it dereferences these stale addresses and triggers a fatal
bus error (not kernel panic, just a Fatal Bus Error interrupt and
RX DMA engine halts).
Fix this by introducing stmmac_reinit_rx_descriptors(), called from
stmmac_resume() immediately after stmmac_clear_descriptors(). The
helper iterates every RX descriptor slot and re-programs its buffer
address fields:
- For normal (page_pool) queues: restore RDES0/1 from buf->addr and
RDES2 from buf->sec_addr. The DMA mapping has remained valid across
suspend/resume because no pages were freed. Slots left NULL by a
prior GFP_ATOMIC failure in stmmac_rx_refill() before suspend
are re-allocated here with GFP_KERNEL;
-ENOMEM is returned and resume is aborted if allocation fails.
The slots with null buffer are unacceptable, because they will
cause a DMA suspend dead lock problem by the condition of
Current Descriptor Pointer == Descriptor Tail Pointer.
- For AF_XDP zero-copy queues: restore the DMA address from
xsk_buff_xdp_get_dma(buf->xdp). Slots with no xdp buffer
(e.g. TX-only socket, empty fill ring) attempt xsk_buff_alloc()
first; on failure the descriptor is zeroed so the DMA engine skips
the slot safely via an RBU event.
- For chain mode: call stmmac_mode_init() to rebuild the des3 next-
descriptor pointer chain, which hardware may have overwritten with
a PTP timestamp value (as noted in chain_mode.c:refill_desc3()).
After reprogramming all address fields, a final pass restores OWN=1
on every valid slot. This is necessary because set_sec_addr and
chain-mode init unconditionally overwrite des3 (clearing the OWN bit
set by stmmac_clear_descriptors()), and must run after all address
writes are complete.
Also fix stmmac_init_rx_buffers() to actually use its gfp_t flags
parameter instead of the hardcoded GFP_ATOMIC | __GFP_NOWARN.
Signed-off-by: Ding Hui <dinghui@lixiang.com>
---
Changes in v4:
- Just add description for return value of 'stmmac_reinit_rx_descriptors'.
- Link to v3:
https://lore.kernel.org/netdev/20260604144557.3175399-1-dinghui1111@163.com/
Changes in v3:
- Re-allocate page_pool NULL slots (from prior GFP_ATOMIC failures)
with GFP_KERNEL in stmmac_reinit_rx_descriptors(); return -ENOMEM and
abort resume.
- For XSK NULL slots, attempt xsk_buff_alloc() first; fall back to
stmmac_clear_desc() only when allocation fails.
- Add a re-arm loop at the end of stmmac_reinit_rx_descriptors() to
restore OWN=1 on all valid slots, since set_sec_addr and
chain-mode init both write des3 unconditionally.
- stmmac_reinit_rx_descriptors() now returns int; stmmac_resume()
checks the return value and propagates -ENOMEM with mutex/rtnl cleanup.
- Fix stmmac_init_rx_buffers() to use its flags parameter instead of
hardcoded GFP_ATOMIC | __GFP_NOWARN.
(884d2b845477 ("net: stmmac: Add GFP_DMA32 for rx buffers if no 64
capability"))
- Run stmmac_reinit_rx_descriptors() after stmmac_clear_descriptors()
so that stmmac_clear_desc() on XSK NULL slots overrides the OWN
bit set by stmmac_clear_descriptors().
- Update commit message.
- Link to v2:
https://lore.kernel.org/netdev/20260526022620.501229-1-dinghui1111@163.com/
Changes in v2:
- Introducing stmmac_reinit_rx_descriptors() to reinitializing rx
buffers without any allocation.
- Modify commit log.
- Link to v1:
https://lore.kernel.org/netdev/20260515053856.2310369-1-dinghui1111@163.com/
---
.../net/ethernet/stmicro/stmmac/stmmac_main.c | 164 +++++++++++++++++-
1 file changed, 163 insertions(+), 1 deletion(-)
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index 3591755ea30b..c82f3d5dbd43 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -1660,7 +1660,7 @@ static int stmmac_init_rx_buffers(struct stmmac_priv *priv,
{
struct stmmac_rx_queue *rx_q = &dma_conf->rx_queue[queue];
struct stmmac_rx_buffer *buf = &rx_q->buf_pool[i];
- gfp_t gfp = (GFP_ATOMIC | __GFP_NOWARN);
+ gfp_t gfp = flags;
if (priv->dma_cap.host_dma_width <= 32)
gfp |= GFP_DMA32;
@@ -1693,6 +1693,148 @@ static int stmmac_init_rx_buffers(struct stmmac_priv *priv,
return 0;
}
+/**
+ * stmmac_reinit_rx_descriptors - re-program RX descriptor buffer addresses
+ * after stmmac_clear_descriptors()
+ * @priv: driver private structure
+ * @dma_conf: structure holding the dma data
+ * @queue: RX queue index
+ *
+ * Description: Called in the resume path after stmmac_clear_descriptors()
+ * has re-armed the OWN bit on every descriptor. Walk buf_pool[] and
+ * re-program the buffer-address fields of every RX descriptor from the
+ * buffers that are already attached to the queue. Slots whose page was
+ * never allocated (GFP_ATOMIC failure before suspend) are re-allocated
+ * here with GFP_KERNEL; the resume path is in process context.
+ *
+ * Between suspend and resume the hardware may have written back status/
+ * length information into the descriptor address fields (RDESx are reused
+ * for status on completion for GMAC4/XGMAC), so the address fields must be
+ * repopulated before the DMA is restarted.
+ *
+ * For XSK slots that have no xdp buffer at suspend time (TX-only socket,
+ * empty fill ring for Rx), xsk_buff_alloc() is attempted but does not
+ * return an error on failure because we can't identify a real TX-only
+ * socket from an alloc error (same as stmmac_alloc_rx_buffers_zc() in
+ * __init_dma_rx_desc_rings); on failure the descriptor is zeroed so the DMA
+ * engine skips the slot safely.
+ *
+ * To avoid the DMA stall after resume in non-XSK mode, this function
+ * re-allocates pages for NULL slots using GFP_KERNEL (the resume path runs
+ * in process context). If allocation fails, -%ENOMEM is returned immediately
+ * and the resume is aborted; the caller should report the error.
+ *
+ * This helper must be called after stmmac_clear_descriptors() and before
+ * stmmac_hw_setup() in stmmac_resume() because we need to wipe the OWN bit
+ * set in stmmac_clear_descriptors() for NULL slots in XSK mode.
+ *
+ * Returns: 0 on success, or a negative errno on allocation failure in
+ * non-XSK mode (e.g. -%ENOMEM).
+ */
+static int stmmac_reinit_rx_descriptors(struct stmmac_priv *priv,
+ struct stmmac_dma_conf *dma_conf,
+ u32 queue)
+{
+ struct stmmac_rx_queue *rx_q = &dma_conf->rx_queue[queue];
+ struct stmmac_rx_buffer *buf;
+ struct dma_desc *p;
+ int i;
+
+ if (rx_q->xsk_pool) {
+ for (i = 0; i < dma_conf->dma_rx_size; i++) {
+ buf = &rx_q->buf_pool[i];
+ p = stmmac_get_rx_desc(priv, rx_q, i);
+
+ /* The XSK pool may not be fully populated (e.g.
+ * xdpsock TX-only, empty fill ring). Try to refill
+ * from the pool; on failure zero the descriptor so the
+ * DMA engine skips this slot safely.
+ */
+ if (!buf->xdp) {
+ buf->xdp = xsk_buff_alloc(rx_q->xsk_pool);
+ if (!buf->xdp) {
+ stmmac_clear_desc(priv, p);
+ continue;
+ }
+ }
+
+ stmmac_set_desc_addr(priv, p,
+ xsk_buff_xdp_get_dma(buf->xdp));
+ stmmac_set_desc_sec_addr(priv, p, 0, false);
+ }
+ } else {
+ for (i = 0; i < dma_conf->dma_rx_size; i++) {
+ buf = &rx_q->buf_pool[i];
+ p = stmmac_get_rx_desc(priv, rx_q, i);
+
+ /* buf->page can be NULL when stmmac_rx_refill() hit a
+ * GFP_ATOMIC failure before suspend and left the slot
+ * without a buffer. The resume path runs in process
+ * context, so re-allocate with GFP_KERNEL. Allocation
+ * failure aborts the resume.
+ */
+ if (!buf->page) {
+ int err;
+
+ err = stmmac_init_rx_buffers(priv, dma_conf, p,
+ i, GFP_KERNEL,
+ queue);
+ if (err)
+ return err;
+ /* stmmac_init_rx_buffers() already programmed
+ * the descriptor; skip the reprogramming below.
+ */
+ continue;
+ }
+
+ stmmac_set_desc_addr(priv, p, buf->addr);
+ stmmac_set_desc_sec_addr(priv, p, buf->sec_addr,
+ priv->sph_active &&
+ buf->sec_page);
+
+ if (dma_conf->dma_buf_sz == BUF_SIZE_16KiB)
+ stmmac_init_desc3(priv, p);
+ }
+ }
+
+ /* Chain mode: re-link descriptor 'next' pointers. This is
+ * allocation-free; it just rewrites the per-descriptor next
+ * field which may have been clobbered by HW writeback.
+ */
+ if (priv->descriptor_mode == STMMAC_CHAIN_MODE) {
+ void *des = priv->extend_desc ? (void *)rx_q->dma_erx
+ : (void *)rx_q->dma_rx;
+
+ stmmac_mode_init(priv, des, rx_q->dma_rx_phy,
+ dma_conf->dma_rx_size, priv->extend_desc);
+ }
+
+ /* Re-arm OWN=1 on every valid slot.
+ *
+ * Two address-programming helpers write des3 unconditionally and
+ * therefore clear the OWN bit that stmmac_clear_descriptors() set:
+ *
+ * - stmmac_desc_ops.set_sec_addr (called by stmmac_set_desc_sec_addr()):
+ * writes des3 with upper_32_bits(addr).
+ *
+ * - stmmac_mode_ops.init() (called by stmmac_mode_init() above): writes
+ * des3 with the next-descriptor physical address.
+ *
+ * A single pass over valid slots restores OWN=1 after all descriptor
+ * fields have been written. NULL slots are left with OWN=0 for XSK mode
+ * so the Rx DMA engine stalls safely.
+ */
+ for (i = 0; i < dma_conf->dma_rx_size; i++) {
+ buf = &rx_q->buf_pool[i];
+ p = stmmac_get_rx_desc(priv, rx_q, i);
+
+ if (rx_q->xsk_pool ? !!buf->xdp : !!buf->page)
+ stmmac_set_rx_owner(priv, p, false);
+ }
+
+ return 0;
+}
+
/**
* stmmac_free_rx_buffer - free RX dma buffers
* @priv: private structure
@@ -8272,6 +8414,7 @@ int stmmac_resume(struct device *dev)
{
struct net_device *ndev = dev_get_drvdata(dev);
struct stmmac_priv *priv = netdev_priv(ndev);
+ u32 queue;
int ret;
if (priv->plat->resume) {
@@ -8321,6 +8464,25 @@ int stmmac_resume(struct device *dev)
stmmac_free_tx_skbufs(priv);
stmmac_clear_descriptors(priv, &priv->dma_conf);
+ /* Re-program the RX descriptor buffer-address fields. Slots that
+ * had no page at suspend time (GFP_ATOMIC failure) are re-allocated
+ * here with GFP_KERNEL; XSK slots without an xdp buffer are refilled
+ * from the pool if possible. Any unrecoverable allocation failure
+ * is reported so the resume can be aborted cleanly.
+ */
+ for (queue = 0; queue < priv->plat->rx_queues_to_use; queue++) {
+ ret = stmmac_reinit_rx_descriptors(priv, &priv->dma_conf,
+ queue);
+ if (ret) {
+ netdev_err(priv->dev,
+ "%s: rx desc reinit failed on queue %u\n",
+ __func__, queue);
+ mutex_unlock(&priv->lock);
+ rtnl_unlock();
+ return ret;
+ }
+ }
+
ret = stmmac_hw_setup(ndev);
if (ret < 0) {
netdev_err(priv->dev, "%s: Hw setup failed\n", __func__);
--
2.34.1
^ permalink raw reply related
* Re: [PATCH net v4 2/2] net: phy: mdio-i2c: defer RollBall bridge probe to PHY discovery
From: Maxime Chevallier @ 2026-06-27 12:03 UTC (permalink / raw)
To: Petr Wozniak, olek2, linux, andrew, hkallweit1
Cc: kuba, davem, edumazet, pabeni, netdev, linux-kernel, linux-phy,
bjorn, kabel
In-Reply-To: <20260626163519.10678-1-petr.wozniak@gmail.com>
Hi Petr
On 6/26/26 18:35, Petr Wozniak wrote:
> Maxime Chevallier wrote:
>> I finally got time to test this with a RollBall module, and I
>> confirm what Aleksander says, the RollBall module's PHY doesn't
>> get detected even with this patch. It does work on v7.0 though,
>> so before the bridge probing was introduced.
>
> Thanks a lot for taking the time to test, Maxime - and Aleksander
> for the original report.
>
> That settles it: the deferred-probe approach in patch 2/2 doesn't
> actually restore genuine RollBall PHY detection, and as you both
> confirm it worked before 8fe125892f40 introduced the bridge probing.
> Sashiko's static review flagged the same thing (the probe bypasses the
> PHY discovery retry loop for slow-initializing modules), so the static
> analysis and the two hardware reports all point at the same flaw.
>
> I only have RTL8261BE-based copper modules here, not a genuine RollBall
> one, so I can't develop and verify a proper slow-init timing fix
> (module_t_wait / a retry that waits for the bridge) without the
> hardware to test against.
>
> Given that, my suggestion:
>
> - Please drop patch 2/2 from the series.
>
> - Since 8fe125892f40 regressed genuine RollBall detection and the
> deferred probe doesn't restore it, I think the cleanest fix is to
> revert 8fe125892f40. I'm happy to send that revert if you'd prefer.
> The 5-minute RTL8261BE probe loop it was addressing is handled in our
> downstream tree, so reverting it upstream is fine on our side.
>
> - Patch 1/2 (the mii_bus leak fix) is independent of all this and
> already has Reviewed-by from Maxime and Larysa - it would be good to
> take that one regardless. I can resend it standalone if that's easier.
>
> A proper fix covering slow-firmware modules really needs a genuine
> RollBall module to validate, so it's better owned by someone who has
> that hardware - happy to help review.
>
I only have a single rollball module here, and none with the missing
bridge, so I can't test the full thing either.
In that case, I agree with reverting 8fe125892f40 as this is breaking
all rollball modules right now.
Can you send the revert ?
Maxime
^ permalink raw reply
* Re: [PATCH v2] fix: net: renesas: rswitch_mii_register: fix double of_node_put after of_mdiobus_register
From: WenTao Liang @ 2026-06-27 11:49 UTC (permalink / raw)
To: Andrew Lunn
Cc: netdev, Yoshihiro Shimoda, David S . Miller, Jakub Kicinski,
Paolo Abeni, stable, linux-kernel
In-Reply-To: <fdb2120d-5d0e-445f-97a5-ef2307ebd4d1@lunn.ch>
> 2026年6月27日 00:10,Andrew Lunn <andrew@lunn.ch> 写道:
>
> On Fri, Jun 26, 2026 at 11:25:50PM +0800, WenTao Liang wrote:
>> After of_mdiobus_register succeeds, the mdio_np reference ownership is
>> transferred to the mii_bus device (released via fwnode_handle_put during
>> mdiobus_release). The success path calls of_node_put(mdio_np) which,
>> combined with the automatic release via bus teardown, results in a double
>> put and refcount underflow.
>>
>> Move of_node_put so it is only called in the error path where
>> of_mdiobus_register failed. On success, the bus driver manages the
>> reference lifecycle.
>
> Please stop with these patches.
>
> First please read:
>
> https://www.kernel.org/doc/html/latest/process/maintainer-netdev.html
>
> and
>
> https://docs.kernel.org/process/submitting-patches.html
>
> You are getting a lot of things wrong.
>
> * don’t repost your patches within one 24h period
> * Don't thread new versions of a patch to the old one
> * Include version history, how is v2 different to v1
> * When you see your own patch is broken, reply with NACK, and explain
> what is wrong with it.
>
> Until you learn how to correctly submit patches, please only submit
> them one at a time, get it accepted, and move onto the next. Otherwise
> you are wasting peoples time, and getting yourself a bad reputation.
>
> Andrew
Hi Andrew,
Thank you for your careful review and for pointing out this issue. You are
absolutely correct — my previous analysis was flawed, and I appreciate you
taking the time to clarify.
Let me address your concerns:
1. On the double of_node_put():
You are right. In the current error path, if mdiobus_register() succeeds
and later macb_mii_probe() fails, the code jumps to err_out_unregister_bus,
which calls mdiobus_unregister() and then mdiobus_free().
- mdiobus_free() indeed releases the reference to the device node (via
put_device()).
- Adding an explicit of_node_put() in that same path would indeed result
in a double decrement, which is incorrect and could lead to use-after-free
or refcount underflow.
2. On the risk of untested patches:
You are right to be cautious. I do not have access to the specific hardware
to test this change, and I should have been more careful in reasoning about
the reference counting semantics. I will refrain from submitting further
patches in this area without proper testing or more thorough review of the
existing code paths.
3. Proposed way forward:
I will withdraw this patch for now. If I find a way to test it or gain more
confidence through static analysis and documentation, I will resubmit with
a clearer explanation and, ideally, test results.
Again, thank you for your diligence. I apologize for the noise and any extra
work this may have caused.
Please let me know if there is anything else I can clarify or help with.
Best regards,
WenTao Liang
^ permalink raw reply
* [PATCH] netdevsim: remove debugfs files before freeing net_device
From: syzbot @ 2026-06-27 11:20 UTC (permalink / raw)
To: syzkaller-bugs, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, netdev, Paolo Abeni
Cc: linux-kernel, syzbot
From: Aleksandr Nogikh <nogikh@google.com>
A KASAN slab-use-after-free was detected in debugfs_u32_get() when reading
a debugfs file associated with a netdevsim port.
BUG: KASAN: slab-use-after-free in debugfs_u32_get+0x6c/0x70
Read of size 4 at addr ffff88810cd6db00
Call Trace:
debugfs_u32_get+0x6c/0x70
simple_attr_read+0x227/0x490
debugfs_attr_read+0x76/0x130
vfs_read+0x213/0xa80
The root cause is that the net_device (and its embedded netdevsim private
data) is freed before its associated debugfs files are removed. When a user
reads a debugfs file like rx_max_pending, debugfs_u32_get() dereferences a
pointer to the freed memory. This happens because nsim_destroy() calls
free_netdev() but does not remove the debugfs files created by
nsim_ethtool_init() or nsim_bpf_init(). These files are only removed later
when nsim_dev_port_debugfs_exit() is called. A similar issue exists in the
error path of nsim_create().
To fix this, swap the teardown order in __nsim_dev_port_del() and the
__nsim_dev_port_add() error path so that nsim_dev_port_debugfs_exit() is
called before nsim_destroy(). This ensures the debugfs directory is
recursively removed before the net_device is freed. Additionally, remove
explicit debugfs_remove() calls from various subsystem teardown functions
(like nsim_psp_uninit, nsim_ipsec_teardown, etc.) since the parent
directory is now removed earlier, which would otherwise lead to a
use-after-free of the dentry itself. Finally, ensure the debugfs directory
is removed before free_netdev() is called in the nsim_create() error path.
Fixes: e05b2d141fef ("netdevsim: move netdev creation/destruction to dev probe")
Assisted-by: Gemini:gemini-3.1-pro-preview Gemini:gemini-3-flash-preview syzbot
Reported-by: syzbot+6c25f4750230faf70be9@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=6c25f4750230faf70be9
Link: https://syzkaller.appspot.com/ai_job?id=f0f1a72a-a527-478e-a31c-7b83cdfee8d1
Signed-off-by: Aleksandr Nogikh <nogikh@google.com>
---
diff --git a/drivers/net/netdevsim/dev.c b/drivers/net/netdevsim/dev.c
index f00fc2f9e..4c2c9aa1e 100644
--- a/drivers/net/netdevsim/dev.c
+++ b/drivers/net/netdevsim/dev.c
@@ -1516,17 +1516,17 @@ static int __nsim_dev_port_add(struct nsim_dev *nsim_dev, enum nsim_dev_port_typ
err = devl_rate_leaf_create(&nsim_dev_port->devlink_port,
nsim_dev_port, NULL);
if (err)
- goto err_nsim_destroy;
+ goto err_port_debugfs_exit;
}
list_add(&nsim_dev_port->list, &nsim_dev->port_list);
return 0;
-err_nsim_destroy:
- nsim_destroy(nsim_dev_port->ns);
err_port_debugfs_exit:
nsim_dev_port_debugfs_exit(nsim_dev_port);
+ if (!IS_ERR_OR_NULL(nsim_dev_port->ns))
+ nsim_destroy(nsim_dev_port->ns);
err_port_resource_unregister:
if (nsim_dev_port_is_pf(nsim_dev_port))
devl_port_resources_unregister(devlink_port);
@@ -1544,8 +1544,8 @@ static void __nsim_dev_port_del(struct nsim_dev_port *nsim_dev_port)
list_del(&nsim_dev_port->list);
if (nsim_dev_port_is_vf(nsim_dev_port))
devl_rate_leaf_destroy(&nsim_dev_port->devlink_port);
- nsim_destroy(nsim_dev_port->ns);
nsim_dev_port_debugfs_exit(nsim_dev_port);
+ nsim_destroy(nsim_dev_port->ns);
if (nsim_dev_port_is_pf(nsim_dev_port))
devl_port_resources_unregister(devlink_port);
devl_port_unregister(devlink_port);
diff --git a/drivers/net/netdevsim/ipsec.c b/drivers/net/netdevsim/ipsec.c
index 36a1be492..0bc9f42a5 100644
--- a/drivers/net/netdevsim/ipsec.c
+++ b/drivers/net/netdevsim/ipsec.c
@@ -292,5 +292,4 @@ void nsim_ipsec_teardown(struct netdevsim *ns)
if (ipsec->count)
netdev_err(ns->netdev, "tearing down IPsec offload with %d SAs left\n",
ipsec->count);
- debugfs_remove_recursive(ipsec->pfile);
}
diff --git a/drivers/net/netdevsim/netdev.c b/drivers/net/netdevsim/netdev.c
index a75076891..240107369 100644
--- a/drivers/net/netdevsim/netdev.c
+++ b/drivers/net/netdevsim/netdev.c
@@ -1154,16 +1154,17 @@ struct netdevsim *nsim_create(struct nsim_dev *nsim_dev,
if (err)
goto err_free_netdev;
- ns->pp_dfs = debugfs_create_file("pp_hold", 0600, nsim_dev_port->ddir,
- ns, &nsim_pp_hold_fops);
- ns->qr_dfs = debugfs_create_file("queue_reset", 0200,
- nsim_dev_port->ddir, ns,
- &nsim_qreset_fops);
- ns->vlan_dfs = debugfs_create_file("vlan", 0400, nsim_dev_port->ddir,
- ns, &nsim_vlan_fops);
+ debugfs_create_file("pp_hold", 0600, nsim_dev_port->ddir, ns,
+ &nsim_pp_hold_fops);
+ debugfs_create_file("queue_reset", 0200, nsim_dev_port->ddir, ns,
+ &nsim_qreset_fops);
+ debugfs_create_file("vlan", 0400, nsim_dev_port->ddir, ns,
+ &nsim_vlan_fops);
return ns;
err_free_netdev:
+ debugfs_remove_recursive(nsim_dev_port->ddir);
+ nsim_dev_port->ddir = NULL;
free_netdev(dev);
return ERR_PTR(err);
}
@@ -1174,10 +1175,6 @@ void nsim_destroy(struct netdevsim *ns)
struct netdevsim *peer;
u16 vid;
- debugfs_remove(ns->vlan_dfs);
- debugfs_remove(ns->qr_dfs);
- debugfs_remove(ns->pp_dfs);
-
if (ns->nb.notifier_call)
unregister_netdevice_notifier_dev_net(ns->netdev, &ns->nb,
&ns->nn);
diff --git a/drivers/net/netdevsim/netdevsim.h b/drivers/net/netdevsim/netdevsim.h
index d909c4160..29cbf004a 100644
--- a/drivers/net/netdevsim/netdevsim.h
+++ b/drivers/net/netdevsim/netdevsim.h
@@ -153,9 +153,6 @@ struct netdevsim {
} udp_ports;
struct page *page;
- struct dentry *pp_dfs;
- struct dentry *qr_dfs;
- struct dentry *vlan_dfs;
struct nsim_ethtool ethtool;
struct netdevsim __rcu *peer;
diff --git a/drivers/net/netdevsim/psp.c b/drivers/net/netdevsim/psp.c
index 6936ecb81..2f27a2114 100644
--- a/drivers/net/netdevsim/psp.c
+++ b/drivers/net/netdevsim/psp.c
@@ -227,7 +227,6 @@ static void __nsim_psp_uninit(struct netdevsim *ns, bool teardown)
void nsim_psp_uninit(struct netdevsim *ns)
{
- debugfs_remove(ns->psp.rereg);
mutex_destroy(&ns->psp.rereg_lock);
__nsim_psp_uninit(ns, true);
}
diff --git a/drivers/net/netdevsim/udp_tunnels.c b/drivers/net/netdevsim/udp_tunnels.c
index 89fff76e5..a69b15f12 100644
--- a/drivers/net/netdevsim/udp_tunnels.c
+++ b/drivers/net/netdevsim/udp_tunnels.c
@@ -188,9 +188,6 @@ int nsim_udp_tunnels_info_create(struct nsim_dev *nsim_dev,
void nsim_udp_tunnels_info_destroy(struct net_device *dev)
{
- struct netdevsim *ns = netdev_priv(dev);
-
- debugfs_remove_recursive(ns->udp_ports.ddir);
kfree(dev->udp_tunnel_nic_info);
dev->udp_tunnel_nic_info = NULL;
}
base-commit: 8cd9520d35a6c38db6567e97dd93b1f11f185dc6
--
See https://goo.gle/syzbot-ai-patches for information about AI-generated patches.
You can comment on the patch as usual, syzbot will try to address
the comments and send a new version of the patch if necessary.
syzbot engineers can be reached at syzkaller@googlegroups.com.
^ permalink raw reply related
* Re: [PATCH net v4 2/2] net: phy: mdio-i2c: defer RollBall bridge probe to PHY discovery
From: Aleksander Jan Bajkowski @ 2026-06-27 10:51 UTC (permalink / raw)
To: Jakub Kicinski
Cc: Petr Wozniak, Russell King, Andrew Lunn, Heiner Kallweit,
David S . Miller, Eric Dumazet, Paolo Abeni, netdev, linux-kernel,
linux-phy, Maxime Chevallier, Bjorn Mork, Marek Behun
In-Reply-To: <20260625082335.3d13d875@kernel.org>
Hi Jakub,
On 25/06/2026 17:23, Jakub Kicinski wrote:
> On Wed, 24 Jun 2026 23:44:19 +0200 Aleksander Jan Bajkowski wrote:
>>> For genuine RollBall modules (e.g. FLYPRO SFP-10GT-CS-30M with Aquantia
>>> AQR113C) the probe now runs after initialization is complete and
>>> correctly returns 0, so PHY detection proceeds normally.
>> The FLPRO SFP module still fails to detect the PHY. It is necessary to
>> increase `module_t_wait` to 20 seconds. Most likely, during this time
>> the module loads the PHY firmware from SPI memory or from the
>> microcontroller (rollball bridge) via MDIO. Same probably applies to
>> most SFP modules with a PHY that load firmware at start-up (AQR113,
>> RTL8261C etc.).
> Just to clarify is FLPRO a typo or a knock off ?
Typo on my comment FLPRO->FLYPRO :)
> Do you want something to be changed here or you're just flagging that
> more follow ups are needed if we want to cover more modules?
I don’t know how this should be fixed. I’m just sharing information.
Even with this patch, most Rollball modules still don’t work. This
patch at least fixes one bug. According to SFF-8472[1], we shouldn’t
communicate with the SFP module until 300ms have elapsed since the
module was inserted. The second problem still remains. 300ms is still
not enough for most Rollball modules. Unfortunately, we’d need to test
a quirk for each module to find out what delay is required. We need a
quirk similar to sfp_fixup_rollball_wait4s. But with a longer delay.
Should the fix be included in this series or follow up?
1. SFF-8472 Rev 12.5a. Table 8-7
Best regards,
Aleksander
^ permalink raw reply
* [RFC PATCH net-next] netpoll: hold RCU while walking napi_list
From: Runyu Xiao @ 2026-06-27 10:12 UTC (permalink / raw)
To: davem, edumazet, kuba, pabeni
Cc: horms, leitao, sashal, bigeasy, netdev, linux-kernel, runyu.xiao,
jianhao.xu
poll_napi() walks dev->napi_list with list_for_each_entry_rcu(). Some
netpoll send paths are already inside an RCU read-side section, but the
helper itself does not document or enforce that contract.
CONFIG_PROVE_RCU_LIST reports the poll_napi() traversal when the helper
is exercised directly from netpoll_poll_dev(). The current source has
important lifetime defenses around NAPI deletion and netpoll device
close, so this is not presented as a proven use-after-free. The issue is
that the RCU-list reader contract is implicit at the helper boundary.
Take rcu_read_lock() locally while walking the NAPI list. This keeps the
contract with netif_napi_del() and synchronize_net() explicit and avoids
relying on every current or future caller to provide the read-side
section.
This was found by our static analysis tool and then manually reviewed
against the current tree. CONFIG_PROVE_RCU_LIST was used as
target-matched triage evidence; the RFC is limited to making the
helper's RCU-list reader contract explicit.
This is an RFC because maintainers may prefer to express the existing
netpoll dev_lock/NAPI-list lifetime contract instead of adding a local
RCU reader around the polling loop.
Signed-off-by: Runyu Xiao <runyu.xiao@seu.edu.cn>
---
net/core/netpoll.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/net/core/netpoll.c b/net/core/netpoll.c
index 5af14f14a362..2e13ca0d09fe 100644
--- a/net/core/netpoll.c
+++ b/net/core/netpoll.c
@@ -165,12 +165,14 @@ static void poll_napi(struct net_device *dev)
struct napi_struct *napi;
int cpu = smp_processor_id();
+ rcu_read_lock();
list_for_each_entry_rcu(napi, &dev->napi_list, dev_list) {
if (cmpxchg(&napi->poll_owner, -1, cpu) == -1) {
poll_one_napi(napi);
smp_store_release(&napi->poll_owner, -1);
}
}
+ rcu_read_unlock();
}
void netpoll_poll_dev(struct net_device *dev)
--
2.34.1
^ permalink raw reply related
* [PATCH v7 09/10] rust: macros: remove `THIS_MODULE` static from `module!`
From: Alvin Sun @ 2026-06-27 9:28 UTC (permalink / raw)
To: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Luis Chamberlain, Petr Pavlu, Daniel Gomez,
Sami Tolvanen, Aaron Tomlin, Greg Kroah-Hartman,
Rafael J. Wysocki, David Airlie, Simona Vetter, Daniel Almeida,
Arnd Bergmann, Brendan Higgins, David Gow, Rae Moar, Breno Leitao,
Jens Axboe, Dave Ertman, Leon Romanovsky, Igor Korotin,
FUJITA Tomonori, Bjorn Helgaas, Krzysztof Wilczyński,
Arve Hjønnevåg, Todd Kjos, Christian Brauner,
Carlos Llamas
Cc: rust-for-linux, linux-modules, driver-core, dri-devel, nova-gpu,
linux-kselftest, kunit-dev, linux-block, linux-kernel, netdev,
linux-pci, Alvin Sun
In-Reply-To: <20260627-fix-fops-owner-v7-0-33cd3990edf0@linux.dev>
All users have been migrated to `ModuleMetadata::THIS_MODULE` const or
`this_module::<LocalModule>()` helper. The `static THIS_MODULE`
generated by the `module!` macro is no longer referenced anywhere,
so remove it to avoid having two sources of the same `ThisModule`
pointer.
Assisted-by: opencode:glm-5.2
Reviewed-by: Andreas Hindborg <a.hindborg@kernel.org>
Reviewed-by: Gary Guo <gary@garyguo.net>
Acked-by: Danilo Krummrich <dakr@kernel.org>
Signed-off-by: Alvin Sun <alvin.sun@linux.dev>
---
rust/macros/module.rs | 16 ----------------
1 file changed, 16 deletions(-)
diff --git a/rust/macros/module.rs b/rust/macros/module.rs
index aa9a618d5d19e..23b6a1b456b80 100644
--- a/rust/macros/module.rs
+++ b/rust/macros/module.rs
@@ -497,22 +497,6 @@ pub(crate) fn module(info: ModuleInfo) -> Result<TokenStream> {
/// Used by the printing macros, e.g. [`info!`].
const __LOG_PREFIX: &[u8] = #name_cstr.to_bytes_with_nul();
- // SAFETY: `__this_module` is constructed by the kernel at load time and will not be
- // freed until the module is unloaded.
- #[cfg(MODULE)]
- static THIS_MODULE: ::kernel::ThisModule = unsafe {
- extern "C" {
- static __this_module: ::kernel::types::Opaque<::kernel::bindings::module>;
- };
-
- ::kernel::ThisModule::from_ptr(__this_module.get())
- };
-
- #[cfg(not(MODULE))]
- static THIS_MODULE: ::kernel::ThisModule = unsafe {
- ::kernel::ThisModule::from_ptr(::core::ptr::null_mut())
- };
-
/// The `LocalModule` type is the type of the module created by `module!`,
/// `module_pci_driver!`, `module_platform_driver!`, etc.
type LocalModule = #type_;
--
2.43.0
^ permalink raw reply related
* [PATCH v7 08/10] rust: binder: use `LocalModule` for `THIS_MODULE`
From: Alvin Sun @ 2026-06-27 9:28 UTC (permalink / raw)
To: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Luis Chamberlain, Petr Pavlu, Daniel Gomez,
Sami Tolvanen, Aaron Tomlin, Greg Kroah-Hartman,
Rafael J. Wysocki, David Airlie, Simona Vetter, Daniel Almeida,
Arnd Bergmann, Brendan Higgins, David Gow, Rae Moar, Breno Leitao,
Jens Axboe, Dave Ertman, Leon Romanovsky, Igor Korotin,
FUJITA Tomonori, Bjorn Helgaas, Krzysztof Wilczyński,
Arve Hjønnevåg, Todd Kjos, Christian Brauner,
Carlos Llamas
Cc: rust-for-linux, linux-modules, driver-core, dri-devel, nova-gpu,
linux-kselftest, kunit-dev, linux-block, linux-kernel, netdev,
linux-pci, Alvin Sun
In-Reply-To: <20260627-fix-fops-owner-v7-0-33cd3990edf0@linux.dev>
Replace the `THIS_MODULE` static reference in the binder fops with
`this_module::<LocalModule>()`, consistent with the move of
`THIS_MODULE` into the `ModuleMetadata` trait.
Assisted-by: opencode:glm-5.2
Reviewed-by: Gary Guo <gary@garyguo.net>
Acked-by: Danilo Krummrich <dakr@kernel.org>
Signed-off-by: Alvin Sun <alvin.sun@linux.dev>
---
drivers/android/binder/rust_binder_main.rs | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/android/binder/rust_binder_main.rs b/drivers/android/binder/rust_binder_main.rs
index dc1941cd2407b..d6ceebbd5f94e 100644
--- a/drivers/android/binder/rust_binder_main.rs
+++ b/drivers/android/binder/rust_binder_main.rs
@@ -17,6 +17,7 @@
bindings::{self, seq_file},
fs::File,
list::{ListArc, ListArcSafe, ListLinksSelfPtr, TryNewListArc},
+ module::this_module,
prelude::*,
seq_file::SeqFile,
seq_print,
@@ -318,7 +319,7 @@ unsafe impl<T> Sync for AssertSync<T> {}
let zeroed_ops = unsafe { core::mem::MaybeUninit::zeroed().assume_init() };
let ops = kernel::bindings::file_operations {
- owner: THIS_MODULE.as_ptr(),
+ owner: this_module::<LocalModule>().as_ptr(),
poll: Some(rust_binder_poll),
unlocked_ioctl: Some(rust_binder_ioctl),
compat_ioctl: bindings::compat_ptr_ioctl,
--
2.43.0
^ permalink raw reply related
* [PATCH v7 06/10] rust: miscdevice: set fops.owner from driver module pointer
From: Alvin Sun @ 2026-06-27 9:28 UTC (permalink / raw)
To: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Luis Chamberlain, Petr Pavlu, Daniel Gomez,
Sami Tolvanen, Aaron Tomlin, Greg Kroah-Hartman,
Rafael J. Wysocki, David Airlie, Simona Vetter, Daniel Almeida,
Arnd Bergmann, Brendan Higgins, David Gow, Rae Moar, Breno Leitao,
Jens Axboe, Dave Ertman, Leon Romanovsky, Igor Korotin,
FUJITA Tomonori, Bjorn Helgaas, Krzysztof Wilczyński,
Arve Hjønnevåg, Todd Kjos, Christian Brauner,
Carlos Llamas
Cc: rust-for-linux, linux-modules, driver-core, dri-devel, nova-gpu,
linux-kselftest, kunit-dev, linux-block, linux-kernel, netdev,
linux-pci, Alvin Sun
In-Reply-To: <20260627-fix-fops-owner-v7-0-33cd3990edf0@linux.dev>
Set the miscdevice fops owner field from the driver module pointer
via the `this_module::<T::OwnerModule>()` helper, instead of
defaulting to null.
Assisted-by: opencode:glm-5.2
Reviewed-by: Andreas Hindborg <a.hindborg@kernel.org>
Reviewed-by: Gary Guo <gary@garyguo.net>
Acked-by: Danilo Krummrich <dakr@kernel.org>
Signed-off-by: Alvin Sun <alvin.sun@linux.dev>
---
rust/kernel/miscdevice.rs | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/rust/kernel/miscdevice.rs b/rust/kernel/miscdevice.rs
index 83ce50def5ac9..2a4329f98614e 100644
--- a/rust/kernel/miscdevice.rs
+++ b/rust/kernel/miscdevice.rs
@@ -24,12 +24,13 @@
IovIterSource, //
},
mm::virt::VmaNew,
+ module::this_module,
prelude::*,
seq_file::SeqFile,
types::{
ForeignOwnable,
Opaque, //
- },
+ }, //
};
use core::marker::PhantomData;
@@ -430,6 +431,7 @@ impl<T: MiscDevice> MiscdeviceVTable<T> {
} else {
None
},
+ owner: this_module::<T::OwnerModule>().as_ptr(),
..pin_init::zeroed()
};
--
2.43.0
^ permalink raw reply related
* [PATCH v7 07/10] rust: configfs: use `LocalModule` for `THIS_MODULE`
From: Alvin Sun @ 2026-06-27 9:28 UTC (permalink / raw)
To: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Luis Chamberlain, Petr Pavlu, Daniel Gomez,
Sami Tolvanen, Aaron Tomlin, Greg Kroah-Hartman,
Rafael J. Wysocki, David Airlie, Simona Vetter, Daniel Almeida,
Arnd Bergmann, Brendan Higgins, David Gow, Rae Moar, Breno Leitao,
Jens Axboe, Dave Ertman, Leon Romanovsky, Igor Korotin,
FUJITA Tomonori, Bjorn Helgaas, Krzysztof Wilczyński,
Arve Hjønnevåg, Todd Kjos, Christian Brauner,
Carlos Llamas
Cc: rust-for-linux, linux-modules, driver-core, dri-devel, nova-gpu,
linux-kselftest, kunit-dev, linux-block, linux-kernel, netdev,
linux-pci, Alvin Sun
In-Reply-To: <20260627-fix-fops-owner-v7-0-33cd3990edf0@linux.dev>
Replace the `THIS_MODULE` static reference in the `configfs_attrs!`
macro with `this_module::<LocalModule>()`, and update
rnull to import `LocalModule` instead of `THIS_MODULE`, consistent
with the move of `THIS_MODULE` into the `ModuleMetadata` trait.
Assisted-by: opencode:glm-5.2
Reviewed-by: Andreas Hindborg <a.hindborg@kernel.org>
Acked-by: Danilo Krummrich <dakr@kernel.org>
Signed-off-by: Alvin Sun <alvin.sun@linux.dev>
---
drivers/block/rnull/configfs.rs | 6 ++----
rust/kernel/configfs.rs | 9 ++++++---
2 files changed, 8 insertions(+), 7 deletions(-)
diff --git a/drivers/block/rnull/configfs.rs b/drivers/block/rnull/configfs.rs
index c10a55fc58948..b2547ad1e5ddd 100644
--- a/drivers/block/rnull/configfs.rs
+++ b/drivers/block/rnull/configfs.rs
@@ -1,9 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
-use super::{
- NullBlkDevice,
- THIS_MODULE, //
-};
+use super::NullBlkDevice;
+use crate::LocalModule;
use kernel::{
block::mq::gen_disk::{
GenDisk,
diff --git a/rust/kernel/configfs.rs b/rust/kernel/configfs.rs
index 2339c6467325d..cd082b83e9e74 100644
--- a/rust/kernel/configfs.rs
+++ b/rust/kernel/configfs.rs
@@ -875,13 +875,14 @@ fn as_ptr(&self) -> *const bindings::config_item_type {
/// configfs::Subsystem<Configuration>,
/// Configuration
/// >::new_with_child_ctor::<N,Child>(
-/// &THIS_MODULE,
+/// ::kernel::module::this_module::<crate::LocalModule>(),
/// &CONFIGURATION_ATTRS
/// );
///
/// &CONFIGURATION_TPE
/// }
/// ```
+#[allow(clippy::crate_in_macro_def)]
#[macro_export]
macro_rules! configfs_attrs {
(
@@ -1021,7 +1022,8 @@ macro_rules! configfs_attrs {
static [< $data:upper _TPE >] : $crate::configfs::ItemType<$container, $data> =
$crate::configfs::ItemType::<$container, $data>::new::<N>(
- &THIS_MODULE, &[<$ data:upper _ATTRS >]
+ $crate::module::this_module::<crate::LocalModule>(),
+ &[<$ data:upper _ATTRS >]
);
)?
@@ -1030,7 +1032,8 @@ macro_rules! configfs_attrs {
$crate::configfs::ItemType<$container, $data> =
$crate::configfs::ItemType::<$container, $data>::
new_with_child_ctor::<N, $child>(
- &THIS_MODULE, &[<$ data:upper _ATTRS >]
+ $crate::module::this_module::<crate::LocalModule>(),
+ &[<$ data:upper _ATTRS >]
);
)?
--
2.43.0
^ permalink raw reply related
* [PATCH v7 10/10] rust: module: update MAINTAINERS to cover module.rs
From: Alvin Sun @ 2026-06-27 9:28 UTC (permalink / raw)
To: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Luis Chamberlain, Petr Pavlu, Daniel Gomez,
Sami Tolvanen, Aaron Tomlin, Greg Kroah-Hartman,
Rafael J. Wysocki, David Airlie, Simona Vetter, Daniel Almeida,
Arnd Bergmann, Brendan Higgins, David Gow, Rae Moar, Breno Leitao,
Jens Axboe, Dave Ertman, Leon Romanovsky, Igor Korotin,
FUJITA Tomonori, Bjorn Helgaas, Krzysztof Wilczyński,
Arve Hjønnevåg, Todd Kjos, Christian Brauner,
Carlos Llamas
Cc: rust-for-linux, linux-modules, driver-core, dri-devel, nova-gpu,
linux-kselftest, kunit-dev, linux-block, linux-kernel, netdev,
linux-pci, Alvin Sun
In-Reply-To: <20260627-fix-fops-owner-v7-0-33cd3990edf0@linux.dev>
Module types now live in `rust/kernel/module.rs` alongside
`rust/kernel/module_param.rs`. Update the MODULE SUPPORT file pattern
from `rust/kernel/module_param.rs` to `rust/kernel/module*.rs` so both
files are covered.
Assisted-by: opencode:glm-5.2
Link: https://lore.kernel.org/rust-for-linux/8ea21b29-9baf-4926-a16f-7d21c5a1a1b8@suse.com
Signed-off-by: Alvin Sun <alvin.sun@linux.dev>
---
MAINTAINERS | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/MAINTAINERS b/MAINTAINERS
index e035a3be797c4..74733de3e41ee 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -17984,7 +17984,7 @@ F: include/linux/module*.h
F: kernel/module/
F: lib/test_kmod.c
F: lib/tests/module/
-F: rust/kernel/module_param.rs
+F: rust/kernel/module*.rs
F: rust/macros/module.rs
F: scripts/module*
F: tools/testing/selftests/kmod/
--
2.43.0
^ permalink raw reply related
* [PATCH v7 05/10] rust: drm: set fops.owner from driver module pointer
From: Alvin Sun @ 2026-06-27 9:28 UTC (permalink / raw)
To: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Luis Chamberlain, Petr Pavlu, Daniel Gomez,
Sami Tolvanen, Aaron Tomlin, Greg Kroah-Hartman,
Rafael J. Wysocki, David Airlie, Simona Vetter, Daniel Almeida,
Arnd Bergmann, Brendan Higgins, David Gow, Rae Moar, Breno Leitao,
Jens Axboe, Dave Ertman, Leon Romanovsky, Igor Korotin,
FUJITA Tomonori, Bjorn Helgaas, Krzysztof Wilczyński,
Arve Hjønnevåg, Todd Kjos, Christian Brauner,
Carlos Llamas
Cc: rust-for-linux, linux-modules, driver-core, dri-devel, nova-gpu,
linux-kselftest, kunit-dev, linux-block, linux-kernel, netdev,
linux-pci, Alvin Sun
In-Reply-To: <20260627-fix-fops-owner-v7-0-33cd3990edf0@linux.dev>
Change `create_fops()` to accept an owner module pointer instead of
hardcoding `null_mut()`, ensuring the kernel correctly tracks the
module owning the DRM device's file operations.
Assisted-by: opencode:glm-5.2
Reviewed-by: Andreas Hindborg <a.hindborg@kernel.org>
Reviewed-by: Gary Guo <gary@garyguo.net>
Acked-by: Danilo Krummrich <dakr@kernel.org>
Signed-off-by: Alvin Sun <alvin.sun@linux.dev>
---
rust/kernel/drm/device.rs | 3 ++-
rust/kernel/drm/gem/mod.rs | 4 ++--
2 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/rust/kernel/drm/device.rs b/rust/kernel/drm/device.rs
index 403fc35353c74..d92cacb665366 100644
--- a/rust/kernel/drm/device.rs
+++ b/rust/kernel/drm/device.rs
@@ -111,7 +111,8 @@ impl<T: drm::Driver> Device<T> {
fops: &Self::GEM_FOPS,
};
- const GEM_FOPS: bindings::file_operations = drm::gem::create_fops();
+ const GEM_FOPS: bindings::file_operations =
+ drm::gem::create_fops(crate::module::this_module::<T::OwnerModule>().as_ptr());
/// Create a new `drm::Device` for a `drm::Driver`.
pub fn new(dev: &device::Device, data: impl PinInit<T::Data, Error>) -> Result<ARef<Self>> {
diff --git a/rust/kernel/drm/gem/mod.rs b/rust/kernel/drm/gem/mod.rs
index 01b5bd47a3332..9a203efc59116 100644
--- a/rust/kernel/drm/gem/mod.rs
+++ b/rust/kernel/drm/gem/mod.rs
@@ -357,10 +357,10 @@ impl<T: DriverObject> AllocImpl for Object<T> {
};
}
-pub(super) const fn create_fops() -> bindings::file_operations {
+pub(super) const fn create_fops(owner: *mut bindings::module) -> bindings::file_operations {
let mut fops: bindings::file_operations = pin_init::zeroed();
- fops.owner = core::ptr::null_mut();
+ fops.owner = owner;
fops.open = Some(bindings::drm_open);
fops.release = Some(bindings::drm_release);
fops.unlocked_ioctl = Some(bindings::drm_ioctl);
--
2.43.0
^ permalink raw reply related
* [PATCH v7 03/10] rust: doctest: add LocalModule fallback for #[vtable] ThisModule
From: Alvin Sun @ 2026-06-27 9:28 UTC (permalink / raw)
To: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Luis Chamberlain, Petr Pavlu, Daniel Gomez,
Sami Tolvanen, Aaron Tomlin, Greg Kroah-Hartman,
Rafael J. Wysocki, David Airlie, Simona Vetter, Daniel Almeida,
Arnd Bergmann, Brendan Higgins, David Gow, Rae Moar, Breno Leitao,
Jens Axboe, Dave Ertman, Leon Romanovsky, Igor Korotin,
FUJITA Tomonori, Bjorn Helgaas, Krzysztof Wilczyński,
Arve Hjønnevåg, Todd Kjos, Christian Brauner,
Carlos Llamas
Cc: rust-for-linux, linux-modules, driver-core, dri-devel, nova-gpu,
linux-kselftest, kunit-dev, linux-block, linux-kernel, netdev,
linux-pci, Alvin Sun
In-Reply-To: <20260627-fix-fops-owner-v7-0-33cd3990edf0@linux.dev>
Add a `LocalModule` struct with a null-pointer `ModuleMetadata` impl
in the doctest harness, so that `crate::LocalModule` (auto-inserted
by `#[vtable]`) resolves correctly when there is no `module!` macro.
Assisted-by: opencode:glm-5.2
Reviewed-by: Andreas Hindborg <a.hindborg@kernel.org>
Reviewed-by: Gary Guo <gary@garyguo.net>
Acked-by: Danilo Krummrich <dakr@kernel.org>
Signed-off-by: Alvin Sun <alvin.sun@linux.dev>
---
scripts/rustdoc_test_gen.rs | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/scripts/rustdoc_test_gen.rs b/scripts/rustdoc_test_gen.rs
index ee76e96b41eea..198af4e446c8c 100644
--- a/scripts/rustdoc_test_gen.rs
+++ b/scripts/rustdoc_test_gen.rs
@@ -239,6 +239,22 @@ macro_rules! assert_eq {{
const __LOG_PREFIX: &[u8] = b"rust_doctests_kernel\0";
+/// Dummy module type for doctest context.
+struct LocalModule;
+
+use kernel::{{
+ str::CStr,
+ ModuleMetadata,
+ ThisModule, //
+}};
+use core::ptr::null_mut;
+
+impl ModuleMetadata for LocalModule {{
+ const NAME: &'static CStr = c"rust_doctests_kernel";
+ // SAFETY: `try_module_get`/`module_put` handle null module pointers gracefully.
+ const THIS_MODULE: ThisModule = unsafe {{ ThisModule::from_ptr(null_mut()) }};
+}}
+
{rust_tests}
"#
)
--
2.43.0
^ permalink raw reply related
* [PATCH v7 04/10] rust: macros: auto-insert OwnerModule in #[vtable]
From: Alvin Sun @ 2026-06-27 9:28 UTC (permalink / raw)
To: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Luis Chamberlain, Petr Pavlu, Daniel Gomez,
Sami Tolvanen, Aaron Tomlin, Greg Kroah-Hartman,
Rafael J. Wysocki, David Airlie, Simona Vetter, Daniel Almeida,
Arnd Bergmann, Brendan Higgins, David Gow, Rae Moar, Breno Leitao,
Jens Axboe, Dave Ertman, Leon Romanovsky, Igor Korotin,
FUJITA Tomonori, Bjorn Helgaas, Krzysztof Wilczyński,
Arve Hjønnevåg, Todd Kjos, Christian Brauner,
Carlos Llamas
Cc: rust-for-linux, linux-modules, driver-core, dri-devel, nova-gpu,
linux-kselftest, kunit-dev, linux-block, linux-kernel, netdev,
linux-pci, Alvin Sun
In-Reply-To: <20260627-fix-fops-owner-v7-0-33cd3990edf0@linux.dev>
Auto-add `type OwnerModule: ::kernel::ModuleMetadata;` as a required
associated type on the trait side if not already defined, and
auto-insert `type OwnerModule = crate::LocalModule;` on the impl side
if not explicitly provided, eliminating the need to manually declare
and implement `OwnerModule` in every vtable trait and impl.
Assisted-by: opencode:glm-5.2
Reviewed-by: Andreas Hindborg <a.hindborg@kernel.org>
Suggested-by: Gary Guo <gary@garyguo.net>
Link: https://lore.kernel.org/all/DIMMWHUOLPSH.13JFRHDKDQJGO@garyguo.net
Reviewed-by: Gary Guo <gary@garyguo.net>
Acked-by: Danilo Krummrich <dakr@kernel.org>
Signed-off-by: Alvin Sun <alvin.sun@linux.dev>
---
rust/macros/lib.rs | 6 ++++++
rust/macros/vtable.rs | 41 ++++++++++++++++++++++++++++++++++++-----
2 files changed, 42 insertions(+), 5 deletions(-)
diff --git a/rust/macros/lib.rs b/rust/macros/lib.rs
index 2cfd59e0f9e7c..bc7ded353c5ca 100644
--- a/rust/macros/lib.rs
+++ b/rust/macros/lib.rs
@@ -176,6 +176,12 @@ pub fn module(input: TokenStream) -> TokenStream {
///
/// This macro should not be used when all functions are required.
///
+/// Additionally, this macro automatically handles the `OwnerModule`
+/// associated type: on the trait side, `type OwnerModule: ModuleMetadata;`
+/// is added as a required associated type if not already defined; on the
+/// impl side, `type OwnerModule = LocalModule;` is automatically inserted
+/// if not explicitly defined.
+///
/// # Examples
///
/// ```
diff --git a/rust/macros/vtable.rs b/rust/macros/vtable.rs
index c6510b0c4ea1d..be9a5ed8abe5e 100644
--- a/rust/macros/vtable.rs
+++ b/rust/macros/vtable.rs
@@ -30,6 +30,22 @@ fn handle_trait(mut item: ItemTrait) -> Result<ItemTrait> {
const USE_VTABLE_ATTR: ();
});
+ // Add `type OwnerModule: ModuleMetadata` as a required associated type if
+ // the trait does not already define it.
+ if !item
+ .items
+ .iter()
+ .any(|i| matches!(i, TraitItem::Type(t) if t.ident == "OwnerModule"))
+ {
+ gen_items.push(parse_quote! {
+ /// The module implementing this vtable trait.
+ ///
+ /// Automatically set to `crate::LocalModule` by the `#[vtable]`
+ /// impl macro.
+ type OwnerModule: ::kernel::ModuleMetadata;
+ });
+ }
+
for item in &item.items {
if let TraitItem::Fn(fn_item) = item {
let name = &fn_item.sig.ident;
@@ -57,12 +73,18 @@ fn handle_trait(mut item: ItemTrait) -> Result<ItemTrait> {
fn handle_impl(mut item: ItemImpl) -> Result<ItemImpl> {
let mut gen_items = Vec::new();
- let mut defined_consts = HashSet::new();
+ let mut defined_items = HashSet::new();
- // Iterate over all user-defined constants to gather any possible explicit overrides.
+ // Iterate over all user-defined items to gather any possible explicit overrides.
for item in &item.items {
- if let ImplItem::Const(const_item) = item {
- defined_consts.insert(const_item.ident.clone());
+ match item {
+ ImplItem::Const(const_item) => {
+ defined_items.insert(const_item.ident.clone());
+ }
+ ImplItem::Type(type_item) => {
+ defined_items.insert(type_item.ident.clone());
+ }
+ _ => {}
}
}
@@ -70,6 +92,15 @@ fn handle_impl(mut item: ItemImpl) -> Result<ItemImpl> {
const USE_VTABLE_ATTR: () = ();
});
+ // Auto-insert `type OwnerModule = crate::LocalModule` if not explicitly defined.
+ // `crate::LocalModule` resolves to the real module type (via `module!`) or a
+ // dummy fallback in non-module contexts (e.g., doctests).
+ if !defined_items.contains(&parse_quote!(OwnerModule)) {
+ gen_items.push(parse_quote! {
+ type OwnerModule = crate::LocalModule;
+ });
+ }
+
for item in &item.items {
if let ImplItem::Fn(fn_item) = item {
let name = &fn_item.sig.ident;
@@ -78,7 +109,7 @@ fn handle_impl(mut item: ItemImpl) -> Result<ItemImpl> {
name.span(),
);
// Skip if it's declared already -- this allows user override.
- if defined_consts.contains(&gen_const_name) {
+ if defined_items.contains(&gen_const_name) {
continue;
}
let cfg_attrs = crate::helpers::gather_cfg_attrs(&fn_item.attrs);
--
2.43.0
^ permalink raw reply related
* [PATCH v7 01/10] rust: module: move module types into `module.rs`
From: Alvin Sun @ 2026-06-27 9:28 UTC (permalink / raw)
To: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Luis Chamberlain, Petr Pavlu, Daniel Gomez,
Sami Tolvanen, Aaron Tomlin, Greg Kroah-Hartman,
Rafael J. Wysocki, David Airlie, Simona Vetter, Daniel Almeida,
Arnd Bergmann, Brendan Higgins, David Gow, Rae Moar, Breno Leitao,
Jens Axboe, Dave Ertman, Leon Romanovsky, Igor Korotin,
FUJITA Tomonori, Bjorn Helgaas, Krzysztof Wilczyński,
Arve Hjønnevåg, Todd Kjos, Christian Brauner,
Carlos Llamas
Cc: rust-for-linux, linux-modules, driver-core, dri-devel, nova-gpu,
linux-kselftest, kunit-dev, linux-block, linux-kernel, netdev,
linux-pci, Alvin Sun
In-Reply-To: <20260627-fix-fops-owner-v7-0-33cd3990edf0@linux.dev>
Move `Module`, `InPlaceModule`, `ModuleMetadata` and `ThisModule` from
`lib.rs` into a new `rust/kernel/module.rs`. Re-export them from `lib.rs`
to avoid tree-wide changes.
Switch six bus driver registrations from `module.0` to the public
`ThisModule::as_ptr()` accessor, since the field is no longer visible
outside the new `module` submodule.
No functional change.
Assisted-by: opencode:glm-5.2
Acked-by: Danilo Krummrich <dakr@kernel.org>
Signed-off-by: Alvin Sun <alvin.sun@linux.dev>
---
rust/kernel/auxiliary.rs | 2 +-
rust/kernel/i2c.rs | 2 +-
rust/kernel/lib.rs | 75 +++++-------------------------------------------
rust/kernel/module.rs | 71 +++++++++++++++++++++++++++++++++++++++++++++
rust/kernel/net/phy.rs | 6 +++-
rust/kernel/pci.rs | 2 +-
rust/kernel/platform.rs | 2 +-
rust/kernel/usb.rs | 2 +-
8 files changed, 88 insertions(+), 74 deletions(-)
diff --git a/rust/kernel/auxiliary.rs b/rust/kernel/auxiliary.rs
index 93c0db1f66555..4a02f83240be3 100644
--- a/rust/kernel/auxiliary.rs
+++ b/rust/kernel/auxiliary.rs
@@ -63,7 +63,7 @@ unsafe fn register(
// SAFETY: `adrv` is guaranteed to be a valid `DriverType`.
to_result(unsafe {
- bindings::__auxiliary_driver_register(adrv.get(), module.0, name.as_char_ptr())
+ bindings::__auxiliary_driver_register(adrv.get(), module.as_ptr(), name.as_char_ptr())
})
}
diff --git a/rust/kernel/i2c.rs b/rust/kernel/i2c.rs
index 7b908f0c5a58d..24eff08f47123 100644
--- a/rust/kernel/i2c.rs
+++ b/rust/kernel/i2c.rs
@@ -142,7 +142,7 @@ unsafe fn register(
}
// SAFETY: `idrv` is guaranteed to be a valid `DriverType`.
- to_result(unsafe { bindings::i2c_register_driver(module.0, idrv.get()) })
+ to_result(unsafe { bindings::i2c_register_driver(module.as_ptr(), idrv.get()) })
}
unsafe fn unregister(idrv: &Opaque<Self::DriverType>) {
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index b72b2fbe046d6..040ae85056509 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -93,6 +93,7 @@
pub mod maple_tree;
pub mod miscdevice;
pub mod mm;
+pub mod module;
pub mod module_param;
#[cfg(CONFIG_NET)]
pub mod net;
@@ -139,79 +140,17 @@
#[doc(hidden)]
pub use bindings;
pub use macros;
+pub use module::{
+ InPlaceModule,
+ Module,
+ ModuleMetadata,
+ ThisModule, //
+};
pub use uapi;
/// Prefix to appear before log messages printed from within the `kernel` crate.
const __LOG_PREFIX: &[u8] = b"rust_kernel\0";
-/// The top level entrypoint to implementing a kernel module.
-///
-/// For any teardown or cleanup operations, your type may implement [`Drop`].
-pub trait Module: Sized + Sync + Send {
- /// Called at module initialization time.
- ///
- /// Use this method to perform whatever setup or registration your module
- /// should do.
- ///
- /// Equivalent to the `module_init` macro in the C API.
- fn init(module: &'static ThisModule) -> error::Result<Self>;
-}
-
-/// A module that is pinned and initialised in-place.
-pub trait InPlaceModule: Sync + Send {
- /// Creates an initialiser for the module.
- ///
- /// It is called when the module is loaded.
- fn init(module: &'static ThisModule) -> impl pin_init::PinInit<Self, error::Error>;
-}
-
-impl<T: Module> InPlaceModule for T {
- fn init(module: &'static ThisModule) -> impl pin_init::PinInit<Self, error::Error> {
- let initer = move |slot: *mut Self| {
- let m = <Self as Module>::init(module)?;
-
- // SAFETY: `slot` is valid for write per the contract with `pin_init_from_closure`.
- unsafe { slot.write(m) };
- Ok(())
- };
-
- // SAFETY: On success, `initer` always fully initialises an instance of `Self`.
- unsafe { pin_init::pin_init_from_closure(initer) }
- }
-}
-
-/// Metadata attached to a [`Module`] or [`InPlaceModule`].
-pub trait ModuleMetadata {
- /// The name of the module as specified in the `module!` macro.
- const NAME: &'static crate::str::CStr;
-}
-
-/// Equivalent to `THIS_MODULE` in the C API.
-///
-/// C header: [`include/linux/init.h`](srctree/include/linux/init.h)
-pub struct ThisModule(*mut bindings::module);
-
-// SAFETY: `THIS_MODULE` may be used from all threads within a module.
-unsafe impl Sync for ThisModule {}
-
-impl ThisModule {
- /// Creates a [`ThisModule`] given the `THIS_MODULE` pointer.
- ///
- /// # Safety
- ///
- /// The pointer must be equal to the right `THIS_MODULE`.
- pub const unsafe fn from_ptr(ptr: *mut bindings::module) -> ThisModule {
- ThisModule(ptr)
- }
-
- /// Access the raw pointer for this module.
- ///
- /// It is up to the user to use it correctly.
- pub const fn as_ptr(&self) -> *mut bindings::module {
- self.0
- }
-}
-
#[cfg(not(testlib))]
#[panic_handler]
fn panic(info: &core::panic::PanicInfo<'_>) -> ! {
diff --git a/rust/kernel/module.rs b/rust/kernel/module.rs
new file mode 100644
index 0000000000000..be242a82e86d2
--- /dev/null
+++ b/rust/kernel/module.rs
@@ -0,0 +1,71 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Module-related types and helpers.
+
+/// The entrypoint to implementing a kernel module.
+///
+/// For any teardown or cleanup operations, your type may implement [`Drop`].
+pub trait Module: Sized + Sync + Send {
+ /// Called at module initialization time.
+ ///
+ /// Use this method to perform whatever setup or registration your module
+ /// should do.
+ ///
+ /// Equivalent to the `module_init` macro in the C API.
+ fn init(module: &'static ThisModule) -> crate::error::Result<Self>;
+}
+
+/// A module that is pinned and initialised in-place.
+pub trait InPlaceModule: Sync + Send {
+ /// Creates an initialiser for the module.
+ ///
+ /// It is called when the module is loaded.
+ fn init(module: &'static ThisModule) -> impl pin_init::PinInit<Self, crate::error::Error>;
+}
+
+impl<T: Module> InPlaceModule for T {
+ fn init(module: &'static ThisModule) -> impl pin_init::PinInit<Self, crate::error::Error> {
+ let initer = move |slot: *mut Self| {
+ let m = <Self as Module>::init(module)?;
+
+ // SAFETY: `slot` is valid for write per the contract with `pin_init_from_closure`.
+ unsafe { slot.write(m) };
+ Ok(())
+ };
+
+ // SAFETY: On success, `initer` always fully initialises an instance of `Self`.
+ unsafe { pin_init::pin_init_from_closure(initer) }
+ }
+}
+
+/// Metadata attached to a [`Module`] or [`InPlaceModule`].
+pub trait ModuleMetadata {
+ /// The name of the module as specified in the `module!` macro.
+ const NAME: &'static crate::str::CStr;
+}
+
+/// Equivalent to `THIS_MODULE` in the C API.
+///
+/// C header: [`include/linux/init.h`](srctree/include/linux/init.h)
+pub struct ThisModule(*mut crate::bindings::module);
+
+// SAFETY: `THIS_MODULE` may be used from all threads within a module.
+unsafe impl Sync for ThisModule {}
+
+impl ThisModule {
+ /// Creates a [`ThisModule`] given the `THIS_MODULE` pointer.
+ ///
+ /// # Safety
+ ///
+ /// The pointer must be equal to the right `THIS_MODULE`.
+ pub const unsafe fn from_ptr(ptr: *mut crate::bindings::module) -> ThisModule {
+ ThisModule(ptr)
+ }
+
+ /// Access the raw pointer for this module.
+ ///
+ /// It is up to the user to use it correctly.
+ pub const fn as_ptr(&self) -> *mut crate::bindings::module {
+ self.0
+ }
+}
diff --git a/rust/kernel/net/phy.rs b/rust/kernel/net/phy.rs
index 3ca99db5cccf2..8b7036b8fe480 100644
--- a/rust/kernel/net/phy.rs
+++ b/rust/kernel/net/phy.rs
@@ -659,7 +659,11 @@ pub fn register(
// the `drivers` slice are initialized properly. `drivers` will not be moved.
// So it's just an FFI call.
to_result(unsafe {
- bindings::phy_drivers_register(drivers[0].0.get(), drivers.len().try_into()?, module.0)
+ bindings::phy_drivers_register(
+ drivers[0].0.get(),
+ drivers.len().try_into()?,
+ module.as_ptr(),
+ )
})?;
// INVARIANT: The `drivers` slice is successfully registered to the kernel via `phy_drivers_register`.
Ok(Registration { drivers })
diff --git a/rust/kernel/pci.rs b/rust/kernel/pci.rs
index af74ddff6114d..916ed2cb6b70b 100644
--- a/rust/kernel/pci.rs
+++ b/rust/kernel/pci.rs
@@ -86,7 +86,7 @@ unsafe fn register(
// SAFETY: `pdrv` is guaranteed to be a valid `DriverType`.
to_result(unsafe {
- bindings::__pci_register_driver(pdrv.get(), module.0, name.as_char_ptr())
+ bindings::__pci_register_driver(pdrv.get(), module.as_ptr(), name.as_char_ptr())
})
}
diff --git a/rust/kernel/platform.rs b/rust/kernel/platform.rs
index 8917d4ee499fb..9fdbafd53bc21 100644
--- a/rust/kernel/platform.rs
+++ b/rust/kernel/platform.rs
@@ -82,7 +82,7 @@ unsafe fn register(
}
// SAFETY: `pdrv` is guaranteed to be a valid `DriverType`.
- to_result(unsafe { bindings::__platform_driver_register(pdrv.get(), module.0) })
+ to_result(unsafe { bindings::__platform_driver_register(pdrv.get(), module.as_ptr()) })
}
unsafe fn unregister(pdrv: &Opaque<Self::DriverType>) {
diff --git a/rust/kernel/usb.rs b/rust/kernel/usb.rs
index 9c17a672cd275..213db32727c17 100644
--- a/rust/kernel/usb.rs
+++ b/rust/kernel/usb.rs
@@ -63,7 +63,7 @@ unsafe fn register(
// SAFETY: `udrv` is guaranteed to be a valid `DriverType`.
to_result(unsafe {
- bindings::usb_register_driver(udrv.get(), module.0, name.as_char_ptr())
+ bindings::usb_register_driver(udrv.get(), module.as_ptr(), name.as_char_ptr())
})
}
--
2.43.0
^ permalink raw reply related
* [PATCH v7 00/10] Fix missing fops.owner in Rust DRM/misc abstractions
From: Alvin Sun @ 2026-06-27 9:28 UTC (permalink / raw)
To: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Luis Chamberlain, Petr Pavlu, Daniel Gomez,
Sami Tolvanen, Aaron Tomlin, Greg Kroah-Hartman,
Rafael J. Wysocki, David Airlie, Simona Vetter, Daniel Almeida,
Arnd Bergmann, Brendan Higgins, David Gow, Rae Moar, Breno Leitao,
Jens Axboe, Dave Ertman, Leon Romanovsky, Igor Korotin,
FUJITA Tomonori, Bjorn Helgaas, Krzysztof Wilczyński,
Arve Hjønnevåg, Todd Kjos, Christian Brauner,
Carlos Llamas
Cc: rust-for-linux, linux-modules, driver-core, dri-devel, nova-gpu,
linux-kselftest, kunit-dev, linux-block, linux-kernel, netdev,
linux-pci, Alvin Sun
During tyr debugfs development, a kernel NULL pointer dereference was
encountered after `rmmod tyr` while gnome-shell still held /dev/card1 open:
```
[158827.868132] Unable to handle kernel NULL pointer dereference at virtual address 0000000000000000
[158827.868918] Mem abort info:
[158827.869177] ESR = 0x0000000086000004
[158827.869519] EC = 0x21: IABT (current EL), IL = 32 bits
[158827.870000] SET = 0, FnV = 0
[158827.870281] EA = 0, S1PTW = 0
[158827.870571] FSC = 0x04: level 0 translation fault
[158827.871043] user pgtable: 4k pages, 48-bit VAs, pgdp=0000000108dec000
[158827.871623] [0000000000000000] pgd=0000000000000000, p4d=0000000000000000
[158827.872242] Internal error: Oops: 0000000086000004 [#1] SMP
[158827.872246] Modules linked in: tyr sunrpc snd_soc_simple_card rk805_pwrkey snd_soc_simple_card_utils rtw88_8822bu display_connector rtw88_usb rtw88_8822b snd_soc_rockchip_i2s_tdm snd_soc_hdmi_codec
rtw88_core]
[158827.872337] CPU: 4 UID: 1000 PID: 11276 Comm: gnome-s:disk$0 Tainted: G N 7.1.0-rc1+ #331 PREEMPT
[158827.880534] Tainted: [N]=TEST
[158827.880535] Hardware name: FriendlyElec NanoPi R6C/NanoPi R6C, BIOS v1.1 04/09/2025
[158827.880538] pstate: 60400009 (nZCv daif +PAN -UAO -TCO -DIT -SSBS BTYPE=--)
[158827.880542] pc : 0x0
[158827.880547] lr : _RNvNtCs257m05FHVbX_3tyr2vm8pt_unmap+0x8c/0x12c [tyr]
[158827.880578] sp : ffff800083c236b0
[158827.880579] x29: ffff800083c236d0 x28: ffff00013f8a0000 x27: 0000000000000000
[158827.880585] x26: 000000000000007c x25: ffff000108e6ed80 x24: 0000000000401000
[158827.880590] x23: 0000000000000000 x22: 0000000040000000 x21: 0000000000001000
[158827.880595] x20: ffff00010f778138 x19: 0000000000400000 x18: 00000000ffffffff
[158827.880600] x17: 000000040044ffff x16: 045000f2b5503510 x15: 0720072007200720
[158827.880606] x14: 0720072007200720 x13: 0000000000401000 x12: 0000000000400000
[158827.880611] x11: ffff800083c239d0 x10: ffff000141e4fd88 x9 : 0000000000000000
[158827.880615] x8 : 0000000000000000 x7 : 0000000000000000 x6 : 0000000000400000
[158827.880620] x5 : ffff00013f8a0000 x4 : 0000000000000000 x3 : 0000000000000001
[158827.880625] x2 : 0000000000001000 x1 : 0000000000400000 x0 : ffff00010f778138
[158827.880630] Call trace:
[158827.880632] 0x0 (P)
[158827.880635] _RNvXs6_NtCs257m05FHVbX_3tyr2vmNtB5_9GpuVmDataNtNtNtCsgmSOfgXi5CZ_6kernel3drm5gpuvm11DriverGpuVm13sm_step_unmap+0x3c/0x120 [tyr]
[158827.891166] _RNvMs4_NtNtNtCsgmSOfgXi5CZ_6kernel3drm5gpuvm6sm_opsINtB7_5GpuVmNtNtCs257m05FHVbX_3tyr2vm9GpuVmDataE13sm_step_unmapB13_+0x18/0x34 [tyr]
[158827.891187] op_unmap_cb+0x78/0xb0
[158827.891196] __drm_gpuvm_sm_unmap+0x18c/0x1b4
[158827.891204] drm_gpuvm_sm_unmap+0x38/0x4c
[158827.891209] _RNvMs5_NtCs257m05FHVbX_3tyr2vmNtB5_2Vm7exec_op+0x1cc/0x254 [tyr]
[158827.894085] _RNvMs5_NtCs257m05FHVbX_3tyr2vmNtB5_2Vm11unmap_range+0x124/0x188 [tyr]
[158827.894105] _RINvNtCs5hGKnPbRUFW_4core3ptr13drop_in_placeNtNtCs257m05FHVbX_3tyr3gem8KernelBoEBK_+0x44/0xd8 [tyr]
[158827.894125] _RINvNtCs5hGKnPbRUFW_4core3ptr13drop_in_placeINtNtNtCsgmSOfgXi5CZ_6kernel5alloc4kvec3VecNtNtCs257m05FHVbX_3tyr2fw7SectionNtNtBL_9allocator7KmallocEEB1r_+0x3c/0x100 [tyr]
[158827.894147] _RINvNtCs5hGKnPbRUFW_4core3ptr13drop_in_placeINtNtNtCsgmSOfgXi5CZ_6kernel4sync3arc3ArcNtNtCs257m05FHVbX_3tyr2fw8FirmwareEEB1p_+0x94/0x190 [tyr]
[158827.894167] _RNvMs4_NtNtCsgmSOfgXi5CZ_6kernel3drm6deviceINtB5_6DeviceNtNtCs257m05FHVbX_3tyr6driver12TyrDrmDriverE7releaseBW_+0x30/0x98 [tyr]
[158827.899550] drm_dev_put.part.0+0x88/0xc0
[158827.899557] drm_minor_release+0x18/0x28
[158827.899562] drm_release+0x144/0x170
[158827.899567] __fput+0xe4/0x30c
[158827.899573] ____fput+0x14/0x20
[158827.899579] task_work_run+0x7c/0xe8
[158827.899586] do_exit+0x2a8/0xac4
[158827.899590] do_group_exit+0x34/0x90
[158827.899594] get_signal+0xaac/0xabc
[158827.899599] arch_do_signal_or_restart+0x90/0x3e8
[158827.899606] exit_to_user_mode_loop+0x140/0x1d0
[158827.899613] el0_svc+0x2f4/0x2f8
[158827.899620] el0t_64_sync_handler+0xa0/0xe4
[158827.899627] el0t_64_sync+0x198/0x19c
[158827.899632] ---[ end trace 0000000000000000 ]---
```
The root cause: `fops.owner` was `NULL` in Rust DRM drivers, so the kernel
never blocked module unloading while file descriptors were open. This leads to
use-after-free when drm_release (or other fops) is called on freed module code.
The series moves `THIS_MODULE` into the `ModuleMetadata` as a const, threads it
through `#[vtable]` to set `fops.owner` in DRM/miscdevice, and updates configfs
and rnull to use `this_module::<LocalModule>()`.
Assisted-by: opencode:glm-5.2
Signed-off-by: Alvin Sun <alvin.sun@linux.dev>
---
Changes in v7:
- Use `crate::LocalModule` in `configfs_attrs!` and silence `clippy::crate_in_macro_def`, per Gary's review.
- Link to v6: https://lore.kernel.org/r/20260624-fix-fops-owner-v6-0-5295e333cb3e@linux.dev
Changes in v6:
- Update MAINTAINERS to cover the new `rust/kernel/module.rs`.
- Link to v5: https://lore.kernel.org/r/20260624-fix-fops-owner-v5-0-aa1cba242f05@linux.dev
Changes in v5:
- Add `#[inline]` to the `this_module()` helper.
- Fix configfs doc comment to reference `crate::LocalModule` instead of
bare `LocalModule`.
- Link to v4: https://lore.kernel.org/r/20260623-fix-fops-owner-v4-0-0daf5f077d5c@linux.dev
Changes in v4:
- Move module-related types into a new `rust/kernel/module.rs`.
- Migrate binder from the `module!`-generated `THIS_MODULE` static to
`this_module::<LocalModule>()`.
- Reorganise the series so that every commit builds independently, and
drop the legacy `THIS_MODULE` static once all users are migrated.
- Link to v3: https://lore.kernel.org/r/20260622-fix-fops-owner-v3-0-49d45cb37032@linux.dev
Changes in v3:
- Renamed vtable associated type `ThisModule` to `OwnerModule`
- Added `this_module()` helper for ergonomic `THIS_MODULE` access
- Refined vtable macro implementation: one-liner detection and single `defined_items` set
- Reordered commits to place doctest fallback before vtable auto-insert
- Link to v2: https://lore.kernel.org/r/20260521-fix-fops-owner-v2-0-fd99079c5a04@linux.dev
Changes in v2:
- Merged old `static THIS_MODULE` and v1's `MODULE_PTR` into a single
`ModuleMetadata::THIS_MODULE` const
- `#[vtable]` macro now auto-inserts `type ThisModule`, removing all per-driver
manual patches from v1
- Added configfs & rnull usage site updates and doctest `LocalModule` fallback
- Link to v1: https://lore.kernel.org/r/20260519-fix-fops-owner-v1-0-2ded9830da14@linux.dev
---
Alvin Sun (10):
rust: module: move module types into `module.rs`
rust: module: add `THIS_MODULE` const to `ModuleMetadata` trait
rust: doctest: add LocalModule fallback for #[vtable] ThisModule
rust: macros: auto-insert OwnerModule in #[vtable]
rust: drm: set fops.owner from driver module pointer
rust: miscdevice: set fops.owner from driver module pointer
rust: configfs: use `LocalModule` for `THIS_MODULE`
rust: binder: use `LocalModule` for `THIS_MODULE`
rust: macros: remove `THIS_MODULE` static from `module!`
rust: module: update MAINTAINERS to cover module.rs
MAINTAINERS | 2 +-
drivers/android/binder/rust_binder_main.rs | 3 +-
drivers/block/rnull/configfs.rs | 6 +--
rust/kernel/auxiliary.rs | 2 +-
rust/kernel/configfs.rs | 9 ++--
rust/kernel/drm/device.rs | 3 +-
rust/kernel/drm/gem/mod.rs | 4 +-
rust/kernel/i2c.rs | 2 +-
rust/kernel/lib.rs | 75 +++-------------------------
rust/kernel/miscdevice.rs | 4 +-
rust/kernel/module.rs | 80 ++++++++++++++++++++++++++++++
rust/kernel/net/phy.rs | 6 ++-
rust/kernel/pci.rs | 2 +-
rust/kernel/platform.rs | 2 +-
rust/kernel/usb.rs | 2 +-
rust/macros/lib.rs | 6 +++
rust/macros/module.rs | 34 ++++++-------
rust/macros/vtable.rs | 41 +++++++++++++--
scripts/rustdoc_test_gen.rs | 16 ++++++
19 files changed, 190 insertions(+), 109 deletions(-)
---
base-commit: b7e5ac83cb16f7ffd11dc23736f84276602100ed
change-id: 20260519-fix-fops-owner-e3a77bb27c6c
prerequisite-change-id: 20260519-miscdev-use-format-9ab7e83b1c11:v3
prerequisite-patch-id: 405b334ff0d48ad350014f05a2321bdbaa025400
prerequisite-patch-id: 604b631c81d5423f4ebb2e12ba2d22e9ce371bfc
prerequisite-patch-id: cb550d94cefe01920e0d3ced2b2bcbecd76f3907
prerequisite-patch-id: 3bc830839742591460cb86d9472c04f4686dc600
prerequisite-patch-id: 571058244bc8c7088638d2e3225713011246c7e9
prerequisite-patch-id: 347c5a3c6dbef9832bfce8419fc23e6e08ba477f
prerequisite-patch-id: 3e202d988b56b88446f7535e90d3f00cf5f15701
Best regards,
--
Alvin Sun <alvin.sun@linux.dev>
^ permalink raw reply
* [PATCH v7 02/10] rust: module: add `THIS_MODULE` const to `ModuleMetadata` trait
From: Alvin Sun @ 2026-06-27 9:28 UTC (permalink / raw)
To: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Luis Chamberlain, Petr Pavlu, Daniel Gomez,
Sami Tolvanen, Aaron Tomlin, Greg Kroah-Hartman,
Rafael J. Wysocki, David Airlie, Simona Vetter, Daniel Almeida,
Arnd Bergmann, Brendan Higgins, David Gow, Rae Moar, Breno Leitao,
Jens Axboe, Dave Ertman, Leon Romanovsky, Igor Korotin,
FUJITA Tomonori, Bjorn Helgaas, Krzysztof Wilczyński,
Arve Hjønnevåg, Todd Kjos, Christian Brauner,
Carlos Llamas
Cc: rust-for-linux, linux-modules, driver-core, dri-devel, nova-gpu,
linux-kselftest, kunit-dev, linux-block, linux-kernel, netdev,
linux-pci, Alvin Sun
In-Reply-To: <20260627-fix-fops-owner-v7-0-33cd3990edf0@linux.dev>
Since `const_refs_to_static` has been stable as of the MSRV bump, a
`ThisModule` pointer can now be used in const contexts.
Add a `THIS_MODULE` const to the `ModuleMetadata` trait so that modules
can provide their `ThisModule` pointer in const contexts such as static
`file_operations`.
Add a `this_module()` helper to retrieve the `THIS_MODULE` pointer of a
given module type, and update `__init` to use it instead of the
`THIS_MODULE` static generated by the `module!` macro.
The `static THIS_MODULE` generated by the `module!` macro is retained
for backwards compatibility with existing users and removed in a later
patch once all references have been migrated.
Assisted-by: opencode:glm-5.2
Reviewed-by: Andreas Hindborg <a.hindborg@kernel.org>
Reviewed-by: Gary Guo <gary@garyguo.net>
Acked-by: Danilo Krummrich <dakr@kernel.org>
Signed-off-by: Alvin Sun <alvin.sun@linux.dev>
---
rust/kernel/module.rs | 9 +++++++++
rust/macros/module.rs | 18 +++++++++++++++++-
2 files changed, 26 insertions(+), 1 deletion(-)
diff --git a/rust/kernel/module.rs b/rust/kernel/module.rs
index be242a82e86d2..d713705984477 100644
--- a/rust/kernel/module.rs
+++ b/rust/kernel/module.rs
@@ -42,6 +42,15 @@ fn init(module: &'static ThisModule) -> impl pin_init::PinInit<Self, crate::erro
pub trait ModuleMetadata {
/// The name of the module as specified in the `module!` macro.
const NAME: &'static crate::str::CStr;
+
+ /// The module's `THIS_MODULE` pointer.
+ const THIS_MODULE: ThisModule;
+}
+
+/// Returns a reference to the `THIS_MODULE` of the given module type.
+#[inline]
+pub const fn this_module<M: ModuleMetadata>() -> &'static ThisModule {
+ &M::THIS_MODULE
}
/// Equivalent to `THIS_MODULE` in the C API.
diff --git a/rust/macros/module.rs b/rust/macros/module.rs
index 06c18e2075083..aa9a618d5d19e 100644
--- a/rust/macros/module.rs
+++ b/rust/macros/module.rs
@@ -519,6 +519,22 @@ pub(crate) fn module(info: ModuleInfo) -> Result<TokenStream> {
impl ::kernel::ModuleMetadata for #type_ {
const NAME: &'static ::kernel::str::CStr = #name_cstr;
+
+ #[cfg(MODULE)]
+ const THIS_MODULE: ::kernel::ThisModule = {
+ extern "C" {
+ static __this_module: ::kernel::types::Opaque<::kernel::bindings::module>;
+ }
+
+ // SAFETY: `__this_module` is constructed by the kernel at load time
+ // and lives until the module is unloaded.
+ unsafe { ::kernel::ThisModule::from_ptr(__this_module.get()) }
+ };
+
+ #[cfg(not(MODULE))]
+ const THIS_MODULE: ::kernel::ThisModule = unsafe {
+ ::kernel::ThisModule::from_ptr(::core::ptr::null_mut())
+ };
}
// Double nested modules, since then nobody can access the public items inside.
@@ -616,7 +632,7 @@ pub extern "C" fn #ident_exit() {
/// This function must only be called once.
unsafe fn __init() -> ::kernel::ffi::c_int {
let initer = <super::super::LocalModule as ::kernel::InPlaceModule>::init(
- &super::super::THIS_MODULE
+ ::kernel::module::this_module::<super::super::LocalModule>()
);
// SAFETY: No data race, since `__MOD` can only be accessed by this module
// and there only `__init` and `__exit` access it. These functions are only
--
2.43.0
^ permalink raw reply related
* [RFC PATCH net 0/2] net/ncsi: tighten RCU-list lifetime handling
From: Runyu Xiao @ 2026-06-27 9:22 UTC (permalink / raw)
To: sam, fercerpav
Cc: davem, edumazet, kuba, pabeni, horms, netdev, linux-kernel,
runyu.xiao, jianhao.xu
Hi,
This small RFC series addresses two NCSI RCU-list lifetime issues.
The candidates were found by our static analysis tool and then manually
reviewed against the current tree. CONFIG_PROVE_RCU_LIST was used as
target-matched triage evidence; the RFC patches below are based on the
source-level lifetime review rather than on the dynamic warning alone.
Patch 1 defers freeing VLAN filter entries removed with list_del_rcu().
The NCSI configuration workqueue is a real RCU reader of the same list
in set_one_vid(), so RTNL serialization of VLAN add/delete callbacks is
not enough to protect that reader.
Patch 2 moves the existing NCSI device workqueue shutdown before
package/channel teardown and clears channel_queue while the channel
objects are still alive. Current trees already disable the work item
before freeing ndp itself, but still free channels before
disable_work_sync(). NCSI work can walk the package/channel lists and
channel_queue during that window.
I am sending this as RFC because I have not tested it on NCSI hardware,
and maintainers should confirm whether additional request timer
cancellation or RCU-delayed package/channel release is desirable for the
unregister path.
The patches intentionally do not claim a standalone exploitable UAF. The
evidence is a source-level lifetime risk plus target-matched
CONFIG_PROVE_RCU_LIST warnings for the affected NCSI helpers.
Runyu Xiao (2):
net/ncsi: defer freeing VLAN filter entries after RCU removal
net/ncsi: stop device work before freeing channels
net/ncsi/internal.h | 1 +
net/ncsi/ncsi-manage.c | 11 +++++++++--
2 files changed, 10 insertions(+), 2 deletions(-)
--
2.34.1
^ permalink raw reply
* [RFC PATCH net 2/2] net/ncsi: stop device work before freeing channels
From: Runyu Xiao @ 2026-06-27 9:22 UTC (permalink / raw)
To: sam, fercerpav
Cc: davem, edumazet, kuba, pabeni, horms, netdev, linux-kernel,
runyu.xiao, jianhao.xu
In-Reply-To: <20260627092214.373480-1-runyu.xiao@seu.edu.cn>
ncsi_unregister_dev() tears down packages and channels before it
disables the NCSI device work item. Current trees already stop the work
before freeing ndp itself, but a teardown window remains where a running
or already scheduled ncsi_dev_work() can still reach package/channel
readers after ncsi_remove_package() has freed the underlying objects.
One visible path is the configuration flow through ncsi_channel_is_tx(),
which walks package channels and ndp->channel_queue while choosing a
transmit channel. Channel queue entries are struct ncsi_channel objects
owned by the package channel lists, and ncsi_remove_channel() frees
those objects during package removal.
Disable the device work before package/channel teardown. Also clear any
remaining channel_queue links while the channel objects are still alive,
so no stale queue membership is carried into teardown.
This was found by our static analysis tool and then manually reviewed
against the current tree. CONFIG_PROVE_RCU_LIST was used as
target-matched triage evidence; the teardown change is based on the
source-level workqueue and channel lifetime review rather than on the
dynamic warning alone.
This is deliberately limited to the teardown ordering problem. It does
not change the normal package/channel RCU-list update model.
Signed-off-by: Runyu Xiao <runyu.xiao@seu.edu.cn>
---
net/ncsi/ncsi-manage.c | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/net/ncsi/ncsi-manage.c b/net/ncsi/ncsi-manage.c
index 5316eadd8ce4..289974dff0c1 100644
--- a/net/ncsi/ncsi-manage.c
+++ b/net/ncsi/ncsi-manage.c
@@ -1956,10 +1956,18 @@ void ncsi_unregister_dev(struct ncsi_dev *nd)
{
struct ncsi_dev_priv *ndp = TO_NCSI_DEV_PRIV(nd);
struct ncsi_package *np, *tmp;
+ struct ncsi_channel *nc, *ntmp;
unsigned long flags;
dev_remove_pack(&ndp->ptype);
+ disable_work_sync(&ndp->work);
+
+ spin_lock_irqsave(&ndp->lock, flags);
+ list_for_each_entry_safe(nc, ntmp, &ndp->channel_queue, link)
+ list_del_init(&nc->link);
+ spin_unlock_irqrestore(&ndp->lock, flags);
+
list_for_each_entry_safe(np, tmp, &ndp->packages, node)
ncsi_remove_package(np);
@@ -1967,7 +1975,6 @@ void ncsi_unregister_dev(struct ncsi_dev *nd)
list_del_rcu(&ndp->node);
spin_unlock_irqrestore(&ncsi_dev_lock, flags);
- disable_work_sync(&ndp->work);
kfree(ndp);
}
--
2.34.1
^ permalink raw reply related
* [RFC PATCH net 1/2] net/ncsi: defer freeing VLAN filter entries after RCU removal
From: Runyu Xiao @ 2026-06-27 9:22 UTC (permalink / raw)
To: sam, fercerpav
Cc: davem, edumazet, kuba, pabeni, horms, netdev, linux-kernel,
runyu.xiao, jianhao.xu
In-Reply-To: <20260627092214.373480-1-runyu.xiao@seu.edu.cn>
NCSI keeps VLAN filter entries on ndp->vlan_vids and updates the list
with RCU primitives. The configuration workqueue reads the list in
set_one_vid() under rcu_read_lock(), then dereferences vlan->vid while
constructing a Set VLAN Filter command.
ncsi_vlan_rx_kill_vid() removes matching entries with list_del_rcu(),
but it frees the object immediately with kfree(). VLAN add/delete
callbacks are serialized by RTNL, but RTNL does not serialize the NCSI
configuration workqueue reader. A reader can therefore keep a pointer to
a struct vlan_vid across list_del_rcu() and race with the immediate
free.
Give struct vlan_vid an rcu_head and release removed entries with
kfree_rcu(). This keeps the existing list structure and makes the
list_del_rcu() lifetime contract match the real set_one_vid() reader.
This was found by our static analysis tool and then manually reviewed
against the current tree. CONFIG_PROVE_RCU_LIST was used as
target-matched triage evidence; the lifetime change is based on the
matching source-level reader and updater paths rather than on the
dynamic warning alone.
Signed-off-by: Runyu Xiao <runyu.xiao@seu.edu.cn>
---
net/ncsi/internal.h | 1 +
net/ncsi/ncsi-manage.c | 2 +-
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/net/ncsi/internal.h b/net/ncsi/internal.h
index adee6dcabdc3..d9f0eadc7a24 100644
--- a/net/ncsi/internal.h
+++ b/net/ncsi/internal.h
@@ -310,6 +310,7 @@ enum {
struct vlan_vid {
struct list_head list;
+ struct rcu_head rcu;
__be16 proto;
u16 vid;
};
diff --git a/net/ncsi/ncsi-manage.c b/net/ncsi/ncsi-manage.c
index 446e4e3b9553..5316eadd8ce4 100644
--- a/net/ncsi/ncsi-manage.c
+++ b/net/ncsi/ncsi-manage.c
@@ -1737,7 +1737,7 @@ int ncsi_vlan_rx_kill_vid(struct net_device *dev, __be16 proto, u16 vid)
netdev_dbg(dev, "NCSI: vid %u found, removing\n", vid);
list_del_rcu(&vlan->list);
found = true;
- kfree(vlan);
+ kfree_rcu(vlan, rcu);
}
if (!found) {
--
2.34.1
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox