Skip to contentSkip to conversation

sched: fix the PREEMPT_ACTIVE check in __trace_sched_switch_state()

PATCH In review Oleg Nesterov 4 participants 26 messages Oct 7, 2014 – Oct 28, 2014

Subsystems (4)
lore
Share & tools
lore
#0 lore raw

cover sched: fix the PREEMPT_ACTIVE check in __trace_sched_switch_state()

And note that another caller of task_preempt_count(), set_cpu(), is fine but it doesn't really need this helper.

And afaics we do not need ->saved_preempt_count at all, the trivial patch below makes it unnecessary, we can kill it and all its users.

Not only this will simplify the code, this will make (well, almost) the per-cpu preempt counter arch-agnostic.

Or I missed something?

Do you think this makes sense? If yes, I'll try to make the patches.

Oleg.
---

OBVIOUSLY INCOMPLETE, BREAKS EVERYTHING EXCEPT x86. Just to explain what I mean.

(this depends on !__ARCH_WANT_UNLOCKED_CTXSW, but it was already removed
and x86 doesn't use it anyway).

kernel/sched/core.c +6 -1
@@ -2279,6 +2279,7 @@ asmlinkage __visible void schedule_tail(struct task_struct *prev)
 {
 	struct rq *rq = this_rq();
 
+	preempt_count_set(PREEMPT_DISABLED);
 	finish_task_switch(rq, prev);
 
 	/*
@@ -2304,6 +2305,7 @@ context_switch(struct rq *rq, struct task_struct *prev,
 	       struct task_struct *next)
 {
 	struct mm_struct *mm, *oldmm;
+	int pc;
 
 	prepare_task_switch(rq, prev, next);
 
@@ -2338,10 +2340,13 @@ context_switch(struct rq *rq, struct task_struct *prev,
 #endif
 
 	context_tracking_task_switch(prev, next);
+
 	/* Here we just switch the register state and the stack. */
+	pc = this_cpu_read(__preempt_count);
 	switch_to(prev, next, prev);
-
 	barrier();
+	preempt_count_set(pc);
+
 	/*
 	 * this_rq must be evaluated again because prev may have moved
 	 * CPUs since it called schedule(), thus the 'rq' on its stack
#1 lore raw

PATCH 1/1 sched: fix the PREEMPT_ACTIVE check in __trace_sched_switch_state()

task_preempt_count() has nothing to do with the actual preempt counter, thread_info->saved_preempt_count is only valid right after switch_to().

__trace_sched_switch_state() can use preempt_count(), prev is still the current task when trace_sched_switch() is called.

---
 include/trace/events/sched.h |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)
include/trace/events/sched.h +1 -1
index 0a68d5a..a7d67bc 100644
@@ -100,7 +100,7 @@ static inline long __trace_sched_switch_state(struct task_struct *p)
 	/*
 	 * For all intents and purposes a preempted task is a running task.
 	 */
-	if (task_preempt_count(p) & PREEMPT_ACTIVE)
+	if (preempt_count() & PREEMPT_ACTIVE)
 		state = TASK_RUNNING | TASK_STATE_MAX;
 #endif
 
Signature · 1 line
1.5.5.1
maintainer
#5 lore raw
In reply to Oleg Nesterov
Quoted 10 lines · Oleg Nesterov

And note that another caller of task_preempt_count(), set_cpu(), is fine but it doesn't really need this helper.

And afaics we do not need ->saved_preempt_count at all, the trivial patch below makes it unnecessary, we can kill it and all its users.

Not only this will simplify the code, this will make (well, almost) the per-cpu preempt counter arch-agnostic.

Or I missed something?

Two things, per-cpu isn't always faster on some archs, and load-store archs have problems with PREEMPT_NEED_RESCHED, although arguably you can do per-cpu preempt count without that.

Quoted 1 line

Do you think this makes sense? If yes, I'll try to make the patches.

It penalizes everything but x86 I think. There is no other arch that has per-cpu preempt count atm.

#6 lore raw

cover (Was: sched: fix the PREEMPT_ACTIVE check in __trace_sched_switch_state())

Quoted 8 lines · Peter Zijlstra
Quoted 6 lines · Oleg Nesterov

And note that another caller of task_preempt_count(), set_cpu(), is fine but it doesn't really need this helper.

And afaics we do not need ->saved_preempt_count at all, the trivial patch below makes it unnecessary, we can kill it and all its users.

Quoted 8 lines
Quoted 4 lines

Not only this will simplify the code, this will make (well, almost) the per-cpu preempt counter arch-agnostic.

Or I missed something?

Two things, per-cpu isn't always faster on some archs, and load-store archs have problems with PREEMPT_NEED_RESCHED, although arguably you can do per-cpu preempt count without that.

Ah, but I didn't mean we should make it per-cpu on every arch.

I meant that (imo) this change can cleanup x86 code, and it can also help if we want to change another arch to use per-cpu preempt_count.

Quoted 3 lines
Quoted 1 line

Do you think this makes sense? If yes, I'll try to make the patches.

It penalizes everything but x86 I think.

I don't think so.

But please forget for the moment, lets discuss this later. Let me start with 2 simple preparations which imho make sense anyway. Then we will see.

1/2 looks like the obvious bugfix (iirc we already discussed this a bit), 2/2 depends on this patch.

Oleg.

#7 lore raw

PATCH 1/2 sched: schedule_tail() should disable preemption

finish_task_switch() enables preemption, so post_schedule(rq) can be called on the wrong (and even dead) CPU. Afaics, nothing really bad can happen, but in this case we can wrongly clear rq->post_schedule on that CPU. And this simply looks wrong in any case.

Another problem is that finish_task_switch() itself runs with preempt enabled after finish_lock_switch(). If nothing else this means that ->sched_in() notifier can't trust its "cpu" arg.

---
 kernel/sched/core.c |   11 +++++------
 1 files changed, 5 insertions(+), 6 deletions(-)
kernel/sched/core.c +5 -6
index 703c7e6..3f267e8 100644
@@ -2277,15 +2277,14 @@ static inline void post_schedule(struct rq *rq)
 asmlinkage __visible void schedule_tail(struct task_struct *prev)
 	__releases(rq->lock)
 {
-	struct rq *rq = this_rq();
+	struct rq *rq;
 
+	/* finish_task_switch() drops rq->lock and enables preemtion */
+	preempt_disable();
+	rq = this_rq();
 	finish_task_switch(rq, prev);
-
-	/*
-	 * FIXME: do we need to worry about rq being invalidated by the
-	 * task_switch?
-	 */
 	post_schedule(rq);
+	preempt_enable();
 
 	if (current->set_child_tid)
 		put_user(task_pid_vnr(current), current->set_child_tid);
Signature · 1 line
1.5.5.1
#17 lore raw

PATCH 2/2 sched: kill task_preempt_count()

In reply to Oleg Nesterov

task_preempt_count() is pointless if preemption counter is per-cpu, currently this is x86 only. It is only valid if the task is not running, and even in this case the only info it can provide is the state of PREEMPT_ACTIVE bit.

Change its single caller to check p->on_rq instead, this should be the same if p->state != TASK_RUNNING, and kill this helper.

---
 arch/x86/include/asm/preempt.h |    3 ---
 include/asm-generic/preempt.h  |    3 ---
 kernel/sched/core.c            |    2 +-
 3 files changed, 1 insertions(+), 7 deletions(-)
arch/x86/include/asm/preempt.h +0 -3
index 4008734..8f32718 100644
@@ -30,9 +30,6 @@ static __always_inline void preempt_count_set(int pc)
 /*
  * must be macros to avoid header recursion hell
  */
-#define task_preempt_count(p) \
-	(task_thread_info(p)->saved_preempt_count & ~PREEMPT_NEED_RESCHED)
-
 #define init_task_preempt_count(p) do { \
 	task_thread_info(p)->saved_preempt_count = PREEMPT_DISABLED; \
 } while (0)
include/asm-generic/preempt.h +0 -3
index 1cd3f5d..eb6f9e6 100644
@@ -23,9 +23,6 @@ static __always_inline void preempt_count_set(int pc)
 /*
  * must be macros to avoid header recursion hell
  */
-#define task_preempt_count(p) \
-	(task_thread_info(p)->preempt_count & ~PREEMPT_NEED_RESCHED)
-
 #define init_task_preempt_count(p) do { \
 	task_thread_info(p)->preempt_count = PREEMPT_DISABLED; \
 } while (0)
kernel/sched/core.c +1 -1
index 3f267e8..cfe9905 100644
@@ -1056,7 +1056,7 @@ void set_task_cpu(struct task_struct *p, unsigned int new_cpu)
 	 * ttwu() will sort out the placement.
 	 */
 	WARN_ON_ONCE(p->state != TASK_RUNNING && p->state != TASK_WAKING &&
-			!(task_preempt_count(p) & PREEMPT_ACTIVE));
+			!p->on_rq);
 
 #ifdef CONFIG_LOCKDEP
 	/*
Signature · 1 line
1.5.5.1
#8 lore raw

PATCH v2 1/2 sched: schedule_tail() should disable preemption

In reply to Oleg Nesterov
Quoted 4 lines · Oleg Nesterov

Another problem is that finish_task_switch() itself runs with preempt enabled after finish_lock_switch(). If nothing else this means that ->sched_in() notifier can't trust its "cpu" arg.

OOPS, this obviously can't happen, ->preempt_notifiers must be empty. Remove this part from the changelog, please see v2.

I am not sure about finish_arch_post_lock_switch() ... but probably it should be fine without preempt_disable.

-------------------------------------------------------------------------------
Subject: [PATCH v2 1/2] sched: schedule_tail() should disable preemption

finish_task_switch() enables preemption, so post_schedule(rq) can be called on the wrong (and even dead) CPU. Afaics, nothing really bad can happen, but in this case we can wrongly clear rq->post_schedule on that CPU. And this simply looks wrong in any case.

---
 kernel/sched/core.c |   11 +++++------
 1 files changed, 5 insertions(+), 6 deletions(-)
kernel/sched/core.c +5 -6
index 703c7e6..3f267e8 100644
@@ -2277,15 +2277,14 @@ static inline void post_schedule(struct rq *rq)
 asmlinkage __visible void schedule_tail(struct task_struct *prev)
 	__releases(rq->lock)
 {
-	struct rq *rq = this_rq();
+	struct rq *rq;
 
+	/* finish_task_switch() drops rq->lock and enables preemtion */
+	preempt_disable();
+	rq = this_rq();
 	finish_task_switch(rq, prev);
