|
@@ -35,17 +35,13 @@ struct kthread_create_info
|
|
|
struct list_head list;
|
|
|
};
|
|
|
|
|
|
-struct kthread_stop_info
|
|
|
-{
|
|
|
- struct task_struct *k;
|
|
|
- int err;
|
|
|
- struct completion done;
|
|
|
+struct kthread {
|
|
|
+ int should_stop;
|
|
|
+ struct completion exited;
|
|
|
};
|
|
|
|
|
|
-/* 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;
|
|
|
+#define to_kthread(tsk) \
|
|
|
+ container_of((tsk)->vfork_done, struct kthread, exited)
|
|
|
|
|
|
/**
|
|
|
* kthread_should_stop - should this kthread return now?
|
|
@@ -56,20 +52,22 @@ static struct kthread_stop_info kthread_stop_info;
|
|
|
*/
|
|
|
int kthread_should_stop(void)
|
|
|
{
|
|
|
- return (kthread_stop_info.k == current);
|
|
|
+ return to_kthread(current)->should_stop;
|
|
|
}
|
|
|
EXPORT_SYMBOL(kthread_should_stop);
|
|
|
|
|
|
static int kthread(void *_create)
|
|
|
{
|
|
|
+ /* Copy data: it's on kthread's stack */
|
|
|
struct kthread_create_info *create = _create;
|
|
|
- int (*threadfn)(void *data);
|
|
|
- void *data;
|
|
|
- int ret = -EINTR;
|
|
|
+ int (*threadfn)(void *data) = create->threadfn;
|
|
|
+ void *data = create->data;
|
|
|
+ struct kthread self;
|
|
|
+ int ret;
|
|
|
|
|
|
- /* Copy data: it's on kthread's stack */
|
|
|
- threadfn = create->threadfn;
|
|
|
- data = create->data;
|
|
|
+ self.should_stop = 0;
|
|
|
+ init_completion(&self.exited);
|
|
|
+ current->vfork_done = &self.exited;
|
|
|
|
|
|
/* OK, tell user we're spawned, wait for stop or wakeup */
|
|
|
__set_current_state(TASK_UNINTERRUPTIBLE);
|
|
@@ -77,15 +75,12 @@ static int kthread(void *_create)
|
|
|
complete(&create->done);
|
|
|
schedule();
|
|
|
|
|
|
- if (!kthread_should_stop())
|
|
|
+ ret = -EINTR;
|
|
|
+ if (!self.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;
|
|
|
+ /* we can't just return, we must preserve "self" on stack */
|
|
|
+ do_exit(ret);
|
|
|
}
|
|
|
|
|
|
static void create_kthread(struct kthread_create_info *create)
|
|
@@ -195,30 +190,22 @@ EXPORT_SYMBOL(kthread_bind);
|
|
|
*/
|
|
|
int kthread_stop(struct task_struct *k)
|
|
|
{
|
|
|
+ struct kthread *kthread;
|
|
|
int ret;
|
|
|
|
|
|
- mutex_lock(&kthread_stop_lock);
|
|
|
-
|
|
|
- /* It could exit after stop_info.k set, but before wake_up_process. */
|
|
|
- get_task_struct(k);
|
|
|
-
|
|
|
trace_sched_kthread_stop(k);
|
|
|
+ get_task_struct(k);
|
|
|
|
|
|
- /* Must init completion *before* thread sees kthread_stop_info.k */
|
|
|
- init_completion(&kthread_stop_info.done);
|
|
|
- smp_wmb();
|
|
|
+ kthread = to_kthread(k);
|
|
|
+ barrier(); /* it might have exited */
|
|
|
+ if (k->vfork_done != NULL) {
|
|
|
+ kthread->should_stop = 1;
|
|
|
+ wake_up_process(k);
|
|
|
+ wait_for_completion(&kthread->exited);
|
|
|
+ }
|
|
|
+ ret = k->exit_code;
|
|
|
|
|
|
- /* 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;
|
|
|
- mutex_unlock(&kthread_stop_lock);
|
|
|
-
|
|
|
trace_sched_kthread_stop_ret(ret);
|
|
|
|
|
|
return ret;
|