Skip to contentSkip to conversation

hotplug thread issues

PATCH In review Peter Zijlstra 3 participants 5 messages Nov 6, 2014 – Nov 7, 2014

lore
Share & tools
lore
#0 lore raw

Hi Thomas,

So there have been some reports on hitting:

  BUG_ON(td->cpu != smp_processor_id());

in smpboot_thread_fn.

Now I've been staring at this for a wee bit today and I've found two issues, but I'm not sure either are enough to explain the observed.

1) smpboot_register_percpu_thread() seems to lack serialization against hotplug. It has a for_each_online() loop, but no get_online_cpus() -- unlike smpboot_unregister_percpu_thread, which does.

Typical usage like spawn_ksoftirqd() should be fine, they're early init calls and those run before we bring up the other CPUs. Therefore this does not explain the observation that its ksoftirqd/n triggering the BUG.

However, the usage in proc_dowatchdog() is susceptible to this race and its entirely possible to go wrong there.

2) the usage of __set_current_state(TASK_PARKED) in __kthread_parkme() is wrong AFAICT, one should always use set_current_state() for setting !TASK_RUNNING state. The comment with set_current_state() explains why.

This would've allowed the test_bit(KTHREAD_SHOULD_PARK) load to have been satisfied before the store of TASK_PARKED.

In any case, I'm not sure either of these are enough, I'll go stare at it a bit more I suppose.

---
 kernel/kthread.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
kernel/kthread.c +2 -2
index 10e489c448fe..9787244d43ec 100644
@@ -156,12 +156,12 @@ void *probe_kthread_data(struct task_struct *task)
 
 static void __kthread_parkme(struct kthread *self)
 {
-	__set_current_state(TASK_PARKED);
+	set_current_state(TASK_PARKED);
 	while (test_bit(KTHREAD_SHOULD_PARK, &self->flags)) {
 		if (!test_and_set_bit(KTHREAD_IS_PARKED, &self->flags))
 			complete(&self->parked);
 		schedule();
-		__set_current_state(TASK_PARKED);
+		set_current_state(TASK_PARKED);
 	}
 	clear_bit(KTHREAD_IS_PARKED, &self->flags);
 	__set_current_state(TASK_RUNNING);
#1 lore raw
Quoted 15 lines · Peter Zijlstra

Hi Thomas,

So there have been some reports on hitting:

  BUG_ON(td->cpu != smp_processor_id());

in smpboot_thread_fn.

Now I've been staring at this for a wee bit today and I've found two issues, but I'm not sure either are enough to explain the observed.

1) smpboot_register_percpu_thread() seems to lack serialization against hotplug. It has a for_each_online() loop, but no get_online_cpus() -- unlike smpboot_unregister_percpu_thread, which does.

Quoted 7 lines

Typical usage like spawn_ksoftirqd() should be fine, they're early init calls and those run before we bring up the other CPUs. Therefore this does not explain the observation that its ksoftirqd/n triggering the BUG.

However, the usage in proc_dowatchdog() is susceptible to this race and its entirely possible to go wrong there.

Hmm. Need to have a look.

Quoted 8 lines

2) the usage of __set_current_state(TASK_PARKED) in __kthread_parkme() is wrong AFAICT, one should always use set_current_state() for setting !TASK_RUNNING state. The comment with set_current_state() explains why.

This would've allowed the test_bit(KTHREAD_SHOULD_PARK) load to have been satisfied before the store of TASK_PARKED.

My bad. Can you send a proper patch addressing that issue please? That should be tagged stable as well I guess.

Thanks,

tglx

#2 lore raw

PATCH 1/1 : kthread: Fix memory ordering in __kthread_parkme

Quoted 10 lines · Thomas Gleixner
Quoted 7 lines

2) the usage of __set_current_state(TASK_PARKED) in __kthread_parkme() is wrong AFAICT, one should always use set_current_state() for setting !TASK_RUNNING state. The comment with set_current_state() explains why.

This would've allowed the test_bit(KTHREAD_SHOULD_PARK) load to have been satisfied before the store of TASK_PARKED.

My bad. Can you send a proper patch addressing that issue please? That should be tagged stable as well I guess.

Sure thing, something like so then?

---
Subject: kthread: Fix memory ordering in __kthread_parkme