-
-	/*
-	 * FIXME: do we need to worry about rq being invalidated by the
-	 * task_switch?
-	 */
 	post_schedule(rq);
+	preempt_enable();
 
 	if (current->set_child_tid)
 		put_user(task_pid_vnr(current), current->set_child_tid);
Signature · 1 line
1.5.5.1
Reviewed-by on v2 1/2
#9 lore raw

Re: [PATCH v2 1/2] sched: schedule_tail() should disable preemption

В Ср, 08/10/2014 в 21:36 +0200, Oleg Nesterov пишет:

Quoted 30 lines · Oleg Nesterov
Quoted 4 lines

Another problem is that finish_task_switch() itself runs with preempt enabled after finish_lock_switch(). If nothing else this means that ->sched_in() notifier can't trust its "cpu" arg.

OOPS, this obviously can't happen, ->preempt_notifiers must be empty. Remove this part from the changelog, please see v2.

I am not sure about finish_arch_post_lock_switch() ... but probably it should be fine without preempt_disable.

-------------------------------------------------------------------------------
Subject: [PATCH v2 1/2] sched: schedule_tail() should disable preemption

finish_task_switch() enables preemption, so post_schedule(rq) can be called on the wrong (and even dead) CPU. Afaics, nothing really bad can happen, but in this case we can wrongly clear rq->post_schedule on that CPU. And this simply looks wrong in any case.

