2002-07-24 20:00:33

by Roman Zippel

[permalink] [raw]
Subject: [PATCH][RFC] new module interface

Hi,

The patch below is for 2.4 but it's easily ported to 2.5, beside of this I
think the core is stable and will allow a more flexible module handling
in the future. After updating to 2.5 and updating some more archs I will
submit the patch officially, so any feedback now would be very welcome.
(The patch requires no new modutils, although a new version could avoid
some workarounds, but that can wait.)

This patch implements a new module interface. The most important change is
probably that the usercount is not part of the module structure anymore.
If the module code needs it, it asks the module for it. That count doesn't
has to be accurate, only the return value of the exit functions decides
about the unloading of the module.
This also means no kernel structures have to be polluted with module
pointers, the module structure is basically now private to the module
code, no normal module needs to mess with it. This also means
MOD_INC_USE_COUNT/MOD_DEC_USE_COUNT/MOD_IN_USE are now finally really
obsolete. Modules have to maintain their own usecount, but this usually
less painful than using a module pointer (see fs example below). In other
situation such a count already exists, e.g. a proc like interface could
use the i_count field of the inode (stop() unlinks the entry, exit()
would test if i_count became 1).
Below I converted affs and the fs code to demonstrate the new interface.
So a module is defined like this:

DEFINE_MODULE
.start = start_affs_fs,
.stop = stop_affs_fs,
.exit = exit_affs_fs,
.usecount = usecount_affs_fs,
DEFINE_MODULE_END

This defines a structure, which is automatically registered at module
init. A structure is more flexible than lots of function entries. insmod
has to know about all these functions, where as above structure is
automatically initialized when the module is linked. New fields can be
added easily, e.g. new fixup sections can be added without bothering
insmod.
On the other hand the old interfaces still work (even a direct
init_module() call), it's just not possible to unload such modules.

One user visible difference is that /proc/modules now includes builtin
modules. It requires a small kbuild change, as it needs unique module
names. I'm not completely happy with this part yet, but it shows what is
basically possible. More important is the difference between builtin
modules and loaded modules becomes less.

Some more boring implementation details:
- get_kernel_syms syscall is gone, it's not needed by modutils anymore,
only klogd is still using it, but it can live without it and should
rather be updated.
- new macros mod_for_each_locked/mod_for_done_locked to iterate over the
modules list
- __MODULE_STRING is replaced with __stringify
- BKL is replaced with a semaphore
- get_mod_name/put_mod_name is replaced with getname/putname

bye, Roman

Index: Rules.make
===================================================================
RCS file: /home/other/cvs/linux/linux-2.4/Rules.make,v
retrieving revision 1.1.1.8
diff -u -p -r1.1.1.8 Rules.make
--- Rules.make 2002/03/12 13:09:42 1.1.1.8
+++ Rules.make 2002/07/24 10:06:19
@@ -44,6 +44,7 @@ SUB_DIRS := $(subdir-y)
MOD_SUB_DIRS := $(sort $(subdir-m) $(both-m))
ALL_SUB_DIRS := $(sort $(subdir-y) $(subdir-m) $(subdir-n) $(subdir-))

+KBUILD_BASEDIR := $(subst /,_,$(subst -,_,$(subst $(TOPDIR)/,,$(shell /bin/pwd))))

