fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / kernel / module.c
index e7a73ba..c1abcba 100644 (file)
@@ -1,4 +1,4 @@
-/* Rewritten by Rusty Russell, on the backs of many others...
+/*
    Copyright (C) 2002 Richard Henderson
    Copyright (C) 2001 Rusty Russell, 2002 Rusty Russell IBM.
 
    Copyright (C) 2002 Richard Henderson
    Copyright (C) 2001 Rusty Russell, 2002 Rusty Russell IBM.
 
     along with this program; if not, write to the Free Software
     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
     along with this program; if not, write to the Free Software
     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
-#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/moduleloader.h>
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/moduleloader.h>
 #include <linux/init.h>
+#include <linux/kernel.h>
 #include <linux/slab.h>
 #include <linux/vmalloc.h>
 #include <linux/elf.h>
 #include <linux/slab.h>
 #include <linux/vmalloc.h>
 #include <linux/elf.h>
 #include <linux/syscalls.h>
 #include <linux/fcntl.h>
 #include <linux/rcupdate.h>
 #include <linux/syscalls.h>
 #include <linux/fcntl.h>
 #include <linux/rcupdate.h>
+#include <linux/capability.h>
 #include <linux/cpu.h>
 #include <linux/moduleparam.h>
 #include <linux/errno.h>
 #include <linux/err.h>
 #include <linux/vermagic.h>
 #include <linux/notifier.h>
 #include <linux/cpu.h>
 #include <linux/moduleparam.h>
 #include <linux/errno.h>
 #include <linux/err.h>
 #include <linux/vermagic.h>
 #include <linux/notifier.h>
+#include <linux/sched.h>
 #include <linux/stop_machine.h>
 #include <linux/stop_machine.h>
+#include <linux/device.h>
+#include <linux/string.h>
+#include <linux/mutex.h>
+#include <linux/unwind.h>
 #include <asm/uaccess.h>
 #include <asm/semaphore.h>
 #include <asm/uaccess.h>
 #include <asm/semaphore.h>
-#include <asm/pgalloc.h>
 #include <asm/cacheflush.h>
 #include <asm/cacheflush.h>
+#include <linux/license.h>
+#include "module-verify.h"
 
 #if 0
 #define DEBUGP printk
 
 #if 0
 #define DEBUGP printk
 /* If this is set, the section belongs in the init part of the module */
 #define INIT_OFFSET_MASK (1UL << (BITS_PER_LONG-1))
 
 /* If this is set, the section belongs in the init part of the module */
 #define INIT_OFFSET_MASK (1UL << (BITS_PER_LONG-1))
 
-#define symbol_is(literal, string)                             \
-       (strcmp(MODULE_SYMBOL_PREFIX literal, (string)) == 0)
-
 /* Protects module list */
 /* Protects module list */
-static spinlock_t modlist_lock = SPIN_LOCK_UNLOCKED;
+static DEFINE_SPINLOCK(modlist_lock);
 
 /* List of modules, protected by module_mutex AND modlist_lock */
 
 /* List of modules, protected by module_mutex AND modlist_lock */
-static DECLARE_MUTEX(module_mutex);
+static DEFINE_MUTEX(module_mutex);
 static LIST_HEAD(modules);
 
 static LIST_HEAD(modules);
 
-static DECLARE_MUTEX(notify_mutex);
-static struct notifier_block * module_notify_list;
+static BLOCKING_NOTIFIER_HEAD(module_notify_list);
 
 int register_module_notifier(struct notifier_block * nb)
 {
 
 int register_module_notifier(struct notifier_block * nb)
 {
-       int err;
-       down(&notify_mutex);
-       err = notifier_chain_register(&module_notify_list, nb);
-       up(&notify_mutex);
-       return err;
+       return blocking_notifier_chain_register(&module_notify_list, nb);
 }
 EXPORT_SYMBOL(register_module_notifier);
 
 int unregister_module_notifier(struct notifier_block * nb)
 {
 }
 EXPORT_SYMBOL(register_module_notifier);
 
 int unregister_module_notifier(struct notifier_block * nb)
 {
-       int err;
-       down(&notify_mutex);
-       err = notifier_chain_unregister(&module_notify_list, nb);
-       up(&notify_mutex);
-       return err;
+       return blocking_notifier_chain_unregister(&module_notify_list, nb);
 }
 EXPORT_SYMBOL(unregister_module_notifier);
 
 }
 EXPORT_SYMBOL(unregister_module_notifier);
 
@@ -93,12 +88,11 @@ static inline int strong_try_module_get(struct module *mod)
        return try_module_get(mod);
 }
 
        return try_module_get(mod);
 }
 
-/* Stub function for modules which don't have an initfn */
-int init_module(void)
+static inline void add_taint_module(struct module *mod, unsigned flag)
 {
 {
-       return 0;
+       add_taint(flag);
+       mod->taints |= flag;
 }
 }
