Export devres_node_init() and devres_node_add() through base.h, such
that we can access is from the Rust devres code.
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
drivers/base/base.h | 3 +++
drivers/base/devres.c | 10 ++++++----
2 files changed, 9 insertions(+), 4 deletions(-)
diff --git a/drivers/base/base.h b/drivers/base/base.h
index 5e0e7eefa405..0ad52a847bd0 100644
--- a/drivers/base/base.h
+++ b/drivers/base/base.h
@@ -234,6 +234,9 @@ struct devres_node {
size_t size;
};
+void devres_node_init(struct devres_node *node, dr_node_release_t release,
+ dr_node_free_t free_node);
+void devres_node_add(struct device *dev, struct devres_node *node);
void devres_for_each_res(struct device *dev, dr_release_t release,
dr_match_t match, void *match_data,
void (*fn)(struct device *, void *, void *),
diff --git a/drivers/base/devres.c b/drivers/base/devres.c
index 7c9ef6fc6827..51d079acc9b3 100644
--- a/drivers/base/devres.c
+++ b/drivers/base/devres.c
@@ -36,14 +36,15 @@ struct devres_group {
/* -- 8 pointers */
};
-static void devres_node_init(struct devres_node *node,
- dr_node_release_t release,
- dr_node_free_t free_node)
+void devres_node_init(struct devres_node *node,
+ dr_node_release_t release,
+ dr_node_free_t free_node)
{
INIT_LIST_HEAD(&node->entry);
node->release = release;
node->free_node = free_node;
}
+EXPORT_SYMBOL_GPL(devres_node_init);
static inline void free_node(struct devres_node *node)
{
@@ -256,12 +257,13 @@ void devres_free(void *res)
}
EXPORT_SYMBOL_GPL(devres_free);
-static void devres_node_add(struct device *dev, struct devres_node *node)
+void devres_node_add(struct device *dev, struct devres_node *node)
{
guard(spinlock_irqsave)(&dev->devres_lock);
add_dr(dev, node);
}
+EXPORT_SYMBOL_GPL(devres_node_add);
/**
* devres_add - Register device resource
--
2.52.0
On Thu Feb 5, 2026 at 11:31 PM CET, Danilo Krummrich wrote: > +EXPORT_SYMBOL_GPL(devres_node_init); I actually intended to use a Rust helper instead of exporting those symbols directly, but forgot to do it eventually.
On Fri, Feb 06, 2026 at 11:43:48AM +0100, Danilo Krummrich wrote: > On Thu Feb 5, 2026 at 11:31 PM CET, Danilo Krummrich wrote: > > +EXPORT_SYMBOL_GPL(devres_node_init); > > I actually intended to use a Rust helper instead of exporting those symbols > directly, but forgot to do it eventually. > I don't understand, does that mean we do not need to export these? Shouldn't the rust bindings just export these symbols are rust exports, and the C exports are not needed? We "only" want these symbols to go to the rust binding, not to any module at all, which is what this patch series does, and is probably not a good idea. thanks, greg k-h
On Fri Feb 6, 2026 at 12:04 PM CET, Greg KH wrote:
> On Fri, Feb 06, 2026 at 11:43:48AM +0100, Danilo Krummrich wrote:
>> On Thu Feb 5, 2026 at 11:31 PM CET, Danilo Krummrich wrote:
>> > +EXPORT_SYMBOL_GPL(devres_node_init);
>>
>> I actually intended to use a Rust helper instead of exporting those symbols
>> directly, but forgot to do it eventually.
>>
>
> I don't understand, does that mean we do not need to export these?
> Shouldn't the rust bindings just export these symbols are rust exports,
> and the C exports are not needed?
>
> We "only" want these symbols to go to the rust binding, not to any
> module at all, which is what this patch series does, and is probably not
> a good idea.
Correct, I just forgot to replace the exports with a Rust helper.
The Rust compiler might inline some of the core code into modules (e.g. due to
generics), thus requiring an export.
But, instead of adding
EXPORT_SYMBOL_GPL(devres_node_init)
in drivers/base/devres.c, I actually intended to create a Rust helper in
rust/helpers/devres.c:
__rust_helper void rust_helper_devres_node_init(struct devres_node *node,
dr_node_release_t release,
dr_node_free_t free_node)
{
devres_nod_init(node, release, free_node);
}
This will automatically create:
EXPORT_SYMBOL_RUST_GPL(rust_helper_devres_node_init)
behind the scenes.
On Fri, Feb 6, 2026 at 12:32 PM Danilo Krummrich <dakr@kernel.org> wrote:
>
> On Fri Feb 6, 2026 at 12:04 PM CET, Greg KH wrote:
> > On Fri, Feb 06, 2026 at 11:43:48AM +0100, Danilo Krummrich wrote:
> >> On Thu Feb 5, 2026 at 11:31 PM CET, Danilo Krummrich wrote:
> >> > +EXPORT_SYMBOL_GPL(devres_node_init);
> >>
> >> I actually intended to use a Rust helper instead of exporting those symbols
> >> directly, but forgot to do it eventually.
> >>
> >
> > I don't understand, does that mean we do not need to export these?
> > Shouldn't the rust bindings just export these symbols are rust exports,
> > and the C exports are not needed?
> >
> > We "only" want these symbols to go to the rust binding, not to any
> > module at all, which is what this patch series does, and is probably not
> > a good idea.
>
> Correct, I just forgot to replace the exports with a Rust helper.
>
> The Rust compiler might inline some of the core code into modules (e.g. due to
> generics), thus requiring an export.
>
> But, instead of adding
>
> EXPORT_SYMBOL_GPL(devres_node_init)
>
> in drivers/base/devres.c, I actually intended to create a Rust helper in
> rust/helpers/devres.c:
>
> __rust_helper void rust_helper_devres_node_init(struct devres_node *node,
> dr_node_release_t release,
> dr_node_free_t free_node)
> {
> devres_nod_init(node, release, free_node);
> }
>
> This will automatically create:
>
> EXPORT_SYMBOL_RUST_GPL(rust_helper_devres_node_init)
>
> behind the scenes.
That doesn't work if this option is enabled:
https://lore.kernel.org/all/20260203-inline-helpers-v2-0-beb8547a03c9@google.com/
then the helper is linked into the module, so it still has a direct
call to devres_nod_init.
Alice
On Fri, Feb 06, 2026 at 12:34:01PM +0100, Alice Ryhl wrote:
> On Fri, Feb 6, 2026 at 12:32 PM Danilo Krummrich <dakr@kernel.org> wrote:
> >
> > On Fri Feb 6, 2026 at 12:04 PM CET, Greg KH wrote:
> > > On Fri, Feb 06, 2026 at 11:43:48AM +0100, Danilo Krummrich wrote:
> > >> On Thu Feb 5, 2026 at 11:31 PM CET, Danilo Krummrich wrote:
> > >> > +EXPORT_SYMBOL_GPL(devres_node_init);
> > >>
> > >> I actually intended to use a Rust helper instead of exporting those symbols
> > >> directly, but forgot to do it eventually.
> > >>
> > >
> > > I don't understand, does that mean we do not need to export these?
> > > Shouldn't the rust bindings just export these symbols are rust exports,
> > > and the C exports are not needed?
> > >
> > > We "only" want these symbols to go to the rust binding, not to any
> > > module at all, which is what this patch series does, and is probably not
> > > a good idea.
> >
> > Correct, I just forgot to replace the exports with a Rust helper.
> >
> > The Rust compiler might inline some of the core code into modules (e.g. due to
> > generics), thus requiring an export.
> >
> > But, instead of adding
> >
> > EXPORT_SYMBOL_GPL(devres_node_init)
> >
> > in drivers/base/devres.c, I actually intended to create a Rust helper in
> > rust/helpers/devres.c:
> >
> > __rust_helper void rust_helper_devres_node_init(struct devres_node *node,
> > dr_node_release_t release,
> > dr_node_free_t free_node)
> > {
> > devres_nod_init(node, release, free_node);
> > }
> >
> > This will automatically create:
> >
> > EXPORT_SYMBOL_RUST_GPL(rust_helper_devres_node_init)
> >
> > behind the scenes.
>
> That doesn't work if this option is enabled:
> https://lore.kernel.org/all/20260203-inline-helpers-v2-0-beb8547a03c9@google.com/
>
> then the helper is linked into the module, so it still has a direct
> call to devres_nod_init.
That's fine, because the rust driver core code should also be built into
the kernel, not as a module, right?
thanks,
greg k-h
On Fri Feb 6, 2026 at 1:34 PM CET, Greg KH wrote: > That's fine, because the rust driver core code should also be built into > the kernel, not as a module, right? Yes, but the Rust compiler may still choose to put some of the core code into the module. Especially with generic types and functions the Rust compiler may move some the generated code for a certain type instance into the module that instanciates the type. For instance, even though rust/kernel/devres.rs is *always* built-in, we get the following error when devres_node_init() is not exported when the users of this built-in code are built as module. ERROR: modpost: "devres_node_init" [drivers/pwm/pwm_th1520.ko] undefined! ERROR: modpost: "devres_node_init" [drivers/gpu/drm/tyr/tyr.ko] undefined! ERROR: modpost: "devres_node_init" [drivers/gpu/nova-core/nova_core.ko] undefined! ERROR: modpost: "devres_node_init" [samples/rust/rust_dma.ko] undefined! ERROR: modpost: "devres_node_init" [samples/rust/rust_driver_pci.ko] undefined! ERROR: modpost: "devres_node_init" [samples/rust/rust_driver_auxiliary.ko] undefined! make[2]: *** [scripts/Makefile.modpost:147: Module.symvers] Error 1 However, sprinkling "raw" EXPORT_SYMBOL_GPL() due to that is not great at all. Hence, we could do something like in [1] instead. I don't know if there are other options that may be better though. [1] https://lore.kernel.org/all/DG7UR3WWZB4V.2MYMJJH1VDHH@kernel.org/
On Fri, Feb 06, 2026 at 02:16:05PM +0100, Danilo Krummrich wrote: > On Fri Feb 6, 2026 at 1:34 PM CET, Greg KH wrote: > > That's fine, because the rust driver core code should also be built into > > the kernel, not as a module, right? > > Yes, but the Rust compiler may still choose to put some of the core code into > the module. What exactly do you mean by "the module"? > Especially with generic types and functions the Rust compiler may move some the > generated code for a certain type instance into the module that instanciates the > type. Ah, that's a mess. why? The code lives in the .rs file in the kernel core, right? > For instance, even though rust/kernel/devres.rs is *always* built-in, we get the > following error when devres_node_init() is not exported when the users of this > built-in code are built as module. > > ERROR: modpost: "devres_node_init" [drivers/pwm/pwm_th1520.ko] undefined! > ERROR: modpost: "devres_node_init" [drivers/gpu/drm/tyr/tyr.ko] undefined! > ERROR: modpost: "devres_node_init" [drivers/gpu/nova-core/nova_core.ko] undefined! > ERROR: modpost: "devres_node_init" [samples/rust/rust_dma.ko] undefined! > ERROR: modpost: "devres_node_init" [samples/rust/rust_driver_pci.ko] undefined! > ERROR: modpost: "devres_node_init" [samples/rust/rust_driver_auxiliary.ko] undefined! > make[2]: *** [scripts/Makefile.modpost:147: Module.symvers] Error 1 This feels like a compiler bug, how is the compiler reaching into devres.rs and sucking out code to put into the module? Doesn't the build/link boundry stay at the .rs boundry? > However, sprinkling "raw" EXPORT_SYMBOL_GPL() due to that is not great at all. > Hence, we could do something like in [1] instead. I don't know if there are > other options that may be better though. > > [1] https://lore.kernel.org/all/DG7UR3WWZB4V.2MYMJJH1VDHH@kernel.org/ That's a start, but still messy. There's no compiler options to prevent this "lifting" of the code out of devres.rs? If not, this is not going to be the only problem that drivers run into like this in the future. thanks, greg k-h
On Fri, Feb 06, 2026 at 02:22:42PM +0100, Greg KH wrote:
> On Fri, Feb 06, 2026 at 02:16:05PM +0100, Danilo Krummrich wrote:
> > On Fri Feb 6, 2026 at 1:34 PM CET, Greg KH wrote:
> > > That's fine, because the rust driver core code should also be built into
> > > the kernel, not as a module, right?
> >
> > Yes, but the Rust compiler may still choose to put some of the core code into
> > the module.
>
> What exactly do you mean by "the module"?
>
> > Especially with generic types and functions the Rust compiler may move some the
> > generated code for a certain type instance into the module that instanciates the
> > type.
>
> Ah, that's a mess. why? The code lives in the .rs file in the kernel
> core, right?
It might still be inlined into downstream compilation units. Rust has no
equivalent to 'static inline' function in a header file, after all.
> > For instance, even though rust/kernel/devres.rs is *always* built-in, we get the
> > following error when devres_node_init() is not exported when the users of this
> > built-in code are built as module.
> >
> > ERROR: modpost: "devres_node_init" [drivers/pwm/pwm_th1520.ko] undefined!
> > ERROR: modpost: "devres_node_init" [drivers/gpu/drm/tyr/tyr.ko] undefined!
> > ERROR: modpost: "devres_node_init" [drivers/gpu/nova-core/nova_core.ko] undefined!
> > ERROR: modpost: "devres_node_init" [samples/rust/rust_dma.ko] undefined!
> > ERROR: modpost: "devres_node_init" [samples/rust/rust_driver_pci.ko] undefined!
> > ERROR: modpost: "devres_node_init" [samples/rust/rust_driver_auxiliary.ko] undefined!
> > make[2]: *** [scripts/Makefile.modpost:147: Module.symvers] Error 1
>
> This feels like a compiler bug, how is the compiler reaching into
> devres.rs and sucking out code to put into the module? Doesn't the
> build/link boundry stay at the .rs boundry?
It's quite intentional.
It used to be the case that only functions marked #[inline] could be
inlined like this, but it was changed so that small functions without
any marker are also eligible for inlining. Now you need #[inline(never)]
to ensure it does not happen.
Note that this analysis only applies to non-generic code. If you call
devres_node_init() from within this function:
impl Devres<T> {
fn new() -> Self {
... devres_node_init() ...
}
}
then since `new()` is generic over T, it is duplicated for each type T
it is used with (via monomorphisation, like C++ templates). So the
actual `new` function might be instantiated in the crate that uses
Devres<MyDriverType>, and in this case it ends up in the module even
with #[inline(never)].
So you'd need a non-generic Rust function with #[inline(never)] in this
case, and have Devres::<T>::new() call that function.
> > However, sprinkling "raw" EXPORT_SYMBOL_GPL() due to that is not great at all.
> > Hence, we could do something like in [1] instead. I don't know if there are
> > other options that may be better though.
> >
> > [1] https://lore.kernel.org/all/DG7UR3WWZB4V.2MYMJJH1VDHH@kernel.org/
>
> That's a start, but still messy. There's no compiler options to prevent
> this "lifting" of the code out of devres.rs? If not, this is not going
> to be the only problem that drivers run into like this in the future.
Because of how monomorphisation, as-is the code actually lives in the
module to begin with.
And not lifting out code causes issues with super small wrapper
functions around C functions too.
Alice
On Fri Feb 6, 2026 at 2:33 PM CET, Alice Ryhl wrote:
> So you'd need a non-generic Rust function with #[inline(never)] in this case,
> and have Devres::<T>::new() call that function.
Then we should probably figure out how we can teach bindgen to apply
#[inline(never)] for functions that are not exported. Because I don't think we
always want to write such wrappers by hand.
#[inline(never)]
#[allow(clippy::missing_safety_doc)]
unsafe fn devres_node_init(
node: *mut bindings::devres_node,
release: bindings::dr_node_release_t,
free: bindings::dr_node_free_t,
) {
// SAFETY: `devres_node_init()` inherits the safety requirements of
// `bindings::devres_node_init()`.
unsafe { bindings::devres_node_init(node, release, free) };
}
(I could probably abstract it on a higher level, but it would still create quite
some (unsafe) churn.)
On Fri, Feb 06, 2026 at 01:33:53PM +0000, Alice Ryhl wrote:
> On Fri, Feb 06, 2026 at 02:22:42PM +0100, Greg KH wrote:
> > On Fri, Feb 06, 2026 at 02:16:05PM +0100, Danilo Krummrich wrote:
> > > On Fri Feb 6, 2026 at 1:34 PM CET, Greg KH wrote:
> > > > That's fine, because the rust driver core code should also be built into
> > > > the kernel, not as a module, right?
> > >
> > > Yes, but the Rust compiler may still choose to put some of the core code into
> > > the module.
> >
> > What exactly do you mean by "the module"?
> >
> > > Especially with generic types and functions the Rust compiler may move some the
> > > generated code for a certain type instance into the module that instanciates the
> > > type.
> >
> > Ah, that's a mess. why? The code lives in the .rs file in the kernel
> > core, right?
>
> It might still be inlined into downstream compilation units. Rust has no
> equivalent to 'static inline' function in a header file, after all.
>
> > > For instance, even though rust/kernel/devres.rs is *always* built-in, we get the
> > > following error when devres_node_init() is not exported when the users of this
> > > built-in code are built as module.
> > >
> > > ERROR: modpost: "devres_node_init" [drivers/pwm/pwm_th1520.ko] undefined!
> > > ERROR: modpost: "devres_node_init" [drivers/gpu/drm/tyr/tyr.ko] undefined!
> > > ERROR: modpost: "devres_node_init" [drivers/gpu/nova-core/nova_core.ko] undefined!
> > > ERROR: modpost: "devres_node_init" [samples/rust/rust_dma.ko] undefined!
> > > ERROR: modpost: "devres_node_init" [samples/rust/rust_driver_pci.ko] undefined!
> > > ERROR: modpost: "devres_node_init" [samples/rust/rust_driver_auxiliary.ko] undefined!
> > > make[2]: *** [scripts/Makefile.modpost:147: Module.symvers] Error 1
> >
> > This feels like a compiler bug, how is the compiler reaching into
> > devres.rs and sucking out code to put into the module? Doesn't the
> > build/link boundry stay at the .rs boundry?
>
> It's quite intentional.
>
> It used to be the case that only functions marked #[inline] could be
> inlined like this, but it was changed so that small functions without
> any marker are also eligible for inlining. Now you need #[inline(never)]
> to ensure it does not happen.
>
> Note that this analysis only applies to non-generic code. If you call
> devres_node_init() from within this function:
>
> impl Devres<T> {
> fn new() -> Self {
> ... devres_node_init() ...
> }
> }
>
> then since `new()` is generic over T, it is duplicated for each type T
> it is used with (via monomorphisation, like C++ templates). So the
> actual `new` function might be instantiated in the crate that uses
> Devres<MyDriverType>, and in this case it ends up in the module even
> with #[inline(never)].
>
> So you'd need a non-generic Rust function with #[inline(never)] in this
> case, and have Devres::<T>::new() call that function.
>
> > > However, sprinkling "raw" EXPORT_SYMBOL_GPL() due to that is not great at all.
> > > Hence, we could do something like in [1] instead. I don't know if there are
> > > other options that may be better though.
> > >
> > > [1] https://lore.kernel.org/all/DG7UR3WWZB4V.2MYMJJH1VDHH@kernel.org/
> >
> > That's a start, but still messy. There's no compiler options to prevent
> > this "lifting" of the code out of devres.rs? If not, this is not going
> > to be the only problem that drivers run into like this in the future.
>
> Because of how monomorphisation, as-is the code actually lives in the
> module to begin with.
Ok, but again, that is going to cause all sorts of "the symbol is
undefined" type of problems going forward as a developers just "assumes"
that the place where the symbol is exported will actually have the
symbol exported from it, not that this place will be copied inline into
somewhere else.
Think about the interaction between module symbol namespaces here. This
isn't going to scale, and will trip people up and cause us to be forced
to export way more than we really want to (like this patch series shows,
I don't want to export any of these...)
thanks,
greg k-h
On Fri, Feb 06, 2026 at 02:55:50PM +0100, Greg KH wrote: > On Fri, Feb 06, 2026 at 01:33:53PM +0000, Alice Ryhl wrote: > > On Fri, Feb 06, 2026 at 02:22:42PM +0100, Greg KH wrote: > > > On Fri, Feb 06, 2026 at 02:16:05PM +0100, Danilo Krummrich wrote: > > > > However, sprinkling "raw" EXPORT_SYMBOL_GPL() due to that is not great at all. > > > > Hence, we could do something like in [1] instead. I don't know if there are > > > > other options that may be better though. > > > > > > > > [1] https://lore.kernel.org/all/DG7UR3WWZB4V.2MYMJJH1VDHH@kernel.org/ > > > > > > That's a start, but still messy. There's no compiler options to prevent > > > this "lifting" of the code out of devres.rs? If not, this is not going > > > to be the only problem that drivers run into like this in the future. > > > > Because of how monomorphisation, as-is the code actually lives in the > > module to begin with. > > Ok, but again, that is going to cause all sorts of "the symbol is > undefined" type of problems going forward as a developers just "assumes" > that the place where the symbol is exported will actually have the > symbol exported from it, not that this place will be copied inline into > somewhere else. > > Think about the interaction between module symbol namespaces here. This > isn't going to scale, and will trip people up and cause us to be forced > to export way more than we really want to (like this patch series shows, > I don't want to export any of these...) Hmm. I don't know how to reconcile namespaces operating on symbols with monomorphisation. We should probably think about what to do about ... :) Alice
On Fri, Feb 06, 2026 at 03:09:09PM +0000, Alice Ryhl wrote: > On Fri, Feb 06, 2026 at 02:55:50PM +0100, Greg KH wrote: > > On Fri, Feb 06, 2026 at 01:33:53PM +0000, Alice Ryhl wrote: > > > On Fri, Feb 06, 2026 at 02:22:42PM +0100, Greg KH wrote: > > > > On Fri, Feb 06, 2026 at 02:16:05PM +0100, Danilo Krummrich wrote: > > > > > However, sprinkling "raw" EXPORT_SYMBOL_GPL() due to that is not great at all. > > > > > Hence, we could do something like in [1] instead. I don't know if there are > > > > > other options that may be better though. > > > > > > > > > > [1] https://lore.kernel.org/all/DG7UR3WWZB4V.2MYMJJH1VDHH@kernel.org/ > > > > > > > > That's a start, but still messy. There's no compiler options to prevent > > > > this "lifting" of the code out of devres.rs? If not, this is not going > > > > to be the only problem that drivers run into like this in the future. > > > > > > Because of how monomorphisation, as-is the code actually lives in the > > > module to begin with. > > > > Ok, but again, that is going to cause all sorts of "the symbol is > > undefined" type of problems going forward as a developers just "assumes" > > that the place where the symbol is exported will actually have the > > symbol exported from it, not that this place will be copied inline into > > somewhere else. > > > > Think about the interaction between module symbol namespaces here. This > > isn't going to scale, and will trip people up and cause us to be forced > > to export way more than we really want to (like this patch series shows, > > I don't want to export any of these...) > > Hmm. I don't know how to reconcile namespaces operating on symbols with > monomorphisation. > > We should probably think about what to do about ... :) Matthew Maurer pointed out to me that behavior-wise exporting and namespaces behave almost exactly the same as how Rust handles unstable language features. In the Rust standard library, all functions are marked with one of: #[unstable(feature = "feature_name_here")] #[stable] when you use a #[stable] method, all is good. But if you use an unstable method, then that's a compiler error unless you have #![feature(feature_name_here)] at the crate root. This seems equivalent to our situation here, if we consider: * EXPORT_SYMBOL_GPL symbols are #[stable] * un-exported symbols are #[unstable(feature = "core_kernel")] * other namespaces are #[unstable(feature = "namespace_name_here")] Of course the way this is enforced is through an entirely different mechanism than symbol exports. Alice
On Fri Feb 6, 2026 at 1:55 PM GMT, Greg KH wrote:
> On Fri, Feb 06, 2026 at 01:33:53PM +0000, Alice Ryhl wrote:
>> On Fri, Feb 06, 2026 at 02:22:42PM +0100, Greg KH wrote:
>> > On Fri, Feb 06, 2026 at 02:16:05PM +0100, Danilo Krummrich wrote:
>> > > On Fri Feb 6, 2026 at 1:34 PM CET, Greg KH wrote:
>> > > > That's fine, because the rust driver core code should also be built into
>> > > > the kernel, not as a module, right?
>> > >
>> > > Yes, but the Rust compiler may still choose to put some of the core code into
>> > > the module.
>> >
>> > What exactly do you mean by "the module"?
>> >
>> > > Especially with generic types and functions the Rust compiler may move some the
>> > > generated code for a certain type instance into the module that instanciates the
>> > > type.
>> >
>> > Ah, that's a mess. why? The code lives in the .rs file in the kernel
>> > core, right?
>>
>> It might still be inlined into downstream compilation units. Rust has no
>> equivalent to 'static inline' function in a header file, after all.
>>
>> > > For instance, even though rust/kernel/devres.rs is *always* built-in, we get the
>> > > following error when devres_node_init() is not exported when the users of this
>> > > built-in code are built as module.
>> > >
>> > > ERROR: modpost: "devres_node_init" [drivers/pwm/pwm_th1520.ko] undefined!
>> > > ERROR: modpost: "devres_node_init" [drivers/gpu/drm/tyr/tyr.ko] undefined!
>> > > ERROR: modpost: "devres_node_init" [drivers/gpu/nova-core/nova_core.ko] undefined!
>> > > ERROR: modpost: "devres_node_init" [samples/rust/rust_dma.ko] undefined!
>> > > ERROR: modpost: "devres_node_init" [samples/rust/rust_driver_pci.ko] undefined!
>> > > ERROR: modpost: "devres_node_init" [samples/rust/rust_driver_auxiliary.ko] undefined!
>> > > make[2]: *** [scripts/Makefile.modpost:147: Module.symvers] Error 1
>> >
>> > This feels like a compiler bug, how is the compiler reaching into
>> > devres.rs and sucking out code to put into the module? Doesn't the
>> > build/link boundry stay at the .rs boundry?
>>
>> It's quite intentional.
>>
>> It used to be the case that only functions marked #[inline] could be
>> inlined like this, but it was changed so that small functions without
>> any marker are also eligible for inlining. Now you need #[inline(never)]
>> to ensure it does not happen.
>>
>> Note that this analysis only applies to non-generic code. If you call
>> devres_node_init() from within this function:
>>
>> impl Devres<T> {
>> fn new() -> Self {
>> ... devres_node_init() ...
>> }
>> }
>>
>> then since `new()` is generic over T, it is duplicated for each type T
>> it is used with (via monomorphisation, like C++ templates). So the
>> actual `new` function might be instantiated in the crate that uses
>> Devres<MyDriverType>, and in this case it ends up in the module even
>> with #[inline(never)].
>>
>> So you'd need a non-generic Rust function with #[inline(never)] in this
>> case, and have Devres::<T>::new() call that function.
>>
>> > > However, sprinkling "raw" EXPORT_SYMBOL_GPL() due to that is not great at all.
>> > > Hence, we could do something like in [1] instead. I don't know if there are
>> > > other options that may be better though.
>> > >
>> > > [1] https://lore.kernel.org/all/DG7UR3WWZB4V.2MYMJJH1VDHH@kernel.org/
>> >
>> > That's a start, but still messy. There's no compiler options to prevent
>> > this "lifting" of the code out of devres.rs? If not, this is not going
>> > to be the only problem that drivers run into like this in the future.
>>
>> Because of how monomorphisation, as-is the code actually lives in the
>> module to begin with.
>
> Ok, but again, that is going to cause all sorts of "the symbol is
> undefined" type of problems going forward as a developers just "assumes"
> that the place where the symbol is exported will actually have the
> symbol exported from it, not that this place will be copied inline into
> somewhere else.
Note that this won't be an issue for a Rust module to reference a symbol defiend
in Rust, because everything is EXPORT_SYMBOL_RUST_GPL'd (we use language-builtin
visibilities to control whether people should access an API or not).
For APIs intended for general usage inlined through helpers, it is also a
non-issue because everything is exported already.
The issue is only when a subsystem wants to export a non-public API for Rust
abstraction to use. So far we haven't had a need yet, Devres is the first one
that runs into this.
Best,
Gary
>
> Think about the interaction between module symbol namespaces here. This
> isn't going to scale, and will trip people up and cause us to be forced
> to export way more than we really want to (like this patch series shows,
> I don't want to export any of these...)
>
> thanks,
>
> greg k-h
On Fri, Feb 06, 2026 at 02:23:44PM +0000, Gary Guo wrote:
> On Fri Feb 6, 2026 at 1:55 PM GMT, Greg KH wrote:
> > On Fri, Feb 06, 2026 at 01:33:53PM +0000, Alice Ryhl wrote:
> >> On Fri, Feb 06, 2026 at 02:22:42PM +0100, Greg KH wrote:
> >> > On Fri, Feb 06, 2026 at 02:16:05PM +0100, Danilo Krummrich wrote:
> >> > > On Fri Feb 6, 2026 at 1:34 PM CET, Greg KH wrote:
> >> > > > That's fine, because the rust driver core code should also be built into
> >> > > > the kernel, not as a module, right?
> >> > >
> >> > > Yes, but the Rust compiler may still choose to put some of the core code into
> >> > > the module.
> >> >
> >> > What exactly do you mean by "the module"?
> >> >
> >> > > Especially with generic types and functions the Rust compiler may move some the
> >> > > generated code for a certain type instance into the module that instanciates the
> >> > > type.
> >> >
> >> > Ah, that's a mess. why? The code lives in the .rs file in the kernel
> >> > core, right?
> >>
> >> It might still be inlined into downstream compilation units. Rust has no
> >> equivalent to 'static inline' function in a header file, after all.
> >>
> >> > > For instance, even though rust/kernel/devres.rs is *always* built-in, we get the
> >> > > following error when devres_node_init() is not exported when the users of this
> >> > > built-in code are built as module.
> >> > >
> >> > > ERROR: modpost: "devres_node_init" [drivers/pwm/pwm_th1520.ko] undefined!
> >> > > ERROR: modpost: "devres_node_init" [drivers/gpu/drm/tyr/tyr.ko] undefined!
> >> > > ERROR: modpost: "devres_node_init" [drivers/gpu/nova-core/nova_core.ko] undefined!
> >> > > ERROR: modpost: "devres_node_init" [samples/rust/rust_dma.ko] undefined!
> >> > > ERROR: modpost: "devres_node_init" [samples/rust/rust_driver_pci.ko] undefined!
> >> > > ERROR: modpost: "devres_node_init" [samples/rust/rust_driver_auxiliary.ko] undefined!
> >> > > make[2]: *** [scripts/Makefile.modpost:147: Module.symvers] Error 1
> >> >
> >> > This feels like a compiler bug, how is the compiler reaching into
> >> > devres.rs and sucking out code to put into the module? Doesn't the
> >> > build/link boundry stay at the .rs boundry?
> >>
> >> It's quite intentional.
> >>
> >> It used to be the case that only functions marked #[inline] could be
> >> inlined like this, but it was changed so that small functions without
> >> any marker are also eligible for inlining. Now you need #[inline(never)]
> >> to ensure it does not happen.
> >>
> >> Note that this analysis only applies to non-generic code. If you call
> >> devres_node_init() from within this function:
> >>
> >> impl Devres<T> {
> >> fn new() -> Self {
> >> ... devres_node_init() ...
> >> }
> >> }
> >>
> >> then since `new()` is generic over T, it is duplicated for each type T
> >> it is used with (via monomorphisation, like C++ templates). So the
> >> actual `new` function might be instantiated in the crate that uses
> >> Devres<MyDriverType>, and in this case it ends up in the module even
> >> with #[inline(never)].
> >>
> >> So you'd need a non-generic Rust function with #[inline(never)] in this
> >> case, and have Devres::<T>::new() call that function.
> >>
> >> > > However, sprinkling "raw" EXPORT_SYMBOL_GPL() due to that is not great at all.
> >> > > Hence, we could do something like in [1] instead. I don't know if there are
> >> > > other options that may be better though.
> >> > >
> >> > > [1] https://lore.kernel.org/all/DG7UR3WWZB4V.2MYMJJH1VDHH@kernel.org/
> >> >
> >> > That's a start, but still messy. There's no compiler options to prevent
> >> > this "lifting" of the code out of devres.rs? If not, this is not going
> >> > to be the only problem that drivers run into like this in the future.
> >>
> >> Because of how monomorphisation, as-is the code actually lives in the
> >> module to begin with.
> >
> > Ok, but again, that is going to cause all sorts of "the symbol is
> > undefined" type of problems going forward as a developers just "assumes"
> > that the place where the symbol is exported will actually have the
> > symbol exported from it, not that this place will be copied inline into
> > somewhere else.
>
> Note that this won't be an issue for a Rust module to reference a symbol defiend
> in Rust, because everything is EXPORT_SYMBOL_RUST_GPL'd (we use language-builtin
> visibilities to control whether people should access an API or not).
What about module symbol namespaces?
> For APIs intended for general usage inlined through helpers, it is also a
> non-issue because everything is exported already.
>
> The issue is only when a subsystem wants to export a non-public API for Rust
> abstraction to use. So far we haven't had a need yet, Devres is the first one
> that runs into this.
And this will happen more as we have more bindings from subsystem rust
code that should not be exported directly to modules/drivers to allow
them to access those symbols without going through the rust core.
Also, again, module symbol namespaces will play here, think about a
driver calling a symbol in another driver's namespace, that has within
it a call to a different namespace. That first driver would then have
to "import" that last namespace, which isn't ok.
thanks,
greg k-h
On Fri, Feb 6, 2026 at 2:16 PM Danilo Krummrich <dakr@kernel.org> wrote: > > On Fri Feb 6, 2026 at 1:34 PM CET, Greg KH wrote: > > That's fine, because the rust driver core code should also be built into > > the kernel, not as a module, right? > > Yes, but the Rust compiler may still choose to put some of the core code into > the module. > > Especially with generic types and functions the Rust compiler may move some the > generated code for a certain type instance into the module that instanciates the > type. > > For instance, even though rust/kernel/devres.rs is *always* built-in, we get the > following error when devres_node_init() is not exported when the users of this > built-in code are built as module. > > ERROR: modpost: "devres_node_init" [drivers/pwm/pwm_th1520.ko] undefined! > ERROR: modpost: "devres_node_init" [drivers/gpu/drm/tyr/tyr.ko] undefined! > ERROR: modpost: "devres_node_init" [drivers/gpu/nova-core/nova_core.ko] undefined! > ERROR: modpost: "devres_node_init" [samples/rust/rust_dma.ko] undefined! > ERROR: modpost: "devres_node_init" [samples/rust/rust_driver_pci.ko] undefined! > ERROR: modpost: "devres_node_init" [samples/rust/rust_driver_auxiliary.ko] undefined! > make[2]: *** [scripts/Makefile.modpost:147: Module.symvers] Error 1 > > However, sprinkling "raw" EXPORT_SYMBOL_GPL() due to that is not great at all. > Hence, we could do something like in [1] instead. I don't know if there are > other options that may be better though. > > [1] https://lore.kernel.org/all/DG7UR3WWZB4V.2MYMJJH1VDHH@kernel.org/ You can use #[inline(never)] to prevent this. Alice
On Fri, Feb 06, 2026 at 02:20:06PM +0100, Alice Ryhl wrote: > On Fri, Feb 6, 2026 at 2:16 PM Danilo Krummrich <dakr@kernel.org> wrote: > > > > On Fri Feb 6, 2026 at 1:34 PM CET, Greg KH wrote: > > > That's fine, because the rust driver core code should also be built into > > > the kernel, not as a module, right? > > > > Yes, but the Rust compiler may still choose to put some of the core code into > > the module. > > > > Especially with generic types and functions the Rust compiler may move some the > > generated code for a certain type instance into the module that instanciates the > > type. > > > > For instance, even though rust/kernel/devres.rs is *always* built-in, we get the > > following error when devres_node_init() is not exported when the users of this > > built-in code are built as module. > > > > ERROR: modpost: "devres_node_init" [drivers/pwm/pwm_th1520.ko] undefined! > > ERROR: modpost: "devres_node_init" [drivers/gpu/drm/tyr/tyr.ko] undefined! > > ERROR: modpost: "devres_node_init" [drivers/gpu/nova-core/nova_core.ko] undefined! > > ERROR: modpost: "devres_node_init" [samples/rust/rust_dma.ko] undefined! > > ERROR: modpost: "devres_node_init" [samples/rust/rust_driver_pci.ko] undefined! > > ERROR: modpost: "devres_node_init" [samples/rust/rust_driver_auxiliary.ko] undefined! > > make[2]: *** [scripts/Makefile.modpost:147: Module.symvers] Error 1 > > > > However, sprinkling "raw" EXPORT_SYMBOL_GPL() due to that is not great at all. > > Hence, we could do something like in [1] instead. I don't know if there are > > other options that may be better though. > > > > [1] https://lore.kernel.org/all/DG7UR3WWZB4V.2MYMJJH1VDHH@kernel.org/ > > You can use #[inline(never)] to prevent this. Ah, good!
On Fri Feb 6, 2026 at 2:25 PM CET, Greg KH wrote: > On Fri, Feb 06, 2026 at 02:20:06PM +0100, Alice Ryhl wrote: >> On Fri, Feb 6, 2026 at 2:16 PM Danilo Krummrich <dakr@kernel.org> wrote: >> > >> > On Fri Feb 6, 2026 at 1:34 PM CET, Greg KH wrote: >> > > That's fine, because the rust driver core code should also be built into >> > > the kernel, not as a module, right? >> > >> > Yes, but the Rust compiler may still choose to put some of the core code into >> > the module. >> > >> > Especially with generic types and functions the Rust compiler may move some the >> > generated code for a certain type instance into the module that instanciates the >> > type. >> > >> > For instance, even though rust/kernel/devres.rs is *always* built-in, we get the >> > following error when devres_node_init() is not exported when the users of this >> > built-in code are built as module. >> > >> > ERROR: modpost: "devres_node_init" [drivers/pwm/pwm_th1520.ko] undefined! >> > ERROR: modpost: "devres_node_init" [drivers/gpu/drm/tyr/tyr.ko] undefined! >> > ERROR: modpost: "devres_node_init" [drivers/gpu/nova-core/nova_core.ko] undefined! >> > ERROR: modpost: "devres_node_init" [samples/rust/rust_dma.ko] undefined! >> > ERROR: modpost: "devres_node_init" [samples/rust/rust_driver_pci.ko] undefined! >> > ERROR: modpost: "devres_node_init" [samples/rust/rust_driver_auxiliary.ko] undefined! >> > make[2]: *** [scripts/Makefile.modpost:147: Module.symvers] Error 1 >> > >> > However, sprinkling "raw" EXPORT_SYMBOL_GPL() due to that is not great at all. >> > Hence, we could do something like in [1] instead. I don't know if there are >> > other options that may be better though. >> > >> > [1] https://lore.kernel.org/all/DG7UR3WWZB4V.2MYMJJH1VDHH@kernel.org/ >> >> You can use #[inline(never)] to prevent this. > > Ah, good! I tried this before, but it doesn't work. It might prevent inlining, but it does not prevent the compiler to move the function into the module binary. For instance, even with #[inline(never)] on Devres::new() I see this: $ objdump -t drivers/pwm/pwm_th1520.o | grep " F " | grep Devres | grep new 0000000000000000 g F .text 00000000000001d0 _RINvMNtCsgzhNYVB7wSz_6kernel6devresINtB3_6DevresINtNtNtB5_2io3mem5IoMemKjb0_EE3newNtNtB5_5error5ErrorINtNtCs4gKlGRWyJ5S_4core6result6ResultBK_B1i_EECsjBm7MKWXKwW_10pwm_th1520
On Fri, Feb 6, 2026 at 2:33 PM Danilo Krummrich <dakr@kernel.org> wrote: > > On Fri Feb 6, 2026 at 2:25 PM CET, Greg KH wrote: > > On Fri, Feb 06, 2026 at 02:20:06PM +0100, Alice Ryhl wrote: > >> On Fri, Feb 6, 2026 at 2:16 PM Danilo Krummrich <dakr@kernel.org> wrote: > >> > > >> > On Fri Feb 6, 2026 at 1:34 PM CET, Greg KH wrote: > >> > > That's fine, because the rust driver core code should also be built into > >> > > the kernel, not as a module, right? > >> > > >> > Yes, but the Rust compiler may still choose to put some of the core code into > >> > the module. > >> > > >> > Especially with generic types and functions the Rust compiler may move some the > >> > generated code for a certain type instance into the module that instanciates the > >> > type. > >> > > >> > For instance, even though rust/kernel/devres.rs is *always* built-in, we get the > >> > following error when devres_node_init() is not exported when the users of this > >> > built-in code are built as module. > >> > > >> > ERROR: modpost: "devres_node_init" [drivers/pwm/pwm_th1520.ko] undefined! > >> > ERROR: modpost: "devres_node_init" [drivers/gpu/drm/tyr/tyr.ko] undefined! > >> > ERROR: modpost: "devres_node_init" [drivers/gpu/nova-core/nova_core.ko] undefined! > >> > ERROR: modpost: "devres_node_init" [samples/rust/rust_dma.ko] undefined! > >> > ERROR: modpost: "devres_node_init" [samples/rust/rust_driver_pci.ko] undefined! > >> > ERROR: modpost: "devres_node_init" [samples/rust/rust_driver_auxiliary.ko] undefined! > >> > make[2]: *** [scripts/Makefile.modpost:147: Module.symvers] Error 1 > >> > > >> > However, sprinkling "raw" EXPORT_SYMBOL_GPL() due to that is not great at all. > >> > Hence, we could do something like in [1] instead. I don't know if there are > >> > other options that may be better though. > >> > > >> > [1] https://lore.kernel.org/all/DG7UR3WWZB4V.2MYMJJH1VDHH@kernel.org/ > >> > >> You can use #[inline(never)] to prevent this. > > > > Ah, good! > > I tried this before, but it doesn't work. It might prevent inlining, but it does > not prevent the compiler to move the function into the module binary. > > For instance, even with #[inline(never)] on Devres::new() I see this: > > $ objdump -t drivers/pwm/pwm_th1520.o | grep " F " | grep Devres | grep new > > 0000000000000000 g F .text 00000000000001d0 _RINvMNtCsgzhNYVB7wSz_6kernel6devresINtB3_6DevresINtNtNtB5_2io3mem5IoMemKjb0_EE3newNtNtB5_5error5ErrorINtNtCs4gKlGRWyJ5S_4core6result6ResultBK_B1i_EECsjBm7MKWXKwW_10pwm_th1520 Because it's generic, see my sibling reply.
On Fri Feb 6, 2026 at 12:34 PM CET, Alice Ryhl wrote: > That doesn't work if this option is enabled: > https://lore.kernel.org/all/20260203-inline-helpers-v2-0-beb8547a03c9@google.com/ > > then the helper is linked into the module, so it still has a direct > call to devres_nod_init. That's unfortunate, maybe we can leverage MODULE_IMPORT_NS() and EXPORT_SYMBOL_NS_GPL(), i.e. #define EXPORT_SYMBOL_RUST_INTERNAL(sym) EXPORT_SYMBOL_NS_GPL(sym, "RUST_INTERNAL")
© 2016 - 2026 Red Hat, Inc.