diff options
| author | Rusty Russell <rusty@rustcorp.com.au> | 2002-11-16 19:22:06 -0800 |
|---|---|---|
| committer | Linus Torvalds <torvalds@home.transmeta.com> | 2002-11-16 19:22:06 -0800 |
| commit | a89a8e24521557f9e1b4f01fa581dd979382b4cb (patch) | |
| tree | c96797f95cd3326ea1ca00953a5e5ab5911c75dd /kernel/module.c | |
| parent | bbeabef1b1bba4a0eb2c8b6ae7418f9ac1331887 (diff) | |
[PATCH] Forced module unload
This is the logical counterpoint to the code which marks modules
"[unsafe]" when obsolete (racy) interfaces are used. Allows "just
remove the damn thing" rmmod -f, and taints the kernel.
Mark it dangerous and experimental in the config file to make this
doubly clear.
Diffstat (limited to 'kernel/module.c')
| -rw-r--r-- | kernel/module.c | 53 |
1 files changed, 38 insertions, 15 deletions
diff --git a/kernel/module.c b/kernel/module.c index 728e8698422c..7f98ae91e698 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -348,12 +348,24 @@ static unsigned int module_refcount(struct module *mod) /* This exists whether we can unload or not */ static void free_module(struct module *mod); +#ifdef CONFIG_MODULE_FORCE_UNLOAD +static inline int try_force(unsigned int flags) +{ + return (flags & O_TRUNC); +} +#else +static inline int try_force(unsigned int flags) +{ + return 0; +} +#endif /* CONFIG_MODULE_FORCE_UNLOAD */ + asmlinkage long sys_delete_module(const char *name_user, unsigned int flags) { struct module *mod; char name[MODULE_NAME_LEN]; - int ret; + int ret, forced = 0; if (!capable(CAP_SYS_MODULE)) return -EPERM; @@ -371,24 +383,29 @@ sys_delete_module(const char *name_user, unsigned int flags) goto out; } + if (!list_empty(&mod->modules_which_use_me)) { + /* Other modules depend on us: get rid of them first. */ + ret = -EWOULDBLOCK; + goto out; + } + /* Already dying? */ if (!mod->live) { + /* FIXME: if (force), slam module count and wake up + waiter --RR */ DEBUGP("%s already dying\n", mod->name); ret = -EBUSY; goto out; } if (!mod->exit || mod->unsafe) { - /* This module can't be removed */ - ret = -EBUSY; - goto out; - } - if (!list_empty(&mod->modules_which_use_me)) { - /* Other modules depend on us: get rid of them first. */ - ret = -EWOULDBLOCK; - goto out; + forced = try_force(flags); + if (!forced) { + /* This module can't be removed */ + ret = -EBUSY; + goto out; + } } - /* Stop the machine so refcounts can't move: irqs disabled. */ DEBUGP("Stopping refcounts...\n"); ret = stop_refcounts(); @@ -396,9 +413,11 @@ sys_delete_module(const char *name_user, unsigned int flags) goto out; /* If it's not unused, quit unless we are told to block. */ - if ((flags & O_NONBLOCK) && module_refcount(mod) != 0) - ret = -EWOULDBLOCK; - else { + if ((flags & O_NONBLOCK) && module_refcount(mod) != 0) { + forced = try_force(flags); + if (!forced) + ret = -EWOULDBLOCK; + } else { mod->waiter = current; mod->live = 0; } @@ -407,6 +426,9 @@ sys_delete_module(const char *name_user, unsigned int flags) if (ret != 0) goto out; + if (forced) + goto destroy; + /* Since we might sleep for some time, drop the semaphore first */ up(&module_mutex); for (;;) { @@ -421,10 +443,11 @@ sys_delete_module(const char *name_user, unsigned int flags) DEBUGP("Regrabbing mutex...\n"); down(&module_mutex); + destroy: /* Final destruction now noone is using it. */ - mod->exit(); + if (mod->exit) + mod->exit(); free_module(mod); - ret = 0; out: up(&module_mutex); |
