Linux Kernel Development – Kernel Module Parameters

In the previous post , I covered the basics of kernel development with a simple example of loadable kernel module that has only init and exit functions. In this post we will add a parameters to the module. Using the parameters, you can access the module global variables while loading the module and on runtime while the module is already loaded.

Loading a module with parameters

When you load a module using insmod command you can supply the parameters as key=value pairs for example:

# insmod ./mymod.ko irq=20 name=mydev debug=1 address=0x1000,0x2000,0x3000

The parameter can be a number, a string or an array (of numbers or strings)

To declare a simple parameter in a module, declare a global variable and use module_param macro for example:

#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/kernel.h> 

static int irq=10;
module_param(irq,int,0660);

static int debug=0;
module_param(debug,int,0660);

static char *devname = "simpdev";
module_param(devname,charp,0660);


static int simple_init(void)
{
	printk(KERN_WARNING "hello... irq=%d name=%s debug=%d\n",irq,devname,debug);
	return 0;
}

static void simple_cleanup(void)
{
	printk(KERN_WARNING "bye... irq=%d name=%s debug=%d\n",irq,devname,debug);
}

module_init(simple_init);
module_exit(simple_cleanup);

Now you can compile and load the module with irq, debug and devname parameters:

# insmod ./simple.ko irq=44 devname=simpdev debug=0

The global variable will be initialized before the init function called use dmesg to see the output:

# dmesg 
...
[  144.526050] hello... irq=44 name=simpdev debug=0

module_param macro

The macro prototype is

 module_param(name, type, perm)

name – name of an already defined variable

type – parameter type(int,long,charp,…)

perm – permissions for /sys/module/<module_name>/parameters/<param> (or 0 for no such module parameter value file)

 

Reading and Changing the parameter value

The file in /sys/module/<module_name>/parameters is used to read/write the parameter value while the module is loaded. For example if we loaded a module with debug=0 and we want to turn on debugging messages without reloading the module we can use the file:

# echo "1">/sys/module/simple/parameters/debug

You can see the debug variable changed while you unload the module

# rmmod simple
# dmesg 
...
[  362.125050] bye... irq=44 name=simpdev debug=0

Validating the parameter value

Using the file in /sys , if we have permission, we can set any value we want. If for example the irq can only be in the range 1 – 32 , there is no way to validate it because the user access the variable directly. To solve this problem we can implement an interface for setting an getting parameter values – kernel_param_ops

struct kernel_param_ops {
	/* How the ops should behave */
	unsigned int flags;
	/* Returns 0, or -errno.  arg is in kp->arg. */
	int (*set)(const char *val, const struct kernel_param *kp);
	/* Returns length written or -errno.  Buffer is 4k (ie. be short!) */
	int (*get)(char *buffer, const struct kernel_param *kp);
	/* Optional function to free kp->arg when module unloaded. */
	void (*free)(void *arg);
};

To implement the interface we need to define a variable , implement the set/get methods and register it using module_param_cb:

static int my_set(const char *val, const struct kernel_param *kp)
{
	int n = 0, ret;

	ret = kstrtoint(val, 10, &n);
	if (ret != 0 || n < 1 || n > 32)
		return -EINVAL;

	return param_set_int(val, kp);
}

static const struct kernel_param_ops param_ops = {
	.set	= my_set,
	.get	= param_get_int,
};

static int num;
module_param_cb(simpcb, &param_ops, &num, 0664);

The actual parameter value is saved in num , the set function get the user input as a string and convert it to int , then we check if the value is correct (1 – 32) and set it using the param_set_int function provided by the kernel. To return the parameter value we use the provided param_get_int function

If we try to load the module with wrong value or if we try to set a wrong value using the /sys file beget an error:

$ sudo insmod ./simp.ko irq=9 devname=mydev debug=0 simpcb=200
insmod: ERROR: could not insert module ./simp.ko: Invalid parameters
# echo "70">/sys/module/simp/parameters/simpcb 
bash: echo: write error: Invalid argument

We can also define a custom function for get but in this example its not necessary. It will be useful if you want the parameter value provided using a string but saved as a number

For example if we want a parameter for interrupt mode to be one from level, edge, polling but we want to save it as a number (1 – level, 2-edge, 3-polling) we will define the set function to convert from string to int and the get function to convert from int to string

For Example:

enum irq_type {
	IRQ_TYPE_LEVEL,
	IRQ_TYPE_EDGE,
	IRQ_TYPE_POLLING
};

static int irq_type = IRQ_TYPE_LEVEL; // default

static int irqtype_op_write_handler(const char *val, const struct kernel_param *kp)
{
	char valcp[16];
	char *s;

	strncpy(valcp, val, 16);
	valcp[15] = '\0';

	s = strstrip(valcp);

	if (strcmp(s, "level") == 0)
		irq_type = IRQ_TYPE_LEVEL;
	else if (strcmp(s, "edge") == 0)
		irq_type = IRQ_TYPE_EDGE;
	else if (strcmp(s, "polling") == 0)
		irq_type = IRQ_TYPE_POLLING;
	else
		return -EINVAL;

	return 0;
}

static int irqtype_op_read_handler(char *buffer, const struct kernel_param *kp)
{
	switch (irq_type) {
	case IRQ_TYPE_LEVEL:
		strcpy(buffer, "Level");
		break;

	case IRQ_TYPE_EDGE:
		strcpy(buffer, "Edge");
		break;

	case IRQ_TYPE_POLLING:
		strcpy(buffer, "Polling");
		break;

	default:
		strcpy(buffer, "error");
		break;
	}

	return strlen(buffer);
}

static const struct kernel_param_ops irqtype_op_ops = {
	.set = irqtype_op_write_handler,
	.get = irqtype_op_read_handler
};

module_param_cb(irqtype, &irqtype_op_ops, NULL, 0660);

To load the kernel module or change the parameter value, use a string:

# sudo insmod ./simp.ko irq=9 devname=mydev debug=0 simpcb=20 irqtype=edge
# cat /sys/module/simp/parameters/irqtype 
Edge

 

Declare a module parameter array

You can declare a parameter as array of numbers or string using module_param_array. For example:

static int addr[SIZE];
static int count;
module_param_array(addr, int, &count,  0660);

While loading the module, use comma separated list to provide the values:

# insmod ./simp.ko addr=0x1000,0x2000,0x3000

count is an output parameter and updated with the number of element that provided by the user (in this example 3)

Some notes about module parameters:

  • If you load a module using modprobe, you can set the parameters in /etc/modprobe.conf (or other file – depends on your distribution)
  • If you compile the module with the kernel (static), you can provide the parameters using the kernel command line on boot time
  • There are more functions – look at the source code (moduleparam.h)

You can find the full source code here

 

 

Tagged

2 thoughts on “Linux Kernel Development – Kernel Module Parameters

  1. Thanks for the post. very concise in briefing the concept.
    I have one more doubt, How to pass the value to the module while loading automatically, suppose If I load the module manually using insmod I can pass the value by setting the “variable=123” but in my case the modules are loaded automatically at bootup. I tried creating my_module.conf file with module name and variable name = “” in “/etc/modules-load.d/” But my printk in the module prints only the default global value. Please give me suggestion How to pass the values to the module while loading it automatically.

    Thanks,
    Srini.

  2. […] 로드할 때 insmod helloworld key=value 처럼 파라미터를 넘길 수 있다. 이 부분은 관련 링크로 남기고 글을 […]

Comments are closed.