linux-bk/kernel/context.c
<<
>>
Prefs
   1/*
   2 * linux/kernel/context.c
   3 *
   4 * Mechanism for running arbitrary tasks in process context
   5 *
   6 * dwmw2@redhat.com:            Genesis
   7 *
   8 * andrewm@uow.edu.au:          2.4.0-test12
   9 *      - Child reaping
  10 *      - Support for tasks which re-add themselves
  11 *      - flush_scheduled_tasks.
  12 */
  13
  14#define __KERNEL_SYSCALLS__
  15
  16#include <linux/module.h>
  17#include <linux/kernel.h>
  18#include <linux/sched.h>
  19#include <linux/init.h>
  20#include <linux/unistd.h>
  21#include <linux/signal.h>
  22#include <linux/completion.h>
  23#include <linux/tqueue.h>
  24
  25static DECLARE_TASK_QUEUE(tq_context);
  26static DECLARE_WAIT_QUEUE_HEAD(context_task_wq);
  27static DECLARE_WAIT_QUEUE_HEAD(context_task_done);
  28static int keventd_running;
  29static struct task_struct *keventd_task;
  30
  31static int need_keventd(const char *who)
  32{
  33        if (keventd_running == 0)
  34                printk(KERN_ERR "%s(): keventd has not started\n", who);
  35        return keventd_running;
  36}
  37        
  38int current_is_keventd(void)
  39{
  40        int ret = 0;
  41        if (need_keventd(__FUNCTION__))
  42                ret = (current == keventd_task);
  43        return ret;
  44}
  45
  46/**
  47 * schedule_task - schedule a function for subsequent execution in process context.
  48 * @task: pointer to a &tq_struct which defines the function to be scheduled.
  49 *
  50 * May be called from interrupt context.  The scheduled function is run at some
  51 * time in the near future by the keventd kernel thread.  If it can sleep, it
  52 * should be designed to do so for the minimum possible time, as it will be
  53 * stalling all other scheduled tasks.
  54 *
  55 * schedule_task() returns non-zero if the task was successfully scheduled.
  56 * If @task is already residing on a task queue then schedule_task() fails
  57 * to schedule your task and returns zero.
  58 */
  59int schedule_task(struct tq_struct *task)
  60{
  61        int ret;
  62        need_keventd(__FUNCTION__);
  63        ret = queue_task(task, &tq_context);
  64        wake_up(&context_task_wq);
  65        return ret;
  66}
  67
  68static int context_thread(void *startup)
  69{
  70        struct task_struct *curtask = current;
  71        DECLARE_WAITQUEUE(wait, curtask);
  72        struct k_sigaction sa;
  73
  74        daemonize();
  75        strcpy(curtask->comm, "keventd");
  76        current->flags |= PF_IOTHREAD;
  77        keventd_running = 1;
  78        keventd_task = curtask;
  79
  80        spin_lock_irq(&curtask->sigmask_lock);
  81        siginitsetinv(&curtask->blocked, sigmask(SIGCHLD));
  82        recalc_sigpending();
  83        spin_unlock_irq(&curtask->sigmask_lock);
  84
  85        complete((struct completion *)startup);
  86
  87        /* Install a handler so SIGCLD is delivered */
  88        sa.sa.sa_handler = SIG_IGN;
  89        sa.sa.sa_flags = 0;
  90        siginitset(&sa.sa.sa_mask, sigmask(SIGCHLD));
  91        do_sigaction(SIGCHLD, &sa, (struct k_sigaction *)0);
  92
  93        /*
  94         * If one of the functions on a task queue re-adds itself
  95         * to the task queue we call schedule() in state TASK_RUNNING
  96         */
  97        for (;;) {
  98                set_task_state(curtask, TASK_INTERRUPTIBLE);
  99                add_wait_queue(&context_task_wq, &wait);
 100                if (TQ_ACTIVE(tq_context))
 101                        set_task_state(curtask, TASK_RUNNING);
 102                schedule();
 103                remove_wait_queue(&context_task_wq, &wait);
 104                run_task_queue(&tq_context);
 105                wake_up(&context_task_done);
 106                if (signal_pending(curtask)) {
 107                        while (waitpid(-1, (unsigned int *)0, __WALL|WNOHANG) > 0)
 108                                ;
 109                        spin_lock_irq(&curtask->sigmask_lock);
 110                        flush_signals(curtask);
 111                        recalc_sigpending();
 112                        spin_unlock_irq(&curtask->sigmask_lock);
 113                }
 114        }
 115}
 116
 117/**
 118 * flush_scheduled_tasks - ensure that any scheduled tasks have run to completion.
 119 *
 120 * Forces execution of the schedule_task() queue and blocks until its completion.
 121 *
 122 * If a kernel subsystem uses schedule_task() and wishes to flush any pending
 123 * tasks, it should use this function.  This is typically used in driver shutdown
 124 * handlers.
 125 *
 126 * The caller should hold no spinlocks and should hold no semaphores which could
 127 * cause the scheduled tasks to block.
 128 */
 129static struct tq_struct dummy_task;
 130
 131void flush_scheduled_tasks(void)
 132{
 133        int count;
 134        DECLARE_WAITQUEUE(wait, current);
 135
 136        /*
 137         * Do it twice. It's possible, albeit highly unlikely, that
 138         * the caller queued a task immediately before calling us,
 139         * and that the eventd thread was already past the run_task_queue()
 140         * but not yet into wake_up(), so it woke us up before completing
 141         * the caller's queued task or our new dummy task.
 142         */
 143        add_wait_queue(&context_task_done, &wait);
 144        for (count = 0; count < 2; count++) {
 145                set_current_state(TASK_UNINTERRUPTIBLE);
 146
 147                /* Queue a dummy task to make sure we get kicked */
 148                schedule_task(&dummy_task);
 149
 150                /* Wait for it to complete */
 151                schedule();
 152        }
 153        remove_wait_queue(&context_task_done, &wait);
 154}
 155        
 156int start_context_thread(void)
 157{
 158        static struct completion startup __initdata = COMPLETION_INITIALIZER(startup);
 159
 160        kernel_thread(context_thread, &startup, CLONE_FS | CLONE_FILES);
 161        wait_for_completion(&startup);
 162        return 0;
 163}
 164
 165EXPORT_SYMBOL(schedule_task);
 166EXPORT_SYMBOL(flush_scheduled_tasks);
 167
 168
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.