The Kernel Newbie Corner: Loadable Kernel Modules, Coming and Going

2185

This week, we’re going to take an excruciatingly close look at one of the fundamental features of loadable kernel modules–the “init” and “exit” routines–those functions that are invoked upon module loading and unloading, respectively. And I’m not at all ashamed to admit that I’m swiping a good chunk of this column from the classic book, “Linux Device Drivers (3rd ed.)” (LDD3), available online here. Specifically, we’ll be filching our stuff from Chapter 2, but expanding on a good bit of it. (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 What Are Those Routines Supposed to Do?

Let’s start over with the most basic of loadable modules, shall we?

#include <linux/module.h>       // for all modules
#include <linux/init.h> // for entry/exit macros
#include <linux/kernel.h> // for printk priority macros

static int __init hi(void)
{
printk(KERN_INFO "hi module being loaded.n");
return 0;
}

static void __exit bye(void)
{
printk(KERN_INFO "hi module being unloaded.n");
}

module_init(hi);
module_exit(bye);

MODULE_AUTHOR("Robert P. J. Day");
MODULE_LICENSE("GPL");

If you want, do a quick build, load and unload of that module to make sure everything still works.

Now, the purpose of a module’s entry and exit routines is simple: it’s your job to invoke everything that needs to happen upon module loading (allocating memory, registering devices, etc.), as well as module unloading (returning memory, unregistering devices, etc.), and here’s the most important part: You have to clean up after yourself completely.

Let me say that again, with feeling: Whatever you choose to do in a module’s entry routine must be explicitly undone upon exit. Unlike what you might be used to in user space programming, you can’t leave open files lying around or space dynamically allocated, knowing that someone is going to tidy up after you when your program eventually terminates. This is kernel space programming–no one is there to pick up after you. If you forget to deallocate some space that was dynamically allocated with, say, kmalloc(), then you’ve just introduced a memory leak, and other kernel programmers will not look kindly upon that.

In addition, it’s typical to undo those entry operations in reverse order from how they were invoked, as in, say:

static int __init hi(void)
{
do_this();
do_that();
do_something_else();
return 0; // success!
}

static void __exit bye(void)
{
undo_something_else();
undo_that();
undo_this();
}

But this is not the whole story–not even close to it. Moving on…

What If Something Goes Wrong?

And here’s the first complication–what if something goes wrong during your module init routine? It may be that for this module to load properly, you need to do a number of things including allocating space, registering a device and so on, and all of those steps have to succeed. So you need to be testing the return code of every operation and, if anything goes wrong, you need to bail and return a negative value identifying that something went wrong and that this module shouldn’t load.

To see how this works, take your basic module above and make the simple change of returning a -1 instead of zero from your init routine, rebuild the module and try to insmod it. What you should get is something like:

# insmod hi.ko
insmod: error inserting 'hi.ko': -1 Operation not permitted
#

and if you check the list of loaded modules with lsmod, you’ll see hi is not there. The act of returning that negative value from your init routine caused the load to fail, and it’s your responsibility to do all necessary error-checking during your init routine to identify any failures and decide whether you can continue to load or whether you have to abort and return a negative value. But wait… there’s more.

So I Should Always Return -1 on Failure?

That depends on how informative you want to be. Returning any negative value will cause the load to fail so if that’s all you care about, that’s fine. But if you want to be more specific about what kind of error it was, examine the possible error codes in the kernel source file include/asm-generic/errno-base.h:

#define EPERM     1   /* Operation not permitted */
#define ENOENT 2 /* No such file or directory */
#define ESRCH 3 /* No such process */
#define EINTR 4 /* Interrupted system call */
#define EIO 5 /* I/O error */
#define ENXIO 6 /* No such device or address */
#define E2BIG 7 /* Argument list too long */
... snip ...

Depending on the error message you want to see printed when you run insmod, you would additionally include the kernel header file linux/errno.h and your return statement signifying, say, an I/O error would look like:

    return -EIO;  // return *negative* value

As a test, use that exact return statement in your basic module and see what happens when you try to load it. But, yet again, there’s more–we’re not done yet.

No, Really–What If Something Goes Wrong?

And here’s where things get even more complicated. Recall that, in kernel space programming, it’s your job to clean up everything you do, and that includes any operations you might have done in your module init routine before you suddenly realize things didn’t work and you have to abort. The best way to see how this works is to steal shamelessly from the aforementioned LDD3 book, thusly:

static int __init hi(void)
{
int err;
/* registration takes a pointer and a name */
err = register_this(ptr1, "skull");
if (err) goto fail_this;
err = register_that(ptr2, "skull");
if (err) goto fail_that;
err = register_those(ptr3, "skull");
if (err) goto fail_those;
return 0; /* success */

fail_those: unregister_that(ptr2, "skull");
fail_that: unregister_this(ptr1, "skull");
fail_this: return err; /* propagate the error */
}

static void __exit bye(void)
{
unregister_those(ptr3, "skull");
unregister_that(ptr2, "skull");
unregister_this(ptr1, "skull");
return;
}

What’s happening above should be fairly obvious: depending on how far you get into your init routine before things go bad, you have to undo all of the operations already done, and in the reverse order, then return the corresponding error code for the step that failed. And, yes, goto statements are perfectly acceptable here.

As an advanced tip, if there’s a lot of undoing to perform, you can factor out that common code in the two routines and put it in a single cleanup() routine, which is demonstrated on the next couple of pages in that chapter in LDD3. Checking that out is left as an exercise for the reader. And we’re almost done.

What’s With That __init and __exit Stuff?

Ah, you’d noticed that, had you? Those markers are used to identify code that should be treated specially at module load and unload time.

An __init marker on a module function means that function can be discarded after a successful load, which only makes sense since, after the load, that code won’t be run again–its job is done; hence, the freedom to throw it away and not leave it around to waste kernel space.

The __exit marker is a bit trickier. Obviously, it can’t mean that you can throw the code away after a load since you still need that code for the unload. What it identifies, instead, is code that can be discarded if there is absolutely no possibility of that module ever being unloaded, and that can happen in either of two situations:

  • Your running kernel hasn’t even been configured with module unloading support, or
  • Your module has been added to the kernel source tree, and been configured to be built into the kernel image itself.

In both of those cases, there is no chance of your module code ever being unloaded from a running kernel so there’s no point hanging onto the exit code. In short, just use these markers for the time being for your init and exit routines–it’s the right thing to do.

Is There Anything Else You Should Know?

Sure, why not? First, whether or not you’re even allowed to do a lot of these module operations with insmod or rmmod depends on how your running kernel has been configured, as you can see if you’ve ever tried to configure a kernel:

--- Enable loadable module support 
[ ] Forced module loading
[*] Module unloading
[ ] Forced module unloading
[ ] Module versioning support
[*] Source checksum for all modules

As you can see, it’s quite possible to configure a kernel without any module support whatsoever, or with the ability to load but not unload modules, and so on. The above, however, is a fairly typical combination of settings, and I’m guessing it’s the same on your current system (feel free to check).

Another interesting property that I discovered only recently is that, if your init routine returns a positive value (say, 42), the module will still load but /var/log/messages produces:

... kernel: hi module being loaded.
... kernel: sys_init_module: 'hi'->init
suspiciously returned 42, it should follow 0/-E convention
... kernel: sys_init_module: loading module anyway...

Obviously, that warning is to discourage you from doing stuff like that. So don’t. Stick with the “0/-E” return code convention.

The final observation (which, again, I discovered only recently) is that if you load a module that has no exit routine, that module will be tagged as “permanent” and will be unloadable under normal circumstances. If such a module is loaded, it will show up in the output of lsmod as “[permanent]”, which means that you’ll have to reboot your system to get rid of it. (You can test this if you want, as long as you’re prepared to reboot when you finally want to get rid of that module.)

Actually, the above is not entirely true–you can unload such a module but only if your kernel is configured with “Forced module unloading,” whereupon you can use rmmod -f. That that’s how it works is obvious if (for the ambitious among you) you examine the kernel source file kernel/module.c and scroll down to around line 835 in the system call delete_module, where you can see the test:

/* If it has an init func, it must have an exit func to unload */
if (mod->init && !mod->exit) {
forced = try_force_unload(flags);
if (!forced) {
/* This module can't be removed */
ret = -EBUSY;
goto out;
}
}

The truly ambitious are welcome to peruse kernel/module.c to see how the kernel handles other module operations.

Next week: Adding parameters to your modules. I think.

(The author would like to acknowledge Kernel Newbies mailing list regular Mulyadi Santosa for pointing out the above snippet in the kernel/module.c file.)

Robert P. J. Day is a Linux consultant and long-time corporate trainer who lives in Waterloo, Ontario. He provides assistance to the Linux Foundation’s Training Program. Robert can be reached at
This e-mail address is being protected from spambots. You need JavaScript enabled to view it
, and can be followed at http://twitter.com/rpjday.