This week, we're going to discuss the ins and outs of adding parameters to your loadable modules and, as we did last week, we're going to work off the appropriate section in the book Linux Device Drivers (3rd ed.) (LDD3), that you can find online here. And again, as we did last week, we'll be dealing with some of the content from Chapter 2. (The archive of all previous "Kernel Newbie Corner" articles can be found here.)
This is ongoing content from the Linux Foundation training program. If you want more content please, consider signing up for one of these classes.
So How Do We Start?
Let's start with a module source file p1.c that has nothing to do with parameters:
static int answer = 42 ;
static int __init hi(void)
printk(KERN_INFO "p1 module being loaded.\n");
printk(KERN_INFO "Initial answer = %d.\n", answer);
static void __exit bye(void)
printk(KERN_INFO "p1 module being unloaded.\n");
printk(KERN_INFO "Final answer = %d.\n", answer);
MODULE_AUTHOR("Robert P. J. Day");
MODULE_DESCRIPTION("No parameters here.");
Only one thing is slightly different about this new version of our classic module--the definition of the static int variable answer, which is initialized to 42. And since that's the initial value that's compiled directly into the module, it comes as no surprise that, when we compile, load and unload this module, we expect to see the following in /var/log/messages:
... localhost kernel: p1 module being loaded.
... localhost kernel: Initial answer = 42.
... time passes, until we unload ...
... localhost kernel: p1 module being unloaded.
... localhost kernel: Final answer = 42.
Obviously, since we started with answer having the value 42, and didn't change it anywhere, we're not at all surprised to see that the value is still 42 upon module exit. And now, we drag parameters into it.
So What Do We Do With "Parameters?"
As the name suggests, module parameters give us the ability to pass a value of some kind to a module upon loading, and also to examine its value while the module is loaded and running, and even change it on the fly if we want. Here's the simple change we would make to the code above (let's call the new source file p2.c):
#include <linux/moduleparam.h> // add this
static int answer = 42 ;
module_param(answer, int, 0644);
MODULE_PARM_DESC(answer, "Life, the universe, etc.");
Now recompile your module and, without even loading it, let's discuss what just happened above:
Technically, if you want to define any parameters, you should include the header file moduleparam.h. Practically, it turns out that that's not really necessary since the file module.h already does that, but it's cleaner for you to do it explicitly, just in case that weird and indefensible inclusion is ever fixed some day.
Parameter variables should be defined in your module statically and at file scope, not within the scope of any function. (Typically, all parameter variable definition takes place near the top of your module source file. It just looks cleaner that way.)
What the above does is define the variable answer as a module parameter of (not surprisingly) type int, with permission 0644 (to be discussed shortly) and a brief description of the parameter itself.
After you compile your module, you can see all of the above via the modinfo command, thusly:
$ modinfo p2.ko
description: Trivial example of an int parameter
author: Robert P. J. Day
vermagic: 126.96.36.199-191.fc11.x86_64 SMP mod_unload
parm: answer:Life, the universe, etc. (int) <-- oh, look!
The old macro for defining module parameters was MODULE_PARM, as opposed to the newer macro of module_param, but you'll probably find the occasional historical cruft hanging out in the kernel source tree referring back to the original form, at least in the occasional comment or for backward compatibility.
Given that you'll be working with numerous module source files this week, you can create different subdirectories for each single source file or, for brevity, you can just stuff all the source files in the same directory and modify the Makefile thusly:
obj-m = p1.o p2.o p3.o ...
which will rebuild all of your loadable modules as needed upon each invocation of make. Your choice.
So What Do I Do With That Parameter?
# insmod p2.ko answer=6
# rmmod p2
whereupon you expect to see in /var/log/messages:
... kernel: p2 module being loaded.
... kernel: Initial answer = 6.
... time passes ...
... kernel: p2 module being unloaded.
... kernel: Final answer = 6.
If you hadn't set the parameter value during the module load, the value would have been 42 throughout the life of the module as it was before. But since you set the parameter value to six at load time, that value was used instead to initialize the variable. This is the sort of thing that is used by some modules to be told, for instance, their I/O address or interrupt line upon loading, and you should appreciate just how handy that can be.
But wait ... there's so much more.
Poking Around Under /sys
We've never mentioned it before but, on most sane Linux systems, the instant you load a module, an entire subdirectory structure is created for it under /sys/module/modulename. Assuming you've reloaded your p2 module with an answer parameter value of 6, here's what you should expect to find:
$ ls -1F /sys/module/p2
You can poke around the other entries related to your p2 module but we're most interested in:
$ ls -l /sys/module/p2/parameters/
-rw-r--r--. 1 root root 4096 2009-07-11 09:00 answer
and there it is--a user-readable way to examine the current value of a module parameter in real time while the module is loaded and running. Just list it:
$ cat /sys/module/p2/parameters/answer
Not surprisingly, if your module changes that variable while it's running, the value you see will correspond to whatever is stored there at the time of examination. But, yes, there's more.
What's With That Parameter Permission Setting?
Easy--as with regular files, those permission settings dictate who is allowed to do what with that parameter file under /sys/module. In our case, since it has a file mode of 644, the owner (root) can both read and write, and everyone else can read.
It's easy to see what it means to have read access, but what does it mean to have write access? It means that, on the fly, you can do this to change the value of that internal variable at any time while the module is loaded:
# echo 21 > /sys/module/p2/parameters/answer
# cat /sys/module/p2/parameters/answer
and when you finally unload the module, you shouldn't be surprised to note that that variable now has the value 21 (unless, of course, you changed it yet again).
So what else do you need to know about parameter permissions? There are a few things.
First, when you define parameter permissions in your code, you can use numeric values as we did above (such as 0644) or, if you include the header file <linux/stat.h>, you can use the fairly intuitive macros:
#define S_IRWXU 00700
#define S_IRUSR 00400
#define S_IWUSR 00200
#define S_IXUSR 00100
#define S_IRWXG 00070
#define S_IRGRP 00040
#define S_IWGRP 00020
#define S_IXGRP 00010
#define S_IRWXO 00007
#define S_IROTH 00004
#define S_IWOTH 00002
#define S_IXOTH 00001
or any bitwise OR'ed combination to get the same effect. It's entirely up to you.
Next, if you create a parameter with a permission setting of zero, that means that that parameter will not show up under /sys at all, so no one will have any read or write access to it whatsoever (not even root). The only use that parameter will have is that you can set it at module load time, and that's it.
Finally (and this one's kind of important), if you choose to define writable parameters and really do write to them while your module is loaded, your module is not informed that the value has changed. That is, there is no callback or notification mechanism for modified parameters; the value will quietly change in your module while your code keeps running, oblivious to the fact that there's a new value in that variable.
If you truly need write access to your module and some sort of notification mechanism, you probably don't want to use parameters. There are better ways to get that functionality.
Of course. Where do we even begin?
First, module parameters can have a number of different types such as int, uint, short, ushort, long, bool and a few others, all defined in the header file <linux/moduleparam.h>. You can even define array-type parameters. All of this is covered in the aforementioned section of LDD3, and testing any of those is left as an exercise for the reader.
Next, now that you know that loading your module creates an entire directory substructure under /sys/module/modulename that lists that module's properties and attributes, it should come as no surprise that you can view that same directory structure for any loaded module thusly:
$ modinfo fuse
which will pick up most of its output from that very directory.
The next neat feature is that it's possible to define the filename that shows up under /sys as being different from the name of the actual static variable thusly:
static int answer = 42 ;
module_param_named(readable_answer, answer, int, 0444);
MODULE_PARM_DESC(readable_answer, "The readable answer");
In the above snippet, the internal variable has the conveniently brief name of answer, while the visible filename under /sys that corresponds to it will have the (perhaps) more self-descriptive name of readable_answer. In fact, nothing stops you from defining more than one renaming of the same internal variable with different permission settings, for whatever reason you can imagine.
And, finally, if the pre-defined parameter data types aren't adequate, you can in fact create parameters with entirely user-defined data types as described here, although this doesn't appear to be a very commonly-used feature so I wouldn't worry too much about that.
Exercises for the Reader
And here's where we start a brand new feature of this column--puzzles for the reader to solve.
- As we noted above, it's possible (although admittedly unlikely) that a single static variable in a module source file has been defined with multiple module_param_named() calls to show up as more than one filename under the directory /sys/module/modulename/parameters/. Can you find such an example anywhere under the kernel source drivers/ directory? Leave a solution in the comments section.
- Is there any example anywhere under drivers/ of someone creating a user-defined parameter type for their module? Where?
- Use lsmod to list the loaded modules on your system, then run modinfo on some of them to see which ones have interesting parameters.