---
 kernel/sched/core.c |   11 +++++------
 1 files changed, 5 insertions(+), 6 deletions(-)
kernel/sched/core.c +0 -0
index 703c7e6..3f267e8 100644

kernel/sched/core.c static inline void post_schedule(struct rq *rq) · line 2283

Quoted 8 lines
kernel/sched/core.c +3 -1
@@ -2277,15 +2277,14 @@ static inline void post_schedule(struct rq *rq)
 asmlinkage __visible void schedule_tail(struct task_struct *prev)
 	__releases(rq->lock)
 {
-	struct rq *rq = this_rq();
+	struct rq *rq;
 
+	/* finish_task_switch() drops rq->lock and enables preemtion */
+	preempt_disable();

Maybe, the code would look simpler if we change init_task_preempt_count() and create new tasks with preempt_count() == 2, so this preempt_disable() won't be necessary. But it's more or less subjectively.

Quoted 12 lines
kernel/sched/core.c +2 -5
+	rq = this_rq();
 	finish_task_switch(rq, prev);
-
-	/*
-	 * FIXME: do we need to worry about rq being invalidated by the
-	 * task_switch?
-	 */
 	post_schedule(rq);
+	preempt_enable();
 
 	if (current->set_child_tid)
 		put_user(task_pid_vnr(current), current->set_child_tid);
#10 lore raw

Re: [PATCH v2 1/2] sched: schedule_tail() should disable preemption

Quoted 9 lines · Kirill Tkhai

В Ср, 08/10/2014 в 21:36 +0200, Oleg Nesterov пишет:

Quoted 7 lines
kernel/sched/core.c +1 -1
@@ -2277,15 +2277,14 @@ static inline void post_schedule(struct rq *rq)
 asmlinkage __visible void schedule_tail(struct task_struct *prev)
 	__releases(rq->lock)
 {
-	struct rq *rq = this_rq();
+	struct rq *rq;
 
Quoted 7 lines

kernel/sched/core.c static inline void post_schedule(struct rq *rq) · line 2283

Quoted 2 lines
kernel/sched/core.c +2 -0
+	/* finish_task_switch() drops rq->lock and enables preemtion */
+	preempt_disable();

Maybe, the code would look simpler if we change init_task_preempt_count() and create new tasks with preempt_count() == 2, so this preempt_disable() won't be necessary. But it's more or less subjectively.

Yes, yes, I thought about the same.

Except I think we should kill init_task_preempt_count() and change schedule_tail()

	-	preempt_disable();
	+	preempt_count_set(PREEMPT_DISABLED + 1);

but first we need to remove ->saved_preempt_count.

Quoted 1 line

Thanks!

Oleg.

maintainer
#11 lore raw

Re: [PATCH v2 1/2] sched: schedule_tail() should disable preemption

Quoted 1 line · Oleg Nesterov

but first we need to remove ->saved_preempt_count.

Why do you want to kill that? Your earlier proposal would penalize every !x86 arch by adding extra code to the scheduler core while they already automagically preserve their thread_info::preempt_count.

#12 lore raw

Re: [PATCH v2 1/2] sched: schedule_tail() should disable preemption

Peter,

let me first say that I understand that cleanups are always subjective. So if you do not like it - I won't argue at all.

Quoted 6 lines · Peter Zijlstra
Quoted 1 line · Oleg Nesterov

but first we need to remove ->saved_preempt_count.

Why do you want to kill that?

Because imo this makes the code a bit simpler. But (perhaps) mostly because personally I dislike any "special" member in task_struct/thread_info, and it seems to me that ->saved_preempt_count buys nothing. We only need it to record/restore the counter before/after switch_to(), a local variably looks better to me.

But again, see above. If the maintainer doesn't like the cleanup - then it should be counted as uglification ;)

Quoted 3 lines

Your earlier proposal would penalize every
!x86 arch by adding extra code to the scheduler core while they already automagically preserve their thread_info::preempt_count.

Sure, and it can't be even compiled on !x86.

But this is simple, just we need a new helper, preempt_count_restore(), defined as nop in asm-generic/preempt.h. Well, perhaps another helper makes sense, preempt_count_raw() which simply reads the counter, but this is minor.

After the patch below we can remove ->saved_preempt_count. Including init_task_preempt_count(), it is no longer needed after the change in schedule_tail().

No?

Oleg.

arch/x86/include/asm/preempt.h +5 -0
index 8f32718..695307f 100644
@@ -27,6 +27,11 @@ static __always_inline void preempt_count_set(int pc)
 	raw_cpu_write_4(__preempt_count, pc);
 }
 
+static __always_inline void preempt_count_restore(int pc)
+{
+	preempt_count_set(pc);
+}
+
 /*
  * must be macros to avoid header recursion hell
  */
include/asm-generic/preempt.h +4 -0
index eb6f9e6..14de30e 100644
@@ -20,6 +20,10 @@ static __always_inline void preempt_count_set(int pc)
 	*preempt_count_ptr() = pc;
 }
 
+static __always_inline void preempt_count_restore(int pc)
+{
+}
+
 /*
  * must be macros to avoid header recursion hell
  */
