From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753644AbXDNDPS (ORCPT ); Fri, 13 Apr 2007 23:15:18 -0400 Received: (majordomo@vger.kernel.org) by vger.kernel.org id S1753748AbXDNDPS (ORCPT ); Fri, 13 Apr 2007 23:15:18 -0400 Received: from ebiederm.dsl.xmission.com ([166.70.28.69]:42135 "EHLO ebiederm.dsl.xmission.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753644AbXDNDPO (ORCPT ); Fri, 13 Apr 2007 23:15:14 -0400 From: ebiederm@xmission.com (Eric W. Biederman) To: Oleg Nesterov Cc: Andrew Morton , Davide Libenzi , "Eric W. Biederman" , Ingo Molnar , Linus Torvalds , "Rafael J. Wysocki" , Roland McGrath , Rusty Russell , linux-kernel@vger.kernel.org, Subject: [PATCH] kthread: Enhance kthread_stop to abort interruptible sleeps References: <20070413130236.GA173@tv-sign.ru> Date: Fri, 13 Apr 2007 21:13:13 -0600 In-Reply-To: <20070413130236.GA173@tv-sign.ru> (Oleg Nesterov's message of "Fri, 13 Apr 2007 17:02:36 +0400") Message-ID: User-Agent: Gnus/5.110006 (No Gnus v0.6) Emacs/21.4 (gnu/linux) MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Sender: linux-kernel-owner@vger.kernel.org X-Mailing-List: linux-kernel@vger.kernel.org This patch reworks kthread_stop so it is more flexible and it causes the target kthread to abort interruptible sleeps. Allowing a larger class of kernel threads to use to the kthread API. The changes start by defining TIF_KTHREAD_STOP on all architectures. TIF_KTHREAD_STOP is a per process flag that I can set from another process to indicate that a kernel thread should stop. wake_up_process in kthread_stop has been replaced by signal_wake_up ensuring that the kernel thread if sleeping is woken up in a timely manner and with TIF_SIGNAL_PENDING set, which causes us to break out of interruptible sleeps. recalc_signal_pending was modified to keep TIF_SIGNAL_PENDING set for as long as TIF_KTHREAD_STOP is set. Arbitrary paths to do_exit are now allowed. I have placed a completion on the thread stack and pointed vfork_done at it, when the mm_release is called from do_exit the completion will be called. Since the completion is stored on the stack it is important that kthread() now calls do_exit ensuring the stack frame that holds the completion is never released, and so that our exit_code is certain to make it unchanged all the way to do_exit. To allow kthread_stop to read the process exit code when exit_mm wakes it up I have moved the setting of exit_code to the beginning of do_exit. Signed-off-by: Eric W. Biederman --- include/asm-alpha/thread_info.h | 1 + include/asm-arm/thread_info.h | 1 + include/asm-arm26/thread_info.h | 1 + include/asm-avr32/thread_info.h | 1 + include/asm-cris/thread_info.h | 1 + include/asm-frv/thread_info.h | 1 + include/asm-h8300/thread_info.h | 1 + include/asm-i386/thread_info.h | 1 + include/asm-ia64/thread_info.h | 1 + include/asm-m32r/thread_info.h | 1 + include/asm-m68k/thread_info.h | 1 + include/asm-m68knommu/thread_info.h | 1 + include/asm-mips/thread_info.h | 1 + include/asm-parisc/thread_info.h | 1 + include/asm-powerpc/thread_info.h | 1 + include/asm-s390/thread_info.h | 1 + include/asm-sh/thread_info.h | 1 + include/asm-sh64/thread_info.h | 1 + include/asm-sparc/thread_info.h | 1 + include/asm-sparc64/thread_info.h | 1 + include/asm-um/thread_info.h | 1 + include/asm-v850/thread_info.h | 1 + include/asm-x86_64/thread_info.h | 1 + include/asm-xtensa/thread_info.h | 1 + include/linux/kthread.h | 20 ++++++++- kernel/exit.c | 2 +- kernel/kthread.c | 81 ++++++++++++----------------------- kernel/signal.c | 3 +- 28 files changed, 72 insertions(+), 58 deletions(-) diff --git a/include/asm-alpha/thread_info.h b/include/asm-alpha/thread_info.h index 69ffd93..31c41e0 100644 --- a/include/asm-alpha/thread_info.h +++ b/include/asm-alpha/thread_info.h @@ -76,6 +76,7 @@ register struct thread_info *__current_thread_info __asm__("$8"); #define TIF_UAC_NOFIX 7 #define TIF_UAC_SIGBUS 8 #define TIF_MEMDIE 9 +#define TIF_KTHREAD_STOP 10 #define _TIF_SYSCALL_TRACE (1<flags |= PF_EXITING; + tsk->exit_code = code; if (unlikely(in_atomic())) printk(KERN_INFO "note: %s[%d] exited with preempt_count %d\n", @@ -937,7 +938,6 @@ fastcall NORET_TYPE void do_exit(long code) if (tsk->binfmt) module_put(tsk->binfmt->module); - tsk->exit_code = code; proc_exit_connector(tsk); exit_task_namespaces(tsk); exit_notify(tsk); diff --git a/kernel/kthread.c b/kernel/kthread.c index df8a8e8..06c7e51 100644 --- a/kernel/kthread.c +++ b/kernel/kthread.c @@ -33,38 +33,18 @@ struct kthread_create_info struct list_head list; }; -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 DEFINE_MUTEX(kthread_stop_lock); -static struct kthread_stop_info kthread_stop_info; - -/** - * 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) -{ - return (kthread_stop_info.k == current); -} -EXPORT_SYMBOL(kthread_should_stop); - static int kthread(void *_create) { struct kthread_create_info *create = _create; + struct completion done; int (*threadfn)(void *data); void *data; int ret = -EINTR; + /* Setup a completion on this thread's stack */ + init_completion(&done); + current->vfork_done = &done; + /* Copy data: it's on kthread's stack */ threadfn = create->threadfn; data = create->data; @@ -77,12 +57,7 @@ static int kthread(void *_create) if (!kthread_should_stop()) ret = threadfn(data); - /* 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; + do_exit(ret); } static void create_kthread(struct kthread_create_info *create) @@ -104,7 +79,7 @@ static void create_kthread(struct kthread_create_info *create) /** * kthread_create - create a kthread. - * @threadfn: the function to run until signal_pending(current). + * @threadfn: the function to run until kthread_should_stop(). * @data: data ptr for @threadfn. * @namefmt: printf-style name for the thread. * @@ -174,38 +149,36 @@ EXPORT_SYMBOL(kthread_bind); * kthread_stop - stop a thread created by kthread_create(). * @k: thread created by kthread_create(). * - * 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(). + * Sets TIF_KTHREAD_STOP for @k, wakes it and waits for it to exit. + * + * kthread_stop should be called exactly once on @k. + * + * This can also be called after kthread_create() instead of calling + * wake_up_process(): the thread will exit without calling threadfn(). + * + * If your threadfn may terminate before you call kthread_stop + * you should call get_task_struct on @k so it is valid when you call + * kthread_stop. * * Returns the result of threadfn(), or %-EINTR if wake_up_process() * was never called. */ -int kthread_stop(struct task_struct *k) +int kthread_stop(struct task_struct *tsk) { int ret; - mutex_lock(&kthread_stop_lock); - - /* It could exit after stop_info.k set, but before wake_up_process. */ - get_task_struct(k); + /* Ensure the task struct persists until I read the exit code. */ + get_task_struct(tsk); - /* Must init completion *before* thread sees kthread_stop_info.k */ - init_completion(&kthread_stop_info.done); - smp_wmb(); + set_tsk_thread_flag(tsk, TIF_KTHREAD_STOP); + spin_lock_irq(&tsk->sighand->siglock); + signal_wake_up(tsk, 1); + spin_unlock_irq(&tsk->sighand->siglock); - /* Now set kthread_should_stop() to true, and wake it up. */ - kthread_stop_info.k = k; - wake_up_process(k); - put_task_struct(k); + wait_for_completion(tsk->vfork_done); + ret = tsk->exit_code; - /* 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; - mutex_unlock(&kthread_stop_lock); + put_task_struct(tsk); return ret; } diff --git a/kernel/signal.c b/kernel/signal.c index 2d437f6..fca2a45 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -218,7 +219,7 @@ static inline int has_pending_signals(sigset_t *signal, sigset_t *blocked) fastcall void recalc_sigpending_tsk(struct task_struct *t) { if (t->signal->group_stop_count > 0 || - (freezing(t)) || + (freezing(t)) || __kthread_should_stop(t) || PENDING(&t->pending, &t->blocked) || PENDING(&t->signal->shared_pending, &t->blocked)) set_tsk_thread_flag(t, TIF_SIGPENDING); -- 1.5.0.g53756