Intermediate
Linux 6.x
Free LKP Course
Welcome to this lecture from the free Linux kernel programming course at EmbeddedPathashala. In this tutorial, you will learn how kernel module parameters work in Linux 6.x. This is one of the most practical topics in free Linux device drivers development — it lets you control your module’s behaviour without recompiling it every time. Whether you are writing a device driver or a standalone kernel module, understanding module parameters will save you a lot of debugging time.
What You Will Learn
- What kernel module parameters are and why they matter in Linux 6.x driver development
- How to declare and use the
module_param()macro - How to inspect module parameters using
modinfo - How to pass parameters during module insertion with
insmod - How the sysfs filesystem exposes module parameters at runtime
- All supported parameter data types in the kernel
- How to validate parameters and make them mandatory
- Best practices around permission bits for sysfs pseudo-files
Prerequisites
- Basic understanding of C programming
- A working Linux 6.x environment with kernel headers installed
- Familiarity with writing and loading a simple kernel module (
insmod,rmmod) - Basic comfort with the Linux terminal
What Are Kernel Module Parameters?
When you write a kernel module, sometimes you want to change its behaviour based on runtime input rather than hardcoding values inside the source code. For example, you might want to control the verbosity of debug messages, or pass a device name, or set a timeout value — all without touching the source and recompiling.
This is exactly what kernel module parameters allow. They are variables declared inside your module that can be set from the command line at load time, or even changed later through the sysfs filesystem while the module is running. In the Linux 6.x kernel, this feature is mature, well-supported, and widely used in real device drivers.
| Developer declares variable static int mp_debug_level = 0; |
→ | Registers with kernel using module_param() |
→ | User passes value at load time insmod mod.ko val=2 |
→ | Kernel sets variable before module_init() runs |
The module_param() Macro — Syntax and Usage
The kernel provides the module_param() macro to register a module parameter. You must include <linux/moduleparam.h> in your module source (this is usually pulled in transitively through <linux/module.h>).
The macro takes three arguments:
| Argument | What It Means | Example |
|---|---|---|
| name | The name of the C variable declared in your module | mp_debug_level |
| type | The data type of the variable (see full list below) | int |
| perm | Octal permission for the sysfs pseudo-file (0 = no sysfs file created) | 0660 |
Here is a complete, minimal example of declaring two module parameters — an integer and a string:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
/* Declare parameters with defaults */
static int mp_debug_level = 0;
static char *mp_strparam = "My string param";
/* Register them with the kernel */
module_param(mp_debug_level, int, 0660);
module_param(mp_strparam, charp, 0660);
/* Describe each parameter — shown by modinfo */
MODULE_PARM_DESC(mp_debug_level,
"Debug level [0-2]; 0 => no debug messages, 2 => high verbosity");
MODULE_PARM_DESC(mp_strparam,
"A demo string parameter");
static int __init mymod_init(void)
{
pr_info("mymod: inserted\n");
pr_info("mp_debug_level=%d mp_strparam=%s\n",
mp_debug_level, mp_strparam);
return 0;
}
static void __exit mymod_exit(void)
{
pr_info("mymod: removed\n");
}
module_init(mymod_init);
module_exit(mymod_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("EmbeddedPathashala");
MODULE_DESCRIPTION("Module parameter demo — Linux 6.x");
A few important things to note about this code:
- The variables must be declared
static— they belong to the module’s scope only. - The default values you assign in the declaration are used when the user does not pass anything.
charpis the kernel’s type name for achar *(character pointer / string).MODULE_PARM_DESC()is optional but strongly recommended — it makes your module self-documenting.- In Linux 6.x the macro internals have not changed from a usage standpoint, so this same pattern works identically on modern kernels.
Inspecting Parameters Before Loading — modinfo
Before you even load a kernel module, you can check what parameters it accepts using the modinfo command. This reads the compiled .ko file directly and prints metadata including all registered parameters:
$ modinfo -p ./mymod.ko
parm: mp_debug_level:Debug level [0-2]; 0 => no debug messages, 2 => high verbosity (int)
parm: mp_strparam:A demo string parameter (charp)
The format is: parameter_name : description (data_type). This is what gets populated from your MODULE_PARM_DESC() calls. It is useful for end users and system integrators who need to understand what the module accepts without reading the source code.
Passing Kernel Module Parameters at Load Time
When you load a module using insmod, parameters are passed as key-value pairs right on the command line. If you do not pass them, the variables retain their default values.
Loading without parameters (uses defaults):
sudo dmesg -C
sudo insmod ./mymod.ko
dmesg
You will see output like:
[ 1234.000001] mymod: inserted
[ 1234.000002] mp_debug_level=0 mp_strparam=My string param
Loading with explicit parameter values:
sudo rmmod mymod
sudo insmod ./mymod.ko mp_debug_level=2 mp_strparam="Hello from EmbeddedPathashala"
dmesg
Output:
[ 1300.000001] mymod: inserted
[ 1300.000002] mp_debug_level=2 mp_strparam=Hello from EmbeddedPathashala
mp_strparam=\"Hello World\". The kernel receives the string without the quotes.
Reading and Writing Parameters at Runtime — sysfs
This is where kernel module parameters become truly powerful. When you set the permission argument in module_param() to a non-zero value like 0660, the kernel automatically creates pseudo-files under /sys/module/<module-name>/parameters/. These files represent your parameters and can be read or written at any time while the module is loaded — without reloading the module.
| / |
|
Here is how you work with these files in practice:
Check the current value of a parameter:
sudo cat /sys/module/mymod/parameters/mp_debug_level
2
Change the value at runtime (no module reload needed):
sudo bash -c "echo 0 > /sys/module/mymod/parameters/mp_debug_level"
sudo cat /sys/module/mymod/parameters/mp_debug_level
0
This is extremely useful in production. For example, you can increase the debug level on a running device to capture diagnostic messages and then reduce it again without any downtime. Device drivers in real products use this technique extensively.
module_param(). With 0660, only root (owner and group) can read or write them. A non-root user will get “Permission denied”. Always choose permissions carefully for production drivers.
All Supported Module Parameter Data Types
The kernel supports several built-in parameter types. You do not need to write any custom parsing code — the kernel handles conversion from the string representation the user types to the actual C type automatically.
| Type Keyword | C Equivalent | Example Value | Notes |
|---|---|---|---|
| byte | unsigned char | 255 | 0–255 only |
| short | short | -32768 | Signed 16-bit |
| ushort | unsigned short | 65535 | Unsigned 16-bit |
| int | int | 42 | Most common integer type |
| uint | unsigned int | 100 | Unsigned 32-bit |
| long | long | 1000000 | Platform-dependent size |
| ulong | unsigned long | 4294967295 | Unsigned long |
| charp | char * | “mystring” | Character pointer / string |
| bool | bool | 0/1 or y/n or Y/N | True/false flag |
| invbool | bool (inverted) | N = true | Sense-reversed bool |
The kernel also supports arrays of these types using module_param_array(), which is covered in an advanced lecture. For most use cases, the standard types above are more than sufficient.
Permission Bits for sysfs Files — Best Practice
The third argument to module_param() sets the file permissions of the corresponding sysfs entry. This is an octal value, just like what you use with chmod. The kernel community and the official kernel coding style checker (scripts/checkpatch.pl) both recommend using octal literals rather than symbolic macros like S_IRUSR.
| Octal Value | Meaning | Typical Use Case |
|---|---|---|
| 0 | No sysfs file created at all | Parameter only settable at load time; no runtime access |
| 0444 | Read-only for everyone | Show a value but never let it change at runtime |
| 0644 | Root can write; all can read | Read by any user, changed only by root |
| 0660 | Owner+group read/write; others no access | Most common for driver parameters |
/* Recommended style per kernel checkpatch — use octal, not symbolic */
module_param(mp_debug_level, int, 0660);
/* Avoid this even though it is technically valid */
/* module_param(mp_debug_level, int, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP); */
Making a Parameter Mandatory — Validation at Init Time
By default all kernel module parameters are optional. The module will load and use the default value if you do not pass a parameter. But what if your driver absolutely needs a specific value to function correctly? For example, a device address or a hardware channel number that has no sensible default?
The kernel does not have a built-in “mandatory parameter” mechanism. The standard approach is to check the parameter value inside your module_init() function and return an error if it is still at the default (usually 0 or NULL) or out of an expected range. This is a straightforward technique used by many real drivers.
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/errno.h>
static int control_level = 0;
module_param(control_level, int, 0660);
MODULE_PARM_DESC(control_level,
"Hardware control level [1-5]. MANDATORY — module will not load without this.");
static int __init mydriver_init(void)
{
/* Validate the mandatory parameter */
if (control_level 5) {
pr_err("mydriver: 'control_level' must be in range [1-5]. "
"Pass it as: insmod mydriver.ko control_level=\n");
return -EINVAL;
}
pr_info("mydriver: loaded successfully with control_level=%d\n",
control_level);
return 0;
}
static void __exit mydriver_exit(void)
{
pr_info("mydriver: unloaded\n");
}
module_init(mydriver_init);
module_exit(mydriver_exit);
MODULE_LICENSE("GPL");
When the user tries to load this module without the parameter:
sudo insmod ./mydriver.ko
# dmesg will show:
# mydriver: 'control_level' must be in range [1-5]. Pass it as: insmod mydriver.ko control_level=<1..5>
# insmod: ERROR: could not insert module ./mydriver.ko: Invalid argument
When the user provides the correct value:
sudo insmod ./mydriver.ko control_level=3
# dmesg will show:
# mydriver: loaded successfully with control_level=3
Best Practices for Kernel Module Parameters
- Always write MODULE_PARM_DESC() for every parameter you declare. It is the only documentation many users will ever see.
- Use meaningful prefix names like
mp_so parameters are easy to identify in large module source files. - Use octal literals for permissions, not symbolic macros like
S_IRUSR— this is whatcheckpatch.plrecommends. - Validate parameters early in
module_init()before allocating any resources. If validation fails, return-EINVALimmediately so no cleanup is needed. - Choose default values carefully. The default should either be safe to use as-is (for optional parameters) or obviously invalid (for mandatory ones).
- Set permissions to 0 if runtime change is not needed. No sysfs file will be created, reducing attack surface.
- Test your module with and without parameters during development to confirm default behaviour works correctly.
Common Mistakes and Troubleshooting
Mistake 1 — Using a non-static variable:
If you declare the variable without static, it leaks into the global kernel namespace and may cause link errors or symbol conflicts. Always use static.
/* Wrong */
int mp_debug_level = 0;
/* Correct */
static int mp_debug_level = 0;
Mistake 2 — Wrong type in module_param():
The type you write in module_param() must match the actual C type of the variable. Mismatches cause silent data corruption or build warnings.
static char *my_str = "default";
/* Wrong — using int for a char pointer */
module_param(my_str, int, 0660);
/* Correct */
module_param(my_str, charp, 0660);
Mistake 3 — Expecting sysfs file when permissions are 0:
If the third argument to module_param() is 0, no sysfs pseudo-file will appear under /sys/module/. The parameter is still usable at load time, but it cannot be read or changed at runtime.
Mistake 4 — Not checking parameter validity before using it:
The kernel does basic type conversion but does NOT validate ranges for you. If you pass mp_debug_level=99 and your code only handles 0–2, you will get undefined behaviour. Always add your own range check.
Real-World Use Cases in Linux Device Drivers
Module parameters are not just a learning exercise — they are used constantly in production Linux drivers. Here are common real-world applications:
| Parameter Name | Type | Driver Category | What It Controls |
|---|---|---|---|
| debug | int | Most drivers | Verbosity level of kernel log output |
| irq | int | Legacy ISA/PCI | Interrupt request line number |
| baudrate | uint | UART/Serial | Serial port baud rate |
| timeout_ms | uint | I2C/SPI | Hardware communication timeout |
| num_queues | uint | Network drivers | Number of TX/RX hardware queues |
| max_speed_hz | uint | SPI | Maximum SPI clock speed |
Summary and Key Takeaways
module_param(name, type, perm)registers a C variable as a kernel module parameter accessible from userspace.- Parameters can be passed at load time via
insmod mod.ko param=value. - When permissions are non-zero, sysfs creates a pseudo-file under
/sys/module/<name>/parameters/that can be read/written at runtime. - The supported types are:
byte, short, ushort, int, uint, long, ulong, charp, bool, invbool. - Always use
MODULE_PARM_DESC()to document your parameters — this is whatmodinfoshows. - Use octal literals (e.g.,
0660) for the permission argument, as per kernel coding style. - Mandatory parameters are implemented by checking the value in
module_init()and returning-EINVALif it is invalid. - All of this works identically in Linux 6.x — the API has been stable for a long time.
Frequently Asked Questions
Q1: Can I change a module parameter after the module is loaded?
Yes, if you set a non-zero permission value in module_param(). The kernel creates a sysfs file and you can write to it using echo value > /sys/module/<name>/parameters/<param> as root. However, your module code needs to handle race conditions if the parameter change triggers behaviour on another code path.
Q2: What happens if I pass an out-of-range value?
The kernel does NOT validate ranges for you. It only does type conversion. If you pass a value that your code cannot handle, the behaviour depends entirely on your driver logic. This is why you should always write your own range checks in module_init().
Q3: What is the difference between module_param() and module_param_array()?
module_param() handles a single value. module_param_array() allows passing a comma-separated list of values into an array. For example, you can pass multiple IRQ numbers or port addresses to a single driver parameter using the array variant.
Q4: Is using module parameters safe in a production driver?
Yes, and it is a common design pattern. The key is to set appropriate permissions so that parameters that affect hardware are not writable by unprivileged users. Setting permission to 0444 (read-only) or 0 (no sysfs file) reduces the attack surface significantly.
Q5: Does modprobe work the same way as insmod for parameters?
Yes. modprobe mod param=value works exactly like insmod for passing parameters. Additionally, modprobe reads /etc/modprobe.d/ configuration files, so you can set default parameter values persistently using options <module> param=value in a conf file there.
Q6: Will this code work on Linux 6.x ARM cross-compiled modules?
Yes. The module_param() API is architecture-independent. The same source code works on x86_64, ARM, ARM64 (AArch64), and RISC-V. When cross-compiling, you just point your Makefile to the right kernel source tree with ARCH and CROSS_COMPILE set correctly.
Q7: How do I find the parameters of a module that is already loaded?
Look under /sys/module/<module_name>/parameters/. Each file there is a parameter you can cat. If a parameter was declared with permission 0, it will not appear there — you would need to check the source or use modinfo on the .ko file before loading.
Q8: Can two kernel modules share the same parameter name?
Yes, because each module has its own isolated namespace in sysfs under /sys/module/<its_own_name>/. Parameter names only need to be unique within the same module’s source.
Further Reading and References
EmbeddedPathashala offers a completely free Linux kernel programming and device drivers course. No fees, no login walls.
Visit EmbeddedPathashala