kernel/sched/core.c +6 -1
index cfe9905..ad8ca02 100644
@@ -2279,6 +2279,8 @@ asmlinkage __visible void schedule_tail(struct task_struct *prev)
 {
 	struct rq *rq;
 
+	preempt_count_set(PREEMPT_DISABLED);
+
 	/* finish_task_switch() drops rq->lock and enables preemtion */
 	preempt_disable();
 	rq = this_rq();
@@ -2299,6 +2301,7 @@ context_switch(struct rq *rq, struct task_struct *prev,
 	       struct task_struct *next)
 {
 	struct mm_struct *mm, *oldmm;
+	int pc;
 
 	prepare_task_switch(rq, prev, next);
 
@@ -2333,10 +2336,12 @@ context_switch(struct rq *rq, struct task_struct *prev,
 #endif
 
 	context_tracking_task_switch(prev, next);
+
+	pc = preempt_count();
 	/* Here we just switch the register state and the stack. */
 	switch_to(prev, next, prev);
-
 	barrier();
+	preempt_count_restore(pc);
 	/*
 	 * this_rq must be evaluated again because prev may have moved
 	 * CPUs since it called schedule(), thus the 'rq' on its stack
maintainer
#13 lore raw

Re: [PATCH v2 1/2] sched: schedule_tail() should disable preemption

Quoted 6 lines · Oleg Nesterov
Quoted 3 lines

Your earlier proposal would penalize every
!x86 arch by adding extra code to the scheduler core while they already automagically preserve their thread_info::preempt_count.

Sure, and it can't be even compiled on !x86.

Quoted 8 lines

But this is simple, just we need a new helper, preempt_count_restore(), defined as nop in asm-generic/preempt.h. Well, perhaps another helper makes sense, preempt_count_raw() which simply reads the counter, but this is minor.

After the patch below we can remove ->saved_preempt_count. Including init_task_preempt_count(), it is no longer needed after the change in schedule_tail().

Ah, right, this makes more sense.

kernel/sched/core.c context_switch(struct rq *rq, struct task_struct *prev, · line 2340

Quoted 6 lines
kernel/sched/core.c +2 -0
@@ -2333,10 +2336,12 @@ context_switch(struct rq *rq, struct task_struct *prev,
 #endif
 
 	context_tracking_task_switch(prev, next);
+
+	pc = preempt_count();

The only problem here is that you can loose PREEMPT_NEED_RESCHED, I haven't thought about whether that is a problem here or not.

Quoted 9 lines
kernel/sched/core.c +1 -1
 	/* Here we just switch the register state and the stack. */
 	switch_to(prev, next, prev);
-
 	barrier();
+	preempt_count_restore(pc);
 	/*
 	 * this_rq must be evaluated again because prev may have moved
 	 * CPUs since it called schedule(), thus the 'rq' on its stack
maintainer
#14 lore raw

Re: [PATCH v2 1/2] sched: schedule_tail() should disable preemption

Quoted 21 lines · Peter Zijlstra
Quoted 6 lines · Oleg Nesterov
Quoted 3 lines

Your earlier proposal would penalize every
!x86 arch by adding extra code to the scheduler core while they already automagically preserve their thread_info::preempt_count.

Sure, and it can't be even compiled on !x86.

Quoted 8 lines

But this is simple, just we need a new helper, preempt_count_restore(), defined as nop in asm-generic/preempt.h. Well, perhaps another helper makes sense, preempt_count_raw() which simply reads the counter, but this is minor.

After the patch below we can remove ->saved_preempt_count. Including init_task_preempt_count(), it is no longer needed after the change in schedule_tail().

Ah, right, this makes more sense.

Quoted 3 lines
(quoted diff) +0 -0
@@ -2333,10 +2336,12 @@ context_switch(struct rq *rq, struct task_struct *prev,
 #endif
 
Quoted 6 lines

kernel/sched/core.c context_switch(struct rq *rq, struct task_struct *prev, · line 2340

Quoted 3 lines
kernel/sched/core.c +2 -0
 	context_tracking_task_switch(prev, next);
+
+	pc = preempt_count();

The only problem here is that you can loose PREEMPT_NEED_RESCHED, I haven't thought about whether that is a problem here or not.

Also, if you make that preempt_count_save(), to mirror the restore, you can both preserve PREEMPT_NEED_RESCHED and avoid emitting code on !x86.

#15 lore raw

Re: [PATCH v2 1/2] sched: schedule_tail() should disable preemption

Quoted 6 lines · Peter Zijlstra
Quoted 4 lines · Peter Zijlstra
Quoted 3 lines · Oleg Nesterov
(quoted diff) +0 -0
@@ -2333,10 +2336,12 @@ context_switch(struct rq *rq, struct task_struct *prev,
 #endif
 
Quoted 6 lines
Quoted 6 lines

kernel/sched/core.c context_switch(struct rq *rq, struct task_struct *prev, · line 2340

Quoted 3 lines
kernel/sched/core.c +2 -0
 	context_tracking_task_switch(prev, next);
+
+	pc = preempt_count();

The only problem here is that you can loose PREEMPT_NEED_RESCHED, I haven't thought about whether that is a problem here or not.

No, it must not be set. It was cleared by clear_preempt_need_resched() in __schedule(), and nobody can set it.

(schedule_tail() also relies on fact it runs with irqs disabled).

But,

Quoted 1 line

Also, if you make that preempt_count_save(), to mirror the restore,

Yes, agreed. That is why I said we probably want preempt_count_raw() (or _save). This way we avoid the unnecessary "&= ~PREEMPT_NEED_RESCHED" on x86.

Quoted 1 line

can both preserve PREEMPT_NEED_RESCHED and avoid emitting code on !x86.

This too, although gcc should optimize this code out anyway. At least it seems to do on x86 if I make preempt_count_restore() empty.

Oleg.

#19 lore raw

cover sched: make finish_task_switch() return struct rq *

In reply to Oleg Nesterov

speaking of minor cleanups...

Let me send another once before ->saved_preempt_count removal, it looks simple and less subjective.

Again, this is just minor cleanup so please feel free to ignore, but somehow to me this duplicated this_rq() (and the comment!) looks a bit annoying. Plus it saves a few insns, but this is minor.

Depends on "sched: schedule_tail() should disable preemption".

Oleg.

#20 lore raw

PATCH 1/1 sched: make finish_task_switch() return struct rq *

Both callers of finish_task_switch() need to recalculate this_rq() and pass it as an argument, plus __schedule() does this again after context_switch().

It would be simpler to call this_rq() once in finish_task_switch() and return the this rq to the callers.

Note: probably "int cpu" in __schedule() should die; it is not used and both rcu_note_context_switch() and wq_worker_sleeping() do not really need this argument.

---
 kernel/sched/core.c |   37 +++++++++++++++----------------------
 1 files changed, 15 insertions(+), 22 deletions(-)
kernel/sched/core.c +15 -22
index cfe9905..3bfbd3d 100644
@@ -2200,10 +2200,16 @@ prepare_task_switch(struct rq *rq, struct task_struct *prev,
  * so, we finish that here outside of the runqueue lock. (Doing it
  * with the lock held can cause deadlocks; see schedule() for
  * details.)
+ *
+ * The context switch have flipped the stack from under us and restored the
+ * local variables which were saved when this task called schedule() in the
+ * past. prev == current is still correct but we need to recalculate this_rq
+ * because prev may have moved to another CPU.
  */
-static void finish_task_switch(struct rq *rq, struct task_struct *prev)
+static struct rq *finish_task_switch(struct task_struct *prev)
 	__releases(rq->lock)
 {
+	struct rq *rq = this_rq();
 	struct mm_struct *mm = rq->prev_mm;
 	long prev_state;
 
@@ -2243,6 +2249,7 @@ static void finish_task_switch(struct rq *rq, struct task_struct *prev)
 	}
 
 	tick_nohz_task_switch(current);
+	return rq;
 }
 
 #ifdef CONFIG_SMP
@@ -2281,8 +2288,7 @@ asmlinkage __visible void schedule_tail(struct task_struct *prev)
 
 	/* finish_task_switch() drops rq->lock and enables preemtion */
 	preempt_disable();
-	rq = this_rq();
-	finish_task_switch(rq, prev);
+	rq = finish_task_switch(prev);
 	post_schedule(rq);
 	preempt_enable();
 
@@ -2291,10 +2297,9 @@ asmlinkage __visible void schedule_tail(struct task_struct *prev)
 }
 
 /*
- * context_switch - switch to the new MM and the new
- * thread's register state.
+ * context_switch - switch to the new MM and the new thread's register state.
  */
-static inline void
+static inline struct rq *
 context_switch(struct rq *rq, struct task_struct *prev,
 	       struct task_struct *next)
 {
@@ -2335,14 +2340,9 @@ context_switch(struct rq *rq, struct task_struct *prev,
 	context_tracking_task_switch(prev, next);
 	/* Here we just switch the register state and the stack. */
 	switch_to(prev, next, prev);
-
 	barrier();
-	/*
-	 * this_rq must be evaluated again because prev may have moved
-	 * CPUs since it called schedule(), thus the 'rq' on its stack
-	 * frame will be invalid.
-	 */
-	finish_task_switch(this_rq(), prev);
+
+	return finish_task_switch(prev);
 }
 
 /*
@@ -2802,15 +2802,8 @@ need_resched:
 		rq->curr = next;
 		++*switch_count;
 
-		context_switch(rq, prev, next); /* unlocks the rq */
-		/*
-		 * The context switch have flipped the stack from under us
-		 * and restored the local variables which were saved when
-		 * this task called schedule() in the past. prev == current
-		 * is still correct, but it can be moved to another cpu/rq.
-		 */
-		cpu = smp_processor_id();
-		rq = cpu_rq(cpu);
+		rq = context_switch(rq, prev, next); /* unlocks the rq */
+		cpu = rq->cpu;
 	} else
 		raw_spin_unlock_irq(&rq->lock);
 
Signature · 1 line
1.5.5.1
maintainer
#2 lore raw
In reply to Oleg Nesterov
Quoted 5 lines · Oleg Nesterov

task_preempt_count() has nothing to do with the actual preempt counter, thread_info->saved_preempt_count is only valid right after switch_to().

__trace_sched_switch_state() can use preempt_count(), prev is still the current task when trace_sched_switch() is called.

This is true, but the paranoid in me would like a BUG_ON(p != current) right along with that to make sure we don't accidentally change this.

But yes, its good to get rid of task_preempt_count(), its horrible.

Quoted 22 lines
---
 include/trace/events/sched.h |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)
