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, ¶m_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
2 thoughts on “Linux Kernel Development – Kernel Module Parameters”
Comments are closed.
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.
[…] 로드할 때 insmod helloworld key=value 처럼 파라미터를 넘길 수 있다. 이 부분은 관련 링크로 남기고 글을 […]