diff options
Diffstat (limited to 'kernel/kthread.c')
-rw-r--r-- | kernel/kthread.c | 69 |
1 files changed, 28 insertions, 41 deletions
diff --git a/kernel/kthread.c b/kernel/kthread.c index bc5d1f0b25a4..9b1a7de26979 100644 --- a/kernel/kthread.c +++ b/kernel/kthread.c | |||
@@ -35,17 +35,13 @@ struct kthread_create_info | |||
35 | struct list_head list; | 35 | struct list_head list; |
36 | }; | 36 | }; |
37 | 37 | ||
38 | struct kthread_stop_info | 38 | struct kthread { |
39 | { | 39 | int should_stop; |
40 | struct task_struct *k; | 40 | struct completion exited; |
41 | int err; | ||
42 | struct completion done; | ||
43 | }; | 41 | }; |
44 | 42 | ||
45 | /* Thread stopping is done by setthing this var: lock serializes | 43 | #define to_kthread(tsk) \ |
46 | * multiple kthread_stop calls. */ | 44 | container_of((tsk)->vfork_done, struct kthread, exited) |
47 | static DEFINE_MUTEX(kthread_stop_lock); | ||
48 | static struct kthread_stop_info kthread_stop_info; | ||
49 | 45 | ||
50 | /** | 46 | /** |
51 | * kthread_should_stop - should this kthread return now? | 47 | * kthread_should_stop - should this kthread return now? |
@@ -56,20 +52,22 @@ static struct kthread_stop_info kthread_stop_info; | |||
56 | */ | 52 | */ |
57 | int kthread_should_stop(void) | 53 | int kthread_should_stop(void) |
58 | { | 54 | { |
59 | return (kthread_stop_info.k == current); | 55 | return to_kthread(current)->should_stop; |
60 | } | 56 | } |
61 | EXPORT_SYMBOL(kthread_should_stop); | 57 | EXPORT_SYMBOL(kthread_should_stop); |
62 | 58 | ||
63 | static int kthread(void *_create) | 59 | static int kthread(void *_create) |
64 | { | 60 | { |
61 | /* Copy data: it's on kthread's stack */ | ||
65 | struct kthread_create_info *create = _create; | 62 | struct kthread_create_info *create = _create; |
66 | int (*threadfn)(void *data); | 63 | int (*threadfn)(void *data) = create->threadfn; |
67 | void *data; | 64 | void *data = create->data; |
68 | int ret = -EINTR; | 65 | struct kthread self; |
66 | int ret; | ||
69 | 67 | ||
70 | /* Copy data: it's on kthread's stack */ | 68 | self.should_stop = 0; |
71 | threadfn = create->threadfn; | 69 | init_completion(&self.exited); |
72 | data = create->data; | 70 | current->vfork_done = &self.exited; |
73 | 71 | ||
74 | /* OK, tell user we're spawned, wait for stop or wakeup */ | 72 | /* OK, tell user we're spawned, wait for stop or wakeup */ |
75 | __set_current_state(TASK_UNINTERRUPTIBLE); | 73 | __set_current_state(TASK_UNINTERRUPTIBLE); |
@@ -77,15 +75,12 @@ static int kthread(void *_create) | |||
77 | complete(&create->done); | 75 | complete(&create->done); |
78 | schedule(); | 76 | schedule(); |
79 | 77 | ||
80 | if (!kthread_should_stop()) | 78 | ret = -EINTR; |
79 | if (!self.should_stop) | ||
81 | ret = threadfn(data); | 80 | ret = threadfn(data); |
82 | 81 | ||
83 | /* It might have exited on its own, w/o kthread_stop. Check. */ | 82 | /* we can't just return, we must preserve "self" on stack */ |
84 | if (kthread_should_stop()) { | 83 | do_exit(ret); |
85 | kthread_stop_info.err = ret; | ||
86 | complete(&kthread_stop_info.done); | ||
87 | } | ||
88 | return 0; | ||
89 | } | 84 | } |
90 | 85 | ||
91 | static void create_kthread(struct kthread_create_info *create) | 86 | static void create_kthread(struct kthread_create_info *create) |
@@ -195,30 +190,22 @@ EXPORT_SYMBOL(kthread_bind); | |||
195 | */ | 190 | */ |
196 | int kthread_stop(struct task_struct *k) | 191 | int kthread_stop(struct task_struct *k) |
197 | { | 192 | { |
193 | struct kthread *kthread; | ||
198 | int ret; | 194 | int ret; |
199 | 195 | ||
200 | mutex_lock(&kthread_stop_lock); | ||
201 | |||
202 | /* It could exit after stop_info.k set, but before wake_up_process. */ | ||
203 | get_task_struct(k); | ||
204 | |||
205 | trace_sched_kthread_stop(k); | 196 | trace_sched_kthread_stop(k); |
197 | get_task_struct(k); | ||
206 | 198 | ||
207 | /* Must init completion *before* thread sees kthread_stop_info.k */ | 199 | kthread = to_kthread(k); |
208 | init_completion(&kthread_stop_info.done); | 200 | barrier(); /* it might have exited */ |
209 | smp_wmb(); | 201 | if (k->vfork_done != NULL) { |
202 | kthread->should_stop = 1; | ||
203 | wake_up_process(k); | ||
204 | wait_for_completion(&kthread->exited); | ||
205 | } | ||
206 | ret = k->exit_code; | ||
210 | 207 | ||
211 | /* Now set kthread_should_stop() to true, and wake it up. */ | ||
212 | kthread_stop_info.k = k; | ||
213 | wake_up_process(k); | ||
214 | put_task_struct(k); | 208 | put_task_struct(k); |
215 | |||
216 | /* Once it dies, reset stop ptr, gather result and we're done. */ | ||
217 | wait_for_completion(&kthread_stop_info.done); | ||
218 | kthread_stop_info.k = NULL; | ||
219 | ret = kthread_stop_info.err; | ||
220 | mutex_unlock(&kthread_stop_lock); | ||
221 | |||
222 | trace_sched_kthread_stop_ret(ret); | 209 | trace_sched_kthread_stop_ret(ret); |
223 | 210 | ||
224 | return ret; | 211 | return ret; |