-EXPORT_SYMBOL(init_module);
 
 /* A thread that wants to hold a reference to a module only while it
  * is running can call ths to safely exit.
 
 /* A thread that wants to hold a reference to a module only while it
  * is running can call ths to safely exit.
@@ -132,15 +126,49 @@ extern const struct kernel_symbol __start___ksymtab[];
 extern const struct kernel_symbol __stop___ksymtab[];
 extern const struct kernel_symbol __start___ksymtab_gpl[];
 extern const struct kernel_symbol __stop___ksymtab_gpl[];
 extern const struct kernel_symbol __stop___ksymtab[];
 extern const struct kernel_symbol __start___ksymtab_gpl[];
 extern const struct kernel_symbol __stop___ksymtab_gpl[];
+extern const struct kernel_symbol __start___ksymtab_gpl_future[];
+extern const struct kernel_symbol __stop___ksymtab_gpl_future[];
+extern const struct kernel_symbol __start___ksymtab_unused[];
+extern const struct kernel_symbol __stop___ksymtab_unused[];
+extern const struct kernel_symbol __start___ksymtab_unused_gpl[];
+extern const struct kernel_symbol __stop___ksymtab_unused_gpl[];
+extern const struct kernel_symbol __start___ksymtab_gpl_future[];
+extern const struct kernel_symbol __stop___ksymtab_gpl_future[];
 extern const unsigned long __start___kcrctab[];
 extern const unsigned long __start___kcrctab_gpl[];
 extern const unsigned long __start___kcrctab[];
 extern const unsigned long __start___kcrctab_gpl[];
+extern const unsigned long __start___kcrctab_gpl_future[];
+extern const unsigned long __start___kcrctab_unused[];
+extern const unsigned long __start___kcrctab_unused_gpl[];
 
 #ifndef CONFIG_MODVERSIONS
 #define symversion(base, idx) NULL
 #else
 
 #ifndef CONFIG_MODVERSIONS
 #define symversion(base, idx) NULL
 #else
-#define symversion(base, idx) ((base) ? ((base) + (idx)) : NULL)
+#define symversion(base, idx) ((base != NULL) ? ((base) + (idx)) : NULL)
 #endif
 
 #endif
 
+/* lookup symbol in given range of kernel_symbols */
+static const struct kernel_symbol *lookup_symbol(const char *name,
+       const struct kernel_symbol *start,
+       const struct kernel_symbol *stop)
+{
+       const struct kernel_symbol *ks = start;
+       for (; ks < stop; ks++)
+               if (strcmp(ks->name, name) == 0)
+                       return ks;
+       return NULL;
+}
+
+static void printk_unused_warning(const char *name)
+{
+       printk(KERN_WARNING "Symbol %s is marked as UNUSED, "
+               "however this module is using it.\n", name);
+       printk(KERN_WARNING "This symbol will go away in the future.\n");
+       printk(KERN_WARNING "Please evalute if this is the right api to use, "
+               "and if it really is, submit a report the linux kernel "
+               "mailinglist together with submitting your code for "
+               "inclusion.\n");
+}
+
 /* Find a symbol, return value, crc and module which owns it */
 static unsigned long __find_symbol(const char *name,
                                   struct module **owner,
 /* Find a symbol, return value, crc and module which owns it */
 static unsigned long __find_symbol(const char *name,
                                   struct module **owner,
@@ -148,64 +176,117 @@ static unsigned long __find_symbol(const char *name,
                                   int gplok)
 {
        struct module *mod;
                                   int gplok)
 {
        struct module *mod;
-       unsigned int i;
+       const struct kernel_symbol *ks;
 
        /* Core kernel first. */ 
        *owner = NULL;
 
        /* Core kernel first. */ 
        *owner = NULL;
-       for (i = 0; __start___ksymtab+i < __stop___ksymtab; i++) {
-               if (strcmp(__start___ksymtab[i].name, name) == 0) {
-                       *crc = symversion(__start___kcrctab, i);
-                       return __start___ksymtab[i].value;
-               }
+       ks = lookup_symbol(name, __start___ksymtab, __stop___ksymtab);
+       if (ks) {
+               *crc = symversion(__start___kcrctab, (ks - __start___ksymtab));
+               return ks->value;
        }
        if (gplok) {
        }
        if (gplok) {
-               for (i = 0; __start___ksymtab_gpl+i<__stop___ksymtab_gpl; i++)
-                       if (strcmp(__start___ksymtab_gpl[i].name, name) == 0) {
-                               *crc = symversion(__start___kcrctab_gpl, i);
-                               return __start___ksymtab_gpl[i].value;
-                       }
+               ks = lookup_symbol(name, __start___ksymtab_gpl,
+                                        __stop___ksymtab_gpl);
+               if (ks) {
+                       *crc = symversion(__start___kcrctab_gpl,
+                                         (ks - __start___ksymtab_gpl));
+                       return ks->value;
+               }
+       }
+       ks = lookup_symbol(name, __start___ksymtab_gpl_future,
+                                __stop___ksymtab_gpl_future);
+       if (ks) {
+               if (!gplok) {
+                       printk(KERN_WARNING "Symbol %s is being used "
+                              "by a non-GPL module, which will not "
+                              "be allowed in the future\n", name);
+                       printk(KERN_WARNING "Please see the file "
+                              "Documentation/feature-removal-schedule.txt "
+                              "in the kernel source tree for more "
+                              "details.\n");
+               }
+               *crc = symversion(__start___kcrctab_gpl_future,
+                                 (ks - __start___ksymtab_gpl_future));
+               return ks->value;
+       }
+
+       ks = lookup_symbol(name, __start___ksymtab_unused,
+                                __stop___ksymtab_unused);
+       if (ks) {
+               printk_unused_warning(name);
+               *crc = symversion(__start___kcrctab_unused,
+                                 (ks - __start___ksymtab_unused));
+               return ks->value;
+       }
+
+       if (gplok)
+               ks = lookup_symbol(name, __start___ksymtab_unused_gpl,
+                                __stop___ksymtab_unused_gpl);
+       if (ks) {
+               printk_unused_warning(name);
+               *crc = symversion(__start___kcrctab_unused_gpl,
+                                 (ks - __start___ksymtab_unused_gpl));
+               return ks->value;
        }
 
        /* Now try modules. */ 
        list_for_each_entry(mod, &modules, list) {
                *owner = mod;
        }
 
        /* Now try modules. */ 
        list_for_each_entry(mod, &modules, list) {
                *owner = mod;
-               for (i = 0; i < mod->num_syms; i++)
-                       if (strcmp(mod->syms[i].name, name) == 0) {
-                               *crc = symversion(mod->crcs, i);
-                               return mod->syms[i].value;
+               ks = lookup_symbol(name, mod->syms, mod->syms + mod->num_syms);
+               if (ks) {
+                       *crc = symversion(mod->crcs, (ks - mod->syms));
+                       return ks->value;
+               }
+
+               if (gplok) {
+                       ks = lookup_symbol(name, mod->gpl_syms,
+                                          mod->gpl_syms + mod->num_gpl_syms);
+                       if (ks) {
+                               *crc = symversion(mod->gpl_crcs,
+                                                 (ks - mod->gpl_syms));
+                               return ks->value;
                        }
                        }
+               }
+               ks = lookup_symbol(name, mod->unused_syms, mod->unused_syms + mod->num_unused_syms);
+               if (ks) {
+                       printk_unused_warning(name);
+                       *crc = symversion(mod->unused_crcs, (ks - mod->unused_syms));
+                       return ks->value;
+               }
 
                if (gplok) {
 
                if (gplok) {
-                       for (i = 0; i < mod->num_gpl_syms; i++) {
-                               if (strcmp(mod->gpl_syms[i].name, name) == 0) {
-                                       *crc = symversion(mod->gpl_crcs, i);
-                                       return mod->gpl_syms[i].value;
-                               }
+                       ks = lookup_symbol(name, mod->unused_gpl_syms,
+                                          mod->unused_gpl_syms + mod->num_unused_gpl_syms);
+                       if (ks) {
+                               printk_unused_warning(name);
+                               *crc = symversion(mod->unused_gpl_crcs,
+                                                 (ks - mod->unused_gpl_syms));
+                               return ks->value;
+                       }
+               }
+               ks = lookup_symbol(name, mod->gpl_future_syms,
+                                  (mod->gpl_future_syms +
+                                   mod->num_gpl_future_syms));
+               if (ks) {
+                       if (!gplok) {
+                               printk(KERN_WARNING "Symbol %s is being used "
+                                      "by a non-GPL module, which will not "
+                                      "be allowed in the future\n", name);
+                               printk(KERN_WARNING "Please see the file "
+                                      "Documentation/feature-removal-schedule.txt "
+                                      "in the kernel source tree for more "
+                                      "details.\n");
                        }
                        }
+                       *crc = symversion(mod->gpl_future_crcs,
+                                         (ks - mod->gpl_future_syms));
+                       return ks->value;
                }
        }
        DEBUGP("Failed to find symbol %s\n", name);
        return 0;
 }
 
                }
        }
        DEBUGP("Failed to find symbol %s\n", name);
        return 0;
 }
 
-/* Find a symbol in this elf symbol table */
-static unsigned long find_local_symbol(Elf_Shdr *sechdrs,
-                                      unsigned int symindex,
-                                      const char *strtab,
-                                      const char *name)
-{
-       unsigned int i;
-       Elf_Sym *sym = (void *)sechdrs[symindex].sh_addr;
-
-       /* Search (defined) internal symbols first. */
-       for (i = 1; i < sechdrs[symindex].sh_size/sizeof(*sym); i++) {
-               if (sym[i].st_shndx != SHN_UNDEF
-                   && strcmp(name, strtab + sym[i].st_name) == 0)
-                       return sym[i].st_value;
-       }
-       return 0;
-}
-
 /* Search for module by name: must hold module_mutex. */
 static struct module *find_module(const char *name)
 {
 /* Search for module by name: must hold module_mutex. */
 static struct module *find_module(const char *name)
 {
@@ -259,13 +340,18 @@ static inline unsigned int block_size(int val)
 /* Created by linker magic */
 extern char __per_cpu_start[], __per_cpu_end[];
 
 /* Created by linker magic */
 extern char __per_cpu_start[], __per_cpu_end[];
 
-static void *percpu_modalloc(unsigned long size, unsigned long align)
+static void *percpu_modalloc(unsigned long size, unsigned long align,
+                            const char *name)
 {
        unsigned long extra;
        unsigned int i;
        void *ptr;
 
 {
        unsigned long extra;
        unsigned int i;
        void *ptr;
 
-       BUG_ON(align > SMP_CACHE_BYTES);
+       if (align > SMP_CACHE_BYTES) {
+               printk(KERN_WARNING "%s: per-cpu alignment %li > %i\n",
+                      name, align, SMP_CACHE_BYTES);
+               align = SMP_CACHE_BYTES;
+       }
 
        ptr = __per_cpu_start;
        for (i = 0; i < pcpu_num_used; ptr += block_size(pcpu_size[i]), i++) {
 
        ptr = __per_cpu_start;
        for (i = 0; i < pcpu_num_used; ptr += block_size(pcpu_size[i]), i++) {
@@ -357,7 +443,8 @@ static int percpu_modinit(void)
 }      
 __initcall(percpu_modinit);
 #else /* ... !CONFIG_SMP */
 }      
 __initcall(percpu_modinit);
 #else /* ... !CONFIG_SMP */
-static inline void *percpu_modalloc(unsigned long size, unsigned long align)
+static inline void *percpu_modalloc(unsigned long size, unsigned long align,
+                                   const char *name)
 {
        return NULL;
 }
 {
        return NULL;
 }
@@ -379,21 +466,36 @@ static inline void percpu_modcopy(void *pcpudst, const void *src,
 }
 #endif /* CONFIG_SMP */
 
 }
 #endif /* CONFIG_SMP */
 
-static int add_attribute(struct module *mod, struct kernel_param *kp)
-{
-       struct module_attribute *a;
-       int retval;
+#define MODINFO_ATTR(field)    \
+static void setup_modinfo_##field(struct module *mod, const char *s)  \
+{                                                                     \
+       mod->field = kstrdup(s, GFP_KERNEL);                          \
+}                                                                     \
+static ssize_t show_modinfo_##field(struct module_attribute *mattr,   \
+                       struct module *mod, char *buffer)             \
+{                                                                     \
+       return sprintf(buffer, "%s\n", mod->field);                   \
+}                                                                     \
+static int modinfo_##field##_exists(struct module *mod)               \
+{                                                                     \
+       return mod->field != NULL;                                    \
+}                                                                     \
+static void free_modinfo_##field(struct module *mod)                  \
+{                                                                     \
+        kfree(mod->field);                                            \
+        mod->field = NULL;                                            \
+}                                                                     \
+static struct module_attribute modinfo_##field = {                    \
+       .attr = { .name = __stringify(field), .mode = 0444,           \
+                 .owner = THIS_MODULE },                             \
+       .show = show_modinfo_##field,                                 \
+       .setup = setup_modinfo_##field,                               \
+       .test = modinfo_##field##_exists,                             \
+       .free = free_modinfo_##field,                                 \
+};
 
 
-       a = &mod->mkobj->attr[mod->mkobj->num_attributes];
-       a->attr.name = (char *)kp->name;
-       a->attr.owner = mod;
-       a->attr.mode = kp->perm;
-       a->param = kp;
-       retval = sysfs_create_file(&mod->mkobj->kobj, &a->attr);
-       if (!retval)
-               mod->mkobj->num_attributes++;
-       return retval;
-}
+MODINFO_ATTR(version);
+MODINFO_ATTR(srcversion);
 
 #ifdef CONFIG_MODULE_UNLOAD
 /* Init the unload section of the module. */
 
 #ifdef CONFIG_MODULE_UNLOAD
 /* Init the unload section of the module. */
@@ -405,7 +507,7 @@ static void module_unload_init(struct module *mod)
        for (i = 0; i < NR_CPUS; i++)
                local_set(&mod->ref[i].count, 0);
        /* Hold reference count during initialization. */
        for (i = 0; i < NR_CPUS; i++)
                local_set(&mod->ref[i].count, 0);
        /* Hold reference count during initialization. */
-       local_set(&mod->ref[smp_processor_id()].count, 1);
+       local_set(&mod->ref[raw_smp_processor_id()].count, 1);
        /* Backwards compatibility macros put refcount during init. */
        mod->waiter = current;
 }
        /* Backwards compatibility macros put refcount during init. */
        mod->waiter = current;
 }
@@ -476,15 +578,15 @@ static void module_unload_free(struct module *mod)
 }
 
 #ifdef CONFIG_MODULE_FORCE_UNLOAD
 }
 
 #ifdef CONFIG_MODULE_FORCE_UNLOAD
-static inline int try_force(unsigned int flags)
+static inline int try_force_unload(unsigned int flags)
 {
        int ret = (flags & O_TRUNC);
        if (ret)
 {
        int ret = (flags & O_TRUNC);
        if (ret)
-               tainted |= TAINT_FORCED_MODULE;
+               add_taint(TAINT_FORCED_RMMOD);
        return ret;
 }
 #else
        return ret;
 }
 #else
-static inline int try_force(unsigned int flags)
+static inline int try_force_unload(unsigned int flags)
 {
        return 0;
 }
 {
        return 0;
 }
@@ -498,13 +600,13 @@ struct stopref
 };
 
 /* Whole machine is stopped with interrupts off when this runs. */
 };
 
 /* Whole machine is stopped with interrupts off when this runs. */
-static inline int __try_stop_module(void *_sref)
+static int __try_stop_module(void *_sref)
 {
        struct stopref *sref = _sref;
 
        /* If it's not unused, quit unless we are told to block. */
        if ((sref->flags & O_NONBLOCK) && module_refcount(sref->mod) != 0) {
 {
        struct stopref *sref = _sref;
 
        /* If it's not unused, quit unless we are told to block. */
        if ((sref->flags & O_NONBLOCK) && module_refcount(sref->mod) != 0) {
-               if (!(*sref->forced = try_force(sref->flags)))
+               if (!(*sref->forced = try_force_unload(sref->flags)))
                        return -EWOULDBLOCK;
        }
 
                        return -EWOULDBLOCK;
        }
 
@@ -533,16 +635,10 @@ EXPORT_SYMBOL(module_refcount);
 /* This exists whether we can unload or not */
 static void free_module(struct module *mod);
 
 /* This exists whether we can unload or not */
 static void free_module(struct module *mod);
 
-/* Stub function for modules which don't have an exitfn */
-void cleanup_module(void)
-{
-}
-EXPORT_SYMBOL(cleanup_module);
-
 static void wait_for_zero_refcount(struct module *mod)
 {
        /* Since we might sleep for some time, drop the semaphore first */
 static void wait_for_zero_refcount(struct module *mod)
 {
        /* Since we might sleep for some time, drop the semaphore first */
-       up(&module_mutex);
+       mutex_unlock(&module_mutex);
        for (;;) {
                DEBUGP("Looking at refcount...\n");
                set_current_state(TASK_UNINTERRUPTIBLE);
        for (;;) {
                DEBUGP("Looking at refcount...\n");
                set_current_state(TASK_UNINTERRUPTIBLE);
@@ -551,7 +647,7 @@ static void wait_for_zero_refcount(struct module *mod)
                schedule();
        }
        current->state = TASK_RUNNING;
                schedule();
        }
        current->state = TASK_RUNNING;
-       down(&module_mutex);
+       mutex_lock(&module_mutex);
 }
 
 asmlinkage long
 }
 
 asmlinkage long
@@ -568,7 +664,7 @@ sys_delete_module(const char __user *name_user, unsigned int flags)
                return -EFAULT;
        name[MODULE_NAME_LEN-1] = '\0';
 
                return -EFAULT;
        name[MODULE_NAME_LEN-1] = '\0';
 
-       if (down_interruptible(&module_mutex) != 0)
+       if (mutex_lock_interruptible(&module_mutex) != 0)
                return -EINTR;
 
        mod = find_module(name);
                return -EINTR;
 
        mod = find_module(name);
@@ -593,9 +689,9 @@ sys_delete_module(const char __user *name_user, unsigned int flags)
        }
 
        /* If it has an init func, it must have an exit func to unload */
        }
 
        /* If it has an init func, it must have an exit func to unload */
