sched/core: Create preempt_count invariant
Assuming units of PREEMPT_DISABLE_OFFSET for preempt_count() numbers. Now that TASK_DEAD no longer results in preempt_count() == 3 during scheduling, we will always call context_switch() with preempt_count() == 2. However, we don't always end up with preempt_count() == 2 in finish_task_switch() because new tasks get created with preempt_count() == 1. Create FORK_PREEMPT_COUNT and set it to 2 and use that in the right places. Note that we cannot use INIT_PREEMPT_COUNT as that serves another purpose (boot). After this, preempt_count() is invariant across the context switch, with exception of PREEMPT_ACTIVE. Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Mike Galbraith <efault@gmx.de> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: linux-kernel@vger.kernel.org Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
parent
b99def8b96
commit
609ca06638
4 changed files with 35 additions and 9 deletions
|
@ -31,7 +31,7 @@ static __always_inline void preempt_count_set(int pc)
|
||||||
* must be macros to avoid header recursion hell
|
* must be macros to avoid header recursion hell
|
||||||
*/
|
*/
|
||||||
#define init_task_preempt_count(p) do { \
|
#define init_task_preempt_count(p) do { \
|
||||||
task_thread_info(p)->saved_preempt_count = PREEMPT_DISABLED; \
|
task_thread_info(p)->saved_preempt_count = FORK_PREEMPT_COUNT; \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define init_idle_preempt_count(p, cpu) do { \
|
#define init_idle_preempt_count(p, cpu) do { \
|
||||||
|
|
|
@ -24,7 +24,7 @@ static __always_inline void preempt_count_set(int pc)
|
||||||
* must be macros to avoid header recursion hell
|
* must be macros to avoid header recursion hell
|
||||||
*/
|
*/
|
||||||
#define init_task_preempt_count(p) do { \
|
#define init_task_preempt_count(p) do { \
|
||||||
task_thread_info(p)->preempt_count = PREEMPT_DISABLED; \
|
task_thread_info(p)->preempt_count = FORK_PREEMPT_COUNT; \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define init_idle_preempt_count(p, cpu) do { \
|
#define init_idle_preempt_count(p, cpu) do { \
|
||||||
|
|
|
@ -599,11 +599,7 @@ struct task_cputime_atomic {
|
||||||
.sum_exec_runtime = ATOMIC64_INIT(0), \
|
.sum_exec_runtime = ATOMIC64_INIT(0), \
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_PREEMPT_COUNT
|
#define PREEMPT_DISABLED (PREEMPT_DISABLE_OFFSET + PREEMPT_ENABLED)
|
||||||
#define PREEMPT_DISABLED (1 + PREEMPT_ENABLED)
|
|
||||||
#else
|
|
||||||
#define PREEMPT_DISABLED PREEMPT_ENABLED
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Disable preemption until the scheduler is running -- use an unconditional
|
* Disable preemption until the scheduler is running -- use an unconditional
|
||||||
|
@ -613,6 +609,17 @@ struct task_cputime_atomic {
|
||||||
*/
|
*/
|
||||||
#define INIT_PREEMPT_COUNT PREEMPT_OFFSET
|
#define INIT_PREEMPT_COUNT PREEMPT_OFFSET
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initial preempt_count value; reflects the preempt_count schedule invariant
|
||||||
|
* which states that during context switches:
|
||||||
|
*
|
||||||
|
* preempt_count() == 2*PREEMPT_DISABLE_OFFSET
|
||||||
|
*
|
||||||
|
* Note: PREEMPT_DISABLE_OFFSET is 0 for !PREEMPT_COUNT kernels.
|
||||||
|
* Note: See finish_task_switch().
|
||||||
|
*/
|
||||||
|
#define FORK_PREEMPT_COUNT (2*PREEMPT_DISABLE_OFFSET + PREEMPT_ENABLED)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct thread_group_cputimer - thread group interval timer counts
|
* struct thread_group_cputimer - thread group interval timer counts
|
||||||
* @cputime_atomic: atomic thread group interval timers.
|
* @cputime_atomic: atomic thread group interval timers.
|
||||||
|
|
|
@ -2504,6 +2504,18 @@ static struct rq *finish_task_switch(struct task_struct *prev)
|
||||||
struct mm_struct *mm = rq->prev_mm;
|
struct mm_struct *mm = rq->prev_mm;
|
||||||
long prev_state;
|
long prev_state;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The previous task will have left us with a preempt_count of 2
|
||||||
|
* because it left us after:
|
||||||
|
*
|
||||||
|
* schedule()
|
||||||
|
* preempt_disable(); // 1
|
||||||
|
* __schedule()
|
||||||
|
* raw_spin_lock_irq(&rq->lock) // 2
|
||||||
|
*
|
||||||
|
* Also, see FORK_PREEMPT_COUNT.
|
||||||
|
*/
|
||||||
|
|
||||||
rq->prev_mm = NULL;
|
rq->prev_mm = NULL;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -2588,8 +2600,15 @@ asmlinkage __visible void schedule_tail(struct task_struct *prev)
|
||||||
{
|
{
|
||||||
struct rq *rq;
|
struct rq *rq;
|
||||||
|
|
||||||
/* finish_task_switch() drops rq->lock and enables preemtion */
|
/*
|
||||||
preempt_disable();
|
* New tasks start with FORK_PREEMPT_COUNT, see there and
|
||||||
|
* finish_task_switch() for details.
|
||||||
|
*
|
||||||
|
* finish_task_switch() will drop rq->lock() and lower preempt_count
|
||||||
|
* and the preempt_enable() will end up enabling preemption (on
|
||||||
|
* PREEMPT_COUNT kernels).
|
||||||
|
*/
|
||||||
|
|
||||||
rq = finish_task_switch(prev);
|
rq = finish_task_switch(prev);
|
||||||
balance_callback(rq);
|
balance_callback(rq);
|
||||||
preempt_enable();
|
preempt_enable();
|
||||||
|
|
Loading…
Reference in a new issue