include/trace/events/sched.h +1 -1
index 0a68d5a..a7d67bc 100644
@@ -100,7 +100,7 @@ static inline long __trace_sched_switch_state(struct task_struct *p)
 	/*
 	 * For all intents and purposes a preempted task is a running task.
 	 */
-	if (task_preempt_count(p) & PREEMPT_ACTIVE)
+	if (preempt_count() & PREEMPT_ACTIVE)
 		state = TASK_RUNNING | TASK_STATE_MAX;
 #endif
 
Signature · 1 line
1.5.5.1
maintainer
#3 lore raw
Quoted 11 lines · Peter Zijlstra
Quoted 5 lines · Oleg Nesterov

task_preempt_count() has nothing to do with the actual preempt counter, thread_info->saved_preempt_count is only valid right after switch_to().

__trace_sched_switch_state() can use preempt_count(), prev is still the current task when trace_sched_switch() is called.

This is true, but the paranoid in me would like a BUG_ON(p != current) right along with that to make sure we don't accidentally change this.

But yes, its good to get rid of task_preempt_count(), its horrible.

Like so then?

---

include/trace/events/sched.h +6 -3
@@ -97,16 +97,19 @@ static inline long __trace_sched_switch_
 	long state = p->state;
 
 #ifdef CONFIG_PREEMPT
