diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2001-01-10 05:27:25 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2001-01-10 05:27:25 +0000 |
commit | c9c06167e7933d93a6e396174c68abf242294abb (patch) | |
tree | d9a8bb30663e9a3405a1ef37ffb62bc14b9f019f /kernel | |
parent | f79e8cc3c34e4192a3e5ef4cc9c6542fdef703c0 (diff) |
Merge with Linux 2.4.0-test12.
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/Makefile | 7 | ||||
-rw-r--r-- | kernel/context.c | 143 | ||||
-rw-r--r-- | kernel/dma.c | 1 | ||||
-rw-r--r-- | kernel/exit.c | 12 | ||||
-rw-r--r-- | kernel/fork.c | 9 | ||||
-rw-r--r-- | kernel/kmod.c | 121 | ||||
-rw-r--r-- | kernel/ksyms.c | 4 | ||||
-rw-r--r-- | kernel/module.c | 13 | ||||
-rw-r--r-- | kernel/sched.c | 24 | ||||
-rw-r--r-- | kernel/sysctl.c | 2 | ||||
-rw-r--r-- | kernel/timer.c | 3 | ||||
-rw-r--r-- | kernel/user.c | 26 |
12 files changed, 254 insertions, 111 deletions
diff --git a/kernel/Makefile b/kernel/Makefile index 8f4c218f3..311d66cb8 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -26,6 +26,13 @@ ifeq ($(CONFIG_PM),y) OX_OBJS += pm.o endif +ifneq ($(CONFIG_IA64),y) +# According to Alan Modra <alan@linuxcare.com.au>, the -fno-omit-frame-pointer is +# needed for x86 only. Why this used to be enabled for all architectures is beyond +# me. I suspect most platforms don't need this, but until we know that for sure +# I turn this off for IA-64 only. Andreas Schwab says it's also needed on m68k +# to get a correct value for the wait-channel (WCHAN in ps). --davidm CFLAGS_sched.o := $(PROFILING) -fno-omit-frame-pointer +endif include $(TOPDIR)/Rules.make diff --git a/kernel/context.c b/kernel/context.c index b5219786a..864a70131 100644 --- a/kernel/context.c +++ b/kernel/context.c @@ -3,58 +3,155 @@ * * Mechanism for running arbitrary tasks in process context * - * dwmw2@redhat.com + * dwmw2@redhat.com: Genesis + * + * andrewm@uow.edu.au: 2.4.0-test12 + * - Child reaping + * - Support for tasks which re-add themselves + * - flush_scheduled_tasks. */ +#define __KERNEL_SYSCALLS__ + #include <linux/module.h> #include <linux/kernel.h> #include <linux/sched.h> #include <linux/init.h> +#include <linux/unistd.h> +#include <linux/signal.h> static DECLARE_TASK_QUEUE(tq_context); static DECLARE_WAIT_QUEUE_HEAD(context_task_wq); +static DECLARE_WAIT_QUEUE_HEAD(context_task_done); +static int keventd_running; +static struct task_struct *keventd_task; -void schedule_task(struct tq_struct *task) +static int need_keventd(const char *who) { - queue_task(task, &tq_context); - wake_up(&context_task_wq); + if (keventd_running == 0) + printk(KERN_ERR "%s(): keventd has not started\n", who); + return keventd_running; +} + +int current_is_keventd(void) +{ + int ret = 0; + if (need_keventd(__FUNCTION__)) + ret = (current == keventd_task); + return ret; } -EXPORT_SYMBOL(schedule_task); +/** + * schedule_task - schedule a function for subsequent execution in process context. + * @task: pointer to a &tq_struct which defines the function to be scheduled. + * + * May be called from interrupt context. The scheduled function is run at some + * time in the near future by the keventd kernel thread. If it can sleep, it + * should be designed to do so for the minimum possible time, as it will be + * stalling all other scheduled tasks. + * + * schedule_task() returns non-zero if the task was successfully scheduled. + * If @task is already residing on a task queue then schedule_task() fails + * to schedule your task and returns zero. + */ +int schedule_task(struct tq_struct *task) +{ + int ret; + need_keventd(__FUNCTION__); + ret = queue_task(task, &tq_context); + wake_up(&context_task_wq); + return ret; +} static int context_thread(void *dummy) { - DECLARE_WAITQUEUE(wait, current); + struct task_struct *curtask = current; + DECLARE_WAITQUEUE(wait, curtask); + struct k_sigaction sa; daemonize(); - strcpy(current->comm, "eventd"); + strcpy(curtask->comm, "keventd"); + keventd_running = 1; + keventd_task = curtask; + + spin_lock_irq(&curtask->sigmask_lock); + siginitsetinv(&curtask->blocked, sigmask(SIGCHLD)); + recalc_sigpending(curtask); + spin_unlock_irq(&curtask->sigmask_lock); - spin_lock_irq(¤t->sigmask_lock); - sigfillset(¤t->blocked); - recalc_sigpending(current); - spin_unlock_irq(¤t->sigmask_lock); + /* Install a handler so SIGCLD is delivered */ + sa.sa.sa_handler = SIG_IGN; + sa.sa.sa_flags = 0; + siginitset(&sa.sa.sa_mask, sigmask(SIGCHLD)); + do_sigaction(SIGCHLD, &sa, (struct k_sigaction *)0); + /* + * If one of the functions on a task queue re-adds itself + * to the task queue we call schedule() in state TASK_RUNNING + */ for (;;) { - current->state = TASK_INTERRUPTIBLE; + set_task_state(curtask, 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(); - + if (TQ_ACTIVE(tq_context)) + set_task_state(curtask, TASK_RUNNING); + schedule(); remove_wait_queue(&context_task_wq, &wait); - current->state = TASK_RUNNING; run_task_queue(&tq_context); + wake_up(&context_task_done); + if (signal_pending(curtask)) { + while (waitpid(-1, (unsigned int *)0, __WALL|WNOHANG) > 0) + ; + flush_signals(curtask); + recalc_sigpending(curtask); + } } } -static int __init start_context_thread(void) +/** + * flush_scheduled_tasks - ensure that any scheduled tasks have run to completion. + * + * Forces execution of the schedule_task() queue and blocks until its completion. + * + * If a kernel subsystem uses schedule_task() and wishes to flush any pending + * tasks, it should use this function. This is typically used in driver shutdown + * handlers. + * + * The caller should hold no spinlocks and should hold no semaphores which could + * cause the scheduled tasks to block. + */ +static struct tq_struct dummy_task; + +void flush_scheduled_tasks(void) +{ + int count; + DECLARE_WAITQUEUE(wait, current); + + /* + * Do it twice. It's possible, albeit highly unlikely, that + * the caller queued a task immediately before calling us, + * and that the eventd thread was already past the run_task_queue() + * but not yet into wake_up(), so it woke us up before completing + * the caller's queued task or our new dummy task. + */ + add_wait_queue(&context_task_done, &wait); + for (count = 0; count < 2; count++) { + set_current_state(TASK_UNINTERRUPTIBLE); + + /* Queue a dummy task to make sure we get kicked */ + schedule_task(&dummy_task); + + /* Wait for it to complete */ + schedule(); + } + remove_wait_queue(&context_task_done, &wait); +} + +int start_context_thread(void) { kernel_thread(context_thread, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGHAND); return 0; } -module_init(start_context_thread); +EXPORT_SYMBOL(schedule_task); +EXPORT_SYMBOL(flush_scheduled_tasks); + diff --git a/kernel/dma.c b/kernel/dma.c index 983dedb60..3ee09759f 100644 --- a/kernel/dma.c +++ b/kernel/dma.c @@ -13,6 +13,7 @@ #include <linux/kernel.h> #include <linux/errno.h> #include <linux/spinlock.h> +#include <linux/string.h> #include <asm/dma.h> #include <asm/system.h> diff --git a/kernel/exit.c b/kernel/exit.c index 33b68980b..99d4f6770 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -22,7 +22,7 @@ extern struct task_struct *child_reaper; int getrusage(struct task_struct *, int, struct rusage *); -void release(struct task_struct * p) +void release_task(struct task_struct * p) { if (p != current) { #ifdef CONFIG_SMP @@ -31,15 +31,15 @@ void release(struct task_struct * p) * runqueue (active on some other CPU still) */ for (;;) { - spin_lock_irq(&runqueue_lock); + task_lock(p); if (!p->has_cpu) break; - spin_unlock_irq(&runqueue_lock); + task_unlock(p); do { barrier(); } while (p->has_cpu); } - spin_unlock_irq(&runqueue_lock); + task_unlock(p); #endif atomic_dec(&p->user->processes); free_uid(p->user); @@ -302,9 +302,9 @@ static inline void __exit_mm(struct task_struct * tsk) { struct mm_struct * mm = tsk->mm; + mm_release(); if (mm) { atomic_inc(&mm->mm_count); - mm_release(); if (mm != tsk->active_mm) BUG(); /* more a memory barrier than a real lock */ task_lock(tsk); @@ -550,7 +550,7 @@ repeat: do_notify_parent(p, SIGCHLD); write_unlock_irq(&tasklist_lock); } else - release(p); + release_task(p); goto end_wait4; default: continue; diff --git a/kernel/fork.c b/kernel/fork.c index d85c3494a..bf3e36cfb 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -196,6 +196,7 @@ fail_nomem: } #define allocate_mm() (kmem_cache_alloc(mm_cachep, SLAB_KERNEL)) +#define free_mm(mm) (kmem_cache_free(mm_cachep, (mm))) static struct mm_struct * mm_init(struct mm_struct * mm) { @@ -206,7 +207,7 @@ static struct mm_struct * mm_init(struct mm_struct * mm) mm->pgd = pgd_alloc(); if (mm->pgd) return mm; - kmem_cache_free(mm_cachep, mm); + free_mm(mm); return NULL; } @@ -236,7 +237,7 @@ inline void __mmdrop(struct mm_struct *mm) if (mm == &init_mm) BUG(); pgd_free(mm->pgd); destroy_context(mm); - kmem_cache_free(mm_cachep, mm); + free_mm(mm); } /* @@ -541,7 +542,7 @@ static inline void copy_flags(unsigned long clone_flags, struct task_struct *p) * arch/ia64/kernel/process.c. */ int do_fork(unsigned long clone_flags, unsigned long stack_start, - struct pt_regs *regs, unsigned long stack_top) + struct pt_regs *regs, unsigned long stack_size) { int retval = -ENOMEM; struct task_struct *p; @@ -636,7 +637,7 @@ int do_fork(unsigned long clone_flags, unsigned long stack_start, goto bad_fork_cleanup_fs; if (copy_mm(clone_flags, p)) goto bad_fork_cleanup_sighand; - retval = copy_thread(0, clone_flags, stack_start, stack_top, p, regs); + retval = copy_thread(0, clone_flags, stack_start, stack_size, p, regs); if (retval) goto bad_fork_cleanup_sighand; p->semundo = NULL; diff --git a/kernel/kmod.c b/kernel/kmod.c index a21507eec..b68392685 100644 --- a/kernel/kmod.c +++ b/kernel/kmod.c @@ -19,6 +19,7 @@ #include <linux/module.h> #include <linux/sched.h> #include <linux/unistd.h> +#include <linux/kmod.h> #include <linux/smp_lock.h> #include <asm/uaccess.h> @@ -165,7 +166,7 @@ static int exec_modprobe(void * module_name) int request_module(const char * module_name) { - int pid; + pid_t pid; int waitpid_result; sigset_t tmpsig; int i; @@ -256,43 +257,105 @@ EXPORT_SYMBOL(hotplug_path); #endif /* CONFIG_HOTPLUG */ +struct subprocess_info { + struct semaphore *sem; + char *path; + char **argv; + char **envp; + pid_t retval; +}; -static int exec_helper (void *arg) +/* + * This is the task which runs the usermode application + */ +static int ____call_usermodehelper(void *data) { - void **params = (void **) arg; - char *path = (char *) params [0]; - char **argv = (char **) params [1]; - char **envp = (char **) params [2]; - return exec_usermodehelper (path, argv, envp); -} + struct subprocess_info *sub_info = data; + int retval; + retval = -EPERM; + if (current->fs->root) + retval = exec_usermodehelper(sub_info->path, sub_info->argv, sub_info->envp); -int call_usermodehelper (char *path, char **argv, char **envp) + /* Exec failed? */ + sub_info->retval = (pid_t)retval; + do_exit(0); +} + +/* + * This is run by keventd. + */ +static void __call_usermodehelper(void *data) { - void *params [3] = { path, argv, envp }; - int pid, pid2, retval; - mm_segment_t fs; + struct subprocess_info *sub_info = data; + pid_t pid; - if ( ! current->fs->root ) { - printk(KERN_ERR "call_usermodehelper[%s]: no root fs\n", - path); - return -EPERM; - } - if ((pid = kernel_thread (exec_helper, (void *) params, 0)) < 0) { - printk(KERN_ERR "failed fork %s, errno = %d", argv [0], -pid); - return -1; + /* + * CLONE_VFORK: wait until the usermode helper has execve'd successfully + * We need the data structures to stay around until that is done. + */ + pid = kernel_thread(____call_usermodehelper, sub_info, CLONE_VFORK | SIGCHLD); + if (pid < 0) + sub_info->retval = pid; + up(sub_info->sem); +} + +/** + * call_usermodehelper - start a usermode application + * @path: pathname for the application + * @argv: null-terminated argument list + * @envp: null-terminated environment list + * + * Runs a user-space application. The application is started asynchronously. It + * runs as a child of keventd. It runs with full root capabilities. keventd silently + * reaps the child when it exits. + * + * Must be called from process context. Returns zero on success, else a negative + * error code. + */ +int call_usermodehelper(char *path, char **argv, char **envp) +{ + DECLARE_MUTEX_LOCKED(sem); + struct subprocess_info sub_info = { + sem: &sem, + path: path, + argv: argv, + envp: envp, + retval: 0, + }; + struct tq_struct tqs = { + routine: __call_usermodehelper, + data: &sub_info, + }; + + if (path[0] == '\0') + goto out; + + if (current_is_keventd()) { + /* We can't wait on keventd! */ + __call_usermodehelper(&sub_info); + } else { + schedule_task(&tqs); + down(&sem); /* Wait until keventd has started the subprocess */ } +out: + return sub_info.retval; +} - fs = get_fs (); - set_fs (KERNEL_DS); - pid2 = waitpid (pid, &retval, __WCLONE); - set_fs (fs); +/* + * This is for the serialisation of device probe() functions + * against device open() functions + */ +static DECLARE_MUTEX(dev_probe_sem); - if (pid2 != pid) { - printk(KERN_ERR "waitpid(%d) failed, %d\n", pid, pid2); - return -1; - } - return retval; +void dev_probe_lock(void) +{ + down(&dev_probe_sem); +} + +void dev_probe_unlock(void) +{ + up(&dev_probe_sem); } EXPORT_SYMBOL(exec_usermodehelper); diff --git a/kernel/ksyms.c b/kernel/ksyms.c index 100adaeb3..4ed94117e 100644 --- a/kernel/ksyms.c +++ b/kernel/ksyms.c @@ -53,7 +53,6 @@ #include <linux/kmod.h> #endif -extern int console_loglevel; extern void set_device_ro(kdev_t dev,int flag); extern void *sys_call_table; @@ -175,6 +174,7 @@ EXPORT_SYMBOL(invalidate_inode_pages); EXPORT_SYMBOL(truncate_inode_pages); EXPORT_SYMBOL(fsync_dev); EXPORT_SYMBOL(permission); +EXPORT_SYMBOL(vfs_permission); EXPORT_SYMBOL(inode_setattr); EXPORT_SYMBOL(inode_change_ok); EXPORT_SYMBOL(write_inode_now); @@ -185,7 +185,6 @@ EXPORT_SYMBOL(getblk); EXPORT_SYMBOL(bdget); EXPORT_SYMBOL(bdput); EXPORT_SYMBOL(bread); -EXPORT_SYMBOL(breada); EXPORT_SYMBOL(__brelse); EXPORT_SYMBOL(__bforget); EXPORT_SYMBOL(ll_rw_block); @@ -369,7 +368,6 @@ EXPORT_SYMBOL(del_timer_sync); EXPORT_SYMBOL(mod_timer); EXPORT_SYMBOL(tq_timer); EXPORT_SYMBOL(tq_immediate); -EXPORT_SYMBOL(tq_scheduler); #ifdef CONFIG_SMP /* Various random spinlocks we want to export */ diff --git a/kernel/module.c b/kernel/module.c index e89149de9..dd02b40cd 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -100,7 +100,8 @@ void inter_module_register(const char *im_name, struct module *owner, const void spin_unlock(&ime_lock); kfree(ime_new); /* Program logic error, fatal */ - panic("inter_module_register: duplicate im_name '%s'", im_name); + printk(KERN_ERR "inter_module_register: duplicate im_name '%s'", im_name); + BUG(); } } list_add(&(ime_new->list), &ime_list); @@ -140,7 +141,8 @@ void inter_module_unregister(const char *im_name) } else { /* Program logic error, fatal */ - panic("inter_module_unregister: no entry for '%s'", im_name); + printk(KERN_ERR "inter_module_unregister: no entry for '%s'", im_name); + BUG(); } } @@ -211,7 +213,8 @@ void inter_module_put(const char *im_name) } } spin_unlock(&ime_lock); - panic("inter_module_put: no entry for '%s'", im_name); + printk(KERN_ERR "inter_module_put: no entry for '%s'", im_name); + BUG(); } @@ -480,7 +483,9 @@ sys_init_module(const char *name_user, struct module *mod_user) /* Ok, that's about all the sanity we can stomach; copy the rest. */ - if (copy_from_user(mod+1, mod_user+1, mod->size-sizeof(*mod))) { + if (copy_from_user((char *)mod+mod_user_size, + (char *)mod_user+mod_user_size, + mod->size-mod_user_size)) { error = -EFAULT; goto err3; } diff --git a/kernel/sched.c b/kernel/sched.c index 119edeb81..1299c8365 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -197,7 +197,7 @@ static inline int preemption_goodness(struct task_struct * prev, struct task_str /* * This is ugly, but reschedule_idle() is very timing-critical. - * We `are called with the runqueue spinlock held and we must + * 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)); @@ -272,8 +272,10 @@ send_now_idle: } tsk = target_tsk; if (tsk) { - if (oldest_idle != -1ULL) + if (oldest_idle != -1ULL) { + best_cpu = tsk->processor; goto send_now_idle; + } tsk->need_resched = 1; if (tsk->processor != this_cpu) smp_send_reschedule(tsk->processor); @@ -452,7 +454,7 @@ static inline void __schedule_tail(struct task_struct *prev) goto needs_resched; out_unlock: - task_unlock(prev); + task_unlock(prev); /* Synchronise here with release_task() if prev is TASK_ZOMBIE */ return; /* @@ -511,10 +513,7 @@ asmlinkage void schedule(void) int this_cpu, c; if (!current->active_mm) BUG(); - if (tq_scheduler) - goto handle_tq_scheduler; -tq_scheduler_back: - +need_resched_back: prev = current; this_cpu = prev->processor; @@ -652,7 +651,7 @@ still_running_back: same_process: reacquire_kernel_lock(current); if (current->need_resched) - goto tq_scheduler_back; + goto need_resched_back; return; @@ -677,15 +676,6 @@ handle_softirq: do_softirq(); goto handle_softirq_back; -handle_tq_scheduler: - /* - * do not run the task queue with disabled interrupts, - * cli() wouldn't work on SMP - */ - sti(); - run_task_queue(&tq_scheduler); - goto tq_scheduler_back; - move_rr_last: if (!prev->counter) { prev->counter = NICE_TO_TICKS(prev->nice); diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 5591ee2bc..3fdf03c34 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -41,7 +41,7 @@ /* External variables not in a header file. */ extern int panic_timeout; -extern int console_loglevel, C_A_D; +extern int C_A_D; extern int bdf_prm[], bdflush_min[], bdflush_max[]; extern int sysctl_overcommit_memory; extern int max_threads; diff --git a/kernel/timer.c b/kernel/timer.c index e67783a30..579b065f3 100644 --- a/kernel/timer.c +++ b/kernel/timer.c @@ -39,7 +39,6 @@ int tickadj = 500/HZ ? : 1; /* microsecs */ DECLARE_TASK_QUEUE(tq_timer); DECLARE_TASK_QUEUE(tq_immediate); -DECLARE_TASK_QUEUE(tq_scheduler); /* * phase-lock loop variables @@ -681,7 +680,7 @@ void do_timer(struct pt_regs *regs) update_process_times(user_mode(regs)); #endif mark_bh(TIMER_BH); - if (tq_timer) + if (TQ_ACTIVE(tq_timer)) mark_bh(TQUEUE_BH); } diff --git a/kernel/user.c b/kernel/user.c index d033c9659..be99b110e 100644 --- a/kernel/user.c +++ b/kernel/user.c @@ -8,7 +8,6 @@ * able to have per-user limits for system resources. */ -#include <linux/config.h> #include <linux/init.h> #include <linux/sched.h> #include <linux/slab.h> @@ -74,29 +73,12 @@ static inline struct user_struct *uid_hash_find(uid_t uid, struct user_struct ** } } -/* - * For SMP, we need to re-test the user struct counter - * after having acquired the spinlock. This allows us to do - * the common case (not freeing anything) without having - * any locking. - */ -#ifdef CONFIG_SMP - #define uid_hash_free(up) (!atomic_read(&(up)->__count)) -#else - #define uid_hash_free(up) (1) -#endif - void free_uid(struct user_struct *up) { - if (up) { - if (atomic_dec_and_test(&up->__count)) { - spin_lock(&uidhash_lock); - if (uid_hash_free(up)) { - uid_hash_remove(up); - kmem_cache_free(uid_cachep, up); - } - spin_unlock(&uidhash_lock); - } + if (up && atomic_dec_and_lock(&up->__count, &uidhash_lock)) { + uid_hash_remove(up); + kmem_cache_free(uid_cachep, up); + spin_unlock(&uidhash_lock); } } |