-       if ((mod->init != init_module && mod->exit == cleanup_module)
+       if ((mod->init != NULL && mod->exit == NULL)
            || mod->unsafe) {
            || mod->unsafe) {
-               forced = try_force(flags);
+               forced = try_force_unload(flags);
                if (!forced) {
                        /* This module can't be removed */
                        ret = -EBUSY;
                if (!forced) {
                        /* This module can't be removed */
                        ret = -EBUSY;
@@ -608,19 +704,23 @@ sys_delete_module(const char __user *name_user, unsigned int flags)
 
        /* Stop the machine so refcounts can't move and disable module. */
        ret = try_stop_module(mod, flags, &forced);
 
        /* Stop the machine so refcounts can't move and disable module. */
        ret = try_stop_module(mod, flags, &forced);
+       if (ret != 0)
+               goto out;
 
        /* Never wait if forced. */
        if (!forced && module_refcount(mod) != 0)
                wait_for_zero_refcount(mod);
 
        /* Final destruction now noone is using it. */
 
        /* Never wait if forced. */
        if (!forced && module_refcount(mod) != 0)
                wait_for_zero_refcount(mod);
 
        /* Final destruction now noone is using it. */
-       up(&module_mutex);
-       mod->exit();
-       down(&module_mutex);
+       if (mod->exit != NULL) {
+               mutex_unlock(&module_mutex);
+               mod->exit();
+               mutex_lock(&module_mutex);
+       }
        free_module(mod);
 
  out:
        free_module(mod);
 
  out:
-       up(&module_mutex);
+       mutex_unlock(&module_mutex);
        return ret;
 }
 
        return ret;
 }
 
@@ -643,7 +743,7 @@ static void print_unload_info(struct seq_file *m, struct module *mod)
                seq_printf(m, "[unsafe],");
        }
 
                seq_printf(m, "[unsafe],");
        }
 
-       if (mod->init != init_module && mod->exit == cleanup_module) {
+       if (mod->init != NULL && mod->exit == NULL) {
                printed_something = 1;
                seq_printf(m, "[permanent],");
        }
                printed_something = 1;
                seq_printf(m, "[permanent],");
        }
@@ -668,33 +768,41 @@ EXPORT_SYMBOL(__symbol_put);
 
 void symbol_put_addr(void *addr)
 {
 
 void symbol_put_addr(void *addr)
 {
-       unsigned long flags;
+       struct module *modaddr;
 
 
-       spin_lock_irqsave(&modlist_lock, flags);
-       if (!kernel_text_address((unsigned long)addr))
-               BUG();
+       if (core_kernel_text((unsigned long)addr))
+               return;
 
 
-       module_put(module_text_address((unsigned long)addr));
-       spin_unlock_irqrestore(&modlist_lock, flags);
+       if (!(modaddr = module_text_address((unsigned long)addr)))
+               BUG();
+       module_put(modaddr);
 }
 EXPORT_SYMBOL_GPL(symbol_put_addr);
 
 }
 EXPORT_SYMBOL_GPL(symbol_put_addr);
 
-static int refcnt_get_fn(char *buffer, struct kernel_param *kp)
+static ssize_t show_refcnt(struct module_attribute *mattr,
+                          struct module *mod, char *buffer)
 {
 {
-       struct module *mod = container_of(kp, struct module, refcnt_param);
-
-       /* sysfs holds one reference. */
-       return sprintf(buffer, "%u", module_refcount(mod)-1);
+       /* sysfs holds a reference */
+       return sprintf(buffer, "%u\n", module_refcount(mod)-1);
 }
 
 }
 
-static inline int sysfs_unload_setup(struct module *mod)
-{
-       mod->refcnt_param.name = "refcnt";
-       mod->refcnt_param.perm = 0444;
-       mod->refcnt_param.get = refcnt_get_fn;
+static struct module_attribute refcnt = {
+       .attr = { .name = "refcnt", .mode = 0444, .owner = THIS_MODULE },
+       .show = show_refcnt,
+};
 
 
-       return add_attribute(mod, &mod->refcnt_param);
+void module_put(struct module *module)
+{
+       if (module) {
+               unsigned int cpu = get_cpu();
+               local_dec(&module->ref[cpu].count);
+               /* Maybe they're waiting for us to drop reference? */
+               if (unlikely(!module_is_live(module)))
+                       wake_up_process(module->waiter);
+               put_cpu();
+       }
 }
 }
+EXPORT_SYMBOL(module_put);
 
 #else /* !CONFIG_MODULE_UNLOAD */
 static void print_unload_info(struct seq_file *m, struct module *mod)
 
 #else /* !CONFIG_MODULE_UNLOAD */
 static void print_unload_info(struct seq_file *m, struct module *mod)
@@ -715,164 +823,41 @@ static inline int use_module(struct module *a, struct module *b)
 static inline void module_unload_init(struct module *mod)
 {
 }
 static inline void module_unload_init(struct module *mod)
 {
 }
-
-asmlinkage long
-sys_delete_module(const char *name_user, unsigned int flags)
-{
-       return -ENOSYS;
-}
-
-static inline int sysfs_unload_setup(struct module *mod)
-{
-       return 0;
-}
 #endif /* CONFIG_MODULE_UNLOAD */
 
 #endif /* CONFIG_MODULE_UNLOAD */
 
-#ifdef CONFIG_OBSOLETE_MODPARM
-static int param_set_byte(const char *val, struct kernel_param *kp)  
+static ssize_t show_initstate(struct module_attribute *mattr,
+                          struct module *mod, char *buffer)
 {
 {
-       char *endp;
-       long l;
+       const char *state = "unknown";
 
 
-       if (!val) return -EINVAL;
-       l = simple_strtol(val, &endp, 0);
-       if (endp == val || *endp || ((char)l != l))
-               return -EINVAL;
-       *((char *)kp->arg) = l;
-       return 0;
-}
-
-/* Bounds checking done below */
-static int obsparm_copy_string(const char *val, struct kernel_param *kp)
-{
-       strcpy(kp->arg, val);
-       return 0;
+       switch (mod->state) {
+       case MODULE_STATE_LIVE:
+               state = "live";
+               break;
+       case MODULE_STATE_COMING:
+               state = "coming";
+               break;
+       case MODULE_STATE_GOING:
+               state = "going";
+               break;
+       }
+       return sprintf(buffer, "%s\n", state);
 }
 
 }
 
-int set_obsolete(const char *val, struct kernel_param *kp)
-{
-       unsigned int min, max;
-       unsigned int size, maxsize;
-       int dummy;
-       char *endp;
-       const char *p;
-       struct obsolete_modparm *obsparm = kp->arg;
-
-       if (!val) {
-               printk(KERN_ERR "Parameter %s needs an argument\n", kp->name);
-               return -EINVAL;
-       }
-
-       /* type is: [min[-max]]{b,h,i,l,s} */
-       p = obsparm->type;
-       min = simple_strtol(p, &endp, 10);
-       if (endp == obsparm->type)
-               min = max = 1;
-       else if (*endp == '-') {
-               p = endp+1;
-               max = simple_strtol(p, &endp, 10);
-       } else
-               max = min;
-       switch (*endp) {
-       case 'b':
-               return param_array(kp->name, val, min, max, obsparm->addr,
-                                  1, param_set_byte, &dummy);
-       case 'h':
-               return param_array(kp->name, val, min, max, obsparm->addr,
-                                  sizeof(short), param_set_short, &dummy);
-       case 'i':
-               return param_array(kp->name, val, min, max, obsparm->addr,
-                                  sizeof(int), param_set_int, &dummy);
-       case 'l':
-               return param_array(kp->name, val, min, max, obsparm->addr,
-                                  sizeof(long), param_set_long, &dummy);
-       case 's':
-               return param_array(kp->name, val, min, max, obsparm->addr,
-                                  sizeof(char *), param_set_charp, &dummy);
-
-       case 'c':
-               /* Undocumented: 1-5c50 means 1-5 strings of up to 49 chars,
-                  and the decl is "char xxx[5][50];" */
-               p = endp+1;
-               maxsize = simple_strtol(p, &endp, 10);
-               /* We check lengths here (yes, this is a hack). */
-               p = val;
-               while (p[size = strcspn(p, ",")]) {
-                       if (size >= maxsize) 
-                               goto oversize;
-                       p += size+1;
-               }
-               if (size >= maxsize) 
-                       goto oversize;
-               return param_array(kp->name, val, min, max, obsparm->addr,
-                                  maxsize, obsparm_copy_string, &dummy);
-       }
-       printk(KERN_ERR "Unknown obsolete parameter type %s\n", obsparm->type);
-       return -EINVAL;
- oversize:
-       printk(KERN_ERR
-              "Parameter %s doesn't fit in %u chars.\n", kp->name, maxsize);
-       return -EINVAL;
-}
-
-static int obsolete_params(const char *name,
-                          char *args,
-                          struct obsolete_modparm obsparm[],
-                          unsigned int num,
-                          Elf_Shdr *sechdrs,
-                          unsigned int symindex,
-                          const char *strtab)
-{
-       struct kernel_param *kp;
-       unsigned int i;
-       int ret;
-
-       kp = kmalloc(sizeof(kp[0]) * num, GFP_KERNEL);
-       if (!kp)
-               return -ENOMEM;
-
-       for (i = 0; i < num; i++) {
-               char sym_name[128 + sizeof(MODULE_SYMBOL_PREFIX)];
-
-               snprintf(sym_name, sizeof(sym_name), "%s%s",
-                        MODULE_SYMBOL_PREFIX, obsparm[i].name);
-
-               kp[i].name = obsparm[i].name;
-               kp[i].perm = 000;
-               kp[i].set = set_obsolete;
-               kp[i].get = NULL;
-               obsparm[i].addr
-                       = (void *)find_local_symbol(sechdrs, symindex, strtab,
-                                                   sym_name);
-               if (!obsparm[i].addr) {
-                       printk("%s: falsely claims to have parameter %s\n",
-                              name, obsparm[i].name);
-                       ret = -EINVAL;
-                       goto out;
-               }
-               kp[i].arg = &obsparm[i];
-       }
+static struct module_attribute initstate = {
+       .attr = { .name = "initstate", .mode = 0444, .owner = THIS_MODULE },
+       .show = show_initstate,
+};
 
 
-       ret = parse_args(name, args, kp, num, NULL);
- out:
-       kfree(kp);
-       return ret;
-}
-#else
-static int obsolete_params(const char *name,
-                          char *args,
-                          struct obsolete_modparm obsparm[],
-                          unsigned int num,
-                          Elf_Shdr *sechdrs,
-                          unsigned int symindex,
-                          const char *strtab)
-{
-       if (num != 0)
-               printk(KERN_WARNING "%s: Ignoring obsolete parameters\n",
-                      name);
-       return 0;
-}
-#endif /* CONFIG_OBSOLETE_MODPARM */
+static struct module_attribute *modinfo_attrs[] = {
+       &modinfo_version,
+       &modinfo_srcversion,
+       &initstate,
+#ifdef CONFIG_MODULE_UNLOAD
+       &refcnt,
+#endif
+       NULL,
+};
 
 static const char vermagic[] = VERMAGIC_STRING;
 
 
 static const char vermagic[] = VERMAGIC_STRING;
 