+#ifdef CONFIG_SCHED_DEBUG
+	BUG_ON(p != current);
+#endif /* CONFIG_SCHED_DEBUG */
 	/*
 	 * For all intents and purposes a preempted task is a running task.
 	 */
-	if (task_preempt_count(p) & PREEMPT_ACTIVE)
+	if (preempt_count() & PREEMPT_ACTIVE)
 		state = TASK_RUNNING | TASK_STATE_MAX;
-#endif
+#endif /* CONFIG_PREEMPT */
 
 	return state;
 }
-#endif
+#endif /* CREATE_TRACE_POINTS */
 
 /*
  * Tracepoint for task switches, performed by the scheduler:
maintainer
#21 lore raw

Re: [PATCH 1/1] sched: make finish_task_switch() return struct rq *

In reply to Oleg Nesterov
Quoted 5 lines · Oleg Nesterov
kernel/sched/core.c +0 -1
@@ -2802,15 +2802,8 @@ need_resched:
 		rq->curr = next;
 		++*switch_count;
 
-		context_switch(rq, prev, next); /* unlocks the rq */

kernel/sched/core.c need_resched: · line 2806

Quoted 10 lines
kernel/sched/core.c +2 -8
-		/*
-		 * The context switch have flipped the stack from under us
-		 * and restored the local variables which were saved when
-		 * this task called schedule() in the past. prev == current
-		 * is still correct, but it can be moved to another cpu/rq.
-		 */
-		cpu = smp_processor_id();
-		rq = cpu_rq(cpu);
+		rq = context_switch(rq, prev, next); /* unlocks the rq */
+		cpu = rq->cpu;

This won't compile on UP, cpu_of(rq) works though.

#22 lore raw

Re: [PATCH 1/1] sched: make finish_task_switch() return struct rq *

Quoted 6 lines · Peter Zijlstra
Quoted 4 lines · Oleg Nesterov
kernel/sched/core.c +0 -0
@@ -2802,15 +2802,8 @@ need_resched:
 		rq->curr = next;
 		++*switch_count;
 
Quoted 13 lines

kernel/sched/core.c need_resched: · line 2806

Quoted 11 lines
kernel/sched/core.c +2 -9
-		context_switch(rq, prev, next); /* unlocks the rq */
-		/*
-		 * The context switch have flipped the stack from under us
-		 * and restored the local variables which were saved when
-		 * this task called schedule() in the past. prev == current
-		 * is still correct, but it can be moved to another cpu/rq.
-		 */
-		cpu = smp_processor_id();
-		rq = cpu_rq(cpu);
+		rq = context_switch(rq, prev, next); /* unlocks the rq */
+		cpu = rq->cpu;

This won't compile on UP, cpu_of(rq) works though.

Heh ;) and I swear I was going to write cpu_of().

and I agree with BUG_ON() in __trace_sched_switch() if CONFIG_SCHED_DEBUG.

I am travelling till next Wednesday, will resend everything once I return.

Thanks a lot!

Oleg.

maintainer
#23 lore raw

Re: [PATCH 1/1] sched: make finish_task_switch() return struct rq *

Quoted 21 lines · Oleg Nesterov
Quoted 6 lines · Peter Zijlstra
Quoted 4 lines · Oleg Nesterov
kernel/sched/core.c +0 -0
@@ -2802,15 +2802,8 @@ need_resched:
 		rq->curr = next;
 		++*switch_count;
 
Quoted 13 lines

kernel/sched/core.c need_resched: · line 2806

Quoted 11 lines
kernel/sched/core.c +2 -9
-		context_switch(rq, prev, next); /* unlocks the rq */
-		/*
-		 * The context switch have flipped the stack from under us
-		 * and restored the local variables which were saved when
-		 * this task called schedule() in the past. prev == current
-		 * is still correct, but it can be moved to another cpu/rq.
-		 */
-		cpu = smp_processor_id();
-		rq = cpu_rq(cpu);
+		rq = context_switch(rq, prev, next); /* unlocks the rq */
+		cpu = rq->cpu;

This won't compile on UP, cpu_of(rq) works though.

Quoted 5 lines

Heh ;) and I swear I was going to write cpu_of().

and I agree with BUG_ON() in __trace_sched_switch() if CONFIG_SCHED_DEBUG.

I am travelling till next Wednesday, will resend everything once I return.

Not towards DUS by any chance? I fixed up the patches, they should be in here:
https://git.kernel.org/cgit/linux/kernel/git/peterz/queue.git/log/?h=sched/core

If I missed some, please do resend (or ping me on) those.

#24 lore raw

Re: [PATCH 1/1] sched: make finish_task_switch() return struct rq *

Quoted 6 lines · Peter Zijlstra
Quoted 2 lines · Oleg Nesterov

I am travelling till next Wednesday, will resend everything once I return.

Not towards DUS by any chance?

No, Moscow ;)

Quoted 3 lines

I fixed up the patches, they should be in
here:
https://git.kernel.org/cgit/linux/kernel/git/peterz/queue.git/log/?h=sched/core

Thanks! In particular thanks for taking the preempt_schedule_context() fix.

Oleg.

#16 lore raw

[tip:sched/core] sched: Fix schedule_tail() to disable preemption

