[PATCH 14/23] modpost: cache section relocation mismatch state
From: Lorenzo Stoakes (ARM)
Date: Tue Sep 08 2026 - 17:19:10 EST
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@xxxxxxxxxx>
---
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