summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNeilBrown <neilb@suse.com>2019-04-11 21:52:08 -0400
committerDavid S. Miller <davem@davemloft.net>2019-04-12 20:34:45 -0400
commitca0b709d1a07b1fe1fb356d8d58f220287f85672 (patch)
tree62c8abb3a46c5a79950205f59c42c3a996df43eb
parentf4712b46a529ca2da078c82d5d99d367c7ebf82b (diff)
rhashtable: use BIT(0) for locking.
As reported by Guenter Roeck, the new bit-locking using BIT(1) doesn't work on the m68k architecture. m68k only requires 2-byte alignment for words and longwords, so there is only one unused bit in pointers to structs - We current use two, one for the NULLS marker at the end of the linked list, and one for the bit-lock in the head of the list. The two uses don't need to conflict as we never need the head of the list to be a NULLS marker - the marker is only needed to check if an object has moved to a different table, and the bucket head cannot move. The NULLS marker is only needed in a ->next pointer. As we already have different types for the bucket head pointer (struct rhash_lock_head) and the ->next pointers (struct rhash_head), it is fairly easy to treat the lsb differently in each. So: Initialize buckets heads to NULL, and use the lsb for locking. When loading the pointer from the bucket head, if it is NULL (ignoring the lock big), report as being the expected NULLS marker. When storing a value into a bucket head, if it is a NULLS marker, store NULL instead. And convert all places that used bit 1 for locking, to use bit 0. Fixes: 8f0db018006a ("rhashtable: use bit_spin_locks to protect hash bucket.") Reported-by: Guenter Roeck <linux@roeck-us.net> Tested-by: Guenter Roeck <linux@roeck-us.net> Signed-off-by: NeilBrown <neilb@suse.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--include/linux/rhashtable.h35
-rw-r--r--lib/rhashtable.c2
2 files changed, 25 insertions, 12 deletions
diff --git a/include/linux/rhashtable.h b/include/linux/rhashtable.h
index 882bc0fcea4b..f7714d3b46bd 100644
--- a/include/linux/rhashtable.h
+++ b/include/linux/rhashtable.h
@@ -35,7 +35,7 @@
35 * the least significant bit set but otherwise stores the address of 35 * the least significant bit set but otherwise stores the address of
36 * the hash bucket. This allows us to be be sure we've found the end 36 * the hash bucket. This allows us to be be sure we've found the end
37 * of the right list. 37 * of the right list.
38 * The value stored in the hash bucket has BIT(2) used as a lock bit. 38 * The value stored in the hash bucket has BIT(0) used as a lock bit.
39 * This bit must be atomically set before any changes are made to 39 * This bit must be atomically set before any changes are made to
40 * the chain. To avoid dereferencing this pointer without clearing 40 * the chain. To avoid dereferencing this pointer without clearing
41 * the bit first, we use an opaque 'struct rhash_lock_head *' for the 41 * the bit first, we use an opaque 'struct rhash_lock_head *' for the
@@ -91,15 +91,19 @@ struct bucket_table {
91 * NULLS_MARKER() expects a hash value with the low 91 * NULLS_MARKER() expects a hash value with the low
92 * bits mostly likely to be significant, and it discards 92 * bits mostly likely to be significant, and it discards
93 * the msb. 93 * the msb.
94 * We git it an address, in which the bottom 2 bits are 94 * We give it an address, in which the bottom bit is
95 * always 0, and the msb might be significant. 95 * always 0, and the msb might be significant.
96 * So we shift the address down one bit to align with 96 * So we shift the address down one bit to align with
97 * expectations and avoid losing a significant bit. 97 * expectations and avoid losing a significant bit.
98 *
99 * We never store the NULLS_MARKER in the hash table
100 * itself as we need the lsb for locking.
101 * Instead we store a NULL
98 */ 102 */
99#define RHT_NULLS_MARKER(ptr) \ 103#define RHT_NULLS_MARKER(ptr) \
100 ((void *)NULLS_MARKER(((unsigned long) (ptr)) >> 1)) 104 ((void *)NULLS_MARKER(((unsigned long) (ptr)) >> 1))
101#define INIT_RHT_NULLS_HEAD(ptr) \ 105#define INIT_RHT_NULLS_HEAD(ptr) \
102 ((ptr) = RHT_NULLS_MARKER(&(ptr))) 106 ((ptr) = NULL)
103 107
104static inline bool rht_is_a_nulls(const struct rhash_head *ptr) 108static inline bool rht_is_a_nulls(const struct rhash_head *ptr)
105{ 109{
@@ -302,8 +306,9 @@ static inline struct rhash_lock_head __rcu **rht_bucket_insert(
302} 306}
303 307
304/* 308/*
305 * We lock a bucket by setting BIT(1) in the pointer - this is always 309 * We lock a bucket by setting BIT(0) in the pointer - this is always
306 * zero in real pointers and in the nulls marker. 310 * zero in real pointers. The NULLS mark is never stored in the bucket,
311 * rather we store NULL if the bucket is empty.
307 * bit_spin_locks do not handle contention well, but the whole point 312 * bit_spin_locks do not handle contention well, but the whole point
308 * of the hashtable design is to achieve minimum per-bucket contention. 313 * of the hashtable design is to achieve minimum per-bucket contention.
309 * A nested hash table might not have a bucket pointer. In that case 314 * A nested hash table might not have a bucket pointer. In that case
@@ -323,7 +328,7 @@ static inline void rht_lock(struct bucket_table *tbl,
323 struct rhash_lock_head **bkt) 328 struct rhash_lock_head **bkt)
324{ 329{
325 local_bh_disable(); 330 local_bh_disable();
326 bit_spin_lock(1, (unsigned long *)bkt); 331 bit_spin_lock(0, (unsigned long *)bkt);
327 lock_map_acquire(&tbl->dep_map); 332 lock_map_acquire(&tbl->dep_map);
328} 333}
329 334
@@ -332,7 +337,7 @@ static inline void rht_lock_nested(struct bucket_table *tbl,
332 unsigned int subclass) 337 unsigned int subclass)
333{ 338{
334 local_bh_disable(); 339 local_bh_disable();
335 bit_spin_lock(1, (unsigned long *)bucket); 340 bit_spin_lock(0, (unsigned long *)bucket);
336 lock_acquire_exclusive(&tbl->dep_map, subclass, 0, NULL, _THIS_IP_); 341 lock_acquire_exclusive(&tbl->dep_map, subclass, 0, NULL, _THIS_IP_);
337} 342}
338 343
@@ -340,7 +345,7 @@ static inline void rht_unlock(struct bucket_table *tbl,
340 struct rhash_lock_head **bkt) 345 struct rhash_lock_head **bkt)
341{ 346{
342 lock_map_release(&tbl->dep_map); 347 lock_map_release(&tbl->dep_map);
343 bit_spin_unlock(1, (unsigned long *)bkt); 348 bit_spin_unlock(0, (unsigned long *)bkt);
344 local_bh_enable(); 349 local_bh_enable();
345} 350}
346 351
@@ -358,7 +363,9 @@ static inline struct rhash_head *rht_ptr(
358 const struct rhash_lock_head *p = 363 const struct rhash_lock_head *p =
359 rht_dereference_bucket_rcu(*bkt, tbl, hash); 364 rht_dereference_bucket_rcu(*bkt, tbl, hash);
360 365
361 return (void *)(((unsigned long)p) & ~BIT(1)); 366 if ((((unsigned long)p) & ~BIT(0)) == 0)
367 return RHT_NULLS_MARKER(bkt);
368 return (void *)(((unsigned long)p) & ~BIT(0));
362} 369}
363 370
364static inline struct rhash_head *rht_ptr_exclusive( 371static inline struct rhash_head *rht_ptr_exclusive(
@@ -367,7 +374,9 @@ static inline struct rhash_head *rht_ptr_exclusive(
367 const struct rhash_lock_head *p = 374 const struct rhash_lock_head *p =
368 rcu_dereference_protected(*bkt, 1); 375 rcu_dereference_protected(*bkt, 1);
369 376
370 return (void *)(((unsigned long)p) & ~BIT(1)); 377 if (!p)
378 return RHT_NULLS_MARKER(bkt);
379 return (void *)(((unsigned long)p) & ~BIT(0));
371} 380}
372 381
373static inline void rht_assign_locked(struct rhash_lock_head __rcu **bkt, 382static inline void rht_assign_locked(struct rhash_lock_head __rcu **bkt,
@@ -375,7 +384,9 @@ static inline void rht_assign_locked(struct rhash_lock_head __rcu **bkt,
375{ 384{
376 struct rhash_head __rcu **p = (struct rhash_head __rcu **)bkt; 385 struct rhash_head __rcu **p = (struct rhash_head __rcu **)bkt;
377 386
378 rcu_assign_pointer(*p, (void *)((unsigned long)obj | BIT(1))); 387 if (rht_is_a_nulls(obj))
388 obj = NULL;
389 rcu_assign_pointer(*p, (void *)((unsigned long)obj | BIT(0)));
379} 390}
380 391
381static inline void rht_assign_unlock(struct bucket_table *tbl, 392static inline void rht_assign_unlock(struct bucket_table *tbl,
@@ -384,6 +395,8 @@ static inline void rht_assign_unlock(struct bucket_table *tbl,
384{ 395{
385 struct rhash_head __rcu **p = (struct rhash_head __rcu **)bkt; 396 struct rhash_head __rcu **p = (struct rhash_head __rcu **)bkt;
386 397
398 if (rht_is_a_nulls(obj))
399 obj = NULL;
387 lock_map_release(&tbl->dep_map); 400 lock_map_release(&tbl->dep_map);
388 rcu_assign_pointer(*p, obj); 401 rcu_assign_pointer(*p, obj);
389 preempt_enable(); 402 preempt_enable();
diff --git a/lib/rhashtable.c b/lib/rhashtable.c
index ef5378efdef3..6529fe1b45c1 100644
--- a/lib/rhashtable.c
+++ b/lib/rhashtable.c
@@ -59,7 +59,7 @@ int lockdep_rht_bucket_is_held(const struct bucket_table *tbl, u32 hash)
59 return 1; 59 return 1;
60 if (unlikely(tbl->nest)) 60 if (unlikely(tbl->nest))
61 return 1; 61 return 1;
62 return bit_spin_is_locked(1, (unsigned long *)&tbl->buckets[hash]); 62 return bit_spin_is_locked(0, (unsigned long *)&tbl->buckets[hash]);
63} 63}
64EXPORT_SYMBOL_GPL(lockdep_rht_bucket_is_held); 64EXPORT_SYMBOL_GPL(lockdep_rht_bucket_is_held);
65#else 65#else