Modules with parameters

Posted by Marcus Folkesson on Tuesday, June 27, 2017

Modules with parameters

Everybody knows that modules can take parameters, either via /sys/modules/<module>/parameters, sysctl or via cmdline to the kernel, but how are these parameters created?

Parameters without callbacks

The Linux kernel provides the module_param() macro. The syntax is:

1module_param(name, type, perm)

Which will simply create the module parameter and expose it as an entry in /sys/modules/<module>/parameters.

Code example

1int debug_flag;
2module_param(debug_flag, bool, S_IRUSR | S_IWUSR | S_IRGRP)
3MODULE_PARM_DESC(debug_flag, "Set to 1 if debug should be enabled, 0 otherwise"); 

MODULE_PARM_DESC() is a short description of the parameter. modinfo will read the description and present it for you.

Parameters with callbacks

Sometimes it may be useful to actually notify the driver that the value of a parameter has changed, which not the regular module_param() macro does.

module_param_cb is the way to go.

The macro takes two callbacks functions, set and get, that is called when the user (or kernel if in cmdline) interact with the parameters. This is done by passing a struct kernel_param_ops to the macro. The syntax is:

1module_param_cb(name, ops, arg, perm)

module_param_cb() is not heavily used in the kernel if we look in the drivers: :

1$ git grep module_param_cb drivers/
2drivers/acpi/sysfs.c:module_param_cb(debug_layer, &param_ops_debug_layer, &acpi_dbg_layer, 0644);
3drivers/acpi/sysfs.c:module_param_cb(debug_level, &param_ops_debug_level, &acpi_dbg_level, 0644);
4drivers/char/ipmi/ipmi_watchdog.c:module_param_cb(action, &param_ops_str, action_op, 0644);
5drivers/char/ipmi/ipmi_watchdog.c:module_param_cb(preaction, &param_ops_str, preaction_op, 0644);
6drivers/char/ipmi/ipmi_watchdog.c:module_param_cb(preop, &param_ops_str, preop_op, 0644);

In fact, there are just 5 entries, don't ask me why, I think the macro is terrific. The interface is really simple, just fill the kernel_param_ops struct and pass it to the module_param_cb() macro. I think the code is quite self-explained, so I just post an example taken from drivers/acpi/sysfs.c.

Code example

 1static int param_get_debug_level(char *buffer, const struct kernel_param *kp)
 2{
 3	int result = 0;
 4	int i;
 5	result = sprintf(buffer, "%-25s\tHex        SET\n", "Description");
 6	for (i = 0; i < ARRAY_SIZE(acpi_debug_levels); i++) {
 7		result += sprintf(buffer + result, "%-25s\t0x%08lX [%c]\n",
 8				acpi_debug_levels[i].name,
 9				acpi_debug_levels[i].value,
10				(acpi_dbg_level & acpi_debug_levels[i].value)
11				? '*' : ' ');
12	}
13	result +=
14		sprintf(buffer + result, "--\ndebug_level = 0x%08X (* = enabled)\n",
15				acpi_dbg_level);
16	return result;
17}
18
19static struct kernel_param_ops param_ops_debug_level = {
20	.set = param_set_uint,
21	.get = param_get_debug_level,
22};
23module_param_cb(debug_level, &param_ops_debug_level, &acpi_dbg_level, 0644);

There is also a set of standard set/get-functions (the code above use param_set_uint for example).

Take a look in include/linux/moduleparam.h for further reading!