diff options
Diffstat (limited to 'kernel/kthread.c')
| -rw-r--r-- | kernel/kthread.c | 89 | 
1 files changed, 61 insertions, 28 deletions
diff --git a/kernel/kthread.c b/kernel/kthread.c index 7113003fab63..a2c156ee8275 100644 --- a/kernel/kthread.c +++ b/kernel/kthread.c @@ -52,6 +52,7 @@ struct kthread_create_info  struct kthread {  	unsigned long flags;  	unsigned int cpu; +	int result;  	int (*threadfn)(void *);  	void *data;  	mm_segment_t oldfs; @@ -71,7 +72,7 @@ enum KTHREAD_BITS {  static inline struct kthread *to_kthread(struct task_struct *k)  {  	WARN_ON(!(k->flags & PF_KTHREAD)); -	return (__force void *)k->set_child_tid; +	return k->worker_private;  }  /* @@ -79,7 +80,7 @@ static inline struct kthread *to_kthread(struct task_struct *k)   *   * Per construction; when:   * - *   (p->flags & PF_KTHREAD) && p->set_child_tid + *   (p->flags & PF_KTHREAD) && p->worker_private   *   * the task is both a kthread and struct kthread is persistent. However   * PF_KTHREAD on it's own is not, kernel_thread() can exec() (See umh.c and @@ -87,26 +88,29 @@ static inline struct kthread *to_kthread(struct task_struct *k)   */  static inline struct kthread *__to_kthread(struct task_struct *p)  { -	void *kthread = (__force void *)p->set_child_tid; +	void *kthread = p->worker_private;  	if (kthread && !(p->flags & PF_KTHREAD))  		kthread = NULL;  	return kthread;  } -void set_kthread_struct(struct task_struct *p) +bool set_kthread_struct(struct task_struct *p)  {  	struct kthread *kthread; -	if (__to_kthread(p)) -		return; +	if (WARN_ON_ONCE(to_kthread(p))) +		return false;  	kthread = kzalloc(sizeof(*kthread), GFP_KERNEL); -	/* -	 * We abuse ->set_child_tid to avoid the new member and because it -	 * can't be wrongly copied by copy_process(). We also rely on fact -	 * that the caller can't exec, so PF_KTHREAD can't be cleared. -	 */ -	p->set_child_tid = (__force void __user *)kthread; +	if (!kthread) +		return false; + +	init_completion(&kthread->exited); +	init_completion(&kthread->parked); +	p->vfork_done = &kthread->exited; + +	p->worker_private = kthread; +	return true;  }  void free_kthread_struct(struct task_struct *k) @@ -114,13 +118,13 @@ void free_kthread_struct(struct task_struct *k)  	struct kthread *kthread;  	/* -	 * Can be NULL if this kthread was created by kernel_thread() -	 * or if kmalloc() in kthread() failed. +	 * Can be NULL if kmalloc() in set_kthread_struct() failed.  	 */  	kthread = to_kthread(k);  #ifdef CONFIG_BLK_CGROUP  	WARN_ON_ONCE(kthread && kthread->blkcg_css);  #endif +	k->worker_private = NULL;  	kfree(kthread);  } @@ -268,6 +272,44 @@ void kthread_parkme(void)  }  EXPORT_SYMBOL_GPL(kthread_parkme); +/** + * kthread_exit - Cause the current kthread return @result to kthread_stop(). + * @result: The integer value to return to kthread_stop(). + * + * While kthread_exit can be called directly, it exists so that + * functions which do some additional work in non-modular code such as + * module_put_and_kthread_exit can be implemented. + * + * Does not return. + */ +void __noreturn kthread_exit(long result) +{ +	struct kthread *kthread = to_kthread(current); +	kthread->result = result; +	do_exit(0); +} + +/** + * kthread_complete_and_exit - Exit the current kthread. + * @comp: Completion to complete + * @code: The integer value to return to kthread_stop(). + * + * If present complete @comp and the reuturn code to kthread_stop(). + * + * A kernel thread whose module may be removed after the completion of + * @comp can use this function exit safely. + * + * Does not return. + */ +void __noreturn kthread_complete_and_exit(struct completion *comp, long code) +{ +	if (comp) +		complete(comp); + +	kthread_exit(code); +} +EXPORT_SYMBOL(kthread_complete_and_exit); +  static int kthread(void *_create)  {  	static const struct sched_param param = { .sched_priority = 0 }; @@ -279,27 +321,17 @@ static int kthread(void *_create)  	struct kthread *self;  	int ret; -	set_kthread_struct(current);  	self = to_kthread(current);  	/* If user was SIGKILLed, I release the structure. */  	done = xchg(&create->done, NULL);  	if (!done) {  		kfree(create); -		do_exit(-EINTR); -	} - -	if (!self) { -		create->result = ERR_PTR(-ENOMEM); -		complete(done); -		do_exit(-ENOMEM); +		kthread_exit(-EINTR);  	}  	self->threadfn = threadfn;  	self->data = data; -	init_completion(&self->exited); -	init_completion(&self->parked); -	current->vfork_done = &self->exited;  	/*  	 * The new thread inherited kthreadd's priority and CPU mask. Reset @@ -326,7 +358,7 @@ static int kthread(void *_create)  		__kthread_parkme(self);  		ret = threadfn(data);  	} -	do_exit(ret); +	kthread_exit(ret);  }  /* called from kernel_clone() to get node information for about to be created task */ @@ -523,6 +555,7 @@ struct task_struct *kthread_create_on_cpu(int (*threadfn)(void *data),  	to_kthread(p)->cpu = cpu;  	return p;  } +EXPORT_SYMBOL(kthread_create_on_cpu);  void kthread_set_per_cpu(struct task_struct *k, int cpu)  { @@ -627,7 +660,7 @@ EXPORT_SYMBOL_GPL(kthread_park);   * instead of calling wake_up_process(): the thread will exit without   * calling threadfn().   * - * If threadfn() may call do_exit() itself, the caller must ensure + * If threadfn() may call kthread_exit() itself, the caller must ensure   * task_struct can't go away.   *   * Returns the result of threadfn(), or %-EINTR if wake_up_process() @@ -646,7 +679,7 @@ int kthread_stop(struct task_struct *k)  	kthread_unpark(k);  	wake_up_process(k);  	wait_for_completion(&kthread->exited); -	ret = k->exit_code; +	ret = kthread->result;  	put_task_struct(k);  	trace_sched_kthread_stop_ret(ret);  |