Messages in this thread Patch in this message | | | Date | Sun, 15 Mar 1998 01:26:37 -0500 (EST) | From | Greg Zornetzer <> | Subject | Re: kmod fix |
| |
Well, the code is _much_ bigger than the original kmod, but I've modified kmod to allow multiple requests. It splits up the signalling into the original kmod_queue and an array of module slots. Each of these slots contains the wait queue for waking up request_module, the name of the module, and a information byte telling the status of the module. This allows the lp module to load properly again. Although it's somewhat bloated, I think it bypasses the races in the current kmod code.
In addition, I've added a switch that shuts kmod off by default. It is activated by putting a non-null string in systcl kernel/modprobe_path. This is necessary because kmod starts after initrd is run. Inserting modules that run request_module will cause a freeze (scsi fits into that category - a la Redhat installs). So the rc scripts must put a modprobe path into kernel/modprobe_path (this could be easily changed)
Another thing is that request_module will now return -1 if a module load failed - I couldn't find the proper return code, but I think the return code should change if a failure occurs.
Patch is against 2.1.90pre2 but should work against 2.1.90pre3 and 1, (I'm compiling pre3 now) Anyway, what do you think? Is the extra code worth it?
Greg Zornetzer - gaz+@andrew.cmu.edu "Light shines brightest in the darkest night" http://www.contrib.andrew.cmu.edu/~gaz
--- linux/kernel/kmod.c.old Thu Mar 12 23:18:22 1998 +++ linux/kernel/kmod.c Sun Mar 15 00:20:38 1998 @@ -1,6 +1,9 @@ /* kmod, the new module loader (replaces kerneld) Kirk Petersen + Modifications to support multiple modules loading + by Greg Zornetzer (gaz@andrew.cmu.edu) + */ #define __KERNEL_SYSCALLS__ @@ -9,17 +12,51 @@ #include <linux/types.h> #include <linux/unistd.h> + +/* + Define the number of modules that we can concurrently load. + It's easier to statically define this amount than to make + it scale dynamically +*/ + +#define NUMMODS 10 + +/* + A module slot can be in several states - unused, or in various + states of processing +*/ +#define MODRQ_NONE 0 +#define MODRQ_REQUESTED 1 +#define MODRQ_SERVICING 2 +#define MODRQ_REMOVE 4 +#define MODRQ_FAILURE 8 + static inline _syscall1(int,delete_module,const char *,name_user) /* kmod_unload_delay and modprobe_path are set via /proc/sys. + Make modprobe path null by default - this signals that + request_module (and therefore kmod) will be unable to service + requests until explicitly activated using sysctl */ int kmod_unload_delay = 60; -char modprobe_path[256] = "/sbin/modprobe"; -char module_name[64] = ""; +char modprobe_path[256] = ""; char * argv[] = { "modprobe", "-k", NULL, NULL, }; char * envp[] = { "HOME=/", "TERM=linux", NULL, }; + +/* + A modslot defines the state of a module being loaded and holds the + wait_queue to reactivate request_module +*/ +typedef struct { + struct wait_queue *mreq_queue; + unsigned char info; + char module_name[64]; +} modslot; + +modslot modlist[NUMMODS]; + /* kmod_queue synchronizes the kmod thread and the rest of the system kmod_unload_timer is what we use to unload modules @@ -28,6 +65,33 @@ struct wait_queue * kmod_queue = NULL; struct timer_list kmod_unload_timer; +void install_module(int listnum) { + + int pid; + pid = fork(); + + if (pid > 0) { + waitpid(pid, NULL, 0); + wake_up(&(modlist[listnum].mreq_queue)); + + } else if (pid == 0) { + /* + Call modprobe with module_name. If execve returns, + print out an error. + */ + argv[2] = modlist[listnum].module_name; + execve(modprobe_path, argv, envp); + + printk("kmod: failed to load module %s\n", modlist[listnum].module_name); + modlist[listnum].info = MODRQ_FAILURE; + _exit(0); + } else { + printk("error, fork failed in kmod - couldn't load %s\n",modlist[listnum].module_name); + modlist[listnum].info = MODRQ_FAILURE; + wake_up(&(modlist[listnum].mreq_queue)); + } +} + /* kmod_thread is the thread that does most of the work. kmod_unload and request_module tell it to wake up and do work. @@ -35,6 +99,7 @@ int kmod_thread(void * data) { int pid; + int i; /* Initialize basic thread information @@ -45,6 +110,13 @@ sigfillset(¤t->blocked); /* + Initialize the modlist array + */ + for(i = 0; i < NUMMODS; i++) { + modlist[i].info = MODRQ_NONE; + modlist[i].mreq_queue = NULL; + } + /* This is the main kmod_thread loop. It first sleeps, then handles requests from request_module or kmod_unload. */ @@ -52,37 +124,38 @@ while (1) { interruptible_sleep_on(&kmod_queue); - /* - If request_module woke us up, we should try to - load module_name. If not, kmod_unload woke us up, - do call delete_module. - (if somehow both want us to do something, ignore the - delete_module request) + /* + We've been awakened - check all of the module + slots for new requests */ - if (module_name[0] == '\0') { - delete_module(NULL); - } else { - pid = fork(); - if (pid > 0) { - waitpid(pid, NULL, 0); - module_name[0] = '\0'; - wake_up(&kmod_queue); - } else - if (pid == 0) { - - /* - Call modprobe with module_name. If execve returns, - print out an error. - */ - argv[2] = module_name; - execve(modprobe_path, argv, envp); - printk("kmod: failed to load module %s\n", module_name); - _exit(0); - } else { - printk("error, fork failed in kmod\n"); + for(i = 0; i < NUMMODS; i++) { + switch (modlist[i].info) { + case MODRQ_REQUESTED : + modlist[i].info = MODRQ_SERVICING; + /* + Gotta fork off a new process to make + sure that kmod can handle + subsequent requests + */ + pid = fork(); + if (pid == 0) { + install_module(i); + _exit(0); + } + else if (pid < 0) { + printk("KMod: Couldn't fork - Cannot load %s\n",modlist[i].module_name); + modlist[i].info = MODRQ_FAILURE; + wake_up(&(modlist[i].mreq_queue)); + } + break; + case MODRQ_REMOVE : + modlist[i].info = MODRQ_NONE; + delete_module(NULL); + break; } } + } return 0; /* Never reached. */ @@ -94,13 +167,23 @@ */ void kmod_unload(unsigned long x) { + int i=0; /* + Find a free slot, and set it to the remove status + KMod will pick this up and call delete_module wake up the kmod thread, which does the work (we can't call delete_module, as it locks the kernel and - we are in the bottom half of the kernel (right?)) + we are in the bottom half of the kernel (right?)) once it is awake, reset the timer */ - wake_up(&kmod_queue); + while((i < NUMMODS) && (modlist[i].info != MODRQ_NONE)) + i++; + if(i != NUMMODS) { + modlist[i].info = MODRQ_REMOVE; + wake_up(&kmod_queue); + } + else + printk("Couldn't get slot for module cleanup\n"); kmod_unload_timer.expires = jiffies + (kmod_unload_delay * HZ); add_timer(&kmod_unload_timer); } @@ -127,16 +210,30 @@ */ int request_module(const char * name) { - /* first, copy the name of the module into module_name */ - /* then wake_up() the kmod daemon */ - /* wait for the kmod daemon to finish (it will wake us up) */ +/* + request_module will (if kmod is running) get free modslot + and wake up kmod. +*/ - /* - kmod_thread is sleeping, so start by copying the name of - the module into module_name. Once that is done, wake up - kmod_thread. - */ - strcpy(module_name, name); + int i = 0; + + +/* + modprobe_path hasn't been initialized, the kmod is inactive. + So we fail +*/ + if (modprobe_path[0]=='\0') + return(-1); + + while((i < NUMMODS) && (modlist[i].info != MODRQ_NONE)) + i++; + if(i == NUMMODS) { + printk("KMod - module load failure - no free mod slots\n"); + return(-1); + } + + modlist[i].info = MODRQ_REQUESTED; + strcpy(modlist[i].module_name, name); wake_up(&kmod_queue); /* @@ -144,6 +241,22 @@ go to sleep and let it do its work. It will wake us up, at which point we will be done (the module will be loaded). */ - interruptible_sleep_on(&kmod_queue); + + interruptible_sleep_on(&(modlist[i].mreq_queue)); + + /* + If the module load returns a failure for any reason, + we should fail also + */ + if(modlist[i].info == MODRQ_FAILURE) { + modlist[i].info = MODRQ_NONE; + return(-1); + } + modlist[i].info = MODRQ_NONE; return 0; } + + + + + | |