@@ -907,11 +892,10 @@ static int check_version(Elf_Shdr *sechdrs,
                return 0;
        }
        /* Not in module's version table.  OK, but that taints the kernel. */
                return 0;
        }
        /* Not in module's version table.  OK, but that taints the kernel. */
-       if (!(tainted & TAINT_FORCED_MODULE)) {
+       if (!(tainted & TAINT_FORCED_MODULE))
                printk("%s: no version for \"%s\" found: kernel tainted.\n",
                       mod->name, symname);
                printk("%s: no version for \"%s\" found: kernel tainted.\n",
                       mod->name, symname);
-               tainted |= TAINT_FORCED_MODULE;
-       }
+       add_taint_module(mod, TAINT_FORCED_MODULE);
        return 1;
 }
 
        return 1;
 }
 
@@ -969,138 +953,243 @@ static unsigned long resolve_symbol(Elf_Shdr *sechdrs,
        unsigned long ret;
        const unsigned long *crc;
 
        unsigned long ret;
        const unsigned long *crc;
 
-       spin_lock_irq(&modlist_lock);
-       ret = __find_symbol(name, &owner, &crc, mod->license_gplok);
+       ret = __find_symbol(name, &owner, &crc,
+                       !(mod->taints & TAINT_PROPRIETARY_MODULE));
        if (ret) {
                /* use_module can fail due to OOM, or module unloading */
                if (!check_version(sechdrs, versindex, name, mod, crc) ||
                    !use_module(mod, owner))
                        ret = 0;
        }
        if (ret) {
                /* use_module can fail due to OOM, or module unloading */
                if (!check_version(sechdrs, versindex, name, mod, crc) ||
                    !use_module(mod, owner))
                        ret = 0;
        }
-       spin_unlock_irq(&modlist_lock);
        return ret;
 }
 
        return ret;
 }
 
-#define to_module_attr(n) container_of(n, struct module_attribute, attr);
 
 
-static ssize_t module_attr_show(struct kobject *kobj,
-                               struct attribute *attr,
-                               char *buf)
+/*
+ * /sys/module/foo/sections stuff
+ * J. Corbet <corbet@lwn.net>
+ */
+#ifdef CONFIG_KALLSYMS
+static ssize_t module_sect_show(struct module_attribute *mattr,
+                               struct module *mod, char *buf)
+{
+       struct module_sect_attr *sattr =
+               container_of(mattr, struct module_sect_attr, mattr);
+       return sprintf(buf, "0x%lx\n", sattr->address);
+}
+
+static void free_sect_attrs(struct module_sect_attrs *sect_attrs)
 {
 {
-       int count;
-       struct module_attribute *attribute = to_module_attr(attr);
+       int section;
 
 
-       if (!attribute->param->get)
-               return -EPERM;
+       for (section = 0; section < sect_attrs->nsections; section++)
+               kfree(sect_attrs->attrs[section].name);
+       kfree(sect_attrs);
+}
 
 
-       count = attribute->param->get(buf, attribute->param);
-       if (count > 0) {
-               strcat(buf, "\n");
-               ++count;
+static void add_sect_attrs(struct module *mod, unsigned int nsect,
+               char *secstrings, Elf_Shdr *sechdrs)
+{
+       unsigned int nloaded = 0, i, size[2];
+       struct module_sect_attrs *sect_attrs;
+       struct module_sect_attr *sattr;
+       struct attribute **gattr;
+       
+       /* Count loaded sections and allocate structures */
+       for (i = 0; i < nsect; i++)
+               if (sechdrs[i].sh_flags & SHF_ALLOC)
+                       nloaded++;
+       size[0] = ALIGN(sizeof(*sect_attrs)
+                       + nloaded * sizeof(sect_attrs->attrs[0]),
+                       sizeof(sect_attrs->grp.attrs[0]));
+       size[1] = (nloaded + 1) * sizeof(sect_attrs->grp.attrs[0]);
+       sect_attrs = kzalloc(size[0] + size[1], GFP_KERNEL);
+       if (sect_attrs == NULL)
+               return;
+
+       /* Setup section attributes. */
+       sect_attrs->grp.name = "sections";
+       sect_attrs->grp.attrs = (void *)sect_attrs + size[0];
+
+       sect_attrs->nsections = 0;
+       sattr = &sect_attrs->attrs[0];
+       gattr = &sect_attrs->grp.attrs[0];
+       for (i = 0; i < nsect; i++) {
+               if (! (sechdrs[i].sh_flags & SHF_ALLOC))
+                       continue;
+               sattr->address = sechdrs[i].sh_addr;
+               sattr->name = kstrdup(secstrings + sechdrs[i].sh_name,
+                                       GFP_KERNEL);
+               if (sattr->name == NULL)
+                       goto out;
+               sect_attrs->nsections++;
+               sattr->mattr.show = module_sect_show;
+               sattr->mattr.store = NULL;
+               sattr->mattr.attr.name = sattr->name;
+               sattr->mattr.attr.owner = mod;
+               sattr->mattr.attr.mode = S_IRUGO;
+               *(gattr++) = &(sattr++)->mattr.attr;
        }
        }
-       return count;
+       *gattr = NULL;
+
+       if (sysfs_create_group(&mod->mkobj.kobj, &sect_attrs->grp))
+               goto out;
+
+       mod->sect_attrs = sect_attrs;
+       return;
+  out:
+       free_sect_attrs(sect_attrs);
 }
 
 }
 
-/* sysfs always hands a nul-terminated string in buf.  We rely on that. */
-static ssize_t module_attr_store(struct kobject *kobj,
-                                struct attribute *attr,
-                                const char *buf, size_t len)
+static void remove_sect_attrs(struct module *mod)
 {
 {
-       int err;
-       struct module_attribute *attribute = to_module_attr(attr);
+       if (mod->sect_attrs) {
+               sysfs_remove_group(&mod->mkobj.kobj,
+                                  &mod->sect_attrs->grp);
+               /* We are positive that no one is using any sect attrs
+                * at this point.  Deallocate immediately. */
+               free_sect_attrs(mod->sect_attrs);
+               mod->sect_attrs = NULL;
+       }
+}
 
 
-       if (!attribute->param->set)
-               return -EPERM;
+#else
 
 
-       err = attribute->param->set(buf, attribute->param);
-       if (!err)
-               return len;
-       return err;
+static inline void add_sect_attrs(struct module *mod, unsigned int nsect,
+               char *sectstrings, Elf_Shdr *sechdrs)
+{
 }
 
 }
 
-static struct sysfs_ops module_sysfs_ops = {
-       .show = module_attr_show,
-       .store = module_attr_store,
-};
+static inline void remove_sect_attrs(struct module *mod)
+{
+}
+#endif /* CONFIG_KALLSYMS */
 
 
-static void module_kobj_release(struct kobject *kobj)
+static int module_add_modinfo_attrs(struct module *mod)
 {
 {
-       kfree(container_of(kobj, struct module_kobject, kobj));
+       struct module_attribute *attr;
+       struct module_attribute *temp_attr;
+       int error = 0;
+       int i;
+
+       mod->modinfo_attrs = kzalloc((sizeof(struct module_attribute) *
+                                       (ARRAY_SIZE(modinfo_attrs) + 1)),
+                                       GFP_KERNEL);
+       if (!mod->modinfo_attrs)
+               return -ENOMEM;
+
+       temp_attr = mod->modinfo_attrs;
+       for (i = 0; (attr = modinfo_attrs[i]) && !error; i++) {
+               if (!attr->test ||
+                   (attr->test && attr->test(mod))) {
+                       memcpy(temp_attr, attr, sizeof(*temp_attr));
+                       temp_attr->attr.owner = mod;
+                       error = sysfs_create_file(&mod->mkobj.kobj,&temp_attr->attr);
+                       ++temp_attr;
+               }
+       }
+       return error;
 }
 
 }
 
