Linux Kernel Module Parameters and Runtime Configuration

Linux Kernel Module Parameters
Free Linux Kernel Programming Course — EmbeddedPathashala
Level
Intermediate
Kernel
Linux 6.x
Part of
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
Topics Covered in This Lecture
module_param() MODULE_PARM_DESC() modinfo sysfs parameters insmod parameters Linux 6.x kernel module kernel parameter validation charp int bool free linux kernel course

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.

How Kernel Module Parameters Work
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:

module_param() Macro — Argument Breakdown
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.
  • charp is the kernel’s type name for a char * (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
Note: When passing string values that contain spaces, wrap them in escaped quotes on the shell: 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.

sysfs Layout for a Loaded Kernel Module
/
sys/
module/
mymod/
parameters/
mp_debug_level    [rw-rw—- root root]
mp_strparam      [rw-rw—- root root]

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.

Important: Access to these sysfs files is governed by the permission bits you set in 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.

Supported module_param() Data Types in Linux 6.x
Type Keyword C Equivalent Example Value Notes
byteunsigned char2550–255 only
shortshort-32768Signed 16-bit
ushortunsigned short65535Unsigned 16-bit
intint42Most common integer type
uintunsigned int100Unsigned 32-bit
longlong1000000Platform-dependent size
ulongunsigned long4294967295Unsigned long
charpchar *“mystring”Character pointer / string
boolbool0/1 or y/n or Y/NTrue/false flag
invboolbool (inverted)N = trueSense-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.

Common Permission Values for module_param()
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
Design Tip: Always choose a default value that is clearly invalid for mandatory parameters. Using 0 or -1 as the default makes the “was this passed?” check simple and unambiguous.

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 what checkpatch.pl recommends.
  • Validate parameters early in module_init() before allocating any resources. If validation fails, return -EINVAL immediately 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:

Real-World Module Parameter Use Cases
Parameter Name Type Driver Category What It Controls
debugintMost driversVerbosity level of kernel log output
irqintLegacy ISA/PCIInterrupt request line number
baudrateuintUART/SerialSerial port baud rate
timeout_msuintI2C/SPIHardware communication timeout
num_queuesuintNetwork driversNumber of TX/RX hardware queues
max_speed_hzuintSPIMaximum 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 what modinfo shows.
  • 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 -EINVAL if 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

Continue Learning — Free Linux Kernel Course

EmbeddedPathashala offers a completely free Linux kernel programming and device drivers course. No fees, no login walls.

Visit EmbeddedPathashala

Leave a Reply

Your email address will not be published. Required fields are marked *