#
# Common rules
@@ -56,7 +57,7 @@ ALL_SUB_DIRS := $(sort $(subdir-y) $(sub
$(CPP) $(CFLAGS) $(EXTRA_CFLAGS) $(CFLAGS_$@) $< > $@

%.o: %.c
- $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -DKBUILD_BASENAME=$(subst $(comma),_,$(subst -,_,$(*F))) $(CFLAGS_$@) -c -o $@ $<
+ $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -DKBUILD_BASENAME=$(KBUILD_BASEDIR)_$(subst $(comma),_,$(subst -,_,$(*F))) $(CFLAGS_$@) -c -o $@ $<
@ ( \
echo 'ifeq ($(strip $(subst $(comma),:,$(CFLAGS) $(EXTRA_CFLAGS) $(CFLAGS_$@))),$$(strip $$(subst $$(comma),:,$$(CFLAGS) $$(EXTRA_CFLAGS) $$(CFLAGS_$@))))' ; \
echo 'FILES_FLAGS_UP_TO_DATE += $@' ; \
@@ -272,7 +273,7 @@ endif # CONFIG_MODVERSIONS

ifneq "$(strip $(export-objs))" ""
$(export-objs): $(export-objs:.o=.c) $(TOPDIR)/include/linux/modversions.h
- $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -DKBUILD_BASENAME=$(subst $(comma),_,$(subst -,_,$(*F))) $(CFLAGS_$@) -DEXPORT_SYMTAB -c $(@:.o=.c)
+ $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -DKBUILD_BASENAME=$(KBUILD_BASEDIR)_$(subst $(comma),_,$(subst -,_,$(*F))) $(CFLAGS_$@) -DEXPORT_SYMTAB -c $(@:.o=.c)
@ ( \
echo 'ifeq ($(strip $(subst $(comma),:,$(CFLAGS) $(EXTRA_CFLAGS) $(CFLAGS_$@) -DEXPORT_SYMTAB)),$$(strip $$(subst $$(comma),:,$$(CFLAGS) $$(EXTRA_CFLAGS) $$(CFLAGS_$@) -DEXPORT_SYMTAB)))' ; \
echo 'FILES_FLAGS_UP_TO_DATE += $@' ; \
Index: arch/i386/kernel/entry.S
===================================================================
RCS file: /home/other/cvs/linux/linux-2.4/arch/i386/kernel/entry.S,v
retrieving revision 1.1.1.14
diff -u -p -r1.1.1.14 entry.S
--- arch/i386/kernel/entry.S 2002/03/12 13:09:49 1.1.1.14
+++ arch/i386/kernel/entry.S 2002/07/24 10:06:19
@@ -526,7 +526,7 @@ ENTRY(sys_call_table)
.long SYMBOL_NAME(sys_create_module)
.long SYMBOL_NAME(sys_init_module)
.long SYMBOL_NAME(sys_delete_module)
- .long SYMBOL_NAME(sys_get_kernel_syms) /* 130 */
+ .long SYMBOL_NAME(sys_ni_syscall) /* 130 */ /* old get_kernel_syms syscall */
.long SYMBOL_NAME(sys_quotactl)
.long SYMBOL_NAME(sys_getpgid)
.long SYMBOL_NAME(sys_fchdir)
Index: arch/i386/kernel/traps.c
===================================================================
RCS file: /home/other/cvs/linux/linux-2.4/arch/i386/kernel/traps.c,v
retrieving revision 1.1.1.19
diff -u -p -r1.1.1.19 traps.c
--- arch/i386/kernel/traps.c 2001/10/18 14:09:04 1.1.1.19
+++ arch/i386/kernel/traps.c 2002/07/24 10:06:19
@@ -94,21 +94,19 @@ int kstack_depth_to_print = 24;
* be the address of a calling routine
*/

-#ifdef CONFIG_MODULES
-
-extern struct module *module_list;
-extern struct module kernel_module;
-
static inline int kernel_text_address(unsigned long addr)
{
int retval = 0;
+#ifdef CONFIG_MODULES
struct module *mod;
+#endif

if (addr >= (unsigned long) &_stext &&
addr <= (unsigned long) &_etext)
return 1;

- for (mod = module_list; mod != &kernel_module; mod = mod->next) {
+#ifdef CONFIG_MODULES
+ mod_for_each_locked(mod) {
/* mod_bound tests for addr being inside the vmalloc'ed
* module area. Of course it'd be better to test only
* for the .text subset... */
@@ -116,20 +114,11 @@ static inline int kernel_text_address(un
retval = 1;
break;
}
- }
+ } mod_for_done_locked
+#endif

return retval;
}
-
-#else
-
-static inline int kernel_text_address(unsigned long addr)
-{
- return (addr >= (unsigned long) &_stext &&
- addr <= (unsigned long) &_etext);
-}
-
-#endif

void show_trace(unsigned long * stack)
{
Index: arch/i386/mm/extable.c
===================================================================
RCS file: /home/other/cvs/linux/linux-2.4/arch/i386/mm/extable.c,v
retrieving revision 1.1.1.4
diff -u -p -r1.1.1.4 extable.c
--- arch/i386/mm/extable.c 2001/10/18 13:38:54 1.1.1.4
+++ arch/i386/mm/extable.c 2002/07/24 10:06:19
@@ -15,7 +15,7 @@ search_one_table(const struct exception_
const struct exception_table_entry *last,
unsigned long value)
{
- while (first <= last) {
+ while (first < last) {
const struct exception_table_entry *mid;
long diff;

@@ -31,32 +31,24 @@ search_one_table(const struct exception_
return 0;
}

-extern spinlock_t modlist_lock;
-
unsigned long
search_exception_table(unsigned long addr)
{
- unsigned long ret = 0;

#ifndef CONFIG_MODULES
/* There is only the kernel to search. */
- ret = search_one_table(__start___ex_table, __stop___ex_table-1, addr);
- return ret;
+ return search_one_table(__start___ex_table, __stop___ex_table, addr);
#else
- unsigned long flags;
- /* The kernel is the last "module" -- no need to treat it special. */
+ unsigned long ret = 0;
struct module *mp;

- spin_lock_irqsave(&modlist_lock, flags);
- for (mp = module_list; mp != NULL; mp = mp->next) {
- if (mp->ex_table_start == NULL || !(mp->flags&(MOD_RUNNING|MOD_INITIALIZING)))
- continue;
+ mod_for_each_locked(mp) {
ret = search_one_table(mp->ex_table_start,
- mp->ex_table_end - 1, addr);
+ mp->ex_table_end, addr);
if (ret)
break;
- }
- spin_unlock_irqrestore(&modlist_lock, flags);
+ } mod_for_done_locked
+
return ret;
#endif
}
Index: fs/dnotify.c
===================================================================
RCS file: /home/other/cvs/linux/linux-2.4/fs/dnotify.c,v
retrieving revision 1.1.1.2
diff -u -p -r1.1.1.2 dnotify.c
--- fs/dnotify.c 2000/12/05 11:46:49 1.1.1.2
+++ fs/dnotify.c 2002/07/24 10:06:19
@@ -13,6 +13,7 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*/
+#include <linux/module.h>
#include <linux/fs.h>
#include <linux/sched.h>
#include <linux/dnotify.h>
Index: fs/fcntl.c
===================================================================
RCS file: /home/other/cvs/linux/linux-2.4/fs/fcntl.c,v
retrieving revision 1.1.1.10
diff -u -p -r1.1.1.10 fcntl.c
--- fs/fcntl.c 2001/10/18 13:40:46 1.1.1.10
+++ fs/fcntl.c 2002/07/24 10:06:19
@@ -4,6 +4,7 @@
* Copyright (C) 1991, 1992 Linus Torvalds
*/

+#include <linux/module.h>
#include <linux/init.h>
#include <linux/mm.h>
#include <linux/file.h>
Index: fs/locks.c
===================================================================
RCS file: /home/other/cvs/linux/linux-2.4/fs/locks.c,v
retrieving revision 1.1.1.13
diff -u -p -r1.1.1.13 locks.c
--- fs/locks.c 2001/10/24 09:03:18 1.1.1.13
+++ fs/locks.c 2002/07/24 10:06:19
@@ -115,6 +115,7 @@
* Stephen Rothwell <[email protected]>, June, 2000.
*/

+#include <linux/module.h>
#include <linux/slab.h>
#include <linux/file.h>
#include <linux/smp_lock.h>
Index: fs/super.c
===================================================================
RCS file: /home/other/cvs/linux/linux-2.4/fs/super.c,v
retrieving revision 1.1.1.24
diff -u -p -r1.1.1.24 super.c
--- fs/super.c 2002/03/12 13:11:52 1.1.1.24
+++ fs/super.c 2002/07/24 10:06:19
@@ -73,14 +73,12 @@ static rwlock_t file_systems_lock = RW_L
/* WARNING: This can be used only if we _already_ own a reference */
static void get_filesystem(struct file_system_type *fs)
{
- if (fs->owner)
- __MOD_INC_USE_COUNT(fs->owner);
+ atomic_inc(&fs->usecount);
}

static void put_filesystem(struct file_system_type *fs)
{
- if (fs->owner)
- __MOD_DEC_USE_COUNT(fs->owner);
+ atomic_dec(&fs->usecount);
}

static struct file_system_type **find_filesystem(const char *name)
@@ -187,8 +185,10 @@ static int fs_name(unsigned int index, c

read_lock(&file_systems_lock);
for (tmp = file_systems; tmp; tmp = tmp->next, index--)
- if (index <= 0 && try_inc_mod_count(tmp->owner))
- break;
+ if (index <= 0) {
+ get_filesystem(tmp);
+ break;
+ }
read_unlock(&file_systems_lock);
if (!tmp)
return -EINVAL;
@@ -258,14 +258,14 @@ struct file_system_type *get_fs_type(con

read_lock(&file_systems_lock);
fs = *(find_filesystem(name));
- if (fs && !try_inc_mod_count(fs->owner))
- fs = NULL;
+ if (fs)
+ get_filesystem(fs);
read_unlock(&file_systems_lock);
if (!fs && (request_module(name) == 0)) {
read_lock(&file_systems_lock);
fs = *(find_filesystem(name));
- if (fs && !try_inc_mod_count(fs->owner))
- fs = NULL;
+ if (fs)
+ get_filesystem(fs);
read_unlock(&file_systems_lock);
}
return fs;
Index: fs/affs/super.c
===================================================================
RCS file: /home/other/cvs/linux/linux-2.4/fs/affs/super.c,v
retrieving revision 1.1.1.8
diff -u -p -r1.1.1.8 super.c
--- fs/affs/super.c 2002/03/12 13:11:52 1.1.1.8
+++ fs/affs/super.c 2002/07/24 11:42:41
@@ -492,20 +492,35 @@ affs_statfs(struct super_block *sb, stru

static DECLARE_FSTYPE_DEV(affs_fs_type, "affs", affs_read_super);

-static int __init init_affs_fs(void)
+static int __init start_affs_fs(void)
{
return register_filesystem(&affs_fs_type);
}

-static void __exit exit_affs_fs(void)
+static int stop_affs_fs(void)
{
- unregister_filesystem(&affs_fs_type);
+ return unregister_filesystem(&affs_fs_type);
}

+static int __exit exit_affs_fs(void)
+{
+ return atomic_read(&affs_fs_type.usecount) ? -EBUSY : 0;
+}
+
+static int usecount_affs_fs(void)
+{
+ return atomic_read(&affs_fs_type.usecount);
+}
+
+DEFINE_MODULE
+ .start = start_affs_fs,
+ .stop = stop_affs_fs,
+ .exit = exit_affs_fs,
+ .usecount = usecount_affs_fs,
+DEFINE_MODULE_END
+
EXPORT_NO_SYMBOLS;

MODULE_DESCRIPTION("Amiga filesystem support for Linux");
MODULE_LICENSE("GPL");

-module_init(init_affs_fs)
-module_exit(exit_affs_fs)
Index: include/linux/fs.h
===================================================================
RCS file: /home/other/cvs/linux/linux-2.4/include/linux/fs.h,v
retrieving revision 1.1.1.30
diff -u -p -r1.1.1.30 fs.h
--- include/linux/fs.h 2002/03/12 13:13:10 1.1.1.30
+++ include/linux/fs.h 2002/07/24 10:14:18
@@ -951,7 +951,7 @@ struct file_system_type {
const char *name;
int fs_flags;
struct super_block *(*read_super) (struct super_block *, void *, int);
- struct module *owner;
+ atomic_t usecount;
struct file_system_type * next;
struct list_head fs_supers;
};
@@ -961,7 +961,7 @@ struct file_system_type var = { \
name: type, \
read_super: read, \
fs_flags: flags, \
- owner: THIS_MODULE, \
+ usecount: ATOMIC_INIT(0), \
}

#define DECLARE_FSTYPE_DEV(var,type,read) \
Index: include/linux/init.h
===================================================================
RCS file: /home/other/cvs/linux/linux-2.4/include/linux/init.h,v
retrieving revision 1.1.1.8
diff -u -p -r1.1.1.8 init.h
--- include/linux/init.h 2002/01/08 13:14:55 1.1.1.8
+++ include/linux/init.h 2002/07/24 10:06:20
@@ -88,29 +88,6 @@ extern struct kernel_param __setup_start
#define __FINIT .previous
#define __INITDATA .section ".data.init","aw"

-/**
- * module_init() - driver initialization entry point
- * @x: function to be run at kernel boot time or module insertion
- *
- * module_init() will add the driver initialization routine in
- * the "__initcall.int" code segment if the driver is checked as
- * "y" or static, or else it will wrap the driver initialization
- * routine with init_module() which is used by insmod and
- * modprobe when the driver is used as a module.
- */
-#define module_init(x) __initcall(x);
-
-/**
- * module_exit() - driver exit entry point
- * @x: function to be run when driver is removed
- *
- * module_exit() will wrap the driver clean-up code
- * with cleanup_module() when used with rmmod when
- * the driver is a module. If the driver is statically
- * compiled into the kernel, module_exit() has no effect.
- */
-#define module_exit(x) __exitcall(x);
-
#else /* MODULE */

#define __init
@@ -122,22 +99,6 @@ extern struct kernel_param __setup_start
#define __INIT
#define __FINIT
#define __INITDATA
-
-/* 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. */
-typedef int (*__init_module_func_t)(void);
-typedef void (*__cleanup_module_func_t)(void);
-#define module_init(x) \
- int init_module(void) __attribute__((alias(#x))); \
- static inline __init_module_func_t __init_module_inline(void) \
- { return x; }
-#define module_exit(x) \
- void cleanup_module(void) __attribute__((alias(#x))); \
- static inline __cleanup_module_func_t __cleanup_module_inline(void) \
- { return x; }

#define __setup(str,func) /* nothing */

Index: include/linux/module.h
===================================================================
RCS file: /home/other/cvs/linux/linux-2.4/include/linux/module.h,v
retrieving revision 1.1.1.11
diff -u -p -r1.1.1.11 module.h
--- include/linux/module.h 2001/11/23 09:32:36 1.1.1.11
+++ include/linux/module.h 2002/07/24 10:06:20
@@ -10,6 +10,7 @@
#include <linux/config.h>
#include <linux/spinlock.h>
#include <linux/list.h>
+#include <linux/stringify.h>

#ifdef __GENKSYMS__
# define _set_ver(sym) sym
@@ -50,11 +51,11 @@ struct module_ref
/* TBD */
struct module_persist;

-struct module
+struct old_module
{
unsigned long size_of_struct; /* == sizeof(module) */
struct module *next;
- const char *name;
+ char *name;
unsigned long size;

union
@@ -71,7 +72,7 @@ struct module
struct module_symbol *syms;
struct module_ref *deps;
struct module_ref *refs;
- int (*init)(void);
+ int (*init)(struct module *);
void (*cleanup)(void);
const struct exception_table_entry *ex_table_start;
const struct exception_table_entry *ex_table_end;
@@ -92,6 +93,41 @@ struct module
const char *kernel_data; /* Reserved for kernel internal use */
};

+struct module
+{
+ struct list_head list;
+ char *name;
+ unsigned long size;
+ unsigned long flags;
+
+ atomic_t ref_cnt;
+ struct old_module *old;
+
+ struct module_symbol *syms;
+ unsigned nsyms;
+ struct module_ref *deps;
+ unsigned ndeps;
+ struct module_ref *refs;
+
+ int (*init)(void);
+ int (*exit)(void);
+ int (*start)(void);
+ int (*stop)(void);
+ int (*usecount)(void);
+
+ const struct exception_table_entry *ex_table_start;
+ const struct exception_table_entry *ex_table_end;
+#ifdef __alpha__
+ unsigned long gp;
+#endif
+ const struct module_persist *persist_start;
+ const struct module_persist *persist_end;
+ const char *kallsyms_start; /* All symbols for kernel debugging */
+ const char *kallsyms_end;
+ const char *archdata_start; /* arch specific data for module */
+ const char *archdata_end;
+};
+
struct module_info
{
unsigned long addr;
@@ -102,14 +138,20 @@ struct module_info

/* Bits of module.flags. */

-#define MOD_UNINITIALIZED 0
-#define MOD_RUNNING 1
-#define MOD_DELETED 2
-#define MOD_AUTOCLEAN 4
-#define MOD_VISITED 8
-#define MOD_USED_ONCE 16
-#define MOD_JUST_FREED 32
-#define MOD_INITIALIZING 64
+#define MOD_LOCKED 0
+#define MOD_LOADED 1
+#define MOD_INITIALIZED 2
+#define MOD_RUNNING 3
+#define MOD_BUILTIN 4
+#define MOD_AUTOCLEAN 5
+#define MOD_ALLOCED 6
+#define MOD_OLDSTYLE 7
+
+#define OLD_MOD_RUNNING 1
+#define OLD_MOD_DELETED 2
+#define OLD_MOD_AUTOCLEAN 4
+#define OLD_MOD_USED_ONCE 16
+#define OLD_MOD_INITIALIZING 64

/* Values for query_module's which. */

@@ -119,16 +161,11 @@ struct module_info
#define QM_SYMBOLS 4
#define QM_INFO 5

-/* Can the module be queried? */
-#define MOD_CAN_QUERY(mod) (((mod)->flags & (MOD_RUNNING | MOD_INITIALIZING)) && !((mod)->flags & MOD_DELETED))
-
/* When struct module is extended, we must test whether the new member
is present in the header received from insmod before we can use it.
This function returns true if the member is present. */

-#define mod_member_present(mod,member) \
- ((unsigned long)(&((struct module *)0L)->member + 1) \
- <= (mod)->size_of_struct)
+#define mod_member_present(mod,member) 1

/*
* Ditto for archdata. Assumes mod->archdata_start and mod->archdata_end
@@ -138,31 +175,22 @@ struct module_info
(((unsigned long)(&((type *)0L)->member) + \
sizeof(((type *)0L)->member)) <= \
((mod)->archdata_end - (mod)->archdata_start))
-

+
/* Check if an address p with number of entries n is within the body of module m */
-#define mod_bound(p, n, m) ((unsigned long)(p) >= ((unsigned long)(m) + ((m)->size_of_struct)) && \
- (unsigned long)((p)+(n)) <= (unsigned long)(m) + (m)->size)
+#define mod_bound(p, n, m) ((unsigned long)(p) >= ((unsigned long)(m)->old + (sizeof(struct module))) && \
+ (unsigned long)((p)+(n)) < (unsigned long)(m)->old + (m)->size)

/* Backwards compatibility definition. */

-#define GET_USE_COUNT(module) (atomic_read(&(module)->uc.usecount))
+#define GET_USE_COUNT(module) (-1)

/* Poke the use count of a module. */
-
-#define __MOD_INC_USE_COUNT(mod) \
- (atomic_inc(&(mod)->uc.usecount), (mod)->flags |= MOD_VISITED|MOD_USED_ONCE)
-#define __MOD_DEC_USE_COUNT(mod) \
- (atomic_dec(&(mod)->uc.usecount), (mod)->flags |= MOD_VISITED)
-#define __MOD_IN_USE(mod) \
- (mod_member_present((mod), can_unload) && (mod)->can_unload \
- ? (mod)->can_unload() : atomic_read(&(mod)->uc.usecount))

-/* Indirect stringification. */
+#define __MOD_INC_USE_COUNT(mod) ((void)0)
+#define __MOD_DEC_USE_COUNT(mod) ((void)0)
+#define __MOD_IN_USE(mod) (-1)

-#define __MODULE_STRING_1(x) #x
-#define __MODULE_STRING(x) __MODULE_STRING_1(x)
-
/* Generic inter module communication.
*
* NOTE: This interface is intended for small amounts of data that are
@@ -192,8 +220,32 @@ struct inter_module_entry {
struct module *owner;
const void *userdata;
};
+
+#define try_inc_mod_count(mod) (1)
+
+#define ___concat(x, y) x ## y
+#define __concat(x, y) ___concat(x, y)

-extern int try_inc_mod_count(struct module *mod);
+#define __THIS_MODULE __concat(__this_module_, KBUILD_BASENAME)
+extern struct module __THIS_MODULE;
+
+extern int init_module_hack(struct module *, struct module *);
+
+#define mod_for_each_locked(mod) \
+{ \
+ unsigned long flags; \
+ struct list_head *list; \
+ \
+ spin_lock_irqsave(&modlist_lock, flags); \
+ list_for_each(list, &module_list) { \
+ mod = list_entry(list, struct module, list); \
+ if (!test_bit(MOD_LOADED, &mod->flags)) \
+ continue;
+#define mod_for_done_locked \
+ } \
+ spin_unlock_irqrestore(&modlist_lock, flags); \
+}
+
#endif /* __KERNEL__ */

#if defined(MODULE) && !defined(__GENKSYMS__)
@@ -232,12 +284,12 @@ const char __module_device[] __attribute
#define MODULE_PARM(var,type) \
const char __module_parm_##var[] \
__attribute__((section(".modinfo"))) = \
-"parm_" __MODULE_STRING(var) "=" type
+"parm_" __stringify(var) "=" type

#define MODULE_PARM_DESC(var,desc) \
const char __module_parm_desc_##var[] \
__attribute__((section(".modinfo"))) = \
-"parm_desc_" __MODULE_STRING(var) "=" desc
+"parm_desc_" __stringify(var) "=" desc

/*
* MODULE_DEVICE_TABLE exports information about devices
@@ -287,9 +339,24 @@ static const char __module_license[] __a
"license=" license

/* Define the module variable, and usage macros. */
-extern struct module __this_module;
+extern struct old_module __this_module;

-#define THIS_MODULE (&__this_module)
+#define DEFINE_MODULE \
+int __init init_module(struct module *m) \
+{ return init_module_hack(m, THIS_MODULE); } \
+struct module __THIS_MODULE = { \
+ .old = &__this_module,
+#define DEFINE_MODULE_END \
+};
+
+#define module_init(x) \
+DEFINE_MODULE \
+ .flags = (1 << MOD_OLDSTYLE), \
+ .init = x, \
+DEFINE_MODULE_END
+#define module_exit(x)
+
+#define THIS_MODULE (&__THIS_MODULE)
#define MOD_INC_USE_COUNT __MOD_INC_USE_COUNT(THIS_MODULE)
#define MOD_DEC_USE_COUNT __MOD_DEC_USE_COUNT(THIS_MODULE)
#define MOD_IN_USE __MOD_IN_USE(THIS_MODULE)
@@ -323,11 +390,31 @@ static const struct gtype##_id * __modul
#ifndef __GENKSYMS__

#define THIS_MODULE NULL
-#define MOD_INC_USE_COUNT do { } while (0)
-#define MOD_DEC_USE_COUNT do { } while (0)
+#define MOD_INC_USE_COUNT ((void)0)
+#define MOD_DEC_USE_COUNT ((void)0)
#define MOD_IN_USE 1
+
+extern int init_builtin_module(struct module *mod);
+
+#define DEFINE_MODULE \
+static int __init __init_module(void) \
+{ return init_builtin_module(&__THIS_MODULE); } \
+__initcall(__init_module); \
+struct module __THIS_MODULE = { \
+ .name = __stringify(KBUILD_BASENAME), \
+ .flags = (1 << MOD_OLDSTYLE),
+#define DEFINE_MODULE_END \
+};

-extern struct module *module_list;
+#define module_init(x) \
+DEFINE_MODULE \
+ .init = x, \
+DEFINE_MODULE_END
+#define module_exit(x)
+
+extern struct list_head module_list;
+extern spinlock_t modlist_lock;
+extern struct semaphore modlist_readlock;

#endif /* !__GENKSYMS__ */

@@ -387,14 +474,14 @@ __attribute__((section("__ksymtab"))) =
{ (unsigned long)&sym, __kstrtab_##sym }

#if defined(MODVERSIONS) || !defined(CONFIG_MODVERSIONS)
-#define EXPORT_SYMBOL(var) __EXPORT_SYMBOL(var, __MODULE_STRING(var))
-#define EXPORT_SYMBOL_GPL(var) __EXPORT_SYMBOL_GPL(var, __MODULE_STRING(var))
+#define EXPORT_SYMBOL(var) __EXPORT_SYMBOL(var, __stringify(var))
+#define EXPORT_SYMBOL_GPL(var) __EXPORT_SYMBOL_GPL(var, __stringify(var))
#else
-#define EXPORT_SYMBOL(var) __EXPORT_SYMBOL(var, __MODULE_STRING(__VERSIONED_SYMBOL(var)))
-#define EXPORT_SYMBOL_GPL(var) __EXPORT_SYMBOL(var, __MODULE_STRING(__VERSIONED_SYMBOL(var)))
+#define EXPORT_SYMBOL(var) __EXPORT_SYMBOL(var, __stringify(__VERSIONED_SYMBOL(var)))
+#define EXPORT_SYMBOL_GPL(var) __EXPORT_SYMBOL(var, __stringify(__VERSIONED_SYMBOL(var)))
#endif

-#define EXPORT_SYMBOL_NOVERS(var) __EXPORT_SYMBOL(var, __MODULE_STRING(var))
+#define EXPORT_SYMBOL_NOVERS(var) __EXPORT_SYMBOL(var, __stringify(var))

#endif /* __GENKSYMS__ */

Index: init/main.c
===================================================================
RCS file: /home/other/cvs/linux/linux-2.4/init/main.c,v
retrieving revision 1.1.1.25
diff -u -p -r1.1.1.25 main.c
--- init/main.c 2002/03/12 13:13:33 1.1.1.25
+++ init/main.c 2002/07/24 10:06:20
@@ -12,6 +12,7 @@
#define __KERNEL_SYSCALLS__

#include <linux/config.h>
+#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/devfs_fs_kernel.h>
#include <linux/unistd.h>
Index: kernel/ksyms.c
===================================================================
RCS file: /home/other/cvs/linux/linux-2.4/kernel/ksyms.c,v
retrieving revision 1.1.1.30
diff -u -p -r1.1.1.30 ksyms.c
--- kernel/ksyms.c 2002/03/12 13:13:33 1.1.1.30
+++ kernel/ksyms.c 2002/07/24 10:06:20
@@ -78,7 +78,7 @@ EXPORT_SYMBOL(inter_module_unregister);
EXPORT_SYMBOL(inter_module_get);
EXPORT_SYMBOL(inter_module_get_request);
EXPORT_SYMBOL(inter_module_put);
-EXPORT_SYMBOL(try_inc_mod_count);
+EXPORT_SYMBOL(init_module_hack);

/* process memory management */
EXPORT_SYMBOL(do_mmap_pgoff);
Index: kernel/module.c
===================================================================
RCS file: /home/other/cvs/linux/linux-2.4/kernel/module.c,v
retrieving revision 1.1.1.11
diff -u -p -r1.1.1.11 module.c
--- kernel/module.c 2001/11/23 09:32:47 1.1.1.11
+++ kernel/module.c 2002/07/24 10:06:20
@@ -42,18 +42,17 @@ extern const char __stop___kallsyms[] __

struct module kernel_module =
{
- size_of_struct: sizeof(struct module),
- name: "",
- uc: {ATOMIC_INIT(1)},
- flags: MOD_RUNNING,
- syms: __start___ksymtab,
- ex_table_start: __start___ex_table,
- ex_table_end: __stop___ex_table,
- kallsyms_start: __start___kallsyms,
- kallsyms_end: __stop___kallsyms,
+ .name = "kernel",
+ .ref_cnt = ATOMIC_INIT(1),
+ .flags = (1 << MOD_BUILTIN) | (1 << MOD_LOADED) | (1 << MOD_INITIALIZED) | (1 << MOD_RUNNING),
+ .syms = __start___ksymtab,
+ .ex_table_start = __start___ex_table,
+ .ex_table_end = __stop___ex_table,
+ .kallsyms_start = __start___kallsyms,
+ .kallsyms_end = __stop___kallsyms,
};

-struct module *module_list = &kernel_module;
+LIST_HEAD(module_list);

#endif /* defined(CONFIG_MODULES) || defined(CONFIG_KALLSYMS) */

@@ -64,20 +63,30 @@ struct module *module_list = &kernel_mod
*/

static struct list_head ime_list = LIST_HEAD_INIT(ime_list);
-static spinlock_t ime_lock = SPIN_LOCK_UNLOCKED;
static int kmalloc_failed;

/*
- * This lock prevents modifications that might race the kernel fault
- * fixups. It does not prevent reader walks that the modules code
- * does. The kernel lock does that.
- *
- * Since vmalloc fault fixups occur in any context this lock is taken
- * irqsave at all times.
+ * modlist_lock prevents modifications that might race the kernel fault
+ * fixups. It does not prevent other list operations, modlist_readlock
+ * has to be used for for this.
*/
-
+
+DECLARE_MUTEX(modlist_readlock);
spinlock_t modlist_lock = SPIN_LOCK_UNLOCKED;

+void free_module(struct module *);
+
+static inline void get_module(struct module *mod)
+{
+ atomic_inc(&mod->ref_cnt);
+}
+
+static inline void put_module(struct module *mod)
+{
+ if (atomic_dec_and_test(&mod->ref_cnt))
+ free_module(mod);
+}
+
/**
* inter_module_register - register a new set of inter module data.
* @im_name: an arbitrary string to identify the data, must be unique
@@ -106,19 +115,21 @@ void inter_module_register(const char *i
ime_new->owner = owner;
ime_new->userdata = userdata;

- spin_lock(&ime_lock);
+ down(&modlist_readlock);
list_for_each(tmp, &ime_list) {
ime = list_entry(tmp, struct inter_module_entry, list);
if (strcmp(ime->im_name, im_name) == 0) {
- spin_unlock(&ime_lock);
+ up(&modlist_readlock);
kfree(ime_new);
/* Program logic error, fatal */
printk(KERN_ERR "inter_module_register: duplicate im_name '%s'", im_name);
BUG();
}
}
+ if (owner)
+ get_module(owner);
list_add(&(ime_new->list), &ime_list);
- spin_unlock(&ime_lock);
+ up(&modlist_readlock);
}

/**
@@ -134,17 +145,19 @@ void inter_module_unregister(const char
struct list_head *tmp;
struct inter_module_entry *ime;

- spin_lock(&ime_lock);
+ down(&modlist_readlock);
list_for_each(tmp, &ime_list) {
ime = list_entry(tmp, struct inter_module_entry, list);
if (strcmp(ime->im_name, im_name) == 0) {
+ if (ime->owner)
+ put_module(ime->owner);
list_del(&(ime->list));
- spin_unlock(&ime_lock);
+ up(&modlist_readlock);
kfree(ime);
return;
}
}
- spin_unlock(&ime_lock);
+ up(&modlist_readlock);
if (kmalloc_failed) {
printk(KERN_ERR
"inter_module_unregister: no entry for '%s', "
@@ -173,16 +186,17 @@ const void *inter_module_get(const char
struct inter_module_entry *ime;
const void *result = NULL;

- spin_lock(&ime_lock);
+ down(&modlist_readlock);
list_for_each(tmp, &ime_list) {
ime = list_entry(tmp, struct inter_module_entry, list);
if (strcmp(ime->im_name, im_name) == 0) {
- if (try_inc_mod_count(ime->owner))
- result = ime->userdata;
+ if (ime->owner)
+ get_module(ime->owner);
+ result = ime->userdata;
break;
}
}
- spin_unlock(&ime_lock);
+ up(&modlist_readlock);
return(result);
}

@@ -215,17 +229,17 @@ void inter_module_put(const char *im_nam
struct list_head *tmp;
struct inter_module_entry *ime;

- spin_lock(&ime_lock);
+ down(&modlist_readlock);
list_for_each(tmp, &ime_list) {
ime = list_entry(tmp, struct inter_module_entry, list);
if (strcmp(ime->im_name, im_name) == 0) {
if (ime->owner)
- __MOD_DEC_USE_COUNT(ime->owner);
- spin_unlock(&ime_lock);
+ put_module(ime->owner);
+ up(&modlist_readlock);
return;
}
}
- spin_unlock(&ime_lock);
+ up(&modlist_readlock);
printk(KERN_ERR "inter_module_put: no entry for '%s'", im_name);
BUG();
}
@@ -233,55 +247,125 @@ void inter_module_put(const char *im_nam

#if defined(CONFIG_MODULES) /* The rest of the source */

-static long get_mod_name(const char *user_name, char **buf);
-static void put_mod_name(char *buf);
-struct module *find_module(const char *name);
-void free_module(struct module *, int tag_freed);
+int register_module(struct module *);
+void unregister_module(struct module *);

-
/*
- * Called at boot time
+ * Look for a module by name.
*/

-void __init init_modules(void)
+static struct module *find_module(const char *name)
{
- kernel_module.nsyms = __stop___ksymtab - __start___ksymtab;
+ struct module *mod;
+ struct list_head *list;

- arch_init_modules(&kernel_module);
+ down(&modlist_readlock);
+ list_for_each(list, &module_list) {
+ mod = list_entry(list, struct module, list);
+ if (!strcmp(name, mod->name)) {
+ get_module(mod);
+ goto found;
+ }
+ }
+ mod = NULL;
+found:
+ up(&modlist_readlock);
+
+ return mod;
+}
+
+static inline int lock_module(struct module *mod)
+{
+ return test_and_set_bit(MOD_LOCKED, &mod->flags);
+}
+
+static inline void unlock_module(struct module *mod)
+{
+ clear_bit(MOD_LOCKED, &mod->flags);
+}
+
+static inline int init_module(struct module *mod)
+{
+ int res = 0;
+
+ //printk("init_module(%p,%lx):", mod, mod->flags);
+ if (!mod->init || !(res = mod->init()))
+ set_bit(MOD_INITIALIZED, &mod->flags);
+ //printk(" %d\n", res);
+ return res;
+}
+
+static inline int exit_module(struct module *mod)
+{
+ int res = 0;
+
+ //printk("exit_module(%p,%lx):", mod, mod->flags);
+ if (!mod->exit || !(res = mod->exit()))
+ clear_bit(MOD_INITIALIZED, &mod->flags);
+ //printk(" %d\n", res);
+ return res;
+}
+
+static inline int start_module(struct module *mod)
+{
+ int res = 0;
+
+ //printk("start_module(%p,%lx):", mod, mod->flags);
+ if (!mod->start || !(res = mod->start()))
+ set_bit(MOD_RUNNING, &mod->flags);
+ //printk(" %d\n", res);
+ return res;
}

+static inline int stop_module(struct module *mod)
+{
+ int res = 0;
+
+ //printk("stop_module(%p,%lx):", mod, mod->flags);
+ if (!mod->stop || !(res = mod->stop()))
+ clear_bit(MOD_RUNNING, &mod->flags);
+ //printk(" %d\n", res);
+ return res;
+}
+
+static inline int usecount_module(struct module *mod)
+{
+ int res = 0;
+
+ if (mod->usecount)
+ res = mod->usecount();
+ return res;
+}
+
/*
- * Copy the name of a module from user space.
+ * Called at boot time
*/

-static inline long
-get_mod_name(const char *user_name, char **buf)
+void __init init_modules(void)
{
- unsigned long page;
- long retval;
+ kernel_module.nsyms = __stop___ksymtab - __start___ksymtab;

- page = __get_free_page(GFP_KERNEL);
- if (!page)
- return -ENOMEM;
-
- retval = strncpy_from_user((char *)page, user_name, PAGE_SIZE);
- if (retval > 0) {
- if (retval < PAGE_SIZE) {
- *buf = (char *)page;
- return retval;
- }
- retval = -ENAMETOOLONG;
- } else if (!retval)
- retval = -EINVAL;
+ list_add_tail(&kernel_module.list, &module_list);

- free_page(page);
- return retval;
+ arch_init_modules(&kernel_module);
}

-static inline void
-put_mod_name(char *buf)
+int __init init_builtin_module(struct module *mod)
{
- free_page((unsigned long)buf);
+ int res;
+
+ set_bit(MOD_BUILTIN, &mod->flags);
+ atomic_set(&mod->ref_cnt, 1);
+ list_add_tail(&mod->list, &module_list);
+
+ set_bit(MOD_LOADED, &mod->flags);
+ res = init_module(mod);
+ if (!res) {
+ res = start_module(mod);
+ if (res)
+ exit_module(mod);
+ }
+ return res;
}

/*
@@ -294,47 +378,50 @@ sys_create_module(const char *name_user,
char *name;
long namelen, error;
struct module *mod;
- unsigned long flags;
+ struct old_module *old;

if (!capable(CAP_SYS_MODULE))
return -EPERM;
- lock_kernel();
- if ((namelen = get_mod_name(name_user, &name)) < 0) {
- error = namelen;
- goto err0;
- }
- if (size < sizeof(struct module)+namelen) {
- error = -EINVAL;
- goto err1;
- }
- if (find_module(name) != NULL) {
+
+ name = getname(name_user);
+ if (IS_ERR(name))
+ return PTR_ERR(name);
+ namelen = strlen(name);
+
+ mod = find_module(name);
+ if (mod) {
+ put_module(mod);
error = -EEXIST;
- goto err1;
+ goto err0;
}
- if ((mod = (struct module *)module_map(size)) == NULL) {
+
+ old = (struct old_module *)module_map(size);
+ if (!old) {
error = -ENOMEM;
- goto err1;
+ goto err0;
}

+ mod = kmalloc(sizeof(struct module) + namelen + 1, GFP_KERNEL);
+ if (!mod) {
+ module_unmap(old);
+ error = -ENOMEM;
+ goto err0;
+ }
memset(mod, 0, sizeof(*mod));
- mod->size_of_struct = sizeof(*mod);
- mod->name = (char *)(mod + 1);
+ atomic_set(&mod->ref_cnt, 1);
+ mod->flags = (1 << MOD_LOCKED) | (1 << MOD_ALLOCED);
+ mod->name = (char *)mod + size;
mod->size = size;
- memcpy((char*)(mod+1), name, namelen+1);
-
- put_mod_name(name);
+ mod->old = old;
+ strcpy(mod->name, name);

- spin_lock_irqsave(&modlist_lock, flags);
- mod->next = module_list;
- module_list = mod; /* link it in */
- spin_unlock_irqrestore(&modlist_lock, flags);
-
- error = (long) mod;
- goto err0;
-err1:
- put_mod_name(name);
+ error = register_module(mod);
+ unlock_module(mod);
+ if (!error)
+ error = (long)old;
+ put_module(mod);
err0:
- unlock_kernel();
+ putname(name);
return error;
}

@@ -345,256 +432,220 @@ err0:
asmlinkage long
sys_init_module(const char *name_user, struct module *mod_user)
{
- struct module mod_tmp, *mod;
- char *name, *n_name, *name_tmp = NULL;
- long namelen, n_namelen, i, error;
- unsigned long mod_user_size;
- struct module_ref *dep;
+ struct module *mod, *mod2;
+ char *name;
+ int namelen, error, i;
+ struct module_ref *ref;
+ struct list_head *list;

if (!capable(CAP_SYS_MODULE))
return -EPERM;
- lock_kernel();
- if ((namelen = get_mod_name(name_user, &name)) < 0) {
- error = namelen;
- goto err0;
- }
- if ((mod = find_module(name)) == NULL) {
+
+ name = getname(name_user);
+ if (IS_ERR(name))
+ return PTR_ERR(name);
+ namelen = strlen(name);
+
+ mod = find_module(name);
+ if (!mod) {
error = -ENOENT;
- goto err1;
+ goto err0;
}

- /* Check module header size. We allow a bit of slop over the
- size we are familiar with to cope with a version of insmod
- for a newer kernel. But don't over do it. */
- if ((error = get_user(mod_user_size, &mod_user->size_of_struct)) != 0)
- goto err1;
- if (mod_user_size < (unsigned long)&((struct module *)0L)->persist_start
- || mod_user_size > sizeof(struct module) + 16*sizeof(void*)) {
- printk(KERN_ERR "init_module: Invalid module header size.\n"
- KERN_ERR "A new version of the modutils is likely "
- "needed.\n");
- error = -EINVAL;
+ if (lock_module(mod)) {
+ error = -EBUSY;
goto err1;
}

- /* Hold the current contents while we play with the user's idea
- of righteousness. */
- mod_tmp = *mod;
- name_tmp = kmalloc(strlen(mod->name) + 1, GFP_KERNEL); /* Where's kstrdup()? */
- if (name_tmp == NULL) {
- error = -ENOMEM;
- goto err1;
+ if (test_bit(MOD_LOADED, &mod->flags)) {
+ error = -EEXIST;
+ goto err2;
}
- strcpy(name_tmp, mod->name);

- error = copy_from_user(mod, mod_user, mod_user_size);
+ error = copy_from_user(mod->old, mod_user, mod->size);
if (error) {
error = -EFAULT;
goto err2;
}

- /* Sanity check the size of the module. */
- error = -EINVAL;
+ if (mod->old->flags & OLD_MOD_AUTOCLEAN)
+ set_bit(MOD_AUTOCLEAN, &mod->flags);
+ mod->name = mod->old->name;
+ mod->syms = mod->old->syms;
+ mod->nsyms = mod->old->nsyms;
+ mod->deps = mod->old->deps;
+ mod->ndeps = mod->old->ndeps;
+ mod->ex_table_start = mod->old->ex_table_start;
+ mod->ex_table_end = mod->old->ex_table_end;

- if (mod->size > mod_tmp.size) {
- printk(KERN_ERR "init_module: Size of initialized module "
- "exceeds size of created module.\n");
- goto err2;
- }
+ /* Sanity check the module's dependents */
+ down(&modlist_readlock);
+ for (i = 0, ref = mod->deps; i < mod->ndeps; ++i, ++ref) {
+ struct module *dep = ref->dep;

- /* Make sure all interesting pointers are sane. */
+ /* Make sure the indicated dependencies are really modules. */
+ if (dep == mod) {
+ up(&modlist_readlock);
+ printk(KERN_ERR "init_module: self-referential "
+ "dependency in mod->deps.\n");
+ error = -EINVAL;
+ goto err2;
+ }

- if (!mod_bound(mod->name, namelen, mod)) {
- printk(KERN_ERR "init_module: mod->name out of bounds.\n");
- goto err2;
- }
- if (mod->nsyms && !mod_bound(mod->syms, mod->nsyms, mod)) {
- printk(KERN_ERR "init_module: mod->syms out of bounds.\n");
- goto err2;
- }
- if (mod->ndeps && !mod_bound(mod->deps, mod->ndeps, mod)) {
- printk(KERN_ERR "init_module: mod->deps out of bounds.\n");
- goto err2;
- }
- if (mod->init && !mod_bound(mod->init, 0, mod)) {
- printk(KERN_ERR "init_module: mod->init out of bounds.\n");
- goto err2;
- }
- if (mod->cleanup && !mod_bound(mod->cleanup, 0, mod)) {
- printk(KERN_ERR "init_module: mod->cleanup out of bounds.\n");
- goto err2;
- }
- if (mod->ex_table_start > mod->ex_table_end
- || (mod->ex_table_start &&
- !((unsigned long)mod->ex_table_start >= ((unsigned long)mod + mod->size_of_struct)
- && ((unsigned long)mod->ex_table_end
- < (unsigned long)mod + mod->size)))
- || (((unsigned long)mod->ex_table_start
- - (unsigned long)mod->ex_table_end)
- % sizeof(struct exception_table_entry))) {
- printk(KERN_ERR "init_module: mod->ex_table_* invalid.\n");
- goto err2;
- }
- if (mod->flags & ~MOD_AUTOCLEAN) {
- printk(KERN_ERR "init_module: mod->flags invalid.\n");
- goto err2;
- }
- if (mod_member_present(mod, can_unload)
- && mod->can_unload && !mod_bound(mod->can_unload, 0, mod)) {
- printk(KERN_ERR "init_module: mod->can_unload out of bounds.\n");
- goto err2;
- }
- if (mod_member_present(mod, kallsyms_end)) {
- if (mod->kallsyms_end &&
- (!mod_bound(mod->kallsyms_start, 0, mod) ||
- !mod_bound(mod->kallsyms_end, 0, mod))) {
- printk(KERN_ERR "init_module: mod->kallsyms out of bounds.\n");
- goto err2;
- }
- if (mod->kallsyms_start > mod->kallsyms_end) {
- printk(KERN_ERR "init_module: mod->kallsyms invalid.\n");
- goto err2;
- }
- }
- if (mod_member_present(mod, archdata_end)) {
- if (mod->archdata_end &&
- (!mod_bound(mod->archdata_start, 0, mod) ||
- !mod_bound(mod->archdata_end, 0, mod))) {
- printk(KERN_ERR "init_module: mod->archdata out of bounds.\n");
- goto err2;
- }
- if (mod->archdata_start > mod->archdata_end) {
- printk(KERN_ERR "init_module: mod->archdata invalid.\n");
+ /* Scan the current modules for this dependency */
+ list_for_each(list, &module_list) {
+ if (list_entry(list, struct module, list) == dep)
+ goto found;
+ }
+ up(&modlist_readlock);
+ printk(KERN_ERR "init_module: found dependency that is "
+ "(no longer?) a module.\n");
+ error = -EINVAL;
goto err2;
- }
- }
- if (mod_member_present(mod, kernel_data) && mod->kernel_data) {
- printk(KERN_ERR "init_module: mod->kernel_data must be zero.\n");
- goto err2;
+ found:
+ ;
}

- /* Check that the user isn't doing something silly with the name. */
+ /* Update module references. */
+ for (i = 0, ref = mod->deps; i < mod->ndeps; ++i, ++ref) {
+ struct module *dep = ref->dep;

- if ((n_namelen = get_mod_name(mod->name - (unsigned long)mod
- + (unsigned long)mod_user,
- &n_name)) < 0) {
- printk(KERN_ERR "init_module: get_mod_name failure.\n");
- error = n_namelen;
- goto err2;
- }
- if (namelen != n_namelen || strcmp(n_name, mod_tmp.name) != 0) {
- printk(KERN_ERR "init_module: changed module name to "
- "`%s' from `%s'\n",
- n_name, mod_tmp.name);
- goto err3;
+ ref->ref = mod;
+ ref->next_ref = dep->refs;
+ dep->refs = ref;
}

- /* Ok, that's about all the sanity we can stomach; copy the rest. */
-
- if (copy_from_user((char *)mod+mod_user_size,
- (char *)mod_user+mod_user_size,
- mod->size-mod_user_size)) {
- error = -EFAULT;
- goto err3;
- }
+ error = module_arch_init(mod);
+ if (error)
+ goto err2;

- if (module_arch_init(mod))
- goto err3;
+ up(&modlist_readlock);

/* On some machines it is necessary to do something here
to make the I and D caches consistent. */
flush_icache_range((unsigned long)mod, (unsigned long)mod + mod->size);

- mod->next = mod_tmp.next;
- mod->refs = NULL;
+ set_bit(MOD_LOADED, &mod->flags);

- /* Sanity check the module's dependents */
- for (i = 0, dep = mod->deps; i < mod->ndeps; ++i, ++dep) {
- struct module *o, *d = dep->dep;
-
- /* Make sure the indicated dependencies are really modules. */
- if (d == mod) {
- printk(KERN_ERR "init_module: self-referential "
- "dependency in mod->deps.\n");
- goto err3;
- }
-
- /* Scan the current modules for this dependency */
- for (o = module_list; o != &kernel_module && o != d; o = o->next)
- ;
-
- if (o != d) {
- printk(KERN_ERR "init_module: found dependency that is "
- "(no longer?) a module.\n");
- goto err3;
- }
+ if (!mod->old->init) {
+ error = -EINVAL;
+ goto err2;
}
-
- /* Update module references. */
- for (i = 0, dep = mod->deps; i < mod->ndeps; ++i, ++dep) {
- struct module *d = dep->dep;
+ error = mod->old->init(mod);
+ if (error)
+ goto err2;

- dep->ref = mod;
- dep->next_ref = d->refs;
- d->refs = dep;
- /* Being referenced by a dependent module counts as a
- use as far as kmod is concerned. */
- d->flags |= MOD_USED_ONCE;
- }
+ mod2 = find_module(name);
+ if (!mod2 || !test_bit(MOD_LOCKED, &mod2->flags))
+ BUG();

- /* Free our temporary memory. */
- put_mod_name(n_name);
- put_mod_name(name);
+ if (mod == mod2)
+ set_bit(MOD_OLDSTYLE, &mod->flags);
+ unlock_module(mod);
+ put_module(mod);
+ mod = mod2;

/* Initialize the module. */
- atomic_set(&mod->uc.usecount,1);
- mod->flags |= MOD_INITIALIZING;
- if (mod->init && (error = mod->init()) != 0) {
- atomic_set(&mod->uc.usecount,0);
- mod->flags &= ~MOD_INITIALIZING;
- if (error > 0) /* Buggy module */
- error = -EBUSY;
- goto err0;
+ error = init_module(mod);
+ if (!error) {
+ error = start_module(mod);
+ if (error)
+ exit_module(mod);
}
- atomic_dec(&mod->uc.usecount);

- /* And set it running. */
- mod->flags = (mod->flags | MOD_RUNNING) & ~MOD_INITIALIZING;
- error = 0;
- goto err0;
-
-err3:
- put_mod_name(n_name);
+ if (error && !test_bit(MOD_INITIALIZED, &mod->flags))
+ unregister_module(mod);
err2:
- *mod = mod_tmp;
- strcpy((char *)mod->name, name_tmp); /* We know there is room for this */
+ unlock_module(mod);
err1:
- put_mod_name(name);
+ put_module(mod);
err0:
- unlock_kernel();
- kfree(name_tmp);
+ putname(name);
return error;
}

-static spinlock_t unload_lock = SPIN_LOCK_UNLOCKED;
-int try_inc_mod_count(struct module *mod)
+int init_module_hack(struct module *tmp_mod, struct module *mod)
{
- int res = 1;
- if (mod) {
- spin_lock(&unload_lock);
- if (mod->flags & MOD_DELETED)
- res = 0;
- else
- __MOD_INC_USE_COUNT(mod);
- spin_unlock(&unload_lock);
+ int i;
+
+ down(&modlist_readlock);
+
+ lock_module(mod);
+ if (test_bit(MOD_AUTOCLEAN, &mod->flags))
+ set_bit(MOD_AUTOCLEAN, &mod->flags);
+ set_bit(MOD_LOADED, &mod->flags);
+ atomic_set(&mod->ref_cnt, 1);
+ mod->size = tmp_mod->size;
+ mod->name = tmp_mod->name;
+ mod->syms = tmp_mod->syms;
+ mod->nsyms = tmp_mod->nsyms;
+ mod->deps = tmp_mod->deps;
+ mod->ndeps = tmp_mod->ndeps;
+ mod->ex_table_start = tmp_mod->ex_table_start;
+ mod->ex_table_end = tmp_mod->ex_table_end;
+
+ tmp_mod->old = NULL;
+ tmp_mod->ndeps = 0;
+
+ for (i = 0; i < mod->ndeps; ++i)
+ mod->deps[i].ref = mod;
+
+ spin_lock_irq(&modlist_lock);
+ list_add_tail(&mod->list, &module_list);
+ list_del_init(&tmp_mod->list);
+ spin_unlock_irq(&modlist_lock);
+
+ /* "unregister" temporary module structure */
+ put_module(tmp_mod);
+
+ up(&modlist_readlock);
+
+ return 0;
+}
+
+static int try_unregister_module(struct module *mod)
+{
+ int res = 0;
+
+ if (lock_module(mod))
+ return -EBUSY;
+
+ if (test_bit(MOD_BUILTIN, &mod->flags) ||
+ test_bit(MOD_OLDSTYLE, &mod->flags)) {
+ res = -EBUSY;
+ goto out;
+ }
+
+ if (mod->refs || usecount_module(mod)) {
+ res = -EBUSY;
+ goto out;
+ }
+
+ if (test_bit(MOD_RUNNING, &mod->flags)) {
+ res = stop_module(mod);
+ if (res)
+ goto out;
+ }
+
+ if (test_bit(MOD_INITIALIZED, &mod->flags)) {
+ res = exit_module(mod);
+ if (res) {
+ start_module(mod);
+ goto out;
+ }
}
+
+ unregister_module(mod);
+out:
+ unlock_module(mod);
return res;
}

asmlinkage long
sys_delete_module(const char *name_user)
{
- struct module *mod, *next;
+ struct module *mod;
+ struct list_head *list;
char *name;
long error;
int something_changed;
@@ -602,70 +653,40 @@ sys_delete_module(const char *name_user)
if (!capable(CAP_SYS_MODULE))
return -EPERM;

- lock_kernel();
if (name_user) {
- if ((error = get_mod_name(name_user, &name)) < 0)
- goto out;
- error = -ENOENT;
- if ((mod = find_module(name)) == NULL) {
- put_mod_name(name);
- goto out;
- }
- put_mod_name(name);
- error = -EBUSY;
- if (mod->refs != NULL)
- goto out;
-
- spin_lock(&unload_lock);
- if (!__MOD_IN_USE(mod)) {
- mod->flags |= MOD_DELETED;
- spin_unlock(&unload_lock);
- free_module(mod, 0);
- error = 0;
- } else {
- spin_unlock(&unload_lock);
- }
- goto out;
+ name = getname(name_user);
+ if (IS_ERR(name))
+ return PTR_ERR(name);
+
+ mod = find_module(name);
+ putname(name);
+ if (!mod)
+ return -ENOENT;
+
+ error = try_unregister_module(mod);
+ put_module(mod);
+ if (error)
+ return error;
}

/* Do automatic reaping */
restart:
something_changed = 0;
-
- for (mod = module_list; mod != &kernel_module; mod = next) {
- next = mod->next;
- spin_lock(&unload_lock);
- if (mod->refs == NULL
- && (mod->flags & MOD_AUTOCLEAN)
- && (mod->flags & MOD_RUNNING)
- && !(mod->flags & MOD_DELETED)
- && (mod->flags & MOD_USED_ONCE)
- && !__MOD_IN_USE(mod)) {
- if ((mod->flags & MOD_VISITED)
- && !(mod->flags & MOD_JUST_FREED)) {
- spin_unlock(&unload_lock);
- mod->flags &= ~MOD_VISITED;
- } else {
- mod->flags |= MOD_DELETED;
- spin_unlock(&unload_lock);
- free_module(mod, 1);
- something_changed = 1;
- }
- } else {
- spin_unlock(&unload_lock);
- }
+
+ down(&modlist_readlock);
+ list_for_each(list, &module_list) {
+ mod = list_entry(list, struct module, list);
+ if (!test_bit(MOD_AUTOCLEAN, &mod->flags))
+ continue;
+ if (!try_unregister_module(mod))
+ something_changed = 1;
}
-
+ up(&modlist_readlock);
+
if (something_changed)
goto restart;
-
- for (mod = module_list; mod != &kernel_module; mod = mod->next)
- mod->flags &= ~MOD_JUST_FREED;
-
- error = 0;
-out:
- unlock_kernel();
- return error;
+
+ return 0;
}

/* Query various bits about modules. */
@@ -674,78 +695,66 @@ static int
qm_modules(char *buf, size_t bufsize, size_t *ret)
{
struct module *mod;
+ struct list_head *list;
size_t nmod, space, len;
+ int res = 0;

nmod = space = 0;

- for (mod=module_list; mod != &kernel_module; mod=mod->next, ++nmod) {
+ down(&modlist_readlock);
+ list_for_each(list, &module_list) {
+ mod = list_entry(list, struct module, list);
+ if (test_bit(MOD_BUILTIN, &mod->flags))
+ continue;
+
+ nmod++;
len = strlen(mod->name)+1;
- if (len > bufsize)
- goto calc_space_needed;
- if (copy_to_user(buf, mod->name, len))
- return -EFAULT;
- buf += len;
- bufsize -= len;
+ if (len <= bufsize) {
+ if (copy_to_user(buf, mod->name, len)) {
+ up(&modlist_readlock);
+ return -EFAULT;
+ }
+ buf += len;
+ bufsize -= len;
+ } else {
+ bufsize = 0;
+ res = -ENOSPC;
+ }
space += len;
}
+ up(&modlist_readlock);

- if (put_user(nmod, ret))
- return -EFAULT;
- else
- return 0;
-
-calc_space_needed:
- space += len;
- while ((mod = mod->next) != &kernel_module)
- space += strlen(mod->name)+1;
-
- if (put_user(space, ret))
- return -EFAULT;
- else
- return -ENOSPC;
+ if (put_user(res ? space : nmod, ret))
+ res = -EFAULT;
+ return res;
}

static int
qm_deps(struct module *mod, char *buf, size_t bufsize, size_t *ret)
{
size_t i, space, len;
-
- if (mod == &kernel_module)
- return -EINVAL;
- if (!MOD_CAN_QUERY(mod))
- if (put_user(0, ret))
- return -EFAULT;
- else
- return 0;
+ int res = 0;

space = 0;
for (i = 0; i < mod->ndeps; ++i) {
const char *dep_name = mod->deps[i].dep->name;

len = strlen(dep_name)+1;
- if (len > bufsize)
- goto calc_space_needed;
- if (copy_to_user(buf, dep_name, len))
- return -EFAULT;
- buf += len;
- bufsize -= len;
+ if (len <= bufsize) {
+ if (copy_to_user(buf, dep_name, len))
+ return -EFAULT;
+ buf += len;
+ bufsize -= len;
+ } else {
+ bufsize = 0;
+ res = -ENOSPC;
+ }
space += len;
}
-
- if (put_user(i, ret))
- return -EFAULT;
- else
- return 0;
-
-calc_space_needed:
- space += len;
- while (++i < mod->ndeps)
- space += strlen(mod->deps[i].dep->name)+1;

- if (put_user(space, ret))
- return -EFAULT;
- else
- return -ENOSPC;
+ if (put_user(res ? space : i, ret))
+ res = -EFAULT;
+ return res;
}

static int
@@ -753,43 +762,28 @@ qm_refs(struct module *mod, char *buf, s
{
size_t nrefs, space, len;
struct module_ref *ref;
-
- if (mod == &kernel_module)
- return -EINVAL;
- if (!MOD_CAN_QUERY(mod))
- if (put_user(0, ret))
- return -EFAULT;
- else
- return 0;
+ int res = 0;

space = 0;
for (nrefs = 0, ref = mod->refs; ref ; ++nrefs, ref = ref->next_ref) {
const char *ref_name = ref->ref->name;

len = strlen(ref_name)+1;
- if (len > bufsize)
- goto calc_space_needed;
- if (copy_to_user(buf, ref_name, len))
- return -EFAULT;
- buf += len;
- bufsize -= len;
+ if (len <= bufsize) {
+ if (copy_to_user(buf, ref_name, len))
+ return -EFAULT;
+ buf += len;
+ bufsize -= len;
+ } else {
+ bufsize = 0;
+ res = -ENOSPC;
+ }
space += len;
}
-
- if (put_user(nrefs, ret))
- return -EFAULT;
- else
- return 0;

-calc_space_needed:
- space += len;
- while ((ref = ref->next_ref) != NULL)
- space += strlen(ref->ref->name)+1;
-
- if (put_user(space, ret))
- return -EFAULT;
- else
- return -ENOSPC;
+ if (put_user(res ? space : nrefs, ret))
+ res = -EFAULT;
+ return res;
}

static int
@@ -799,20 +793,15 @@ qm_symbols(struct module *mod, char *buf
struct module_symbol *s;
char *strings;
unsigned long *vals;
-
- if (!MOD_CAN_QUERY(mod))
- if (put_user(0, ret))
- return -EFAULT;
- else
- return 0;
+ int res = 0;

- space = mod->nsyms * 2*sizeof(void *);
+ space = mod->nsyms * 2 * sizeof(void *);

i = len = 0;
s = mod->syms;

if (space > bufsize)
- goto calc_space_needed;
+ bufsize = 0;

if (!access_ok(VERIFY_WRITE, buf, space))
return -EFAULT;
@@ -823,31 +812,24 @@ qm_symbols(struct module *mod, char *buf

for (; i < mod->nsyms ; ++i, ++s, vals += 2) {
len = strlen(s->name)+1;
- if (len > bufsize)
- goto calc_space_needed;
+ if (len <= bufsize) {
+ if (copy_to_user(strings, s->name, len)
+ || __put_user(s->value, vals+0)
+ || __put_user(space, vals+1))
+ return -EFAULT;

- if (copy_to_user(strings, s->name, len)
- || __put_user(s->value, vals+0)
- || __put_user(space, vals+1))
- return -EFAULT;
-
- strings += len;
- bufsize -= len;
+ strings += len;
+ bufsize -= len;
+ } else {
+ bufsize = 0;
+ res = -ENOSPC;
+ }
space += len;
}
- if (put_user(i, ret))
- return -EFAULT;
- else
- return 0;
-
-calc_space_needed:
- for (; i < mod->nsyms; ++i, ++s)
- space += strlen(s->name)+1;

- if (put_user(space, ret))
- return -EFAULT;
- else
- return -ENOSPC;
+ if (put_user(res ? space : i, ret))
+ res = -EFAULT;
+ return res;
}

static int
@@ -860,14 +842,21 @@ qm_info(struct module *mod, char *buf, s

if (sizeof(struct module_info) <= bufsize) {
struct module_info info;
+
info.addr = (unsigned long)mod;
info.size = mod->size;
- info.flags = mod->flags;
-
- /* usecount is one too high here - report appropriately to
- compensate for locking */
- info.usecount = (mod_member_present(mod, can_unload)
- && mod->can_unload ? -1 : atomic_read(&mod->uc.usecount)-1);
+ info.flags = 0;
+ if (test_bit(MOD_RUNNING, &mod->flags))
+ info.flags |= OLD_MOD_RUNNING;
+ else if (!test_bit(MOD_LOADED, &mod->flags))
+ info.flags |= OLD_MOD_DELETED;
+ else
+ info.flags |= OLD_MOD_INITIALIZING;
+ if (test_bit(MOD_AUTOCLEAN, &mod->flags))
+ info.flags |= OLD_MOD_AUTOCLEAN;
+ if (mod->refs || usecount_module(mod))
+ info.flags |= OLD_MOD_USED_ONCE;
+ info.usecount = usecount_module(mod);

if (copy_to_user(buf, &info, sizeof(struct module_info)))
return -EFAULT;
@@ -887,29 +876,22 @@ sys_query_module(const char *name_user,
struct module *mod;
int err;

- lock_kernel();
- if (name_user == NULL)
- mod = &kernel_module;
- else {
- long namelen;
+ if (name_user) {
char *name;

- if ((namelen = get_mod_name(name_user, &name)) < 0) {
- err = namelen;
- goto out;
- }
- err = -ENOENT;
- if ((mod = find_module(name)) == NULL) {
- put_mod_name(name);
- goto out;
- }
- put_mod_name(name);
+ name = getname(name_user);
+ if (IS_ERR(name))
+ return PTR_ERR(name);
+
+ mod = find_module(name);
+ putname(name);
+ if (!mod)
+ return -ENOENT;
+ } else {
+ mod = &kernel_module;
+ get_module(mod);
}

- /* __MOD_ touches the flags. We must avoid that */
-
- atomic_inc(&mod->uc.usecount);
-
switch (which)
{
case 0:
@@ -934,92 +916,47 @@ sys_query_module(const char *name_user,
err = -EINVAL;
break;
}
- atomic_dec(&mod->uc.usecount);
-
-out:
- unlock_kernel();
+ put_module(mod);
+
return err;
}

-/*
- * Copy the kernel symbol table to user space. If the argument is
- * NULL, just return the size of the table.
- *
- * This call is obsolete. New programs should use query_module+QM_SYMBOLS
- * which does not arbitrarily limit the length of symbols.
- */
-
-asmlinkage long
-sys_get_kernel_syms(struct kernel_sym *table)
+int register_module(struct module *mod)
{
- struct module *mod;
- int i;
- struct kernel_sym ksym;
-
- lock_kernel();
- for (mod = module_list, i = 0; mod; mod = mod->next) {
- /* include the count for the module name! */
- i += mod->nsyms + 1;
- }
-
- if (table == NULL)
- goto out;
-
- /* So that we don't give the user our stack content */
- memset (&ksym, 0, sizeof (ksym));
-
- for (mod = module_list, i = 0; mod; mod = mod->next) {
- struct module_symbol *msym;
- unsigned int j;
-
- if (!MOD_CAN_QUERY(mod))
- continue;
-
- /* magic: write module info as a pseudo symbol */
- ksym.value = (unsigned long)mod;
- ksym.name[0] = '#';
- strncpy(ksym.name+1, mod->name, sizeof(ksym.name)-1);
- ksym.name[sizeof(ksym.name)-1] = '\0';
-
- if (copy_to_user(table, &ksym, sizeof(ksym)) != 0)
- goto out;
- ++i, ++table;
+ struct module *mod2;
+ struct list_head *list;
+ int res = -EEXIST;
+
+ //printk("register_module: %p\n", mod);
+ down(&modlist_readlock);
+ list_for_each(list, &module_list) {
+ mod2 = list_entry(list, struct module, list);
+ if (!strcmp(mod2->name, mod->name))
+ goto exists;
+ }
+ lock_module(mod);
+ get_module(mod);
+
+ spin_lock_irq(&modlist_lock);
+ list_add_tail(&mod->list, &module_list);
+ spin_unlock_irq(&modlist_lock);
+
+ res = 0;
+exists:
+ up(&modlist_readlock);

- if (mod->nsyms == 0)
- continue;
-
- for (j = 0, msym = mod->syms; j < mod->nsyms; ++j, ++msym) {
- ksym.value = msym->value;
- strncpy(ksym.name, msym->name, sizeof(ksym.name));
- ksym.name[sizeof(ksym.name)-1] = '\0';
-
- if (copy_to_user(table, &ksym, sizeof(ksym)) != 0)
- goto out;
- ++i, ++table;
- }
- }
-out:
- unlock_kernel();
- return i;
+ return res;
}

-/*
- * Look for a module by name, ignoring modules marked for deletion.
- */
-
-struct module *
-find_module(const char *name)
+void unregister_module(struct module *mod)
{
- struct module *mod;
-
- for (mod = module_list; mod ; mod = mod->next) {
- if (mod->flags & MOD_DELETED)
- continue;
- if (!strcmp(mod->name, name))
- break;
- }
+ if (!(mod->flags & (1 << MOD_LOCKED)))
+ BUG();
+ if (mod->flags & ((1 << MOD_RUNNING) | (1 << MOD_INITIALIZED) | (1 << MOD_BUILTIN)))
+ BUG();

- return mod;
+ //printk("unregister_module: %p\n", mod);
+ put_module(mod);
}

/*
@@ -1027,48 +964,39 @@ find_module(const char *name)
*/

void
-free_module(struct module *mod, int tag_freed)
+free_module(struct module *mod)
{
struct module_ref *dep;
- unsigned i;
- unsigned long flags;
-
- /* Let the module clean up. */
+ int i;

- if (mod->flags & MOD_RUNNING)
- {
- if(mod->cleanup)
- mod->cleanup();
- mod->flags &= ~MOD_RUNNING;
+ //printk("free_module: %p\n", mod);
+ down(&modlist_readlock);
+ if (atomic_read(&mod->ref_cnt)) {
+ up(&modlist_readlock);
+ return;
}

/* Remove the module from the dependency lists. */
-
for (i = 0, dep = mod->deps; i < mod->ndeps; ++i, ++dep) {
struct module_ref **pp;
for (pp = &dep->dep->refs; *pp != dep; pp = &(*pp)->next_ref)
continue;
*pp = dep->next_ref;
- if (tag_freed && dep->dep->refs == NULL)
- dep->dep->flags |= MOD_JUST_FREED;
}

/* And from the main module list. */
-
- spin_lock_irqsave(&modlist_lock, flags);
- if (mod == module_list) {
- module_list = mod->next;
- } else {
- struct module *p;
- for (p = module_list; p->next != mod; p = p->next)
- continue;
- p->next = mod->next;
- }
- spin_unlock_irqrestore(&modlist_lock, flags);
+ spin_lock_irq(&modlist_lock);
+ list_del(&mod->list);
+ spin_unlock_irq(&modlist_lock);
+ up(&modlist_readlock);

/* And free the memory. */
-
- module_unmap(mod);
+ if (test_bit(MOD_ALLOCED, &mod->flags)) {
+ if (mod->old)
+ module_unmap(mod->old);
+ kfree(mod);
+ } else
+ module_unmap(mod->old);
}

/*
@@ -1081,8 +1009,10 @@ int get_module_list(char *p)
struct module *mod;
char tmpstr[64];
struct module_ref *ref;
+ struct list_head *list;

- for (mod = module_list; mod != &kernel_module; mod = mod->next) {
+ down(&modlist_readlock);
+ list_for_each(list, &module_list) {
long len;
const char *q;

@@ -1094,6 +1024,7 @@ int get_module_list(char *p)
} while (0)
#define safe_copy_cstr(str) safe_copy_str(str, sizeof(str)-1)

+ mod = list_entry(list, struct module, list);
len = strlen(mod->name);
safe_copy_str(mod->name, len);

@@ -1108,24 +1039,18 @@ int get_module_list(char *p)
len = sprintf(tmpstr, "%8lu", mod->size);
safe_copy_str(tmpstr, len);

- if (mod->flags & MOD_RUNNING) {
- len = sprintf(tmpstr, "%4ld",
- (mod_member_present(mod, can_unload)
- && mod->can_unload
- ? -1L : (long)atomic_read(&mod->uc.usecount)));
- safe_copy_str(tmpstr, len);
- }
+ len = sprintf(tmpstr, "%4ld",
+ (long)atomic_read(&mod->ref_cnt));
+ safe_copy_str(tmpstr, len);

- if (mod->flags & MOD_DELETED)
- safe_copy_cstr(" (deleted)");
- else if (mod->flags & MOD_RUNNING) {
- if (mod->flags & MOD_AUTOCLEAN)
- safe_copy_cstr(" (autoclean)");
- if (!(mod->flags & MOD_USED_ONCE))
+ if (test_bit(MOD_RUNNING, &mod->flags)) {
+ if (!usecount_module(mod) && !mod->refs)
safe_copy_cstr(" (unused)");
}
- else if (mod->flags & MOD_INITIALIZING)
- safe_copy_cstr(" (initializing)");
+ else if (test_bit(MOD_INITIALIZED, &mod->flags))
+ safe_copy_cstr(" (initialized)");
+ else if (test_bit(MOD_LOADED, &mod->flags))
+ safe_copy_cstr(" (loaded)");
else
safe_copy_cstr(" (uninitialized)");

@@ -1150,6 +1075,7 @@ int get_module_list(char *p)
}

fini:
+ up(&modlist_readlock);
return PAGE_SIZE - left;
}

@@ -1167,20 +1093,23 @@ struct mod_sym {
static void *s_start(struct seq_file *m, loff_t *pos)
{
struct mod_sym *p = kmalloc(sizeof(*p), GFP_KERNEL);
- struct module *v;
+ struct list_head *list;
+ struct module *mod;
loff_t n = *pos;

if (!p)
return ERR_PTR(-ENOMEM);
- lock_kernel();
- for (v = module_list, n = *pos; v; n -= v->nsyms, v = v->next) {
- if (n < v->nsyms) {
- p->mod = v;
+ down(&modlist_readlock);
+ list_for_each(list, &module_list) {
+ mod = list_entry(list, struct module, list);
+ if (n < mod->nsyms) {
+ p->mod = mod;
p->index = n;
return p;
}
+ n -= mod->nsyms;
}
- unlock_kernel();
+ up(&modlist_readlock);
kfree(p);
return NULL;
}
@@ -1188,16 +1117,19 @@ static void *s_start(struct seq_file *m,
static void *s_next(struct seq_file *m, void *p, loff_t *pos)
{
struct mod_sym *v = p;
+ struct module *mod = v->mod;
+
(*pos)++;
- if (++v->index >= v->mod->nsyms) {
+ if (++v->index >= mod->nsyms) {
do {
- v->mod = v->mod->next;
- if (!v->mod) {
- unlock_kernel();
+ if (mod->list.next == &module_list) {
+ up(&modlist_readlock);
kfree(p);
return NULL;
}
- } while (!v->mod->nsyms);
+ mod = list_entry(mod->list.next, struct module, list);
+ } while (!mod->nsyms);
+ v->mod = mod;
v->index = 0;
}
return p;
@@ -1206,7 +1138,7 @@ static void *s_next(struct seq_file *m,
static void s_stop(struct seq_file *m, void *p)
{
if (p && !IS_ERR(p)) {
- unlock_kernel();
+ up(&modlist_readlock);
kfree(p);
}
}
@@ -1216,8 +1148,6 @@ static int s_show(struct seq_file *m, vo
struct mod_sym *v = p;
struct module_symbol *sym;

- if (!MOD_CAN_QUERY(v->mod))
- return 0;
sym = &v->mod->syms[v->index];
if (*v->mod->name)
seq_printf(m, "%0*lx %s\t[%s]\n", (int)(2*sizeof(void*)),
@@ -1267,17 +1197,6 @@ sys_query_module(const char *name_user,
return 0;

return -ENOSYS;
-}
-
-asmlinkage long
-sys_get_kernel_syms(struct kernel_sym *table)
-{
- return -ENOSYS;
-}
-
-int try_inc_mod_count(struct module *mod)
-{
- return 1;
}

#endif /* CONFIG_MODULES */
Index: kernel/user.c
===================================================================
RCS file: /home/other/cvs/linux/linux-2.4/kernel/user.c,v
retrieving revision 1.1.1.3
diff -u -p -r1.1.1.3 user.c
--- kernel/user.c 2001/01/11 12:09:28 1.1.1.3
+++ kernel/user.c 2002/07/24 10:06:20
@@ -8,6 +8,7 @@
* able to have per-user limits for system resources.
*/

+#include <linux/module.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/slab.h>
Index: mm/vmscan.c
===================================================================
RCS file: /home/other/cvs/linux/linux-2.4/mm/vmscan.c,v
retrieving revision 1.1.1.27
diff -u -p -r1.1.1.27 vmscan.c
--- mm/vmscan.c 2002/03/12 13:13:34 1.1.1.27
+++ mm/vmscan.c 2002/07/24 10:06:20
@@ -11,6 +11,7 @@
* Multiqueue VM started 5.8.00, Rik van Riel.
*/

+#include <linux/module.h>
#include <linux/slab.h>
#include <linux/kernel_stat.h>
#include <linux/swap.h>
Index: net/ipv4/af_inet.c
===================================================================
RCS file: /home/other/cvs/linux/linux-2.4/net/ipv4/af_inet.c,v
retrieving revision 1.1.1.12
diff -u -p -r1.1.1.12 af_inet.c
--- net/ipv4/af_inet.c 2002/01/08 13:15:11 1.1.1.12
+++ net/ipv4/af_inet.c 2002/07/24 10:06:20
@@ -64,6 +64,7 @@
*/

#include <linux/config.h>
+#include <linux/module.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/socket.h>


2002-07-25 08:57:09

by Rusty Russell

[permalink] [raw]
Subject: Re: [PATCH][RFC] new module interface

On Wed, 24 Jul 2002 22:02:36 +0200 (CEST)
Roman Zippel <[email protected]> wrote:

> The patch below is for 2.4 but it's easily ported to 2.5, beside of this I
> think the core is stable and will allow a more flexible module handling
> in the future. After updating to 2.5 and updating some more archs I will
> submit the patch officially, so any feedback now would be very welcome.
> (The patch requires no new modutils, although a new version could avoid
> some workarounds, but that can wait.)

Hi Roman!

Firstly, I give up: what kernel is this patch against? It's
hard to read a patch this big which doesn't apply to any kernel I can find 8(

> DEFINE_MODULE
> .start = start_affs_fs,
> .stop = stop_affs_fs,
> .exit = exit_affs_fs,
> .usecount = usecount_affs_fs,
> DEFINE_MODULE_END

Interesting approach. Splitting init and start and stop and exit is
normal, but encapsulating the usecount is different. I made start
and exit return void, though.

Hmmm... you sidestepped the "rmmod -f" problem, by running module->start()
again if module->exit() fails. I decided against this because module
authors have to make sure this works.

I chose the more standard "INIT(init, start)" & "EXIT(stop, exit)" which
makes it easier to drop the exit part if it's built-in.

My favorite part is including the builtin-modules. I assume this means
that "request_module("foo")" returns success if CONFIG_DRIVER_FOO=y now?

Sorry I've been slack in posting my patch: will do tonight I promise 8)

Cheers!
Rusty.
--
there are those who do and those who hang on and you don't see too
many doers quoting their contemporaries. -- Larry McVoy

2002-07-25 09:53:04

by Roman Zippel

[permalink] [raw]
Subject: Re: [PATCH][RFC] new module interface

Hi,

On Thu, 25 Jul 2002, Rusty Russell wrote:

> Firstly, I give up: what kernel is this patch against? It's
> hard to read a patch this big which doesn't apply to any kernel I can find 8(

2.4.18. Maybe pine garbled the patch... Here is a copy of the patch:
http://www.xs4all.nl/~zippel/mod.diff

> Interesting approach. Splitting init and start and stop and exit is
> normal, but encapsulating the usecount is different. I made start
> and exit return void, though.

I introduced usecount() to gain more flexibility, currently one is forced
to pass the module pointer everywhere.
Allowing exit to fail simplifies the interface. Normal code doesn't has
to bother about the current state of the module, instead exit() is now the
synchronization point. This also means the unload_lock via
try_inc_mod_count is not needed anymore.

> Hmmm... you sidestepped the "rmmod -f" problem, by running module->start()
> again if module->exit() fails. I decided against this because module
> authors have to make sure this works.

In the future it rather should work. The problem is that the current
module interface is very limited, later we can introduce a interface to
just stop a module. Right now I just emulate the old behaviour to avoid
having a module in half initailized state.

> I chose the more standard "INIT(init, start)" & "EXIT(stop, exit)" which
> makes it easier to drop the exit part if it's built-in.

I was thinking about it, but couldn't we just put these function in a
seperate section and discard them during init (maybe depending on some
hotplug switch)?

> My favorite part is including the builtin-modules. I assume this means
> that "request_module("foo")" returns success if CONFIG_DRIVER_FOO=y now?

Not yet. The problem is the module name, e.g. ext2 is called
fs_ext2_super, it will need some kbuild changes to get the right module
name.

bye, Roman

2002-07-26 03:44:59

by Rusty Russell

[permalink] [raw]
Subject: Re: [PATCH][RFC] new module interface

In message <Pine.LNX.4.44.0207251121310.28515-100000@serv> you write:
> Hi,
>
> On Thu, 25 Jul 2002, Rusty Russell wrote:
>
> > Firstly, I give up: what kernel is this patch against? It's
> > hard to read a patch this big which doesn't apply to any kernel I can find
8(
>
> 2.4.18. Maybe pine garbled the patch... Here is a copy of the patch:
> http://www.xs4all.nl/~zippel/mod.diff

Much better: thanks!

> > Interesting approach. Splitting init and start and stop and exit is
> > normal, but encapsulating the usecount is different. I made start
> > and exit return void, though.
>
> I introduced usecount() to gain more flexibility, currently one is forced
> to pass the module pointer everywhere.

Well, you substituted the module pointer for an atomic counter. Bit
of a wash, really.

> Allowing exit to fail simplifies the interface. Normal code doesn't has
> to bother about the current state of the module, instead exit() is now the
> synchronization point. This also means the unload_lock via
> try_inc_mod_count is not needed anymore.

Except that rmmod fails rather frequently on busy modules. Which
might be ok.

> > I chose the more standard "INIT(init, start)" & "EXIT(stop, exit)" which
> > makes it easier to drop the exit part if it's built-in.
>
> I was thinking about it, but couldn't we just put these function in a
> seperate section and discard them during init (maybe depending on some
> hotplug switch)?

No, if you drop them newer binutils notices the link problem, hence
the __devexit_p(x) macro.

> > My favorite part is including the builtin-modules. I assume this means
> > that "request_module("foo")" returns success if CONFIG_DRIVER_FOO=y now?
>
> Not yet. The problem is the module name, e.g. ext2 is called
> fs_ext2_super, it will need some kbuild changes to get the right module
> name.

I need that too: the mythical "KBUILD_MODNAME". Both Keith and Kai
promised it to me...

Rusty.
--
Anyone who quotes me in their sig is an idiot. -- Rusty Russell.

2002-07-26 04:19:50

by Keith Owens

[permalink] [raw]
Subject: Re: [PATCH][RFC] new module interface

On Fri, 26 Jul 2002 13:43:39 +1000,
Rusty Russell <[email protected]> wrote:
>I need that too: the mythical "KBUILD_MODNAME". Both Keith and Kai
>promised it to me...

KBUILD_OBJECT has been in kbuild 2.5 since April 5 (kbuild-2.5-core-1).
It is not my fault if Linus won't take it and Kai will not implement
it.

2002-07-26 10:09:07

by Roman Zippel

[permalink] [raw]
Subject: Re: [PATCH][RFC] new module interface

Hi,

On Fri, 26 Jul 2002, Rusty Russell wrote:

> > I introduced usecount() to gain more flexibility, currently one is forced
> > to pass the module pointer everywhere.
>
> Well, you substituted the module pointer for an atomic counter. Bit
> of a wash, really.

Converting the module pointer into a counter is the easiest way to convert
to the new interface. Behind that is a very important concept - complete
seperation of module state management (done in kernel/module.c) and module
usage management (done by the module). Both are independent in my patch,
so the module has complete freedom how to do the later. This means it
doesn't has to use a counter, the usecount function could be as well
something like "return busy ? 1 : 0;" and the module won't be bothered
with unloading. On the other hand if a module needs something more
complex, it can do so without bothering the remaining the module code
(e.g. if I look at the LSM hooks, I'm really not sure how to sanely unload
a module from that).
We get this flexibility only by removing the usecount from the module
structure. As long as that usecount is used for synchronisation, we have
to pass around the module pointer everywhere to get to that damned
usecount.

> > I was thinking about it, but couldn't we just put these function in a
> > seperate section and discard them during init (maybe depending on some
> > hotplug switch)?
>
> No, if you drop them newer binutils notices the link problem, hence
> the __devexit_p(x) macro.

AFAIK we have that problem if we discard the sections immediately at link
time, but we could also discard them at kernel init. On the other hand I'm
not completely against wrapping the field initialization in a macro.

> > Not yet. The problem is the module name, e.g. ext2 is called
> > fs_ext2_super, it will need some kbuild changes to get the right module
> > name.
>
> I need that too: the mythical "KBUILD_MODNAME". Both Keith and Kai
> promised it to me...

I found a solution for that yesterday. :)

I looked at your patch and some interesting parts are missing, so it's
difficult to comment. It's really small, but it also has lots of FIXMEs. :)
My module pointer comment above applies to your patch as well. Since
module unloading isn't implemented yet, it's difficult to say how you want
to avoid the races.
One thing I mentioned earlier is that I still think that module linkage
is better done in user space, if we also keep all the symbol and
dependency information in user space. Insmod just had to relocate the
module and the kernel only needs the pointer to the module structure and
finds the rest through it, so no adding of new sections/symbols or
initialization of the module structure would be required, so insmod hadn'

BTW I also looked at the automatic initcall generation script, I think I
have a solution for a big part of the problem, that would at least allow
to order the init calls of all normal modules. (Patches are coming
hopefully soon. :) )

bye, Roman

2002-07-26 18:12:42

by Kai Germaschewski

[permalink] [raw]
Subject: Re: [PATCH][RFC] new module interface

On Fri, 26 Jul 2002, Rusty Russell wrote:

> > Not yet. The problem is the module name, e.g. ext2 is called
> > fs_ext2_super, it will need some kbuild changes to get the right module
> > name.
>
> I need that too: the mythical "KBUILD_MODNAME". Both Keith and Kai
> promised it to me...

I told you that I have the code, and that I can provide it to you as soon
as you need it. You did never ask for it, though. Do you need it now?

--Kai


2002-07-27 07:00:03

by Rusty Russell

[permalink] [raw]
Subject: Re: [PATCH][RFC] new module interface

In message <Pine.LNX.4.44.0207261109470.28515-100000@serv> you write:
> Hi,
>
> On Fri, 26 Jul 2002, Rusty Russell wrote:
> > Well, you substituted the module pointer for an atomic counter. Bit
> > of a wash, really.
>
> Converting the module pointer into a counter is the easiest way to convert
> to the new interface. Behind that is a very important concept - complete
> seperation of module state management (done in kernel/module.c) and module
> usage management (done by the module). Both are independent in my patch,
> so the module has complete freedom how to do the later. This means it
> doesn't has to use a counter, the usecount function could be as well
> something like "return busy ? 1 : 0;" and the module won't be bothered
> with unloading.

But your added flexibility doesn't actually buy you any more power.
It does mean (as implemented) that even without module support in your
kernel, filesystems have to be reference counted.

> On the other hand if a module needs something more
> complex, it can do so without bothering the remaining the module code
> (e.g. if I look at the LSM hooks, I'm really not sure how to sanely unload
> a module from that).

Exactly. I don't see how it is a definitive win 8(

> > No, if you drop them newer binutils notices the link problem, hence
> > the __devexit_p(x) macro.
>
> AFAIK we have that problem if we discard the sections immediately at link
> time, but we could also discard them at kernel init. On the other hand I'm
> not completely against wrapping the field initialization in a macro.

Yes. I hate the macro wrapping myself though 8(

> > > Not yet. The problem is the module name, e.g. ext2 is called
> > > fs_ext2_super, it will need some kbuild changes to get the right module
> > > name.
> >
> > I need that too: the mythical "KBUILD_MODNAME". Both Keith and Kai
> > promised it to me...
>
> I found a solution for that yesterday. :)


> I looked at your patch and some interesting parts are missing, so it's
> difficult to comment. It's really small, but it also has lots of FIXMEs. :)
> My module pointer comment above applies to your patch as well. Since
> module unloading isn't implemented yet, it's difficult to say how you want
> to avoid the races.

I removed module unloading from the current patch, because of ongoing
debate (and as you know, this is a whole issue by itself 8).

The implementation in the older patches did:

module->waiting = current;
err = module->stop();
if (err)
return err;
synchronize_kernel();
module_put(module);
while (atomic_read(&module->count)) {
set_current_state(TASK_INTERRUPTIBLE);
schedule();
}

synchronize_kernel() waits for every CPU to schedule (with preemption,
it means wait for every preempted thread to voluntarily schedule).
This prevents the "MOD_DEC_USE_COUNT; return" race.

module_put() was a simple:
if (atomic_dec_and_test(&module->count))
wake_up_process(module->waiting);

> One thing I mentioned earlier is that I still think that module linkage
> is better done in user space, if we also keep all the symbol and
> dependency information in user space. Insmod just had to relocate the
> module and the kernel only needs the pointer to the module structure and
> finds the rest through it, so no adding of new sections/symbols or
> initialization of the module structure would be required, so insmod hadn'

I think the in-kernel linker is much neater, and much smaller than the
combined one. One big advantage is that I don't have to do 64-bit
links on a 32-bit userspace.

> BTW I also looked at the automatic initcall generation script, I think I
> have a solution for a big part of the problem, that would at least allow
> to order the init calls of all normal modules. (Patches are coming
> hopefully soon. :) )

Yes. I thought of treating each module as one "unit" for the purposes
of initialization order, and resolving the ordering within each module
as a separate problem, but I ran out of energy, and I'm not convinced
that some core code loops are not fundamental. The problem is that if
there are still loops, they may well depend on .config so some poor
user with a different config can't compile the kernel 8(

And the explicit initcalls are pretty nice, too!

Thanks!
Rusty.
--
Anyone who quotes me in their sig is an idiot. -- Rusty Russell.

2002-07-27 07:00:00

by Rusty Russell

[permalink] [raw]
Subject: Re: [PATCH][RFC] new module interface

In message <[email protected]> you
write:
> On Fri, 26 Jul 2002, Rusty Russell wrote:
>
> > > Not yet. The problem is the module name, e.g. ext2 is called
> > > fs_ext2_super, it will need some kbuild changes to get the right module
> > > name.
> >
> > I need that too: the mythical "KBUILD_MODNAME". Both Keith and Kai
> > promised it to me...
>
> I told you that I have the code, and that I can provide it to you as soon
> as you need it. You did never ask for it, though. Do you need it now?

Sorry for the misunderstanding, I expected it to appear in the build
system one day magically 8)

You can just send me the patch though, and I'll throw it on my page
and make the module replacement patch Depend: on it. I'll also give
it some good testing.

Thanks!
Rusty.
--
Anyone who quotes me in their sig is an idiot. -- Rusty Russell.

2002-07-27 17:07:56

by Kai Germaschewski

[permalink] [raw]
Subject: Re: [PATCH][RFC] new module interface

On Sat, 27 Jul 2002, Rusty Russell wrote:

> You can just send me the patch though, and I'll throw it on my page
> and make the module replacement patch Depend: on it. I'll also give
> it some good testing.

Here we go.

--Kai


===== Rules.make 1.67 vs edited =====
--- 1.67/Rules.make Thu Jun 20 12:50:27 2002
+++ edited/Rules.make Sat Jul 27 12:08:31 2002
@@ -95,11 +95,15 @@
multi-used-y := $(filter-out $(list-multi),$(__multi-used-y))
multi-used-m := $(filter-out $(list-multi),$(__multi-used-m))

+multi-used := $(multi-used-y) $(multi-used-m)
+
# Build list of the parts of our composite objects, our composite
# objects depend on those (obviously)
multi-objs-y := $(foreach m, $(multi-used-y), $($(m:.o=-objs)))
multi-objs-m := $(foreach m, $(multi-used-m), $($(m:.o=-objs)))

+multi-objs := $(multi-objs-y) $(multi-objs-m)
+
# $(subdir-obj-y) is the list of objects in $(obj-y) which do not live
# in the local directory
subdir-obj-y := $(foreach o,$(obj-y),$(if $(filter-out $(o),$(notdir $(o))),$(o)))
@@ -115,6 +119,23 @@
# contain a comma
depfile = $(subst $(comma),_,$(@D)/.$(@F).d)

+# These flags are needed for modversions and compiling, so we define them here
+# already
+# $(modname_flags) #defines KBUILD_OBJECT as the name of the module it will
+# end up in (or would, if it gets compiled in)
+# Note: It's possible that one object gets potentially linked into more
+# than one module. In that case KBUILD_OBJECT will be set to foo_bar,
+# where foo and bar are the name of the modules.
+basename_flags = -DKBUILD_BASENAME=$(subst $(comma),_,$(subst -,_,$(*F)))
+modname_flags = -DKBUILD_OBJECT=$(subst $(space),_,$(strip $(if $(filter $(*F).o,$(multi-objs)),\
+ $(foreach m,$(multi-used),\
+ $(if $(filter $(*F).o,$($(m:.o=-objs))),$(m:.o=))),\
+ $(*F))))
+c_flags = -Wp,-MD,$(depfile) $(CFLAGS) $(NOSTDINC_FLAGS) \
+ $(modkern_cflags) $(EXTRA_CFLAGS) $(CFLAGS_$(*F).o) \
+ $(basename_flags) $(modname_flags) $(export_flags)
+
+
# We're called for one of three purposes:
# o fastdep: build module version files (.ver) for $(export-objs) in
# the current directory
@@ -165,11 +186,6 @@
$(addprefix $(MODVERDIR)/,$(real-objs-m:.o=.ver)): modkern_cflags := $(CFLAGS_MODULE)
$(addprefix $(MODVERDIR)/,$(export-objs:.o=.ver)): export_flags := -D__GENKSYMS__

-c_flags = -Wp,-MD,$(depfile) $(CFLAGS) $(NOSTDINC_FLAGS) \
- $(modkern_cflags) $(EXTRA_CFLAGS) $(CFLAGS_$(*F).o) \
- -DKBUILD_BASENAME=$(subst $(comma),_,$(subst -,_,$(*F))) \
- $(export_flags)
-
# Our objects only depend on modversions.h, not on the individual .ver
# files (fix-dep filters them), so touch modversions.h if any of the .ver
# files changes
@@ -265,11 +281,6 @@
$(export-objs:.o=.i) : export_flags := $(EXPORT_FLAGS)
$(export-objs:.o=.s) : export_flags := $(EXPORT_FLAGS)
$(export-objs:.o=.lst): export_flags := $(EXPORT_FLAGS)
-
-c_flags = -Wp,-MD,$(depfile) $(CFLAGS) $(NOSTDINC_FLAGS) \
- $(modkern_cflags) $(EXTRA_CFLAGS) $(CFLAGS_$(*F).o) \
- -DKBUILD_BASENAME=$(subst $(comma),_,$(subst -,_,$(*F))) \
- $(export_flags)

quiet_cmd_cc_s_c = CC $(echo_target)
cmd_cc_s_c = $(CC) $(c_flags) -S -o $@ $<

2002-07-28 11:54:47

by Roman Zippel

[permalink] [raw]
Subject: Re: [PATCH][RFC] new module interface

Hi,

On Sat, 27 Jul 2002, Rusty Russell wrote:

> > Converting the module pointer into a counter is the easiest way to convert
> > to the new interface. Behind that is a very important concept - complete
> > seperation of module state management (done in kernel/module.c) and module
> > usage management (done by the module). Both are independent in my patch,
> > so the module has complete freedom how to do the later. This means it
> > doesn't has to use a counter, the usecount function could be as well
> > something like "return busy ? 1 : 0;" and the module won't be bothered
> > with unloading.
>
> But your added flexibility doesn't actually buy you any more power.
> It does mean (as implemented) that even without module support in your
> kernel, filesystems have to be reference counted.

Somehow you to keep the filesystem busy, while it's mounted, how else do
you want to do this?

> > On the other hand if a module needs something more
> > complex, it can do so without bothering the remaining the module code
> > (e.g. if I look at the LSM hooks, I'm really not sure how to sanely unload
> > a module from that).
>
> Exactly. I don't see how it is a definitive win 8(

The important point is seperation of concepts and consequently making them
independent. This means I can exchange parts of them without breaking
anything else. Reference counting is simple and portable, whereas
scheduling tricks are not. If LSM needs a synchronize_kernel() to unload,
it's not difficult to add it to my patch, but if someone changes the
scheduler and breaks synchronize_kernel(), it will only break LSM
unloading and not every single module. My module interface is more
resistant to changes in other parts of the kernel.

> The implementation in the older patches did:
>
> module->waiting = current;
> err = module->stop();
> if (err)
> return err;
> synchronize_kernel();
> module_put(module);
> while (atomic_read(&module->count)) {
> set_current_state(TASK_INTERRUPTIBLE);
> schedule();
> }

What is stop() supposed to do? If it just removes interfaces, it still
possible someone starts using the module and you possibly wait forever in
the loop.

> > Insmod just had to relocate the
> > module and the kernel only needs the pointer to the module structure and
> > finds the rest through it, so no adding of new sections/symbols or
> > initialization of the module structure would be required, so insmod hadn'
>
> I think the in-kernel linker is much neater, and much smaller than the
> combined one. One big advantage is that I don't have to do 64-bit
> links on a 32-bit userspace.

Linking a 64-bit module in 32-bit userspace shouldn't be that big
problem? Initializing some kernel structures would be a problem, but I
want to get rid of this too and keep them in userspace. The userspace
linker could be as simple as your kernel linker.

bye, Roman