Message ID | 20130528144845.4538.93485.stgit@patser (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
On Tue, May 28, 2013 at 04:48:45PM +0200, Maarten Lankhorst wrote: > This stresses the lockdep code in some ways specifically useful to > ww_mutexes. It adds checks for most of the common locking errors. > > Changes since v1: > - Add tests to verify reservation_id is untouched. > - Use L() and U() macros where possible. > > Changes since v2: > - Use the ww_mutex api directly. > - Use macros for most of the code. > Changes since v3: > - Rework tests for the api changes. > > Signed-off-by: Maarten Lankhorst <maarten.lankhorst@canonical.com> > --- > lib/locking-selftest.c | 405 ++++++++++++++++++++++++++++++++++++++++++++++-- > 1 file changed, 386 insertions(+), 19 deletions(-) > > diff --git a/lib/locking-selftest.c b/lib/locking-selftest.c > index c3eb261..b18f1d3 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,16 @@ static void init_shared_classes(void) > #define RSU(x) up_read(&rwsem_##x) > #define RWSI(x) init_rwsem(&rwsem_##x) > > +#define WWAI(x) ww_acquire_init(x, &ww_lockdep) > +#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 +910,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 +938,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 +965,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 +972,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 +1124,355 @@ 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_normal(void) > +{ > + int ret; > + > + WWAI(&t); > + > + /* > + * test if ww_id is kept identical if not > + * called with any of the ww_* locking calls > + */ > + > + /* 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); > +} Since we don't really allow this any more (instead allow ww_mutex_lock without context) do we need this test here really? > + > +static void ww_test_two_contexts(void) > +{ > + WWAI(&t); > + WWAI(&t2); > +} > + > +static void ww_test_context_unlock_twice(void) > +{ > + WWAI(&t); > + WWAD(&t); > + WWAF(&t); > + WWAF(&t); > +} > + > +static void ww_test_object_unlock_twice(void) > +{ > + WWL1(&o); > + WWU(&o); > + WWU(&o); > +} > + > +static void ww_test_spin_nest_unlocked(void) > +{ > + raw_spin_lock_nest_lock(&lock_A, &o.base); > + U(A); > +} I don't quite see the point of this one here ... > + > +static void ww_test_unneeded_slow(void) > +{ > + int ret; > + > + WWAI(&t); > + > + ww_mutex_lock_slow(&o, &t); > +} I think checking the _slow debug stuff would be neat, i.e. - fail/success tests for properly unlocking all held locks - fail/success tests for lock_slow acquiring the right lock. Otherwise I didn't spot anything that seems missing in these self-tests here. Cheers, Daniel > + > +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_normal, SUCCESS, LOCKTYPE_WW); > + dotest(ww_test_unneeded_slow, FAILURE, LOCKTYPE_WW); > + printk("\n"); > + > + print_testname("using two ww contexts"); > + dotest(ww_test_two_contexts, FAILURE, LOCKTYPE_WW); > + printk("\n"); > + > + print_testname("finish ww context twice"); > + dotest(ww_test_context_unlock_twice, FAILURE, LOCKTYPE_WW); > + printk("\n"); > + > + print_testname("unlock twice"); > + dotest(ww_test_object_unlock_twice, 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 +1553,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; >
Op 28-05-13 21:18, Daniel Vetter schreef: > On Tue, May 28, 2013 at 04:48:45PM +0200, Maarten Lankhorst wrote: >> This stresses the lockdep code in some ways specifically useful to >> ww_mutexes. It adds checks for most of the common locking errors. >> >> Changes since v1: >> - Add tests to verify reservation_id is untouched. >> - Use L() and U() macros where possible. >> >> Changes since v2: >> - Use the ww_mutex api directly. >> - Use macros for most of the code. >> Changes since v3: >> - Rework tests for the api changes. >> >> <snip> >> >> +static void ww_test_normal(void) >> +{ >> + int ret; >> + >> + WWAI(&t); >> + >> + /* >> + * test if ww_id is kept identical if not >> + * called with any of the ww_* locking calls >> + */ >> + >> + /* 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); >> +} > Since we don't really allow this any more (instead allow ww_mutex_lock > without context) do we need this test here really? Yes. This test verifies all the normal locking paths are not affected by the ww_ctx changes. >> + >> +static void ww_test_two_contexts(void) >> +{ >> + WWAI(&t); >> + WWAI(&t2); >> +} >> + >> +static void ww_test_context_unlock_twice(void) >> +{ >> + WWAI(&t); >> + WWAD(&t); >> + WWAF(&t); >> + WWAF(&t); >> +} >> + >> +static void ww_test_object_unlock_twice(void) >> +{ >> + WWL1(&o); >> + WWU(&o); >> + WWU(&o); >> +} >> + >> +static void ww_test_spin_nest_unlocked(void) >> +{ >> + raw_spin_lock_nest_lock(&lock_A, &o.base); >> + U(A); >> +} > I don't quite see the point of this one here ... It's a lockdep test that was missing. o.base is not locked. So lock_A is being nested into an unlocked lock, resulting in a lockdep error. >> + >> +static void ww_test_unneeded_slow(void) >> +{ >> + int ret; >> + >> + WWAI(&t); >> + >> + ww_mutex_lock_slow(&o, &t); >> +} > I think checking the _slow debug stuff would be neat, i.e. > - fail/success tests for properly unlocking all held locks > - fail/success tests for lock_slow acquiring the right lock. > > Otherwise I didn't spot anything that seems missing in these self-tests > here. > Yes it would be nice, doing so is left as an excercise for the reviewer, who failed to raise this point sooner. ;-) ~Maarten
On Tue, May 28, 2013 at 11:12 PM, Maarten Lankhorst <maarten.lankhorst@canonical.com> wrote: >>> +static void ww_test_spin_nest_unlocked(void) >>> +{ >>> + raw_spin_lock_nest_lock(&lock_A, &o.base); >>> + U(A); >>> +} >> I don't quite see the point of this one here ... > It's a lockdep test that was missing. o.base is not locked. So lock_A is being nested into an unlocked lock, resulting in a lockdep error. Sounds like a different patch then ... >>> + >>> +static void ww_test_unneeded_slow(void) >>> +{ >>> + int ret; >>> + >>> + WWAI(&t); >>> + >>> + ww_mutex_lock_slow(&o, &t); >>> +} >> I think checking the _slow debug stuff would be neat, i.e. >> - fail/success tests for properly unlocking all held locks >> - fail/success tests for lock_slow acquiring the right lock. >> >> Otherwise I didn't spot anything that seems missing in these self-tests >> here. >> > Yes it would be nice, doing so is left as an excercise for the reviewer, who failed to raise this point sooner. ;-) Hm, I guess I've volunteered myself to look into this a bit ;-) -Daniel -- Daniel Vetter Software Engineer, Intel Corporation +41 (0) 79 365 57 48 - http://blog.ffwll.ch
diff --git a/lib/locking-selftest.c b/lib/locking-selftest.c index c3eb261..b18f1d3 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,16 @@ static void init_shared_classes(void) #define RSU(x) up_read(&rwsem_##x) #define RWSI(x) init_rwsem(&rwsem_##x) +#define WWAI(x) ww_acquire_init(x, &ww_lockdep) +#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 +910,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 +938,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 +965,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 +972,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 +1124,355 @@ 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_normal(void) +{ + int ret; + + WWAI(&t); + + /* + * test if ww_id is kept identical if not + * called with any of the ww_* locking calls + */ + + /* 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); + WWAI(&t2); +} + +static void ww_test_context_unlock_twice(void) +{ + WWAI(&t); + WWAD(&t); + WWAF(&t); + WWAF(&t); +} + +static void ww_test_object_unlock_twice(void) +{ + WWL1(&o); + WWU(&o); + WWU(&o); +} + +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) +{ + int ret; + + 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_normal, SUCCESS, LOCKTYPE_WW); + dotest(ww_test_unneeded_slow, FAILURE, LOCKTYPE_WW); + printk("\n"); + + print_testname("using two ww contexts"); + dotest(ww_test_two_contexts, FAILURE, LOCKTYPE_WW); + printk("\n"); + + print_testname("finish ww context twice"); + dotest(ww_test_context_unlock_twice, FAILURE, LOCKTYPE_WW); + printk("\n"); + + print_testname("unlock twice"); + dotest(ww_test_object_unlock_twice, 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 +1553,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;
This stresses the lockdep code in some ways specifically useful to ww_mutexes. It adds checks for most of the common locking errors. Changes since v1: - Add tests to verify reservation_id is untouched. - Use L() and U() macros where possible. Changes since v2: - Use the ww_mutex api directly. - Use macros for most of the code. Changes since v3: - Rework tests for the api changes. Signed-off-by: Maarten Lankhorst <maarten.lankhorst@canonical.com> --- lib/locking-selftest.c | 405 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 386 insertions(+), 19 deletions(-)