]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - kernel/module.c
ext4: add checksum calculation when clearing UNINIT flag in ext4_new_inode
[linux-2.6-omap-h63xx.git] / kernel / module.c
index 3d256681ab64364f5dcf25d782d5e00f40f12b14..1f4cc00e0c200b7c69272694d121558fd6a10d06 100644 (file)
 #include <linux/moduleloader.h>
 #include <linux/init.h>
 #include <linux/kallsyms.h>
+#include <linux/fs.h>
 #include <linux/sysfs.h>
 #include <linux/kernel.h>
 #include <linux/slab.h>
 #include <linux/vmalloc.h>
 #include <linux/elf.h>
+#include <linux/proc_fs.h>
 #include <linux/seq_file.h>
 #include <linux/syscalls.h>
 #include <linux/fcntl.h>
@@ -42,6 +44,7 @@
 #include <linux/string.h>
 #include <linux/mutex.h>
 #include <linux/unwind.h>
+#include <linux/rculist.h>
 #include <asm/uaccess.h>
 #include <asm/cacheflush.h>
 #include <linux/license.h>
@@ -63,7 +66,7 @@
 #define INIT_OFFSET_MASK (1UL << (BITS_PER_LONG-1))
 
 /* List of modules, protected by module_mutex or preempt_disable
- * (add/delete uses stop_machine). */
+ * (delete uses stop_machine/add uses RCU list operations). */
 static DEFINE_MUTEX(module_mutex);
 static LIST_HEAD(modules);
 
