rhashtable: use private lockdep class for all locks.

This patch builds on
 Commit: 060d4e94b8 ("rhashtable: give each instance its own lockdep class")

to allow lockdep to see each rhashtable as unique with respect to all of
the locks, not just the ht->mutex.

This is needed if rhashtable is to be used tracepoint BPF which could
run while an rhashtable lock (in a different table) is held - see Link
below.

Rather then adding extra keys for the other locks, we use distinct
sub-classes for the different locks.
rhashtable->lock uses class 0
rhashtable->mutex uses class 1
bucket bitlocks, which are the only ones that are ever nested, use
 classes 2 and 3.

Currently rht_lock() and rht_lock_nested() are quite separate code
despite the near-identical function.  This patch moves rht_lock() to
after rht_lock_nested(), and simply calls that other function with a
nesting level of zero.

Link: https://lore.kernel.org/all/20260801-fix-rhashtable-bucket-lockdep-v1-1-15a0f8ae094c@gmail.com/
Closes: https://syzkaller.appspot.com/bug?extid=ef8d17bae14efb960935
Tested-by: quanyeyang <quanyeyang@proton.me>
Signed-off-by: NeilBrown <neil@brown.name>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
This commit is contained in:
NeilBrown authored and Herbert Xu committed 2026-09-04 19:49:32 +10:00
1 parent 58d27c4154
commit 4333ab90aa
4 files changed
+24 -17

No files matched your search

+3
View File
@@ -97,6 +97,9 @@ struct rhashtable {
#ifdef CONFIG_MEM_ALLOC_PROFILING
struct alloc_tag *alloc_tag;
#endif
#ifdef CONFIG_LOCKDEP
struct lock_class_key *lockdep_key;
#endif
};
/**
+9 -13
View File
@@ -319,18 +319,6 @@ static inline struct rhash_lock_head __rcu **rht_bucket_insert(
* When we write to a bucket without unlocking, we use rht_assign_locked().
*/
static inline unsigned long rht_lock(struct bucket_table *tbl,
struct rhash_lock_head __rcu **bkt)
__acquires(__bitlock(0, bkt))
{
unsigned long flags;
local_irq_save(flags);
bit_spin_lock(0, (unsigned long *)bkt);
lock_map_acquire(&tbl->dep_map);
return flags;
}
static inline unsigned long rht_lock_nested(struct bucket_table *tbl,
struct rhash_lock_head __rcu **bucket,
unsigned int subclass)
@@ -340,10 +328,18 @@ static inline unsigned long rht_lock_nested(struct bucket_table *tbl,
local_irq_save(flags);
bit_spin_lock(0, (unsigned long *)bucket);
lock_acquire_exclusive(&tbl->dep_map, subclass, 0, NULL, _THIS_IP_);
/* subclass 0 is used for ->lock and 1 for ->mutex. 2+ for bitlocks */
lock_acquire_exclusive(&tbl->dep_map, subclass+2, 0, NULL, _THIS_IP_);
return flags;
}
static inline unsigned long rht_lock(struct bucket_table *tbl,
struct rhash_lock_head __rcu **bkt)
__acquires(__bitlock(0, bkt))
{
return rht_lock_nested(tbl, bkt, 0);
}
static inline void rht_unlock(struct bucket_table *tbl,
struct rhash_lock_head __rcu **bkt,
unsigned long flags)
+11 -3
View File
@@ -191,7 +191,6 @@ static struct bucket_table *bucket_table_alloc(struct rhashtable *ht,
struct bucket_table *tbl = NULL;
size_t size;
int i;
static struct lock_class_key __key;
tbl = alloc_hooks_tag(ht->alloc_tag,
kvmalloc_node_align_noprof(struct_size(tbl, buckets, nbuckets),
@@ -207,7 +206,10 @@ static struct bucket_table *bucket_table_alloc(struct rhashtable *ht,
if (tbl == NULL)
return NULL;
lockdep_init_map(&tbl->dep_map, "rhashtable_bucket", &__key, 0);
#ifdef CONFIG_LOCKDEP
/* bitlocks must use nesting level 2 or more */
lockdep_init_map(&tbl->dep_map, "rhashtable_bucket", ht->lockdep_key, 0);
#endif
tbl->size = size;
@@ -430,7 +432,7 @@ static void rht_deferred_worker(struct work_struct *work)
int err = 0;
ht = container_of(work, struct rhashtable, run_work);
mutex_lock(&ht->mutex);
mutex_lock_nested(&ht->mutex, 1);
tbl = rht_dereference(ht->tbl, ht);
tbl = rhashtable_last_table(ht, tbl);
@@ -1174,8 +1176,14 @@ int __rhashtable_init_noprof(struct rhashtable *ht,
return -EINVAL;
memset(ht, 0, sizeof(*ht));
/* mutex_lock must use nesting level 1 */
mutex_init_with_key(&ht->mutex, key);
spin_lock_init(&ht->lock);
/* spin_lock can use nesting level 0 */
lockdep_set_class(&ht->lock, key);
#ifdef CONFIG_LOCKDEP
ht->lockdep_key = key;
#endif
memcpy(&ht->p, params, sizeof(*params));
alloc_tag_record(ht->alloc_tag);
+1 -1
View File
@@ -477,7 +477,7 @@ static unsigned int __init print_ht(struct rhltable *rhlt)
ht = &rhlt->ht;
/* Take the mutex to avoid RCU warning */
mutex_lock(&ht->mutex);
mutex_lock_nested(&ht->mutex, 1);
tbl = rht_dereference(ht->tbl, ht);
for (i = 0; i < tbl->size; i++) {
struct rhash_head *pos, *next;