Netdev List
 help / color / mirror / Atom feed
From: Jim Cromie via B4 Relay <devnull+jim.cromie.gmail.com@kernel.org>
To: "Andrew Morton" <akpm@linux-foundation.org>,
	"Pablo Neira Ayuso" <pablo@netfilter.org>,
	"Florian Westphal" <fw@strlen.de>, "Phil Sutter" <phil@nwl.cc>,
	"Alexei Starovoitov" <ast@kernel.org>,
	"Daniel Borkmann" <daniel@iogearbox.net>,
	"Andrii Nakryiko" <andrii@kernel.org>,
	"Eduard Zingerman" <eddyz87@gmail.com>,
	"Danilo Krummrich" <dakr@kernel.org>,
	"Matthew Brost" <matthew.brost@intel.com>,
	"Boris Brezillon" <boris.brezillon@collabora.com>,
	"Peter Zijlstra" <peterz@infradead.org>,
	"Ingo Molnar" <mingo@redhat.com>, "Will Deacon" <will@kernel.org>,
	"Waiman Long" <longman@redhat.com>,
	"David S. Miller" <davem@davemloft.net>,
	"Eric Dumazet" <edumazet@google.com>,
	"Jakub Kicinski" <kuba@kernel.org>,
	"Paolo Abeni" <pabeni@redhat.com>,
	"Simon Horman" <horms@kernel.org>,
	"Kumar Kartikeya Dwivedi" <memxor@gmail.com>,
	"Martin KaFai Lau" <martin.lau@linux.dev>,
	"Song Liu" <song@kernel.org>,
	"Yonghong Song" <yonghong.song@linux.dev>,
	"Jiri Olsa" <jolsa@kernel.org>,
	"Emil Tsalapatis" <emil@etsalapatis.com>,
	"John Fastabend" <john.fastabend@gmail.com>,
	"Thomas Hellström" <thomas.hellstrom@linux.intel.com>,
	"Alice Ryhl" <aliceryhl@google.com>,
	"Maarten Lankhorst" <maarten.lankhorst@linux.intel.com>,
	"Maxime Ripard" <mripard@kernel.org>,
	"Thomas Zimmermann" <tzimmermann@suse.de>,
	"David Airlie" <airlied@gmail.com>,
	"Simona Vetter" <simona@ffwll.ch>,
	"Boqun Feng" <boqun@kernel.org>, "Boqun Feng" <boqun@kernel.org>
Cc: netfilter-devel@vger.kernel.org, bpf@vger.kernel.org,
	 dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org,
	 linux-mm@kvack.org, coreteam@netfilter.org,
	netdev@vger.kernel.org,  Jim Cromie <jim.cromie@gmail.com>
Subject: [PATCH 9/9] locking/lockdep: Migrate and compact boot-time dependency graph from __initdata
Date: Mon, 17 Aug 2026 11:22:23 -0600	[thread overview]
Message-ID: <20260817-folio-pool-v1-v1-9-0c1d230aa3af@gmail.com> (raw)
In-Reply-To: <20260817-folio-pool-v1-v1-0-0c1d230aa3af@gmail.com>

From: Jim Cromie <jim.cromie@gmail.com>

Mark early_list_entries[BOOTSTRAP_LOCKDEP_ENTRIES] and its tracking
bitmap as __initdata, dropping permanent static .bss allocation for
lockdep dependency edges from 1.31 MB to 0 KB.

During early boot before buddy page allocator initialization, lockdep
records initial spinlock dependencies into early_list_entries. At
late_initcall, lockdep_compact_boot_graph() completes the handoff:

0. Pre-allocates a 64 KB folio chunk from lockdep_pool outside
   graph_lock to prevent memory allocation recursion with core MM locks.
1. Under raw_local_irq_save() and graph_lock(), walks all_lock_classes,
   migrates live bootstrap nodes into contiguous folio slots, and
   updates locks_after/locks_before lists via list_replace_rcu().
2. Clears bootstrap pointers to NULL so subsequent runtime allocations
   and zap_class() operations bypass the bootstrap address range.
3. Adjusts nr_list_entries to preserve exact live edge accounting.

When free_initmem() executes before userspace launch, the entire 160 KB
bootstrap array is released back to the page allocator.

Live telemetry and graph verification:
- Pre-buddy watermark: 714/4096 bootstrap entries recorded.
- Late initcall migration: 2,656 edges compacted into lockdep_pool.
- Post-boot validation: BFS multi-hop traversals and IRQ constraint
  checks navigate the compacted folio topology with zero corruption.

