From: Rusty Russell Avoid signal usage and thread adoption in kthread code. This version doesn't use them, preferring a kthread_should_stop() function instead. --- 25-akpm/include/linux/kthread.h | 20 +++++- 25-akpm/kernel/kthread.c | 120 +++++++++++++++------------------------- 25-akpm/kernel/module.c | 2 25-akpm/kernel/sched.c | 2 25-akpm/kernel/softirq.c | 2 25-akpm/kernel/workqueue.c | 2 6 files changed, 66 insertions(+), 82 deletions(-) diff -puN include/linux/kthread.h~kthread-stop-using-signals include/linux/kthread.h --- 25/include/linux/kthread.h~kthread-stop-using-signals Tue Feb 17 15:52:09 2004 +++ 25-akpm/include/linux/kthread.h Tue Feb 17 15:52:09 2004 @@ -17,7 +17,7 @@ * When woken, the thread will run @threadfn() with @data as its * argument. @threadfn can either call do_exit() directly if it is a * standalone thread for which noone will call kthread_stop(), or - * return when 'signal_pending(current)' is true (which means + * return when 'kthread_should_stop()' is true (which means * kthread_stop() has been called). The return value should be zero * or a negative error number: it will be passed to kthread_stop(). * @@ -59,13 +59,23 @@ void kthread_bind(struct task_struct *k, * kthread_stop: stop a thread created by kthread_create(). * @k: thread created by kthread_create(). * - * Sends a signal to @k, and waits for it to exit. Your threadfn() - * must not call do_exit() itself if you use this function! This can - * also be called after kthread_create() instead of calling - * wake_up_process(): the thread will exit without calling threadfn(). + * Sets kthread_should_stop() for @k to return true, wakes it, and + * waits for it to exit. Your threadfn() must not call do_exit() + * itself if you use this function! This can also be called after + * kthread_create() instead of calling wake_up_process(): the thread + * will exit without calling threadfn(). * * Returns the result of threadfn(), or -EINTR if wake_up_process() * was never called. */ int kthread_stop(struct task_struct *k); +/** + * kthread_should_stop: should this kthread return now? + * + * When someone calls kthread_stop on your kthread, it will be woken + * and this will return true. You should then return, and your return + * value will be passed through to kthread_stop(). + */ +int kthread_should_stop(void); + #endif /* _LINUX_KTHREAD_H */ diff -puN kernel/kthread.c~kthread-stop-using-signals kernel/kthread.c --- 25/kernel/kthread.c~kthread-stop-using-signals Tue Feb 17 15:52:09 2004 +++ 25-akpm/kernel/kthread.c Tue Feb 17 15:52:09 2004 @@ -1,12 +1,10 @@ /* Kernel thread helper functions. * Copyright (C) 2004 IBM Corporation, Rusty Russell. * - * Everything is done via keventd, so that we get a clean environment + * Creation is done via keventd, so that we get a clean environment * even if we're invoked from userspace (think modprobe, hotplug cpu, - * etc.). Also, it allows us to wait for dying kthreads without side - * effects involved in adopting kthreads to random processes. + * etc.). */ -#define __KERNEL_SYSCALLS__ #include #include #include @@ -26,7 +24,23 @@ struct kthread_create_info struct completion done; }; -/* Returns so that WEXITSTATUS(ret) == errno. */ +struct kthread_stop_info +{ + struct task_struct *k; + int err; + struct completion done; +}; + +/* Thread stopping is done by setthing this var: lock serializes + * multiple kthread_stop calls. */ +static DECLARE_MUTEX(kthread_stop_lock); +static struct kthread_stop_info kthread_stop_info; + +int kthread_should_stop(void) +{ + return (kthread_stop_info.k == current); +} + static int kthread(void *_create) { struct kthread_create_info *create = _create; @@ -53,10 +67,15 @@ static int kthread(void *_create) complete(&create->started); schedule(); - while (!signal_pending(current)) + if (!kthread_should_stop()) ret = threadfn(data); - return (-ret) << 8; + /* It might have exited on its own, w/o kthread_stop. Check. */ + if (kthread_should_stop()) { + kthread_stop_info.err = ret; + complete(&kthread_stop_info.done); + } + return 0; } /* We are keventd: create a thread. */ @@ -77,60 +96,6 @@ static void keventd_create_kthread(void complete(&create->done); } -struct kthread_stop_info -{ - struct task_struct *k; - int result; - struct completion done; -}; - -/* "to look upon me as her own dad -- in a very real, and legally - binding sense." - Michael Palin */ -static void adopt_kthread(struct task_struct *k) -{ - write_lock_irq(&tasklist_lock); - REMOVE_LINKS(k); - k->parent = current; - k->real_parent = current; - SET_LINKS(k); - write_unlock_irq(&tasklist_lock); -} - -/* We are keventd: stop the thread. */ -static void keventd_stop_kthread(void *_stop) -{ - struct kthread_stop_info *stop = _stop; - int status, pid; - sigset_t chldonly, oldset; - struct k_sigaction sa, oldsa; - - /* Install a handler so SIGCHLD is actually delivered */ - sa.sa.sa_handler = SIG_DFL; - sa.sa.sa_flags = 0; - siginitset(&sa.sa.sa_mask, sigmask(SIGCHLD)); - siginitset(&chldonly, sigmask(SIGCHLD)); - do_sigaction(SIGCHLD, &sa, &oldsa); - sigprocmask(SIG_UNBLOCK, &chldonly, &oldset); - - adopt_kthread(stop->k); - /* Grab pid now: after waitpid(), stop->k is invalid. */ - pid = stop->k->tgid; - - /* All signals are blocked, hence the force. */ - force_sig(SIGTERM, stop->k); - /* Other threads might exit: if we ask for one pid that - * returns -ERESTARTSYS. */ - while (waitpid(-1, &status, __WALL) != pid) - flush_signals(current); - stop->result = -((status >> 8) & 0xFF); - complete(&stop->done); - - /* Return to normal, then reap any children who died in the race. */ - sigprocmask(SIG_SETMASK, &oldset, NULL); - do_sigaction(SIGCHLD, &oldsa, NULL); - while (waitpid(-1, &status, __WALL|WNOHANG) > 0); -} - struct task_struct *kthread_create(int (*threadfn)(void *data), void *data, const char namefmt[], @@ -172,18 +137,27 @@ void kthread_bind(struct task_struct *k, int kthread_stop(struct task_struct *k) { - struct kthread_stop_info stop; - DECLARE_WORK(work, keventd_stop_kthread, &stop); + int ret; - stop.k = k; - init_completion(&stop.done); + down(&kthread_stop_lock); - /* At boot, if CPUs fail to come up, this happens. */ - if (!keventd_up()) - work.func(work.data); - else { - schedule_work(&work); - wait_for_completion(&stop.done); - } - return stop.result; + /* It could exit after stop_info.k set, but before wake_up_process. */ + get_task_struct(k); + + /* Must init completion *before* thread sees kthread_stop_info.k */ + init_completion(&kthread_stop_info.done); + wmb(); + + /* Now set kthread_should_stop() to true, and wake it up. */ + kthread_stop_info.k = k; + wake_up_process(k); + put_task_struct(k); + + /* Once it dies, reset stop ptr, gather result and we're done. */ + wait_for_completion(&kthread_stop_info.done); + kthread_stop_info.k = NULL; + ret = kthread_stop_info.err; + up(&kthread_stop_lock); + + return ret; } diff -puN kernel/module.c~kthread-stop-using-signals kernel/module.c --- 25/kernel/module.c~kthread-stop-using-signals Tue Feb 17 15:52:09 2004 +++ 25-akpm/kernel/module.c Tue Feb 17 15:52:09 2004 @@ -624,7 +624,7 @@ static int spawn_stopref(void *data) out: /* Wait for kthread_stop */ - while (!signal_pending(current)) { + while (!kthread_should_stop()) { __set_current_state(TASK_INTERRUPTIBLE); schedule(); } diff -puN kernel/sched.c~kthread-stop-using-signals kernel/sched.c --- 25/kernel/sched.c~kthread-stop-using-signals Tue Feb 17 15:52:09 2004 +++ 25-akpm/kernel/sched.c Tue Feb 17 15:52:09 2004 @@ -3118,7 +3118,7 @@ static int migration_thread(void * data) rq = this_rq(); BUG_ON(rq->migration_thread != current); - while (!signal_pending(current)) { + while (!kthread_should_stop()) { struct list_head *head; migration_req_t *req; diff -puN kernel/softirq.c~kthread-stop-using-signals kernel/softirq.c --- 25/kernel/softirq.c~kthread-stop-using-signals Tue Feb 17 15:52:09 2004 +++ 25-akpm/kernel/softirq.c Tue Feb 17 15:52:09 2004 @@ -356,7 +356,7 @@ static int ksoftirqd(void * __bind_cpu) set_current_state(TASK_INTERRUPTIBLE); - while (!signal_pending(current)) { + while (!kthread_should_stop()) { if (!local_softirq_pending()) schedule(); diff -puN kernel/workqueue.c~kthread-stop-using-signals kernel/workqueue.c --- 25/kernel/workqueue.c~kthread-stop-using-signals Tue Feb 17 15:52:09 2004 +++ 25-akpm/kernel/workqueue.c Tue Feb 17 15:52:09 2004 @@ -177,7 +177,7 @@ static int worker_thread(void *__cwq) siginitset(&sa.sa.sa_mask, sigmask(SIGCHLD)); do_sigaction(SIGCHLD, &sa, (struct k_sigaction *)0); - while (!signal_pending(current)) { + while (!kthread_should_stop()) { set_task_state(current, TASK_INTERRUPTIBLE); add_wait_queue(&cwq->more_work, &wait); _