|  | Subject: sched: Add saved_state for tasks blocked on sleeping locks | 
|  | From: Thomas Gleixner <tglx@linutronix.de> | 
|  | Date: Sat, 25 Jun 2011 09:21:04 +0200 | 
|  |  | 
|  | Spinlocks are state preserving in !RT. RT changes the state when a | 
|  | task gets blocked on a lock. So we need to remember the state before | 
|  | the lock contention. If a regular wakeup (not a RTmutex related | 
|  | wakeup) happens, the saved_state is updated to running. When the lock | 
|  | sleep is done, the saved state is restored. | 
|  |  | 
|  | Signed-off-by: Thomas Gleixner <tglx@linutronix.de> | 
|  | --- | 
|  | include/linux/sched.h |    3 +++ | 
|  | kernel/sched/core.c   |   31 ++++++++++++++++++++++++++++++- | 
|  | kernel/sched/sched.h  |    1 + | 
|  | 3 files changed, 34 insertions(+), 1 deletion(-) | 
|  |  | 
|  | --- a/include/linux/sched.h | 
|  | +++ b/include/linux/sched.h | 
|  | @@ -530,6 +530,8 @@ struct task_struct { | 
|  | #endif | 
|  | /* -1 unrunnable, 0 runnable, >0 stopped: */ | 
|  | volatile long			state; | 
|  | +	/* saved state for "spinlock sleepers" */ | 
|  | +	volatile long			saved_state; | 
|  |  | 
|  | /* | 
|  | * This begins the randomizable portion of task_struct. Only | 
|  | @@ -1529,6 +1531,7 @@ extern struct task_struct *find_get_task | 
|  |  | 
|  | extern int wake_up_state(struct task_struct *tsk, unsigned int state); | 
|  | extern int wake_up_process(struct task_struct *tsk); | 
|  | +extern int wake_up_lock_sleeper(struct task_struct *tsk); | 
|  | extern void wake_up_new_task(struct task_struct *tsk); | 
|  |  | 
|  | #ifdef CONFIG_SMP | 
|  | --- a/kernel/sched/core.c | 
|  | +++ b/kernel/sched/core.c | 
|  | @@ -2027,8 +2027,25 @@ try_to_wake_up(struct task_struct *p, un | 
|  | */ | 
|  | raw_spin_lock_irqsave(&p->pi_lock, flags); | 
|  | smp_mb__after_spinlock(); | 
|  | -	if (!(p->state & state)) | 
|  | +	if (!(p->state & state)) { | 
|  | +		/* | 
|  | +		 * The task might be running due to a spinlock sleeper | 
|  | +		 * wakeup. Check the saved state and set it to running | 
|  | +		 * if the wakeup condition is true. | 
|  | +		 */ | 
|  | +		if (!(wake_flags & WF_LOCK_SLEEPER)) { | 
|  | +			if (p->saved_state & state) | 
|  | +				p->saved_state = TASK_RUNNING; | 
|  | +		} | 
|  | goto out; | 
|  | +	} | 
|  | + | 
|  | +	/* | 
|  | +	 * If this is a regular wakeup, then we can unconditionally | 
|  | +	 * clear the saved state of a "lock sleeper". | 
|  | +	 */ | 
|  | +	if (!(wake_flags & WF_LOCK_SLEEPER)) | 
|  | +		p->saved_state = TASK_RUNNING; | 
|  |  | 
|  | trace_sched_waking(p); | 
|  |  | 
|  | @@ -2192,6 +2209,18 @@ int wake_up_process(struct task_struct * | 
|  | } | 
|  | EXPORT_SYMBOL(wake_up_process); | 
|  |  | 
|  | +/** | 
|  | + * wake_up_lock_sleeper - Wake up a specific process blocked on a "sleeping lock" | 
|  | + * @p: The process to be woken up. | 
|  | + * | 
|  | + * Same as wake_up_process() above, but wake_flags=WF_LOCK_SLEEPER to indicate | 
|  | + * the nature of the wakeup. | 
|  | + */ | 
|  | +int wake_up_lock_sleeper(struct task_struct *p) | 
|  | +{ | 
|  | +	return try_to_wake_up(p, TASK_ALL, WF_LOCK_SLEEPER); | 
|  | +} | 
|  | + | 
|  | int wake_up_state(struct task_struct *p, unsigned int state) | 
|  | { | 
|  | return try_to_wake_up(p, state, 0); | 
|  | --- a/kernel/sched/sched.h | 
|  | +++ b/kernel/sched/sched.h | 
|  | @@ -1362,6 +1362,7 @@ static inline int task_on_rq_migrating(s | 
|  | #define WF_SYNC		0x01		/* waker goes to sleep after wakeup */ | 
|  | #define WF_FORK		0x02		/* child wakeup after fork */ | 
|  | #define WF_MIGRATED	0x4		/* internal use, task got migrated */ | 
|  | +#define WF_LOCK_SLEEPER	0x08		/* wakeup spinlock "sleeper" */ | 
|  |  | 
|  | /* | 
|  | * To aid in avoiding the subversion of "niceness" due to uneven distribution |