diff options
Diffstat (limited to 'kernel/kthread.c')
| -rw-r--r-- | kernel/kthread.c | 121 | 
1 files changed, 91 insertions, 30 deletions
| diff --git a/kernel/kthread.c b/kernel/kthread.c index 7113003fab63..38c6dd822da8 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; @@ -60,6 +61,8 @@ struct kthread {  #ifdef CONFIG_BLK_CGROUP  	struct cgroup_subsys_state *blkcg_css;  #endif +	/* To store the full name if task comm is truncated. */ +	char *full_name;  };  enum KTHREAD_BITS { @@ -71,7 +74,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 +82,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 +90,41 @@ 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) +void get_kthread_comm(char *buf, size_t buf_size, struct task_struct *tsk)  { -	struct kthread *kthread; +	struct kthread *kthread = to_kthread(tsk); -	if (__to_kthread(p)) +	if (!kthread || !kthread->full_name) { +		__get_task_comm(buf, buf_size, tsk);  		return; +	} + +	strscpy_pad(buf, kthread->full_name, buf_size); +} + +bool set_kthread_struct(struct task_struct *p) +{ +	struct kthread *kthread; + +	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 +132,17 @@ 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); +	if (!kthread) +		return; +  #ifdef CONFIG_BLK_CGROUP -	WARN_ON_ONCE(kthread && kthread->blkcg_css); +	WARN_ON_ONCE(kthread->blkcg_css);  #endif +	k->worker_private = NULL; +	kfree(kthread->full_name);  	kfree(kthread);  } @@ -268,6 +290,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 +339,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 +376,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 */ @@ -406,12 +456,22 @@ struct task_struct *__kthread_create_on_node(int (*threadfn)(void *data),  	task = create->result;  	if (!IS_ERR(task)) {  		char name[TASK_COMM_LEN]; +		va_list aq; +		int len;  		/*  		 * task is already visible to other tasks, so updating  		 * COMM must be protected.  		 */ -		vsnprintf(name, sizeof(name), namefmt, args); +		va_copy(aq, args); +		len = vsnprintf(name, sizeof(name), namefmt, aq); +		va_end(aq); +		if (len >= TASK_COMM_LEN) { +			struct kthread *kthread = to_kthread(task); + +			/* leave it truncated when out of memory. */ +			kthread->full_name = kvasprintf(GFP_KERNEL, namefmt, args); +		}  		set_task_comm(task, name);  	}  	kfree(create); @@ -523,6 +583,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 +688,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 +707,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); |