Beginner–Intermediate
Linux 6.x
2 of 2
1. Unloading a module with rmmod
When you want to remove a kernel module from memory, you use rmmod. You pass it the module’s name — not a file path.
# This works
$ sudo rmmod bluetooth
# This does NOT work — do not use a .ko path
$ sudo rmmod /lib/modules/6.8.0/kernel/net/bluetooth/bluetooth.ko.xz
What rmmod does under the hood
rmmod is just a thin shell. All the real work happens in a Linux system call called delete_module(2). When you run rmmod bluetooth, the flow looks like this:
2. Why rmmod fails — the three error codes
Understanding why rmmod fails is one of the most common struggles for kernel developers. There are three distinct reasons, each with its own error code.
| Error | Reason | How to fix |
|---|---|---|
| EPERM | Missing CAP_SYS_MODULE capability, or kernel.modules_disabled is set to 1 | Run with sudo. If modules_disabled=1, a reboot is required — it cannot be undone at runtime. |
| EBUSY | Module is not in LIVE state (still loading or already going), OR it has no exit function | Wait for load to finish. If no exit function, the module is permanent and cannot be unloaded without a reboot. |
| EWOULDBLOCK | Reference count is nonzero — something is actively using the module | Find and close/stop whatever is using the module (open files, processes, active connections), then retry. |
Permanent modules — no exit function
A module without a module_exit() registration is considered permanent. The kernel refuses to unload it because there is no cleanup function to call — unloading it would leave the kernel in an inconsistent state.
Such a module appears in lsmod with a – in the dependency column and cannot be removed with rmmod (you get EBUSY). The only way to remove it is to reboot. This is intentional — some kernel subsystems should never be unloaded once active.
3. modprobe — the smart way to load and unload
While insmod and rmmod are “dumb” (they handle exactly one module and do zero dependency resolution), modprobe is intelligent. It is the tool you should use in almost every situation.
How modprobe resolves dependencies
When you install a new kernel, a utility called depmod scans every .ko file under /lib/modules/$(uname -r)/, looks at the symbols each module exports and imports, and builds a dependency map. This map is saved as /lib/modules/$(uname -r)/modules.dep.
When you run modprobe bluetooth, it reads modules.dep, sees that bluetooth needs a module called rfkill, loads rfkill first, then loads bluetooth.
Common modprobe commands
# Load a module (and all its dependencies)
$ sudo modprobe bluetooth
# Remove a module (and remove unused dependencies)
$ sudo modprobe -r bluetooth
# Show what would be loaded WITHOUT actually loading
$ modprobe --show-depends bluetooth
# Regenerate modules.dep (run after adding out-of-tree modules)
$ sudo depmod -a
Setting permanent module options
If you want a module to always load with specific parameters, create a .conf file in /etc/modprobe.d/:
# File: /etc/modprobe.d/my-module.conf
options usbcore autosuspend=2
# Prevent a module from loading automatically
blacklist nouveau
Use insmod only when you are actively developing a module and loading it by path from your build directory. Use modprobe for everything else — it handles dependencies and reads your configuration files.
4. Security in Linux 6.x — three layers that protect the kernel
On older Linux systems, anyone with root access could insert any kernel module. Modern Linux 6.x systems have three independent security layers that raise the bar significantly. Understanding these layers is essential because they are the most common reason a correctly-built module refuses to load on a modern system.
kernel.modules_disabled sysctl (one-way latch) can lock the system permanently until reboot.
Layer 1: CAP_SYS_MODULE and modules_disabled
The CAP_SYS_MODULE Linux capability is required for both init_module() (load) and delete_module() (unload). Running as root grants this capability by default.
The kernel.modules_disabled sysctl adds a second check. When set to 1, all module loading and unloading is blocked regardless of capability.
# Check current value
$ cat /proc/sys/kernel/modules_disabled
0
# Lock module loading permanently until reboot (cannot be undone!)
$ echo 1 | sudo tee /proc/sys/kernel/modules_disabled
Layer 2: Module signing
The kernel can require every .ko file to carry a cryptographic signature. This signature is checked against a set of trusted public keys that are baked into the kernel at build time.
| Kernel config / boot param | Effect |
|---|---|
| CONFIG_MODULE_SIG=y | Enable signature checking. Unsigned modules load but taint the kernel. |
| CONFIG_MODULE_SIG_FORCE=y | Compile-time enforcement. Unsigned or invalid modules are rejected (ENOKEY). |
| module.sig_enforce=1 (boot param) | Runtime enforcement equivalent to SIG_FORCE, without recompiling the kernel. |
For custom or out-of-tree modules on a system with module signing, you need to sign your module with a key whose certificate is trusted by the running kernel. On systems with UEFI Secure Boot, this means enrolling your certificate as a Machine Owner Key (MOK).
# Generate a key pair for signing (one-time setup)
$ openssl req -new -x509 -newkey rsa:2048 -keyout my_key.pem \
-out my_cert.pem -days 365 -subj "/CN=My Kernel Module Key/"
# Sign a module
$ /usr/src/linux-headers-$(uname -r)/scripts/sign-file \
sha256 my_key.pem my_cert.pem my_module.ko
# Enroll the certificate for Secure Boot (requires reboot to confirm)
$ sudo mokutil --import my_cert.pem
Layer 3: Kernel lockdown and Secure Boot
The lockdown LSM (Linux Security Module) was mainlined in Linux 5.4 and is active in all 6.x kernels. It adds a security level above and beyond capabilities and module signing.
# Check current lockdown level
$ cat /sys/kernel/security/lockdown
none [integrity] confidentiality
The value in square brackets is the currently active level. There are three levels:
- none — no additional restrictions from lockdown
- integrity — unsigned modules are blocked, /dev/mem access restricted, kexec of unsigned images blocked
- confidentiality — stricter: also blocks reading kernel memory, hibernation, and more
5. Writing good init and exit functions
Every kernel module has two optional but strongly recommended functions: an init function that runs when the module is loaded, and an exit function that runs when it is unloaded. Understanding how to write them correctly avoids kernel crashes, memory leaks, and resource deadlocks.
The basic structure
#include <linux/init.h>
#include <linux/module.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A short description of what this module does");
static int __init my_module_init(void)
{
/* Initialization code goes here */
/* Return 0 on success, negative errno on failure */
return 0;
}
static void __exit my_module_exit(void)
{
/* Cleanup code goes here */
/* No return value — this must always succeed */
}
module_init(my_module_init);
module_exit(my_module_exit);
The __init and __exit markers — what they do
The __init marker tells the compiler to place the init function in a special section. After the module loads successfully and the init function returns, the kernel frees that section’s memory. This is an optimization — you do not need the init code after it has run once.
The __exit marker places the exit function in its own section. When a module is compiled directly into the kernel (built-in), the exit function can never run — so the compiler discards it entirely, saving space.
The error-path unwind pattern
Your init function may acquire multiple resources in sequence. If step 3 fails after steps 1 and 2 succeeded, you must undo steps 1 and 2 in reverse order before returning the error. The standard pattern uses goto labels:
static int __init my_module_init(void)
{
int ret;
ret = register_thing_a();
if (ret)
return ret; /* Nothing to undo yet */
ret = register_thing_b();
if (ret)
goto err_b; /* Undo step A */
ret = register_thing_c();
if (ret)
goto err_c; /* Undo steps B and A */
return 0; /* All good */
err_c:
unregister_thing_b();
err_b:
unregister_thing_a();
return ret;
}
static void __exit my_module_exit(void)
{
/* Undo everything in reverse order of init */
unregister_thing_c();
unregister_thing_b();
unregister_thing_a();
}
The six rules for safe init/exit code
The init function must return 0 for success. For failure, return a negative error code like -ENOMEM, -ENODEV, or -EINVAL. Never return a positive value — the kernel may warn about it. A nonzero return aborts the load and the module never becomes LIVE.
The exit function has no return value and is expected to always clean up completely. If your exit function can fail, you have a design problem. Whatever you register or allocate in init, you must unregister or free in exit.
MODULE_LICENSE() is mandatory. If it is missing or contains a non-GPL-compatible value, the kernel marks itself tainted and blocks access to symbols exported with EXPORT_SYMBOL_GPL(), which includes a large portion of the modern kernel API.
Without module_exit(), the module becomes permanent — it cannot be unloaded without a reboot. Only omit the exit function intentionally when the module truly must never be removed (rare in practice).
Before exit returns, cancel all pending work queue items, cancel all timers, wait for any RCU callbacks, and deregister all callbacks and interrupt handlers. If any callback fires after your module memory is freed, the kernel will crash.
When you register a struct file_operations or similar structure, set its .owner = THIS_MODULE. The kernel uses this to automatically increment your module’s reference count when the device is opened, preventing unload while it is in use. Never rely on manual reference counting unless absolutely necessary.
Interview Questions & Answers
Free Linux Kernel Programming Course
This tutorial is part of a completely free Linux kernel programming course at EmbeddedPathashala. No registration required. Learn at your own pace, from fundamentals to advanced device driver development.
← Part 1: Module Inspection Back to Course Index