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: syzbot+4d0e4d2db6dfde01b52f@syzkaller.appspotmail.com
Assisted-by: Gemini:gemini-3.6-flash
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
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 <herbert@gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
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: syzbot+4d0e4d2db6dfde01b52f@syzkaller.appspotmail.com
> Assisted-by: Gemini:gemini-3.6-flash
> Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
That's a much better approach - thanks for that.
Reviewed-by: NeilBrown <neil@brown.name>
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 <herbert@gondor.apana.org.au>
> Home Page: http://gondor.apana.org.au/~herbert/
> PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
>
v2 fixes the build failure when LOCKDEP is disabled.
---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: syzbot+4d0e4d2db6dfde01b52f@syzkaller.appspotmail.com
Assisted-by: Gemini:gemini-3.6-flash
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Reviewed-by: NeilBrown <neil@brown.name>
diff --git a/include/linux/rhashtable-types.h b/include/linux/rhashtable-types.h
index 0e1b172a4f6c..3576b8f08aff 100644
--- a/include/linux/rhashtable-types.h
+++ b/include/linux/rhashtable-types.h
@@ -70,6 +70,12 @@ struct rhashtable_params {
rht_obj_cmpfn_t obj_cmpfn;
};
+struct rhashtable_lockdep_keys {
+ struct lock_class_key lock_key;
+ struct lock_class_key mutex_key;
+ struct lock_class_key bucket_key;
+};
+
/**
* struct rhashtable - Hash table handle
* @tbl: Bucket table
@@ -141,24 +147,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 <herbert@gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
v3 removes an obsolete comment regarding the use of nesting level 2 or more.
---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: syzbot+4d0e4d2db6dfde01b52f@syzkaller.appspotmail.com
Assisted-by: Gemini:gemini-3.6-flash
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Reviewed-by: NeilBrown <neil@brown.name>
diff --git a/include/linux/rhashtable-types.h b/include/linux/rhashtable-types.h
index 0e1b172a4f6c..3576b8f08aff 100644
--- a/include/linux/rhashtable-types.h
+++ b/include/linux/rhashtable-types.h
@@ -70,6 +70,12 @@ struct rhashtable_params {
rht_obj_cmpfn_t obj_cmpfn;
};
+struct rhashtable_lockdep_keys {
+ struct lock_class_key lock_key;
+ struct lock_class_key mutex_key;
+ struct lock_class_key bucket_key;
+};
+
/**
* struct rhashtable - Hash table handle
* @tbl: Bucket table
@@ -141,24 +147,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..a3a4a1f7751e 100644
--- a/lib/rhashtable.c
+++ b/lib/rhashtable.c
@@ -207,7 +207,6 @@ static struct bucket_table *bucket_table_alloc(struct rhashtable *ht,
return NULL;
#ifdef CONFIG_LOCKDEP
- /* bitlocks must use nesting level 2 or more */
lockdep_init_map(&tbl->dep_map, "rhashtable_bucket", ht->lockdep_key, 0);
#endif
@@ -432,7 +431,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 +1121,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 +1133,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 +1191,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 <herbert@gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
On Sep 16, 2026 / 18:46, Herbert Xu wrote:
> v3 removes an obsolete comment regarding the use of nesting level 2 or more.
>
> ---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: syzbot+4d0e4d2db6dfde01b52f@syzkaller.appspotmail.com
> Assisted-by: Gemini:gemini-3.6-flash
> Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
> Reviewed-by: NeilBrown <neil@brown.name>
Thank you for the fix action. I evaluated the patch. It takes very long time to
reproduce the WARN with my test set, so I asked my AI to create a reproducer.
The attached source file is a simple kernel module that recreates the WARN at
module load. Using this module, I tried three kernels:
v7.3-rc3 kernel : no WARN
v7.3-rc3 kernel + 4333ab90aaae : recreated the WARN [2]
v7.3-rc3 kernel + 4333ab90aaae + this v3 fix patch : no WARN
The result looks good, hence,
Tested-by: Shin'ichiro Kawasaki <shinichiro.kawasaki@wdc.com>
[2] WARN observed with the reproducer
Sep 18 15:39:28 redsun41 kernel: ------------[ cut here ]------------
Sep 18 15:39:28 redsun41 kernel: Looking for class "&ht->mutex" with key __key.2 [nf_tables], but found a different class "key" with the same key
Sep 18 15:39:28 redsun41 kernel: WARNING: kernel/locking/lockdep.c:955 at look_up_lock_class+0xa8/0x170, CPU#2: kworker/2:1/78
Sep 18 15:39:28 redsun41 kernel: Modules linked in: xt_conntrack nf_conntrack_netbios_ns nf_conntrack_broadcast bridge nft_fib_inet stp nft_fib_ipv4 llc nft_fib_ipv6 nft_fib nft_reject_inet nf_reject_ipv4 nf_reject_ipv6 nft_reject nft_ct nft_chain_nat target_core_user ip6table_nat ip6table_mangle target_core_mod ip6table_raw ip6table_security iptable_nat nf_nat nf_conntrack nf_defrag_ipv6 nf_defrag_ipv4 iptable_mangle iptable_raw iptable_security qrtr rfkill nf_tables ip6table_filter ip6_tables iptable_filter ip_tables intel_rapl_msr intel_rapl_common sb_edac x86_pkg_temp_thermal intel_powerclamp coretemp kvm_intel kvm irqbypass rapl intel_cstate iTCO_wdt intel_pmc_bxt sunrpc intel_uncore pcspkr i2c_i801 i2c_smbus igb mei_me lpc_ich mei ses enclosure ioatdma dca binfmt_misc wmi joydev acpi_pad acpi_power_meter btrfs raid6_pq xor dm_multipath zram lz4hc_compress zstd_compress ast drm_client_lib i2c_algo_bit drm_shmem_helper drm_kms_helper drm mpi3mr mpt3sas raid_class scsi_transport_sas fuse scsi_dh_rdac scsi_dh_emc scsi_dh_alua i2c_dev
Sep 18 15:39:28 redsun41 kernel: CPU: 2 UID: 0 PID: 78 Comm: kworker/2:1 Not tainted 7.3.0-rc3-kts+ #85 PREEMPT(lazy)
Sep 18 15:39:28 redsun41 kernel: Hardware name: Supermicro Super Server/X10SRL-F, BIOS 2.0 12/17/2015
Sep 18 15:39:28 redsun41 kernel: Workqueue: events drm_fb_helper_damage_work [drm_kms_helper]
Sep 18 15:39:28 redsun41 kernel: RIP: 0010:look_up_lock_class+0xa8/0x170
Sep 18 15:39:28 redsun41 kernel: Code: 39 6b 40 75 ed 48 8b 8b b8 00 00 00 49 8b 74 24 18 48 39 f1 74 34 49 8b 14 24 48 81 fa 30 a1 92 ae 74 27 48 8d 3d d8 c5 4e 02 <67> 48 0f b9 3a 48 89 d8 48 8b 6c 24 10 4c 8b 64 24 18 48 8b 5c 24
Sep 18 15:39:28 redsun41 kernel: RSP: 0000:ffff88b77df08148 EFLAGS: 00010002
Sep 18 15:39:28 redsun41 kernel: RAX: 0000000000000001 RBX: ffffffffb2988310 RCX: ffffffffaba2e200
Sep 18 15:39:28 redsun41 kernel: RDX: ffffffffc21822a0 RSI: ffffffffaba2e180 RDI: ffffffffad913390
Sep 18 15:39:28 redsun41 kernel: RBP: ffffffffc21822a0 R08: 0000000000000001 R09: ffff8881c25a3818
Sep 18 15:39:28 redsun41 kernel: R10: 0000000000000004 R11: ffff88b77df08420 R12: ffff8881c25a11f8
Sep 18 15:39:28 redsun41 kernel: R13: 0000000000000003 R14: 0000000000000246 R15: 00000000ffffffff
Sep 18 15:39:28 redsun41 kernel: FS: 0000000000000000(0000) GS:ffff88b7cf69c000(0000) knlGS:0000000000000000
Sep 18 15:39:28 redsun41 kernel: CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
Sep 18 15:39:28 redsun41 kernel: CR2: 0000000001791b40 CR3: 000000015e8e2005 CR4: 00000000001726f0
Sep 18 15:39:28 redsun41 kernel: Call Trace:
Sep 18 15:39:28 redsun41 kernel: <IRQ>
Sep 18 15:39:28 redsun41 kernel: ? ip_local_deliver+0x184/0x4a0
Sep 18 15:39:28 redsun41 kernel: match_held_lock+0xdb/0x130
Sep 18 15:39:28 redsun41 kernel: lock_is_held_type+0xb0/0x180
Sep 18 15:39:28 redsun41 kernel: nft_rhash_lookup+0x541/0x7e0 [nf_tables]
Sep 18 15:39:28 redsun41 kernel: ? asm_common_interrupt+0x26/0x40
Sep 18 15:39:28 redsun41 kernel: ? lock_release.part.0+0x39/0x50
Sep 18 15:39:28 redsun41 kernel: ? lock_vma_under_rcu+0x15e/0x3e0
Sep 18 15:39:28 redsun41 kernel: ? do_user_addr_fault+0x3a6/0xf30
Sep 18 15:39:28 redsun41 kernel: ? exc_page_fault+0x98/0x140
Sep 18 15:39:28 redsun41 kernel: ? asm_exc_page_fault+0x26/0x30
Sep 18 15:39:28 redsun41 kernel: ? __pfx_nft_rhash_lookup+0x10/0x10 [nf_tables]
Sep 18 15:39:28 redsun41 kernel: nft_set_do_lookup+0x1ac/0x2a0 [nf_tables]
Sep 18 15:39:28 redsun41 kernel: ? rcu_read_lock_sched_held+0x40/0x70
Sep 18 15:39:28 redsun41 kernel: nft_lookup_eval+0xcc/0x590 [nf_tables]
Sep 18 15:39:28 redsun41 kernel: ? lock_is_held_type+0xb0/0x180
Sep 18 15:39:28 redsun41 kernel: nft_do_chain+0x2a9/0x1680 [nf_tables]
Sep 18 15:39:28 redsun41 kernel: ? update_load_avg+0x120/0x2c70
Sep 18 15:39:28 redsun41 kernel: ? __lock_acquire+0x691/0xd00
Sep 18 15:39:28 redsun41 kernel: ? __pfx_nft_do_chain+0x10/0x10 [nf_tables]
Sep 18 15:39:28 redsun41 kernel: ? __local_bh_enable_ip+0xb4/0x150
Sep 18 15:39:28 redsun41 kernel: ? ipt_do_table+0xa10/0x11e0 [ip_tables]
Sep 18 15:39:28 redsun41 kernel: ? ipt_do_table+0xa15/0x11e0 [ip_tables]
Sep 18 15:39:28 redsun41 kernel: nft_do_chain_inet+0xdc/0x4a0 [nf_tables]
Sep 18 15:39:28 redsun41 kernel: ? __pfx_nft_do_chain_inet+0x10/0x10 [nf_tables]
Sep 18 15:39:28 redsun41 kernel: ? __pfx_ipt_do_table+0x10/0x10 [ip_tables]
Sep 18 15:39:28 redsun41 kernel: nf_hook_slow+0xbe/0x210
Sep 18 15:39:28 redsun41 kernel: ? lock_is_held_type+0xb0/0x180
Sep 18 15:39:28 redsun41 kernel: ip_local_deliver+0x2d0/0x4a0
Sep 18 15:39:28 redsun41 kernel: ? __pfx_ip_local_deliver+0x10/0x10
Sep 18 15:39:28 redsun41 kernel: ? tcp_v4_early_demux.isra.0+0x5c4/0xb20
Sep 18 15:39:28 redsun41 kernel: ? __pfx_ip_local_deliver_finish+0x10/0x10
Sep 18 15:39:28 redsun41 kernel: ? ip_rcv_finish_core+0x75b/0x1470
Sep 18 15:39:28 redsun41 kernel: ? lock_is_held_type+0xb0/0x180
Sep 18 15:39:28 redsun41 kernel: ip_list_rcv_finish+0x815/0xbd0
Sep 18 15:39:28 redsun41 kernel: ? ip_sublist_rcv+0x106/0x2f0
Sep 18 15:39:28 redsun41 kernel: ? __pfx_ip_list_rcv_finish+0x10/0x10
Sep 18 15:39:28 redsun41 kernel: ? __lock_release.isra.0+0x69/0x1a0
Sep 18 15:39:28 redsun41 kernel: ip_sublist_rcv+0x8b/0x2f0
Sep 18 15:39:28 redsun41 kernel: ? __pfx_ip_sublist_rcv+0x10/0x10
Sep 18 15:39:28 redsun41 kernel: ? __kasan_mempool_unpoison_object+0x128/0x1c0
Sep 18 15:39:28 redsun41 kernel: ? __pfx_ip_rcv_finish+0x10/0x10
Sep 18 15:39:28 redsun41 kernel: ? ip_rcv_core+0x619/0xd00
Sep 18 15:39:28 redsun41 kernel: ? __kasan_slab_alloc+0x6a/0x90
Sep 18 15:39:28 redsun41 kernel: ip_list_rcv+0x2dd/0x440
Sep 18 15:39:28 redsun41 kernel: ? __pfx_ip_list_rcv+0x10/0x10
Sep 18 15:39:28 redsun41 kernel: ? lock_is_held_type+0xb0/0x180
Sep 18 15:39:28 redsun41 kernel: ? __lock_acquire+0x691/0xd00
Sep 18 15:39:28 redsun41 kernel: __netif_receive_skb_list_core+0x6bf/0xa10
Sep 18 15:39:28 redsun41 kernel: ? __pfx___netif_receive_skb_list_core+0x10/0x10
Sep 18 15:39:28 redsun41 kernel: ? lock_acquire.part.0+0xc8/0x240
Sep 18 15:39:28 redsun41 kernel: ? netif_receive_skb_list_internal+0x382/0xc90
Sep 18 15:39:28 redsun41 kernel: ? lock_acquire+0x11a/0x140
Sep 18 15:39:28 redsun41 kernel: netif_receive_skb_list_internal+0x603/0xc90
Sep 18 15:39:28 redsun41 kernel: ? __pfx_netif_receive_skb_list_internal+0x10/0x10
Sep 18 15:39:28 redsun41 kernel: ? igb_clean_tx_irq+0x10f1/0x17f0 [igb]
Sep 18 15:39:28 redsun41 kernel: ? __pfx_igb_clean_rx_irq+0x10/0x10 [igb]
Sep 18 15:39:28 redsun41 kernel: napi_complete_done+0x1a4/0x900
Sep 18 15:39:28 redsun41 kernel: ? __pfx_napi_complete_done+0x10/0x10
Sep 18 15:39:28 redsun41 kernel: ? __pfx_igb_clean_tx_irq+0x10/0x10 [igb]
Sep 18 15:39:28 redsun41 kernel: ? __lock_acquire+0x691/0xd00
Sep 18 15:39:28 redsun41 kernel: igb_poll+0x18f/0x1f0 [igb]
Sep 18 15:39:28 redsun41 kernel: __napi_poll+0x351/0x520
Sep 18 15:39:28 redsun41 kernel: net_rx_action+0x446/0xc30
Sep 18 15:39:28 redsun41 kernel: ? __pfx_net_rx_action+0x10/0x10
Sep 18 15:39:28 redsun41 kernel: ? do_raw_spin_unlock+0x59/0x230
Sep 18 15:39:28 redsun41 kernel: ? lock_release.part.0+0x1c/0x50
Sep 18 15:39:28 redsun41 kernel: ? sched_clock_cpu+0x69/0x630
Sep 18 15:39:28 redsun41 kernel: ? __lock_acquire+0x691/0xd00
Sep 18 15:39:28 redsun41 kernel: ? mark_held_locks+0x40/0x70
Sep 18 15:39:28 redsun41 kernel: handle_softirqs+0x1db/0x870
Sep 18 15:39:28 redsun41 kernel: ? __pfx_handle_softirqs+0x10/0x10
Sep 18 15:39:28 redsun41 kernel: ? irqtime_account_irq+0x3e/0x2d0
Sep 18 15:39:28 redsun41 kernel: __irq_exit_rcu+0x164/0x290
Sep 18 15:39:28 redsun41 kernel: irq_exit_rcu+0xe/0x20
Sep 18 15:39:28 redsun41 kernel: common_interrupt+0x85/0xa0
Sep 18 15:39:28 redsun41 kernel: </IRQ>
Sep 18 15:39:28 redsun41 kernel: <TASK>
Sep 18 15:39:28 redsun41 kernel: asm_common_interrupt+0x26/0x40
Sep 18 15:39:28 redsun41 kernel: RIP: 0010:qlist_free_all+0x2e/0x130
Sep 18 15:39:28 redsun41 kernel: Code: 85 c0 0f 84 16 01 00 00 41 57 41 56 41 be 00 00 00 80 41 55 49 89 fd 41 54 49 89 f4 55 53 eb 3b 48 63 93 cc 00 00 00 48 8b 28 <48> 89 df 48 29 d0 48 89 c6 49 89 c7 e8 d1 ef ff ff 66 90 4c 89 fe
Sep 18 15:39:28 redsun41 kernel: RSP: 0000:ffff88810284f848 EFLAGS: 00000246
Sep 18 15:39:28 redsun41 kernel: RAX: ffff8882958a3200 RBX: ffff8881003aec80 RCX: 0000000000000000
Sep 18 15:39:28 redsun41 kernel: RDX: 0000000000000000 RSI: 0000000000000000 RDI: ffff88810284f7e0
Sep 18 15:39:28 redsun41 kernel: RBP: ffff8882958a3f00 R08: ffff8882956f3500 R09: ffffffffa8df51ce
Sep 18 15:39:28 redsun41 kernel: R10: 0000000000000000 R11: 0000000000000001 R12: 0000000000000000
Sep 18 15:39:28 redsun41 kernel: R13: ffff88810284f880 R14: 0000000080000000 R15: ffff8882956f3500
Sep 18 15:39:28 redsun41 kernel: ? qlist_free_all+0x4e/0x130
Sep 18 15:39:28 redsun41 kernel: ? qlist_free_all+0x53/0x130
Sep 18 15:39:28 redsun41 kernel: kasan_quarantine_reduce+0x19a/0x250
Sep 18 15:39:28 redsun41 kernel: __kasan_slab_alloc+0x6a/0x90
Sep 18 15:39:28 redsun41 kernel: __kmalloc_cache_noprof+0x219/0x660
Sep 18 15:39:28 redsun41 kernel: ? drm_gem_duplicate_shadow_plane_state+0x6f/0xf0 [drm_kms_helper]
Sep 18 15:39:28 redsun41 kernel: drm_gem_duplicate_shadow_plane_state+0x6f/0xf0 [drm_kms_helper]
Sep 18 15:39:28 redsun41 kernel: drm_atomic_get_plane_state+0x227/0x960 [drm]
Sep 18 15:39:28 redsun41 kernel: ? modeset_lock+0x164/0x620 [drm]
Sep 18 15:39:28 redsun41 kernel: drm_atomic_helper_dirtyfb+0x45b/0x770 [drm_kms_helper]
Sep 18 15:39:28 redsun41 kernel: ? __pfx_drm_atomic_helper_dirtyfb+0x10/0x10 [drm_kms_helper]
Sep 18 15:39:28 redsun41 kernel: ? find_held_lock+0x2b/0x80
Sep 18 15:39:28 redsun41 kernel: ? do_raw_spin_lock+0x131/0x280
Sep 18 15:39:28 redsun41 kernel: ? find_held_lock+0x2b/0x80
Sep 18 15:39:28 redsun41 kernel: ? mark_held_locks+0x40/0x70
Sep 18 15:39:28 redsun41 kernel: drm_fbdev_shmem_helper_fb_dirty+0x1a0/0x440 [drm_shmem_helper]
Sep 18 15:39:28 redsun41 kernel: ? _raw_spin_unlock_irqrestore+0x45/0x60
Sep 18 15:39:28 redsun41 kernel: drm_fb_helper_fb_dirty+0x3fa/0xa60 [drm_kms_helper]
Sep 18 15:39:28 redsun41 kernel: ? process_one_work+0x8c3/0x1760
Sep 18 15:39:28 redsun41 kernel: ? __pfx_drm_fb_helper_fb_dirty+0x10/0x10 [drm_kms_helper]
Sep 18 15:39:28 redsun41 kernel: ? lock_acquire+0x11a/0x140
Sep 18 15:39:28 redsun41 kernel: process_one_work+0x947/0x1760
Sep 18 15:39:28 redsun41 kernel: ? __pfx_process_one_work+0x10/0x10
Sep 18 15:39:28 redsun41 kernel: ? lock_acquire.part.0+0xc8/0x240
Sep 18 15:39:28 redsun41 kernel: ? lock_is_held_type+0xb0/0x180
Sep 18 15:39:28 redsun41 kernel: worker_thread+0x601/0xff0
Sep 18 15:39:28 redsun41 kernel: ? __pfx_worker_thread+0x10/0x10
Sep 18 15:39:28 redsun41 kernel: ? __kthread_parkme+0xbd/0x210
Sep 18 15:39:28 redsun41 kernel: ? __pfx_worker_thread+0x10/0x10
Sep 18 15:39:28 redsun41 kernel: ? __pfx_worker_thread+0x10/0x10
Sep 18 15:39:28 redsun41 kernel: kthread+0x361/0x460
Sep 18 15:39:28 redsun41 kernel: ? __pfx_kthread+0x10/0x10
Sep 18 15:39:28 redsun41 kernel: ret_from_fork+0x560/0x860
Sep 18 15:39:28 redsun41 kernel: ? __pfx_ret_from_fork+0x10/0x10
Sep 18 15:39:28 redsun41 kernel: ? __switch_to+0x473/0xd50
Sep 18 15:39:28 redsun41 kernel: ? __switch_to_asm+0x39/0x70
Sep 18 15:39:28 redsun41 kernel: ? __switch_to_asm+0x33/0x70
Sep 18 15:39:28 redsun41 kernel: ? __pfx_kthread+0x10/0x10
Sep 18 15:39:28 redsun41 kernel: ret_from_fork_asm+0x1a/0x30
Sep 18 15:39:28 redsun41 kernel: </TASK>
Sep 18 15:39:28 redsun41 kernel: irq event stamp: 332632
Sep 18 15:39:28 redsun41 kernel: hardirqs last enabled at (332632): [<ffffffffa81e3134>] __local_bh_enable_ip+0xb4/0x150
Sep 18 15:39:28 redsun41 kernel: hardirqs last disabled at (332631): [<ffffffffa81e316a>] __local_bh_enable_ip+0xea/0x150
Sep 18 15:39:28 redsun41 kernel: softirqs last enabled at (332492): [<ffffffffa81e2802>] handle_softirqs+0x632/0x870
Sep 18 15:39:28 redsun41 kernel: softirqs last disabled at (332613): [<ffffffffa81e2bb4>] __irq_exit_rcu+0x164/0x290
Sep 18 15:39:28 redsun41 kernel: ---[ end trace 0000000000000000 ]---
// SPDX-License-Identifier: GPL-2.0
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/mutex.h>
#include <linux/ww_mutex.h>
#include <linux/spinlock.h>
#include <linux/lockdep.h>
#include <linux/rhashtable.h>
#include <linux/slab.h>
/*
* ww_mutex machinery used to give a held lock a non-zero ->references.
* All ww_mutexes of one ww_class share a single lockdep class, and
* ww_mutex_lock() passes nest_lock = &ctx->dep_map, so acquiring the second
* one sets ->references on the first one's held_lock entry.
*/
static DEFINE_WW_CLASS(repro_ww_class);
static struct ww_mutex ww_a;
static struct ww_mutex ww_b;
struct repro_obj {
int value;
struct rhash_head node;
};
static struct rhashtable ht;
static const struct rhashtable_params repro_params = {
.key_len = sizeof(int),
.key_offset = offsetof(struct repro_obj, value),
.head_offset = offsetof(struct repro_obj, node),
};
/*
* Run fn() while holding two same-class ww_mutexes under one acquire context,
* so that the first held_lock entry has ->references != 0.
*/
static int with_referenced_held_lock(void (*fn)(void))
{
struct ww_acquire_ctx ctx;
int ret;
ww_mutex_init(&ww_a, &repro_ww_class);
ww_mutex_init(&ww_b, &repro_ww_class);
ww_acquire_init(&ctx, &repro_ww_class);
ret = ww_mutex_lock(&ww_a, &ctx);
if (ret) {
pr_err("ww_mutex_lock(ww_a) failed: %d\n", ret);
goto out_fini;
}
/* same class as ww_a plus a nest_lock -> bumps ->references */
ret = ww_mutex_lock(&ww_b, &ctx);
if (ret) {
pr_err("ww_mutex_lock(ww_b) failed: %d\n", ret);
ww_mutex_unlock(&ww_a);
goto out_fini;
}
fn();
ww_mutex_unlock(&ww_b);
ww_mutex_unlock(&ww_a);
out_fini:
ww_acquire_fini(&ctx);
return ret;
}
static void lookup_rhashtable(void)
{
int wanted = 42;
pr_info("doing rhashtable_lookup_fast()\n");
pr_info("rhashtable_lookup_fast() = %px\n",
rhashtable_lookup_fast(&ht, &wanted, repro_params));
}
static int __init repro_rhashtable(void)
{
struct rhashtable_iter iter;
int ret;
ret = rhashtable_init(&ht, &repro_params);
if (ret) {
pr_err("rhashtable_init failed: %d\n", ret);
return ret;
}
/*
* rhashtable_walk_enter() takes spin_lock(&ht->lock), which is what
* registers subclass 0 of this instance's key under the name "key".
*/
rhashtable_walk_enter(&ht, &iter);
rhashtable_walk_exit(&iter);
ret = with_referenced_held_lock(lookup_rhashtable);
rhashtable_destroy(&ht);
return ret;
}
static int __init repro_init(void)
{
if (!IS_ENABLED(CONFIG_PROVE_LOCKING)) {
pr_err("CONFIG_PROVE_LOCKING is required\n");
return -EOPNOTSUPP;
}
if (!debug_locks) {
pr_err("lockdep is already disabled, cannot test\n");
return -EOPNOTSUPP;
}
return repro_rhashtable();
}
static void __exit repro_exit(void)
{
pr_info("rht_lockdep_repro: unloaded\n");
}
module_init(repro_init);
module_exit(repro_exit);
MODULE_DESCRIPTION("Reproducer for the rhashtable lockdep subclass collision");
MODULE_LICENSE("GPL");
© 2016 - 2026 Red Hat, Inc.