[PATCH] kthread: allow kthread_stop calls to run in parallel

From: Jeff Layton
Date: Sat Jan 12 2008 - 16:10:49 EST


Currently, all kthread_stop calls are serialized. If something calls
kthread_stop on a kthread, no other kthread will be able to be
brought down until the first kthread_stop returns. A "rogue" kthread
could therefore prevent other kthreads from ever coming down.

This patch makes it so that kthread_stop calls can be done in parallel.
To do this, it adds a new pointer to the task struct. This works, but
I'd prefer to use an existing pointer that's never used for kthreads
rather than adding a new one. I'm just not sure what pointer would be
a good candidate for this.

This is really an RFC at this point, so comments and suggestions
are appreciated...

Signed-off-by: Jeff Layton <jlayton@xxxxxxxxxx>
---
include/linux/sched.h | 2 ++
kernel/kthread.c | 33 +++++++++++++--------------------
2 files changed, 15 insertions(+), 20 deletions(-)

diff --git a/include/linux/sched.h b/include/linux/sched.h
index ac3d496..bc38574 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -1178,6 +1178,8 @@ struct task_struct {
int make_it_fail;
#endif
struct prop_local_single dirties;
+
+ struct kthread_stop_info *kthread_stop_info;
};

/*
diff --git a/kernel/kthread.c b/kernel/kthread.c
index dcfe724..12594fa 100644
--- a/kernel/kthread.c
+++ b/kernel/kthread.c
@@ -35,26 +35,23 @@ struct kthread_create_info

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().
+ *
+ * In this implementation, setting the kthread_stop_info pointer to
+ * a non-NULL value means that it should stop.
*/
int kthread_should_stop(void)
{
- return (kthread_stop_info.k == current);
+ return (current->kthread_stop_info != NULL);
}
EXPORT_SYMBOL(kthread_should_stop);

@@ -68,6 +65,7 @@ static int kthread(void *_create)
/* Copy data: it's on kthread's stack */
threadfn = create->threadfn;
data = create->data;
+ current->kthread_stop_info = NULL;

/* OK, tell user we're spawned, wait for stop or wakeup */
__set_current_state(TASK_UNINTERRUPTIBLE);
@@ -79,8 +77,8 @@ static int kthread(void *_create)

/* 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);
+ current->kthread_stop_info->err = ret;
+ complete(&current->kthread_stop_info->done);
}
return 0;
}
@@ -104,7 +102,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.
*
@@ -188,29 +186,24 @@ EXPORT_SYMBOL(kthread_bind);
*/
int kthread_stop(struct task_struct *k)
{
- int ret;
-
- mutex_lock(&kthread_stop_lock);
+ struct kthread_stop_info kstop_info;

/* 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);
+ init_completion(&kstop_info.done);
smp_wmb();

/* Now set kthread_should_stop() to true, and wake it up. */
- kthread_stop_info.k = k;
+ k->kthread_stop_info = &kstop_info;
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;
- mutex_unlock(&kthread_stop_lock);
+ wait_for_completion(&kstop_info.done);

- return ret;
+ return kstop_info.err;
}
EXPORT_SYMBOL(kthread_stop);

--
1.5.3.7

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/