summaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>2001-01-10 05:27:25 +0000
committerRalf Baechle <ralf@linux-mips.org>2001-01-10 05:27:25 +0000
commitc9c06167e7933d93a6e396174c68abf242294abb (patch)
treed9a8bb30663e9a3405a1ef37ffb62bc14b9f019f /kernel
parentf79e8cc3c34e4192a3e5ef4cc9c6542fdef703c0 (diff)
Merge with Linux 2.4.0-test12.
Diffstat (limited to 'kernel')
-rw-r--r--kernel/Makefile7
-rw-r--r--kernel/context.c143
-rw-r--r--kernel/dma.c1
-rw-r--r--kernel/exit.c12
-rw-r--r--kernel/fork.c9
-rw-r--r--kernel/kmod.c121
-rw-r--r--kernel/ksyms.c4
-rw-r--r--kernel/module.c13
-rw-r--r--kernel/sched.c24
-rw-r--r--kernel/sysctl.c2
-rw-r--r--kernel/timer.c3
-rw-r--r--kernel/user.c26
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(&current->sigmask_lock);
- sigfillset(&current->blocked);
- recalc_sigpending(current);
- spin_unlock_irq(&current->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);
}
}