[PATCH 1/8] lockdep: Traverse adjacency lists directly in zap_class()
From: Jim Cromie
Date: Wed Aug 26 2026 - 23:59:09 EST
Lockdep's canonical graph representation is its per-class adjacency
lists (locks_after and locks_before). However, zap_class() operates on
a flat storage-layer projection of the graph: it scans the global
list_entries_in_use bitmap across the entire edge pool.
This global scan has a few defects:
0. Search Inefficiency:
On a typical booted laptop with ~2,000 lock classes and ~6,500 active
dependency list entries, zapping a single class forces 6,500+ table
inspections under graph_lock across 4 KB of bitmap. Zapping a batch of
classes during module unload multiplies this into tens of thousands of
global array iterations.
1. Projection maintenance: the bitmap must be kept up-to-date.
given 0, its a net burden, but we still need the bitmap elsewhere.
2. Incompatible with Array Segmentation:
The loop relies on contiguous pointer arithmetic (list_entries + i) to
map bitmap indices back to entries. This completely breaks once
list_entries is segmented into dynamic 64 kB memblock slabs residing
on disjoint memory pages.
So just implement the adjacency check literally, per graph-theory.
Real-world lock classes have very short adjacency lists: 3..5 entries
on average for class->locks_after and class->locks_before, rarely
exceeding 15.
Directly walking these lists visits only ~10..15 nodes per zapped
class, replacing 6,500+ global table dereferences with a handful of
cacheline-local pointer hops (>99.8% reduction in loop iterations).
Note: We continue clearing bits in list_entries_in_use for now, as
alloc_list_entry() still queries the bitmap in this commit. The bitmap
itself is eliminated soon
Signed-off-by: Jim Cromie <jim.cromie@xxxxxxxxx>
---
kernel/locking/lockdep.c | 31 ++++++++++++++++++++++++-------
1 file changed, 24 insertions(+), 7 deletions(-)
diff --git a/kernel/locking/lockdep.c b/kernel/locking/lockdep.c
index 2d4c5bab5af8..6a4f21f3e9c8 100644
--- a/kernel/locking/lockdep.c
+++ b/kernel/locking/lockdep.c
@@ -6243,8 +6243,7 @@ static void remove_class_from_lock_chains(struct pending_free *pf,
*/
static void zap_class(struct pending_free *pf, struct lock_class *class)
{
- struct lock_list *entry;
- int i;
+ struct lock_list *entry, *tmp, *other, *other_tmp;
WARN_ON_ONCE(!class->key);
@@ -6252,11 +6251,29 @@ static void zap_class(struct pending_free *pf, struct lock_class *class)
* Remove all dependencies this lock is
* involved in:
*/
- for_each_set_bit(i, list_entries_in_use, ARRAY_SIZE(list_entries)) {
- entry = list_entries + i;
- if (entry->class != class && entry->links_to != class)
- continue;
- __clear_bit(i, list_entries_in_use);
+ list_for_each_entry_safe(entry, tmp, &class->locks_after, entry) {
+ list_for_each_entry_safe(other, other_tmp, &entry->links_to->locks_before, entry) {
+ if (other->links_to == class) {
+ __clear_bit(other - list_entries, list_entries_in_use);
+ nr_list_entries--;
+ list_del_rcu(&other->entry);
+ break;
+ }
+ }
+ __clear_bit(entry - list_entries, list_entries_in_use);
+ nr_list_entries--;
+ list_del_rcu(&entry->entry);
+ }
+ list_for_each_entry_safe(entry, tmp, &class->locks_before, entry) {
+ list_for_each_entry_safe(other, other_tmp, &entry->links_to->locks_after, entry) {
+ if (other->links_to == class) {
+ __clear_bit(other - list_entries, list_entries_in_use);
+ nr_list_entries--;
+ list_del_rcu(&other->entry);
+ break;
+ }
+ }
+ __clear_bit(entry - list_entries, list_entries_in_use);
nr_list_entries--;
list_del_rcu(&entry->entry);
}
--
2.55.0