One should always use set_current_state() to set !TASK_RUNNING states.

	set_current_state(TASK_*);		cond = true;
	/* mb */				/* wmb */
	if (!cond)				wake_up_state(, TASK_*);
		schedule();

By not having the mb we allow for the cond load to be satisfied before the state store, this can result in:

	if (!cond)
						cond = true;
						wake_up_state(, TASK_*);
	__set_current_state(TASK_*);
		schedule();

Which would block 'forever', since the cond is still false and the wakeup would not have seen the !TASK_RUNNING state.

---
 kernel/kthread.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
kernel/kthread.c +2 -2
index 10e489c448fe..9787244d43ec 100644
@@ -156,12 +156,12 @@ void *probe_kthread_data(struct task_struct *task)
 
 static void __kthread_parkme(struct kthread *self)
 {
-	__set_current_state(TASK_PARKED);
+	set_current_state(TASK_PARKED);
 	while (test_bit(KTHREAD_SHOULD_PARK, &self->flags)) {
 		if (!test_and_set_bit(KTHREAD_IS_PARKED, &self->flags))
 			complete(&self->parked);
 		schedule();
-		__set_current_state(TASK_PARKED);
+		set_current_state(TASK_PARKED);
 	}
 	clear_bit(KTHREAD_IS_PARKED, &self->flags);
 	__set_current_state(TASK_RUNNING);
#3 lore raw

Re: [PATCH]: kthread: Fix memory ordering in __kthread_parkme

kernel/kthread.c void *probe_kthread_data(struct task_struct *task) · line 165

Quoted 12 lines · Peter Zijlstra

static void __kthread_parkme(struct kthread *self) {

kernel/kthread.c +2 -2
-	__set_current_state(TASK_PARKED);
+	set_current_state(TASK_PARKED);
 	while (test_bit(KTHREAD_SHOULD_PARK, &self->flags)) {
 		if (!test_and_set_bit(KTHREAD_IS_PARKED, &self->flags))
 			complete(&self->parked);
 		schedule();
-		__set_current_state(TASK_PARKED);
+		set_current_state(TASK_PARKED);
 	}

Perhaps it makses sense to do set_current_state(PARKED) once at the start of "for (;;)" loop, but this is cosmetic.

What if kthread_unpark() is called right after test_bit(KTHREAD_SHOULD_PARK) and KTHREAD_IS_PARKED is not set? It seems that __kthread_unpark() should call wake_up_state() unconditionally ?

Oleg.

#4 lore raw

Re: [PATCH]: kthread: Fix memory ordering in __kthread_parkme

Quoted 16 lines · Oleg Nesterov

kernel/kthread.c void *probe_kthread_data(struct task_struct *task) · line 165

Quoted 12 lines · Peter Zijlstra

static void __kthread_parkme(struct kthread *self) {

kernel/kthread.c +2 -2
-	__set_current_state(TASK_PARKED);
+	set_current_state(TASK_PARKED);
 	while (test_bit(KTHREAD_SHOULD_PARK, &self->flags)) {
 		if (!test_and_set_bit(KTHREAD_IS_PARKED, &self->flags))
 			complete(&self->parked);
 		schedule();
-		__set_current_state(TASK_PARKED);
+		set_current_state(TASK_PARKED);
 	}

Perhaps it makses sense to do set_current_state(PARKED) once at the start of "for (;;)" loop, but this is cosmetic.

Yeah, we should probably clean that up, it looks a bit odd. But I didn't want to do too many changes.

Quoted 3 lines

What if kthread_unpark() is called right after test_bit(KTHREAD_SHOULD_PARK) and KTHREAD_IS_PARKED is not set? It seems that __kthread_unpark() should call wake_up_state() unconditionally ?

set_current_state(TASK_PARKED)

while (test_bit(KTHREAD_SHOULD_PARK, ..)) {
						clear_bit(KTHREAD_SHOULD_PARK, ..);
						if (test_and_clear_bit(KTHREAD_IS_PARKED, ..) {
							...
							wake_up_state(, TASK_PARKED);
						}
	if (!test_and_set_bit(KTHREAD_IS_PARKED, ..))
		complete(..);
	schedule();

Then yes we'll miss the wakeup, but we also miss the __kthread_bind().

Now I don't think this'll actually happen because kthread_park() waits for the completion under the hotplug and smpboot_threads_lock lock, and we do the unpark under the hotplug lock as well, so its fully serialized

But yes, we should probably clean this up as well.