Commit a89a8e24 authored by Rusty Russell's avatar Rusty Russell Committed by Linus Torvalds

[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.
parent bbeabef1
...@@ -102,6 +102,7 @@ extern const char *print_tainted(void); ...@@ -102,6 +102,7 @@ extern const char *print_tainted(void);
#define TAINT_PROPRIETORY_MODULE (1<<0) #define TAINT_PROPRIETORY_MODULE (1<<0)
#define TAINT_FORCED_MODULE (1<<1) #define TAINT_FORCED_MODULE (1<<1)
#define TAINT_UNSAFE_SMP (1<<2) #define TAINT_UNSAFE_SMP (1<<2)
#define TAINT_FORCED_RMMOD (1<<3)
extern void dump_stack(void); extern void dump_stack(void);
......
...@@ -125,6 +125,16 @@ config MODULE_UNLOAD ...@@ -125,6 +125,16 @@ config MODULE_UNLOAD
anyway), which makes your kernel slightly smaller and anyway), which makes your kernel slightly smaller and
simpler. If unsure, say Y. simpler. If unsure, say Y.
config MODULE_FORCE_UNLOAD
bool "Forced module unloading"
depends on MODULES && EXPERIMENTAL
help
This option allows you to force a module to unload, even if the
kernel believes it is unsafe: the kernel will remove the module
without waiting for anyone to stop using it (using the -f option to
rmmod). This is mainly for kernel developers and desparate users.
If unsure, say N.
config KMOD config KMOD
bool "Kernel module loader" bool "Kernel module loader"
depends on MODULES depends on MODULES
......
...@@ -348,12 +348,24 @@ static unsigned int module_refcount(struct module *mod) ...@@ -348,12 +348,24 @@ static unsigned int module_refcount(struct module *mod)
/* This exists whether we can unload or not */ /* This exists whether we can unload or not */
static void free_module(struct module *mod); 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 asmlinkage long
sys_delete_module(const char *name_user, unsigned int flags) sys_delete_module(const char *name_user, unsigned int flags)
{ {
struct module *mod; struct module *mod;
char name[MODULE_NAME_LEN]; char name[MODULE_NAME_LEN];
int ret; int ret, forced = 0;
if (!capable(CAP_SYS_MODULE)) if (!capable(CAP_SYS_MODULE))
return -EPERM; return -EPERM;
...@@ -371,24 +383,29 @@ sys_delete_module(const char *name_user, unsigned int flags) ...@@ -371,24 +383,29 @@ sys_delete_module(const char *name_user, unsigned int flags)
goto out; 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? */ /* Already dying? */
if (!mod->live) { if (!mod->live) {
/* FIXME: if (force), slam module count and wake up
waiter --RR */
DEBUGP("%s already dying\n", mod->name); DEBUGP("%s already dying\n", mod->name);
ret = -EBUSY; ret = -EBUSY;
goto out; goto out;
} }
if (!mod->exit || mod->unsafe) { if (!mod->exit || mod->unsafe) {
/* This module can't be removed */ forced = try_force(flags);
ret = -EBUSY; if (!forced) {
goto out; /* This module can't be removed */
} ret = -EBUSY;
if (!list_empty(&mod->modules_which_use_me)) { goto out;
/* Other modules depend on us: get rid of them first. */ }
ret = -EWOULDBLOCK;
goto out;
} }
/* Stop the machine so refcounts can't move: irqs disabled. */ /* Stop the machine so refcounts can't move: irqs disabled. */
DEBUGP("Stopping refcounts...\n"); DEBUGP("Stopping refcounts...\n");
ret = stop_refcounts(); ret = stop_refcounts();
...@@ -396,9 +413,11 @@ sys_delete_module(const char *name_user, unsigned int flags) ...@@ -396,9 +413,11 @@ sys_delete_module(const char *name_user, unsigned int flags)
goto out; goto out;
/* If it's not unused, quit unless we are told to block. */ /* If it's not unused, quit unless we are told to block. */
if ((flags & O_NONBLOCK) && module_refcount(mod) != 0) if ((flags & O_NONBLOCK) && module_refcount(mod) != 0) {
ret = -EWOULDBLOCK; forced = try_force(flags);
else { if (!forced)
ret = -EWOULDBLOCK;
} else {
mod->waiter = current; mod->waiter = current;
mod->live = 0; mod->live = 0;
} }
...@@ -407,6 +426,9 @@ sys_delete_module(const char *name_user, unsigned int flags) ...@@ -407,6 +426,9 @@ sys_delete_module(const char *name_user, unsigned int flags)
if (ret != 0) if (ret != 0)
goto out; goto out;
if (forced)
goto destroy;
/* Since we might sleep for some time, drop the semaphore first */ /* Since we might sleep for some time, drop the semaphore first */
up(&module_mutex); up(&module_mutex);
for (;;) { for (;;) {
...@@ -421,10 +443,11 @@ sys_delete_module(const char *name_user, unsigned int flags) ...@@ -421,10 +443,11 @@ sys_delete_module(const char *name_user, unsigned int flags)
DEBUGP("Regrabbing mutex...\n"); DEBUGP("Regrabbing mutex...\n");
down(&module_mutex); down(&module_mutex);
destroy:
/* Final destruction now noone is using it. */ /* Final destruction now noone is using it. */
mod->exit(); if (mod->exit)
mod->exit();
free_module(mod); free_module(mod);
ret = 0;
out: out:
up(&module_mutex); up(&module_mutex);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment