Module parameters in linux kernel

Introduction

In a general C program, we use command line arguments to provide values to the code during execution. This enables us to modify the process of execution and its functionality simply by passing different command line arguments. Similarly, within the kernel, there exists a method to pass values as command line arguments when loading kernel modules, this is achieved through module_param. Let’s explore into module_param in this article.

How to use it?

The variable which we are expecting to act as a module_param or a command line argument, have to declare globally.

static int val = 0;

This is just a global variable, set with default value 0. It turns into module parameter after using the below macro:

module_param(val, int, 0);

The prototype of module_param is:

module_param(name, type, permission);

name: the name of the variable to be used as a module parameter.
type: the data type of the variable.
module param supports the following data types:
byte, hexint, short, ushort, int, uint, long, ulong, charp(a character pointer), bool (a bool, values 0/1, y/n, Y/N)
invbool(inverted bool, only sense-reversed (N = true)).
permission: permission given to the user space.
these module params can be visible to the user space and will be discussed later in this chapter.
we used 0 is to disable module_param visiblity at userspace.

Sample program with interger type

#include <linux/kernel.h>
#include <linux/module.h>
/* define variable */
static int val = 0;
/* declare with module param */
module_param(val, int, 0);
static int __init my_module_init(void)
{
        printk("%s\n", __func__);
        printk("Module parameter value:%d\n", val);
        return 0;                                                               
}
static void __exit my_module_exit(void)
{
        printk("%s\n", __func__);
}
module_init(my_module_init);
module_exit(my_module_exit);

MODULE_LICENSE("GPL");

insert the module

$ sudo insmod onz_mod_param.ko val=10

This passes the value 10 to the variable val. if module param is not used at the time of loading the module, kernel module will take default value for the parameter, initialised at the time of global declaration. This can be verified at logs by dmesg

my_module_init<br>Module parameter value:10

When loaded without module param:

$ sudo insmod onz_mod_param.ko
my_module_init<br>Module parameter value:0

Accessing module parameter from user space

module parameters can be accessed from user space based on the permissions given at 3rd argument of module_param() macro. If the permissions are given other than 0, then the module params are visible at /sys/module/<your_module>/paramters/ directory. If 0 is given as a permission the parameters folders will not present in /sys/module/<your_module>/ directory, which means user user space have no idea of module parameters using by your_module.

module_param(val, int, S_IRUSR);

This gives read permissions to the user space and can be seen at

#cat /sys/module/onz_mod_param/parameters/val<br>10

A write permission can be given by using

module_param(val, int, S_IRUSR | S_IWUSR);
#echo 2 > /sys/module/onz_mod_param/parameters/val<br>#cat /sys/module/onz_mod_param/parameters/val<br>2

By using echo 2 > /sys/module/onz_mod_param/parameters/val, the module param value is changed to 2. Kernel module have no idea about this change and can lead to malfunction of the driver. So the driver writer have to use kernel_param_lock() and kernel_param_unlock(), when write permission is given to a module param.

Protecting module parameters from user space

While module parameters can be overwritten from userspace, users can change the value at any time. A driver might be performing a task where it depends on the value of a module parameter. However, if the user changes the value at the same time, this affects the driver’s functionalities. Let’s examine an example to understand how we can protect or prevent users from changing the value when the driver is using the module parameter.

#include <linux/kernel.h>
#include <linux/module.h>
/* define variable */
static int val; 
/* declare with module param */
module_param(val, int, S_IRUSR | S_IWUSR);

static int __init my_module_init(void)
{
        int i = 0;

        printk("%s\n", __func__);
        /* Lock the module paramter */
        kernel_param_lock(THIS_MODULE);
        printk("Module parameter value:%d\n", val);
        for (i; i < val; i++)
                  printk("iteration:%d/%d\n", i, val);
        /* Release the module parameter after use */
        kernel_param_lock(THIS_MODULE);
        return 0;                                                               
}
static void __exit my_module_exit(void)
{
        printk("%s\n", __func__);
}

module_init(my_module_init);
module_exit(my_module_exit);


MODULE_LICENSE("GPL");

As there is a loop running in the kernel module, the number of iterations are depended on the val. If user changes the val at the time of iteration, this will effect the iteration count leading to malfunction of the driver. so module parameter is protected within this region, so that even if user attempts to change the val kernel keeps user waiting till the lock is released.