On Tue, 15 Sep 2026, Herbert Xu wrote:
> On Mon, Sep 14, 2026 at 09:06:49PM -0700, Eric Dumazet wrote:
> >
> > Try to revert 4333ab90aaae ("rhashtable: use private lockdep class for
> > all locks.")
>
> Yes this is indeed buggy. I know nothing about lockdep but Gemini
> reckons that it should use separate keys instead of different
> depths which seems fair enough:
>
> ---8<---
> Use separate lockdep keys for the different types of locks in
> rhashtable (mutex, spin lock, and bucket locks). They are
> separate and not normally nested with respect to each other.
>
> Also move the rhashtable_init/rhltable_init kdoc to the header
> file as that's where the macros are defined.
>
> Fixes: 4333ab90aaae ("rhashtable: use private lockdep class for all locks.")
> Reported-by: [email protected]
> Assisted-by: Gemini:gemini-3.6-flash
> Signed-off-by: Herbert Xu <[email protected]>
That's a much better approach - thanks for that.
Reviewed-by: NeilBrown <[email protected]>
Thanks,
NeilBrown
>
> diff --git a/include/linux/rhashtable-types.h
> b/include/linux/rhashtable-types.h
> index 0e1b172a4f6c..afbc12ba71f5 100644
> --- a/include/linux/rhashtable-types.h
> +++ b/include/linux/rhashtable-types.h
> @@ -70,6 +70,14 @@ struct rhashtable_params {
> rht_obj_cmpfn_t obj_cmpfn;
> };
>
> +struct rhashtable_lockdep_keys {
> +#ifdef CONFIG_LOCKDEP
> + struct lock_class_key lock_key;
> + struct lock_class_key mutex_key;
> + struct lock_class_key bucket_key;
> +#endif
> +};
> +
> /**
> * struct rhashtable - Hash table handle
> * @tbl: Bucket table
> @@ -141,24 +149,77 @@ struct rhashtable_iter {
>
> int __rhashtable_init_noprof(struct rhashtable *ht,
> const struct rhashtable_params *params,
> - struct lock_class_key *key);
> + struct rhashtable_lockdep_keys *keys);
> #define rhashtable_init_noprof(ht, params) \
> ({ \
> - static struct lock_class_key __key; \
> + static struct rhashtable_lockdep_keys __keys; \
> \
> - __rhashtable_init_noprof(ht, params, &__key); \
> + __rhashtable_init_noprof(ht, params, &__keys); \
> })
> +
> +/**
> + * rhashtable_init - initialize a new hash table
> + * @ht: hash table to be initialized
> + * @params: configuration parameters
> + *
> + * Initializes a new hash table based on the provided configuration
> + * parameters. A table can be configured either with a variable or
> + * fixed length key:
> + *
> + * Configuration Example 1: Fixed length keys
> + * struct test_obj {
> + * int key;
> + * void * my_member;
> + * struct rhash_head node;
> + * };
> + *
> + * struct rhashtable_params params = {
> + * .head_offset = offsetof(struct test_obj, node),
> + * .key_offset = offsetof(struct test_obj, key),
> + * .key_len = sizeof(int),
> + * .hashfn = jhash,
> + * };
> + *
> + * Configuration Example 2: Variable length keys
> + * struct test_obj {
> + * [...]
> + * struct rhash_head node;
> + * };
> + *
> + * u32 my_hash_fn(const void *data, u32 len, u32 seed)
> + * {
> + * struct test_obj *obj = data;
> + *
> + * return [... hash ...];
> + * }
> + *
> + * struct rhashtable_params params = {
> + * .head_offset = offsetof(struct test_obj, node),
> + * .hashfn = jhash,
> + * .obj_hashfn = my_hash_fn,
> + * };
> + */
> #define rhashtable_init(...) alloc_hooks(rhashtable_init_noprof(__VA_ARGS__))
>
> int __rhltable_init_noprof(struct rhltable *hlt,
> const struct rhashtable_params *params,
> - struct lock_class_key *key);
> + struct rhashtable_lockdep_keys *keys);
> #define rhltable_init_noprof(hlt, params) \
> ({ \
> - static struct lock_class_key __key; \
> + static struct rhashtable_lockdep_keys __keys; \
> \
> - __rhltable_init_noprof(hlt, params, &__key); \
> + __rhltable_init_noprof(hlt, params, &__keys); \
> })
> +
> +/**
> + * rhltable_init - initialize a new hash list table
> + * @hlt: hash list table to be initialized
> + * @params: configuration parameters
> + *
> + * Initializes a new hash list table.
> + *
> + * See documentation for rhashtable_init.
> + */
> #define rhltable_init(...) alloc_hooks(rhltable_init_noprof(__VA_ARGS__))
>
> #endif /* _LINUX_RHASHTABLE_TYPES_H */
> diff --git a/include/linux/rhashtable.h b/include/linux/rhashtable.h
> index 6c5e6d9accba..ec853c1b9af3 100644
> --- a/include/linux/rhashtable.h
> +++ b/include/linux/rhashtable.h
> @@ -328,8 +328,7 @@ static inline unsigned long rht_lock_nested(struct
> bucket_table *tbl,
>
> local_irq_save(flags);
> bit_spin_lock(0, (unsigned long *)bucket);
> - /* 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_);
> + lock_acquire_exclusive(&tbl->dep_map, subclass, 0, NULL, _THIS_IP_);
> return flags;
> }
>
> diff --git a/lib/rhashtable.c b/lib/rhashtable.c
> index 5da0e53a8d42..918f15a2ac69 100644
> --- a/lib/rhashtable.c
> +++ b/lib/rhashtable.c
> @@ -432,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_nested(&ht->mutex, 1);
> + mutex_lock(&ht->mutex);
>
> tbl = rht_dereference(ht->tbl, ht);
> tbl = rhashtable_last_table(ht, tbl);
> @@ -1122,51 +1122,9 @@ static u32 rhashtable_jhash2(const void *key, u32
> length, u32 seed)
> return jhash2(key, length, seed);
> }
>
> -/**
> - * rhashtable_init - initialize a new hash table
> - * @ht: hash table to be initialized
> - * @params: configuration parameters
> - *
> - * Initializes a new hash table based on the provided configuration
> - * parameters. A table can be configured either with a variable or
> - * fixed length key:
> - *
> - * Configuration Example 1: Fixed length keys
> - * struct test_obj {
> - * int key;
> - * void * my_member;
> - * struct rhash_head node;
> - * };
> - *
> - * struct rhashtable_params params = {
> - * .head_offset = offsetof(struct test_obj, node),
> - * .key_offset = offsetof(struct test_obj, key),
> - * .key_len = sizeof(int),
> - * .hashfn = jhash,
> - * };
> - *
> - * Configuration Example 2: Variable length keys
> - * struct test_obj {
> - * [...]
> - * struct rhash_head node;
> - * };
> - *
> - * u32 my_hash_fn(const void *data, u32 len, u32 seed)
> - * {
> - * struct test_obj *obj = data;
> - *
> - * return [... hash ...];
> - * }
> - *
> - * struct rhashtable_params params = {
> - * .head_offset = offsetof(struct test_obj, node),
> - * .hashfn = jhash,
> - * .obj_hashfn = my_hash_fn,
> - * };
> - */
> int __rhashtable_init_noprof(struct rhashtable *ht,
> - const struct rhashtable_params *params,
> - struct lock_class_key *key)
> + const struct rhashtable_params *params,
> + struct rhashtable_lockdep_keys *keys)
> {
> struct bucket_table *tbl;
> size_t size;
> @@ -1176,13 +1134,11 @@ 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);
> + mutex_init_with_key(&ht->mutex, &keys->mutex_key);
> spin_lock_init(&ht->lock);
> - /* spin_lock can use nesting level 0 */
> - lockdep_set_class(&ht->lock, key);
> + lockdep_set_class(&ht->lock, &keys->lock_key);
> #ifdef CONFIG_LOCKDEP
> - ht->lockdep_key = key;
> + ht->lockdep_key = &keys->bucket_key;
> #endif
> memcpy(&ht->p, params, sizeof(*params));
>
> @@ -1236,22 +1192,13 @@ int __rhashtable_init_noprof(struct rhashtable *ht,
> }
> EXPORT_SYMBOL_GPL(__rhashtable_init_noprof);
>
> -/**
> - * rhltable_init - initialize a new hash list table
> - * @hlt: hash list table to be initialized
> - * @params: configuration parameters
> - *
> - * Initializes a new hash list table.
> - *
> - * See documentation for rhashtable_init.
> - */
> int __rhltable_init_noprof(struct rhltable *hlt,
> const struct rhashtable_params *params,
> - struct lock_class_key *key)
> + struct rhashtable_lockdep_keys *keys)
> {
> int err;
>
> - err = __rhashtable_init_noprof(&hlt->ht, params, key);
> + err = __rhashtable_init_noprof(&hlt->ht, params, keys);
> hlt->ht.rhlist = true;
> return err;
> }
> diff --git a/lib/test_rhashtable.c b/lib/test_rhashtable.c
> index 85a615e74591..b767a38a74f9 100644
> --- a/lib/test_rhashtable.c
> +++ b/lib/test_rhashtable.c
> @@ -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_nested(&ht->mutex, 1);
> + mutex_lock(&ht->mutex);
> tbl = rht_dereference(ht->tbl, ht);
> for (i = 0; i < tbl->size; i++) {
> struct rhash_head *pos, *next;
> --
> Email: Herbert Xu <[email protected]>
> Home Page: http://gondor.apana.org.au/~herbert/
> PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
>