Inline helpers into Rust without full LTO
Subsystems (9)
Alice Ryhl
reviewerv2 cover Inline helpers into Rust without full LTO
Currently the only way for Rust code to call a static inline function is to go through a helper in rust/helpers/. This introduces performance costs due to additional function calls and also clutters backtraces and flame graphs with helper symbols.
To get rid of these helper symbols, provide functionality to inline helpers into Rust using llvm-link. This option complements full LTO, by being much cheaper and avoiding incompatibility with BTF.
I ran a microbenchmark showing the benefit of this. All the benchmark does is call refcount_inc() in a loop. This was chosen since refcounting is quite hot in Binder. The results are that Rust spends 6.35 ns per call vs 5.73 ns per call in C. When enabling this option, the two languages become equally fast, and disassembly confirms the exact same machine code is used (in particular there is no call to rust_helper_refcount_inc). Benchmarking Binder also results in an improvement from this change.
This patch is complementary to:
https://lore.kernel.org/all/[email protected]/
---
Changes in v2:
- Dropped merged patches.
- Generate two copies of helpers.bc, one for built-in and one for modules.
- Remove unused llvm-as tool.
- Add $(cmd_ld_single) to avoid breaking CONFIG_LTO builds (note that
LTO does not happen over the language boundary, except for helpers).
- Improve Kconfig help message.
- Link to v1: https://lore.kernel.org/r/[email protected]
---
Gary Guo (3):
kbuild: rust: add CONFIG_RUSTC_CLANG_LLVM_COMPATIBLE
rust: helpers: #define __rust_helper
build: rust: provide an option to inline C helpers into Rust
Makefile | 3 ++- init/Kconfig | 15 +++++++++++++++ lib/Kconfig.debug | 15 +++++++++++++++ rust/Makefile | 29 +++++++++++++++++++++++++---- rust/exports.c | 5 ++++- rust/helpers/helpers.c | 29 +++++++++++++++++++++++++++++ scripts/Makefile.build | 7 ++++++- 7 files changed, 96 insertions(+), 7 deletions(-)
---
base-commit: 33a647c659ffa5bdb94abc345c8c86768ff96215
change-id: 20251202-inline-helpers-996f4db65e18
Best regards,
Signature · 1 line
Alice Ryhl <[email protected]>
Alice Ryhl
reviewerPATCH v2 1/3 kbuild: rust: add `CONFIG_RUSTC_CLANG_LLVM_COMPATIBLE`
From: Gary Guo <[email protected]>
This config detects if Rust and Clang have matching LLVM major version. All IR or bitcode operations (e.g. LTO) rely on LLVM major version to be matching, otherwise it may generate errors, or worse, miscompile silently due to change of IR semantics.
It's usually suggested to use the exact same LLVM version, but this can be difficult to guarantee. Rust's suggestion [1] is also major-version only, so I think this check is sufficient for the kernel.
--- init/Kconfig | 15 +++++++++++++++ 1 file changed, 15 insertions(+)
init/Kconfig +15 -0
@@ -82,6 +82,21 @@ config RUSTC_LLVM_VERSION int default $(rustc-llvm-version) +config RUSTC_LLVM_MAJOR_VERSION + int + default $(shell,expr $(rustc-llvm-version) / 10000) + +config RUSTC_CLANG_LLVM_COMPATIBLE + bool + default y if CC_IS_CLANG && RUSTC_LLVM_MAJOR_VERSION = $(shell,expr $(cc-version) / 10000) + help + This indicates whether Rust and Clang use LLVM of the same major + version. + + Operations involving handling LLVM IR or bitcode (e.g. cross-language + LTO) requires the same LLVM major version to work properly. For best + compatibility it is recommended that the exact same LLVM is used. + config ARCH_HAS_CC_CAN_LINK bool
Signature · 1 line
2.53.0.rc1.225.gd81095ad13-goog
Re: [PATCH v2 1/3] kbuild: rust: add `CONFIG_RUSTC_CLANG_LLVM_COMPATIBLE`
Quoted 32 lines · Alice Ryhl
From: Gary Guo <[email protected]>
This config detects if Rust and Clang have matching LLVM major version. All IR or bitcode operations (e.g. LTO) rely on LLVM major version to be matching, otherwise it may generate errors, or worse, miscompile silently due to change of IR semantics.
It's usually suggested to use the exact same LLVM version, but this can be difficult to guarantee. Rust's suggestion [1] is also major-version only, so I think this check is sufficient for the kernel.
Reviewed-by Andreas Hindborg <[email protected]>Signed-off-by Gary Guo <[email protected]>Signed-off-by Matthew Maurer <[email protected]>Signed-off-by Alice Ryhl <[email protected]>--- init/Kconfig | 15 +++++++++++++++ 1 file changed, 15 insertions(+)init/Kconfig +4
-0@@ -82,6 +82,21 @@ config RUSTC_LLVM_VERSION int default $(rustc-llvm-version) +config RUSTC_LLVM_MAJOR_VERSION + int + default $(shell,expr $(rustc-llvm-version) / 10000) +
init/Kconfig config RUSTC_LLVM_VERSION · line 98
Quoted 10 lines
init/Kconfig +10
-0+config RUSTC_CLANG_LLVM_COMPATIBLE + bool + default y if CC_IS_CLANG && RUSTC_LLVM_MAJOR_VERSION = $(shell,expr $(cc-version) / 10000) + help + This indicates whether Rust and Clang use LLVM of the same major + version. + + Operations involving handling LLVM IR or bitcode (e.g. cross-language + LTO) requires the same LLVM major version to work properly. For best + compatibility it is recommended that the exact same LLVM is used.
Is cross-language LTO something we're actually looking at doing for the kernel, or is this just stale help text given what you're using it for in this series?
Will
Alice Ryhl
reviewerRe: [PATCH v2 1/3] kbuild: rust: add `CONFIG_RUSTC_CLANG_LLVM_COMPATIBLE`
Quoted 39 lines · Will Deacon
Quoted 38 lines · Alice Ryhl
From: Gary Guo <[email protected]>
This config detects if Rust and Clang have matching LLVM major version. All IR or bitcode operations (e.g. LTO) rely on LLVM major version to be matching, otherwise it may generate errors, or worse, miscompile silently due to change of IR semantics.
It's usually suggested to use the exact same LLVM version, but this can be difficult to guarantee. Rust's suggestion [1] is also major-version only, so I think this check is sufficient for the kernel.
Reviewed-by Andreas Hindborg <[email protected]>Signed-off-by Gary Guo <[email protected]>Signed-off-by Matthew Maurer <[email protected]>Signed-off-by Alice Ryhl <[email protected]>--- init/Kconfig | 15 +++++++++++++++ 1 file changed, 15 insertions(+)init/Kconfig +10
-0@@ -82,6 +82,21 @@ config RUSTC_LLVM_VERSION int default $(rustc-llvm-version) +config RUSTC_LLVM_MAJOR_VERSION + int + default $(shell,expr $(rustc-llvm-version) / 10000) + +config RUSTC_CLANG_LLVM_COMPATIBLE + bool + default y if CC_IS_CLANG && RUSTC_LLVM_MAJOR_VERSION = $(shell,expr $(cc-version) / 10000) + help + This indicates whether Rust and Clang use LLVM of the same major + version.
Quoted 8 lines
init/Kconfig config RUSTC_LLVM_VERSION · line 98
Quoted 4 lines
init/Kconfig +4
-0+ + Operations involving handling LLVM IR or bitcode (e.g. cross-language + LTO) requires the same LLVM major version to work properly. For best + compatibility it is recommended that the exact same LLVM is used.Is cross-language LTO something we're actually looking at doing for the kernel, or is this just stale help text given what you're using it for in this series?
I would like to see CONFIG_LTO actually perform cross-language inlining, but it'd require doing something about both:
1. The issues mentioned in patch 2 of this series.
2. The issues mentioned in commit 5daa0c35a1f0 ("rust: Disallow BTF generation with Rust + LTO").
As for whether it's stale help text ... CONFIG_RUST_INLINE_HELPERS works by performing cross-language LTO too. But I can see your point that it would make sense to improve this wording given that we're not making CONFIG_LTO work now.
Alice
Re: [PATCH v2 1/3] kbuild: rust: add `CONFIG_RUSTC_CLANG_LLVM_COMPATIBLE`
In reply to Alice RyhlQuoted 35 lines · Alice Ryhl
This config detects if Rust and Clang have matching LLVM major version. All IR or bitcode operations (e.g. LTO) rely on LLVM major version to be matching, otherwise it may generate errors, or worse, miscompile silently due to change of IR semantics.
It's usually suggested to use the exact same LLVM version, but this can be difficult to guarantee. Rust's suggestion [1] is also major-version only, so I think this check is sufficient for the kernel.
Reviewed-by Andreas Hindborg <[email protected]>Signed-off-by Gary Guo <[email protected]>Signed-off-by Matthew Maurer <[email protected]>Signed-off-by Alice Ryhl <[email protected]>--- init/Kconfig | 15 +++++++++++++++ 1 file changed, 15 insertions(+)init/Kconfig +8
-0@@ -82,6 +82,21 @@ config RUSTC_LLVM_VERSION int default $(rustc-llvm-version) +config RUSTC_LLVM_MAJOR_VERSION + int + default $(shell,expr $(rustc-llvm-version) / 10000) + +config RUSTC_CLANG_LLVM_COMPATIBLE + bool + default y if CC_IS_CLANG && RUSTC_LLVM_MAJOR_VERSION = $(shell,expr $(cc-version) / 10000) + help
init/Kconfig config RUSTC_LLVM_VERSION · line 102
Quoted 10 lines
init/Kconfig +7
-0+ This indicates whether Rust and Clang use LLVM of the same major + version. + + Operations involving handling LLVM IR or bitcode (e.g. cross-language + LTO) requires the same LLVM major version to work properly. For best + compatibility it is recommended that the exact same LLVM is used. + config ARCH_HAS_CC_CAN_LINK bool
Signature · 1 line
Nicolas
Alice Ryhl
reviewerRe: [PATCH v2 1/3] kbuild: rust: add `CONFIG_RUSTC_CLANG_LLVM_COMPATIBLE`
Quoted 2 lines · Nicolas Schier
Reviewed-by Nicolas Schier <[email protected]>Tested-by Nicolas Schier <[email protected]>
Thanks!
Re: [PATCH v2 1/3] kbuild: rust: add `CONFIG_RUSTC_CLANG_LLVM_COMPATIBLE`
In reply to Alice RyhlQuoted 11 lines · Alice Ryhl
From: Gary Guo <[email protected]>
This config detects if Rust and Clang have matching LLVM major version. All IR or bitcode operations (e.g. LTO) rely on LLVM major version to be matching, otherwise it may generate errors, or worse, miscompile silently due to change of IR semantics.
It's usually suggested to use the exact same LLVM version, but this can be difficult to guarantee. Rust's suggestion [1] is also major-version only, so I think this check is sufficient for the kernel.
Quoted 5 lines
Reviewed-by Andreas Hindborg <[email protected]>Signed-off-by Gary Guo <[email protected]>Signed-off-by Matthew Maurer <[email protected]>Signed-off-by Aliee Ryhl <[email protected]>
Quoted 34 lines
--- init/Kconfig | 15 +++++++++++++++ 1 file changed, 15 insertions(+)init/Kconfig +15
-0@@ -82,6 +82,21 @@ config RUSTC_LLVM_VERSION int default $(rustc-llvm-version) +config RUSTC_LLVM_MAJOR_VERSION + int + default $(shell,expr $(rustc-llvm-version) / 10000) + +config RUSTC_CLANG_LLVM_COMPATIBLE + bool + default y if CC_IS_CLANG && RUSTC_LLVM_MAJOR_VERSION = $(shell,expr $(cc-version) / 10000) + help + This indicates whether Rust and Clang use LLVM of the same major + version. + + Operations involving handling LLVM IR or bitcode (e.g. cross-language + LTO) requires the same LLVM major version to work properly. For best + compatibility it is recommended that the exact same LLVM is used. + config ARCH_HAS_CC_CAN_LINK boolSignature · 2 lines
-- 2.53.0.rc1.225.gd81095ad13-goog
Alice Ryhl
reviewerPATCH v2 2/3 rust: helpers: #define __rust_helper
In reply to Alice RyhlFrom: Gary Guo <[email protected]>
Because of LLVM inling checks, it's generally not possible to inline a C helper into Rust code, even with LTO:
* LLVM doesn't want to inline functions compiled with -fno-delete-null-pointer-checks with code compiled without. The C CGUs all have this enabled and Rust CGUs don't. Inlining is okay since this is one of the hardening features that does not change the ABI, and we shouldn't have null pointer dereferences in these helpers.
* LLVM doesn't want to inline functions with different list of builtins. C side has -fno-builtin-wcslen; wcslen is not a Rust builtin, so they should be compatible, but LLVM does not perform inlining due to attributes mismatch.
* clang and Rust doesn't have the exact target string. Clang generates +cmov,+cx8,+fxsr but Rust doesn't enable them (in fact, Rust will complain if -Ctarget-feature=+cmov,+cx8,+fxsr is used). x86-64 always enable these features, so they are in fact the same target string, but LLVM doesn't understand this and so inlining is inhibited. This can be bypassed with --ignore-tti-inline-compatible, but this is a hidden option.
To fix this, we can add __always_inline on every helper, which skips these LLVM inlining checks. For this purpose, introduce a new __rust_helper macro that needs to be added to every helper.
Most helpers already have __rust_helper specified, but there are a few missing. The only consequence of this is that those specific helpers do not get inlined.
--- rust/helpers/helpers.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+)
rust/helpers/helpers.c +29 -0
@@ -7,7 +7,36 @@* Sorted alphabetically. */ +#include <linux/compiler_types.h> + +#ifdef __BINDGEN__ +// Omit `inline` for bindgen as it ignores inline functions. #define __rust_helper +#else +// The helper functions are all inline functions. +// +// We use `__always_inline` here to bypass LLVM inlining checks, in case the +// helpers are inlined directly into Rust CGUs. +// +// The LLVM inlining checks are false positives: +// * LLVM doesn't want to inline functions compiled with +// `-fno-delete-null-pointer-checks` with code compiled without. +// The C CGUs all have this enabled and Rust CGUs don't. Inlining is okay +// since this is one of the hardening features that does not change the ABI, +// and we shouldn't have null pointer dereferences in these helpers. +// * LLVM doesn't want to inline functions with different list of builtins. C +// side has `-fno-builtin-wcslen`; `wcslen` is not a Rust builtin, so they +// should be compatible, but LLVM does not perform inlining due to attributes +// mismatch. +// * clang and Rust doesn't have the exact target string. Clang generates +// `+cmov,+cx8,+fxsr` but Rust doesn't enable them (in fact, Rust will +// complain if `-Ctarget-feature=+cmov,+cx8,+fxsr` is used). x86-64 always +// enable these features, so they are in fact the same target string, but +// LLVM doesn't understand this and so inlining is inhibited. This can be +// bypassed with `--ignore-tti-inline-compatible`, but this is a hidden +// option. +#define __rust_helper __always_inline +#endif #include "atomic.c" #include "atomic_ext.c"
Signature · 1 line
2.53.0.rc1.225.gd81095ad13-goog
Re: [PATCH v2 2/3] rust: helpers: #define __rust_helper
Quoted 28 lines · Alice Ryhl
From: Gary Guo <[email protected]>
Because of LLVM inling checks, it's generally not possible to inline a C helper into Rust code, even with LTO:
* LLVM doesn't want to inline functions compiled with
-fno-delete-null-pointer-checkswith code compiled without. The C CGUs all have this enabled and Rust CGUs don't. Inlining is okay since this is one of the hardening features that does not change the ABI, and we shouldn't have null pointer dereferences in these helpers.* LLVM doesn't want to inline functions with different list of builtins. C side has
-fno-builtin-wcslen;wcslenis not a Rust builtin, so they should be compatible, but LLVM does not perform inlining due to attributes mismatch.* clang and Rust doesn't have the exact target string. Clang generates
+cmov,+cx8,+fxsrbut Rust doesn't enable them (in fact, Rust will complain if-Ctarget-feature=+cmov,+cx8,+fxsris used). x86-64 always enable these features, so they are in fact the same target string, but LLVM doesn't understand this and so inlining is inhibited. This can be bypassed with--ignore-tti-inline-compatible, but this is a hidden option.To fix this, we can add
__always_inlineon every helper, which skips these LLVM inlining checks. For this purpose, introduce a new__rust_helpermacro that needs to be added to every helper.
Quoted 7 lines
Most helpers already have
__rust_helperspecified, but there are a few missing. The only consequence of this is that those specific helpers do not get inlined.Signed-off-by Gary Guo <[email protected]>Signed-off-by Alice Ryhl <[email protected]>---
Quoted 48 lines
rust/helpers/helpers.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+)rust/helpers/helpers.c +29
-0@@ -7,7 +7,36 @@* Sorted alphabetically. */ +#include <linux/compiler_types.h> + +#ifdef __BINDGEN__ +// Omit `inline` for bindgen as it ignores inline functions. #define __rust_helper +#else +// The helper functions are all inline functions. +// +// We use `__always_inline` here to bypass LLVM inlining checks, in case the +// helpers are inlined directly into Rust CGUs. +// +// The LLVM inlining checks are false positives: +// * LLVM doesn't want to inline functions compiled with +// `-fno-delete-null-pointer-checks` with code compiled without. +// The C CGUs all have this enabled and Rust CGUs don't. Inlining is okay +// since this is one of the hardening features that does not change the ABI, +// and we shouldn't have null pointer dereferences in these helpers. +// * LLVM doesn't want to inline functions with different list of builtins. C +// side has `-fno-builtin-wcslen`; `wcslen` is not a Rust builtin, so they +// should be compatible, but LLVM does not perform inlining due to attributes +// mismatch. +// * clang and Rust doesn't have the exact target string. Clang generates +// `+cmov,+cx8,+fxsr` but Rust doesn't enable them (in fact, Rust will +// complain if `-Ctarget-feature=+cmov,+cx8,+fxsr` is used). x86-64 always +// enable these features, so they are in fact the same target string, but +// LLVM doesn't understand this and so inlining is inhibited. This can be +// bypassed with `--ignore-tti-inline-compatible`, but this is a hidden +// option. +#define __rust_helper __always_inline +#endif #include "atomic.c" #include "atomic_ext.c" Signature · 2 lines
-- 2.53.0.rc1.225.gd81095ad13-goog
Alice Ryhl
reviewerPATCH v2 3/3 build: rust: provide an option to inline C helpers into Rust
In reply to Alice RyhlFrom: Gary Guo <[email protected]>
A new experimental Kconfig option, RUST_INLINE_HELPERS is added to allow C helpers (which were created to allow Rust to call into inline/macro C functions without having to re-implement the logic in Rust) to be inlined into Rust crates without performing global LTO.
If the option is enabled, the following is performed:
* For helpers, instead of compiling them to an object file to be linked into vmlinux, they're compiled to LLVM IR bitcode. Two versions are
generated: one for built-in code (helpers.bc) and one for modules (helpers_module.bc, with -DMODULE defined). This ensures that C macros/inlines that behave differently for modules (e.g. static calls) function correctly when inlined.
* When a Rust crate or object is compiled, instead of generating an object file, LLVM bitcode is generated.
* llvm-link is invoked with --internalize to combine the helper bitcode with the crate bitcode. This step is similar to LTO, but this is much faster since it only needs to inline the helpers.
* clang is invoked to turn the combined bitcode into a final object file.
* Since clang may produce LLVM bitcode when LTO is enabled, and objtool requires ELF input, $(cmd_ld_single) is invoked to ensure the object is converted to ELF before objtool runs.
The --internalize flag tells llvm-link to treat all symbols in helpers.bc using internal linkage [1]. This matches the behavior of clang on static inline functions, and avoids exporting the symbol from the object file.
To ensure that RUST_INLINE_HELPERS is not incompatible with BTF, we pass the -g0 flag when building helpers. See commit 5daa0c35a1f0 ("rust:
Disallow BTF generation with Rust + LTO") for details.
We have an intended triple mismatch of aarch64-unknown-none vs aarch64-unknown-linux-gnu, so we pass --suppress-warnings to llvm-link to suppress it.
We add $(cmd_ld_single) before $(cmd_objtool). Otherwise objtool fails to parse the resulting files as Elf when CONFIG_LTO is enabled.
I considered adding some sort of check that KBUILD_MODNAME is not present in helpers_module.bc, but this is actually not so easy to carry out because .bc files store strings in a weird binary format, so you cannot just grep it for a string to check whether it ended up using KBUILD_MODNAME anywhere.
--- Makefile | 3 ++- lib/Kconfig.debug | 15 +++++++++++++++ rust/Makefile | 29 +++++++++++++++++++++++++---- rust/exports.c | 5 ++++- scripts/Makefile.build | 7 ++++++- 5 files changed, 52 insertions(+), 7 deletions(-)
Makefile +2 -1
@@ -519,6 +519,7 @@ OBJCOPY = $(LLVM_PREFIX)llvm-objcopy$(LLVM_SUFFIX) OBJDUMP = $(LLVM_PREFIX)llvm-objdump$(LLVM_SUFFIX) READELF = $(LLVM_PREFIX)llvm-readelf$(LLVM_SUFFIX) STRIP = $(LLVM_PREFIX)llvm-strip$(LLVM_SUFFIX) +LLVM_LINK = $(LLVM_PREFIX)llvm-link$(LLVM_SUFFIX) else CC = $(CROSS_COMPILE)gcc LD = $(CROSS_COMPILE)ld
@@ -627,7 +628,7 @@ export RUSTC_BOOTSTRAP := 1 export CLIPPY_CONF_DIR := $(srctree) export ARCH SRCARCH CONFIG_SHELL BASH HOSTCC KBUILD_HOSTCFLAGS CROSS_COMPILE LD CC HOSTPKG_CONFIG -export RUSTC RUSTDOC RUSTFMT RUSTC_OR_CLIPPY_QUIET RUSTC_OR_CLIPPY BINDGEN +export RUSTC RUSTDOC RUSTFMT RUSTC_OR_CLIPPY_QUIET RUSTC_OR_CLIPPY BINDGEN LLVM_LINK export HOSTRUSTC KBUILD_HOSTRUSTFLAGS export CPP AR NM STRIP OBJCOPY OBJDUMP READELF PAHOLE RESOLVE_BTFIDS LEX YACC AWK INSTALLKERNEL export PERL PYTHON3 CHECK CHECKFLAGS MAKE UTS_MACHINE HOSTCXX
lib/Kconfig.debug +15 -0
@@ -3551,6 +3551,21 @@ config RUST_KERNEL_DOCTESTS If unsure, say N. +config RUST_INLINE_HELPERS + bool "Inline C helpers into Rust code" + depends on RUST && RUSTC_CLANG_LLVM_COMPATIBLE + depends on EXPERT + help + Inlines C helpers into Rust code using Link Time Optimization. + + If this option is enabled, C helper functions declared in + rust/helpers/ are inlined into Rust code, which is helpful for + performance of Rust code. This requires a matching LLVM version for + Clang and rustc. + + If you are sure that you're using Clang and rustc with matching LLVM + versions, say Y. Otherwise say N. + endmenu # "Rust" endmenu # Kernel hacking
rust/Makefile +25 -4
@@ -6,15 +6,19 @@ rustdoc_output := $(objtree)/Documentation/output/rust/rustdoc obj-$(CONFIG_RUST) += core.o compiler_builtins.o ffi.o always-$(CONFIG_RUST) += exports_core_generated.h +ifdef CONFIG_RUST_INLINE_HELPERS +always-$(CONFIG_RUST) += helpers/helpers.bc helpers/helpers_module.bc +else +obj-$(CONFIG_RUST) += helpers/helpers.o +always-$(CONFIG_RUST) += exports_helpers_generated.h +endif # Missing prototypes are expected in the helpers since these are exported # for Rust only, thus there is no header nor prototypes. -obj-$(CONFIG_RUST) += helpers/helpers.o CFLAGS_REMOVE_helpers/helpers.o = -Wmissing-prototypes -Wmissing-declarations always-$(CONFIG_RUST) += bindings/bindings_generated.rs bindings/bindings_helpers_generated.rs obj-$(CONFIG_RUST) += bindings.o pin_init.o kernel.o -always-$(CONFIG_RUST) += exports_helpers_generated.h \ - exports_bindings_generated.h exports_kernel_generated.h +always-$(CONFIG_RUST) += exports_bindings_generated.h exports_kernel_generated.h always-$(CONFIG_RUST) += uapi/uapi_generated.rs obj-$(CONFIG_RUST) += uapi.o
@@ -480,6 +484,14 @@ $(obj)/bindings/bindings_helpers_generated.rs: private bindgen_target_extra = ; $(obj)/bindings/bindings_helpers_generated.rs: $(src)/helpers/helpers.c FORCE $(call if_changed_dep,bindgen) +quiet_cmd_rust_helper = HELPER $@ + cmd_rust_helper = \ + $(CC) $(filter-out $(CFLAGS_REMOVE_helpers/helpers.o), $(c_flags)) \ + -c -g0 $< $(if $(filter %_module.bc,$@),-DMODULE) -emit-llvm -o $@ + +$(obj)/helpers/helpers.bc $(obj)/helpers/helpers_module.bc: $(src)/helpers/helpers.c FORCE + +$(call if_changed_dep,rust_helper) + rust_exports = $(NM) -p --defined-only $(1) | awk '$$2~/(T|R|D|B)/ && $$3!~/__(pfx|cfi|odr_asan)/ { printf $(2),$$3 }' quiet_cmd_exports = EXPORTS $@
@@ -561,12 +573,16 @@ quiet_cmd_rustc_library = $(if $(skip_clippy),RUSTC,$(RUSTC_OR_CLIPPY_QUIET)) L OBJTREE=$(abspath $(objtree)) \ $(if $(skip_clippy),$(RUSTC),$(RUSTC_OR_CLIPPY)) \ $(filter-out $(skip_flags),$(rust_flags)) $(rustc_target_flags) \ - --emit=dep-info=$(depfile) --emit=obj=$@ \ + --emit=dep-info=$(depfile) --emit=$(if $(link_helper),llvm-bc=$(patsubst %.o,%.bc,$@),obj=$@) \ --emit=metadata=$(dir $@)$(patsubst %.o,lib%.rmeta,$(notdir $@)) \ --crate-type rlib -L$(objtree)/$(obj) \ --crate-name $(patsubst %.o,%,$(notdir $@)) $< \ --sysroot=/dev/null \ + $(if $(link_helper),;$(LLVM_LINK) --internalize --suppress-warnings $(patsubst %.o,%.bc,$@) \ + $(obj)/helpers/helpers$(if $(part-of-module),_module).bc -o $(patsubst %.o,%.m.bc,$@); \ + $(CC) $(CLANG_FLAGS) $(KBUILD_CFLAGS) -Wno-override-module -c $(patsubst %.o,%.m.bc,$@) -o $@) \ $(if $(rustc_objcopy),;$(OBJCOPY) $(rustc_objcopy) $@) \ + $(cmd_ld_single) \ $(cmd_objtool) rust-analyzer:
@@ -696,4 +712,9 @@ $(obj)/kernel.o: $(obj)/kernel/generated_arch_warn_asm.rs $(obj)/kernel/generate endif endif +ifdef CONFIG_RUST_INLINE_HELPERS +$(obj)/kernel.o: private link_helper = 1 +$(obj)/kernel.o: $(obj)/helpers/helpers.bc +endif + endif # CONFIG_RUST
rust/exports.c +4 -1
@@ -16,10 +16,13 @@#define EXPORT_SYMBOL_RUST_GPL (sym) extern int sym; EXPORT_SYMBOL_GPL(sym)#include "exports_core_generated.h" -#include "exports_helpers_generated.h" #include "exports_bindings_generated.h" #include "exports_kernel_generated.h" +#ifndef CONFIG_RUST_INLINE_HELPERS +#include "exports_helpers_generated.h" +#endif +// For modules using `rust/build_error.rs`. #ifdef CONFIG_RUST_BUILD_ASSERT_ALLOW EXPORT_SYMBOL_RUST_GPL (rust_build_error);
scripts/Makefile.build +6 -1
@@ -343,7 +343,12 @@ rust_common_cmd = \ # would not match each other. quiet_cmd_rustc_o_rs = $(RUSTC_OR_CLIPPY_QUIET) $(quiet_modtag) $@ - cmd_rustc_o_rs = $(rust_common_cmd) --emit=obj=$@ $< $(cmd_objtool) + cmd_rustc_o_rs = $(rust_common_cmd) --emit=$(if $(CONFIG_RUST_INLINE_HELPERS),llvm-bc=$(patsubst %.o,%.bc,$@),obj=$@) $< \ + $(if $(CONFIG_RUST_INLINE_HELPERS),;$(LLVM_LINK) --internalize --suppress-warnings $(patsubst %.o,%.bc,$@) \ + $(objtree)/rust/helpers/helpers$(if $(part-of-module),_module).bc -o $(patsubst %.o,%.m.bc,$@); \ + $(CC) $(CLANG_FLAGS) $(KBUILD_CFLAGS) -Wno-override-module -c $(patsubst %.o,%.m.bc,$@) -o $@) \ + $(cmd_ld_single) \ + $(cmd_objtool) define rule_rustc_o_rs $(call cmd_and_fixdep,rustc_o_rs)
Signature · 1 line
2.53.0.rc1.225.gd81095ad13-goog
Alice Ryhl
reviewerRe: [PATCH v2 3/3] build: rust: provide an option to inline C helpers into Rust
rust/Makefile $(obj)/bindings/bindings_helpers_generated.rs: private bindgen_target_extra = ; · line 493
Quoted 10 lines · Alice Ryhl
$(obj)/bindings/bindings_helpers_generated.rs: $(src)/helpers/helpers.c FORCE
rust/Makefile +7
-0$(call if_changed_dep,bindgen) +quiet_cmd_rust_helper = HELPER $@ + cmd_rust_helper = \ + $(CC) $(filter-out $(CFLAGS_REMOVE_helpers/helpers.o), $(c_flags)) \ + -c -g0 $< $(if $(filter %_module.bc,$@),-DMODULE) -emit-llvm -o $@ + +$(obj)/helpers/helpers.bc $(obj)/helpers/helpers_module.bc: $(src)/helpers/helpers.c FORCE + +$(call if_changed_dep,rust_helper)
I think this needs:
$(obj)/helpers/helpers.bc: private part-of-builtin := y $(obj)/helpers/helpers_module.bc: private part-of-module := y
at least it seems to make a difference for AutoFDO.
Alice
Nathan Chancellor
maintainerRe: [PATCH v2 3/3] build: rust: provide an option to inline C helpers into Rust
Quoted 14 lines · Alice Ryhl
rust/Makefile $(obj)/bindings/bindings_helpers_generated.rs: private bindgen_target_extra = ; · line 493
Quoted 10 lines · Alice Ryhl
$(obj)/bindings/bindings_helpers_generated.rs: $(src)/helpers/helpers.c FORCE
rust/Makefile +7
-0$(call if_changed_dep,bindgen) +quiet_cmd_rust_helper = HELPER $@ + cmd_rust_helper = \ + $(CC) $(filter-out $(CFLAGS_REMOVE_helpers/helpers.o), $(c_flags)) \ + -c -g0 $< $(if $(filter %_module.bc,$@),-DMODULE) -emit-llvm -o $@ + +$(obj)/helpers/helpers.bc $(obj)/helpers/helpers_module.bc: $(src)/helpers/helpers.c FORCE + +$(call if_changed_dep,rust_helper)I think this needs:
Quoted 4 lines
$(obj)/helpers/helpers.bc: private part-of-builtin := y $(obj)/helpers/helpers_module.bc: private part-of-module := y
at least it seems to make a difference for AutoFDO.
Would
$(obj)/helpers/helpers.bc $(obj)/helpers/helpers_module.bc: private is-kernel-object := y
work as well? Presumably, this is because c_flags only has the AutoFDO flags when is-kernel-object is set, which is only true when helpers/helpers.o is a part of obj-y.
Cheers,
Nathan
Alice Ryhl
reviewerRe: [PATCH v2 3/3] build: rust: provide an option to inline C helpers into Rust
Quoted 20 lines · Nathan Chancellor
Quoted 14 lines · Alice Ryhl
rust/Makefile $(obj)/bindings/bindings_helpers_generated.rs: private bindgen_target_extra = ; · line 493
Quoted 10 lines · Alice Ryhl
$(obj)/bindings/bindings_helpers_generated.rs: $(src)/helpers/helpers.c FORCE
rust/Makefile +7
-0$(call if_changed_dep,bindgen) +quiet_cmd_rust_helper = HELPER $@ + cmd_rust_helper = \ + $(CC) $(filter-out $(CFLAGS_REMOVE_helpers/helpers.o), $(c_flags)) \ + -c -g0 $< $(if $(filter %_module.bc,$@),-DMODULE) -emit-llvm -o $@ + +$(obj)/helpers/helpers.bc $(obj)/helpers/helpers_module.bc: $(src)/helpers/helpers.c FORCE + +$(call if_changed_dep,rust_helper)I think this needs:
Quoted 4 lines
$(obj)/helpers/helpers.bc: private part-of-builtin := y $(obj)/helpers/helpers_module.bc: private part-of-module := y
at least it seems to make a difference for AutoFDO.
Quoted 7 lines
Would
$(obj)/helpers/helpers.bc $(obj)/helpers/helpers_module.bc: private is-kernel-object := ywork as well? Presumably, this is because
c_flagsonly has the AutoFDO flags when is-kernel-object is set, which is only true whenhelpers/helpers.ois a part of obj-y.
I assume so, but isn't it more correct to set part-of-builtin and part-of-module? I don't see any make rules making use of those directly (instead of is-kernel-object) right now, but if someone adds such uses, then this could become wrong, right?
Alice
Nathan Chancellor
maintainerRe: [PATCH v2 3/3] build: rust: provide an option to inline C helpers into Rust
Quoted 25 lines · Alice Ryhl
Quoted 24 lines · Nathan Chancellor
Quoted 14 lines · Alice Ryhl
rust/Makefile $(obj)/bindings/bindings_helpers_generated.rs: private bindgen_target_extra = ; · line 493
Quoted 10 lines · Alice Ryhl
$(obj)/bindings/bindings_helpers_generated.rs: $(src)/helpers/helpers.c FORCE
rust/Makefile +7
-0$(call if_changed_dep,bindgen) +quiet_cmd_rust_helper = HELPER $@ + cmd_rust_helper = \ + $(CC) $(filter-out $(CFLAGS_REMOVE_helpers/helpers.o), $(c_flags)) \ + -c -g0 $< $(if $(filter %_module.bc,$@),-DMODULE) -emit-llvm -o $@ + +$(obj)/helpers/helpers.bc $(obj)/helpers/helpers_module.bc: $(src)/helpers/helpers.c FORCE + +$(call if_changed_dep,rust_helper)I think this needs:
Quoted 4 lines
$(obj)/helpers/helpers.bc: private part-of-builtin := y $(obj)/helpers/helpers_module.bc: private part-of-module := y
at least it seems to make a difference for AutoFDO.
Would
$(obj)/helpers/helpers.bc $(obj)/helpers/helpers_module.bc: private is-kernel-object := y
Quoted 8 lines
Quoted 3 lines
work as well? Presumably, this is because
c_flagsonly has the AutoFDO flags when is-kernel-object is set, which is only true whenhelpers/helpers.ois a part of obj-y.I assume so, but isn't it more correct to set part-of-builtin and part-of-module? I don't see any make rules making use of those directly (instead of is-kernel-object) right now, but if someone adds such uses, then this could become wrong, right?
Hmmm, yeah, I suppose so. Actually, adding those would allow you to drop the manual inclusion of -DMODULE in cmd_rust_helper because it will be included via modkern_cflags.
Cheers,
Nathan
Re: [PATCH v2 3/3] build: rust: provide an option to inline C helpers into Rust
Quoted 4 lines · Nathan Chancellor
Hmmm, yeah, I suppose so. Actually, adding those would allow you to drop the manual inclusion of -DMODULE in
cmd_rust_helperbecause it will be included viamodkern_cflags.
Yeah, a week ago I asked Alice about simplifying to is-kernel-object as well (mostly because I used it elsewhere in our Makefile in the past), but similarly I ended up thinking that if we are OK using directly part-of-*, then it would be better anyway.
Cheers,
Miguel
Re: [PATCH v2 3/3] build: rust: provide an option to inline C helpers into Rust
In reply to Alice RyhlQuoted 33 lines · Alice Ryhl
From: Gary Guo <[email protected]>
A new experimental
Kconfigoption,RUST_INLINE_HELPERSis added to allow C helpers (which were created to allow Rust to call into inline/macro C functions without having to re-implement the logic in Rust) to be inlined into Rust crates without performing global LTO.If the option is enabled, the following is performed:
* For helpers, instead of compiling them to an object file to be linked into vmlinux, they're compiled to LLVM IR bitcode. Two versions are
generated: one for built-in code (helpers.bc) and one for modules (helpers_module.bc, with -DMODULE defined). This ensures that C macros/inlines that behave differently for modules (e.g. static calls) function correctly when inlined.
* When a Rust crate or object is compiled, instead of generating an object file, LLVM bitcode is generated.
* llvm-link is invoked with --internalize to combine the helper bitcode with the crate bitcode. This step is similar to LTO, but this is much faster since it only needs to inline the helpers.
* clang is invoked to turn the combined bitcode into a final object file.
* Since clang may produce LLVM bitcode when LTO is enabled, and objtool requires ELF input, $(cmd_ld_single) is invoked to ensure the object is converted to ELF before objtool runs.The --internalize flag tells llvm-link to treat all symbols in helpers.bc using
internallinkage [1]. This matches the behavior ofclangonstatic inlinefunctions, and avoids exporting the symbol from the object file.To ensure that
RUST_INLINE_HELPERSis not incompatible with BTF, we pass the -g0 flag when building helpers. See commit5daa0c35a1f0("rust:
Disallow BTF generation with Rust + LTO") for details.
Quoted 6 lines
We have an intended triple mismatch of
aarch64-unknown-nonevsaarch64-unknown-linux-gnu, so we pass --suppress-warnings to llvm-link to suppress it.We add $(cmd_ld_single) before $(cmd_objtool). Otherwise objtool fails to parse the resulting files as Elf when
CONFIG_LTOis enabled.
You mention this as the last bullet point above, so this paragraph can probably be dropped.
Quoted 6 lines
I considered adding some sort of check that
KBUILD_MODNAMEis not present in helpers_module.bc, but this is actually not so easy to carry out because .bc files store strings in a weird binary format, so you cannot just grep it for a string to check whether it ended up usingKBUILD_MODNAMEanywhere.
Quoted 8 lines
Co-developed-by Boqun Feng <[email protected]>Signed-off-by Boqun Feng <[email protected]>Co-developed-by Matthew Maurer <[email protected]>Signed-off-by Matthew Maurer <[email protected]>Signed-off-by Gary Guo <[email protected]>Co-developed-by Alice Ryhl <[email protected]>Signed-off-by Alice Ryhl <[email protected]>
Quoted 13 lines
--- Makefile | 3 ++- lib/Kconfig.debug | 15 +++++++++++++++ rust/Makefile | 29 +++++++++++++++++++++++++---- rust/exports.c | 5 ++++- scripts/Makefile.build | 7 ++++++- 5 files changed, 52 insertions(+), 7 deletions(-)Makefile +0
-0
Makefile OBJCOPY = $(LLVM_PREFIX)llvm-objcopy$(LLVM_SUFFIX) · line 522
Quoted 4 lines
Makefile +1
-0@@ -519,6 +519,7 @@ OBJCOPY = $(LLVM_PREFIX)llvm-objcopy$(LLVM_SUFFIX) OBJDUMP = $(LLVM_PREFIX)llvm-objdump$(LLVM_SUFFIX) READELF = $(LLVM_PREFIX)llvm-readelf$(LLVM_SUFFIX) STRIP = $(LLVM_PREFIX)llvm-strip$(LLVM_SUFFIX) +LLVM_LINK = $(LLVM_PREFIX)llvm-link$(LLVM_SUFFIX)
I don't have a strong preference but this could be alphabetized among the binutils variables.
Quoted 149 lines
Makefile +1
-1else CC = $(CROSS_COMPILE)gcc LD = $(CROSS_COMPILE)ld@@ -627,7 +628,7 @@ export RUSTC_BOOTSTRAP := 1 export CLIPPY_CONF_DIR := $(srctree) export ARCH SRCARCH CONFIG_SHELL BASH HOSTCC KBUILD_HOSTCFLAGS CROSS_COMPILE LD CC HOSTPKG_CONFIG -export RUSTC RUSTDOC RUSTFMT RUSTC_OR_CLIPPY_QUIET RUSTC_OR_CLIPPY BINDGEN +export RUSTC RUSTDOC RUSTFMT RUSTC_OR_CLIPPY_QUIET RUSTC_OR_CLIPPY BINDGEN LLVM_LINK export HOSTRUSTC KBUILD_HOSTRUSTFLAGS export CPP AR NM STRIP OBJCOPY OBJDUMP READELF PAHOLE RESOLVE_BTFIDS LEX YACC AWK INSTALLKERNEL export PERL PYTHON3 CHECK CHECKFLAGS MAKE UTS_MACHINE HOSTCXXlib/Kconfig.debug +15
-0@@ -3551,6 +3551,21 @@ config RUST_KERNEL_DOCTESTS If unsure, say N. +config RUST_INLINE_HELPERS + bool "Inline C helpers into Rust code" + depends on RUST && RUSTC_CLANG_LLVM_COMPATIBLE + depends on EXPERT + help + Inlines C helpers into Rust code using Link Time Optimization. + + If this option is enabled, C helper functions declared in + rust/helpers/ are inlined into Rust code, which is helpful for + performance of Rust code. This requires a matching LLVM version for + Clang and rustc. + + If you are sure that you're using Clang and rustc with matching LLVM + versions, say Y. Otherwise say N. + endmenu # "Rust" endmenu # Kernel hackingrust/Makefile +25
-4@@ -6,15 +6,19 @@ rustdoc_output := $(objtree)/Documentation/output/rust/rustdoc obj-$(CONFIG_RUST) += core.o compiler_builtins.o ffi.o always-$(CONFIG_RUST) += exports_core_generated.h +ifdef CONFIG_RUST_INLINE_HELPERS +always-$(CONFIG_RUST) += helpers/helpers.bc helpers/helpers_module.bc +else +obj-$(CONFIG_RUST) += helpers/helpers.o +always-$(CONFIG_RUST) += exports_helpers_generated.h +endif # Missing prototypes are expected in the helpers since these are exported # for Rust only, thus there is no header nor prototypes. -obj-$(CONFIG_RUST) += helpers/helpers.o CFLAGS_REMOVE_helpers/helpers.o = -Wmissing-prototypes -Wmissing-declarations always-$(CONFIG_RUST) += bindings/bindings_generated.rs bindings/bindings_helpers_generated.rs obj-$(CONFIG_RUST) += bindings.o pin_init.o kernel.o -always-$(CONFIG_RUST) += exports_helpers_generated.h \ - exports_bindings_generated.h exports_kernel_generated.h +always-$(CONFIG_RUST) += exports_bindings_generated.h exports_kernel_generated.h always-$(CONFIG_RUST) += uapi/uapi_generated.rs obj-$(CONFIG_RUST) += uapi.o@@ -480,6 +484,14 @@ $(obj)/bindings/bindings_helpers_generated.rs: private bindgen_target_extra = ; $(obj)/bindings/bindings_helpers_generated.rs: $(src)/helpers/helpers.c FORCE $(call if_changed_dep,bindgen) +quiet_cmd_rust_helper = HELPER $@ + cmd_rust_helper = \ + $(CC) $(filter-out $(CFLAGS_REMOVE_helpers/helpers.o), $(c_flags)) \ + -c -g0 $< $(if $(filter %_module.bc,$@),-DMODULE) -emit-llvm -o $@ + +$(obj)/helpers/helpers.bc $(obj)/helpers/helpers_module.bc: $(src)/helpers/helpers.c FORCE + +$(call if_changed_dep,rust_helper) + rust_exports = $(NM) -p --defined-only $(1) | awk '$$2~/(T|R|D|B)/ && $$3!~/__(pfx|cfi|odr_asan)/ { printf $(2),$$3 }' quiet_cmd_exports = EXPORTS $@@@ -561,12 +573,16 @@ quiet_cmd_rustc_library = $(if $(skip_clippy),RUSTC,$(RUSTC_OR_CLIPPY_QUIET)) L OBJTREE=$(abspath $(objtree)) \ $(if $(skip_clippy),$(RUSTC),$(RUSTC_OR_CLIPPY)) \ $(filter-out $(skip_flags),$(rust_flags)) $(rustc_target_flags) \ - --emit=dep-info=$(depfile) --emit=obj=$@ \ + --emit=dep-info=$(depfile) --emit=$(if $(link_helper),llvm-bc=$(patsubst %.o,%.bc,$@),obj=$@) \ --emit=metadata=$(dir $@)$(patsubst %.o,lib%.rmeta,$(notdir $@)) \ --crate-type rlib -L$(objtree)/$(obj) \ --crate-name $(patsubst %.o,%,$(notdir $@)) $< \ --sysroot=/dev/null \ + $(if $(link_helper),;$(LLVM_LINK) --internalize --suppress-warnings $(patsubst %.o,%.bc,$@) \ + $(obj)/helpers/helpers$(if $(part-of-module),_module).bc -o $(patsubst %.o,%.m.bc,$@); \ + $(CC) $(CLANG_FLAGS) $(KBUILD_CFLAGS) -Wno-override-module -c $(patsubst %.o,%.m.bc,$@) -o $@) \ $(if $(rustc_objcopy),;$(OBJCOPY) $(rustc_objcopy) $@) \ + $(cmd_ld_single) \ $(cmd_objtool) rust-analyzer:@@ -696,4 +712,9 @@ $(obj)/kernel.o: $(obj)/kernel/generated_arch_warn_asm.rs $(obj)/kernel/generate endif endif +ifdef CONFIG_RUST_INLINE_HELPERS +$(obj)/kernel.o: private link_helper = 1 +$(obj)/kernel.o: $(obj)/helpers/helpers.bc +endif + endif # CONFIG_RUSTrust/exports.c +4
-1@@ -16,10 +16,13 @@#define EXPORT_SYMBOL_RUST_GPL (sym) extern int sym; EXPORT_SYMBOL_GPL(sym)#include "exports_core_generated.h" -#include "exports_helpers_generated.h" #include "exports_bindings_generated.h" #include "exports_kernel_generated.h" +#ifndef CONFIG_RUST_INLINE_HELPERS +#include "exports_helpers_generated.h" +#endif +// For modules using `rust/build_error.rs`. #ifdef CONFIG_RUST_BUILD_ASSERT_ALLOW EXPORT_SYMBOL_RUST_GPL (rust_build_error);scripts/Makefile.build +6
-1@@ -343,7 +343,12 @@ rust_common_cmd = \ # would not match each other. quiet_cmd_rustc_o_rs = $(RUSTC_OR_CLIPPY_QUIET) $(quiet_modtag) $@ - cmd_rustc_o_rs = $(rust_common_cmd) --emit=obj=$@ $< $(cmd_objtool) + cmd_rustc_o_rs = $(rust_common_cmd) --emit=$(if $(CONFIG_RUST_INLINE_HELPERS),llvm-bc=$(patsubst %.o,%.bc,$@),obj=$@) $< \ + $(if $(CONFIG_RUST_INLINE_HELPERS),;$(LLVM_LINK) --internalize --suppress-warnings $(patsubst %.o,%.bc,$@) \ + $(objtree)/rust/helpers/helpers$(if $(part-of-module),_module).bc -o $(patsubst %.o,%.m.bc,$@); \ + $(CC) $(CLANG_FLAGS) $(KBUILD_CFLAGS) -Wno-override-module -c $(patsubst %.o,%.m.bc,$@) -o $@) \ + $(cmd_ld_single) \ + $(cmd_objtool) define rule_rustc_o_rs $(call cmd_and_fixdep,rustc_o_rs)Signature · 2 lines
-- 2.53.0.rc1.225.gd81095ad13-goog
Quoted 18 lines · Alice Ryhl
Currently the only way for Rust code to call a static inline function is to go through a helper in
rust/helpers/. This introduces performance costs due to additional function calls and also clutters backtraces and flame graphs with helper symbols.To get rid of these helper symbols, provide functionality to inline helpers into Rust using llvm-link. This option complements full LTO, by being much cheaper and avoiding incompatibility with BTF.
I ran a microbenchmark showing the benefit of this. All the benchmark does is call
refcount_inc()in a loop. This was chosen since refcounting is quite hot in Binder. The results are that Rust spends 6.35 ns per call vs 5.73 ns per call in C. When enabling this option, the two languages become equally fast, and disassembly confirms the exact same machine code is used (in particular there is no call torust_helper_refcount_inc). Benchmarking Binder also results in an improvement from this change.
Quoted 4 lines
This patch is complementary to:
https://lore.kernel.org/all/[email protected]/Signed-off-by Alice Ryhl <[email protected]>
Tested with the rust null block driver patch series [1]. I did a few functional verification tests and a set of performance tests.
For the rnull driver, enabling helper inlining with this patch gives an average speedup of 2% over the set of 120 workloads that we publish on [2].
Best regards,
Andreas Hindborg
[1] https://lore.kernel.org/rust-for-linux/[email protected]/
[2] https://rust-for-linux.com/null-block-driver
Miguel Ojeda
maintainerQuoted 4 lines · On Tue, 03 Feb 2026 11:34:07 +0000 Alice Ryhl <[email protected]> wrote:
To get rid of these helper symbols, provide functionality to inline helpers into Rust using llvm-link. This option complements full LTO, by being much cheaper and avoiding incompatibility with BTF.
I have been testing this. I think we can go ahead with it, with a few notes.
I will reply to a couple other bindings in separate emails to avoid spamming people too much.
- I will mark the Kconfig option as "(EXPERIMENTAL)", since that is what the commit message says and it allows us to be a bit conservative.
- Clang passes -Werror=unused-command-line-argument, which means under arm (i.e. 32-bit) we get:
clang: error: argument unused during compilation: '-U arm' [-Werror,-Wunused-command-line-argument]
And under UML I see:
clang: error: argument unused during compilation: '-I ./arch/um/include/shared' [-Werror,-Wunused-command-line-argument]
clang: error: argument unused during compilation: '-I ./arch/x86/um/shared' [-Werror,-Wunused-command-line-argument]
clang: error: argument unused during compilation: '-I ./arch/um/include/shared/skas' [-Werror,-Wunused-command-line-argument]
So we would need e.g. -Wno-unused-command-line-argument there close to the -Wno-override-module one, unless Kbuild or ClangBuiltLinux thinks it is important to keep it for this case.
On the other hand, regardless of whether we fix this (and another issue in a separate email found thanks to the UML build), we could instead add depends on listing explicitly the architectures where this is going to be actually tested. That way maintainers can decide whether they want to support it when they are ready. Thoughts?
Cc'ing Nathan, Nicolas, Nick, Bill, Justin, David, UML, ARM.
- If we use the .bc extension, we need to add a .gitignore for .bc files, and an exception for kernel/time/timeconst.bc.
I guess we will not have too many bc scripts in the future for that to be a problem. On the other hand, we have the chance to use another extension (either for LLVM bitcode or for bc scripts).
But please let me know on e.g. the Kbuild side if someone has concerns...
- Do we want to have the cmd_ld_single step even if the new mode is not enabled? i.e. we are gating the other steps, so we could gate that one too easily, no?
- $(cmd_ld_single) \
+ $(if $(link_helper),$(cmd_ld_single)) \
- $(cmd_ld_single) \
+ $(if $(CONFIG_RUST_INLINE_HELPERS),$(cmd_ld_single)) \
I mean, it only affects CONFIG_LTO_CLANG configs, which are on their own experimental as far as I can see, so it is probably fine as it is, but still, while making things unconditional on the caller is good, it is also a behaviour change for configs outside the thing introduced here, so I am asking.
(We could use something like a is-rust-object to do it on the definition side, but I would leave that for later.)
Thanks!
Cheers,
Miguel
Miguel Ojeda
maintainerQuoted 3 lines · On Sun, 22 Mar 2026 20:21:59 +0100 Miguel Ojeda <[email protected]> wrote:
I will reply to a couple other bindings in separate emails to avoid spamming people too much.
In a series of tests, in some cases, I noticed an objtool warning for x86_64:
rust/kernel.o: warning: objtool: _R..._9RegulatorNtB5_8DisabledE3get() is missing an ELF size annotation rust/kernel.o: warning: objtool: _R..._9RegulatorNtB5_7EnabledE3get() is missing an ELF size annotation
I noticed that it only happened when CONFIG_REGULATOR was disabled. It seems that we have undefined behavior in regulator.rs...
We have Regulator<Enabled/Disabled>::get() which calls get_internal() which calls the regulator_get() helper, which returns NULL when CONFIG_REGULATOR is not set. Then the return value is passed to from_err_ptr which checks IS_ERR, which returns false (unlike IS_ERR_OR_NULL), and thus we return an Ok(NULL), which we then pass to NonNull::new_unchecked:
// SAFETY: We can safely trust `inner` to be a pointer to a valid
// regulator if `ERR_PTR` was not returned.
let inner = unsafe { NonNull::new_unchecked(inner) };So we should fix that -- it is there since its introduction in commit 9b614ceada7c ("rust: regulator: add a bare minimum regulator abstraction"). How to fix that depends on whether the Rust abstraction supposed to work transparently like the C API, I assume.
Now, two improvements I think we should independently do too:
- The docs on regulator_get() don't say it may return NULL. It originally that case, but commit be1a50d4eba4 ("regulator: Let drivers know when they use the stub API") changed that without changing the docs.
I mean, the "docs" are on the real function, but still, one may look into those and not realize the stub does something different.
The implementation comment on the stub is also a bit contradictory. The original sentence (which still is there) says that nothing should look at the value, but then it goes onto say that drivers may actually look at the value.
- On the Rust general infrastructure side, we should document more prominently that from_err_ptr may return Ok(NULL) just fine, since it calls IS_ERR. It may be obvious, since NULL is not an error value, but still, it could have perhaps prevented this issue.
We should also include an example to the doctest showing and testing that particular case to drive the point home.
I can add that as a "good first issue".
Cc'ing Liam, Mark, Daniel, Jean.
I hope that helps.
Cheers,
Miguel
Quoted 4 lines · Miguel Ojeda
- The docs on
regulator_get()don't say it may returnNULL. It originally that case, but commitbe1a50d4eba4("regulator: Let drivers know when they use the stub API") changed that without changing the docs.
Callers should never treat NULL any differently to any other non-error pointer, this is not a value intended for inspection by users.
Quoted 3 lines
The original sentence (which still is there) says that nothing should look at the value, but then it goes onto say that drivers may actually look at the value.
Having specific handling for NULLs is very much not a preferred interface, and most things shouldn't. The caller should probably have it's own !CONFIG_REGULATOR checks rather than using the return value.
Quoted 7 lines · Mark Brown
Callers should never treat NULL any differently to any other non-error pointer, this is not a value intended for inspection by users.
Having specific handling for NULLs is very much not a preferred interface, and most things shouldn't. The caller should probably have it's own !CONFIG_REGULATOR checks rather than using the return value.
Yeah, that is the understanding I got from reading both the docs and the comment, but I think the docs (i.e. not the comment in the stub) could point that NULL is one possible pointer value (perhaps including discouraging relying on particular values at the same time, and perhaps mentioning !CONFIG_REGULATOR).
i.e. to me, "Pointer to a struct regulator corresponding to the regulator producer," sounds in the docs a bit too much like it will be a valid one... no?
Another perspective is that, when one reads the rendered docs:
https://docs.kernel.org/driver-api/regulator.html#c.regulator_get
one sees the postconditions, but they happen to not apply in all cases (in fact, perhaps not having the "Returns" there would increase the chances one realizes that, but I understand that you want them to treat it as fully opaque as much as possible).
It could also help things like AI review systems which may only notice the docs and not the comment inside the stub.
Perhaps it could be at least a one-line comment in the real definition, even if not in the rendered docs, to have a chance for others to notice.
I don't know -- I hope that helps, in any case. Perhaps Daniel can tell us what he thought when he was writing the code, as another perspective, i.e. whether it was the docs or not the issue.
Thanks!
Cheers,
Miguel
Miguel Ojeda
maintainerQuoted 3 lines · On Sun, 22 Mar 2026 20:21:59 +0100 Miguel Ojeda <[email protected]> wrote:
I will reply to a couple other bindings in separate emails to avoid spamming people too much.
Under UML, I got -Wstatic-in-inline for dma_free_attrs:
BINDGEN rust/bindings/bindings_generated.rs - due to target missing
In file included from rust/helpers/helpers.c:59:
rust/helpers/dma.c:17:2: warning: static function 'dma_free_attrs' is used in an inline function with external linkage [-Wstatic-in-inline]
17 | dma_free_attrs(dev, size, cpu_addr, dma_handle, attrs);
| ^
rust/helpers/dma.c:12:1: note: use 'static' to give inline function 'rust_helper_dma_free_attrs' internal linkage
12 | __rust_helper void rust_helper_dma_free_attrs(struct device *dev, size_t size,
| ^
| static
For some reason, dma_free_attrs is not marked inline in include/linux/dma-mapping.h to begin with, unlike the rest.
Unless I am missing something and there is a reason for that, it looks like it has just been missing since it was added in commit ed6ccf10f24b ("dma-mapping: properly stub out the DMA API for !CONFIG_HAS_DMA").
Do you want a patch?
Cc'ing Marek, Robin, Danilo, Abdiel, Daniel, Andreas, Christoph.
I hope that helps!
Cheers,
Miguel
Marek Szyprowski
maintainerQuoted 15 lines · Miguel Ojeda
Quoted 2 lines · On Sun, 22 Mar 2026 20:21:59 +0100 Miguel Ojeda <[email protected]> wrote:
I will reply to a couple other bindings in separate emails to avoid spamming people too much.
Under UML, I got
-Wstatic-in-inlinefordma_free_attrs:BINDGEN
rust/bindings/bindings_generated.rs- due to target missing
In file included fromrust/helpers/helpers.c:59:
rust/helpers/dma.c:17:2: warning: static function 'dma_free_attrs' is used in an inline function with external linkage [-Wstatic-in-inline]
17 |dma_free_attrs(dev, size, cpu_addr, dma_handle, attrs);
| ^
rust/helpers/dma.c:12:1: note: use 'static' to give inline function 'rust_helper_dma_free_attrs' internal linkage
12 |__rust_helpervoid rust_helper_dma_free_attrs(struct device *dev,size_tsize,
| ^
| static
Quoted 8 lines
For some reason,
dma_free_attrsis not markedinlineininclude/linux/dma-mapping.hto begin with, unlike the rest.Unless I am missing something and there is a reason for that, it looks like it has just been missing since it was added in commit
ed6ccf10f24b("dma-mapping: properly stub out the DMA API for !CONFIG_HAS_DMA").Do you want a patch?
Yes please, this looks like an oversight.
Best regards
Signature · 2 lines
Marek Szyprowski, PhD Samsung R&D Institute Poland
Quoted 2 lines · Marek Szyprowski
Yes please, this looks like an oversight.
https://lore.kernel.org/rust-for-linux/[email protected]/
I hope that helps!
Cheers,
Miguel
Quoted 17 lines · Miguel Ojeda
Quoted 1 line · On Sun, 22 Mar 2026 20:21:59 +0100 Miguel Ojeda <[email protected]> wrote:
BINDGEN
rust/bindings/bindings_generated.rs- due to target
missing
In file included fromrust/helpers/helpers.c:59:
rust/helpers/dma.c:17:2: warning: static function 'dma_free_attrs'
is used in an inline function with external linkage [-Wstatic-in-inline]
17 | dma_free_attrs(dev, size,cpu_addr,dma_handle,
attrs);
| ^
rust/helpers/dma.c:12:1: note: use 'static' to give inline function
'rust_helper_dma_free_attrs' internal linkage
12 |__rust_helpervoid rust_helper_dma_free_attrs(struct device
*dev,size_tsize,| ^ | static
Quoted 8 lines
For some reason,
dma_free_attrsis not markedinlineininclude/linux/dma-mapping.hto begin with, unlike the rest.Unless I am missing something and there is a reason for that, it looks like it has just been missing since it was added in commit
ed6ccf10f24b("dma-mapping: properly stub out the DMA API for !CONFIG_HAS_DMA").Do you want a patch?
I have an older patch to drop CONFIG_NO_DMA entirely, which may be better here, since we know that nobody cares about the empty stubs.
The only targets that 'select NO_DMA' today are m68k (sun3 and dragonball), sh2 and um, which are some of the targets that don't have any DMA masters, but there is little downside of enabling the DMA mapping interfaces on those as well.
arch/m68k/Kconfig +0 -1
@@ -38,7 +38,6 @@ config M68K select MMU_GATHER_NO_RANGE if MMU select MODULES_USE_ELF_REL select MODULES_USE_ELF_RELA - select NO_DMA if !MMU && !COLDFIRE select OLD_SIGACTION select OLD_SIGSUSPEND3 select UACCESS_MEMCPY if !MMU
arch/m68k/Kconfig.cpu +1 -2
@@ -38,7 +38,6 @@ config SUN3 depends on MMU select HAVE_ARCH_PFN_VALID select LEGACY_TIMER_TICK - select NO_DMA select M68020 help This option enables support for the Sun 3 series of workstations
@@ -558,4 +557,4 @@ config COLDFIRE_COHERENT_DMA config M68K_NONCOHERENT_DMA bool default y - depends on HAS_DMA && !COLDFIRE_COHERENT_DMA + depends on HAS_DMA && !COLDFIRE_COHERENT_DMA && !SUN3
arch/m68k/include/asm/pgtable_mm.h +2 -0
@@ -159,8 +159,10 @@ static inline void update_mmu_cache_range(struct vm_fault *vmf, ? (__pgprot((pgprot_val(prot) & _CACHEMASK040) | _PAGE_NOCACHE_S)) \ : (prot))) +#ifndef CONFIG_SUN3 pgprot_t pgprot_dmacoherent (pgprot_t prot);#define pgprot_dmacoherent (prot) pgprot_dmacoherent(prot) +#endif #endif /* CONFIG_COLDFIRE */ #endif /* !__ASSEMBLER__ */
arch/m68k/kernel/dma.c +1 -1
@@ -8,7 +8,7 @@#include <linux/kernel.h> #include <asm/cacheflush.h> -#ifndef CONFIG_COLDFIRE +#if !defined(CONFIG_COLDFIRE ) && !defined(CONFIG_SUN3 )void arch_dma_prep_coherent (struct page *page,size_t size) {cache_push (page_to_phys (page), size);
arch/sh/Kconfig +2 -3
@@ -63,7 +63,6 @@ config SUPERH select LOCK_MM_AND_FIND_VMA select MODULES_USE_ELF_RELA select NEED_SG_DMA_LENGTH - select NO_DMA if !MMU && !DMA_COHERENT select NO_GENERIC_PCI_IOPORT_MAP if PCI select OLD_SIGACTION select OLD_SIGSUSPEND
@@ -133,10 +132,10 @@ config SWAP_IO_SPACE bool config DMA_COHERENT - bool + def_bool !MMU config DMA_NONCOHERENT - def_bool !NO_DMA && !DMA_COHERENT + def_bool !DMA_COHERENT select ARCH_HAS_DMA_PREP_COHERENT select ARCH_HAS_SYNC_DMA_FOR_DEVICE select DMA_DIRECT_REMAP
arch/um/Kconfig +0 -1
@@ -25,7 +25,6 @@ config UML select HAVE_DEBUG_KMEMLEAK select HAVE_DEBUG_BUGVERBOSE select HAVE_PAGE_SIZE_4KB - select NO_DMA if !UML_DMA_EMULATION select OF_EARLY_FLATTREE if OF select GENERIC_IRQ_SHOW select GENERIC_CPU_DEVICES
include/linux/dma-mapping.h +0 -141
153 lines not on this page · raw message
kernel/dma/Kconfig +1 -6
@@ -1,12 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only -config NO_DMA - bool - config HAS_DMA - bool - depends on !NO_DMA - default y + def_bool y config DMA_OPS_HELPERS bool
Quoted 4 lines · Arnd Bergmann
I have an older patch to drop
CONFIG_NO_DMAentirely, which may be better here, since we know that nobody cares about the empty stubs.
In case it helps / for future reference: the patch I sent to Marek is already in mainline: commit 2cdaff22ed26 ("dma-mapping: add missing inline for dma_free_attrs").
Cheers,
Miguel
Marek Szyprowski
maintainerQuoted 20 lines · Arnd Bergmann
Quoted 19 lines · Miguel Ojeda
On Sun, 22 Mar 2026 20:21:59 +0100 Miguel Ojeda <[email protected]> wrote:
BINDGENrust/bindings/bindings_generated.rs- due to target
missing
In file included fromrust/helpers/helpers.c:59:
rust/helpers/dma.c:17:2: warning: static function 'dma_free_attrs'
is used in an inline function with external linkage [-Wstatic-in-inline]
17 | dma_free_attrs(dev, size,cpu_addr,dma_handle,
attrs);
| ^
rust/helpers/dma.c:12:1: note: use 'static' to give inline function
'rust_helper_dma_free_attrs' internal linkage
12 |__rust_helpervoid rust_helper_dma_free_attrs(struct device
*dev,size_tsize,| ^ | staticFor some reason,
dma_free_attrsis not markedinlineininclude/linux/dma-mapping.hto begin with, unlike the rest.
Quoted 8 lines
Quoted 5 lines
Unless I am missing something and there is a reason for that, it looks like it has just been missing since it was added in commit
ed6ccf10f24b("dma-mapping: properly stub out the DMA API for !CONFIG_HAS_DMA").Do you want a patch?
I have an older patch to drop
CONFIG_NO_DMAentirely, which may be better here, since we know that nobody cares about the empty stubs.
The inline fix is already merged to v7.0-rc, but I'm fine with stubs removal and I can queue them to -next. Could You send a proper patch separately so I can easily apply it?
Quoted 270 lines
The only targets that 'select
NO_DMA' today are m68k (sun3 and dragonball), sh2 and um, which are some of the targets that don't have any DMA masters, but there is little downside of enabling the DMA mapping interfaces on those as well.Signed-off-by Arnd Bergmann <[email protected]>arch/m68k/Kconfig +0
-1@@ -38,7 +38,6 @@ config M68K select MMU_GATHER_NO_RANGE if MMU select MODULES_USE_ELF_REL select MODULES_USE_ELF_RELA - select NO_DMA if !MMU && !COLDFIRE select OLD_SIGACTION select OLD_SIGSUSPEND3 select UACCESS_MEMCPY if !MMUarch/m68k/Kconfig.cpu +1
-2@@ -38,7 +38,6 @@ config SUN3 depends on MMU select HAVE_ARCH_PFN_VALID select LEGACY_TIMER_TICK - select NO_DMA select M68020 help This option enables support for the Sun 3 series of workstations@@ -558,4 +557,4 @@ config COLDFIRE_COHERENT_DMA config M68K_NONCOHERENT_DMA bool default y - depends on HAS_DMA && !COLDFIRE_COHERENT_DMA + depends on HAS_DMA && !COLDFIRE_COHERENT_DMA && !SUN3arch/m68k/include/asm/pgtable_mm.h +2
-0@@ -159,8 +159,10 @@ static inline void update_mmu_cache_range(struct vm_fault *vmf, ? (__pgprot((pgprot_val(prot) & _CACHEMASK040) | _PAGE_NOCACHE_S)) \ : (prot))) +#ifndef CONFIG_SUN3 pgprot_t pgprot_dmacoherent (pgprot_t prot);#define pgprot_dmacoherent (prot) pgprot_dmacoherent(prot) +#endif #endif /* CONFIG_COLDFIRE */ #endif /* !__ASSEMBLER__ */ arch/m68k/kernel/dma.c +1
-1@@ -8,7 +8,7 @@#include <linux/kernel.h> #include <asm/cacheflush.h> -#ifndef CONFIG_COLDFIRE +#if !defined(CONFIG_COLDFIRE ) && !defined(CONFIG_SUN3 )void arch_dma_prep_coherent (struct page *page,size_t size) {cache_push (page_to_phys (page), size);arch/sh/Kconfig +2
-3@@ -63,7 +63,6 @@ config SUPERH select LOCK_MM_AND_FIND_VMA select MODULES_USE_ELF_RELA select NEED_SG_DMA_LENGTH - select NO_DMA if !MMU && !DMA_COHERENT select NO_GENERIC_PCI_IOPORT_MAP if PCI select OLD_SIGACTION select OLD_SIGSUSPEND@@ -133,10 +132,10 @@ config SWAP_IO_SPACE bool config DMA_COHERENT - bool + def_bool !MMU config DMA_NONCOHERENT - def_bool !NO_DMA && !DMA_COHERENT + def_bool !DMA_COHERENT select ARCH_HAS_DMA_PREP_COHERENT select ARCH_HAS_SYNC_DMA_FOR_DEVICE select DMA_DIRECT_REMAParch/um/Kconfig +0
-1@@ -25,7 +25,6 @@ config UML select HAVE_DEBUG_KMEMLEAK select HAVE_DEBUG_BUGVERBOSE select HAVE_PAGE_SIZE_4KB - select NO_DMA if !UML_DMA_EMULATION select OF_EARLY_FLATTREE if OF select GENERIC_IRQ_SHOW select GENERIC_CPU_DEVICESinclude/linux/dma-mapping.h +0
-141@@ -138,7 +138,6 @@ static inline void debug_dma_map_single(struct device *dev, const void *addr, }#endif /* CONFIG_DMA_API_DEBUG */ -#ifdef CONFIG_HAS_DMA static inline int dma_mapping_error (struct device *dev,dma_addr_t dma_addr) {debug_dma_mapping_error (dev, dma_addr);@@ -200,146 +199,6 @@ void *dma_vmap_noncontiguous(struct device *dev, size_t size,void dma_vunmap_noncontiguous (struct device *dev,void *vaddr);int dma_mmap_noncontiguous (struct device *dev,struct vm_area_struct *vma,size_t size,struct sg_table *sgt); -#else /* CONFIG_HAS_DMA */ -static inline dma_addr_t dma_map_page_attrs (struct device *dev, -struct page *page,size_t offset,size_t size, -enum dma_data_direction dir,unsigned long attrs) -{ -return DMA_MAPPING_ERROR ; -} -static inline void dma_unmap_page_attrs (struct device *dev,dma_addr_t addr, -size_t size,enum dma_data_direction dir,unsigned long attrs) -{ -} -static inline dma_addr_t dma_map_phys (struct device *dev,phys_addr_t phys, -size_t size,enum dma_data_direction dir,unsigned long attrs) -{ -return DMA_MAPPING_ERROR ; -} -static inline void dma_unmap_phys (struct device *dev,dma_addr_t addr, -size_t size,enum dma_data_direction dir,unsigned long attrs) -{ -} -static inline unsigned int dma_map_sg_attrs (struct device *dev, -struct scatterlist *sg,int nents,enum dma_data_direction dir, -unsigned long attrs) -{ -return 0 ; -} -static inline void dma_unmap_sg_attrs (struct device *dev, -struct scatterlist *sg,int nents,enum dma_data_direction dir, -unsigned long attrs) -{ -} -static inline int dma_map_sgtable (struct device *dev,struct sg_table *sgt, -enum dma_data_direction dir,unsigned long attrs) -{ -return -EOPNOTSUPP ; -} -static inline dma_addr_t dma_map_resource (struct device *dev, -phys_addr_t phys_addr,size_t size,enum dma_data_direction dir, -unsigned long attrs) -{ -return DMA_MAPPING_ERROR ; -} -static inline void dma_unmap_resource (struct device *dev,dma_addr_t addr, -size_t size,enum dma_data_direction dir,unsigned long attrs) -{ -} -static inline int dma_mapping_error (struct device *dev,dma_addr_t dma_addr) -{ -return -ENOMEM ; -} -static inline void *dma_alloc_attrs (struct device *dev,size_t size, -dma_addr_t *dma_handle,gfp_t flag,unsigned long attrs) -{ -return NULL ; -} -static inline void dma_free_attrs (struct device *dev,size_t size, -void *cpu_addr,dma_addr_t dma_handle,unsigned long attrs) -{ -} -static inline void *dmam_alloc_attrs (struct device *dev,size_t size, -dma_addr_t *dma_handle,gfp_t gfp,unsigned long attrs) -{ -return NULL ; -} -static inline void dmam_free_coherent (struct device *dev,size_t size, -void *vaddr,dma_addr_t dma_handle) -{ -} -static inline int dma_get_sgtable_attrs (struct device *dev, -struct sg_table *sgt,void *cpu_addr,dma_addr_t dma_addr, -size_t size,unsigned long attrs) -{ -return -ENXIO ; -} -static inline int dma_mmap_attrs (struct device *dev,struct vm_area_struct *vma, -void *cpu_addr,dma_addr_t dma_addr,size_t size, -unsigned long attrs) -{ -return -ENXIO ; -} -static inline bool dma_can_mmap (struct device *dev) -{ -return false; -} -static inline bool dma_pci_p2pdma_supported (struct device *dev) -{ -return false; -} -static inline int dma_set_mask (struct device *dev,u64 mask) -{ -return -EIO ; -} -static inline int dma_set_coherent_mask (struct device *dev,u64 mask) -{ -return -EIO ; -} -static inline u64 dma_get_required_mask (struct device *dev) -{ -return 0 ; -} -static inline bool dma_addressing_limited (struct device *dev) -{ -return false; -} -static inline size_t dma_max_mapping_size (struct device *dev) -{ -return 0 ; -} -static inline size_t dma_opt_mapping_size (struct device *dev) -{ -return 0 ; -} -static inline unsigned long dma_get_merge_boundary (struct device *dev) -{ -return 0 ; -} -static inline struct sg_table *dma_alloc_noncontiguous (struct device *dev, -size_t size,enum dma_data_direction dir,gfp_t gfp, -unsigned long attrs) -{ -return NULL ; -} -static inline void dma_free_noncontiguous (struct device *dev,size_t size, -struct sg_table *sgt,enum dma_data_direction dir) -{ -} -static inline void *dma_vmap_noncontiguous (struct device *dev,size_t size, -struct sg_table *sgt) -{ -return NULL ; -} -static inline void dma_vunmap_noncontiguous (struct device *dev,void *vaddr) -{ -} -static inline int dma_mmap_noncontiguous (struct device *dev, -struct vm_area_struct *vma,size_t size,struct sg_table *sgt) -{ -return -EINVAL ; -} -#endif /* CONFIG_HAS_DMA */ #ifdef CONFIG_IOMMU_DMA /** kernel/dma/Kconfig +0
-1@@ -1,12 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only -config NO_DMA
kernel/dma/Kconfig line 7
Quoted 10 lines
kernel/dma/Kconfig +1
-5- bool - config HAS_DMA - bool - depends on !NO_DMA - default y + def_bool y config DMA_OPS_HELPERS bool
Best regards
Signature · 2 lines
Marek Szyprowski, PhD Samsung R&D Institute Poland
Miguel Ojeda
maintainerQuoted 6 lines · On Sun, 22 Mar 2026 20:21:59 +0100 Miguel Ojeda <[email protected]> wrote:
On the other hand, regardless of whether we fix this (and another issue in a separate email found thanks to the UML build), we could instead add
depends onlisting explicitly the architectures where this is going to be actually tested. That way maintainers can decide whether they want to support it when they are ready. Thoughts?
Another one for arm 32-bit:
LD .tmp_vmlinux1
ld.lld: error: undefined symbol: __aeabi_read_tp >>> referenced by uaccess.rs:349 (rust/kernel/uaccess.rs:349)
>>> samples/rust/rust_misc_device.o:(<rust_misc_device::RustMiscDevice as kernel::miscdevice::MiscDevice>::ioctl) in archive vmlinux.a
>>> referenced by uaccess.rs:543 (rust/kernel/uaccess.rs:543)
>>> samples/rust/rust_misc_device.o:(<rust_misc_device::RustMiscDevice as kernel::miscdevice::MiscDevice>::ioctl) in archive vmlinux.a
>>> referenced by uaccess.rs:543 (rust/kernel/uaccess.rs:543)
>>> drivers/android/binder/rust_binder_main.o:(rust_binder_main::rust_binder_ioctl) in archive vmlinux.a
>>> referenced 36 more times
I think this reinforces the case that this is something that arch folks should think about case by case, and decide if they want to try -- we may want to start simple with x86_64 and arm64 or similar first.
Thanks!
Cheers,
Miguel
Quoted 20 lines · Miguel Ojeda
Quoted 6 lines · On Sun, 22 Mar 2026 20:21:59 +0100 Miguel Ojeda <[email protected]> wrote:
On the other hand, regardless of whether we fix this (and another issue in a separate email found thanks to the UML build), we could instead add
depends onlisting explicitly the architectures where this is going to be actually tested. That way maintainers can decide whether they want to support it when they are ready. Thoughts?Another one for arm 32-bit:
LD .tmp_vmlinux1
ld.lld: error: undefined symbol:__aeabi_read_tp>>> referenced by uaccess.rs:349 (rust/kernel/uaccess.rs:349)
>>> samples/rust/rust_misc_device.o:(<rust_misc_device::RustMiscDevice as kernel::miscdevice::MiscDevice>::ioctl) in archive vmlinux.a
>>> referenced by uaccess.rs:543 (rust/kernel/uaccess.rs:543)
>>> samples/rust/rust_misc_device.o:(<rust_misc_device::RustMiscDevice as kernel::miscdevice::MiscDevice>::ioctl) in archive vmlinux.a
>>> referenced by uaccess.rs:543 (rust/kernel/uaccess.rs:543)
>>> drivers/android/binder/rust_binder_main.o:(rust_binder_main::rust_binder_ioctl) in archive vmlinux.a
>>> referenced 36 more times
Quoted 3 lines
I think this reinforces the case that this is something that arch folks should think about case by case, and decide if they want to try -- we may want to start simple with
x86_64and arm64 or similar first.
But is this an architecture thing?
The netlink binding required this, because many of the netlink API calls are implemented in inline function. However, netlink is architecture independent.
Rust is already fragmented, because it does not support all architectures. Do we really want to make it even more fragmented by having some bindings only work on a subset of the subset of architectures?
Andrew
Quoted 5 lines · Andrew Lunn
Rust is already fragmented, because it does not support all architectures. Do we really want to make it even more fragmented by having some bindings only work on a subset of the subset of architectures?
That is not the case. The depends on is not about putting them on abstractions, but on this experimental build feature, which is gated on EXPERT to begin with, because it uses a fairly exotic approach involving LLVM bitcode, which carries potential pitfalls, like the mismatches on the target string like one of the commit messages mentions, and possibly others.
Cheers,
Miguel
Quoted 7 lines · Miguel Ojeda
Quoted 5 lines · Andrew Lunn
Rust is already fragmented, because it does not support all architectures. Do we really want to make it even more fragmented by having some bindings only work on a subset of the subset of architectures?
Quoted 6 lines
That is not the case. The
depends onis not about putting them on abstractions, but on this experimental build feature, which is gated onEXPERTto begin with, because it uses a fairly exotic approach involving LLVM bitcode, which carries potential pitfalls, like the mismatches on the target string like one of the commit messages mentions, and possibly others.
I'm not sure i follow this.
Maybe i should ask a different question.
Quoted 2 lines · You
we
may want to start simple withx86_64and arm64 or similar first.
The current proposed code for netlink needs this feature, because it needs access to inline C functions. Is the implication, following a chain of dependencies, that netlink would only build on x86_64 and arm64?
If you want netlink on um, arm32, riscv, loongarch you would need a different implementation of the binding?
And a completely different question. Are there other work in progress solutions to allow the use of inline C functions? For networking, in particularly MAC and protocol code, anything which needs to access a struct sk_buf, a solution to this problem will be required. Do you see this "fairly exotic approach" as just a sort term bridge until some other "boring approach" is ready?
Andrew
Gary Guo
reviewerQuoted 24 lines · Andrew Lunn
Quoted 7 lines · Miguel Ojeda
Quoted 5 lines · Andrew Lunn
Rust is already fragmented, because it does not support all architectures. Do we really want to make it even more fragmented by having some bindings only work on a subset of the subset of architectures?
Quoted 6 lines
That is not the case. The
depends onis not about putting them on abstractions, but on this experimental build feature, which is gated onEXPERTto begin with, because it uses a fairly exotic approach involving LLVM bitcode, which carries potential pitfalls, like the mismatches on the target string like one of the commit messages mentions, and possibly others.I'm not sure i follow this.
Maybe i should ask a different question.
Quoted 2 lines · You
we
may want to start simple withx86_64and arm64 or similar first.
Quoted 7 lines
The current proposed code for netlink needs this feature, because it needs access to inline C functions. Is the implication, following a chain of dependencies, that netlink would only build on
x86_64and arm64?If you want netlink on um, arm32, riscv, loongarch you would need a different implementation of the binding?
It doesn't need this feature to build and function. It'll just be a bit slower because inlining from C to Rust won't happen.
Quoted 7 lines
And a completely different question. Are there other work in progress solutions to allow the use of inline C functions? For networking, in particularly MAC and protocol code, anything which needs to access a struct
sk_buf, a solution to this problem will be required. Do you see this "fairly exotic approach" as just a sort term bridge until some other "boring approach" is ready?
The actually inlining itself is not exotic, it's exactly same as LTO, just in smaller scale. Although, the way we do it is probably somewhat uncommon due all the constraints that the kernel has (mostly due to loadable modules, so we cannot perform a global LTO and thus need custom handling of LLVM bitcode).
Best,
Gary
Quoted 5 lines
Quoted 2 lines
If you want netlink on um, arm32, riscv, loongarch you would need a different implementation of the binding?
It doesn't need this feature to build and function. It'll just be a bit slower because inlining from C to Rust won't happen.
So it sounds like my understanding is out of date. When the first Ethernet PHY driver was being merged, Rust could not access inline C functions. The binding had to replicate the functionality. But you are saying that Rust can now make use of inline C code, but not actually inline it? It results in a function call?
What this feature does it actually allows inline C functions to be inline? So it is just an optimisation?
Andrew
Quoted 9 lines · Andrew Lunn
So it sounds like my understanding is out of date. When the first Ethernet PHY driver was being merged, Rust could not access inline C functions. The binding had to replicate the functionality. But you are saying that Rust can now make use of inline C code, but not actually inline it? It results in a function call?
What this feature does it actually allows inline C functions to be inline? So it is just an optimisation?
We have always been using the helpers to access the inline functions, please see the top comment in rust/helpers/helpers.c:
Non-trivial C macros cannot be used in Rust. Similarly, inlined C functions cannot be called either. This file explicitly creates functions ("helpers") that wrap those so that they can be called from Rust.
It doesn't duplicate the functionality, it simply forwards the call, i.e. it is not as bad as a full copy-paste of the implementation.
This patch series aims to improve performance (cheaply in terms of built time) because for Rust calling those helpers are not inline anymore otherwise.
You may be referring, instead, to avoiding to have to write manually the helper (the forwarder) -- for that, please see "Support static inline functions in headers (by generating a wrapper)" at:
https://github.com/Rust-for-Linux/linux/issues/353
(In fact, I had your previous mention of skbuff.h from Lore there :)
Assuming --wrap-static-fns works well etc., then it would be nice to use it, yeah. But that is different from this patch series (and this patch series would apply as well even if we use the automated wrappers etc.).
I hope that clarifies.
Cheers,
Miguel
Alice Ryhl
reviewerQuoted 6 lines · Andrew Lunn
Quoted 5 lines
Quoted 2 lines
If you want netlink on um, arm32, riscv, loongarch you would need a different implementation of the binding?
It doesn't need this feature to build and function. It'll just be a bit slower because inlining from C to Rust won't happen.
Quoted 8 lines
So it sounds like my understanding is out of date. When the first Ethernet PHY driver was being merged, Rust could not access inline C functions. The binding had to replicate the functionality. But you are saying that Rust can now make use of inline C code, but not actually inline it? It results in a function call?
What this feature does it actually allows inline C functions to be inline? So it is just an optimisation?
That's right, this series applies cross-language inlining to the helpers we have for calling inline functions.
Sorry if I was unclear on the netlink patch series. I mentioned this series because you told me that inlining is important for skbuff due to performance (not for netlink though), and this series is necessary to make such code on par with C perf-wise. But if you accept the function call overhead from not having cross-language inlining, this series is not needed.
Alice
Quoted 8 lines · Andrew Lunn
The current proposed code for netlink needs this feature, because it needs access to inline C functions. Is the implication, following a chain of dependencies, that netlink would only build on
x86_64and arm64?If you want netlink on um, arm32, riscv, loongarch you would need a different implementation of the binding?
I still don't follow -- this new mode is opt-in, and only a couple architectures (or whoever decides to support it) would be able to opt-in. Nothing else changes.
Quoted 6 lines
And a completely different question. Are there other work in progress solutions to allow the use of inline C functions? For networking, in particularly MAC and protocol code, anything which needs to access a struct
sk_buf, a solution to this problem will be required. Do you see this "fairly exotic approach" as just a sort term bridge until some other "boring approach" is ready?
This series is really just a way to increase performance, i.e. it doesn't really change what we can do.
Cheers,
Miguel
Quoted 10 lines · Miguel Ojeda
Quoted 6 lines · On Sun, 22 Mar 2026 20:21:59 +0100 Miguel Ojeda <[email protected]> wrote:
On the other hand, regardless of whether we fix this (and another issue in a separate email found thanks to the UML build), we could instead add
depends onlisting explicitly the architectures where this is going to be actually tested. That way maintainers can decide whether they want to support it when they are ready. Thoughts?Another one for arm 32-bit:
Quoted 9 lines
LD .tmp_vmlinux1
ld.lld: error: undefined symbol:__aeabi_read_tp>>> referenced by uaccess.rs:349 (rust/kernel/uaccess.rs:349)
>>> samples/rust/rust_misc_device.o:(<rust_misc_device::RustMiscDevice as kernel::miscdevice::MiscDevice>::ioctl) in archive vmlinux.a
>>> referenced by uaccess.rs:543 (rust/kernel/uaccess.rs:543)
>>> samples/rust/rust_misc_device.o:(<rust_misc_device::RustMiscDevice as kernel::miscdevice::MiscDevice>::ioctl) in archive vmlinux.a
>>> referenced by uaccess.rs:543 (rust/kernel/uaccess.rs:543)
>>> drivers/android/binder/rust_binder_main.o:(rust_binder_main::rust_binder_ioctl) in archive vmlinux.a
>>> referenced 36 more times
Why is Rust generating code for userspace thread accessors for kernel space, where userspace threads are meaningless. This is totally wrong. The kernel must not reference __aeabi_read_tp().
Note: I know nothing about Rust, but I know enough to say the above is pointing to a fundamental issue in Rust for 32-bit ARM.
Signature · 2 lines
RMK's Patch system: https://www.armlinux.org.uk/developer/patches/ FTTP is here! 80Mbps down 10Mbps up. Decent connectivity at last!
Quoted 7 lines · Russell King (Oracle)
Why is Rust generating code for userspace thread accessors for kernel space, where userspace threads are meaningless. This is totally wrong. The kernel must not reference
__aeabi_read_tp().Note: I know nothing about Rust, but I know enough to say the above is pointing to a fundamental issue in Rust for 32-bit ARM.
If it happens outside this patch series (i.e. the new opt-in mode), then yeah, something is broken.
That is why I proposed to gate this new mode to the architectures/configs where it is known to be OK (and where maintainers may be OK with it etc.).
Cheers,
Miguel
Alice Ryhl
reviewerQuoted 21 lines · Russell King (Oracle)
Quoted 10 lines · Miguel Ojeda
Quoted 6 lines · On Sun, 22 Mar 2026 20:21:59 +0100 Miguel Ojeda <[email protected]> wrote:
On the other hand, regardless of whether we fix this (and another issue in a separate email found thanks to the UML build), we could instead add
depends onlisting explicitly the architectures where this is going to be actually tested. That way maintainers can decide whether they want to support it when they are ready. Thoughts?Another one for arm 32-bit:
Quoted 9 lines
LD .tmp_vmlinux1
ld.lld: error: undefined symbol:__aeabi_read_tp>>> referenced by uaccess.rs:349 (rust/kernel/uaccess.rs:349)
>>> samples/rust/rust_misc_device.o:(<rust_misc_device::RustMiscDevice as kernel::miscdevice::MiscDevice>::ioctl) in archive vmlinux.a
>>> referenced by uaccess.rs:543 (rust/kernel/uaccess.rs:543)
>>> samples/rust/rust_misc_device.o:(<rust_misc_device::RustMiscDevice as kernel::miscdevice::MiscDevice>::ioctl) in archive vmlinux.a
>>> referenced by uaccess.rs:543 (rust/kernel/uaccess.rs:543)
>>> drivers/android/binder/rust_binder_main.o:(rust_binder_main::rust_binder_ioctl) in archive vmlinux.a
>>> referenced 36 more times
Quoted 6 lines
Why is Rust generating code for userspace thread accessors for kernel space, where userspace threads are meaningless. This is totally wrong. The kernel must not reference
__aeabi_read_tp().Note: I know nothing about Rust, but I know enough to say the above is pointing to a fundamental issue in Rust for 32-bit ARM.
I noticed that the Makefile currently uses the arm-unknown-linux-gnueabi target. It should probably not be -linux target to avoid this? Probably it should just be armv7a-none-eabi, right? We gate HAVE_RUST on CPU_32v7, so we should not need to consider the other variants.
Alice
Quoted 5 lines · Alice Ryhl
I noticed that the
Makefilecurrently uses the arm-unknown-linux-gnueabi target. It should probably not be -linux target to avoid this? Probably it should just be armv7a-none-eabi, right? We gateHAVE_RUSTonCPU_32v7, so we should not need to consider the other variants.
I think Christian tried several targets back then and eventually picked that one.
Christian: what was the reason to pick the -linux- one? e.g. was there something you wanted to rely on that target spec that you couldn't enable or disable via rustc flags or similar?
Cc'ing a few folks.
Thanks!
Cheers,
Miguel
Hi Miguel,
Quoted 7 lines · Miguel Ojeda
Quoted 5 lines · Alice Ryhl
I noticed that the
Makefilecurrently uses the arm-unknown-linux-gnueabi target. It should probably not be -linux target to avoid this? Probably it should just be armv7a-none-eabi, right? We gateHAVE_RUSTonCPU_32v7, so we should not need to consider the other variants.
Quoted 6 lines
I think Christian tried several targets back then and eventually picked that one.
Christian: what was the reason to pick the
-linux-one? e.g. was there something you wanted to rely on that target spec that you couldn't enable or disable viarustcflags or similar?
It should probably be fine to use armv7a-none-eabi. I've mostly used arm-unknown-linux-gnueabi since I though it needed to match the bindgen-target (which is -linux-gnu for all architectures) and
because from what I understand clang also uses arm-linux-gnueabi [1]. Also when I selected the target I thought that we would also support armv6, but since I had no v6 hardware to test on I disabled it.
[1]: https://github.com/Rust-for-Linux/linux/blob/rust-next/scripts/Makefile.clang#L4
Cheers,
Christian
Quoted 13 lines · Christian Schrefl
Hi Miguel,
Quoted 10 lines · Miguel Ojeda
Quoted 5 lines · Alice Ryhl
I noticed that the
Makefilecurrently uses the arm-unknown-linux-gnueabi target. It should probably not be -linux target to avoid this? Probably it should just be armv7a-none-eabi, right? We gateHAVE_RUSTonCPU_32v7, so we should not need to consider the other variants.I think Christian tried several targets back then and eventually picked that one.
Quoted 6 lines
Quoted 3 lines
Christian: what was the reason to pick the
-linux-one? e.g. was there something you wanted to rely on that target spec that you couldn't enable or disable viarustcflags or similar?It should probably be fine to use armv7a-none-eabi. I've mostly used arm-unknown-linux-gnueabi
I'm not sure if this is still true, but I believe it used to be the case that the -linux-gnueabi target has one behaviour for enums (fixed size) whereas -none-eabi, the size of the type depends on the range of values included in the enum.
Certianly, when Arm Ltd were proposing EABI, EABI had the latter behaviour, and I think there were cases where Linux used "enum" in its UAPI.
Signature · 2 lines
RMK's Patch system: https://www.armlinux.org.uk/developer/patches/ FTTP is here! 80Mbps down 10Mbps up. Decent connectivity at last!
Quoted 9 lines · Russell King (Oracle)
I'm not sure if this is still true, but I believe it used to be the case that the -linux-gnueabi target has one behaviour for enums (fixed size) whereas -none-eabi, the size of the type depends on the range of values included in the enum.
Certianly, when Arm Ltd were proposing EABI, EABI had the latter behaviour, and I think there were cases where Linux used "enum" in its UAPI.
Short enums? I see c-enum-min-bits in the armv7a-none-eabi built-in rustc target, and indeed:
#![no_std]
#[repr(C)]
enum T {
A,
B,
}
pub static S: usize = core::mem::size_of::<T>();is 1 for that one, and 4 for the other.
Thanks!
Cheers,
Miguel
Quoted 25 lines · Miguel Ojeda
Quoted 9 lines · Russell King (Oracle)
I'm not sure if this is still true, but I believe it used to be the case that the -linux-gnueabi target has one behaviour for enums (fixed size) whereas -none-eabi, the size of the type depends on the range of values included in the enum.
Certianly, when Arm Ltd were proposing EABI, EABI had the latter behaviour, and I think there were cases where Linux used "enum" in its UAPI.
Short enums? I see
c-enum-min-bitsin the armv7a-none-eabi built-inrustctarget, and indeed:#![no_std] #[repr(C)] enum T { A, B, } pub static S: usize = core::mem::size_of::<T>();
Quoted 1 line
is 1 for that one, and 4 for the other.
I guess we could use a custom target spec, but I'm not sure if that is worth the hassle of adding another one.
Cheers,
Christian
Quoted 24 lines · Christian Schrefl
Quoted 23 lines · Miguel Ojeda
Quoted 9 lines · Russell King (Oracle)
I'm not sure if this is still true, but I believe it used to be the case that the -linux-gnueabi target has one behaviour for enums (fixed size) whereas -none-eabi, the size of the type depends on the range of values included in the enum.
Certianly, when Arm Ltd were proposing EABI, EABI had the latter behaviour, and I think there were cases where Linux used "enum" in its UAPI.
Short enums? I see
c-enum-min-bitsin the armv7a-none-eabi built-inrustctarget, and indeed:#![no_std] #[repr(C)] enum T { A, B, }
Quoted 6 lines
Quoted 3 lines
pub static S: usize = core::mem::size_of::<T>();is 1 for that one, and 4 for the other.
I guess we could use a custom target spec, but I'm not sure if that is worth the hassle of adding another one.
The kernel Makefile forces the Linux enum behavior using
arch/arm/Makefile:CFLAGS_ABI :=-mabi=aapcs-linux -mfpu=vfp
ifeq ($(CONFIG_CC_IS_CLANG),y)CFLAGS_ABI += -meabi gnu
endifKBUILD_RUSTFLAGS += --target=arm-unknown-linux-gnueabi
regardless of the compiler. I think this should be safe for any combination of C and rust compilers.
Arnd
Quoted 8 lines · Russell King (Oracle)
Quoted 2 lines · Christian Schrefl
Quoted 1 line · Miguel Ojeda
On Thu, Mar 26, 2026 at 11:10 AM Alice Ryhl <[email protected]> wrote:
I'm not sure if this is still true, but I believe it used to be the case that the -linux-gnueabi target has one behaviour for enums (fixed size) whereas -none-eabi, the size of the type depends on the range of values included in the enum.
I checked Debian's arm-none-eabi-gcc, which indeed still has this behavior:
$ echo 'enum { A, B } x = sizeof(x);' | arm-none-eabi-gcc -xc - -O2 -o- -S | grep -A1 x:
x:
.byte 1and I see the same thing for the hexagon target in clang, but none of the other targets that Linux runs on. In particular, clang always behaves like linux-gnueabi even when targeting plain eabi.
$ echo 'enum { A, B } x = sizeof(x);' | clang --target=arm-none-eabi -xc - -O2 -o- -S | grep -A1 x:
x:
.long 4I noticed a similar issue with m68k-linux, which has a bitfield alignment different from anything else on gcc, but uses the normal behavior on clang.
Arnd
Geert Uytterhoeven
maintainerHi Arnd,
Quoted 20 lines · Arnd Bergmann
Quoted 8 lines · Russell King (Oracle)
Quoted 2 lines · Christian Schrefl
Quoted 1 line · Miguel Ojeda
On Thu, Mar 26, 2026 at 11:10 AM Alice Ryhl <[email protected]> wrote:
I'm not sure if this is still true, but I believe it used to be the case that the -linux-gnueabi target has one behaviour for enums (fixed size) whereas -none-eabi, the size of the type depends on the range of values included in the enum.
I checked Debian's arm-none-eabi-gcc, which indeed still has this behavior:
$ echo 'enum { A, B } x = sizeof(x);' | arm-none-eabi-gcc -xc - -O2 -o- -S | grep -A1 x: x: .byte 1and I see the same thing for the hexagon target in clang, but none of the other targets that Linux runs on. In particular, clang always behaves like linux-gnueabi even when targeting plain eabi.
Quoted 7 lines
$ echo 'enum { A, B } x = sizeof(x);' | clang --target=arm-none-eabi -xc - -O2 -o- -S | grep -A1 x: x: .long 4I noticed a similar issue with m68k-linux, which has a bitfield alignment different from anything else on gcc, but uses the normal behavior on clang.
Ugh, I wasn't aware of that. Adrian, did you know?
Gr{oetje,eeting}s,
Geert
Signature · 5 lines
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- [email protected] In personal conversations with technical people, I call myself a hacker. But when I'm talking to journalists I just say "programmer" or something like that. -- Linus Torvalds
Quoted 6 lines · Geert Uytterhoeven
Quoted 4 lines
I noticed a similar issue with m68k-linux, which has a bitfield alignment different from anything else on gcc, but uses the normal behavior on clang.
Ugh, I wasn't aware of that. Adrian, did you know?
To clarify, this illustrates what I mean here:
echo 'struct { short a : 3; short b :15; short c :14; } x; int y = sizeof(x);' | m68k-linux-gcc -xc - -S -o-
this produces '4' on m68k-linux-gcc, but '6' everywhere else. I originally thought this was related to this 2009 change in both compilers
https://github.com/llvm/llvm-project/commit/830dfccfb2c06a1749938ce8cb78a1d6a295b942
https://gcc.gnu.org/cgit/gcc/commit/?id=2cd36c22d3d16e24f342829a989ac35f2f442656
but that only allowed bitfields to be marked as __attribute__((packed)) in order to get tightly packed fields and return '4' on all architectures, while m68k-linux-gcc apparently has all bitfields implicitly packed unless they are explicitly marked __attribute__((aligned(x))). This behavior is independent of the -malign-int flag.
Arnd
Quoted 6 lines · Arnd Bergmann
On Fri, Mar 27, 2026, at 08:56, Geert Uytterhoeven wrote:
but that only allowed bitfields to be marked as __attribute__((packed)) in order to get tightly packed fields and return '4' on all architectures, while m68k-linux-gcc apparently has all bitfields implicitly packed unless they are explicitly marked __attribute__((aligned(x))). This behavior is independent of the -malign-int flag.
I had another look and found that this has been in gcc since ELF support was originally added for m68k:
gcc/config/m68k/linux.h:#undef PCC_BITFIELD_TYPE_MATTERS
All other current Linux/ELF targets get the default from gcc/config/elfos.h
Arnd
Geert Uytterhoeven
maintainerHi Arnd,
Quoted 5 lines · Arnd Bergmann
Quoted 4 lines · Geert Uytterhoeven
Quoted 3 lines
I noticed a similar issue with m68k-linux, which has a bitfield alignment different from anything else on gcc, but uses the normal behavior on clang.
Quoted 8 lines
Quoted 1 line
Ugh, I wasn't aware of that. Adrian, did you know?
To clarify, this illustrates what I mean here:
echo 'struct { short a : 3; short b :15; short c :14; } x; int y = sizeof(x);' | m68k-linux-gcc -xc - -S -o-
this produces '4' on m68k-linux-gcc, but '6' everywhere else. I originally thought this was related to this 2009 change in both compilers
Oh, now I remember. AFAIK (holding wood and a rabbit leg) we don't have any bitfield members spanning multiple base type instances in the kernel.
Gr{oetje,eeting}s,
Geert
Signature · 5 lines
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- [email protected] In personal conversations with technical people, I call myself a hacker. But when I'm talking to journalists I just say "programmer" or something like that. -- Linus Torvalds
Quoted 10 lines · Geert Uytterhoeven
Quoted 5 lines · Arnd Bergmann
echo 'struct { short a : 3; short b :15; short c :14; } x; int y = sizeof(x);' | m68k-linux-gcc -xc - -S -o-
this produces '4' on m68k-linux-gcc, but '6' everywhere else. I originally thought this was related to this 2009 change in both compilers
Oh, now I remember. AFAIK (holding wood and a rabbit leg) we don't have any bitfield members spanning multiple base type instances in the kernel.
There are certainly very few of those, but two example I found in UAPI are
struct dvd_layer {
__u8 book_version : 4;
__u8 book_type : 4;
__u8 min_rate : 4;
__u8 disc_size : 4;
__u8 layer_type : 4;
__u8 track_path : 1;
__u8 nlayers : 2;
__u8 track_density : 4; // crosses u8 boundary
__u8 linear_density : 4;
__u8 bca : 1;
__u32 start_sector;
__u32 end_sector;
__u32 end_sector_l0;
};struct usb_raw_ep_caps {
__u32 type_control : 1;
__u32 type_iso : 1;
__u32 type_bulk : 1;
__u32 type_int : 1;
__u32 dir_in : 1;
__u32 dir_out : 1;
// 2 bit padding on m68k, 26 bits elsewhere
};Arnd
Geert Uytterhoeven
maintainerHi Arnd,
Quoted 34 lines · Arnd Bergmann
Quoted 10 lines · Geert Uytterhoeven
Quoted 5 lines · Arnd Bergmann
echo 'struct { short a : 3; short b :15; short c :14; } x; int y = sizeof(x);' | m68k-linux-gcc -xc - -S -o-
this produces '4' on m68k-linux-gcc, but '6' everywhere else. I originally thought this was related to this 2009 change in both compilers
Oh, now I remember. AFAIK (holding wood and a rabbit leg) we don't have any bitfield members spanning multiple base type instances in the kernel.
There are certainly very few of those, but two example I found in UAPI are
struct dvd_layer { __u8 book_version : 4; __u8 book_type : 4; __u8 min_rate : 4; __u8 disc_size : 4; __u8 layer_type : 4; __u8 track_path : 1; __u8 nlayers : 2; __u8 track_density : 4; // crosses u8 boundary __u8 linear_density : 4; __u8 bca : 1; __u32 start_sector; __u32 end_sector; __u32 end_sector_l0; };
Quoted 9 lines
struct usb_raw_ep_caps {
__u32 type_control : 1;
__u32 type_iso : 1;
__u32 type_bulk : 1;
__u32 type_int : 1;
__u32 dir_in : 1;
__u32 dir_out : 1;
// 2 bit padding on m68k, 26 bits elsewhere
};So we just need to make padding explicit? Sounds familiar ;-)
Gr{oetje,eeting}s,
Geert
Signature · 5 lines
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- [email protected] In personal conversations with technical people, I call myself a hacker. But when I'm talking to journalists I just say "programmer" or something like that. -- Linus Torvalds
Quoted 7 lines · Christian Schrefl
It should probably be fine to use armv7a-none-eabi. I've mostly used arm-unknown-linux-gnueabi since I though it needed to match the bindgen-target (which is -linux-gnu for all architectures) and because from what I understand clang also uses arm-linux-gnueabi [1]. Also when I selected the target I thought that we would also support armv6, but since I had no v6 hardware to test on I disabled it.
I see, thanks for the quick reply!
My current thinking is to enable this only for x86_64 and arm64, so if you/arm decide to switch the target (and/or enable this experimental feature), then that can be done without a rush, independently.
(Please take a look at Russell's reply as well about the short enums thing.)
Cheers,
Miguel
Nathan Chancellor
maintainerQuoted 7 lines · Miguel Ojeda
- Clang passes
-Werror=unused-command-line-argument, which means under arm (i.e. 32-bit) we get:clang: error: argument unused during compilation: '-U arm' [-Werror,-Wunused-command-line-argument]
And under UML I see:
Quoted 7 lines
clang: error: argument unused during compilation: '-I
./arch/um/include/shared' [-Werror,-Wunused-command-line-argument]
clang: error: argument unused during compilation: '-I./arch/x86/um/shared' [-Werror,-Wunused-command-line-argument]
clang: error: argument unused during compilation: '-I./arch/um/include/shared/skas' [-Werror,-Wunused-command-line-argument]So we would need e.g.
-Wno-unused-command-line-argumentthere close to the-Wno-override-moduleone, unlessKbuildor ClangBuiltLinux thinks it is important to keep it for this case.
No, I don't think it is worth trying to make -Wunused-command-line-argument work for this command. Just disable it. This mirrors what is being done for cmd_cc_o_bc in the distributed ThinLTO:
https://lore.kernel.org/[email protected]/
Quoted 7 lines
On the other hand, regardless of whether we fix this (and another issue in a separate email found thanks to the UML build), we could instead add
depends onlisting explicitly the architectures where this is going to be actually tested. That way maintainers can decide whether they want to support it when they are ready. Thoughts?Cc'ing Nathan, Nicolas, Nick, Bill, Justin, David, UML, ARM.
I do agree with some of the concerns that adding an architecure dimension to this is a little complicated. I would rather try to flush out those build problems with patches and keep it enabled for all architectures. At the same time though, I understand that enabling it for the "tier 1" architectures is a low barrier of entry for getting the feature upstream, validated, and distributed to the majority of people that would actually use and depend on it, so I ultimately leave that call up to you.
Quoted 9 lines
- If we use the
.bcextension, we need to add a.gitignorefor.bcfiles, and an exception forkernel/time/timeconst.bc.I guess we will not have too many
bcscripts in the future for that to be a problem. On the other hand, we have the chance to use another extension (either for LLVM bitcode or forbcscripts).But please let me know on e.g. the
Kbuildside if someone has concerns...
No real concern on that front but .gitignore has a command to run when modifying it, which will require a !timeconst.bc in a kernel/time/.gitignore file.
Cheers,
Nathan
Quoted 9 lines · Nathan Chancellor
I do agree with some of the concerns that adding an architecure dimension to this is a little complicated. I would rather try to flush out those build problems with patches and keep it enabled for all architectures. At the same time though, I understand that enabling it for the "tier 1" architectures is a low barrier of entry for getting the feature upstream, validated, and distributed to the majority of people that would actually use and depend on it, so I ultimately leave that call up to you.
Thanks! I agree that it would be ideal to get it clean everywhere, but given it is experimental and that arch maintainers should likely known about this, I think it is best to start simple first.
In fact, let me Cc the x86 and arm64 maintainers so that they are aware.
My current thinking is that I will add:
depends on ARM64 || X86_64
and try to land it this cycle.
My understanding is that this will be used at least by Google, mostly for Android (and mostly arm64, but possibly x86_64 too), so I think at least arm64 will see some actual users on an ongoing basis, i.e. apart from the "synthetic" testing I was doing.
Quoted 3 lines
No real concern on that front but .gitignore has a command to run when modifying it, which will require a !timeconst.bc in a
kernel/time/.gitignorefile.
Yeah, that is the exception I mentioned. Initially I thought about putting it in a local .gitignore, because local is best. But another option, with a different kind of locality, is keeping the exception close to the rule, i.e. in the global one, which has the advantage of showing us all the exceptions easily (and being able to write a comment for all of them at once).
I am not sure what is best clearer, but I am happy to do either:
diff --git a/.gitignore b/.gitignore
index 3a7241c941f5..3044b9590f05 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,6 +13,7 @@
.*
*.a
*.asn1.[ch]
+*.bc
*.bin
*.bz2
*.c.[012]*.*
@@ -184,3 +185,6 @@ sphinx_*/
# Rust analyzer configuration
/rust-project.json
+
+# bc language scripts (not LLVM bitcode)
+!kernel/time/timeconst.bcThanks again!
Cheers,
Miguel
Le 23/03/2026 à 3:21 AM, Miguel Ojeda a écrit :
Quoted 32 lines · On Tue, 03 Feb 2026 11:34:07 +0000 Alice Ryhl <[email protected]> wrote:
Quoted 4 lines
To get rid of these helper symbols, provide functionality to inline helpers into Rust using llvm-link. This option complements full LTO, by being much cheaper and avoiding incompatibility with BTF.
I have been testing this. I think we can go ahead with it, with a few notes.
I will reply to a couple other bindings in separate emails to avoid spamming people too much.
- I will mark the
Kconfigoption as "(EXPERIMENTAL)", since that is what the commit message says and it allows us to be a bit conservative.- Clang passes
-Werror=unused-command-line-argument, which means under arm (i.e. 32-bit) we get:clang: error: argument unused during compilation: '-U arm' [-Werror,-Wunused-command-line-argument]
And under UML I see:
clang: error: argument unused during compilation: '-I
./arch/um/include/shared' [-Werror,-Wunused-command-line-argument]
clang: error: argument unused during compilation: '-I./arch/x86/um/shared' [-Werror,-Wunused-command-line-argument]
clang: error: argument unused during compilation: '-I./arch/um/include/shared/skas' [-Werror,-Wunused-command-line-argument]So we would need e.g.
-Wno-unused-command-line-argumentthere close to the-Wno-override-moduleone, unlessKbuildor ClangBuiltLinux thinks it is important to keep it for this case.
Quoted 7 lines
On the other hand, regardless of whether we fix this (and another issue in a separate email found thanks to the UML build), we could instead add
depends onlisting explicitly the architectures where this is going to be actually tested. That way maintainers can decide whether they want to support it when they are ready. Thoughts?Cc'ing Nathan, Nicolas, Nick, Bill, Justin, David, UML, ARM.
I'm quite happy personally to just have -Wno-unused-command-line-argument here, given that it otherwise is working pretty well on UML and 32-bit UML.
That being said, I expect it won't get a lot of testing (regardless of architecture) while it's hidden behind CONFIG_EXPERT.
Cheers,
-- David
Miguel Ojeda
AppliedQuoted 9 lines · Alice Ryhl
Currently the only way for Rust code to call a static inline function is to go through a helper in
rust/helpers/. This introduces performance costs due to additional function calls and also clutters backtraces and flame graphs with helper symbols.To get rid of these helper symbols, provide functionality to inline helpers into Rust using llvm-link. This option complements full LTO, by being much cheaper and avoiding incompatibility with BTF.
Applied to rust-next -- thanks everyone!
[ Fixed typo. - Miguel ]
[ Andreas writes:
For the rnull driver, enabling helper inlining with this patch gives an average speedup of 2% over the set of 120 workloads that we publish on [2].
This series also uncovered a pre-existing UB instance thanks to an objtool warning which I noticed while testing the series (details in the mailing list).
- Miguel ]
[ Some changes, apart from the rebase:
- Added "(EXPERIMENTAL)" to Kconfig as the commit mentions.
- Added depends on ARM64 || X86_64 and !UML for now, since this is experimental, other architectures may require other changes (e.g. the issues I mentioned in the mailing list for ARM and UML) and they are not really tested so far. So let arch maintainers pick this up if they think it is worth it.
- Gated the cmd_ld_single step also into the new mode, which also means that any possible future objcopy step is done after the translation, as expected.
- Added .gitignore for .bc with exception for existing script.
- Added part-of-* for helpers bitcode files as discussed, and dropped $(if $(filter %_module.bc,$@),-DMODULE) since -DMODULE is already there (would be duplicated otherwise).
- Moved LLVM_LINK to keep binutils list alphabetized.
- Fixed typo in title.
- Dropped second cmd_ld_single commit message paragraph.
- Miguel ]
...to boldly go where no Rust build has gone before.
Cheers,
Miguel