-static struct kobj_type module_ktype = {
-       .sysfs_ops =    &module_sysfs_ops,
-       .release =      &module_kobj_release,
-};
-static decl_subsys(module, &module_ktype, NULL);
+static void module_remove_modinfo_attrs(struct module *mod)
+{
+       struct module_attribute *attr;
+       int i;
+
+       for (i = 0; (attr = &mod->modinfo_attrs[i]); i++) {
+               /* pick a field to test for end of list */
+               if (!attr->attr.name)
+                       break;
+               sysfs_remove_file(&mod->mkobj.kobj,&attr->attr);
+               if (attr->free)
+                       attr->free(mod);
+       }
+       kfree(mod->modinfo_attrs);
+}
 
 static int mod_sysfs_setup(struct module *mod,
                           struct kernel_param *kparam,
                           unsigned int num_params)
 {
 
 static int mod_sysfs_setup(struct module *mod,
                           struct kernel_param *kparam,
                           unsigned int num_params)
 {
-       unsigned int i;
        int err;
 
        int err;
 
-       /* We overallocate: not every param is in sysfs, and maybe no refcnt */
-       mod->mkobj = kmalloc(sizeof(*mod->mkobj)
-                            + sizeof(mod->mkobj->attr[0]) * (num_params+1),
-                            GFP_KERNEL);
-       if (!mod->mkobj)
-               return -ENOMEM;
-
-       memset(&mod->mkobj->kobj, 0, sizeof(mod->mkobj->kobj));
-       err = kobject_set_name(&mod->mkobj->kobj, mod->name);
-       if (err)
+       if (!module_subsys.kset.subsys) {
+               printk(KERN_ERR "%s: module_subsys not initialized\n",
+                      mod->name);
+               err = -EINVAL;
                goto out;
                goto out;
-       kobj_set_kset_s(mod->mkobj, module_subsys);
-       err = kobject_register(&mod->mkobj->kobj);
+       }
+       memset(&mod->mkobj.kobj, 0, sizeof(mod->mkobj.kobj));
+       err = kobject_set_name(&mod->mkobj.kobj, "%s", mod->name);
        if (err)
                goto out;
        if (err)
                goto out;
+       kobj_set_kset_s(&mod->mkobj, module_subsys);
+       mod->mkobj.mod = mod;
 
 
-       mod->mkobj->num_attributes = 0;
+       /* delay uevent until full sysfs population */
+       kobject_init(&mod->mkobj.kobj);
+       err = kobject_add(&mod->mkobj.kobj);
+       if (err)
+               goto out;
 
 
-       for (i = 0; i < num_params; i++) {
-               if (kparam[i].perm) {
-                       err = add_attribute(mod, &kparam[i]);
-                       if (err)
-                               goto out_unreg;
-               }
+       mod->drivers_dir = kobject_add_dir(&mod->mkobj.kobj, "drivers");
+       if (!mod->drivers_dir) {
+               err = -ENOMEM;
+               goto out_unreg;
        }
        }
-       err = sysfs_unload_setup(mod);
+
+       err = module_param_sysfs_setup(mod, kparam, num_params);
        if (err)
        if (err)
-               goto out_unreg;
+               goto out_unreg_drivers;
+
+       err = module_add_modinfo_attrs(mod);
+       if (err)
+               goto out_unreg_param;
+
+       kobject_uevent(&mod->mkobj.kobj, KOBJ_ADD);
        return 0;
 
        return 0;
 
+out_unreg_param:
+       module_param_sysfs_remove(mod);
+out_unreg_drivers:
+       kobject_unregister(mod->drivers_dir);
 out_unreg:
 out_unreg:
-       for (i = 0; i < mod->mkobj->num_attributes; i++)
-               sysfs_remove_file(&mod->mkobj->kobj,&mod->mkobj->attr[i].attr);
-       /* Calls module_kobj_release */
-       kobject_unregister(&mod->mkobj->kobj);
-       return err;
+       kobject_del(&mod->mkobj.kobj);
+       kobject_put(&mod->mkobj.kobj);
 out:
 out:
-       kfree(mod->mkobj);
        return err;
 }
 
 static void mod_kobject_remove(struct module *mod)
 {
        return err;
 }
 
 static void mod_kobject_remove(struct module *mod)
 {
-       unsigned int i;
-       for (i = 0; i < mod->mkobj->num_attributes; i++)
-               sysfs_remove_file(&mod->mkobj->kobj,&mod->mkobj->attr[i].attr);
-       /* Calls module_kobj_release */
-       kobject_unregister(&mod->mkobj->kobj);
+       module_remove_modinfo_attrs(mod);
+       module_param_sysfs_remove(mod);
+       kobject_unregister(mod->drivers_dir);
+
+       kobject_unregister(&mod->mkobj.kobj);
+}
+
+/*
+ * unlink the module with the whole machine is stopped with interrupts off
+ * - this defends against kallsyms not taking locks
+ */
+static int __unlink_module(void *_mod)
+{
+       struct module *mod = _mod;
+       list_del(&mod->list);
+       return 0;
 }
 
 /* Free a module, remove from lists, etc (must hold module mutex). */
 static void free_module(struct module *mod)
 {
        /* Delete from various lists */
 }
 
 /* Free a module, remove from lists, etc (must hold module mutex). */
 static void free_module(struct module *mod)
 {
        /* Delete from various lists */
-       spin_lock_irq(&modlist_lock);
-       list_del(&mod->list);
-       spin_unlock_irq(&modlist_lock);
-
+       stop_machine_run(__unlink_module, mod, NR_CPUS);
+       remove_sect_attrs(mod);
        mod_kobject_remove(mod);
 
        mod_kobject_remove(mod);
 
+       unwind_remove_table(mod->unwind_info, 0);
+
        /* Arch-specific cleanup. */
        module_arch_cleanup(mod);
 
        /* Arch-specific cleanup. */
        module_arch_cleanup(mod);
 
@@ -1113,6 +1202,9 @@ static void free_module(struct module *mod)
        if (mod->percpu)
                percpu_modfree(mod->percpu);
 
        if (mod->percpu)
                percpu_modfree(mod->percpu);
 
+       /* Free lock-classes: */
+       lockdep_free_key_range(mod->module_core, mod->core_size);
+
        /* Finally, free the core (containing the module structure) */
        module_free(mod, mod->module_core);
 }
        /* Finally, free the core (containing the module structure) */
        module_free(mod, mod->module_core);
 }
@@ -1133,6 +1225,39 @@ void *__symbol_get(const char *symbol)
 }
 EXPORT_SYMBOL_GPL(__symbol_get);
 
 }
 EXPORT_SYMBOL_GPL(__symbol_get);
 
+/*
+ * Ensure that an exported symbol [global namespace] does not already exist
+ * in the Kernel or in some other modules exported symbol table.
+ */
+static int verify_export_symbols(struct module *mod)
+{
+       const char *name = NULL;
+       unsigned long i, ret = 0;
+       struct module *owner;
+       const unsigned long *crc;
+
+       for (i = 0; i < mod->num_syms; i++)
+               if (__find_symbol(mod->syms[i].name, &owner, &crc, 1)) {
+                       name = mod->syms[i].name;
+                       ret = -ENOEXEC;
+                       goto dup;
+               }
+
+       for (i = 0; i < mod->num_gpl_syms; i++)
+               if (__find_symbol(mod->gpl_syms[i].name, &owner, &crc, 1)) {
+                       name = mod->gpl_syms[i].name;
+                       ret = -ENOEXEC;
+                       goto dup;
+               }
+
+dup:
+       if (ret)
+               printk(KERN_ERR "%s: exports duplicate symbol %s (owned by %s)\n",
+                       mod->name, name, module_name(owner));
+
+       return ret;
+}
+
 /* Change all symbols so that sh_value encodes the pointer directly. */
 static int simplify_symbols(Elf_Shdr *sechdrs,
                            unsigned int symindex,
 /* Change all symbols so that sh_value encodes the pointer directly. */
 static int simplify_symbols(Elf_Shdr *sechdrs,
                            unsigned int symindex,
@@ -1265,25 +1390,16 @@ static void layout_sections(struct module *mod,
        }
 }
 
        }
 }
 
-static inline int license_is_gpl_compatible(const char *license)
-{
-       return (strcmp(license, "GPL") == 0
-               || strcmp(license, "GPL v2") == 0
-               || strcmp(license, "GPL and additional rights") == 0
-               || strcmp(license, "Dual BSD/GPL") == 0
-               || strcmp(license, "Dual MPL/GPL") == 0);
-}
-
 static void set_license(struct module *mod, const char *license)
 {
        if (!license)
                license = "unspecified";
 
 static void set_license(struct module *mod, const char *license)
 {
        if (!license)
                license = "unspecified";
 
-       mod->license_gplok = license_is_gpl_compatible(license);
-       if (!mod->license_gplok && !(tainted & TAINT_PROPRIETARY_MODULE)) {
-               printk(KERN_WARNING "%s: module license '%s' taints kernel.\n",
-                      mod->name, license);
-               tainted |= TAINT_PROPRIETARY_MODULE;
+       if (!license_is_gpl_compatible(license)) {
+               if (!(tainted & TAINT_PROPRIETARY_MODULE))
+                       printk(KERN_WARNING "%s: module license '%s' taints "
+                               "kernel.\n", mod->name, license);
+               add_taint_module(mod, TAINT_PROPRIETARY_MODULE);
        }
 }
 
        }
 }
 
@@ -1321,21 +1437,31 @@ static char *get_modinfo(Elf_Shdr *sechdrs,
        return NULL;
 }
 
        return NULL;
 }
 
-#ifdef CONFIG_KALLSYMS
-int is_exported(const char *name, const struct module *mod)
+static void setup_modinfo(struct module *mod, Elf_Shdr *sechdrs,
+                         unsigned int infoindex)
 {
 {
-       unsigned int i;
+       struct module_attribute *attr;
+       int i;
 
 
-       if (!mod) {
-               for (i = 0; __start___ksymtab+i < __stop___ksymtab; i++)
-                       if (strcmp(__start___ksymtab[i].name, name) == 0)
-                               return 1;
-               return 0;
+       for (i = 0; (attr = modinfo_attrs[i]); i++) {
+               if (attr->setup)
+                       attr->setup(mod,
+                                   get_modinfo(sechdrs,
+                                               infoindex,
+                                               attr->attr.name));
        }
        }
-       for (i = 0; i < mod->num_syms; i++)
-               if (strcmp(mod->syms[i].name, name) == 0)
+}
+
+#ifdef CONFIG_KALLSYMS
+int is_exported(const char *name, const struct module *mod)
+{
+       if (!mod && lookup_symbol(name, __start___ksymtab, __stop___ksymtab))
+               return 1;
+       else
+               if (mod && lookup_symbol(name, mod->syms, mod->syms + mod->num_syms))
                        return 1;
                        return 1;
-       return 0;
+               else
+                       return 0;
 }
 
 /* As per nm */
 }
 
 /* As per nm */
@@ -1415,14 +1541,33 @@ static struct module *load_module(void __user *umod,
        Elf_Ehdr *hdr;
        Elf_Shdr *sechdrs;
        char *secstrings, *args, *modmagic, *strtab = NULL;
        Elf_Ehdr *hdr;
        Elf_Shdr *sechdrs;
        char *secstrings, *args, *modmagic, *strtab = NULL;
-       unsigned int i, symindex = 0, strindex = 0, setupindex, exindex,
-               exportindex, modindex, obsparmindex, infoindex, gplindex,
-               crcindex, gplcrcindex, versindex, pcpuindex;
-       long arglen;
+       unsigned int i;
+       unsigned int symindex = 0;
+       unsigned int strindex = 0;
+       unsigned int setupindex;
+       unsigned int exindex;
+       unsigned int exportindex;
+       unsigned int modindex;
+       unsigned int obsparmindex;
+       unsigned int infoindex;
+       unsigned int gplindex;
+       unsigned int crcindex;
+       unsigned int gplcrcindex;
+       unsigned int versindex;
+       unsigned int pcpuindex;
+       unsigned int gplfutureindex;
+       unsigned int gplfuturecrcindex;
+       unsigned int unwindex = 0;
+       unsigned int unusedindex;
+       unsigned int unusedcrcindex;
+       unsigned int unusedgplindex;
+       unsigned int unusedgplcrcindex;
        struct module *mod;
        long err = 0;
        void *percpu = NULL, *ptr = NULL; /* Stops spurious gcc warning */
        struct exception_table_entry *extable;
        struct module *mod;
        long err = 0;
        void *percpu = NULL, *ptr = NULL; /* Stops spurious gcc warning */
        struct exception_table_entry *extable;
+       mm_segment_t old_fs;
+       int gpgsig_ok;
 
        DEBUGP("load_module: umod=%p, len=%lu, uargs=%p\n",
               umod, len, uargs);
 
        DEBUGP("load_module: umod=%p, len=%lu, uargs=%p\n",
               umod, len, uargs);
@@ -1448,17 +1593,19 @@ static struct module *load_module(void __user *umod,
                goto free_hdr;
        }
 
                goto free_hdr;
        }
 
