On Thu, Sep 03, 2026 at 01:29:24PM -0700, Andrew Morton wrote:
> On Thu, 3 Sep 2026 14:55:55 -0400 Aaron Tomlin <[email protected]> wrote:
>
> > Currently, the "module_blacklist=" command-line parameter only applies to
> > loadable modules. If a module is built-in, the parameter is silently
> > ignored. This patch series extends the blacklisting functionality to
> > built-in modules by intercepting their initialisation routines during early
> > boot.
>
> Why? What are the use-cases and what is the value of this change
> to our users?
>
> Important, so please don't skimp on the details.
Hi Andrew,
Thanks for asking.
Here is the rationale, concrete use cases, and the value this hopefully
brings to users and administrators.
1. The Core Problem and User Experience Gap
============================================
Today, module_blacklist= works strictly on loadable modules. When a user or
system administrator encounters a driver bug, hang during device probe, or
hardware fault during boot, the natural and widely documented remedy is to
pass module_blacklist=[driver] via the bootloader (GRUB, systemd-boot,
etc.).
However, if that driver is built into the kernel, the parameter is silently
ignored. The kernel proceeds to run the driver's initialisation routine
anyway, leading to the same panic, hang, or hardware misbehaviour.
>From the user's standpoint, whether a driver was packaged by their
distribution or built by their provider as =m or =y is an internal
implementation detail. Having module_blacklist= silently fail solely based
on compilation configuration violates the principle of least surprise and
complicates system recovery.
2. Why initcall_blacklist= is not an adequate substitute
=========================================================
The kernel does provide initcall_blacklist=, but it is impractical for
general users, sysadmins, and automated fleet management tools for several
reasons:
Obscure symbol names
- initcall_blacklist= requires the exact function name of the
initcall (e.g., snb_pci_uarch_init and e1000_init_module). Users
typically know the module name, not the internal function name.
Internal instability
- Initcall function names are internal kernel implementation
details. They change across kernel releases, refactors, or macro
rewrites, making it impossible to write stable bootloader
configurations or recovery documentation across multiple kernel
versions.
Mangled names (Rust)
- For modern drivers written in Rust, the initcall symbol names are
compiler-mangled symbols (e.g. "_RNvX"), making
initcall_blacklist= practically impossible for a human user to
specify manually at a boot prompt.
module_blacklist= (or module_denylist=) resolves this by allowing users to
specify the canonical, user-facing module name (KBUILD_MODNAME) that they
already know.
3. Concrete use cases
======================
A. Disaster recovery and triage on production systems
When a kernel update introduces a regression in a built-in driver
(e.g., a storage controller), administrators need a way to bypass
that driver at boot time to get the system into a usable emergency
shell or collect diagnostic logs, without having to rebuild the
kernel on another machine.
B. Monolithic/Hardened environments (CONFIG_MODULES=n)
In security-sensitive environments, kernels are frequently compiled
without loadable module support (CONFIG_MODULES=n) to eliminate
module loading attack vectors. On these systems, all drivers are
built-in. If a hardware erratum or firmware bug triggers a hang in
a built-in driver, administrators previously had no
module-name-based mechanism to disable the offending driver.
C. Hardware Errata and Conflicting Devices
On systems with buggy firmware or conflicting device IDs where two
drivers
attempt to bind to the same hardware, users can prevent the conflicting
built-in driver from initializing without patching and recompiling the
entire kernel image.
4. Implementation and Overhead Considerations
=============================================
We took great care to ensure this change introduces virtually zero runtime
overhead:
Scoped to module_init():
- Only built-in drivers that explicitly use module_init() are
tracked. Core kernel subsystems using core_initcall(),
subsys_initcall(), etc. are unaffected.
Zero Resident Memory:
- The metadata table (.initcall.modnames) and the module name
strings (.init.rodata) are placed entirely in init sections and
are completely freed from memory after boot via free_initmem().
Fast Path:
- During boot, if neither module_blacklist= nor module_denylist=
was supplied on the kernel command line, the lookup is bypassed
entirely.
In summary, this patch brings parity between modular and built-in drivers,
removes a pain point in boot-time disaster recovery, and provides users
with a predictable, consistent interface.
Thanks,
--
Aaron Tomlin