rtmutex: Repair deadlock detector and cleanup
Subsystems (5)
cover rtmutex: Repair deadlock detector and cleanup
The first patch in the series makes the deadlock detector work again.
The second is removing the rtmutex tester as with the current implementation of the lock chain walk plus the demise of the lock steal mechanism and the BKL extra logic, we can now build a tester in userspace via the futex syscall.
The other patches clarify and optimize the deadlock detection logic and the lock chain walk mechanism.
Thanks,
Thomas
PATCH 1/6 rtmutex: Fix deadlock detector for real
The current deadlock detection logic does not work reliably due to the following early exit path:
/* * Drop out, when the task has no waiters. Note, * top_waiter can be NULL, when we are in the deboosting * mode! */ if (top_waiter && (!task_has_pi_waiters(task) || top_waiter != task_top_pi_waiter(task))) goto out_unlock_pi;
So this not only exits when the task has no waiters, it also exits unconditionally when the current waiter is not the top priority waiter of the task.
So in a nested locking scenario, it might abort the lock chain walk and therefor miss a potential deadlock.
Simple fix: Continue the chain walk, when deadlock detection is enabled.
--- kernel/locking/rtmutex.c | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-)
kernel/locking/rtmutex.c +21 -6
@@ -343,16 +343,22 @@ static int rt_mutex_adjust_prio_chain(st* top_waiter can be NULL, when we are in the deboosting * mode! */ -if (top_waiter && (!task_has_pi_waiters (task) || - top_waiter !=task_top_pi_waiter (task))) - goto out_unlock_pi; +if (top_waiter) { +if (!task_has_pi_waiters (task)) + goto out_unlock_pi; + +if (!detect_deadlock && top_waiter !=task_top_pi_waiter (task)) + goto out_unlock_pi; + }/* * When deadlock detection is off then we check, if further * priority adjustment is necessary. */ -if (!detect_deadlock && waiter->prio == task->prio) - goto out_unlock_pi; +if (waiter->prio == task->prio) { +if (!detect_deadlock) + goto out_unlock_pi; + } lock = waiter->lock;if (!raw_spin_trylock (&lock->wait_lock)) {
@@ -361,7 +367,12 @@ static int rt_mutex_adjust_prio_chain(st goto retry; } -/* Deadlock detection */ +/* +* Deadlock detection. If the lock is the same as the original +* lock which caused us to walk the lock chain or if the +* current lock is owned by the task which initiated the chain +* walk, we detected a deadlock. +*/ if (lock == orig_lock ||rt_mutex_owner (lock) == top_task) {debug_rt_mutex_deadlock (deadlock_detect, orig_waiter, lock);raw_spin_unlock (&lock->wait_lock);
@@ -527,6 +538,10 @@ static int task_blocks_on_rt_mutex(strucunsigned long flags;int chain_walk =0 , res; +/* Early deadlock detection */ +if (detect_deadlock && owner == task) +return -EDEADLK ; +raw_spin_lock_irqsave (&task->pi_lock, flags);__rt_mutex_adjust_prio (task); waiter->task = task;
Re: [patch 1/6] rtmutex: Fix deadlock detector for real
Quoted 36 lines · On Thu, 22 May 2014 03:25:39 -0000 Thomas Gleixner <[email protected]> wrote:
The current deadlock detection logic does not work reliably due to the following early exit path:
/* * Drop out, when the task has no waiters. Note, * top_waiter can be NULL, when we are in the deboosting * mode! */ if (top_waiter && (!task_has_pi_waiters(task) || top_waiter != task_top_pi_waiter(task))) goto out_unlock_pi;So this not only exits when the task has no waiters, it also exits unconditionally when the current waiter is not the top priority waiter of the task.
So in a nested locking scenario, it might abort the lock chain walk and therefor miss a potential deadlock.
Simple fix: Continue the chain walk, when deadlock detection is enabled.
Signed-off-by Thomas Gleixner <[email protected]>--- kernel/locking/rtmutex.c | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-)Index:
tip/kernel/locking/rtmutex.c
===================================================================kernel/locking/rtmutex.c +0
-0@@ -343,16 +343,22 @@ static int rt_mutex_adjust_prio_chain(st* top_waiter can be NULL, when we are in the deboosting * mode! */
kernel/locking/rtmutex.c static int rt_mutex_adjust_prio_chain(st · line 352
Quoted 10 lines
kernel/locking/rtmutex.c +7
-3-if (top_waiter && (!task_has_pi_waiters (task) || - top_waiter !=task_top_pi_waiter (task))) - goto out_unlock_pi; +if (top_waiter) { +if (!task_has_pi_waiters (task)) + goto out_unlock_pi; + +if (!detect_deadlock && top_waiter !=task_top_pi_waiter (task)) + goto out_unlock_pi; + }
The above seems obvious.
kernel/locking/rtmutex.c static int rt_mutex_adjust_prio_chain(st · line 361
Quoted 11 lines
kernel/locking/rtmutex.c +4
-2/* * When deadlock detection is off then we check, if further * priority adjustment is necessary. */ -if (!detect_deadlock && waiter->prio == task->prio) - goto out_unlock_pi; +if (waiter->prio == task->prio) { +if (!detect_deadlock) + goto out_unlock_pi; + }
This too.
Although! if you want to micro-optimize the detect_deadlock case where !detect_deadlock is false. You might want to reverse the order. That way we don't need to dereference the ->prio for both waiter and task before seeing that we don't go to the out_unlock_pi.
if (!detect_deadlock) {
if (waiter->prio == task->prio)
goto out_unlock_pi;
}Hmm, or you did it this way for your "don't requeue" patch? Looking at that one, it seems you did.
if (waiter->prio == task->prio) {
if (!detect_deadlock)
goto out_unlock_pi;
requeue = false;
}Oh well. But for stable maybe have the optimized way? And change it back when you add the requeue patch?
Quoted 18 lines
kernel/locking/rtmutex.c +6
-1lock = waiter->lock;if (!raw_spin_trylock (&lock->wait_lock)) {@@ -361,7 +367,12 @@ static int rt_mutex_adjust_prio_chain(st goto retry; } -/* Deadlock detection */ +/* +* Deadlock detection. If the lock is the same as the original +* lock which caused us to walk the lock chain or if the +* current lock is owned by the task which initiated the chain +* walk, we detected a deadlock. +*/ if (lock == orig_lock ||rt_mutex_owner (lock) == top_task) {debug_rt_mutex_deadlock (deadlock_detect, orig_waiter, lock);raw_spin_unlock (&lock->wait_lock);
kernel/locking/rtmutex.c static int task_blocks_on_rt_mutex(struc · line 544
Quoted 7 lines
kernel/locking/rtmutex.c +4
-0@@ -527,6 +538,10 @@ static int task_blocks_on_rt_mutex(strucunsigned long flags;int chain_walk =0 , res; +/* Early deadlock detection */ +if (detect_deadlock && owner == task) +return -EDEADLK ; +
This is an optimization, right? Does it belong for stable?
-- Steve
Quoted 4 lines
kernel/locking/rtmutex.c +0
-0raw_spin_lock_irqsave (&task->pi_lock, flags);__rt_mutex_adjust_prio (task); waiter->task = task;
Re: [patch 1/6] rtmutex: Fix deadlock detector for real
Quoted 28 lines · Steven Rostedt
kernel/locking/rtmutex.c static int rt_mutex_adjust_prio_chain(st · line 361
Quoted 9 lines · On Thu, 22 May 2014 03:25:39 -0000 Thomas Gleixner <[email protected]> wrote:
kernel/locking/rtmutex.c +4
-2* When deadlock detection is off then we check, if further * priority adjustment is necessary. */ -if (!detect_deadlock && waiter->prio == task->prio) - goto out_unlock_pi; +if (waiter->prio == task->prio) { +if (!detect_deadlock) + goto out_unlock_pi; + }This too.
Although! if you want to micro-optimize the
detect_deadlockcase where !detect_deadlock is false. You might want to reverse the order. That way we don't need to dereference the ->prio for both waiter and task before seeing that we don't go to theout_unlock_pi.if (!detect_deadlock) { if (waiter->prio == task->prio) goto out_unlock_pi; }Hmm, or you did it this way for your "don't requeue" patch? Looking at that one, it seems you did.
Quoted 8 lines
if (waiter->prio == task->prio) { if (!detect_deadlock) goto out_unlock_pi; requeue = false; }Oh well. But for stable maybe have the optimized way? And change it back when you add the requeue patch?
Right.
Quoted 10 lines
kernel/locking/rtmutex.c static int task_blocks_on_rt_mutex(struc · line 544
Quoted 8 lines
kernel/locking/rtmutex.c +4
-0@@ -527,6 +538,10 @@ static int task_blocks_on_rt_mutex(strucunsigned long flags;int chain_walk =0 , res; +/* Early deadlock detection */ +if (detect_deadlock && owner == task) +return -EDEADLK ; +This is an optimization, right? Does it belong for stable?
It's kind of an optimization, but we really want to avoid enqueueing the task on its own pi waiter list in the first place. So I rather keep it for stable as well.
Thanks,
tglx
tip-bot for Thomas Gleixner
merge bot[tip:core/urgent] rtmutex: Fix deadlock detector for real
In reply to Thomas Gleixnerrtmutex: Fix deadlock detector for real
Commit-ID: 397335f004f41e5fcf7a795e94eb3ab83411a17c Gitweb: http://git.kernel.org/tip/397335f004f41e5fcf7a795e94eb3ab83411a17c Author: Thomas Gleixner <[email protected]> AuthorDate: Thu, 22 May 2014 03:25:39 +0000 Committer: Thomas Gleixner <[email protected]> CommitDate: Wed, 28 May 2014 17:28:13 +0200
rtmutex: Fix deadlock detector for real
The current deadlock detection logic does not work reliably due to the following early exit path:
/* * Drop out, when the task has no waiters. Note, * top_waiter can be NULL, when we are in the deboosting * mode! */ if (top_waiter && (!task_has_pi_waiters(task) || top_waiter != task_top_pi_waiter(task))) goto out_unlock_pi;
So this not only exits when the task has no waiters, it also exits unconditionally when the current waiter is not the top priority waiter of the task.
So in a nested locking scenario, it might abort the lock chain walk and therefor miss a potential deadlock.
Simple fix: Continue the chain walk, when deadlock detection is enabled.
We also avoid the whole enqueue, if we detect the deadlock right away (A-A). It's an optimization, but also prevents that another waiter who comes in after the detection and before the task has undone the damage observes the situation and detects the deadlock and returns -EDEADLOCK, which is wrong as the other task is not in a deadlock situation.
--- kernel/locking/rtmutex.c | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-)
kernel/locking/rtmutex.c +28 -4
@@ -343,9 +343,16 @@ static int rt_mutex_adjust_prio_chain(struct task_struct *task,* top_waiter can be NULL, when we are in the deboosting * mode! */ -if (top_waiter && (!task_has_pi_waiters (task) || - top_waiter !=task_top_pi_waiter (task))) - goto out_unlock_pi; +if (top_waiter) { +if (!task_has_pi_waiters (task)) + goto out_unlock_pi; +/* +* If deadlock detection is off, we stop here if we +* are not the top pi waiter of the task. +*/ +if (!detect_deadlock && top_waiter !=task_top_pi_waiter (task)) + goto out_unlock_pi; + }/* * When deadlock detection is off then we check, if further
@@ -361,7 +368,12 @@ static int rt_mutex_adjust_prio_chain(struct task_struct *task, goto retry; } -/* Deadlock detection */ +/* +* Deadlock detection. If the lock is the same as the original +* lock which caused us to walk the lock chain or if the +* current lock is owned by the task which initiated the chain +* walk, we detected a deadlock. +*/ if (lock == orig_lock ||rt_mutex_owner (lock) == top_task) {debug_rt_mutex_deadlock (deadlock_detect, orig_waiter, lock);raw_spin_unlock (&lock->wait_lock);
@@ -527,6 +539,18 @@ static int task_blocks_on_rt_mutex(struct rt_mutex *lock,unsigned long flags;int chain_walk =0 , res; +/* +* Early deadlock detection. We really don't want the task to +* enqueue on itself just to untangle the mess later. It's not +* only an optimization. We drop the locks, so another waiter +* can come in before the chain walk detects the deadlock. So +* the other will detect the deadlock and return -EDEADLOCK, +* which is wrong, as the other waiter is not in a deadlock +* situation. +*/ +if (detect_deadlock && owner == task) +return -EDEADLK ; +raw_spin_lock_irqsave (&task->pi_lock, flags);__rt_mutex_adjust_prio (task); waiter->task = task;
PATCH 2/6 rtmutex: Remove builtin tester
In reply to Thomas GleixnerThe tester has been broken for quite some time. It's possible to fix it, but the main reason for having it in the kernel was the lock steal mechanism in the rtmutex code. That's gone, so we can implement a stateful correctness tester just via the futex syscall.
--- kernel/locking/Makefile | 1 kernel/locking/rtmutex-tester.c | 420 ------------------ kernel/locking/rtmutex.c | 2 kernel/locking/rtmutex_common.h | 22 lib/Kconfig.debug | 6 scripts/rt-tester/check-all.sh | 22 scripts/rt-tester/rt-tester.py | 220 --------- scripts/rt-tester/t2-l1-2rt-sameprio.tst | 94 ---- scripts/rt-tester/t2-l1-pi.tst | 77 --- scripts/rt-tester/t2-l1-signal.tst | 72 --- scripts/rt-tester/t2-l2-2rt-deadlock.tst | 84 --- scripts/rt-tester/t3-l1-pi-1rt.tst | 87 --- scripts/rt-tester/t3-l1-pi-2rt.tst | 88 --- scripts/rt-tester/t3-l1-pi-3rt.tst | 87 --- scripts/rt-tester/t3-l1-pi-signal.tst | 93 --- scripts/rt-tester/t3-l1-pi-steal.tst | 91 --- scripts/rt-tester/t3-l2-pi.tst | 87 --- scripts/rt-tester/t4-l2-pi-deboost.tst | 118 ----- scripts/rt-tester/t5-l4-pi-boost-deboost-setsched.tst | 178 ------- scripts/rt-tester/t5-l4-pi-boost-deboost.tst | 138 ----- 20 files changed, 1 insertion(+), 1986 deletions(-)
kernel/locking/Makefile +0 -1
@@ -18,7 +18,6 @@ obj-$(CONFIG_SMP) += lglock.o obj-$(CONFIG_PROVE_LOCKING) += spinlock.o obj-$(CONFIG_RT_MUTEXES) += rtmutex.o obj-$(CONFIG_DEBUG_RT_MUTEXES) += rtmutex-debug.o -obj-$(CONFIG_RT_MUTEX_TESTER) += rtmutex-tester.o obj-$(CONFIG_DEBUG_SPINLOCK) += spinlock.o obj-$(CONFIG_DEBUG_SPINLOCK) += spinlock_debug.o obj-$(CONFIG_RWSEM_GENERIC_SPINLOCK) += rwsem-spinlock.o
kernel/locking/rtmutex-tester.c deleted +0 -420
420 lines not on this page · raw message
kernel/locking/rtmutex.c +1 -1
@@ -745,7 +745,7 @@ __rt_mutex_slowlock(struct rt_mutex *locdebug_rt_mutex_print_deadlock (waiter); -schedule_rt_mutex (lock); +schedule ();raw_spin_lock (&lock->wait_lock);set_current_state (state);
kernel/locking/rtmutex_common.h +0 -22
@@ -15,28 +15,6 @@#include <linux/rtmutex.h> /* -* The rtmutex in kernel tester is independent of rtmutex debugging. We -* call schedule_rt_mutex_test() instead of schedule() for the tasks which -* belong to the tester. That way we can delay the wakeup path of those -* threads to provoke lock stealing and testing of complex boosting scenarios. -*/ -#ifdef CONFIG_RT_MUTEX_TESTER - -extern void schedule_rt_mutex_test (struct rt_mutex *lock); - -#define schedule_rt_mutex (_lock) \ - do { \ - if (!(current->flags & PF_MUTEX_TESTER)) \ - schedule(); \ - else \ - schedule_rt_mutex_test(_lock); \ - } while (0) - -#else -# define schedule_rt_mutex (_lock) schedule() -#endif - -/* * This is the control structure for tasks blocked on a rt_mutex, * which is allocated on the kernel stack on of the blocked task. *
lib/Kconfig.debug +0 -6
@@ -828,12 +828,6 @@ config DEBUG_PI_LIST default y depends on DEBUG_RT_MUTEXES -config RT_MUTEX_TESTER - bool "Built-in scriptable tester for rt-mutexes" - depends on DEBUG_KERNEL && RT_MUTEXES - help - This option enables a rt-mutex tester. - config DEBUG_SPINLOCK bool "Spinlock and rw-lock debugging: basic checks" depends on DEBUG_KERNEL
scripts/rt-tester/check-all.sh deleted +0 -22
@@ -1,22 +0,0 @@ - - -function testit () -{ - printf "%-30s: " $1 - ./rt-tester.py $1 | grep Pass -} - -testit t2-l1-2rt-sameprio.tst -testit t2-l1-pi.tst -testit t2-l1-signal.tst -#testit t2-l2-2rt-deadlock.tst -testit t3-l1-pi-1rt.tst -testit t3-l1-pi-2rt.tst -testit t3-l1-pi-3rt.tst -testit t3-l1-pi-signal.tst -testit t3-l1-pi-steal.tst -testit t3-l2-pi.tst -testit t4-l2-pi-deboost.tst -testit t5-l4-pi-boost-deboost.tst -testit t5-l4-pi-boost-deboost-setsched.tst -
scripts/rt-tester/rt-tester.py deleted +0 -220
220 lines not on this page · raw message
scripts/rt-tester/t2-l1-2rt-sameprio.tst deleted +0 -94
94 lines not on this page · raw message
scripts/rt-tester/t2-l1-pi.tst deleted +0 -77
77 lines not on this page · raw message
scripts/rt-tester/t2-l1-signal.tst deleted +0 -72
72 lines not on this page · raw message
scripts/rt-tester/t2-l2-2rt-deadlock.tst deleted +0 -84
84 lines not on this page · raw message
scripts/rt-tester/t3-l1-pi-1rt.tst deleted +0 -87
87 lines not on this page · raw message
scripts/rt-tester/t3-l1-pi-2rt.tst deleted +0 -88
88 lines not on this page · raw message
scripts/rt-tester/t3-l1-pi-3rt.tst deleted +0 -87
87 lines not on this page · raw message
scripts/rt-tester/t3-l1-pi-signal.tst deleted +0 -93
93 lines not on this page · raw message
scripts/rt-tester/t3-l1-pi-steal.tst deleted +0 -91
91 lines not on this page · raw message
scripts/rt-tester/t3-l2-pi.tst deleted +0 -87
87 lines not on this page · raw message
scripts/rt-tester/t4-l2-pi-deboost.tst deleted +0 -118
118 lines not on this page · raw message
scripts/rt-tester/t5-l4-pi-boost-deboost-setsched.tst deleted +0 -178
178 lines not on this page · raw message
scripts/rt-tester/t5-l4-pi-boost-deboost.tst deleted +0 -138
138 lines not on this page · raw message
Re: [patch 2/6] rtmutex: Remove builtin tester
Quoted 20 lines · On Thu, 22 May 2014 03:25:42 -0000 Thomas Gleixner <[email protected]> wrote:
The tester has been broken for quite some time. It's possible to fix it, but the main reason for having it in the kernel was the lock steal mechanism in the rtmutex code. That's gone, so we can implement a stateful correctness tester just via the futex syscall.
Signed-off-by Thomas Gleixner <[email protected]>--- kernel/locking/Makefile | 1 kernel/locking/rtmutex-tester.c | 420 ------------------ kernel/locking/rtmutex.c | 2 kernel/locking/rtmutex_common.h | 22 lib/Kconfig.debug | 6 scripts/rt-tester/check-all.sh | 22 scripts/rt-tester/rt-tester.py | 220 --------- scripts/rt-tester/t2-l1-2rt-sameprio.tst | 94 ---- scripts/rt-tester/t2-l1-pi.tst | 77 --- scripts/rt-tester/t2-l1-signal.tst | 72 --- scripts/rt-tester/t2-l2-2rt-deadlock.tst | 84 --- scripts/rt-tester/t3-l1-pi-1rt.tst | 87 --- scripts/rt-tester/t3-l1-pi-2rt.tst | 88 ---
Quoted 9 lines
scripts/rt-tester/t3-l1-pi-3rt.tst | 87 --- scripts/rt-tester/t3-l1-pi-signal.tst | 93 --- scripts/rt-tester/t3-l1-pi-steal.tst | 91 --- scripts/rt-tester/t3-l2-pi.tst | 87 --- scripts/rt-tester/t4-l2-pi-deboost.tst | 118 ----- scripts/rt-tester/t5-l4-pi-boost-deboost-setsched.tst | 178 ------- scripts/rt-tester/t5-l4-pi-boost-deboost.tst | 138 ----- 20 files changed, 1 insertion(+), 1986 deletions(-)
Missed some:
-- Steve
arch/parisc/configs/c8000_defconfig +0 -1
@@ -241,7 +241,6 @@ CONFIG_LOCKUP_DETECTOR=y CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC=y CONFIG_PANIC_ON_OOPS=y CONFIG_DEBUG_RT_MUTEXES=y -CONFIG_RT_MUTEX_TESTER=y CONFIG_PROVE_RCU_DELAY=y CONFIG_DEBUG_BLOCK_EXT_DEVT=y CONFIG_LATENCYTOP=y
arch/parisc/configs/generic-32bit_defconfig +0 -1
@@ -295,7 +295,6 @@ CONFIG_DEBUG_SHIRQ=y CONFIG_DETECT_HUNG_TASK=y CONFIG_TIMER_STATS=y CONFIG_DEBUG_RT_MUTEXES=y -CONFIG_RT_MUTEX_TESTER=y CONFIG_DEBUG_SPINLOCK=y CONFIG_DEBUG_MUTEXES=y CONFIG_RCU_CPU_STALL_INFO=y
arch/s390/configs/default_defconfig +0 -1
@@ -574,7 +574,6 @@ CONFIG_DEBUG_SHIRQ=y CONFIG_DETECT_HUNG_TASK=y CONFIG_TIMER_STATS=y CONFIG_DEBUG_RT_MUTEXES=y -CONFIG_RT_MUTEX_TESTER=y CONFIG_DEBUG_WW_MUTEX_SLOWPATH=y CONFIG_PROVE_LOCKING=y CONFIG_LOCK_STAT=y
arch/xtensa/configs/iss_defconfig +0 -1
@@ -619,7 +619,6 @@ CONFIG_SCHED_DEBUG=y # CONFIG_SLUB_DEBUG_ON is not set # CONFIG_SLUB_STATS is not set # CONFIG_DEBUG_RT_MUTEXES is not set -# CONFIG_RT_MUTEX_TESTER is not set # CONFIG_DEBUG_SPINLOCK is not set # CONFIG_DEBUG_MUTEXES is not set # CONFIG_DEBUG_SPINLOCK_SLEEP is not set
arch/xtensa/configs/s6105_defconfig +0 -1
@@ -560,7 +560,6 @@ CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=0 # CONFIG_DEBUG_OBJECTS is not set # CONFIG_DEBUG_SLAB is not set # CONFIG_DEBUG_RT_MUTEXES is not set -# CONFIG_RT_MUTEX_TESTER is not set CONFIG_DEBUG_SPINLOCK=y CONFIG_DEBUG_MUTEXES=y CONFIG_DEBUG_SPINLOCK_SLEEP=y
PATCH 3/6 rtmutex: Cleanup deadlock detector debug logic
In reply to Thomas GleixnerThe conditions under which deadlock detection is conducted are unclear and undocumented.
Add constants instead of using 0/1 and provide a selection function which hides the additional debug dependency from the calling code.
Add comments where needed.
--- kernel/futex.c | 13 ++++++++----- kernel/locking/rtmutex.c | 37 ++++++++++++++++++++++++++++++------- kernel/locking/rtmutex.h | 7 ++++++- kernel/locking/rtmutex_common.h | 15 +++++++++++++++ 4 files changed, 59 insertions(+), 13 deletions(-)
kernel/futex.c +8 -5
@@ -1619,7 +1619,8 @@ retry_private: this->pi_state = pi_state; ret =rt_mutex_start_proxy_lock (&pi_state->pi_mutex, this->rt_waiter, - this->task,1 ); + this->task, +RT_MUTEX_FULL_CHAINWALK );if (ret ==1 ) {/* We got the lock. */ requeue_pi_wake_futex (this, &key2, hb2);
@@ -2238,9 +2239,10 @@ retry_private:/* * Block on the PI mutex: */ -if (!trylock) - ret =rt_mutex_timed_lock (&q.pi_state->pi_mutex, to,1 ); -else { +if (!trylock) { + ret =rt_mutex_timed_lock (&q.pi_state->pi_mutex, to, +RT_MUTEX_FULL_CHAINWALK ); + }else { ret =rt_mutex_trylock (&q.pi_state->pi_mutex);/* Fixup the trylock return value: */ ret = ret ?0 : -EWOULDBLOCK ;
@@ -2562,7 +2564,8 @@ static int futex_wait_requeue_pi(u32 __u*/ WARN_ON (!q.pi_state); pi_mutex = &q.pi_state->pi_mutex; - ret =rt_mutex_finish_proxy_lock (pi_mutex, to, &rt_waiter,1 ); + ret =rt_mutex_finish_proxy_lock (pi_mutex, to, &rt_waiter, +RT_MUTEX_FULL_CHAINWALK );debug_rt_mutex_free_waiter (&rt_waiter);spin_lock (q.lock_ptr);
kernel/locking/rtmutex.c +30 -7
@@ -256,6 +256,25 @@ static void rt_mutex_adjust_prio(struct }/* +* Deadlock detection is conditional: +* +* If CONFIG_DEBUG_RT_MUTEXES=n, deadlock detection is only conducted +* if the detect argument is != 0. +* +* If CONFIG_DEBUG_RT_MUTEXES=y, deadlock detection is always +* conducted independent of the detect argument. +* +* If the waiter argument is NULL this indicates the deboost path and +* deadlock detection is disabled independent of the detect argument +* and the config settings. +*/ +static int +rt_mutex_cond_detect_deadlock (struct rt_mutex_waiter *waiter,int detect) +{ +return debug_rt_mutex_detect_deadlock (waiter, detect); +} + +/* * Max number of times we'll walk the boosting chain: */ int max_lock_depth =1024 ;
@@ -289,8 +308,8 @@ static int rt_mutex_adjust_prio_chain(stint detect_deadlock, ret =0 , depth =0 ;unsigned long flags; - detect_deadlock =debug_rt_mutex_detect_deadlock (orig_waiter, - deadlock_detect); + detect_deadlock =rt_mutex_cond_detect_deadlock (orig_waiter, + deadlock_detect);/* * The (de)boosting is a step by step approach with a lot of
@@ -569,9 +588,10 @@ static int task_blocks_on_rt_mutex(strucif (owner->pi_blocked_on) chain_walk =1 ;raw_spin_unlock_irqrestore (&owner->pi_lock, flags); - } -else if (debug_rt_mutex_detect_deadlock (waiter, detect_deadlock)) + + }else if (rt_mutex_cond_detect_deadlock (waiter, detect_deadlock)) { chain_walk =1 ; + }if (!chain_walk)return 0 ;
@@ -674,7 +694,8 @@ static void remove_waiter(struct rt_muteraw_spin_unlock (&lock->wait_lock); -rt_mutex_adjust_prio_chain (owner,0 , lock,NULL , current); +rt_mutex_adjust_prio_chain (owner,RT_MUTEX_MIN_CHAINWALK , lock, +NULL , current);raw_spin_lock (&lock->wait_lock); }
@@ -702,7 +723,8 @@ void rt_mutex_adjust_pi(struct task_stru/* gets dropped in rt_mutex_adjust_prio_chain()! */ get_task_struct (task); -rt_mutex_adjust_prio_chain (task,0 ,NULL ,NULL , task); +rt_mutex_adjust_prio_chain (task,RT_MUTEX_MIN_CHAINWALK ,NULL ,NULL , + task); }/**
@@ -928,7 +950,8 @@ void __sched rt_mutex_lock(struct rt_mut {might_sleep (); -rt_mutex_fastlock (lock,TASK_UNINTERRUPTIBLE ,0 , rt_mutex_slowlock); +rt_mutex_fastlock (lock,TASK_UNINTERRUPTIBLE ,RT_MUTEX_MIN_CHAINWALK , + rt_mutex_slowlock); }EXPORT_SYMBOL_GPL (rt_mutex_lock);
kernel/locking/rtmutex.h +6 -1
@@ -22,5 +22,10 @@#define debug_rt_mutex_init (m, n) do { } while (0)#define debug_rt_mutex_deadlock (d, a ,l) do { } while (0)#define debug_rt_mutex_print_deadlock (w) do { } while (0) -#define debug_rt_mutex_detect_deadlock (w,d) (d)#define debug_rt_mutex_reset_waiter (w) do { } while (0) + +static inline int debug_rt_mutex_detect_deadlock (struct rt_mutex_waiter *waiter, +int detect) +{ +return detect ==RT_MUTEX_FULL_CHAINWALK ; +}
kernel/locking/rtmutex_common.h +15 -0
@@ -80,6 +80,21 @@ static inline struct task_struct *rt_mut }/* +* Constants for rt mutex functions which have a selectable deadlock +* detection. +* +* RT_MUTEX_MIN_CHAINWALK: Stops the lock chain walk when there are +* no further PI adjustments to be made. +* +* RT_MUTEX_FULL_CHAINWALK: Invoke deadlock detection with a full +* walk of the lock chain. +*/ +enum { +RT_MUTEX_MIN_CHAINWALK , +RT_MUTEX_FULL_CHAINWALK , +}; + +/* * PI-futex support (proxy locking functions, etc.): */ extern struct task_struct *rt_mutex_next_owner (struct rt_mutex *lock);
Re: [patch 3/6] rtmutex: Cleanup deadlock detector debug logic
kernel/locking/rtmutex.c static void rt_mutex_adjust_prio(struct · line 262
Quoted 12 lines · On Thu, 22 May 2014 03:25:47 -0000 Thomas Gleixner <[email protected]> wrote:
Index:
tip/kernel/locking/rtmutex.c
===================================================================kernel/locking/rtmutex.c +4
-0@@ -256,6 +256,25 @@ static void rt_mutex_adjust_prio(struct }/* +* Deadlock detection is conditional: +* +* If CONFIG_DEBUG_RT_MUTEXES=n, deadlock detection is only conducted +* if the detect argument is != 0.
"if the detect argument is not RT_MUTEX_MIN_CHAINWALK."
We want to get rid of magic numbers right?
Quoted 21 lines
kernel/locking/rtmutex.c +18
-0+* +* If CONFIG_DEBUG_RT_MUTEXES=y, deadlock detection is always +* conducted independent of the detect argument. +* +* If the waiter argument is NULL this indicates the deboost path and +* deadlock detection is disabled independent of the detect argument +* and the config settings. +*/ +static int +rt_mutex_cond_detect_deadlock (struct rt_mutex_waiter *waiter,int detect) +{ +return debug_rt_mutex_detect_deadlock (waiter, detect); +} + +/* /* +* Constants for rt mutex functions which have a selectable deadlock +* detection. +*
kernel/locking/rtmutex.c static void rt_mutex_adjust_prio(struct
Quoted 10 lines
kernel/locking/rtmutex.c +10
-0+* RT_MUTEX_MIN_CHAINWALK: Stops the lock chain walk when there are +* no further PI adjustments to be made. +* +* RT_MUTEX_FULL_CHAINWALK: Invoke deadlock detection with a full +* walk of the lock chain. +*/ +enum { +RT_MUTEX_MIN_CHAINWALK , +RT_MUTEX_FULL_CHAINWALK , +};
Why not name this enum and instead of passing in int into the functions, pass the enum in. That way, in the future, people looking at this code can see what parameters are permitted.
-- Steve
Quoted 6 lines
kernel/locking/rtmutex.c +2
-0+ +/* * PI-futex support (proxy locking functions, etc.): */ extern struct task_struct *rt_mutex_next_owner (struct rt_mutex *lock);
tip-bot for Thomas Gleixner
merge bot[tip:locking/core] rtmutex: Cleanup deadlock detector debug logic
In reply to Thomas Gleixnerrtmutex: Cleanup deadlock detector debug logic
Commit-ID: 8930ed80f970a90a795239e7415c9b0e6f964649 Gitweb: http://git.kernel.org/tip/8930ed80f970a90a795239e7415c9b0e6f964649 Author: Thomas Gleixner <[email protected]> AuthorDate: Thu, 22 May 2014 03:25:47 +0000 Committer: Thomas Gleixner <[email protected]> CommitDate: Sat, 21 Jun 2014 22:05:30 +0200
rtmutex: Cleanup deadlock detector debug logic
The conditions under which deadlock detection is conducted are unclear and undocumented.
Add constants instead of using 0/1 and provide a selection function which hides the additional debug dependency from the calling code.
Add comments where needed.
--- kernel/locking/rtmutex-debug.c | 5 +-- kernel/locking/rtmutex-debug.h | 7 ++-- kernel/locking/rtmutex.c | 77 +++++++++++++++++++++++++++++------------ kernel/locking/rtmutex.h | 7 +++- kernel/locking/rtmutex_common.h | 15 ++++++++ 5 files changed, 83 insertions(+), 28 deletions(-)
kernel/locking/rtmutex-debug.c +3 -2
@@ -66,12 +66,13 @@ void rt_mutex_debug_task_free(struct task_struct *task)* the deadlock. We print when we return. act_waiter can be NULL in * case of a remove waiter operation. */ -void debug_rt_mutex_deadlock (int detect,struct rt_mutex_waiter *act_waiter, +void debug_rt_mutex_deadlock (enum rtmutex_chainwalk chwalk, +struct rt_mutex_waiter *act_waiter,struct rt_mutex *lock) {struct task_struct *task; -if (!debug_locks || detect || !act_waiter) +if (!debug_locks || chwalk ==RT_MUTEX_FULL_CHAINWALK || !act_waiter)return ; task =rt_mutex_owner (act_waiter->lock);
kernel/locking/rtmutex-debug.h +4 -3
@@ -20,14 +20,15 @@ extern void debug_rt_mutex_unlock(struct rt_mutex *lock);extern void debug_rt_mutex_proxy_lock (struct rt_mutex *lock,struct task_struct *powner);extern void debug_rt_mutex_proxy_unlock (struct rt_mutex *lock); -extern void debug_rt_mutex_deadlock (int detect,struct rt_mutex_waiter *waiter, +extern void debug_rt_mutex_deadlock (enum rtmutex_chainwalk chwalk, +struct rt_mutex_waiter *waiter,struct rt_mutex *lock);extern void debug_rt_mutex_print_deadlock (struct rt_mutex_waiter *waiter);# define debug_rt_mutex_reset_waiter (w) \ do { (w)->deadlock_lock = NULL; } while (0) -static inline int debug_rt_mutex_detect_deadlock (struct rt_mutex_waiter *waiter, -int detect) +static inline bool debug_rt_mutex_detect_deadlock (struct rt_mutex_waiter *waiter, +enum rtmutex_chainwalk walk) {return (waiter !=NULL ); }
kernel/locking/rtmutex.c +55 -22
191 lines not on this page · raw message
kernel/locking/rtmutex.h +6 -1
@@ -22,10 +22,15 @@#define debug_rt_mutex_init (m, n) do { } while (0)#define debug_rt_mutex_deadlock (d, a ,l) do { } while (0)#define debug_rt_mutex_print_deadlock (w) do { } while (0) -#define debug_rt_mutex_detect_deadlock (w,d) (d)#define debug_rt_mutex_reset_waiter (w) do { } while (0)static inline void rt_mutex_print_deadlock (struct rt_mutex_waiter *w) {WARN (1 ,"rtmutex deadlock detected\n" ); } + +static inline bool debug_rt_mutex_detect_deadlock (struct rt_mutex_waiter *w, +enum rtmutex_chainwalk walk) +{ +return walk ==RT_MUTEX_FULL_CHAINWALK ; +}
kernel/locking/rtmutex_common.h +15 -0
@@ -102,6 +102,21 @@ static inline struct task_struct *rt_mutex_owner(struct rt_mutex *lock) }/* +* Constants for rt mutex functions which have a selectable deadlock +* detection. +* +* RT_MUTEX_MIN_CHAINWALK: Stops the lock chain walk when there are +* no further PI adjustments to be made. +* +* RT_MUTEX_FULL_CHAINWALK: Invoke deadlock detection with a full +* walk of the lock chain. +*/ +enum rtmutex_chainwalk { +RT_MUTEX_MIN_CHAINWALK , +RT_MUTEX_FULL_CHAINWALK , +}; + +/* * PI-futex support (proxy locking functions, etc.): */ extern struct task_struct *rt_mutex_next_owner (struct rt_mutex *lock);
PATCH 4/6 rtmutex: Confine deadlock logic to futex
In reply to Thomas GleixnerThe builtin tester is gone,, so the deadlock logic is now only required for futexes.
Remove the extra arguments for the public functions and also for the futex specific ones which get always called with deadlock detection enabled.
--- include/linux/rtmutex.h | 6 +--- kernel/futex.c | 9 ++---- kernel/locking/rtmutex.c | 54 +++++++++++++++++++++------------------- kernel/locking/rtmutex_common.h | 7 ++--- 4 files changed, 37 insertions(+), 39 deletions(-)
include/linux/rtmutex.h +2 -4
@@ -90,11 +90,9 @@ extern void __rt_mutex_init(struct rt_muextern void rt_mutex_destroy (struct rt_mutex *lock);extern void rt_mutex_lock (struct rt_mutex *lock); -extern int rt_mutex_lock_interruptible (struct rt_mutex *lock, -int detect_deadlock); +extern int rt_mutex_lock_interruptible (struct rt_mutex *lock);extern int rt_mutex_timed_lock (struct rt_mutex *lock, -struct hrtimer_sleeper *timeout, -int detect_deadlock); +struct hrtimer_sleeper *timeout);extern int rt_mutex_trylock (struct rt_mutex *lock);
kernel/futex.c +3 -6
@@ -1619,8 +1619,7 @@ retry_private: this->pi_state = pi_state; ret =rt_mutex_start_proxy_lock (&pi_state->pi_mutex, this->rt_waiter, - this->task, -RT_MUTEX_FULL_CHAINWALK ); + this->task);if (ret ==1 ) {/* We got the lock. */ requeue_pi_wake_futex (this, &key2, hb2);
@@ -2240,8 +2239,7 @@ retry_private:* Block on the PI mutex: */ if (!trylock) { - ret =rt_mutex_timed_lock (&q.pi_state->pi_mutex, to, -RT_MUTEX_FULL_CHAINWALK ); + ret =__rt_mutex_timed_lock (&q.pi_state->pi_mutex, to); }else { ret =rt_mutex_trylock (&q.pi_state->pi_mutex);/* Fixup the trylock return value: */
@@ -2564,8 +2562,7 @@ static int futex_wait_requeue_pi(u32 __u*/ WARN_ON (!q.pi_state); pi_mutex = &q.pi_state->pi_mutex; - ret =rt_mutex_finish_proxy_lock (pi_mutex, to, &rt_waiter, -RT_MUTEX_FULL_CHAINWALK ); + ret =rt_mutex_finish_proxy_lock (pi_mutex, to, &rt_waiter);debug_rt_mutex_free_waiter (&rt_waiter);spin_lock (q.lock_ptr);
kernel/locking/rtmutex.c +29 -25
114 lines not on this page · raw message
kernel/locking/rtmutex_common.h +3 -4
@@ -104,12 +104,11 @@ extern void rt_mutex_proxy_unlock(struct struct task_struct *proxy_owner);extern int rt_mutex_start_proxy_lock (struct rt_mutex *lock,struct rt_mutex_waiter *waiter, -struct task_struct *task, -int detect_deadlock); +struct task_struct *task);extern int rt_mutex_finish_proxy_lock (struct rt_mutex *lock,struct hrtimer_sleeper *to, -struct rt_mutex_waiter *waiter, -int detect_deadlock); +struct rt_mutex_waiter *waiter); +extern int __rt_mutex_timed_lock (struct rt_mutex *l,struct hrtimer_sleeper *to);#ifdef CONFIG_DEBUG_RT_MUTEXES # include "rtmutex-debug.h"
Peter Zijlstra
maintainerRe: [patch 4/6] rtmutex: Confine deadlock logic to futex
Quoted 6 lines · Thomas Gleixner
The builtin tester is gone,, so the deadlock logic is now only required for futexes.
Remove the extra arguments for the public functions and also for the futex specific ones which get always called with deadlock detection enabled.
This reminds me, the raw rt_mutex primitives have no lockdep support, and we've grown a few users in-tree. We should fix that.
Re: [patch 4/6] rtmutex: Confine deadlock logic to futex
Quoted 10 lines · Peter Zijlstra
Quoted 6 lines · Thomas Gleixner
The builtin tester is gone,, so the deadlock logic is now only required for futexes.
Remove the extra arguments for the public functions and also for the futex specific ones which get always called with deadlock detection enabled.
This reminds me, the raw
rt_mutexprimitives have no lockdep support, and we've grown a few users in-tree. We should fix that.
It shouldn't be that hard. We just need to exclude the futex mess ...
Thanks,
tglx
Re: [patch 4/6] rtmutex: Confine deadlock logic to futex
In reply to Thomas GleixnerQuoted 18 lines · On Thu, 22 May 2014 03:25:50 -0000 Thomas Gleixner <[email protected]> wrote:
Index:
tip/kernel/locking/rtmutex.c
===================================================================kernel/locking/rtmutex.c +3
-5@@ -957,47 +957,53 @@ EXPORT_SYMBOL_GPL(rt_mutex_lock);/** * rt_mutex_lock_interruptible - lock a rt_mutex interruptible * -* @lock: the rt_mutex to be locked -* @detect_deadlock: deadlock detection on/off +* @lock: the rt_mutex to be locked * * Returns: -* 0 on success -* -EINTR when interrupted by a signal -* -EDEADLK when the lock would deadlock (when deadlock detection is on) +* 0 on success +* -EINTR when interrupted by a signal
kernel/locking/rtmutex.c EXPORT_SYMBOL_GPL(rt_mutex_lock); · line 971
Quoted 10 lines
kernel/locking/rtmutex.c +2
-3*/ -int __sched rt_mutex_lock_interruptible(struct rt_mutex *lock, -int detect_deadlock) +int __sched rt_mutex_lock_interruptible(struct rt_mutex *lock) {might_sleep ();return rt_mutex_fastlock (lock,TASK_INTERRUPTIBLE , - detect_deadlock, rt_mutex_slowlock); +RT_MUTEX_MIN_CHAINWALK , rt_mutex_slowlock);
I noticed after this change, the only two callers of rt_mutex_fastlock() only pass in RT_MUTEX_MIN_CHAINWALK. We could remove the passing of detect_deadlock to that too, and simplify that function.
-- Steve
Quoted 16 lines
kernel/locking/rtmutex.c +13
-0}EXPORT_SYMBOL_GPL (rt_mutex_lock_interruptible); +/* +* Futex variant to allow full deadlock detection. +*/ +int __rt_mutex_timed_lock (struct rt_mutex *lock, +struct hrtimer_sleeper *timeout) +{ +might_sleep (); + +return rt_mutex_timed_fastlock (lock,TASK_INTERRUPTIBLE , timeout, +RT_MUTEX_FULL_CHAINWALK , + rt_mutex_slowlock); +} +
PATCH 5/6 rtmutex: Clarify the lock chain walk
In reply to Thomas GleixnerAdd a separate local variable for the boost/deboost logic to make the code more readable. Add comments where appropriate.
--- kernel/locking/rtmutex.c | 50 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 42 insertions(+), 8 deletions(-)
kernel/locking/rtmutex.c +42 -8
@@ -302,9 +302,10 @@ static int rt_mutex_adjust_prio_chain(ststruct rt_mutex_waiter *orig_waiter, struct task_struct *top_task) { - struct rt_mutex *lock; struct rt_mutex_waiter *waiter, *top_waiter = orig_waiter; +struct rt_mutex_waiter *prerequeue_top_waiter;int detect_deadlock, ret =0 , depth =0 ; +struct rt_mutex *lock;unsigned long flags; detect_deadlock = rt_mutex_cond_detect_deadlock(orig_waiter,
@@ -379,6 +380,11 @@ static int rt_mutex_adjust_prio_chain(st } lock = waiter->lock; +/* +* We need to trylock here as we are holding task->pi_lock, +* which is the reverse lock order versus the other rtmutex +* operations. +*/ if (!raw_spin_trylock (&lock->wait_lock)) {raw_spin_unlock_irqrestore (&task->pi_lock, flags);cpu_relax ();
@@ -398,7 +404,11 @@ static int rt_mutex_adjust_prio_chain(st goto out_unlock_pi; } - top_waiter =rt_mutex_top_waiter (lock); +/* +* Store the top waiter before doing any requeue operation. We +* need it for the boost/deboost decision below. +*/ + prerequeue_top_waiter =rt_mutex_top_waiter (lock);/* Requeue the waiter */ rt_mutex_dequeue (lock, waiter);
@@ -407,13 +417,18 @@ static int rt_mutex_adjust_prio_chain(st/* Release the task */ raw_spin_unlock_irqrestore (&task->pi_lock, flags); + +/* +* We must abort the chain walk if there is no lock owner even +* in the dead lock detection case, as we have nothing to +* follow here. +*/ if (!rt_mutex_owner (lock)) {/* * If the requeue above changed the top waiter, then we need * to wake the new top waiter up to try to get the lock. */ - -if (top_waiter !=rt_mutex_top_waiter (lock)) +if (prerequeue_top_waiter !=rt_mutex_top_waiter (lock))wake_up_process (rt_mutex_top_waiter (lock)->task);raw_spin_unlock (&lock->wait_lock); goto out_put_task;
@@ -426,17 +441,31 @@ static int rt_mutex_adjust_prio_chain(straw_spin_lock_irqsave (&task->pi_lock, flags);if (waiter ==rt_mutex_top_waiter (lock)) { -/* Boost the owner */ -rt_mutex_dequeue_pi (task, top_waiter); +/* +* The waiter became the top waiter on the +* lock. Remove the previous top waiter from the tasks +* pi waiters list and add waiter to it. +*/ +rt_mutex_dequeue_pi (task, prerequeue_top_waiter);rt_mutex_enqueue_pi (task, waiter);__rt_mutex_adjust_prio (task); - }else if (top_waiter == waiter) { -/* Deboost the owner */ + }else if (prerequeue_top_waiter == waiter) { +/* +* The waiter was the top waiter on the lock. Remove +* waiter from the tasks pi waiters list and add the +* new top waiter to it. +*/ rt_mutex_dequeue_pi (task, waiter); waiter =rt_mutex_top_waiter (lock);rt_mutex_enqueue_pi (task, waiter);__rt_mutex_adjust_prio (task); + + }else { +/* +* Nothing changed. No need to do any priority +* adjustment. +*/ }raw_spin_unlock_irqrestore (&task->pi_lock, flags);
@@ -444,6 +473,11 @@ static int rt_mutex_adjust_prio_chain(st top_waiter =rt_mutex_top_waiter (lock);raw_spin_unlock (&lock->wait_lock); +/* +* If the current waiter is not the top waiter on the lock, +* then we can stop the chain walk here if we are not in full +* deadlock detection mode. +*/ if (!detect_deadlock && waiter != top_waiter) goto out_put_task;
Re: [patch 5/6] rtmutex: Clarify the lock chain walk
Quoted 50 lines · On Thu, 22 May 2014 03:25:54 -0000 Thomas Gleixner <[email protected]> wrote:
Add a separate local variable for the boost/deboost logic to make the code more readable. Add comments where appropriate.
Signed-off-by Thomas Gleixner <[email protected]>--- kernel/locking/rtmutex.c | 50 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 42 insertions(+), 8 deletions(-)Index:
tip/kernel/locking/rtmutex.c
===================================================================kernel/locking/rtmutex.c +12
-2@@ -302,9 +302,10 @@ static int rt_mutex_adjust_prio_chain(ststruct rt_mutex_waiter *orig_waiter, struct task_struct *top_task) { - struct rt_mutex *lock; struct rt_mutex_waiter *waiter, *top_waiter = orig_waiter; +struct rt_mutex_waiter *prerequeue_top_waiter;int detect_deadlock, ret =0 , depth =0 ; +struct rt_mutex *lock;unsigned long flags; detect_deadlock = rt_mutex_cond_detect_deadlock(orig_waiter,@@ -379,6 +380,11 @@ static int rt_mutex_adjust_prio_chain(st } lock = waiter->lock; +/* +* We need to trylock here as we are holding task->pi_lock, +* which is the reverse lock order versus the other rtmutex +* operations. +*/ if (!raw_spin_trylock (&lock->wait_lock)) {raw_spin_unlock_irqrestore (&task->pi_lock, flags);cpu_relax ();@@ -398,7 +404,11 @@ static int rt_mutex_adjust_prio_chain(st goto out_unlock_pi; } - top_waiter =rt_mutex_top_waiter (lock); +/* +* Store the top waiter before doing any requeue operation. We +* need it for the boost/deboost decision below. +*/ + prerequeue_top_waiter =rt_mutex_top_waiter (lock);/* Requeue the waiter */ rt_mutex_dequeue (lock, waiter);
kernel/locking/rtmutex.c static int rt_mutex_adjust_prio_chain(st · line 424
Quoted 8 lines
kernel/locking/rtmutex.c +5
-0@@ -407,13 +417,18 @@ static int rt_mutex_adjust_prio_chain(st/* Release the task */ raw_spin_unlock_irqrestore (&task->pi_lock, flags); + +/* +* We must abort the chain walk if there is no lock owner even +* in the dead lock detection case, as we have nothing to +* follow here.
You could mention that this is the end of the chain. That is, the chain is lock->owner->lock->owner->lock->... Once you have a lock without owner, or a owner not blocked on a lock, that's it. Pretty self explanatory. Don't Need to change anything. Just add "This is the end of the chain that we are walking."
Quoted 13 lines
kernel/locking/rtmutex.c +2
-2+*/ if (!rt_mutex_owner (lock)) {/* * If the requeue above changed the top waiter, then we need * to wake the new top waiter up to try to get the lock. */ - -if (top_waiter !=rt_mutex_top_waiter (lock)) +if (prerequeue_top_waiter !=rt_mutex_top_waiter (lock))wake_up_process (rt_mutex_top_waiter (lock)->task);raw_spin_unlock (&lock->wait_lock); goto out_put_task;
kernel/locking/rtmutex.c static int rt_mutex_adjust_prio_chain(st · line 447
Quoted 9 lines
kernel/locking/rtmutex.c +4
-2@@ -426,17 +441,31 @@ static int rt_mutex_adjust_prio_chain(straw_spin_lock_irqsave (&task->pi_lock, flags);if (waiter ==rt_mutex_top_waiter (lock)) { -/* Boost the owner */ -rt_mutex_dequeue_pi (task, top_waiter); +/* +* The waiter became the top waiter on the +* lock. Remove the previous top waiter from the tasks +* pi waiters list and add waiter to it.
Just to be a bit more verbose:
The waiter became the new top waiter on the lock. Replace the previous top waiter from the tasks pi waiters list with this waiter.
If anything, I like to use "Replace .. with" instead of "Remove .. add" as for me, it gives a better idea of what is actually being done instead of just stating how it is being implemented.
kernel/locking/rtmutex.c static int rt_mutex_adjust_prio_chain(st · line 457
Quoted 12 lines
kernel/locking/rtmutex.c +7
-2+*/ +rt_mutex_dequeue_pi (task, prerequeue_top_waiter);rt_mutex_enqueue_pi (task, waiter);__rt_mutex_adjust_prio (task); - }else if (top_waiter == waiter) { -/* Deboost the owner */ + }else if (prerequeue_top_waiter == waiter) { +/* +* The waiter was the top waiter on the lock. Remove +* waiter from the tasks pi waiters list and add the +* new top waiter to it.
Again, I would prefer to use "Replace .. with":
The waiter was the top waiter on the lock but it is no longer the highest priority waiter. Replace the waiter from the tasks pi waiters list with the new top waiter (the highest priority one).
Quoted 17 lines
kernel/locking/rtmutex.c +7
-0+*/ rt_mutex_dequeue_pi (task, waiter); waiter =rt_mutex_top_waiter (lock);rt_mutex_enqueue_pi (task, waiter);__rt_mutex_adjust_prio (task); + + }else { +/* +* Nothing changed. No need to do any priority +* adjustment. +*/ }raw_spin_unlock_irqrestore (&task->pi_lock, flags);@@ -444,6 +473,11 @@ static int rt_mutex_adjust_prio_chain(st top_waiter =rt_mutex_top_waiter (lock);
kernel/locking/rtmutex.c static int rt_mutex_adjust_prio_chain(st · line 483
Quoted 10 lines
kernel/locking/rtmutex.c +5
-0raw_spin_unlock (&lock->wait_lock); +/* +* If the current waiter is not the top waiter on the lock, +* then we can stop the chain walk here if we are not in full +* deadlock detection mode. +*/ if (!detect_deadlock && waiter != top_waiter) goto out_put_task;
Rest looks good.
-- Steve
PATCH 6/6 rtmutex: Avoid pointless requeueing in the deadlock detection chain walk
In reply to Thomas GleixnerIn case the dead lock detector is enabled we follow the lock chain to the end in rt_mutex_adjust_prio_chain, even if we could stop earlier due to the priority/waiter constellation.
But once we are not longer the top priority waiter in a certain step or the task holding the lock has already the same priority then there is no point in dequeing and enqueing along the lock chain as there is no change at all.
So stop the queueing at this point.
--- kernel/locking/rtmutex.c | 87 +++++++++++++++++++++++++++++------------------ 1 file changed, 54 insertions(+), 33 deletions(-)
kernel/locking/rtmutex.c +54 -33
@@ -307,6 +307,7 @@ static int rt_mutex_adjust_prio_chain(stint detect_deadlock, ret =0 , depth =0 ;struct rt_mutex *lock;unsigned long flags; +bool requeue = true; detect_deadlock =rt_mutex_cond_detect_deadlock (orig_waiter, deadlock_detect);
@@ -366,8 +367,11 @@ static int rt_mutex_adjust_prio_chain(stif (!task_has_pi_waiters (task)) goto out_unlock_pi; -if (!detect_deadlock && top_waiter !=task_top_pi_waiter (task)) - goto out_unlock_pi; +if (top_waiter !=task_top_pi_waiter (task)) { +if (!detect_deadlock) + goto out_unlock_pi; + requeue = false; + } }/*
@@ -377,6 +381,7 @@ static int rt_mutex_adjust_prio_chain(stif (waiter->prio == task->prio) {if (!detect_deadlock) goto out_unlock_pi; + requeue = false; } lock = waiter->lock;
@@ -410,10 +415,16 @@ static int rt_mutex_adjust_prio_chain(st*/ prerequeue_top_waiter =rt_mutex_top_waiter (lock); -/* Requeue the waiter */ -rt_mutex_dequeue (lock, waiter); - waiter->prio = task->prio; -rt_mutex_enqueue (lock, waiter); +/* +* Requeue the waiter, if we are in the boost/deboost +* operation and not just following the lock chain for +* deadlock detection. +*/ +if (requeue) { +rt_mutex_dequeue (lock, waiter); + waiter->prio = task->prio; +rt_mutex_enqueue (lock, waiter); + }/* Release the task */ raw_spin_unlock_irqrestore (&task->pi_lock, flags);
@@ -428,7 +439,8 @@ static int rt_mutex_adjust_prio_chain(st* If the requeue above changed the top waiter, then we need * to wake the new top waiter up to try to get the lock. */ -if (prerequeue_top_waiter !=rt_mutex_top_waiter (lock)) +if (requeue && + prerequeue_top_waiter !=rt_mutex_top_waiter (lock))wake_up_process (rt_mutex_top_waiter (lock)->task);raw_spin_unlock (&lock->wait_lock); goto out_put_task;
@@ -440,32 +452,41 @@ static int rt_mutex_adjust_prio_chain(stget_task_struct (task);raw_spin_lock_irqsave (&task->pi_lock, flags); -if (waiter ==rt_mutex_top_waiter (lock)) { -/* -* The waiter became the top waiter on the -* lock. Remove the previous top waiter from the tasks -* pi waiters list and add waiter to it. -*/ -rt_mutex_dequeue_pi (task, prerequeue_top_waiter); -rt_mutex_enqueue_pi (task, waiter); -__rt_mutex_adjust_prio (task); - - }else if (prerequeue_top_waiter == waiter) { -/* -* The waiter was the top waiter on the lock. Remove -* waiter from the tasks pi waiters list and add the -* new top waiter to it. -*/ -rt_mutex_dequeue_pi (task, waiter); - waiter =rt_mutex_top_waiter (lock); -rt_mutex_enqueue_pi (task, waiter); -__rt_mutex_adjust_prio (task); - - }else { -/* -* Nothing changed. No need to do any priority -* adjustment. -*/ +/* +* In case we are just following the lock chain for deadlock +* detection we can avoid the whole requeue and priority +* adjustment business. +*/ +if (requeue) { +if (waiter ==rt_mutex_top_waiter (lock)) { +/* +* The waiter became the top waiter on the +* lock. Remove the previous top waiter from +* the tasks pi waiters list and add waiter to +* it. +*/ +rt_mutex_dequeue_pi (task, prerequeue_top_waiter); +rt_mutex_enqueue_pi (task, waiter); +__rt_mutex_adjust_prio (task); + + }else if (prerequeue_top_waiter == waiter) { +/* +* The waiter was the top waiter on the +* lock. Remove waiter from the tasks pi +* waiters list and add the new top waiter to +* it. +*/ +rt_mutex_dequeue_pi (task, waiter); + waiter =rt_mutex_top_waiter (lock); +rt_mutex_enqueue_pi (task, waiter); +__rt_mutex_adjust_prio (task); + + }else { +/* +* Nothing changed. No need to do any priority +* adjustment. +*/ + } }raw_spin_unlock_irqrestore (&task->pi_lock, flags);
Re: [patch 6/6] rtmutex: Avoid pointless requeueing in the deadlock detection chain walk
Quoted 58 lines · Thomas Gleixner
kernel/locking/rtmutex.c +28
-26@@ -440,32 +452,41 @@ static int rt_mutex_adjust_prio_chain(stget_task_struct (task);raw_spin_lock_irqsave (&task->pi_lock, flags); -if (waiter ==rt_mutex_top_waiter (lock)) { -/* -* The waiter became the top waiter on the -* lock. Remove the previous top waiter from the tasks -* pi waiters list and add waiter to it. -*/ -rt_mutex_dequeue_pi (task, prerequeue_top_waiter); -rt_mutex_enqueue_pi (task, waiter); -__rt_mutex_adjust_prio (task); - - }else if (prerequeue_top_waiter == waiter) { -/* -* The waiter was the top waiter on the lock. Remove -* waiter from the tasks pi waiters list and add the -* new top waiter to it. -*/ -rt_mutex_dequeue_pi (task, waiter); - waiter =rt_mutex_top_waiter (lock); -rt_mutex_enqueue_pi (task, waiter); -__rt_mutex_adjust_prio (task); - - }else { -/* -* Nothing changed. No need to do any priority -* adjustment. -*/ +/* +* In case we are just following the lock chain for deadlock +* detection we can avoid the whole requeue and priority +* adjustment business. +*/ +if (requeue) { +if (waiter ==rt_mutex_top_waiter (lock)) { +/* +* The waiter became the top waiter on the +* lock. Remove the previous top waiter from +* the tasks pi waiters list and add waiter to +* it. +*/ +rt_mutex_dequeue_pi (task, prerequeue_top_waiter); +rt_mutex_enqueue_pi (task, waiter); +__rt_mutex_adjust_prio (task); + + }else if (prerequeue_top_waiter == waiter) { +/* +* The waiter was the top waiter on the +* lock. Remove waiter from the tasks pi +* waiters list and add the new top waiter to +* it. +*/ +rt_mutex_dequeue_pi (task, waiter); + waiter =rt_mutex_top_waiter (lock); +rt_mutex_enqueue_pi (task, waiter); +__rt_mutex_adjust_prio (task);
kernel/locking/rtmutex.c static int rt_mutex_adjust_prio_chain(st · line 492
Quoted 10 lines
kernel/locking/rtmutex.c +7
-0+ + }else { +/* +* Nothing changed. No need to do any priority +* adjustment. +*/ + } }raw_spin_unlock_irqrestore (&task->pi_lock, flags);
In the above case, could we go 1 step further and avoid taking the pi lock as well?
if (requeue) {
raw_spin_lock_irqsave(&task->pi_lock, flags);
if (waiter == rt_mutex_top_waiter(lock)) {
/*
* The waiter became the top waiter on the
* lock. Remove the previous top waiter from
* the tasks pi waiters list and add waiter to
* it.
*/
rt_mutex_dequeue_pi(task, prerequeue_top_waiter);
rt_mutex_enqueue_pi(task, waiter);
__rt_mutex_adjust_prio(task);
} else if (prerequeue_top_waiter == waiter) {
/*
* The waiter was the top waiter on the
* lock. Remove waiter from the tasks pi
* waiters list and add the new top waiter to
* it.
*/
rt_mutex_dequeue_pi(task, waiter);
waiter = rt_mutex_top_waiter(lock);
rt_mutex_enqueue_pi(task, waiter);
__rt_mutex_adjust_prio(task);
} else {
/*
* Nothing changed. No need to do any priority
* adjustment.
*/
}
raw_spin_unlock_irqrestore(&task->pi_lock, flags);
}Re: [patch 6/6] rtmutex: Avoid pointless requeueing in the deadlock detection chain walk
Quoted 101 lines · Jason Low
Quoted 58 lines · Thomas Gleixner
kernel/locking/rtmutex.c +28
-26@@ -440,32 +452,41 @@ static int rt_mutex_adjust_prio_chain(stget_task_struct (task);raw_spin_lock_irqsave (&task->pi_lock, flags); -if (waiter ==rt_mutex_top_waiter (lock)) { -/* -* The waiter became the top waiter on the -* lock. Remove the previous top waiter from the tasks -* pi waiters list and add waiter to it. -*/ -rt_mutex_dequeue_pi (task, prerequeue_top_waiter); -rt_mutex_enqueue_pi (task, waiter); -__rt_mutex_adjust_prio (task); - - }else if (prerequeue_top_waiter == waiter) { -/* -* The waiter was the top waiter on the lock. Remove -* waiter from the tasks pi waiters list and add the -* new top waiter to it. -*/ -rt_mutex_dequeue_pi (task, waiter); - waiter =rt_mutex_top_waiter (lock); -rt_mutex_enqueue_pi (task, waiter); -__rt_mutex_adjust_prio (task); - - }else { -/* -* Nothing changed. No need to do any priority -* adjustment. -*/ +/* +* In case we are just following the lock chain for deadlock +* detection we can avoid the whole requeue and priority +* adjustment business. +*/ +if (requeue) { +if (waiter ==rt_mutex_top_waiter (lock)) { +/* +* The waiter became the top waiter on the +* lock. Remove the previous top waiter from +* the tasks pi waiters list and add waiter to +* it. +*/ +rt_mutex_dequeue_pi (task, prerequeue_top_waiter); +rt_mutex_enqueue_pi (task, waiter); +__rt_mutex_adjust_prio (task); + + }else if (prerequeue_top_waiter == waiter) { +/* +* The waiter was the top waiter on the +* lock. Remove waiter from the tasks pi +* waiters list and add the new top waiter to +* it. +*/ +rt_mutex_dequeue_pi (task, waiter); + waiter =rt_mutex_top_waiter (lock); +rt_mutex_enqueue_pi (task, waiter); +__rt_mutex_adjust_prio (task);kernel/locking/rtmutex.c static int rt_mutex_adjust_prio_chain(st · line 492
Quoted 10 lines
kernel/locking/rtmutex.c +7
-0+ + }else { +/* +* Nothing changed. No need to do any priority +* adjustment. +*/ + } }raw_spin_unlock_irqrestore (&task->pi_lock, flags);In the above case, could we go 1 step further and avoid taking the pi lock as well?
if (requeue) { raw_spin_lock_irqsave(&task->pi_lock, flags); if (waiter == rt_mutex_top_waiter(lock)) { /* * The waiter became the top waiter on the * lock. Remove the previous top waiter from * the tasks pi waiters list and add waiter to * it. */ rt_mutex_dequeue_pi(task, prerequeue_top_waiter); rt_mutex_enqueue_pi(task, waiter); __rt_mutex_adjust_prio(task); } else if (prerequeue_top_waiter == waiter) { /* * The waiter was the top waiter on the * lock. Remove waiter from the tasks pi * waiters list and add the new top waiter to * it. */ rt_mutex_dequeue_pi(task, waiter); waiter = rt_mutex_top_waiter(lock); rt_mutex_enqueue_pi(task, waiter); __rt_mutex_adjust_prio(task); } else {
Quoted 8 lines
/*
* Nothing changed. No need to do any priority
* adjustment.
*/
}
raw_spin_unlock_irqrestore(&task->pi_lock, flags);
}Indeed.
Re: [patch 6/6] rtmutex: Avoid pointless requeueing in the deadlock detection chain walk
On Wed, 28 May 2014 11:43:16 +0200 (CEST)
Thomas Gleixner <[email protected]> wrote:
[snip]
Quoted 3 lines
Quoted 3 lines
In the above case, could we go 1 step further and avoid taking the pi lock as well?
[snip]
Quoted 4 lines
Indeed.
Are you going to repost this patch? I'd like to review that one instead of this one if you're going to make such a charge.
Thanks,
-- Steve
tip-bot for Thomas Gleixner
merge bot[tip:locking/core] rtmutex: Avoid pointless requeueing in the deadlock detection chain walk
In reply to Thomas Gleixnerrtmutex: Avoid pointless requeueing in the deadlock detection chain walk
Commit-ID: 67792e2cabadbadd1a93f6790fa7bcbd47eca7c3 Gitweb: http://git.kernel.org/tip/67792e2cabadbadd1a93f6790fa7bcbd47eca7c3 Author: Thomas Gleixner <[email protected]> AuthorDate: Thu, 22 May 2014 03:25:57 +0000 Committer: Thomas Gleixner <[email protected]> CommitDate: Sat, 21 Jun 2014 22:05:31 +0200
rtmutex: Avoid pointless requeueing in the deadlock detection chain walk
In case the dead lock detector is enabled we follow the lock chain to the end in rt_mutex_adjust_prio_chain, even if we could stop earlier due to the priority/waiter constellation.
But once we are no longer the top priority waiter in a certain step or the task holding the lock has already the same priority then there is no point in dequeing and enqueing along the lock chain as there is no change at all.
So stop the queueing at this point.
--- kernel/locking/rtmutex.c | 77 +++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 70 insertions(+), 7 deletions(-)
kernel/locking/rtmutex.c +70 -7
@@ -419,6 +419,7 @@ static int rt_mutex_adjust_prio_chain(struct task_struct *task,struct rt_mutex *lock;bool detect_deadlock;unsigned long flags; +bool requeue = true; detect_deadlock =rt_mutex_cond_detect_deadlock (orig_waiter, chwalk);
@@ -508,18 +509,31 @@ static int rt_mutex_adjust_prio_chain(struct task_struct *task, goto out_unlock_pi;/* * If deadlock detection is off, we stop here if we -* are not the top pi waiter of the task. +* are not the top pi waiter of the task. If deadlock +* detection is enabled we continue, but stop the +* requeueing in the chain walk. */ -if (!detect_deadlock && top_waiter !=task_top_pi_waiter (task)) - goto out_unlock_pi; +if (top_waiter !=task_top_pi_waiter (task)) { +if (!detect_deadlock) + goto out_unlock_pi; +else + requeue = false; + } }/* -* When deadlock detection is off then we check, if further -* priority adjustment is necessary. +* If the waiter priority is the same as the task priority +* then there is no further priority adjustment necessary. If +* deadlock detection is off, we stop the chain walk. If its +* enabled we continue, but stop the requeueing in the chain +* walk. */ -if (!detect_deadlock && waiter->prio == task->prio) - goto out_unlock_pi; +if (waiter->prio == task->prio) { +if (!detect_deadlock) + goto out_unlock_pi; +else + requeue = false; + }/* * [4] Get the next lock
@@ -553,6 +567,55 @@ static int rt_mutex_adjust_prio_chain(struct task_struct *task, }/* +* If we just follow the lock chain for deadlock detection, no +* need to do all the requeue operations. To avoid a truckload +* of conditionals around the various places below, just do the +* minimum chain walk checks. +*/ +if (!requeue) { +/* +* No requeue[7] here. Just release @task [8] +*/ +raw_spin_unlock_irqrestore (&task->pi_lock, flags); +put_task_struct (task); + +/* +* [9] check_exit_conditions_3 protected by lock->wait_lock. +* If there is no owner of the lock, end of chain. +*/ +if (!rt_mutex_owner (lock)) { +raw_spin_unlock (&lock->wait_lock); +return 0 ; + } + +/* [10] Grab the next task, i.e. owner of @lock */ + task =rt_mutex_owner (lock); +get_task_struct (task); +raw_spin_lock_irqsave (&task->pi_lock, flags); + +/* +* No requeue [11] here. We just do deadlock detection. +* +* [12] Store whether owner is blocked +* itself. Decision is made after dropping the locks +*/ + next_lock =task_blocked_on_lock (task); +/* +* Get the top waiter for the next iteration +*/ + top_waiter =rt_mutex_top_waiter (lock); + +/* [13] Drop locks */ +raw_spin_unlock_irqrestore (&task->pi_lock, flags); +raw_spin_unlock (&lock->wait_lock); + +/* If owner is not blocked, end of chain. */ +if (!next_lock) + goto out_put_task; + goto again; + } + +/* * Store the current top waiter before doing the requeue * operation on @lock. We need it for the boost/deboost * decision below.