In reply to Oleg Nesterov
sched: Fix schedule_tail() to disable preemption
Commit-ID:  1a43a14a5bd9c32dbd7af35e35a5afa703944bcb
Gitweb:     http://git.kernel.org/tip/1a43a14a5bd9c32dbd7af35e35a5afa703944bcb
Author:     Oleg Nesterov <[email protected]>
AuthorDate: Wed, 8 Oct 2014 21:36:44 +0200
Committer:  Ingo Molnar <[email protected]>
CommitDate: Tue, 28 Oct 2014 10:47:54 +0100

sched: Fix schedule_tail() to disable preemption

finish_task_switch() enables preemption, so post_schedule(rq) can be called on the wrong (and even dead) CPU. Afaics, nothing really bad can happen, but in this case we can wrongly clear rq->post_schedule on that CPU. And this simply looks wrong in any case.

---
 kernel/sched/core.c | 11 +++++------
 1 file changed, 5 insertions(+), 6 deletions(-)
kernel/sched/core.c +5 -6
index cde8481..b493560 100644
@@ -2309,15 +2309,14 @@ static inline void post_schedule(struct rq *rq)
 asmlinkage __visible void schedule_tail(struct task_struct *prev)
 	__releases(rq->lock)
 {
-	struct rq *rq = this_rq();
+	struct rq *rq;
 
+	/* finish_task_switch() drops rq->lock and enables preemtion */
+	preempt_disable();
+	rq = this_rq();
 	finish_task_switch(rq, prev);
-
-	/*
-	 * FIXME: do we need to worry about rq being invalidated by the
-	 * task_switch?
-	 */
 	post_schedule(rq);
+	preempt_enable();
 
 	if (current->set_child_tid)
 		put_user(task_pid_vnr(current), current->set_child_tid);
#4 lore raw

[tip:sched/core] sched: Fix the PREEMPT_ACTIVE check in __trace_sched_switch_state()

In reply to Oleg Nesterov
sched: Fix the PREEMPT_ACTIVE check in __trace_sched_switch_state()
Commit-ID:  8f9fbf092cd0ae31722b42c9abb427a87d55c18a
Gitweb:     http://git.kernel.org/tip/8f9fbf092cd0ae31722b42c9abb427a87d55c18a
Author:     Oleg Nesterov <[email protected]>
AuthorDate: Tue, 7 Oct 2014 21:51:08 +0200
Committer:  Ingo Molnar <[email protected]>
CommitDate: Tue, 28 Oct 2014 10:47:53 +0100

sched: Fix the PREEMPT_ACTIVE check in __trace_sched_switch_state()

task_preempt_count() has nothing to do with the actual preempt counter, thread_info->saved_preempt_count is only valid right after switch_to().

__trace_sched_switch_state() can use preempt_count(), prev is still the current task when trace_sched_switch() is called.

[ Added BUG_ON(). ]
Cc Andrew Morton <[email protected]>
Cc Andy Lutomirski <[email protected]>
Cc Linus Torvalds <[email protected]>
Cc Mel Gorman <[email protected]>
Cc Oleg Nesterov <[email protected]>
Cc Steven Rostedt <[email protected]>
---
 include/trace/events/sched.h | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)
include/trace/events/sched.h +6 -3
index 0a68d5a..30fedaf 100644
@@ -97,16 +97,19 @@ static inline long __trace_sched_switch_state(struct task_struct *p)
 	long state = p->state;
 
 #ifdef CONFIG_PREEMPT
+#ifdef CONFIG_SCHED_DEBUG
+	BUG_ON(p != current);
+#endif /* CONFIG_SCHED_DEBUG */
 	/*
 	 * For all intents and purposes a preempted task is a running task.
 	 */
-	if (task_preempt_count(p) & PREEMPT_ACTIVE)
+	if (preempt_count() & PREEMPT_ACTIVE)
 		state = TASK_RUNNING | TASK_STATE_MAX;
-#endif
+#endif /* CONFIG_PREEMPT */
 
 	return state;
 }
-#endif
+#endif /* CREATE_TRACE_POINTS */
 
 /*
  * Tracepoint for task switches, performed by the scheduler:
#25 lore raw

[tip:sched/core] sched: Make finish_task_switch() return ' struct rq *'

In reply to Oleg Nesterov
sched: Make finish_task_switch() return 'struct rq *'
Commit-ID:  dfa50b605c2a933b7bb1c1d575a0da4e897e3c7d
Gitweb:     http://git.kernel.org/tip/dfa50b605c2a933b7bb1c1d575a0da4e897e3c7d
Author:     Oleg Nesterov <[email protected]>
AuthorDate: Thu, 9 Oct 2014 21:32:32 +0200
Committer:  Ingo Molnar <[email protected]>
CommitDate: Tue, 28 Oct 2014 10:47:55 +0100

sched: Make finish_task_switch() return 'struct rq *'

Both callers of finish_task_switch() need to recalculate this_rq() and pass it as an argument, plus __schedule() does this again after context_switch().

It would be simpler to call this_rq() once in finish_task_switch() and return the this rq to the callers.

Note: probably "int cpu" in __schedule() should die; it is not used and both rcu_note_context_switch() and wq_worker_sleeping() do not really need this argument.

---
 kernel/sched/core.c | 38 +++++++++++++++-----------------------
 1 file changed, 15 insertions(+), 23 deletions(-)
kernel/sched/core.c +15 -23
index b493560..1b69603 100644
@@ -2220,7 +2220,6 @@ prepare_task_switch(struct rq *rq, struct task_struct *prev,
 
 /**
  * finish_task_switch - clean up after a task-switch
- * @rq: runqueue associated with task-switch
  * @prev: the thread we just switched away from.
  *
  * finish_task_switch must be called after the context switch, paired
@@ -2232,10 +2231,16 @@ prepare_task_switch(struct rq *rq, struct task_struct *prev,
  * so, we finish that here outside of the runqueue lock. (Doing it
  * with the lock held can cause deadlocks; see schedule() for
  * details.)
+ *
+ * The context switch have flipped the stack from under us and restored the
+ * local variables which were saved when this task called schedule() in the
+ * past. prev == current is still correct but we need to recalculate this_rq
+ * because prev may have moved to another CPU.
  */
