Thread (93 messages) flat view 93 messages, 14 authors, 11h ago

Re: [PATCH 17/23] modpost: perform srcversion hashing in parallel

From: David Laight <hidden>
Date: 2026-09-10 10:33:04
Also in: linux-doc, linux-efi, linux-kbuild, linux-riscv, lkml, llvm, rust-for-linux

On Tue, 08 Sep 2026 21:55:17 +0100
"Lorenzo Stoakes (ARM)" [off-list ref] wrote:
quoted hunk ↗ jump to hunk
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
quoted hunk ↗ jump to hunk
+	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;
 			}
-
 		}
 
 	}
  
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help