Signed-off-by: Jim Cromie <jim.cromie@gmail.com>
---
Notes:
  Live proof of post-compaction validator integrity on v7.2-rc7:
  Immediately following free_initmem(), a live multi-hop locking
  constraint warning (fs_reclaim vs mmu_notifier) triggered lockdep's
  full reporting pipeline.

  This report acts as concrete validation of four core invariants:
  1. BFS Graph Traversal: __bfs() successfully resolved a 3-hop cycle
     across the newly migrated direct-map folio nodes.
  2. Payload Integrity: Stack traces stored during early boot were
     dereferenced from target->trace and printed without corruption.
  3. RCU List Updates: list_replace_rcu() preserved list head pointers
     with zero data races or poisoned node dereferences.
  4. Zero Stale References: No accesses to reclaimed .init.data memory
     occurred during the full graph walk and stack formatting.
---
 kernel/locking/lockdep.c | 227 ++++++++++++++++++++++++++++++++++-------------
 1 file changed, 164 insertions(+), 63 deletions(-)

diff --git a/kernel/locking/lockdep.c b/kernel/locking/lockdep.c
index 5968a976bf8e..d9541207b342 100644
--- a/kernel/locking/lockdep.c
+++ b/kernel/locking/lockdep.c
@@ -231,9 +231,11 @@ static inline int debug_locks_off_graph_unlock(void)
 
 #define BOOTSTRAP_LOCKDEP_ENTRIES 4096UL
 
+static struct lock_list early_list_entries[BOOTSTRAP_LOCKDEP_ENTRIES] __initdata;
+static unsigned long early_list_entries_in_use[BITS_TO_LONGS(BOOTSTRAP_LOCKDEP_ENTRIES)] __initdata;
+static struct lock_list *bootstrap_entries __read_mostly = early_list_entries;
+static unsigned long *bootstrap_entries_in_use __read_mostly = early_list_entries_in_use;
 unsigned long nr_list_entries;