-static void finish_task_switch(struct rq *rq, struct task_struct *prev)
+static struct rq *finish_task_switch(struct task_struct *prev)
 	__releases(rq->lock)
 {
+	struct rq *rq = this_rq();
 	struct mm_struct *mm = rq->prev_mm;
 	long prev_state;
 
@@ -2275,6 +2280,7 @@ static void finish_task_switch(struct rq *rq, struct task_struct *prev)
 	}
 
 	tick_nohz_task_switch(current);
+	return rq;
 }
 
 #ifdef CONFIG_SMP
@@ -2313,8 +2319,7 @@ asmlinkage __visible void schedule_tail(struct task_struct *prev)
 
 	/* finish_task_switch() drops rq->lock and enables preemtion */
 	preempt_disable();
-	rq = this_rq();
-	finish_task_switch(rq, prev);
+	rq = finish_task_switch(prev);
 	post_schedule(rq);
 	preempt_enable();
 
@@ -2323,10 +2328,9 @@ asmlinkage __visible void schedule_tail(struct task_struct *prev)
 }
 
 /*
- * context_switch - switch to the new MM and the new
- * thread's register state.
+ * context_switch - switch to the new MM and the new thread's register state.
  */
-static inline void
+static inline struct rq *
 context_switch(struct rq *rq, struct task_struct *prev,
 	       struct task_struct *next)
 {
@@ -2365,14 +2369,9 @@ context_switch(struct rq *rq, struct task_struct *prev,
 	context_tracking_task_switch(prev, next);
 	/* Here we just switch the register state and the stack. */
 	switch_to(prev, next, prev);
-
 	barrier();
-	/*
-	 * this_rq must be evaluated again because prev may have moved
-	 * CPUs since it called schedule(), thus the 'rq' on its stack
-	 * frame will be invalid.
-	 */
-	finish_task_switch(this_rq(), prev);
+
+	return finish_task_switch(prev);
 }
 
 /*
@@ -2854,15 +2853,8 @@ need_resched:
 		rq->curr = next;
 		++*switch_count;
 
-		context_switch(rq, prev, next); /* unlocks the rq */
-		/*
-		 * The context switch have flipped the stack from under us
-		 * and restored the local variables which were saved when
-		 * this task called schedule() in the past. prev == current
-		 * is still correct, but it can be moved to another cpu/rq.
-		 */
-		cpu = smp_processor_id();
-		rq = cpu_rq(cpu);
+		rq = context_switch(rq, prev, next); /* unlocks the rq */
+		cpu = cpu_of(rq);
 	} else
 		raw_spin_unlock_irq(&rq->lock);
 
#18 lore raw

[tip:sched/core] sched: Kill task_preempt_count()

In reply to Oleg Nesterov
sched: Kill task_preempt_count()
Commit-ID:  e2336f6e51edda875a49770b616ed5b02a74665b
Gitweb:     http://git.kernel.org/tip/e2336f6e51edda875a49770b616ed5b02a74665b
Author:     Oleg Nesterov <[email protected]>
AuthorDate: Wed, 8 Oct 2014 20:33:48 +0200
Committer:  Ingo Molnar <[email protected]>
CommitDate: Tue, 28 Oct 2014 10:47:56 +0100

sched: Kill task_preempt_count()

task_preempt_count() is pointless if preemption counter is per-cpu, currently this is x86 only. It is only valid if the task is not running, and even in this case the only info it can provide is the state of PREEMPT_ACTIVE bit.

Change its single caller to check p->on_rq instead, this should be the same if p->state != TASK_RUNNING, and kill this helper.

---
 arch/x86/include/asm/preempt.h | 3 ---
 include/asm-generic/preempt.h  | 3 ---
 kernel/sched/core.c            | 2 +-
 3 files changed, 1 insertion(+), 7 deletions(-)
arch/x86/include/asm/preempt.h +0 -3
index 4008734..8f327184 100644
@@ -30,9 +30,6 @@ static __always_inline void preempt_count_set(int pc)
 /*
  * must be macros to avoid header recursion hell
  */
-#define task_preempt_count(p) \
-	(task_thread_info(p)->saved_preempt_count & ~PREEMPT_NEED_RESCHED)
-
 #define init_task_preempt_count(p) do { \
 	task_thread_info(p)->saved_preempt_count = PREEMPT_DISABLED; \
 } while (0)
include/asm-generic/preempt.h +0 -3
index 1cd3f5d..eb6f9e6 100644
@@ -23,9 +23,6 @@ static __always_inline void preempt_count_set(int pc)
 /*
  * must be macros to avoid header recursion hell
  */
-#define task_preempt_count(p) \
-	(task_thread_info(p)->preempt_count & ~PREEMPT_NEED_RESCHED)
-
 #define init_task_preempt_count(p) do { \
 	task_thread_info(p)->preempt_count = PREEMPT_DISABLED; \
 } while (0)
kernel/sched/core.c +1 -1
index 1b69603..5c067fd 100644
@@ -1054,7 +1054,7 @@ void set_task_cpu(struct task_struct *p, unsigned int new_cpu)
 	 * ttwu() will sort out the placement.
 	 */
 	WARN_ON_ONCE(p->state != TASK_RUNNING && p->state != TASK_WAKING &&
-			!(task_preempt_count(p) & PREEMPT_ACTIVE));
+			!p->on_rq);
 
 #ifdef CONFIG_LOCKDEP
 	/*