diff options
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/Makefile | 6 | ||||
-rw-r--r-- | kernel/context.c | 60 | ||||
-rw-r--r-- | kernel/exit.c | 8 | ||||
-rw-r--r-- | kernel/fork.c | 6 | ||||
-rw-r--r-- | kernel/kmod.c | 28 | ||||
-rw-r--r-- | kernel/ksyms.c | 26 | ||||
-rw-r--r-- | kernel/module.c | 343 | ||||
-rw-r--r-- | kernel/ptrace.c | 6 | ||||
-rw-r--r-- | kernel/resource.c | 2 | ||||
-rw-r--r-- | kernel/sched.c | 79 | ||||
-rw-r--r-- | kernel/sysctl.c | 13 |
11 files changed, 416 insertions, 161 deletions
diff --git a/kernel/Makefile b/kernel/Makefile index a13812119..8f4c218f3 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -12,16 +12,12 @@ O_OBJS = sched.o dma.o fork.o exec_domain.o panic.o printk.o \ module.o exit.o itimer.o info.o time.o softirq.o resource.o \ sysctl.o acct.o capability.o ptrace.o timer.o user.o -OX_OBJS += signal.o sys.o +OX_OBJS += signal.o sys.o kmod.o context.o ifeq ($(CONFIG_UID16),y) O_OBJS += uid16.o endif -ifeq ($(CONFIG_KMOD),y) -O_OBJS += kmod.o -endif - ifeq ($(CONFIG_MODULES),y) OX_OBJS += ksyms.o endif diff --git a/kernel/context.c b/kernel/context.c new file mode 100644 index 000000000..b5219786a --- /dev/null +++ b/kernel/context.c @@ -0,0 +1,60 @@ +/* + * linux/kernel/context.c + * + * Mechanism for running arbitrary tasks in process context + * + * dwmw2@redhat.com + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/init.h> + +static DECLARE_TASK_QUEUE(tq_context); +static DECLARE_WAIT_QUEUE_HEAD(context_task_wq); + +void schedule_task(struct tq_struct *task) +{ + queue_task(task, &tq_context); + wake_up(&context_task_wq); +} + +EXPORT_SYMBOL(schedule_task); + +static int context_thread(void *dummy) +{ + DECLARE_WAITQUEUE(wait, current); + + daemonize(); + strcpy(current->comm, "eventd"); + + spin_lock_irq(¤t->sigmask_lock); + sigfillset(¤t->blocked); + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + + for (;;) { + current->state = TASK_INTERRUPTIBLE; + add_wait_queue(&context_task_wq, &wait); + + /* + * Careful: we depend on the wait-queue modifications + * to also act as memory barriers. + */ + if (!tq_context) + schedule(); + + remove_wait_queue(&context_task_wq, &wait); + current->state = TASK_RUNNING; + run_task_queue(&tq_context); + } +} + +static int __init start_context_thread(void) +{ + kernel_thread(context_thread, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGHAND); + return 0; +} + +module_init(start_context_thread); diff --git a/kernel/exit.c b/kernel/exit.c index 3f60ec29d..33b68980b 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -467,6 +467,14 @@ fake_volatile: goto fake_volatile; } +NORET_TYPE void up_and_exit(struct semaphore *sem, long code) +{ + if (sem) + up(sem); + + do_exit(code); +} + asmlinkage long sys_exit(int error_code) { do_exit((error_code&0xff)<<8); diff --git a/kernel/fork.c b/kernel/fork.c index b93b0b0e4..d85c3494a 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -39,6 +39,7 @@ void add_wait_queue(wait_queue_head_t *q, wait_queue_t * wait) unsigned long flags; wq_write_lock_irqsave(&q->lock, flags); + wait->flags = 0; __add_wait_queue(q, wait); wq_write_unlock_irqrestore(&q->lock, flags); } @@ -48,6 +49,7 @@ void add_wait_queue_exclusive(wait_queue_head_t *q, wait_queue_t * wait) unsigned long flags; wq_write_lock_irqsave(&q->lock, flags); + wait->flags = WQ_FLAG_EXCLUSIVE; __add_wait_queue_tail(q, wait); wq_write_unlock_irqrestore(&q->lock, flags); } @@ -572,9 +574,9 @@ int do_fork(unsigned long clone_flags, unsigned long stack_start, */ if (nr_threads >= max_threads) goto bad_fork_cleanup_count; + + get_exec_domain(p->exec_domain); - if (p->exec_domain && p->exec_domain->module) - __MOD_INC_USE_COUNT(p->exec_domain->module); if (p->binfmt && p->binfmt->module) __MOD_INC_USE_COUNT(p->binfmt->module); diff --git a/kernel/kmod.c b/kernel/kmod.c index 3fff3ed3d..a21507eec 100644 --- a/kernel/kmod.c +++ b/kernel/kmod.c @@ -16,17 +16,13 @@ #define __KERNEL_SYSCALLS__ #include <linux/config.h> +#include <linux/module.h> #include <linux/sched.h> #include <linux/unistd.h> #include <linux/smp_lock.h> #include <asm/uaccess.h> -/* - modprobe_path is set via /proc/sys. -*/ -char modprobe_path[256] = "/sbin/modprobe"; - extern int max_threads; static inline void @@ -118,7 +114,8 @@ int exec_usermodehelper(char *program_path, char *argv[], char *envp[]) } /* Give kmod all effective privileges.. */ - current->uid = current->euid = current->fsuid = 0; + current->euid = current->fsuid = 0; + current->egid = current->fsgid = 0; cap_set_full(current->cap_effective); /* Allow execve args to be in kernel space. */ @@ -130,10 +127,17 @@ int exec_usermodehelper(char *program_path, char *argv[], char *envp[]) return 0; } +#ifdef CONFIG_KMOD + +/* + modprobe_path is set via /proc/sys. +*/ +char modprobe_path[256] = "/sbin/modprobe"; + static int exec_modprobe(void * module_name) { static char * envp[] = { "HOME=/", "TERM=linux", "PATH=/sbin:/usr/sbin:/bin:/usr/bin", NULL }; - char *argv[] = { modprobe_path, "-s", "-k", (char*)module_name, NULL }; + char *argv[] = { modprobe_path, "-s", "-k", "--", (char*)module_name, NULL }; int ret; ret = exec_usermodehelper(modprobe_path, argv, envp); @@ -226,6 +230,7 @@ int request_module(const char * module_name) } return 0; } +#endif /* CONFIG_KMOD */ #ifdef CONFIG_HOTPLUG @@ -247,6 +252,10 @@ int request_module(const char * module_name) */ char hotplug_path[256] = "/sbin/hotplug"; +EXPORT_SYMBOL(hotplug_path); + +#endif /* CONFIG_HOTPLUG */ + static int exec_helper (void *arg) { @@ -286,5 +295,10 @@ int call_usermodehelper (char *path, char **argv, char **envp) return retval; } +EXPORT_SYMBOL(exec_usermodehelper); +EXPORT_SYMBOL(call_usermodehelper); + +#ifdef CONFIG_KMOD +EXPORT_SYMBOL(request_module); #endif diff --git a/kernel/ksyms.c b/kernel/ksyms.c index b8eb6b4f0..100adaeb3 100644 --- a/kernel/ksyms.c +++ b/kernel/ksyms.c @@ -71,20 +71,12 @@ __attribute__((section("__ksymtab"))) = { #endif -#ifdef CONFIG_KMOD -EXPORT_SYMBOL(request_module); -EXPORT_SYMBOL(exec_usermodehelper); -#ifdef CONFIG_HOTPLUG -EXPORT_SYMBOL(hotplug_path); -EXPORT_SYMBOL(call_usermodehelper); -#endif -#endif - -#ifdef CONFIG_MODULES -EXPORT_SYMBOL(get_module_symbol); -EXPORT_SYMBOL(put_module_symbol); +EXPORT_SYMBOL(inter_module_register); +EXPORT_SYMBOL(inter_module_unregister); +EXPORT_SYMBOL(inter_module_get); +EXPORT_SYMBOL(inter_module_get_request); +EXPORT_SYMBOL(inter_module_put); EXPORT_SYMBOL(try_inc_mod_count); -#endif /* process memory management */ EXPORT_SYMBOL(do_mmap_pgoff); @@ -124,6 +116,7 @@ EXPORT_SYMBOL(vmtruncate); EXPORT_SYMBOL(find_vma); EXPORT_SYMBOL(get_unmapped_area); EXPORT_SYMBOL(init_mm); +EXPORT_SYMBOL(deactivate_page); #ifdef CONFIG_HIGHMEM EXPORT_SYMBOL(kmap_high); EXPORT_SYMBOL(kunmap_high); @@ -215,7 +208,6 @@ EXPORT_SYMBOL(generic_buffer_fdatasync); EXPORT_SYMBOL(page_hash_bits); EXPORT_SYMBOL(page_hash_table); EXPORT_SYMBOL(file_lock_list); -EXPORT_SYMBOL(file_lock_sem); EXPORT_SYMBOL(locks_init_lock); EXPORT_SYMBOL(locks_copy_lock); EXPORT_SYMBOL(posix_lock_file); @@ -342,6 +334,7 @@ EXPORT_SYMBOL(register_sysctl_table); EXPORT_SYMBOL(unregister_sysctl_table); EXPORT_SYMBOL(sysctl_string); EXPORT_SYMBOL(sysctl_intvec); +EXPORT_SYMBOL(sysctl_jiffies); EXPORT_SYMBOL(proc_dostring); EXPORT_SYMBOL(proc_dointvec); EXPORT_SYMBOL(proc_dointvec_jiffies); @@ -425,6 +418,7 @@ EXPORT_SYMBOL(ioport_resource); EXPORT_SYMBOL(iomem_resource); /* process management */ +EXPORT_SYMBOL(up_and_exit); EXPORT_SYMBOL(__wake_up); EXPORT_SYMBOL(wake_up_process); EXPORT_SYMBOL(sleep_on); @@ -494,10 +488,6 @@ EXPORT_SYMBOL(remove_inode_hash); EXPORT_SYMBOL(make_bad_inode); EXPORT_SYMBOL(is_bad_inode); EXPORT_SYMBOL(event); -EXPORT_SYMBOL(__down); -EXPORT_SYMBOL(__down_interruptible); -EXPORT_SYMBOL(__down_trylock); -EXPORT_SYMBOL(__up); EXPORT_SYMBOL(brw_page); #ifdef CONFIG_UID16 diff --git a/kernel/module.c b/kernel/module.c index 15efa305c..e89149de9 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -1,11 +1,14 @@ #include <linux/config.h> #include <linux/mm.h> #include <linux/module.h> +#include <asm/module.h> #include <asm/uaccess.h> #include <linux/vmalloc.h> #include <linux/smp_lock.h> #include <asm/pgalloc.h> #include <linux/init.h> +#include <linux/slab.h> +#include <linux/kmod.h> /* * Originally by Anonymous (as far as I know...) @@ -14,11 +17,17 @@ * Heavily modified by Bjorn Ekwall <bj0rn@blox.se> May 1994 (C) * Rewritten by Richard Henderson <rth@tamu.edu> Dec 1996 * Add MOD_INITIALIZING Keith Owens <kaos@ocs.com.au> Nov 1999 + * Add kallsyms support, Keith Owens <kaos@ocs.com.au> Apr 2000 + * Add asm/module support, IA64 has special requirements. Keith Owens <kaos@ocs.com.au> Sep 2000 + * Fix assorted bugs in module verification. Keith Owens <kaos@ocs.com.au> Sep 2000 + * Fix sys_init_module race, Andrew Morton <andrewm@uow.edu.au> Oct 2000 + * http://www.uwsg.iu.edu/hypermail/linux/kernel/0008.3/0379.html + * Replace xxx_module_symbol with inter_module_xxx. Keith Owens <kaos@ocs.com.au> Oct 2000 * * This source is covered by the GNU GPL, the same as all kernel sources. */ -#ifdef CONFIG_MODULES /* a *big* #ifdef block... */ +#if defined(CONFIG_MODULES) || defined(CONFIG_KALLSYMS) extern struct module_symbol __start___ksymtab[]; extern struct module_symbol __stop___ksymtab[]; @@ -26,6 +35,9 @@ extern struct module_symbol __stop___ksymtab[]; extern const struct exception_table_entry __start___ex_table[]; extern const struct exception_table_entry __stop___ex_table[]; +extern const char __start___kallsyms[] __attribute__ ((weak)); +extern const char __stop___kallsyms[] __attribute__ ((weak)); + static struct module kernel_module = { size_of_struct: sizeof(struct module), @@ -33,16 +45,182 @@ static struct module kernel_module = uc: {ATOMIC_INIT(1)}, flags: MOD_RUNNING, syms: __start___ksymtab, - ex_table_start: __start___ex_table, - ex_table_end: __stop___ex_table + ex_table_start: __start___ex_table, + ex_table_end: __stop___ex_table, + kallsyms_start: __start___kallsyms, + kallsyms_end: __stop___kallsyms, }; struct module *module_list = &kernel_module; +#endif /* defined(CONFIG_MODULES) || defined(CONFIG_KALLSYMS) */ + +/* inter_module functions are always available, even when the kernel is + * compiled without modules. Consumers of inter_module_xxx routines + * will always work, even when both are built into the kernel, this + * approach removes lots of #ifdefs in mainline code. + */ + +static struct list_head ime_list = LIST_HEAD_INIT(ime_list); +static spinlock_t ime_lock = SPIN_LOCK_UNLOCKED; +static int kmalloc_failed; + +/** + * inter_module_register - register a new set of inter module data. + * @im_name: an arbitrary string to identify the data, must be unique + * @owner: module that is registering the data, always use THIS_MODULE + * @userdata: pointer to arbitrary userdata to be registered + * + * Description: Check that the im_name has not already been registered, + * complain if it has. For new data, add it to the inter_module_entry + * list. + */ +void inter_module_register(const char *im_name, struct module *owner, const void *userdata) +{ + struct list_head *tmp; + struct inter_module_entry *ime, *ime_new; + + if (!(ime_new = kmalloc(sizeof(*ime), GFP_KERNEL))) { + /* Overloaded kernel, not fatal */ + printk(KERN_ERR + "Aiee, inter_module_register: cannot kmalloc entry for '%s'\n", + im_name); + kmalloc_failed = 1; + return; + } + memset(ime_new, 0, sizeof(*ime_new)); + ime_new->im_name = im_name; + ime_new->owner = owner; + ime_new->userdata = userdata; + + spin_lock(&ime_lock); + list_for_each(tmp, &ime_list) { + ime = list_entry(tmp, struct inter_module_entry, list); + if (strcmp(ime->im_name, im_name) == 0) { + spin_unlock(&ime_lock); + kfree(ime_new); + /* Program logic error, fatal */ + panic("inter_module_register: duplicate im_name '%s'", im_name); + } + } + list_add(&(ime_new->list), &ime_list); + spin_unlock(&ime_lock); +} + +/** + * inter_module_unregister - unregister a set of inter module data. + * @im_name: an arbitrary string to identify the data, must be unique + * + * Description: Check that the im_name has been registered, complain if + * it has not. For existing data, remove it from the + * inter_module_entry list. + */ +void inter_module_unregister(const char *im_name) +{ + struct list_head *tmp; + struct inter_module_entry *ime; + + spin_lock(&ime_lock); + list_for_each(tmp, &ime_list) { + ime = list_entry(tmp, struct inter_module_entry, list); + if (strcmp(ime->im_name, im_name) == 0) { + list_del(&(ime->list)); + spin_unlock(&ime_lock); + kfree(ime); + return; + } + } + spin_unlock(&ime_lock); + if (kmalloc_failed) { + printk(KERN_ERR + "inter_module_unregister: no entry for '%s', " + "probably caused by previous kmalloc failure\n", + im_name); + return; + } + else { + /* Program logic error, fatal */ + panic("inter_module_unregister: no entry for '%s'", im_name); + } +} + +/** + * inter_module_get - return arbitrary userdata from another module. + * @im_name: an arbitrary string to identify the data, must be unique + * + * Description: If the im_name has not been registered, return NULL. + * Try to increment the use count on the owning module, if that fails + * then return NULL. Otherwise return the userdata. + */ +const void *inter_module_get(const char *im_name) +{ + struct list_head *tmp; + struct inter_module_entry *ime; + const void *result = NULL; + + spin_lock(&ime_lock); + list_for_each(tmp, &ime_list) { + ime = list_entry(tmp, struct inter_module_entry, list); + if (strcmp(ime->im_name, im_name) == 0) { + if (try_inc_mod_count(ime->owner)) + result = ime->userdata; + break; + } + } + spin_unlock(&ime_lock); + return(result); +} + +/** + * inter_module_get_request - im get with automatic request_module. + * @im_name: an arbitrary string to identify the data, must be unique + * @modname: module that is expected to register im_name + * + * Description: If inter_module_get fails, do request_module then retry. + */ +const void *inter_module_get_request(const char *im_name, const char *modname) +{ + const void *result = inter_module_get(im_name); + if (!result) { + request_module(modname); + result = inter_module_get(im_name); + } + return(result); +} + +/** + * inter_module_put - release use of data from another module. + * @im_name: an arbitrary string to identify the data, must be unique + * + * Description: If the im_name has not been registered, complain, + * otherwise decrement the use count on the owning module. + */ +void inter_module_put(const char *im_name) +{ + struct list_head *tmp; + struct inter_module_entry *ime; + + spin_lock(&ime_lock); + list_for_each(tmp, &ime_list) { + ime = list_entry(tmp, struct inter_module_entry, list); + if (strcmp(ime->im_name, im_name) == 0) { + if (ime->owner) + __MOD_DEC_USE_COUNT(ime->owner); + spin_unlock(&ime_lock); + return; + } + } + spin_unlock(&ime_lock); + panic("inter_module_put: no entry for '%s'", im_name); +} + + +#if defined(CONFIG_MODULES) /* The rest of the source */ + static long get_mod_name(const char *user_name, char **buf); static void put_mod_name(char *buf); -static struct module *find_module(const char *name); -static void free_module(struct module *, int tag_freed); +struct module *find_module(const char *name); +void free_module(struct module *, int tag_freed); /* @@ -151,7 +329,7 @@ asmlinkage long sys_init_module(const char *name_user, struct module *mod_user) { struct module mod_tmp, *mod; - char *name, *n_name; + char *name, *n_name, *name_tmp = NULL; long namelen, n_namelen, i, error; unsigned long mod_user_size; struct module_ref *dep; @@ -168,7 +346,7 @@ sys_init_module(const char *name_user, struct module *mod_user) goto err1; } - /* Check module header size. We allow a bit of slop over the + /* Check module header size. We allow a bit of slop over the size we are familiar with to cope with a version of insmod for a newer kernel. But don't over do it. */ if ((error = get_user(mod_user_size, &mod_user->size_of_struct)) != 0) @@ -185,8 +363,14 @@ sys_init_module(const char *name_user, struct module *mod_user) /* Hold the current contents while we play with the user's idea of righteousness. */ mod_tmp = *mod; + name_tmp = kmalloc(strlen(mod->name) + 1, GFP_KERNEL); /* Where's kstrdup()? */ + if (name_tmp == NULL) { + error = -ENOMEM; + goto err1; + } + strcpy(name_tmp, mod->name); - error = copy_from_user(mod, mod_user, sizeof(struct module)); + error = copy_from_user(mod, mod_user, mod_user_size); if (error) { error = -EFAULT; goto err2; @@ -203,32 +387,29 @@ sys_init_module(const char *name_user, struct module *mod_user) /* Make sure all interesting pointers are sane. */ -#define bound(p, n, m) ((unsigned long)(p) >= (unsigned long)(m+1) && \ - (unsigned long)((p)+(n)) <= (unsigned long)(m) + (m)->size) - - if (!bound(mod->name, namelen, mod)) { + if (!mod_bound(mod->name, namelen, mod)) { printk(KERN_ERR "init_module: mod->name out of bounds.\n"); goto err2; } - if (mod->nsyms && !bound(mod->syms, mod->nsyms, mod)) { + if (mod->nsyms && !mod_bound(mod->syms, mod->nsyms, mod)) { printk(KERN_ERR "init_module: mod->syms out of bounds.\n"); goto err2; } - if (mod->ndeps && !bound(mod->deps, mod->ndeps, mod)) { + if (mod->ndeps && !mod_bound(mod->deps, mod->ndeps, mod)) { printk(KERN_ERR "init_module: mod->deps out of bounds.\n"); goto err2; } - if (mod->init && !bound(mod->init, 0, mod)) { + if (mod->init && !mod_bound(mod->init, 0, mod)) { printk(KERN_ERR "init_module: mod->init out of bounds.\n"); goto err2; } - if (mod->cleanup && !bound(mod->cleanup, 0, mod)) { + if (mod->cleanup && !mod_bound(mod->cleanup, 0, mod)) { printk(KERN_ERR "init_module: mod->cleanup out of bounds.\n"); goto err2; } if (mod->ex_table_start > mod->ex_table_end || (mod->ex_table_start && - !((unsigned long)mod->ex_table_start >= (unsigned long)(mod+1) + !((unsigned long)mod->ex_table_start >= ((unsigned long)mod + mod->size_of_struct) && ((unsigned long)mod->ex_table_end < (unsigned long)mod + mod->size))) || (((unsigned long)mod->ex_table_start @@ -242,24 +423,51 @@ sys_init_module(const char *name_user, struct module *mod_user) goto err2; } #ifdef __alpha__ - if (!bound(mod->gp - 0x8000, 0, mod)) { + if (!mod_bound(mod->gp - 0x8000, 0, mod)) { printk(KERN_ERR "init_module: mod->gp out of bounds.\n"); goto err2; } #endif if (mod_member_present(mod, can_unload) - && mod->can_unload && !bound(mod->can_unload, 0, mod)) { + && mod->can_unload && !mod_bound(mod->can_unload, 0, mod)) { printk(KERN_ERR "init_module: mod->can_unload out of bounds.\n"); goto err2; } - -#undef bound + if (mod_member_present(mod, kallsyms_end)) { + if (mod->kallsyms_end && + (!mod_bound(mod->kallsyms_start, 0, mod) || + !mod_bound(mod->kallsyms_end, 0, mod))) { + printk(KERN_ERR "init_module: mod->kallsyms out of bounds.\n"); + goto err2; + } + if (mod->kallsyms_start > mod->kallsyms_end) { + printk(KERN_ERR "init_module: mod->kallsyms invalid.\n"); + goto err2; + } + } + if (mod_member_present(mod, archdata_end)) { + if (mod->archdata_end && + (!mod_bound(mod->archdata_start, 0, mod) || + !mod_bound(mod->archdata_end, 0, mod))) { + printk(KERN_ERR "init_module: mod->archdata out of bounds.\n"); + goto err2; + } + if (mod->archdata_start > mod->archdata_end) { + printk(KERN_ERR "init_module: mod->archdata invalid.\n"); + goto err2; + } + } + if (mod_member_present(mod, kernel_data) && mod->kernel_data) { + printk(KERN_ERR "init_module: mod->kernel_data must be zero.\n"); + goto err2; + } /* Check that the user isn't doing something silly with the name. */ if ((n_namelen = get_mod_name(mod->name - (unsigned long)mod + (unsigned long)mod_user, &n_name)) < 0) { + printk(KERN_ERR "init_module: get_mod_name failure.\n"); error = n_namelen; goto err2; } @@ -277,13 +485,17 @@ sys_init_module(const char *name_user, struct module *mod_user) goto err3; } + if (module_arch_init(mod)) + goto err3; + /* On some machines it is necessary to do something here to make the I and D caches consistent. */ flush_icache_range((unsigned long)mod, (unsigned long)mod + mod->size); - /* Update module references. */ mod->next = mod_tmp.next; mod->refs = NULL; + + /* Sanity check the module's dependents */ for (i = 0, dep = mod->deps; i < mod->ndeps; ++i, ++dep) { struct module *o, *d = dep->dep; @@ -294,18 +506,25 @@ sys_init_module(const char *name_user, struct module *mod_user) goto err3; } - for (o = module_list; o != &kernel_module; o = o->next) - if (o == d) goto found_dep; + /* Scan the current modules for this dependency */ + for (o = module_list; o != &kernel_module && o != d; o = o->next) + ; - printk(KERN_ERR "init_module: found dependency that is " + if (o != d) { + printk(KERN_ERR "init_module: found dependency that is " "(no longer?) a module.\n"); - goto err3; - - found_dep: + goto err3; + } + } + + /* Update module references. */ + for (i = 0, dep = mod->deps; i < mod->ndeps; ++i, ++dep) { + struct module *d = dep->dep; + dep->ref = mod; dep->next_ref = d->refs; d->refs = dep; - /* Being referenced by a dependent module counts as a + /* Being referenced by a dependent module counts as a use as far as kmod is concerned. */ d->flags |= MOD_USED_ONCE; } @@ -335,10 +554,12 @@ err3: put_mod_name(n_name); err2: *mod = mod_tmp; + strcpy((char *)mod->name, name_tmp); /* We know there is room for this */ err1: put_mod_name(name); err0: unlock_kernel(); + kfree(name_tmp); return error; } @@ -384,11 +605,11 @@ sys_delete_module(const char *name_user) } put_mod_name(name); error = -EBUSY; - if (mod->refs != NULL) + if (mod->refs != NULL) goto out; spin_lock(&unload_lock); - if (!__MOD_IN_USE(mod)) { + if (!__MOD_IN_USE(mod)) { mod->flags |= MOD_DELETED; spin_unlock(&unload_lock); free_module(mod, 0); @@ -768,7 +989,7 @@ out: * Look for a module by name, ignoring modules marked for deletion. */ -static struct module * +struct module * find_module(const char *name) { struct module *mod; @@ -787,7 +1008,7 @@ find_module(const char *name) * Free the given module. */ -static void +void free_module(struct module *mod, int tag_freed) { struct module_ref *dep; @@ -795,7 +1016,7 @@ free_module(struct module *mod, int tag_freed) /* Let the module clean up. */ - if (mod->flags & MOD_RUNNING) + if (mod->flags & MOD_RUNNING) { if(mod->cleanup) mod->cleanup(); @@ -852,7 +1073,7 @@ int get_module_list(char *p) } while (0) #define safe_copy_cstr(str) safe_copy_str(str, sizeof(str)-1) - len = strlen(mod->name); + len = strlen(mod->name); safe_copy_str(mod->name, len); if ((len = 20 - len) > 0) { @@ -961,58 +1182,6 @@ leave_the_loop: return len; } -/* - * Gets the address for a symbol in the given module. If modname is - * NULL, it looks for the name in any registered symbol table. If the - * modname is an empty string, it looks for the symbol in kernel exported - * symbol tables. Increase the usage count of the module in which the - * symbol was found - it's the only way we can guarantee that it's still - * there by the time our caller actually uses it. - */ -unsigned long -get_module_symbol(char *modname, char *symname) -{ - struct module *mp; - struct module_symbol *sym; - int i; - - spin_lock(&unload_lock); - for (mp = module_list; mp; mp = mp->next) { - if (((modname == NULL) || (strcmp(mp->name, modname) == 0)) && - MOD_CAN_QUERY(mp) && - (mp->nsyms > 0)) { - for (i = mp->nsyms, sym = mp->syms; - i > 0; --i, ++sym) { - - if (strcmp(sym->name, symname) == 0) { - __MOD_INC_USE_COUNT(mp); - spin_unlock(&unload_lock); - return sym->value; - } - } - } - } - spin_unlock(&unload_lock); - return 0; -} - -/* Decrease the use count of the module containing a symbol with the - * address passed. - */ -void put_module_symbol(unsigned long addr) -{ - struct module *mp; - - for (mp = module_list; mp; mp = mp->next) { - if (MOD_CAN_QUERY(mp) && - addr >= (unsigned long)mp && - addr < (unsigned long)mp + mp->size) { - __MOD_DEC_USE_COUNT(mp); - return; - } - } -} - #else /* CONFIG_MODULES */ /* Dummy syscalls for people who don't want modules */ diff --git a/kernel/ptrace.c b/kernel/ptrace.c index 87dea8254..957355ef4 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c @@ -24,7 +24,7 @@ static int access_one_page(struct mm_struct * mm, struct vm_area_struct * vma, u pgd_t * pgdir; pmd_t * pgmiddle; pte_t * pgtable; - unsigned long maddr; + char *maddr; struct page *page; repeat: @@ -54,13 +54,13 @@ repeat: if (write) { maddr = kmap(page); - memcpy((char *)maddr + (addr & ~PAGE_MASK), buf, len); + memcpy(maddr + (addr & ~PAGE_MASK), buf, len); flush_page_to_ram(page); flush_icache_page(vma, page, addr); kunmap(page); } else { maddr = kmap(page); - memcpy(buf, (char *)maddr + (addr & ~PAGE_MASK), len); + memcpy(buf, maddr + (addr & ~PAGE_MASK), len); flush_page_to_ram(page); kunmap(page); } diff --git a/kernel/resource.c b/kernel/resource.c index 3d7aa17d0..b553eb0ff 100644 --- a/kernel/resource.c +++ b/kernel/resource.c @@ -288,7 +288,7 @@ void __release_region(struct resource *parent, unsigned long start, unsigned lon } p = &res->sibling; } - printk("Trying to free nonexistent resource <%04lx-%04lx>\n", start, end); + printk("Trying to free nonexistent resource <%08lx-%08lx>\n", start, end); } /* diff --git a/kernel/sched.c b/kernel/sched.c index 927bf47e7..119edeb81 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -77,16 +77,14 @@ struct task_struct * init_tasks[NR_CPUS] = {&init_task, }; /* * The tasklist_lock protects the linked list of processes. * - * The scheduler lock is protecting against multiple entry - * into the scheduling code, and doesn't need to worry - * about interrupts (because interrupts cannot call the - * scheduler). - * - * The run-queue lock locks the parts that actually access + * The runqueue_lock locks the parts that actually access * and change the run-queues, and have to be interrupt-safe. + * + * If both locks are to be concurrently held, the runqueue_lock + * nests inside the tasklist_lock. */ -spinlock_t runqueue_lock __cacheline_aligned = SPIN_LOCK_UNLOCKED; /* second */ -rwlock_t tasklist_lock __cacheline_aligned = RW_LOCK_UNLOCKED; /* third */ +spinlock_t runqueue_lock __cacheline_aligned = SPIN_LOCK_UNLOCKED; /* inner */ +rwlock_t tasklist_lock __cacheline_aligned = RW_LOCK_UNLOCKED; /* outer */ static LIST_HEAD(runqueue_head); @@ -199,12 +197,8 @@ static inline int preemption_goodness(struct task_struct * prev, struct task_str /* * This is ugly, but reschedule_idle() is very timing-critical. - * We enter with the runqueue spinlock held, but we might end - * up unlocking it early, so the caller must not unlock the - * runqueue, it's always done by reschedule_idle(). - * - * This function must be inline as anything that saves and restores - * flags has to do so within the same register window on sparc (Anton) + * We `are called with the runqueue spinlock held and we must + * not claim the tasklist_lock. */ static FASTCALL(void reschedule_idle(struct task_struct * p)); @@ -433,15 +427,27 @@ static inline void __schedule_tail(struct task_struct *prev) int policy; /* + * prev->policy can be written from here only before `prev' + * can be scheduled (before setting prev->has_cpu to zero). + * Of course it must also be read before allowing prev + * to be rescheduled, but since the write depends on the read + * to complete, wmb() is enough. (the spin_lock() acquired + * before setting has_cpu is not enough because the spin_lock() + * common code semantics allows code outside the critical section + * to enter inside the critical section) + */ + policy = prev->policy; + prev->policy = policy & ~SCHED_YIELD; + wmb(); + + /* * fast path falls through. We have to clear has_cpu before * checking prev->state to avoid a wakeup race - thus we * also have to protect against the task exiting early. */ task_lock(prev); - policy = prev->policy; - prev->policy = policy & ~SCHED_YIELD; prev->has_cpu = 0; - wmb(); + mb(); if (prev->state == TASK_RUNNING) goto needs_resched; @@ -535,7 +541,7 @@ handle_softirq_back: goto move_rr_last; move_rr_back: - switch (prev->state & ~TASK_EXCLUSIVE) { + switch (prev->state) { case TASK_INTERRUPTIBLE: if (signal_pending(prev)) { prev->state = TASK_RUNNING; @@ -694,14 +700,14 @@ scheduling_in_interrupt: } static inline void __wake_up_common (wait_queue_head_t *q, unsigned int mode, - const int sync) + unsigned int wq_mode, const int sync) { struct list_head *tmp, *head; struct task_struct *p, *best_exclusive; unsigned long flags; int best_cpu, irq; - if (!q || !waitqueue_active(q)) + if (!q) goto out; best_cpu = smp_processor_id(); @@ -730,7 +736,7 @@ static inline void __wake_up_common (wait_queue_head_t *q, unsigned int mode, #endif p = curr->task; state = p->state; - if (state & (mode & ~TASK_EXCLUSIVE)) { + if (state & mode) { #if WAITQUEUE_DEBUG curr->__waker = (long)__builtin_return_address(0); #endif @@ -739,18 +745,19 @@ static inline void __wake_up_common (wait_queue_head_t *q, unsigned int mode, * prefer processes which are affine to this * CPU. */ - if (irq && (state & mode & TASK_EXCLUSIVE)) { + if (irq && (curr->flags & wq_mode & WQ_FLAG_EXCLUSIVE)) { if (!best_exclusive) best_exclusive = p; - else if ((p->processor == best_cpu) && - (best_exclusive->processor != best_cpu)) - best_exclusive = p; + if (p->processor == best_cpu) { + best_exclusive = p; + break; + } } else { if (sync) wake_up_process_synchronous(p); else wake_up_process(p); - if (state & mode & TASK_EXCLUSIVE) + if (curr->flags & wq_mode & WQ_FLAG_EXCLUSIVE) break; } } @@ -766,14 +773,14 @@ out: return; } -void __wake_up(wait_queue_head_t *q, unsigned int mode) +void __wake_up(wait_queue_head_t *q, unsigned int mode, unsigned int wq_mode) { - __wake_up_common(q, mode, 0); + __wake_up_common(q, mode, wq_mode, 0); } -void __wake_up_sync(wait_queue_head_t *q, unsigned int mode) +void __wake_up_sync(wait_queue_head_t *q, unsigned int mode, unsigned int wq_mode) { - __wake_up_common(q, mode, 1); + __wake_up_common(q, mode, wq_mode, 1); } #define SLEEP_ON_VAR \ @@ -905,8 +912,8 @@ static int setscheduler(pid_t pid, int policy, /* * We play safe to avoid deadlocks. */ - spin_lock_irq(&runqueue_lock); - read_lock(&tasklist_lock); + read_lock_irq(&tasklist_lock); + spin_lock(&runqueue_lock); p = find_process_by_pid(pid); @@ -950,8 +957,8 @@ static int setscheduler(pid_t pid, int policy, current->need_resched = 1; out_unlock: - read_unlock(&tasklist_lock); - spin_unlock_irq(&runqueue_lock); + spin_unlock(&runqueue_lock); + read_unlock_irq(&tasklist_lock); out_nounlock: return retval; @@ -1227,7 +1234,9 @@ void daemonize(void) fs = init_task.fs; current->fs = fs; atomic_inc(&fs->count); - + exit_files(current); + current->files = init_task.files; + atomic_inc(¤t->files->count); } void __init init_idle(void) diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 70da2a029..5591ee2bc 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -54,10 +54,10 @@ static int minolduid; #ifdef CONFIG_KMOD extern char modprobe_path[]; +#endif #ifdef CONFIG_HOTPLUG extern char hotplug_path[]; #endif -#endif #ifdef CONFIG_CHR_DEV_SG extern int sg_big_buff; #endif @@ -188,11 +188,11 @@ static ctl_table kern_table[] = { #ifdef CONFIG_KMOD {KERN_MODPROBE, "modprobe", &modprobe_path, 256, 0644, NULL, &proc_dostring, &sysctl_string }, +#endif #ifdef CONFIG_HOTPLUG {KERN_HOTPLUG, "hotplug", &hotplug_path, 256, 0644, NULL, &proc_dostring, &sysctl_string }, #endif -#endif #ifdef CONFIG_CHR_DEV_SG {KERN_SG_BIG_BUFF, "sg-big-buff", &sg_big_buff, sizeof (int), 0444, NULL, &proc_dointvec}, @@ -571,7 +571,7 @@ static void unregister_proc_table(ctl_table * table, struct proc_dir_entry *root } /* Don't unregister proc entries that are still being used.. */ - if (de->count) + if (atomic_read(&de->count)) continue; table->de = NULL; @@ -1240,6 +1240,13 @@ int sysctl_intvec(ctl_table *table, int *name, int nlen, return -ENOSYS; } +int sysctl_jiffies(ctl_table *table, int *name, int nlen, + void *oldval, size_t *oldlenp, + void *newval, size_t newlen, void **context) +{ + return -ENOSYS; +} + int proc_dostring(ctl_table *table, int write, struct file *filp, void *buffer, size_t *lenp) { |