diff options
Diffstat (limited to 'kernel/kthread.c')
| -rw-r--r-- | kernel/kthread.c | 58 |
1 files changed, 45 insertions, 13 deletions
diff --git a/kernel/kthread.c b/kernel/kthread.c index be2cc1f9dd57..9d64b6526d0b 100644 --- a/kernel/kthread.c +++ b/kernel/kthread.c | |||
| @@ -53,14 +53,38 @@ enum KTHREAD_BITS { | |||
| 53 | KTHREAD_IS_PARKED, | 53 | KTHREAD_IS_PARKED, |
| 54 | }; | 54 | }; |
| 55 | 55 | ||
| 56 | #define __to_kthread(vfork) \ | 56 | static inline void set_kthread_struct(void *kthread) |
| 57 | container_of(vfork, struct kthread, exited) | 57 | { |
| 58 | /* | ||
| 59 | * We abuse ->set_child_tid to avoid the new member and because it | ||
| 60 | * can't be wrongly copied by copy_process(). We also rely on fact | ||
| 61 | * that the caller can't exec, so PF_KTHREAD can't be cleared. | ||
| 62 | */ | ||
| 63 | current->set_child_tid = (__force void __user *)kthread; | ||
| 64 | } | ||
| 58 | 65 | ||
| 59 | static inline struct kthread *to_kthread(struct task_struct *k) | 66 | static inline struct kthread *to_kthread(struct task_struct *k) |
| 60 | { | 67 | { |
| 61 | return __to_kthread(k->vfork_done); | 68 | WARN_ON(!(k->flags & PF_KTHREAD)); |
| 69 | return (__force void *)k->set_child_tid; | ||
| 70 | } | ||
| 71 | |||
| 72 | void free_kthread_struct(struct task_struct *k) | ||
| 73 | { | ||
| 74 | /* | ||
| 75 | * Can be NULL if this kthread was created by kernel_thread() | ||
| 76 | * or if kmalloc() in kthread() failed. | ||
| 77 | */ | ||
| 78 | kfree(to_kthread(k)); | ||
| 62 | } | 79 | } |
| 63 | 80 | ||
| 81 | #define __to_kthread(vfork) \ | ||
| 82 | container_of(vfork, struct kthread, exited) | ||
| 83 | |||
| 84 | /* | ||
| 85 | * TODO: kill it and use to_kthread(). But we still need the users | ||
| 86 | * like kthread_stop() which has to sync with the exiting kthread. | ||
| 87 | */ | ||
| 64 | static struct kthread *to_live_kthread(struct task_struct *k) | 88 | static struct kthread *to_live_kthread(struct task_struct *k) |
| 65 | { | 89 | { |
| 66 | struct completion *vfork = ACCESS_ONCE(k->vfork_done); | 90 | struct completion *vfork = ACCESS_ONCE(k->vfork_done); |
| @@ -181,14 +205,11 @@ static int kthread(void *_create) | |||
| 181 | int (*threadfn)(void *data) = create->threadfn; | 205 | int (*threadfn)(void *data) = create->threadfn; |
| 182 | void *data = create->data; | 206 | void *data = create->data; |
| 183 | struct completion *done; | 207 | struct completion *done; |
| 184 | struct kthread self; | 208 | struct kthread *self; |
| 185 | int ret; | 209 | int ret; |
| 186 | 210 | ||
| 187 | self.flags = 0; | 211 | self = kmalloc(sizeof(*self), GFP_KERNEL); |
| 188 | self.data = data; | 212 | set_kthread_struct(self); |
| 189 | init_completion(&self.exited); | ||
| 190 | init_completion(&self.parked); | ||
| 191 | current->vfork_done = &self.exited; | ||
| 192 | 213 | ||
| 193 | /* If user was SIGKILLed, I release the structure. */ | 214 | /* If user was SIGKILLed, I release the structure. */ |
| 194 | done = xchg(&create->done, NULL); | 215 | done = xchg(&create->done, NULL); |
| @@ -196,6 +217,19 @@ static int kthread(void *_create) | |||
| 196 | kfree(create); | 217 | kfree(create); |
| 197 | do_exit(-EINTR); | 218 | do_exit(-EINTR); |
| 198 | } | 219 | } |
| 220 | |||
| 221 | if (!self) { | ||
| 222 | create->result = ERR_PTR(-ENOMEM); | ||
| 223 | complete(done); | ||
| 224 | do_exit(-ENOMEM); | ||
| 225 | } | ||
| 226 | |||
| 227 | self->flags = 0; | ||
| 228 | self->data = data; | ||
| 229 | init_completion(&self->exited); | ||
| 230 | init_completion(&self->parked); | ||
| 231 | current->vfork_done = &self->exited; | ||
| 232 | |||
| 199 | /* OK, tell user we're spawned, wait for stop or wakeup */ | 233 | /* OK, tell user we're spawned, wait for stop or wakeup */ |
| 200 | __set_current_state(TASK_UNINTERRUPTIBLE); | 234 | __set_current_state(TASK_UNINTERRUPTIBLE); |
| 201 | create->result = current; | 235 | create->result = current; |
| @@ -203,12 +237,10 @@ static int kthread(void *_create) | |||
| 203 | schedule(); | 237 | schedule(); |
| 204 | 238 | ||
| 205 | ret = -EINTR; | 239 | ret = -EINTR; |
| 206 | 240 | if (!test_bit(KTHREAD_SHOULD_STOP, &self->flags)) { | |
| 207 | if (!test_bit(KTHREAD_SHOULD_STOP, &self.flags)) { | 241 | __kthread_parkme(self); |
| 208 | __kthread_parkme(&self); | ||
| 209 | ret = threadfn(data); | 242 | ret = threadfn(data); |
| 210 | } | 243 | } |
| 211 | /* we can't just return, we must preserve "self" on stack */ | ||
| 212 | do_exit(ret); | 244 | do_exit(ret); |
| 213 | } | 245 | } |
| 214 | 246 | ||
