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]>

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

Reply via email to