From 040a0a37100563754bb1fee6ff6427420bcfa609 Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Mon, 24 Jun 2013 10:30:04 +0200 Subject: mutex: Add support for wound/wait style locks Wound/wait mutexes are used when other multiple lock acquisitions of a similar type can be done in an arbitrary order. The deadlock handling used here is called wait/wound in the RDBMS literature: The older tasks waits until it can acquire the contended lock. The younger tasks needs to back off and drop all the locks it is currently holding, i.e. the younger task is wounded. For full documentation please read Documentation/ww-mutex-design.txt. References: https://lwn.net/Articles/548909/ Signed-off-by: Maarten Lankhorst Acked-by: Daniel Vetter Acked-by: Rob Clark Acked-by: Peter Zijlstra Cc: dri-devel@lists.freedesktop.org Cc: linaro-mm-sig@lists.linaro.org Cc: rostedt@goodmis.org Cc: daniel@ffwll.ch Cc: Linus Torvalds Cc: Andrew Morton Cc: Thomas Gleixner Link: http://lkml.kernel.org/r/51C8038C.9000106@canonical.com Signed-off-by: Ingo Molnar --- lib/debug_locks.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'lib') diff --git a/lib/debug_locks.c b/lib/debug_locks.c index f2fa60c..96c4c63 100644 --- a/lib/debug_locks.c +++ b/lib/debug_locks.c @@ -30,6 +30,7 @@ EXPORT_SYMBOL_GPL(debug_locks); * a locking bug is detected. */ int debug_locks_silent; +EXPORT_SYMBOL_GPL(debug_locks_silent); /* * Generic 'turn off all lock debugging' function: @@ -44,3 +45,4 @@ int debug_locks_off(void) } return 0; } +EXPORT_SYMBOL_GPL(debug_locks_off); -- cgit v1.1 From 230100276955529d5a7c69207421756b9a61a8e5 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 20 Jun 2013 13:31:17 +0200 Subject: mutex: Add w/w mutex slowpath debugging Injects EDEADLK conditions at pseudo-random interval, with exponential backoff up to UINT_MAX (to ensure that every lock operation still completes in a reasonable time). This way we can test the wound slowpath even for ww mutex users where contention is never expected, and the ww deadlock avoidance algorithm is only needed for correctness against malicious userspace. An example would be protecting kernel modesetting properties, which thanks to single-threaded X isn't really expected to contend, ever. I've looked into using the CONFIG_FAULT_INJECTION infrastructure, but decided against it for two reasons: - EDEADLK handling is mandatory for ww mutex users and should never affect the outcome of a syscall. This is in contrast to -ENOMEM injection. So fine configurability isn't required. - The fault injection framework only allows to set a simple probability for failure. Now the probability that a ww mutex acquire stage with N locks will never complete (due to too many injected EDEADLK backoffs) is zero. But the expected number of ww_mutex_lock operations for the completely uncontended case would be O(exp(N)). The per-acuiqire ctx exponential backoff solution choosen here only results in O(log N) overhead due to injection and so O(log N * N) lock operations. This way we can fail with high probability (and so have good test coverage even for fancy backoff and lock acquisition paths) without running into patalogical cases. Note that EDEADLK will only ever be injected when we managed to acquire the lock. This prevents any behaviour changes for users which rely on the EALREADY semantics. Signed-off-by: Daniel Vetter Signed-off-by: Maarten Lankhorst Acked-by: Peter Zijlstra Cc: dri-devel@lists.freedesktop.org Cc: linaro-mm-sig@lists.linaro.org Cc: rostedt@goodmis.org Cc: daniel@ffwll.ch Cc: Linus Torvalds Cc: Andrew Morton Cc: Thomas Gleixner Link: http://lkml.kernel.org/r/20130620113117.4001.21681.stgit@patser Signed-off-by: Ingo Molnar --- lib/Kconfig.debug | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'lib') diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index 566cf2b..7154f79 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -547,6 +547,19 @@ config DEBUG_MUTEXES This feature allows mutex semantics violations to be detected and reported. +config DEBUG_WW_MUTEX_SLOWPATH + bool "Wait/wound mutex debugging: Slowpath testing" + depends on DEBUG_KERNEL && TRACE_IRQFLAGS_SUPPORT && STACKTRACE_SUPPORT && LOCKDEP_SUPPORT + select DEBUG_LOCK_ALLOC + select DEBUG_SPINLOCK + select DEBUG_MUTEXES + help + This feature enables slowpath testing for w/w mutex users by + injecting additional -EDEADLK wound/backoff cases. Together with + the full mutex checks enabled with (CONFIG_PROVE_LOCKING) this + will test all possible w/w mutex interface abuse with the + exception of simply not acquiring all the required locks. + config DEBUG_LOCK_ALLOC bool "Lock debugging: detect incorrect freeing of live locks" depends on DEBUG_KERNEL && TRACE_IRQFLAGS_SUPPORT && STACKTRACE_SUPPORT && LOCKDEP_SUPPORT -- cgit v1.1 From 1de994452f44005e4b1f5c6c77eae4a26f86d484 Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Thu, 20 Jun 2013 13:31:24 +0200 Subject: mutex: Add w/w tests to lib/locking-selftest.c This stresses the lockdep code in some ways specifically useful to ww_mutexes. It adds checks for most of the common locking errors. Signed-off-by: Maarten Lankhorst Acked-by: Peter Zijlstra Cc: dri-devel@lists.freedesktop.org Cc: linaro-mm-sig@lists.linaro.org Cc: robclark@gmail.com Cc: rostedt@goodmis.org Cc: daniel@ffwll.ch Cc: Linus Torvalds Cc: Andrew Morton Cc: Thomas Gleixner Link: http://lkml.kernel.org/r/20130620113124.4001.23186.stgit@patser Signed-off-by: Ingo Molnar --- lib/locking-selftest.c | 400 ++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 381 insertions(+), 19 deletions(-) (limited to 'lib') diff --git a/lib/locking-selftest.c b/lib/locking-selftest.c index c3eb261..9962262 100644 --- a/lib/locking-selftest.c +++ b/lib/locking-selftest.c @@ -26,6 +26,8 @@ */ static unsigned int debug_locks_verbose; +static DEFINE_WW_CLASS(ww_lockdep); + static int __init setup_debug_locks_verbose(char *str) { get_option(&str, &debug_locks_verbose); @@ -42,6 +44,10 @@ __setup("debug_locks_verbose=", setup_debug_locks_verbose); #define LOCKTYPE_RWLOCK 0x2 #define LOCKTYPE_MUTEX 0x4 #define LOCKTYPE_RWSEM 0x8 +#define LOCKTYPE_WW 0x10 + +static struct ww_acquire_ctx t, t2; +static struct ww_mutex o, o2; /* * Normal standalone locks, for the circular and irq-context @@ -193,6 +199,20 @@ static void init_shared_classes(void) #define RSU(x) up_read(&rwsem_##x) #define RWSI(x) init_rwsem(&rwsem_##x) +#ifndef CONFIG_DEBUG_WW_MUTEX_SLOWPATH +#define WWAI(x) ww_acquire_init(x, &ww_lockdep) +#else +#define WWAI(x) do { ww_acquire_init(x, &ww_lockdep); (x)->deadlock_inject_countdown = ~0U; } while (0) +#endif +#define WWAD(x) ww_acquire_done(x) +#define WWAF(x) ww_acquire_fini(x) + +#define WWL(x, c) ww_mutex_lock(x, c) +#define WWT(x) ww_mutex_trylock(x) +#define WWL1(x) ww_mutex_lock(x, NULL) +#define WWU(x) ww_mutex_unlock(x) + + #define LOCK_UNLOCK_2(x,y) LOCK(x); LOCK(y); UNLOCK(y); UNLOCK(x) /* @@ -894,11 +914,13 @@ GENERATE_PERMUTATIONS_3_EVENTS(irq_read_recursion_soft) # define I_RWLOCK(x) lockdep_reset_lock(&rwlock_##x.dep_map) # define I_MUTEX(x) lockdep_reset_lock(&mutex_##x.dep_map) # define I_RWSEM(x) lockdep_reset_lock(&rwsem_##x.dep_map) +# define I_WW(x) lockdep_reset_lock(&x.dep_map) #else # define I_SPINLOCK(x) # define I_RWLOCK(x) # define I_MUTEX(x) # define I_RWSEM(x) +# define I_WW(x) #endif #define I1(x) \ @@ -920,11 +942,20 @@ GENERATE_PERMUTATIONS_3_EVENTS(irq_read_recursion_soft) static void reset_locks(void) { local_irq_disable(); + lockdep_free_key_range(&ww_lockdep.acquire_key, 1); + lockdep_free_key_range(&ww_lockdep.mutex_key, 1); + I1(A); I1(B); I1(C); I1(D); I1(X1); I1(X2); I1(Y1); I1(Y2); I1(Z1); I1(Z2); + I_WW(t); I_WW(t2); I_WW(o.base); I_WW(o2.base); lockdep_reset(); I2(A); I2(B); I2(C); I2(D); init_shared_classes(); + + ww_mutex_init(&o, &ww_lockdep); ww_mutex_init(&o2, &ww_lockdep); + memset(&t, 0, sizeof(t)); memset(&t2, 0, sizeof(t2)); + memset(&ww_lockdep.acquire_key, 0, sizeof(ww_lockdep.acquire_key)); + memset(&ww_lockdep.mutex_key, 0, sizeof(ww_lockdep.mutex_key)); local_irq_enable(); } @@ -938,7 +969,6 @@ static int unexpected_testcase_failures; static void dotest(void (*testcase_fn)(void), int expected, int lockclass_mask) { unsigned long saved_preempt_count = preempt_count(); - int expected_failure = 0; WARN_ON(irqs_disabled()); @@ -946,26 +976,16 @@ static void dotest(void (*testcase_fn)(void), int expected, int lockclass_mask) /* * Filter out expected failures: */ + if (debug_locks != expected) { #ifndef CONFIG_PROVE_LOCKING - if ((lockclass_mask & LOCKTYPE_SPIN) && debug_locks != expected) - expected_failure = 1; - if ((lockclass_mask & LOCKTYPE_RWLOCK) && debug_locks != expected) - expected_failure = 1; - if ((lockclass_mask & LOCKTYPE_MUTEX) && debug_locks != expected) - expected_failure = 1; - if ((lockclass_mask & LOCKTYPE_RWSEM) && debug_locks != expected) - expected_failure = 1; + expected_testcase_failures++; + printk("failed|"); +#else + unexpected_testcase_failures++; + printk("FAILED|"); + + dump_stack(); #endif - if (debug_locks != expected) { - if (expected_failure) { - expected_testcase_failures++; - printk("failed|"); - } else { - unexpected_testcase_failures++; - - printk("FAILED|"); - dump_stack(); - } } else { testcase_successes++; printk(" ok |"); @@ -1108,6 +1128,346 @@ static inline void print_testname(const char *testname) DO_TESTCASE_6IRW(desc, name, 312); \ DO_TESTCASE_6IRW(desc, name, 321); +static void ww_test_fail_acquire(void) +{ + int ret; + + WWAI(&t); + t.stamp++; + + ret = WWL(&o, &t); + + if (WARN_ON(!o.ctx) || + WARN_ON(ret)) + return; + + /* No lockdep test, pure API */ + ret = WWL(&o, &t); + WARN_ON(ret != -EALREADY); + + ret = WWT(&o); + WARN_ON(ret); + + t2 = t; + t2.stamp++; + ret = WWL(&o, &t2); + WARN_ON(ret != -EDEADLK); + WWU(&o); + + if (WWT(&o)) + WWU(&o); +#ifdef CONFIG_DEBUG_LOCK_ALLOC + else + DEBUG_LOCKS_WARN_ON(1); +#endif +} + +static void ww_test_two_contexts(void) +{ + WWAI(&t); + WWAI(&t2); +} + +static void ww_test_diff_class(void) +{ + WWAI(&t); +#ifdef CONFIG_DEBUG_MUTEXES + t.ww_class = NULL; +#endif + WWL(&o, &t); +} + +static void ww_test_context_done_twice(void) +{ + WWAI(&t); + WWAD(&t); + WWAD(&t); + WWAF(&t); +} + +static void ww_test_context_unlock_twice(void) +{ + WWAI(&t); + WWAD(&t); + WWAF(&t); + WWAF(&t); +} + +static void ww_test_context_fini_early(void) +{ + WWAI(&t); + WWL(&o, &t); + WWAD(&t); + WWAF(&t); +} + +static void ww_test_context_lock_after_done(void) +{ + WWAI(&t); + WWAD(&t); + WWL(&o, &t); +} + +static void ww_test_object_unlock_twice(void) +{ + WWL1(&o); + WWU(&o); + WWU(&o); +} + +static void ww_test_object_lock_unbalanced(void) +{ + WWAI(&t); + WWL(&o, &t); + t.acquired = 0; + WWU(&o); + WWAF(&t); +} + +static void ww_test_object_lock_stale_context(void) +{ + WWAI(&t); + o.ctx = &t2; + WWL(&o, &t); +} + +static void ww_test_spin_nest_unlocked(void) +{ + raw_spin_lock_nest_lock(&lock_A, &o.base); + U(A); +} + +static void ww_test_unneeded_slow(void) +{ + WWAI(&t); + + ww_mutex_lock_slow(&o, &t); +} + +static void ww_test_context_block(void) +{ + int ret; + + WWAI(&t); + + ret = WWL(&o, &t); + WARN_ON(ret); + WWL1(&o2); +} + +static void ww_test_context_try(void) +{ + int ret; + + WWAI(&t); + + ret = WWL(&o, &t); + WARN_ON(ret); + + ret = WWT(&o2); + WARN_ON(!ret); + WWU(&o2); + WWU(&o); +} + +static void ww_test_context_context(void) +{ + int ret; + + WWAI(&t); + + ret = WWL(&o, &t); + WARN_ON(ret); + + ret = WWL(&o2, &t); + WARN_ON(ret); + + WWU(&o2); + WWU(&o); +} + +static void ww_test_try_block(void) +{ + bool ret; + + ret = WWT(&o); + WARN_ON(!ret); + + WWL1(&o2); + WWU(&o2); + WWU(&o); +} + +static void ww_test_try_try(void) +{ + bool ret; + + ret = WWT(&o); + WARN_ON(!ret); + ret = WWT(&o2); + WARN_ON(!ret); + WWU(&o2); + WWU(&o); +} + +static void ww_test_try_context(void) +{ + int ret; + + ret = WWT(&o); + WARN_ON(!ret); + + WWAI(&t); + + ret = WWL(&o2, &t); + WARN_ON(ret); +} + +static void ww_test_block_block(void) +{ + WWL1(&o); + WWL1(&o2); +} + +static void ww_test_block_try(void) +{ + bool ret; + + WWL1(&o); + ret = WWT(&o2); + WARN_ON(!ret); +} + +static void ww_test_block_context(void) +{ + int ret; + + WWL1(&o); + WWAI(&t); + + ret = WWL(&o2, &t); + WARN_ON(ret); +} + +static void ww_test_spin_block(void) +{ + L(A); + U(A); + + WWL1(&o); + L(A); + U(A); + WWU(&o); + + L(A); + WWL1(&o); + WWU(&o); + U(A); +} + +static void ww_test_spin_try(void) +{ + bool ret; + + L(A); + U(A); + + ret = WWT(&o); + WARN_ON(!ret); + L(A); + U(A); + WWU(&o); + + L(A); + ret = WWT(&o); + WARN_ON(!ret); + WWU(&o); + U(A); +} + +static void ww_test_spin_context(void) +{ + int ret; + + L(A); + U(A); + + WWAI(&t); + + ret = WWL(&o, &t); + WARN_ON(ret); + L(A); + U(A); + WWU(&o); + + L(A); + ret = WWL(&o, &t); + WARN_ON(ret); + WWU(&o); + U(A); +} + +static void ww_tests(void) +{ + printk(" --------------------------------------------------------------------------\n"); + printk(" | Wound/wait tests |\n"); + printk(" ---------------------\n"); + + print_testname("ww api failures"); + dotest(ww_test_fail_acquire, SUCCESS, LOCKTYPE_WW); + dotest(ww_test_unneeded_slow, FAILURE, LOCKTYPE_WW); + printk("\n"); + + print_testname("ww contexts mixing"); + dotest(ww_test_two_contexts, FAILURE, LOCKTYPE_WW); + dotest(ww_test_diff_class, FAILURE, LOCKTYPE_WW); + printk("\n"); + + print_testname("finishing ww context"); + dotest(ww_test_context_done_twice, FAILURE, LOCKTYPE_WW); + dotest(ww_test_context_unlock_twice, FAILURE, LOCKTYPE_WW); + dotest(ww_test_context_fini_early, FAILURE, LOCKTYPE_WW); + dotest(ww_test_context_lock_after_done, FAILURE, LOCKTYPE_WW); + printk("\n"); + + print_testname("locking mismatches"); + dotest(ww_test_object_unlock_twice, FAILURE, LOCKTYPE_WW); + dotest(ww_test_object_lock_unbalanced, FAILURE, LOCKTYPE_WW); + dotest(ww_test_object_lock_stale_context, FAILURE, LOCKTYPE_WW); + printk("\n"); + + print_testname("spinlock nest unlocked"); + dotest(ww_test_spin_nest_unlocked, FAILURE, LOCKTYPE_WW); + printk("\n"); + + printk(" -----------------------------------------------------\n"); + printk(" |block | try |context|\n"); + printk(" -----------------------------------------------------\n"); + + print_testname("context"); + dotest(ww_test_context_block, FAILURE, LOCKTYPE_WW); + dotest(ww_test_context_try, SUCCESS, LOCKTYPE_WW); + dotest(ww_test_context_context, SUCCESS, LOCKTYPE_WW); + printk("\n"); + + print_testname("try"); + dotest(ww_test_try_block, FAILURE, LOCKTYPE_WW); + dotest(ww_test_try_try, SUCCESS, LOCKTYPE_WW); + dotest(ww_test_try_context, FAILURE, LOCKTYPE_WW); + printk("\n"); + + print_testname("block"); + dotest(ww_test_block_block, FAILURE, LOCKTYPE_WW); + dotest(ww_test_block_try, SUCCESS, LOCKTYPE_WW); + dotest(ww_test_block_context, FAILURE, LOCKTYPE_WW); + printk("\n"); + + print_testname("spinlock"); + dotest(ww_test_spin_block, FAILURE, LOCKTYPE_WW); + dotest(ww_test_spin_try, SUCCESS, LOCKTYPE_WW); + dotest(ww_test_spin_context, FAILURE, LOCKTYPE_WW); + printk("\n"); +} void locking_selftest(void) { @@ -1188,6 +1548,8 @@ void locking_selftest(void) DO_TESTCASE_6x2("irq read-recursion", irq_read_recursion); // DO_TESTCASE_6x2B("irq read-recursion #2", irq_read_recursion2); + ww_tests(); + if (unexpected_testcase_failures) { printk("-----------------------------------------------------------------\n"); debug_locks = 0; -- cgit v1.1 From 2fe3d4b149ccebbb384062fbbe6634439f2bf120 Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Thu, 20 Jun 2013 13:31:30 +0200 Subject: mutex: Add more tests to lib/locking-selftest.c None of the ww_mutex codepaths should be taken in the 'normal' mutex calls. The easiest way to verify this is by using the normal mutex calls, and making sure o.ctx is unmodified. Signed-off-by: Maarten Lankhorst Acked-by: Peter Zijlstra Cc: dri-devel@lists.freedesktop.org Cc: linaro-mm-sig@lists.linaro.org Cc: robclark@gmail.com Cc: rostedt@goodmis.org Cc: daniel@ffwll.ch Cc: Linus Torvalds Cc: Andrew Morton Cc: Thomas Gleixner Link: http://lkml.kernel.org/r/20130620113130.4001.45423.stgit@patser Signed-off-by: Ingo Molnar --- lib/locking-selftest.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) (limited to 'lib') diff --git a/lib/locking-selftest.c b/lib/locking-selftest.c index 9962262..37faefd 100644 --- a/lib/locking-selftest.c +++ b/lib/locking-selftest.c @@ -1162,6 +1162,67 @@ static void ww_test_fail_acquire(void) #endif } +static void ww_test_normal(void) +{ + int ret; + + WWAI(&t); + + /* + * None of the ww_mutex codepaths should be taken in the 'normal' + * mutex calls. The easiest way to verify this is by using the + * normal mutex calls, and making sure o.ctx is unmodified. + */ + + /* mutex_lock (and indirectly, mutex_lock_nested) */ + o.ctx = (void *)~0UL; + mutex_lock(&o.base); + mutex_unlock(&o.base); + WARN_ON(o.ctx != (void *)~0UL); + + /* mutex_lock_interruptible (and *_nested) */ + o.ctx = (void *)~0UL; + ret = mutex_lock_interruptible(&o.base); + if (!ret) + mutex_unlock(&o.base); + else + WARN_ON(1); + WARN_ON(o.ctx != (void *)~0UL); + + /* mutex_lock_killable (and *_nested) */ + o.ctx = (void *)~0UL; + ret = mutex_lock_killable(&o.base); + if (!ret) + mutex_unlock(&o.base); + else + WARN_ON(1); + WARN_ON(o.ctx != (void *)~0UL); + + /* trylock, succeeding */ + o.ctx = (void *)~0UL; + ret = mutex_trylock(&o.base); + WARN_ON(!ret); + if (ret) + mutex_unlock(&o.base); + else + WARN_ON(1); + WARN_ON(o.ctx != (void *)~0UL); + + /* trylock, failing */ + o.ctx = (void *)~0UL; + mutex_lock(&o.base); + ret = mutex_trylock(&o.base); + WARN_ON(ret); + mutex_unlock(&o.base); + WARN_ON(o.ctx != (void *)~0UL); + + /* nest_lock */ + o.ctx = (void *)~0UL; + mutex_lock_nest_lock(&o.base, &t); + mutex_unlock(&o.base); + WARN_ON(o.ctx != (void *)~0UL); +} + static void ww_test_two_contexts(void) { WWAI(&t); @@ -1415,6 +1476,7 @@ static void ww_tests(void) print_testname("ww api failures"); dotest(ww_test_fail_acquire, SUCCESS, LOCKTYPE_WW); + dotest(ww_test_normal, SUCCESS, LOCKTYPE_WW); dotest(ww_test_unneeded_slow, FAILURE, LOCKTYPE_WW); printk("\n"); -- cgit v1.1 From f3cf139efa4bc0fe4f032af6ca3e49e38a5d9ae5 Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Thu, 20 Jun 2013 13:31:42 +0200 Subject: mutex: Add more w/w tests to test EDEADLK path handling Signed-off-by: Maarten Lankhorst Acked-by: Peter Zijlstra Cc: dri-devel@lists.freedesktop.org Cc: linaro-mm-sig@lists.linaro.org Cc: rostedt@goodmis.org Cc: daniel@ffwll.ch Cc: Linus Torvalds Cc: Andrew Morton Cc: Thomas Gleixner Link: http://lkml.kernel.org/r/20130620113141.4001.54331.stgit@patser Signed-off-by: Ingo Molnar --- lib/locking-selftest.c | 264 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 261 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/locking-selftest.c b/lib/locking-selftest.c index 37faefd..d554f3f 100644 --- a/lib/locking-selftest.c +++ b/lib/locking-selftest.c @@ -47,7 +47,7 @@ __setup("debug_locks_verbose=", setup_debug_locks_verbose); #define LOCKTYPE_WW 0x10 static struct ww_acquire_ctx t, t2; -static struct ww_mutex o, o2; +static struct ww_mutex o, o2, o3; /* * Normal standalone locks, for the circular and irq-context @@ -947,12 +947,12 @@ static void reset_locks(void) I1(A); I1(B); I1(C); I1(D); I1(X1); I1(X2); I1(Y1); I1(Y2); I1(Z1); I1(Z2); - I_WW(t); I_WW(t2); I_WW(o.base); I_WW(o2.base); + I_WW(t); I_WW(t2); I_WW(o.base); I_WW(o2.base); I_WW(o3.base); lockdep_reset(); I2(A); I2(B); I2(C); I2(D); init_shared_classes(); - ww_mutex_init(&o, &ww_lockdep); ww_mutex_init(&o2, &ww_lockdep); + ww_mutex_init(&o, &ww_lockdep); ww_mutex_init(&o2, &ww_lockdep); ww_mutex_init(&o3, &ww_lockdep); memset(&t, 0, sizeof(t)); memset(&t2, 0, sizeof(t2)); memset(&ww_lockdep.acquire_key, 0, sizeof(ww_lockdep.acquire_key)); memset(&ww_lockdep.mutex_key, 0, sizeof(ww_lockdep.mutex_key)); @@ -1292,6 +1292,251 @@ static void ww_test_object_lock_stale_context(void) WWL(&o, &t); } +static void ww_test_edeadlk_normal(void) +{ + int ret; + + mutex_lock(&o2.base); + o2.ctx = &t2; + mutex_release(&o2.base.dep_map, 1, _THIS_IP_); + + WWAI(&t); + t2 = t; + t2.stamp--; + + ret = WWL(&o, &t); + WARN_ON(ret); + + ret = WWL(&o2, &t); + WARN_ON(ret != -EDEADLK); + + o2.ctx = NULL; + mutex_acquire(&o2.base.dep_map, 0, 1, _THIS_IP_); + mutex_unlock(&o2.base); + WWU(&o); + + WWL(&o2, &t); +} + +static void ww_test_edeadlk_normal_slow(void) +{ + int ret; + + mutex_lock(&o2.base); + mutex_release(&o2.base.dep_map, 1, _THIS_IP_); + o2.ctx = &t2; + + WWAI(&t); + t2 = t; + t2.stamp--; + + ret = WWL(&o, &t); + WARN_ON(ret); + + ret = WWL(&o2, &t); + WARN_ON(ret != -EDEADLK); + + o2.ctx = NULL; + mutex_acquire(&o2.base.dep_map, 0, 1, _THIS_IP_); + mutex_unlock(&o2.base); + WWU(&o); + + ww_mutex_lock_slow(&o2, &t); +} + +static void ww_test_edeadlk_no_unlock(void) +{ + int ret; + + mutex_lock(&o2.base); + o2.ctx = &t2; + mutex_release(&o2.base.dep_map, 1, _THIS_IP_); + + WWAI(&t); + t2 = t; + t2.stamp--; + + ret = WWL(&o, &t); + WARN_ON(ret); + + ret = WWL(&o2, &t); + WARN_ON(ret != -EDEADLK); + + o2.ctx = NULL; + mutex_acquire(&o2.base.dep_map, 0, 1, _THIS_IP_); + mutex_unlock(&o2.base); + + WWL(&o2, &t); +} + +static void ww_test_edeadlk_no_unlock_slow(void) +{ + int ret; + + mutex_lock(&o2.base); + mutex_release(&o2.base.dep_map, 1, _THIS_IP_); + o2.ctx = &t2; + + WWAI(&t); + t2 = t; + t2.stamp--; + + ret = WWL(&o, &t); + WARN_ON(ret); + + ret = WWL(&o2, &t); + WARN_ON(ret != -EDEADLK); + + o2.ctx = NULL; + mutex_acquire(&o2.base.dep_map, 0, 1, _THIS_IP_); + mutex_unlock(&o2.base); + + ww_mutex_lock_slow(&o2, &t); +} + +static void ww_test_edeadlk_acquire_more(void) +{ + int ret; + + mutex_lock(&o2.base); + mutex_release(&o2.base.dep_map, 1, _THIS_IP_); + o2.ctx = &t2; + + WWAI(&t); + t2 = t; + t2.stamp--; + + ret = WWL(&o, &t); + WARN_ON(ret); + + ret = WWL(&o2, &t); + WARN_ON(ret != -EDEADLK); + + ret = WWL(&o3, &t); +} + +static void ww_test_edeadlk_acquire_more_slow(void) +{ + int ret; + + mutex_lock(&o2.base); + mutex_release(&o2.base.dep_map, 1, _THIS_IP_); + o2.ctx = &t2; + + WWAI(&t); + t2 = t; + t2.stamp--; + + ret = WWL(&o, &t); + WARN_ON(ret); + + ret = WWL(&o2, &t); + WARN_ON(ret != -EDEADLK); + + ww_mutex_lock_slow(&o3, &t); +} + +static void ww_test_edeadlk_acquire_more_edeadlk(void) +{ + int ret; + + mutex_lock(&o2.base); + mutex_release(&o2.base.dep_map, 1, _THIS_IP_); + o2.ctx = &t2; + + mutex_lock(&o3.base); + mutex_release(&o3.base.dep_map, 1, _THIS_IP_); + o3.ctx = &t2; + + WWAI(&t); + t2 = t; + t2.stamp--; + + ret = WWL(&o, &t); + WARN_ON(ret); + + ret = WWL(&o2, &t); + WARN_ON(ret != -EDEADLK); + + ret = WWL(&o3, &t); + WARN_ON(ret != -EDEADLK); +} + +static void ww_test_edeadlk_acquire_more_edeadlk_slow(void) +{ + int ret; + + mutex_lock(&o2.base); + mutex_release(&o2.base.dep_map, 1, _THIS_IP_); + o2.ctx = &t2; + + mutex_lock(&o3.base); + mutex_release(&o3.base.dep_map, 1, _THIS_IP_); + o3.ctx = &t2; + + WWAI(&t); + t2 = t; + t2.stamp--; + + ret = WWL(&o, &t); + WARN_ON(ret); + + ret = WWL(&o2, &t); + WARN_ON(ret != -EDEADLK); + + ww_mutex_lock_slow(&o3, &t); +} + +static void ww_test_edeadlk_acquire_wrong(void) +{ + int ret; + + mutex_lock(&o2.base); + mutex_release(&o2.base.dep_map, 1, _THIS_IP_); + o2.ctx = &t2; + + WWAI(&t); + t2 = t; + t2.stamp--; + + ret = WWL(&o, &t); + WARN_ON(ret); + + ret = WWL(&o2, &t); + WARN_ON(ret != -EDEADLK); + if (!ret) + WWU(&o2); + + WWU(&o); + + ret = WWL(&o3, &t); +} + +static void ww_test_edeadlk_acquire_wrong_slow(void) +{ + int ret; + + mutex_lock(&o2.base); + mutex_release(&o2.base.dep_map, 1, _THIS_IP_); + o2.ctx = &t2; + + WWAI(&t); + t2 = t; + t2.stamp--; + + ret = WWL(&o, &t); + WARN_ON(ret); + + ret = WWL(&o2, &t); + WARN_ON(ret != -EDEADLK); + if (!ret) + WWU(&o2); + + WWU(&o); + + ww_mutex_lock_slow(&o3, &t); +} + static void ww_test_spin_nest_unlocked(void) { raw_spin_lock_nest_lock(&lock_A, &o.base); @@ -1498,6 +1743,19 @@ static void ww_tests(void) dotest(ww_test_object_lock_stale_context, FAILURE, LOCKTYPE_WW); printk("\n"); + print_testname("EDEADLK handling"); + dotest(ww_test_edeadlk_normal, SUCCESS, LOCKTYPE_WW); + dotest(ww_test_edeadlk_normal_slow, SUCCESS, LOCKTYPE_WW); + dotest(ww_test_edeadlk_no_unlock, FAILURE, LOCKTYPE_WW); + dotest(ww_test_edeadlk_no_unlock_slow, FAILURE, LOCKTYPE_WW); + dotest(ww_test_edeadlk_acquire_more, FAILURE, LOCKTYPE_WW); + dotest(ww_test_edeadlk_acquire_more_slow, FAILURE, LOCKTYPE_WW); + dotest(ww_test_edeadlk_acquire_more_edeadlk, FAILURE, LOCKTYPE_WW); + dotest(ww_test_edeadlk_acquire_more_edeadlk_slow, FAILURE, LOCKTYPE_WW); + dotest(ww_test_edeadlk_acquire_wrong, FAILURE, LOCKTYPE_WW); + dotest(ww_test_edeadlk_acquire_wrong_slow, FAILURE, LOCKTYPE_WW); + printk("\n"); + print_testname("spinlock nest unlocked"); dotest(ww_test_spin_nest_unlocked, FAILURE, LOCKTYPE_WW); printk("\n"); -- cgit v1.1 From 166989e366ffa66108b2f37b870e66b85b2185ad Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Thu, 20 Jun 2013 13:31:51 +0200 Subject: locking-selftests: Handle unexpected failures more strictly When CONFIG_PROVE_LOCKING is not enabled, more tests are expected to pass unexpectedly, but there no tests that should start to fail that pass with CONFIG_PROVE_LOCKING enabled. Signed-off-by: Maarten Lankhorst Acked-by: Peter Zijlstra Cc: dri-devel@lists.freedesktop.org Cc: linaro-mm-sig@lists.linaro.org Cc: rostedt@goodmis.org Cc: daniel@ffwll.ch Cc: Linus Torvalds Cc: Andrew Morton Cc: Thomas Gleixner Link: http://lkml.kernel.org/r/20130620113151.4001.77963.stgit@patser Signed-off-by: Ingo Molnar --- lib/locking-selftest.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/locking-selftest.c b/lib/locking-selftest.c index d554f3f..aad024d 100644 --- a/lib/locking-selftest.c +++ b/lib/locking-selftest.c @@ -976,16 +976,18 @@ static void dotest(void (*testcase_fn)(void), int expected, int lockclass_mask) /* * Filter out expected failures: */ - if (debug_locks != expected) { #ifndef CONFIG_PROVE_LOCKING + if (expected == FAILURE && debug_locks) { expected_testcase_failures++; printk("failed|"); -#else + } + else +#endif + if (debug_locks != expected) { unexpected_testcase_failures++; printk("FAILED|"); dump_stack(); -#endif } else { testcase_successes++; printk(" ok |"); -- cgit v1.1