
Use Kconfig for New Kernel Module
You want a clear path to add a configuration option that shows up in menuconfig and builds cleanly without wasted rebuilds.
I’ll walk you through a compact, practical approach that ties a tiny example driver into the kernel configuration, so the symbol appears where it should and obeys dependencies.
We’ll cover the essential config and menu keywords and show how to compile Image.gz and modules, install them, and verify behavior with modinfo, insmod, and dmesg.
Along the way, I point out common pitfalls—hidden options, recursive dependency traps, and misuse of select—so you avoid surprises when shipping code.
Key Takeaways
- Define a clear config symbol and prompt so it appears in menuconfig.
- Wire the option into Kbuild and verify build targets like Image.gz and modules.
- Use modinfo, lsmod, and dmesg to confirm runtime behavior.
- Avoid hidden dependency traps and careless use of select.
- Keep prompts and help text concise to aid maintainability and review.
What this How-To covers and why Kconfig matters for kernel modules
I’ll lay out what this guide covers and why an organized configuration layer controls what gets built and what can be loaded later. You’ll get a compact, practical path from writing a tiny driver to exposing a clear option in the menu and managing dependencies.
We walk end-to-end: authoring a small source file, defining a config symbol, wiring build rules, compiling, installing, and loading the result on a running system.
Why this matters: the config system decides if a feature is built-in (y), built as a module (m), or disabled (n). That choice gives you flexibility—no need to rebuild the entire image when you want dynamic loading via modprobe or insmod and safe unloads with rmmod.
Build mode | Meaning | User impact |
---|---|---|
y | Built-in | No runtime load/unload |
m | As modules | Loadable, smaller image |
n | Disabled | Hidden in menus |
Clear dependencies keep the UI tidy by hiding options that won’t work on a given platform. I’ll also show how to write concise prompt and help text so users find the right option in menuconfig and avoid surprising reverse dependencies.
Prerequisites, tooling, and source layout for a clean start
A tidy tree and predictable build targets make iterating on a small driver fast and safe. Start by placing your primary source under drivers/misc/ and add entries alphabetically to drivers/misc/Kconfig and drivers/misc/Makefile.
You’ll need a working kernel source, common build essentials, and permission to install modules—use sudo when calling modules_install. Keep one user account with sudo access for installs and testing in your VM or target system.
Use standard make targets: make -C “$IIO_TREE” menuconfig, make -C “$IIO_TREE” -j$(nproc) Image.gz modules, and make -C “$IIO_TREE” modules_install. When changing just your directory, run make -C “$IIO_TREE” M= to rebuild only that area.
- Use obj-$(CONFIG_FOO) += foo.o in the Makefile.
- Keep Kconfig and Makefile entries alphabetical to aid reviewers.
- After install, run depmod and validate with modinfo, lsmod, and dmesg.
One last tip: ensure the running kernel version matches the installed .ko path to avoid undefined behavior. Small edits, M= builds, copy .ko, depmod, and test—repeat quickly.
Fast path: create, configure, build, and load a simple example module
Let’s get a working example running quickly: create a tiny C file, expose a tristate config symbol, and load the built object to verify the full path.
Author a tiny file and minimal init/exit code
Create simple_mod.c with these includes: <linux/module.h> and <linux/init.h>.
Define module_init and module_exit and use pr_info in both functions so dmesg shows init and exit messages.
Enable the symbol and save .config
Add this entry under drivers/misc/Kconfig: config SIMPLE_MOD, tristate, default n, and a brief help paragraph. In drivers/misc/Makefile add:
- obj-$(CONFIG_SIMPLE_MOD) += simple_mod.o
Run menuconfig, search SIMPLE_MOD, set it to m, and save the .config used by the build.
Build, install, and verify the path
Build with: make -C “$IIO_TREE” -j$(nproc) Image.gz modules. Then run modules_install and depmod so modprobe can find your .ko.
Check metadata and load status with modinfo and lsmod. Use dmesg to see the pr_info lines from init and exit.
Unload and reload to validate lifecycle
Remove using rmmod or modprobe -r, then reload with modprobe (preferred) or insmod if you need a direct path. Iterate by rebuilding with M= to speed edits.
Kconfig building blocks: keywords, entries, and menu structure
Understanding the minimal set of directives makes the config tree predictable and easy to scan. I map each keyword to a clear role so UI rendering matches intent.
- config — defines a symbol and its type.
- menuconfig — creates a visible parent that groups related entries.
- choice/endchoice — enforces one selection among many.
- menu/endmenu, comment — structure and guidance, shown only when dependencies apply.
- if/endif — applies a dependency to all nested items to avoid repetition.
- source — pulls in other files unconditionally to keep files tidy.
Keyword | Role | Rendering note |
---|---|---|
config | Defines a symbol | Shows as selectable entry |
menuconfig | Group visible options | Subentries must depend on the menu symbol |
choice | Mutual exclusion | Front ends show radio-style options |
if/endif | Scoped dependency | Applies to all nested entries |
Help text matters — use it to explain y/m/n choices and warn about side effects. Keep names and prompts consistent so menu entries stay discoverable as the tree grows.
Defining a config symbol with prompt, help text, and default value
Design the symbol so it is descriptive and predictable in the config UI. Start with a clear name and a user-friendly prompt that matches nearby entries. A concise prompt helps users find the option quickly.
Choose the right type: use tristate when you want y/m/n values. Tristate is ideal when building as a module speeds iteration and keeps the image small. Use bool when the option must be built-in (y/n) or when modular loading is not applicable.
Set a safe default. Prefer default n unless the value is widely useful and harmless. You can also use conditional defaults like default m if … to favor modular builds on common setups.
Use depends on to hide an option until prerequisites are met—this improves discoverability and avoids errors. Avoid select unless you must force a symbol to y; it bypasses normal checks and can cause surprising results.
Practical checklist
- Write a short prompt and clear help text that explains y vs m vs n.
- Prefer tristate when modular support is useful; use bool for core bits.
- Pick a safe default value and document rationale in help.
- Use depends on to enforce prerequisites; use select sparingly.
Attribute | When to use | Effect |
---|---|---|
tristate | Driver that can be modular | y/m/n values |
bool | Always-on core features | y/n values |
default | Sets initial choice | Controls saved .config value |
Wire-up in Kbuild: Makefile entries and object output control
Linking a config symbol to build rules is what turns a menu entry into real output you can test. I keep the Makefile lines tiny and explicit so a change in the config reliably changes which files compile.
Use the obj-$(CONFIG_FOO) += foo.o pattern to connect a config name to the object file. Place that line in the directory Makefile and keep entries alphabetical—this helps reviewers and avoids merge churn.
When iterating, compile only your subtree with: make -C “$IIO_TREE” M=drivers/misc/. Run modules_prepare if you add exported headers or change interfaces so autogenerated files are current.
- CONFIG_FOO toggles foo.o; the installed .ko lands under the matching path.
- Switching m ↔ y changes linkage and final output — verify by toggling the option and rebuilding.
- For multi-file units list each .o or use foo-y / foo-m as the codebase grows.
Action | Effect | Tip |
---|---|---|
obj-$(CONFIG_…) | Controls object inclusion | Keep alphabetical |
M= | Partial build of a subtree | Saves time when editing |
modules_prepare | Updates headers/autogen | Run after interface changes |
Kconfig for new kernel module
I place the tristate symbol beside the C file so the option, object, and menu stay together. This keeps review simple and makes the path from prompt to .ko obvious.
Add an entry in drivers/misc/Kconfig with a clear prompt, short help text, and tristate. Then add this line to the directory Makefile:
- obj-$(CONFIG_SIMPLE_MOD) += simple_mod.o
Open menuconfig and search (/) for the symbol. Set it to m, save .config, and rebuild modules. Use a partial build (M=…) during development to speed iterate cycles.
Verify end-to-end: run modules_install, depmod, then check metadata with modinfo and load with modprobe. If other features are required, add depends on so the option only appears when prerequisites exist.
Action | Target file | Tip |
---|---|---|
Add config entry | drivers/misc/Kconfig | Keep prompt concise |
Makefile linkage | drivers/misc/Makefile | Use obj-$(CONFIG_…) += file.o |
Verify | .config & .ko | Search, set to m, save, rebuild |
Test | Installed .ko | modinfo + modprobe to validate |
Controlling visibility and behavior with depends on and select
Good dependency design keeps the config UI tidy and prevents link-time surprises. I use depends on to hide symbols unless the platform or build coverage makes them sensible.
Prefer expressions like depends on ARCH_FOO_VENDOR || COMPILE_TEST so CI compiles the code even when hardware is absent. For optional pieces, use the BAR || !BAR pattern. That prevents your option being forced built-in while BAR is only modular.
Avoid select except for leaf symbols that truly must be y. Select forces a symbol to y and bypasses other dependency checks — that creates surprising dependency chains and hidden build failures.
- Define helper symbols when many drivers share a pattern.
- Reflect runtime constraints in depends expressions and document stubs when !BAR applies.
- Verify by toggling y/m/n on both sides to catch link issues early.
Pattern | When to use | Effect |
---|---|---|
ARCH_FOO_VENDOR || COMPILE_TEST | CI + platform coverage | Builds in CI, hides on wrong arch |
BAR || !BAR | Optional dependency | Keeps y/m combinations valid |
select LEAF | Leaf, no deps | Forces y, bypasses checks — use rarely |
helper symbol | Shared complex rules | Simplifies maintenance and review |
Using menuconfig, nconfig, and xconfig effectively
A few small habits—searching prompts, jumping from results, and diffing configs—cut guesswork when adjusting options.
Use menuconfig when you want a straightforward TUI. Try nconfig or xconfig when you need richer search and navigation.
Search and jump tricks
In nconfig press “/” to search menu prompts and F8 to search by symbol. Results show linked numbers—press the number to jump straight to that entry.
Track what changed
Run make listnewconfig to list added options since your last .config. Use make oldconfig to step through prompts interactively.
After that, run scripts/diffconfig .config.old .config to review differences before committing.
- Keep your .config under version control so changes are auditable by other users.
- If your .config is a symlink, set KCONFIG_OVERWRITECONFIG to preserve it when saving.
- Search both by symbol name and by prompt text when a name is not obvious.
- Always save before exiting—choices don’t apply until the file is written.
Tool | Best use | Tip |
---|---|---|
menuconfig | Quick TUI edits | Simple navigation |
nconfig | Prompt and symbol search | Use “/” and F8 to jump |
xconfig | GUI edits | Good for visual review of entries |
Environment variables that streamline kernel configuration
A handful of environment variables make automated config workflows reliable and auditable. I use them to pin which config file to read, seed randconfig runs, and control how autogenerated files are written.
KCONFIG_CONFIG and miniconfig workflows
Set KCONFIG_CONFIG to point at an alternate config file when you maintain multiple target profiles. This lets a user or CI pick a specific config file without interactive edits.
KCONFIG_ALLCONFIG can point to a tiny miniconfig that seeds an all*config flow. A small file with a few pinned options will expand into a full .config deterministically.
Seeding randconfig and tuning probabilities
Use KCONFIG_SEED to make randconfig runs reproducible in CI. Record the seed alongside logs so failures are debuggable later.
KCONFIG_PROBABILITY biases how often values land on y/m/n — a handy way to stress uncommon option combinations during testing.
Syncconfig and autogenerated file control
Set KCONFIG_NOSILENTUPDATE to force explicit confirmation when saved values change. That avoids surprise updates in shared trees.
Redirect outputs with KCONFIG_AUTOCONFIG and KCONFIG_AUTOHEADER when your build expects different paths for auto.conf and autoconf.h.
- Keep miniconfigs small and documented—note which values must stay fixed and which can float.
- Store seeds and probability strings with CI logs to reproduce test runs later.
- Combine these variables with listnewconfig and diffconfig to track what changed and why.
Variable | Primary use | Tip |
---|---|---|
KCONFIG_CONFIG | Alternate config file | Name configs per target |
KCONFIG_ALLCONFIG | Miniconfig seed | Pin a few critical options |
KCONFIG_SEED / PROBABILITY | Deterministic randconfig | Log seed and ratio |
KCONFIG_AUTOCONFIG / AUTOHEADER | Output paths | Match CI layout |
Module lifecycle: building, installing, and dependency resolution
Let’s pin down the lifecycle: how a build turns source into installed artifacts and how to verify they load cleanly. I focus on the practical commands and quick checks I use during development.
Build and stage
Run make -C “$IIO_TREE” -j$(nproc) Image.gz modules to keep ABI alignment between image and modules. Then install with make -C “$IIO_TREE” modules_install so tools like modprobe can find the installed files.
Prepare and refresh metadata
If you change exported headers or build only a subtree, run make -C “$IIO_TREE” modules_prepare first. After copying a .ko during quick iterations, run depmod –quick to refresh dependency metadata.
- Use modinfo to inspect metadata and detect required firmware or symbols.
- Prefer modprobe over insmod — modprobe resolves dependencies via modules.dep.
- Check dmesg after load/unload to see pr_info lines and diagnose probe errors.
- Verify uname -r matches installed modules and use INSTALL_MOD_PATH when staging into a VM or chroot.
Action | Command | Why |
---|---|---|
Build & install | make … Image.gz modules; modules_install | Keep image and modules in sync |
Prepare | modules_prepare | Update autogenerated headers |
Deps | depmod –quick | Fast dependency refresh during iteration |
Document this lifecycle in your repo so teammates reproduce tests without friction. Copying just the updated .ko, rerunning depmod, then modprobe saves time during tight loops.
Expressing inter-module APIs, namespaces, and dependencies
When subsystems need a clear API, using named export namespaces keeps ownership explicit and avoids accidental linkages. I recommend exposing only the functions you intend to support and guarding experimental hooks behind a config toggle.
Use the EXPORT_SYMBOL_NS_GPL macro to publish a symbol with a name string. Consumers must declare the function prototype and use MODULE_IMPORT_NS(“NAMESPACE”) so the loader and linker enforce the relationship.
Example pattern in code: the provider exports simple_mod_func with
EXPORT_SYMBOL_NS_GPL(simple_mod_func, “IIO_WORKSHOP_SIMPLE_MOD”);
Consumers declare extern, import the namespace, and call the API. Let modprobe handle load order by verifying dependencies in the provider’s metadata.
- Keep exported APIs minimal and documented—treat them like contracts.
- Prefer GPL-only exports when appropriate to signal intent.
- Group exports near implementations and comment the public surface.
Action | What to add | Why |
---|---|---|
Export | EXPORT_SYMBOL_NS_GPL(symbol, “NAMESPACE”) | Named ownership and clear linkage |
Import | MODULE_IMPORT_NS(“NAMESPACE”); extern decl | Loader enforces provider presence |
Guard | config toggle around API | Safe iteration and staged support |
Common pitfalls and how to avoid recursive dependency issues
Circular dependencies are a frequent roadside hazard; spotting them early saves long debugging sessions.
When the config system reports a recursive dependency detected, it means symbols reference each other in a loop. I advise removing any superfluous select lines and replacing them with depends on when semantics require respecting target constraints.
Keep dependency chains short. Long chains make dependencies hard to reason about and hide why an option became visible or forced to y. Introduce a helper symbol when multiple entries must share a common requirement.
- Avoid reverse dependencies that force y — they create hidden build and visibility surprises.
- Remove redundant selects — parents often already depend on the same core symbol.
- Document tricky expressions with comments so future maintainers understand the shape.
- Reproduce issues with minimal test files and run allnoconfig to validate fixes quickly.
Problem | Symptom | Fix |
---|---|---|
Circular dependency | Recursive dependency detected | Swap select to depends on |
Redundant select | Unexpected y forced | Remove select or rely on parent dependency |
Long chain | Hard to debug visibility | Introduce helper symbol and shorten chain |
Finally, check that help text and prompt text match the real dependency logic and that any variable or entry mentioned in the menu remains accurate. Clarity beats cleverness — simplify when unsure.
Testing strategies: COMPILE_TEST, architecture constraints, and CI
I combine platform guards with a compile-time test flag to widen coverage without cluttering the configuration UI. Use the literal depends on ARCH_FOO_VENDOR || COMPILE_TEST so CI builds drivers even when hardware is absent.
This pattern keeps architecture-specific options hidden on user systems while giving CI and maintainers broader build support. When code compiles without real hardware, the probe path must exit cleanly and avoid crashes at runtime.
Practical checks I run
- Add COMPILE_TEST to dependency lines to expand CI coverage without exposing irrelevant options.
- Build across toolchains and arches to catch endianness, pointer-size, and header issues early.
- Use a fixed seed in randconfig to replay odd failures and track which symbols gate hardware paths.
- Keep test docs and miniconfigs near the code so contributors can validate changes locally.
Action | Why | Tip |
---|---|---|
Include COMPILE_TEST | CI builds absent hardware | Keeps system menus clean |
Cross-toolchain | Catch ABI issues | Run on example boards or QEMU |
Record configs | Reproduce failures | Use diffconfig with CI logs |
Next steps to ship reliable Kconfig entries and modules today
Ship readiness is about small habits that prevent big surprises. Keep Kconfig and Makefile entries alphabetized so reviewers see the intended order. Use menuconfig to confirm the prompt and that the symbol shows in the right section for users.
Start with modular builds to iterate fast. Verify each option and related options with listnewconfig and scripts/diffconfig. Keep one example file and a clear symbol name so tests map back to source quickly.
Automate: add COMPILE_TEST in CI, run M= builds and modules_prepare during edits, and re-run depmod plus modprobe checks. Document dependencies, exported APIs, and the review checklist so the next contributor ships even smoother.