Module Parameters: Passing Data to Modules

In this article, we will dive into one of the crucial aspects of Linux kernel modules: module parameters. Module parameters offer an elegant way to configure and tune kernel modules without needing to recompile them. Understanding how to define and use these parameters effectively is essential for anyone looking to enhance the functionality of their modules.

What are Module Parameters?

Module parameters are variables that can be passed to kernel modules at load time, allowing users to configure how the module behaves. They allow for a more flexible module design by enabling runtime configuration. This way, you can tweak settings and performance without going through the entire recompilation and deployment cycle.

Defining Module Parameters

To define a module parameter, you use the module_param macro within your module's source code. The syntax for defining a module parameter is:

module_param(name, type, permission);
  • name: The name of the parameter as it will be visible to users.
  • type: The data type of the parameter. Valid types include int, bool, string, charp (character pointer), etc.
  • permission: The permissions for the parameter. This could be S_IRUGO for read-only to all, or S_IRWXU for read and write access as per user permissions.

Here’s a simple example of defining a module parameter of type int:

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

static int my_param = 1; // Default value

module_param(my_param, int, S_IRUGO);
MODULE_PARM_DESC(my_param, "An integer parameter");

In this snippet, we define an integer parameter named my_param. It’s initialized to 1, is read-only for users, and has a module parameter description that helps in understanding what it does.

Loading the Module with Parameters

Once you have defined your module parameter and compiled your kernel module, the next step is to load it with the specified parameters. You can do this with the insmod command followed by the module name and parameter assignment.

sudo insmod mymodule.ko my_param=10

In this case, my_param will be set to 10 when the module is loaded. If you don't specify a parameter, the default value defined in your code will be used.

Accessing Module Parameters in Code

When accessing the value of your module parameters in the code, you simply use the variable name that you have defined. Any changes made to the parameters from the command line or through other means reflect immediately in the module.

Here's how you can print the value of your parameter within the module:

printk(KERN_INFO "my_param is set to %d\n", my_param);

This line will output the current value of my_param in the kernel logs, which you can view using the dmesg command.

Parameter Types and Their Usage

Let’s explore some commonly used parameter types:

  1. Integer Parameters (int): These are used for integer values. You might use it for buffers sizes, timeouts, or any numeric configurations.

    static int buffer_size = 256;
    module_param(buffer_size, int, S_IRUGO);
    
  2. Boolean Parameters (bool): For toggling features on or off, boolean parameters are ideal.

    static bool enable_feature = false;
    module_param(enable_feature, bool, S_IRUGO | S_IWUSR);
    
  3. String Parameters (charp): Useful for passing file paths or other string data.

    static char *file_path = "/tmp/default";
    module_param(file_path, charp, S_IRUGO | S_IWUSR);
    

Describe Your Parameters

Using the MODULE_PARM_DESC macro is crucial. Providing descriptions can significantly help users understand what each parameter does, especially when working in a larger team or for public modules.

MODULE_PARM_DESC(buffer_size, "Size of the buffer");
MODULE_PARM_DESC(enable_feature, "Enable or disable the feature");
MODULE_PARM_DESC(file_path, "Path to the configuration file");

Runtime Changes and Dynamic Parameters

In addition to defining static parameters, you can also use dynamic parameters by employing a combination of the kernel's kobject and sysfs. This allows you to modify parameter values while the module is loaded, which can be very powerful for fine-tuning performance or behavior at runtime.

To create a sysfs entry, follow these steps:

  1. Use the kobject_create to create a new kobject.
  2. Inside the kobject, create attributes associated with the parameters.
  3. Implement show and store methods for reading and writing parameters.

Here’s a quick example:

ssize_t my_param_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) {
    return sprintf(buf, "%d\n", my_param);
}

ssize_t my_param_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) {
    sscanf(buf, "%d", &my_param);
    return count;
}

static struct kobj_attribute my_param_attribute = __ATTR(my_param, 0664, my_param_show, my_param_store);

Removing Module Parameters

If you ever need to remove a parameter or change its behavior, make sure you clean up properly. When you unload your module with rmmod, the parameters will no longer be accessible, but it's always good practice to handle resource cleanup in your module code before removal.

Debugging with Parameters

Module parameters can also assist in debugging. By allowing configurable log levels or enabling verbose debug output, you can quickly adapt the module’s behavior without recompilation.

static int debug_level = 0;
module_param(debug_level, int, 0);

if (debug_level > 0) {
    printk(KERN_DEBUG "Debugging enabled at level %d\n", debug_level);
}

Conclusion

Module parameters play a vital role in making Linux kernel modules flexible and user-friendly. By defining, using, and documenting parameters properly, you can significantly enhance the usability and configurability of your modules. Remember to choose the right types for your parameters, utilize permissions wisely, and provide clear descriptions to make life easier for users interacting with your kernel module.

As you develop more advanced modules, you'll likely come across additional parameter types and management techniques. Keep experimenting and refining your approach, and you'll find that the kernel can be an incredibly powerful tool for networking and infrastructure tasks. Happy coding!