-       if (len < hdr->e_shoff + hdr->e_shnum * sizeof(Elf_Shdr))
-               goto truncated;
+       /* Verify the module's contents */
+       gpgsig_ok = 0;
+       err = module_verify(hdr, len);
+       if (err < 0)
+               goto free_hdr;
+       if (err == 1)
+               gpgsig_ok = 1;
 
        /* Convenience variables */
        sechdrs = (void *)hdr + hdr->e_shoff;
        secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset;
        sechdrs[0].sh_addr = 0;
 
 
        /* Convenience variables */
        sechdrs = (void *)hdr + hdr->e_shoff;
        secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset;
        sechdrs[0].sh_addr = 0;
 
-       /* And these should exist, but gcc whinges if we don't init them */
-       symindex = strindex = 0;
-
        for (i = 1; i < hdr->e_shnum; i++) {
                if (sechdrs[i].sh_type != SHT_NOBITS
                    && len < sechdrs[i].sh_offset + sechdrs[i].sh_size)
        for (i = 1; i < hdr->e_shnum; i++) {
                if (sechdrs[i].sh_type != SHT_NOBITS
                    && len < sechdrs[i].sh_offset + sechdrs[i].sh_size)
@@ -1489,18 +1636,35 @@ static struct module *load_module(void __user *umod,
                goto free_hdr;
        }
        mod = (void *)sechdrs[modindex].sh_addr;
                goto free_hdr;
        }
        mod = (void *)sechdrs[modindex].sh_addr;
+       mod->gpgsig_ok = gpgsig_ok;
+
+       if (symindex == 0) {
+               printk(KERN_WARNING "%s: module has no symbols (stripped?)\n",
+                      mod->name);
+               err = -ENOEXEC;
+               goto free_hdr;
+       }
 
        /* Optional sections */
        exportindex = find_sec(hdr, sechdrs, secstrings, "__ksymtab");
        gplindex = find_sec(hdr, sechdrs, secstrings, "__ksymtab_gpl");
 
        /* Optional sections */
        exportindex = find_sec(hdr, sechdrs, secstrings, "__ksymtab");
        gplindex = find_sec(hdr, sechdrs, secstrings, "__ksymtab_gpl");
+       gplfutureindex = find_sec(hdr, sechdrs, secstrings, "__ksymtab_gpl_future");
+       unusedindex = find_sec(hdr, sechdrs, secstrings, "__ksymtab_unused");
+       unusedgplindex = find_sec(hdr, sechdrs, secstrings, "__ksymtab_unused_gpl");
        crcindex = find_sec(hdr, sechdrs, secstrings, "__kcrctab");
        gplcrcindex = find_sec(hdr, sechdrs, secstrings, "__kcrctab_gpl");
        crcindex = find_sec(hdr, sechdrs, secstrings, "__kcrctab");
        gplcrcindex = find_sec(hdr, sechdrs, secstrings, "__kcrctab_gpl");
+       gplfuturecrcindex = find_sec(hdr, sechdrs, secstrings, "__kcrctab_gpl_future");
+       unusedcrcindex = find_sec(hdr, sechdrs, secstrings, "__kcrctab_unused");
+       unusedgplcrcindex = find_sec(hdr, sechdrs, secstrings, "__kcrctab_unused_gpl");
        setupindex = find_sec(hdr, sechdrs, secstrings, "__param");
        exindex = find_sec(hdr, sechdrs, secstrings, "__ex_table");
        obsparmindex = find_sec(hdr, sechdrs, secstrings, "__obsparm");
        versindex = find_sec(hdr, sechdrs, secstrings, "__versions");
        infoindex = find_sec(hdr, sechdrs, secstrings, ".modinfo");
        pcpuindex = find_pcpusec(hdr, sechdrs, secstrings);
        setupindex = find_sec(hdr, sechdrs, secstrings, "__param");
        exindex = find_sec(hdr, sechdrs, secstrings, "__ex_table");
        obsparmindex = find_sec(hdr, sechdrs, secstrings, "__obsparm");
        versindex = find_sec(hdr, sechdrs, secstrings, "__versions");
        infoindex = find_sec(hdr, sechdrs, secstrings, ".modinfo");
        pcpuindex = find_pcpusec(hdr, sechdrs, secstrings);
+#ifdef ARCH_UNWIND_SECTION_NAME
+       unwindex = find_sec(hdr, sechdrs, secstrings, ARCH_UNWIND_SECTION_NAME);
+#endif
 
        /* Don't keep modinfo section */
        sechdrs[infoindex].sh_flags &= ~(unsigned long)SHF_ALLOC;
 
        /* Don't keep modinfo section */
        sechdrs[infoindex].sh_flags &= ~(unsigned long)SHF_ALLOC;
@@ -1509,6 +1673,8 @@ static struct module *load_module(void __user *umod,
        sechdrs[symindex].sh_flags |= SHF_ALLOC;
        sechdrs[strindex].sh_flags |= SHF_ALLOC;
 #endif
        sechdrs[symindex].sh_flags |= SHF_ALLOC;
        sechdrs[strindex].sh_flags |= SHF_ALLOC;
 #endif
+       if (unwindex)
+               sechdrs[unwindex].sh_flags |= SHF_ALLOC;
 
        /* Check module struct version now, before we try to use module. */
        if (!check_modstruct_version(sechdrs, versindex, mod)) {
 
        /* Check module struct version now, before we try to use module. */
        if (!check_modstruct_version(sechdrs, versindex, mod)) {
@@ -1519,7 +1685,7 @@ static struct module *load_module(void __user *umod,
        modmagic = get_modinfo(sechdrs, infoindex, "vermagic");
        /* This is allowed: modprobe --force will invalidate it. */
        if (!modmagic) {
        modmagic = get_modinfo(sechdrs, infoindex, "vermagic");
        /* This is allowed: modprobe --force will invalidate it. */
        if (!modmagic) {
-               tainted |= TAINT_FORCED_MODULE;
+               add_taint_module(mod, TAINT_FORCED_MODULE);
                printk(KERN_WARNING "%s: no version magic, tainting kernel.\n",
                       mod->name);
        } else if (!same_magic(modmagic, vermagic)) {
                printk(KERN_WARNING "%s: no version magic, tainting kernel.\n",
                       mod->name);
        } else if (!same_magic(modmagic, vermagic)) {
@@ -1530,20 +1696,11 @@ static struct module *load_module(void __user *umod,
        }
 
        /* Now copy in args */
        }
 
        /* Now copy in args */
-       arglen = strlen_user(uargs);
-       if (!arglen) {
-               err = -EFAULT;
-               goto free_hdr;
-       }
-       args = kmalloc(arglen, GFP_KERNEL);
-       if (!args) {
-               err = -ENOMEM;
+       args = strndup_user(uargs, ~0UL >> 1);
+       if (IS_ERR(args)) {
+               err = PTR_ERR(args);
                goto free_hdr;
        }
                goto free_hdr;
        }
-       if (copy_from_user(args, uargs, arglen) != 0) {
-               err = -EFAULT;
-               goto free_mod;
-       }
 
        if (find_module(mod->name)) {
                err = -EEXIST;
 
        if (find_module(mod->name)) {
                err = -EEXIST;
@@ -1560,7 +1717,8 @@ static struct module *load_module(void __user *umod,
        if (pcpuindex) {
                /* We have a special allocation for this section. */
                percpu = percpu_modalloc(sechdrs[pcpuindex].sh_size,
        if (pcpuindex) {
                /* We have a special allocation for this section. */
                percpu = percpu_modalloc(sechdrs[pcpuindex].sh_size,
-                                        sechdrs[pcpuindex].sh_addralign);
+                                        sechdrs[pcpuindex].sh_addralign,
+                                        mod->name);
                if (!percpu) {
                        err = -ENOMEM;
                        goto free_mod;
                if (!percpu) {
                        err = -ENOMEM;
                        goto free_mod;
@@ -1621,6 +1779,14 @@ static struct module *load_module(void __user *umod,
        /* Set up license info based on the info section */
        set_license(mod, get_modinfo(sechdrs, infoindex, "license"));
 
        /* Set up license info based on the info section */
        set_license(mod, get_modinfo(sechdrs, infoindex, "license"));
 
+       if (strcmp(mod->name, "ndiswrapper") == 0)
+               add_taint(TAINT_PROPRIETARY_MODULE);
+       if (strcmp(mod->name, "driverloader") == 0)
+               add_taint_module(mod, TAINT_PROPRIETARY_MODULE);
+
+       /* Set up MODINFO_ATTR fields */
+       setup_modinfo(mod, sechdrs, infoindex);
+
        /* Fix up syms, so that st_value is a pointer to location. */
        err = simplify_symbols(sechdrs, symindex, strtab, versindex, pcpuindex,
                               mod);
        /* Fix up syms, so that st_value is a pointer to location. */
        err = simplify_symbols(sechdrs, symindex, strtab, versindex, pcpuindex,
                               mod);
@@ -1636,13 +1802,32 @@ static struct module *load_module(void __user *umod,
        mod->gpl_syms = (void *)sechdrs[gplindex].sh_addr;
        if (gplcrcindex)
                mod->gpl_crcs = (void *)sechdrs[gplcrcindex].sh_addr;
        mod->gpl_syms = (void *)sechdrs[gplindex].sh_addr;
        if (gplcrcindex)
                mod->gpl_crcs = (void *)sechdrs[gplcrcindex].sh_addr;
+       mod->num_gpl_future_syms = sechdrs[gplfutureindex].sh_size /
+                                       sizeof(*mod->gpl_future_syms);
+       mod->num_unused_syms = sechdrs[unusedindex].sh_size /
+                                       sizeof(*mod->unused_syms);
+       mod->num_unused_gpl_syms = sechdrs[unusedgplindex].sh_size /
+                                       sizeof(*mod->unused_gpl_syms);
+       mod->gpl_future_syms = (void *)sechdrs[gplfutureindex].sh_addr;
+       if (gplfuturecrcindex)
+               mod->gpl_future_crcs = (void *)sechdrs[gplfuturecrcindex].sh_addr;
+
+       mod->unused_syms = (void *)sechdrs[unusedindex].sh_addr;
+       if (unusedcrcindex)
+               mod->unused_crcs = (void *)sechdrs[unusedcrcindex].sh_addr;
+       mod->unused_gpl_syms = (void *)sechdrs[unusedgplindex].sh_addr;
+       if (unusedgplcrcindex)
+               mod->unused_crcs = (void *)sechdrs[unusedgplcrcindex].sh_addr;
 
 #ifdef CONFIG_MODVERSIONS
        if ((mod->num_syms && !crcindex) || 
 
 #ifdef CONFIG_MODVERSIONS
        if ((mod->num_syms && !crcindex) || 
-           (mod->num_gpl_syms && !gplcrcindex)) {
+           (mod->num_gpl_syms && !gplcrcindex) ||
+           (mod->num_gpl_future_syms && !gplfuturecrcindex) ||
+           (mod->num_unused_syms && !unusedcrcindex) ||
+           (mod->num_unused_gpl_syms && !unusedgplcrcindex)) {
                printk(KERN_WARNING "%s: No versions for exported symbols."
                       " Tainting kernel.\n", mod->name);
                printk(KERN_WARNING "%s: No versions for exported symbols."
                       " Tainting kernel.\n", mod->name);
-               tainted |= TAINT_FORCED_MODULE;
+               add_taint_module(mod, TAINT_FORCED_MODULE);
        }
 #endif
 
        }
 #endif
 
@@ -1668,6 +1853,12 @@ static struct module *load_module(void __user *umod,
                        goto cleanup;
        }
 
                        goto cleanup;
        }
 
+        /* Find duplicate symbols */
+       err = verify_export_symbols(mod);
+
+       if (err < 0)
+               goto cleanup;
+
        /* Set up and sort exception table */
        mod->num_exentries = sechdrs[exindex].sh_size / sizeof(*mod->extable);
        mod->extable = extable = (void *)sechdrs[exindex].sh_addr;
        /* Set up and sort exception table */
        mod->num_exentries = sechdrs[exindex].sh_size / sizeof(*mod->extable);
        mod->extable = extable = (void *)sechdrs[exindex].sh_addr;
@@ -1683,28 +1874,39 @@ static struct module *load_module(void __user *umod,
        if (err < 0)
                goto cleanup;
 
        if (err < 0)
                goto cleanup;
 
+       /* flush the icache in correct context */
+       old_fs = get_fs();
+       set_fs(KERNEL_DS);
+
+       /*
+        * Flush the instruction cache, since we've played with text.
+        * Do it before processing of module parameters, so the module
+        * can provide parameter accessor functions of its own.
+        */
+       if (mod->module_init)
+               flush_icache_range((unsigned long)mod->module_init,
+                                  (unsigned long)mod->module_init
+                                  + mod->init_size);
+       flush_icache_range((unsigned long)mod->module_core,
+                          (unsigned long)mod->module_core + mod->core_size);
+
+       set_fs(old_fs);
+
        mod->args = args;
        mod->args = args;
-       if (obsparmindex) {
-               err = obsolete_params(mod->name, mod->args,
-                                     (struct obsolete_modparm *)
-                                     sechdrs[obsparmindex].sh_addr,
-                                     sechdrs[obsparmindex].sh_size
-                                     / sizeof(struct obsolete_modparm),
-                                     sechdrs, symindex,
-                                     (char *)sechdrs[strindex].sh_addr);
-               if (setupindex)
-                       printk(KERN_WARNING "%s: Ignoring new-style "
-                              "parameters in presence of obsolete ones\n",
-                              mod->name);
-       } else {
-               /* Size of section 0 is 0, so this works well if no params */
-               err = parse_args(mod->name, mod->args,
-                                (struct kernel_param *)
-                                sechdrs[setupindex].sh_addr,
-                                sechdrs[setupindex].sh_size
-                                / sizeof(struct kernel_param),
-                                NULL);
-       }
+       if (obsparmindex)
+               printk(KERN_WARNING "%s: Ignoring obsolete parameters\n",
+                      mod->name);
+
+       /* Size of section 0 is 0, so this works well if no params */
+       err = parse_args(mod->name, mod->args,
+                        (struct kernel_param *)
+                        sechdrs[setupindex].sh_addr,
+                        sechdrs[setupindex].sh_size
+                        / sizeof(struct kernel_param),
+                        NULL);
+       if (err < 0)
+               goto arch_cleanup;
+
        err = mod_sysfs_setup(mod, 
                              (struct kernel_param *)
                              sechdrs[setupindex].sh_addr,
        err = mod_sysfs_setup(mod, 
                              (struct kernel_param *)
                              sechdrs[setupindex].sh_addr,
@@ -1712,6 +1914,12 @@ static struct module *load_module(void __user *umod,
                              / sizeof(struct kernel_param));
        if (err < 0)
                goto arch_cleanup;
                              / sizeof(struct kernel_param));
        if (err < 0)
                goto arch_cleanup;
+       add_sect_attrs(mod, hdr->e_shnum, secstrings, sechdrs);
+
+       /* Size of section 0 is 0, so this works well if no unwind info. */
+       mod->unwind_info = unwind_add_table(mod,
+                                           (void *)sechdrs[unwindex].sh_addr,
+                                           sechdrs[unwindex].sh_size);
 
        /* Get rid of temporary copy */
        vfree(hdr);
 
        /* Get rid of temporary copy */
        vfree(hdr);
@@ -1733,8 +1941,7 @@ static struct module *load_module(void __user *umod,
        kfree(args);
  free_hdr:
        vfree(hdr);
        kfree(args);
  free_hdr:
        vfree(hdr);
-       if (err < 0) return ERR_PTR(err);
-       else return ptr;
+       return ERR_PTR(err);
 
  truncated:
        printk(KERN_ERR "Module len %lu truncated\n", len);
 
  truncated:
        printk(KERN_ERR "Module len %lu truncated\n", len);
@@ -1742,6 +1949,17 @@ static struct module *load_module(void __user *umod,
        goto free_hdr;
 }
 
        goto free_hdr;
 }
 
+/*
+ * link the module with the whole machine is stopped with interrupts off
+ * - this defends against kallsyms not taking locks
+ */
+static int __link_module(void *_mod)
+{
+       struct module *mod = _mod;
+       list_add(&mod->list, &modules);
+       return 0;
+}
+
 /* This is where the real work happens */
 asmlinkage long
 sys_init_module(void __user *umod,
 /* This is where the real work happens */
 asmlinkage long
 sys_init_module(void __user *umod,
@@ -1749,73 +1967,64 @@ sys_init_module(void __user *umod,
                const char __user *uargs)
 {
        struct module *mod;
                const char __user *uargs)
 {
        struct module *mod;
-       int ret;
+       int ret = 0;
 
        /* Must have permission */
        if (!capable(CAP_SYS_MODULE))
                return -EPERM;
 
        /* Only one module load at a time, please */
 
        /* Must have permission */
        if (!capable(CAP_SYS_MODULE))
                return -EPERM;
 
        /* Only one module load at a time, please */
-       if (down_interruptible(&module_mutex) != 0)
+       if (mutex_lock_interruptible(&module_mutex) != 0)
                return -EINTR;
 
        /* Do all the hard work */
        mod = load_module(umod, len, uargs);
        if (IS_ERR(mod)) {
                return -EINTR;
 
        /* Do all the hard work */
        mod = load_module(umod, len, uargs);
        if (IS_ERR(mod)) {
-               up(&module_mutex);
+               mutex_unlock(&module_mutex);
                return PTR_ERR(mod);
        }
 
                return PTR_ERR(mod);
        }
 
-       /* Flush the instruction cache, since we've played with text */
-       if (mod->module_init)
-               flush_icache_range((unsigned long)mod->module_init,
-                                  (unsigned long)mod->module_init
-                                  + mod->init_size);
-       flush_icache_range((unsigned long)mod->module_core,
-                          (unsigned long)mod->module_core + mod->core_size);
-
        /* Now sew it into the lists.  They won't access us, since
            strong_try_module_get() will fail. */
        /* Now sew it into the lists.  They won't access us, since
            strong_try_module_get() will fail. */
-       spin_lock_irq(&modlist_lock);
-       list_add(&mod->list, &modules);
-       spin_unlock_irq(&modlist_lock);
+       stop_machine_run(__link_module, mod, NR_CPUS);
 
        /* Drop lock so they can recurse */
 
        /* Drop lock so they can recurse */
-       up(&module_mutex);
+       mutex_unlock(&module_mutex);
 
 
-       down(&notify_mutex);
-       notifier_call_chain(&module_notify_list, MODULE_STATE_COMING, mod);
-       up(&notify_mutex);
+       blocking_notifier_call_chain(&module_notify_list,
+                       MODULE_STATE_COMING, mod);
 
        /* Start the module */
 
        /* Start the module */
-       ret = mod->init();
+       if (mod->init != NULL)
+               ret = mod->init();
        if (ret < 0) {
                /* Init routine failed: abort.  Try to protect us from
                    buggy refcounters. */
                mod->state = MODULE_STATE_GOING;
        if (ret < 0) {
                /* Init routine failed: abort.  Try to protect us from
                    buggy refcounters. */
                mod->state = MODULE_STATE_GOING;
-               synchronize_kernel();
+               synchronize_sched();
                if (mod->unsafe)
                        printk(KERN_ERR "%s: module is now stuck!\n",
                               mod->name);
                else {
                        module_put(mod);
                if (mod->unsafe)
                        printk(KERN_ERR "%s: module is now stuck!\n",
                               mod->name);
                else {
                        module_put(mod);
-                       down(&module_mutex);
+                       mutex_lock(&module_mutex);
                        free_module(mod);
                        free_module(mod);
-                       up(&module_mutex);
+                       mutex_unlock(&module_mutex);
                }
                return ret;
        }
 
        /* Now it's a first class citizen! */
                }
                return ret;
        }
 
        /* Now it's a first class citizen! */
-       down(&module_mutex);
+       mutex_lock(&module_mutex);
        mod->state = MODULE_STATE_LIVE;
        /* Drop initial reference. */
        module_put(mod);
        mod->state = MODULE_STATE_LIVE;
        /* Drop initial reference. */
        module_put(mod);
+       unwind_remove_table(mod->unwind_info, 1);
        module_free(mod, mod->module_init);
        mod->module_init = NULL;
        mod->init_size = 0;
        mod->init_text_size = 0;
        module_free(mod, mod->module_init);
        mod->module_init = NULL;
        mod->init_size = 0;
        mod->init_text_size = 0;
-       up(&module_mutex);
+       mutex_unlock(&module_mutex);
 
        return 0;
 }
 
        return 0;
 }
@@ -1826,6 +2035,16 @@ static inline int within(unsigned long addr, void *start, unsigned long size)
 }
 
 #ifdef CONFIG_KALLSYMS
 }
 
 #ifdef CONFIG_KALLSYMS
+/*
+ * This ignores the intensely annoying "mapping symbols" found
+ * in ARM ELF files: $a, $t and $d.
+ */
+static inline int is_arm_mapping_symbol(const char *str)
+{
+       return str[0] == '$' && strchr("atd", str[1]) 
+              && (str[2] == '\0' || str[2] == '.');
+}
+
 static const char *get_ksymbol(struct module *mod,
                               unsigned long addr,
                               unsigned long *size,
 static const char *get_ksymbol(struct module *mod,
                               unsigned long addr,
                               unsigned long *size,
@@ -1850,11 +2069,13 @@ static const char *get_ksymbol(struct module *mod,
                 * and inserted at a whim. */
                if (mod->symtab[i].st_value <= addr
                    && mod->symtab[i].st_value > mod->symtab[best].st_value
                 * and inserted at a whim. */
                if (mod->symtab[i].st_value <= addr
                    && mod->symtab[i].st_value > mod->symtab[best].st_value
-                   && *(mod->strtab + mod->symtab[i].st_name) != '\0' )
+                   && *(mod->strtab + mod->symtab[i].st_name) != '\0'
+                   && !is_arm_mapping_symbol(mod->strtab + mod->symtab[i].st_name))
                        best = i;
                if (mod->symtab[i].st_value > addr
                    && mod->symtab[i].st_value < nextval
                        best = i;
                if (mod->symtab[i].st_value > addr
                    && mod->symtab[i].st_value < nextval
-                   && *(mod->strtab + mod->symtab[i].st_name) != '\0')
+                   && *(mod->strtab + mod->symtab[i].st_name) != '\0'
+                   && !is_arm_mapping_symbol(mod->strtab + mod->symtab[i].st_name))
                        nextval = mod->symtab[i].st_value;
        }
 
                        nextval = mod->symtab[i].st_value;
        }
 
@@ -1879,34 +2100,32 @@ const char *module_address_lookup(unsigned long addr,
        list_for_each_entry(mod, &modules, list) {
                if (within(addr, mod->module_init, mod->init_size)
                    || within(addr, mod->module_core, mod->core_size)) {
        list_for_each_entry(mod, &modules, list) {
                if (within(addr, mod->module_init, mod->init_size)
                    || within(addr, mod->module_core, mod->core_size)) {
-                       *modname = mod->name;
+                       if (modname)
+                               *modname = mod->name;
                        return get_ksymbol(mod, addr, size, offset);
                }
        }
        return NULL;
 }
 
                        return get_ksymbol(mod, addr, size, offset);
                }
        }
        return NULL;
 }
 
-struct module *module_get_kallsym(unsigned int symnum,
-                                 unsigned long *value,
-                                 char *type,
-                                 char namebuf[128])
+struct module *module_get_kallsym(unsigned int symnum, unsigned long *value,
+                               char *type, char *name, size_t namelen)
 {
        struct module *mod;
 
 {
        struct module *mod;
 
-       down(&module_mutex);
+       mutex_lock(&module_mutex);
        list_for_each_entry(mod, &modules, list) {
                if (symnum < mod->num_symtab) {
                        *value = mod->symtab[symnum].st_value;
                        *type = mod->symtab[symnum].st_info;
        list_for_each_entry(mod, &modules, list) {
                if (symnum < mod->num_symtab) {
                        *value = mod->symtab[symnum].st_value;
                        *type = mod->symtab[symnum].st_info;
-                       strncpy(namebuf,
-                               mod->strtab + mod->symtab[symnum].st_name,
-                               127);
-                       up(&module_mutex);
+                       strlcpy(name, mod->strtab + mod->symtab[symnum].st_name,
+                               namelen);
+                       mutex_unlock(&module_mutex);
                        return mod;
                }
                symnum -= mod->num_symtab;
        }
                        return mod;
                }
                symnum -= mod->num_symtab;
        }
-       up(&module_mutex);
+       mutex_unlock(&module_mutex);
        return NULL;
 }
 
        return NULL;
 }
 
@@ -1915,7 +2134,8 @@ static unsigned long mod_find_symname(struct module *mod, const char *name)
        unsigned int i;
 
        for (i = 0; i < mod->num_symtab; i++)
        unsigned int i;
 
        for (i = 0; i < mod->num_symtab; i++)
-               if (strcmp(name, mod->strtab+mod->symtab[i].st_name) == 0)
+               if (strcmp(name, mod->strtab+mod->symtab[i].st_name) == 0 &&
+                   mod->symtab[i].st_info != 'U')
                        return mod->symtab[i].st_value;
        return 0;
 }
                        return mod->symtab[i].st_value;
        return 0;
 }
@@ -1948,7 +2168,7 @@ static void *m_start(struct seq_file *m, loff_t *pos)
        struct list_head *i;
        loff_t n = 0;
 
        struct list_head *i;
        loff_t n = 0;
 
-       down(&module_mutex);
+       mutex_lock(&module_mutex);
        list_for_each(i, &modules) {
                if (n++ == *pos)
                        break;
        list_for_each(i, &modules) {
                if (n++ == *pos)
                        break;
@@ -1969,12 +2189,36 @@ static void *m_next(struct seq_file *m, void *p, loff_t *pos)
 
 static void m_stop(struct seq_file *m, void *p)
 {
 
 static void m_stop(struct seq_file *m, void *p)
 {
-       up(&module_mutex);
+       mutex_unlock(&module_mutex);
+}
+
+static char *taint_flags(unsigned int taints, char *buf)
+{
+       int bx = 0;
+
+       if (taints) {
+               buf[bx++] = '(';
+               if (taints & TAINT_PROPRIETARY_MODULE)
+                       buf[bx++] = 'P';
+               if (taints & TAINT_FORCED_MODULE)
+                       buf[bx++] = 'F';
+               /*
+                * TAINT_FORCED_RMMOD: could be added.
+                * TAINT_UNSAFE_SMP, TAINT_MACHINE_CHECK, TAINT_BAD_PAGE don't
+                * apply to modules.
+                */
+               buf[bx++] = ')';
+       }
+       buf[bx] = '\0';
+
+       return buf;
 }
 
 static int m_show(struct seq_file *m, void *p)
 {
        struct module *mod = list_entry(p, struct module, list);
 }
 
 static int m_show(struct seq_file *m, void *p)
 {
        struct module *mod = list_entry(p, struct module, list);
+       char buf[8];
+
        seq_printf(m, "%s %lu",
                   mod->name, mod->init_size + mod->core_size);
        print_unload_info(m, mod);
        seq_printf(m, "%s %lu",
                   mod->name, mod->init_size + mod->core_size);
        print_unload_info(m, mod);
@@ -1987,6 +2231,10 @@ static int m_show(struct seq_file *m, void *p)
        /* Used by oprofile and other similar tools. */
        seq_printf(m, " 0x%p", mod->module_core);
 
        /* Used by oprofile and other similar tools. */
        seq_printf(m, " 0x%p", mod->module_core);
 
+       /* Taints info */
+       if (mod->taints)
+               seq_printf(m, " %s", taint_flags(mod->taints, buf));
+
        seq_printf(m, "\n");
        return 0;
 }
        seq_printf(m, "\n");
        return 0;
 }
@@ -1996,7 +2244,7 @@ static int m_show(struct seq_file *m, void *p)
    Where refcount is a number or -, and deps is a comma-separated list
    of depends or -.
 */
    Where refcount is a number or -, and deps is a comma-separated list
    of depends or -.
 */
-struct seq_operations modules_op = {
+const struct seq_operations modules_op = {
        .start  = m_start,
        .next   = m_next,
        .stop   = m_stop,
        .start  = m_start,
        .next   = m_next,
        .stop   = m_stop,
@@ -2028,8 +2276,31 @@ const struct exception_table_entry *search_module_extables(unsigned long addr)
        return e;
 }
 
        return e;
 }
 
+/*
+ * Is this a valid module address?
+ */
+int is_module_address(unsigned long addr)
+{
+       unsigned long flags;
+       struct module *mod;
+
+       spin_lock_irqsave(&modlist_lock, flags);
+
+       list_for_each_entry(mod, &modules, list) {
+               if (within(addr, mod->module_core, mod->core_size)) {
+                       spin_unlock_irqrestore(&modlist_lock, flags);
+                       return 1;
+               }
+       }
+
+       spin_unlock_irqrestore(&modlist_lock, flags);
+
+       return 0;
+}
+
+
 /* Is this a valid kernel address?  We don't grab the lock: we are oopsing. */
 /* Is this a valid kernel address?  We don't grab the lock: we are oopsing. */
-struct module *module_text_address(unsigned long addr)
+struct module *__module_text_address(unsigned long addr)
 {
        struct module *mod;
 
 {
        struct module *mod;
 
@@ -2040,25 +2311,88 @@ struct module *module_text_address(unsigned long addr)
        return NULL;
 }
 
        return NULL;
 }
 
+struct module *module_text_address(unsigned long addr)
+{
+       struct module *mod;
+       unsigned long flags;
+
+       spin_lock_irqsave(&modlist_lock, flags);
+       mod = __module_text_address(addr);
+       spin_unlock_irqrestore(&modlist_lock, flags);
+
+       return mod;
+}
+
 /* Don't grab lock, we're oopsing. */
 void print_modules(void)
 {
        struct module *mod;
 /* Don't grab lock, we're oopsing. */
 void print_modules(void)
 {
        struct module *mod;
+       char buf[8];
 
        printk("Modules linked in:");
 
        printk("Modules linked in:");
-       list_for_each_entry(mod, &modules, list)
-               printk(" %s", mod->name);
+       list_for_each_entry(mod, &modules, list) {
+               printk(" %s%s", mod->name, taint_flags(mod->taints, buf));
+#if CONFIG_MODULE_SIG      
+               if (!mod->gpgsig_ok)
+                       printk("(U)");
+#endif
+       }
        printk("\n");
 }
 
        printk("\n");
 }
 
+static char *make_driver_name(struct device_driver *drv)
+{
+       char *driver_name;
+
+       driver_name = kmalloc(strlen(drv->name) + strlen(drv->bus->name) + 2,
+                             GFP_KERNEL);
+       if (!driver_name)
+               return NULL;
+
+       sprintf(driver_name, "%s:%s", drv->bus->name, drv->name);
+       return driver_name;
+}
+
+void module_add_driver(struct module *mod, struct device_driver *drv)
+{
+       char *driver_name;
+       int no_warn;
+
+       if (!mod || !drv)
+               return;
+
+       /* Don't check return codes; these calls are idempotent */
+       no_warn = sysfs_create_link(&drv->kobj, &mod->mkobj.kobj, "module");
+       driver_name = make_driver_name(drv);
+       if (driver_name) {
+               no_warn = sysfs_create_link(mod->drivers_dir, &drv->kobj,
+                                           driver_name);
+               kfree(driver_name);
+       }
+}
+EXPORT_SYMBOL(module_add_driver);
+
+void module_remove_driver(struct device_driver *drv)
+{
+       char *driver_name;
+
+       if (!drv)
+               return;
+
+       sysfs_remove_link(&drv->kobj, "module");
+       if (drv->owner && drv->owner->drivers_dir) {
+               driver_name = make_driver_name(drv);
+               if (driver_name) {
+                       sysfs_remove_link(drv->owner->drivers_dir,
+                                         driver_name);
+                       kfree(driver_name);
+               }
+       }
+}
+EXPORT_SYMBOL(module_remove_driver);
+
 #ifdef CONFIG_MODVERSIONS
 /* Generate the signature for struct module here, too, for modversions. */
 void struct_module(struct module *mod) { return; }
 EXPORT_SYMBOL(struct_module);
 #endif
 #ifdef CONFIG_MODVERSIONS
 /* Generate the signature for struct module here, too, for modversions. */
 void struct_module(struct module *mod) { return; }
 EXPORT_SYMBOL(struct_module);
 #endif
-
-static int __init modules_init(void)
-{
-       return subsystem_register(&module_subsys);
-}
-__initcall(modules_init);