Keywords covered in this tutorial
PTR_ERR()
IS_ERR()
IS_ERR_OR_NULL()
ERR_CAST()
__init
__exit
__initdata
insmod
rmmod
modprobe
lsmod
modinfo
CAP_SYS_MODULE
include/linux/err.h
In the previous tutorial we covered the module init and cleanup entry points.
Just to recap: the cleanup function (registered with module_exit()) takes
no parameters and returns nothing (void). Its only job is to undo whatever
the init function set up — freeing memory, unregistering devices, releasing locks, and so on.
One thing beginners often miss: if you do not call
module_exit() in your module, the kernel will refuse to unload it.
You will see an error when you try to run rmmod. The module becomes
stuck in memory until the next reboot. This is by design — the kernel protects itself
from losing a module that it does not know how to clean up.
| .ko file on | ============================> | Module loaded in |
| disk | | kernel memory |
+——————+ +——————–+
|
init() runs once
|
v
+————-+
| Module is |
| LIVE |
+————-+
|
rmmod / modprobe -r
|
exit() runs once
|
v
+————-+
| Module |
| UNLOADED |
+————-+
2.1 Why do we need these?
In normal C programming, a function either returns an integer (where a negative value
means an error) or it returns a pointer (where NULL means failure). The kernel has many
functions that must return a pointer when they succeed, but also need to tell
the caller which specific error occurred when they fail. Returning just NULL is
not enough — NULL only says “something went wrong”, not “what went wrong”.
The kernel solves this elegantly using the fact that virtual address space on
modern Linux systems reserves the top 4096 bytes (addresses
0xFFFFF000 to 0xFFFFFFFF on 32-bit,
and equivalent on 64-bit) for kernel use and these addresses are never
valid pointer values. Negative error codes (like -ENOMEM = -12,
-EINVAL = -22) happen to fall in this range when cast to an unsigned
pointer. The kernel exploits this to encode error codes inside pointer-sized values.
The constant MAX_ERRNO is defined as 4095 in
include/linux/err.h. Any pointer whose unsigned value is
>= (unsigned long)(-MAX_ERRNO) is considered an error pointer.
That covers all negative errno values from -1 to -4095.
2.2 The helper functions (Linux 6.x, include/linux/err.h)
========================================== ===================================Success? return real_pointer; ptr = callee();
if (!IS_ERR(ptr)) { use ptr; }Failure? return ERR_PTR(-ENOMEM); if (IS_ERR(ptr)) {
| err = PTR_ERR(ptr); /* -12 */
v handle error;
Encodes -12 into a void* }
pointer value that looks
like a very high address.
Here is what each helper does, written from scratch for clarity:
/* ------ ERR_PTR ------
* Takes a negative error code (e.g. -ENOMEM)
* and returns it disguised as a void pointer.
* The caller can store it in any pointer variable.
*/
void *ERR_PTR(long error);
/* ------ PTR_ERR ------
* Takes a pointer that you suspect holds an error code
* and extracts the error value back as a long integer.
* Use ONLY after IS_ERR() confirms it is an error pointer.
*/
long PTR_ERR(const void *ptr);
/* ------ IS_ERR ------
* Returns true (non-zero) if the pointer actually holds
* an error code, false (0) if it is a real valid pointer.
*/
bool IS_ERR(const void *ptr);
/* ------ IS_ERR_OR_NULL ------
* Returns true if ptr is NULL OR holds an error code.
* Useful when a function could return NULL on soft failure
* and ERR_PTR on hard failure.
*/
bool IS_ERR_OR_NULL(const void *ptr);
/* ------ ERR_CAST ------
* Casts an error pointer from one pointer type to another.
* Used when you need to propagate an error across type boundaries.
*/
void *ERR_CAST(const void *ptr);
/* ------ PTR_ERR_OR_ZERO ------
* Returns 0 if ptr is a valid pointer, otherwise returns
* the error code encoded in ptr. Useful for int-returning wrappers.
*/
int PTR_ERR_OR_ZERO(const void *ptr);
All these functions live in include/linux/err.h.
You do not need to include it manually — it is pulled in via
linux/module.h which you already include in every module.
2.3 A clean example showing the pattern
Suppose you have a function that allocates a structure and returns a pointer to it.
If allocation fails, it should tell the caller the exact error. Here is how you write
both the function and its caller using the ERR_PTR pattern:
#include <linux/module.h>
#include <linux/slab.h> /* kzalloc, kfree */
#include <linux/err.h> /* ERR_PTR, PTR_ERR, IS_ERR */
/* A sample structure */
struct my_device {
int id;
char name[32];
};
/* ---------------------------------------------------
* Callee: function that returns pointer OR error code
* --------------------------------------------------- */
static struct my_device *create_device(int id)
{
struct my_device *dev;
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev)
return ERR_PTR(-ENOMEM); /* encode error into pointer */
dev->id = id;
snprintf(dev->name, sizeof(dev->name), "device-%d", id);
return dev; /* real pointer on success */
}
/* ---------------------------------------------------
* Caller: checks whether it received a valid pointer
* --------------------------------------------------- */
static int __init mymodule_init(void)
{
struct my_device *dev;
int ret;
dev = create_device(42);
/* Step 1: check if we got an error pointer */
if (IS_ERR(dev)) {
ret = PTR_ERR(dev); /* extract the actual errno */
pr_err("create_device failed: %d\n", ret);
return ret;
}
/* Step 2: dev is a valid pointer here — use it */
pr_info("Created device: %s (id=%d)\n", dev->name, dev->id);
kfree(dev);
return 0;
}
static void __exit mymodule_exit(void)
{
pr_info("Module unloaded\n");
}
module_init(mymodule_init);
module_exit(mymodule_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("EmbeddedPathashala");
MODULE_DESCRIPTION("ERR_PTR pattern demo");
2.4 The IS_ERR_OR_NULL pattern
Some kernel functions return NULL to indicate “not found” and
ERR_PTR() to indicate “hard error”. In those cases, checking only
IS_ERR() is not enough — you must also check for NULL:
ptr = some_lookup_function(key);
if (IS_ERR_OR_NULL(ptr)) {
if (!ptr)
pr_warn("Item not found\n");
else
pr_err("Lookup error: %ld\n", PTR_ERR(ptr));
return -ENODEV;
}
/* ptr is valid here */
use(ptr);
2.5 Common mistakes beginners make
The biggest mistake is checking if (ptr == NULL) after a function that
uses the ERR_PTR pattern. If the function returned ERR_PTR(-ENOMEM),
the pointer is not NULL — it is a non-zero value that holds an error
code. Your NULL check will pass and you will dereference a garbage address, causing a
kernel panic. Always use IS_ERR() first.
Never pass an error pointer to kfree() or any function expecting a real
address. Always call IS_ERR() before doing anything with the pointer.
3.1 What problem do they solve?
When a kernel module (or built-in driver) initializes, it runs its init function exactly
once. After that, the init function is never called again during the
lifetime of that boot session. Keeping the init function’s code sitting in RAM forever
wastes precious kernel memory. The __init macro is the kernel’s way of
reclaiming that memory automatically.
3.2 __init — for initialization code
When you mark your init function with __init, the compiler places
that function’s machine code into a special ELF section called .init.text.
For built-in drivers (compiled directly into the kernel image), the kernel frees the
entire .init.text section after boot completes. You may have seen the
boot message “Freeing unused kernel memory: 256K freed” — that is exactly
this mechanism at work.
For loadable modules (the .ko files we build), the memory is freed
right after the module’s init function returns. The module is now live but its
initialization code no longer occupies RAM.
/* __init marks the function for the .init.text section */
static int __init mydriver_init(void)
{
pr_info("Driver init – this code is freed after return\n");
return 0;
}
/* __initdata marks variables used only during init */
static int __initdata config_value = 42;
/* Both are freed once init completes */
3.3 __exit — for cleanup code
Similarly, __exit places the cleanup function into .exit.text.
The behavior differs between built-in and loadable:
For built-in drivers: the cleanup function is never called
(you cannot unload a built-in driver without rebooting), so the compiler simply
discards the entire .exit.text section and the code never makes it
into the kernel image at all. This saves space in the kernel binary.
For loadable modules: the cleanup function is needed when
rmmod is called, so it stays in memory until the module is unloaded.
After module_exit() runs, that memory is also freed.
static void __exit mydriver_exit(void)
{
pr_info("Driver exit – cleanup here\n");
}
module_init(mydriver_init);
module_exit(mydriver_exit);
3.4 Where are __init and __exit defined?
Both macros are defined in include/linux/init.h. In Linux 6.x, their
definitions use GCC section attributes:
/* From include/linux/init.h (Linux 6.x) */
#define __init __section(".init.text") __cold __latent_entropy __noinitretpoline
#define __exit __section(".exit.text") __exitused __cold notrace
/* For data:
* __initdata -> placed in .init.data section
* __exitdata -> placed in .exit.data section
*/
The __cold attribute is a GCC hint meaning “this code is rarely executed”
— it allows the compiler to optimize branch prediction accordingly.
+—————————–+
| .text (main code) | <– always in memory while module is loaded
+—————————–+
| .init.text (__init fn) | <– freed after init() returns
+—————————–+
| .init.data (__initdata) | <– freed after init() returns
+—————————–+
| .exit.text (__exit fn) | <– freed after exit() returns (or discarded
+—————————–+ for built-in drivers)
| .data (globals) | <– stays for module lifetime
+—————————–+
| .rodata (constants) | <– stays for module lifetime
+—————————–+
To build a kernel module you need the kernel headers (or the full
kernel source) matching your running kernel. On Ubuntu/Debian:
# Install headers for the currently running kernel
sudo apt install linux-headers-$(uname -r)
# Verify the headers are present
ls /lib/modules/$(uname -r)/build
Every kernel module needs a Makefile. The kernel build system
(called Kbuild) takes care of the compilation. Here is a minimal Makefile for a
module called mymodule.c:
# Minimal Kbuild Makefile for an out-of-tree module
obj-m += mymodule.o
# If your module has multiple source files:
# mymodule-objs := file1.o file2.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
The -C flag tells make to change into the kernel build directory first.
The M=$(PWD) flag tells the kernel build system to come back to your
module’s directory to build the module files. This is the standard pattern for all
out-of-tree kernel modules and has not changed in Linux 6.x.
# Build the module
make
# You should see output ending with something like:
# LD [M] /path/to/mymodule.ko
# List the generated files
ls *.ko
The output .ko file (Kernel Object) is your loadable module. In Linux
6.x on Ubuntu 22.04+, you may see .ko.zst files in the system module
directory — these are zstd-compressed modules. Out-of-tree modules you build yourself
remain as plain .ko files.
5.1 Why do you need root / CAP_SYS_MODULE?
Loading a kernel module means running your code directly inside the kernel — at the
highest privilege level on the CPU. A bug in your module can crash the entire system.
For this reason, inserting a module requires either root privileges or the
CAP_SYS_MODULE Linux capability. Without it, you get
“Operation not permitted”.
On systems with Secure Boot enabled and locked-down kernel mode, even root cannot
load unsigned modules. You will encounter this on modern Ubuntu and Fedora installations.
We will cover kernel module signing in a later tutorial.
5.2 insmod – direct insert
insmod takes the full path to a .ko file and loads it:
# Load your module (must be run as root or with sudo)
sudo insmod ./mymodule.ko
# Check dmesg for your module's printk output
dmesg | tail -20
# insmod does NOT resolve dependencies – if your module
# depends on another module, you must load that one first.
5.3 modprobe – smarter loader (preferred)
modprobe is the recommended way to load modules in production. It reads
the module dependency database (/lib/modules/$(uname -r)/modules.dep)
and automatically loads any required dependency modules in the right order before
loading your module.
# Load a module by name (no path needed, no .ko extension)
sudo modprobe mymodule
# modprobe looks for the module in /lib/modules/$(uname -r)/
# For out-of-tree development, insmod is fine.
5.4 Verifying the module is loaded with lsmod
lsmod reads /proc/modules and shows all currently loaded
modules in a formatted table:
lsmod
# Example output:
# Module Size Used by
# mymodule 16384 0
# bluetooth 733184 12 btusb,bnep
# cfg80211 1069056 1 iwlwifi
The three columns mean: Module – name of the module.
Size – memory it occupies in bytes.
Used by – count of other modules/processes that depend on it,
followed by their names. A “Used by” count of 0 means the module can be safely unloaded.
5.5 Getting module details with modinfo
# Show metadata about a loaded or .ko file
modinfo mymodule.ko
# Output includes:
# filename, license, author, description, depends,
# vermagic (kernel version it was built against), parm (parameters)
The vermagic field is critical — it must match your running kernel version.
If it does not match, insmod will refuse to load the module with
“Invalid module format”.
6.1 rmmod
# Remove the module (use module name, not file path)
sudo rmmod mymodule
# Check dmesg to confirm exit() ran
dmesg | tail -5
rmmod will fail if the module’s “Used by” count is greater than zero —
meaning something else still depends on it. You must remove the dependent modules first.
Like insmod, rmmod does not handle dependencies automatically.
6.2 modprobe -r (preferred for removal)
# Remove a module AND its now-unused dependencies
sudo modprobe -r mymodule
modprobe -r is smarter — it also removes any dependency modules that
are no longer needed by anything else after your module is removed.
6.3 What happens during unload
|
v
Kernel checks: Used-by count == 0?
|
YES | NO –> return -EBUSY (module in use)
v
Kernel calls: mymodule_exit()
|
v
exit() performs cleanup (free memory, unregister devices, etc.)
|
v
Kernel removes module from /proc/modules and frees .ko memory
|
v
lsmod no longer shows the module
| Command | Purpose | Handles Dependencies? |
|---|---|---|
insmod mymod.ko |
Load module from file path | No – manual |
modprobe mymod |
Load module by name (preferred) | Yes – automatic |
rmmod mymod |
Remove module by name | No – manual |
modprobe -r mymod |
Remove module + unused deps | Yes – automatic |
lsmod |
List all loaded modules | N/A |
modinfo mymod.ko |
Show module metadata | N/A |
cat /proc/modules |
Raw module info (lsmod reads this) | N/A |
dmesg | tail -20 |
View kernel log (printk output) | N/A |
Here is a complete, minimal kernel module that demonstrates all the concepts
from this tutorial: __init, __exit, proper return values,
and module metadata macros.
/* hello_lkm.c – Complete Hello World Kernel Module
* Demonstrates: __init, __exit, module_init, module_exit
* Tested on Linux 6.x
*/
#include <linux/module.h> /* module_init, module_exit, MODULE_* macros */
#include <linux/kernel.h> /* pr_info, pr_err */
#include <linux/init.h> /* __init, __exit */
/* Module metadata – these show up in modinfo output */
MODULE_LICENSE("GPL");
MODULE_AUTHOR("EmbeddedPathashala");
MODULE_DESCRIPTION("Hello World LKM – Linux 6.x");
MODULE_VERSION("1.0");
/* __init: code freed from memory after this function returns */
static int __init hello_init(void)
{
pr_info("Hello from EmbeddedPathashala! Module loaded.\n");
/* Return 0 for success. Any non-zero value aborts loading. */
return 0;
}
/* __exit: code freed after rmmod completes */
static void __exit hello_exit(void)
{
pr_info("Goodbye! Module unloaded.\n");
}
/* Register the init and exit entry points with the kernel */
module_init(hello_init);
module_exit(hello_exit);
Build and test sequence:
# 1. Build
make
# 2. Load
sudo insmod hello_lkm.ko
# 3. Check log
dmesg | tail -5
# Should see: Hello from EmbeddedPathashala! Module loaded.
# 4. Verify it is in the list
lsmod | grep hello
# 5. Unload
sudo rmmod hello_lkm
# 6. Verify exit ran
dmesg | tail -5
# Should see: Goodbye! Module unloaded.
Q1. What is the purpose of ERR_PTR() and why does the kernel need it?
Kernel functions that return pointers cannot return a simple integer error code.
ERR_PTR() encodes a negative error value into a pointer-sized type by
exploiting the fact that the highest 4096 virtual addresses (-1 to
-4095) are never valid kernel pointers. This lets a single return type
carry either a valid pointer or an error code, making APIs cleaner.
Q2. What is MAX_ERRNO in the Linux kernel and why is it 4095?
MAX_ERRNO is defined as 4095 in include/linux/err.h. The
value means the kernel can represent error codes from -1 to
-4095 inside a pointer. 4095 is the maximum number of errno values the
kernel will ever define, leaving room for future additions while keeping the invalid
pointer range small enough not to clash with valid addresses.
Q3. What is the difference between IS_ERR() and IS_ERR_OR_NULL()?
IS_ERR() returns true only if the pointer encodes an error code (value
in the [-1, -4095] range). It returns false for a NULL pointer.
IS_ERR_OR_NULL() returns true for both NULL and error-encoded pointers.
Use IS_ERR_OR_NULL() when the function you called can return NULL to
indicate “not found” in addition to returning error codes.
Q4. What does __init do and when is the memory freed?
__init places the function in the .init.text ELF section.
For loadable modules, this memory is freed immediately after the module’s init function
returns successfully. For built-in drivers (compiled into the kernel image), the
memory is freed at the end of the kernel’s boot sequence when
free_initmem() is called.
Q5. What happens if you omit module_exit() from your module?
The kernel will refuse to unload the module. Attempting rmmod will return
an error because the kernel has no registered exit function to call safely. The module
remains stuck in kernel memory until the next reboot. This is a protection mechanism —
the kernel will not remove a module it cannot safely clean up.
Q6. What is the difference between insmod and modprobe?
insmod takes a full file path to a .ko file and loads it
directly without checking dependencies. If a required dependency module is not already
loaded, insmod will fail. modprobe takes a module name,
looks up the dependency graph in modules.dep, automatically loads all
required dependencies in the correct order, and then loads your module. For production
use, modprobe is always preferred. For out-of-tree development and
testing, insmod is convenient.
Q7. What does the “Used by” column in lsmod output mean?
It shows two things: the reference count (how many other modules or kernel subsystems
are currently using this module) and the names of those dependent modules. A module
cannot be unloaded if its reference count is greater than zero. You must first remove
or stop whatever is depending on it before rmmod will succeed.
Q8. Why does insmod fail with “Operation not permitted” even for a simple hello world module?
Loading a kernel module requires root privileges or the CAP_SYS_MODULE
Linux capability because the code runs directly inside the kernel with full hardware
access. On systems with Secure Boot in locked-down mode, even root may not load
unsigned modules. Always use sudo insmod or run as root.
Q9. Where does lsmod get its data from?
lsmod simply reads and formats the contents of /proc/modules,
which is a virtual file maintained by the kernel and updated in real time as modules
are loaded and unloaded.
Q10. Can you use ERR_PTR() in user-space programs?
No. The ERR_PTR mechanism depends on kernel virtual memory layout where the top 4096
addresses are guaranteed to never be valid pointer values. In user space, those addresses
could potentially be valid mappings, so the trick does not work reliably. This is a
kernel-only pattern. The functions are defined in include/linux/err.h which
is a kernel header and cannot be included in user-space programs.
