From: Rusty Russell <rusty@rustcorp.com.au>

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 <linux/sched.h>
 #include <linux/kthread.h>
 #include <linux/completion.h>
@@ -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);

_