* [PATCH 01/23] scripts/mksysmap: drop the MODULE_INFO() symbols from kallsyms
2026-09-08 20:55 [PATCH 00/23] kbuild: significantly speed up kernel builds Lorenzo Stoakes (ARM)
@ 2026-09-08 20:55 ` Lorenzo Stoakes (ARM)
2026-09-09 19:47 ` Nicolas Schier
` (2 more replies)
2026-09-08 20:55 ` [PATCH 02/23] scripts/mksysmap: fix escape of '$' in the __pi_ pattern Lorenzo Stoakes (ARM)
` (25 subsequent siblings)
26 siblings, 3 replies; 73+ messages in thread
From: Lorenzo Stoakes (ARM) @ 2026-09-08 20:55 UTC (permalink / raw)
To: Linus Torvalds, Nathan Chancellor, Nicolas Schier,
Nick Desaulniers, Bill Wendling, Justin Stitt, Masahiro Yamada,
Alexey Gladkov, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, x86, H. Peter Anvin, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Alexandre Ghiti, Arnd Bergmann, Catalin Marinas,
Will Deacon, Mark Rutland, Ard Biesheuvel, Ilias Apalodimas,
Josh Poimboeuf, Peter Zijlstra, Miguel Ojeda, Boqun Feng,
Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Alice Ryhl, Trevor Gross, Danilo Krummrich, Daniel Almeida,
Tamir Duberstein, Alexandre Courbot, Onur Özkan,
Jonathan Corbet, Randy Dunlap
Cc: linux-kbuild, linux-kernel, llvm, linux-riscv, linux-arch,
linux-arm-kernel, linux-efi, rust-for-linux, linux-doc,
Jens Axboe, Lorenzo Stoakes (ARM)
Commit 3e86e4d74c04 ("kbuild: keep .modinfo section in vmlinux.unstripped")
keeps .modinfo symbols out of System.map and kallsyms, which assumes unique
IDs have a format like '__UNIQUE_ID_modinfo123'.
However, commit afb026b6d35c ("compiler: Tweak __UNIQUE_ID() naming"), sent
in the same cycle, changes this to '__UNIQUE_ID_modinfo_123'.
As a result this regexp has never matched and every kernel since v6.18 has
carried one kallsyms entries for every MODULE_INFO() declaration in the
kernel whether the modules are compiled or not.
That's 5,810 entries for an x86 defconfig build and 15,200 for arm64.
On x86 defconfig that is 113 KiB of kallsyms tables and 32 KiB of bzImage,
and every lookup walks past them.
Fix the pattern.
Fixes: 3e86e4d74c04 ("kbuild: keep .modinfo section in vmlinux.unstripped")
No measurable change in build time, the smaller tables are not on any path
the build waits for.
Assisted-by: LLM
Signed-off-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
---
scripts/mksysmap | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/scripts/mksysmap b/scripts/mksysmap
index c4531eacde20..56a8b8bbdb37 100755
--- a/scripts/mksysmap
+++ b/scripts/mksysmap
@@ -83,7 +83,7 @@
/ _SDA2_BASE_$/d
# MODULE_INFO()
-/ __UNIQUE_ID_modinfo[0-9]*$/d
+/ __UNIQUE_ID_modinfo_[0-9]*$/d
# ---------------------------------------------------------------------------
# Ignored patterns
--
2.55.0
^ permalink raw reply [flat|nested] 73+ messages in thread* Re: [PATCH 01/23] scripts/mksysmap: drop the MODULE_INFO() symbols from kallsyms
2026-09-08 20:55 ` [PATCH 01/23] scripts/mksysmap: drop the MODULE_INFO() symbols from kallsyms Lorenzo Stoakes (ARM)
@ 2026-09-09 19:47 ` Nicolas Schier
2026-09-10 11:00 ` Lorenzo Stoakes (ARM)
2026-09-10 4:19 ` Nathan Chancellor
2026-09-10 19:00 ` Nicolas Schier
2 siblings, 1 reply; 73+ messages in thread
From: Nicolas Schier @ 2026-09-09 19:47 UTC (permalink / raw)
To: Lorenzo Stoakes (ARM)
Cc: Linus Torvalds, Nathan Chancellor, Nicolas Schier,
Nick Desaulniers, Bill Wendling, Justin Stitt, Masahiro Yamada,
Alexey Gladkov, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, x86, H. Peter Anvin, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Alexandre Ghiti, Arnd Bergmann, Catalin Marinas,
Will Deacon, Mark Rutland, Ard Biesheuvel, Ilias Apalodimas,
Josh Poimboeuf, Peter Zijlstra, Miguel Ojeda, Boqun Feng,
Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Alice Ryhl, Trevor Gross, Danilo Krummrich, Daniel Almeida,
Tamir Duberstein, Alexandre Courbot, Onur Özkan,
Jonathan Corbet, Randy Dunlap, linux-kbuild, linux-kernel, llvm,
linux-riscv, linux-arch, linux-arm-kernel, linux-efi,
rust-for-linux, linux-doc, Jens Axboe
> Commit 3e86e4d74c04 ("kbuild: keep .modinfo section in vmlinux.unstripped")
> keeps .modinfo symbols out of System.map and kallsyms, which assumes unique
> IDs have a format like '__UNIQUE_ID_modinfo123'.
>
> However, commit afb026b6d35c ("compiler: Tweak __UNIQUE_ID() naming"), sent
> in the same cycle, changes this to '__UNIQUE_ID_modinfo_123'.
>
> As a result this regexp has never matched and every kernel since v6.18 has
> carried one kallsyms entries for every MODULE_INFO() declaration in the
> kernel whether the modules are compiled or not.
>
> That's 5,810 entries for an x86 defconfig build and 15,200 for arm64.
>
> On x86 defconfig that is 113 KiB of kallsyms tables and 32 KiB of bzImage,
> and every lookup walks past them.
>
> Fix the pattern.
>
> Fixes: 3e86e4d74c04 ("kbuild: keep .modinfo section in vmlinux.unstripped")
>
> No measurable change in build time, the smaller tables are not on any path
> the build waits for.
>
For consistency, it would be nice to have the 'Fixes:' trailer at the
beginning of the trailer block (that is: here.)
> Assisted-by: LLM
> Signed-off-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
>
> diff --git a/scripts/mksysmap b/scripts/mksysmap
> index c4531eacde20..56a8b8bbdb37 100755
> --- a/scripts/mksysmap
> +++ b/scripts/mksysmap
> @@ -83,7 +83,7 @@
> / _SDA2_BASE_$/d
>
> # MODULE_INFO()
> -/ __UNIQUE_ID_modinfo[0-9]*$/d
> +/ __UNIQUE_ID_modinfo_[0-9]*$/d
>
> # ---------------------------------------------------------------------------
> # Ignored patterns
Thanks!
Reviewed-by: Nicolas Schier <nsc@kernel.org>
--
Nicolas
^ permalink raw reply [flat|nested] 73+ messages in thread* Re: [PATCH 01/23] scripts/mksysmap: drop the MODULE_INFO() symbols from kallsyms
2026-09-09 19:47 ` Nicolas Schier
@ 2026-09-10 11:00 ` Lorenzo Stoakes (ARM)
0 siblings, 0 replies; 73+ messages in thread
From: Lorenzo Stoakes (ARM) @ 2026-09-10 11:00 UTC (permalink / raw)
To: Nicolas Schier
Cc: Linus Torvalds, Nathan Chancellor, Nick Desaulniers,
Bill Wendling, Justin Stitt, Masahiro Yamada, Alexey Gladkov,
Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin, Paul Walmsley, Palmer Dabbelt, Albert Ou,
Alexandre Ghiti, Arnd Bergmann, Catalin Marinas, Will Deacon,
Mark Rutland, Ard Biesheuvel, Ilias Apalodimas, Josh Poimboeuf,
Peter Zijlstra, Miguel Ojeda, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, Danilo Krummrich, Daniel Almeida, Tamir Duberstein,
Alexandre Courbot, Onur Özkan, Jonathan Corbet,
Randy Dunlap, linux-kbuild, linux-kernel, llvm, linux-riscv,
linux-arch, linux-arm-kernel, linux-efi, rust-for-linux,
linux-doc, Jens Axboe
On Wed, Sep 09, 2026 at 09:47:18PM +0200, Nicolas Schier wrote:
> > Commit 3e86e4d74c04 ("kbuild: keep .modinfo section in vmlinux.unstripped")
> > keeps .modinfo symbols out of System.map and kallsyms, which assumes unique
> > IDs have a format like '__UNIQUE_ID_modinfo123'.
> >
> > However, commit afb026b6d35c ("compiler: Tweak __UNIQUE_ID() naming"), sent
> > in the same cycle, changes this to '__UNIQUE_ID_modinfo_123'.
> >
> > As a result this regexp has never matched and every kernel since v6.18 has
> > carried one kallsyms entries for every MODULE_INFO() declaration in the
> > kernel whether the modules are compiled or not.
> >
> > That's 5,810 entries for an x86 defconfig build and 15,200 for arm64.
> >
> > On x86 defconfig that is 113 KiB of kallsyms tables and 32 KiB of bzImage,
> > and every lookup walks past them.
> >
> > Fix the pattern.
> >
> > Fixes: 3e86e4d74c04 ("kbuild: keep .modinfo section in vmlinux.unstripped")
> >
> > No measurable change in build time, the smaller tables are not on any path
> > the build waits for.
> >
>
> For consistency, it would be nice to have the 'Fixes:' trailer at the
> beginning of the trailer block (that is: here.)
Yeah sorry my screw up when rejigging the commit message :)
>
> > Assisted-by: LLM
> > Signed-off-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
> >
> > diff --git a/scripts/mksysmap b/scripts/mksysmap
> > index c4531eacde20..56a8b8bbdb37 100755
> > --- a/scripts/mksysmap
> > +++ b/scripts/mksysmap
> > @@ -83,7 +83,7 @@
> > / _SDA2_BASE_$/d
> >
> > # MODULE_INFO()
> > -/ __UNIQUE_ID_modinfo[0-9]*$/d
> > +/ __UNIQUE_ID_modinfo_[0-9]*$/d
> >
> > # ---------------------------------------------------------------------------
> > # Ignored patterns
>
> Thanks!
>
> Reviewed-by: Nicolas Schier <nsc@kernel.org>
Thanks!
>
> --
> Nicolas
>
--
Cheers, Lorenzo
^ permalink raw reply [flat|nested] 73+ messages in thread
* Re: [PATCH 01/23] scripts/mksysmap: drop the MODULE_INFO() symbols from kallsyms
2026-09-08 20:55 ` [PATCH 01/23] scripts/mksysmap: drop the MODULE_INFO() symbols from kallsyms Lorenzo Stoakes (ARM)
2026-09-09 19:47 ` Nicolas Schier
@ 2026-09-10 4:19 ` Nathan Chancellor
2026-09-10 11:03 ` Lorenzo Stoakes (ARM)
2026-09-10 19:00 ` Nicolas Schier
2 siblings, 1 reply; 73+ messages in thread
From: Nathan Chancellor @ 2026-09-10 4:19 UTC (permalink / raw)
To: Lorenzo Stoakes (ARM)
Cc: Linus Torvalds, Nathan Chancellor, Nicolas Schier,
Nick Desaulniers, Bill Wendling, Justin Stitt, Masahiro Yamada,
Alexey Gladkov, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, x86, H. Peter Anvin, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Alexandre Ghiti, Arnd Bergmann, Catalin Marinas,
Will Deacon, Mark Rutland, Ard Biesheuvel, Ilias Apalodimas,
Josh Poimboeuf, Peter Zijlstra, Miguel Ojeda, Boqun Feng,
Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Alice Ryhl, Trevor Gross, Danilo Krummrich, Daniel Almeida,
Tamir Duberstein, Alexandre Courbot, Onur Özkan,
Jonathan Corbet, Randy Dunlap, linux-kbuild, linux-kernel, llvm,
linux-riscv, linux-arch, linux-arm-kernel, linux-efi,
rust-for-linux, linux-doc, Jens Axboe
> Commit 3e86e4d74c04 ("kbuild: keep .modinfo section in vmlinux.unstripped")
> keeps .modinfo symbols out of System.map and kallsyms, which assumes unique
> IDs have a format like '__UNIQUE_ID_modinfo123'.
>
> However, commit afb026b6d35c ("compiler: Tweak __UNIQUE_ID() naming"), sent
> in the same cycle, changes this to '__UNIQUE_ID_modinfo_123'.
>
> As a result this regexp has never matched and every kernel since v6.18 has
> carried one kallsyms entries for every MODULE_INFO() declaration in the
> kernel whether the modules are compiled or not.
>
> That's 5,810 entries for an x86 defconfig build and 15,200 for arm64.
>
> On x86 defconfig that is 113 KiB of kallsyms tables and 32 KiB of bzImage,
> and every lookup walks past them.
Bummer :/
> Fix the pattern.
>
> Fixes: 3e86e4d74c04 ("kbuild: keep .modinfo section in vmlinux.unstripped")
>
> No measurable change in build time, the smaller tables are not on any path
> the build waits for.
I don't think this needs to be in the commit message, it could be below
the fold, since it appears to be a valid fix on its own. Agree with
Nicolas on adjusting the Fixes tag.
Is this worth Cc'ing stable for?
> Assisted-by: LLM
> Signed-off-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
Reviewed-by: Nathan Chancellor <nathan@kernel.org>
--
Cheers,
Nathan
^ permalink raw reply [flat|nested] 73+ messages in thread* Re: [PATCH 01/23] scripts/mksysmap: drop the MODULE_INFO() symbols from kallsyms
2026-09-10 4:19 ` Nathan Chancellor
@ 2026-09-10 11:03 ` Lorenzo Stoakes (ARM)
0 siblings, 0 replies; 73+ messages in thread
From: Lorenzo Stoakes (ARM) @ 2026-09-10 11:03 UTC (permalink / raw)
To: Nathan Chancellor
Cc: Linus Torvalds, Nicolas Schier, Nick Desaulniers, Bill Wendling,
Justin Stitt, Masahiro Yamada, Alexey Gladkov, Thomas Gleixner,
Ingo Molnar, Borislav Petkov, Dave Hansen, x86, H. Peter Anvin,
Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti,
Arnd Bergmann, Catalin Marinas, Will Deacon, Mark Rutland,
Ard Biesheuvel, Ilias Apalodimas, Josh Poimboeuf, Peter Zijlstra,
Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Daniel Almeida, Tamir Duberstein,
Alexandre Courbot, Onur Özkan, Jonathan Corbet,
Randy Dunlap, linux-kbuild, linux-kernel, llvm, linux-riscv,
linux-arch, linux-arm-kernel, linux-efi, rust-for-linux,
linux-doc, Jens Axboe
On Wed, Sep 09, 2026 at 09:19:12PM -0700, Nathan Chancellor wrote:
> > Commit 3e86e4d74c04 ("kbuild: keep .modinfo section in vmlinux.unstripped")
> > keeps .modinfo symbols out of System.map and kallsyms, which assumes unique
> > IDs have a format like '__UNIQUE_ID_modinfo123'.
> >
> > However, commit afb026b6d35c ("compiler: Tweak __UNIQUE_ID() naming"), sent
> > in the same cycle, changes this to '__UNIQUE_ID_modinfo_123'.
> >
> > As a result this regexp has never matched and every kernel since v6.18 has
> > carried one kallsyms entries for every MODULE_INFO() declaration in the
> > kernel whether the modules are compiled or not.
> >
> > That's 5,810 entries for an x86 defconfig build and 15,200 for arm64.
> >
> > On x86 defconfig that is 113 KiB of kallsyms tables and 32 KiB of bzImage,
> > and every lookup walks past them.
>
> Bummer :/
Yeah super easy to miss though I think!
>
> > Fix the pattern.
> >
> > Fixes: 3e86e4d74c04 ("kbuild: keep .modinfo section in vmlinux.unstripped")
> >
> > No measurable change in build time, the smaller tables are not on any path
> > the build waits for.
>
> I don't think this needs to be in the commit message, it could be below
> the fold, since it appears to be a valid fix on its own. Agree with
> Nicolas on adjusting the Fixes tag.
Ack yeah I screwed this bit up sorry!
>
> Is this worth Cc'ing stable for?
Yeah I wasn't sure, it's not like it breaks anything but you see stuff in
kallsyms that you shouldn't.
So I'd say probably on balance yes we should actually?
>
> > Assisted-by: LLM
> > Signed-off-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
>
> Reviewed-by: Nathan Chancellor <nathan@kernel.org>
Thanks!
>
> --
> Cheers,
> Nathan
>
--
Cheers, Lorenzo
^ permalink raw reply [flat|nested] 73+ messages in thread
* Re: [PATCH 01/23] scripts/mksysmap: drop the MODULE_INFO() symbols from kallsyms
2026-09-08 20:55 ` [PATCH 01/23] scripts/mksysmap: drop the MODULE_INFO() symbols from kallsyms Lorenzo Stoakes (ARM)
2026-09-09 19:47 ` Nicolas Schier
2026-09-10 4:19 ` Nathan Chancellor
@ 2026-09-10 19:00 ` Nicolas Schier
2 siblings, 0 replies; 73+ messages in thread
From: Nicolas Schier @ 2026-09-10 19:00 UTC (permalink / raw)
To: Linus Torvalds, Nathan Chancellor, Nick Desaulniers,
Bill Wendling, Justin Stitt, Masahiro Yamada, Alexey Gladkov,
Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin, Paul Walmsley, Palmer Dabbelt, Albert Ou,
Alexandre Ghiti, Arnd Bergmann, Catalin Marinas, Will Deacon,
Mark Rutland, Ard Biesheuvel, Ilias Apalodimas, Josh Poimboeuf,
Peter Zijlstra, Miguel Ojeda, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, Danilo Krummrich, Daniel Almeida, Tamir Duberstein,
Alexandre Courbot, Onur Özkan, Jonathan Corbet,
Randy Dunlap, Lorenzo Stoakes (ARM)
Cc: Nicolas Schier, linux-kbuild, linux-kernel, llvm, linux-riscv,
linux-arch, linux-arm-kernel, linux-efi, rust-for-linux,
linux-doc, Jens Axboe
On Tue, 08 Sep 2026 21:55:01 +0100, Lorenzo Stoakes (ARM) wrote:
> Commit 3e86e4d74c04 ("kbuild: keep .modinfo section in vmlinux.unstripped")
> keeps .modinfo symbols out of System.map and kallsyms, which assumes unique
> IDs have a format like '__UNIQUE_ID_modinfo123'.
>
> However, commit afb026b6d35c ("compiler: Tweak __UNIQUE_ID() naming"), sent
> in the same cycle, changes this to '__UNIQUE_ID_modinfo_123'.
>
> [...]
Applied to kbuild/linux.git (kbuild-fixes-unstable), thanks!
[01/23] scripts/mksysmap: drop the MODULE_INFO() symbols from kallsyms
https://git.kernel.org/kbuild/c/3e2276de
Please look out for regression or issue reports or other follow up
comments, as they may result in the patch/series getting dropped,
reverted or modified (e.g. trailers).
The patch is applied to the kbuild-fixes-unstable branch and accepted
pending wider testing in linux-next and any post-commit review; it will
generally be moved to the kbuild-fixes branch within about a week if no
issues are found.
Best regards,
--
Nicolas
^ permalink raw reply [flat|nested] 73+ messages in thread
* [PATCH 02/23] scripts/mksysmap: fix escape of '$' in the __pi_ pattern
2026-09-08 20:55 [PATCH 00/23] kbuild: significantly speed up kernel builds Lorenzo Stoakes (ARM)
2026-09-08 20:55 ` [PATCH 01/23] scripts/mksysmap: drop the MODULE_INFO() symbols from kallsyms Lorenzo Stoakes (ARM)
@ 2026-09-08 20:55 ` Lorenzo Stoakes (ARM)
2026-09-09 19:47 ` Nicolas Schier
` (2 more replies)
2026-09-08 20:55 ` [PATCH 03/23] kallsyms: index symbols by token to speed up table compression Lorenzo Stoakes (ARM)
` (24 subsequent siblings)
26 siblings, 3 replies; 73+ messages in thread
From: Lorenzo Stoakes (ARM) @ 2026-09-08 20:55 UTC (permalink / raw)
To: Linus Torvalds, Nathan Chancellor, Nicolas Schier,
Nick Desaulniers, Bill Wendling, Justin Stitt, Masahiro Yamada,
Alexey Gladkov, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, x86, H. Peter Anvin, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Alexandre Ghiti, Arnd Bergmann, Catalin Marinas,
Will Deacon, Mark Rutland, Ard Biesheuvel, Ilias Apalodimas,
Josh Poimboeuf, Peter Zijlstra, Miguel Ojeda, Boqun Feng,
Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Alice Ryhl, Trevor Gross, Danilo Krummrich, Daniel Almeida,
Tamir Duberstein, Alexandre Courbot, Onur Özkan,
Jonathan Corbet, Randy Dunlap
Cc: linux-kbuild, linux-kernel, llvm, linux-riscv, linux-arch,
linux-arm-kernel, linux-efi, rust-for-linux, linux-doc,
Jens Axboe, Lorenzo Stoakes (ARM)
Commit b18b047002b7 ("kbuild: change scripts/mksysmap into sed script")
converted scripts/mksysmap from a shell script to a sed script.
However an error was made - escaping of '$' required \\ escaping in shell
but only \ in a sed script.
This was mostly corrected in commit 7a6c355b55c0 ("scripts/mksysmap: Fix
escape chars '$'"), but this fix missed arm64 PIE namespace local symbols
like __pi_$x and __pi_$d which appear in System.mapand /proc/kallsyms:
$ grep __pi_\\$ /proc/kallsyms | sort -u
0000000000000000 d __pi_$d
0000000000000000 t __pi_$x
Fix the escaping properly.
Fixes: b18b047002b7 ("kbuild: change scripts/mksysmap into sed script")
No measurable change in build time.
Assisted-by: LLM
Signed-off-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
---
scripts/mksysmap | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/scripts/mksysmap b/scripts/mksysmap
index 56a8b8bbdb37..856b26ba2ac0 100755
--- a/scripts/mksysmap
+++ b/scripts/mksysmap
@@ -35,7 +35,7 @@
/ __efistub_/d
# arm64 local symbols in PIE namespace
-/ __pi_\\$/d
+/ __pi_\$/d
/ __pi_\.L/d
# arm64 local symbols in non-VHE KVM namespace
--
2.55.0
^ permalink raw reply [flat|nested] 73+ messages in thread* Re: [PATCH 02/23] scripts/mksysmap: fix escape of '$' in the __pi_ pattern
2026-09-08 20:55 ` [PATCH 02/23] scripts/mksysmap: fix escape of '$' in the __pi_ pattern Lorenzo Stoakes (ARM)
@ 2026-09-09 19:47 ` Nicolas Schier
2026-09-10 11:04 ` Lorenzo Stoakes (ARM)
2026-09-10 4:19 ` Nathan Chancellor
2026-09-10 19:00 ` Nicolas Schier
2 siblings, 1 reply; 73+ messages in thread
From: Nicolas Schier @ 2026-09-09 19:47 UTC (permalink / raw)
To: Lorenzo Stoakes (ARM)
Cc: Linus Torvalds, Nathan Chancellor, Nicolas Schier,
Nick Desaulniers, Bill Wendling, Justin Stitt, Masahiro Yamada,
Alexey Gladkov, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, x86, H. Peter Anvin, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Alexandre Ghiti, Arnd Bergmann, Catalin Marinas,
Will Deacon, Mark Rutland, Ard Biesheuvel, Ilias Apalodimas,
Josh Poimboeuf, Peter Zijlstra, Miguel Ojeda, Boqun Feng,
Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Alice Ryhl, Trevor Gross, Danilo Krummrich, Daniel Almeida,
Tamir Duberstein, Alexandre Courbot, Onur Özkan,
Jonathan Corbet, Randy Dunlap, linux-kbuild, linux-kernel, llvm,
linux-riscv, linux-arch, linux-arm-kernel, linux-efi,
rust-for-linux, linux-doc, Jens Axboe
> Commit b18b047002b7 ("kbuild: change scripts/mksysmap into sed script")
> converted scripts/mksysmap from a shell script to a sed script.
>
> However an error was made - escaping of '$' required \\ escaping in shell
> but only \ in a sed script.
>
> This was mostly corrected in commit 7a6c355b55c0 ("scripts/mksysmap: Fix
> escape chars '$'"), but this fix missed arm64 PIE namespace local symbols
> like __pi_$x and __pi_$d which appear in System.mapand /proc/kallsyms:
>
> $ grep __pi_\\$ /proc/kallsyms | sort -u
> 0000000000000000 d __pi_$d
> 0000000000000000 t __pi_$x
>
> Fix the escaping properly.
>
> Fixes: b18b047002b7 ("kbuild: change scripts/mksysmap into sed script")
>
> No measurable change in build time.
>
> Assisted-by: LLM
> Signed-off-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
>
> diff --git a/scripts/mksysmap b/scripts/mksysmap
> index 56a8b8bbdb37..856b26ba2ac0 100755
> --- a/scripts/mksysmap
> +++ b/scripts/mksysmap
> @@ -35,7 +35,7 @@
> / __efistub_/d
>
> # arm64 local symbols in PIE namespace
> -/ __pi_\\$/d
> +/ __pi_\$/d
> / __pi_\.L/d
>
> # arm64 local symbols in non-VHE KVM namespace
(Move 'Fixes:' to trailer block)
Reviewed-by: Nicolas Schier <nsc@kernel.org>
--
Nicolas
^ permalink raw reply [flat|nested] 73+ messages in thread* Re: [PATCH 02/23] scripts/mksysmap: fix escape of '$' in the __pi_ pattern
2026-09-09 19:47 ` Nicolas Schier
@ 2026-09-10 11:04 ` Lorenzo Stoakes (ARM)
0 siblings, 0 replies; 73+ messages in thread
From: Lorenzo Stoakes (ARM) @ 2026-09-10 11:04 UTC (permalink / raw)
To: Nicolas Schier
Cc: Linus Torvalds, Nathan Chancellor, Nick Desaulniers,
Bill Wendling, Justin Stitt, Masahiro Yamada, Alexey Gladkov,
Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin, Paul Walmsley, Palmer Dabbelt, Albert Ou,
Alexandre Ghiti, Arnd Bergmann, Catalin Marinas, Will Deacon,
Mark Rutland, Ard Biesheuvel, Ilias Apalodimas, Josh Poimboeuf,
Peter Zijlstra, Miguel Ojeda, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, Danilo Krummrich, Daniel Almeida, Tamir Duberstein,
Alexandre Courbot, Onur Özkan, Jonathan Corbet,
Randy Dunlap, linux-kbuild, linux-kernel, llvm, linux-riscv,
linux-arch, linux-arm-kernel, linux-efi, rust-for-linux,
linux-doc, Jens Axboe
On Wed, Sep 09, 2026 at 09:47:18PM +0200, Nicolas Schier wrote:
> > Commit b18b047002b7 ("kbuild: change scripts/mksysmap into sed script")
> > converted scripts/mksysmap from a shell script to a sed script.
> >
> > However an error was made - escaping of '$' required \\ escaping in shell
> > but only \ in a sed script.
> >
> > This was mostly corrected in commit 7a6c355b55c0 ("scripts/mksysmap: Fix
> > escape chars '$'"), but this fix missed arm64 PIE namespace local symbols
> > like __pi_$x and __pi_$d which appear in System.mapand /proc/kallsyms:
> >
> > $ grep __pi_\\$ /proc/kallsyms | sort -u
> > 0000000000000000 d __pi_$d
> > 0000000000000000 t __pi_$x
> >
> > Fix the escaping properly.
> >
> > Fixes: b18b047002b7 ("kbuild: change scripts/mksysmap into sed script")
> >
> > No measurable change in build time.
> >
> > Assisted-by: LLM
> > Signed-off-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
> >
> > diff --git a/scripts/mksysmap b/scripts/mksysmap
> > index 56a8b8bbdb37..856b26ba2ac0 100755
> > --- a/scripts/mksysmap
> > +++ b/scripts/mksysmap
> > @@ -35,7 +35,7 @@
> > / __efistub_/d
> >
> > # arm64 local symbols in PIE namespace
> > -/ __pi_\\$/d
> > +/ __pi_\$/d
> > / __pi_\.L/d
> >
> > # arm64 local symbols in non-VHE KVM namespace
>
> (Move 'Fixes:' to trailer block)
Yeah similar screw up. I think it was when perf times were appended
per-patch but then then ended up on the fix patches + I yanked them bit
didn't correct the tags, ugh!
>
> Reviewed-by: Nicolas Schier <nsc@kernel.org>
Thanks! :)
>
> --
> Nicolas
>
--
Cheers, Lorenzo
^ permalink raw reply [flat|nested] 73+ messages in thread
* Re: [PATCH 02/23] scripts/mksysmap: fix escape of '$' in the __pi_ pattern
2026-09-08 20:55 ` [PATCH 02/23] scripts/mksysmap: fix escape of '$' in the __pi_ pattern Lorenzo Stoakes (ARM)
2026-09-09 19:47 ` Nicolas Schier
@ 2026-09-10 4:19 ` Nathan Chancellor
2026-09-10 11:21 ` Lorenzo Stoakes (ARM)
2026-09-10 19:00 ` Nicolas Schier
2 siblings, 1 reply; 73+ messages in thread
From: Nathan Chancellor @ 2026-09-10 4:19 UTC (permalink / raw)
To: Lorenzo Stoakes (ARM)
Cc: Linus Torvalds, Nathan Chancellor, Nicolas Schier,
Nick Desaulniers, Bill Wendling, Justin Stitt, Masahiro Yamada,
Alexey Gladkov, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, x86, H. Peter Anvin, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Alexandre Ghiti, Arnd Bergmann, Catalin Marinas,
Will Deacon, Mark Rutland, Ard Biesheuvel, Ilias Apalodimas,
Josh Poimboeuf, Peter Zijlstra, Miguel Ojeda, Boqun Feng,
Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Alice Ryhl, Trevor Gross, Danilo Krummrich, Daniel Almeida,
Tamir Duberstein, Alexandre Courbot, Onur Özkan,
Jonathan Corbet, Randy Dunlap, linux-kbuild, linux-kernel, llvm,
linux-riscv, linux-arch, linux-arm-kernel, linux-efi,
rust-for-linux, linux-doc, Jens Axboe
> Commit b18b047002b7 ("kbuild: change scripts/mksysmap into sed script")
> converted scripts/mksysmap from a shell script to a sed script.
>
> However an error was made - escaping of '$' required \\ escaping in shell
> but only \ in a sed script.
>
> This was mostly corrected in commit 7a6c355b55c0 ("scripts/mksysmap: Fix
> escape chars '$'"), but this fix missed arm64 PIE namespace local symbols
> like __pi_$x and __pi_$d which appear in System.mapand /proc/kallsyms:
>
> $ grep __pi_\\$ /proc/kallsyms | sort -u
> 0000000000000000 d __pi_$d
> 0000000000000000 t __pi_$x
>
> Fix the escaping properly.
>
> Fixes: b18b047002b7 ("kbuild: change scripts/mksysmap into sed script")
>
> No measurable change in build time.
Same comments as before.
> Assisted-by: LLM
> Signed-off-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
Same thing, is this worth taking to stable? Truthfully, you could
probably send these two changes separately from the series so that we
could send them to Linus with the other pending Kbuild fixes, unless
there are objections from Linus or Nicolas on taking that route? Shaving
two patches off of an ~20 patch series is a 10% reduction so seems worth
it?
I'll review some of the other low hanging fruit now so that we can start
chunking this series out, since I too care deeply about build times.
--
Cheers,
Nathan
^ permalink raw reply [flat|nested] 73+ messages in thread* Re: [PATCH 02/23] scripts/mksysmap: fix escape of '$' in the __pi_ pattern
2026-09-10 4:19 ` Nathan Chancellor
@ 2026-09-10 11:21 ` Lorenzo Stoakes (ARM)
0 siblings, 0 replies; 73+ messages in thread
From: Lorenzo Stoakes (ARM) @ 2026-09-10 11:21 UTC (permalink / raw)
To: Nathan Chancellor
Cc: Linus Torvalds, Nicolas Schier, Nick Desaulniers, Bill Wendling,
Justin Stitt, Masahiro Yamada, Alexey Gladkov, Thomas Gleixner,
Ingo Molnar, Borislav Petkov, Dave Hansen, x86, H. Peter Anvin,
Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti,
Arnd Bergmann, Catalin Marinas, Will Deacon, Mark Rutland,
Ard Biesheuvel, Ilias Apalodimas, Josh Poimboeuf, Peter Zijlstra,
Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Daniel Almeida, Tamir Duberstein,
Alexandre Courbot, Onur Özkan, Jonathan Corbet,
Randy Dunlap, linux-kbuild, linux-kernel, llvm, linux-riscv,
linux-arch, linux-arm-kernel, linux-efi, rust-for-linux,
linux-doc, Jens Axboe
On Wed, Sep 09, 2026 at 09:19:12PM -0700, Nathan Chancellor wrote:
> > Commit b18b047002b7 ("kbuild: change scripts/mksysmap into sed script")
> > converted scripts/mksysmap from a shell script to a sed script.
> >
> > However an error was made - escaping of '$' required \\ escaping in shell
> > but only \ in a sed script.
> >
> > This was mostly corrected in commit 7a6c355b55c0 ("scripts/mksysmap: Fix
> > escape chars '$'"), but this fix missed arm64 PIE namespace local symbols
> > like __pi_$x and __pi_$d which appear in System.mapand /proc/kallsyms:
> >
> > $ grep __pi_\\$ /proc/kallsyms | sort -u
> > 0000000000000000 d __pi_$d
> > 0000000000000000 t __pi_$x
> >
> > Fix the escaping properly.
> >
> > Fixes: b18b047002b7 ("kbuild: change scripts/mksysmap into sed script")
> >
> > No measurable change in build time.
>
> Same comments as before.
Yeah sorry same screw up!
>
> > Assisted-by: LLM
> > Signed-off-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
>
> Same thing, is this worth taking to stable? Truthfully, you could
Ack I think it's probably worth it yes like patch 1.
> probably send these two changes separately from the series so that we
> could send them to Linus with the other pending Kbuild fixes, unless
> there are objections from Linus or Nicolas on taking that route? Shaving
> two patches off of an ~20 patch series is a 10% reduction so seems worth
> it?
Patch 8 relies on 1 + 2, so as long as that ordering was enforced then that's
OK.
I mean if these go as hotfixes then we naturally get that ordering of cousre!
>
> I'll review some of the other low hanging fruit now so that we can start
> chunking this series out, since I too care deeply about build times.
Thanks!
>
> --
> Cheers,
> Nathan
>
--
Cheers, Lorenzo
^ permalink raw reply [flat|nested] 73+ messages in thread
* Re: [PATCH 02/23] scripts/mksysmap: fix escape of '$' in the __pi_ pattern
2026-09-08 20:55 ` [PATCH 02/23] scripts/mksysmap: fix escape of '$' in the __pi_ pattern Lorenzo Stoakes (ARM)
2026-09-09 19:47 ` Nicolas Schier
2026-09-10 4:19 ` Nathan Chancellor
@ 2026-09-10 19:00 ` Nicolas Schier
2 siblings, 0 replies; 73+ messages in thread
From: Nicolas Schier @ 2026-09-10 19:00 UTC (permalink / raw)
To: Linus Torvalds, Nathan Chancellor, Nick Desaulniers,
Bill Wendling, Justin Stitt, Masahiro Yamada, Alexey Gladkov,
Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin, Paul Walmsley, Palmer Dabbelt, Albert Ou,
Alexandre Ghiti, Arnd Bergmann, Catalin Marinas, Will Deacon,
Mark Rutland, Ard Biesheuvel, Ilias Apalodimas, Josh Poimboeuf,
Peter Zijlstra, Miguel Ojeda, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, Danilo Krummrich, Daniel Almeida, Tamir Duberstein,
Alexandre Courbot, Onur Özkan, Jonathan Corbet,
Randy Dunlap, Lorenzo Stoakes (ARM)
Cc: Nicolas Schier, linux-kbuild, linux-kernel, llvm, linux-riscv,
linux-arch, linux-arm-kernel, linux-efi, rust-for-linux,
linux-doc, Jens Axboe
On Tue, 08 Sep 2026 21:55:02 +0100, Lorenzo Stoakes (ARM) wrote:
> Commit b18b047002b7 ("kbuild: change scripts/mksysmap into sed script")
> converted scripts/mksysmap from a shell script to a sed script.
>
> However an error was made - escaping of '$' required \\ escaping in shell
> but only \ in a sed script.
>
> This was mostly corrected in commit 7a6c355b55c0 ("scripts/mksysmap: Fix
> escape chars '$'"), but this fix missed arm64 PIE namespace local symbols
> like __pi_$x and __pi_$d which appear in System.mapand /proc/kallsyms:
>
> [...]
Applied to kbuild/linux.git (kbuild-fixes-unstable), thanks!
[02/23] scripts/mksysmap: fix escape of '$' in the __pi_ pattern
https://git.kernel.org/kbuild/c/b99d1f50
Please look out for regression or issue reports or other follow up
comments, as they may result in the patch/series getting dropped,
reverted or modified (e.g. trailers).
The patch is applied to the kbuild-fixes-unstable branch and accepted
pending wider testing in linux-next and any post-commit review; it will
generally be moved to the kbuild-fixes branch within about a week if no
issues are found.
Best regards,
--
Nicolas
^ permalink raw reply [flat|nested] 73+ messages in thread
* [PATCH 03/23] kallsyms: index symbols by token to speed up table compression
2026-09-08 20:55 [PATCH 00/23] kbuild: significantly speed up kernel builds Lorenzo Stoakes (ARM)
2026-09-08 20:55 ` [PATCH 01/23] scripts/mksysmap: drop the MODULE_INFO() symbols from kallsyms Lorenzo Stoakes (ARM)
2026-09-08 20:55 ` [PATCH 02/23] scripts/mksysmap: fix escape of '$' in the __pi_ pattern Lorenzo Stoakes (ARM)
@ 2026-09-08 20:55 ` Lorenzo Stoakes (ARM)
2026-09-08 20:55 ` [PATCH 04/23] kallsyms: output binary data to speed output and kallsyms assembly Lorenzo Stoakes (ARM)
` (23 subsequent siblings)
26 siblings, 0 replies; 73+ messages in thread
From: Lorenzo Stoakes (ARM) @ 2026-09-08 20:55 UTC (permalink / raw)
To: Linus Torvalds, Nathan Chancellor, Nicolas Schier,
Nick Desaulniers, Bill Wendling, Justin Stitt, Masahiro Yamada,
Alexey Gladkov, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, x86, H. Peter Anvin, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Alexandre Ghiti, Arnd Bergmann, Catalin Marinas,
Will Deacon, Mark Rutland, Ard Biesheuvel, Ilias Apalodimas,
Josh Poimboeuf, Peter Zijlstra, Miguel Ojeda, Boqun Feng,
Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Alice Ryhl, Trevor Gross, Danilo Krummrich, Daniel Almeida,
Tamir Duberstein, Alexandre Courbot, Onur Özkan,
Jonathan Corbet, Randy Dunlap
Cc: linux-kbuild, linux-kernel, llvm, linux-riscv, linux-arch,
linux-arm-kernel, linux-efi, rust-for-linux, linux-doc,
Jens Axboe, Lorenzo Stoakes (ARM)
The kallsyms program compresses symbols by figuring out the most commonly
used substrings in all of the input symbols then uses special character
codes to represent them.
For instance, 0xf7 might end up representing "write_", then every single
symbol that contains "write_" can use 0xf7 as a shorthand and save 5 bytes
each time.
'Special' character codes are any byte value that is not used in any
symbol, either due to being an invalid character, or not being present in
any symbol (e.g. if no symbol contains 'z', then 'z' can be used as special
character).
It does this by first figuring out which special characters are available
in insert_real_symbols_in_table(), then iterating through every available
special character, counting how many times each pair of adjacent characters
appear in symbols in build_initial_token_table().
These adjacent pairs are known as 'tokens'.
Token counts are initially obtained by build_initial_token_table(), then
optimize_result() calls find_best_token() to determine the token that
appeared the most number of times and assigns it the next special
character.
Finally, optimize_result() calls compress_symbols() to replace every token
in every symbol with its special character, which updates token_profit[] as
it does so.
This process is repeated for each remaining available special character,
with tokens now perhaps containing previously assigned special
characters (e.g. if 'wr' was assigned 0x80, then the token representing
'wri' would be '\x80i').
This compresses that token by 50% in each symbol it appears in (two bytes
are now represented by one) and thus by repeatedly doing this kallsyms
obtains good symbol compression.
However, compress_symbols() is seriously inefficient - it iterates through
EVERY symbol for EVERY special character assignment, i.e. ~256 *
nr_symbols.
Modern x86-64 kernels, for instance, have ~158,000 symbols, so millions of
iterations are performed, most of which are entirely unnecessary (tokens
don't appear in most symbols).
In practice kallsyms spends half its runtime doing this, two or three times
per vmlinux link step.
Fix this by tracking which symbols each token appears in token_syms[], and
only compress symbols which actually need to be updated.
Each time a token is compressed that token can no longer appear in any
symbol, so that token_syms[] entry can be freed.
However new token_syms[] entries must be created for each new token
containing the assigned special character, but this is bounded by the
number of replacements in the symbol which is very small.
In testing on an x86-64 platform using clang, each kallsyms invocation
dropped from 0.59s to 0.33s with CONFIG_KALLSYMS_ALL set and from 0.38s to
0.22s without it set.
The data was carefully checked and verified to be byte-for-byte identical
for six symbol sets (two vmlinux passes, vmlinux.o, three userspace
binaries) with all option combinations.
As part of this change, additionally refactor the code to be a little
easier to follow.
kallsyms runs two or three times on the serial tail of every build that
links vmlinux, no-op builds do not link and are unchanged.
Whole build, 128-thread Threadripper 9980X, best of N runs:
before after delta
-------------------------------
x86 defconfig, touch mm/vma.c, gcc 11.4s 10.8s -0.55s (-5%)
x86 defconfig, touch mm/vma.c, clang 11.4s 10.7s -0.66s (-6%)
x86 defconfig, clean, gcc 30.3s 29.5s -0.80s (-3%)
x86 defconfig, clean, clang 30.3s 29.7s -0.62s (-2%)
x86 allmodconfig, touch mm/vma.c, gcc 46.2s 45.3s -0.91s (-2%)
x86 allmodconfig, touch mm/vma.c, clang 44.2s 42.9s -1.3s (-3%)
Assisted-by: LLM
Signed-off-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
---
scripts/kallsyms.c | 138 +++++++++++++++++++++++++++++++++++++++++++++++------
1 file changed, 124 insertions(+), 14 deletions(-)
diff --git a/scripts/kallsyms.c b/scripts/kallsyms.c
index 494852ade6d8..350d118c3b9e 100644
--- a/scripts/kallsyms.c
+++ b/scripts/kallsyms.c
@@ -58,12 +58,47 @@ static unsigned int table_size, table_cnt;
static int all_symbols;
static int pc_relative;
+/* A dynamic array of symbols, encoded by symbol index. */
+struct sym_arr {
+ unsigned int *sym_indexes;
+ unsigned int cnt, cap;
+};
+
static int token_profit[0x10000];
+static struct sym_arr token_syms[0x10000];
/* the table that holds the result of the compression */
static unsigned char best_table[256][2];
static unsigned char best_table_len[256];
+static unsigned int sym_arr_last(const struct sym_arr *arr)
+{
+ return arr->cnt ? arr->sym_indexes[arr->cnt - 1] : UINT_MAX;
+}
+
+static void sym_arr_maybe_expand(struct sym_arr *arr)
+{
+ if (arr->cap > arr->cnt)
+ return;
+
+ arr->cap = arr->cap ? arr->cap * 2 : 16;
+ arr->sym_indexes = xrealloc(arr->sym_indexes,
+ arr->cap * sizeof(*arr->sym_indexes));
+}
+
+static void sym_arr_add(struct sym_arr *arr, unsigned int sym_idx)
+{
+ sym_arr_maybe_expand(arr);
+ arr->sym_indexes[arr->cnt++] = sym_idx;
+}
+
+static void sym_arr_free(struct sym_arr *arr)
+{
+ free(arr->sym_indexes);
+ arr->sym_indexes = NULL;
+ arr->cnt = 0;
+ arr->cap = 0;
+}
static void usage(void)
{
@@ -458,6 +493,15 @@ static void write_src(void)
printf("\n");
}
+static unsigned int token_index(unsigned char first, unsigned char second)
+{
+ return first + (second << 8);
+}
+
+static unsigned int sym_token_index(const unsigned char *symbol, int first_idx)
+{
+ return token_index(symbol[first_idx], symbol[first_idx + 1]);
+}
/* table lookup compression functions */
@@ -467,7 +511,7 @@ static void learn_symbol(const unsigned char *symbol, int len)
int i;
for (i = 0; i < len - 1; i++)
- token_profit[ symbol[i] + (symbol[i + 1] << 8) ]++;
+ token_profit[sym_token_index(symbol, i)]++;
}
/* decrease the count for all the possible tokens in a symbol */
@@ -476,16 +520,76 @@ static void forget_symbol(const unsigned char *symbol, int len)
int i;
for (i = 0; i < len - 1; i++)
- token_profit[ symbol[i] + (symbol[i + 1] << 8) ]--;
+ token_profit[sym_token_index(symbol, i)]--;
+}
+
+static void token_add_symbol(unsigned int token_idx, unsigned int sym_idx)
+{
+ struct sym_arr *arr = &token_syms[token_idx];
+
+ /* Symbol indexes kept in sorted order, check for duplicate. */
+ if (sym_arr_last(arr) == sym_idx)
+ return;
+
+ sym_arr_add(arr, sym_idx);
+}
+
+static void symbol_index_all_tokens(const unsigned char *symbol, int len,
+ unsigned int sym_idx)
+{
+ int i;
+
+ for (i = 0; i < len - 1; i++) {
+ const unsigned int token_idx = sym_token_index(symbol, i);
+
+ token_add_symbol(token_idx, sym_idx);
+ }
+}
+
+/*
+ * The symbol just got compressed. The only parts of the symbol that changed
+ * meaningfully are those containing the newly assigned compressed char, so
+ * index those.
+ */
+static void symbol_index_new_tokens(const unsigned char *symbol, int len,
+ unsigned int sym_idx, int compressed_chr)
+{
+ int i;
+
+ for (i = 0; i < len - 1; i++) {
+ const unsigned int token_idx = sym_token_index(symbol, i);
+
+ if (symbol[i] == compressed_chr ||
+ symbol[i + 1] == compressed_chr)
+ token_add_symbol(token_idx, sym_idx);
+ }
}
-/* do the initial token count */
static void build_initial_token_table(void)
{
unsigned int i;
for (i = 0; i < table_cnt; i++)
learn_symbol(table[i]->sym, table[i]->len);
+
+ /*
+ * The initial occurrence counts tell us exactly how much memory should
+ * be reserved for each token's symbol array.
+ */
+ for (i = 0; i < ARRAY_SIZE(token_syms); i++) {
+ const int nr_syms = token_profit[i];
+
+ if (!nr_syms)
+ continue;
+
+ token_syms[i].cap = nr_syms;
+ token_syms[i].sym_indexes =
+ xmalloc(nr_syms * sizeof(unsigned int));
+ }
+
+ /* For every symbol, index every token -> symbol it is present in. */
+ for (i = 0; i < table_cnt; i++)
+ symbol_index_all_tokens(table[i]->sym, table[i]->len, i);
}
static unsigned char *find_token(unsigned char *str, int len,
@@ -502,27 +606,30 @@ static unsigned char *find_token(unsigned char *str, int len,
/* replace a given token in all the valid symbols. Use the sampled symbols
* to update the counts */
-static void compress_symbols(const unsigned char *str, int idx)
+static void compress_symbols(const unsigned char *str, int compressed_chr)
{
- unsigned int i, len, size;
+ const unsigned int token_idx = sym_token_index(str, 0);
+ struct sym_arr *arr = &token_syms[token_idx];
+ unsigned int sym_idx, j, len, size;
unsigned char *p1, *p2;
- for (i = 0; i < table_cnt; i++) {
+ /* Iterate through all symbols this token is found in and compress. */
+ for (j = 0; j < arr->cnt; j++) {
+ sym_idx = arr->sym_indexes[j];
- len = table[i]->len;
- p1 = table[i]->sym;
+ len = table[sym_idx]->len;
+ p1 = table[sym_idx]->sym;
- /* find the token on the symbol */
p2 = find_token(p1, len, str);
if (!p2) continue;
/* decrease the counts for this symbol's tokens */
- forget_symbol(table[i]->sym, len);
+ forget_symbol(table[sym_idx]->sym, len);
size = len;
do {
- *p2 = idx;
+ *p2 = compressed_chr;
p2++;
size -= (p2 - p1);
memmove(p2, p2 + 1, size);
@@ -536,11 +643,14 @@ static void compress_symbols(const unsigned char *str, int idx)
} while (p2);
- table[i]->len = len;
+ table[sym_idx]->len = len;
- /* increase the counts for this symbol's new tokens */
- learn_symbol(table[i]->sym, len);
+ learn_symbol(table[sym_idx]->sym, len);
+ symbol_index_new_tokens(table[sym_idx]->sym, len, sym_idx,
+ compressed_chr);
}
+
+ sym_arr_free(arr); /* No symbol contains this token any more. */
}
/* search the token with the maximum profit */
--
2.55.0
^ permalink raw reply [flat|nested] 73+ messages in thread* [PATCH 04/23] kallsyms: output binary data to speed output and kallsyms assembly
2026-09-08 20:55 [PATCH 00/23] kbuild: significantly speed up kernel builds Lorenzo Stoakes (ARM)
` (2 preceding siblings ...)
2026-09-08 20:55 ` [PATCH 03/23] kallsyms: index symbols by token to speed up table compression Lorenzo Stoakes (ARM)
@ 2026-09-08 20:55 ` Lorenzo Stoakes (ARM)
2026-09-09 14:35 ` Linus Torvalds
2026-09-10 9:29 ` David Laight
2026-09-08 20:55 ` [PATCH 05/23] kbuild: do not sort nm output where the order is irrelevant Lorenzo Stoakes (ARM)
` (22 subsequent siblings)
26 siblings, 2 replies; 73+ messages in thread
From: Lorenzo Stoakes (ARM) @ 2026-09-08 20:55 UTC (permalink / raw)
To: Linus Torvalds, Nathan Chancellor, Nicolas Schier,
Nick Desaulniers, Bill Wendling, Justin Stitt, Masahiro Yamada,
Alexey Gladkov, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, x86, H. Peter Anvin, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Alexandre Ghiti, Arnd Bergmann, Catalin Marinas,
Will Deacon, Mark Rutland, Ard Biesheuvel, Ilias Apalodimas,
Josh Poimboeuf, Peter Zijlstra, Miguel Ojeda, Boqun Feng,
Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Alice Ryhl, Trevor Gross, Danilo Krummrich, Daniel Almeida,
Tamir Duberstein, Alexandre Courbot, Onur Özkan,
Jonathan Corbet, Randy Dunlap
Cc: linux-kbuild, linux-kernel, llvm, linux-riscv, linux-arch,
linux-arm-kernel, linux-efi, rust-for-linux, linux-doc,
Jens Axboe, Lorenzo Stoakes (ARM)
kallsyms generates an assembly file that consists mostly of .byte entries
containing compressed names, token strings and name-sorted sequence
numbers.
For an x86-64 build with 158k symbols that is a 37 MiB .S file which takes
0.57s to assemble each of the two to three times it is built over a kernel
build.
Each time it is generated it also takes kallsyms a similar amount of time
to output it.
Avoid this overhead by instead outputting this data as binary and importing
it into the assembly using the .incbin directive.
Tables that are wider than a byte remain part of the assembly to ensure
endianness and relative relocations are performed correctly.
With this change, the output assembly file shrinks from 37 MiB to 9.8 MiB,
with a 2.6 MiB binary data file alongside it, and the object remains
identical.
The generated binary file is deleted correctly on build clean along with
all other ephemeral data.
On an x86-64 system with CONFIG_KALLSYMS_ALL set:
before after delta
scripts/kallsyms 0.24s 0.18s 0.06s
assemble 0.57s 0.16s 0.41s
Per kallsyms invocation/assembly, for a total of 0.47s time saving upon
invocation.
An incremental build on the same system was reduced from 11.15s to 9.65s,
indicating a total of 1.5 seconds saved over the build.
The kallsyms runs and their assembly are on the serial tail of every build
that links vmlinux, no-op builds are unchanged.
Whole build, 128-thread Threadripper 9980X, best of N runs:
before after delta
-------------------------------
x86 defconfig, touch mm/vma.c, gcc 10.8s 9.9s -0.92s (-8%)
x86 defconfig, touch mm/vma.c, clang 10.7s 9.5s -1.2s (-11%)
x86 defconfig, clean, gcc 29.5s 28.7s -0.81s (-3%)
x86 defconfig, clean, clang 29.7s 28.6s -1.1s (-4%)
x86 allmodconfig, touch mm/vma.c, gcc 45.3s 44.0s -1.3s (-3%)
x86 allmodconfig, touch mm/vma.c, clang 42.9s 40.2s -2.7s (-6%)
Assisted-by: LLM
Signed-off-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
---
scripts/kallsyms.c | 97 ++++++++++++++++++++++++++++++++++++++-----------
scripts/link-vmlinux.sh | 2 +-
2 files changed, 77 insertions(+), 22 deletions(-)
diff --git a/scripts/kallsyms.c b/scripts/kallsyms.c
index 350d118c3b9e..61c5eb537ed4 100644
--- a/scripts/kallsyms.c
+++ b/scripts/kallsyms.c
@@ -5,7 +5,10 @@
* This software may be used and distributed according to the terms
* of the GNU General Public License, incorporated herein by reference.
*
- * Usage: kallsyms [--all-symbols] in.map > out.S
+ * Usage: kallsyms [--all-symbols] [--pc-relative] in.map out.bin > out.S
+ *
+ * The byte tables go to out.bin and are pulled into out.S with .incbin;
+ * wider tables stay assembler source for endianness and relocations.
*
* Table compression uses all the unused char codes on the symbols and
* maps these to the most used substrings (tokens). For instance, it might
@@ -102,7 +105,7 @@ static void sym_arr_free(struct sym_arr *arr)
static void usage(void)
{
- fprintf(stderr, "Usage: kallsyms [--all-symbols] in.map > out.S\n");
+ fprintf(stderr, "Usage: kallsyms [--all-symbols] [--pc-relative] in.map out.bin > out.S\n");
exit(1);
}
@@ -319,6 +322,40 @@ static void output_label(const char *label)
printf("%s:\n", label);
}
+static void write_bin(FILE *file, const void *data, size_t len)
+{
+ if (fwrite(data, 1, len, file) == len)
+ return;
+
+ perror("kallsyms: write");
+ exit(EXIT_FAILURE);
+}
+
+static void write_byte_bin(FILE *file, unsigned char byte)
+{
+ write_bin(file, &byte, 1);
+}
+
+static long bin_pos(FILE *file)
+{
+ const long pos = ftell(file);
+
+ if (pos < 0) {
+ perror("kallsyms: ftell");
+ exit(EXIT_FAILURE);
+ }
+
+ return pos;
+}
+
+static void write_incbin(const char *filename, long start, long end)
+{
+ if (start >= end)
+ return;
+
+ printf("\t.incbin \"%s\", %ld, %ld\n", filename, start, end - start);
+}
+
/* uncompress a compressed symbol. When this function is called, the best table
* might still be compressed itself, so the function needs to be recursive */
static int expand_symbol(const unsigned char *data, int len, char *result)
@@ -371,11 +408,12 @@ static void sort_symbols_by_name(void)
qsort(table, table_cnt, sizeof(table[0]), compare_names);
}
-static void write_src(void)
+static void write_src(FILE *out_bin_file, const char *out_bin_name)
{
- unsigned int i, k, off;
+ unsigned int i, off;
unsigned int best_idx[256];
unsigned int *markers, markers_cnt;
+ long bin_start;
char buf[KSYM_NAME_LEN];
printf("\t.section .rodata, \"a\"\n");
@@ -390,6 +428,7 @@ static void write_src(void)
markers = xmalloc(sizeof(*markers) * markers_cnt);
output_label("kallsyms_names");
+ bin_start = bin_pos(out_bin_file);
off = 0;
for (i = 0; i < table_cnt; i++) {
if ((i & 0xFF) == 0)
@@ -413,26 +452,24 @@ static void write_src(void)
/* Encode length with ULEB128. */
if (table[i]->len <= 0x7F) {
/* Most symbols use a single byte for the length. */
- printf("\t.byte 0x%02x", table[i]->len);
+ write_byte_bin(out_bin_file, table[i]->len);
off += table[i]->len + 1;
} else {
/* "Big" symbols use two bytes. */
- printf("\t.byte 0x%02x, 0x%02x",
- (table[i]->len & 0x7F) | 0x80,
- (table[i]->len >> 7) & 0x7F);
+ write_byte_bin(out_bin_file, (table[i]->len & 0x7F) | 0x80);
+ write_byte_bin(out_bin_file, (table[i]->len >> 7) & 0x7F);
off += table[i]->len + 2;
}
- for (k = 0; k < table[i]->len; k++)
- printf(", 0x%02x", table[i]->sym[k]);
+ write_bin(out_bin_file, table[i]->sym, table[i]->len);
/*
* Now that we wrote out the compressed symbol name, restore the
- * original name and print it in the comment.
+ * original name for the comments below.
*/
expand_symbol(table[i]->sym, table[i]->len, buf);
strcpy((char *)table[i]->sym, buf);
- printf("\t/* %s */\n", table[i]->sym);
}
+ write_incbin(out_bin_name, bin_start, bin_pos(out_bin_file));
printf(".size kallsyms_names, . - kallsyms_names\n");
printf("\n");
@@ -445,13 +482,15 @@ static void write_src(void)
free(markers);
output_label("kallsyms_token_table");
+ bin_start = bin_pos(out_bin_file);
off = 0;
for (i = 0; i < 256; i++) {
best_idx[i] = off;
expand_symbol(best_table[i], best_table_len[i], buf);
- printf("\t.asciz\t\"%s\"\n", buf);
+ write_bin(out_bin_file, buf, strlen(buf) + 1);
off += strlen(buf) + 1;
}
+ write_incbin(out_bin_name, bin_start, bin_pos(out_bin_file));
printf(".size kallsyms_token_table, . - kallsyms_token_table\n");
printf("\n");
@@ -484,12 +523,13 @@ static void write_src(void)
sort_symbols_by_name();
output_label("kallsyms_seqs_of_names");
- for (i = 0; i < table_cnt; i++)
- printf("\t.byte 0x%02x, 0x%02x, 0x%02x\t/* %s */\n",
- (unsigned char)(table[i]->seq >> 16),
- (unsigned char)(table[i]->seq >> 8),
- (unsigned char)(table[i]->seq >> 0),
- table[i]->sym);
+ bin_start = bin_pos(out_bin_file);
+ for (i = 0; i < table_cnt; i++) {
+ write_byte_bin(out_bin_file, table[i]->seq >> 16);
+ write_byte_bin(out_bin_file, table[i]->seq >> 8);
+ write_byte_bin(out_bin_file, table[i]->seq >> 0);
+ }
+ write_incbin(out_bin_name, bin_start, bin_pos(out_bin_file));
printf("\n");
}
@@ -798,6 +838,9 @@ static void sort_symbols(void)
int main(int argc, char **argv)
{
+ const char *out_bin_name;
+ FILE *out_bin_file;
+
while (1) {
static const struct option long_options[] = {
{"all-symbols", no_argument, &all_symbols, 1},
@@ -813,14 +856,26 @@ int main(int argc, char **argv)
usage();
}
- if (optind >= argc)
+ if (optind + 2 != argc)
usage();
+ out_bin_name = argv[optind + 1];
+ out_bin_file = fopen(out_bin_name, "w");
+ if (!out_bin_file) {
+ perror(out_bin_name);
+ exit(EXIT_FAILURE);
+ }
+
read_map(argv[optind]);
shrink_table();
sort_symbols();
optimize_token_table();
- write_src();
+ write_src(out_bin_file, out_bin_name);
+
+ if (fclose(out_bin_file)) {
+ perror(out_bin_name);
+ exit(EXIT_FAILURE);
+ }
return 0;
}
diff --git a/scripts/link-vmlinux.sh b/scripts/link-vmlinux.sh
index ab0b8125c8cb..e88604150d2c 100755
--- a/scripts/link-vmlinux.sh
+++ b/scripts/link-vmlinux.sh
@@ -132,7 +132,7 @@ kallsyms()
fi
info KSYMS "${2}.S"
- scripts/kallsyms ${kallsymopt} "${1}" > "${2}.S"
+ scripts/kallsyms ${kallsymopt} "${1}" "${2}.bin" > "${2}.S"
info AS "${2}.o"
${CC} ${NOSTDINC_FLAGS} ${LINUXINCLUDE} ${KBUILD_CPPFLAGS} \
--
2.55.0
^ permalink raw reply [flat|nested] 73+ messages in thread* Re: [PATCH 04/23] kallsyms: output binary data to speed output and kallsyms assembly
2026-09-08 20:55 ` [PATCH 04/23] kallsyms: output binary data to speed output and kallsyms assembly Lorenzo Stoakes (ARM)
@ 2026-09-09 14:35 ` Linus Torvalds
2026-09-10 9:29 ` David Laight
1 sibling, 0 replies; 73+ messages in thread
From: Linus Torvalds @ 2026-09-09 14:35 UTC (permalink / raw)
To: Lorenzo Stoakes (ARM)
Cc: Nathan Chancellor, Nicolas Schier, Nick Desaulniers,
Bill Wendling, Justin Stitt, Masahiro Yamada, Alexey Gladkov,
Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin, Paul Walmsley, Palmer Dabbelt, Albert Ou,
Alexandre Ghiti, Arnd Bergmann, Catalin Marinas, Will Deacon,
Mark Rutland, Ard Biesheuvel, Ilias Apalodimas, Josh Poimboeuf,
Peter Zijlstra, Miguel Ojeda, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, Danilo Krummrich, Daniel Almeida, Tamir Duberstein,
Alexandre Courbot, Onur Özkan, Jonathan Corbet,
Randy Dunlap, linux-kbuild, linux-kernel, llvm, linux-riscv,
linux-arch, linux-arm-kernel, linux-efi, rust-for-linux,
linux-doc, Jens Axboe
I'm finally going through this series, because I do care about build
speed, so I love the concept.
However, I haven't actually applied it and tried it, I'm just reading
the patches.
And this one triggered something:
On Tue, 8 Sept 2026 at 13:56, Lorenzo Stoakes (ARM) <ljs@kernel.org> wrote:
>
> kallsyms generates an assembly file that consists mostly of .byte entries
> containing compressed names, token strings and name-sorted sequence
> numbers.
>
> For an x86-64 build with 158k symbols that is a 37 MiB .S file which takes
> 0.57s to assemble each of the two to three times it is built over a kernel
> build.
It strikes me that this is clearly a bit faster, but it still seems to
be not really fixing the underlying silliness of that code.
Wouldn't it be much better to actually create an object file directly?
Afaik, the *only* think we ever do with that kallsyms-generated file
is to immediately assemble it into an object file, because the actual
asm file is useless to anybody.
And we already require libelf as part of the build, and we already
kind of do things like that with gendwarfksyms.
Don't get my wrong: I think going through an asm file was very much
the right thing originally: not every architecture used ELF in the
first place, and when scripting things like this the current situation
makes 100% sense.
And no sane human wants to deal with this kind of grotty detail and
spend the effort on writing an ELF file writer.
But when there's a LLM that was used to find these things and help fix
them up, and it's something this mechanical and the result really
isn't ever human-readable and never has been, I feel like the obvious
step would be to just ask the little helper bot to switch the whole
kallsyms to using libelf-devel and writing an object file directly.
I don't think anybody has any deep emotional ties to the current
kallsyms code and that horrific step of the kernel build, do they?
Linus
^ permalink raw reply [flat|nested] 73+ messages in thread* Re: [PATCH 04/23] kallsyms: output binary data to speed output and kallsyms assembly
2026-09-08 20:55 ` [PATCH 04/23] kallsyms: output binary data to speed output and kallsyms assembly Lorenzo Stoakes (ARM)
2026-09-09 14:35 ` Linus Torvalds
@ 2026-09-10 9:29 ` David Laight
2026-09-11 11:07 ` Lorenzo Stoakes (ARM)
1 sibling, 1 reply; 73+ messages in thread
From: David Laight @ 2026-09-10 9:29 UTC (permalink / raw)
To: Lorenzo Stoakes (ARM)
Cc: Linus Torvalds, Nathan Chancellor, Nicolas Schier,
Nick Desaulniers, Bill Wendling, Justin Stitt, Masahiro Yamada,
Alexey Gladkov, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, x86, H. Peter Anvin, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Alexandre Ghiti, Arnd Bergmann, Catalin Marinas,
Will Deacon, Mark Rutland, Ard Biesheuvel, Ilias Apalodimas,
Josh Poimboeuf, Peter Zijlstra, Miguel Ojeda, Boqun Feng,
Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Alice Ryhl, Trevor Gross, Danilo Krummrich, Daniel Almeida,
Tamir Duberstein, Alexandre Courbot, Onur Özkan,
Jonathan Corbet, Randy Dunlap, linux-kbuild, linux-kernel, llvm,
linux-riscv, linux-arch, linux-arm-kernel, linux-efi,
rust-for-linux, linux-doc, Jens Axboe
On Tue, 08 Sep 2026 21:55:04 +0100
"Lorenzo Stoakes (ARM)" <ljs@kernel.org> wrote:
> kallsyms generates an assembly file that consists mostly of .byte entries
> containing compressed names, token strings and name-sorted sequence
> numbers.
>
> For an x86-64 build with 158k symbols that is a 37 MiB .S file which takes
> 0.57s to assemble each of the two to three times it is built over a kernel
> build.
>
> Each time it is generated it also takes kallsyms a similar amount of time
> to output it.
>
> Avoid this overhead by instead outputting this data as binary and importing
> it into the assembly using the .incbin directive.
>
> Tables that are wider than a byte remain part of the assembly to ensure
> endianness and relative relocations are performed correctly.
>
> With this change, the output assembly file shrinks from 37 MiB to 9.8 MiB,
> with a 2.6 MiB binary data file alongside it, and the object remains
> identical.
>
> The generated binary file is deleted correctly on build clean along with
> all other ephemeral data.
>
> On an x86-64 system with CONFIG_KALLSYMS_ALL set:
>
> before after delta
> scripts/kallsyms 0.24s 0.18s 0.06s
> assemble 0.57s 0.16s 0.41s
>
> Per kallsyms invocation/assembly, for a total of 0.47s time saving upon
> invocation.
>
> An incremental build on the same system was reduced from 11.15s to 9.65s,
> indicating a total of 1.5 seconds saved over the build.
>
> The kallsyms runs and their assembly are on the serial tail of every build
> that links vmlinux, no-op builds are unchanged.
>
> Whole build, 128-thread Threadripper 9980X, best of N runs:
>
> before after delta
> -------------------------------
> x86 defconfig, touch mm/vma.c, gcc 10.8s 9.9s -0.92s (-8%)
> x86 defconfig, touch mm/vma.c, clang 10.7s 9.5s -1.2s (-11%)
> x86 defconfig, clean, gcc 29.5s 28.7s -0.81s (-3%)
> x86 defconfig, clean, clang 29.7s 28.6s -1.1s (-4%)
> x86 allmodconfig, touch mm/vma.c, gcc 45.3s 44.0s -1.3s (-3%)
> x86 allmodconfig, touch mm/vma.c, clang 42.9s 40.2s -2.7s (-6%)
>
> Assisted-by: LLM
> Signed-off-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
> ---
> scripts/kallsyms.c | 97 ++++++++++++++++++++++++++++++++++++++-----------
> scripts/link-vmlinux.sh | 2 +-
> 2 files changed, 77 insertions(+), 22 deletions(-)
>
> diff --git a/scripts/kallsyms.c b/scripts/kallsyms.c
> index 350d118c3b9e..61c5eb537ed4 100644
> --- a/scripts/kallsyms.c
> +++ b/scripts/kallsyms.c
> @@ -5,7 +5,10 @@
> * This software may be used and distributed according to the terms
> * of the GNU General Public License, incorporated herein by reference.
> *
> - * Usage: kallsyms [--all-symbols] in.map > out.S
> + * Usage: kallsyms [--all-symbols] [--pc-relative] in.map out.bin > out.S
> + *
> + * The byte tables go to out.bin and are pulled into out.S with .incbin;
> + * wider tables stay assembler source for endianness and relocations.
> *
> * Table compression uses all the unused char codes on the symbols and
> * maps these to the most used substrings (tokens). For instance, it might
> @@ -102,7 +105,7 @@ static void sym_arr_free(struct sym_arr *arr)
>
> static void usage(void)
> {
> - fprintf(stderr, "Usage: kallsyms [--all-symbols] in.map > out.S\n");
> + fprintf(stderr, "Usage: kallsyms [--all-symbols] [--pc-relative] in.map out.bin > out.S\n");
> exit(1);
> }
>
> @@ -319,6 +322,40 @@ static void output_label(const char *label)
> printf("%s:\n", label);
> }
>
> +static void write_bin(FILE *file, const void *data, size_t len)
> +{
> + if (fwrite(data, 1, len, file) == len)
> + return;
> +
> + perror("kallsyms: write");
> + exit(EXIT_FAILURE);
> +}
It is pretty pointless checking the return value from fwrite().
Most of the time it is just doing a memcpy().
Instead call fflush() and the ferror() prior to the fclose().
(Or just rely on fclose() giving you that error status.)
David
^ permalink raw reply [flat|nested] 73+ messages in thread* Re: [PATCH 04/23] kallsyms: output binary data to speed output and kallsyms assembly
2026-09-10 9:29 ` David Laight
@ 2026-09-11 11:07 ` Lorenzo Stoakes (ARM)
0 siblings, 0 replies; 73+ messages in thread
From: Lorenzo Stoakes (ARM) @ 2026-09-11 11:07 UTC (permalink / raw)
To: David Laight
Cc: Linus Torvalds, Nathan Chancellor, Nicolas Schier,
Nick Desaulniers, Bill Wendling, Justin Stitt, Masahiro Yamada,
Alexey Gladkov, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, x86, H. Peter Anvin, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Alexandre Ghiti, Arnd Bergmann, Catalin Marinas,
Will Deacon, Mark Rutland, Ard Biesheuvel, Ilias Apalodimas,
Josh Poimboeuf, Peter Zijlstra, Miguel Ojeda, Boqun Feng,
Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Alice Ryhl, Trevor Gross, Danilo Krummrich, Daniel Almeida,
Tamir Duberstein, Alexandre Courbot, Onur Özkan,
Jonathan Corbet, Randy Dunlap, linux-kbuild, linux-kernel, llvm,
linux-riscv, linux-arch, linux-arm-kernel, linux-efi,
rust-for-linux, linux-doc, Jens Axboe
On Thu, Sep 10, 2026 at 10:29:03AM +0100, David Laight wrote:
> On Tue, 08 Sep 2026 21:55:04 +0100
> "Lorenzo Stoakes (ARM)" <ljs@kernel.org> wrote:
>
> > kallsyms generates an assembly file that consists mostly of .byte entries
> > containing compressed names, token strings and name-sorted sequence
> > numbers.
> >
> > For an x86-64 build with 158k symbols that is a 37 MiB .S file which takes
> > 0.57s to assemble each of the two to three times it is built over a kernel
> > build.
> >
> > Each time it is generated it also takes kallsyms a similar amount of time
> > to output it.
> >
> > Avoid this overhead by instead outputting this data as binary and importing
> > it into the assembly using the .incbin directive.
> >
> > Tables that are wider than a byte remain part of the assembly to ensure
> > endianness and relative relocations are performed correctly.
> >
> > With this change, the output assembly file shrinks from 37 MiB to 9.8 MiB,
> > with a 2.6 MiB binary data file alongside it, and the object remains
> > identical.
> >
> > The generated binary file is deleted correctly on build clean along with
> > all other ephemeral data.
> >
> > On an x86-64 system with CONFIG_KALLSYMS_ALL set:
> >
> > before after delta
> > scripts/kallsyms 0.24s 0.18s 0.06s
> > assemble 0.57s 0.16s 0.41s
> >
> > Per kallsyms invocation/assembly, for a total of 0.47s time saving upon
> > invocation.
> >
> > An incremental build on the same system was reduced from 11.15s to 9.65s,
> > indicating a total of 1.5 seconds saved over the build.
> >
> > The kallsyms runs and their assembly are on the serial tail of every build
> > that links vmlinux, no-op builds are unchanged.
> >
> > Whole build, 128-thread Threadripper 9980X, best of N runs:
> >
> > before after delta
> > -------------------------------
> > x86 defconfig, touch mm/vma.c, gcc 10.8s 9.9s -0.92s (-8%)
> > x86 defconfig, touch mm/vma.c, clang 10.7s 9.5s -1.2s (-11%)
> > x86 defconfig, clean, gcc 29.5s 28.7s -0.81s (-3%)
> > x86 defconfig, clean, clang 29.7s 28.6s -1.1s (-4%)
> > x86 allmodconfig, touch mm/vma.c, gcc 45.3s 44.0s -1.3s (-3%)
> > x86 allmodconfig, touch mm/vma.c, clang 42.9s 40.2s -2.7s (-6%)
> >
> > Assisted-by: LLM
> > Signed-off-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
> > ---
> > scripts/kallsyms.c | 97 ++++++++++++++++++++++++++++++++++++++-----------
> > scripts/link-vmlinux.sh | 2 +-
> > 2 files changed, 77 insertions(+), 22 deletions(-)
> >
> > diff --git a/scripts/kallsyms.c b/scripts/kallsyms.c
> > index 350d118c3b9e..61c5eb537ed4 100644
> > --- a/scripts/kallsyms.c
> > +++ b/scripts/kallsyms.c
> > @@ -5,7 +5,10 @@
> > * This software may be used and distributed according to the terms
> > * of the GNU General Public License, incorporated herein by reference.
> > *
> > - * Usage: kallsyms [--all-symbols] in.map > out.S
> > + * Usage: kallsyms [--all-symbols] [--pc-relative] in.map out.bin > out.S
> > + *
> > + * The byte tables go to out.bin and are pulled into out.S with .incbin;
> > + * wider tables stay assembler source for endianness and relocations.
> > *
> > * Table compression uses all the unused char codes on the symbols and
> > * maps these to the most used substrings (tokens). For instance, it might
> > @@ -102,7 +105,7 @@ static void sym_arr_free(struct sym_arr *arr)
> >
> > static void usage(void)
> > {
> > - fprintf(stderr, "Usage: kallsyms [--all-symbols] in.map > out.S\n");
> > + fprintf(stderr, "Usage: kallsyms [--all-symbols] [--pc-relative] in.map out.bin > out.S\n");
> > exit(1);
> > }
> >
> > @@ -319,6 +322,40 @@ static void output_label(const char *label)
> > printf("%s:\n", label);
> > }
> >
> > +static void write_bin(FILE *file, const void *data, size_t len)
> > +{
> > + if (fwrite(data, 1, len, file) == len)
> > + return;
> > +
> > + perror("kallsyms: write");
> > + exit(EXIT_FAILURE);
> > +}
>
> It is pretty pointless checking the return value from fwrite().
> Most of the time it is just doing a memcpy().
> Instead call fflush() and the ferror() prior to the fclose().
> (Or just rely on fclose() giving you that error status.)
Ack, will fix up in v2.
>
> David
--
Cheers, Lorenzo
^ permalink raw reply [flat|nested] 73+ messages in thread
* [PATCH 05/23] kbuild: do not sort nm output where the order is irrelevant
2026-09-08 20:55 [PATCH 00/23] kbuild: significantly speed up kernel builds Lorenzo Stoakes (ARM)
` (3 preceding siblings ...)
2026-09-08 20:55 ` [PATCH 04/23] kallsyms: output binary data to speed output and kallsyms assembly Lorenzo Stoakes (ARM)
@ 2026-09-08 20:55 ` Lorenzo Stoakes (ARM)
2026-09-10 4:19 ` Nathan Chancellor
2026-09-08 20:55 ` [PATCH 06/23] kbuild: only emit vmlinux relocations when required Lorenzo Stoakes (ARM)
` (21 subsequent siblings)
26 siblings, 1 reply; 73+ messages in thread
From: Lorenzo Stoakes (ARM) @ 2026-09-08 20:55 UTC (permalink / raw)
To: Linus Torvalds, Nathan Chancellor, Nicolas Schier,
Nick Desaulniers, Bill Wendling, Justin Stitt, Masahiro Yamada,
Alexey Gladkov, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, x86, H. Peter Anvin, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Alexandre Ghiti, Arnd Bergmann, Catalin Marinas,
Will Deacon, Mark Rutland, Ard Biesheuvel, Ilias Apalodimas,
Josh Poimboeuf, Peter Zijlstra, Miguel Ojeda, Boqun Feng,
Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Alice Ryhl, Trevor Gross, Danilo Krummrich, Daniel Almeida,
Tamir Duberstein, Alexandre Courbot, Onur Özkan,
Jonathan Corbet, Randy Dunlap
Cc: linux-kbuild, linux-kernel, llvm, linux-riscv, linux-arch,
linux-arm-kernel, linux-efi, rust-for-linux, linux-doc,
Jens Axboe, Lorenzo Stoakes (ARM)
Unless instructed otherwise, nm sorts by name.
There are places where this is unnecessary - an invocation from
scripts/sorttable every vmlinux link, scripts/check-function-names.sh run
after every vmlinux.o link, and the x86 VOFFSET and ZOFFSET listings
between vmlinux and bzImage.
Both GNU nm and llvm-nm accept the same '-p' parameter to disable sorting
in these instances, so use that to prevent this unnecessary work.
Each nm run on its own, x86-64, median of 5:
GNU nm 2.47 llvm-nm 22
before after delta before after delta
defconfig
sorttable, nm -S vmlinux 0.080s 0.039s -0.041s 0.265s 0.113s -0.152s
check-function-names.sh 0.072s 0.033s -0.039s 0.244s 0.102s -0.142s
VOFFSET 0.077s 0.034s -0.043s 0.261s 0.113s -0.148s
ZOFFSET 0.007s 0.005s -0.002s 0.005s 0.005s 0.000s
TOTAL -0.125s -0.442s
allmodconfig
sorttable, nm -S vmlinux 0.156s 0.063s -0.093s 0.539s 0.223s -0.316s
check-function-names.sh 0.149s 0.054s -0.095s 0.537s 0.217s -0.320s
VOFFSET 0.146s 0.052s -0.094s 0.524s 0.221s -0.303s
ZOFFSET 0.007s 0.005s -0.002s 0.006s 0.004s -0.002s
TOTAL -0.284s -0.941s
llvm-nm appears to be a lot slower than GNU nm, so these builds naturally
improve the most.
The four run one after another in the serial tail of every build that links
vmlinux so impact kernel builds directly.
In an allmodconfig the decompressor is built in parallel while the modules,
so only the two before the vmlinux link contribute to build time.
The build outputs remain unchanged.
Whole build, 128-thread Threadripper 9980X, median of 3:
before after delta
----------------------------------
x86 defconfig, touch mm/vma.c, gcc 10.02s 9.83s -0.19s (-2%)
x86 defconfig, touch mm/vma.c, clang 9.52s 9.24s -0.28s (-3%)
x86 defconfig, clean, gcc 28.71s 28.58s -0.13s (-0.5%)
x86 defconfig, clean, clang 28.69s 28.31s -0.38s (-1%)
x86 allmodconfig, touch mm/vma.c, cl 40.52s 39.83s -0.69s (-2%)
Assisted-by: LLM
Signed-off-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
---
arch/x86/boot/Makefile | 2 +-
arch/x86/boot/compressed/Makefile | 2 +-
scripts/check-function-names.sh | 3 ++-
scripts/link-vmlinux.sh | 2 +-
4 files changed, 5 insertions(+), 4 deletions(-)
diff --git a/arch/x86/boot/Makefile b/arch/x86/boot/Makefile
index 3f9fb3698d66..4a9bce32586d 100644
--- a/arch/x86/boot/Makefile
+++ b/arch/x86/boot/Makefile
@@ -74,7 +74,7 @@ SETUP_OBJS = $(addprefix $(obj)/,$(setup-y))
sed-zoffset := -e 's/^\([0-9a-fA-F]*\) [a-zA-Z] \(startup_32\|efi.._stub_entry\|efi\(32\)\?_pe_entry\|input_data\|kernel_info\|_end\|_ehead\|_text\|_e\?data\|_e\?sbat\|z_.*\)$$/\#define ZO_\2 0x\1/p'
quiet_cmd_zoffset = ZOFFSET $@
- cmd_zoffset = $(NM) $< | sed -n $(sed-zoffset) > $@
+ cmd_zoffset = $(NM) -p $< | sed -n $(sed-zoffset) > $@
targets += zoffset.h
$(obj)/zoffset.h: $(obj)/compressed/vmlinux FORCE
diff --git a/arch/x86/boot/compressed/Makefile b/arch/x86/boot/compressed/Makefile
index 06934f9691d6..6ec5e031db1d 100644
--- a/arch/x86/boot/compressed/Makefile
+++ b/arch/x86/boot/compressed/Makefile
@@ -76,7 +76,7 @@ HOST_EXTRACFLAGS += -I$(srctree)/tools/include
sed-voffset := -e 's/^\([0-9a-fA-F]*\) [ABbCDGRSTtVW] \(_text\|__start_rodata\|_sinittext\|__inittext_end\|__bss_start\|_end\)$$/\#define VO_\2 _AC(0x\1,UL)/p'
quiet_cmd_voffset = VOFFSET $@
- cmd_voffset = $(NM) $< | sed -n $(sed-voffset) > $@
+ cmd_voffset = $(NM) -p $< | sed -n $(sed-voffset) > $@
targets += ../voffset.h
diff --git a/scripts/check-function-names.sh b/scripts/check-function-names.sh
index 08071133e5a5..94883e690627 100755
--- a/scripts/check-function-names.sh
+++ b/scripts/check-function-names.sh
@@ -13,7 +13,8 @@ if [ ! -f "$objfile" ]; then
exit 1
fi
-bad_symbols=$(${NM:-nm} "$objfile" | awk '$2 ~ /^[TtWw]$/ {print $3}' | grep -E '^(startup|exit|split|unlikely|hot|unknown)(\.|$)')
+bad_symbols=$(${NM:-nm} -p "$objfile" | awk '$2 ~ /^[TtWw]$/ {print $3}' |
+ grep -E '^(startup|exit|split|unlikely|hot|unknown)(\.|$)')
if [ -n "$bad_symbols" ]; then
echo "$bad_symbols" | while read -r sym; do
diff --git a/scripts/link-vmlinux.sh b/scripts/link-vmlinux.sh
index e88604150d2c..970ca10f8fa9 100755
--- a/scripts/link-vmlinux.sh
+++ b/scripts/link-vmlinux.sh
@@ -161,7 +161,7 @@ mksysmap()
sorttable()
{
- ${NM} -S ${1} > .tmp_vmlinux.nm-sort
+ ${NM} -p -S ${1} > .tmp_vmlinux.nm-sort
${objtree}/scripts/sorttable -s .tmp_vmlinux.nm-sort ${1}
}
--
2.55.0
^ permalink raw reply [flat|nested] 73+ messages in thread* Re: [PATCH 05/23] kbuild: do not sort nm output where the order is irrelevant
2026-09-08 20:55 ` [PATCH 05/23] kbuild: do not sort nm output where the order is irrelevant Lorenzo Stoakes (ARM)
@ 2026-09-10 4:19 ` Nathan Chancellor
0 siblings, 0 replies; 73+ messages in thread
From: Nathan Chancellor @ 2026-09-10 4:19 UTC (permalink / raw)
To: Lorenzo Stoakes (ARM)
Cc: Linus Torvalds, Nathan Chancellor, Nicolas Schier,
Nick Desaulniers, Bill Wendling, Justin Stitt, Masahiro Yamada,
Alexey Gladkov, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, x86, H. Peter Anvin, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Alexandre Ghiti, Arnd Bergmann, Catalin Marinas,
Will Deacon, Mark Rutland, Ard Biesheuvel, Ilias Apalodimas,
Josh Poimboeuf, Peter Zijlstra, Miguel Ojeda, Boqun Feng,
Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Alice Ryhl, Trevor Gross, Danilo Krummrich, Daniel Almeida,
Tamir Duberstein, Alexandre Courbot, Onur Özkan,
Jonathan Corbet, Randy Dunlap, linux-kbuild, linux-kernel, llvm,
linux-riscv, linux-arch, linux-arm-kernel, linux-efi,
rust-for-linux, linux-doc, Jens Axboe
> Unless instructed otherwise, nm sorts by name.
>
> There are places where this is unnecessary - an invocation from
> scripts/sorttable every vmlinux link, scripts/check-function-names.sh run
> after every vmlinux.o link, and the x86 VOFFSET and ZOFFSET listings
> between vmlinux and bzImage.
>
> Both GNU nm and llvm-nm accept the same '-p' parameter to disable sorting
> in these instances, so use that to prevent this unnecessary work.
>
> Each nm run on its own, x86-64, median of 5:
>
> GNU nm 2.47 llvm-nm 22
> before after delta before after delta
> defconfig
> sorttable, nm -S vmlinux 0.080s 0.039s -0.041s 0.265s 0.113s -0.152s
> check-function-names.sh 0.072s 0.033s -0.039s 0.244s 0.102s -0.142s
> VOFFSET 0.077s 0.034s -0.043s 0.261s 0.113s -0.148s
> ZOFFSET 0.007s 0.005s -0.002s 0.005s 0.005s 0.000s
> TOTAL -0.125s -0.442s
>
> allmodconfig
> sorttable, nm -S vmlinux 0.156s 0.063s -0.093s 0.539s 0.223s -0.316s
> check-function-names.sh 0.149s 0.054s -0.095s 0.537s 0.217s -0.320s
> VOFFSET 0.146s 0.052s -0.094s 0.524s 0.221s -0.303s
> ZOFFSET 0.007s 0.005s -0.002s 0.006s 0.004s -0.002s
> TOTAL -0.284s -0.941s
>
> llvm-nm appears to be a lot slower than GNU nm, so these builds naturally
> improve the most.
>
> The four run one after another in the serial tail of every build that links
> vmlinux so impact kernel builds directly.
>
> In an allmodconfig the decompressor is built in parallel while the modules,
> so only the two before the vmlinux link contribute to build time.
>
> The build outputs remain unchanged.
>
> Whole build, 128-thread Threadripper 9980X, median of 3:
>
> before after delta
> ----------------------------------
> x86 defconfig, touch mm/vma.c, gcc 10.02s 9.83s -0.19s (-2%)
> x86 defconfig, touch mm/vma.c, clang 9.52s 9.24s -0.28s (-3%)
> x86 defconfig, clean, gcc 28.71s 28.58s -0.13s (-0.5%)
> x86 defconfig, clean, clang 28.69s 28.31s -0.38s (-1%)
> x86 allmodconfig, touch mm/vma.c, cl 40.52s 39.83s -0.69s (-2%)
>
> Assisted-by: LLM
> Signed-off-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
This seems like it is worth taking now but I would appreciate an x86
maintainer ack.
--
Cheers,
Nathan
^ permalink raw reply [flat|nested] 73+ messages in thread
* [PATCH 06/23] kbuild: only emit vmlinux relocations when required
2026-09-08 20:55 [PATCH 00/23] kbuild: significantly speed up kernel builds Lorenzo Stoakes (ARM)
` (4 preceding siblings ...)
2026-09-08 20:55 ` [PATCH 05/23] kbuild: do not sort nm output where the order is irrelevant Lorenzo Stoakes (ARM)
@ 2026-09-08 20:55 ` Lorenzo Stoakes (ARM)
2026-09-10 4:19 ` Nathan Chancellor
2026-09-08 20:55 ` [PATCH 07/23] elf-parse: add section flags, symbol binding and a read-only mapping Lorenzo Stoakes (ARM)
` (20 subsequent siblings)
26 siblings, 1 reply; 73+ messages in thread
From: Lorenzo Stoakes (ARM) @ 2026-09-08 20:55 UTC (permalink / raw)
To: Linus Torvalds, Nathan Chancellor, Nicolas Schier,
Nick Desaulniers, Bill Wendling, Justin Stitt, Masahiro Yamada,
Alexey Gladkov, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, x86, H. Peter Anvin, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Alexandre Ghiti, Arnd Bergmann, Catalin Marinas,
Will Deacon, Mark Rutland, Ard Biesheuvel, Ilias Apalodimas,
Josh Poimboeuf, Peter Zijlstra, Miguel Ojeda, Boqun Feng,
Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Alice Ryhl, Trevor Gross, Danilo Krummrich, Daniel Almeida,
Tamir Duberstein, Alexandre Courbot, Onur Özkan,
Jonathan Corbet, Randy Dunlap
Cc: linux-kbuild, linux-kernel, llvm, linux-riscv, linux-arch,
linux-arm-kernel, linux-efi, rust-for-linux, linux-doc,
Jens Axboe, Lorenzo Stoakes (ARM)
A kernel build consists of more than one linking pass on vmlinux.o and
vmlinux, at minimum two, and with CONFIG_KALLSYMS and BTF enabled on x86-64
for example there are 5 such stages.
For architectures that build their own relocation tables (x86, riscv, mips,
s390), vmlinux is linked with the --emit-relocs parameter specified.
However, this is only required on the final vmlinux link.
Symbol tables of trial links preceding it don't need it because they
already check that System.map matches kallsyms symbols on each build.
GNU ld is slow at emitting relocation tables, so this results in a
reduction in build time.
Whole build, 128-thread Threadripper 9980X, best of N runs:
before after delta
-------------------------------
x86 defconfig, touch mm/vma.c, gcc 9.8s 9.4s -0.43s (-4%)
x86 defconfig, clean, gcc 28.6s 28.1s -0.47s (-2%)
x86 allmodconfig, touch mm/vma.c, gcc 44.0s 42.7s -1.3s (-3%)
Note that this has little impact on LLVM ld which performs this operation
more efficiently.
Assisted-by: LLM
Signed-off-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
---
Makefile | 4 +++-
scripts/Makefile.vmlinux | 3 ++-
scripts/link-vmlinux.sh | 6 ++++++
3 files changed, 11 insertions(+), 2 deletions(-)
diff --git a/Makefile b/Makefile
index 66654fa71655..85eb2fd026cb 100644
--- a/Makefile
+++ b/Makefile
@@ -1263,8 +1263,10 @@ LDFLAGS_vmlinux += --orphan-handling=$(CONFIG_LD_ORPHAN_WARN_LEVEL)
endif
ifneq ($(CONFIG_ARCH_VMLINUX_NEEDS_RELOCS),)
-LDFLAGS_vmlinux += --emit-relocs --discard-none
+LDFLAGS_vmlinux += --discard-none
+LDFLAGS_vmlinux_relocs := --emit-relocs
endif
+export LDFLAGS_vmlinux_relocs
# Align the architecture of userspace programs with the kernel
USERFLAGS_FROM_KERNEL := --target=%
diff --git a/scripts/Makefile.vmlinux b/scripts/Makefile.vmlinux
index fcae1e432d9a..4b54aaeca65d 100644
--- a/scripts/Makefile.vmlinux
+++ b/scripts/Makefile.vmlinux
@@ -64,7 +64,8 @@ ARCH_POSTLINK := $(wildcard $(srctree)/arch/$(SRCARCH)/Makefile.postlink)
# Final link of vmlinux with optional arch pass after final link
cmd_link_vmlinux = \
- $< "$(LD)" "$(KBUILD_LDFLAGS)" "$(LDFLAGS_vmlinux)" "$@"; \
+ $< "$(LD)" "$(KBUILD_LDFLAGS)" "$(LDFLAGS_vmlinux)" "$@" \
+ "$(LDFLAGS_vmlinux_relocs)"; \
$(if $(ARCH_POSTLINK), $(MAKE) -f $(ARCH_POSTLINK) $@, true)
targets += vmlinux.unstripped .vmlinux.export.o
diff --git a/scripts/link-vmlinux.sh b/scripts/link-vmlinux.sh
index 970ca10f8fa9..09c5222ccb94 100755
--- a/scripts/link-vmlinux.sh
+++ b/scripts/link-vmlinux.sh
@@ -32,6 +32,7 @@ LD="$1"
KBUILD_LDFLAGS="$2"
LDFLAGS_vmlinux="$3"
VMLINUX="$4"
+LDFLAGS_vmlinux_relocs="$5"
is_enabled() {
grep -q "^$1=y" include/config/auto.conf
@@ -96,6 +97,11 @@ vmlinux_link()
ldflags="${ldflags} ${wl}--strip-debug"
fi
+ # Only the final link actually requires the relocations.
+ if [ "${output}" = "${VMLINUX}" ] && [ -n "${LDFLAGS_vmlinux_relocs}" ]; then
+ ldflags="${ldflags} ${wl}${LDFLAGS_vmlinux_relocs}"
+ fi
+
if [ -n "${generate_map}" ]; then
ldflags="${ldflags} ${wl}-Map=vmlinux.map"
fi
--
2.55.0
^ permalink raw reply [flat|nested] 73+ messages in thread* Re: [PATCH 06/23] kbuild: only emit vmlinux relocations when required
2026-09-08 20:55 ` [PATCH 06/23] kbuild: only emit vmlinux relocations when required Lorenzo Stoakes (ARM)
@ 2026-09-10 4:19 ` Nathan Chancellor
0 siblings, 0 replies; 73+ messages in thread
From: Nathan Chancellor @ 2026-09-10 4:19 UTC (permalink / raw)
To: Lorenzo Stoakes (ARM)
Cc: Linus Torvalds, Nathan Chancellor, Nicolas Schier,
Nick Desaulniers, Bill Wendling, Justin Stitt, Masahiro Yamada,
Alexey Gladkov, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, x86, H. Peter Anvin, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Alexandre Ghiti, Arnd Bergmann, Catalin Marinas,
Will Deacon, Mark Rutland, Ard Biesheuvel, Ilias Apalodimas,
Josh Poimboeuf, Peter Zijlstra, Miguel Ojeda, Boqun Feng,
Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Alice Ryhl, Trevor Gross, Danilo Krummrich, Daniel Almeida,
Tamir Duberstein, Alexandre Courbot, Onur Özkan,
Jonathan Corbet, Randy Dunlap, linux-kbuild, linux-kernel, llvm,
linux-riscv, linux-arch, linux-arm-kernel, linux-efi,
rust-for-linux, linux-doc, Jens Axboe
> A kernel build consists of more than one linking pass on vmlinux.o and
> vmlinux, at minimum two, and with CONFIG_KALLSYMS and BTF enabled on x86-64
> for example there are 5 such stages.
>
> For architectures that build their own relocation tables (x86, riscv, mips,
> s390), vmlinux is linked with the --emit-relocs parameter specified.
>
> However, this is only required on the final vmlinux link.
>
> Symbol tables of trial links preceding it don't need it because they
> already check that System.map matches kallsyms symbols on each build.
>
> GNU ld is slow at emitting relocation tables, so this results in a
> reduction in build time.
>
> Whole build, 128-thread Threadripper 9980X, best of N runs:
>
> before after delta
> -------------------------------
> x86 defconfig, touch mm/vma.c, gcc 9.8s 9.4s -0.43s (-4%)
> x86 defconfig, clean, gcc 28.6s 28.1s -0.47s (-2%)
> x86 allmodconfig, touch mm/vma.c, gcc 44.0s 42.7s -1.3s (-3%)
>
> Note that this has little impact on LLVM ld which performs this operation
> more efficiently.
>
> Assisted-by: LLM
> Signed-off-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
>
> diff --git a/Makefile b/Makefile
> index 9bd226726358..de095b11e20a 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -1266,8 +1266,10 @@ LDFLAGS_vmlinux += --orphan-handling=$(CONFIG_LD_ORPHAN_WARN_LEVEL)
> endif
>
> ifneq ($(CONFIG_ARCH_VMLINUX_NEEDS_RELOCS),)
> -LDFLAGS_vmlinux += --emit-relocs --discard-none
> +LDFLAGS_vmlinux += --discard-none
> +LDFLAGS_vmlinux_relocs := --emit-relocs
> endif
> +export LDFLAGS_vmlinux_relocs
>
> # Align the architecture of userspace programs with the kernel
> USERFLAGS_FROM_KERNEL := --target=%
> diff --git a/scripts/Makefile.vmlinux b/scripts/Makefile.vmlinux
> index fcae1e432d9a..4b54aaeca65d 100644
> --- a/scripts/Makefile.vmlinux
> +++ b/scripts/Makefile.vmlinux
> @@ -64,7 +64,8 @@ ARCH_POSTLINK := $(wildcard $(srctree)/arch/$(SRCARCH)/Makefile.postlink)
>
> # Final link of vmlinux with optional arch pass after final link
> cmd_link_vmlinux = \
> - $< "$(LD)" "$(KBUILD_LDFLAGS)" "$(LDFLAGS_vmlinux)" "$@"; \
> + $< "$(LD)" "$(KBUILD_LDFLAGS)" "$(LDFLAGS_vmlinux)" "$@" \
> + "$(LDFLAGS_vmlinux_relocs)"; \
> $(if $(ARCH_POSTLINK), $(MAKE) -f $(ARCH_POSTLINK) $@, true)
>
> targets += vmlinux.unstripped .vmlinux.export.o
> diff --git a/scripts/link-vmlinux.sh b/scripts/link-vmlinux.sh
> index 970ca10f8fa9..09c5222ccb94 100755
> --- a/scripts/link-vmlinux.sh
> +++ b/scripts/link-vmlinux.sh
> @@ -32,6 +32,7 @@ LD="$1"
> KBUILD_LDFLAGS="$2"
> LDFLAGS_vmlinux="$3"
> VMLINUX="$4"
> +LDFLAGS_vmlinux_relocs="$5"
>
> is_enabled() {
> grep -q "^$1=y" include/config/auto.conf
> @@ -96,6 +97,11 @@ vmlinux_link()
> ldflags="${ldflags} ${wl}--strip-debug"
> fi
>
> + # Only the final link actually requires the relocations.
> + if [ "${output}" = "${VMLINUX}" ] && [ -n "${LDFLAGS_vmlinux_relocs}" ]; then
> + ldflags="${ldflags} ${wl}${LDFLAGS_vmlinux_relocs}"
> + fi
> +
We don't use LDFLAGS_vmlinux_relocs anywhere else, so do we even need
this whole dance to just end up with the equivalent of
if [ "${output}" = "${VMLINUX}" ] && is_enabled CONFIG_ARCH_VMLINUX_NEEDS_RELOCS; then
ldflags="${ldflags} ${wl}--emit-relocs"
fi
which would make this diff much simpler?
That being said, Sashiko makes a comment that may be relevant, have not
verified though:
https://sashiko.dev/#/message/20260908-build-speedup-v1-6-5dc1ac01672d@kernel.org
--
Cheers,
Nathan
^ permalink raw reply [flat|nested] 73+ messages in thread
* [PATCH 07/23] elf-parse: add section flags, symbol binding and a read-only mapping
2026-09-08 20:55 [PATCH 00/23] kbuild: significantly speed up kernel builds Lorenzo Stoakes (ARM)
` (5 preceding siblings ...)
2026-09-08 20:55 ` [PATCH 06/23] kbuild: only emit vmlinux relocations when required Lorenzo Stoakes (ARM)
@ 2026-09-08 20:55 ` Lorenzo Stoakes (ARM)
2026-09-08 20:55 ` [PATCH 08/23] kallsyms: reimplement mksysmap in C Lorenzo Stoakes (ARM)
` (19 subsequent siblings)
26 siblings, 0 replies; 73+ messages in thread
From: Lorenzo Stoakes (ARM) @ 2026-09-08 20:55 UTC (permalink / raw)
To: Linus Torvalds, Nathan Chancellor, Nicolas Schier,
Nick Desaulniers, Bill Wendling, Justin Stitt, Masahiro Yamada,
Alexey Gladkov, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, x86, H. Peter Anvin, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Alexandre Ghiti, Arnd Bergmann, Catalin Marinas,
Will Deacon, Mark Rutland, Ard Biesheuvel, Ilias Apalodimas,
Josh Poimboeuf, Peter Zijlstra, Miguel Ojeda, Boqun Feng,
Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Alice Ryhl, Trevor Gross, Danilo Krummrich, Daniel Almeida,
Tamir Duberstein, Alexandre Courbot, Onur Özkan,
Jonathan Corbet, Randy Dunlap
Cc: linux-kbuild, linux-kernel, llvm, linux-riscv, linux-arch,
linux-arm-kernel, linux-efi, rust-for-linux, linux-doc,
Jens Axboe, Lorenzo Stoakes (ARM)
Extend elf-parse to be able to read the symbol table of vmlinux in kallyms.
This requires the ability to open ELF files in read-only mode, so provide
elf_map_ro() to do so.
It also requires accessors for section flags and symbol bindings, so
provide these via shdr_flags() and sym_bind().
Also, check for the file being an ELF file first in elf_parse(). This is
the logical thing to check for first, but additionally prevents kallsyms
from having to check this it self.
Assisted-by: LLM
Signed-off-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
---
scripts/elf-parse.c | 48 +++++++++++++++++++++++++++++++++++-------------
scripts/elf-parse.h | 19 +++++++++++++++++++
2 files changed, 54 insertions(+), 13 deletions(-)
diff --git a/scripts/elf-parse.c b/scripts/elf-parse.c
index 99869ff91a8c..87aa95b1e5e1 100644
--- a/scripts/elf-parse.c
+++ b/scripts/elf-parse.c
@@ -17,15 +17,21 @@ struct elf_funcs elf_parser;
* Get the whole file as a programming convenience in order to avoid
* malloc+lseek+read+free of many pieces. If successful, then mmap
* avoids copying unused pieces; else just read the whole file.
- * Open for both read and write.
+ * Open for both read and write if writable is true, otherwise open
+ * read-only.
*/
-static void *map_file(char const *fname, size_t *size)
+static void *map_file(char const *fname, size_t *size, bool writable)
{
- int fd;
+ int fd, prot = PROT_READ, flags = MAP_PRIVATE;
struct stat sb;
void *addr = NULL;
- fd = open(fname, O_RDWR);
+ if (writable) {
+ prot |= PROT_WRITE;
+ flags = MAP_SHARED;
+ }
+
+ fd = open(fname, writable ? O_RDWR : O_RDONLY);
if (fd < 0) {
perror(fname);
return NULL;
@@ -39,7 +45,7 @@ static void *map_file(char const *fname, size_t *size)
goto out;
}
- addr = mmap(0, sb.st_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
+ addr = mmap(0, sb.st_size, prot, flags, fd, 0);
if (addr == MAP_FAILED) {
fprintf(stderr, "Could not mmap file: %s\n", fname);
goto out;
@@ -57,6 +63,12 @@ static int elf_parse(const char *fname, void *addr, uint32_t types)
Elf_Ehdr *ehdr = addr;
uint16_t type;
+ if (memcmp(ELFMAG, ehdr->e32.e_ident, SELFMAG) != 0 ||
+ ehdr->e32.e_ident[EI_VERSION] != EV_CURRENT) {
+ fprintf(stderr, "unrecognized ELF file %s\n", fname);
+ return -1;
+ }
+
switch (ehdr->e32.e_ident[EI_DATA]) {
case ELFDATA2LSB:
elf_parser.r = rle;
@@ -78,12 +90,6 @@ static int elf_parse(const char *fname, void *addr, uint32_t types)
return -1;
}
- if (memcmp(ELFMAG, ehdr->e32.e_ident, SELFMAG) != 0 ||
- ehdr->e32.e_ident[EI_VERSION] != EV_CURRENT) {
- fprintf(stderr, "unrecognized ELF file %s\n", fname);
- return -1;
- }
-
type = elf_parser.r2(&ehdr->e32.e_type);
if (!((1 << type) & types)) {
fprintf(stderr, "Invalid ELF type file %s\n", fname);
@@ -103,7 +109,9 @@ static int elf_parse(const char *fname, void *addr, uint32_t types)
elf_parser.shdr_name = shdr32_name;
elf_parser.shdr_type = shdr32_type;
elf_parser.shdr_entsize = shdr32_entsize;
+ elf_parser.shdr_flags = shdr32_flags;
elf_parser.sym_type = sym32_type;
+ elf_parser.sym_bind = sym32_bind;
elf_parser.sym_name = sym32_name;
elf_parser.sym_value = sym32_value;
elf_parser.sym_shndx = sym32_shndx;
@@ -133,7 +141,9 @@ static int elf_parse(const char *fname, void *addr, uint32_t types)
elf_parser.shdr_name = shdr64_name;
elf_parser.shdr_type = shdr64_type;
elf_parser.shdr_entsize = shdr64_entsize;
+ elf_parser.shdr_flags = shdr64_flags;
elf_parser.sym_type = sym64_type;
+ elf_parser.sym_bind = sym64_bind;
elf_parser.sym_name = sym64_name;
elf_parser.sym_value = sym64_value;
elf_parser.sym_shndx = sym64_shndx;
@@ -174,12 +184,13 @@ int elf_map_long_size(void *addr)
return ehdr->e32.e_ident[EI_CLASS] == ELFCLASS32 ? 4 : 8;
}
-void *elf_map(char const *fname, size_t *size, uint32_t types)
+static void *__elf_map(char const *fname, size_t *size, uint32_t types,
+ bool writable)
{
void *addr;
int ret;
- addr = map_file(fname, size);
+ addr = map_file(fname, size, writable);
if (!addr)
return NULL;
@@ -192,6 +203,17 @@ void *elf_map(char const *fname, size_t *size, uint32_t types)
return addr;
}
+void *elf_map(char const *fname, size_t *size, uint32_t types)
+{
+ return __elf_map(fname, size, types, true);
+}
+
+/* For tools that only read the file. */
+void *elf_map_ro(char const *fname, size_t *size, uint32_t types)
+{
+ return __elf_map(fname, size, types, false);
+}
+
void elf_unmap(void *addr, size_t size)
{
munmap(addr, size);
diff --git a/scripts/elf-parse.h b/scripts/elf-parse.h
index f4411e03069d..453286e63032 100644
--- a/scripts/elf-parse.h
+++ b/scripts/elf-parse.h
@@ -37,10 +37,12 @@ struct elf_funcs {
uint64_t (*shdr_offset)(Elf_Shdr *shdr);
uint64_t (*shdr_size)(Elf_Shdr *shdr);
uint64_t (*shdr_entsize)(Elf_Shdr *shdr);
+ uint64_t (*shdr_flags)(Elf_Shdr *shdr);
uint32_t (*shdr_link)(Elf_Shdr *shdr);
uint32_t (*shdr_name)(Elf_Shdr *shdr);
uint32_t (*shdr_type)(Elf_Shdr *shdr);
uint8_t (*sym_type)(Elf_Sym *sym);
+ uint8_t (*sym_bind)(Elf_Sym *sym);
uint32_t (*sym_name)(Elf_Sym *sym);
uint64_t (*sym_value)(Elf_Sym *sym);
uint16_t (*sym_shndx)(Elf_Sym *sym);
@@ -143,6 +145,7 @@ SHDR_ADDR(addr)
SHDR_ADDR(offset)
SHDR_ADDR(size)
SHDR_ADDR(entsize)
+SHDR_ADDR(flags)
SHDR_WORD(link)
SHDR_WORD(name)
@@ -211,6 +214,21 @@ static inline uint8_t sym_type(Elf_Sym *sym)
return elf_parser.sym_type(sym);
}
+static inline uint8_t sym64_bind(Elf_Sym *sym)
+{
+ return ELF64_ST_BIND(sym->e64.st_info);
+}
+
+static inline uint8_t sym32_bind(Elf_Sym *sym)
+{
+ return ELF32_ST_BIND(sym->e32.st_info);
+}
+
+static inline uint8_t sym_bind(Elf_Sym *sym)
+{
+ return elf_parser.sym_bind(sym);
+}
+
SYM_ADDR(value)
SYM_WORD(name)
SYM_HALF(shndx)
@@ -298,6 +316,7 @@ static inline void w8le(uint64_t val, uint64_t *x)
}
void *elf_map(char const *fname, size_t *size, uint32_t types);
+void *elf_map_ro(char const *fname, size_t *size, uint32_t types);
void elf_unmap(void *addr, size_t size);
int elf_map_machine(void *addr);
int elf_map_long_size(void *addr);
--
2.55.0
^ permalink raw reply [flat|nested] 73+ messages in thread* [PATCH 08/23] kallsyms: reimplement mksysmap in C
2026-09-08 20:55 [PATCH 00/23] kbuild: significantly speed up kernel builds Lorenzo Stoakes (ARM)
` (6 preceding siblings ...)
2026-09-08 20:55 ` [PATCH 07/23] elf-parse: add section flags, symbol binding and a read-only mapping Lorenzo Stoakes (ARM)
@ 2026-09-08 20:55 ` Lorenzo Stoakes (ARM)
2026-09-08 20:55 ` [PATCH 09/23] kbuild: do not allocate .modinfo in vmlinux Lorenzo Stoakes (ARM)
` (18 subsequent siblings)
26 siblings, 0 replies; 73+ messages in thread
From: Lorenzo Stoakes (ARM) @ 2026-09-08 20:55 UTC (permalink / raw)
To: Linus Torvalds, Nathan Chancellor, Nicolas Schier,
Nick Desaulniers, Bill Wendling, Justin Stitt, Masahiro Yamada,
Alexey Gladkov, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, x86, H. Peter Anvin, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Alexandre Ghiti, Arnd Bergmann, Catalin Marinas,
Will Deacon, Mark Rutland, Ard Biesheuvel, Ilias Apalodimas,
Josh Poimboeuf, Peter Zijlstra, Miguel Ojeda, Boqun Feng,
Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Alice Ryhl, Trevor Gross, Danilo Krummrich, Daniel Almeida,
Tamir Duberstein, Alexandre Courbot, Onur Özkan,
Jonathan Corbet, Randy Dunlap
Cc: linux-kbuild, linux-kernel, llvm, linux-riscv, linux-arch,
linux-arm-kernel, linux-efi, rust-for-linux, linux-doc,
Jens Axboe, Lorenzo Stoakes (ARM)
mksysmap is a sed script consisting of 30 patterns which link-vmlinux.sh
uses to generate *.syms files, and which kallsyms is then called against to
generate *.kallsyms files, with the final vmlinux build ultimately
generating System.map.
For an x86-64 allmodconfig build, this involves three nm runs over a 250
MiB file and half a million lines written and read each time - 0.5s per
pass for llvm-nm, and 0.2s for GNU nm, with parsing on top of that.
This is unnecessary, instead have kallsyms simply read the ELF file
directly making use of the existing elf-parse library in scripts/.
This changes kallsyms such that its input is no longer the output from nm,
but rather an ELF file.
However, if the input file is empty, it outputs an empty table, which
retains the same behaviour on first pass that the build system expects.
System.map is byte-identical to nm | mksysmap for GNU nm and llvm-nm on
two x86 configurations each, and for llvm-nm on arm64, arm, s390 and
loongarch defconfigs, so are the kallsyms tables of every pass.
Relinking vmlinux, link steps included:
before after
allmodconfig clang 9.1s 7.9s
allmodconfig gcc 7.8s 7.6s
defconfig clang 3.7s 3.0s
defconfig gcc 3.5s 3.4s
Whole build, 128-thread Threadripper 9980X, best of N runs:
before after delta
-------------------------------
x86 defconfig, touch mm/vma.c, gcc 9.4s 8.9s -0.45s (-5%)
x86 defconfig, touch mm/vma.c, clang 9.2s 8.1s -1.1s (-12%)
x86 defconfig, clean, gcc 28.1s 27.6s -0.51s (-2%)
x86 defconfig, clean, clang 28.2s 27.2s -1.0s (-4%)
x86 allmodconfig, touch mm/vma.c, gcc 42.7s 42.0s -0.70s (-2%)
x86 allmodconfig, touch mm/vma.c, clang 40.2s 38.1s -2.1s (-5%)
Assisted-by: LLM
Signed-off-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
---
scripts/Makefile | 4 +-
scripts/kallsyms-sysmap.c | 269 ++++++++++++++++++++++++++++++++++++++++++++++
scripts/kallsyms.c | 162 ++++++++++++++++------------
scripts/kallsyms.h | 44 ++++++++
scripts/link-vmlinux.sh | 15 +--
scripts/mksysmap | 94 ----------------
6 files changed, 415 insertions(+), 173 deletions(-)
diff --git a/scripts/Makefile b/scripts/Makefile
index 3434a82a119f..d46932113b5f 100644
--- a/scripts/Makefile
+++ b/scripts/Makefile
@@ -3,7 +3,7 @@
# scripts contains sources for various helper programs used throughout
# the kernel for the build process.
-hostprogs-always-$(CONFIG_KALLSYMS) += kallsyms
+hostprogs-always-y += kallsyms
hostprogs-always-$(BUILD_C_RECORDMCOUNT) += recordmcount
hostprogs-always-$(CONFIG_BUILDTIME_TABLE_SORT) += sorttable
hostprogs-always-$(CONFIG_ASN1) += asn1_compiler
@@ -13,6 +13,7 @@ hostprogs-always-$(CONFIG_RUST_KERNEL_DOCTESTS) += rustdoc_test_builder
hostprogs-always-$(CONFIG_RUST_KERNEL_DOCTESTS) += rustdoc_test_gen
hostprogs-always-$(CONFIG_TRACEPOINTS) += tracepoint-update
+kallsyms-objs := kallsyms.o kallsyms-sysmap.o elf-parse.o
sorttable-objs := sorttable.o elf-parse.o
tracepoint-update-objs := tracepoint-update.o elf-parse.o
@@ -30,6 +31,7 @@ rustdoc_test_builder-rust := y
rustdoc_test_gen-rust := y
HOSTCFLAGS_tracepoint-update.o = -I$(srctree)/tools/include
+HOSTCFLAGS_kallsyms-sysmap.o = -I$(srctree)/tools/include
HOSTCFLAGS_elf-parse.o = -I$(srctree)/tools/include
HOSTCFLAGS_sorttable.o = -I$(srctree)/tools/include
HOSTLDLIBS_sorttable = -lpthread
diff --git a/scripts/kallsyms-sysmap.c b/scripts/kallsyms-sysmap.c
new file mode 100644
index 000000000000..64b2e11d0344
--- /dev/null
+++ b/scripts/kallsyms-sysmap.c
@@ -0,0 +1,269 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Obtain symbols from vmlinux for usage by kallsyms. Replaces mksysmap.
+ *
+ * To retain compatibility, it provides the same output as nm, only faster.
+ */
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <xalloc.h>
+
+#include "elf-parse.h"
+#include "kallsyms.h"
+
+/* The mapped file and its symbol table. */
+struct elf_file {
+ void *base;
+ size_t size;
+ const char *shdrs;
+ unsigned int shnum, shentsize;
+ const char *shstrtab;
+ Elf_Shdr *symtab;
+ const char *strtab;
+ size_t nr_syms;
+};
+
+/* What mksysmap dropped from System.map, by name. */
+static const char *const sysmap_omit_prefixes[] = {
+ "$", ".L", "__efistub_", "__pi_$", "__pi_.L", "__kvm_nvhe_$",
+ "__kvm_nvhe_.L", "__kcfi_typeid_", "__kvm_nvhe___kcfi_typeid_",
+ "__pi___kcfi_typeid_", "__crc_", "__kstrtab_", "__kstrtabns_",
+ "__mod_device_table__",
+};
+static const char *const sysmap_omit_suffixes[] = {
+ "_from_arm", "_from_thumb", "_veneer",
+};
+static const char *const sysmap_omit_names[] = {
+ "L0", "_SDA_BASE_", "_SDA2_BASE_",
+};
+
+/* __<alnum>*Thunk_: the linker's range extension thunks on arm. */
+static bool is_range_thunk(const char *name)
+{
+ const char *p;
+
+ if (!string_starts_with(name, "__"))
+ return false;
+ for (p = name + 2; isalnum((unsigned char)*p); p++)
+ ;
+ return p - name >= 7 && *p == '_' && strncmp(p - 5, "Thunk", 5) == 0;
+}
+
+/* __UNIQUE_ID_modinfo_<n>: the MODULE_INFO() strings of built-in code. */
+static bool is_modinfo_id(const char *name)
+{
+ static const char prefix[] = "__UNIQUE_ID_modinfo_";
+ const char *p;
+
+ if (!string_starts_with(name, prefix))
+ return false;
+ for (p = name + strlen(prefix); isdigit((unsigned char)*p); p++)
+ ;
+ return !*p;
+}
+
+static bool sysmap_omits(const char *name, char type)
+{
+ size_t i;
+
+ /* Absolute, undefined and debugging symbols. */
+ if (type == 'a' || type == 'N' || type == 'U' || type == 'w')
+ return true;
+
+ for (i = 0; i < ARRAY_SIZE(sysmap_omit_prefixes); i++)
+ if (string_starts_with(name, sysmap_omit_prefixes[i]))
+ return true;
+ for (i = 0; i < ARRAY_SIZE(sysmap_omit_suffixes); i++)
+ if (string_ends_with(name, sysmap_omit_suffixes[i]))
+ return true;
+ for (i = 0; i < ARRAY_SIZE(sysmap_omit_names); i++)
+ if (strcmp(name, sysmap_omit_names[i]) == 0)
+ return true;
+
+ return is_range_thunk(name) || is_modinfo_id(name) ||
+ strstr(name, ".long_branch.") || strstr(name, ".plt_branch.");
+}
+
+static Elf_Shdr *elf_section(const struct elf_file *elf, unsigned int index)
+{
+ return (Elf_Shdr *)(elf->shdrs + (size_t)index * elf->shentsize);
+}
+
+static const char *elf_section_name(const struct elf_file *elf, Elf_Shdr *shdr)
+{
+ return elf->shstrtab + shdr_name(shdr);
+}
+
+static Elf_Sym *elf_symbol(const struct elf_file *elf, size_t index)
+{
+ const char *base = elf->base;
+
+ return (Elf_Sym *)(base + shdr_offset(elf->symtab) +
+ index * shdr_entsize(elf->symtab));
+}
+
+/* nm's letter for a symbol defined in a section, as BFD classifies it. */
+static char section_symbol_type(Elf_Shdr *shdr, const char *secname)
+{
+ static const char *const debug_prefixes[] = {
+ ".debug", ".zdebug", ".gnu.debuglto_.debug_",
+ ".gnu.linkonce.wi.", ".line", ".stab",
+ };
+ const uint64_t flags = shdr_flags(shdr);
+ size_t i;
+
+ if (flags & SHF_EXECINSTR)
+ return 't';
+ if (flags & SHF_ALLOC) {
+ if (shdr_type(shdr) == SHT_NOBITS)
+ return 'b';
+ return flags & SHF_WRITE ? 'd' : 'r';
+ }
+ for (i = 0; i < ARRAY_SIZE(debug_prefixes); i++)
+ if (string_starts_with(secname, debug_prefixes[i]))
+ return 'N';
+ if (shdr_type(shdr) != SHT_NOBITS && !(flags & SHF_WRITE))
+ return 'n';
+ return '?';
+}
+
+/* The letter nm prints for a symbol, or 0 for one it leaves out. */
+static char elf_symbol_type(const struct elf_file *elf, Elf_Sym *sym)
+{
+ unsigned int bind = sym_bind(sym), type = sym_type(sym);
+ unsigned int shndx = sym_shndx(sym);
+ Elf_Shdr *shdr;
+ char c;
+
+ if (type == STT_SECTION || type == STT_FILE)
+ return 0;
+ if (shndx == SHN_COMMON)
+ return 'C';
+ if (shndx == SHN_UNDEF) {
+ if (bind == STB_WEAK)
+ return type == STT_OBJECT ? 'v' : 'w';
+ return 'U';
+ }
+ if (type == STT_GNU_IFUNC)
+ return 'i';
+ if (bind == STB_WEAK)
+ return type == STT_OBJECT ? 'V' : 'W';
+ if (bind == STB_GNU_UNIQUE)
+ return 'u';
+ if (bind != STB_GLOBAL && bind != STB_LOCAL)
+ return '?';
+
+ if (shndx == SHN_ABS) {
+ c = 'a';
+ } else if (shndx < elf->shnum) {
+ shdr = elf_section(elf, shndx);
+ c = section_symbol_type(shdr, elf_section_name(elf, shdr));
+ } else {
+ return '?';
+ }
+
+ return bind == STB_GLOBAL ? toupper(c) : c;
+}
+
+/* nm -n order: by address, then by name. */
+static int compare_symbols(const void *a, const void *b)
+{
+ const struct sysmap_symbol *sa = a, *sb = b;
+
+ if (sa->addr != sb->addr)
+ return sa->addr < sb->addr ? -1 : 1;
+ return strcmp(sa->name, sb->name);
+}
+
+static void elf_open(struct elf_file *elf, const char *path)
+{
+ Elf_Ehdr *ehdr;
+ unsigned int i;
+
+ elf->base = elf_map_ro(path, &elf->size, (1 << ET_EXEC) | (1 << ET_DYN));
+ if (!elf->base)
+ exit(EXIT_FAILURE);
+
+ ehdr = elf->base;
+ elf->shdrs = (const char *)elf->base + ehdr_shoff(ehdr);
+ elf->shnum = ehdr_shnum(ehdr);
+ elf->shentsize = ehdr_shentsize(ehdr);
+ elf->shstrtab = (const char *)elf->base +
+ shdr_offset(elf_section(elf, ehdr_shstrndx(ehdr)));
+
+ for (i = 0; i < elf->shnum && !elf->symtab; i++)
+ if (shdr_type(elf_section(elf, i)) == SHT_SYMTAB)
+ elf->symtab = elf_section(elf, i);
+
+ if (!elf->symtab) {
+ fprintf(stderr, "%s: no symbol table\n", path);
+ exit(EXIT_FAILURE);
+ }
+
+ elf->strtab = (const char *)elf->base +
+ shdr_offset(elf_section(elf, shdr_link(elf->symtab)));
+ elf->nr_syms = shdr_size(elf->symtab) / shdr_entsize(elf->symtab);
+}
+
+/* The symbols "nm -n | mksysmap" would list, in that order. */
+static struct sysmap_symbol *elf_read_symbols(const struct elf_file *elf,
+ size_t *nr_kept)
+{
+ struct sysmap_symbol *syms = xmalloc(elf->nr_syms * sizeof(*syms));
+ size_t i, n = 0;
+
+ for (i = 1; i < elf->nr_syms; i++) {
+ Elf_Sym *sym = elf_symbol(elf, i);
+ const char *name = elf->strtab + sym_name(sym);
+ char type = elf_symbol_type(elf, sym);
+
+ if (!type || sysmap_omits(name, type))
+ continue;
+
+ syms[n].addr = sym_value(sym);
+ syms[n].name = name;
+ syms[n].type = type;
+ n++;
+ }
+
+ qsort(syms, n, sizeof(*syms), compare_symbols);
+ *nr_kept = n;
+ return syms;
+}
+
+struct sysmap *sysmap_read(const char *path)
+{
+ struct sysmap *map = xcalloc(1, sizeof(*map));
+ struct elf_file elf = {};
+
+ elf_open(&elf, path);
+ map->syms = elf_read_symbols(&elf, &map->nr_syms);
+ map->addr_width = elf_map_long_size(elf.base) * 2;
+ /* The names point into the mapping; keep it until the map is freed. */
+ map->file = elf.base;
+ map->file_size = elf.size;
+
+ return map;
+}
+
+void sysmap_write(const struct sysmap *map, FILE *out)
+{
+ size_t i;
+
+ for (i = 0; i < map->nr_syms; i++) {
+ const struct sysmap_symbol *s = &map->syms[i];
+
+ fprintf(out, "%0*llx %c %s\n", map->addr_width, s->addr, s->type,
+ s->name);
+ }
+}
+
+void sysmap_free(struct sysmap *map)
+{
+ free(map->syms);
+ elf_unmap(map->file, map->file_size);
+ free(map);
+}
diff --git a/scripts/kallsyms.c b/scripts/kallsyms.c
index 61c5eb537ed4..b383696c3d81 100644
--- a/scripts/kallsyms.c
+++ b/scripts/kallsyms.c
@@ -5,7 +5,12 @@
* This software may be used and distributed according to the terms
* of the GNU General Public License, incorporated herein by reference.
*
- * Usage: kallsyms [--all-symbols] [--pc-relative] in.map out.bin > out.S
+ * Usage: kallsyms [--all-symbols] [--pc-relative] [--sysmap=out.map] in out.bin > out.S
+ * kallsyms --sysmap=out.map in
+ *
+ * in is vmlinux; an empty file stands for the first link, which has no
+ * symbols yet, and gives an empty table. --sysmap also writes the symbols
+ * in System.map format.
*
* The byte tables go to out.bin and are pulled into out.S with .incbin;
* wider tables stay assembler source for endianness and relocations.
@@ -21,7 +26,6 @@
*
*/
-#include <errno.h>
#include <getopt.h>
#include <stdbool.h>
#include <stdio.h>
@@ -29,10 +33,10 @@
#include <string.h>
#include <ctype.h>
#include <limits.h>
-
+#include <sys/stat.h>
#include <xalloc.h>
-#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0]))
+#include "kallsyms.h"
#define KSYM_NAME_LEN 512
@@ -105,11 +109,13 @@ static void sym_arr_free(struct sym_arr *arr)
static void usage(void)
{
- fprintf(stderr, "Usage: kallsyms [--all-symbols] [--pc-relative] in.map out.bin > out.S\n");
+ fprintf(stderr, "Usage: kallsyms [--all-symbols] [--pc-relative] [--sysmap=out.map]\n"
+ " in out.bin > out.S\n"
+ " kallsyms --sysmap=out.map vmlinux\n");
exit(1);
}
-static char *sym_name(const struct sym_entry *s)
+static char *sym_entry_name(const struct sym_entry *s)
{
return (char *)s->sym + 1;
}
@@ -150,37 +156,12 @@ static void check_symbol_range(const char *sym, unsigned long long addr,
}
}
-static struct sym_entry *read_symbol(FILE *in, char **buf, size_t *buf_len)
+static struct sym_entry *add_symbol(unsigned long long addr, char type,
+ const char *name)
{
- char *name, type, *p;
- unsigned long long addr;
- size_t len;
- ssize_t readlen;
+ size_t len = strlen(name);
struct sym_entry *sym;
- errno = 0;
- readlen = getline(buf, buf_len, in);
- if (readlen < 0) {
- if (errno) {
- perror("read_symbol");
- exit(EXIT_FAILURE);
- }
- return NULL;
- }
-
- if ((*buf)[readlen - 1] == '\n')
- (*buf)[readlen - 1] = 0;
-
- addr = strtoull(*buf, &p, 16);
-
- if (*buf == p || *p++ != ' ' || !isascii((type = *p++)) || *p++ != ' ') {
- fprintf(stderr, "line format error\n");
- exit(EXIT_FAILURE);
- }
-
- name = p;
- len = strlen(name);
-
if (len >= KSYM_NAME_LEN) {
fprintf(stderr, "Symbol %s too long for kallsyms (%zu >= %d).\n"
"Please increase KSYM_NAME_LEN both in kernel and kallsyms.c\n",
@@ -205,7 +186,7 @@ static struct sym_entry *read_symbol(FILE *in, char **buf, size_t *buf_len)
sym->addr = addr;
sym->len = len;
sym->sym[0] = type;
- strcpy(sym_name(sym), name);
+ strcpy(sym_entry_name(sym), name);
return sym;
}
@@ -226,14 +207,9 @@ static int symbol_in_range(const struct sym_entry *s,
return 0;
}
-static bool string_starts_with(const char *s, const char *prefix)
-{
- return strncmp(s, prefix, strlen(prefix)) == 0;
-}
-
static int symbol_valid(const struct sym_entry *s)
{
- const char *name = sym_name(s);
+ const char *name = sym_entry_name(s);
/* if --all-symbols is not specified, then symbols outside the text
* and inittext sections are discarded */
@@ -283,36 +259,55 @@ static void shrink_table(void)
table_cnt = pos;
}
-static void read_map(const char *in)
+static void add_table_entry(struct sym_entry *sym)
{
- FILE *fp;
- struct sym_entry *sym;
- char *buf = NULL;
- size_t buflen = 0;
+ sym->seq = table_cnt;
- fp = fopen(in, "r");
- if (!fp) {
- perror(in);
- exit(1);
+ if (table_cnt >= table_size) {
+ table_size += 10000;
+ table = xrealloc(table, sizeof(*table) * table_size);
}
- while (!feof(fp)) {
- sym = read_symbol(fp, &buf, &buflen);
- if (!sym)
- continue;
+ table[table_cnt++] = sym;
+}
- sym->seq = table_cnt;
+static bool file_is_empty(const char *path)
+{
+ struct stat st;
- if (table_cnt >= table_size) {
- table_size += 10000;
- table = xrealloc(table, sizeof(*table) * table_size);
- }
+ if (stat(path, &st)) {
+ perror(path);
+ exit(EXIT_FAILURE);
+ }
+
+ return st.st_size == 0;
+}
+
+/*
+ * Read the symbols from vmlinux, writing System.map if asked to. The first
+ * link has no symbols yet: an empty file gives an empty table.
+ */
+static void read_elf(const char *path, FILE *sysmap_out)
+{
+ struct sysmap *map;
+ size_t i;
+
+ if (file_is_empty(path))
+ return;
+
+ map = sysmap_read(path);
+ if (sysmap_out)
+ sysmap_write(map, sysmap_out);
+
+ for (i = 0; i < map->nr_syms; i++) {
+ const struct sysmap_symbol *s = &map->syms[i];
+ struct sym_entry *sym = add_symbol(s->addr, s->type, s->name);
- table[table_cnt++] = sym;
+ if (sym)
+ add_table_entry(sym);
}
- free(buf);
- fclose(fp);
+ sysmap_free(map);
}
static void output_label(const char *label)
@@ -389,7 +384,7 @@ static int compare_names(const void *a, const void *b)
const struct sym_entry *sa = *(const struct sym_entry **)a;
const struct sym_entry *sb = *(const struct sym_entry **)b;
- ret = strcmp(sym_name(sa), sym_name(sb));
+ ret = strcmp(sym_entry_name(sa), sym_entry_name(sb));
if (!ret) {
if (sa->addr > sb->addr)
return 1;
@@ -765,7 +760,7 @@ static void optimize_token_table(void)
/* guess for "linker script provide" symbol */
static int may_be_linker_script_provide_symbol(const struct sym_entry *se)
{
- const char *symbol = sym_name(se);
+ const char *symbol = sym_entry_name(se);
int len = se->len - 1;
if (len < 8)
@@ -822,8 +817,8 @@ static int compare_symbols(const void *a, const void *b)
return wa - wb;
/* sort by the number of prefix underscores */
- wa = strspn(sym_name(sa), "_");
- wb = strspn(sym_name(sb), "_");
+ wa = strspn(sym_entry_name(sa), "_");
+ wb = strspn(sym_entry_name(sb), "_");
if (wa != wb)
return wa - wb;
@@ -838,13 +833,14 @@ static void sort_symbols(void)
int main(int argc, char **argv)
{
- const char *out_bin_name;
- FILE *out_bin_file;
+ const char *in, *sysmap = NULL, *out_bin_name;
+ FILE *sysmap_out = NULL, *out_bin_file;
while (1) {
static const struct option long_options[] = {
{"all-symbols", no_argument, &all_symbols, 1},
{"pc-relative", no_argument, &pc_relative, 1},
+ {"sysmap", required_argument, NULL, 's'},
{},
};
@@ -852,13 +848,33 @@ int main(int argc, char **argv)
if (c == -1)
break;
- if (c != 0)
+ if (c == 's')
+ sysmap = optarg;
+ else if (c != 0)
usage();
}
- if (optind + 2 != argc)
+ if (optind + 2 != argc && !(sysmap && optind + 1 == argc))
usage();
+ in = argv[optind];
+ if (sysmap) {
+ sysmap_out = fopen(sysmap, "w");
+ if (!sysmap_out) {
+ perror(sysmap);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ if (optind + 1 == argc) {
+ read_elf(in, sysmap_out);
+ if (fclose(sysmap_out)) {
+ perror(sysmap);
+ exit(EXIT_FAILURE);
+ }
+ return 0;
+ }
+
out_bin_name = argv[optind + 1];
out_bin_file = fopen(out_bin_name, "w");
if (!out_bin_file) {
@@ -866,7 +882,11 @@ int main(int argc, char **argv)
exit(EXIT_FAILURE);
}
- read_map(argv[optind]);
+ read_elf(in, sysmap_out);
+ if (sysmap_out && fclose(sysmap_out)) {
+ perror(sysmap);
+ exit(EXIT_FAILURE);
+ }
shrink_table();
sort_symbols();
optimize_token_table();
diff --git a/scripts/kallsyms.h b/scripts/kallsyms.h
new file mode 100644
index 000000000000..12096978075f
--- /dev/null
+++ b/scripts/kallsyms.h
@@ -0,0 +1,44 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef KALLSYMS_H
+#define KALLSYMS_H
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <array_size.h>
+
+static inline bool string_starts_with(const char *s, const char *prefix)
+{
+ return strncmp(s, prefix, strlen(prefix)) == 0;
+}
+
+static inline bool string_ends_with(const char *s, const char *suffix)
+{
+ size_t len = strlen(s), suffix_len = strlen(suffix);
+
+ return len >= suffix_len && strcmp(s + len - suffix_len, suffix) == 0;
+}
+
+/* A symbol as nm lists it. */
+struct sysmap_symbol {
+ unsigned long long addr;
+ const char *name;
+ char type;
+};
+
+/* The symbols of an ELF file that System.map lists, in its order. */
+struct sysmap {
+ struct sysmap_symbol *syms;
+ size_t nr_syms;
+ int addr_width; /* hex digits of an address */
+ void *file; /* the mapping the names point into */
+ size_t file_size;
+};
+
+struct sysmap *sysmap_read(const char *path);
+void sysmap_write(const struct sysmap *map, FILE *out);
+void sysmap_free(struct sysmap *map);
+
+#endif /* KALLSYMS_H */
diff --git a/scripts/link-vmlinux.sh b/scripts/link-vmlinux.sh
index 09c5222ccb94..7e9d0676f59f 100755
--- a/scripts/link-vmlinux.sh
+++ b/scripts/link-vmlinux.sh
@@ -124,11 +124,15 @@ kallsymso_changed()
! cmp -s "${kallsymso_prev}.sym" "${kallsymso}.sym"
}
-# Create ${2}.o file with all symbols from the ${1} object file
+# Create ${2}.o with the kallsyms tables for ${1} (the vmlinux, or an empty
+# listing for the first pass); list the symbols used in ${3} if given.
kallsyms()
{
local kallsymopt;
+ if [ -n "${3:-}" ]; then
+ kallsymopt="--sysmap=${3}"
+ fi
if is_enabled CONFIG_KALLSYMS_ALL; then
kallsymopt="${kallsymopt} --all-symbols"
fi
@@ -151,18 +155,15 @@ kallsyms()
# Perform kallsyms for the given temporary vmlinux.
sysmap_and_kallsyms()
{
- mksysmap "${1}" "${1}.syms"
- kallsyms "${1}.syms" "${1}.kallsyms"
-
+ kallsyms "${1}" "${1}.kallsyms" "${1}.syms"
kallsyms_sysmap=${1}.syms
}
# Create map file with all symbols from ${1}
-# See mksymap for additional details
mksysmap()
{
- info NM ${2}
- ${NM} -n "${1}" | sed -f "${srctree}/scripts/mksysmap" > "${2}"
+ info SYSMAP ${2}
+ scripts/kallsyms --sysmap="${2}" "${1}"
}
sorttable()
diff --git a/scripts/mksysmap b/scripts/mksysmap
deleted file mode 100755
index 856b26ba2ac0..000000000000
--- a/scripts/mksysmap
+++ /dev/null
@@ -1,94 +0,0 @@
-#!/bin/sed -f
-# SPDX-License-Identifier: GPL-2.0-only
-#
-# sed script to filter out symbols that are not needed for System.map,
-# or not suitable for kallsyms. The input should be 'nm -n <file>'.
-#
-# System.map is used by module-init tools and some debugging
-# tools to retrieve the actual addresses of symbols in the kernel.
-#
-# readprofile starts reading symbols when _stext is found, and
-# continue until it finds a symbol which is not either of 'T', 't',
-# 'W' or 'w'.
-#
-# ---------------------------------------------------------------------------
-# Ignored symbol types
-#
-
-# a: local absolute symbols
-# N: debugging symbols
-# U: undefined global symbols
-# w: local weak symbols
-/ [aNUw] /d
-
-# ---------------------------------------------------------------------------
-# Ignored prefixes
-# (do not forget a space before each pattern)
-
-# local symbols for ARM, MIPS, etc.
-/ \$/d
-
-# local labels, .LBB, .Ltmpxxx, .L__unnamed_xx, .LASANPC, etc.
-/ \.L/d
-
-# arm64 EFI stub namespace
-/ __efistub_/d
-
-# arm64 local symbols in PIE namespace
-/ __pi_\$/d
-/ __pi_\.L/d
-
-# arm64 local symbols in non-VHE KVM namespace
-/ __kvm_nvhe_\$/d
-/ __kvm_nvhe_\.L/d
-
-# lld arm/aarch64/mips thunks
-/ __[[:alnum:]]*Thunk_/d
-
-# CFI type identifiers
-/ __kcfi_typeid_/d
-/ __kvm_nvhe___kcfi_typeid_/d
-/ __pi___kcfi_typeid_/d
-
-# CRC from modversions
-/ __crc_/d
-
-# EXPORT_SYMBOL (symbol name)
-/ __kstrtab_/d
-
-# EXPORT_SYMBOL (namespace)
-/ __kstrtabns_/d
-
-# MODULE_DEVICE_TABLE (symbol name)
-/ __mod_device_table__/d
-
-# ---------------------------------------------------------------------------
-# Ignored suffixes
-# (do not forget '$' after each pattern)
-
-# arm
-/_from_arm$/d
-/_from_thumb$/d
-/_veneer$/d
-
-# ---------------------------------------------------------------------------
-# Ignored symbols (exact match)
-# (do not forget a space before and '$' after each pattern)
-
-# for LoongArch?
-/ L0$/d
-
-# ppc
-/ _SDA_BASE_$/d
-/ _SDA2_BASE_$/d
-
-# MODULE_INFO()
-/ __UNIQUE_ID_modinfo_[0-9]*$/d
-
-# ---------------------------------------------------------------------------
-# Ignored patterns
-# (symbols that contain the pattern are ignored)
-
-# ppc stub
-/\.long_branch\./d
-/\.plt_branch\./d
--
2.55.0
^ permalink raw reply [flat|nested] 73+ messages in thread* [PATCH 09/23] kbuild: do not allocate .modinfo in vmlinux
2026-09-08 20:55 [PATCH 00/23] kbuild: significantly speed up kernel builds Lorenzo Stoakes (ARM)
` (7 preceding siblings ...)
2026-09-08 20:55 ` [PATCH 08/23] kallsyms: reimplement mksysmap in C Lorenzo Stoakes (ARM)
@ 2026-09-08 20:55 ` Lorenzo Stoakes (ARM)
2026-09-10 4:19 ` Nathan Chancellor
2026-09-08 20:55 ` [PATCH 10/23] kbuild: cache list, composite object state per object Lorenzo Stoakes (ARM)
` (17 subsequent siblings)
26 siblings, 1 reply; 73+ messages in thread
From: Lorenzo Stoakes (ARM) @ 2026-09-08 20:55 UTC (permalink / raw)
To: Linus Torvalds, Nathan Chancellor, Nicolas Schier,
Nick Desaulniers, Bill Wendling, Justin Stitt, Masahiro Yamada,
Alexey Gladkov, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, x86, H. Peter Anvin, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Alexandre Ghiti, Arnd Bergmann, Catalin Marinas,
Will Deacon, Mark Rutland, Ard Biesheuvel, Ilias Apalodimas,
Josh Poimboeuf, Peter Zijlstra, Miguel Ojeda, Boqun Feng,
Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Alice Ryhl, Trevor Gross, Danilo Krummrich, Daniel Almeida,
Tamir Duberstein, Alexandre Courbot, Onur Özkan,
Jonathan Corbet, Randy Dunlap
Cc: linux-kbuild, linux-kernel, llvm, linux-riscv, linux-arch,
linux-arm-kernel, linux-efi, rust-for-linux, linux-doc,
Jens Axboe, Lorenzo Stoakes (ARM)
Commit 3e86e4d74c04 ("kbuild: keep .modinfo section in vmlinux.unstripped")
placed MODULE_INFO() strings in the .modinfo section in vmlinux.unstripped,
however it left the section allocatable, so it gets assigned an address and
on arm64, arm and riscv, the section is tagged as PT_LOAD.
This is useless as the data is ultimately stripped anyway.
Doing this results in a lot of unnecessary work - each pass copies the
whole file, 450 MIB with relocations for an x86 allmodconfig build and 250
MiB for an arm64 allmodconfig build.
This adds ~0.7s on the serial tail of every build for arm64 and ~0.2s for
x86 (the tail is single-threaded work done after parallel work has
finished).
Nothing requires .modinfo to exist at an address, so fix this by using
--dump-section which prevents the allocation.
Image, bzImage, System.map and modules.builtin.modinfo are unchanged and
the stripped vmlinux differs only in its program headers.
The objcopy passes are on the serial tail of every build that links
vmlinux, no-op builds are unchanged.
Whole build, 128-thread Threadripper 9980X, best of N runs:
before after delta
-------------------------------
x86 defconfig, touch mm/vma.c, gcc 8.9s 8.8s -0.16s (-2%)
x86 defconfig, touch mm/vma.c, clang 8.1s 7.9s -0.20s (-2%)
x86 defconfig, clean, gcc 27.6s 27.5s -0.13s (0%)
x86 defconfig, clean, clang 27.2s 27.0s -0.18s (-1%)
x86 allmodconfig, touch mm/vma.c, gcc 42.0s 41.6s -0.40s (-1%)
Assisted-by: LLM
Signed-off-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
---
include/asm-generic/vmlinux.lds.h | 2 +-
scripts/Makefile.vmlinux | 24 ++++++------------------
2 files changed, 7 insertions(+), 19 deletions(-)
diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
index b2988aa12f66..7be9e0321867 100644
--- a/include/asm-generic/vmlinux.lds.h
+++ b/include/asm-generic/vmlinux.lds.h
@@ -855,7 +855,7 @@
KLP_SYMID
#define MODINFO \
- .modinfo : { *(.modinfo) . = ALIGN(8); }
+ .modinfo (INFO) : { *(.modinfo) . = ALIGN(8); }
#ifdef CONFIG_GENERIC_BUG
#define BUG_TABLE \
diff --git a/scripts/Makefile.vmlinux b/scripts/Makefile.vmlinux
index 4b54aaeca65d..1fc7a0ca8eaf 100644
--- a/scripts/Makefile.vmlinux
+++ b/scripts/Makefile.vmlinux
@@ -90,11 +90,9 @@ remove-section-$(CONFIG_ARCH_VMLINUX_NEEDS_RELOCS) += '.rel.*'
remove-symbols := -w --strip-unneeded-symbol='__mod_device_table__*'
-# To avoid warnings: "empty loadable segment detected at ..." from GNU objcopy,
-# it is necessary to remove the PT_LOAD flag from the segment.
+# none of the removed sections is allocated, so no segment is left empty
quiet_cmd_strip_relocs = OBJCOPY $@
- cmd_strip_relocs = $(OBJCOPY) $(patsubst %,--set-section-flags %=noload,$(remove-section-y)) $< $@; \
- $(OBJCOPY) $(addprefix --remove-section=,$(remove-section-y)) $(remove-symbols) $@
+ cmd_strip_relocs = $(OBJCOPY) $(addprefix --remove-section=,$(remove-section-y)) $(remove-symbols) $< $@
targets += vmlinux
vmlinux: vmlinux.unstripped FORCE
@@ -103,21 +101,11 @@ vmlinux: vmlinux.unstripped FORCE
# modules.builtin.modinfo
# ---------------------------------------------------------------------------
-# .modinfo in vmlinux.unstripped is aligned to 8 bytes for compatibility with
-# tools that expect vmlinux to have sufficiently aligned sections but the
-# additional bytes used for padding .modinfo to satisfy this requirement break
-# certain versions of kmod with
-#
-# depmod: ERROR: kmod_builtin_iter_next: unexpected string without modname prefix
-#
-# Strip the trailing padding bytes after extracting .modinfo to comply with
-# what kmod expects to parse.
+# use --dump-section to include non-allocated sections
quiet_cmd_modules_builtin_modinfo = GEN $@
- cmd_modules_builtin_modinfo = $(cmd_objcopy); \
- sed -i 's/\x00\+$$/\x00/g' $@; \
- chmod -x $@
-
-OBJCOPYFLAGS_modules.builtin.modinfo := -j .modinfo -O binary
+ cmd_modules_builtin_modinfo = $(OBJCOPY) -O binary -j .modinfo --dump-section .modinfo=$@ $< $@.tmp; \
+ rm -f $@.tmp; \
+ sed -i 's/\x00\+$$/\x00/g' $@
targets += modules.builtin.modinfo
modules.builtin.modinfo: vmlinux.unstripped FORCE
--
2.55.0
^ permalink raw reply [flat|nested] 73+ messages in thread* Re: [PATCH 09/23] kbuild: do not allocate .modinfo in vmlinux
2026-09-08 20:55 ` [PATCH 09/23] kbuild: do not allocate .modinfo in vmlinux Lorenzo Stoakes (ARM)
@ 2026-09-10 4:19 ` Nathan Chancellor
2026-09-10 10:59 ` Lorenzo Stoakes (ARM)
0 siblings, 1 reply; 73+ messages in thread
From: Nathan Chancellor @ 2026-09-10 4:19 UTC (permalink / raw)
To: Lorenzo Stoakes (ARM)
Cc: Linus Torvalds, Nathan Chancellor, Nicolas Schier,
Nick Desaulniers, Bill Wendling, Justin Stitt, Masahiro Yamada,
Alexey Gladkov, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, x86, H. Peter Anvin, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Alexandre Ghiti, Arnd Bergmann, Catalin Marinas,
Will Deacon, Mark Rutland, Ard Biesheuvel, Ilias Apalodimas,
Josh Poimboeuf, Peter Zijlstra, Miguel Ojeda, Boqun Feng,
Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Alice Ryhl, Trevor Gross, Danilo Krummrich, Daniel Almeida,
Tamir Duberstein, Alexandre Courbot, Onur Özkan,
Jonathan Corbet, Randy Dunlap, linux-kbuild, linux-kernel, llvm,
linux-riscv, linux-arch, linux-arm-kernel, linux-efi,
rust-for-linux, linux-doc, Jens Axboe
> Commit 3e86e4d74c04 ("kbuild: keep .modinfo section in vmlinux.unstripped")
> placed MODULE_INFO() strings in the .modinfo section in vmlinux.unstripped,
> however it left the section allocatable, so it gets assigned an address and
> on arm64, arm and riscv, the section is tagged as PT_LOAD.
>
> This is useless as the data is ultimately stripped anyway.
>
> Doing this results in a lot of unnecessary work - each pass copies the
> whole file, 450 MIB with relocations for an x86 allmodconfig build and 250
> MiB for an arm64 allmodconfig build.
>
> This adds ~0.7s on the serial tail of every build for arm64 and ~0.2s for
> x86 (the tail is single-threaded work done after parallel work has
> finished).
>
> Nothing requires .modinfo to exist at an address, so fix this by using
> --dump-section which prevents the allocation.
>
> Image, bzImage, System.map and modules.builtin.modinfo are unchanged and
> the stripped vmlinux differs only in its program headers.
>
> The objcopy passes are on the serial tail of every build that links
> vmlinux, no-op builds are unchanged.
>
> Whole build, 128-thread Threadripper 9980X, best of N runs:
>
> before after delta
> -------------------------------
> x86 defconfig, touch mm/vma.c, gcc 8.9s 8.8s -0.16s (-2%)
> x86 defconfig, touch mm/vma.c, clang 8.1s 7.9s -0.20s (-2%)
> x86 defconfig, clean, gcc 27.6s 27.5s -0.13s (0%)
> x86 defconfig, clean, clang 27.2s 27.0s -0.18s (-1%)
> x86 allmodconfig, touch mm/vma.c, gcc 42.0s 41.6s -0.40s (-1%)
>
> Assisted-by: LLM
> Signed-off-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
>
> diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
> index b2988aa12f66..7be9e0321867 100644
> --- a/include/asm-generic/vmlinux.lds.h
> +++ b/include/asm-generic/vmlinux.lds.h
> @@ -855,7 +855,7 @@
> KLP_SYMID
>
> #define MODINFO \
> - .modinfo : { *(.modinfo) . = ALIGN(8); }
> + .modinfo (INFO) : { *(.modinfo) . = ALIGN(8); }
>
> #ifdef CONFIG_GENERIC_BUG
> #define BUG_TABLE \
> diff --git a/scripts/Makefile.vmlinux b/scripts/Makefile.vmlinux
> index 4b54aaeca65d..1fc7a0ca8eaf 100644
> --- a/scripts/Makefile.vmlinux
> +++ b/scripts/Makefile.vmlinux
> @@ -90,11 +90,9 @@ remove-section-$(CONFIG_ARCH_VMLINUX_NEEDS_RELOCS) += '.rel.*'
>
> remove-symbols := -w --strip-unneeded-symbol='__mod_device_table__*'
>
> -# To avoid warnings: "empty loadable segment detected at ..." from GNU objcopy,
> -# it is necessary to remove the PT_LOAD flag from the segment.
> +# none of the removed sections is allocated, so no segment is left empty
I think we could just drop this comment, I don't think it is
particularly helpful
> quiet_cmd_strip_relocs = OBJCOPY $@
> - cmd_strip_relocs = $(OBJCOPY) $(patsubst %,--set-section-flags %=noload,$(remove-section-y)) $< $@; \
> - $(OBJCOPY) $(addprefix --remove-section=,$(remove-section-y)) $(remove-symbols) $@
> + cmd_strip_relocs = $(OBJCOPY) $(addprefix --remove-section=,$(remove-section-y)) $(remove-symbols) $< $@
>
> targets += vmlinux
> vmlinux: vmlinux.unstripped FORCE
> @@ -103,21 +101,11 @@ vmlinux: vmlinux.unstripped FORCE
> # modules.builtin.modinfo
> # ---------------------------------------------------------------------------
>
> -# .modinfo in vmlinux.unstripped is aligned to 8 bytes for compatibility with
> -# tools that expect vmlinux to have sufficiently aligned sections but the
> -# additional bytes used for padding .modinfo to satisfy this requirement break
> -# certain versions of kmod with
> -#
> -# depmod: ERROR: kmod_builtin_iter_next: unexpected string without modname prefix
> -#
> -# Strip the trailing padding bytes after extracting .modinfo to comply with
> -# what kmod expects to parse.
> +# use --dump-section to include non-allocated sections
Same thing here, I am not sure anyone will question '--dump-section'
here. Otherwise, this seems reasonable. I was curious about the $@.tmp
until I read the objcopy manual but that probably is not worth a
comment.
I can probably take this one faster than the rest if you want to put it
early in the stack since it is pretty low hanging fruit and earlier it
gets exposure to -next, the better.
--
Cheers,
Nathan
^ permalink raw reply [flat|nested] 73+ messages in thread* Re: [PATCH 09/23] kbuild: do not allocate .modinfo in vmlinux
2026-09-10 4:19 ` Nathan Chancellor
@ 2026-09-10 10:59 ` Lorenzo Stoakes (ARM)
0 siblings, 0 replies; 73+ messages in thread
From: Lorenzo Stoakes (ARM) @ 2026-09-10 10:59 UTC (permalink / raw)
To: Nathan Chancellor
Cc: Linus Torvalds, Nicolas Schier, Nick Desaulniers, Bill Wendling,
Justin Stitt, Masahiro Yamada, Alexey Gladkov, Thomas Gleixner,
Ingo Molnar, Borislav Petkov, Dave Hansen, x86, H. Peter Anvin,
Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti,
Arnd Bergmann, Catalin Marinas, Will Deacon, Mark Rutland,
Ard Biesheuvel, Ilias Apalodimas, Josh Poimboeuf, Peter Zijlstra,
Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Daniel Almeida, Tamir Duberstein,
Alexandre Courbot, Onur Özkan, Jonathan Corbet,
Randy Dunlap, linux-kbuild, linux-kernel, llvm, linux-riscv,
linux-arch, linux-arm-kernel, linux-efi, rust-for-linux,
linux-doc, Jens Axboe
On Wed, Sep 09, 2026 at 09:19:12PM -0700, Nathan Chancellor wrote:
> > Commit 3e86e4d74c04 ("kbuild: keep .modinfo section in vmlinux.unstripped")
> > placed MODULE_INFO() strings in the .modinfo section in vmlinux.unstripped,
> > however it left the section allocatable, so it gets assigned an address and
> > on arm64, arm and riscv, the section is tagged as PT_LOAD.
> >
> > This is useless as the data is ultimately stripped anyway.
> >
> > Doing this results in a lot of unnecessary work - each pass copies the
> > whole file, 450 MIB with relocations for an x86 allmodconfig build and 250
> > MiB for an arm64 allmodconfig build.
> >
> > This adds ~0.7s on the serial tail of every build for arm64 and ~0.2s for
> > x86 (the tail is single-threaded work done after parallel work has
> > finished).
> >
> > Nothing requires .modinfo to exist at an address, so fix this by using
> > --dump-section which prevents the allocation.
> >
> > Image, bzImage, System.map and modules.builtin.modinfo are unchanged and
> > the stripped vmlinux differs only in its program headers.
> >
> > The objcopy passes are on the serial tail of every build that links
> > vmlinux, no-op builds are unchanged.
> >
> > Whole build, 128-thread Threadripper 9980X, best of N runs:
> >
> > before after delta
> > -------------------------------
> > x86 defconfig, touch mm/vma.c, gcc 8.9s 8.8s -0.16s (-2%)
> > x86 defconfig, touch mm/vma.c, clang 8.1s 7.9s -0.20s (-2%)
> > x86 defconfig, clean, gcc 27.6s 27.5s -0.13s (0%)
> > x86 defconfig, clean, clang 27.2s 27.0s -0.18s (-1%)
> > x86 allmodconfig, touch mm/vma.c, gcc 42.0s 41.6s -0.40s (-1%)
> >
> > Assisted-by: LLM
> > Signed-off-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
> >
> > diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
> > index b2988aa12f66..7be9e0321867 100644
> > --- a/include/asm-generic/vmlinux.lds.h
> > +++ b/include/asm-generic/vmlinux.lds.h
> > @@ -855,7 +855,7 @@
> > KLP_SYMID
> >
> > #define MODINFO \
> > - .modinfo : { *(.modinfo) . = ALIGN(8); }
> > + .modinfo (INFO) : { *(.modinfo) . = ALIGN(8); }
> >
> > #ifdef CONFIG_GENERIC_BUG
> > #define BUG_TABLE \
> > diff --git a/scripts/Makefile.vmlinux b/scripts/Makefile.vmlinux
> > index 4b54aaeca65d..1fc7a0ca8eaf 100644
> > --- a/scripts/Makefile.vmlinux
> > +++ b/scripts/Makefile.vmlinux
> > @@ -90,11 +90,9 @@ remove-section-$(CONFIG_ARCH_VMLINUX_NEEDS_RELOCS) += '.rel.*'
> >
> > remove-symbols := -w --strip-unneeded-symbol='__mod_device_table__*'
> >
> > -# To avoid warnings: "empty loadable segment detected at ..." from GNU objcopy,
> > -# it is necessary to remove the PT_LOAD flag from the segment.
> > +# none of the removed sections is allocated, so no segment is left empty
>
> I think we could just drop this comment, I don't think it is
> particularly helpful
Ack!
>
> > quiet_cmd_strip_relocs = OBJCOPY $@
> > - cmd_strip_relocs = $(OBJCOPY) $(patsubst %,--set-section-flags %=noload,$(remove-section-y)) $< $@; \
> > - $(OBJCOPY) $(addprefix --remove-section=,$(remove-section-y)) $(remove-symbols) $@
> > + cmd_strip_relocs = $(OBJCOPY) $(addprefix --remove-section=,$(remove-section-y)) $(remove-symbols) $< $@
> >
> > targets += vmlinux
> > vmlinux: vmlinux.unstripped FORCE
> > @@ -103,21 +101,11 @@ vmlinux: vmlinux.unstripped FORCE
> > # modules.builtin.modinfo
> > # ---------------------------------------------------------------------------
> >
> > -# .modinfo in vmlinux.unstripped is aligned to 8 bytes for compatibility with
> > -# tools that expect vmlinux to have sufficiently aligned sections but the
> > -# additional bytes used for padding .modinfo to satisfy this requirement break
> > -# certain versions of kmod with
> > -#
> > -# depmod: ERROR: kmod_builtin_iter_next: unexpected string without modname prefix
> > -#
> > -# Strip the trailing padding bytes after extracting .modinfo to comply with
> > -# what kmod expects to parse.
> > +# use --dump-section to include non-allocated sections
>
> Same thing here, I am not sure anyone will question '--dump-section'
> here. Otherwise, this seems reasonable. I was curious about the $@.tmp
> until I read the objcopy manual but that probably is not worth a
> comment.
Ack too.
>
> I can probably take this one faster than the rest if you want to put it
> early in the stack since it is pretty low hanging fruit and earlier it
> gets exposure to -next, the better.
Sure sounds good, the sooner we get these in -next the better.
I was planning to go through feedback/sashiko (+ give a bit more time for people
to respond) before doing a v2, but I guess you could put this to -next as-is,
given v2 will just be stripping comments here (as it stands anyway!)
>
> --
> Cheers,
> Nathan
>
--
Cheers, Lorenzo
^ permalink raw reply [flat|nested] 73+ messages in thread
* [PATCH 10/23] kbuild: cache list, composite object state per object
2026-09-08 20:55 [PATCH 00/23] kbuild: significantly speed up kernel builds Lorenzo Stoakes (ARM)
` (8 preceding siblings ...)
2026-09-08 20:55 ` [PATCH 09/23] kbuild: do not allocate .modinfo in vmlinux Lorenzo Stoakes (ARM)
@ 2026-09-08 20:55 ` Lorenzo Stoakes (ARM)
2026-09-08 20:55 ` [PATCH 11/23] kbuild: implement and use depcheck to check dependency timestamps Lorenzo Stoakes (ARM)
` (16 subsequent siblings)
26 siblings, 0 replies; 73+ messages in thread
From: Lorenzo Stoakes (ARM) @ 2026-09-08 20:55 UTC (permalink / raw)
To: Linus Torvalds, Nathan Chancellor, Nicolas Schier,
Nick Desaulniers, Bill Wendling, Justin Stitt, Masahiro Yamada,
Alexey Gladkov, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, x86, H. Peter Anvin, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Alexandre Ghiti, Arnd Bergmann, Catalin Marinas,
Will Deacon, Mark Rutland, Ard Biesheuvel, Ilias Apalodimas,
Josh Poimboeuf, Peter Zijlstra, Miguel Ojeda, Boqun Feng,
Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Alice Ryhl, Trevor Gross, Danilo Krummrich, Daniel Almeida,
Tamir Duberstein, Alexandre Courbot, Onur Özkan,
Jonathan Corbet, Randy Dunlap
Cc: linux-kbuild, linux-kernel, llvm, linux-riscv, linux-arch,
linux-arm-kernel, linux-efi, rust-for-linux, linux-doc,
Jens Axboe, Lorenzo Stoakes (ARM)
When each object's command line is expanded, kbuild has to figure out on
multiple occasions whether it's built-in or part of a module and which
composite object it belongs to.
This causes the time spent on each directory in the kernel tree to grow
O(n^2) with its object count, which is especially problematic for instance
in drivers/gpu/drm/amd/amdgpu with 310 objects.
No-op builds (i.e. make -j $(nproc) when nothing has changed) are
particularly impacted by this.
Fix the issue by caching this data and looking it up instead of getting it
over and over again.
This has a particularly large impact on allmodconfig builds.
Whole build, 128-thread Threadripper 9980X, best of N runs:
before after delta
-------------------------------
x86 defconfig, no-op make, gcc 0.94s 0.92s -0.02s (-2%)
x86 defconfig, no-op make, clang 1.1s 1.1s -0.01s (-1%)
x86 allmodconfig, no-op make, gcc 13.0s 12.3s -0.64s (-5%)
x86 allmodconfig, no-op make, clang 13.8s 13.2s -0.64s (-5%)
x86 allmodconfig, touch mm/vma.c, gcc 41.6s 40.9s -0.70s (-2%)
x86 allmodconfig, touch mm/vma.c, clang 38.1s 37.6s -0.52s (-1%)
Assisted-by: LLM
Signed-off-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
---
scripts/Makefile.build | 8 ++++++++
scripts/Makefile.lib | 7 +++----
2 files changed, 11 insertions(+), 4 deletions(-)
diff --git a/scripts/Makefile.build b/scripts/Makefile.build
index 4349108e75e1..2cabfe85b798 100644
--- a/scripts/Makefile.build
+++ b/scripts/Makefile.build
@@ -122,6 +122,14 @@ multi-obj-m := $(addprefix $(obj)/, $(multi-obj-m))
subdir-ym := $(addprefix $(obj)/, $(subdir-ym))
endif
+# Cache which list each object is in and which composite objects it belongs to,
+# once per object for $(part-of-builtin), $(part-of-module) and $(modname-multi).
+$(foreach o, $(real-obj-y) $(lib-y), $(eval part-of-builtin_$o := y))
+$(foreach o, $(real-obj-m), $(eval part-of-module_$o := y))
+$(foreach m, $(multi-obj-ym), \
+ $(foreach o, $(call suffix-search, $m, .o, -objs -y -m), \
+ $(eval modname-multi_$o += $(m:.o=))))
+
ifndef obj
$(warning kbuild: Makefile.build is included improperly)
endif
diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib
index 0a4fdd8bd975..2f447bc25e7b 100644
--- a/scripts/Makefile.lib
+++ b/scripts/Makefile.lib
@@ -2,8 +2,7 @@
# Finds the multi-part object the current object will be linked into.
# If the object belongs to two or more multi-part objects, list them all.
-modname-multi = $(sort $(foreach m,$(multi-obj-ym),\
- $(if $(filter $*.o, $(call suffix-search, $m, .o, -objs -y -m)),$(m:.o=))))
+modname-multi = $(sort $(modname-multi_$*.o))
__modname = $(or $(modname-multi),$(basetarget))
@@ -149,8 +148,8 @@ endif
# If $(is-kernel-object) is 'y', this object will be linked to vmlinux or modules
is-kernel-object = $(or $(part-of-builtin),$(part-of-module))
-part-of-builtin = $(if $(filter $(basename $@).o, $(real-obj-y) $(lib-y)),y)
-part-of-module = $(if $(filter $(basename $@).o, $(real-obj-m)),y)
+part-of-builtin = $(part-of-builtin_$(basename $@).o)
+part-of-module = $(part-of-module_$(basename $@).o)
quiet_modtag = $(if $(part-of-module),[M], )
modkern_cflags = \
--
2.55.0
^ permalink raw reply [flat|nested] 73+ messages in thread* [PATCH 11/23] kbuild: implement and use depcheck to check dependency timestamps
2026-09-08 20:55 [PATCH 00/23] kbuild: significantly speed up kernel builds Lorenzo Stoakes (ARM)
` (9 preceding siblings ...)
2026-09-08 20:55 ` [PATCH 10/23] kbuild: cache list, composite object state per object Lorenzo Stoakes (ARM)
@ 2026-09-08 20:55 ` Lorenzo Stoakes (ARM)
2026-09-09 15:26 ` Linus Torvalds
2026-09-08 20:55 ` [PATCH 12/23] kbuild: avoid re-running compiler and linker probes Lorenzo Stoakes (ARM)
` (15 subsequent siblings)
26 siblings, 1 reply; 73+ messages in thread
From: Lorenzo Stoakes (ARM) @ 2026-09-08 20:55 UTC (permalink / raw)
To: Linus Torvalds, Nathan Chancellor, Nicolas Schier,
Nick Desaulniers, Bill Wendling, Justin Stitt, Masahiro Yamada,
Alexey Gladkov, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, x86, H. Peter Anvin, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Alexandre Ghiti, Arnd Bergmann, Catalin Marinas,
Will Deacon, Mark Rutland, Ard Biesheuvel, Ilias Apalodimas,
Josh Poimboeuf, Peter Zijlstra, Miguel Ojeda, Boqun Feng,
Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Alice Ryhl, Trevor Gross, Danilo Krummrich, Daniel Almeida,
Tamir Duberstein, Alexandre Courbot, Onur Özkan,
Jonathan Corbet, Randy Dunlap
Cc: linux-kbuild, linux-kernel, llvm, linux-riscv, linux-arch,
linux-arm-kernel, linux-efi, rust-for-linux, linux-doc,
Jens Axboe, Lorenzo Stoakes (ARM)
Each object's .cmd file lists its header dependencies, which often
consists of over a thousand dependencies.
When little has changed in the tree, this is what make spends most of its
time doing, spending over a second, single-threaded when parsing larger
directory trees.
It performs considerably more work that is actually necessary to get the
job done.
This can be done faster in C, so implement scripts/basic/depcheck to do so.
It does as little work as possible, reading the .cmd files from a
directory's targets and running stat on each dependency only a single time.
It generates a fragment holding only the saved command line for a target
whose dependencies all exist and are older than it, and the entire .cmd
file for any other.
That way, Makefile.build simply includes the fragment and the amount of
work make has to do is significantly reduced when there is not much work to
do.
If the operation fails, kbuild falls back to using .cmd files.
The result is exactly the same as before.
For drivers/gpu/drm/amd/amdgpu in an allmodconfig tree the fragment is a
fifth of the size of the .cmd files, make reads it in 10ms instead of
380ms, and the instance goes from 2.2s to 0.4s.
Whole build, 128-thread Threadripper 9980X, best of N runs:
before after delta
-------------------------------
x86 defconfig, no-op make, gcc 0.92s 0.62s -0.29s (-32%)
x86 defconfig, no-op make, clang 1.1s 0.78s -0.31s (-29%)
x86 allmodconfig, no-op make, gcc 12.3s 11.2s -1.2s (-10%)
x86 allmodconfig, no-op make, clang 13.2s 11.9s -1.3s (-10%)
x86 allmodconfig, touch mm/vma.c, gcc 40.9s 40.6s -0.3s (-1%)
x86 allmodconfig, touch mm/vma.c, clang 37.6s 36.5s -1.0s (-3%)
Assisted-by: LLM
Signed-off-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
---
Makefile | 1 +
scripts/Makefile.build | 14 +-
scripts/basic/.gitignore | 1 +
scripts/basic/Makefile | 2 +-
scripts/basic/depcheck.c | 442 +++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 458 insertions(+), 2 deletions(-)
diff --git a/Makefile b/Makefile
index 85eb2fd026cb..56e56bea53c3 100644
--- a/Makefile
+++ b/Makefile
@@ -2239,6 +2239,7 @@ clean: $(clean-dirs)
$(call cmd,rmfiles)
@find . $(RCS_FIND_IGNORE) \
\( -name '*.[aios]' -o -name '*.rsi' -o -name '*.ko' -o -name '.*.cmd' \
+ -o -name '.depcheck.mk' -o -name '.depcheck.mk.tmp' \
-o -name '*.ko.*' -o -name '*.o.thinlto.bc' \
-o -name '*.dtb' -o -name '*.dtbo' \
-o -name '*.dtb.S' -o -name '*.dtbo.S' \
diff --git a/scripts/Makefile.build b/scripts/Makefile.build
index 2cabfe85b798..b9093b39cc2f 100644
--- a/scripts/Makefile.build
+++ b/scripts/Makefile.build
@@ -592,7 +592,19 @@ $(obj)/: $(if $(KBUILD_BUILTIN), $(targets-for-builtin)) \
existing-targets := $(wildcard $(sort $(targets)))
--include $(foreach f,$(existing-targets),$(dir $(f)).$(notdir $(f)).cmd)
+cmd-files := $(foreach f,$(existing-targets),$(dir $(f)).$(notdir $(f)).cmd)
+
+# depcheck checks timestamps first and outputs only saved command lines of
+# up-to-date targets, falling back to .cmd files if it fails.
+depcheck := $(wildcard $(objtree)/scripts/basic/depcheck)
+
+ifneq ($(and $(depcheck),$(cmd-files)),)
+ifeq ($(shell $(depcheck) $(obj)/.depcheck.mk $(cmd-files) && echo ok),ok)
+cmd-files := $(obj)/.depcheck.mk
+endif
+endif
+
+-include $(cmd-files)
# Create directories for object files if they do not exist
obj-dirs := $(sort $(patsubst %/,%, $(dir $(targets))))
diff --git a/scripts/basic/.gitignore b/scripts/basic/.gitignore
index 07c195f605a1..761ee14f9477 100644
--- a/scripts/basic/.gitignore
+++ b/scripts/basic/.gitignore
@@ -1,3 +1,4 @@
# SPDX-License-Identifier: GPL-2.0-only
+/depcheck
/fixdep
/randstruct.seed
diff --git a/scripts/basic/Makefile b/scripts/basic/Makefile
index fb8e2c38fbc7..ff98780e474d 100644
--- a/scripts/basic/Makefile
+++ b/scripts/basic/Makefile
@@ -2,7 +2,7 @@
#
# fixdep: used to generate dependency information during build process
-hostprogs-always-y += fixdep
+hostprogs-always-y += fixdep depcheck
# randstruct: the seed is needed before building the gcc-plugin or
# before running a Clang kernel build.
diff --git a/scripts/basic/depcheck.c b/scripts/basic/depcheck.c
new file mode 100644
index 000000000000..5f9bbdff8803
--- /dev/null
+++ b/scripts/basic/depcheck.c
@@ -0,0 +1,442 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * depcheck - check the dependency timestamps of a directory's targets so that
+ * make reads only what it needs from their .cmd files.
+ *
+ * fixdep writes a .cmd file as:
+ *
+ * savedcmd_dir/foo.o := <command line>
+ *
+ * source_dir/foo.o := dir/foo.c
+ *
+ * deps_dir/foo.o := \
+ * include/linux/bar.h \
+ * $(wildcard include/config/BAZ) \
+ *
+ * dir/foo.o: $(deps_dir/foo.o)
+ *
+ * $(deps_dir/foo.o):
+ *
+ * and kbuild may append rules of its own after that, such as one making the
+ * target depend on objtool.
+ *
+ * For a target that exists and is newer than every dependency listed, make can
+ * have nothing to do with the list, so it is left out and only what precedes
+ * and follows it is passed on; for anything else the .cmd file is passed on in
+ * full.
+ *
+ * Usage: depcheck <output> <.cmd files...>
+ *
+ * The output is a makefile fragment to include in place of the .cmd files; a
+ * non-zero exit status means the caller should include those instead.
+ */
+#include <limits.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <hash.h>
+#include <hashtable.h>
+#include <xalloc.h>
+
+/* What fixdep writes, see above. */
+#define DEPS_PREFIX "deps_"
+#define DEPS_RULE_PREFIX "$(" DEPS_PREFIX
+#define RULE_SUFFIX ":"
+#define LINE_CONTINUATION " \\"
+#define WILDCARD_OPEN "$(wildcard "
+#define WILDCARD_CLOSE ")"
+#define CMD_SUFFIX ".cmd"
+
+/* A line of a .cmd file, without its newline. */
+struct line {
+ const char *text;
+ size_t len;
+};
+
+static bool is_blank(char chr)
+{
+ return chr == ' ' || chr == '\t';
+}
+
+static bool str_ends_with(const char *str, const char *suffix)
+{
+ const size_t len = strlen(str), suffix_len = strlen(suffix);
+
+ return len >= suffix_len && !strcmp(str + len - suffix_len, suffix);
+}
+
+static bool line_starts_with(const struct line *line, const char *prefix)
+{
+ const size_t len = strlen(prefix);
+
+ return line->len >= len && !memcmp(line->text, prefix, len);
+}
+
+static bool line_ends_with(const struct line *line, const char *suffix)
+{
+ const size_t len = strlen(suffix);
+
+ return line->len >= len &&
+ !memcmp(line->text + line->len - len, suffix, len);
+}
+
+static bool line_is_blank(const struct line *line)
+{
+ size_t i;
+
+ for (i = 0; i < line->len; i++)
+ if (!is_blank(line->text[i]))
+ return false;
+
+ return true;
+}
+
+/* Whether the line ends in " \", continuing the list on the next line. */
+static bool line_is_continued(const struct line *line)
+{
+ return line_ends_with(line, LINE_CONTINUATION);
+}
+
+static void line_strip_continuation(struct line *line)
+{
+ if (line_is_continued(line))
+ line->len -= strlen(LINE_CONTINUATION);
+}
+
+static void line_trim(struct line *line)
+{
+ while (line->len && is_blank(line->text[0])) {
+ line->text++;
+ line->len--;
+ }
+ while (line->len && is_blank(line->text[line->len - 1]))
+ line->len--;
+}
+
+/* Take the next line out of [*pos, end); false once there are none left. */
+static bool next_line(const char **pos, const char *end, struct line *line)
+{
+ const char *newline;
+
+ if (*pos >= end)
+ return false;
+
+ newline = memchr(*pos, '\n', end - *pos);
+ line->text = *pos;
+ line->len = (newline ? newline : end) - *pos;
+ *pos = newline ? newline + 1 : end;
+
+ return true;
+}
+
+/* Describes a dependency file. */
+struct dep {
+ struct hlist_node hnode;
+ struct timespec mtime;
+ bool exists;
+ char path[];
+};
+
+static HASHTABLE_DEFINE(dep_table, 1U << 16);
+
+static const struct dep *lookup_dep(const char *path)
+{
+ const unsigned int key = hash_str(path);
+ struct dep *dep;
+ struct stat st;
+
+ hash_for_each_possible(dep_table, dep, hnode, key) {
+ if (!strcmp(dep->path, path))
+ return dep;
+ }
+
+ dep = xmalloc(sizeof(*dep) + strlen(path) + 1);
+ strcpy(dep->path, path);
+ dep->exists = !stat(path, &st);
+ if (dep->exists)
+ dep->mtime = st.st_mtim;
+ hash_add(dep_table, &dep->hnode, key);
+
+ return dep;
+}
+
+/* Strictly newer, as make compares timestamps. */
+static bool newer(const struct timespec *time_a, const struct timespec *time_b)
+{
+ if (time_a->tv_sec != time_b->tv_sec)
+ return time_a->tv_sec > time_b->tv_sec;
+
+ return time_a->tv_nsec > time_b->tv_nsec;
+}
+
+/*
+ * $(wildcard include/config/FOO) is a prerequisite only while FOO is set:
+ * unwrap it and say that it is optional.
+ */
+static bool line_unwrap_wildcard(struct line *line)
+{
+ if (!line_starts_with(line, WILDCARD_OPEN) ||
+ !line_ends_with(line, WILDCARD_CLOSE))
+ return false;
+
+ line->text += strlen(WILDCARD_OPEN);
+ line->len -= strlen(WILDCARD_OPEN) + strlen(WILDCARD_CLOSE);
+
+ return true;
+}
+
+/* fixdep doubles '$' and escapes '#' in a path: undo that into path[]. */
+static bool unescape_path(const struct line *line, char *path, size_t size)
+{
+ size_t i, out_len = 0;
+
+ if (line->len >= size)
+ return false;
+
+ for (i = 0; i < line->len; i++) {
+ const char chr = line->text[i];
+ const char next_chr = i + 1 < line->len ? line->text[i + 1] : '\0';
+
+ if ((chr == '$' && next_chr == '$') || (chr == '\\' && next_chr == '#'))
+ i++;
+ path[out_len++] = line->text[i];
+ }
+ path[out_len] = '\0';
+
+ return true;
+}
+
+/* Is the path contained in line older than the target? */
+static bool dep_is_fresh(struct line line, const struct timespec *target)
+{
+ char path[PATH_MAX];
+ const struct dep *dep;
+ bool optional;
+
+ line_strip_continuation(&line);
+ line_trim(&line);
+ if (!line.len)
+ return false;
+
+ optional = line_unwrap_wildcard(&line);
+ if (!line.len || !unescape_path(&line, path, sizeof(path)))
+ return false;
+
+ dep = lookup_dep(path);
+ if (!dep->exists)
+ return optional;
+
+ return !newer(&dep->mtime, target);
+}
+
+/*
+ * Parse a dependency list which consists of one file per line and determine if
+ * all dependencies are 'fresh', i.e. older than the target.
+ */
+static bool deps_are_fresh(const char *pos, const char *end,
+ const struct timespec *target)
+{
+ struct line line;
+
+ while (next_line(&pos, end, &line)) {
+ if (line_is_blank(&line))
+ return true;
+ if (!dep_is_fresh(line, target))
+ return false;
+ if (!line_is_continued(&line))
+ return true;
+ }
+
+ return true;
+}
+
+/*
+ * Find the "deps_" line in the input.
+ *
+ * On success returns true and *deps_off is set to its offset and *list is set
+ * to the list's own lines start or NULL if the line is not continued.
+ *
+ * Otherwise returns false if the prefix cannot be found.
+ */
+static bool find_deps(const char *buf, size_t len, size_t *deps_off,
+ const char **list)
+{
+ const char *pos = buf, *end = buf + len;
+ struct line line;
+
+ while (next_line(&pos, end, &line)) {
+ if (!line_starts_with(&line, DEPS_PREFIX))
+ continue;
+
+ *deps_off = line.text - buf;
+ *list = line_is_continued(&line) ? pos : NULL;
+ return true;
+ }
+
+ return false;
+}
+
+/*
+ * Is this the target of a .cmd file, i.e. 'dir/.name.cmd names dir/name.'?
+ */
+static bool target_of(const char *cmd_path, char *target, size_t size)
+{
+ const char *slash = strrchr(cmd_path, '/');
+ const char *base = slash ? slash + 1 : cmd_path;
+ const size_t dir_len = base - cmd_path;
+ size_t name_len;
+
+ if (base[0] != '.' || !str_ends_with(base, CMD_SUFFIX))
+ return false;
+
+ name_len = strlen(base) - strlen(".") - strlen(CMD_SUFFIX);
+ if (!name_len)
+ return false;
+
+ return snprintf(target, size, "%.*s%.*s", (int)dir_len, cmd_path,
+ (int)name_len, base + 1) < (int)size;
+}
+
+/*
+ * Does the .cmd file's target exists and is it newer than everything in its
+ * dependency list?
+ *
+ * Returns true if so and sets *deps_off to the start of the list, otherwise
+ * returns false.
+ */
+static bool target_is_fresh(const char *cmd_path, const char *buf, size_t len,
+ size_t *deps_off)
+{
+ char target[PATH_MAX];
+ struct stat st;
+ const char *list;
+
+ if (!target_of(cmd_path, target, sizeof(target)) || stat(target, &st))
+ return false;
+ if (!find_deps(buf, len, deps_off, &list))
+ return false;
+ if (!list)
+ return true;
+
+ return deps_are_fresh(list, buf + len, &st.st_mtim);
+}
+
+static char *read_file(const char *path, size_t *len)
+{
+ FILE *file = fopen(path, "r");
+ struct stat st;
+ char *buf;
+ size_t nr_read = 0;
+
+ if (!file)
+ return NULL;
+ if (fstat(fileno(file), &st)) {
+ fclose(file);
+ return NULL;
+ }
+
+ buf = xmalloc(st.st_size + 1);
+ while (nr_read < (size_t)st.st_size) {
+ const size_t chunk = fread(buf + nr_read, 1, st.st_size - nr_read, file);
+
+ if (!chunk)
+ break;
+ nr_read += chunk;
+ }
+ fclose(file);
+
+ buf[nr_read] = '\0';
+ *len = nr_read;
+ return buf;
+}
+
+/*
+ * Find the end of the dependency block: the offset just past its closing
+ * "$(deps_x):" line. Anything after that was appended by kbuild and is not for
+ * us to judge. Returns len if the line cannot be found.
+ */
+static size_t deps_block_end(const char *buf, size_t len, size_t deps_off)
+{
+ const char *pos = buf + deps_off, *end = buf + len;
+ struct line line;
+
+ while (next_line(&pos, end, &line)) {
+ if (line_starts_with(&line, DEPS_RULE_PREFIX) &&
+ line_ends_with(&line, RULE_SUFFIX))
+ return pos - buf;
+ }
+
+ return len;
+}
+
+/* The target is fresh: pass on everything but the dependency block. */
+static void write_without_deps(FILE *out, const char *buf, size_t len,
+ size_t deps_off)
+{
+ const size_t tail_off = deps_block_end(buf, len, deps_off);
+
+ fwrite(buf, 1, deps_off, out);
+ fputc('\n', out);
+ fwrite(buf + tail_off, 1, len - tail_off, out);
+}
+
+/*
+ * Generate a fragment for make from one .cmd file - without the dependency
+ * block if the target is fresh, otherwise all of it.
+ */
+static void process(FILE *out, const char *cmd_path)
+{
+ size_t len, deps_off;
+ char *buf;
+
+ buf = read_file(cmd_path, &len);
+ if (!buf)
+ return;
+
+ if (target_is_fresh(cmd_path, buf, len, &deps_off))
+ write_without_deps(out, buf, len, deps_off);
+ else
+ fwrite(buf, 1, len, out);
+
+ free(buf);
+}
+
+int main(int argc, char **argv)
+{
+ char tmp_path[PATH_MAX];
+ FILE *out;
+ int i;
+
+ if (argc < 2) {
+ fprintf(stderr, "usage: %s <output> <.cmd files...>\n", argv[0]);
+ return 1;
+ }
+
+ if (snprintf(tmp_path, sizeof(tmp_path), "%s.tmp", argv[1]) >=
+ (int)sizeof(tmp_path)) {
+ fprintf(stderr, "%s: path too long\n", argv[1]);
+ return 1;
+ }
+
+ out = fopen(tmp_path, "w");
+ if (!out) {
+ perror(tmp_path);
+ return 1;
+ }
+
+ for (i = 2; i < argc; i++)
+ process(out, argv[i]);
+
+ if (fclose(out) || rename(tmp_path, argv[1])) {
+ perror(argv[1]);
+ unlink(tmp_path);
+ return 1;
+ }
+
+ return 0;
+}
--
2.55.0
^ permalink raw reply [flat|nested] 73+ messages in thread* Re: [PATCH 11/23] kbuild: implement and use depcheck to check dependency timestamps
2026-09-08 20:55 ` [PATCH 11/23] kbuild: implement and use depcheck to check dependency timestamps Lorenzo Stoakes (ARM)
@ 2026-09-09 15:26 ` Linus Torvalds
0 siblings, 0 replies; 73+ messages in thread
From: Linus Torvalds @ 2026-09-09 15:26 UTC (permalink / raw)
To: Lorenzo Stoakes (ARM)
Cc: Nathan Chancellor, Nicolas Schier, Nick Desaulniers,
Bill Wendling, Justin Stitt, Masahiro Yamada, Alexey Gladkov,
Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin, Paul Walmsley, Palmer Dabbelt, Albert Ou,
Alexandre Ghiti, Arnd Bergmann, Catalin Marinas, Will Deacon,
Mark Rutland, Ard Biesheuvel, Ilias Apalodimas, Josh Poimboeuf,
Peter Zijlstra, Miguel Ojeda, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, Danilo Krummrich, Daniel Almeida, Tamir Duberstein,
Alexandre Courbot, Onur Özkan, Jonathan Corbet,
Randy Dunlap, linux-kbuild, linux-kernel, llvm, linux-riscv,
linux-arch, linux-arm-kernel, linux-efi, rust-for-linux,
linux-doc, Jens Axboe
On Tue, 8 Sept 2026 at 13:57, Lorenzo Stoakes (ARM) <ljs@kernel.org> wrote:
>
> Each object's .cmd file lists its header dependencies, which often
> consists of over a thousand dependencies.
>
> When little has changed in the tree, this is what make spends most of its
> time doing, spending over a second, single-threaded when parsing larger
> directory trees.
Side note: from some kernel build profiles I did long ago, I have this
memory of it being made much worse by the fact that we use
"$(wildcard xyzzy)"
for all our config header files in those .cmd.o files. That then
causes extra work for 'make' for all of this.
And we do that 'wildcard' thing not because we want to actually match
a pattern, but because that was the only way to have 'make' not
complain about missing config headers.
I'm not even sure the original reason for it is relevant any more -
this is all a very dim memory from years and years ago when I actually
tried to look at profiles of 'make' itself (normally when I do kernel
build profiles I only look at the what the kernel part of the build
is, not user mode).
I just tried removing the 'wildcard' generation from
scripts/basic/fixdep.c and the kernel build didn't care about it at
all. The whole 'wildcard' trick goes all the way back to the original
commit that added that whole 'fixdep.c' phase.
Maybe the whole thing is entirely stale now and should be removed. I
probably just didn't trigger the situation where those extra
'wildcard' things matter. That whole thing is from a quarter century
ago, and nobody has dared look at that code since..
Or maybe 'make' is better at those wildcards these days - or other
things have shifted.
From those dim memories, make spent a lot of time doing filesystem
lookups (directory reading and 'stat()' calls - the wildcard thing
triggered more getdents calls), but even more time just in string
handling.
Linus
^ permalink raw reply [flat|nested] 73+ messages in thread
* [PATCH 12/23] kbuild: avoid re-running compiler and linker probes
2026-09-08 20:55 [PATCH 00/23] kbuild: significantly speed up kernel builds Lorenzo Stoakes (ARM)
` (10 preceding siblings ...)
2026-09-08 20:55 ` [PATCH 11/23] kbuild: implement and use depcheck to check dependency timestamps Lorenzo Stoakes (ARM)
@ 2026-09-08 20:55 ` Lorenzo Stoakes (ARM)
2026-09-10 4:19 ` Nathan Chancellor
2026-09-08 20:55 ` [PATCH 13/23] modpost: hash module source per-file, not per-byte Lorenzo Stoakes (ARM)
` (14 subsequent siblings)
26 siblings, 1 reply; 73+ messages in thread
From: Lorenzo Stoakes (ARM) @ 2026-09-08 20:55 UTC (permalink / raw)
To: Linus Torvalds, Nathan Chancellor, Nicolas Schier,
Nick Desaulniers, Bill Wendling, Justin Stitt, Masahiro Yamada,
Alexey Gladkov, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, x86, H. Peter Anvin, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Alexandre Ghiti, Arnd Bergmann, Catalin Marinas,
Will Deacon, Mark Rutland, Ard Biesheuvel, Ilias Apalodimas,
Josh Poimboeuf, Peter Zijlstra, Miguel Ojeda, Boqun Feng,
Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Alice Ryhl, Trevor Gross, Danilo Krummrich, Daniel Almeida,
Tamir Duberstein, Alexandre Courbot, Onur Özkan,
Jonathan Corbet, Randy Dunlap
Cc: linux-kbuild, linux-kernel, llvm, linux-riscv, linux-arch,
linux-arm-kernel, linux-efi, rust-for-linux, linux-doc,
Jens Axboe, Lorenzo Stoakes (ARM)
Each kernel make invocation begins with ~30 compiler and linker runs each
of which performs duplicate probe for a number of compiler and linker
options.
This is useless work - the compiler and its version is known, so use these
to determine which options are available, once.
A convention already exists for this - CC_HAS_xxx, LD_HAS_xxx in Kconfig
files (for example, CC_HAS_COUNTED_BY), so convert these probes to Kconfig
options where appropriate.
With gcc and clang, defconfig and allmodconfig, the recorded command
lines are unchanged, a build with nothing to do rebuilds nothing and W=1
continues to function correctly.
Doing this improves all builds, but has a particularly positive impact on
no-op builds (builds where nothing has changed).
Whole build, 128-thread Threadripper 9980X, best of N runs:
before after delta
-------------------------------
x86 defconfig, no-op make, gcc 0.62s 0.46s -0.17s (-27%)
x86 defconfig, no-op make, clang 0.78s 0.53s -0.25s (-32%)
x86 defconfig, touch mm/vma.c, gcc 8.7s 8.5s -0.17s (-2%)
x86 defconfig, touch mm/vma.c, clang 7.9s 7.6s -0.25s (-3%)
x86 defconfig, clean, clang 27.0s 26.8s -0.26s (-1%)
x86 allmodconfig, no-op make, gcc 11.2s 11.0s -0.18s (-2%)
x86 allmodconfig, no-op make, clang 11.9s 11.6s -0.28s (-2%)
x86 allmodconfig, touch mm/vma.c, clang 36.5s 36.2s -0.31s (-1%)
Assisted-by: LLM
Signed-off-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
---
Makefile | 23 +++++------
arch/arm64/kernel/pi/Makefile | 2 +-
arch/riscv/kernel/pi/Makefile | 2 +-
arch/x86/Kconfig | 17 ++++++++
arch/x86/Makefile | 12 +++---
drivers/firmware/efi/libstub/Makefile | 2 +-
init/Kconfig | 77 +++++++++++++++++++++++++++++++++++
scripts/Makefile.warn | 28 ++++++-------
8 files changed, 128 insertions(+), 35 deletions(-)
diff --git a/Makefile b/Makefile
index 56e56bea53c3..088e901f0a72 100644
--- a/Makefile
+++ b/Makefile
@@ -946,8 +946,8 @@ KBUILD_RUSTFLAGS += -Coverflow-checks=$(if $(CONFIG_RUST_OVERFLOW_CHECKS),y,n)
ifdef CONFIG_CC_IS_GCC
# gcc-10 renamed --param=allow-store-data-races=0 to
# -fno-allow-store-data-races.
-KBUILD_CFLAGS += $(call cc-option,--param=allow-store-data-races=0)
-KBUILD_CFLAGS += $(call cc-option,-fno-allow-store-data-races)
+KBUILD_CFLAGS += $(if $(CONFIG_CC_HAS_ALLOW_STORE_DATA_RACES_PARAM),--param=allow-store-data-races=0)
+KBUILD_CFLAGS += $(if $(CONFIG_CC_HAS_NO_ALLOW_STORE_DATA_RACES),-fno-allow-store-data-races)
endif
ifdef CONFIG_READABLE_ASM
@@ -1011,18 +1011,18 @@ endif
endif
# Explicitly clear padding bits during variable initialization
-KBUILD_CFLAGS += $(call cc-option,-fzero-init-padding-bits=all)
+KBUILD_CFLAGS += $(if $(CONFIG_CC_HAS_ZERO_INIT_PADDING_BITS),-fzero-init-padding-bits=all)
# While VLAs have been removed, GCC produces unreachable stack probes
# for the randomize_kstack_offset feature. Disable it for all compilers.
-KBUILD_CFLAGS += $(call cc-option, -fno-stack-clash-protection)
+KBUILD_CFLAGS += $(if $(CONFIG_CC_HAS_NO_STACK_CLASH_PROTECTION),-fno-stack-clash-protection)
# Get details on warnings generated due to GCC value tracking.
-KBUILD_CFLAGS += $(call cc-option, -fdiagnostics-show-context=2)
+KBUILD_CFLAGS += $(if $(CONFIG_CC_HAS_DIAGNOSTICS_SHOW_CONTEXT),-fdiagnostics-show-context=2)
# Show inlining notes for __attribute__((warning/error)) call chains.
# GCC supports this unconditionally while Clang 23+ provides a flag.
-KBUILD_CFLAGS += $(call cc-option, -fdiagnostics-show-inlining-chain)
+KBUILD_CFLAGS += $(if $(CONFIG_CC_HAS_DIAGNOSTICS_SHOW_INLINING_CHAIN),-fdiagnostics-show-inlining-chain)
# Clear used registers at func exit (to reduce data lifetime and ROP gadgets).
ifdef CONFIG_ZERO_CALL_USED_REGS
@@ -1033,7 +1033,7 @@ ifdef CONFIG_FUNCTION_TRACER
ifdef CONFIG_FTRACE_MCOUNT_USE_CC
CC_FLAGS_FTRACE += -mrecord-mcount
ifdef CONFIG_HAVE_NOP_MCOUNT
- ifeq ($(call cc-option-yn, -mnop-mcount),y)
+ ifdef CONFIG_CC_HAS_MNOP_MCOUNT
CC_FLAGS_FTRACE += -mnop-mcount
CC_FLAGS_USING += -DCC_USING_NOP_MCOUNT
endif
@@ -1051,8 +1051,7 @@ ifdef CONFIG_FTRACE_MCOUNT_USE_RECORDMCOUNT
endif
endif
ifdef CONFIG_HAVE_FENTRY
- # s390-linux-gnu-gcc did not support -mfentry until gcc-9.
- ifeq ($(call cc-option-yn, -mfentry),y)
+ ifdef CONFIG_CC_HAS_MFENTRY
CC_FLAGS_FTRACE += -mfentry
CC_FLAGS_USING += -DCC_USING_FENTRY
endif
@@ -1160,7 +1159,7 @@ NOSTDINC_FLAGS += -nostdinc
# the kernel uses only C99 flexible arrays for dynamically sized trailing
# arrays. Enforce this for everything that may examine structure sizes and
# perform bounds checking.
-KBUILD_CFLAGS += $(call cc-option, -fstrict-flex-arrays=3)
+KBUILD_CFLAGS += $(if $(CONFIG_CC_HAS_STRICT_FLEX_ARRAYS),-fstrict-flex-arrays=3)
# disable invalid "can't wrap" optimizations for signed / pointers
KBUILD_CFLAGS += -fno-strict-overflow
@@ -1240,11 +1239,11 @@ LDFLAGS_vmlinux += --build-id=sha1
# COMDAT-deduplicated sections. Use --force-group-allocation to resolve these
# groups when linking modules. The option is available from ld.bfd 2.29 and
# ld.lld 19.1.0.
-KBUILD_LDFLAGS_MODULE += $(call ld-option,--force-group-allocation)
+KBUILD_LDFLAGS_MODULE += $(if $(CONFIG_LD_HAS_FORCE_GROUP_ALLOCATION),--force-group-allocation)
KBUILD_LDFLAGS += -z noexecstack
ifeq ($(CONFIG_LD_IS_BFD),y)
-KBUILD_LDFLAGS += $(call ld-option,--no-warn-rwx-segments)
+KBUILD_LDFLAGS += $(if $(CONFIG_LD_HAS_NO_WARN_RWX_SEGMENTS),--no-warn-rwx-segments)
endif
ifeq ($(CONFIG_STRIP_ASM_SYMS),y)
diff --git a/arch/arm64/kernel/pi/Makefile b/arch/arm64/kernel/pi/Makefile
index be92d73c25b2..5aa8dffe492b 100644
--- a/arch/arm64/kernel/pi/Makefile
+++ b/arch/arm64/kernel/pi/Makefile
@@ -9,7 +9,7 @@ KBUILD_CFLAGS := $(subst $(CC_FLAGS_FTRACE),,$(KBUILD_CFLAGS)) -fpie \
-include $(srctree)/include/linux/hidden.h \
-D__DISABLE_EXPORTS -ffreestanding -D__NO_FORTIFY \
-fno-asynchronous-unwind-tables -fno-unwind-tables \
- $(call cc-option,-fno-addrsig)
+ $(if $(CONFIG_CC_HAS_NO_ADDRSIG),-fno-addrsig)
# this code may run with the MMU off so disable unaligned accesses
CFLAGS_map_range.o += -mstrict-align
diff --git a/arch/riscv/kernel/pi/Makefile b/arch/riscv/kernel/pi/Makefile
index bc098edac898..e0ed7ca7e347 100644
--- a/arch/riscv/kernel/pi/Makefile
+++ b/arch/riscv/kernel/pi/Makefile
@@ -8,7 +8,7 @@ KBUILD_CFLAGS := $(subst $(CC_FLAGS_FTRACE),,$(KBUILD_CFLAGS)) -fpie \
-include $(srctree)/include/linux/hidden.h \
-D__DISABLE_EXPORTS -ffreestanding \
-fno-asynchronous-unwind-tables -fno-unwind-tables \
- $(call cc-option,-fno-addrsig)
+ $(if $(CONFIG_CC_HAS_NO_ADDRSIG),-fno-addrsig)
# Disable LTO
KBUILD_CFLAGS := $(filter-out $(CC_FLAGS_LTO), $(KBUILD_CFLAGS))
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 15fd9ec5ecac..be0624fee27a 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -2364,6 +2364,23 @@ config CC_HAS_KCFI_ARITY
def_bool $(cc-option,-fsanitize=kcfi -fsanitize-kcfi-arity)
depends on CC_IS_CLANG && !RUST
+config CC_HAS_INDIRECT_BRANCH_CS_PREFIX
+ def_bool $(cc-option,-mindirect-branch-cs-prefix)
+
+config CC_HAS_CF_PROTECTION_NONE
+ def_bool $(cc-option,-fcf-protection=none)
+
+# with jump tables off: the compilers use NOTRACK for them, which kernel IBT
+# does not allow
+config CC_HAS_CF_PROTECTION_BRANCH
+ def_bool $(cc-option,-fcf-protection=branch -fno-jump-tables)
+
+config CC_HAS_FALIGN_JUMPS
+ def_bool $(cc-option,-falign-jumps=1)
+
+config CC_HAS_FALIGN_LOOPS
+ def_bool $(cc-option,-falign-loops=1)
+
config FUNCTION_PADDING_CFI
int
default 59 if FUNCTION_ALIGNMENT_64B
diff --git a/arch/x86/Makefile b/arch/x86/Makefile
index 598f178102ee..29bb515df9f6 100644
--- a/arch/x86/Makefile
+++ b/arch/x86/Makefile
@@ -20,7 +20,7 @@ ifdef CONFIG_CC_IS_CLANG
RETPOLINE_CFLAGS := -mretpoline-external-thunk
RETPOLINE_VDSO_CFLAGS := -mretpoline
endif
-RETPOLINE_CFLAGS += $(call cc-option,-mindirect-branch-cs-prefix)
+RETPOLINE_CFLAGS += $(if $(CONFIG_CC_HAS_INDIRECT_BRANCH_CS_PREFIX),-mindirect-branch-cs-prefix)
ifdef CONFIG_MITIGATION_RETHUNK
RETHUNK_CFLAGS := -mfunction-return=thunk-extern
@@ -52,7 +52,7 @@ REALMODE_CFLAGS := $(CC_FLAGS_DIALECT) -m16 -g -Os \
-DDISABLE_BRANCH_PROFILING -D__DISABLE_EXPORTS \
-Wall -Wstrict-prototypes -march=i386 -mregparm=3 \
-fno-strict-aliasing -fomit-frame-pointer -fno-pic \
- -mno-mmx -mno-sse $(call cc-option,-fcf-protection=none)
+ -mno-mmx -mno-sse $(if $(CONFIG_CC_HAS_CF_PROTECTION_NONE),-fcf-protection=none)
REALMODE_CFLAGS += -ffreestanding
REALMODE_CFLAGS += -fno-stack-protector
@@ -99,10 +99,10 @@ ifeq ($(CONFIG_X86_KERNEL_IBT),y)
#
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=104816
#
-KBUILD_CFLAGS += $(call cc-option,-fcf-protection=branch -fno-jump-tables)
+KBUILD_CFLAGS += $(if $(CONFIG_CC_HAS_CF_PROTECTION_BRANCH),-fcf-protection=branch -fno-jump-tables)
KBUILD_RUSTFLAGS += -Zcf-protection=branch $(if $(call rustc-min-version,109300),-Cjump-tables=n,-Zno-jump-tables)
else
-KBUILD_CFLAGS += $(call cc-option,-fcf-protection=none)
+KBUILD_CFLAGS += $(if $(CONFIG_CC_HAS_CF_PROTECTION_NONE),-fcf-protection=none)
endif
ifeq ($(CONFIG_X86_32),y)
@@ -138,10 +138,10 @@ else
KBUILD_CFLAGS += -m64
# Align jump targets to 1 byte, not the default 16 bytes:
- KBUILD_CFLAGS += $(call cc-option,-falign-jumps=1)
+ KBUILD_CFLAGS += $(if $(CONFIG_CC_HAS_FALIGN_JUMPS),-falign-jumps=1)
# Pack loops tightly as well:
- KBUILD_CFLAGS += $(call cc-option,-falign-loops=1)
+ KBUILD_CFLAGS += $(if $(CONFIG_CC_HAS_FALIGN_LOOPS),-falign-loops=1)
# Don't autogenerate traditional x87 instructions
KBUILD_CFLAGS += -mno-80387
diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile
index 77a2b2d74f3f..80058bbddaf5 100644
--- a/drivers/firmware/efi/libstub/Makefile
+++ b/drivers/firmware/efi/libstub/Makefile
@@ -40,7 +40,7 @@ KBUILD_CFLAGS := $(subst $(CC_FLAGS_FTRACE),,$(cflags-y)) \
-D__NO_FORTIFY \
-ffreestanding \
-fno-stack-protector \
- $(call cc-option,-fno-addrsig) \
+ $(if $(CONFIG_CC_HAS_NO_ADDRSIG),-fno-addrsig) \
-D__DISABLE_EXPORTS
#
diff --git a/init/Kconfig b/init/Kconfig
index 8583d9f06c52..3c92c87254a3 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -144,6 +144,44 @@ config CC_HAS_ASSUME
config CC_HAS_NO_PROFILE_FN_ATTR
def_bool $(success,echo '__attribute__((no_profile_instrument_function)) int x();' | $(CC) -x c - -c -o /dev/null -Werror)
+config CC_HAS_ZERO_INIT_PADDING_BITS
+ def_bool $(cc-option,-fzero-init-padding-bits=all)
+
+config CC_HAS_NO_STACK_CLASH_PROTECTION
+ def_bool $(cc-option,-fno-stack-clash-protection)
+
+config CC_HAS_NO_ADDRSIG
+ def_bool $(cc-option,-fno-addrsig)
+
+config CC_HAS_DIAGNOSTICS_SHOW_CONTEXT
+ def_bool $(cc-option,-fdiagnostics-show-context=2)
+
+config CC_HAS_DIAGNOSTICS_SHOW_INLINING_CHAIN
+ def_bool $(cc-option,-fdiagnostics-show-inlining-chain)
+
+config CC_HAS_STRICT_FLEX_ARRAYS
+ def_bool $(cc-option,-fstrict-flex-arrays=3)
+
+# gcc-10 renamed --param=allow-store-data-races=0 to -fno-allow-store-data-races
+config CC_HAS_ALLOW_STORE_DATA_RACES_PARAM
+ def_bool CC_IS_GCC && $(cc-option,--param=allow-store-data-races=0)
+
+config CC_HAS_NO_ALLOW_STORE_DATA_RACES
+ def_bool CC_IS_GCC && $(cc-option,-fno-allow-store-data-races)
+
+config CC_HAS_MNOP_MCOUNT
+ def_bool $(cc-option,-mnop-mcount)
+
+# s390-linux-gnu-gcc did not support -mfentry until gcc-9.
+config CC_HAS_MFENTRY
+ def_bool $(cc-option,-mfentry)
+
+config LD_HAS_FORCE_GROUP_ALLOCATION
+ def_bool $(ld-option,--force-group-allocation)
+
+config LD_HAS_NO_WARN_RWX_SEGMENTS
+ def_bool $(ld-option,--no-warn-rwx-segments)
+
config CC_HAS_COUNTED_BY
bool
# clang needs to be at least 20.1.0 to avoid potential crashes
@@ -1006,6 +1044,45 @@ config CC_STRINGOP_OVERFLOW
bool
default y if CC_IS_GCC && !CC_NO_STRINGOP_OVERFLOW
+config CC_HAS_WNO_ADDRESS_OF_PACKED_MEMBER
+ def_bool $(cc-option,-Wno-address-of-packed-member)
+
+config CC_HAS_WNO_FORMAT_OVERFLOW_NON_KPRINTF
+ def_bool CC_IS_CLANG && $(cc-option,-Wno-format-overflow-non-kprintf)
+
+config CC_HAS_WNO_FORMAT_TRUNCATION_NON_KPRINTF
+ def_bool CC_IS_CLANG && $(cc-option,-Wno-format-truncation-non-kprintf)
+
+config CC_HAS_WNO_DEFAULT_CONST_INIT_UNSAFE
+ def_bool CC_IS_CLANG && $(cc-option,-Wno-default-const-init-unsafe)
+
+config CC_HAS_WNO_DANGLING_POINTER
+ def_bool $(cc-option,-Wno-dangling-pointer)
+
+config CC_HAS_WVLA_LARGER_THAN
+ def_bool $(cc-option,-Wvla-larger-than=1)
+
+config CC_HAS_WSTRINGOP_OVERFLOW
+ def_bool $(cc-option,-Wstringop-overflow)
+
+config CC_HAS_WNO_UNTERMINATED_STRING_INITIALIZATION
+ def_bool $(cc-option,-Wno-unterminated-string-initialization)
+
+config CC_HAS_WERROR_DESIGNATED_INIT
+ def_bool $(cc-option,-Werror=designated-init)
+
+config CC_HAS_WENUM_CONVERSION
+ def_bool $(cc-option,-Wenum-conversion)
+
+config CC_HAS_WNO_PACKED_NOT_ALIGNED
+ def_bool $(cc-option,-Wno-packed-not-aligned)
+
+config CC_HAS_WNO_FORMAT_OVERFLOW
+ def_bool $(cc-option,-Wno-format-overflow)
+
+config CC_HAS_WNO_STRINGOP_TRUNCATION
+ def_bool $(cc-option,-Wno-stringop-truncation)
+
#
# For architectures that know their GCC __int128 support is sound
#
diff --git a/scripts/Makefile.warn b/scripts/Makefile.warn
index 35af7d6c6d18..83d274fcbfaf 100644
--- a/scripts/Makefile.warn
+++ b/scripts/Makefile.warn
@@ -17,7 +17,7 @@ KBUILD_CFLAGS += -Werror=strict-prototypes
KBUILD_CFLAGS += -Wno-format-security
KBUILD_CFLAGS += -Wno-trigraphs
KBUILD_CFLAGS += -Wno-frame-address
-KBUILD_CFLAGS += $(call cc-option, -Wno-address-of-packed-member)
+KBUILD_CFLAGS += $(if $(CONFIG_CC_HAS_WNO_ADDRESS_OF_PACKED_MEMBER),-Wno-address-of-packed-member)
KBUILD_CFLAGS += -Wmissing-declarations
KBUILD_CFLAGS += -Wmissing-prototypes
@@ -30,8 +30,8 @@ KBUILD_CFLAGS-$(CONFIG_CC_NO_ARRAY_BOUNDS) += -Wno-array-bounds
ifdef CONFIG_CC_IS_CLANG
# Clang checks for overflow/truncation with '%p', while GCC does not:
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=111219
-KBUILD_CFLAGS += $(call cc-option, -Wno-format-overflow-non-kprintf)
-KBUILD_CFLAGS += $(call cc-option, -Wno-format-truncation-non-kprintf)
+KBUILD_CFLAGS += $(if $(CONFIG_CC_HAS_WNO_FORMAT_OVERFLOW_NON_KPRINTF),-Wno-format-overflow-non-kprintf)
+KBUILD_CFLAGS += $(if $(CONFIG_CC_HAS_WNO_FORMAT_TRUNCATION_NON_KPRINTF),-Wno-format-truncation-non-kprintf)
# Clang may emit a warning when a const variable, such as the dummy variables
# in typecheck(), or const member of an aggregate type are not initialized,
@@ -43,7 +43,7 @@ KBUILD_CFLAGS += $(call cc-option, -Wno-format-truncation-non-kprintf)
# disabled with this same switch, there should not be too much coverage lost
# because -Wuninitialized will still flag when an uninitialized const variable
# is used.
-KBUILD_CFLAGS += $(call cc-option, -Wno-default-const-init-unsafe)
+KBUILD_CFLAGS += $(if $(CONFIG_CC_HAS_WNO_DEFAULT_CONST_INIT_UNSAFE),-Wno-default-const-init-unsafe)
else
# gcc inanely warns about local variables called 'main'
@@ -54,7 +54,7 @@ endif
KBUILD_CFLAGS += -Wno-type-limits
# These result in bogus false positives
-KBUILD_CFLAGS += $(call cc-option, -Wno-dangling-pointer)
+KBUILD_CFLAGS += $(if $(CONFIG_CC_HAS_WNO_DANGLING_POINTER),-Wno-dangling-pointer)
# Stack Variable Length Arrays (VLAs) must not be used in the kernel.
# Function array parameters should, however, be usable, but -Wvla will
@@ -62,7 +62,7 @@ KBUILD_CFLAGS += $(call cc-option, -Wno-dangling-pointer)
# types, so depend on GCC for now to keep stack VLAs out of the tree.
# https://github.com/llvm/llvm-project/issues/57098
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=98217
-KBUILD_CFLAGS += $(call cc-option,-Wvla-larger-than=1)
+KBUILD_CFLAGS += $(if $(CONFIG_CC_HAS_WVLA_LARGER_THAN),-Wvla-larger-than=1)
# disable pointer signed / unsigned warnings in gcc 4.0
KBUILD_CFLAGS += -Wno-pointer-sign
@@ -73,11 +73,11 @@ KBUILD_CFLAGS += -Wno-pointer-sign
KBUILD_CFLAGS += -Wcast-function-type
# Currently, disable -Wstringop-overflow for GCC 11, globally.
-KBUILD_CFLAGS-$(CONFIG_CC_NO_STRINGOP_OVERFLOW) += $(call cc-option, -Wno-stringop-overflow)
-KBUILD_CFLAGS-$(CONFIG_CC_STRINGOP_OVERFLOW) += $(call cc-option, -Wstringop-overflow)
+KBUILD_CFLAGS-$(CONFIG_CC_NO_STRINGOP_OVERFLOW) += $(if $(CONFIG_CC_HAS_WSTRINGOP_OVERFLOW),-Wno-stringop-overflow)
+KBUILD_CFLAGS-$(CONFIG_CC_STRINGOP_OVERFLOW) += $(if $(CONFIG_CC_HAS_WSTRINGOP_OVERFLOW),-Wstringop-overflow)
# Currently, disable -Wunterminated-string-initialization as broken
-KBUILD_CFLAGS += $(call cc-option, -Wno-unterminated-string-initialization)
+KBUILD_CFLAGS += $(if $(CONFIG_CC_HAS_WNO_UNTERMINATED_STRING_INITIALIZATION),-Wno-unterminated-string-initialization)
# The allocators already balk at large sizes, so silence the compiler
# warnings for bounds checks involving those possible values. While
@@ -100,10 +100,10 @@ KBUILD_CFLAGS += -Werror=date-time
KBUILD_CFLAGS += -Werror=incompatible-pointer-types
# Require designated initializers for all marked structures
-KBUILD_CFLAGS += $(call cc-option,-Werror=designated-init)
+KBUILD_CFLAGS += $(if $(CONFIG_CC_HAS_WERROR_DESIGNATED_INIT),-Werror=designated-init)
# Warn if there is an enum types mismatch
-KBUILD_CFLAGS += $(call cc-option,-Wenum-conversion)
+KBUILD_CFLAGS += $(if $(CONFIG_CC_HAS_WENUM_CONVERSION),-Wenum-conversion)
KBUILD_CFLAGS += -Wunused
@@ -125,12 +125,12 @@ else
# Suppress them by using -Wno... except for W=1.
KBUILD_CFLAGS += -Wno-unused-but-set-variable
KBUILD_CFLAGS += -Wno-unused-const-variable
-KBUILD_CFLAGS += $(call cc-option, -Wno-packed-not-aligned)
-KBUILD_CFLAGS += $(call cc-option, -Wno-format-overflow)
+KBUILD_CFLAGS += $(if $(CONFIG_CC_HAS_WNO_PACKED_NOT_ALIGNED),-Wno-packed-not-aligned)
+KBUILD_CFLAGS += $(if $(CONFIG_CC_HAS_WNO_FORMAT_OVERFLOW),-Wno-format-overflow)
ifdef CONFIG_CC_IS_GCC
KBUILD_CFLAGS += -Wno-format-truncation
endif
-KBUILD_CFLAGS += $(call cc-option, -Wno-stringop-truncation)
+KBUILD_CFLAGS += $(if $(CONFIG_CC_HAS_WNO_STRINGOP_TRUNCATION),-Wno-stringop-truncation)
KBUILD_CFLAGS += -Wno-override-init # alias for -Wno-initializer-overrides in clang
--
2.55.0
^ permalink raw reply [flat|nested] 73+ messages in thread* Re: [PATCH 12/23] kbuild: avoid re-running compiler and linker probes
2026-09-08 20:55 ` [PATCH 12/23] kbuild: avoid re-running compiler and linker probes Lorenzo Stoakes (ARM)
@ 2026-09-10 4:19 ` Nathan Chancellor
2026-09-10 15:42 ` Nicolas Schier
2026-09-11 10:26 ` Lorenzo Stoakes (ARM)
0 siblings, 2 replies; 73+ messages in thread
From: Nathan Chancellor @ 2026-09-10 4:19 UTC (permalink / raw)
To: Lorenzo Stoakes (ARM)
Cc: Linus Torvalds, Nathan Chancellor, Nicolas Schier,
Nick Desaulniers, Bill Wendling, Justin Stitt, Masahiro Yamada,
Alexey Gladkov, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, x86, H. Peter Anvin, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Alexandre Ghiti, Arnd Bergmann, Catalin Marinas,
Will Deacon, Mark Rutland, Ard Biesheuvel, Ilias Apalodimas,
Josh Poimboeuf, Peter Zijlstra, Miguel Ojeda, Boqun Feng,
Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Alice Ryhl, Trevor Gross, Danilo Krummrich, Daniel Almeida,
Tamir Duberstein, Alexandre Courbot, Onur Özkan,
Jonathan Corbet, Randy Dunlap, linux-kbuild, linux-kernel, llvm,
linux-riscv, linux-arch, linux-arm-kernel, linux-efi,
rust-for-linux, linux-doc, Jens Axboe
> diff --git a/init/Kconfig b/init/Kconfig
> index 8583d9f06c52..3c92c87254a3 100644
> --- a/init/Kconfig
> +++ b/init/Kconfig
> @@ -144,6 +144,44 @@ config CC_HAS_ASSUME
> config CC_HAS_NO_PROFILE_FN_ATTR
> def_bool $(success,echo '__attribute__((no_profile_instrument_function)) int x();' | $(CC) -x c - -c -o /dev/null -Werror)
>
> +config CC_HAS_ZERO_INIT_PADDING_BITS
> + def_bool $(cc-option,-fzero-init-padding-bits=all)
> +
> +config CC_HAS_NO_STACK_CLASH_PROTECTION
> + def_bool $(cc-option,-fno-stack-clash-protection)
> +
> +config CC_HAS_NO_ADDRSIG
> + def_bool $(cc-option,-fno-addrsig)
I am generally in favor of this proposal since I think these checks
should be cached by Kconfig where possible to clear up running them
twice. That said, I wonder if we should pick a better dumping ground for
these checks other than init/Kconfig. Maybe init/Kconfig.tools or
scripts/Kconfig.tools or something?
I also worry about backsliding and growing cc-option checks. Should we
update the documentation to state that it is preferrable for these
checks to live in Kconfig so that they can be properly cached?
--
Cheers,
Nathan
^ permalink raw reply [flat|nested] 73+ messages in thread
* Re: [PATCH 12/23] kbuild: avoid re-running compiler and linker probes
2026-09-10 4:19 ` Nathan Chancellor
@ 2026-09-10 15:42 ` Nicolas Schier
2026-09-11 10:30 ` Lorenzo Stoakes (ARM)
2026-09-11 10:26 ` Lorenzo Stoakes (ARM)
1 sibling, 1 reply; 73+ messages in thread
From: Nicolas Schier @ 2026-09-10 15:42 UTC (permalink / raw)
To: Nathan Chancellor
Cc: Lorenzo Stoakes (ARM),
Linus Torvalds, Nick Desaulniers, Bill Wendling, Justin Stitt,
Masahiro Yamada, Alexey Gladkov, Thomas Gleixner, Ingo Molnar,
Borislav Petkov, Dave Hansen, x86, H. Peter Anvin, Paul Walmsley,
Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Arnd Bergmann,
Catalin Marinas, Will Deacon, Mark Rutland, Ard Biesheuvel,
Ilias Apalodimas, Josh Poimboeuf, Peter Zijlstra, Miguel Ojeda,
Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin,
Andreas Hindborg, Alice Ryhl, Trevor Gross, Danilo Krummrich,
Daniel Almeida, Tamir Duberstein, Alexandre Courbot,
Onur Özkan, Jonathan Corbet, Randy Dunlap, linux-kbuild,
linux-kernel, llvm, linux-riscv, linux-arch, linux-arm-kernel,
linux-efi, rust-for-linux, linux-doc, Jens Axboe
[-- Attachment #1: Type: text/plain, Size: 1589 bytes --]
On Wed, Sep 09, 2026 at 09:19:12PM -0700, Nathan Chancellor wrote:
> > diff --git a/init/Kconfig b/init/Kconfig
> > index 8583d9f06c52..3c92c87254a3 100644
> > --- a/init/Kconfig
> > +++ b/init/Kconfig
> > @@ -144,6 +144,44 @@ config CC_HAS_ASSUME
> > config CC_HAS_NO_PROFILE_FN_ATTR
> > def_bool $(success,echo '__attribute__((no_profile_instrument_function)) int x();' | $(CC) -x c - -c -o /dev/null -Werror)
> >
> > +config CC_HAS_ZERO_INIT_PADDING_BITS
> > + def_bool $(cc-option,-fzero-init-padding-bits=all)
> > +
> > +config CC_HAS_NO_STACK_CLASH_PROTECTION
> > + def_bool $(cc-option,-fno-stack-clash-protection)
> > +
> > +config CC_HAS_NO_ADDRSIG
> > + def_bool $(cc-option,-fno-addrsig)
>
> I am generally in favor of this proposal since I think these checks
> should be cached by Kconfig where possible to clear up running them
> twice. That said, I wonder if we should pick a better dumping ground for
> these checks other than init/Kconfig. Maybe init/Kconfig.tools or
> scripts/Kconfig.tools or something?
Yes, skimming through the patch, I thought about that, too. Moving more
cc option checks to Kconfig will make building out-of-tree kmods with a
different compiler more dangerous. Maybe we will have to make the
warning in Makefile's prepare (external kmods) target a bit louder.
> I also worry about backsliding and growing cc-option checks. Should we
> update the documentation to state that it is preferrable for these
> checks to live in Kconfig so that they can be properly cached?
Sounds good, yes.
--
Nicolas
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 73+ messages in thread
* Re: [PATCH 12/23] kbuild: avoid re-running compiler and linker probes
2026-09-10 15:42 ` Nicolas Schier
@ 2026-09-11 10:30 ` Lorenzo Stoakes (ARM)
0 siblings, 0 replies; 73+ messages in thread
From: Lorenzo Stoakes (ARM) @ 2026-09-11 10:30 UTC (permalink / raw)
To: Nicolas Schier
Cc: Nathan Chancellor, Linus Torvalds, Nick Desaulniers,
Bill Wendling, Justin Stitt, Masahiro Yamada, Alexey Gladkov,
Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin, Paul Walmsley, Palmer Dabbelt, Albert Ou,
Alexandre Ghiti, Arnd Bergmann, Catalin Marinas, Will Deacon,
Mark Rutland, Ard Biesheuvel, Ilias Apalodimas, Josh Poimboeuf,
Peter Zijlstra, Miguel Ojeda, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, Danilo Krummrich, Daniel Almeida, Tamir Duberstein,
Alexandre Courbot, Onur Özkan, Jonathan Corbet,
Randy Dunlap, linux-kbuild, linux-kernel, llvm, linux-riscv,
linux-arch, linux-arm-kernel, linux-efi, rust-for-linux,
linux-doc, Jens Axboe
On Thu, Sep 10, 2026 at 05:42:35PM +0200, Nicolas Schier wrote:
> On Wed, Sep 09, 2026 at 09:19:12PM -0700, Nathan Chancellor wrote:
> > > diff --git a/init/Kconfig b/init/Kconfig
> > > index 8583d9f06c52..3c92c87254a3 100644
> > > --- a/init/Kconfig
> > > +++ b/init/Kconfig
> > > @@ -144,6 +144,44 @@ config CC_HAS_ASSUME
> > > config CC_HAS_NO_PROFILE_FN_ATTR
> > > def_bool $(success,echo '__attribute__((no_profile_instrument_function)) int x();' | $(CC) -x c - -c -o /dev/null -Werror)
> > >
> > > +config CC_HAS_ZERO_INIT_PADDING_BITS
> > > + def_bool $(cc-option,-fzero-init-padding-bits=all)
> > > +
> > > +config CC_HAS_NO_STACK_CLASH_PROTECTION
> > > + def_bool $(cc-option,-fno-stack-clash-protection)
> > > +
> > > +config CC_HAS_NO_ADDRSIG
> > > + def_bool $(cc-option,-fno-addrsig)
> >
> > I am generally in favor of this proposal since I think these checks
> > should be cached by Kconfig where possible to clear up running them
> > twice. That said, I wonder if we should pick a better dumping ground for
> > these checks other than init/Kconfig. Maybe init/Kconfig.tools or
> > scripts/Kconfig.tools or something?
>
(Sorry hadn't seen you'd replied also!)
> Yes, skimming through the patch, I thought about that, too. Moving more
> cc option checks to Kconfig will make building out-of-tree kmods with a
> different compiler more dangerous. Maybe we will have to make the
> warning in Makefile's prepare (external kmods) target a bit louder.
I guess this bit can be a follow up potentially?
>
> > I also worry about backsliding and growing cc-option checks. Should we
> > update the documentation to state that it is preferrable for these
> > checks to live in Kconfig so that they can be properly cached?
>
> Sounds good, yes.
I can add a doc update as part of this series, unless you guys want to handle
separately?
>
> --
> Nicolas
--
Cheers, Lorenzo
^ permalink raw reply [flat|nested] 73+ messages in thread
* Re: [PATCH 12/23] kbuild: avoid re-running compiler and linker probes
2026-09-10 4:19 ` Nathan Chancellor
2026-09-10 15:42 ` Nicolas Schier
@ 2026-09-11 10:26 ` Lorenzo Stoakes (ARM)
1 sibling, 0 replies; 73+ messages in thread
From: Lorenzo Stoakes (ARM) @ 2026-09-11 10:26 UTC (permalink / raw)
To: Nathan Chancellor
Cc: Linus Torvalds, Nicolas Schier, Nick Desaulniers, Bill Wendling,
Justin Stitt, Masahiro Yamada, Alexey Gladkov, Thomas Gleixner,
Ingo Molnar, Borislav Petkov, Dave Hansen, x86, H. Peter Anvin,
Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti,
Arnd Bergmann, Catalin Marinas, Will Deacon, Mark Rutland,
Ard Biesheuvel, Ilias Apalodimas, Josh Poimboeuf, Peter Zijlstra,
Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Daniel Almeida, Tamir Duberstein,
Alexandre Courbot, Onur Özkan, Jonathan Corbet,
Randy Dunlap, linux-kbuild, linux-kernel, llvm, linux-riscv,
linux-arch, linux-arm-kernel, linux-efi, rust-for-linux,
linux-doc, Jens Axboe
On Wed, Sep 09, 2026 at 09:19:12PM -0700, Nathan Chancellor wrote:
> > diff --git a/init/Kconfig b/init/Kconfig
> > index 8583d9f06c52..3c92c87254a3 100644
> > --- a/init/Kconfig
> > +++ b/init/Kconfig
> > @@ -144,6 +144,44 @@ config CC_HAS_ASSUME
> > config CC_HAS_NO_PROFILE_FN_ATTR
> > def_bool $(success,echo '__attribute__((no_profile_instrument_function)) int x();' | $(CC) -x c - -c -o /dev/null -Werror)
> >
> > +config CC_HAS_ZERO_INIT_PADDING_BITS
> > + def_bool $(cc-option,-fzero-init-padding-bits=all)
> > +
> > +config CC_HAS_NO_STACK_CLASH_PROTECTION
> > + def_bool $(cc-option,-fno-stack-clash-protection)
> > +
> > +config CC_HAS_NO_ADDRSIG
> > + def_bool $(cc-option,-fno-addrsig)
>
> I am generally in favor of this proposal since I think these checks
> should be cached by Kconfig where possible to clear up running them
> twice. That said, I wonder if we should pick a better dumping ground for
> these checks other than init/Kconfig. Maybe init/Kconfig.tools or
> scripts/Kconfig.tools or something?
Yeah makes sense to separate them out so they're not all clumped with unrelated
stuff.
It feels like init is the right place, having actual substantive Kconfig entries
in scripts/ feels a bit weird.
I can add an extra patch to move existing entries over before adding these in in
the next patch?
>
> I also worry about backsliding and growing cc-option checks. Should we
> update the documentation to state that it is preferrable for these
> checks to live in Kconfig so that they can be properly cached?
Yeah makes sense. I can add a patch to update the docs too?
>
> --
> Cheers,
> Nathan
>
--
Cheers, Lorenzo
^ permalink raw reply [flat|nested] 73+ messages in thread
* [PATCH 13/23] modpost: hash module source per-file, not per-byte
2026-09-08 20:55 [PATCH 00/23] kbuild: significantly speed up kernel builds Lorenzo Stoakes (ARM)
` (11 preceding siblings ...)
2026-09-08 20:55 ` [PATCH 12/23] kbuild: avoid re-running compiler and linker probes Lorenzo Stoakes (ARM)
@ 2026-09-08 20:55 ` Lorenzo Stoakes (ARM)
2026-09-10 12:52 ` Petr Pavlu
2026-09-08 20:55 ` [PATCH 14/23] modpost: cache section relocation mismatch state Lorenzo Stoakes (ARM)
` (13 subsequent siblings)
26 siblings, 1 reply; 73+ messages in thread
From: Lorenzo Stoakes (ARM) @ 2026-09-08 20:55 UTC (permalink / raw)
To: Linus Torvalds, Nathan Chancellor, Nicolas Schier,
Nick Desaulniers, Bill Wendling, Justin Stitt, Masahiro Yamada,
Alexey Gladkov, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, x86, H. Peter Anvin, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Alexandre Ghiti, Arnd Bergmann, Catalin Marinas,
Will Deacon, Mark Rutland, Ard Biesheuvel, Ilias Apalodimas,
Josh Poimboeuf, Peter Zijlstra, Miguel Ojeda, Boqun Feng,
Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Alice Ryhl, Trevor Gross, Danilo Krummrich, Daniel Almeida,
Tamir Duberstein, Alexandre Courbot, Onur Özkan,
Jonathan Corbet, Randy Dunlap
Cc: linux-kbuild, linux-kernel, llvm, linux-riscv, linux-arch,
linux-arm-kernel, linux-efi, rust-for-linux, linux-doc,
Jens Axboe, Lorenzo Stoakes (ARM)
modpost spends a long time md4 hashing module source at a per-byte
granularity.
Fix this by doing this hashing per-file instead by accumulating a per-file
buffer in parse_file().
All 11,189 .mod.c files and Module.symvers were confirmed to be identical
with this change applied.
This is especially impactful for allmodconfig incremental builds (where
CONFIG_MODULE_SRCVERSION_ALL is set).
Whole build, 128-thread Threadripper 9980X, best of N runs:
before after delta
-------------------------------
x86 allmodconfig, touch mm/vma.c, gcc 40.6s 38.2s -2.4s (-6%)
x86 allmodconfig, touch mm/vma.c, clang 36.2s 35.0s -1.2s (-3%)
Assisted-by: LLM
Signed-off-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
---
scripts/mod/sumversion.c | 56 +++++++++++++++++++++++++++++++++++++-----------
1 file changed, 44 insertions(+), 12 deletions(-)
diff --git a/scripts/mod/sumversion.c b/scripts/mod/sumversion.c
index 3dd28b4d0099..2cbadd3cd97d 100644
--- a/scripts/mod/sumversion.c
+++ b/scripts/mod/sumversion.c
@@ -224,19 +224,11 @@ static void md4_final_ascii(struct md4_ctx *mctx, char *out, unsigned int len)
mctx->hash[0], mctx->hash[1], mctx->hash[2], mctx->hash[3]);
}
-static inline void add_char(unsigned char c, struct md4_ctx *md)
-{
- md4_update(md, &c, 1);
-}
-
-static int parse_string(const char *file, unsigned long len,
- struct md4_ctx *md)
+static int parse_string(const char *file, unsigned long len)
{
unsigned long i;
- add_char(file[0], md);
for (i = 1; i < len; i++) {
- add_char(file[i], md);
if (file[i] == '"' && file[i-1] != '\\')
break;
}
@@ -255,15 +247,44 @@ static int parse_comment(const char *file, unsigned long len)
}
/* FIXME: Handle .s files differently (eg. # starts comments) --RR */
+static bool stop_char[256];
+
+static void init_stop_chars(void)
+{
+ static bool done;
+ int chr;
+
+ if (done)
+ return;
+
+ for (chr = 0; chr < 256; chr++)
+ if (chr == '\\' || chr == '"' || chr == '/' || isspace(chr))
+ stop_char[chr] = true;
+
+ done = true;
+}
+
static int parse_file(const char *fname, struct md4_ctx *md)
{
+ unsigned long i, len, n = 0;
+ unsigned char *buf;
char *file;
- unsigned long i, len;
file = read_text_file(fname);
len = strlen(file);
+ if (!len)
+ goto out_file;
+ init_stop_chars();
+ buf = xmalloc(len); /* File output buffer. */
for (i = 0; i < len; i++) {
+ const unsigned char chr = file[i];
+
+ if (!stop_char[chr]) {
+ buf[n++] = file[i];
+ continue;
+ }
+
/* Collapse and ignore \ and CR. */
if (file[i] == '\\' && (i+1 < len) && file[i+1] == '\n') {
i++;
@@ -276,7 +297,14 @@ static int parse_file(const char *fname, struct md4_ctx *md)
/* Handle strings as whole units */
if (file[i] == '"') {
- i += parse_string(file+i, len - i, md);
+ unsigned long slen = parse_string(file+i, len - i);
+
+ /* Closing quote is included if there is one. */
+ if (slen < len - i)
+ slen++;
+ memcpy(buf + n, file + i, slen);
+ n += slen;
+ i += slen - 1;
continue;
}
@@ -286,11 +314,15 @@ static int parse_file(const char *fname, struct md4_ctx *md)
continue;
}
- add_char(file[i], md);
+ buf[n++] = file[i];
}
+ md4_update(md, buf, n);
+ free(buf);
+out_file:
free(file);
return 1;
}
+
/* Check whether the file is a static library or not */
static bool is_static_library(const char *objfile)
{
--
2.55.0
^ permalink raw reply [flat|nested] 73+ messages in thread* Re: [PATCH 13/23] modpost: hash module source per-file, not per-byte
2026-09-08 20:55 ` [PATCH 13/23] modpost: hash module source per-file, not per-byte Lorenzo Stoakes (ARM)
@ 2026-09-10 12:52 ` Petr Pavlu
2026-09-11 10:41 ` Lorenzo Stoakes (ARM)
0 siblings, 1 reply; 73+ messages in thread
From: Petr Pavlu @ 2026-09-10 12:52 UTC (permalink / raw)
To: Lorenzo Stoakes (ARM)
Cc: Linus Torvalds, Nathan Chancellor, Nicolas Schier,
Nick Desaulniers, Bill Wendling, Justin Stitt, Masahiro Yamada,
Alexey Gladkov, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, x86, H. Peter Anvin, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Alexandre Ghiti, Arnd Bergmann, Catalin Marinas,
Will Deacon, Mark Rutland, Ard Biesheuvel, Ilias Apalodimas,
Josh Poimboeuf, Peter Zijlstra, Miguel Ojeda, Boqun Feng,
Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Alice Ryhl, Trevor Gross, Danilo Krummrich, Daniel Almeida,
Tamir Duberstein, Alexandre Courbot, Onur Özkan,
Jonathan Corbet, Randy Dunlap, linux-kbuild, linux-kernel, llvm,
linux-riscv, linux-arch, linux-arm-kernel, linux-efi,
rust-for-linux, linux-doc, Jens Axboe
On 9/8/26 10:55 PM, Lorenzo Stoakes (ARM) wrote:
> modpost spends a long time md4 hashing module source at a per-byte
> granularity.
>
> Fix this by doing this hashing per-file instead by accumulating a per-file
> buffer in parse_file().
>
> All 11,189 .mod.c files and Module.symvers were confirmed to be identical
> with this change applied.
>
> This is especially impactful for allmodconfig incremental builds (where
> CONFIG_MODULE_SRCVERSION_ALL is set).
>
> Whole build, 128-thread Threadripper 9980X, best of N runs:
>
> before after delta
> -------------------------------
> x86 allmodconfig, touch mm/vma.c, gcc 40.6s 38.2s -2.4s (-6%)
> x86 allmodconfig, touch mm/vma.c, clang 36.2s 35.0s -1.2s (-3%)
>
> Assisted-by: LLM
> Signed-off-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
> ---
> scripts/mod/sumversion.c | 56 +++++++++++++++++++++++++++++++++++++-----------
> 1 file changed, 44 insertions(+), 12 deletions(-)
Support for the module srcversion is being removed in "module: remove
MODULE_VERSION() and srcversion" [1] and scripts/mod/sumversion.c should
be gone completely.
[1] https://lore.kernel.org/linux-modules/20260731-module_ver_remove-v2-0-c9163858f382@linuxfoundation.org/
--
Thanks,
Petr
^ permalink raw reply [flat|nested] 73+ messages in thread
* Re: [PATCH 13/23] modpost: hash module source per-file, not per-byte
2026-09-10 12:52 ` Petr Pavlu
@ 2026-09-11 10:41 ` Lorenzo Stoakes (ARM)
2026-09-11 11:57 ` Petr Pavlu
0 siblings, 1 reply; 73+ messages in thread
From: Lorenzo Stoakes (ARM) @ 2026-09-11 10:41 UTC (permalink / raw)
To: Petr Pavlu
Cc: Linus Torvalds, Nathan Chancellor, Nicolas Schier,
Nick Desaulniers, Bill Wendling, Justin Stitt, Masahiro Yamada,
Alexey Gladkov, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, x86, H. Peter Anvin, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Alexandre Ghiti, Arnd Bergmann, Catalin Marinas,
Will Deacon, Mark Rutland, Ard Biesheuvel, Ilias Apalodimas,
Josh Poimboeuf, Peter Zijlstra, Miguel Ojeda, Boqun Feng,
Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Alice Ryhl, Trevor Gross, Danilo Krummrich, Daniel Almeida,
Tamir Duberstein, Alexandre Courbot, Onur Özkan,
Jonathan Corbet, Randy Dunlap, linux-kbuild, linux-kernel, llvm,
linux-riscv, linux-arch, linux-arm-kernel, linux-efi,
rust-for-linux, linux-doc, Jens Axboe
On Thu, Sep 10, 2026 at 02:52:50PM +0200, Petr Pavlu wrote:
> On 9/8/26 10:55 PM, Lorenzo Stoakes (ARM) wrote:
> > modpost spends a long time md4 hashing module source at a per-byte
> > granularity.
> >
> > Fix this by doing this hashing per-file instead by accumulating a per-file
> > buffer in parse_file().
> >
> > All 11,189 .mod.c files and Module.symvers were confirmed to be identical
> > with this change applied.
> >
> > This is especially impactful for allmodconfig incremental builds (where
> > CONFIG_MODULE_SRCVERSION_ALL is set).
> >
> > Whole build, 128-thread Threadripper 9980X, best of N runs:
> >
> > before after delta
> > -------------------------------
> > x86 allmodconfig, touch mm/vma.c, gcc 40.6s 38.2s -2.4s (-6%)
> > x86 allmodconfig, touch mm/vma.c, clang 36.2s 35.0s -1.2s (-3%)
> >
> > Assisted-by: LLM
> > Signed-off-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
> > ---
> > scripts/mod/sumversion.c | 56 +++++++++++++++++++++++++++++++++++++-----------
> > 1 file changed, 44 insertions(+), 12 deletions(-)
>
> Support for the module srcversion is being removed in "module: remove
> MODULE_VERSION() and srcversion" [1] and scripts/mod/sumversion.c should
> be gone completely.
Hmm, that series doesn't remove the script though, is that planned as a follow up?
I guess I can drop this patch from the series then :) Also patch 17 I think?
>
> [1] https://lore.kernel.org/linux-modules/20260731-module_ver_remove-v2-0-c9163858f382@linuxfoundation.org/
>
> --
> Thanks,
> Petr
--
Cheers, Lorenzo
^ permalink raw reply [flat|nested] 73+ messages in thread
* Re: [PATCH 13/23] modpost: hash module source per-file, not per-byte
2026-09-11 10:41 ` Lorenzo Stoakes (ARM)
@ 2026-09-11 11:57 ` Petr Pavlu
2026-09-11 12:21 ` Lorenzo Stoakes (ARM)
0 siblings, 1 reply; 73+ messages in thread
From: Petr Pavlu @ 2026-09-11 11:57 UTC (permalink / raw)
To: Lorenzo Stoakes (ARM)
Cc: Linus Torvalds, Nathan Chancellor, Nicolas Schier,
Nick Desaulniers, Bill Wendling, Justin Stitt, Masahiro Yamada,
Alexey Gladkov, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, x86, H. Peter Anvin, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Alexandre Ghiti, Arnd Bergmann, Catalin Marinas,
Will Deacon, Mark Rutland, Ard Biesheuvel, Ilias Apalodimas,
Josh Poimboeuf, Peter Zijlstra, Miguel Ojeda, Boqun Feng,
Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Alice Ryhl, Trevor Gross, Danilo Krummrich, Daniel Almeida,
Tamir Duberstein, Alexandre Courbot, Onur Özkan,
Jonathan Corbet, Randy Dunlap, linux-kbuild, linux-kernel, llvm,
linux-riscv, linux-arch, linux-arm-kernel, linux-efi,
rust-for-linux, linux-doc, Jens Axboe
On 9/11/26 12:41 PM, Lorenzo Stoakes (ARM) wrote:
> On Thu, Sep 10, 2026 at 02:52:50PM +0200, Petr Pavlu wrote:
>> On 9/8/26 10:55 PM, Lorenzo Stoakes (ARM) wrote:
>>> modpost spends a long time md4 hashing module source at a per-byte
>>> granularity.
>>>
>>> Fix this by doing this hashing per-file instead by accumulating a per-file
>>> buffer in parse_file().
>>>
>>> All 11,189 .mod.c files and Module.symvers were confirmed to be identical
>>> with this change applied.
>>>
>>> This is especially impactful for allmodconfig incremental builds (where
>>> CONFIG_MODULE_SRCVERSION_ALL is set).
>>>
>>> Whole build, 128-thread Threadripper 9980X, best of N runs:
>>>
>>> before after delta
>>> -------------------------------
>>> x86 allmodconfig, touch mm/vma.c, gcc 40.6s 38.2s -2.4s (-6%)
>>> x86 allmodconfig, touch mm/vma.c, clang 36.2s 35.0s -1.2s (-3%)
>>>
>>> Assisted-by: LLM
>>> Signed-off-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
>>> ---
>>> scripts/mod/sumversion.c | 56 +++++++++++++++++++++++++++++++++++++-----------
>>> 1 file changed, 44 insertions(+), 12 deletions(-)
>>
>> Support for the module srcversion is being removed in "module: remove
>> MODULE_VERSION() and srcversion" [1] and scripts/mod/sumversion.c should
>> be gone completely.
>
> Hmm, that series doesn't remove the script though, is that planned as a follow up?
My reply to that series [1] notes that it should also remove
scripts/mod/sumversion.c. It should be done in the next version.
>
> I guess I can drop this patch from the series then :) Also patch 17 I think?
I agree. The module srcversion is no longer needed since build IDs are
nowadays the standard mechanism for providing effectively the same
functionality. While I'm not sure when the mentioned removal will land,
reviewing and polishing this patch and patch 17 would IMO be wasteful at
this point.
[1] https://lore.kernel.org/linux-modules/4e321525-708c-4964-9937-d94ea27e8d85@suse.com/
--
Thanks,
Petr
^ permalink raw reply [flat|nested] 73+ messages in thread
* Re: [PATCH 13/23] modpost: hash module source per-file, not per-byte
2026-09-11 11:57 ` Petr Pavlu
@ 2026-09-11 12:21 ` Lorenzo Stoakes (ARM)
0 siblings, 0 replies; 73+ messages in thread
From: Lorenzo Stoakes (ARM) @ 2026-09-11 12:21 UTC (permalink / raw)
To: Petr Pavlu
Cc: Linus Torvalds, Nathan Chancellor, Nicolas Schier,
Nick Desaulniers, Bill Wendling, Justin Stitt, Masahiro Yamada,
Alexey Gladkov, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, x86, H. Peter Anvin, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Alexandre Ghiti, Arnd Bergmann, Catalin Marinas,
Will Deacon, Mark Rutland, Ard Biesheuvel, Ilias Apalodimas,
Josh Poimboeuf, Peter Zijlstra, Miguel Ojeda, Boqun Feng,
Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Alice Ryhl, Trevor Gross, Danilo Krummrich, Daniel Almeida,
Tamir Duberstein, Alexandre Courbot, Onur Özkan,
Jonathan Corbet, Randy Dunlap, linux-kbuild, linux-kernel, llvm,
linux-riscv, linux-arch, linux-arm-kernel, linux-efi,
rust-for-linux, linux-doc, Jens Axboe
On Fri, Sep 11, 2026 at 01:57:34PM +0200, Petr Pavlu wrote:
> On 9/11/26 12:41 PM, Lorenzo Stoakes (ARM) wrote:
> > On Thu, Sep 10, 2026 at 02:52:50PM +0200, Petr Pavlu wrote:
> >> On 9/8/26 10:55 PM, Lorenzo Stoakes (ARM) wrote:
> >>> modpost spends a long time md4 hashing module source at a per-byte
> >>> granularity.
> >>>
> >>> Fix this by doing this hashing per-file instead by accumulating a per-file
> >>> buffer in parse_file().
> >>>
> >>> All 11,189 .mod.c files and Module.symvers were confirmed to be identical
> >>> with this change applied.
> >>>
> >>> This is especially impactful for allmodconfig incremental builds (where
> >>> CONFIG_MODULE_SRCVERSION_ALL is set).
> >>>
> >>> Whole build, 128-thread Threadripper 9980X, best of N runs:
> >>>
> >>> before after delta
> >>> -------------------------------
> >>> x86 allmodconfig, touch mm/vma.c, gcc 40.6s 38.2s -2.4s (-6%)
> >>> x86 allmodconfig, touch mm/vma.c, clang 36.2s 35.0s -1.2s (-3%)
> >>>
> >>> Assisted-by: LLM
> >>> Signed-off-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
> >>> ---
> >>> scripts/mod/sumversion.c | 56 +++++++++++++++++++++++++++++++++++++-----------
> >>> 1 file changed, 44 insertions(+), 12 deletions(-)
> >>
> >> Support for the module srcversion is being removed in "module: remove
> >> MODULE_VERSION() and srcversion" [1] and scripts/mod/sumversion.c should
> >> be gone completely.
> >
> > Hmm, that series doesn't remove the script though, is that planned as a follow up?
>
> My reply to that series [1] notes that it should also remove
> scripts/mod/sumversion.c. It should be done in the next version.
Ack!
>
> >
> > I guess I can drop this patch from the series then :) Also patch 17 I think?
>
> I agree. The module srcversion is no longer needed since build IDs are
> nowadays the standard mechanism for providing effectively the same
> functionality. While I'm not sure when the mentioned removal will land,
> reviewing and polishing this patch and patch 17 would IMO be wasteful at
> this point.
Ack, will drop them for v2 then and add a note to the cover clearly stating the
dependency!
>
> [1] https://lore.kernel.org/linux-modules/4e321525-708c-4964-9937-d94ea27e8d85@suse.com/
>
> --
> Thanks,
> Petr
--
Cheers, Lorenzo
^ permalink raw reply [flat|nested] 73+ messages in thread
* [PATCH 14/23] modpost: cache section relocation mismatch state
2026-09-08 20:55 [PATCH 00/23] kbuild: significantly speed up kernel builds Lorenzo Stoakes (ARM)
` (12 preceding siblings ...)
2026-09-08 20:55 ` [PATCH 13/23] modpost: hash module source per-file, not per-byte Lorenzo Stoakes (ARM)
@ 2026-09-08 20:55 ` Lorenzo Stoakes (ARM)
2026-09-08 20:55 ` [PATCH 15/23] modpost: emit module descriptors as assembly Lorenzo Stoakes (ARM)
` (12 subsequent siblings)
26 siblings, 0 replies; 73+ messages in thread
From: Lorenzo Stoakes (ARM) @ 2026-09-08 20:55 UTC (permalink / raw)
To: Linus Torvalds, Nathan Chancellor, Nicolas Schier,
Nick Desaulniers, Bill Wendling, Justin Stitt, Masahiro Yamada,
Alexey Gladkov, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, x86, H. Peter Anvin, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Alexandre Ghiti, Arnd Bergmann, Catalin Marinas,
Will Deacon, Mark Rutland, Ard Biesheuvel, Ilias Apalodimas,
Josh Poimboeuf, Peter Zijlstra, Miguel Ojeda, Boqun Feng,
Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Alice Ryhl, Trevor Gross, Danilo Krummrich, Daniel Almeida,
Tamir Duberstein, Alexandre Courbot, Onur Özkan,
Jonathan Corbet, Randy Dunlap
Cc: linux-kbuild, linux-kernel, llvm, linux-riscv, linux-arch,
linux-arm-kernel, linux-efi, rust-for-linux, linux-doc,
Jens Axboe, Lorenzo Stoakes (ARM)
For every relocation modpost invokes check_section_mismatch() to determine
whether there is any kind of mismatch between the source and destination,
and if so which classification applies.
Each time it does this it invokes section_mismatch() which iterates
through the sectioncheck[] array every time it's called.
When walked a relocation section the source is fixed and there aren't many
targets, so the same names are looked up over and over again.
Therefore cache not only mismatch categorisation but also whether a
mismatch even exists for a given section and look up the sections in the
cache.
Special indices (undefined, absolute, common) take the uncached path as
before.
This results in very significant speed ups for allmodconfig incremental
builds.
modpost is on the serial tail of every build that links vmlinux.
Whole build, 128-thread Threadripper 9980X, best of N runs:
before after delta
-------------------------------
x86 defconfig, touch mm/vma.c, gcc 8.6s 8.4s -0.16s (-2%)
x86 defconfig, touch mm/vma.c, clang 7.7s 7.5s -0.15s (-2%)
x86 allmodconfig, touch mm/vma.c, gcc 38.2s 33.5s -4.6s (-12%)
x86 allmodconfig, touch mm/vma.c, clang 35.0s 31.1s -4.0s (-11%)
Assisted-by: LLM
Signed-off-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
---
scripts/mod/modpost.c | 66 +++++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 64 insertions(+), 2 deletions(-)
diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c
index 75374c64b8cc..0fd43c8a89ea 100644
--- a/scripts/mod/modpost.c
+++ b/scripts/mod/modpost.c
@@ -1155,12 +1155,67 @@ static void check_export_symbol(struct module *mod, struct elf_info *elf,
name);
}
+/*
+ * mismatch_cache[section index] ->
+ * 0 - uncached.
+ * -1 - no mismatch.
+ * >0 - mismatch index + 1.
+ */
+static int *mismatch_cache;
+
+static void init_mismatch_cache(unsigned int num_sections)
+{
+ mismatch_cache = xcalloc(num_sections, sizeof(*mismatch_cache));
+}
+
+static void reset_mismatch_cache(unsigned int num_sections)
+{
+ memset(mismatch_cache, 0, num_sections * sizeof(*mismatch_cache));
+}
+
+static void free_mismatch_cache(void)
+{
+ free(mismatch_cache);
+ mismatch_cache = NULL;
+}
+
+static const struct sectioncheck
+*cache_mismatch(unsigned int secndx, const struct sectioncheck *mismatch)
+{
+ if (!mismatch) {
+ mismatch_cache[secndx] = -1;
+ return NULL;
+ }
+
+ mismatch_cache[secndx] = (mismatch - sectioncheck) + 1;
+ return mismatch;
+}
+
+static const struct sectioncheck *get_section_mismatch(const char *fromsec,
+ const struct elf_info *elf, unsigned int secndx)
+{
+ int cached;
+
+ if (secndx >= elf->num_sections)
+ return section_mismatch(fromsec, sec_name(elf, secndx));
+
+ cached = mismatch_cache[secndx];
+ if (cached < 0)
+ return NULL;
+ if (cached > 0)
+ return §ioncheck[cached - 1];
+
+ return cache_mismatch(secndx,
+ section_mismatch(fromsec, sec_name(elf, secndx)));
+}
+
static void check_section_mismatch(struct module *mod, struct elf_info *elf,
Elf_Sym *sym,
unsigned int fsecndx, const char *fromsec,
Elf_Addr faddr, Elf_Addr taddr)
{
- const char *tosec = sec_name(elf, get_secindex(elf, sym));
+ const unsigned int to_secndx = get_secindex(elf, sym);
+ const char *tosec = sec_name(elf, to_secndx);
const struct sectioncheck *mismatch;
if (module_enabled && elf->export_symbol_secndx == fsecndx) {
@@ -1168,7 +1223,7 @@ static void check_section_mismatch(struct module *mod, struct elf_info *elf,
return;
}
- mismatch = section_mismatch(fromsec, tosec);
+ mismatch = get_section_mismatch(fromsec, elf, to_secndx);
if (!mismatch)
return;
@@ -1445,6 +1500,8 @@ static void check_sec_ref(struct module *mod, struct elf_info *elf)
{
int i;
+ init_mismatch_cache(elf->num_sections);
+
/* Walk through all sections */
for (i = 0; i < elf->num_sections; i++) {
Elf_Shdr *sechdr = &elf->sechdrs[i];
@@ -1461,6 +1518,9 @@ static void check_sec_ref(struct module *mod, struct elf_info *elf)
if (match(secname, section_white_list))
continue;
+ /* Reset cache per-section. */
+ reset_mismatch_cache(elf->num_sections);
+
start = sym_get_data_by_offset(elf, i, 0);
stop = start + sechdr->sh_size;
@@ -1472,6 +1532,8 @@ static void check_sec_ref(struct module *mod, struct elf_info *elf)
start, stop);
}
}
+
+ free_mismatch_cache();
}
static char *remove_dot(char *s)
--
2.55.0
^ permalink raw reply [flat|nested] 73+ messages in thread* [PATCH 15/23] modpost: emit module descriptors as assembly
2026-09-08 20:55 [PATCH 00/23] kbuild: significantly speed up kernel builds Lorenzo Stoakes (ARM)
` (13 preceding siblings ...)
2026-09-08 20:55 ` [PATCH 14/23] modpost: cache section relocation mismatch state Lorenzo Stoakes (ARM)
@ 2026-09-08 20:55 ` Lorenzo Stoakes (ARM)
2026-09-09 14:59 ` Linus Torvalds
2026-09-08 20:55 ` [PATCH 16/23] kbuild: batch module finalisation Lorenzo Stoakes (ARM)
` (11 subsequent siblings)
26 siblings, 1 reply; 73+ messages in thread
From: Lorenzo Stoakes (ARM) @ 2026-09-08 20:55 UTC (permalink / raw)
To: Linus Torvalds, Nathan Chancellor, Nicolas Schier,
Nick Desaulniers, Bill Wendling, Justin Stitt, Masahiro Yamada,
Alexey Gladkov, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, x86, H. Peter Anvin, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Alexandre Ghiti, Arnd Bergmann, Catalin Marinas,
Will Deacon, Mark Rutland, Ard Biesheuvel, Ilias Apalodimas,
Josh Poimboeuf, Peter Zijlstra, Miguel Ojeda, Boqun Feng,
Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Alice Ryhl, Trevor Gross, Danilo Krummrich, Daniel Almeida,
Tamir Duberstein, Alexandre Courbot, Onur Özkan,
Jonathan Corbet, Randy Dunlap
Cc: linux-kbuild, linux-kernel, llvm, linux-riscv, linux-arch,
linux-arm-kernel, linux-efi, rust-for-linux, linux-doc,
Jens Axboe, Lorenzo Stoakes (ARM)
modpost generates a descriptor for every module in the form of a
<module>.mod.c file with .modinfo strings, the __this_module descriptor,
exported symbol tables and (with CONFIG_MODVERSIONS set), the CRC of
imported symbols.
These files are compiled like any other kernel C file with all of the
-include preamble, as well as including linux/module.h, header dependencies
generated by fixdep of a few hundred headers, an objtool run and if LTO is
being performed, a link is performed to generate native code.
On an x86-64 allmodconfig build 11,189 *.mod.c files are built, each
taking ~0.24s of CPU time to compile, and module finalisation as a whole
6,300 CPU seconds, or 64 seconds of wall time when run over 128 threads.
It also generates ~1.3 GiB of *.mod.o.cmd files that every subsequent build
has to read back.
Avoid all this by emitting the descriptors as assembly instead.
The layout required (size and alignment of struct module, struct
modversion_info, the module's name offsets, init, and exit fields and
whether the architecture uses PREL32 ksymtab references) can all be derived
from scripts/mod/module-offsets.h.
An assembly file avoids all of the issues previously mentioned so this
conversion results in a very significant performance win on kernel build.
As a consequence of this change, since module-offsets.c includes
linux/module.h, scripts/mod is now built after the generated headers in
prepare0, rather than before.
Also update .gitignore and make clean to handle .mod.S files, but keep
.mod.c files there to ensure that users do not end up with untracked
changes/dirty trees after the change takes effect.
The sections were confirmed to be byte-for-byte identical to the C version
produced - each of .modinfo, .gnu.linkonce.this_module, __ksymtab*,
__ksymtab_strings, __kcrctab*, __kflagstab*, __versions,
__version_ext_crcs, __version_ext_names and their relocations - for all
8,135 modules of a clang allmodconfig build with CONFIG_COMPILE_TEST off
and CONFIG_MODVERSIONS, CONFIG_EXTENDED_MODVERSIONS and
CONFIG_MODULE_SRCVERSION_ALL on, and for a sample built with gcc.
What differs is what the compiler added around them: the __UNIQUE_ID_*
locals, the KASAN constructor for the .mod.c globals, and on x86 a
.note.gnu.property that the linker already drops from any module containing
an assembly file and the loader never reads.
None of these have any impact on the build, however.
x86_64 kernels built with gcc and clang, with CONFIG_MODVERSIONS,
CONFIG_EXTENDED_MODVERSIONS and CONFIG_MODULE_SRCVERSION_ALL, were booted,
every module and an external one loaded and unloaded, and the srcversions
checked against modinfo, i386 and arm64 defconfigs (gcc and clang) and a
ThinLTO build were also built as part of testing.
On the x86_64 allmodconfig with clang 22, "make modules" with every
*.mod.o and *.ko deleted goes from 64.5s (6,306 CPU-s) to 28.5s (518
CPU-s).
A consequence of this change is that the make jobs are now so small that
make cannot dispatch them quick enough, however the next commit in the
series addresses this issue.
This impacts clean and no-op builds with a large number of modules most
noticeably.
Whole build, 128-thread Threadripper 9980X, best of N runs:
before after delta
-------------------------------
x86 allmodconfig, no-op make, gcc 11.0s 1.9s -9.1s (-82%)
x86 allmodconfig, no-op make, clang 11.6s 2.4s -9.2s (-79%)
x86 allmodconfig, clean, gcc 342.1s 304.5s -37.6s (-11%)
x86 allmodconfig, clean, clang 338.5s 301.7s -36.8s (-11%)
Assisted-by: LLM
Signed-off-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
---
.gitignore | 1 +
Makefile | 4 +-
include/linux/vermagic.h | 2 +-
scripts/Makefile.modfinal | 10 +-
scripts/Makefile.modpost | 2 +-
scripts/mod/.gitignore | 1 +
scripts/mod/Makefile | 8 +
scripts/mod/modpost.c | 619 +++++++++++++++++++++++++++++++------------
scripts/mod/module-offsets.c | 35 +++
scripts/tags.sh | 5 +-
10 files changed, 512 insertions(+), 175 deletions(-)
diff --git a/.gitignore b/.gitignore
index 9875120ea7bd..00fc262b894b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -38,6 +38,7 @@
*.lzo
*.mod
*.mod.c
+*.mod.S
*.o
*.o.*
*.patch
diff --git a/Makefile b/Makefile
index 088e901f0a72..997fc7ebe096 100644
--- a/Makefile
+++ b/Makefile
@@ -1422,8 +1422,8 @@ archprepare: outputmakefile archheaders archscripts scripts include/config/kerne
include/generated/rustc_cfg remove-stale-files
prepare0: archprepare
- $(Q)$(MAKE) $(build)=scripts/mod
$(Q)$(MAKE) $(build)=. prepare
+ $(Q)$(MAKE) $(build)=scripts/mod
# All the preparing..
prepare: prepare0
@@ -2245,7 +2245,7 @@ clean: $(clean-dirs)
-o -name '*.dt.yaml' -o -name 'dtbs-list' \
-o -name '*.dwo' -o -name '*.lst' \
-o -name '*.su' -o -name '*.mod' \
- -o -name '.*.d' -o -name '.*.tmp' -o -name '*.mod.c' \
+ -o -name '.*.d' -o -name '.*.tmp' -o -name '*.mod.c' -o -name '*.mod.S' \
-o -name '*.lex.c' -o -name '*.tab.[ch]' \
-o -name '*.asn1.[ch]' \
-o -name '*.symtypes' -o -name 'modules.order' \
diff --git a/include/linux/vermagic.h b/include/linux/vermagic.h
index 335c360d4f9b..09f05d02664c 100644
--- a/include/linux/vermagic.h
+++ b/include/linux/vermagic.h
@@ -3,7 +3,7 @@
#define _LINUX_VERMAGIC_H
#ifndef INCLUDE_VERMAGIC
-#error "This header can be included from kernel/module.c or *.mod.c only"
+#error "This header can be included from kernel/module.c or scripts/module-common.c only"
#endif
#include <generated/utsrelease.h>
diff --git a/scripts/Makefile.modfinal b/scripts/Makefile.modfinal
index 01a37ec872b9..75e9effdf02c 100644
--- a/scripts/Makefile.modfinal
+++ b/scripts/Makefile.modfinal
@@ -20,10 +20,14 @@ __modfinal: $(modules:%.o=%.ko)
modname = $(notdir $(@:.mod.o=))
part-of-module = y
GCOV_PROFILE := n
-ccflags-remove-y := $(CC_FLAGS_CFI)
-%.mod.o: %.mod.c FORCE
- $(call if_changed_rule,cc_o_c)
+# modpost lays the <module>.mod.S out completely (write_mod_S_file()), so it
+# needs only the assembler and no dependency tracking.
+quiet_cmd_as_mod_o = AS [M] $@
+ cmd_as_mod_o = $(CC) $(_a_flags) $(modkern_aflags) -c -o $@ $<
+
+%.mod.o: %.mod.S FORCE
+ $(call if_changed,as_mod_o)
.module-common.o: $(srctree)/scripts/module-common.c FORCE
$(call if_changed_rule,cc_o_c)
diff --git a/scripts/Makefile.modpost b/scripts/Makefile.modpost
index d7d45067d08b..eecf5f5c99b4 100644
--- a/scripts/Makefile.modpost
+++ b/scripts/Makefile.modpost
@@ -13,7 +13,7 @@
# Stage 2 is handled by this file and does the following
# 1) Find all modules listed in modules.order
# 2) modpost is then used to
-# 3) create one <module>.mod.c file per module
+# 3) create one <module>.mod.S file per module
# 4) create one Module.symvers file with CRC for all exported symbols
# Step 3 is used to place certain information in the module's ELF
diff --git a/scripts/mod/.gitignore b/scripts/mod/.gitignore
index 0465ec33c9bf..620ab4362094 100644
--- a/scripts/mod/.gitignore
+++ b/scripts/mod/.gitignore
@@ -1,5 +1,6 @@
# SPDX-License-Identifier: GPL-2.0-only
/devicetable-offsets.h
+/module-offsets.h
/elfconfig.h
/mk_elfconfig
/modpost
diff --git a/scripts/mod/Makefile b/scripts/mod/Makefile
index c729bc936bae..fbd5099e0441 100644
--- a/scripts/mod/Makefile
+++ b/scripts/mod/Makefile
@@ -13,10 +13,18 @@ $(obj)/$(devicetable-offsets-file): $(obj)/devicetable-offsets.s FORCE
targets += $(devicetable-offsets-file) devicetable-offsets.s
+module-offsets-file := module-offsets.h
+
+$(obj)/$(module-offsets-file): $(obj)/module-offsets.s FORCE
+ $(call filechk,offsets,__MODULE_OFFSETS_H__)
+
+targets += $(module-offsets-file) module-offsets.s
+
# dependencies on generated files need to be listed explicitly
$(obj)/modpost.o $(obj)/file2alias.o $(obj)/sumversion.o $(obj)/symsearch.o: $(obj)/elfconfig.h
$(obj)/file2alias.o: $(obj)/$(devicetable-offsets-file)
+$(obj)/modpost.o: $(obj)/$(module-offsets-file)
quiet_cmd_elfconfig = MKELF $@
cmd_elfconfig = $(obj)/mk_elfconfig < $< > $@
diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c
index 0fd43c8a89ea..550ccd753ed8 100644
--- a/scripts/mod/modpost.c
+++ b/scripts/mod/modpost.c
@@ -26,6 +26,7 @@
#include <list.h>
#include <xalloc.h>
#include "modpost.h"
+#include "module-offsets.h"
#include "../../include/linux/license.h"
#define MODULE_NS_PREFIX "module:"
@@ -1920,39 +1921,6 @@ static void check_modname_len(struct module *mod)
mod_error(mod, "module name is too long\n");
}
-/**
- * Header for the generated file
- **/
-static void add_header(struct buffer *b, struct module *mod)
-{
- buf_printf(b, "#include <linux/module.h>\n");
- buf_printf(b, "#include <linux/export-internal.h>\n");
- buf_printf(b, "#include <linux/compiler.h>\n");
- buf_printf(b, "\n");
- buf_printf(b, "MODULE_INFO(name, KBUILD_MODNAME);\n");
- buf_printf(b, "\n");
- buf_printf(b, "__visible struct module __this_module\n");
- buf_printf(b, "__section(\".gnu.linkonce.this_module\") = {\n");
- buf_printf(b, "\t.name = KBUILD_MODNAME,\n");
- if (mod->has_init)
- buf_printf(b, "\t.init = init_module,\n");
- if (mod->has_cleanup)
- buf_printf(b, "#ifdef CONFIG_MODULE_UNLOAD\n"
- "\t.exit = cleanup_module,\n"
- "#endif\n");
- buf_printf(b, "\t.arch = MODULE_ARCH_INIT,\n");
- buf_printf(b, "};\n");
-
- if (!external_module)
- buf_printf(b, "\nMODULE_INFO(intree, \"Y\");\n");
-
- if (strstarts(mod->name, "drivers/staging"))
- buf_printf(b, "\nMODULE_INFO(staging, \"Y\");\n");
-
- if (strstarts(mod->name, "tools/testing"))
- buf_printf(b, "\nMODULE_INFO(test, \"Y\");\n");
-}
-
static void add_exported_symbols(struct buffer *buf, struct module *mod)
{
struct symbol *sym;
@@ -1990,123 +1958,6 @@ static void add_exported_symbols(struct buffer *buf, struct module *mod)
}
}
-/**
- * Record CRCs for unresolved symbols, supporting long names
- */
-static void add_extended_versions(struct buffer *b, struct module *mod)
-{
- struct symbol *s;
-
- if (!extended_modversions)
- return;
-
- buf_printf(b, "\n");
- buf_printf(b, "static const u32 ____version_ext_crcs[]\n");
- buf_printf(b, "__used __section(\"__version_ext_crcs\") = {\n");
- list_for_each_entry(s, &mod->unresolved_symbols, list) {
- if (!s->module)
- continue;
- if (!s->crc_valid) {
- mod_warn(mod, "symbol '%s' has no CRC!\n", s->name);
- continue;
- }
- buf_printf(b, "\t0x%08x,\n", s->crc);
- }
- buf_printf(b, "};\n");
-
- buf_printf(b, "static const char ____version_ext_names[]\n");
- buf_printf(b, "__used __section(\"__version_ext_names\") =\n");
- list_for_each_entry(s, &mod->unresolved_symbols, list) {
- if (!s->module)
- continue;
- if (!s->crc_valid)
- /*
- * We already warned on this when producing the crc
- * table.
- * We need to skip its name too, as the indexes in
- * both tables need to align.
- */
- continue;
- buf_printf(b, "\t\"%s\\0\"\n", s->name);
- }
- buf_printf(b, ";\n");
-}
-
-/**
- * Record CRCs for unresolved symbols
- **/
-static void add_versions(struct buffer *b, struct module *mod)
-{
- struct symbol *s;
-
- if (!basic_modversions)
- return;
-
- buf_printf(b, "\n");
- buf_printf(b, "static const struct modversion_info ____versions[]\n");
- buf_printf(b, "__used __section(\"__versions\") = {\n");
-
- list_for_each_entry(s, &mod->unresolved_symbols, list) {
- if (!s->module)
- continue;
- if (!s->crc_valid) {
- mod_warn(mod, "symbol '%s' has no CRC!\n", s->name);
- continue;
- }
- if (strlen(s->name) >= MODULE_NAME_LEN) {
- if (extended_modversions) {
- /* this symbol will only be in the extended info */
- continue;
- } else {
- mod_error(mod, "too long symbol '%s'\n", s->name);
- break;
- }
- }
- buf_printf(b, "\t{ 0x%08x, \"%s\" },\n",
- s->crc, s->name);
- }
-
- buf_printf(b, "};\n");
-}
-
-static void add_depends(struct buffer *b, struct module *mod)
-{
- struct symbol *s;
- int first = 1;
-
- /* Clear ->seen flag of modules that own symbols needed by this. */
- list_for_each_entry(s, &mod->unresolved_symbols, list) {
- if (s->module)
- s->module->seen = s->module->is_vmlinux;
- }
-
- buf_printf(b, "\n");
- buf_printf(b, "MODULE_INFO(depends, \"");
- list_for_each_entry(s, &mod->unresolved_symbols, list) {
- const char *p;
- if (!s->module)
- continue;
-
- if (s->module->seen)
- continue;
-
- s->module->seen = true;
- p = get_basename(s->module->name);
- buf_printf(b, "%s%s", first ? "" : ",", p);
- first = 0;
- }
- buf_printf(b, "\");\n");
-}
-
-static void add_srcversion(struct buffer *b, struct module *mod)
-{
- if (mod->srcversion[0]) {
- buf_printf(b, "\n");
- buf_printf(b, "MODULE_INFO(srcversion, \"%s\");\n",
- mod->srcversion);
- }
-}
-
static void write_buf(struct buffer *b, const char *fname)
{
FILE *file;
@@ -2191,30 +2042,465 @@ static void write_vmlinux_export_c_file(struct module *mod)
free(buf.p);
}
-/* do sanity checks, and generate *.mod.c file */
-static void write_mod_c_file(struct module *mod)
+#if MOD_SIZEOF_LONG == 8
+#define MOD_PTR_DIRECTIVE ".quad"
+#else
+#define MOD_PTR_DIRECTIVE ".long"
+#endif
+
+/* See KSYM_FUNC() in include/linux/export-internal.h. */
+#if MOD_FUNC_PLABEL
+#define MOD_FUNC_PREFIX "P%"
+#else
+#define MOD_FUNC_PREFIX ""
+#endif
+
+/* See __KSYM_ALIGN in include/linux/export-internal.h. */
+#if MOD_PREL32_RELOCATIONS || MOD_SIZEOF_LONG == 4
+#define KSYM_ALIGN 4
+#else
+#define KSYM_ALIGN 8
+#endif
+
+/* Append the body of an assembler string literal, escaped as needed. */
+static void buf_escaped(struct buffer *buf, const char *str)
{
- struct buffer buf = { };
- struct module_alias *alias, *next;
- char fname[PATH_MAX];
- int ret;
+ unsigned char chr;
- add_header(&buf, mod);
- add_exported_symbols(&buf, mod);
- add_versions(&buf, mod);
- add_extended_versions(&buf, mod);
- add_depends(&buf, mod);
+ while ((chr = *str++)) {
+ if (chr == '"' || chr == '\\')
+ buf_printf(buf, "\\%c", chr);
+ else if (isprint(chr))
+ buf_printf(buf, "%c", chr);
+ else
+ buf_printf(buf, "\\%03o", chr);
+ }
+}
- buf_printf(&buf, "\n");
+static void buf_asciz(struct buffer *buf, const char *str)
+{
+ buf_printf(buf, "\t.asciz \"");
+ buf_escaped(buf, str);
+ buf_printf(buf, "\"\n");
+}
+
+/* The equivalent of MODULE_INFO(tag, info). */
+static void add_asm_modinfo(struct buffer *buf, const char *tag,
+ const char *info)
+{
+ buf_printf(buf, "\t.section .modinfo,\"a\",%%progbits\n");
+ buf_printf(buf, "\t.asciz \"%s=", tag);
+ buf_escaped(buf, info);
+ buf_printf(buf, "\"\n");
+}
+
+/* See __KSYM_REF() in include/linux/export-internal.h. */
+static void add_asm_ksym_ref(struct buffer *buf, const char *prefix,
+ const char *sym)
+{
+#if MOD_PREL32_RELOCATIONS
+ buf_printf(buf, "\t.long %s%s - .\n", prefix, sym);
+#else
+ buf_printf(buf, "\t" MOD_PTR_DIRECTIVE " %s%s\n", prefix, sym);
+#endif
+}
+
+/* The name and namespace strings a ksymtab entry refers to. */
+static void add_asm_kstrtab(struct buffer *buf, const struct symbol *sym)
+{
+ buf_printf(buf, "\t.section \"__ksymtab_strings\",\"aMS\",%%progbits,1\n");
+ buf_printf(buf, "__kstrtab_%s:\n", sym->name);
+ buf_asciz(buf, sym->name);
+ buf_printf(buf, "__kstrtabns_%s:\n", sym->name);
+ buf_asciz(buf, sym->namespace);
+ buf_printf(buf, "\t.previous\n");
+}
+
+/* The equivalent of SYMBOL_FLAGS(). */
+static void add_asm_kflagstab(struct buffer *buf, const struct symbol *sym)
+{
+ buf_printf(buf, "\t.section \"___kflagstab+%s\", \"a\"\n", sym->name);
+ buf_printf(buf, "__flags_%s:\n", sym->name);
+ buf_printf(buf, "\t.byte 0x%02x\n", get_symbol_flags(sym));
+ buf_printf(buf, "\t.previous\n");
+}
+
+/* The equivalent of KSYMTAB_FUNC()/KSYMTAB_DATA(). */
+static void add_asm_ksymtab(struct buffer *buf, const struct symbol *sym)
+{
+ const char *name = sym->name;
+
+ add_asm_kstrtab(buf, sym);
+
+ buf_printf(buf, "\t.section \"___ksymtab+%s\", \"a\"\n", name);
+ buf_printf(buf, "\t.balign %d\n", KSYM_ALIGN);
+ buf_printf(buf, "__ksymtab_%s:\n", name);
+ add_asm_ksym_ref(buf, sym->is_func ? MOD_FUNC_PREFIX : "", name);
+ add_asm_ksym_ref(buf, "__kstrtab_", name);
+ add_asm_ksym_ref(buf, "__kstrtabns_", name);
+ buf_printf(buf, "\t.previous\n");
+
+ add_asm_kflagstab(buf, sym);
+}
+
+/* The equivalent of SYMBOL_CRC(). */
+static void add_asm_crc(struct buffer *buf, const struct symbol *sym)
+{
+ buf_printf(buf, "\t.section \"___kcrctab+%s\",\"a\"\n", sym->name);
+ buf_printf(buf, "\t.balign 4\n");
+ buf_printf(buf, "__crc_%s:\n", sym->name);
+ buf_printf(buf, "\t.long 0x%08x\n", sym->crc);
+ buf_printf(buf, "\t.previous\n");
+}
+
+static bool export_is_kept(const struct symbol *sym)
+{
+ return !trim_unused_exports || sym->used;
+}
+
+/* Record the CRCs of the exported symbols. */
+static void add_asm_crcs(struct buffer *buf, struct module *mod)
+{
+ struct symbol *sym;
+
+ list_for_each_entry(sym, &mod->exported_symbols, list) {
+ if (!export_is_kept(sym))
+ continue;
+
+ if (!sym->crc_valid)
+ mod_warn(mod, "EXPORT symbol '%s' version generation failed, symbol will not be versioned.\n"
+ "Is '%s' prototyped in <asm/asm-prototypes.h>?\n",
+ sym->name, sym->name);
+ add_asm_crc(buf, sym);
+ }
+}
+
+static void add_asm_exported_symbols(struct buffer *buf, struct module *mod)
+{
+ struct symbol *sym;
+
+ list_for_each_entry(sym, &mod->exported_symbols, list) {
+ if (export_is_kept(sym))
+ add_asm_ksymtab(buf, sym);
+ }
+
+ if (modversions)
+ add_asm_crcs(buf, mod);
+}
+
+/* Zero fill up to the offset. */
+static void asm_skip_to(struct buffer *buf, unsigned int *pos,
+ unsigned int offset)
+{
+ if (offset > *pos)
+ buf_printf(buf, "\t.skip %u\n", offset - *pos);
+
+ *pos = offset;
+}
+
+/* A pointer field of __this_module: its offset and an assembler expression. */
+struct this_module_field {
+ unsigned int offset;
+ const char *value;
+};
+
+#define THIS_MODULE_MAX_FIELDS 4
+
+static int compare_field_offsets(const void *ptr_a, const void *ptr_b)
+{
+ const struct this_module_field *field_a = ptr_a, *field_b = ptr_b;
+
+ if (field_a->offset != field_b->offset)
+ return field_a->offset < field_b->offset ? -1 : 1;
+
+ return 0;
+}
+
+/* The fields of __this_module which are not zero, in offset order. */
+static unsigned int get_this_module_fields(const struct module *mod,
+ struct this_module_field *fields)
+{
+ unsigned int nr_fields = 0;
+
+ if (mod->has_init)
+ fields[nr_fields++] = (struct this_module_field)
+ { MOD_OFF_module_init, MOD_FUNC_PREFIX "init_module" };
+#ifdef MOD_OFF_module_exit
+ if (mod->has_cleanup)
+ fields[nr_fields++] = (struct this_module_field)
+ { MOD_OFF_module_exit, MOD_FUNC_PREFIX "cleanup_module" };
+#endif
+#ifdef MOD_OFF_module_arch_fixup_start
+ fields[nr_fields++] = (struct this_module_field)
+ { MOD_OFF_module_arch_fixup_start, "__start_fixup" };
+ fields[nr_fields++] = (struct this_module_field)
+ { MOD_OFF_module_arch_fixup_end, "__stop_fixup" };
+#endif
+ qsort(fields, nr_fields, sizeof(*fields), compare_field_offsets);
+
+ return nr_fields;
+}
+
+/*
+ * The equivalent of:
+ *
+ * __visible struct module __this_module __section(".gnu.linkonce.this_module")
+ * = { .name = KBUILD_MODNAME, .init = init_module, .exit = cleanup_module,
+ * .arch = MODULE_ARCH_INIT };
+ *
+ * Everything not listed is zero, MODULE_ARCH_INIT included, except on m68k.
+ */
+static void add_asm_this_module(struct buffer *buf, const struct module *mod,
+ const char *modname)
+{
+ struct this_module_field fields[THIS_MODULE_MAX_FIELDS];
+ const unsigned int nr_fields = get_this_module_fields(mod, fields);
+ unsigned int pos = 0, i;
+
+ buf_printf(buf, "\n\t.section .gnu.linkonce.this_module,\"aw\",%%progbits\n");
+ buf_printf(buf, "\t.balign %d\n", MOD_ALIGNOF_struct_module);
+ buf_printf(buf, "\t.globl __this_module\n");
+ buf_printf(buf, "\t.type __this_module, %%object\n");
+ buf_printf(buf, "\t.size __this_module, %d\n", MOD_SIZEOF_struct_module);
+ buf_printf(buf, "__this_module:\n");
+
+ asm_skip_to(buf, &pos, MOD_OFF_module_name);
+ buf_printf(buf, "\t.ascii \"%s\"\n", modname);
+ pos += strlen(modname);
+
+ for (i = 0; i < nr_fields; i++) {
+ asm_skip_to(buf, &pos, fields[i].offset);
+ buf_printf(buf, "\t" MOD_PTR_DIRECTIVE " %s\n", fields[i].value);
+ pos += MOD_SIZEOF_LONG;
+ }
+
+ asm_skip_to(buf, &pos, MOD_SIZEOF_struct_module);
+}
+
+/*
+ * An unresolved symbol without a module is not versioned; one without a CRC
+ * cannot be, so warn about it.
+ */
+static bool skip_unversioned(struct module *mod, const struct symbol *sym)
+{
+ if (!sym->module)
+ return true;
+ if (sym->crc_valid)
+ return false;
+
+ mod_warn(mod, "symbol '%s' has no CRC!\n", sym->name);
+ return true;
+}
+
+/* One struct modversion_info: the CRC, then the name padded to the end. */
+static void add_asm_version(struct buffer *buf, const struct symbol *sym)
+{
+ buf_printf(buf, "\t" MOD_PTR_DIRECTIVE " 0x%08x\n", sym->crc);
+ buf_printf(buf, "\t.ascii \"%s\"\n", sym->name);
+ buf_printf(buf, "\t.skip %zu\n", MOD_SIZEOF_struct_modversion_info -
+ MOD_OFF_modversion_info_name - strlen(sym->name));
+}
+
+/*
+ * The equivalent of:
+ *
+ * static const struct modversion_info ____versions[]
+ * __used __section("__versions") = { { crc, "name" }, ... };
+ *
+ * for unresolved symbols.
+ */
+static void add_asm_versions(struct buffer *buf, struct module *mod)
+{
+ struct symbol *sym;
+
+ if (!basic_modversions)
+ return;
+
+ buf_printf(buf, "\n\t.section __versions,\"a\",%%progbits\n");
+ buf_printf(buf, "\t.balign %d\n", MOD_ALIGNOF_struct_modversion_info);
+ list_for_each_entry(sym, &mod->unresolved_symbols, list) {
+ if (skip_unversioned(mod, sym))
+ continue;
+
+ if (strlen(sym->name) >= MOD_NAME_LEN) {
+ /* Only the extended table can hold it. */
+ if (extended_modversions)
+ continue;
+
+ mod_error(mod, "too long symbol '%s'\n", sym->name);
+ break;
+ }
+
+ add_asm_version(buf, sym);
+ }
+}
+
+static void add_asm_version_ext_crcs(struct buffer *buf, struct module *mod)
+{
+ struct symbol *sym;
+
+ buf_printf(buf, "\n\t.section __version_ext_crcs,\"a\",%%progbits\n");
+ buf_printf(buf, "\t.balign 4\n");
+ list_for_each_entry(sym, &mod->unresolved_symbols, list) {
+ if (skip_unversioned(mod, sym))
+ continue;
+
+ buf_printf(buf, "\t.long 0x%08x\n", sym->crc);
+ }
+}
+
+/*
+ * A symbol without a CRC was warned about with the CRCs, and is skipped here
+ * too so that the names line up with them.
+ */
+static void add_asm_version_ext_names(struct buffer *buf, struct module *mod)
+{
+ struct symbol *sym;
+
+ buf_printf(buf, "\t.section __version_ext_names,\"a\",%%progbits\n");
+ list_for_each_entry(sym, &mod->unresolved_symbols, list) {
+ if (!sym->module || !sym->crc_valid)
+ continue;
+
+ buf_asciz(buf, sym->name);
+ }
+ /* The terminator of the string literal this used to be. */
+ buf_printf(buf, "\t.byte 0\n");
+}
+
+/*
+ * The equivalent of:
+ * static const u32 ____version_ext_crcs[] __section("__version_ext_crcs") = { crc, ... };
+ * static const char ____version_ext_names[] __section("__version_ext_names") = "name\0" ...;
+ *
+ * for unresolved symbols.
+ */
+static void add_asm_extended_versions(struct buffer *buf, struct module *mod)
+{
+ if (!extended_modversions)
+ return;
+
+ add_asm_version_ext_crcs(buf, mod);
+ add_asm_version_ext_names(buf, mod);
+}
+
+/* Clear ->seen of the modules that own symbols this one needs. */
+static void clear_seen_dependencies(struct module *mod)
+{
+ struct symbol *sym;
+
+ list_for_each_entry(sym, &mod->unresolved_symbols, list) {
+ if (sym->module)
+ sym->module->seen = sym->module->is_vmlinux;
+ }
+}
+
+/* The modules this one depends on, each once, comma separated. */
+static void collect_dependencies(struct module *mod, struct buffer *deps)
+{
+ struct symbol *sym;
+ bool first = true;
+
+ clear_seen_dependencies(mod);
+
+ list_for_each_entry(sym, &mod->unresolved_symbols, list) {
+ struct module *owner = sym->module;
+
+ if (!owner || owner->seen)
+ continue;
+
+ owner->seen = true;
+ buf_printf(deps, "%s%s", first ? "" : ",",
+ get_basename(owner->name));
+ first = false;
+ }
+ buf_write(deps, "", 1);
+}
+
+static void add_asm_depends(struct buffer *buf, struct module *mod)
+{
+ struct buffer deps = { };
+
+ collect_dependencies(mod, &deps);
+ buf_printf(buf, "\n");
+ add_asm_modinfo(buf, "depends", deps.p);
+ free(deps.p);
+}
+
+/*
+ * KBUILD_MODNAME: the basename of the module with '-' and ',' replaced by
+ * '_' (see name-fix in scripts/Makefile.lib).
+ */
+static char *get_kbuild_modname(const struct module *mod)
+{
+ char *name = xstrdup(get_basename(mod->name));
+ char *curr;
+
+ for (curr = name; *curr; curr++) {
+ if (*curr == '-' || *curr == ',')
+ *curr = '_';
+ }
+
+ return name;
+}
+
+/* The module's name, its descriptor, and where it comes from. */
+static void add_asm_header(struct buffer *buf, const struct module *mod,
+ const char *modname)
+{
+ buf_printf(buf, "/* Generated by modpost, see scripts/Makefile.modfinal */\n\n");
+
+ add_asm_modinfo(buf, "name", modname);
+ add_asm_this_module(buf, mod, modname);
+ buf_printf(buf, "\n");
+
+ if (!external_module)
+ add_asm_modinfo(buf, "intree", "Y");
+ if (strstarts(mod->name, "drivers/staging"))
+ add_asm_modinfo(buf, "staging", "Y");
+ if (strstarts(mod->name, "tools/testing"))
+ add_asm_modinfo(buf, "test", "Y");
+}
+
+static void add_asm_aliases(struct buffer *buf, struct module *mod)
+{
+ struct module_alias *alias, *next;
+
+ buf_printf(buf, "\n");
list_for_each_entry_safe(alias, next, &mod->aliases, node) {
- buf_printf(&buf, "MODULE_ALIAS(\"%s\");\n", alias->str);
+ add_asm_modinfo(buf, "alias", alias->str);
list_del(&alias->node);
free(alias);
}
+}
- add_srcversion(&buf, mod);
+static void add_asm_srcversion(struct buffer *buf, const struct module *mod)
+{
+ if (!mod->srcversion[0])
+ return;
- ret = snprintf(fname, sizeof(fname), "%s.mod.c", mod->name);
+ buf_printf(buf, "\n");
+ add_asm_modinfo(buf, "srcversion", mod->srcversion);
+}
+
+static void write_mod_S_file(struct module *mod)
+{
+ struct buffer buf = { };
+ char fname[PATH_MAX];
+ char *modname = get_kbuild_modname(mod);
+ int ret;
+
+ add_asm_header(&buf, mod, modname);
+ buf_printf(&buf, "\n");
+ add_asm_exported_symbols(&buf, mod);
+ add_asm_versions(&buf, mod);
+ add_asm_extended_versions(&buf, mod);
+ add_asm_depends(&buf, mod);
+ add_asm_aliases(&buf, mod);
+ add_asm_srcversion(&buf, mod);
+ buf_printf(&buf, "\n\t.section .note.GNU-stack,\"\",%%progbits\n");
+
+ ret = snprintf(fname, sizeof(fname), "%s.mod.S", mod->name);
if (ret >= sizeof(fname)) {
error("%s: too long path was truncated\n", fname);
goto free;
@@ -2223,6 +2509,7 @@ static void write_mod_c_file(struct module *mod)
write_if_changed(&buf, fname);
free:
+ free(modname);
free(buf.p);
}
@@ -2462,7 +2749,7 @@ int main(int argc, char **argv)
if (mod->is_vmlinux)
write_vmlinux_export_c_file(mod);
else
- write_mod_c_file(mod);
+ write_mod_S_file(mod);
}
if (missing_namespace_deps)
diff --git a/scripts/mod/module-offsets.c b/scripts/mod/module-offsets.c
new file mode 100644
index 000000000000..a336dd47aa33
--- /dev/null
+++ b/scripts/mod/module-offsets.c
@@ -0,0 +1,35 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Layout of the structures modpost emits into *.mod.S, extracted from the
+ * target headers as devicetable-offsets.c does for the device tables.
+ */
+#define COMPILE_OFFSETS
+#include <linux/kbuild.h>
+#include <linux/module.h>
+
+int main(void)
+{
+ DEFINE(MOD_SIZEOF_LONG, sizeof(long));
+ DEFINE(MOD_PREL32_RELOCATIONS, IS_ENABLED(CONFIG_HAVE_ARCH_PREL32_RELOCATIONS));
+ DEFINE(MOD_FUNC_PLABEL, IS_ENABLED(CONFIG_PARISC) && IS_ENABLED(CONFIG_64BIT));
+
+ DEFINE(MOD_SIZEOF_struct_module, sizeof(struct module));
+ DEFINE(MOD_ALIGNOF_struct_module, __alignof__(struct module));
+ OFFSET(MOD_OFF_module_name, module, name);
+ OFFSET(MOD_OFF_module_init, module, init);
+#ifdef CONFIG_MODULE_UNLOAD
+ OFFSET(MOD_OFF_module_exit, module, exit);
+#endif
+#if defined(CONFIG_M68K) && defined(CONFIG_MMU)
+ /* MODULE_ARCH_INIT: the only architecture where it is not all zeroes. */
+ OFFSET(MOD_OFF_module_arch_fixup_start, module, arch.fixup_start);
+ OFFSET(MOD_OFF_module_arch_fixup_end, module, arch.fixup_end);
+#endif
+ DEFINE(MOD_NAME_LEN, MODULE_NAME_LEN);
+
+ DEFINE(MOD_SIZEOF_struct_modversion_info, sizeof(struct modversion_info));
+ DEFINE(MOD_ALIGNOF_struct_modversion_info, __alignof__(struct modversion_info));
+ OFFSET(MOD_OFF_modversion_info_name, modversion_info, name);
+
+ return 0;
+}
diff --git a/scripts/tags.sh b/scripts/tags.sh
index 41e38df96984..c33d0f58a9d4 100755
--- a/scripts/tags.sh
+++ b/scripts/tags.sh
@@ -14,8 +14,9 @@ fi
# RCS_FIND_IGNORE has escaped ()s -- remove them.
ignore="$(echo "$RCS_FIND_IGNORE" | sed 's|\\||g' )"
-# tags and cscope files should also ignore MODVERSION *.mod.c files
-ignore="$ignore ( -name *.mod.c ) -prune -o"
+# tags and cscope files should also ignore the modpost-generated *.mod.S files
+# and any *.mod.c left behind from before they were assembly
+ignore="$ignore ( -name *.mod.c -o -name *.mod.S ) -prune -o"
# ignore arbitrary directories
if [ -n "${IGNORE_DIRS}" ]; then
--
2.55.0
^ permalink raw reply [flat|nested] 73+ messages in thread* Re: [PATCH 15/23] modpost: emit module descriptors as assembly
2026-09-08 20:55 ` [PATCH 15/23] modpost: emit module descriptors as assembly Lorenzo Stoakes (ARM)
@ 2026-09-09 14:59 ` Linus Torvalds
0 siblings, 0 replies; 73+ messages in thread
From: Linus Torvalds @ 2026-09-09 14:59 UTC (permalink / raw)
To: Lorenzo Stoakes (ARM)
Cc: Nathan Chancellor, Nicolas Schier, Nick Desaulniers,
Bill Wendling, Justin Stitt, Masahiro Yamada, Alexey Gladkov,
Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin, Paul Walmsley, Palmer Dabbelt, Albert Ou,
Alexandre Ghiti, Arnd Bergmann, Catalin Marinas, Will Deacon,
Mark Rutland, Ard Biesheuvel, Ilias Apalodimas, Josh Poimboeuf,
Peter Zijlstra, Miguel Ojeda, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, Danilo Krummrich, Daniel Almeida, Tamir Duberstein,
Alexandre Courbot, Onur Özkan, Jonathan Corbet,
Randy Dunlap, linux-kbuild, linux-kernel, llvm, linux-riscv,
linux-arch, linux-arm-kernel, linux-efi, rust-for-linux,
linux-doc, Jens Axboe
On Tue, 8 Sept 2026 at 13:58, Lorenzo Stoakes (ARM) <ljs@kernel.org> wrote:
>
> modpost generates a descriptor for every module in the form of a
> <module>.mod.c file with .modinfo strings, the __this_module descriptor,
> exported symbol tables and (with CONFIG_MODVERSIONS set), the CRC of
> imported symbols.
>
> These files are compiled like any other kernel C file with all of the
> -include preamble, as well as including linux/module.h, header dependencies
> generated by fixdep of a few hundred headers, an objtool run and if LTO is
> being performed, a link is performed to generate native code.
This kind of triggers the same reaction as the kallsyms code: I think
not including <linux/module.h> is probably the biggest win in this
all, and precomputing the offsets of the (few) structures that modpost
uses then makes the whole end result fairly trivial in raw asm.
Fine.
BUT.
You end up with things like this instead:
> diff --git a/scripts/mod/module-offsets.c b/scripts/mod/module-offsets.c
where it generates a header file for those asm scripts so that the asm
generation can do things like
buf_printf(buf, "\t.size __this_module, %d\n",
MOD_SIZEOF_struct_module);
etc.
But I - again - get the feeling that this is all going through AS as a
silly way to generate an object file, and that there really isn't any
obvious advantage to that side quest.
Becuase no human ever wants to see that asm file, and it's no more
legible than generating the object file directly would have been.
So I get the feeling that it would again be more natural to just
generate the object file directly when all those files really end up
being are a few symbol definitions and a couple of fixed structures
(is there anything but the magic "__this_module" thing and the modinfo
unique ID thing?)
And again - I feel like nobody sane would want to write that code. I
certainly don't. But if this was at least partly automated anyway,
going that extra step might not be all that horrific.
I'll also be honest: part of why I do these suggestions is that I do
think it would be faster to skip the intermediate assembler step, but
a big part of it is that I don't love the random turds our build
creates. Getting rid of the *.mod.c files would be a cleanup of our
strange build.
Linus
^ permalink raw reply [flat|nested] 73+ messages in thread
* [PATCH 16/23] kbuild: batch module finalisation
2026-09-08 20:55 [PATCH 00/23] kbuild: significantly speed up kernel builds Lorenzo Stoakes (ARM)
` (14 preceding siblings ...)
2026-09-08 20:55 ` [PATCH 15/23] modpost: emit module descriptors as assembly Lorenzo Stoakes (ARM)
@ 2026-09-08 20:55 ` Lorenzo Stoakes (ARM)
2026-09-10 15:48 ` Nicolas Schier
2026-09-08 20:55 ` [PATCH 17/23] modpost: perform srcversion hashing in parallel Lorenzo Stoakes (ARM)
` (10 subsequent siblings)
26 siblings, 1 reply; 73+ messages in thread
From: Lorenzo Stoakes (ARM) @ 2026-09-08 20:55 UTC (permalink / raw)
To: Linus Torvalds, Nathan Chancellor, Nicolas Schier,
Nick Desaulniers, Bill Wendling, Justin Stitt, Masahiro Yamada,
Alexey Gladkov, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, x86, H. Peter Anvin, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Alexandre Ghiti, Arnd Bergmann, Catalin Marinas,
Will Deacon, Mark Rutland, Ard Biesheuvel, Ilias Apalodimas,
Josh Poimboeuf, Peter Zijlstra, Miguel Ojeda, Boqun Feng,
Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Alice Ryhl, Trevor Gross, Danilo Krummrich, Daniel Almeida,
Tamir Duberstein, Alexandre Courbot, Onur Özkan,
Jonathan Corbet, Randy Dunlap
Cc: linux-kbuild, linux-kernel, llvm, linux-riscv, linux-arch,
linux-arm-kernel, linux-efi, rust-for-linux, linux-doc,
Jens Axboe, Lorenzo Stoakes (ARM)
Module finalisation on allmodconfig builds consists of a large number of
very short-lived jobs, and the make job dispatcher cannot possibly dispatch
jobs fast enough.
For allmodconfig x86-64 this can be on the order of ~22,000 jobs of a few
milliseconds in duration each.
However per-job cost grows with the variables the instance holds, here the
savedcmd_* of every .mod.o and .ko read back from the .cmd files, since it
walks them all to build each child's environment.
This makes module finalisation very inefficient when large numbers of
modules are being built.
Fix this by splitting modules.order into chunks of 128 at a time, run in
parallel.
Each instance holds only its own modules' variables and the top-level one
reads no per-module .cmd files at all, the same rules serve both levels,
and an instance is told its chunk with modfinal-first=<index>.
"make modules" with every *.mod.o and *.ko deleted goes from 28.9s to 15.9s
with clang 22. No-op "make modules" goes from 5.6s to 4.8s, as checking the
22,000 targets is spread over the chunks too.
Whole build, 128-thread Threadripper 9980X, best of N runs:
before after delta
-------------------------------
x86 allmodconfig, no-op make, gcc 1.9s 1.2s -0.74s (-39%)
x86 allmodconfig, no-op make, clang 2.4s 1.6s -0.77s (-32%)
x86 allmodconfig, clean, gcc 304.5s 291.4s -13.1s (-4%)
x86 allmodconfig, clean, clang 301.7s 297.0s -4.7s (-2%)
Assisted-by: LLM
Signed-off-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
---
scripts/Makefile.modfinal | 26 +++++++++++++++++++++++++-
scripts/mod/sumversion.c | 4 ++--
2 files changed, 27 insertions(+), 3 deletions(-)
diff --git a/scripts/Makefile.modfinal b/scripts/Makefile.modfinal
index 75e9effdf02c..858fa798090b 100644
--- a/scripts/Makefile.modfinal
+++ b/scripts/Makefile.modfinal
@@ -13,9 +13,30 @@ include $(srctree)/scripts/Makefile.lib
# find all modules listed in modules.order
modules := $(call read-file, modules.order)
+modfinal-chunk-size := 128
+
+ifdef modfinal-first
+
+# this instance handles the chunk of modules.order starting at $(modfinal-first)
+modules := $(wordlist $(modfinal-first), $(words $(modules)), $(modules))
+modules := $(wordlist 1, $(modfinal-chunk-size), $(modules))
+
__modfinal: $(modules:%.o=%.ko)
@:
+else
+
+modfinal-chunks := $(addprefix chunk-, $(shell seq 1 $(modfinal-chunk-size) $(words $(modules))))
+
+PHONY += $(modfinal-chunks)
+$(modfinal-chunks): .module-common.o
+ $(Q)$(MAKE) -f $(srctree)/scripts/Makefile.modfinal modfinal-first=$(@:chunk-%=%)
+
+__modfinal: $(modfinal-chunks)
+ @:
+
+endif
+
# modname and part-of-module are set to make c_flags define proper module flags
modname = $(notdir $(@:.mod.o=))
part-of-module = y
@@ -58,7 +79,10 @@ ifdef CONFIG_DEBUG_INFO_BTF_MODULES
endif
+$(call cmd,check_tracepoint)
-targets += $(modules:%.o=%.ko) $(modules:%.o=%.mod.o) .module-common.o
+targets += .module-common.o
+ifdef modfinal-first
+targets += $(modules:%.o=%.ko) $(modules:%.o=%.mod.o)
+endif
# Add FORCE to the prerequisites of a target to force it to be always rebuilt.
# ---------------------------------------------------------------------------
diff --git a/scripts/mod/sumversion.c b/scripts/mod/sumversion.c
index 2cbadd3cd97d..5501d6aa0bea 100644
--- a/scripts/mod/sumversion.c
+++ b/scripts/mod/sumversion.c
@@ -249,7 +249,7 @@ static int parse_comment(const char *file, unsigned long len)
/* FIXME: Handle .s files differently (eg. # starts comments) --RR */
static bool stop_char[256];
-static void init_stop_chars(void)
+static void sumversion_init(void)
{
static bool done;
int chr;
@@ -274,7 +274,7 @@ static int parse_file(const char *fname, struct md4_ctx *md)
len = strlen(file);
if (!len)
goto out_file;
- init_stop_chars();
+ sumversion_init();
buf = xmalloc(len); /* File output buffer. */
for (i = 0; i < len; i++) {
--
2.55.0
^ permalink raw reply [flat|nested] 73+ messages in thread* Re: [PATCH 16/23] kbuild: batch module finalisation
2026-09-08 20:55 ` [PATCH 16/23] kbuild: batch module finalisation Lorenzo Stoakes (ARM)
@ 2026-09-10 15:48 ` Nicolas Schier
2026-09-11 10:23 ` Lorenzo Stoakes (ARM)
0 siblings, 1 reply; 73+ messages in thread
From: Nicolas Schier @ 2026-09-10 15:48 UTC (permalink / raw)
To: Lorenzo Stoakes (ARM)
Cc: Linus Torvalds, Nathan Chancellor, Nick Desaulniers,
Bill Wendling, Justin Stitt, Masahiro Yamada, Alexey Gladkov,
Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin, Paul Walmsley, Palmer Dabbelt, Albert Ou,
Alexandre Ghiti, Arnd Bergmann, Catalin Marinas, Will Deacon,
Mark Rutland, Ard Biesheuvel, Ilias Apalodimas, Josh Poimboeuf,
Peter Zijlstra, Miguel Ojeda, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, Danilo Krummrich, Daniel Almeida, Tamir Duberstein,
Alexandre Courbot, Onur Özkan, Jonathan Corbet,
Randy Dunlap, linux-kbuild, linux-kernel, llvm, linux-riscv,
linux-arch, linux-arm-kernel, linux-efi, rust-for-linux,
linux-doc, Jens Axboe
[-- Attachment #1: Type: text/plain, Size: 3109 bytes --]
On Tue, Sep 08, 2026 at 09:55:16PM +0100, Lorenzo Stoakes (ARM) wrote:
> Module finalisation on allmodconfig builds consists of a large number of
> very short-lived jobs, and the make job dispatcher cannot possibly dispatch
> jobs fast enough.
>
> For allmodconfig x86-64 this can be on the order of ~22,000 jobs of a few
> milliseconds in duration each.
>
> However per-job cost grows with the variables the instance holds, here the
> savedcmd_* of every .mod.o and .ko read back from the .cmd files, since it
> walks them all to build each child's environment.
>
> This makes module finalisation very inefficient when large numbers of
> modules are being built.
>
> Fix this by splitting modules.order into chunks of 128 at a time, run in
> parallel.
>
> Each instance holds only its own modules' variables and the top-level one
> reads no per-module .cmd files at all, the same rules serve both levels,
> and an instance is told its chunk with modfinal-first=<index>.
>
> "make modules" with every *.mod.o and *.ko deleted goes from 28.9s to 15.9s
> with clang 22. No-op "make modules" goes from 5.6s to 4.8s, as checking the
> 22,000 targets is spread over the chunks too.
>
> Whole build, 128-thread Threadripper 9980X, best of N runs:
>
> before after delta
> -------------------------------
> x86 allmodconfig, no-op make, gcc 1.9s 1.2s -0.74s (-39%)
> x86 allmodconfig, no-op make, clang 2.4s 1.6s -0.77s (-32%)
> x86 allmodconfig, clean, gcc 304.5s 291.4s -13.1s (-4%)
> x86 allmodconfig, clean, clang 301.7s 297.0s -4.7s (-2%)
>
> Assisted-by: LLM
> Signed-off-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
> ---
> scripts/Makefile.modfinal | 26 +++++++++++++++++++++++++-
> scripts/mod/sumversion.c | 4 ++--
> 2 files changed, 27 insertions(+), 3 deletions(-)
>
> diff --git a/scripts/Makefile.modfinal b/scripts/Makefile.modfinal
> index 75e9effdf02c..858fa798090b 100644
> --- a/scripts/Makefile.modfinal
> +++ b/scripts/Makefile.modfinal
[...]
> diff --git a/scripts/mod/sumversion.c b/scripts/mod/sumversion.c
> index 2cbadd3cd97d..5501d6aa0bea 100644
> --- a/scripts/mod/sumversion.c
> +++ b/scripts/mod/sumversion.c
> @@ -249,7 +249,7 @@ static int parse_comment(const char *file, unsigned long len)
> /* FIXME: Handle .s files differently (eg. # starts comments) --RR */
> static bool stop_char[256];
>
> -static void init_stop_chars(void)
> +static void sumversion_init(void)
> {
> static bool done;
> int chr;
> @@ -274,7 +274,7 @@ static int parse_file(const char *fname, struct md4_ctx *md)
> len = strlen(file);
> if (!len)
> goto out_file;
> - init_stop_chars();
> + sumversion_init();
> buf = xmalloc(len); /* File output buffer. */
>
> for (i = 0; i < len; i++) {
>
> --
> 2.55.0
>
(I have only skimmed through, not yet reviewed this patch.)
The change in sumversion.c seems to be wrong here.
--
Nicolas
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 73+ messages in thread* Re: [PATCH 16/23] kbuild: batch module finalisation
2026-09-10 15:48 ` Nicolas Schier
@ 2026-09-11 10:23 ` Lorenzo Stoakes (ARM)
0 siblings, 0 replies; 73+ messages in thread
From: Lorenzo Stoakes (ARM) @ 2026-09-11 10:23 UTC (permalink / raw)
To: Nicolas Schier
Cc: Linus Torvalds, Nathan Chancellor, Nick Desaulniers,
Bill Wendling, Justin Stitt, Masahiro Yamada, Alexey Gladkov,
Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin, Paul Walmsley, Palmer Dabbelt, Albert Ou,
Alexandre Ghiti, Arnd Bergmann, Catalin Marinas, Will Deacon,
Mark Rutland, Ard Biesheuvel, Ilias Apalodimas, Josh Poimboeuf,
Peter Zijlstra, Miguel Ojeda, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, Danilo Krummrich, Daniel Almeida, Tamir Duberstein,
Alexandre Courbot, Onur Özkan, Jonathan Corbet,
Randy Dunlap, linux-kbuild, linux-kernel, llvm, linux-riscv,
linux-arch, linux-arm-kernel, linux-efi, rust-for-linux,
linux-doc, Jens Axboe
On Thu, Sep 10, 2026 at 05:48:53PM +0200, Nicolas Schier wrote:
> On Tue, Sep 08, 2026 at 09:55:16PM +0100, Lorenzo Stoakes (ARM) wrote:
> > Module finalisation on allmodconfig builds consists of a large number of
> > very short-lived jobs, and the make job dispatcher cannot possibly dispatch
> > jobs fast enough.
> >
> > For allmodconfig x86-64 this can be on the order of ~22,000 jobs of a few
> > milliseconds in duration each.
> >
> > However per-job cost grows with the variables the instance holds, here the
> > savedcmd_* of every .mod.o and .ko read back from the .cmd files, since it
> > walks them all to build each child's environment.
> >
> > This makes module finalisation very inefficient when large numbers of
> > modules are being built.
> >
> > Fix this by splitting modules.order into chunks of 128 at a time, run in
> > parallel.
> >
> > Each instance holds only its own modules' variables and the top-level one
> > reads no per-module .cmd files at all, the same rules serve both levels,
> > and an instance is told its chunk with modfinal-first=<index>.
> >
> > "make modules" with every *.mod.o and *.ko deleted goes from 28.9s to 15.9s
> > with clang 22. No-op "make modules" goes from 5.6s to 4.8s, as checking the
> > 22,000 targets is spread over the chunks too.
> >
> > Whole build, 128-thread Threadripper 9980X, best of N runs:
> >
> > before after delta
> > -------------------------------
> > x86 allmodconfig, no-op make, gcc 1.9s 1.2s -0.74s (-39%)
> > x86 allmodconfig, no-op make, clang 2.4s 1.6s -0.77s (-32%)
> > x86 allmodconfig, clean, gcc 304.5s 291.4s -13.1s (-4%)
> > x86 allmodconfig, clean, clang 301.7s 297.0s -4.7s (-2%)
> >
> > Assisted-by: LLM
> > Signed-off-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
> > ---
> > scripts/Makefile.modfinal | 26 +++++++++++++++++++++++++-
> > scripts/mod/sumversion.c | 4 ++--
> > 2 files changed, 27 insertions(+), 3 deletions(-)
> >
> > diff --git a/scripts/Makefile.modfinal b/scripts/Makefile.modfinal
> > index 75e9effdf02c..858fa798090b 100644
> > --- a/scripts/Makefile.modfinal
> > +++ b/scripts/Makefile.modfinal
>
> [...]
> > diff --git a/scripts/mod/sumversion.c b/scripts/mod/sumversion.c
> > index 2cbadd3cd97d..5501d6aa0bea 100644
> > --- a/scripts/mod/sumversion.c
> > +++ b/scripts/mod/sumversion.c
> > @@ -249,7 +249,7 @@ static int parse_comment(const char *file, unsigned long len)
> > /* FIXME: Handle .s files differently (eg. # starts comments) --RR */
> > static bool stop_char[256];
> >
> > -static void init_stop_chars(void)
> > +static void sumversion_init(void)
> > {
> > static bool done;
> > int chr;
> > @@ -274,7 +274,7 @@ static int parse_file(const char *fname, struct md4_ctx *md)
> > len = strlen(file);
> > if (!len)
> > goto out_file;
> > - init_stop_chars();
> > + sumversion_init();
> > buf = xmalloc(len); /* File output buffer. */
> >
> > for (i = 0; i < len; i++) {
> >
> > --
> > 2.55.0
> >
>
> (I have only skimmed through, not yet reviewed this patch.)
>
> The change in sumversion.c seems to be wrong here.
Ugh yeah this is a merge conflict artifact that I thought I had fixed but
clearly not :) Will fix up on respin.
>
> --
> Nicolas
--
Cheers, Lorenzo
^ permalink raw reply [flat|nested] 73+ messages in thread
* [PATCH 17/23] modpost: perform srcversion hashing in parallel
2026-09-08 20:55 [PATCH 00/23] kbuild: significantly speed up kernel builds Lorenzo Stoakes (ARM)
` (15 preceding siblings ...)
2026-09-08 20:55 ` [PATCH 16/23] kbuild: batch module finalisation Lorenzo Stoakes (ARM)
@ 2026-09-08 20:55 ` Lorenzo Stoakes (ARM)
2026-09-10 10:32 ` David Laight
2026-09-08 20:55 ` [PATCH 18/23] objtool: cache relocations and function dead end state, do less work Lorenzo Stoakes (ARM)
` (9 subsequent siblings)
26 siblings, 1 reply; 73+ messages in thread
From: Lorenzo Stoakes (ARM) @ 2026-09-08 20:55 UTC (permalink / raw)
To: Linus Torvalds, Nathan Chancellor, Nicolas Schier,
Nick Desaulniers, Bill Wendling, Justin Stitt, Masahiro Yamada,
Alexey Gladkov, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, x86, H. Peter Anvin, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Alexandre Ghiti, Arnd Bergmann, Catalin Marinas,
Will Deacon, Mark Rutland, Ard Biesheuvel, Ilias Apalodimas,
Josh Poimboeuf, Peter Zijlstra, Miguel Ojeda, Boqun Feng,
Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Alice Ryhl, Trevor Gross, Danilo Krummrich, Daniel Almeida,
Tamir Duberstein, Alexandre Courbot, Onur Özkan,
Jonathan Corbet, Randy Dunlap
Cc: linux-kbuild, linux-kernel, llvm, linux-riscv, linux-arch,
linux-arm-kernel, linux-efi, rust-for-linux, linux-doc,
Jens Axboe, Lorenzo Stoakes (ARM)
modpost does a lot of single-threaded work hashing files from each object's
.cmd file.
This makes the build slower than it needs to be, so do this work in
parallel.
This is egregious for allmodconfig builds - for instance x86-64 can end up
opening 200,000 files individually and hashing them all serially.
Parallelise this operation by maintaining a thread pool for the hashing
work.
Combined with the per-file hashing commit this cuts modpost's run time
nearly in half for an allmodconfig build.
Module.symvers and every *.mod.S are byte for byte the same.
modpost is on the serial tail of every allmodconfig build, however
defconfig does not set CONFIG_MODULE_SRCVERSION_ALL and is unchanged.
Whole build, 128-thread Threadripper 9980X, best of N runs:
before after delta
-------------------------------
x86 allmodconfig, touch mm/vma.c, gcc 33.4s 30.2s -3.2s (-10%)
x86 allmodconfig, touch mm/vma.c, clang 31.1s 28.1s -3.0s (-10%)
Assisted-by: LLM
Signed-off-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
---
scripts/mod/Makefile | 1 +
scripts/mod/modpost.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++--
scripts/mod/modpost.h | 2 ++
scripts/mod/sumversion.c | 3 +-
4 files changed, 74 insertions(+), 4 deletions(-)
diff --git a/scripts/mod/Makefile b/scripts/mod/Makefile
index fbd5099e0441..fdd486184f9c 100644
--- a/scripts/mod/Makefile
+++ b/scripts/mod/Makefile
@@ -5,6 +5,7 @@ hostprogs-always-y += modpost mk_elfconfig
always-y += empty.o
modpost-objs := modpost.o file2alias.o sumversion.o symsearch.o
+HOSTLDLIBS_modpost := -lpthread
devicetable-offsets-file := devicetable-offsets.h
diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c
index 550ccd753ed8..882169e51851 100644
--- a/scripts/mod/modpost.c
+++ b/scripts/mod/modpost.c
@@ -16,6 +16,7 @@
#include <fnmatch.h>
#include <stdio.h>
#include <ctype.h>
+#include <pthread.h>
#include <string.h>
#include <limits.h>
#include <stdbool.h>
@@ -1717,8 +1718,7 @@ static void read_symbols(const char *modname)
if (!mod->is_vmlinux) {
version = get_modinfo(&info, "version");
if (version || all_versions)
- get_src_version(mod->name, mod->srcversion,
- sizeof(mod->srcversion) - 1);
+ mod->need_srcversion = true;
}
parse_elf_finish(&info);
@@ -1736,6 +1736,72 @@ static void read_symbols(const char *modname)
}
}
+static struct module **srcversion_mods;
+static unsigned int nr_srcversion_mods, next_srcversion_mod;
+
+static bool get_next_src_version(void)
+{
+ struct module *mod;
+ unsigned int idx;
+
+ idx = __sync_fetch_and_add(&next_srcversion_mod, 1);
+ if (idx >= nr_srcversion_mods)
+ return false;
+ mod = srcversion_mods[idx];
+
+ get_src_version(mod->name, mod->srcversion,
+ sizeof(mod->srcversion) - 1);
+ return true;
+}
+
+static void *srcversion_worker(void *arg)
+{
+ while (get_next_src_version())
+ ;
+
+ return NULL;
+}
+
+static void hash_srcversions(void)
+{
+ unsigned int i = 0;
+ struct module *mod;
+ pthread_t *threads;
+ long nr_threads;
+
+ list_for_each_entry(mod, &modules, list)
+ if (mod->need_srcversion)
+ nr_srcversion_mods++;
+
+ if (!nr_srcversion_mods)
+ return;
+
+ srcversion_mods = xmalloc(nr_srcversion_mods * sizeof(*srcversion_mods));
+
+ list_for_each_entry(mod, &modules, list)
+ if (mod->need_srcversion)
+ srcversion_mods[i++] = mod;
+
+ nr_threads = sysconf(_SC_NPROCESSORS_ONLN);
+ nr_threads = nr_threads < 1 ? 1 : nr_threads; /* On error assume 1. */
+ if (nr_threads > nr_srcversion_mods)
+ nr_threads = nr_srcversion_mods;
+
+ sumversion_init();
+ threads = xmalloc(nr_threads * sizeof(*threads));
+ for (i = 0; i < nr_threads; i++) {
+ if (pthread_create(&threads[i], NULL, srcversion_worker, NULL)) {
+ perror("pthread_create");
+ exit(1);
+ }
+ }
+ for (i = 0; i < nr_threads; i++)
+ pthread_join(threads[i], NULL);
+
+ free(threads);
+ free(srcversion_mods);
+}
+
static void read_symbols_from_files(const char *filename)
{
FILE *in = stdin;
@@ -2729,6 +2795,8 @@ int main(int argc, char **argv)
if (files_source)
read_symbols_from_files(files_source);
+ hash_srcversions();
+
list_for_each_entry(mod, &modules, list) {
keep_no_trim_symbols(mod);
diff --git a/scripts/mod/modpost.h b/scripts/mod/modpost.h
index d5f6d82837d5..10d5f8f2f293 100644
--- a/scripts/mod/modpost.h
+++ b/scripts/mod/modpost.h
@@ -127,6 +127,7 @@ struct module {
bool has_init;
bool has_cleanup;
char srcversion[25];
+ bool need_srcversion;
// Missing namespace dependencies
struct list_head missing_namespaces;
// Actual imported namespaces
@@ -213,6 +214,7 @@ void handle_moddevtable(struct module *mod, struct elf_info *info,
Elf_Sym *sym, const char *symname);
/* sumversion.c */
+void sumversion_init(void);
void get_src_version(const char *modname, char sum[], unsigned sumlen);
/* from modpost.c */
diff --git a/scripts/mod/sumversion.c b/scripts/mod/sumversion.c
index 5501d6aa0bea..4521b92ef868 100644
--- a/scripts/mod/sumversion.c
+++ b/scripts/mod/sumversion.c
@@ -249,7 +249,7 @@ static int parse_comment(const char *file, unsigned long len)
/* FIXME: Handle .s files differently (eg. # starts comments) --RR */
static bool stop_char[256];
-static void sumversion_init(void)
+void sumversion_init(void)
{
static bool done;
int chr;
@@ -402,7 +402,6 @@ static int parse_source_files(const char *objfile, struct md4_ctx *md)
line, strerror(errno));
goto out_file;
}
-
}
}
--
2.55.0
^ permalink raw reply [flat|nested] 73+ messages in thread* Re: [PATCH 17/23] modpost: perform srcversion hashing in parallel
2026-09-08 20:55 ` [PATCH 17/23] modpost: perform srcversion hashing in parallel Lorenzo Stoakes (ARM)
@ 2026-09-10 10:32 ` David Laight
2026-09-11 10:49 ` Lorenzo Stoakes (ARM)
0 siblings, 1 reply; 73+ messages in thread
From: David Laight @ 2026-09-10 10:32 UTC (permalink / raw)
To: Lorenzo Stoakes (ARM)
Cc: Linus Torvalds, Nathan Chancellor, Nicolas Schier,
Nick Desaulniers, Bill Wendling, Justin Stitt, Masahiro Yamada,
Alexey Gladkov, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, x86, H. Peter Anvin, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Alexandre Ghiti, Arnd Bergmann, Catalin Marinas,
Will Deacon, Mark Rutland, Ard Biesheuvel, Ilias Apalodimas,
Josh Poimboeuf, Peter Zijlstra, Miguel Ojeda, Boqun Feng,
Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Alice Ryhl, Trevor Gross, Danilo Krummrich, Daniel Almeida,
Tamir Duberstein, Alexandre Courbot, Onur Özkan,
Jonathan Corbet, Randy Dunlap, linux-kbuild, linux-kernel, llvm,
linux-riscv, linux-arch, linux-arm-kernel, linux-efi,
rust-for-linux, linux-doc, Jens Axboe
On Tue, 08 Sep 2026 21:55:17 +0100
"Lorenzo Stoakes (ARM)" <ljs@kernel.org> wrote:
> modpost does a lot of single-threaded work hashing files from each object's
> .cmd file.
>
> This makes the build slower than it needs to be, so do this work in
> parallel.
>
> This is egregious for allmodconfig builds - for instance x86-64 can end up
> opening 200,000 files individually and hashing them all serially.
>
> Parallelise this operation by maintaining a thread pool for the hashing
> work.
>
> Combined with the per-file hashing commit this cuts modpost's run time
> nearly in half for an allmodconfig build.
>
> Module.symvers and every *.mod.S are byte for byte the same.
>
> modpost is on the serial tail of every allmodconfig build, however
> defconfig does not set CONFIG_MODULE_SRCVERSION_ALL and is unchanged.
>
> Whole build, 128-thread Threadripper 9980X, best of N runs:
>
> before after delta
> -------------------------------
> x86 allmodconfig, touch mm/vma.c, gcc 33.4s 30.2s -3.2s (-10%)
> x86 allmodconfig, touch mm/vma.c, clang 31.1s 28.1s -3.0s (-10%)
>
> Assisted-by: LLM
> Signed-off-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
> ---
> scripts/mod/Makefile | 1 +
> scripts/mod/modpost.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++--
> scripts/mod/modpost.h | 2 ++
> scripts/mod/sumversion.c | 3 +-
> 4 files changed, 74 insertions(+), 4 deletions(-)
>
> diff --git a/scripts/mod/Makefile b/scripts/mod/Makefile
> index fbd5099e0441..fdd486184f9c 100644
> --- a/scripts/mod/Makefile
> +++ b/scripts/mod/Makefile
> @@ -5,6 +5,7 @@ hostprogs-always-y += modpost mk_elfconfig
> always-y += empty.o
>
> modpost-objs := modpost.o file2alias.o sumversion.o symsearch.o
> +HOSTLDLIBS_modpost := -lpthread
>
> devicetable-offsets-file := devicetable-offsets.h
>
> diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c
> index 550ccd753ed8..882169e51851 100644
> --- a/scripts/mod/modpost.c
> +++ b/scripts/mod/modpost.c
> @@ -16,6 +16,7 @@
> #include <fnmatch.h>
> #include <stdio.h>
> #include <ctype.h>
> +#include <pthread.h>
> #include <string.h>
> #include <limits.h>
> #include <stdbool.h>
> @@ -1717,8 +1718,7 @@ static void read_symbols(const char *modname)
> if (!mod->is_vmlinux) {
> version = get_modinfo(&info, "version");
> if (version || all_versions)
> - get_src_version(mod->name, mod->srcversion,
> - sizeof(mod->srcversion) - 1);
> + mod->need_srcversion = true;
> }
>
> parse_elf_finish(&info);
> @@ -1736,6 +1736,72 @@ static void read_symbols(const char *modname)
> }
> }
>
> +static struct module **srcversion_mods;
> +static unsigned int nr_srcversion_mods, next_srcversion_mod;
> +
> +static bool get_next_src_version(void)
> +{
> + struct module *mod;
> + unsigned int idx;
> +
> + idx = __sync_fetch_and_add(&next_srcversion_mod, 1);
When I've done this is the past, adding 'a few' items was faster
because it reduced the contention on the counter.
(Although there were probably some very cheap 'actions'.)
> + if (idx >= nr_srcversion_mods)
> + return false;
> + mod = srcversion_mods[idx];
> +
> + get_src_version(mod->name, mod->srcversion,
> + sizeof(mod->srcversion) - 1);
> + return true;
> +}
> +
> +static void *srcversion_worker(void *arg)
> +{
> + while (get_next_src_version())
> + ;
I don't think the extra function call helps.
> +
> + return NULL;
> +}
> +
> +static void hash_srcversions(void)
> +{
> + unsigned int i = 0;
> + struct module *mod;
> + pthread_t *threads;
> + long nr_threads;
> +
> + list_for_each_entry(mod, &modules, list)
> + if (mod->need_srcversion)
> + nr_srcversion_mods++;
> +
> + if (!nr_srcversion_mods)
> + return;
> +
> + srcversion_mods = xmalloc(nr_srcversion_mods * sizeof(*srcversion_mods));
> +
> + list_for_each_entry(mod, &modules, list)
> + if (mod->need_srcversion)
> + srcversion_mods[i++] = mod;
> +
> + nr_threads = sysconf(_SC_NPROCESSORS_ONLN);
One thread per cpu is probably a few to many.
It might be worth adding a command line parameter for the 'max threads'.
Possibly with -n meaning 'all but n'.
> + nr_threads = nr_threads < 1 ? 1 : nr_threads; /* On error assume 1. */
> + if (nr_threads > nr_srcversion_mods)
> + nr_threads = nr_srcversion_mods;
> +
> + sumversion_init();
> + threads = xmalloc(nr_threads * sizeof(*threads));
> + for (i = 0; i < nr_threads; i++) {
> + if (pthread_create(&threads[i], NULL, srcversion_worker, NULL)) {
> + perror("pthread_create");
> + exit(1);
> + }
> + }
The main code can call srcversion_worker() here.
David
> + for (i = 0; i < nr_threads; i++)
> + pthread_join(threads[i], NULL);
> +
> + free(threads);
> + free(srcversion_mods);
> +}
> +
> static void read_symbols_from_files(const char *filename)
> {
> FILE *in = stdin;
> @@ -2729,6 +2795,8 @@ int main(int argc, char **argv)
> if (files_source)
> read_symbols_from_files(files_source);
>
> + hash_srcversions();
> +
> list_for_each_entry(mod, &modules, list) {
> keep_no_trim_symbols(mod);
>
> diff --git a/scripts/mod/modpost.h b/scripts/mod/modpost.h
> index d5f6d82837d5..10d5f8f2f293 100644
> --- a/scripts/mod/modpost.h
> +++ b/scripts/mod/modpost.h
> @@ -127,6 +127,7 @@ struct module {
> bool has_init;
> bool has_cleanup;
> char srcversion[25];
> + bool need_srcversion;
> // Missing namespace dependencies
> struct list_head missing_namespaces;
> // Actual imported namespaces
> @@ -213,6 +214,7 @@ void handle_moddevtable(struct module *mod, struct elf_info *info,
> Elf_Sym *sym, const char *symname);
>
> /* sumversion.c */
> +void sumversion_init(void);
> void get_src_version(const char *modname, char sum[], unsigned sumlen);
>
> /* from modpost.c */
> diff --git a/scripts/mod/sumversion.c b/scripts/mod/sumversion.c
> index 5501d6aa0bea..4521b92ef868 100644
> --- a/scripts/mod/sumversion.c
> +++ b/scripts/mod/sumversion.c
> @@ -249,7 +249,7 @@ static int parse_comment(const char *file, unsigned long len)
> /* FIXME: Handle .s files differently (eg. # starts comments) --RR */
> static bool stop_char[256];
>
> -static void sumversion_init(void)
> +void sumversion_init(void)
> {
> static bool done;
> int chr;
> @@ -402,7 +402,6 @@ static int parse_source_files(const char *objfile, struct md4_ctx *md)
> line, strerror(errno));
> goto out_file;
> }
> -
> }
>
> }
>
^ permalink raw reply [flat|nested] 73+ messages in thread* Re: [PATCH 17/23] modpost: perform srcversion hashing in parallel
2026-09-10 10:32 ` David Laight
@ 2026-09-11 10:49 ` Lorenzo Stoakes (ARM)
0 siblings, 0 replies; 73+ messages in thread
From: Lorenzo Stoakes (ARM) @ 2026-09-11 10:49 UTC (permalink / raw)
To: David Laight
Cc: Linus Torvalds, Nathan Chancellor, Nicolas Schier,
Nick Desaulniers, Bill Wendling, Justin Stitt, Masahiro Yamada,
Alexey Gladkov, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, x86, H. Peter Anvin, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Alexandre Ghiti, Arnd Bergmann, Catalin Marinas,
Will Deacon, Mark Rutland, Ard Biesheuvel, Ilias Apalodimas,
Josh Poimboeuf, Peter Zijlstra, Miguel Ojeda, Boqun Feng,
Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Alice Ryhl, Trevor Gross, Danilo Krummrich, Daniel Almeida,
Tamir Duberstein, Alexandre Courbot, Onur Özkan,
Jonathan Corbet, Randy Dunlap, linux-kbuild, linux-kernel, llvm,
linux-riscv, linux-arch, linux-arm-kernel, linux-efi,
rust-for-linux, linux-doc, Jens Axboe
I'm likely going to be dropping this patch from the series:
https://lore.kernel.org/linux-modules/20260731-module_ver_remove-v2-0-c9163858f382@linuxfoundation.org/
I don't really agree with the points below but not really worth getting into if
the patch is going away :)
On Thu, Sep 10, 2026 at 11:32:59AM +0100, David Laight wrote:
> On Tue, 08 Sep 2026 21:55:17 +0100
> "Lorenzo Stoakes (ARM)" <ljs@kernel.org> wrote:
>
> > modpost does a lot of single-threaded work hashing files from each object's
> > .cmd file.
> >
> > This makes the build slower than it needs to be, so do this work in
> > parallel.
> >
> > This is egregious for allmodconfig builds - for instance x86-64 can end up
> > opening 200,000 files individually and hashing them all serially.
> >
> > Parallelise this operation by maintaining a thread pool for the hashing
> > work.
> >
> > Combined with the per-file hashing commit this cuts modpost's run time
> > nearly in half for an allmodconfig build.
> >
> > Module.symvers and every *.mod.S are byte for byte the same.
> >
> > modpost is on the serial tail of every allmodconfig build, however
> > defconfig does not set CONFIG_MODULE_SRCVERSION_ALL and is unchanged.
> >
> > Whole build, 128-thread Threadripper 9980X, best of N runs:
> >
> > before after delta
> > -------------------------------
> > x86 allmodconfig, touch mm/vma.c, gcc 33.4s 30.2s -3.2s (-10%)
> > x86 allmodconfig, touch mm/vma.c, clang 31.1s 28.1s -3.0s (-10%)
> >
> > Assisted-by: LLM
> > Signed-off-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
> > ---
> > scripts/mod/Makefile | 1 +
> > scripts/mod/modpost.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++--
> > scripts/mod/modpost.h | 2 ++
> > scripts/mod/sumversion.c | 3 +-
> > 4 files changed, 74 insertions(+), 4 deletions(-)
> >
> > diff --git a/scripts/mod/Makefile b/scripts/mod/Makefile
> > index fbd5099e0441..fdd486184f9c 100644
> > --- a/scripts/mod/Makefile
> > +++ b/scripts/mod/Makefile
> > @@ -5,6 +5,7 @@ hostprogs-always-y += modpost mk_elfconfig
> > always-y += empty.o
> >
> > modpost-objs := modpost.o file2alias.o sumversion.o symsearch.o
> > +HOSTLDLIBS_modpost := -lpthread
> >
> > devicetable-offsets-file := devicetable-offsets.h
> >
> > diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c
> > index 550ccd753ed8..882169e51851 100644
> > --- a/scripts/mod/modpost.c
> > +++ b/scripts/mod/modpost.c
> > @@ -16,6 +16,7 @@
> > #include <fnmatch.h>
> > #include <stdio.h>
> > #include <ctype.h>
> > +#include <pthread.h>
> > #include <string.h>
> > #include <limits.h>
> > #include <stdbool.h>
> > @@ -1717,8 +1718,7 @@ static void read_symbols(const char *modname)
> > if (!mod->is_vmlinux) {
> > version = get_modinfo(&info, "version");
> > if (version || all_versions)
> > - get_src_version(mod->name, mod->srcversion,
> > - sizeof(mod->srcversion) - 1);
> > + mod->need_srcversion = true;
> > }
> >
> > parse_elf_finish(&info);
> > @@ -1736,6 +1736,72 @@ static void read_symbols(const char *modname)
> > }
> > }
> >
> > +static struct module **srcversion_mods;
> > +static unsigned int nr_srcversion_mods, next_srcversion_mod;
> > +
> > +static bool get_next_src_version(void)
> > +{
> > + struct module *mod;
> > + unsigned int idx;
> > +
> > + idx = __sync_fetch_and_add(&next_srcversion_mod, 1);
>
> When I've done this is the past, adding 'a few' items was faster
> because it reduced the contention on the counter.
> (Although there were probably some very cheap 'actions'.)
>
> > + if (idx >= nr_srcversion_mods)
> > + return false;
> > + mod = srcversion_mods[idx];
> > +
> > + get_src_version(mod->name, mod->srcversion,
> > + sizeof(mod->srcversion) - 1);
> > + return true;
> > +}
> > +
> > +static void *srcversion_worker(void *arg)
> > +{
> > + while (get_next_src_version())
> > + ;
>
> I don't think the extra function call helps.
>
> > +
> > + return NULL;
> > +}
> > +
> > +static void hash_srcversions(void)
> > +{
> > + unsigned int i = 0;
> > + struct module *mod;
> > + pthread_t *threads;
> > + long nr_threads;
> > +
> > + list_for_each_entry(mod, &modules, list)
> > + if (mod->need_srcversion)
> > + nr_srcversion_mods++;
> > +
> > + if (!nr_srcversion_mods)
> > + return;
> > +
> > + srcversion_mods = xmalloc(nr_srcversion_mods * sizeof(*srcversion_mods));
> > +
> > + list_for_each_entry(mod, &modules, list)
> > + if (mod->need_srcversion)
> > + srcversion_mods[i++] = mod;
> > +
> > + nr_threads = sysconf(_SC_NPROCESSORS_ONLN);
>
> One thread per cpu is probably a few to many.
> It might be worth adding a command line parameter for the 'max threads'.
> Possibly with -n meaning 'all but n'.
>
> > + nr_threads = nr_threads < 1 ? 1 : nr_threads; /* On error assume 1. */
> > + if (nr_threads > nr_srcversion_mods)
> > + nr_threads = nr_srcversion_mods;
> > +
> > + sumversion_init();
> > + threads = xmalloc(nr_threads * sizeof(*threads));
> > + for (i = 0; i < nr_threads; i++) {
> > + if (pthread_create(&threads[i], NULL, srcversion_worker, NULL)) {
> > + perror("pthread_create");
> > + exit(1);
> > + }
> > + }
>
> The main code can call srcversion_worker() here.
>
> David
>
> > + for (i = 0; i < nr_threads; i++)
> > + pthread_join(threads[i], NULL);
> > +
> > + free(threads);
> > + free(srcversion_mods);
> > +}
> > +
> > static void read_symbols_from_files(const char *filename)
> > {
> > FILE *in = stdin;
> > @@ -2729,6 +2795,8 @@ int main(int argc, char **argv)
> > if (files_source)
> > read_symbols_from_files(files_source);
> >
> > + hash_srcversions();
> > +
> > list_for_each_entry(mod, &modules, list) {
> > keep_no_trim_symbols(mod);
> >
> > diff --git a/scripts/mod/modpost.h b/scripts/mod/modpost.h
> > index d5f6d82837d5..10d5f8f2f293 100644
> > --- a/scripts/mod/modpost.h
> > +++ b/scripts/mod/modpost.h
> > @@ -127,6 +127,7 @@ struct module {
> > bool has_init;
> > bool has_cleanup;
> > char srcversion[25];
> > + bool need_srcversion;
> > // Missing namespace dependencies
> > struct list_head missing_namespaces;
> > // Actual imported namespaces
> > @@ -213,6 +214,7 @@ void handle_moddevtable(struct module *mod, struct elf_info *info,
> > Elf_Sym *sym, const char *symname);
> >
> > /* sumversion.c */
> > +void sumversion_init(void);
> > void get_src_version(const char *modname, char sum[], unsigned sumlen);
> >
> > /* from modpost.c */
> > diff --git a/scripts/mod/sumversion.c b/scripts/mod/sumversion.c
> > index 5501d6aa0bea..4521b92ef868 100644
> > --- a/scripts/mod/sumversion.c
> > +++ b/scripts/mod/sumversion.c
> > @@ -249,7 +249,7 @@ static int parse_comment(const char *file, unsigned long len)
> > /* FIXME: Handle .s files differently (eg. # starts comments) --RR */
> > static bool stop_char[256];
> >
> > -static void sumversion_init(void)
> > +void sumversion_init(void)
> > {
> > static bool done;
> > int chr;
> > @@ -402,7 +402,6 @@ static int parse_source_files(const char *objfile, struct md4_ctx *md)
> > line, strerror(errno));
> > goto out_file;
> > }
> > -
> > }
> >
> > }
> >
>
--
Cheers, Lorenzo
^ permalink raw reply [flat|nested] 73+ messages in thread
* [PATCH 18/23] objtool: cache relocations and function dead end state, do less work
2026-09-08 20:55 [PATCH 00/23] kbuild: significantly speed up kernel builds Lorenzo Stoakes (ARM)
` (16 preceding siblings ...)
2026-09-08 20:55 ` [PATCH 17/23] modpost: perform srcversion hashing in parallel Lorenzo Stoakes (ARM)
@ 2026-09-08 20:55 ` Lorenzo Stoakes (ARM)
2026-09-08 20:55 ` [PATCH 19/23] objtool: decode instructions and resolve branch targets in parallel Lorenzo Stoakes (ARM)
` (8 subsequent siblings)
26 siblings, 0 replies; 73+ messages in thread
From: Lorenzo Stoakes (ARM) @ 2026-09-08 20:55 UTC (permalink / raw)
To: Linus Torvalds, Nathan Chancellor, Nicolas Schier,
Nick Desaulniers, Bill Wendling, Justin Stitt, Masahiro Yamada,
Alexey Gladkov, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, x86, H. Peter Anvin, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Alexandre Ghiti, Arnd Bergmann, Catalin Marinas,
Will Deacon, Mark Rutland, Ard Biesheuvel, Ilias Apalodimas,
Josh Poimboeuf, Peter Zijlstra, Miguel Ojeda, Boqun Feng,
Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Alice Ryhl, Trevor Gross, Danilo Krummrich, Daniel Almeida,
Tamir Duberstein, Alexandre Courbot, Onur Özkan,
Jonathan Corbet, Randy Dunlap
Cc: linux-kbuild, linux-kernel, llvm, linux-riscv, linux-arch,
linux-arm-kernel, linux-efi, rust-for-linux, linux-doc,
Jens Axboe, Lorenzo Stoakes (ARM)
Instruction relocations are looked up by destination in objtool via a hash
which is keyed on a 16-byte (OFFSET_STRIDE) window within the section being
walked.
It iterates through each 16-byte window, looking up relocations over
several passes, before moving on to the next 16-byte window, caching only
when a relocation is not found saving further lookups in this case.
Improve upon this by introducing a per-section relocation cache storing the
first relocation at or after each 64-byte window of data (an empirically
determined index range), indexed by chunk.
The lookup is implemented an array lookup and touches no shared state, so
can be used from multiple threads.
This relies upon the entries within a section being sorted, which is the
case for all sections supplied to objtool by the link step during the
kernel build.
For cases where sections are supplied out of order or grown one relocation
at a time (e.g. livepatch), fall back to using the existing hash mechanism.
DWARF sections are a special case - their relocations are never looked up
by destination at all and only need to be on their symbol's list for
elf_update_sym_relocs().
So do not index or hash DWARF relocations at all - however add a mechanism
such that if one were ever looked up, a linear scan will be used.
This is meaningful in practice as on an x86-64 kernel build with
CONFIG_DEBUG_INFO set objtool processing of vmlinux.o is dominated by DWARF
section processing.
For an allmodconfig build ~9 million relocations were hashed, and ~8.2
million of those were DWARF sections, which added overhead on cache miss
and pollution of the hash table. This is now eliminated.
The hash, when used, is read-mostly (every jump, call and memory operand)
and several relocations share a key due to the 16-byte stride, so keep the
hash sparse by scaling the hash by the number of objects to be hashed.
Another hotspot in objtool processing is determining 'dead end' functions,
i.e. functions which never return.
Implement a simple boolean per-function cache for this to avoid determining
this more than once per function.
The output of objtool before and after this change was confirmed to be
byte-for-byte identical both for x86_64 defconfig and allmodconfig with
gcc and clang.
objtool on vmlinux.o is on the serial tail of every build that links
vmlinux, no-op builds are unchanged.
Whole build, 128-thread Threadripper 9980X, best of N runs:
before after delta
-------------------------------
x86 defconfig, touch mm/vma.c, gcc 8.4s 8.1s -0.28s (-3%)
x86 defconfig, touch mm/vma.c, clang 7.5s 7.1s -0.41s (-5%)
x86 defconfig, clean, gcc 27.1s 26.8s -0.34s (-1%)
x86 defconfig, clean, clang 26.6s 26.2s -0.40s (-1%)
x86 allmodconfig, touch mm/vma.c, gcc 30.2s 28.4s -1.8s (-6%)
x86 allmodconfig, touch mm/vma.c, clang 28.1s 26.0s -2.1s (-7%)
Assisted-by: LLM
Signed-off-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
---
tools/objtool/check.c | 10 +-
tools/objtool/elf.c | 242 +++++++++++++++++++++++++++++++++---
tools/objtool/include/objtool/elf.h | 4 +
3 files changed, 238 insertions(+), 18 deletions(-)
diff --git a/tools/objtool/check.c b/tools/objtool/check.c
index 464f6c9d9ff0..2abd41cc3aaf 100644
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -305,7 +305,15 @@ static bool __dead_end_function(struct objtool_file *file, struct symbol *func,
static bool dead_end_function(struct objtool_file *file, struct symbol *func)
{
- return __dead_end_function(file, func, 0);
+ if (!func)
+ return false;
+
+ if (!func->dead_end_known) {
+ func->dead_end = __dead_end_function(file, func, 0);
+ func->dead_end_known = 1;
+ }
+
+ return func->dead_end;
}
static void init_cfi_state(struct cfi_state *cfi)
diff --git a/tools/objtool/elf.c b/tools/objtool/elf.c
index a791f4ea6ec1..13073dc72481 100644
--- a/tools/objtool/elf.c
+++ b/tools/objtool/elf.c
@@ -316,18 +316,117 @@ struct symbol *find_global_symbol_by_name(const struct elf *elf, const char *nam
return NULL;
}
-/* If there are multiple matches, return the first one in the range */
-struct reloc *find_reloc_by_dest_range(const struct elf *elf, struct section *sec,
+static bool is_dwarf_section(struct section *sec)
+{
+ return !strncmp(sec->name, ".debug_", 7);
+}
+
+/* Cache relocations at a 64 byte granularity. */
+#define RELOC_CACHE_INDEX_SHIFT 6
+
+static unsigned long reloc_cache_index(unsigned long offset)
+{
+ return offset >> RELOC_CACHE_INDEX_SHIFT;
+}
+
+static unsigned int reloc_cache_nr_windows(const struct section *rsec)
+{
+ const unsigned long size = sec_size(rsec->base);
+
+ return (size >> RELOC_CACHE_INDEX_SHIFT) + 1;
+}
+
+static int init_reloc_cache(struct section *rsec)
+{
+ const unsigned int nr_relocs = sec_num_entries(rsec);
+ const unsigned int nr_windows = reloc_cache_nr_windows(rsec);
+ unsigned int reloc_idx, next_cache_idx = 0;
+
+ rsec->reloc_cache = malloc(nr_windows * sizeof(unsigned int));
+ if (!rsec->reloc_cache) {
+ ERROR_GLIBC("malloc");
+ return -1;
+ }
+
+ /* Populate relocation indexes reloc cache index -> reloc index. */
+ for (reloc_idx = 0; reloc_idx < nr_relocs; reloc_idx++) {
+ struct reloc *reloc = &rsec->relocs[reloc_idx];
+ const unsigned long offset = reloc_offset(reloc);
+ const unsigned long cache_idx = reloc_cache_index(offset);
+
+ if (cache_idx >= nr_windows)
+ break;
+
+ while (next_cache_idx <= cache_idx)
+ rsec->reloc_cache[next_cache_idx++] = reloc_idx;
+ }
+
+ while (next_cache_idx < nr_windows)
+ rsec->reloc_cache[next_cache_idx++] = nr_relocs;
+
+ rsec->sorted = true;
+ return 0;
+}
+
+static void free_reloc_cache(struct section *rsec)
+{
+ free(rsec->reloc_cache);
+ rsec->reloc_cache = NULL;
+ rsec->sorted = false;
+}
+
+static struct reloc *find_reloc_sorted(struct section *rsec,
unsigned long offset, unsigned int len)
{
- struct reloc *reloc, *r = NULL;
- struct section *rsec;
- unsigned long o;
+ struct reloc *relocs = rsec->relocs;
+ const unsigned int nr_relocs = sec_num_entries(rsec);
+ const unsigned long cache_idx = reloc_cache_index(offset);
+ unsigned int reloc_idx, i;
- rsec = sec->rsec;
- if (!rsec)
+ if (cache_idx >= reloc_cache_nr_windows(rsec))
+ return NULL;
+
+ reloc_idx = rsec->reloc_cache[cache_idx];
+
+ /*
+ * Scan through all relocations covered by cache entry to find the
+ * first at or after offset. Relocations are sorted by offset.
+ */
+ for (i = reloc_idx; i < nr_relocs; i++) {
+ struct reloc *reloc = &relocs[i];
+ const unsigned long curr_offset = reloc_offset(reloc);
+
+ if (curr_offset >= offset)
+ break;
+
+ reloc_idx++;
+ }
+
+ /* Nothing found, or the first candidate lies beyond the range. */
+ if (reloc_idx >= nr_relocs ||
+ reloc_offset(&relocs[reloc_idx]) >= offset + len)
return NULL;
+ /* If there are duplicate entries, return the last. */
+ for (i = reloc_idx; i < nr_relocs - 1; i++) {
+ struct reloc *reloc = &relocs[i];
+ struct reloc *next_reloc = &relocs[i + 1];
+
+ if (reloc_offset(next_reloc) != reloc_offset(reloc))
+ break;
+ reloc_idx++;
+ }
+
+ return &relocs[reloc_idx];
+}
+
+/* Not indexed, so look it up in the hash. */
+static struct reloc *find_reloc_hash(const struct elf *elf, struct section *rsec,
+ unsigned long offset, unsigned int len)
+{
+ unsigned long o;
+ struct reloc *reloc, *r = NULL;
+
for_offset_range(o, offset, offset + len) {
elf_hash_for_each_possible(elf, reloc, reloc, hash,
sec_offset_hash(rsec, o)) {
@@ -347,14 +446,48 @@ struct reloc *find_reloc_by_dest_range(const struct elf *elf, struct section *se
return r;
}
-struct reloc *find_reloc_by_dest(const struct elf *elf, struct section *sec, unsigned long offset)
+/* Should never be invoked, provided as a backstop. */
+static struct reloc *find_reloc_linear(struct section *rsec,
+ unsigned long offset, unsigned int len)
{
- return find_reloc_by_dest_range(elf, sec, offset, 1);
+ struct reloc *reloc, *first = NULL;
+
+ WARN("%s: linear scan for sec %s with %u relocs at offset %lu len %u",
+ __func__, rsec->name, sec_num_entries(rsec), offset, len);
+
+ for_each_reloc(rsec, reloc) {
+ if (reloc_offset(reloc) < offset ||
+ reloc_offset(reloc) >= offset + len)
+ continue;
+
+ if (!first || reloc_offset(reloc) < reloc_offset(first))
+ first = reloc;
+ }
+
+ return first;
}
-static bool is_dwarf_section(struct section *sec)
+/* If there are multiple matches, return the first one in the range. */
+struct reloc *find_reloc_by_dest_range(const struct elf *elf, struct section *sec,
+ unsigned long offset, unsigned int len)
{
- return !strncmp(sec->name, ".debug_", 7);
+ struct section *rsec = sec->rsec;
+
+ if (!rsec)
+ return NULL;
+
+ if (rsec->sorted)
+ return find_reloc_sorted(rsec, offset, len);
+
+ if (rsec->hashed)
+ return find_reloc_hash(elf, rsec, offset, len);
+
+ return find_reloc_linear(rsec, offset, len);
+}
+
+struct reloc *find_reloc_by_dest(const struct elf *elf, struct section *sec, unsigned long offset)
+{
+ return find_reloc_by_dest_range(elf, sec, offset, 1);
}
static int read_sections(struct elf *elf)
@@ -1071,7 +1204,8 @@ struct reloc *elf_init_reloc(struct elf *elf, struct section *rsec,
set_reloc_type(elf, reloc, type);
set_reloc_addend(elf, reloc, addend);
- elf_hash_add(reloc, &reloc->hash, reloc_hash(reloc));
+ if (rsec->hashed)
+ elf_hash_add(reloc, &reloc->hash, reloc_hash(reloc));
set_sym_next_reloc(reloc, sym->relocs);
sym->relocs = reloc;
@@ -1123,18 +1257,44 @@ struct reloc *elf_init_reloc_data_sym(struct elf *elf, struct section *sec,
elf_data_rela_type(elf));
}
+static u64 raw_reloc_offset(const struct section *rsec, unsigned int idx)
+{
+ const void *entry = rsec->data->d_buf + idx * rsec->sh.sh_entsize;
+
+ if (rsec->sh.sh_entsize < sizeof(Elf64_Rel))
+ return ((const Elf32_Rela *)entry)->r_offset;
+
+ return ((const Elf64_Rela *)entry)->r_offset;
+}
+
+static bool reloc_sec_in_order(struct section *rsec)
+{
+ const unsigned int nr_relocs = sec_num_entries(rsec);
+ u64 prev_offset = 0;
+ unsigned int i;
+
+ for (i = 0; i < nr_relocs; i++) {
+ /* Called before relocs exist, so look at raw entry. */
+ const u64 offset = raw_reloc_offset(rsec, i);
+
+ if (offset < prev_offset)
+ return false;
+ prev_offset = offset;
+ }
+
+ return true;
+}
+
static int read_relocs(struct elf *elf)
{
- unsigned long nr_reloc, max_reloc = 0;
+ unsigned long nr_reloc, max_reloc = 0, nr_hashed = 0;
struct section *rsec;
struct reloc *reloc;
unsigned int symndx;
struct symbol *sym;
+ bool hashed;
int i;
- if (!elf_alloc_hash(reloc, elf->num_relocs))
- return -1;
-
list_for_each_entry(rsec, &elf->sections, list) {
if (!is_reloc_sec(rsec))
continue;
@@ -1147,6 +1307,28 @@ static int read_relocs(struct elf *elf)
rsec->base->rsec = rsec;
+ /* DWARF relocs are never looked up. */
+ if (is_dwarf_section(rsec->base))
+ continue;
+ if (reloc_sec_in_order(rsec)) {
+ rsec->sorted = true;
+ continue;
+ }
+
+ rsec->hashed = true;
+ nr_hashed += sec_num_entries(rsec);
+ }
+
+ /* Read mostly, so avoid collisions and keep the hash sparse. */
+ if (!elf_alloc_hash(reloc, nr_hashed * OFFSET_STRIDE))
+ return -1;
+
+ list_for_each_entry(rsec, &elf->sections, list) {
+ if (!is_reloc_sec(rsec))
+ continue;
+
+ hashed = rsec->hashed;
+
/* nr_alloc_relocs=0: libelf owns d_buf */
rsec->nr_alloc_relocs = 0;
@@ -1168,18 +1350,23 @@ static int read_relocs(struct elf *elf)
return -1;
}
- elf_hash_add(reloc, &reloc->hash, reloc_hash(reloc));
+ if (hashed)
+ elf_hash_add(reloc, &reloc->hash, reloc_hash(reloc));
set_sym_next_reloc(reloc, sym->relocs);
sym->relocs = reloc;
nr_reloc++;
}
max_reloc = max(max_reloc, nr_reloc);
+
+ if (rsec->sorted && init_reloc_cache(rsec))
+ return -1;
}
if (opts.stats) {
printf("max_reloc: %lu\n", max_reloc);
printf("num_relocs: %lu\n", elf->num_relocs);
+ printf("num_relocs_hashed: %lu\n", nr_hashed);
printf("reloc_bits: %d\n", elf->reloc_bits);
}
@@ -1541,6 +1728,26 @@ struct section *elf_create_section(struct elf *elf, const char *name,
return sec;
}
+/* A relocation was appended, abandon relocation cache and use hash instead. */
+static void copy_reloc_cache_to_hash(struct elf *elf, struct section *rsec,
+ unsigned int nr_relocs)
+{
+ unsigned int i;
+
+ if (rsec->hashed)
+ return;
+
+ if (rsec->sorted)
+ free_reloc_cache(rsec);
+
+ for (i = 0; i < nr_relocs; i++) {
+ struct reloc *reloc = &rsec->relocs[i];
+
+ elf_hash_add(reloc, &reloc->hash, reloc_hash(reloc));
+ }
+ rsec->hashed = true;
+}
+
static int elf_alloc_reloc(struct elf *elf, struct section *rsec)
{
struct reloc *old_relocs, *old_relocs_end, *new_relocs;
@@ -1592,6 +1799,7 @@ static int elf_alloc_reloc(struct elf *elf, struct section *rsec)
}
rsec->nr_alloc_relocs = nr_alloc;
+ copy_reloc_cache_to_hash(elf, rsec, nr_relocs_old);
old_relocs = rsec->relocs;
new_relocs = calloc(nr_alloc, sizeof(struct reloc));
diff --git a/tools/objtool/include/objtool/elf.h b/tools/objtool/include/objtool/elf.h
index a82517a76a0f..0d61ddfec05f 100644
--- a/tools/objtool/include/objtool/elf.h
+++ b/tools/objtool/include/objtool/elf.h
@@ -59,6 +59,8 @@ struct section {
const char *name;
int idx;
bool _changed, text, rodata, noinstr, init, truncate;
+ bool hashed, sorted;
+ unsigned int *reloc_cache;
struct reloc *relocs;
unsigned long nr_alloc_relocs;
struct section *twin;
@@ -98,6 +100,8 @@ struct symbol {
u8 klp : 1;
u8 dont_correlate : 1;
u8 fake : 1;
+ u8 dead_end_known : 1;
+ u8 dead_end : 1;
struct list_head pv_target;
struct reloc *relocs;
struct section *group_sec;
--
2.55.0
^ permalink raw reply [flat|nested] 73+ messages in thread* [PATCH 19/23] objtool: decode instructions and resolve branch targets in parallel
2026-09-08 20:55 [PATCH 00/23] kbuild: significantly speed up kernel builds Lorenzo Stoakes (ARM)
` (17 preceding siblings ...)
2026-09-08 20:55 ` [PATCH 18/23] objtool: cache relocations and function dead end state, do less work Lorenzo Stoakes (ARM)
@ 2026-09-08 20:55 ` Lorenzo Stoakes (ARM)
2026-09-08 20:55 ` [PATCH 20/23] kbuild: rust: parallelise rustc front end Lorenzo Stoakes (ARM)
` (7 subsequent siblings)
26 siblings, 0 replies; 73+ messages in thread
From: Lorenzo Stoakes (ARM) @ 2026-09-08 20:55 UTC (permalink / raw)
To: Linus Torvalds, Nathan Chancellor, Nicolas Schier,
Nick Desaulniers, Bill Wendling, Justin Stitt, Masahiro Yamada,
Alexey Gladkov, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, x86, H. Peter Anvin, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Alexandre Ghiti, Arnd Bergmann, Catalin Marinas,
Will Deacon, Mark Rutland, Ard Biesheuvel, Ilias Apalodimas,
Josh Poimboeuf, Peter Zijlstra, Miguel Ojeda, Boqun Feng,
Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Alice Ryhl, Trevor Gross, Danilo Krummrich, Daniel Almeida,
Tamir Duberstein, Alexandre Courbot, Onur Özkan,
Jonathan Corbet, Randy Dunlap
Cc: linux-kbuild, linux-kernel, llvm, linux-riscv, linux-arch,
linux-arm-kernel, linux-efi, rust-for-linux, linux-doc,
Jens Axboe, Lorenzo Stoakes (ARM)
During a kernel build objtool is used to decode vmlinux.o's instructions
and resolve every jump and call destination.
This forms a large part of the work objtool does during the build process,
and it is all done in serial.
Decode these in parallel at a function granularity to speed things up.
Only the instruction hash is shared between the threads and nothing is ever
removed from it, so an insertion is a compare-and-swap on the bucket head.
Also update the instruction hash to be more efficient - it was previously
hardcoded to a size of 2^20 buckets.
This is insufficient for an x86-64 allmodconfig kernel where an x86-64
build decodes ~16 million instructions and in practice find_insn() was
observed working chains of four or more entries on lookup.
Instead size it based on the amount of text to be decoded at roughly one
bucket per instruction, identical to the relocation hash (set to
OFFSET_STRIDE).
This results in 2^20 buckets (~8 MiB memory usage) for a defconfig
vmlinux.o and 2^22 (~32 MiB memory usage) for an allmodconfig kernel, so it
is not an egregious use of memory.
Threads are only created for objects with 8 MiB or more of text, meaning
that runs involving smaller objects remain unaffected.
Only decoding and the branch passes are threaded, so the gain flattens out
at 16 threads and more only add overhead - on the clang allmodconfig
vmlinux.o objtool takes 5.58s on 1 thread, 3.96s on 8, 3.91s on 16 and
4.03s on 128. Cap the thread count at 16, or the number of CPUs if fewer.
The passes which resolve jump and call destinations and annotate call
sites all run over the same regions.
They only write to lists in struct objtool_file, so have each range work
with their own copy of this data structure, which are then joined, in range
order afterwards in a map-reduce fashion.
The output of objtool before and after this change was confirmed to be
byte-for-byte identical for x86_64 defconfig and allmodconfig with gcc and
clang, and for a loongarch defconfig, where objtool runs on every object.
On a 128-thread machine, objtool on the clang allmodconfig vmlinux.o goes
from 5.9s to 3.7s (6.5s to 3.7s together with the previous patch), and on
defconfig from 1.87s to 1.26s.
objtool on vmlinux.o is on the serial tail of every build that links
vmlinux, no-op builds are unchanged.
Whole build, 128-thread Threadripper 9980X, best of N runs:
before after delta
-------------------------------
x86 defconfig, touch mm/vma.c, gcc 8.1s 7.5s -0.65s (-8%)
x86 defconfig, touch mm/vma.c, clang 7.1s 6.6s -0.51s (-7%)
x86 defconfig, clean, gcc 26.8s 26.3s -0.46s (-2%)
x86 defconfig, clean, clang 26.2s 25.8s -0.45s (-2%)
x86 allmodconfig, touch mm/vma.c, gcc 28.4s 23.7s -4.7s (-17%)
x86 allmodconfig, touch mm/vma.c, clang 26.0s 22.1s -3.9s (-15%)
Assisted-by: LLM
Signed-off-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
---
tools/objtool/Makefile | 2 +-
tools/objtool/check.c | 757 ++++++++++++++++++++++++--------
tools/objtool/include/objtool/objtool.h | 3 +-
tools/objtool/objtool.c | 1 -
4 files changed, 578 insertions(+), 185 deletions(-)
diff --git a/tools/objtool/Makefile b/tools/objtool/Makefile
index a4484fd22a96..2de50c3917ba 100644
--- a/tools/objtool/Makefile
+++ b/tools/objtool/Makefile
@@ -63,7 +63,7 @@ INCLUDES := -I$(srctree)/tools/include \
OBJTOOL_CFLAGS := -std=gnu11 -fomit-frame-pointer -O2 -g $(WARNINGS) \
$(INCLUDES) $(LIBELF_FLAGS) $(LIBXXHASH_CFLAGS) $(HOSTCFLAGS)
-OBJTOOL_LDFLAGS := $(LIBSUBCMD) $(LIBELF_LIBS) $(LIBXXHASH_LIBS) $(HOSTLDFLAGS)
+OBJTOOL_LDFLAGS := $(LIBSUBCMD) $(LIBELF_LIBS) $(LIBXXHASH_LIBS) -lpthread $(HOSTLDFLAGS)
# Allow old libelf to be used:
elfshdr := $(shell echo '$(pound)include <libelf.h>' | $(HOSTCC) $(OBJTOOL_CFLAGS) -x c -E - 2>/dev/null | grep elf_getshdr)
diff --git a/tools/objtool/check.c b/tools/objtool/check.c
index 2abd41cc3aaf..b092cf8d582f 100644
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -7,6 +7,9 @@
#include <fnmatch.h>
#include <string.h>
#include <stdlib.h>
+#include <pthread.h>
+#include <stddef.h>
+#include <unistd.h>
#include <inttypes.h>
#include <sys/mman.h>
@@ -24,6 +27,7 @@
#include <linux/objtool_types.h>
#include <linux/hashtable.h>
#include <linux/kernel.h>
+#include <linux/sizes.h>
#include <linux/static_call_types.h>
#include <linux/string.h>
@@ -38,12 +42,22 @@ struct disas_context *objtool_disas_ctx;
size_t sym_name_max_len;
+static struct hlist_head *insn_hash_head(struct objtool_file *file,
+ struct section *sec, unsigned long offset)
+{
+ /* Determine instruction hash based on section index and offset. */
+ const u32 sec_hash = sec_offset_hash(sec, offset);
+ const u32 hash = hash_min(sec_hash, file->insn_hash_bits);
+
+ return &file->insn_hash[hash];
+}
+
struct instruction *find_insn(struct objtool_file *file,
struct section *sec, unsigned long offset)
{
struct instruction *insn;
- hash_for_each_possible(file->insn_hash, insn, hash, sec_offset_hash(sec, offset)) {
+ hlist_for_each_entry(insn, insn_hash_head(file, sec, offset), hash) {
if (insn->sec == sec && insn->offset == offset)
return insn;
}
@@ -54,14 +68,13 @@ struct instruction *find_insn(struct objtool_file *file,
struct instruction *next_insn_same_sec(struct objtool_file *file,
struct instruction *insn)
{
- if (insn->idx == INSN_CHUNK_MAX)
- return find_insn(file, insn->sec, insn->offset + insn->len);
+ const unsigned long next_offset = insn->offset + insn->len;
- insn++;
- if (!insn->len)
- return NULL;
+ /* A chunk ends at its last slot or an empty one, so look the next up. */
+ if (insn->idx == INSN_CHUNK_MAX || !insn[1].len)
+ return find_insn(file, insn->sec, next_offset);
- return insn;
+ return insn + 1;
}
struct instruction *next_insn_same_func(struct objtool_file *file,
@@ -411,21 +424,391 @@ static void *cfi_hash_alloc(unsigned long size)
static unsigned long nr_insns;
static unsigned long nr_insns_visited;
+/* Only an object this large, e.g. vmlinux.o, is decoded on several threads. */
+#define DECODE_THREADED_MIN_TEXT SZ_8M
+/* Only decoding and the branch passes are threaded, so more gains nothing. */
+#define DECODE_MAX_THREADS 16
+#define DECODE_RANGES_PER_THREAD 4
+
+/*
+ * sec_offset_hash() keys on OFFSET_STRIDE windows, so the instructions of a
+ * window share a chain and buckets beyond one per window would sit empty.
+ */
+#define INSN_HASH_BYTES_PER_BUCKET OFFSET_STRIDE
+#define INSN_HASH_MIN_BITS 10
+
+static unsigned long total_text_size(struct objtool_file *file)
+{
+ unsigned long size = 0;
+ struct section *sec;
+
+ for_each_sec(file->elf, sec)
+ if (is_text_sec(sec))
+ size += sec_size(sec);
+
+ return size;
+}
+
+static int alloc_insn_hash(struct objtool_file *file, unsigned long text_size)
+{
+ const unsigned long nr_buckets = text_size / INSN_HASH_BYTES_PER_BUCKET;
+ const int bits = ilog2(nr_buckets);
+
+ file->insn_hash_bits = max(INSN_HASH_MIN_BITS, bits);
+ file->insn_hash = calloc(1UL << file->insn_hash_bits,
+ sizeof(*file->insn_hash));
+ if (!file->insn_hash) {
+ ERROR_GLIBC("calloc");
+ return -1;
+ }
+
+ if (opts.stats)
+ printf("insn_hash_bits: %d\n", file->insn_hash_bits);
+
+ return 0;
+}
+
+/* Per-thread state, only instruction hash is shared. */
+struct insn_range {
+ struct section *sec;
+ unsigned long start, end;
+ struct instruction *first, *last;
+ unsigned long nr_insns;
+ int ret;
+
+ /*
+ * Each thread writes to its own copy of an objtool file, which are
+ * combined upon completion.
+ */
+ struct objtool_file shadow;
+};
+
+#define range_for_each_insn(file, range, insn) \
+ for (insn = (range)->first; \
+ insn && insn->offset < (range)->end; \
+ insn = next_insn_same_sec(file, insn))
+
+typedef int (*range_fn_t)(struct objtool_file *file, struct insn_range *range);
+
+struct range_work {
+ range_fn_t fn;
+};
+
+static struct insn_range *decode_ranges;
+static unsigned int nr_decode_ranges, next_decode_range, nr_decode_threads;
+
+/* The kernel's try_cmpxchg(); the tools' cmpxchg() is host-arch only. */
+static bool hlist_try_cmpxchg(struct hlist_node **ptr, struct hlist_node **old,
+ struct hlist_node *new)
+{
+ struct hlist_node *seen = __sync_val_compare_and_swap(ptr, *old, new);
+
+ if (seen == *old)
+ return true;
+
+ *old = seen;
+ return false;
+}
+
+/* Nothing is ever removed, so push onto the bucket as llist_add() does. */
+static void insn_hash_add(struct objtool_file *file, struct instruction *insn)
+{
+ struct hlist_head *head = insn_hash_head(file, insn->sec, insn->offset);
+ struct hlist_node *first = head->first;
+
+ insn->hash.pprev = &head->first;
+ do {
+ insn->hash.next = first;
+ } while (!hlist_try_cmpxchg(&head->first, &first, &insn->hash));
+}
+
+/* The slot after prev in its chunk, or the first of a new chunk. */
+static struct instruction *next_insn_slot(struct instruction *prev)
+{
+ struct instruction *insn;
+
+ if (prev && prev->idx < INSN_CHUNK_MAX) {
+ insn = prev + 1;
+ insn->idx = prev->idx + 1;
+ return insn;
+ }
+
+ insn = calloc(INSN_CHUNK_SIZE, sizeof(*insn));
+ if (!insn)
+ ERROR_GLIBC("calloc");
+
+ return insn;
+}
+
+static int decode_range(struct objtool_file *file, struct insn_range *range)
+{
+ struct instruction *insn = NULL;
+ struct section *sec = range->sec;
+ unsigned long offset;
+ u8 prev_len = 0;
+
+ for (offset = range->start; offset < range->end; offset += insn->len) {
+ const unsigned long remaining = sec_size(sec) - offset;
+
+ insn = next_insn_slot(insn);
+ if (!insn)
+ return -1;
+
+ INIT_LIST_HEAD(&insn->call_node);
+ insn->sec = sec;
+ insn->offset = offset;
+ insn->prev_len = prev_len;
+
+ if (arch_decode_instruction(file, sec, offset, remaining, insn))
+ return -1;
+
+ prev_len = insn->len;
+
+ if (insn->type == INSN_BUG)
+ insn->dead_end = true;
+
+ insn_hash_add(file, insn);
+ if (!range->first)
+ range->first = insn;
+ range->nr_insns++;
+ }
+ range->last = insn;
+
+ /* The range ends at a function symbol, so decoding must land on it. */
+ if (offset != range->end) {
+ ERROR("%s: no instruction boundary at %s", sec->name,
+ offstr(sec, range->end));
+ return -1;
+ }
+
+ return 0;
+}
+
+static int run_threads(void *(*fn)(void *), void *arg, unsigned int nr_threads)
+{
+ unsigned int nr_started, i;
+ pthread_t *threads;
+ int ret = 0;
+
+ if (nr_threads <= 1) {
+ fn(arg);
+ return 0;
+ }
+
+ threads = calloc(nr_threads, sizeof(*threads));
+ if (!threads) {
+ ERROR_GLIBC("calloc");
+ return -1;
+ }
+
+ for (nr_started = 0; nr_started < nr_threads; nr_started++) {
+ if (pthread_create(&threads[nr_started], NULL, fn, arg)) {
+ ERROR_GLIBC("pthread_create");
+ ret = -1;
+ break;
+ }
+ }
+
+ for (i = 0; i < nr_started; i++)
+ pthread_join(threads[i], NULL);
+
+ free(threads);
+ return ret;
+}
+
+/* Hand out the ranges one at a time, or NULL once they are all taken. */
+static struct insn_range *claim_decode_range(void)
+{
+ const unsigned int idx = __sync_fetch_and_add(&next_decode_range, 1);
+
+ return idx < nr_decode_ranges ? &decode_ranges[idx] : NULL;
+}
+
+static void *range_worker(void *arg)
+{
+ const struct range_work *work = arg;
+ struct insn_range *range;
+
+ while ((range = claim_decode_range()))
+ range->ret = work->fn(&range->shadow, range);
+
+ return NULL;
+}
+
+/* The lists in the objtool_file that the passes add instructions to. */
+static const size_t shadow_list_offsets[] = {
+ offsetof(struct objtool_file, retpoline_call_list),
+ offsetof(struct objtool_file, return_thunk_list),
+ offsetof(struct objtool_file, static_call_list),
+ offsetof(struct objtool_file, mcount_loc_list),
+ offsetof(struct objtool_file, endbr_list),
+ offsetof(struct objtool_file, call_list),
+};
+
+static struct list_head *shadow_list(struct objtool_file *file,
+ unsigned int idx)
+{
+ return (void *)file + shadow_list_offsets[idx];
+}
+
+static void init_range_shadow(struct objtool_file *file,
+ struct insn_range *range)
+{
+ unsigned int i;
+
+ range->shadow = *file;
+ range->ret = 0;
+ for (i = 0; i < ARRAY_SIZE(shadow_list_offsets); i++)
+ INIT_LIST_HEAD(shadow_list(&range->shadow, i));
+}
+
+/* Joined in range order, which is the order a single walk would produce. */
+static int join_range_shadow(struct objtool_file *file,
+ struct insn_range *range)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(shadow_list_offsets); i++)
+ list_splice_tail(shadow_list(&range->shadow, i),
+ shadow_list(file, i));
+
+ return range->ret;
+}
+
+/* Run a pass over the instructions, one range per thread at a time. */
+static int run_insn_ranges(struct objtool_file *file, range_fn_t fn)
+{
+ struct range_work work = { .fn = fn };
+ unsigned int i;
+ int ret = 0;
+
+ for (i = 0; i < nr_decode_ranges; i++)
+ init_range_shadow(file, &decode_ranges[i]);
+ next_decode_range = 0;
+
+ if (run_threads(range_worker, &work, nr_decode_threads))
+ return -1;
+
+ for (i = 0; i < nr_decode_ranges; i++) {
+ if (join_range_shadow(file, &decode_ranges[i]))
+ ret = -1;
+ }
+
+ return ret;
+}
+
+static int add_decode_range(struct section *sec, unsigned long start,
+ unsigned long end)
+{
+ const size_t size = (nr_decode_ranges + 1) * sizeof(*decode_ranges);
+ struct insn_range *range;
+
+ decode_ranges = realloc(decode_ranges, size);
+ if (!decode_ranges) {
+ ERROR_GLIBC("realloc");
+ return -1;
+ }
+
+ range = &decode_ranges[nr_decode_ranges++];
+ memset(range, 0, sizeof(*range));
+ range->sec = sec;
+ range->start = start;
+ range->end = end;
+
+ return 0;
+}
+
+/* Split a section into ranges of roughly range_size, at function starts. */
+static int add_decode_ranges(struct section *sec, unsigned long range_size)
+{
+ const unsigned long size = sec_size(sec);
+ unsigned long start = 0;
+ struct symbol *sym;
+
+ if (!range_size)
+ return add_decode_range(sec, 0, size);
+
+ sec_for_each_sym(sec, sym) {
+ if (!is_func_sym(sym) || sym->offset <= start ||
+ sym->offset >= size)
+ continue;
+ if (sym->offset - start < range_size)
+ continue;
+
+ if (add_decode_range(sec, start, sym->offset))
+ return -1;
+ start = sym->offset;
+ }
+
+ return add_decode_range(sec, start, size);
+}
+
+static void free_decode_ranges(void)
+{
+ free(decode_ranges);
+ decode_ranges = NULL;
+ nr_decode_ranges = 0;
+ next_decode_range = 0;
+}
+
+/* A range's first instruction follows the last of the range before it. */
+static void link_decode_ranges(void)
+{
+ unsigned int i;
+
+ for (i = 1; i < nr_decode_ranges; i++) {
+ const struct insn_range *prev = &decode_ranges[i - 1];
+ struct insn_range *range = &decode_ranges[i];
+
+ if (prev->sec != range->sec || !prev->last || !range->first)
+ continue;
+
+ range->first->prev_len = prev->last->len;
+ }
+}
+
+static unsigned int decode_threads(unsigned long text_size)
+{
+ const long nr_cpus = sysconf(_SC_NPROCESSORS_ONLN);
+
+ if (text_size < DECODE_THREADED_MIN_TEXT || nr_cpus < 2)
+ return 1;
+
+ return min_t(unsigned int, nr_cpus, DECODE_MAX_THREADS);
+}
+
+/* Several ranges per thread so uneven ones balance out; 0 means per section. */
+static unsigned long decode_range_size(unsigned long text_size,
+ unsigned int nr_threads)
+{
+ const unsigned int nr_ranges = nr_threads * DECODE_RANGES_PER_THREAD;
+
+ if (nr_threads <= 1)
+ return 0;
+
+ return text_size / nr_ranges;
+}
+
/*
* Call the arch-specific instruction decoder for all the instructions and add
* them to the global instruction list.
*/
static int decode_instructions(struct objtool_file *file)
{
+ const unsigned long text_size = total_text_size(file);
+ unsigned long range_size;
+ struct instruction *insn;
struct section *sec;
struct symbol *func;
- unsigned long offset;
- struct instruction *insn;
+ unsigned int i;
+
+ if (alloc_insn_hash(file, text_size))
+ return -1;
+
+ nr_decode_threads = decode_threads(text_size);
+ range_size = decode_range_size(text_size, nr_decode_threads);
for_each_sec(file->elf, sec) {
- struct instruction *insns = NULL;
- u8 prev_len = 0;
- u8 idx = 0;
if (!is_text_sec(sec))
continue;
@@ -450,41 +833,20 @@ static int decode_instructions(struct objtool_file *file)
if (!strcmp(sec->name, ".init.text") && !opts.module)
sec->init = true;
- for (offset = 0; offset < sec_size(sec); offset += insn->len) {
- if (!insns || idx == INSN_CHUNK_MAX) {
- insns = calloc(INSN_CHUNK_SIZE, sizeof(*insn));
- if (!insns) {
- ERROR_GLIBC("calloc");
- return -1;
- }
- idx = 0;
- } else {
- idx++;
- }
- insn = &insns[idx];
- insn->idx = idx;
-
- INIT_LIST_HEAD(&insn->call_node);
- insn->sec = sec;
- insn->offset = offset;
- insn->prev_len = prev_len;
-
- if (arch_decode_instruction(file, sec, offset, sec_size(sec) - offset, insn))
- return -1;
+ if (add_decode_ranges(sec, range_size))
+ return -1;
+ }
- prev_len = insn->len;
+ if (run_insn_ranges(file, decode_range))
+ return -1;
- /*
- * By default, "ud2" is a dead end unless otherwise
- * annotated, because GCC 7 inserts it for certain
- * divide-by-zero cases.
- */
- if (insn->type == INSN_BUG)
- insn->dead_end = true;
+ for (i = 0; i < nr_decode_ranges; i++)
+ nr_insns += decode_ranges[i].nr_insns;
+ link_decode_ranges();
- hash_add(file->insn_hash, &insn->hash, sec_offset_hash(sec, insn->offset));
- nr_insns++;
- }
+ for_each_sec(file->elf, sec) {
+ if (!is_text_sec(sec))
+ continue;
sec_for_each_sym(sec, func) {
if (!is_notype_sym(func) && !is_func_sym(func))
@@ -1527,133 +1889,147 @@ static bool is_first_func_insn(struct objtool_file *file,
/*
* Find the destination instructions for all jumps.
*/
-static int add_jump_destinations(struct objtool_file *file)
+static int add_jump_destination(struct objtool_file *file, struct instruction *insn)
{
- struct instruction *insn;
struct reloc *reloc;
+ struct symbol *func = insn_func(insn);
+ struct instruction *dest_insn;
+ struct section *dest_sec;
+ struct symbol *dest_sym;
+ unsigned long dest_off;
- for_each_insn(file, insn) {
- struct symbol *func = insn_func(insn);
- struct instruction *dest_insn;
- struct section *dest_sec;
- struct symbol *dest_sym;
- unsigned long dest_off;
+ if (!is_static_jump(insn))
+ return 0;
- if (!is_static_jump(insn))
- continue;
+ if (insn->jump_dest) {
+ /*
+ * handle_group_alt() may have previously set
+ * 'jump_dest' for some alternatives.
+ */
+ return 0;
+ }
- if (insn->jump_dest) {
- /*
- * handle_group_alt() may have previously set
- * 'jump_dest' for some alternatives.
- */
- continue;
- }
+ reloc = insn_reloc(file, insn);
+ if (!reloc) {
+ dest_sec = insn->sec;
+ dest_off = arch_jump_destination(insn);
+ dest_sym = dest_sec->sym;
+ } else {
+ dest_sym = reloc->sym;
+ if (is_undef_sym(dest_sym)) {
+ if (dest_sym->retpoline_thunk) {
+ if (add_retpoline_call(file, insn))
+ return -1;
+ return 0;
+ }
- reloc = insn_reloc(file, insn);
- if (!reloc) {
- dest_sec = insn->sec;
- dest_off = arch_jump_destination(insn);
- dest_sym = dest_sec->sym;
- } else {
- dest_sym = reloc->sym;
- if (is_undef_sym(dest_sym)) {
- if (dest_sym->retpoline_thunk) {
- if (add_retpoline_call(file, insn))
- return -1;
- continue;
- }
+ if (dest_sym->return_thunk) {
+ add_return_call(file, insn, true);
+ return 0;
+ }
- if (dest_sym->return_thunk) {
- add_return_call(file, insn, true);
- continue;
- }
+ /* External symbol */
+ if (func) {
+ /* External sibling call */
+ if (add_call_dest(file, insn, dest_sym, true))
+ return -1;
+ return 0;
+ }
- /* External symbol */
- if (func) {
- /* External sibling call */
- if (add_call_dest(file, insn, dest_sym, true))
- return -1;
- continue;
- }
+ /* Non-func asm code jumping to external symbol */
+ return 0;
+ }
- /* Non-func asm code jumping to external symbol */
- continue;
- }
+ dest_sec = dest_sym->sec;
+ dest_off = dest_sym->offset + arch_insn_adjusted_addend(insn, reloc);
+ }
+
+ dest_insn = find_insn(file, dest_sec, dest_off);
+ if (!dest_insn) {
+ struct symbol *sym = find_symbol_by_offset(dest_sec, dest_off);
- dest_sec = dest_sym->sec;
- dest_off = dest_sym->offset + arch_insn_adjusted_addend(insn, reloc);
+ /*
+ * retbleed_untrain_ret() jumps to
+ * __x86_return_thunk(), but objtool can't find
+ * the thunk's starting RET instruction,
+ * because the RET is also in the middle of
+ * another instruction. Objtool only knows
+ * about the outer instruction.
+ */
+ if (sym && sym->embedded_insn) {
+ add_return_call(file, insn, false);
+ return 0;
}
- dest_insn = find_insn(file, dest_sec, dest_off);
- if (!dest_insn) {
- struct symbol *sym = find_symbol_by_offset(dest_sec, dest_off);
+ /*
+ * GCOV/KCOV dead code can jump to the end of
+ * the function/section.
+ */
+ if (file->ignore_unreachables && func &&
+ dest_sec == insn->sec &&
+ dest_off == func->offset + func->len)
+ return 0;
- /*
- * retbleed_untrain_ret() jumps to
- * __x86_return_thunk(), but objtool can't find
- * the thunk's starting RET instruction,
- * because the RET is also in the middle of
- * another instruction. Objtool only knows
- * about the outer instruction.
- */
- if (sym && sym->embedded_insn) {
- add_return_call(file, insn, false);
- continue;
- }
+ ERROR_INSN(insn, "can't find jump dest instruction at %s",
+ offstr(dest_sec, dest_off));
+ return -1;
+ }
- /*
- * GCOV/KCOV dead code can jump to the end of
- * the function/section.
- */
- if (file->ignore_unreachables && func &&
- dest_sec == insn->sec &&
- dest_off == func->offset + func->len)
- continue;
+ if (!dest_sym || is_sec_sym(dest_sym)) {
+ dest_sym = insn_sym(dest_insn);
+ if (!dest_sym)
+ goto set_jump_dest;
+ }
- ERROR_INSN(insn, "can't find jump dest instruction at %s",
- offstr(dest_sec, dest_off));
+ if (dest_sym->retpoline_thunk && dest_insn->offset == dest_sym->offset) {
+ if (add_retpoline_call(file, insn))
return -1;
- }
+ return 0;
+ }
- if (!dest_sym || is_sec_sym(dest_sym)) {
- dest_sym = insn_sym(dest_insn);
- if (!dest_sym)
- goto set_jump_dest;
- }
+ if (dest_sym->return_thunk && dest_insn->offset == dest_sym->offset) {
+ add_return_call(file, insn, true);
+ return 0;
+ }
- if (dest_sym->retpoline_thunk && dest_insn->offset == dest_sym->offset) {
- if (add_retpoline_call(file, insn))
- return -1;
- continue;
- }
+ if (!insn_sym(insn) || insn_sym(insn)->pfunc == dest_sym->pfunc)
+ goto set_jump_dest;
- if (dest_sym->return_thunk && dest_insn->offset == dest_sym->offset) {
- add_return_call(file, insn, true);
- continue;
- }
+ /*
+ * Internal cross-function jump.
+ */
- if (!insn_sym(insn) || insn_sym(insn)->pfunc == dest_sym->pfunc)
- goto set_jump_dest;
+ if (is_first_func_insn(file, dest_insn)) {
+ /* Internal sibling call */
+ if (add_call_dest(file, insn, dest_sym, true))
+ return -1;
+ return 0;
+ }
- /*
- * Internal cross-function jump.
- */
+set_jump_dest:
+ insn->jump_dest = dest_insn;
- if (is_first_func_insn(file, dest_insn)) {
- /* Internal sibling call */
- if (add_call_dest(file, insn, dest_sym, true))
- return -1;
- continue;
- }
+ return 0;
+}
-set_jump_dest:
- insn->jump_dest = dest_insn;
+static int add_jump_destinations_range(struct objtool_file *file,
+ struct insn_range *range)
+{
+ struct instruction *insn;
+
+ range_for_each_insn(file, range, insn) {
+ if (add_jump_destination(file, insn))
+ return -1;
}
return 0;
}
+static int add_jump_destinations(struct objtool_file *file)
+{
+ return run_insn_ranges(file, add_jump_destinations_range);
+}
+
static struct symbol *find_call_destination(struct section *sec, unsigned long offset)
{
struct symbol *call_dest;
@@ -1668,64 +2044,79 @@ static struct symbol *find_call_destination(struct section *sec, unsigned long o
/*
* Find the destination instructions for all calls.
*/
-static int add_call_destinations(struct objtool_file *file)
+static int add_call_destination(struct objtool_file *file, struct instruction *insn)
{
- struct instruction *insn;
unsigned long dest_off;
struct symbol *dest;
struct reloc *reloc;
+ struct symbol *func = insn_func(insn);
- for_each_insn(file, insn) {
- struct symbol *func = insn_func(insn);
- if (insn->type != INSN_CALL)
- continue;
+ if (insn->type != INSN_CALL)
+ return 0;
- reloc = insn_reloc(file, insn);
- if (!reloc) {
- dest_off = arch_jump_destination(insn);
- dest = find_call_destination(insn->sec, dest_off);
+ reloc = insn_reloc(file, insn);
+ if (!reloc) {
+ dest_off = arch_jump_destination(insn);
+ dest = find_call_destination(insn->sec, dest_off);
- if (add_call_dest(file, insn, dest, false))
- return -1;
+ if (add_call_dest(file, insn, dest, false))
+ return -1;
- if (func && func->ignore)
- continue;
+ if (func && func->ignore)
+ return 0;
- if (!insn_call_dest(insn)) {
- ERROR_INSN(insn, "unannotated intra-function call");
- return -1;
- }
+ if (!insn_call_dest(insn)) {
+ ERROR_INSN(insn, "unannotated intra-function call");
+ return -1;
+ }
- if (func && !is_func_sym(insn_call_dest(insn))) {
- ERROR_INSN(insn, "unsupported call to non-function");
- return -1;
- }
+ if (func && !is_func_sym(insn_call_dest(insn))) {
+ ERROR_INSN(insn, "unsupported call to non-function");
+ return -1;
+ }
- } else if (is_sec_sym(reloc->sym)) {
- dest_off = arch_insn_adjusted_addend(insn, reloc);
- dest = find_call_destination(reloc->sym->sec, dest_off);
- if (!dest) {
- ERROR_INSN(insn, "can't find call dest symbol at %s+0x%lx",
- reloc->sym->sec->name, dest_off);
- return -1;
- }
+ } else if (is_sec_sym(reloc->sym)) {
+ dest_off = arch_insn_adjusted_addend(insn, reloc);
+ dest = find_call_destination(reloc->sym->sec, dest_off);
+ if (!dest) {
+ ERROR_INSN(insn, "can't find call dest symbol at %s+0x%lx",
+ reloc->sym->sec->name, dest_off);
+ return -1;
+ }
- if (add_call_dest(file, insn, dest, false))
- return -1;
+ if (add_call_dest(file, insn, dest, false))
+ return -1;
- } else if (reloc->sym->retpoline_thunk) {
- if (add_retpoline_call(file, insn))
- return -1;
+ } else if (reloc->sym->retpoline_thunk) {
+ if (add_retpoline_call(file, insn))
+ return -1;
- } else {
- if (add_call_dest(file, insn, reloc->sym, false))
- return -1;
- }
+ } else {
+ if (add_call_dest(file, insn, reloc->sym, false))
+ return -1;
}
return 0;
}
+static int add_call_destinations_range(struct objtool_file *file,
+ struct insn_range *range)
+{
+ struct instruction *insn;
+
+ range_for_each_insn(file, range, insn) {
+ if (add_call_destination(file, insn))
+ return -1;
+ }
+
+ return 0;
+}
+
+static int add_call_destinations(struct objtool_file *file)
+{
+ return run_insn_ranges(file, add_call_destinations_range);
+}
+
/*
* The .alternatives section requires some extra special care over and above
* other special sections because alternatives are patched in place.
@@ -2689,6 +3080,8 @@ int decode_file(struct objtool_file *file)
if (read_annotate(file, __annotate_late))
return -1;
+ free_decode_ranges();
+
return 0;
}
diff --git a/tools/objtool/include/objtool/objtool.h b/tools/objtool/include/objtool/objtool.h
index 6dc12a59ad00..79fe82b7397e 100644
--- a/tools/objtool/include/objtool/objtool.h
+++ b/tools/objtool/include/objtool/objtool.h
@@ -21,7 +21,8 @@ struct pv_state {
struct objtool_file {
struct elf *elf;
- DECLARE_HASHTABLE(insn_hash, 20);
+ struct hlist_head *insn_hash;
+ int insn_hash_bits;
struct list_head retpoline_call_list;
struct list_head return_thunk_list;
struct list_head static_call_list;
diff --git a/tools/objtool/objtool.c b/tools/objtool/objtool.c
index a4e139dee7e9..71e048f8582a 100644
--- a/tools/objtool/objtool.c
+++ b/tools/objtool/objtool.c
@@ -29,7 +29,6 @@ struct objtool_file *objtool_open_read(const char *filename)
if (!file.elf)
return NULL;
- hash_init(file.insn_hash);
INIT_LIST_HEAD(&file.retpoline_call_list);
INIT_LIST_HEAD(&file.return_thunk_list);
INIT_LIST_HEAD(&file.static_call_list);
--
2.55.0
^ permalink raw reply [flat|nested] 73+ messages in thread* [PATCH 20/23] kbuild: rust: parallelise rustc front end
2026-09-08 20:55 [PATCH 00/23] kbuild: significantly speed up kernel builds Lorenzo Stoakes (ARM)
` (18 preceding siblings ...)
2026-09-08 20:55 ` [PATCH 19/23] objtool: decode instructions and resolve branch targets in parallel Lorenzo Stoakes (ARM)
@ 2026-09-08 20:55 ` Lorenzo Stoakes (ARM)
2026-09-08 21:13 ` Miguel Ojeda
2026-09-09 10:22 ` Björn Baron
2026-09-08 20:55 ` [PATCH 21/23] rust: make exports.o depend on the headers generated for it Lorenzo Stoakes (ARM)
` (6 subsequent siblings)
26 siblings, 2 replies; 73+ messages in thread
From: Lorenzo Stoakes (ARM) @ 2026-09-08 20:55 UTC (permalink / raw)
To: Linus Torvalds, Nathan Chancellor, Nicolas Schier,
Nick Desaulniers, Bill Wendling, Justin Stitt, Masahiro Yamada,
Alexey Gladkov, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, x86, H. Peter Anvin, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Alexandre Ghiti, Arnd Bergmann, Catalin Marinas,
Will Deacon, Mark Rutland, Ard Biesheuvel, Ilias Apalodimas,
Josh Poimboeuf, Peter Zijlstra, Miguel Ojeda, Boqun Feng,
Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Alice Ryhl, Trevor Gross, Danilo Krummrich, Daniel Almeida,
Tamir Duberstein, Alexandre Courbot, Onur Özkan,
Jonathan Corbet, Randy Dunlap
Cc: linux-kbuild, linux-kernel, llvm, linux-riscv, linux-arch,
linux-arm-kernel, linux-efi, rust-for-linux, linux-doc,
Jens Axboe, Lorenzo Stoakes (ARM)
Rust crates are compiled in a serial chain on the critical path - core,
bindings, kernel crates and then the drivers - each a rustc invocation.
With CONFIG_RUST set and every rust driver enabled, the entire chain takes
around 25 seconds on a 128-thread machine, with the associated C code
taking 24 seconds.
Each time a change is made to any of the bindings, it triggers a rebuild.
Use the -Zthreads option to have rustc perform builds in parallel. This has
been available since rust 1.84 (rust-lang/rust#132282), and the kernel
requires rust 1.85 or above.
A future -j/--jobs option is planned for
rustc:(rust-lang/compiler-team#1005), so check to see if this available and
if so use it.
If the user's rustc supports neither, then it falls back gracefully and
neither are used.
The threads are taken from make's jobserver, so a parallel build is not
oversubscribed. It was found that benefits level off at 8 threads (16 was
found to be around the same, and 32 slower).
Generated output was confirmed byte-for-byte identical.
Observed build time changes (using rustc 1.98, clang):
before after
core.o 7.7s 4.9s
bindings.o 4.3s 3.1s
kernel.o 2.0s 1.4s
clean build 39.3s 34.6s
touch rust/kernel/lib.rs 12.9s 12.1s
touch rust/bindings/bindings_helper.h 19.5s 17.5s
Whole build, 128-thread Threadripper 9980X, best of N runs:
before after delta
-------------------------------
x86 defconfig+RUST, clean 41.0s 36.4s -4.6s (-11%)
x86 defconfig+RUST, touch lib.rs 8.3s 7.7s -0.56s (-7%)
Assisted-by: LLM
Signed-off-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
---
Makefile | 4 ++++
init/Kconfig | 7 +++++++
2 files changed, 11 insertions(+)
diff --git a/Makefile b/Makefile
index 997fc7ebe096..7d23d7b64be3 100644
--- a/Makefile
+++ b/Makefile
@@ -1204,6 +1204,10 @@ KBUILD_RUSTFLAGS += --remap-path-prefix=$(srcroot)/= --remap-path-scope=macro
endif
endif
+ifdef CONFIG_RUST
+KBUILD_RUSTFLAGS += $(if $(CONFIG_RUSTC_HAS_JOBS),-j8,$(if $(CONFIG_RUSTC_HAS_ZTHREADS),-Zthreads=8))
+endif
+
# include additional Makefiles when needed
include-y := scripts/Makefile.warn
include-$(CONFIG_DEBUG_INFO) += scripts/Makefile.debug
diff --git a/init/Kconfig b/init/Kconfig
index 3c92c87254a3..4096a306e2ab 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -231,6 +231,13 @@ config RUSTC_HAS_FILE_AS_C_STR
config RUSTC_HAS_SUSPICIOUS_RUNTIME_SYMBOL_DEFINITIONS
def_bool RUSTC_VERSION >= 109800
+# rustc's parallel front end: -j is the coming spelling, -Zthreads the current one
+config RUSTC_HAS_JOBS
+ def_bool $(rustc-option,-j8)
+
+config RUSTC_HAS_ZTHREADS
+ def_bool $(rustc-option,-Zthreads=8)
+
config PAHOLE_VERSION
int
default "$(PAHOLE_VERSION)"
--
2.55.0
^ permalink raw reply [flat|nested] 73+ messages in thread* Re: [PATCH 20/23] kbuild: rust: parallelise rustc front end
2026-09-08 20:55 ` [PATCH 20/23] kbuild: rust: parallelise rustc front end Lorenzo Stoakes (ARM)
@ 2026-09-08 21:13 ` Miguel Ojeda
2026-09-09 14:22 ` Lorenzo Stoakes (ARM)
2026-09-09 10:22 ` Björn Baron
1 sibling, 1 reply; 73+ messages in thread
From: Miguel Ojeda @ 2026-09-08 21:13 UTC (permalink / raw)
To: Lorenzo Stoakes (ARM)
Cc: Linus Torvalds, Nathan Chancellor, Nicolas Schier,
Nick Desaulniers, Bill Wendling, Justin Stitt, Masahiro Yamada,
Alexey Gladkov, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, x86, H. Peter Anvin, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Alexandre Ghiti, Arnd Bergmann, Catalin Marinas,
Will Deacon, Mark Rutland, Ard Biesheuvel, Ilias Apalodimas,
Josh Poimboeuf, Peter Zijlstra, Miguel Ojeda, Boqun Feng,
Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Alice Ryhl, Trevor Gross, Danilo Krummrich, Daniel Almeida,
Tamir Duberstein, Alexandre Courbot, Onur Özkan,
Jonathan Corbet, Randy Dunlap, linux-kbuild, linux-kernel, llvm,
linux-riscv, linux-arch, linux-arm-kernel, linux-efi,
rust-for-linux, linux-doc, Jens Axboe
On Tue, Sep 8, 2026 at 10:59 PM Lorenzo Stoakes (ARM) <ljs@kernel.org> wrote:
>
> A future -j/--jobs option is planned for
> rustc:(rust-lang/compiler-team#1005), so check to see if this available and
> if so use it.
This is already implemented in nightly as far as I can tell. If it
weren't, we shouldn't add it, even if planned.
(By the way, could you please add Link tags for those URLs?)
> +config RUSTC_HAS_JOBS
> + def_bool $(rustc-option,-j8)
> +
> +config RUSTC_HAS_ZTHREADS
> + def_bool $(rustc-option,-Zthreads=8)
We can simplify using `RUSTC_VERSION` for these like we do elsewhere.
Cheers,
Miguel
^ permalink raw reply [flat|nested] 73+ messages in thread
* Re: [PATCH 20/23] kbuild: rust: parallelise rustc front end
2026-09-08 21:13 ` Miguel Ojeda
@ 2026-09-09 14:22 ` Lorenzo Stoakes (ARM)
0 siblings, 0 replies; 73+ messages in thread
From: Lorenzo Stoakes (ARM) @ 2026-09-09 14:22 UTC (permalink / raw)
To: Miguel Ojeda
Cc: Linus Torvalds, Nathan Chancellor, Nicolas Schier,
Nick Desaulniers, Bill Wendling, Justin Stitt, Masahiro Yamada,
Alexey Gladkov, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, x86, H. Peter Anvin, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Alexandre Ghiti, Arnd Bergmann, Catalin Marinas,
Will Deacon, Mark Rutland, Ard Biesheuvel, Ilias Apalodimas,
Josh Poimboeuf, Peter Zijlstra, Miguel Ojeda, Boqun Feng,
Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Alice Ryhl, Trevor Gross, Danilo Krummrich, Daniel Almeida,
Tamir Duberstein, Alexandre Courbot, Onur Özkan,
Jonathan Corbet, Randy Dunlap, linux-kbuild, linux-kernel, llvm,
linux-riscv, linux-arch, linux-arm-kernel, linux-efi,
rust-for-linux, linux-doc, Jens Axboe
On Tue, Sep 08, 2026 at 11:13:04PM +0200, Miguel Ojeda wrote:
> On Tue, Sep 8, 2026 at 10:59 PM Lorenzo Stoakes (ARM) <ljs@kernel.org> wrote:
> >
> > A future -j/--jobs option is planned for
> > rustc:(rust-lang/compiler-team#1005), so check to see if this available and
> > if so use it.
>
> This is already implemented in nightly as far as I can tell. If it
> weren't, we shouldn't add it, even if planned.
>
> (By the way, could you please add Link tags for those URLs?)
Ack, will do.
>
> > +config RUSTC_HAS_JOBS
> > + def_bool $(rustc-option,-j8)
> > +
> > +config RUSTC_HAS_ZTHREADS
> > + def_bool $(rustc-option,-Zthreads=8)
>
> We can simplify using `RUSTC_VERSION` for these like we do elsewhere.
Ack also!
>
> Cheers,
> Miguel
--
Cheers, Lorenzo
^ permalink raw reply [flat|nested] 73+ messages in thread
* Re: [PATCH 20/23] kbuild: rust: parallelise rustc front end
2026-09-08 20:55 ` [PATCH 20/23] kbuild: rust: parallelise rustc front end Lorenzo Stoakes (ARM)
2026-09-08 21:13 ` Miguel Ojeda
@ 2026-09-09 10:22 ` Björn Baron
2026-09-09 12:59 ` Miguel Ojeda
2026-09-10 12:25 ` Nicolas Schier (FRITZ!)
1 sibling, 2 replies; 73+ messages in thread
From: Björn Baron @ 2026-09-09 10:22 UTC (permalink / raw)
To: Lorenzo Stoakes (ARM)
Cc: Linus Torvalds, Nathan Chancellor, Nicolas Schier,
Nick Desaulniers, Bill Wendling, Justin Stitt, Masahiro Yamada,
Alexey Gladkov, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, x86, H. Peter Anvin, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Alexandre Ghiti, Arnd Bergmann, Catalin Marinas,
Will Deacon, Mark Rutland, Ard Biesheuvel, Ilias Apalodimas,
Josh Poimboeuf, Peter Zijlstra, Miguel Ojeda, Boqun Feng,
Gary Guo, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, Danilo Krummrich, Daniel Almeida, Tamir Duberstein,
Alexandre Courbot, Onur Özkan, Jonathan Corbet,
Randy Dunlap, linux-kbuild, linux-kernel, llvm, linux-riscv,
linux-arch, linux-arm-kernel, linux-efi, rust-for-linux,
linux-doc, Jens Axboe
On Tuesday, September 8th, 2026 at 22:59, Lorenzo Stoakes (ARM) <ljs@kernel.org> wrote:
> Use the -Zthreads option to have rustc perform builds in parallel. This has
> been available since rust 1.84 (rust-lang/rust#132282), and the kernel
> requires rust 1.85 or above.
>
> [..]
>
> Generated output was confirmed byte-for-byte identical.
There are still several known reproducibility issues for the parallel
frontend[1] and at least until fairly recently there were also deadlock
issues and incorrect errors[2], so I would not recommend enabling it without
an explicit request from the user. Perhaps even only allow it on nightly
rustc for now, then you don't need to check which of the two spellings of
the flag is supported.
By the way, currently backend parallelism is entirely disabled due to the
usage of --emit obj (which forces -Ccodegen-units=1). For -O2/3 builds
enabling backend parallelism may have a bigger impact than frontend
parallelism. And it would make incremental compilation for rustc a lot more
effective as editing a single function would no longer need to codegen all
functions in a crate again, only those inside the same codegen unit.
Getting it working would require using --emit link to produce rlibs and
then linking those rlibs in the place of the raw object files. Rlibs are
just static libraries containing some extra rustc specific info that will
be ignored by the linker (no guarantees about this, but there are no
guarantees --emit obj can be linked without missing symbols either and I
don't know of any reason why we would change it right now. It would hurt
compile times due to needing to repackage all rlibs before rustc runs
linking.). I tried implementing this a couple of years ago, but back then
if I recall correctly either binutils ar or llvm-ar was unable to produce a
thin archive referencing members of another archive like Kbuild expects to
do. I'm not familiar enough with Kbuild to know how to skip the thin
archive production.
Link: https://github.com/rust-lang/rust/issues/?q=is%3Aissue%20state%3Aopen%20label%3AA-parallel-compiler [1]
Link: https://github.com/rust-lang/rust/issues/?q=is%3Aissue%20state%3Aclosed%20label%3AA-parallel-compiler [2]
^ permalink raw reply [flat|nested] 73+ messages in thread
* Re: [PATCH 20/23] kbuild: rust: parallelise rustc front end
2026-09-09 10:22 ` Björn Baron
@ 2026-09-09 12:59 ` Miguel Ojeda
2026-09-09 14:26 ` Lorenzo Stoakes (ARM)
2026-09-10 12:25 ` Nicolas Schier (FRITZ!)
1 sibling, 1 reply; 73+ messages in thread
From: Miguel Ojeda @ 2026-09-09 12:59 UTC (permalink / raw)
To: Björn Baron
Cc: Lorenzo Stoakes (ARM),
Linus Torvalds, Nathan Chancellor, Nicolas Schier,
Nick Desaulniers, Bill Wendling, Justin Stitt, Masahiro Yamada,
Alexey Gladkov, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, x86, H. Peter Anvin, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Alexandre Ghiti, Arnd Bergmann, Catalin Marinas,
Will Deacon, Mark Rutland, Ard Biesheuvel, Ilias Apalodimas,
Josh Poimboeuf, Peter Zijlstra, Miguel Ojeda, Boqun Feng,
Gary Guo, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, Danilo Krummrich, Daniel Almeida, Tamir Duberstein,
Alexandre Courbot, Onur Özkan, Jonathan Corbet,
Randy Dunlap, linux-kbuild, linux-kernel, llvm, linux-riscv,
linux-arch, linux-arm-kernel, linux-efi, rust-for-linux,
linux-doc, Jens Axboe
On Wed, Sep 9, 2026 at 12:22 PM Björn Baron <bjorn3_gh@protonmail.com> wrote:
>
> There are still several known reproducibility issues for the parallel
> frontend[1] and at least until fairly recently there were also deadlock
> issues and incorrect errors[2], so I would not recommend enabling it without
> an explicit request from the user. Perhaps even only allow it on nightly
> rustc for now, then you don't need to check which of the two spellings of
> the flag is supported.
Thanks a lot for taking a look Björn.
If there are still major issues even in nightly, then I would say
let's not add it yet -- some users/CIs are testing with nightly
routinely, so it is probably best to avoid surprises for them. (If
someone wants to test the flag, they can still pass it manually.)
On the other hand, if there are no major issues left and it is just
reproducibility issues, then I guess we could add it to help figure
those issues out and avoid surprises later on.
> By the way, currently backend parallelism is entirely disabled due to the
> usage of --emit obj (which forces -Ccodegen-units=1). For -O2/3 builds
Yeah, we even set `-Ccodegen-units=1` explicitly at the moment, so we
would need to change that too (and likely make it an option, because
normally end users will want the best results, while developers may
want speed of iteration).
Cheers,
Miguel
^ permalink raw reply [flat|nested] 73+ messages in thread
* Re: [PATCH 20/23] kbuild: rust: parallelise rustc front end
2026-09-09 12:59 ` Miguel Ojeda
@ 2026-09-09 14:26 ` Lorenzo Stoakes (ARM)
0 siblings, 0 replies; 73+ messages in thread
From: Lorenzo Stoakes (ARM) @ 2026-09-09 14:26 UTC (permalink / raw)
To: Miguel Ojeda
Cc: Björn Baron, Linus Torvalds, Nathan Chancellor,
Nicolas Schier, Nick Desaulniers, Bill Wendling, Justin Stitt,
Masahiro Yamada, Alexey Gladkov, Thomas Gleixner, Ingo Molnar,
Borislav Petkov, Dave Hansen, x86, H. Peter Anvin, Paul Walmsley,
Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Arnd Bergmann,
Catalin Marinas, Will Deacon, Mark Rutland, Ard Biesheuvel,
Ilias Apalodimas, Josh Poimboeuf, Peter Zijlstra, Miguel Ojeda,
Boqun Feng, Gary Guo, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, Danilo Krummrich, Daniel Almeida, Tamir Duberstein,
Alexandre Courbot, Onur Özkan, Jonathan Corbet,
Randy Dunlap, linux-kbuild, linux-kernel, llvm, linux-riscv,
linux-arch, linux-arm-kernel, linux-efi, rust-for-linux,
linux-doc, Jens Axboe
On Wed, Sep 09, 2026 at 02:59:13PM +0200, Miguel Ojeda wrote:
> On Wed, Sep 9, 2026 at 12:22 PM Björn Baron <bjorn3_gh@protonmail.com> wrote:
> >
> > There are still several known reproducibility issues for the parallel
> > frontend[1] and at least until fairly recently there were also deadlock
> > issues and incorrect errors[2], so I would not recommend enabling it without
> > an explicit request from the user. Perhaps even only allow it on nightly
> > rustc for now, then you don't need to check which of the two spellings of
> > the flag is supported.
>
> Thanks a lot for taking a look Björn.
>
> If there are still major issues even in nightly, then I would say
> let's not add it yet -- some users/CIs are testing with nightly
> routinely, so it is probably best to avoid surprises for them. (If
> someone wants to test the flag, they can still pass it manually.)
>
> On the other hand, if there are no major issues left and it is just
> reproducibility issues, then I guess we could add it to help figure
> those issues out and avoid surprises later on.
Yup, I am encouraged by 'until fairly recently' on that ;) so maybe just
bump version requirement to whatever this recently was?
>
> > By the way, currently backend parallelism is entirely disabled due to the
> > usage of --emit obj (which forces -Ccodegen-units=1). For -O2/3 builds
>
> Yeah, we even set `-Ccodegen-units=1` explicitly at the moment, so we
> would need to change that too (and likely make it an option, because
> normally end users will want the best results, while developers may
> want speed of iteration).
>
> Cheers,
> Miguel
--
Cheers, Lorenzo
^ permalink raw reply [flat|nested] 73+ messages in thread
* Re: [PATCH 20/23] kbuild: rust: parallelise rustc front end
2026-09-09 10:22 ` Björn Baron
2026-09-09 12:59 ` Miguel Ojeda
@ 2026-09-10 12:25 ` Nicolas Schier (FRITZ!)
1 sibling, 0 replies; 73+ messages in thread
From: Nicolas Schier (FRITZ!) @ 2026-09-10 12:25 UTC (permalink / raw)
To: Björn Baron
Cc: Lorenzo Stoakes (ARM),
Linus Torvalds, Nathan Chancellor, Nick Desaulniers,
Bill Wendling, Justin Stitt, Masahiro Yamada, Alexey Gladkov,
Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin, Paul Walmsley, Palmer Dabbelt, Albert Ou,
Alexandre Ghiti, Arnd Bergmann, Catalin Marinas, Will Deacon,
Mark Rutland, Ard Biesheuvel, Ilias Apalodimas, Josh Poimboeuf,
Peter Zijlstra, Miguel Ojeda, Boqun Feng, Gary Guo, Benno Lossin,
Andreas Hindborg, Alice Ryhl, Trevor Gross, Danilo Krummrich,
Daniel Almeida, Tamir Duberstein, Alexandre Courbot,
Onur Özkan, Jonathan Corbet, Randy Dunlap, linux-kbuild,
linux-kernel, llvm, linux-riscv, linux-arch, linux-arm-kernel,
linux-efi, rust-for-linux, linux-doc, Jens Axboe
On Wed, Sep 09, 2026 at 10:22:33AM +0000, Björn Baron wrote:
[...]
> I tried implementing this a couple of years ago, but back then
> if I recall correctly either binutils ar or llvm-ar was unable to produce a
> thin archive referencing members of another archive like Kbuild expects to
> do. I'm not familiar enough with Kbuild to know how to skip the thin
> archive production.
You might be referencing to llvm-ar <= 14, cp.
https://git.kernel.org/stable/c/a48bd961fb20
https://github.com/llvm/llvm-project/commit/d17c54d17de22d2961a04163f3dbc8e973de89b8
--
Nicolas
^ permalink raw reply [flat|nested] 73+ messages in thread
* [PATCH 21/23] rust: make exports.o depend on the headers generated for it
2026-09-08 20:55 [PATCH 00/23] kbuild: significantly speed up kernel builds Lorenzo Stoakes (ARM)
` (19 preceding siblings ...)
2026-09-08 20:55 ` [PATCH 20/23] kbuild: rust: parallelise rustc front end Lorenzo Stoakes (ARM)
@ 2026-09-08 20:55 ` Lorenzo Stoakes (ARM)
2026-09-08 20:55 ` [PATCH 22/23] kbuild: build rust crates in parallel with the rest of the build Lorenzo Stoakes (ARM)
` (5 subsequent siblings)
26 siblings, 0 replies; 73+ messages in thread
From: Lorenzo Stoakes (ARM) @ 2026-09-08 20:55 UTC (permalink / raw)
To: Linus Torvalds, Nathan Chancellor, Nicolas Schier,
Nick Desaulniers, Bill Wendling, Justin Stitt, Masahiro Yamada,
Alexey Gladkov, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, x86, H. Peter Anvin, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Alexandre Ghiti, Arnd Bergmann, Catalin Marinas,
Will Deacon, Mark Rutland, Ard Biesheuvel, Ilias Apalodimas,
Josh Poimboeuf, Peter Zijlstra, Miguel Ojeda, Boqun Feng,
Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Alice Ryhl, Trevor Gross, Danilo Krummrich, Daniel Almeida,
Tamir Duberstein, Alexandre Courbot, Onur Özkan,
Jonathan Corbet, Randy Dunlap
Cc: linux-kbuild, linux-kernel, llvm, linux-riscv, linux-arch,
linux-arm-kernel, linux-efi, rust-for-linux, linux-doc,
Jens Axboe, Lorenzo Stoakes (ARM)
rust/exports.c includes the exports_*_generated.h listing the symbols of
the core, bindings, kernel and helpers objects, but this is not expressed
in its Makefile.
This worked previously because the headers are always-y targets, and make
prepare built all of the rust crates first, which is an implicit
dependency.
Fix this as it is a dependency of the subsequent commit.
Assisted-by: LLM
Signed-off-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
---
rust/Makefile | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/rust/Makefile b/rust/Makefile
index da1a7409d984..74d21ae6f383 100644
--- a/rust/Makefile
+++ b/rust/Makefile
@@ -728,6 +728,11 @@ endef
$(obj)/helpers/helpers.o: $(src)/helpers/helpers.c $(recordmcount_source) FORCE
+$(call if_changed_rule,rust_cc_library)
+# The exported symbol lists are generated from the crates' objects.
+$(obj)/exports.o: $(obj)/exports_core_generated.h \
+ $(obj)/exports_bindings_generated.h $(obj)/exports_kernel_generated.h \
+ $(if $(CONFIG_RUST_INLINE_HELPERS),,$(obj)/exports_helpers_generated.h)
+
# Disable symbol versioning for exports.o to avoid conflicts with the actual
# symbol versions generated from Rust objects.
$(obj)/exports.o: private skip_gendwarfksyms = 1
--
2.55.0
^ permalink raw reply [flat|nested] 73+ messages in thread* [PATCH 22/23] kbuild: build rust crates in parallel with the rest of the build
2026-09-08 20:55 [PATCH 00/23] kbuild: significantly speed up kernel builds Lorenzo Stoakes (ARM)
` (20 preceding siblings ...)
2026-09-08 20:55 ` [PATCH 21/23] rust: make exports.o depend on the headers generated for it Lorenzo Stoakes (ARM)
@ 2026-09-08 20:55 ` Lorenzo Stoakes (ARM)
2026-09-08 20:55 ` [PATCH 23/23] kbuild: use pigz for gzip compression if available Lorenzo Stoakes (ARM)
` (4 subsequent siblings)
26 siblings, 0 replies; 73+ messages in thread
From: Lorenzo Stoakes (ARM) @ 2026-09-08 20:55 UTC (permalink / raw)
To: Linus Torvalds, Nathan Chancellor, Nicolas Schier,
Nick Desaulniers, Bill Wendling, Justin Stitt, Masahiro Yamada,
Alexey Gladkov, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, x86, H. Peter Anvin, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Alexandre Ghiti, Arnd Bergmann, Catalin Marinas,
Will Deacon, Mark Rutland, Ard Biesheuvel, Ilias Apalodimas,
Josh Poimboeuf, Peter Zijlstra, Miguel Ojeda, Boqun Feng,
Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Alice Ryhl, Trevor Gross, Danilo Krummrich, Daniel Almeida,
Tamir Duberstein, Alexandre Courbot, Onur Özkan,
Jonathan Corbet, Randy Dunlap
Cc: linux-kbuild, linux-kernel, llvm, linux-riscv, linux-arch,
linux-arm-kernel, linux-efi, rust-for-linux, linux-doc,
Jens Axboe, Lorenzo Stoakes (ARM)
When CONFIG_RUST is specified make prepare builds all of the rust
components before descending into the tree.
This means nothing can be done until all of these are compiled, resulting
in a stall at the start of every clean CONFIG_RUST build.
This also implicitly slows down every rust-capable LLVM allmodconfig build
as this enables the CONFIG_RUST option.
Fix this by allowing rust crates to be built alongside the C code.
Remove the rust build from the make prepare step, then establish a
dependency between rust code located elsewhere in the tree upon rust
components contained in rust/.
Do this by establishing a top-level list of directories containing rust
code, KBUILD_RUST_DIRS, upon which the dependency is expressed.
Finally, ensure that no rust code is located elsewhere and fail the build
if any is found to ensure that nothing is missed in future.
Care is taken to ensure the rust/ dependency is also established for any
module being built to account for external rust modules which rely upon it.
Every Rust and C object was confirmed to be byte-for-byte identical after
this change.
Whole build, 128-thread Threadripper 9980X, best of N runs:
before after delta
-------------------------------
x86 defconfig+RUST, clean 36.5s 32.6s -3.9s (-11%)
x86 allmodconfig, clean 297.0s 278.3s -18.7s (-6%)
Assisted-by: LLM
Signed-off-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
---
Kbuild | 5 +++++
Makefile | 11 ++++++++++-
scripts/Makefile.build | 5 ++++-
3 files changed, 19 insertions(+), 2 deletions(-)
diff --git a/Kbuild b/Kbuild
index a6a0192dea08..9670556059dd 100644
--- a/Kbuild
+++ b/Kbuild
@@ -115,3 +115,8 @@ obj-$(CONFIG_NET) += net/
obj-y += virt/
obj-y += $(ARCH_DRIVERS)
obj-$(CONFIG_DRM_HEADER_TEST) += include/
+
+# Rust code elsewhere in the tree depends upon rust/.
+ifdef CONFIG_RUST
+$(KBUILD_RUST_DIRS): | rust
+endif
diff --git a/Makefile b/Makefile
index 7d23d7b64be3..3805f72428cb 100644
--- a/Makefile
+++ b/Makefile
@@ -1429,11 +1429,14 @@ prepare0: archprepare
$(Q)$(MAKE) $(build)=. prepare
$(Q)$(MAKE) $(build)=scripts/mod
+ifdef CONFIG_RUST
+export KBUILD_RUST_DIRS := drivers lib mm samples
+endif
+
# All the preparing..
prepare: prepare0
ifdef CONFIG_RUST
+$(Q)$(CONFIG_SHELL) $(srctree)/scripts/rust_is_available.sh
- $(Q)$(MAKE) $(build)=rust
endif
PHONY += remove-stale-files
@@ -1760,6 +1763,12 @@ modules: modules_prepare
# Target to prepare building external modules
modules_prepare: prepare
$(Q)$(MAKE) $(build)=scripts scripts/module.lds
+ifdef CONFIG_RUST
+# Ensure rust/ is build before any external rust module which will rely upon it.
+ifeq ($(MAKECMDGOALS),modules_prepare)
+ $(Q)$(MAKE) $(build)=rust
+endif
+endif
endif # CONFIG_MODULES
diff --git a/scripts/Makefile.build b/scripts/Makefile.build
index b9093b39cc2f..77351d6006fa 100644
--- a/scripts/Makefile.build
+++ b/scripts/Makefile.build
@@ -360,8 +360,11 @@ quiet_cmd_rustc_o_rs = $(RUSTC_OR_CLIPPY_QUIET) $(quiet_modtag) $@
$(cmd_ld_single)) \
$(cmd_objtool)
+rust-dir-ok = $(or $(KBUILD_EXTMOD),$(filter rust $(KBUILD_RUST_DIRS),$(firstword $(subst /, ,$@))))
+
+# Ensure that any rust code located elsewhere from rust/ is listed in KBUILD_RUST_DIRS.
define rule_rustc_o_rs
- $(call cmd_and_fixdep,rustc_o_rs)
+ $(if $(rust-dir-ok),,$(error $@: Rust code in a directory not listed in KBUILD_RUST_DIRS, see the top Makefile))$(call cmd_and_fixdep,rustc_o_rs)
$(call cmd,gen_objtooldep)
endef
--
2.55.0
^ permalink raw reply [flat|nested] 73+ messages in thread* [PATCH 23/23] kbuild: use pigz for gzip compression if available
2026-09-08 20:55 [PATCH 00/23] kbuild: significantly speed up kernel builds Lorenzo Stoakes (ARM)
` (21 preceding siblings ...)
2026-09-08 20:55 ` [PATCH 22/23] kbuild: build rust crates in parallel with the rest of the build Lorenzo Stoakes (ARM)
@ 2026-09-08 20:55 ` Lorenzo Stoakes (ARM)
2026-09-10 4:19 ` Nathan Chancellor
2026-09-08 21:06 ` [PATCH 00/23] kbuild: significantly speed up kernel builds Nick Desaulniers
` (3 subsequent siblings)
26 siblings, 1 reply; 73+ messages in thread
From: Lorenzo Stoakes (ARM) @ 2026-09-08 20:55 UTC (permalink / raw)
To: Linus Torvalds, Nathan Chancellor, Nicolas Schier,
Nick Desaulniers, Bill Wendling, Justin Stitt, Masahiro Yamada,
Alexey Gladkov, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, x86, H. Peter Anvin, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Alexandre Ghiti, Arnd Bergmann, Catalin Marinas,
Will Deacon, Mark Rutland, Ard Biesheuvel, Ilias Apalodimas,
Josh Poimboeuf, Peter Zijlstra, Miguel Ojeda, Boqun Feng,
Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Alice Ryhl, Trevor Gross, Danilo Krummrich, Daniel Almeida,
Tamir Duberstein, Alexandre Courbot, Onur Özkan,
Jonathan Corbet, Randy Dunlap
Cc: linux-kbuild, linux-kernel, llvm, linux-riscv, linux-arch,
linux-arm-kernel, linux-efi, rust-for-linux, linux-doc,
Jens Axboe, Lorenzo Stoakes (ARM)
The gzip step of a kernel build is very lengthily, especially for larger
builds such as allmodconfig.
gzip itself cannot be run in parallel, however an alternative tool exists
that can, providing the same feature set as gzip itself - pigz - which
works as a drop-in replacement.
On a 128-core Threadripper, gzip -9 of a 36 MiB x86-64 vmlinux.bin takes
1.6s, and with pigz it takes 0.09s, so the performance increase is
significant.
It is already possible to specify the KGZIP environmental variable to make
use of pigz, however it seems sensible to make use of pigz if it is
available.
Therefore default to using pigz if it is available on the system upon which
the kernel is being built, otherwise fall back to gzip.
The output is byte-for-byte identical between pigz/gzip invocations, but
gzip and pigz do not produce the same stream as one another.
Therefore, for reproducible builds, the same set of tools should be used.
This seems to already be an implicit requirement in any case, but update
the reproducible build documentation to make this clear.
Also update the kbuild documentation to reflect the change.
Every x86 build ends with the compression of vmlinux.bin, 36MB for
defconfig and over 200MB for allmodconfig, no-op builds are unchanged.
Whole build, 128-thread Threadripper 9980X, best of N runs:
before after delta
-------------------------------
x86 defconfig, touch mm/vma.c, gcc 7.4s 5.4s -2.0s (-27%)
x86 defconfig, touch mm/vma.c, clang 6.6s 4.9s -1.7s (-26%)
x86 defconfig, clean, gcc 26.2s 24.4s -1.8s (-7%)
x86 defconfig, clean, clang 25.8s 24.3s -1.5s (-6%)
x86 allmodconfig, touch mm/vma.c, gcc 23.7s 15.3s -8.4s (-35%)
x86 allmodconfig, touch mm/vma.c, clang 22.1s 15.2s -6.9s (-31%)
x86 allmodconfig, clean, gcc 291.4s 275.2s -16.2s (-6%)
x86 allmodconfig, clean, clang 278.3s 265.7s -12.6s (-5%)
Link: https://zlib.net/pigz/
Assisted-by: LLM
Signed-off-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
---
Documentation/kbuild/kbuild.rst | 13 +++++++++++++
Documentation/kbuild/reproducible-builds.rst | 16 ++++++++++++++++
Makefile | 2 +-
3 files changed, 30 insertions(+), 1 deletion(-)
diff --git a/Documentation/kbuild/kbuild.rst b/Documentation/kbuild/kbuild.rst
index 5a9013bacfb7..ebfc2319d6da 100644
--- a/Documentation/kbuild/kbuild.rst
+++ b/Documentation/kbuild/kbuild.rst
@@ -110,6 +110,19 @@ HOSTLDLIBS
----------
Additional libraries to link against when building host programs.
+KGZIP
+-----
+The gzip compressor used for the compressed kernel image, compressed
+modules and packaging.
+
+If unset, it defaults to pigz (a parallel implementation of gzip) if available,
+otherwise gzip.
+
+KBZIP2, KLZOP, LZMA, LZ4, XZ, ZSTD
+----------------------------------
+The compressor programs for the other formats. Each defaults to the program
+of the same name.
+
.. _userkbuildflags:
USERCFLAGS
diff --git a/Documentation/kbuild/reproducible-builds.rst b/Documentation/kbuild/reproducible-builds.rst
index bc1eb82211df..b95586df2cb5 100644
--- a/Documentation/kbuild/reproducible-builds.rst
+++ b/Documentation/kbuild/reproducible-builds.rst
@@ -76,6 +76,22 @@ include generated files. You should ensure the source tree is
pristine by running ``make mrproper`` or ``git clean -d -f -x`` before
building a source package.
+Compression tools
+-----------------
+
+The compressed kernel image, compressed modules and packages are produced
+using the binary specified by the environment variable ``KGZIP`` (described
+in Documentation/kbuild/kbuild.rst).
+
+This variable defaults to ``pigz`` if installed (a parallel implementation
+of gzip), or ``gzip`` otherwise.
+
+The generated output between two invocations of identical builds with
+either of the default tools will be byte-for-byte equivalent.
+
+However, for reproducible builds, ensure the same tool is used on all build
+hosts, as different tools may generate different output from one another.
+
Module signing
--------------
diff --git a/Makefile b/Makefile
index 3805f72428cb..5cb819034372 100644
--- a/Makefile
+++ b/Makefile
@@ -561,7 +561,7 @@ PERL = perl
PYTHON3 = python3
CHECK = sparse
BASH = bash
-KGZIP = gzip
+KGZIP := $(if $(shell command -v pigz 2>/dev/null),pigz,gzip)
KBZIP2 = bzip2
KLZOP = lzop
LZMA = lzma
--
2.55.0
^ permalink raw reply [flat|nested] 73+ messages in thread* Re: [PATCH 23/23] kbuild: use pigz for gzip compression if available
2026-09-08 20:55 ` [PATCH 23/23] kbuild: use pigz for gzip compression if available Lorenzo Stoakes (ARM)
@ 2026-09-10 4:19 ` Nathan Chancellor
2026-09-11 11:03 ` Lorenzo Stoakes (ARM)
0 siblings, 1 reply; 73+ messages in thread
From: Nathan Chancellor @ 2026-09-10 4:19 UTC (permalink / raw)
To: Lorenzo Stoakes (ARM)
Cc: Linus Torvalds, Nathan Chancellor, Nicolas Schier,
Nick Desaulniers, Bill Wendling, Justin Stitt, Masahiro Yamada,
Alexey Gladkov, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, x86, H. Peter Anvin, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Alexandre Ghiti, Arnd Bergmann, Catalin Marinas,
Will Deacon, Mark Rutland, Ard Biesheuvel, Ilias Apalodimas,
Josh Poimboeuf, Peter Zijlstra, Miguel Ojeda, Boqun Feng,
Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Alice Ryhl, Trevor Gross, Danilo Krummrich, Daniel Almeida,
Tamir Duberstein, Alexandre Courbot, Onur Özkan,
Jonathan Corbet, Randy Dunlap, linux-kbuild, linux-kernel, llvm,
linux-riscv, linux-arch, linux-arm-kernel, linux-efi,
rust-for-linux, linux-doc, Jens Axboe
> The gzip step of a kernel build is very lengthily, especially for larger
> builds such as allmodconfig.
>
> gzip itself cannot be run in parallel, however an alternative tool exists
> that can, providing the same feature set as gzip itself - pigz - which
> works as a drop-in replacement.
>
> On a 128-core Threadripper, gzip -9 of a 36 MiB x86-64 vmlinux.bin takes
> 1.6s, and with pigz it takes 0.09s, so the performance increase is
> significant.
>
> It is already possible to specify the KGZIP environmental variable to make
> use of pigz, however it seems sensible to make use of pigz if it is
> available.
>
> Therefore default to using pigz if it is available on the system upon which
> the kernel is being built, otherwise fall back to gzip.
>
> The output is byte-for-byte identical between pigz/gzip invocations, but
> gzip and pigz do not produce the same stream as one another.
>
> Therefore, for reproducible builds, the same set of tools should be used.
>
> This seems to already be an implicit requirement in any case, but update
> the reproducible build documentation to make this clear.
>
> Also update the kbuild documentation to reflect the change.
>
> Every x86 build ends with the compression of vmlinux.bin, 36MB for
> defconfig and over 200MB for allmodconfig, no-op builds are unchanged.
>
> Whole build, 128-thread Threadripper 9980X, best of N runs:
>
> before after delta
> -------------------------------
> x86 defconfig, touch mm/vma.c, gcc 7.4s 5.4s -2.0s (-27%)
> x86 defconfig, touch mm/vma.c, clang 6.6s 4.9s -1.7s (-26%)
> x86 defconfig, clean, gcc 26.2s 24.4s -1.8s (-7%)
> x86 defconfig, clean, clang 25.8s 24.3s -1.5s (-6%)
> x86 allmodconfig, touch mm/vma.c, gcc 23.7s 15.3s -8.4s (-35%)
> x86 allmodconfig, touch mm/vma.c, clang 22.1s 15.2s -6.9s (-31%)
> x86 allmodconfig, clean, gcc 291.4s 275.2s -16.2s (-6%)
> x86 allmodconfig, clean, clang 278.3s 265.7s -12.6s (-5%)
>
> Link: https://zlib.net/pigz/
> Assisted-by: LLM
> Signed-off-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
...
> diff --git a/Makefile b/Makefile
> index a0d29dee81d7..efa6a2d77e6b 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -561,7 +561,7 @@ PERL = perl
> PYTHON3 = python3
> CHECK = sparse
> BASH = bash
> -KGZIP = gzip
> +KGZIP := $(if $(shell command -v pigz 2>/dev/null),pigz,gzip)
This should be '?=', right? Sashiko also notes a concern around parallel
module installation with gzip module compression since there is no
thread limitation, which could bring a little risk to doing this by
default? Might not be that big of a deal though?
--
Cheers,
Nathan
^ permalink raw reply [flat|nested] 73+ messages in thread
* Re: [PATCH 23/23] kbuild: use pigz for gzip compression if available
2026-09-10 4:19 ` Nathan Chancellor
@ 2026-09-11 11:03 ` Lorenzo Stoakes (ARM)
0 siblings, 0 replies; 73+ messages in thread
From: Lorenzo Stoakes (ARM) @ 2026-09-11 11:03 UTC (permalink / raw)
To: Nathan Chancellor
Cc: Linus Torvalds, Nicolas Schier, Nick Desaulniers, Bill Wendling,
Justin Stitt, Masahiro Yamada, Alexey Gladkov, Thomas Gleixner,
Ingo Molnar, Borislav Petkov, Dave Hansen, x86, H. Peter Anvin,
Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti,
Arnd Bergmann, Catalin Marinas, Will Deacon, Mark Rutland,
Ard Biesheuvel, Ilias Apalodimas, Josh Poimboeuf, Peter Zijlstra,
Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Daniel Almeida, Tamir Duberstein,
Alexandre Courbot, Onur Özkan, Jonathan Corbet,
Randy Dunlap, linux-kbuild, linux-kernel, llvm, linux-riscv,
linux-arch, linux-arm-kernel, linux-efi, rust-for-linux,
linux-doc, Jens Axboe
On Wed, Sep 09, 2026 at 09:19:12PM -0700, Nathan Chancellor wrote:
> > The gzip step of a kernel build is very lengthily, especially for larger
> > builds such as allmodconfig.
> >
> > gzip itself cannot be run in parallel, however an alternative tool exists
> > that can, providing the same feature set as gzip itself - pigz - which
> > works as a drop-in replacement.
> >
> > On a 128-core Threadripper, gzip -9 of a 36 MiB x86-64 vmlinux.bin takes
> > 1.6s, and with pigz it takes 0.09s, so the performance increase is
> > significant.
> >
> > It is already possible to specify the KGZIP environmental variable to make
> > use of pigz, however it seems sensible to make use of pigz if it is
> > available.
> >
> > Therefore default to using pigz if it is available on the system upon which
> > the kernel is being built, otherwise fall back to gzip.
> >
> > The output is byte-for-byte identical between pigz/gzip invocations, but
> > gzip and pigz do not produce the same stream as one another.
> >
> > Therefore, for reproducible builds, the same set of tools should be used.
> >
> > This seems to already be an implicit requirement in any case, but update
> > the reproducible build documentation to make this clear.
> >
> > Also update the kbuild documentation to reflect the change.
> >
> > Every x86 build ends with the compression of vmlinux.bin, 36MB for
> > defconfig and over 200MB for allmodconfig, no-op builds are unchanged.
> >
> > Whole build, 128-thread Threadripper 9980X, best of N runs:
> >
> > before after delta
> > -------------------------------
> > x86 defconfig, touch mm/vma.c, gcc 7.4s 5.4s -2.0s (-27%)
> > x86 defconfig, touch mm/vma.c, clang 6.6s 4.9s -1.7s (-26%)
> > x86 defconfig, clean, gcc 26.2s 24.4s -1.8s (-7%)
> > x86 defconfig, clean, clang 25.8s 24.3s -1.5s (-6%)
> > x86 allmodconfig, touch mm/vma.c, gcc 23.7s 15.3s -8.4s (-35%)
> > x86 allmodconfig, touch mm/vma.c, clang 22.1s 15.2s -6.9s (-31%)
> > x86 allmodconfig, clean, gcc 291.4s 275.2s -16.2s (-6%)
> > x86 allmodconfig, clean, clang 278.3s 265.7s -12.6s (-5%)
> >
> > Link: https://zlib.net/pigz/
> > Assisted-by: LLM
> > Signed-off-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
> ...
> > diff --git a/Makefile b/Makefile
> > index a0d29dee81d7..efa6a2d77e6b 100644
> > --- a/Makefile
> > +++ b/Makefile
> > @@ -561,7 +561,7 @@ PERL = perl
> > PYTHON3 = python3
> > CHECK = sparse
> > BASH = bash
> > -KGZIP = gzip
> > +KGZIP := $(if $(shell command -v pigz 2>/dev/null),pigz,gzip)
>
> This should be '?=', right? Sashiko also notes a concern around parallel
Actually make KGZIP=foo will override this still. But it doesn't take an
environment variable for make -e.
However, that's consistent with other variables there like KBZIP2 etc.
Probably the wording in the docs should be tweaked!
> module installation with gzip module compression since there is no
> thread limitation, which could bring a little risk to doing this by
> default? Might not be that big of a deal though?
Yeah, will modify so it single threads for things that aren't vmlinux on
respin!
>
> --
> Cheers,
> Nathan
>
--
Cheers, Lorenzo
^ permalink raw reply [flat|nested] 73+ messages in thread
* Re: [PATCH 00/23] kbuild: significantly speed up kernel builds
2026-09-08 20:55 [PATCH 00/23] kbuild: significantly speed up kernel builds Lorenzo Stoakes (ARM)
` (22 preceding siblings ...)
2026-09-08 20:55 ` [PATCH 23/23] kbuild: use pigz for gzip compression if available Lorenzo Stoakes (ARM)
@ 2026-09-08 21:06 ` Nick Desaulniers
2026-09-09 14:17 ` Lorenzo Stoakes (ARM)
2026-09-09 15:37 ` Linus Torvalds
` (2 subsequent siblings)
26 siblings, 1 reply; 73+ messages in thread
From: Nick Desaulniers @ 2026-09-08 21:06 UTC (permalink / raw)
To: Lorenzo Stoakes (ARM), Nathan Chancellor
Cc: Linus Torvalds, Nicolas Schier, Bill Wendling, Justin Stitt,
Masahiro Yamada, Alexey Gladkov, Thomas Gleixner, Ingo Molnar,
Borislav Petkov, Dave Hansen, x86, H. Peter Anvin, Paul Walmsley,
Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Arnd Bergmann,
Catalin Marinas, Will Deacon, Mark Rutland, Ard Biesheuvel,
Ilias Apalodimas, Josh Poimboeuf, Peter Zijlstra, Miguel Ojeda,
Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin,
Andreas Hindborg, Alice Ryhl, Trevor Gross, Danilo Krummrich,
Daniel Almeida, Tamir Duberstein, Alexandre Courbot,
Onur Özkan, Jonathan Corbet, Randy Dunlap, linux-kbuild,
linux-kernel, llvm, linux-riscv, linux-arch, linux-arm-kernel,
linux-efi, rust-for-linux, linux-doc, Jens Axboe
On Tue, Sep 8, 2026 at 1:55 PM Lorenzo Stoakes (ARM) <ljs@kernel.org> wrote:
>
> A typical kernel build consists of a frustratingly large amount of time
> spent stuck in single-threaded bottlenecks.
>
> It turns out that there's a lot we can do about this and doing so
> significantly impacts kernel build times.
> Fundamentally the series improves build times by parallelising
> single-threaded tasks as much as possible and improving the efficiency of
> code used in the build process.
Cool! I have not reviewed this patch series, but am supportive of
solving the problem, since I do recall this being an issue;
i.e. it always annoyed me that despite have a 128 core machine, much
of the build is sequential! Having all those cores and not saturating
them during a build was always something I noticed (usually have
system monitor always showing system load, always) but never had time
to look into.
It would be cool if your LLM could generate any visualizations of
what's going on during a build? Seeing what's serial vs parallelized
in perhaps useful. Particularly when you can compare the before vs
after.
--
Thanks,
~Nick Desaulniers
^ permalink raw reply [flat|nested] 73+ messages in thread* Re: [PATCH 00/23] kbuild: significantly speed up kernel builds
2026-09-08 21:06 ` [PATCH 00/23] kbuild: significantly speed up kernel builds Nick Desaulniers
@ 2026-09-09 14:17 ` Lorenzo Stoakes (ARM)
2026-09-09 22:09 ` Nick Desaulniers
0 siblings, 1 reply; 73+ messages in thread
From: Lorenzo Stoakes (ARM) @ 2026-09-09 14:17 UTC (permalink / raw)
To: Nick Desaulniers
Cc: Nathan Chancellor, Linus Torvalds, Nicolas Schier, Bill Wendling,
Justin Stitt, Masahiro Yamada, Alexey Gladkov, Thomas Gleixner,
Ingo Molnar, Borislav Petkov, Dave Hansen, x86, H. Peter Anvin,
Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti,
Arnd Bergmann, Catalin Marinas, Will Deacon, Mark Rutland,
Ard Biesheuvel, Ilias Apalodimas, Josh Poimboeuf, Peter Zijlstra,
Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Daniel Almeida, Tamir Duberstein,
Alexandre Courbot, Onur Özkan, Jonathan Corbet,
Randy Dunlap, linux-kbuild, linux-kernel, llvm, linux-riscv,
linux-arch, linux-arm-kernel, linux-efi, rust-for-linux,
linux-doc, Jens Axboe
[-- Attachment #1: Type: text/plain, Size: 3803 bytes --]
On Tue, Sep 08, 2026 at 02:06:18PM -0700, Nick Desaulniers wrote:
> On Tue, Sep 8, 2026 at 1:55 PM Lorenzo Stoakes (ARM) <ljs@kernel.org> wrote:
> >
> > A typical kernel build consists of a frustratingly large amount of time
> > spent stuck in single-threaded bottlenecks.
> >
> > It turns out that there's a lot we can do about this and doing so
> > significantly impacts kernel build times.
>
>
> > Fundamentally the series improves build times by parallelising
> > single-threaded tasks as much as possible and improving the efficiency of
> > code used in the build process.
>
> Cool! I have not reviewed this patch series, but am supportive of
> solving the problem, since I do recall this being an issue;
Thanks :)
>
> i.e. it always annoyed me that despite have a 128 core machine, much
> of the build is sequential! Having all those cores and not saturating
> them during a build was always something I noticed (usually have
> system monitor always showing system load, always) but never had time
> to look into.
Yeah this was exactly the motivation.
I've seen single-threaded stalls endlessly in kernel builds, even on smaller
boxes, and thought that there must be a lot of low-hanging fruit there.
Obviously I have not enough time to do anything extra (TM) so never dug
into it.
Which made it a great candidate for LLM usage (though with a LOT of taming,
reviewing, auditing, rewriting of code because I _still_ have to be able to
maintain my changes and there's still a bar there :)
>
> It would be cool if your LLM could generate any visualizations of
> what's going on during a build? Seeing what's serial vs parallelized
> in perhaps useful. Particularly when you can compare the before vs
> after.
Great idea!
I asked it to do exactly that, attached. Came out quite nice and sums it up
nicely :)
It also provided an ASCII version:
x86 allmodconfig, clang, 128 threads. One character per interval: height = CPU utilisation,
letter beneath = what was running (C compile, L link, O objtool, P modpost, K kallsyms, Z compress, m make, . idle)
== clean build (4s per character) ==
before ▁▁▁▁▁▂█████████████████████████████████████████████████████████▃▁▁▁▁▁▁▁███████████▇▅▁▁ 343.6s
mCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCOOOOPPPCCCCCCCCCCCCCLL
after ▁▆████████████████████████████████████████████████████████▇▁▁▁▄██▆▁ 265.6s
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCOOCCCCC
== touch mm/vma.c (0.5s per character) ==
before ▁▁▁█▂▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ 45.7s
mCmCCmmmCLOOOOOOOOOOOOOOOOOOOOmPPPPPPPPPPPPPPPPPPPCLmKKKCCCmmKKKCCCLmmmmmmCCZZZZZZZZZZZZZZC
after ▁▁▅▁▁▁▁▁▁▁▁▁▁▁▁▁▁▂▁▁▁▁▁▁▁▁▁▁▁▁▂▁ 15.7s
mmCCCLOOOOOOOOOPPPPLKCKCLmmmmmCC
== no-op make (0.2s per character) ==
before ▁▁▁▁▁▁▁███▂▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ 14.4s
CCCCmmmCCCmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm
after ▁▁▁▁▄▆▄▁ 2.0s
CCmmmCmm
> --
> Thanks,
> ~Nick Desaulniers
--
Cheers, Lorenzo
[-- Attachment #2: build-utilisation.png --]
[-- Type: image/png, Size: 153235 bytes --]
[-- Attachment #3: build-utilisation.svg --]
[-- Type: image/svg+xml, Size: 87826 bytes --]
^ permalink raw reply [flat|nested] 73+ messages in thread* Re: [PATCH 00/23] kbuild: significantly speed up kernel builds
2026-09-09 14:17 ` Lorenzo Stoakes (ARM)
@ 2026-09-09 22:09 ` Nick Desaulniers
2026-09-11 11:25 ` Lorenzo Stoakes (ARM)
0 siblings, 1 reply; 73+ messages in thread
From: Nick Desaulniers @ 2026-09-09 22:09 UTC (permalink / raw)
To: Lorenzo Stoakes (ARM)
Cc: Nathan Chancellor, Linus Torvalds, Nicolas Schier, Bill Wendling,
Justin Stitt, Masahiro Yamada, Alexey Gladkov, Thomas Gleixner,
Ingo Molnar, Borislav Petkov, Dave Hansen, x86, H. Peter Anvin,
Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti,
Arnd Bergmann, Catalin Marinas, Will Deacon, Mark Rutland,
Ard Biesheuvel, Ilias Apalodimas, Josh Poimboeuf, Peter Zijlstra,
Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Daniel Almeida, Tamir Duberstein,
Alexandre Courbot, Onur Özkan, Jonathan Corbet,
Randy Dunlap, linux-kbuild, linux-kernel, llvm, linux-riscv,
linux-arch, linux-arm-kernel, linux-efi, rust-for-linux,
linux-doc, Jens Axboe
On Wed, Sep 9, 2026 at 7:17 AM Lorenzo Stoakes (ARM) <ljs@kernel.org> wrote:
>
> On Tue, Sep 08, 2026 at 02:06:18PM -0700, Nick Desaulniers wrote:
> > It would be cool if your LLM could generate any visualizations of
> > what's going on during a build? Seeing what's serial vs parallelized
> > in perhaps useful. Particularly when you can compare the before vs
> > after.
>
> Great idea!
>
> I asked it to do exactly that, attached. Came out quite nice and sums it up
> nicely :)
>
> It also provided an ASCII version:
unreadable, but the pictures you attached are worth a thousand words.
Particularly exciting are the speed ups to the usual edit+compile+run
loop.
For the blue graph (the top one, first), any idea what that dip in the
middle is? That's always what annoyed me.
--
Thanks,
~Nick Desaulniers
^ permalink raw reply [flat|nested] 73+ messages in thread
* Re: [PATCH 00/23] kbuild: significantly speed up kernel builds
2026-09-09 22:09 ` Nick Desaulniers
@ 2026-09-11 11:25 ` Lorenzo Stoakes (ARM)
0 siblings, 0 replies; 73+ messages in thread
From: Lorenzo Stoakes (ARM) @ 2026-09-11 11:25 UTC (permalink / raw)
To: Nick Desaulniers
Cc: Nathan Chancellor, Linus Torvalds, Nicolas Schier, Bill Wendling,
Justin Stitt, Masahiro Yamada, Alexey Gladkov, Thomas Gleixner,
Ingo Molnar, Borislav Petkov, Dave Hansen, x86, H. Peter Anvin,
Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti,
Arnd Bergmann, Catalin Marinas, Will Deacon, Mark Rutland,
Ard Biesheuvel, Ilias Apalodimas, Josh Poimboeuf, Peter Zijlstra,
Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Daniel Almeida, Tamir Duberstein,
Alexandre Courbot, Onur Özkan, Jonathan Corbet,
Randy Dunlap, linux-kbuild, linux-kernel, llvm, linux-riscv,
linux-arch, linux-arm-kernel, linux-efi, rust-for-linux,
linux-doc, Jens Axboe
On Wed, Sep 09, 2026 at 03:09:10PM -0700, Nick Desaulniers wrote:
> On Wed, Sep 9, 2026 at 7:17 AM Lorenzo Stoakes (ARM) <ljs@kernel.org> wrote:
> >
> > On Tue, Sep 08, 2026 at 02:06:18PM -0700, Nick Desaulniers wrote:
> > > It would be cool if your LLM could generate any visualizations of
> > > what's going on during a build? Seeing what's serial vs parallelized
> > > in perhaps useful. Particularly when you can compare the before vs
> > > after.
> >
> > Great idea!
> >
> > I asked it to do exactly that, attached. Came out quite nice and sums it up
> > nicely :)
> >
> > It also provided an ASCII version:
>
> unreadable, but the pictures you attached are worth a thousand words.
:) Yeah I didn't think the ASCII was amazing either but fun that it tried ;) and
yeah I was surprised at how nice the graphic came out...!
> Particularly exciting are the speed ups to the usual edit+compile+run
> loop.
Yes! I never really expected that out of this!
I mean I didn't think there'd be such a big speed up in general, though I
expected at least to shave off a big chunk of the delta between compile + full
build on a defconfig clean build.
But the incremental/noop build improvements are really important to day-to-day
use.
>
> For the blue graph (the top one, first), any idea what that dip in the
> middle is? That's always what annoyed me.
That's ld -r of vmlinux.o, then modpost and objtool which are critical path
blockers on the rest of the build.
Before the series that was 23 seconds for allmodconfig on the threadripper, now
7s :)
There's still more objtool stuff that could be done that I opted not to do yet,
it takes 6s to run so dominating.
> --
> Thanks,
> ~Nick Desaulniers
--
Cheers, Lorenzo
^ permalink raw reply [flat|nested] 73+ messages in thread
* Re: [PATCH 00/23] kbuild: significantly speed up kernel builds
2026-09-08 20:55 [PATCH 00/23] kbuild: significantly speed up kernel builds Lorenzo Stoakes (ARM)
` (23 preceding siblings ...)
2026-09-08 21:06 ` [PATCH 00/23] kbuild: significantly speed up kernel builds Nick Desaulniers
@ 2026-09-09 15:37 ` Linus Torvalds
2026-09-09 16:30 ` Lorenzo Stoakes (ARM)
2026-09-09 21:58 ` Florian Fainelli
2026-09-10 4:19 ` Nathan Chancellor
26 siblings, 1 reply; 73+ messages in thread
From: Linus Torvalds @ 2026-09-09 15:37 UTC (permalink / raw)
To: Lorenzo Stoakes (ARM)
Cc: Nathan Chancellor, Nicolas Schier, Nick Desaulniers,
Bill Wendling, Justin Stitt, Masahiro Yamada, Alexey Gladkov,
Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin, Paul Walmsley, Palmer Dabbelt, Albert Ou,
Alexandre Ghiti, Arnd Bergmann, Catalin Marinas, Will Deacon,
Mark Rutland, Ard Biesheuvel, Ilias Apalodimas, Josh Poimboeuf,
Peter Zijlstra, Miguel Ojeda, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, Danilo Krummrich, Daniel Almeida, Tamir Duberstein,
Alexandre Courbot, Onur Özkan, Jonathan Corbet,
Randy Dunlap, linux-kbuild, linux-kernel, llvm, linux-riscv,
linux-arch, linux-arm-kernel, linux-efi, rust-for-linux,
linux-doc, Jens Axboe
On Tue, 8 Sept 2026 at 13:55, Lorenzo Stoakes (ARM) <ljs@kernel.org> wrote:
>
> A typical kernel build consists of a frustratingly large amount of time
.. you're preaching to the choir.
> == allmodconfig FULL build ==
>
> before after delta
> ----------------------------------
> Threadripper, gcc 345.3s 275.2s -70.1s (-20%)
Well, that's certainly very encouraging. We should do this. I sent out
some emails as just reactions to individual patches, but I have no
numbers to back any of those emails up, and I never actually applied
this series to my tree - they were all based on reading the patches
themselves.
That said:
> An LLM was used to first determine where the bottlenecks were then to
> figure out how to improve them.
>
> It generated a lot of code, much of it hideous.
This made me scared to look at the patches originally, and I held off
in fear that the patches would be horrible and this build time
improvement would be hugely controversial garbage code.
But:
> I extensively audited and rewrote a lot of it, and heavily edited commit
> messages, the cover letter and comments.
None of the patches look at all horrible to me. You clearly excised
the hideous parts. All of my reactions were of the type "this could
probably be taken _further_" rather than me throwing my hands up in
disgust.
So the Rust parallelism thing clearly isn't ready based on feedback
from that quarter, but the rest looked safe and innocuous. I'm all for
merging this, although it should obviously go in through the right
channels. Mostly the kbuild tree, although some of it clearly would be
other cases - the objtool change in particular is fairly substantial
and needsobjtool people to approve. It didn't look all that
contentious, but still..
Anyway, I'd love for this all to go in. Build times are a pet peeve of mine.
Linus
^ permalink raw reply [flat|nested] 73+ messages in thread* Re: [PATCH 00/23] kbuild: significantly speed up kernel builds
2026-09-09 15:37 ` Linus Torvalds
@ 2026-09-09 16:30 ` Lorenzo Stoakes (ARM)
0 siblings, 0 replies; 73+ messages in thread
From: Lorenzo Stoakes (ARM) @ 2026-09-09 16:30 UTC (permalink / raw)
To: Linus Torvalds
Cc: Nathan Chancellor, Nicolas Schier, Nick Desaulniers,
Bill Wendling, Justin Stitt, Masahiro Yamada, Alexey Gladkov,
Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin, Paul Walmsley, Palmer Dabbelt, Albert Ou,
Alexandre Ghiti, Arnd Bergmann, Catalin Marinas, Will Deacon,
Mark Rutland, Ard Biesheuvel, Ilias Apalodimas, Josh Poimboeuf,
Peter Zijlstra, Miguel Ojeda, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, Danilo Krummrich, Daniel Almeida, Tamir Duberstein,
Alexandre Courbot, Onur Özkan, Jonathan Corbet,
Randy Dunlap, linux-kbuild, linux-kernel, llvm, linux-riscv,
linux-arch, linux-arm-kernel, linux-efi, rust-for-linux,
linux-doc, Jens Axboe
On Wed, Sep 09, 2026 at 08:37:10AM -0700, Linus Torvalds wrote:
> On Tue, 8 Sept 2026 at 13:55, Lorenzo Stoakes (ARM) <ljs@kernel.org> wrote:
> >
> > A typical kernel build consists of a frustratingly large amount of time
>
> .. you're preaching to the choir.
:) Yeah, it's something that's annoyed me for a long time too.
>
> > == allmodconfig FULL build ==
> >
> > before after delta
> > ----------------------------------
> > Threadripper, gcc 345.3s 275.2s -70.1s (-20%)
>
> Well, that's certainly very encouraging. We should do this. I sent out
> some emails as just reactions to individual patches, but I have no
> numbers to back any of those emails up, and I never actually applied
> this series to my tree - they were all based on reading the patches
> themselves.
Thanks, will have a read through those!
I do recommend trying them locally ;)
It makes a surprising difference to defconfig even. (Though you need pigz
installed for the full effect)
>
> That said:
>
> > An LLM was used to first determine where the bottlenecks were then to
> > figure out how to improve them.
> >
> > It generated a lot of code, much of it hideous.
>
> This made me scared to look at the patches originally, and I held off
> in fear that the patches would be horrible and this build time
> improvement would be hugely controversial garbage code.
>
> But:
>
> > I extensively audited and rewrote a lot of it, and heavily edited commit
> > messages, the cover letter and comments.
>
> None of the patches look at all horrible to me. You clearly excised
> the hideous parts. All of my reactions were of the type "this could
> probably be taken _further_" rather than me throwing my hands up in
> disgust.
Thanks! :)
I made sure to audit everything it came up with patch-by-patch, got it to
rework the horrors then rewrote large chunks of what it came up with.
>
> So the Rust parallelism thing clearly isn't ready based on feedback
> from that quarter, but the rest looked safe and innocuous. I'm all for
Sure, happy to drop anything like that.
Though based on what Miguel said it _might_ be ok perhaps with a nightly
gate on it? Will see what he/Bjorn say.
> merging this, although it should obviously go in through the right
> channels. Mostly the kbuild tree, although some of it clearly would be
> other cases - the objtool change in particular is fairly substantial
> and needsobjtool people to approve. It didn't look all that
> contentious, but still..
Agreed, it definitely needs some eyes on it, especially the trickier bits.
>
> Anyway, I'd love for this all to go in. Build times are a pet peeve of mine.
Yeah, it'd be nice to see this benefit everyone!
Am happy to iterate on it as needed to get it upstream.
>
> Linus
--
Cheers, Lorenzo
^ permalink raw reply [flat|nested] 73+ messages in thread
* Re: [PATCH 00/23] kbuild: significantly speed up kernel builds
2026-09-08 20:55 [PATCH 00/23] kbuild: significantly speed up kernel builds Lorenzo Stoakes (ARM)
` (24 preceding siblings ...)
2026-09-09 15:37 ` Linus Torvalds
@ 2026-09-09 21:58 ` Florian Fainelli
2026-09-11 11:28 ` Lorenzo Stoakes (ARM)
2026-09-10 4:19 ` Nathan Chancellor
26 siblings, 1 reply; 73+ messages in thread
From: Florian Fainelli @ 2026-09-09 21:58 UTC (permalink / raw)
To: Lorenzo Stoakes (ARM),
Linus Torvalds, Nathan Chancellor, Nicolas Schier,
Nick Desaulniers, Bill Wendling, Justin Stitt, Masahiro Yamada,
Alexey Gladkov, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, x86, H. Peter Anvin, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Alexandre Ghiti, Arnd Bergmann, Catalin Marinas,
Will Deacon, Mark Rutland, Ard Biesheuvel, Ilias Apalodimas,
Josh Poimboeuf, Peter Zijlstra, Miguel Ojeda, Boqun Feng,
Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Alice Ryhl, Trevor Gross, Danilo Krummrich, Daniel Almeida,
Tamir Duberstein, Alexandre Courbot, Onur Özkan,
Jonathan Corbet, Randy Dunlap
Cc: linux-kbuild, linux-kernel, llvm, linux-riscv, linux-arch,
linux-arm-kernel, linux-efi, rust-for-linux, linux-doc,
Jens Axboe
On 9/8/26 13:55, Lorenzo Stoakes (ARM) wrote:
> A typical kernel build consists of a frustratingly large amount of time
> spent stuck in single-threaded bottlenecks.
>
> It turns out that there's a lot we can do about this and doing so
> significantly impacts kernel build times.
>
> This series makes allmodconfig builds up to 36% faster, incremental builds
> up to ~70% faster, and noop builds up to ~90% faster.
>
> Builds are faster across the board on every device I tested.
>
> Machines used for perf testing:
>
> * Threadripper - x86, AMD Threadripper 9980X, 64 cores, 128 threads
> * EPYC - x86, 2 socket EPYC 9754, 256 cores, 512 threads
> * M2 - arm64, 2022 M2 macbook pro, 8 cores, 8 threads
> (4 perf, 4 efficiency)
My AMD EPYC 9454P server shows an improvement in the order of 13% using
the arm64's defconfig and make -j97. Thanks for doing this!
--
Florian
^ permalink raw reply [flat|nested] 73+ messages in thread* Re: [PATCH 00/23] kbuild: significantly speed up kernel builds
2026-09-09 21:58 ` Florian Fainelli
@ 2026-09-11 11:28 ` Lorenzo Stoakes (ARM)
0 siblings, 0 replies; 73+ messages in thread
From: Lorenzo Stoakes (ARM) @ 2026-09-11 11:28 UTC (permalink / raw)
To: Florian Fainelli
Cc: Linus Torvalds, Nathan Chancellor, Nicolas Schier,
Nick Desaulniers, Bill Wendling, Justin Stitt, Masahiro Yamada,
Alexey Gladkov, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, x86, H. Peter Anvin, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Alexandre Ghiti, Arnd Bergmann, Catalin Marinas,
Will Deacon, Mark Rutland, Ard Biesheuvel, Ilias Apalodimas,
Josh Poimboeuf, Peter Zijlstra, Miguel Ojeda, Boqun Feng,
Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Alice Ryhl, Trevor Gross, Danilo Krummrich, Daniel Almeida,
Tamir Duberstein, Alexandre Courbot, Onur Özkan,
Jonathan Corbet, Randy Dunlap, linux-kbuild, linux-kernel, llvm,
linux-riscv, linux-arch, linux-arm-kernel, linux-efi,
rust-for-linux, linux-doc, Jens Axboe
On Wed, Sep 09, 2026 at 02:58:59PM -0700, Florian Fainelli wrote:
> On 9/8/26 13:55, Lorenzo Stoakes (ARM) wrote:
> > A typical kernel build consists of a frustratingly large amount of time
> > spent stuck in single-threaded bottlenecks.
> >
> > It turns out that there's a lot we can do about this and doing so
> > significantly impacts kernel build times.
> >
> > This series makes allmodconfig builds up to 36% faster, incremental builds
> > up to ~70% faster, and noop builds up to ~90% faster.
> >
> > Builds are faster across the board on every device I tested.
> >
> > Machines used for perf testing:
> >
> > * Threadripper - x86, AMD Threadripper 9980X, 64 cores, 128 threads
> > * EPYC - x86, 2 socket EPYC 9754, 256 cores, 512 threads
> > * M2 - arm64, 2022 M2 macbook pro, 8 cores, 8 threads
> > (4 perf, 4 efficiency)
>
> My AMD EPYC 9454P server shows an improvement in the order of 13% using the
> arm64's defconfig and make -j97. Thanks for doing this!
Nice thanks! Good to confirm others are seeing the benefits :)
> --
> Florian
>
--
Cheers, Lorenzo
^ permalink raw reply [flat|nested] 73+ messages in thread
* Re: [PATCH 00/23] kbuild: significantly speed up kernel builds
2026-09-08 20:55 [PATCH 00/23] kbuild: significantly speed up kernel builds Lorenzo Stoakes (ARM)
` (25 preceding siblings ...)
2026-09-09 21:58 ` Florian Fainelli
@ 2026-09-10 4:19 ` Nathan Chancellor
2026-09-11 11:13 ` Lorenzo Stoakes (ARM)
26 siblings, 1 reply; 73+ messages in thread
From: Nathan Chancellor @ 2026-09-10 4:19 UTC (permalink / raw)
To: Lorenzo Stoakes (ARM)
Cc: Linus Torvalds, Nathan Chancellor, Nicolas Schier,
Nick Desaulniers, Bill Wendling, Justin Stitt, Masahiro Yamada,
Alexey Gladkov, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, x86, H. Peter Anvin, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Alexandre Ghiti, Arnd Bergmann, Catalin Marinas,
Will Deacon, Mark Rutland, Ard Biesheuvel, Ilias Apalodimas,
Josh Poimboeuf, Peter Zijlstra, Miguel Ojeda, Boqun Feng,
Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Alice Ryhl, Trevor Gross, Danilo Krummrich, Daniel Almeida,
Tamir Duberstein, Alexandre Courbot, Onur Özkan,
Jonathan Corbet, Randy Dunlap, linux-kbuild, linux-kernel, llvm,
linux-riscv, linux-arch, linux-arm-kernel, linux-efi,
rust-for-linux, linux-doc, Jens Axboe
> A typical kernel build consists of a frustratingly large amount of time
> spent stuck in single-threaded bottlenecks.
>
> It turns out that there's a lot we can do about this and doing so
> significantly impacts kernel build times.
>
> This series makes allmodconfig builds up to 36% faster, incremental builds
> up to ~70% faster, and noop builds up to ~90% faster.
>
> Builds are faster across the board on every device I tested.
>
> Machines used for perf testing:
>
> * Threadripper - x86, AMD Threadripper 9980X, 64 cores, 128 threads
> * EPYC - x86, 2 socket EPYC 9754, 256 cores, 512 threads
> * M2 - arm64, 2022 M2 macbook pro, 8 cores, 8 threads
> (4 perf, 4 efficiency)
>
> Cutting to the chase:
>
> == allmodconfig FULL build ==
>
> before after delta
> ----------------------------------
> Threadripper, gcc 345.3s 275.2s -70.1s (-20%)
> Threadripper, clang 345.5s 265.7s -79.8s (-23%)
> EPYC, gcc 188.6s 121.1s -67.5s (-36%)
> EPYC, clang 261.7s 184.6s -77.1s (-29%)
>
> == allmodconfig INCREMENTAL build ==
>
> before after delta
> ----------------------------------
> Threadripper, gcc 46.4s 15.3s -31.1s (-67%)
> Threadripper, clang 44.4s 15.2s -29.1s (-66%)
> EPYC, gcc 82.0s 24.3s -57.7s (-70%)
> EPYC, clang 77.6s 24.3s -53.3s (-69%)
>
> == allmodconfig NO-OP build ==
>
> before after delta
> ----------------------------------
> Threadripper, gcc 12.97s 1.18s -11.79s (-91%)
> Threadripper, clang 13.84s 1.64s -12.20s (-88%)
> EPYC, gcc 25.92s 1.52s -24.40s (-94%)
> EPYC, clang 27.58s 2.17s -25.41s (-92%)
>
> == defconfig FULL build ==
>
> before after delta
> ----------------------------------
> Threadripper, gcc 35.3s 28.6s -6.7s (-19%)
> Threadripper, clang 33.5s 26.1s -7.4s (-22%)
> EPYC, gcc 31.2s 20.6s -10.6s (-34%)
> EPYC, clang 38.6s 32.2s -6.4s (-17%)
> M2, gcc 564.0s 512.4s -51.6s (-9%)
> M2, clang 616.1s 569.4s -46.7s (-8%)
>
> == defconfig INCREMENTAL build ==
>
> before after delta
> ----------------------------------
> Threadripper, gcc 11.6s 5.5s -6.1s (-53%)
> Threadripper, clang 11.6s 5.0s -6.6s (-57%)
> EPYC, gcc 19.3s 9.2s -10.1s (-52%)
> EPYC, clang 20.1s 8.6s -11.5s (-57%)
> M2, gcc 18.6s 9.9s -8.7s (-47%)
> M2, clang 18.6s 8.2s -10.4s (-56%)
>
> == defconfig NO-OP build ==
>
> before after delta
> ----------------------------------
> Threadripper, gcc 0.96s 0.47s -0.49s (-51%)
> Threadripper, clang 1.21s 0.55s -0.66s (-55%)
> EPYC, gcc 1.47s 0.66s -0.81s (-55%)
> EPYC, clang 1.87s 0.80s -1.07s (-57%)
> M2, gcc 5.59s 1.69s -3.90s (-70%)
> M2, clang 6.62s 1.74s -4.88s (-74%)
>
> Further performance numbers are provided for each commit giving a sense of
> what each contributes to the final result.
>
> == Testing ==
>
> Beyond x86, allmodconfig was built with the series for arm64, arm, riscv,
> powerpc64, s390 and loongarch, and for arm64, arm, s390 and loongarch the
> System.map is identical to what the previous shell mksysmap produces for
> the same vmlinux.
>
> Also tested were parisc64 and m68k build as far as mainline lets them
> (a driver's static assertion and an undefined-symbol check on parisc, gcc
> 16 internal compiler errors on m68k, none of it from this series).
>
> Kernels for x86, arm64, arm, riscv, loongarch, powerpc64, s390, m68k and
> parisc64 all boot under qemu, every text symbol of System.map is in
> /proc/kallsyms at the relocated address, and a module loads and unloads.
> The s390, arm and loongarch kernels also pass the kallsyms selftest.
>
> An x86 kernel with CONFIG_MODVERSIONS, CONFIG_EXTENDED_MODVERSIONS and
> CONFIG_MODULE_SRCVERSION_ALL boots, loads and unloads modules. External
> modules build against both in-tree and O= builds, and every commit builds
> on x86 defconfig.
>
> Build times are the best of several runs, no unexpected errors or
> warnings were seen.
>
> While some aspects of the build process have been changed, all tooling
> should function identically to before.
I appreciate all of the testing that you have done! I will test this
side by side on a couple of my own machines to see what results I get
but I do like those results.
> == LLM usage ==
>
> An LLM was used to first determine where the bottlenecks were then to
> figure out how to improve them.
>
> It generated a lot of code, much of it hideous.
>
> I extensively audited and rewrote a lot of it, and heavily edited commit
> messages, the cover letter and comments.x
>
> The LLM has also orchestrated build runs, testing, debugging and analysis.
>
> I have manually checked for correctness in both build and running kernels
> generated with this series applied.
>
> Performance improvements were also verified manually.
>
> Since an LLM was used extensively, each commit carries an Assisted-by tag.
Thank you for calling all of this out as well.
> == What was changed? ==
>
> Fundamentally the series improves build times by parallelising
> single-threaded tasks as much as possible and improving the efficiency of
> code used in the build process.
>
> kbuild, kallsyms, modpost, objtool, mksysmap and the rust build system were
> all updated as part of this change.
>
> Nothing too controversial was included. There are further improvements that
> could be made, but they would either by very invasive (large scale C header
> changes) or generate diminishing returns.
>
> == Patches ==
>
> mksysmap (1, 2): A couple of bugfixes the rest of the series relies
> upon.
As I note in other threads of this review, I think we should take these
to Linus now, they seem Obviously CorrectTM and it helps chunk out the
series.
I have just reviewed a few of the low hanging fruit patches. I will try
to take a look at the rest over the next couple of weeks but I might not
get to them until after Plumbers. I would greatly appreciate if there
are others who are more knowledgeable in these areas who could review
these changes, as I don't know too much about some of the corners of
Kbuild yet.
--
Cheers,
Nathan
^ permalink raw reply [flat|nested] 73+ messages in thread* Re: [PATCH 00/23] kbuild: significantly speed up kernel builds
2026-09-10 4:19 ` Nathan Chancellor
@ 2026-09-11 11:13 ` Lorenzo Stoakes (ARM)
0 siblings, 0 replies; 73+ messages in thread
From: Lorenzo Stoakes (ARM) @ 2026-09-11 11:13 UTC (permalink / raw)
To: Nathan Chancellor
Cc: Linus Torvalds, Nicolas Schier, Nick Desaulniers, Bill Wendling,
Justin Stitt, Masahiro Yamada, Alexey Gladkov, Thomas Gleixner,
Ingo Molnar, Borislav Petkov, Dave Hansen, x86, H. Peter Anvin,
Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti,
Arnd Bergmann, Catalin Marinas, Will Deacon, Mark Rutland,
Ard Biesheuvel, Ilias Apalodimas, Josh Poimboeuf, Peter Zijlstra,
Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Daniel Almeida, Tamir Duberstein,
Alexandre Courbot, Onur Özkan, Jonathan Corbet,
Randy Dunlap, linux-kbuild, linux-kernel, llvm, linux-riscv,
linux-arch, linux-arm-kernel, linux-efi, rust-for-linux,
linux-doc, Jens Axboe
On Wed, Sep 09, 2026 at 09:19:12PM -0700, Nathan Chancellor wrote:
> > A typical kernel build consists of a frustratingly large amount of time
> > spent stuck in single-threaded bottlenecks.
> >
> > It turns out that there's a lot we can do about this and doing so
> > significantly impacts kernel build times.
> >
> > This series makes allmodconfig builds up to 36% faster, incremental builds
> > up to ~70% faster, and noop builds up to ~90% faster.
> >
> > Builds are faster across the board on every device I tested.
> >
> > Machines used for perf testing:
> >
> > * Threadripper - x86, AMD Threadripper 9980X, 64 cores, 128 threads
> > * EPYC - x86, 2 socket EPYC 9754, 256 cores, 512 threads
> > * M2 - arm64, 2022 M2 macbook pro, 8 cores, 8 threads
> > (4 perf, 4 efficiency)
> >
> > Cutting to the chase:
> >
> > == allmodconfig FULL build ==
> >
> > before after delta
> > ----------------------------------
> > Threadripper, gcc 345.3s 275.2s -70.1s (-20%)
> > Threadripper, clang 345.5s 265.7s -79.8s (-23%)
> > EPYC, gcc 188.6s 121.1s -67.5s (-36%)
> > EPYC, clang 261.7s 184.6s -77.1s (-29%)
> >
> > == allmodconfig INCREMENTAL build ==
> >
> > before after delta
> > ----------------------------------
> > Threadripper, gcc 46.4s 15.3s -31.1s (-67%)
> > Threadripper, clang 44.4s 15.2s -29.1s (-66%)
> > EPYC, gcc 82.0s 24.3s -57.7s (-70%)
> > EPYC, clang 77.6s 24.3s -53.3s (-69%)
> >
> > == allmodconfig NO-OP build ==
> >
> > before after delta
> > ----------------------------------
> > Threadripper, gcc 12.97s 1.18s -11.79s (-91%)
> > Threadripper, clang 13.84s 1.64s -12.20s (-88%)
> > EPYC, gcc 25.92s 1.52s -24.40s (-94%)
> > EPYC, clang 27.58s 2.17s -25.41s (-92%)
> >
> > == defconfig FULL build ==
> >
> > before after delta
> > ----------------------------------
> > Threadripper, gcc 35.3s 28.6s -6.7s (-19%)
> > Threadripper, clang 33.5s 26.1s -7.4s (-22%)
> > EPYC, gcc 31.2s 20.6s -10.6s (-34%)
> > EPYC, clang 38.6s 32.2s -6.4s (-17%)
> > M2, gcc 564.0s 512.4s -51.6s (-9%)
> > M2, clang 616.1s 569.4s -46.7s (-8%)
> >
> > == defconfig INCREMENTAL build ==
> >
> > before after delta
> > ----------------------------------
> > Threadripper, gcc 11.6s 5.5s -6.1s (-53%)
> > Threadripper, clang 11.6s 5.0s -6.6s (-57%)
> > EPYC, gcc 19.3s 9.2s -10.1s (-52%)
> > EPYC, clang 20.1s 8.6s -11.5s (-57%)
> > M2, gcc 18.6s 9.9s -8.7s (-47%)
> > M2, clang 18.6s 8.2s -10.4s (-56%)
> >
> > == defconfig NO-OP build ==
> >
> > before after delta
> > ----------------------------------
> > Threadripper, gcc 0.96s 0.47s -0.49s (-51%)
> > Threadripper, clang 1.21s 0.55s -0.66s (-55%)
> > EPYC, gcc 1.47s 0.66s -0.81s (-55%)
> > EPYC, clang 1.87s 0.80s -1.07s (-57%)
> > M2, gcc 5.59s 1.69s -3.90s (-70%)
> > M2, clang 6.62s 1.74s -4.88s (-74%)
> >
> > Further performance numbers are provided for each commit giving a sense of
> > what each contributes to the final result.
> >
> > == Testing ==
> >
> > Beyond x86, allmodconfig was built with the series for arm64, arm, riscv,
> > powerpc64, s390 and loongarch, and for arm64, arm, s390 and loongarch the
> > System.map is identical to what the previous shell mksysmap produces for
> > the same vmlinux.
> >
> > Also tested were parisc64 and m68k build as far as mainline lets them
> > (a driver's static assertion and an undefined-symbol check on parisc, gcc
> > 16 internal compiler errors on m68k, none of it from this series).
> >
> > Kernels for x86, arm64, arm, riscv, loongarch, powerpc64, s390, m68k and
> > parisc64 all boot under qemu, every text symbol of System.map is in
> > /proc/kallsyms at the relocated address, and a module loads and unloads.
> > The s390, arm and loongarch kernels also pass the kallsyms selftest.
> >
> > An x86 kernel with CONFIG_MODVERSIONS, CONFIG_EXTENDED_MODVERSIONS and
> > CONFIG_MODULE_SRCVERSION_ALL boots, loads and unloads modules. External
> > modules build against both in-tree and O= builds, and every commit builds
> > on x86 defconfig.
> >
> > Build times are the best of several runs, no unexpected errors or
> > warnings were seen.
> >
> > While some aspects of the build process have been changed, all tooling
> > should function identically to before.
>
> I appreciate all of the testing that you have done! I will test this
> side by side on a couple of my own machines to see what results I get
> but I do like those results.
Ack thanks :)
>
> > == LLM usage ==
> >
> > An LLM was used to first determine where the bottlenecks were then to
> > figure out how to improve them.
> >
> > It generated a lot of code, much of it hideous.
> >
> > I extensively audited and rewrote a lot of it, and heavily edited commit
> > messages, the cover letter and comments.x
> >
> > The LLM has also orchestrated build runs, testing, debugging and analysis.
> >
> > I have manually checked for correctness in both build and running kernels
> > generated with this series applied.
> >
> > Performance improvements were also verified manually.
> >
> > Since an LLM was used extensively, each commit carries an Assisted-by tag.
>
> Thank you for calling all of this out as well.
Of course, I am on the receiving end of a flood of LLM patches in mm, so it's
important to me to be transparent and consistent.
I repeatedly tell people - audit what it makes with a human pass, fix up
awful code/comments/commit msgs, make sure you understand it + on you to
make upstreamable + obviously ack that you used it.
So what's good for the goose is good for the gander :)
>
> > == What was changed? ==
> >
> > Fundamentally the series improves build times by parallelising
> > single-threaded tasks as much as possible and improving the efficiency of
> > code used in the build process.
> >
> > kbuild, kallsyms, modpost, objtool, mksysmap and the rust build system were
> > all updated as part of this change.
> >
> > Nothing too controversial was included. There are further improvements that
> > could be made, but they would either by very invasive (large scale C header
> > changes) or generate diminishing returns.
> >
> > == Patches ==
> >
> > mksysmap (1, 2): A couple of bugfixes the rest of the series relies
> > upon.
>
> As I note in other threads of this review, I think we should take these
> to Linus now, they seem Obviously CorrectTM and it helps chunk out the
> series.
Ack thanks. I will continue to include them in respins just to enforce the
ordering in the meantime, but it should all come out in the wash I think?
I can add something in the cover letter too about these.
>
> I have just reviewed a few of the low hanging fruit patches. I will try
> to take a look at the rest over the next couple of weeks but I might not
> get to them until after Plumbers. I would greatly appreciate if there
> are others who are more knowledgeable in these areas who could review
> these changes, as I don't know too much about some of the corners of
> Kbuild yet.
Thanks so much for taking a look! I'll be at plumbers so feel free to come
say hi + discuss any of this in person.
And thanks to everybody else reviewing also!
I've replied to the more straightforward stuff, will take a deeper look at
Linus's comments + anything else that needs a deeper think + sashiko
reports a little later before sending a v2.
>
> --
> Cheers,
> Nathan
>
--
Cheers, Lorenzo
^ permalink raw reply [flat|nested] 73+ messages in thread