diff options
Diffstat (limited to 'kernel/kthread.c')
| -rw-r--r-- | kernel/kthread.c | 42 | 
1 files changed, 34 insertions, 8 deletions
diff --git a/kernel/kthread.c b/kernel/kthread.c index 481951bf091d..087d18d771b5 100644 --- a/kernel/kthread.c +++ b/kernel/kthread.c @@ -177,9 +177,20 @@ void *kthread_probe_data(struct task_struct *task)  static void __kthread_parkme(struct kthread *self)  {  	for (;;) { -		set_current_state(TASK_PARKED); +		/* +		 * TASK_PARKED is a special state; we must serialize against +		 * possible pending wakeups to avoid store-store collisions on +		 * task->state. +		 * +		 * Such a collision might possibly result in the task state +		 * changin from TASK_PARKED and us failing the +		 * wait_task_inactive() in kthread_park(). +		 */ +		set_special_state(TASK_PARKED);  		if (!test_bit(KTHREAD_SHOULD_PARK, &self->flags))  			break; + +		complete(&self->parked);  		schedule();  	}  	__set_current_state(TASK_RUNNING); @@ -191,11 +202,6 @@ void kthread_parkme(void)  }  EXPORT_SYMBOL_GPL(kthread_parkme); -void kthread_park_complete(struct task_struct *k) -{ -	complete_all(&to_kthread(k)->parked); -} -  static int kthread(void *_create)  {  	/* Copy data: it's on kthread's stack */ @@ -319,8 +325,14 @@ struct task_struct *__kthread_create_on_node(int (*threadfn)(void *data),  	task = create->result;  	if (!IS_ERR(task)) {  		static const struct sched_param param = { .sched_priority = 0 }; +		char name[TASK_COMM_LEN]; -		vsnprintf(task->comm, sizeof(task->comm), namefmt, args); +		/* +		 * task is already visible to other tasks, so updating +		 * COMM must be protected. +		 */ +		vsnprintf(name, sizeof(name), namefmt, args); +		set_task_comm(task, name);  		/*  		 * root may have changed our (kthreadd's) priority or CPU mask.  		 * The kernel thread should not inherit these properties. @@ -459,8 +471,10 @@ void kthread_unpark(struct task_struct *k)  	if (test_bit(KTHREAD_IS_PER_CPU, &kthread->flags))  		__kthread_bind(k, kthread->cpu, TASK_PARKED); -	reinit_completion(&kthread->parked);  	clear_bit(KTHREAD_SHOULD_PARK, &kthread->flags); +	/* +	 * __kthread_parkme() will either see !SHOULD_PARK or get the wakeup. +	 */  	wake_up_state(k, TASK_PARKED);  }  EXPORT_SYMBOL_GPL(kthread_unpark); @@ -484,10 +498,22 @@ int kthread_park(struct task_struct *k)  	if (WARN_ON(k->flags & PF_EXITING))  		return -ENOSYS; +	if (WARN_ON_ONCE(test_bit(KTHREAD_SHOULD_PARK, &kthread->flags))) +		return -EBUSY; +  	set_bit(KTHREAD_SHOULD_PARK, &kthread->flags);  	if (k != current) {  		wake_up_process(k); +		/* +		 * Wait for __kthread_parkme() to complete(), this means we +		 * _will_ have TASK_PARKED and are about to call schedule(). +		 */  		wait_for_completion(&kthread->parked); +		/* +		 * Now wait for that schedule() to complete and the task to +		 * get scheduled out. +		 */ +		WARN_ON_ONCE(!wait_task_inactive(k, TASK_PARKED));  	}  	return 0;  | 
