diff options
author | Ingo Molnar <mingo@elte.hu> | 2006-09-29 05:01:46 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-09-29 12:18:25 -0400 |
commit | 03cbc358aab3faf34bfeaa02206fa660127e9b3f (patch) | |
tree | 8d95237532390a1beeef37158174fd045b0f8819 /kernel | |
parent | 632dd2053a1146c826ceb6f26ab689389c05e751 (diff) |
[PATCH] lockdep core: improve the lock-chain-hash
With CONFIG_DEBUG_LOCK_ALLOC turned off i was getting sporadic failures in
the locking self-test:
------------>
| Locking API testsuite:
----------------------------------------------------------------------------
| spin |wlock |rlock |mutex | wsem | rsem |
--------------------------------------------------------------------------
A-A deadlock: ok | ok | ok | ok | ok | ok |
A-B-B-A deadlock: ok | ok | ok | ok | ok | ok |
A-B-B-C-C-A deadlock: ok | ok | ok | ok | ok | ok |
A-B-C-A-B-C deadlock: ok | ok | ok | ok | ok | ok |
A-B-B-C-C-D-D-A deadlock: ok |FAILED| ok | ok | ok | ok |
A-B-C-D-B-D-D-A deadlock: ok | ok | ok | ok | ok | ok |
A-B-C-D-B-C-D-A deadlock: ok | ok | ok | ok | ok |FAILED|
after much debugging it turned out to be caused by accidental chain-hash
key collisions. The current hash is:
#define iterate_chain_key(key1, key2) \
(((key1) << MAX_LOCKDEP_KEYS_BITS/2) ^ \
((key1) >> (64-MAX_LOCKDEP_KEYS_BITS/2)) ^ \
(key2))
where MAX_LOCKDEP_KEYS_BITS is 11. This hash is pretty good as it will
shift by 5 bits in every iteration, where every new ID 'mixed' into the
hash would have up to 11 bits. But because there was a 6 bits overlap
between subsequent IDs and their high bits tended to be similar, there was
a chance for accidental chain-hash collision for a low number of locks
held.
the solution is to shift by 11 bits:
#define iterate_chain_key(key1, key2) \
(((key1) << MAX_LOCKDEP_KEYS_BITS) ^ \
((key1) >> (64-MAX_LOCKDEP_KEYS_BITS)) ^ \
(key2))
This keeps the hash perfect up to 5 locks held, but even above that the
hash is still good because 11 bits is a relative prime to the total 64
bits, so a complete match will only occur after 64 held locks (which doesnt
happen in Linux). Even after 5 locks held, entropy of the 5 IDs mixed into
the hash is already good enough so that overlap doesnt generate a colliding
hash ID.
with this change the false positives went away.
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/lockdep.c | 4 |
1 files changed, 2 insertions, 2 deletions
diff --git a/kernel/lockdep.c b/kernel/lockdep.c index df1c3594de31..e596525669ed 100644 --- a/kernel/lockdep.c +++ b/kernel/lockdep.c | |||
@@ -122,8 +122,8 @@ static struct list_head chainhash_table[CHAINHASH_SIZE]; | |||
122 | * unique. | 122 | * unique. |
123 | */ | 123 | */ |
124 | #define iterate_chain_key(key1, key2) \ | 124 | #define iterate_chain_key(key1, key2) \ |
125 | (((key1) << MAX_LOCKDEP_KEYS_BITS/2) ^ \ | 125 | (((key1) << MAX_LOCKDEP_KEYS_BITS) ^ \ |
126 | ((key1) >> (64-MAX_LOCKDEP_KEYS_BITS/2)) ^ \ | 126 | ((key1) >> (64-MAX_LOCKDEP_KEYS_BITS)) ^ \ |
127 | (key2)) | 127 | (key2)) |
128 | 128 | ||
129 | void lockdep_off(void) | 129 | void lockdep_off(void) |