From: Rusty Russell <rusty@rustcorp.com.au>
To: torvalds@transmeta.com, akpm@zip.com.au, davem@redhat.com
Cc: linux-kernel@vger.kernel.org, mochel@osdl.org
Subject: [PATCH 3/3] Allow arbitrary number of init funcs in modules
Date: Mon, 23 Jun 2003 19:19:48 +1000 [thread overview]
Message-ID: <20030623092028.2B5272C2FC@lists.samba.org> (raw)
This patch requires some explanation, see description fields below.
Thanks to Andrew for prodding, and DaveM for the hard questions.
Feedback is extremely welcome,
Rusty.
--
Anyone who quotes me in their sig is an idiot. -- Rusty Russell.
Name: Allow Arbitrary Number of Init and Exit Functions
Author: Rusty Russell
Status: Tested on 2.5.73
Depends: Misc/unique_id.patch.gz
D: One longstanding complaint is that modules can only have one
D: module_init, and one module_exit (builtin code can have multiple
D: __initcall however). This means, for example, that it is not
D: possible to write a "module_proc_entry(name, readfn)" function
D: which can be used like so:
D:
D: module_init(myinitfn);
D: module_cleanup(myinitfn);
D: module_proc_entry("some/path/foo", read_foo);
D:
D: The reason we don't allow multiple init functions in modules: if
D: one fails, we won't know what to do. The solution is to explicitly
D: pair them, hence module_init_exit(), with a priority arg.
D:
D: If the module fails to load the exit function will be called
D: corresponding to each init function which was called, in backwards
D: order. All exit functions are called on module removal.
D:
D: For non-modules, the exit arg is discarded, and the initcalls are
D: run in priority order at boot (order within priorities is still
D: controlled by linking, as previously). This means we can also get
D: rid of the initcall levels at link time and simply use priorities.
D:
D: This patch also uses kallsyms to print function names when
D: "initcall_debug" is set.
diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .15772-linux-2.5.73/arch/i386/vmlinux.lds.S .15772-linux-2.5.73.updated/arch/i386/vmlinux.lds.S
--- .15772-linux-2.5.73/arch/i386/vmlinux.lds.S 2003-06-15 11:29:47.000000000 +1000
+++ .15772-linux-2.5.73.updated/arch/i386/vmlinux.lds.S 2003-06-23 18:10:41.000000000 +1000
@@ -69,13 +69,7 @@ SECTIONS
__stop___param = .;
__initcall_start = .;
.initcall.init : {
- *(.initcall1.init)
- *(.initcall2.init)
- *(.initcall3.init)
- *(.initcall4.init)
- *(.initcall5.init)
- *(.initcall6.init)
- *(.initcall7.init)
+ *(.initcall.init)
}
__initcall_end = .;
__con_initcall_start = .;
diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .15772-linux-2.5.73/include/linux/init.h .15772-linux-2.5.73.updated/include/linux/init.h
--- .15772-linux-2.5.73/include/linux/init.h 2003-06-15 11:30:08.000000000 +1000
+++ .15772-linux-2.5.73.updated/include/linux/init.h 2003-06-23 18:17:22.000000000 +1000
@@ -2,6 +2,7 @@
#define _LINUX_INIT_H
#include <linux/config.h>
+#include <linux/stringify.h>
/* These macros are used to mark some functions or
* initialized data (doesn't apply to uninitialized data)
@@ -65,34 +66,48 @@ typedef void (*exitcall_t)(void);
extern initcall_t __con_initcall_start, __con_initcall_end;
extern initcall_t __security_initcall_start, __security_initcall_end;
+
+struct module_init_exit
+{
+ int prio;
+ initcall_t init;
+ exitcall_t exit;
+};
+
+struct kernel_init
+{
+ int prio;
+ initcall_t init;
+};
#endif
-
+
#ifndef MODULE
#ifndef __ASSEMBLY__
+/* Suppress unused warning on exitfn, and test type. */
+#define __exitcall(fn) \
+static inline exitcall_t __unique_id(exit_test)(void) { return fn; }
-/* initcalls are now grouped by functionality into separate
- * subsections. Ordering inside the subsections is determined
- * by link order.
- * For backwards compatibility, initcall() puts the call in
- * the device init subsection.
- */
-
-#define __define_initcall(level,fn) \
- static initcall_t __initcall_##fn __attribute__ ((unused,__section__ (".initcall" level ".init"))) = fn
-
-#define core_initcall(fn) __define_initcall("1",fn)
-#define postcore_initcall(fn) __define_initcall("2",fn)
-#define arch_initcall(fn) __define_initcall("3",fn)
-#define subsys_initcall(fn) __define_initcall("4",fn)
-#define fs_initcall(fn) __define_initcall("5",fn)
-#define device_initcall(fn) __define_initcall("6",fn)
-#define late_initcall(fn) __define_initcall("7",fn)
+/* Pair of calls: one called at boot, one at exit (ie. never, when not
+ a module). Unlike module_init, you can have any number of these.
+ prio controls ordering: negative runs before module_init, positive after.
+ Within a priority, link order rules.
+*/
+#define module_init_exit(prio, initfn, exitfn) \
+ __exitcall(exitfn); \
+ static struct kernel_init __initcall_##initfn \
+ __attribute__((unused,__section__ (".initcall.init"))) \
+ = { prio, initfn }
-#define __initcall(fn) device_initcall(fn)
+#define core_initcall(fn) module_init_exit(-5000, fn, NULL)
+#define postcore_initcall(fn) module_init_exit(-4000, fn, NULL)
+#define arch_initcall(fn) module_init_exit(-3000, fn, NULL)
+#define subsys_initcall(fn) module_init_exit(-2000, fn, NULL)
+#define fs_initcall(fn) module_init_exit(-1000, fn, NULL)
+#define device_initcall(fn) module_init_exit(0, fn, NULL)
+#define late_initcall(fn) module_init_exit(1000, fn, NULL)
-#define __exitcall(fn) \
- static exitcall_t __exitcall_##fn __exit_call = fn
+#define __initcall(fn) device_initcall(fn)
#define console_initcall(fn) \
static initcall_t __initcall_##fn __attribute__ ((unused,__section__ (".con_initcall.init")))=fn
@@ -112,8 +127,6 @@ struct obs_kernel_param {
__attribute__((unused,__section__ (".init.setup"))) \
= { __setup_str_##fn, fn }
-#endif /* __ASSEMBLY__ */
-
/**
* module_init() - driver initialization entry point
* @x: function to be run at kernel boot time or module insertion
@@ -135,6 +148,7 @@ struct obs_kernel_param {
* There can only be one per module.
*/
#define module_exit(x) __exitcall(x);
+#endif /* __ASSEMBLY__ */
#else /* MODULE */
@@ -149,25 +163,27 @@ struct obs_kernel_param {
#define security_initcall(fn) module_init(fn)
-/* These macros create a dummy inline: gcc 2.9x does not count alias
- as usage, hence the `unused function' warning when __init functions
- are declared static. We use the dummy __*_module_inline functions
- both to kill the warning and check the type of the init/cleanup
- function. */
-
-/* Each module must use one module_init(), or one no_module_init */
-#define module_init(initfn) \
- static inline initcall_t __inittest(void) \
- { return initfn; } \
- int init_module(void) __attribute__((alias(#initfn)));
+/* Each module can have one module_init(). */
+#define module_init(initfn) \
+ static struct module_init_exit module_init \
+ __attribute__ ((unused,__section__ ("__initexit"))) \
+ = { 0, initfn, NULL };
-/* This is only required if you want to be unloadable. */
-#define module_exit(exitfn) \
- static inline exitcall_t __exittest(void) \
- { return exitfn; } \
- void cleanup_module(void) __attribute__((alias(#exitfn)));
+/* If you have a module_init, and want to be unloadable, you need this too. */
+#define module_exit(exitfn) \
+ static struct module_init_exit module_exit \
+ __attribute__ ((unused,__section__ ("__initexit"))) \
+ = { 0, NULL, exitfn };
#define __setup(str,func) /* nothing */
+
+/* Pair of calls: one called at init, one at exit. Unlike
+ module_init/module_exit, you can have any number of these: ordering is
+ controlled by priority, in ascending order: module_init is 0. */
+#define module_init_exit(prio, initfn, exitfn) \
+ static struct module_init_exit __unique_id(mie) \
+ __attribute__ ((unused,__section__ ("__initexit"))) \
+ = { prio, initfn, exitfn }
#endif
/* Data marked not to be saved by software_suspend() */
diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .15772-linux-2.5.73/include/linux/module.h .15772-linux-2.5.73.updated/include/linux/module.h
--- .15772-linux-2.5.73/include/linux/module.h 2003-06-23 17:29:39.000000000 +1000
+++ .15772-linux-2.5.73.updated/include/linux/module.h 2003-06-23 17:29:42.000000000 +1000
@@ -203,9 +203,6 @@ struct module
unsigned int num_exentries;
const struct exception_table_entry *extable;
- /* Startup function. */
- int (*init)(void);
-
/* If this is non-NULL, vfree after init() returns */
void *module_init;
@@ -224,6 +221,10 @@ struct module
/* Am I GPL-compatible */
int license_gplok;
+ /* Pairs of init/exit functions. */
+ struct module_init_exit *ie_pairs;
+ unsigned int num_ie_pairs;
+
#ifdef CONFIG_MODULE_UNLOAD
/* Reference counts */
struct module_ref ref[NR_CPUS];
@@ -233,9 +234,6 @@ struct module
/* Who is waiting for us to be unloaded */
struct task_struct *waiter;
-
- /* Destruction function. */
- void (*exit)(void);
#endif
#ifdef CONFIG_KALLSYMS
@@ -449,10 +447,6 @@ extern struct module __this_module;
struct module __this_module
__attribute__((section(".gnu.linkonce.this_module"))) = {
.name = __stringify(KBUILD_MODNAME),
- .init = init_module,
-#ifdef CONFIG_MODULE_UNLOAD
- .exit = cleanup_module,
-#endif
};
#endif /* KBUILD_MODNAME */
#endif /* MODULE */
diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .15772-linux-2.5.73/init/main.c .15772-linux-2.5.73.updated/init/main.c
--- .15772-linux-2.5.73/init/main.c 2003-06-17 16:57:33.000000000 +1000
+++ .15772-linux-2.5.73.updated/init/main.c 2003-06-23 18:51:34.000000000 +1000
@@ -38,6 +38,7 @@
#include <linux/moduleparam.h>
#include <linux/writeback.h>
#include <linux/cpu.h>
+#include <linux/kallsyms.h>
#include <asm/io.h>
#include <asm/bugs.h>
@@ -478,34 +479,67 @@ __setup("initcall_debug", initcall_debug
struct task_struct *child_reaper = &init_task;
-extern initcall_t __initcall_start, __initcall_end;
-
-static void __init do_initcalls(void)
+static void do_initcall(initcall_t call)
{
- initcall_t *call;
+ char *msg = NULL;
+ char buf[128];
+ char *m;
+ unsigned long d;
int count = preempt_count();
- for (call = &__initcall_start; call < &__initcall_end; call++) {
- char *msg;
+ if (initcall_debug)
+ printk("calling initcall 0x%p %s\n", *call,
+ kallsyms_lookup((long)call, &d, &d, &m, buf) ?: "");
+ call();
- if (initcall_debug)
- printk("calling initcall 0x%p\n", *call);
+ if (preempt_count() != count) {
+ msg = "preemption imbalance";
+ preempt_count() = count;
+ }
+ if (irqs_disabled()) {
+ msg = "disabled interrupts";
+ local_irq_enable();
+ }
+ if (msg) {
+ printk("error in initcall at 0x%p %s: "
+ "returned with %s\n", *call,
+ kallsyms_lookup((long)call, &d, &d, &m, buf) ?: "",
+ msg);
+ }
+}
- (*call)();
+/* Defined by linker magic. */
+extern struct kernel_init __initcall_start[], __initcall_end[];
- msg = NULL;
- if (preempt_count() != count) {
- msg = "preemption imbalance";
- preempt_count() = count;
- }
- if (irqs_disabled()) {
- msg = "disabled interrupts";
- local_irq_enable();
- }
- if (msg) {
- printk("error in initcall at 0x%p: "
- "returned with %s\n", *call, msg);
+/* Find first initfunc minimally >= this prio level. */
+static struct kernel_init *find_next_prio(int min_prio_todo)
+{
+ struct kernel_init *i, *best;
+
+ best = NULL;
+ for (i = __initcall_start; i < __initcall_end; i++) {
+ if (i->prio >= min_prio_todo
+ && (!best || i->prio < best->prio))
+ best = i;
+ }
+ return best;
+}
+
+static void __init do_initcalls(void)
+{
+ struct kernel_init *next, *i;
+ int min_prio_todo = INT_MIN;
+
+ /* This is O(num calls * num levels), which is OK. */
+ while ((next = find_next_prio(min_prio_todo)) != NULL) {
+ if (initcall_debug)
+ printk("Doing priority %i initcalls:\n", next->prio);
+ /* Now call all at that priority. */
+ for (i = next; i < __initcall_end; i++) {
+ if (i->prio == next->prio)
+ do_initcall(i->init);
}
+ min_prio_todo = next->prio+1;
}
/* Make sure there is no pending stuff from the initcall sequence */
diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .15772-linux-2.5.73/kernel/module.c .15772-linux-2.5.73.updated/kernel/module.c
--- .15772-linux-2.5.73/kernel/module.c 2003-06-15 11:30:11.000000000 +1000
+++ .15772-linux-2.5.73.updated/kernel/module.c 2003-06-23 17:29:42.000000000 +1000
@@ -91,13 +91,6 @@ static inline int strong_try_module_get(
return try_module_get(mod);
}
-/* Stub function for modules which don't have an initfn */
-int init_module(void)
-{
- return 0;
-}
-EXPORT_SYMBOL(init_module);
-
/* Find a module section: 0 means not found. */
static unsigned int find_sec(Elf_Ehdr *hdr,
Elf_Shdr *sechdrs,
@@ -619,11 +612,16 @@ static inline int try_force(unsigned int
}
#endif /* CONFIG_MODULE_FORCE_UNLOAD */
-/* Stub function for modules which don't have an exitfn */
-void cleanup_module(void)
+/* Need as many exits as inits to unload. */
+static int can_unload(unsigned int num_pairs, struct module_init_exit *pairs)
{
+ int i, balance = 0;
+
+ for (i = 0; i < num_pairs; i++)
+ balance += (pairs->init ? 1 : 0) - (pairs->exit ? 1 : 0);
+
+ return balance == 0;
}
-EXPORT_SYMBOL(cleanup_module);
static void wait_for_zero_refcount(struct module *mod)
{
@@ -645,6 +643,7 @@ sys_delete_module(const char __user *nam
{
struct module *mod;
char name[MODULE_NAME_LEN];
+ unsigned int i;
int ret, forced = 0;
if (!capable(CAP_SYS_MODULE))
@@ -678,9 +677,8 @@ sys_delete_module(const char __user *nam
goto out;
}
- /* If it has an init func, it must have an exit func to unload */
- if ((mod->init != init_module && mod->exit == cleanup_module)
- || mod->unsafe) {
+ /* Init and exit functions must balance to unload. */
+ if (!can_unload(mod->num_ie_pairs, mod->ie_pairs) || mod->unsafe) {
forced = try_force(flags);
if (!forced) {
/* This module can't be removed */
@@ -713,8 +711,10 @@ sys_delete_module(const char __user *nam
if (!forced && module_refcount(mod) != 0)
wait_for_zero_refcount(mod);
- /* Final destruction now noone is using it. */
- mod->exit();
+ /* Final destruction now noone is using it, reverse priority order. */
+ for (i = mod->num_ie_pairs - 1; i >= 0; i--)
+ if (mod->ie_pairs[i].exit)
+ mod->ie_pairs[i].exit();
free_module(mod);
out:
@@ -741,7 +741,7 @@ static void print_unload_info(struct seq
seq_printf(m, "[unsafe],");
}
- if (mod->init != init_module && mod->exit == cleanup_module) {
+ if (!can_unload(mod->num_ie_pairs, mod->ie_pairs)) {
printed_something = 1;
seq_printf(m, "[permanent],");
}
@@ -1351,6 +1351,23 @@ static void add_kallsyms(struct module *
}
#endif
+/* Order init_exit pairs in ascending priority. num is small. */
+static void sort_ie_pairs(struct module_init_exit *ie_pairs, unsigned int num)
+{
+ unsigned int i, j;
+ struct module_init_exit tmp;
+
+ for (i = 0; i < num; i++) {
+ for (j = i; j+1 < num; j++) {
+ if (ie_pairs[j].prio > ie_pairs[j+1].prio) {
+ tmp = ie_pairs[j];
+ ie_pairs[j] = ie_pairs[j+1];
+ ie_pairs[j+1] = tmp;
+ }
+ }
+ }
+}
+
/* Allocate and load the module: note that size of section 0 is always
zero, and we rely on this for optional sections. */
static struct module *load_module(void __user *umod,
@@ -1362,7 +1379,7 @@ static struct module *load_module(void _
char *secstrings, *args, *modmagic, *strtab = NULL;
unsigned int i, symindex = 0, strindex = 0, setupindex, exindex,
exportindex, modindex, obsparmindex, infoindex, gplindex,
- crcindex, gplcrcindex, versindex, pcpuindex;
+ crcindex, gplcrcindex, versindex, pcpuindex, iepairindex;
long arglen;
struct module *mod;
long err = 0;
@@ -1437,6 +1454,7 @@ static struct module *load_module(void _
obsparmindex = find_sec(hdr, sechdrs, secstrings, "__obsparm");
versindex = find_sec(hdr, sechdrs, secstrings, "__versions");
infoindex = find_sec(hdr, sechdrs, secstrings, ".modinfo");
+ iepairindex = find_sec(hdr, sechdrs, secstrings, "__initexit");
pcpuindex = find_pcpusec(hdr, sechdrs, secstrings);
/* Don't keep modinfo section */
@@ -1587,6 +1605,12 @@ static struct module *load_module(void _
mod->num_exentries = sechdrs[exindex].sh_size / sizeof(*mod->extable);
mod->extable = (void *)sechdrs[exindex].sh_addr;
+ /* Set up init/exit pair table, and sort into prio order. */
+ mod->num_ie_pairs = sechdrs[iepairindex].sh_size
+ / sizeof(struct module_init_exit);
+ mod->ie_pairs = (void *)sechdrs[iepairindex].sh_addr;
+ sort_ie_pairs(mod->ie_pairs, mod->num_ie_pairs);
+
/* Now do relocations. */
for (i = 1; i < hdr->e_shnum; i++) {
const char *strtab = (char *)sechdrs[strindex].sh_addr;
@@ -1661,7 +1685,7 @@ sys_init_module(void __user *umod,
const char __user *uargs)
{
struct module *mod;
- int ret;
+ int i, ret;
/* Must have permission */
if (!capable(CAP_SYS_MODULE))
@@ -1700,22 +1724,13 @@ sys_init_module(void __user *umod,
up(¬ify_mutex);
/* Start the module */
- ret = mod->init();
- if (ret < 0) {
- /* Init routine failed: abort. Try to protect us from
- buggy refcounters. */
- mod->state = MODULE_STATE_GOING;
- synchronize_kernel();
- if (mod->unsafe)
- printk(KERN_ERR "%s: module is now stuck!\n",
- mod->name);
- else {
- module_put(mod);
- down(&module_mutex);
- free_module(mod);
- up(&module_mutex);
+ for (i = 0; i < mod->num_ie_pairs; i++) {
+ ret = mod->ie_pairs[i].init();
+ if (ret != 0) {
+ DEBUGP("%s: init/exit pair init=%p failed: %i\n",
+ mod->name, mod->ie_pairs[i].init, ret);
+ goto unwind_iepairs;
}
- return ret;
}
/* Now it's a first class citizen! */
@@ -1729,6 +1744,25 @@ sys_init_module(void __user *umod,
up(&module_mutex);
return 0;
+
+unwind_iepairs:
+ /* Init routine failed: abort. Try to protect us from
+ buggy refcounters. */
+ mod->state = MODULE_STATE_GOING;
+ module_put(mod);
+ synchronize_kernel();
+ if (module_refcount(mod)) {
+ printk(KERN_ERR "%s: module is now stuck!\n", mod->name);
+ return ret;
+ }
+
+ down(&module_mutex);
+ while (--i >= 0)
+ if (mod->ie_pairs[i].exit)
+ mod->ie_pairs[i].exit();
+ free_module(mod);
+ up(&module_mutex);
+ return ret;
}
static inline int within(unsigned long addr, void *start, unsigned long size)
next reply other threads:[~2003-06-23 9:07 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2003-06-23 9:19 Rusty Russell [this message]
2003-06-23 19:11 ` [PATCH 3/3] Allow arbitrary number of init funcs in modules Jonathan Corbet
2003-06-24 2:29 ` Rusty Russell
2003-06-24 6:57 ` Rusty Russell
2003-06-24 20:06 ` Horst von Brand
2003-06-24 17:01 ` Roman Zippel
2003-06-25 3:10 ` Rusty Russell
2003-06-25 10:30 ` Roman Zippel
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20030623092028.2B5272C2FC@lists.samba.org \
--to=rusty@rustcorp.com.au \
--cc=akpm@zip.com.au \
--cc=davem@redhat.com \
--cc=linux-kernel@vger.kernel.org \
--cc=mochel@osdl.org \
--cc=torvalds@transmeta.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.