Name: Use per-cpu-allocated module references Author: Rusty Russell Status: Experimental Depends: This uses a dynamic per-cpu variable for the module references, which reduces their size significantly, and allows per-cpu optimizations for some archs. Index: linux-2.6.10-rc2-bk13-Percpu/kernel/module.c =================================================================== --- linux-2.6.10-rc2-bk13-Percpu.orig/kernel/module.c 2004-12-01 15:17:50.000000000 +1100 +++ linux-2.6.10-rc2-bk13-Percpu/kernel/module.c 2004-12-02 12:46:53.908613064 +1100 @@ -231,16 +231,19 @@ #endif /* CONFIG_SMP */ #ifdef CONFIG_MODULE_UNLOAD +void module_release(struct bigref *bigref) +{ + struct module *mod = container_of(bigref, struct module, bigref); + + wake_up_process(mod->waiter); +} +EXPORT_SYMBOL(module_release); + /* Init the unload section of the module. */ static void module_unload_init(struct module *mod) { - unsigned int i; - INIT_LIST_HEAD(&mod->modules_which_use_me); - for (i = 0; i < NR_CPUS; i++) - local_set(&mod->ref[i].count, 0); - /* Hold reference count during initialization. */ - local_set(&mod->ref[smp_processor_id()].count, 1); + bigref_init(&mod->bigref); /* Backwards compatibility macros put refcount during init. */ mod->waiter = current; } @@ -308,6 +311,8 @@ } } } + /* We might not have decremented to zero: forced unload. */ + bigref_destroy(&mod->bigref); } #ifdef CONFIG_MODULE_FORCE_UNLOAD @@ -357,11 +362,8 @@ unsigned int module_refcount(struct module *mod) { - unsigned int i, total = 0; - - for (i = 0; i < NR_CPUS; i++) - total += local_read(&mod->ref[i].count); - return total; + /* We hold one until it unloads. */ + return bigref_val(&mod->bigref) - 1; } EXPORT_SYMBOL(module_refcount); @@ -370,17 +372,17 @@ static void wait_for_zero_refcount(struct module *mod) { - /* Since we might sleep for some time, drop the semaphore first */ - up(&module_mutex); for (;;) { DEBUGP("Looking at refcount...\n"); set_current_state(TASK_UNINTERRUPTIBLE); - if (module_refcount(mod) == 0) + if (bigref_val(&mod->bigref) == 0) break; + /* Since we might sleep for some time, drop the semaphore */ + up(&module_mutex); schedule(); + down(&module_mutex); } current->state = TASK_RUNNING; - down(&module_mutex); } asmlinkage long @@ -440,8 +442,12 @@ if (ret != 0) goto out; + /* OK, reference count can fall to zero now. */ + bigref_disown(&mod->bigref); + module_put(mod); + /* Never wait if forced. */ - if (!forced && module_refcount(mod) != 0) + if (!forced) wait_for_zero_refcount(mod); /* Final destruction now noone is using it. */ @@ -1680,8 +1686,6 @@ /* Now it's a first class citizen! */ down(&module_mutex); mod->state = MODULE_STATE_LIVE; - /* Drop initial reference. */ - module_put(mod); module_free(mod, mod->module_init); mod->module_init = NULL; mod->init_size = 0; Index: linux-2.6.10-rc2-bk13-Percpu/include/linux/module.h =================================================================== --- linux-2.6.10-rc2-bk13-Percpu.orig/include/linux/module.h 2004-11-30 12:45:22.000000000 +1100 +++ linux-2.6.10-rc2-bk13-Percpu/include/linux/module.h 2004-12-02 11:57:29.491273000 +1100 @@ -18,7 +18,7 @@ #include #include #include -#include +#include #include @@ -210,11 +210,6 @@ * special casing EXPORT_SYMBOL_NOVERS. FIXME: Deprecated */ #define EXPORT_SYMBOL_NOVERS(sym) EXPORT_SYMBOL(sym) -struct module_ref -{ - local_t count; -} ____cacheline_aligned; - enum module_state { MODULE_STATE_LIVE, @@ -293,7 +288,7 @@ #ifdef CONFIG_MODULE_UNLOAD /* Reference counts */ - struct module_ref ref[NR_CPUS]; + struct bigref bigref; /* What modules depend on me? */ struct list_head modules_which_use_me; @@ -363,8 +358,7 @@ { if (module) { BUG_ON(module_refcount(module) == 0); - local_inc(&module->ref[get_cpu()].count); - put_cpu(); + bigref_get(&module->bigref); } } @@ -373,26 +367,20 @@ int ret = 1; if (module) { - unsigned int cpu = get_cpu(); if (likely(module_is_live(module))) - local_inc(&module->ref[cpu].count); + bigref_get(&module->bigref); else ret = 0; - put_cpu(); } return ret; } +void module_release(struct bigref *bigref); + static inline void module_put(struct module *module) { - if (module) { - unsigned int cpu = get_cpu(); - local_dec(&module->ref[cpu].count); - /* Maybe they're waiting for us to drop reference? */ - if (unlikely(!module_is_live(module))) - wake_up_process(module->waiter); - put_cpu(); - } + if (module) + bigref_put(&module->bigref, module_release); } #else /*!CONFIG_MODULE_UNLOAD*/