@@ -241,7 +244,7 @@ static bool each_symbol(bool (*fn)(const struct symsearch *arr,
        if (each_symbol_in_section(arr, ARRAY_SIZE(arr), NULL, fn, data))
                return true;
 
-       list_for_each_entry(mod, &modules, list) {
+       list_for_each_entry_rcu(mod, &modules, list) {
                struct symsearch arr[] = {
                        { mod->syms, mod->syms + mod->num_syms, mod->crcs,
                          NOT_GPL_ONLY, false },
@@ -1416,17 +1419,6 @@ static void mod_kobject_remove(struct module *mod)
        mod_sysfs_fini(mod);
 }
 
-/*
- * 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;
-}
-
 /*
  * unlink the module with the whole machine is stopped with interrupts off
  * - this defends against kallsyms not taking locks
@@ -2239,9 +2231,13 @@ static noinline struct module *load_module(void __user *umod,
                       mod->name);
 
        /* Now sew it into the lists so we can get lockdep and oops
-         * info during argument parsing.  Noone should access us, since
-         * strong_try_module_get() will fail. */
-       stop_machine(__link_module, mod, NULL);
+        * info during argument parsing.  Noone should access us, since
+        * strong_try_module_get() will fail.
+        * lockdep/oops can run asynchronous, so use the RCU list insertion
+        * function to insert in a way safe to concurrent readers.
+        * The mutex protects against concurrent writers.
+        */
+       list_add_rcu(&mod->list, &modules);
 
        err = parse_args(mod->name, mod->args, kp, num_kp, NULL);
        if (err < 0)
@@ -2436,7 +2432,7 @@ const char *module_address_lookup(unsigned long addr,
        const char *ret = NULL;
 
        preempt_disable();
-       list_for_each_entry(mod, &modules, list) {
+       list_for_each_entry_rcu(mod, &modules, list) {
                if (within(addr, mod->module_init, mod->init_size)
                    || within(addr, mod->module_core, mod->core_size)) {
                        if (modname)
@@ -2459,7 +2455,7 @@ int lookup_module_symbol_name(unsigned long addr, char *symname)
        struct module *mod;
 
        preempt_disable();
-       list_for_each_entry(mod, &modules, list) {
+       list_for_each_entry_rcu(mod, &modules, list) {
                if (within(addr, mod->module_init, mod->init_size) ||
                    within(addr, mod->module_core, mod->core_size)) {
                        const char *sym;
@@ -2483,7 +2479,7 @@ int lookup_module_symbol_attrs(unsigned long addr, unsigned long *size,
        struct module *mod;
 
        preempt_disable();
-       list_for_each_entry(mod, &modules, list) {
+       list_for_each_entry_rcu(mod, &modules, list) {
                if (within(addr, mod->module_init, mod->init_size) ||
                    within(addr, mod->module_core, mod->core_size)) {
                        const char *sym;
@@ -2510,7 +2506,7 @@ int module_get_kallsym(unsigned int symnum, unsigned long *value, char *type,
        struct module *mod;
 
        preempt_disable();
-       list_for_each_entry(mod, &modules, list) {
+       list_for_each_entry_rcu(mod, &modules, list) {
                if (symnum < mod->num_symtab) {
                        *value = mod->symtab[symnum].st_value;
                        *type = mod->symtab[symnum].st_info;
@@ -2553,7 +2549,7 @@ unsigned long module_kallsyms_lookup_name(const char *name)
                        ret = mod_find_symname(mod, colon+1);
                *colon = ':';
        } else {
-               list_for_each_entry(mod, &modules, list)
+               list_for_each_entry_rcu(mod, &modules, list)
                        if ((ret = mod_find_symname(mod, name)) != 0)
                                break;
        }
@@ -2562,23 +2558,6 @@ unsigned long module_kallsyms_lookup_name(const char *name)
 }
 #endif /* CONFIG_KALLSYMS */
 
-/* Called by the /proc file system to return a list of modules. */
-static void *m_start(struct seq_file *m, loff_t *pos)
-{
-       mutex_lock(&module_mutex);
-       return seq_list_start(&modules, *pos);
-}
-
-static void *m_next(struct seq_file *m, void *p, loff_t *pos)
-{
-       return seq_list_next(p, &modules, pos);
-}
-
-static void m_stop(struct seq_file *m, void *p)
-{
-       mutex_unlock(&module_mutex);
-}
-
 static char *module_flags(struct module *mod, char *buf)
 {
        int bx = 0;
@@ -2612,6 +2591,24 @@ static char *module_flags(struct module *mod, char *buf)
        return buf;
 }
 
+#ifdef CONFIG_PROC_FS
+/* Called by the /proc file system to return a list of modules. */
+static void *m_start(struct seq_file *m, loff_t *pos)
+{
+       mutex_lock(&module_mutex);
+       return seq_list_start(&modules, *pos);
+}
+
+static void *m_next(struct seq_file *m, void *p, loff_t *pos)
+{
+       return seq_list_next(p, &modules, pos);
+}
+
+static void m_stop(struct seq_file *m, void *p)
+{
+       mutex_unlock(&module_mutex);
+}
+
 static int m_show(struct seq_file *m, void *p)
 {
        struct module *mod = list_entry(p, struct module, list);
@@ -2642,13 +2639,33 @@ 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 -.
 */
-const struct seq_operations modules_op = {
+static const struct seq_operations modules_op = {
        .start  = m_start,
        .next   = m_next,
        .stop   = m_stop,
        .show   = m_show
 };
 
+static int modules_open(struct inode *inode, struct file *file)
+{
+       return seq_open(file, &modules_op);
+}
+
+static const struct file_operations proc_modules_operations = {
+       .open           = modules_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = seq_release,
+};
+
+static int __init proc_modules_init(void)
+{
+       proc_create("modules", 0, NULL, &proc_modules_operations);
+       return 0;
+}
+module_init(proc_modules_init);
+#endif
+
 /* Given an address, look for it in the module exception tables. */
 const struct exception_table_entry *search_module_extables(unsigned long addr)
 {
@@ -2656,7 +2673,7 @@ const struct exception_table_entry *search_module_extables(unsigned long addr)
        struct module *mod;
 
        preempt_disable();
-       list_for_each_entry(mod, &modules, list) {
+       list_for_each_entry_rcu(mod, &modules, list) {
                if (mod->num_exentries == 0)
                        continue;
 
@@ -2682,7 +2699,7 @@ int is_module_address(unsigned long addr)
 
        preempt_disable();
 
-       list_for_each_entry(mod, &modules, list) {
+       list_for_each_entry_rcu(mod, &modules, list) {
                if (within(addr, mod->module_core, mod->core_size)) {
                        preempt_enable();
                        return 1;
@@ -2703,7 +2720,7 @@ struct module *__module_text_address(unsigned long addr)
        if (addr < module_addr_min || addr > module_addr_max)
                return NULL;
 
-       list_for_each_entry(mod, &modules, list)
+       list_for_each_entry_rcu(mod, &modules, list)
                if (within(addr, mod->module_init, mod->init_text_size)
                    || within(addr, mod->module_core, mod->core_text_size))
                        return mod;
@@ -2728,8 +2745,11 @@ void print_modules(void)
        char buf[8];
 
        printk("Modules linked in:");
-       list_for_each_entry(mod, &modules, list)
+       /* Most callers should already have preempt disabled, but make sure */
+       preempt_disable();
+       list_for_each_entry_rcu(mod, &modules, list)
                printk(" %s%s", mod->name, module_flags(mod, buf));
+       preempt_enable();
        if (last_unloaded_module[0])
                printk(" [last unloaded: %s]", last_unloaded_module);
        printk("\n");