-static struct lock_list list_entries[BOOTSTRAP_LOCKDEP_ENTRIES];
-static DECLARE_BITMAP(list_entries_in_use, BOOTSTRAP_LOCKDEP_ENTRIES);
 
 /*
  * All data structures here are protected by the global debug_lock.
@@ -1055,9 +1057,9 @@ static bool class_lock_list_valid(struct lock_class *c, struct list_head *h)
 
 	list_for_each_entry(e, h, entry) {
 		if (e->links_to != c) {
-			printk(KERN_INFO "class %s: mismatch for lock entry %ld; class %s <> %s",
+			printk(KERN_INFO "class %s: mismatch for lock entry %px; class %s <> %s",
 			       c->name ? : "(?)",
-			       (unsigned long)(e - list_entries),
+			       e,
 			       e->links_to && e->links_to->name ?
 			       e->links_to->name : "(?)",
 			       e->class && e->class->name ? e->class->name :
@@ -1146,35 +1148,37 @@ static bool __check_data_structures(void)
 		}
 	}
 
-	/*
-	 * Check whether all list entries that are in use occur in a class
-	 * lock list.
-	 */
-	for_each_set_bit(i, list_entries_in_use, ARRAY_SIZE(list_entries)) {
-		e = list_entries + i;
-		if (!in_any_class_list(&e->entry)) {
-			printk(KERN_INFO "list entry %d is not in any class list; class %s <> %s\n",
-			       (unsigned int)(e - list_entries),
-			       e->class->name ? : "(?)",
-			       e->links_to->name ? : "(?)");
-			return false;
+	if (bootstrap_entries) {
+		/*
+		 * Check whether all list entries that are in use occur in a class
+		 * lock list.
+		 */
+		for_each_set_bit(i, bootstrap_entries_in_use, BOOTSTRAP_LOCKDEP_ENTRIES) {
+			e = bootstrap_entries + i;
+			if (!in_any_class_list(&e->entry)) {
+				printk(KERN_INFO "list entry %d is not in any class list; class %s <> %s\n",
+				       (unsigned int)(e - bootstrap_entries),
+				       e->class->name ? : "(?)",
+				       e->links_to->name ? : "(?)");
+				return false;
+			}
 		}
-	}
 
-	/*
-	 * Check whether all list entries that are not in use do not occur in
-	 * a class lock list.
-	 */
-	for_each_clear_bit(i, list_entries_in_use, ARRAY_SIZE(list_entries)) {
-		e = list_entries + i;
-		if (in_any_class_list(&e->entry)) {
-			printk(KERN_INFO "list entry %d occurs in a class list; class %s <> %s\n",
-			       (unsigned int)(e - list_entries),
-			       e->class && e->class->name ? e->class->name :
-			       "(?)",
-			       e->links_to && e->links_to->name ?
-			       e->links_to->name : "(?)");
-			return false;
+		/*
+		 * Check whether all list entries that are not in use do not occur in
+		 * a class lock list.
+		 */
+		for_each_clear_bit(i, bootstrap_entries_in_use, BOOTSTRAP_LOCKDEP_ENTRIES) {
+			e = bootstrap_entries + i;
+			if (in_any_class_list(&e->entry)) {
+				printk(KERN_INFO "list entry %d occurs in a class list; class %s <> %s\n",
+				       (unsigned int)(e - bootstrap_entries),
+				       e->class && e->class->name ? e->class->name :
+				       "(?)",
+				       e->links_to && e->links_to->name ?
+				       e->links_to->name : "(?)");
+				return false;
+			}
 		}
 	}
 
@@ -1427,30 +1431,32 @@ register_lock_class(struct lockdep_map *lock, unsigned int subclass, int force)
  */
 static struct lock_list *alloc_list_entry(void)
 {
-	int idx = find_first_zero_bit(list_entries_in_use,
-				      ARRAY_SIZE(list_entries));
+	struct lock_list *p;
 
-	if (idx >= ARRAY_SIZE(list_entries)) {
-		struct lock_list *p;
+	if (bootstrap_entries) {
+		int idx = find_first_zero_bit(bootstrap_entries_in_use,
+					      BOOTSTRAP_LOCKDEP_ENTRIES);
 
-		p = folio_pool_alloc_type(&lockdep_pool, struct lock_list,
-					  GFP_ATOMIC);
-		if (p) {
+		if (idx < BOOTSTRAP_LOCKDEP_ENTRIES) {
+			__set_bit(idx, bootstrap_entries_in_use);
 			nr_list_entries++;
-			return p;
+			return bootstrap_entries + idx;
 		}
-		if (!debug_locks_off_graph_unlock())
-			return NULL;
+	}
 
-		nbcon_cpu_emergency_enter();
-		print_lockdep_off("BUG: MAX_LOCKDEP_ENTRIES too low and folio_pool exhausted!");
-		dump_stack();
-		nbcon_cpu_emergency_exit();
-		return NULL;
+	p = folio_pool_alloc_type(&lockdep_pool, struct lock_list, GFP_ATOMIC);
+	if (p) {
+		nr_list_entries++;
+		return p;
 	}
-	nr_list_entries++;
-	__set_bit(idx, list_entries_in_use);
-	return list_entries + idx;
+	if (!debug_locks_off_graph_unlock())
+		return NULL;
+
+	nbcon_cpu_emergency_enter();
+	print_lockdep_off("BUG: MAX_LOCKDEP_ENTRIES too low and folio_pool exhausted!");
+	dump_stack();
+	nbcon_cpu_emergency_exit();
+	return NULL;
 }
 
 /*
@@ -6275,8 +6281,15 @@ static void remove_class_from_lock_chains(struct pending_free *pf,
 
 static inline bool is_bootstrap_entry(const struct lock_list *entry)
 {
-	return entry >= list_entries &&
-	       entry < list_entries + ARRAY_SIZE(list_entries);
+	return bootstrap_entries &&
+	       entry >= bootstrap_entries &&
+	       entry < bootstrap_entries + BOOTSTRAP_LOCKDEP_ENTRIES;
+}
+
+static inline void clear_bootstrap_entry_bit(const struct lock_list *entry)
+{
+	if (is_bootstrap_entry(entry))
+		__clear_bit(entry - bootstrap_entries, bootstrap_entries_in_use);
 }
 
 /*
@@ -6295,30 +6308,26 @@ static void zap_class(struct pending_free *pf, struct lock_class *class)
 	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) {
-				if (is_bootstrap_entry(other))
-					__clear_bit(other - list_entries, list_entries_in_use);
+				clear_bootstrap_entry_bit(other);
 				nr_list_entries--;
 				list_del_rcu(&other->entry);
 				break;
 			}
 		}
-		if (is_bootstrap_entry(entry))
-			__clear_bit(entry - list_entries, list_entries_in_use);
+		clear_bootstrap_entry_bit(entry);
 		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) {
-				if (is_bootstrap_entry(other))
-					__clear_bit(other - list_entries, list_entries_in_use);
+				clear_bootstrap_entry_bit(other);
 				nr_list_entries--;
 				list_del_rcu(&other->entry);
 				break;
 			}
 		}
-		if (is_bootstrap_entry(entry))
-			__clear_bit(entry - list_entries, list_entries_in_use);
+		clear_bootstrap_entry_bit(entry);
 		nr_list_entries--;
 		list_del_rcu(&entry->entry);
 	}
@@ -6693,6 +6702,9 @@ EXPORT_SYMBOL_GPL(lockdep_unregister_key);
 
 void __init lockdep_init(void)
 {
+	bootstrap_entries = early_list_entries;
+	bootstrap_entries_in_use = early_list_entries_in_use;
+
 	pr_info("Lock dependency validator: Copyright (c) 2006 Red Hat, Inc., Ingo Molnar\n");
 
 	pr_info("... MAX_LOCKDEP_SUBCLASSES:  %lu\n", MAX_LOCKDEP_SUBCLASSES);
@@ -6707,8 +6719,8 @@ void __init lockdep_init(void)
 	       (sizeof(lock_classes) +
 		sizeof(lock_classes_in_use) +
 		sizeof(classhash_table) +
-		sizeof(list_entries) +
-		sizeof(list_entries_in_use) +
+		sizeof(early_list_entries) +
+		sizeof(early_list_entries_in_use) +
 		sizeof(chainhash_table) +
 		sizeof(delayed_free)
 #ifdef CONFIG_PROVE_LOCKING
@@ -6733,12 +6745,101 @@ void __init lockdep_init(void)
 static int __init lockdep_boot_report(void)
 {
 	pr_info("lockdep: %lu/%lu bootstrap entries used before buddy init, folio_pool active\n",
-		min_t(unsigned long, nr_list_entries, ARRAY_SIZE(list_entries)),
-		ARRAY_SIZE(list_entries));
+		min_t(unsigned long, nr_list_entries, BOOTSTRAP_LOCKDEP_ENTRIES),
+		BOOTSTRAP_LOCKDEP_ENTRIES);
 	return 0;
 }
 core_initcall(lockdep_boot_report);
 
+static int __init lockdep_compact_boot_graph(void)
+{
+	struct lock_class *class;
+	struct lock_list *entry, *tmp, *new_entry;
+	unsigned long flags;
+	unsigned long migrated = 0;
+
+	if (!debug_locks)
+		return 0;
+
+	/* Pre-allocate 64KB folio chunk outside graph_lock to avoid MM recursion */
+	new_entry = folio_pool_alloc_type(&lockdep_pool, struct lock_list, GFP_KERNEL);
+	if (!new_entry) {
+		pr_err("lockdep: failed to pre-allocate folio chunk for boot compaction\n");
+		return -ENOMEM;
+	}
+
+	raw_local_irq_save(flags);
+	if (!graph_lock()) {
+		raw_local_irq_restore(flags);
+		return 0;
+	}
+
+	list_for_each_entry(class, &all_lock_classes, lock_entry) {
+		list_for_each_entry_safe(entry, tmp, &class->locks_after, entry) {
+			if (is_bootstrap_entry(entry)) {
+				if (new_entry) {
+					*new_entry = *entry;
+					list_replace_rcu(&entry->entry, &new_entry->entry);
+					new_entry = NULL;
+				} else {
+					struct lock_list *slot;
+
+					slot = folio_pool_alloc_type(&lockdep_pool,
+								     struct lock_list,
+								     GFP_ATOMIC);
+					if (!slot) {
+						debug_locks_off_graph_unlock();
+						raw_local_irq_restore(flags);
+						pr_err("lockdep: folio chunk exhausted during boot compaction\n");
+						return -ENOMEM;
+					}
+					*slot = *entry;
+					list_replace_rcu(&entry->entry, &slot->entry);
+				}
+				migrated++;
+			}
+		}
+
+		list_for_each_entry_safe(entry, tmp, &class->locks_before, entry) {
+			if (is_bootstrap_entry(entry)) {
+				if (new_entry) {
+					*new_entry = *entry;
+					list_replace_rcu(&entry->entry, &new_entry->entry);
+					new_entry = NULL;
+				} else {
+					struct lock_list *slot;
+
+					slot = folio_pool_alloc_type(&lockdep_pool,
+								     struct lock_list,
+								     GFP_ATOMIC);
+					if (!slot) {
+						debug_locks_off_graph_unlock();
+						raw_local_irq_restore(flags);
+						pr_err("lockdep: folio chunk exhausted during boot compaction\n");
+						return -ENOMEM;
+					}
+					*slot = *entry;
+					list_replace_rcu(&entry->entry, &slot->entry);
+				}
+				migrated++;
+			}
+		}
+	}
+
+	/* Adjust counter so compaction does not double-count migrated nodes */
+	nr_list_entries -= migrated;
+
+	bootstrap_entries = NULL;
+	bootstrap_entries_in_use = NULL;
+	graph_unlock();
+	raw_local_irq_restore(flags);
+
+	pr_info("lockdep: compacted %lu boot entries into folio_pool, freeing bootstrap memory\n",
+		migrated);
+	return 0;
+}
+late_initcall(lockdep_compact_boot_graph);
+
 static void
 print_freed_lock_bug(struct task_struct *curr, const void *mem_from,
 		     const void *mem_to, struct held_lock *hlock)

-- 
2.55.0



  parent reply	other threads:[~2026-08-17 17:22 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-17 17:22 [PATCH 0/9] lib/folio_pool: Direct-Map Large Folio Pool & Scratchpad bump allocators Jim Cromie via B4 Relay
2026-08-17 17:22 ` [PATCH 1/9] lib/folio_pool: Introduce " Jim Cromie via B4 Relay
2026-08-17 17:22 ` [PATCH 2/9] netfilter/nf_tables: Add folio_scratchpad collector to struct nftables_pernet Jim Cromie via B4 Relay
2026-08-17 17:22 ` [PATCH 3/9] bpf/verifier: Route verifier stack state node allocations to folio_pool Jim Cromie via B4 Relay
2026-08-17 17:22 ` [PATCH 4/9] drm/gpuvm: Route gpuva_op allocations to folio_scratchpad Jim Cromie via B4 Relay
2026-08-17 17:22 ` [PATCH 5/9] bpf/syscall: Route generic_map_update_batch key/value " Jim Cromie via B4 Relay
2026-08-17 17:22 ` [PATCH 6/9] locking/lockdep: Fallback to folio_pool in alloc_list_entry when static pool is full Jim Cromie via B4 Relay
2026-08-17 21:01   ` Peter Zijlstra
2026-08-17 17:22 ` [PATCH 7/9] locking/lockdep: Traverse adjacency lists directly in zap_class() Jim Cromie via B4 Relay
2026-08-17 17:22 ` [PATCH 8/9] locking/lockdep: Shrink static list_entries array to early bootstrap buffer Jim Cromie via B4 Relay
2026-08-17 17:22 ` Jim Cromie via B4 Relay [this message]
2026-08-17 18:17 ` [PATCH 0/9] lib/folio_pool: Direct-Map Large Folio Pool & Scratchpad bump allocators David Hildenbrand (Arm)
2026-08-17 18:34 ` Matthew Wilcox

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260817-folio-pool-v1-v1-9-0c1d230aa3af@gmail.com \
    --to=devnull+jim.cromie.gmail.com@kernel.org \
    --cc=airlied@gmail.com \
    --cc=akpm@linux-foundation.org \
    --cc=aliceryhl@google.com \
    --cc=andrii@kernel.org \
    --cc=ast@kernel.org \
    --cc=boqun@kernel.org \
    --cc=boris.brezillon@collabora.com \
    --cc=bpf@vger.kernel.org \
    --cc=coreteam@netfilter.org \
    --cc=dakr@kernel.org \
    --cc=daniel@iogearbox.net \
    --cc=davem@davemloft.net \
    --cc=dri-devel@lists.freedesktop.org \
    --cc=eddyz87@gmail.com \
    --cc=edumazet@google.com \
    --cc=emil@etsalapatis.com \
    --cc=fw@strlen.de \
    --cc=horms@kernel.org \
    --cc=jim.cromie@gmail.com \
    --cc=john.fastabend@gmail.com \
    --cc=jolsa@kernel.org \
    --cc=kuba@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-mm@kvack.org \
    --cc=longman@redhat.com \
    --cc=maarten.lankhorst@linux.intel.com \
    --cc=martin.lau@linux.dev \
    --cc=matthew.brost@intel.com \
    --cc=memxor@gmail.com \
    --cc=mingo@redhat.com \
    --cc=mripard@kernel.org \
    --cc=netdev@vger.kernel.org \
    --cc=netfilter-devel@vger.kernel.org \
    --cc=pabeni@redhat.com \
    --cc=pablo@netfilter.org \
    --cc=peterz@infradead.org \
    --cc=phil@nwl.cc \
    --cc=simona@ffwll.ch \
    --cc=song@kernel.org \
    --cc=thomas.hellstrom@linux.intel.com \
    --cc=tzimmermann@suse.de \
    --cc=will@kernel.org \
    --cc=yonghong.song@linux.dev \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox