module: add post hook

Currently modules cannot pull in other dependent modules in their init
routines: the dependent module would fail to load because it cannot
access symbols of modules which aren't finished initializing.

This adds a "post-init" hook, run once the init has succeeded, for
exactly this kind of case.  The post-init hook is only run if init
succeeds, and obviously this should be congruent in the built-in case.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
---
 include/linux/init.h   |   26 +++++++++++++++++++++++++-
 include/linux/module.h |    1 +
 kernel/module.c        |    8 ++++++++
 scripts/mod/modpost.c  |    4 ++++
 scripts/mod/modpost.h  |    1 +
 5 files changed, 39 insertions(+), 1 deletion(-)

diff -u
--- a/include/linux/init.h
+++ b/include/linux/init.h
@@ -191,6 +191,25 @@ void __init parse_early_param(void);
 #define module_init(x)	__initcall(x);
 
 /**
+ * module_init_and_post() - driver initialization entry point and post-init
+ * @init: function to be run at kernel boot time or module insertion
+ * @post_init: function to be run if init is successful (returns 0).
+ *
+ * module_init_and_post() will either be called during do_initcalls() (if
+ * builtin) or at module insertion time (if a module).  There can only
+ * be one module_init or module_init_and_post per module.
+ */
+#define module_init_and_post(init, post_init)		\
+	static int __init_##init##post_init(void)	\
+	{						\
+		int err = init();			\
+		if (!err)				\
+			post_init();			\
+		return err;				\
+	}						\
+	__initcall(__init_##init##post_init)
+
+/**
  * module_exit() - driver exit entry point
  * @x: function to be run when driver is removed
  * 
@@ -221,11 +240,16 @@ void __init parse_early_param(void);
  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)));
+
+#define module_init_and_post(init, post_init)				\
+	module_init(init);						\
+	static inline void (*__postinittest(void))(void)		\
+	{ return post_init; }						\
+	void post_init_module(void) __attribute__((alias(#post_init)))
 
 /* This is only required if you want to be unloadable. */
 #define module_exit(exitfn)					\
===================================================================
--- a/include/linux/module.h
+++ b/include/linux/module.h
@@ -293,6 +293,7 @@ struct module
 
 	/* Startup function. */
 	int (*init)(void);
+	void (*post_init)(void);
 
 	/* If this is non-NULL, vfree after init() returns */
 	void *module_init;
===================================================================
--- a/kernel/module.c
+++ b/kernel/module.c
@@ -2027,6 +2027,14 @@ sys_init_module(void __user *umod,
 	/* Now it's a first class citizen! */
 	mutex_lock(&module_mutex);
 	mod->state = MODULE_STATE_LIVE;
+
+	/* Drop lock to call post-init function if it has one */
+	if (mod->post_init) {
+		mutex_unlock(&module_mutex);
+		mod->post_init();
+		mutex_lock(&module_mutex);
+	}
+
 	/* Drop initial reference. */
 	module_put(mod);
 	unwind_remove_table(mod->unwind_info, 1);
===================================================================
--- a/scripts/mod/modpost.c
+++ b/scripts/mod/modpost.c
@@ -510,6 +510,8 @@ static void handle_modversions(struct mo
 		}
 		if (strcmp(symname, MODULE_SYMBOL_PREFIX "init_module") == 0)
 			mod->has_init = 1;
+		if (strcmp(symname, MODULE_SYMBOL_PREFIX "post_init_module") == 0)
+			mod->has_post_init = 1;
 		if (strcmp(symname, MODULE_SYMBOL_PREFIX "cleanup_module") == 0)
 			mod->has_cleanup = 1;
 		break;
@@ -1407,6 +1409,8 @@ static void add_header(struct buffer *b,
 	buf_printf(b, " .name = KBUILD_MODNAME,\n");
 	if (mod->has_init)
 		buf_printf(b, " .init = init_module,\n");
+	if (mod->has_post_init)
+		buf_printf(b, " .post_init = post_init_module,\n");
 	if (mod->has_cleanup)
 		buf_printf(b, "#ifdef CONFIG_MODULE_UNLOAD\n"
 			      " .exit = cleanup_module,\n"
===================================================================
--- a/scripts/mod/modpost.h
+++ b/scripts/mod/modpost.h
@@ -108,6 +108,7 @@ struct module {
 	int seen;
 	int skip;
 	int has_init;
+	int has_post_init;
 	int has_cleanup;
 	struct buffer dev_table_buf;
 	char	     srcversion[25];
