Skip to contentSkip to conversation

mm: zone lock tracepoint instrumentation

PATCH In review Dmitry Ilvokhin 9 participants 48 messages Feb 27 – Mar 25

Subsystems (12)
lore
Share & tools
lore
#0 lore raw

v4 cover mm: zone lock tracepoint instrumentation

Zone lock contention can significantly impact allocation and reclaim latency, as it is a central synchronization point in the page allocator and reclaim paths. Improved visibility into its behavior is therefore important for diagnosing performance issues in memory-intensive workloads.

On some production workloads at Meta, we have observed noticeable zone lock contention. Deeper analysis of lock holders and waiters is currently difficult with existing instrumentation.

While generic lock contention_begin/contention_end tracepoints cover the slow path, they do not provide sufficient visibility into lock hold times. In particular, the lack of a release-side event makes it difficult to identify long lock holders and correlate them with waiters. As a result, distinguishing between short bursts of contention and pathological long hold times requires additional instrumentation.

This patch series adds dedicated tracepoint instrumentation to zone lock, following the existing mmap_lock tracing model.

The goal is to enable detailed holder/waiter analysis and lock hold time measurements without affecting the fast path when tracing is disabled.

The series is structured as follows:

1. Introduce zone lock wrappers.
2. Mechanically convert zone lock users to the wrappers.
3. Convert compaction to use the wrappers (requires minor restructuring of compact_lock_irqsave()).
4. Rename zone->lock to zone->_lock.
5. Add zone lock tracepoints.

The tracepoints are added via lightweight inline helpers in the wrappers. When tracing is disabled, the fast path remains unchanged.

Changes in v4:
- Fix build errors in kernel/power/snapshot.c and mm/shuffle.c files.
- Remove EXPORT_SYMBOL().
- Rename header zone_lock.h to mmzone_lock.h.
- Properly indent __acquires() annotation.

Changes in v3:
- Split compact_lock_irqsave() to compact_zone_lock_irqsave() and compact_lruvec_lock_irqsave().
- Rename zone->lock to zone->_lock.

Changes in v2:
- Move mecanical changes from mm/compaction.c to different commit.
- Removed compact_do_zone_trylock() and compact_do_raw_trylock_irqsave().

v1: https://lore.kernel.org/all/[email protected]/
v2: https://lore.kernel.org/all/[email protected]/
v3: https://lore.kernel.org/all/[email protected]/

Dmitry Ilvokhin (5):
mm: introduce zone lock wrappers
mm: convert zone lock users to wrappers
mm: convert compaction to zone lock wrappers
mm: rename zone->lock to zone->_lock
mm: add tracepoints for zone lock

 MAINTAINERS                      |   2 +
 include/linux/mmzone.h           |   7 ++-
 include/linux/mmzone_lock.h      | 100 +++++++++++++++++++++++++++++++
 include/trace/events/zone_lock.h |  64 ++++++++++++++++++++
 kernel/power/snapshot.c          |   5 +-
 mm/Makefile                      |   2 +-
 mm/compaction.c                  |  58 +++++++++++-------
 mm/internal.h                    |   2 +-
 mm/memory_hotplug.c              |   9 +--
 mm/mm_init.c                     |   3 +-
 mm/page_alloc.c                  |  89 +++++++++++++--------------
 mm/page_isolation.c              |  23 +++----
 mm/page_owner.c                  |   2 +-
 mm/page_reporting.c              |  13 ++--
 mm/show_mem.c                    |   5 +-
 mm/shuffle.c                     |   9 +--
 mm/vmscan.c                      |   5 +-
 mm/vmstat.c                      |   9 +--
 mm/zone_lock.c                   |  28 +++++++++
 19 files changed, 330 insertions(+), 105 deletions(-)
 create mode 100644 include/linux/mmzone_lock.h
 create mode 100644 include/trace/events/zone_lock.h
 create mode 100644 mm/zone_lock.c
Signature · 1 line
2.47.3
#1 lore raw

PATCH v4 1/5 mm: introduce zone lock wrappers

Add thin wrappers around zone lock acquire/release operations. This prepares the code for future tracepoint instrumentation without modifying individual call sites.

Centralizing zone lock operations behind wrappers allows future instrumentation or debugging hooks to be added without touching all users.

No functional change intended. The wrappers are introduced in preparation for subsequent patches and are not yet used.

---
 MAINTAINERS                 |  1 +
 include/linux/mmzone_lock.h | 38 +++++++++++++++++++++++++++++++++++++
 2 files changed, 39 insertions(+)
 create mode 100644 include/linux/mmzone_lock.h
MAINTAINERS +1 -0
index 55af015174a5..947298ecb111 100644
@@ -16672,6 +16672,7 @@ F:	include/linux/memory.h
 F:	include/linux/mm.h
 F:	include/linux/mm_*.h
 F:	include/linux/mmzone.h
+F:	include/linux/mmzone_lock.h
 F:	include/linux/mmdebug.h
 F:	include/linux/mmu_notifier.h
 F:	include/linux/pagewalk.h
include/linux/mmzone_lock.h new file +38 -0
new file mode 100644
index 000000000000..a1cfba8408d6
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _LINUX_MMZONE_LOCK_H
+#define _LINUX_MMZONE_LOCK_H
+
+#include <linux/mmzone.h>
+#include <linux/spinlock.h>
+
+static inline void zone_lock_init(struct zone *zone)
+{
+	spin_lock_init(&zone->lock);
+}
+
+#define zone_lock_irqsave(zone, flags)				\
+do {								\
+	spin_lock_irqsave(&(zone)->lock, flags);		\
+} while (0)
+
+#define zone_trylock_irqsave(zone, flags)			\
+({								\
+	spin_trylock_irqsave(&(zone)->lock, flags);		\
+})
+
+static inline void zone_unlock_irqrestore(struct zone *zone, unsigned long flags)
+{
+	spin_unlock_irqrestore(&zone->lock, flags);
+}
+
+static inline void zone_lock_irq(struct zone *zone)
+{
+	spin_lock_irq(&zone->lock);
+}
+
+static inline void zone_unlock_irq(struct zone *zone)
+{
+	spin_unlock_irq(&zone->lock);
+}
+
+#endif /* _LINUX_MMZONE_LOCK_H */
Signature · 1 line
2.47.3
maintainer Acked-by on v4 1/5
#2 lore raw

Re: [PATCH v4 1/5] mm: introduce zone lock wrappers

Quoted 8 lines · Dmitry Ilvokhin

Add thin wrappers around zone lock acquire/release operations. This prepares the code for future tracepoint instrumentation without modifying individual call sites.

Centralizing zone lock operations behind wrappers allows future instrumentation or debugging hooks to be added without touching all users.

Quoted 6 lines

No functional change intended. The wrappers are introduced in preparation for subsequent patches and are not yet used.

---

Signature · 3 lines
Cheers,

David
reviewer Acked-by on v4 1/5
#3 lore raw

Re: [PATCH v4 1/5] mm: introduce zone lock wrappers

In reply to Dmitry Ilvokhin
Quoted 11 lines · Dmitry Ilvokhin

Add thin wrappers around zone lock acquire/release operations. This prepares the code for future tracepoint instrumentation without modifying individual call sites.

Centralizing zone lock operations behind wrappers allows future instrumentation or debugging hooks to be added without touching all users.

No functional change intended. The wrappers are introduced in preparation for subsequent patches and are not yet used.

Quoted 8 lines
---
 MAINTAINERS                 |  1 +
 include/linux/mmzone_lock.h | 38 +++++++++++++++++++++++++++++++++++++
 2 files changed, 39 insertions(+)
 create mode 100644 include/linux/mmzone_lock.h

Best Regards,
Yan, Zi

Reviewed-by on v4 1/5
#4 lore raw

Re: [PATCH v4 1/5] mm: introduce zone lock wrappers

In reply to Dmitry Ilvokhin
Quoted 8 lines · On Fri, 27 Feb 2026 16:00:23 +0000 Dmitry Ilvokhin <[email protected]> wrote:

Add thin wrappers around zone lock acquire/release operations. This prepares the code for future tracepoint instrumentation without modifying individual call sites.

Centralizing zone lock operations behind wrappers allows future instrumentation or debugging hooks to be added without touching all users.

Quoted 5 lines

No functional change intended. The wrappers are introduced in preparation for subsequent patches and are not yet used.

Thanks,
SJ

[...]

maintainer Reviewed-by on v4 1/5
#5 lore raw

Re: [PATCH v4 1/5] mm: introduce zone lock wrappers

In reply to Dmitry Ilvokhin
Quoted 8 lines · Dmitry Ilvokhin

Add thin wrappers around zone lock acquire/release operations. This prepares the code for future tracepoint instrumentation without modifying individual call sites.

Centralizing zone lock operations behind wrappers allows future instrumentation or debugging hooks to be added without touching all users.

Quoted 5 lines

No functional change intended. The wrappers are introduced in preparation for subsequent patches and are not yet used.

*checks patch 2 diffstat*

I think we could do it as mm/zone_lock.h even and not pollute include/linux/ Even kernel/power/snapshot.c could include it in a somewhat ugly way. However we should also later look at moving that particular code somewhere under mm/ really...

Anyway,

Quoted 62 lines
---
 MAINTAINERS                 |  1 +
 include/linux/mmzone_lock.h | 38 +++++++++++++++++++++++++++++++++++++
 2 files changed, 39 insertions(+)
 create mode 100644 include/linux/mmzone_lock.h
MAINTAINERS +1 -0
index 55af015174a5..947298ecb111 100644
@@ -16672,6 +16672,7 @@ F:	include/linux/memory.h
 F:	include/linux/mm.h
 F:	include/linux/mm_*.h
 F:	include/linux/mmzone.h
+F:	include/linux/mmzone_lock.h
 F:	include/linux/mmdebug.h
 F:	include/linux/mmu_notifier.h
 F:	include/linux/pagewalk.h
include/linux/mmzone_lock.h new file +38 -0
new file mode 100644
index 000000000000..a1cfba8408d6
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _LINUX_MMZONE_LOCK_H
+#define _LINUX_MMZONE_LOCK_H
+
+#include <linux/mmzone.h>
+#include <linux/spinlock.h>
+
+static inline void zone_lock_init(struct zone *zone)
+{
+	spin_lock_init(&zone->lock);
+}
+
+#define zone_lock_irqsave(zone, flags)				\
+do {								\
+	spin_lock_irqsave(&(zone)->lock, flags);		\
+} while (0)
+
+#define zone_trylock_irqsave(zone, flags)			\
+({								\
+	spin_trylock_irqsave(&(zone)->lock, flags);		\
+})
+
+static inline void zone_unlock_irqrestore(struct zone *zone, unsigned long flags)
+{
+	spin_unlock_irqrestore(&zone->lock, flags);
+}
+
+static inline void zone_lock_irq(struct zone *zone)
+{
+	spin_lock_irq(&zone->lock);
+}
+
+static inline void zone_unlock_irq(struct zone *zone)
+{
+	spin_unlock_irq(&zone->lock);
+}
+
+#endif /* _LINUX_MMZONE_LOCK_H */
#6 lore raw

PATCH v4 2/5 mm: convert zone lock users to wrappers

In reply to Dmitry Ilvokhin

Replace direct zone lock acquire/release operations with the newly introduced wrappers.

The changes are purely mechanical substitutions. No functional change intended. Locking semantics and ordering remain unchanged.

The compaction path is left unchanged for now and will be handled separately in the following patch due to additional non-trivial modifications.

---
 kernel/power/snapshot.c |  5 +--
 mm/compaction.c         | 25 +++++++-------
 mm/memory_hotplug.c     |  9 ++---
 mm/mm_init.c            |  3 +-
 mm/page_alloc.c         | 73 +++++++++++++++++++++--------------------
 mm/page_isolation.c     | 19 ++++++-----
 mm/page_reporting.c     | 13 ++++----
 mm/show_mem.c           |  5 +--
 mm/shuffle.c            |  9 ++---
 mm/vmscan.c             |  5 +--
 mm/vmstat.c             |  9 ++---
 11 files changed, 94 insertions(+), 81 deletions(-)
kernel/power/snapshot.c +3 -2
index 6e1321837c66..7dcccf378cc2 100644
@@ -13,6 +13,7 @@
 #include <linux/version.h>
 #include <linux/module.h>
 #include <linux/mm.h>
+#include <linux/mmzone_lock.h>
 #include <linux/suspend.h>
 #include <linux/delay.h>
 #include <linux/bitops.h>
@@ -1251,7 +1252,7 @@ static void mark_free_pages(struct zone *zone)
 	if (zone_is_empty(zone))
 		return;
 
-	spin_lock_irqsave(&zone->lock, flags);
+	zone_lock_irqsave(zone, flags);
 
 	max_zone_pfn = zone_end_pfn(zone);
 	for_each_valid_pfn(pfn, zone->zone_start_pfn, max_zone_pfn) {
@@ -1284,7 +1285,7 @@ static void mark_free_pages(struct zone *zone)
 			}
 		}
 	}
-	spin_unlock_irqrestore(&zone->lock, flags);
+	zone_unlock_irqrestore(zone, flags);
 }
 
 #ifdef CONFIG_HIGHMEM
mm/compaction.c +14 -11
index 1e8f8eca318c..fa0e332a8a92 100644
@@ -24,6 +24,7 @@
 #include <linux/page_owner.h>
 #include <linux/psi.h>
 #include <linux/cpuset.h>
+#include <linux/mmzone_lock.h>
 #include "internal.h"
 
 #ifdef CONFIG_COMPACTION
@@ -530,11 +531,14 @@ static bool compact_lock_irqsave(spinlock_t *lock, unsigned long *flags,
  * Returns true if compaction should abort due to fatal signal pending.
  * Returns false when compaction can continue.
  */
-static bool compact_unlock_should_abort(spinlock_t *lock,
-		unsigned long flags, bool *locked, struct compact_control *cc)
+
+static bool compact_unlock_should_abort(struct zone *zone,
+					unsigned long flags,
+					bool *locked,
+					struct compact_control *cc)
 {
 	if (*locked) {
-		spin_unlock_irqrestore(lock, flags);
+		zone_unlock_irqrestore(zone, flags);
 		*locked = false;
 	}
 
@@ -582,9 +586,8 @@ static unsigned long isolate_freepages_block(struct compact_control *cc,
 		 * contention, to give chance to IRQs. Abort if fatal signal
 		 * pending.
 		 */
-		if (!(blockpfn % COMPACT_CLUSTER_MAX)
-		    && compact_unlock_should_abort(&cc->zone->lock, flags,
-								&locked, cc))
+		if (!(blockpfn % COMPACT_CLUSTER_MAX) &&
+		    compact_unlock_should_abort(cc->zone, flags, &locked, cc))
 			break;
 
 		nr_scanned++;
@@ -649,7 +652,7 @@ static unsigned long isolate_freepages_block(struct compact_control *cc,
 	}
 
 	if (locked)
-		spin_unlock_irqrestore(&cc->zone->lock, flags);
+		zone_unlock_irqrestore(cc->zone, flags);
 
 	/*
 	 * Be careful to not go outside of the pageblock.
@@ -1555,7 +1558,7 @@ static void fast_isolate_freepages(struct compact_control *cc)
 		if (!area->nr_free)
 			continue;
 
-		spin_lock_irqsave(&cc->zone->lock, flags);
+		zone_lock_irqsave(cc->zone, flags);
 		freelist = &area->free_list[MIGRATE_MOVABLE];
 		list_for_each_entry_reverse(freepage, freelist, buddy_list) {
 			unsigned long pfn;
@@ -1614,7 +1617,7 @@ static void fast_isolate_freepages(struct compact_control *cc)
 			}
 		}
 
-		spin_unlock_irqrestore(&cc->zone->lock, flags);
+		zone_unlock_irqrestore(cc->zone, flags);
 
 		/* Skip fast search if enough freepages isolated */
 		if (cc->nr_freepages >= cc->nr_migratepages)
@@ -1988,7 +1991,7 @@ static unsigned long fast_find_migrateblock(struct compact_control *cc)
 		if (!area->nr_free)
 			continue;
 
-		spin_lock_irqsave(&cc->zone->lock, flags);
+		zone_lock_irqsave(cc->zone, flags);
 		freelist = &area->free_list[MIGRATE_MOVABLE];
 		list_for_each_entry(freepage, freelist, buddy_list) {
 			unsigned long free_pfn;
@@ -2021,7 +2024,7 @@ static unsigned long fast_find_migrateblock(struct compact_control *cc)
 				break;
 			}
 		}
-		spin_unlock_irqrestore(&cc->zone->lock, flags);
+		zone_unlock_irqrestore(cc->zone, flags);
 	}
 
 	cc->total_migrate_scanned += nr_scanned;
mm/memory_hotplug.c +5 -4
index bc805029da51..36564e2fcef8 100644
@@ -36,6 +36,7 @@
 #include <linux/rmap.h>
 #include <linux/module.h>
 #include <linux/node.h>
+#include <linux/mmzone_lock.h>
 
 #include <asm/tlbflush.h>
 
@@ -1190,9 +1191,9 @@ int online_pages(unsigned long pfn, unsigned long nr_pages,
 	 * Fixup the number of isolated pageblocks before marking the sections
 	 * onlining, such that undo_isolate_page_range() works correctly.
 	 */
-	spin_lock_irqsave(&zone->lock, flags);
+	zone_lock_irqsave(zone, flags);
 	zone->nr_isolate_pageblock += nr_pages / pageblock_nr_pages;
-	spin_unlock_irqrestore(&zone->lock, flags);
+	zone_unlock_irqrestore(zone, flags);
 
 	/*
 	 * If this zone is not populated, then it is not in zonelist.
@@ -2041,9 +2042,9 @@ int offline_pages(unsigned long start_pfn, unsigned long nr_pages,
 	 * effectively stale; nobody should be touching them. Fixup the number
 	 * of isolated pageblocks, memory onlining will properly revert this.
 	 */
-	spin_lock_irqsave(&zone->lock, flags);
+	zone_lock_irqsave(zone, flags);
 	zone->nr_isolate_pageblock -= nr_pages / pageblock_nr_pages;
-	spin_unlock_irqrestore(&zone->lock, flags);
+	zone_unlock_irqrestore(zone, flags);
 
 	lru_cache_enable();
 	zone_pcp_enable(zone);
mm/mm_init.c +2 -1
index 61d983d23f55..fe494514ce03 100644
@@ -32,6 +32,7 @@
 #include <linux/vmstat.h>
 #include <linux/kexec_handover.h>
 #include <linux/hugetlb.h>
+#include <linux/mmzone_lock.h>
 #include "internal.h"
 #include "slab.h"
 #include "shuffle.h"
@@ -1425,7 +1426,7 @@ static void __meminit zone_init_internals(struct zone *zone, enum zone_type idx,
 	zone_set_nid(zone, nid);
 	zone->name = zone_names[idx];
 	zone->zone_pgdat = NODE_DATA(nid);
-	spin_lock_init(&zone->lock);
+	zone_lock_init(zone);
 	zone_seqlock_init(zone);
 	zone_pcp_init(zone);
 }
mm/page_alloc.c +37 -36

266 lines not on this page · raw message

mm/page_isolation.c +10 -9
index c48ff5c00244..91a0836bf1b7 100644
@@ -10,6 +10,7 @@
 #include <linux/hugetlb.h>
 #include <linux/page_owner.h>
 #include <linux/migrate.h>
+#include <linux/mmzone_lock.h>
 #include "internal.h"
 
 #define CREATE_TRACE_POINTS
@@ -173,7 +174,7 @@ static int set_migratetype_isolate(struct page *page, enum pb_isolate_mode mode,
 	if (PageUnaccepted(page))
 		accept_page(page);
 
-	spin_lock_irqsave(&zone->lock, flags);
+	zone_lock_irqsave(zone, flags);
 
 	/*
 	 * We assume the caller intended to SET migrate type to isolate.
@@ -181,7 +182,7 @@ static int set_migratetype_isolate(struct page *page, enum pb_isolate_mode mode,
 	 * set it before us.
 	 */
 	if (is_migrate_isolate_page(page)) {
-		spin_unlock_irqrestore(&zone->lock, flags);
+		zone_unlock_irqrestore(zone, flags);
 		return -EBUSY;
 	}
 
@@ -200,15 +201,15 @@ static int set_migratetype_isolate(struct page *page, enum pb_isolate_mode mode,
 			mode);
 	if (!unmovable) {
 		if (!pageblock_isolate_and_move_free_pages(zone, page)) {
-			spin_unlock_irqrestore(&zone->lock, flags);
+			zone_unlock_irqrestore(zone, flags);
 			return -EBUSY;
 		}
 		zone->nr_isolate_pageblock++;
-		spin_unlock_irqrestore(&zone->lock, flags);
+		zone_unlock_irqrestore(zone, flags);
 		return 0;
 	}
 
-	spin_unlock_irqrestore(&zone->lock, flags);
+	zone_unlock_irqrestore(zone, flags);
 	if (mode == PB_ISOLATE_MODE_MEM_OFFLINE) {
 		/*
 		 * printk() with zone->lock held will likely trigger a
@@ -229,7 +230,7 @@ static void unset_migratetype_isolate(struct page *page)
 	struct page *buddy;
 
 	zone = page_zone(page);
-	spin_lock_irqsave(&zone->lock, flags);
+	zone_lock_irqsave(zone, flags);
 	if (!is_migrate_isolate_page(page))
 		goto out;
 
@@ -280,7 +281,7 @@ static void unset_migratetype_isolate(struct page *page)
 	}
 	zone->nr_isolate_pageblock--;
 out:
-	spin_unlock_irqrestore(&zone->lock, flags);
+	zone_unlock_irqrestore(zone, flags);
 }
 
 static inline struct page *
@@ -641,9 +642,9 @@ int test_pages_isolated(unsigned long start_pfn, unsigned long end_pfn,
 
 	/* Check all pages are free or marked as ISOLATED */
 	zone = page_zone(page);
-	spin_lock_irqsave(&zone->lock, flags);
+	zone_lock_irqsave(zone, flags);
 	pfn = __test_page_isolated_in_pageblock(start_pfn, end_pfn, mode);
-	spin_unlock_irqrestore(&zone->lock, flags);
+	zone_unlock_irqrestore(zone, flags);
 
 	ret = pfn < end_pfn ? -EBUSY : 0;
 
mm/page_reporting.c +7 -6
index f0042d5743af..43976a9dce3f 100644
@@ -1,6 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0
 #include <linux/mm.h>
 #include <linux/mmzone.h>
+#include <linux/mmzone_lock.h>
 #include <linux/page_reporting.h>
 #include <linux/gfp.h>
 #include <linux/export.h>
@@ -161,7 +162,7 @@ page_reporting_cycle(struct page_reporting_dev_info *prdev, struct zone *zone,
 	if (list_empty(list))
 		return err;
 
-	spin_lock_irq(&zone->lock);
+	zone_lock_irq(zone);
 
 	/*
 	 * Limit how many calls we will be making to the page reporting
@@ -219,7 +220,7 @@ page_reporting_cycle(struct page_reporting_dev_info *prdev, struct zone *zone,
 			list_rotate_to_front(&page->lru, list);
 
 		/* release lock before waiting on report processing */
-		spin_unlock_irq(&zone->lock);
+		zone_unlock_irq(zone);
 
 		/* begin processing pages in local list */
 		err = prdev->report(prdev, sgl, PAGE_REPORTING_CAPACITY);
@@ -231,7 +232,7 @@ page_reporting_cycle(struct page_reporting_dev_info *prdev, struct zone *zone,
 		budget--;
 
 		/* reacquire zone lock and resume processing */
-		spin_lock_irq(&zone->lock);
+		zone_lock_irq(zone);
 
 		/* flush reported pages from the sg list */
 		page_reporting_drain(prdev, sgl, PAGE_REPORTING_CAPACITY, !err);
@@ -251,7 +252,7 @@ page_reporting_cycle(struct page_reporting_dev_info *prdev, struct zone *zone,
 	if (!list_entry_is_head(next, list, lru) && !list_is_first(&next->lru, list))
 		list_rotate_to_front(&next->lru, list);
 
-	spin_unlock_irq(&zone->lock);
+	zone_unlock_irq(zone);
 
 	return err;
 }
@@ -296,9 +297,9 @@ page_reporting_process_zone(struct page_reporting_dev_info *prdev,
 		err = prdev->report(prdev, sgl, leftover);
 
 		/* flush any remaining pages out from the last report */
-		spin_lock_irq(&zone->lock);
+		zone_lock_irq(zone);
 		page_reporting_drain(prdev, sgl, leftover, !err);
-		spin_unlock_irq(&zone->lock);
+		zone_unlock_irq(zone);
 	}
 
 	return err;
mm/show_mem.c +3 -2
index 24078ac3e6bc..d7d1b6cd6442 100644
@@ -12,6 +12,7 @@
 #include <linux/hugetlb.h>
 #include <linux/mm.h>
 #include <linux/mmzone.h>
+#include <linux/mmzone_lock.h>
 #include <linux/swap.h>
 #include <linux/vmstat.h>
 
@@ -363,7 +364,7 @@ static void show_free_areas(unsigned int filter, nodemask_t *nodemask, int max_z
 		show_node(zone);
 		printk(KERN_CONT "%s: ", zone->name);
 
-		spin_lock_irqsave(&zone->lock, flags);
+		zone_lock_irqsave(zone, flags);
 		for (order = 0; order < NR_PAGE_ORDERS; order++) {
 			struct free_area *area = &zone->free_area[order];
 			int type;
@@ -377,7 +378,7 @@ static void show_free_areas(unsigned int filter, nodemask_t *nodemask, int max_z
 					types[order] |= 1 << type;
 			}
 		}
-		spin_unlock_irqrestore(&zone->lock, flags);
+		zone_unlock_irqrestore(zone, flags);
 		for (order = 0; order < NR_PAGE_ORDERS; order++) {
 			printk(KERN_CONT "%lu*%lukB ",
 			       nr[order], K(1UL) << order);
mm/shuffle.c +5 -4
index fb1393b8b3a9..5f6ae3c52842 100644
@@ -4,6 +4,7 @@
 #include <linux/mm.h>
 #include <linux/init.h>
 #include <linux/mmzone.h>
+#include <linux/mmzone_lock.h>
 #include <linux/random.h>
 #include <linux/moduleparam.h>
 #include "internal.h"
@@ -85,7 +86,7 @@ void __meminit __shuffle_zone(struct zone *z)
 	const int order = SHUFFLE_ORDER;
 	const int order_pages = 1 << order;
 
-	spin_lock_irqsave(&z->lock, flags);
+	zone_lock_irqsave(z, flags);
 	start_pfn = ALIGN(start_pfn, order_pages);
 	for (i = start_pfn; i < end_pfn; i += order_pages) {
 		unsigned long j;
@@ -138,12 +139,12 @@ void __meminit __shuffle_zone(struct zone *z)
 
 		/* take it easy on the zone lock */
 		if ((i % (100 * order_pages)) == 0) {
-			spin_unlock_irqrestore(&z->lock, flags);
+			zone_unlock_irqrestore(z, flags);
 			cond_resched();
-			spin_lock_irqsave(&z->lock, flags);
+			zone_lock_irqsave(z, flags);
 		}
 	}
-	spin_unlock_irqrestore(&z->lock, flags);
+	zone_unlock_irqrestore(z, flags);
 }
 
 /*
mm/vmscan.c +3 -2
index 0fc9373e8251..44c70e4400e2 100644
@@ -58,6 +58,7 @@
 #include <linux/random.h>
 #include <linux/mmu_notifier.h>
 #include <linux/parser.h>
+#include <linux/mmzone_lock.h>
 
 #include <asm/tlbflush.h>
 #include <asm/div64.h>
@@ -7139,9 +7140,9 @@ static int balance_pgdat(pg_data_t *pgdat, int order, int highest_zoneidx)
 
 			/* Increments are under the zone lock */
 			zone = pgdat->node_zones + i;
-			spin_lock_irqsave(&zone->lock, flags);
+			zone_lock_irqsave(zone, flags);
 			zone->watermark_boost -= min(zone->watermark_boost, zone_boosts[i]);
-			spin_unlock_irqrestore(&zone->lock, flags);
+			zone_unlock_irqrestore(zone, flags);
 		}
 
 		/*
mm/vmstat.c +5 -4
index 86b14b0f77b5..6608bb489790 100644
@@ -28,6 +28,7 @@
 #include <linux/mm_inline.h>
 #include <linux/page_owner.h>
 #include <linux/sched/isolation.h>
+#include <linux/mmzone_lock.h>
 
 #include "internal.h"
 
@@ -1535,10 +1536,10 @@ static void walk_zones_in_node(struct seq_file *m, pg_data_t *pgdat,
 			continue;
 
 		if (!nolock)
-			spin_lock_irqsave(&zone->lock, flags);
+			zone_lock_irqsave(zone, flags);
 		print(m, pgdat, zone);
 		if (!nolock)
-			spin_unlock_irqrestore(&zone->lock, flags);
+			zone_unlock_irqrestore(zone, flags);
 	}
 }
 #endif
@@ -1603,9 +1604,9 @@ static void pagetypeinfo_showfree_print(struct seq_file *m,
 				}
 			}
 			seq_printf(m, "%s%6lu ", overflow ? ">" : "", freecount);
-			spin_unlock_irq(&zone->lock);
+			zone_unlock_irq(zone);
 			cond_resched();
-			spin_lock_irq(&zone->lock);
+			zone_lock_irq(zone);
 		}
 		seq_putc(m, '\n');
 	}
Signature · 1 line
2.47.3
maintainer Acked-by on v4 2/5
#7 lore raw

Re: [PATCH v4 2/5] mm: convert zone lock users to wrappers

Quoted 14 lines · Dmitry Ilvokhin

Replace direct zone lock acquire/release operations with the newly introduced wrappers.

The changes are purely mechanical substitutions. No functional change intended. Locking semantics and ordering remain unchanged.

The compaction path is left unchanged for now and will be handled separately in the following patch due to additional non-trivial modifications.

---

[...]

mm/compaction.c static bool compact_lock_irqsave(spinlock_t *lock, unsigned long *flags, · line 538

Quoted 12 lines

#ifdef CONFIG_COMPACTION

mm/compaction.c +5 -2
@@ -530,11 +531,14 @@ static bool compact_lock_irqsave(spinlock_t *lock, unsigned long *flags,
  * Returns true if compaction should abort due to fatal signal pending.
  * Returns false when compaction can continue.
  */
-static bool compact_unlock_should_abort(spinlock_t *lock,
-		unsigned long flags, bool *locked, struct compact_control *cc)
+
+static bool compact_unlock_should_abort(struct zone *zone,
+					unsigned long flags,
+					bool *locked,
+					struct compact_control *cc)

We tend to use two-tabs on second parameter line; like the existing code did.

Besides that

Signature · 3 lines
Cheers,

David
#8 lore raw

Re: [PATCH v4 2/5] mm: convert zone lock users to wrappers

Quoted 35 lines · David Hildenbrand (Arm)
Quoted 14 lines · Dmitry Ilvokhin

Replace direct zone lock acquire/release operations with the newly introduced wrappers.

The changes are purely mechanical substitutions. No functional change intended. Locking semantics and ordering remain unchanged.

The compaction path is left unchanged for now and will be handled separately in the following patch due to additional non-trivial modifications.

---

[...]

mm/compaction.c static bool compact_lock_irqsave(spinlock_t *lock, unsigned long *flags, · line 538

Quoted 12 lines

#ifdef CONFIG_COMPACTION

mm/compaction.c +5 -2
@@ -530,11 +531,14 @@ static bool compact_lock_irqsave(spinlock_t *lock, unsigned long *flags,
  * Returns true if compaction should abort due to fatal signal pending.
  * Returns false when compaction can continue.
  */
-static bool compact_unlock_should_abort(spinlock_t *lock,
-		unsigned long flags, bool *locked, struct compact_control *cc)
+
+static bool compact_unlock_should_abort(struct zone *zone,
+					unsigned long flags,
+					bool *locked,
+					struct compact_control *cc)

We tend to use two-tabs on second parameter line; like the existing code did.

Quoted 4 lines

Besides that

Thanks, David. Noted. Appreciate the review and ack.

Quoted 4 lines
Signature · 3 lines
Cheers,

David
reviewer Acked-by on v4 2/5
#9 lore raw

Re: [PATCH v4 2/5] mm: convert zone lock users to wrappers

In reply to Dmitry Ilvokhin
Quoted 13 lines · Dmitry Ilvokhin

Replace direct zone lock acquire/release operations with the newly introduced wrappers.

The changes are purely mechanical substitutions. No functional change intended. Locking semantics and ordering remain unchanged.

The compaction path is left unchanged for now and will be handled separately in the following patch due to additional non-trivial modifications.

Quoted 14 lines
---
 kernel/power/snapshot.c |  5 +--
 mm/compaction.c         | 25 +++++++-------
 mm/memory_hotplug.c     |  9 ++---
 mm/mm_init.c            |  3 +-
 mm/page_alloc.c         | 73 +++++++++++++++++++++--------------------
 mm/page_isolation.c     | 19 ++++++-----
 mm/page_reporting.c     | 13 ++++----
 mm/show_mem.c           |  5 +--
 mm/shuffle.c            |  9 ++---
 mm/vmscan.c             |  5 +--
 mm/vmstat.c             |  9 ++---
 11 files changed, 94 insertions(+), 81 deletions(-)

Best Regards,
Yan, Zi

maintainer Reviewed-by on v4 2/5
#10 lore raw

Re: [PATCH v4 2/5] mm: convert zone lock users to wrappers

In reply to Dmitry Ilvokhin
Quoted 6 lines · Dmitry Ilvokhin

Replace direct zone lock acquire/release operations with the newly introduced wrappers.

The changes are purely mechanical substitutions. No functional change intended. Locking semantics and ordering remain unchanged.

Quoted 7 lines

The compaction path is left unchanged for now and will be handled separately in the following patch due to additional non-trivial modifications.

#11 lore raw

PATCH v4 3/5 mm: convert compaction to zone lock wrappers

In reply to Dmitry Ilvokhin

Compaction uses compact_lock_irqsave(), which currently operates on a raw spinlock_t pointer so it can be used for both zone->lock and lruvec->lru_lock. Since zone lock operations are now wrapped, compact_lock_irqsave() can no longer directly operate on a spinlock_t when the lock belongs to a zone.

Split the helper into compact_zone_lock_irqsave() and compact_lruvec_lock_irqsave(), duplicating the small amount of shared logic. As there are only two call sites and both statically know the lock type, this avoids introducing additional abstraction or runtime dispatch in the compaction path.

No functional change intended.

---
 mm/compaction.c | 33 ++++++++++++++++++++++++---------
 1 file changed, 24 insertions(+), 9 deletions(-)
mm/compaction.c +24 -9
index fa0e332a8a92..c68fcc416fc7 100644
@@ -503,19 +503,36 @@ static bool test_and_set_skip(struct compact_control *cc, struct page *page)
  *
  * Always returns true which makes it easier to track lock state in callers.
  */
-static bool compact_lock_irqsave(spinlock_t *lock, unsigned long *flags,
-						struct compact_control *cc)
-	__acquires(lock)
+static bool compact_zone_lock_irqsave(struct zone *zone,
+				      unsigned long *flags,
+				      struct compact_control *cc)
+	__acquires(&zone->lock)
 {
 	/* Track if the lock is contended in async mode */
 	if (cc->mode == MIGRATE_ASYNC && !cc->contended) {
-		if (spin_trylock_irqsave(lock, *flags))
+		if (zone_trylock_irqsave(zone, *flags))
 			return true;
 
 		cc->contended = true;
 	}
 
-	spin_lock_irqsave(lock, *flags);
+	zone_lock_irqsave(zone, *flags);
+	return true;
+}
+
+static bool compact_lruvec_lock_irqsave(struct lruvec *lruvec,
+					unsigned long *flags,
+					struct compact_control *cc)
+	__acquires(&lruvec->lru_lock)
+{
+	if (cc->mode == MIGRATE_ASYNC && !cc->contended) {
+		if (spin_trylock_irqsave(&lruvec->lru_lock, *flags))
+			return true;
+
+		cc->contended = true;
+	}
+
+	spin_lock_irqsave(&lruvec->lru_lock, *flags);
 	return true;
 }
 
@@ -531,7 +548,6 @@ static bool compact_lock_irqsave(spinlock_t *lock, unsigned long *flags,
  * Returns true if compaction should abort due to fatal signal pending.
  * Returns false when compaction can continue.
  */
-
 static bool compact_unlock_should_abort(struct zone *zone,
 					unsigned long flags,
 					bool *locked,
@@ -616,8 +632,7 @@ static unsigned long isolate_freepages_block(struct compact_control *cc,
 
 		/* If we already hold the lock, we can skip some rechecking. */
 		if (!locked) {
-			locked = compact_lock_irqsave(&cc->zone->lock,
-								&flags, cc);
+			locked = compact_zone_lock_irqsave(cc->zone, &flags, cc);
 
 			/* Recheck this is a buddy page under lock */
 			if (!PageBuddy(page))
@@ -1163,7 +1178,7 @@ isolate_migratepages_block(struct compact_control *cc, unsigned long low_pfn,
 			if (locked)
 				unlock_page_lruvec_irqrestore(locked, flags);
 
-			compact_lock_irqsave(&lruvec->lru_lock, &flags, cc);
+			compact_lruvec_lock_irqsave(lruvec, &flags, cc);
 			locked = lruvec;
 
 			lruvec_memcg_debug(lruvec, folio);
Signature · 1 line
2.47.3
maintainer Acked-by on v4 3/5
#12 lore raw

Re: [PATCH v4 3/5] mm: convert compaction to zone lock wrappers

Quoted 25 lines · Dmitry Ilvokhin

Compaction uses compact_lock_irqsave(), which currently operates on a raw spinlock_t pointer so it can be used for both zone->lock and lruvec->lru_lock. Since zone lock operations are now wrapped, compact_lock_irqsave() can no longer directly operate on a spinlock_t when the lock belongs to a zone.

Split the helper into compact_zone_lock_irqsave() and compact_lruvec_lock_irqsave(), duplicating the small amount of shared logic. As there are only two call sites and both statically know the lock type, this avoids introducing additional abstraction or runtime dispatch in the compaction path.

No functional change intended.

---
 mm/compaction.c | 33 ++++++++++++++++++++++++---------
 1 file changed, 24 insertions(+), 9 deletions(-)
mm/compaction.c +0 -0
index fa0e332a8a92..c68fcc416fc7 100644

mm/compaction.c static bool test_and_set_skip(struct compact_control *cc, struct page *page) · line 508

Quoted 9 lines
mm/compaction.c +3 -3
@@ -503,19 +503,36 @@ static bool test_and_set_skip(struct compact_control *cc, struct page *page)
  *
  * Always returns true which makes it easier to track lock state in callers.
  */
-static bool compact_lock_irqsave(spinlock_t *lock, unsigned long *flags,
-						struct compact_control *cc)
-	__acquires(lock)
+static bool compact_zone_lock_irqsave(struct zone *zone,
+				      unsigned long *flags,
+				      struct compact_control *cc)

... two tabs :)

Signature · 3 lines
Cheers,

David
reviewer Acked-by on v4 3/5
#13 lore raw

Re: [PATCH v4 3/5] mm: convert compaction to zone lock wrappers

In reply to Dmitry Ilvokhin
Quoted 12 lines · Dmitry Ilvokhin

Compaction uses compact_lock_irqsave(), which currently operates on a raw spinlock_t pointer so it can be used for both zone->lock and lruvec->lru_lock. Since zone lock operations are now wrapped, compact_lock_irqsave() can no longer directly operate on a spinlock_t when the lock belongs to a zone.

Split the helper into compact_zone_lock_irqsave() and compact_lruvec_lock_irqsave(), duplicating the small amount of shared logic. As there are only two call sites and both statically know the lock type, this avoids introducing additional abstraction or runtime dispatch in the compaction path.

Quoted 8 lines

No functional change intended.

---
 mm/compaction.c | 33 ++++++++++++++++++++++++---------
 1 file changed, 24 insertions(+), 9 deletions(-)

Best Regards,
Yan, Zi

Reviewed-by on v4 3/5
#14 lore raw

Re: [PATCH v4 3/5] mm: convert compaction to zone lock wrappers

In reply to Dmitry Ilvokhin
Quoted 12 lines · On Fri, 27 Feb 2026 16:00:25 +0000 Dmitry Ilvokhin <[email protected]> wrote:

Compaction uses compact_lock_irqsave(), which currently operates on a raw spinlock_t pointer so it can be used for both zone->lock and lruvec->lru_lock. Since zone lock operations are now wrapped, compact_lock_irqsave() can no longer directly operate on a spinlock_t when the lock belongs to a zone.

Split the helper into compact_zone_lock_irqsave() and compact_lruvec_lock_irqsave(), duplicating the small amount of shared logic. As there are only two call sites and both statically know the lock type, this avoids introducing additional abstraction or runtime dispatch in the compaction path.

Quoted 4 lines

No functional change intended.

Thanks,
SJ

[...]

maintainer Reviewed-by on v4 3/5
#15 lore raw

Re: [PATCH v4 3/5] mm: convert compaction to zone lock wrappers

In reply to Dmitry Ilvokhin
Quoted 12 lines · Dmitry Ilvokhin

Compaction uses compact_lock_irqsave(), which currently operates on a raw spinlock_t pointer so it can be used for both zone->lock and lruvec->lru_lock. Since zone lock operations are now wrapped, compact_lock_irqsave() can no longer directly operate on a spinlock_t when the lock belongs to a zone.

Split the helper into compact_zone_lock_irqsave() and compact_lruvec_lock_irqsave(), duplicating the small amount of shared logic. As there are only two call sites and both statically know the lock type, this avoids introducing additional abstraction or runtime dispatch in the compaction path.

Quoted 4 lines

No functional change intended.

#16 lore raw

PATCH v4 4/5 mm: rename zone->lock to zone->_lock

In reply to Dmitry Ilvokhin

This intentionally breaks direct users of zone->lock at compile time so all call sites are converted to the zone lock wrappers. Without the rename, present and future out-of-tree code could continue using spin_lock(&zone->lock) and bypass the wrappers and tracing infrastructure.

No functional change intended.

---
 include/linux/mmzone.h      |  7 +++++--
 include/linux/mmzone_lock.h | 12 ++++++------
 mm/compaction.c             |  4 ++--
 mm/internal.h               |  2 +-
 mm/page_alloc.c             | 16 ++++++++--------
 mm/page_isolation.c         |  4 ++--
 mm/page_owner.c             |  2 +-
 7 files changed, 25 insertions(+), 22 deletions(-)
include/linux/mmzone.h +5 -2
index 3e51190a55e4..32bca655fce5 100644
@@ -1009,8 +1009,11 @@ struct zone {
 	/* zone flags, see below */
 	unsigned long		flags;
 
-	/* Primarily protects free_area */
-	spinlock_t		lock;
+	/*
+	 * Primarily protects free_area. Should be accessed via zone_lock_*
+	 * helpers.
+	 */
+	spinlock_t		_lock;
 
 	/* Pages to be freed when next trylock succeeds */
 	struct llist_head	trylock_free_pages;
include/linux/mmzone_lock.h +6 -6
index a1cfba8408d6..62e34d500078 100644
@@ -7,32 +7,32 @@
 
 static inline void zone_lock_init(struct zone *zone)
 {
-	spin_lock_init(&zone->lock);
+	spin_lock_init(&zone->_lock);
 }
 
 #define zone_lock_irqsave(zone, flags)				\
 do {								\
-	spin_lock_irqsave(&(zone)->lock, flags);		\
+	spin_lock_irqsave(&(zone)->_lock, flags);		\
 } while (0)
 
 #define zone_trylock_irqsave(zone, flags)			\
 ({								\
-	spin_trylock_irqsave(&(zone)->lock, flags);		\
+	spin_trylock_irqsave(&(zone)->_lock, flags);		\
 })
 
 static inline void zone_unlock_irqrestore(struct zone *zone, unsigned long flags)
 {
-	spin_unlock_irqrestore(&zone->lock, flags);
+	spin_unlock_irqrestore(&zone->_lock, flags);
 }
 
 static inline void zone_lock_irq(struct zone *zone)
 {
-	spin_lock_irq(&zone->lock);
+	spin_lock_irq(&zone->_lock);
 }
 
 static inline void zone_unlock_irq(struct zone *zone)
 {
-	spin_unlock_irq(&zone->lock);
+	spin_unlock_irq(&zone->_lock);
 }
 
 #endif /* _LINUX_MMZONE_LOCK_H */
mm/compaction.c +2 -2
index c68fcc416fc7..ac2a259518b1 100644
@@ -506,7 +506,7 @@ static bool test_and_set_skip(struct compact_control *cc, struct page *page)
 static bool compact_zone_lock_irqsave(struct zone *zone,
 				      unsigned long *flags,
 				      struct compact_control *cc)
-	__acquires(&zone->lock)
+	__acquires(&zone->_lock)
 {
 	/* Track if the lock is contended in async mode */
 	if (cc->mode == MIGRATE_ASYNC && !cc->contended) {
@@ -1402,7 +1402,7 @@ static bool suitable_migration_target(struct compact_control *cc,
 		int order = cc->order > 0 ? cc->order : pageblock_order;
 
 		/*
-		 * We are checking page_order without zone->lock taken. But
+		 * We are checking page_order without zone->_lock taken. But
 		 * the only small danger is that we skip a potentially suitable
 		 * pageblock, so it's not worth to check order for valid range.
 		 */
mm/internal.h +1 -1
index cb0af847d7d9..6cb06e21ce15 100644
@@ -710,7 +710,7 @@ static inline unsigned int buddy_order(struct page *page)
  * (d) a page and its buddy are in the same zone.
  *
  * For recording whether a page is in the buddy system, we set PageBuddy.
- * Setting, clearing, and testing PageBuddy is serialized by zone->lock.
+ * Setting, clearing, and testing PageBuddy is serialized by zone->_lock.
  *
  * For recording page's order, we use page_private(page).
  */
mm/page_alloc.c +8 -8
index bcc3fe0368fc..0d078aef8ed6 100644
@@ -815,7 +815,7 @@ compaction_capture(struct capture_control *capc, struct page *page,
 static inline void account_freepages(struct zone *zone, int nr_pages,
 				     int migratetype)
 {
-	lockdep_assert_held(&zone->lock);
+	lockdep_assert_held(&zone->_lock);
 
 	if (is_migrate_isolate(migratetype))
 		return;
@@ -2473,7 +2473,7 @@ enum rmqueue_mode {
 
 /*
  * Do the hard work of removing an element from the buddy allocator.
- * Call me with the zone->lock already held.
+ * Call me with the zone->_lock already held.
  */
 static __always_inline struct page *
 __rmqueue(struct zone *zone, unsigned int order, int migratetype,
@@ -2501,7 +2501,7 @@ __rmqueue(struct zone *zone, unsigned int order, int migratetype,
 	 * fallbacks modes with increasing levels of fragmentation risk.
 	 *
 	 * The fallback logic is expensive and rmqueue_bulk() calls in
-	 * a loop with the zone->lock held, meaning the freelists are
+	 * a loop with the zone->_lock held, meaning the freelists are
 	 * not subject to any outside changes. Remember in *mode where
 	 * we found pay dirt, to save us the search on the next call.
 	 */
@@ -3203,7 +3203,7 @@ void __putback_isolated_page(struct page *page, unsigned int order, int mt)
 	struct zone *zone = page_zone(page);
 
 	/* zone lock should be held when this function is called */
-	lockdep_assert_held(&zone->lock);
+	lockdep_assert_held(&zone->_lock);
 
 	/* Return isolated page to tail of freelist. */
 	__free_one_page(page, page_to_pfn(page), zone, order, mt,
@@ -7086,7 +7086,7 @@ int alloc_contig_frozen_range_noprof(unsigned long start, unsigned long end,
 	 * pages.  Because of this, we reserve the bigger range and
 	 * once this is done free the pages we are not interested in.
 	 *
-	 * We don't have to hold zone->lock here because the pages are
+	 * We don't have to hold zone->_lock here because the pages are
 	 * isolated thus they won't get removed from buddy.
 	 */
 	outer_start = find_large_buddy(start);
@@ -7655,7 +7655,7 @@ void accept_page(struct page *page)
 		return;
 	}
 
-	/* Unlocks zone->lock */
+	/* Unlocks zone->_lock */
 	__accept_page(zone, &flags, page);
 }
 
@@ -7672,7 +7672,7 @@ static bool try_to_accept_memory_one(struct zone *zone)
 		return false;
 	}
 
-	/* Unlocks zone->lock */
+	/* Unlocks zone->_lock */
 	__accept_page(zone, &flags, page);
 
 	return true;
@@ -7813,7 +7813,7 @@ struct page *alloc_frozen_pages_nolock_noprof(gfp_t gfp_flags, int nid, unsigned
 
 	/*
 	 * Best effort allocation from percpu free list.
-	 * If it's empty attempt to spin_trylock zone->lock.
+	 * If it's empty attempt to spin_trylock zone->_lock.
 	 */
 	page = get_page_from_freelist(alloc_gfp, order, alloc_flags, &ac);
 
mm/page_isolation.c +2 -2
index 91a0836bf1b7..cf731370e7a7 100644
@@ -212,7 +212,7 @@ static int set_migratetype_isolate(struct page *page, enum pb_isolate_mode mode,
 	zone_unlock_irqrestore(zone, flags);
 	if (mode == PB_ISOLATE_MODE_MEM_OFFLINE) {
 		/*
-		 * printk() with zone->lock held will likely trigger a
+		 * printk() with zone->_lock held will likely trigger a
 		 * lockdep splat, so defer it here.
 		 */
 		dump_page(unmovable, "unmovable page");
@@ -553,7 +553,7 @@ void undo_isolate_page_range(unsigned long start_pfn, unsigned long end_pfn)
 /*
  * Test all pages in the range is free(means isolated) or not.
  * all pages in [start_pfn...end_pfn) must be in the same zone.
- * zone->lock must be held before call this.
+ * zone->_lock must be held before call this.
  *
  * Returns the last tested pfn.
  */
mm/page_owner.c +1 -1
index 8178e0be557f..54a4ba63b14f 100644
@@ -799,7 +799,7 @@ static void init_pages_in_zone(struct zone *zone)
 				continue;
 
 			/*
-			 * To avoid having to grab zone->lock, be a little
+			 * To avoid having to grab zone->_lock, be a little
 			 * careful when reading buddy page order. The only
 			 * danger is that we skip too much and potentially miss
 			 * some early allocated pages, which is better than
Signature · 1 line
2.47.3
maintainer Acked-by on v4 4/5
#17 lore raw

Re: [PATCH v4 4/5] mm: rename zone->lock to zone->_lock

Quoted 6 lines · Dmitry Ilvokhin

This intentionally breaks direct users of zone->lock at compile time so all call sites are converted to the zone lock wrappers. Without the rename, present and future out-of-tree code could continue using spin_lock(&zone->lock) and bypass the wrappers and tracing infrastructure.

Quoted 7 lines

No functional change intended.

---

Signature · 3 lines
Cheers,

David
reviewer Acked-by on v4 4/5
#18 lore raw

Re: [PATCH v4 4/5] mm: rename zone->lock to zone->_lock

In reply to Dmitry Ilvokhin
Quoted 12 lines · Dmitry Ilvokhin

This intentionally breaks direct users of zone->lock at compile time so all call sites are converted to the zone lock wrappers. Without the rename, present and future out-of-tree code could continue using spin_lock(&zone->lock) and bypass the wrappers and tracing infrastructure.

No functional change intended.

Quoted 10 lines
---
 include/linux/mmzone.h      |  7 +++++--
 include/linux/mmzone_lock.h | 12 ++++++------
 mm/compaction.c             |  4 ++--
 mm/internal.h               |  2 +-
 mm/page_alloc.c             | 16 ++++++++--------
 mm/page_isolation.c         |  4 ++--
 mm/page_owner.c             |  2 +-
 7 files changed, 25 insertions(+), 22 deletions(-)

Best Regards,
Yan, Zi

maintainer Reviewed-by on v4 4/5
#19 lore raw

Re: [PATCH v4 4/5] mm: rename zone->lock to zone->_lock

In reply to Dmitry Ilvokhin
Quoted 6 lines · Dmitry Ilvokhin

This intentionally breaks direct users of zone->lock at compile time so all call sites are converted to the zone lock wrappers. Without the rename, present and future out-of-tree code could continue using spin_lock(&zone->lock) and bypass the wrappers and tracing infrastructure.

Quoted 6 lines

No functional change intended.

I see some more instances of 'zone->lock' in comments in include/linux/mmzone.h and under Documentation/ but otherwise LGTM.

maintainer
#20 lore raw

Re: [PATCH v4 4/5] mm: rename zone->lock to zone->_lock

Quoted 9 lines · On Mon, 2 Mar 2026 15:10:03 +0100 "Vlastimil Babka (SUSE)" <[email protected]>…
Quoted 8 lines · Dmitry Ilvokhin

This intentionally breaks direct users of zone->lock at compile time so all call sites are converted to the zone lock wrappers. Without the rename, present and future out-of-tree code could continue using spin_lock(&zone->lock) and bypass the wrappers and tracing infrastructure.

No functional change intended.

Quoted 8 lines
Quoted 4 lines

I see some more instances of 'zone->lock' in comments in include/linux/mmzone.h and under Documentation/ but otherwise LGTM.

I fixed (most of) that in the previous version but my fix was lost.

 include/linux/mmzone.h |   10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)
include/linux/mmzone.h renamed from include/linux/mmzone.h~mm-rename-zone-lock-to-zone-_lock-fix +5 -5
@@ -1037,12 +1037,12 @@ struct zone {
 	 * Locking rules:
 	 *
 	 * zone_start_pfn and spanned_pages are protected by span_seqlock.
-	 * It is a seqlock because it has to be read outside of zone->lock,
+	 * It is a seqlock because it has to be read outside of zone_lock,
 	 * and it is done in the main allocator path.  But, it is written
 	 * quite infrequently.
 	 *
-	 * The span_seq lock is declared along with zone->lock because it is
-	 * frequently read in proximity to zone->lock.  It's good to
+	 * The span_seq lock is declared along with zone_lock because it is
+	 * frequently read in proximity to zone_lock.  It's good to
 	 * give them a chance of being in the same cacheline.
 	 *
 	 * Write access to present_pages at runtime should be protected by
@@ -1065,7 +1065,7 @@ struct zone {
 	/*
 	 * Number of isolated pageblock. It is used to solve incorrect
 	 * freepage counting problem due to racy retrieving migratetype
-	 * of pageblock. Protected by zone->lock.
+	 * of pageblock. Protected by zone_lock.
 	 */
 	unsigned long		nr_isolate_pageblock;
 #endif
@@ -1502,7 +1502,7 @@ typedef struct pglist_data {
 	 * manipulate node_size_lock without checking for CONFIG_MEMORY_HOTPLUG
 	 * or CONFIG_DEFERRED_STRUCT_PAGE_INIT.
 	 *
-	 * Nests above zone->lock and zone->span_seqlock
+	 * Nests above zone_lock and zone->span_seqlock
 	 */
 	spinlock_t node_size_lock;
 #endif

_

#21 lore raw

Re: [PATCH v4 4/5] mm: rename zone->lock to zone->_lock

Quoted 16 lines · Andrew Morton
Quoted 14 lines · On Mon, 2 Mar 2026 15:10:03 +0100 "Vlastimil Babka (SUSE)" <[email protected]>…
Quoted 12 lines · Dmitry Ilvokhin

This intentionally breaks direct users of zone->lock at compile time so all call sites are converted to the zone lock wrappers. Without the rename, present and future out-of-tree code could continue using spin_lock(&zone->lock) and bypass the wrappers and tracing infrastructure.

No functional change intended.

Quoted 5 lines
Quoted 3 lines

I see some more instances of 'zone->lock' in comments in include/linux/mmzone.h and under Documentation/ but otherwise LGTM.

I fixed (most of) that in the previous version but my fix was lost.

Thanks for the fixups, Andrew.

I still see a few 'zone->lock' references in Documentation remain on mm-new. This patch cleans them up, as noted by Vlastimil.

I'm happy to adjust this patch if anything else needs attention.

From 9142d5a8b60038fa424a6033253960682e5a51f4 Mon Sep 17 00:00:00 2001
From: Dmitry Ilvokhin <[email protected]>
Date: Tue, 3 Mar 2026 06:13:13 -0800
Subject: [PATCH] mm: fix remaining zone->lock references

---
 Documentation/mm/physical_memory.rst | 4 ++--
 Documentation/trace/events-kmem.rst  | 8 ++++----
 2 files changed, 6 insertions(+), 6 deletions(-)
Documentation/mm/physical_memory.rst +2 -2
index b76183545e5b..e344f93515b6 100644
@@ -500,11 +500,11 @@ General
 ``nr_isolate_pageblock``
   Number of isolated pageblocks. It is used to solve incorrect freepage counting
   problem due to racy retrieving migratetype of pageblock. Protected by
-  ``zone->lock``. Defined only when ``CONFIG_MEMORY_ISOLATION`` is enabled.
+  ``zone_lock``. Defined only when ``CONFIG_MEMORY_ISOLATION`` is enabled.
 
 ``span_seqlock``
   The seqlock to protect ``zone_start_pfn`` and ``spanned_pages``. It is a
-  seqlock because it has to be read outside of ``zone->lock``, and it is done in
+  seqlock because it has to be read outside of ``zone_lock``, and it is done in
   the main allocator path. However, the seqlock is written quite infrequently.
   Defined only when ``CONFIG_MEMORY_HOTPLUG`` is enabled.
 
Documentation/trace/events-kmem.rst +4 -4
index 68fa75247488..3c20a972de27 100644
@@ -57,7 +57,7 @@ the per-CPU allocator (high performance) or the buddy allocator.
 
 If pages are allocated directly from the buddy allocator, the
 mm_page_alloc_zone_locked event is triggered. This event is important as high
-amounts of activity imply high activity on the zone->lock. Taking this lock
+amounts of activity imply high activity on the zone_lock. Taking this lock
 impairs performance by disabling interrupts, dirtying cache lines between
 CPUs and serialising many CPUs.
 
@@ -79,11 +79,11 @@ contention on the lruvec->lru_lock.
   mm_page_pcpu_drain		page=%p pfn=%lu order=%d cpu=%d migratetype=%d
 
 In front of the page allocator is a per-cpu page allocator. It exists only
-for order-0 pages, reduces contention on the zone->lock and reduces the
+for order-0 pages, reduces contention on the zone_lock and reduces the
 amount of writing on struct page.
 
 When a per-CPU list is empty or pages of the wrong type are allocated,
-the zone->lock will be taken once and the per-CPU list refilled. The event
+the zone_lock will be taken once and the per-CPU list refilled. The event
 triggered is mm_page_alloc_zone_locked for each page allocated with the
 event indicating whether it is for a percpu_refill or not.
 
@@ -92,7 +92,7 @@ which triggers a mm_page_pcpu_drain event.
 
 The individual nature of the events is so that pages can be tracked
 between allocation and freeing. A number of drain or refill pages that occur
-consecutively imply the zone->lock being taken once. Large amounts of per-CPU
+consecutively imply the zone_lock being taken once. Large amounts of per-CPU
 refills and drains could imply an imbalance between CPUs where too much work
 is being concentrated in one place. It could also indicate that the per-CPU
 lists should be a larger size. Finally, large amounts of refills on one CPU
Signature · 1 line
2.47.3
#22 lore raw

Re: [PATCH v4 4/5] mm: rename zone->lock to zone->_lock

Quoted 46 lines · On Tue, 3 Mar 2026 14:25:55 +0000 Dmitry Ilvokhin <[email protected]> wrote:
Quoted 16 lines · Andrew Morton
Quoted 14 lines · On Mon, 2 Mar 2026 15:10:03 +0100 "Vlastimil Babka (SUSE)" <[email protected]>…
Quoted 12 lines · Dmitry Ilvokhin

This intentionally breaks direct users of zone->lock at compile time so all call sites are converted to the zone lock wrappers. Without the rename, present and future out-of-tree code could continue using spin_lock(&zone->lock) and bypass the wrappers and tracing infrastructure.

No functional change intended.

Quoted 5 lines
Quoted 3 lines

I see some more instances of 'zone->lock' in comments in include/linux/mmzone.h and under Documentation/ but otherwise LGTM.

I fixed (most of) that in the previous version but my fix was lost.

Thanks for the fixups, Andrew.

I still see a few 'zone->lock' references in Documentation remain on mm-new. This patch cleans them up, as noted by Vlastimil.

I'm happy to adjust this patch if anything else needs attention.

From 9142d5a8b60038fa424a6033253960682e5a51f4 Mon Sep 17 00:00:00 2001
From: Dmitry Ilvokhin <[email protected]>
Date: Tue, 3 Mar 2026 06:13:13 -0800
Subject: [PATCH] mm: fix remaining zone->lock references

---
 Documentation/mm/physical_memory.rst | 4 ++--
 Documentation/trace/events-kmem.rst  | 8 ++++----
 2 files changed, 6 insertions(+), 6 deletions(-)
Documentation/mm/physical_memory.rst +0 -0
index b76183545e5b..e344f93515b6 100644

Documentation/mm/physical_memory.rst General · line 503

Quoted 5 lines
Documentation/mm/physical_memory.rst +1 -1
@@ -500,11 +500,11 @@ General
 ``nr_isolate_pageblock``
   Number of isolated pageblocks. It is used to solve incorrect freepage counting
   problem due to racy retrieving migratetype of pageblock. Protected by
-  ``zone->lock``. Defined only when ``CONFIG_MEMORY_ISOLATION`` is enabled.
+  ``zone_lock``. Defined only when ``CONFIG_MEMORY_ISOLATION`` is enabled.

Dmitry's original patch [1] was doing 's/zone->lock/zone->_lock/', which aligns to my expectation. But this patch is doing 's/zone->lock/zone_lock/'. Same for the rest of this patch.

I was initially thinking this is just a mistake, but I also found Andrew is doing same change [2], so I'm bit confused. Is this an intentional change?

[1] https://lore.kernel.org/d61500c5784c64e971f4d328c57639303c475f81.1772206930.git.d@ilvokhin.com
[2] https://lore.kernel.org/[email protected]

Thanks,
SJ

[...]

#23 lore raw

Re: [PATCH v4 4/5] mm: rename zone->lock to zone->_lock

Quoted 58 lines · SeongJae Park
Quoted 46 lines · On Tue, 3 Mar 2026 14:25:55 +0000 Dmitry Ilvokhin <[email protected]> wrote:
Quoted 16 lines · Andrew Morton
Quoted 14 lines · On Mon, 2 Mar 2026 15:10:03 +0100 "Vlastimil Babka (SUSE)" <[email protected]>…
Quoted 12 lines · Dmitry Ilvokhin

This intentionally breaks direct users of zone->lock at compile time so all call sites are converted to the zone lock wrappers. Without the rename, present and future out-of-tree code could continue using spin_lock(&zone->lock) and bypass the wrappers and tracing infrastructure.

No functional change intended.

Quoted 5 lines
Quoted 3 lines

I see some more instances of 'zone->lock' in comments in include/linux/mmzone.h and under Documentation/ but otherwise LGTM.

I fixed (most of) that in the previous version but my fix was lost.

Thanks for the fixups, Andrew.

I still see a few 'zone->lock' references in Documentation remain on mm-new. This patch cleans them up, as noted by Vlastimil.

I'm happy to adjust this patch if anything else needs attention.

From 9142d5a8b60038fa424a6033253960682e5a51f4 Mon Sep 17 00:00:00 2001
From: Dmitry Ilvokhin <[email protected]>
Date: Tue, 3 Mar 2026 06:13:13 -0800
Subject: [PATCH] mm: fix remaining zone->lock references

---
 Documentation/mm/physical_memory.rst | 4 ++--
 Documentation/trace/events-kmem.rst  | 8 ++++----
 2 files changed, 6 insertions(+), 6 deletions(-)
Documentation/mm/physical_memory.rst +0 -0
index b76183545e5b..e344f93515b6 100644

Documentation/mm/physical_memory.rst General · line 503

Quoted 5 lines
Documentation/mm/physical_memory.rst +1 -1
@@ -500,11 +500,11 @@ General
 ``nr_isolate_pageblock``
   Number of isolated pageblocks. It is used to solve incorrect freepage counting
   problem due to racy retrieving migratetype of pageblock. Protected by
-  ``zone->lock``. Defined only when ``CONFIG_MEMORY_ISOLATION`` is enabled.
+  ``zone_lock``. Defined only when ``CONFIG_MEMORY_ISOLATION`` is enabled.

Dmitry's original patch [1] was doing 's/zone->lock/zone->_lock/', which aligns to my expectation. But this patch is doing 's/zone->lock/zone_lock/'. Same for the rest of this patch.

Quoted 6 lines

I was initially thinking this is just a mistake, but I also found Andrew is doing same change [2], so I'm bit confused. Is this an intentional change?

[1] https://lore.kernel.org/d61500c5784c64e971f4d328c57639303c475f81.1772206930.git.d@ilvokhin.com
[2] https://lore.kernel.org/[email protected]

Good catch, thanks for pointing this out, SJ.

Originally the mechanical rename was indeed zone->lock -> zone->_lock. However, in Documentation I intentionally switched references to zone_lock instead of zone->_lock. The reasoning is that _lock is now an internal implementation detail, and direct access is discouraged. The intended interface is via the zone_lock_*() / zone_unlock_*() wrappers, so referencing zone_lock in documentation felt more appropriate than mentioning the private struct field (zone->_lock).

That said, I agree this creates inconsistency with the mechanical rename, and I'm happy to adjust either way: either consistently refer to the wrapper API, or keep documentation aligned with zone->_lock.

I slightly prefer referring to the wrapper API, but don't have a strong preference as long as we're consistent.

Quoted 5 lines

Thanks,
SJ

[...]

#24 lore raw

Re: [PATCH v4 4/5] mm: rename zone->lock to zone->_lock

Quoted 68 lines · On Wed, 4 Mar 2026 13:01:45 +0000 Dmitry Ilvokhin <[email protected]> wrote:
Quoted 58 lines · SeongJae Park
Quoted 46 lines · On Tue, 3 Mar 2026 14:25:55 +0000 Dmitry Ilvokhin <[email protected]> wrote:
Quoted 16 lines · Andrew Morton
Quoted 14 lines · On Mon, 2 Mar 2026 15:10:03 +0100 "Vlastimil Babka (SUSE)" <[email protected]>…
Quoted 12 lines · Dmitry Ilvokhin

This intentionally breaks direct users of zone->lock at compile time so all call sites are converted to the zone lock wrappers. Without the rename, present and future out-of-tree code could continue using spin_lock(&zone->lock) and bypass the wrappers and tracing infrastructure.

No functional change intended.

Quoted 5 lines
Quoted 3 lines

I see some more instances of 'zone->lock' in comments in include/linux/mmzone.h and under Documentation/ but otherwise LGTM.

I fixed (most of) that in the previous version but my fix was lost.

Thanks for the fixups, Andrew.

I still see a few 'zone->lock' references in Documentation remain on mm-new. This patch cleans them up, as noted by Vlastimil.

I'm happy to adjust this patch if anything else needs attention.

From 9142d5a8b60038fa424a6033253960682e5a51f4 Mon Sep 17 00:00:00 2001
From: Dmitry Ilvokhin <[email protected]>
Date: Tue, 3 Mar 2026 06:13:13 -0800
Subject: [PATCH] mm: fix remaining zone->lock references

---
 Documentation/mm/physical_memory.rst | 4 ++--
 Documentation/trace/events-kmem.rst  | 8 ++++----
 2 files changed, 6 insertions(+), 6 deletions(-)
Documentation/mm/physical_memory.rst +0 -0
index b76183545e5b..e344f93515b6 100644

Documentation/mm/physical_memory.rst General · line 503

Quoted 5 lines
Documentation/mm/physical_memory.rst +1 -1
@@ -500,11 +500,11 @@ General
 ``nr_isolate_pageblock``
   Number of isolated pageblocks. It is used to solve incorrect freepage counting
   problem due to racy retrieving migratetype of pageblock. Protected by
-  ``zone->lock``. Defined only when ``CONFIG_MEMORY_ISOLATION`` is enabled.
+  ``zone_lock``. Defined only when ``CONFIG_MEMORY_ISOLATION`` is enabled.

Dmitry's original patch [1] was doing 's/zone->lock/zone->_lock/', which aligns to my expectation. But this patch is doing 's/zone->lock/zone_lock/'. Same for the rest of this patch.

Quoted 6 lines

I was initially thinking this is just a mistake, but I also found Andrew is doing same change [2], so I'm bit confused. Is this an intentional change?

[1] https://lore.kernel.org/d61500c5784c64e971f4d328c57639303c475f81.1772206930.git.d@ilvokhin.com
[2] https://lore.kernel.org/[email protected]

Good catch, thanks for pointing this out, SJ.

Quoted 7 lines

Originally the mechanical rename was indeed zone->lock -> zone->_lock. However, in Documentation I intentionally switched references to zone_lock instead of zone->_lock. The reasoning is that _lock is now an internal implementation detail, and direct access is discouraged. The intended interface is via the zone_lock_*() / zone_unlock_*() wrappers, so referencing zone_lock in documentation felt more appropriate than mentioning the private struct field (zone->_lock).

Thank you for this nice and kind clarification, Dmitry! I agree mentioning zone_[un]lock_*() helpers instead of the hidden member (zone->_lock) can be better.

But, I'm concerned if people like me might not aware the intention under 'zone_lock'. If there is a well-known convention that allows people to know it is for 'zone_[un]lock_*()' helpers, making it more clear would be nice, in my humble opinion. If there is such a convention but I'm just missing it, please ignore. If I'm not, for eaxmaple,

"protected by ``zone->lock``" could be re-wrote to "protected by ``zone_[un]lock_*()`` locking helpers" or, "protected by zone lock helper functions (``zone_[un]lock_*()``)" ?

Quoted 7 lines

That said, I agree this creates inconsistency with the mechanical rename, and I'm happy to adjust either way: either consistently refer to the wrapper API, or keep documentation aligned with zone->_lock.

I slightly prefer referring to the wrapper API, but don't have a strong preference as long as we're consistent.

I also think both approaches are good. But for the wrapper approach, I think giving more contexts rather than just ``zone_lock`` to readers would be nice.

Thanks,
SJ

[...]

#25 lore raw

Re: [PATCH v4 4/5] mm: rename zone->lock to zone->_lock

Quoted 88 lines · On Wed, 4 Mar 2026 07:13:34 -0800 SeongJae Park <[email protected]> wrote:
Quoted 68 lines · On Wed, 4 Mar 2026 13:01:45 +0000 Dmitry Ilvokhin <[email protected]> wrote:
Quoted 58 lines · SeongJae Park
Quoted 46 lines · On Tue, 3 Mar 2026 14:25:55 +0000 Dmitry Ilvokhin <[email protected]> wrote:
Quoted 16 lines · Andrew Morton
Quoted 14 lines · On Mon, 2 Mar 2026 15:10:03 +0100 "Vlastimil Babka (SUSE)" <[email protected]>…
Quoted 12 lines · Dmitry Ilvokhin

This intentionally breaks direct users of zone->lock at compile time so all call sites are converted to the zone lock wrappers. Without the rename, present and future out-of-tree code could continue using spin_lock(&zone->lock) and bypass the wrappers and tracing infrastructure.

No functional change intended.

Quoted 5 lines
Quoted 3 lines

I see some more instances of 'zone->lock' in comments in include/linux/mmzone.h and under Documentation/ but otherwise LGTM.

I fixed (most of) that in the previous version but my fix was lost.

Thanks for the fixups, Andrew.

I still see a few 'zone->lock' references in Documentation remain on mm-new. This patch cleans them up, as noted by Vlastimil.

I'm happy to adjust this patch if anything else needs attention.

From 9142d5a8b60038fa424a6033253960682e5a51f4 Mon Sep 17 00:00:00 2001
From: Dmitry Ilvokhin <[email protected]>
Date: Tue, 3 Mar 2026 06:13:13 -0800
Subject: [PATCH] mm: fix remaining zone->lock references

---
 Documentation/mm/physical_memory.rst | 4 ++--
 Documentation/trace/events-kmem.rst  | 8 ++++----
 2 files changed, 6 insertions(+), 6 deletions(-)
Documentation/mm/physical_memory.rst +0 -0
index b76183545e5b..e344f93515b6 100644

Documentation/mm/physical_memory.rst General · line 503

Quoted 5 lines
Documentation/mm/physical_memory.rst +1 -1
@@ -500,11 +500,11 @@ General
 ``nr_isolate_pageblock``
   Number of isolated pageblocks. It is used to solve incorrect freepage counting
   problem due to racy retrieving migratetype of pageblock. Protected by
-  ``zone->lock``. Defined only when ``CONFIG_MEMORY_ISOLATION`` is enabled.
+  ``zone_lock``. Defined only when ``CONFIG_MEMORY_ISOLATION`` is enabled.

Dmitry's original patch [1] was doing 's/zone->lock/zone->_lock/', which aligns to my expectation. But this patch is doing 's/zone->lock/zone_lock/'. Same for the rest of this patch.

Quoted 6 lines

I was initially thinking this is just a mistake, but I also found Andrew is doing same change [2], so I'm bit confused. Is this an intentional change?

[1] https://lore.kernel.org/d61500c5784c64e971f4d328c57639303c475f81.1772206930.git.d@ilvokhin.com
[2] https://lore.kernel.org/[email protected]

Good catch, thanks for pointing this out, SJ.

Quoted 7 lines

Originally the mechanical rename was indeed zone->lock -> zone->_lock. However, in Documentation I intentionally switched references to zone_lock instead of zone->_lock. The reasoning is that _lock is now an internal implementation detail, and direct access is discouraged. The intended interface is via the zone_lock_*() / zone_unlock_*() wrappers, so referencing zone_lock in documentation felt more appropriate than mentioning the private struct field (zone->_lock).

Thank you for this nice and kind clarification, Dmitry! I agree mentioning zone_[un]lock_*() helpers instead of the hidden member (zone->_lock) can be better.

But, I'm concerned if people like me might not aware the intention under 'zone_lock'. If there is a well-known convention that allows people to know it is for 'zone_[un]lock_*()' helpers, making it more clear would be nice, in my humble opinion. If there is such a convention but I'm just missing it, please ignore. If I'm not, for eaxmaple,

Quoted 3 lines

"protected by ``zone->lock``" could be re-wrote to "protected by ``zone_[un]lock_*()`` locking helpers" or, "protected by zone lock helper functions (``zone_[un]lock_*()``)" ?

Maybe too verbose and people who not used to regex might be confused. Mentioning mmzone_lock.h might be better? E.g.,

protected by functions in mmzone_lock.h

Quoted 11 lines
Quoted 8 lines

That said, I agree this creates inconsistency with the mechanical rename, and I'm happy to adjust either way: either consistently refer to the wrapper API, or keep documentation aligned with zone->_lock.

I slightly prefer referring to the wrapper API, but don't have a strong preference as long as we're consistent.

I also think both approaches are good. But for the wrapper approach, I think giving more contexts rather than just ``zone_lock`` to readers would be nice.

Thanks,
SJ

[...]

#26 lore raw

Re: [PATCH v4 4/5] mm: rename zone->lock to zone->_lock

In reply to SeongJae Park
Quoted 97 lines · SeongJae Park
Quoted 68 lines · On Wed, 4 Mar 2026 13:01:45 +0000 Dmitry Ilvokhin <[email protected]> wrote:
Quoted 58 lines · SeongJae Park
Quoted 46 lines · On Tue, 3 Mar 2026 14:25:55 +0000 Dmitry Ilvokhin <[email protected]> wrote:
Quoted 16 lines · Andrew Morton
Quoted 14 lines · On Mon, 2 Mar 2026 15:10:03 +0100 "Vlastimil Babka (SUSE)" <[email protected]>…
Quoted 12 lines · Dmitry Ilvokhin

This intentionally breaks direct users of zone->lock at compile time so all call sites are converted to the zone lock wrappers. Without the rename, present and future out-of-tree code could continue using spin_lock(&zone->lock) and bypass the wrappers and tracing infrastructure.

No functional change intended.

Quoted 5 lines
Quoted 3 lines

I see some more instances of 'zone->lock' in comments in include/linux/mmzone.h and under Documentation/ but otherwise LGTM.

I fixed (most of) that in the previous version but my fix was lost.

Thanks for the fixups, Andrew.

I still see a few 'zone->lock' references in Documentation remain on mm-new. This patch cleans them up, as noted by Vlastimil.

I'm happy to adjust this patch if anything else needs attention.

From 9142d5a8b60038fa424a6033253960682e5a51f4 Mon Sep 17 00:00:00 2001
From: Dmitry Ilvokhin <[email protected]>
Date: Tue, 3 Mar 2026 06:13:13 -0800
Subject: [PATCH] mm: fix remaining zone->lock references

---
 Documentation/mm/physical_memory.rst | 4 ++--
 Documentation/trace/events-kmem.rst  | 8 ++++----
 2 files changed, 6 insertions(+), 6 deletions(-)
Documentation/mm/physical_memory.rst +0 -0
index b76183545e5b..e344f93515b6 100644

Documentation/mm/physical_memory.rst General · line 503

Quoted 5 lines
Documentation/mm/physical_memory.rst +1 -1
@@ -500,11 +500,11 @@ General
 ``nr_isolate_pageblock``
   Number of isolated pageblocks. It is used to solve incorrect freepage counting
   problem due to racy retrieving migratetype of pageblock. Protected by
-  ``zone->lock``. Defined only when ``CONFIG_MEMORY_ISOLATION`` is enabled.
+  ``zone_lock``. Defined only when ``CONFIG_MEMORY_ISOLATION`` is enabled.

Dmitry's original patch [1] was doing 's/zone->lock/zone->_lock/', which aligns to my expectation. But this patch is doing 's/zone->lock/zone_lock/'. Same for the rest of this patch.

Quoted 6 lines

I was initially thinking this is just a mistake, but I also found Andrew is doing same change [2], so I'm bit confused. Is this an intentional change?

[1] https://lore.kernel.org/d61500c5784c64e971f4d328c57639303c475f81.1772206930.git.d@ilvokhin.com
[2] https://lore.kernel.org/[email protected]

Good catch, thanks for pointing this out, SJ.

Quoted 7 lines

Originally the mechanical rename was indeed zone->lock -> zone->_lock. However, in Documentation I intentionally switched references to zone_lock instead of zone->_lock. The reasoning is that _lock is now an internal implementation detail, and direct access is discouraged. The intended interface is via the zone_lock_*() / zone_unlock_*() wrappers, so referencing zone_lock in documentation felt more appropriate than mentioning the private struct field (zone->_lock).

Thank you for this nice and kind clarification, Dmitry! I agree mentioning zone_[un]lock_*() helpers instead of the hidden member (zone->_lock) can be better.

But, I'm concerned if people like me might not aware the intention under 'zone_lock'. If there is a well-known convention that allows people to know it is for 'zone_[un]lock_*()' helpers, making it more clear would be nice, in my humble opinion. If there is such a convention but I'm just missing it, please ignore. If I'm not, for eaxmaple,

"protected by ``zone->lock``" could be re-wrote to "protected by ``zone_[un]lock_*()`` locking helpers" or, "protected by zone lock helper functions (``zone_[un]lock_*()``)" ?

Quoted 5 lines

That said, I agree this creates inconsistency with the mechanical rename, and I'm happy to adjust either way: either consistently refer to the wrapper API, or keep documentation aligned with zone->_lock.

Quoted 5 lines
Quoted 2 lines

I slightly prefer referring to the wrapper API, but don't have a strong preference as long as we're consistent.

I also think both approaches are good. But for the wrapper approach, I think giving more contexts rather than just ``zone_lock`` to readers would be nice.

Grep tells me that we also have comments mentioning simply "zone lock", btw. And it's also a term used often in informal conversations. Maybe we could just standardize on that in comments/documentations as it's easier to read. Discovering that the field is called _lock and that wrappers should be used, is hopefully not that difficult.

#27 lore raw

Re: [PATCH v4 4/5] mm: rename zone->lock to zone->_lock

Quoted 101 lines · On Thu, 5 Mar 2026 10:27:07 +0100 "Vlastimil Babka (SUSE)" <[email protected]>…
Quoted 100 lines · SeongJae Park
Quoted 68 lines · On Wed, 4 Mar 2026 13:01:45 +0000 Dmitry Ilvokhin <[email protected]> wrote:
Quoted 58 lines · SeongJae Park
Quoted 46 lines · On Tue, 3 Mar 2026 14:25:55 +0000 Dmitry Ilvokhin <[email protected]> wrote:
Quoted 16 lines · Andrew Morton
Quoted 14 lines · On Mon, 2 Mar 2026 15:10:03 +0100 "Vlastimil Babka (SUSE)" <[email protected]>…
Quoted 12 lines · Dmitry Ilvokhin

This intentionally breaks direct users of zone->lock at compile time so all call sites are converted to the zone lock wrappers. Without the rename, present and future out-of-tree code could continue using spin_lock(&zone->lock) and bypass the wrappers and tracing infrastructure.

No functional change intended.

Quoted 5 lines
Quoted 3 lines

I see some more instances of 'zone->lock' in comments in include/linux/mmzone.h and under Documentation/ but otherwise LGTM.

I fixed (most of) that in the previous version but my fix was lost.

Thanks for the fixups, Andrew.

I still see a few 'zone->lock' references in Documentation remain on mm-new. This patch cleans them up, as noted by Vlastimil.

I'm happy to adjust this patch if anything else needs attention.

From 9142d5a8b60038fa424a6033253960682e5a51f4 Mon Sep 17 00:00:00 2001
From: Dmitry Ilvokhin <[email protected]>
Date: Tue, 3 Mar 2026 06:13:13 -0800
Subject: [PATCH] mm: fix remaining zone->lock references

---
 Documentation/mm/physical_memory.rst | 4 ++--
 Documentation/trace/events-kmem.rst  | 8 ++++----
 2 files changed, 6 insertions(+), 6 deletions(-)
Documentation/mm/physical_memory.rst +0 -0
index b76183545e5b..e344f93515b6 100644

Documentation/mm/physical_memory.rst General · line 503

Quoted 5 lines
Documentation/mm/physical_memory.rst +1 -1
@@ -500,11 +500,11 @@ General
 ``nr_isolate_pageblock``
   Number of isolated pageblocks. It is used to solve incorrect freepage counting
   problem due to racy retrieving migratetype of pageblock. Protected by
-  ``zone->lock``. Defined only when ``CONFIG_MEMORY_ISOLATION`` is enabled.
+  ``zone_lock``. Defined only when ``CONFIG_MEMORY_ISOLATION`` is enabled.

Dmitry's original patch [1] was doing 's/zone->lock/zone->_lock/', which aligns to my expectation. But this patch is doing 's/zone->lock/zone_lock/'. Same for the rest of this patch.

Quoted 6 lines

I was initially thinking this is just a mistake, but I also found Andrew is doing same change [2], so I'm bit confused. Is this an intentional change?

[1] https://lore.kernel.org/d61500c5784c64e971f4d328c57639303c475f81.1772206930.git.d@ilvokhin.com
[2] https://lore.kernel.org/[email protected]

Good catch, thanks for pointing this out, SJ.

Quoted 7 lines

Originally the mechanical rename was indeed zone->lock -> zone->_lock. However, in Documentation I intentionally switched references to zone_lock instead of zone->_lock. The reasoning is that _lock is now an internal implementation detail, and direct access is discouraged. The intended interface is via the zone_lock_*() / zone_unlock_*() wrappers, so referencing zone_lock in documentation felt more appropriate than mentioning the private struct field (zone->_lock).

Thank you for this nice and kind clarification, Dmitry! I agree mentioning zone_[un]lock_*() helpers instead of the hidden member (zone->_lock) can be better.

But, I'm concerned if people like me might not aware the intention under 'zone_lock'. If there is a well-known convention that allows people to know it is for 'zone_[un]lock_*()' helpers, making it more clear would be nice, in my humble opinion. If there is such a convention but I'm just missing it, please ignore. If I'm not, for eaxmaple,

"protected by ``zone->lock``" could be re-wrote to "protected by ``zone_[un]lock_*()`` locking helpers" or, "protected by zone lock helper functions (``zone_[un]lock_*()``)" ?

Quoted 7 lines

That said, I agree this creates inconsistency with the mechanical rename, and I'm happy to adjust either way: either consistently refer to the wrapper API, or keep documentation aligned with zone->_lock.

I slightly prefer referring to the wrapper API, but don't have a strong preference as long as we're consistent.

Quoted 8 lines
Quoted 2 lines

I also think both approaches are good. But for the wrapper approach, I think giving more contexts rather than just ``zone_lock`` to readers would be nice.

Grep tells me that we also have comments mentioning simply "zone lock", btw. And it's also a term used often in informal conversations. Maybe we could just standardize on that in comments/documentations as it's easier to read. Discovering that the field is called _lock and that wrappers should be used, is hopefully not that difficult.

Sounds good, that also works for me.

Thanks,
SJ

#28 lore raw

Re: [PATCH v4 4/5] mm: rename zone->lock to zone->_lock

In reply to Vlastimil Babka (SUSE)
Quoted 101 lines · Vlastimil Babka (SUSE)
Quoted 100 lines · SeongJae Park
Quoted 68 lines · On Wed, 4 Mar 2026 13:01:45 +0000 Dmitry Ilvokhin <[email protected]> wrote:
Quoted 58 lines · SeongJae Park
Quoted 46 lines · On Tue, 3 Mar 2026 14:25:55 +0000 Dmitry Ilvokhin <[email protected]> wrote:
Quoted 16 lines · Andrew Morton
Quoted 14 lines · On Mon, 2 Mar 2026 15:10:03 +0100 "Vlastimil Babka (SUSE)" <[email protected]>…
Quoted 12 lines · Dmitry Ilvokhin

This intentionally breaks direct users of zone->lock at compile time so all call sites are converted to the zone lock wrappers. Without the rename, present and future out-of-tree code could continue using spin_lock(&zone->lock) and bypass the wrappers and tracing infrastructure.

No functional change intended.

Quoted 5 lines
Quoted 3 lines

I see some more instances of 'zone->lock' in comments in include/linux/mmzone.h and under Documentation/ but otherwise LGTM.

I fixed (most of) that in the previous version but my fix was lost.

Thanks for the fixups, Andrew.

I still see a few 'zone->lock' references in Documentation remain on mm-new. This patch cleans them up, as noted by Vlastimil.

I'm happy to adjust this patch if anything else needs attention.

From 9142d5a8b60038fa424a6033253960682e5a51f4 Mon Sep 17 00:00:00 2001
From: Dmitry Ilvokhin <[email protected]>
Date: Tue, 3 Mar 2026 06:13:13 -0800
Subject: [PATCH] mm: fix remaining zone->lock references

---
 Documentation/mm/physical_memory.rst | 4 ++--
 Documentation/trace/events-kmem.rst  | 8 ++++----
 2 files changed, 6 insertions(+), 6 deletions(-)
Documentation/mm/physical_memory.rst +0 -0
index b76183545e5b..e344f93515b6 100644

Documentation/mm/physical_memory.rst General · line 503

Quoted 5 lines
Documentation/mm/physical_memory.rst +1 -1
@@ -500,11 +500,11 @@ General
 ``nr_isolate_pageblock``
   Number of isolated pageblocks. It is used to solve incorrect freepage counting
   problem due to racy retrieving migratetype of pageblock. Protected by
-  ``zone->lock``. Defined only when ``CONFIG_MEMORY_ISOLATION`` is enabled.
+  ``zone_lock``. Defined only when ``CONFIG_MEMORY_ISOLATION`` is enabled.

Dmitry's original patch [1] was doing 's/zone->lock/zone->_lock/', which aligns to my expectation. But this patch is doing 's/zone->lock/zone_lock/'. Same for the rest of this patch.

Quoted 6 lines

I was initially thinking this is just a mistake, but I also found Andrew is doing same change [2], so I'm bit confused. Is this an intentional change?

[1] https://lore.kernel.org/d61500c5784c64e971f4d328c57639303c475f81.1772206930.git.d@ilvokhin.com
[2] https://lore.kernel.org/[email protected]

Good catch, thanks for pointing this out, SJ.

Quoted 7 lines

Originally the mechanical rename was indeed zone->lock -> zone->_lock. However, in Documentation I intentionally switched references to zone_lock instead of zone->_lock. The reasoning is that _lock is now an internal implementation detail, and direct access is discouraged. The intended interface is via the zone_lock_*() / zone_unlock_*() wrappers, so referencing zone_lock in documentation felt more appropriate than mentioning the private struct field (zone->_lock).

Thank you for this nice and kind clarification, Dmitry! I agree mentioning zone_[un]lock_*() helpers instead of the hidden member (zone->_lock) can be better.

But, I'm concerned if people like me might not aware the intention under 'zone_lock'. If there is a well-known convention that allows people to know it is for 'zone_[un]lock_*()' helpers, making it more clear would be nice, in my humble opinion. If there is such a convention but I'm just missing it, please ignore. If I'm not, for eaxmaple,

"protected by ``zone->lock``" could be re-wrote to "protected by ``zone_[un]lock_*()`` locking helpers" or, "protected by zone lock helper functions (``zone_[un]lock_*()``)" ?

Quoted 7 lines

That said, I agree this creates inconsistency with the mechanical rename, and I'm happy to adjust either way: either consistently refer to the wrapper API, or keep documentation aligned with zone->_lock.

I slightly prefer referring to the wrapper API, but don't have a strong preference as long as we're consistent.

Quoted 8 lines
Quoted 2 lines

I also think both approaches are good. But for the wrapper approach, I think giving more contexts rather than just ``zone_lock`` to readers would be nice.

Grep tells me that we also have comments mentioning simply "zone lock", btw. And it's also a term used often in informal conversations. Maybe we could just standardize on that in comments/documentations as it's easier to read. Discovering that the field is called _lock and that wrappers should be used, is hopefully not that difficult.

Thanks for the suggestion, Vlastimil. That sounds reasonable to me as well. I'll update the comments and documentation to consistently use "zone lock".

#29 lore raw

Re: [PATCH v4 4/5] mm: rename zone->lock to zone->_lock

Quoted 111 lines · Dmitry Ilvokhin
Quoted 101 lines · Vlastimil Babka (SUSE)
Quoted 100 lines · SeongJae Park
Quoted 68 lines · On Wed, 4 Mar 2026 13:01:45 +0000 Dmitry Ilvokhin <[email protected]> wrote:
Quoted 58 lines · SeongJae Park
Quoted 46 lines · On Tue, 3 Mar 2026 14:25:55 +0000 Dmitry Ilvokhin <[email protected]> wrote:
Quoted 16 lines · Andrew Morton
Quoted 14 lines · On Mon, 2 Mar 2026 15:10:03 +0100 "Vlastimil Babka (SUSE)" <[email protected]>…
Quoted 12 lines · Dmitry Ilvokhin

This intentionally breaks direct users of zone->lock at compile time so all call sites are converted to the zone lock wrappers. Without the rename, present and future out-of-tree code could continue using spin_lock(&zone->lock) and bypass the wrappers and tracing infrastructure.

No functional change intended.

Quoted 5 lines
Quoted 3 lines

I see some more instances of 'zone->lock' in comments in include/linux/mmzone.h and under Documentation/ but otherwise LGTM.

I fixed (most of) that in the previous version but my fix was lost.

Thanks for the fixups, Andrew.

I still see a few 'zone->lock' references in Documentation remain on mm-new. This patch cleans them up, as noted by Vlastimil.

I'm happy to adjust this patch if anything else needs attention.

From 9142d5a8b60038fa424a6033253960682e5a51f4 Mon Sep 17 00:00:00 2001
From: Dmitry Ilvokhin <[email protected]>
Date: Tue, 3 Mar 2026 06:13:13 -0800
Subject: [PATCH] mm: fix remaining zone->lock references

---
 Documentation/mm/physical_memory.rst | 4 ++--
 Documentation/trace/events-kmem.rst  | 8 ++++----
 2 files changed, 6 insertions(+), 6 deletions(-)
Documentation/mm/physical_memory.rst +0 -0
index b76183545e5b..e344f93515b6 100644

Documentation/mm/physical_memory.rst General · line 503

Quoted 5 lines
Documentation/mm/physical_memory.rst +1 -1
@@ -500,11 +500,11 @@ General
 ``nr_isolate_pageblock``
   Number of isolated pageblocks. It is used to solve incorrect freepage counting
   problem due to racy retrieving migratetype of pageblock. Protected by
-  ``zone->lock``. Defined only when ``CONFIG_MEMORY_ISOLATION`` is enabled.
+  ``zone_lock``. Defined only when ``CONFIG_MEMORY_ISOLATION`` is enabled.

Dmitry's original patch [1] was doing 's/zone->lock/zone->_lock/', which aligns to my expectation. But this patch is doing 's/zone->lock/zone_lock/'. Same for the rest of this patch.

Quoted 6 lines

I was initially thinking this is just a mistake, but I also found Andrew is doing same change [2], so I'm bit confused. Is this an intentional change?

[1] https://lore.kernel.org/d61500c5784c64e971f4d328c57639303c475f81.1772206930.git.d@ilvokhin.com
[2] https://lore.kernel.org/[email protected]

Good catch, thanks for pointing this out, SJ.

Quoted 7 lines

Originally the mechanical rename was indeed zone->lock -> zone->_lock. However, in Documentation I intentionally switched references to zone_lock instead of zone->_lock. The reasoning is that _lock is now an internal implementation detail, and direct access is discouraged. The intended interface is via the zone_lock_*() / zone_unlock_*() wrappers, so referencing zone_lock in documentation felt more appropriate than mentioning the private struct field (zone->_lock).

Thank you for this nice and kind clarification, Dmitry! I agree mentioning zone_[un]lock_*() helpers instead of the hidden member (zone->_lock) can be better.

But, I'm concerned if people like me might not aware the intention under 'zone_lock'. If there is a well-known convention that allows people to know it is for 'zone_[un]lock_*()' helpers, making it more clear would be nice, in my humble opinion. If there is such a convention but I'm just missing it, please ignore. If I'm not, for eaxmaple,

"protected by ``zone->lock``" could be re-wrote to "protected by ``zone_[un]lock_*()`` locking helpers" or, "protected by zone lock helper functions (``zone_[un]lock_*()``)" ?

Quoted 7 lines

That said, I agree this creates inconsistency with the mechanical rename, and I'm happy to adjust either way: either consistently refer to the wrapper API, or keep documentation aligned with zone->_lock.

I slightly prefer referring to the wrapper API, but don't have a strong preference as long as we're consistent.

Quoted 8 lines
Quoted 2 lines

I also think both approaches are good. But for the wrapper approach, I think giving more contexts rather than just ``zone_lock`` to readers would be nice.

Grep tells me that we also have comments mentioning simply "zone lock", btw. And it's also a term used often in informal conversations. Maybe we could just standardize on that in comments/documentations as it's easier to read. Discovering that the field is called _lock and that wrappers should be used, is hopefully not that difficult.

Quoted 3 lines

Thanks for the suggestion, Vlastimil. That sounds reasonable to me as well. I'll update the comments and documentation to consistently use "zone lock".

Following the suggestion from SJ and Vlastimil, I prepared fixup to standardize documentation and comments on the term "zone lock".

The patch is based on top of the current mm-new.

Andrew, please let me know if you would prefer a respin of the series instead.

From 267cda3e0e160f97b346009bc48819bfeed92e52 Mon Sep 17 00:00:00 2001
From: Dmitry Ilvokhin <[email protected]>
Date: Thu, 5 Mar 2026 10:36:17 -0800
Subject: [PATCH] mm: documentation: standardize on "zone lock" terminology

During review of the zone lock tracing series it was suggested to standardize documentation and comments on the term "zone lock" instead of using zone_lock or referring to the internal field zone->_lock.

Update references accordingly.

---
 Documentation/mm/physical_memory.rst |  4 ++--
 Documentation/trace/events-kmem.rst  |  8 ++++----
 mm/compaction.c                      |  2 +-
 mm/internal.h                        |  2 +-
 mm/page_alloc.c                      | 12 ++++++------
 mm/page_isolation.c                  |  4 ++--
 mm/page_owner.c                      |  2 +-
 7 files changed, 17 insertions(+), 17 deletions(-)
Documentation/mm/physical_memory.rst +2 -2
index e344f93515b6..2398d87ac156 100644
@@ -500,11 +500,11 @@ General
 ``nr_isolate_pageblock``
   Number of isolated pageblocks. It is used to solve incorrect freepage counting
   problem due to racy retrieving migratetype of pageblock. Protected by
-  ``zone_lock``. Defined only when ``CONFIG_MEMORY_ISOLATION`` is enabled.
+  zone lock. Defined only when ``CONFIG_MEMORY_ISOLATION`` is enabled.
 
 ``span_seqlock``
   The seqlock to protect ``zone_start_pfn`` and ``spanned_pages``. It is a
-  seqlock because it has to be read outside of ``zone_lock``, and it is done in
+  seqlock because it has to be read outside of zone lock, and it is done in
   the main allocator path. However, the seqlock is written quite infrequently.
   Defined only when ``CONFIG_MEMORY_HOTPLUG`` is enabled.
 
Documentation/trace/events-kmem.rst +4 -4
index 3c20a972de27..42f08f3b136c 100644
@@ -57,7 +57,7 @@ the per-CPU allocator (high performance) or the buddy allocator.
 
 If pages are allocated directly from the buddy allocator, the
 mm_page_alloc_zone_locked event is triggered. This event is important as high
-amounts of activity imply high activity on the zone_lock. Taking this lock
+amounts of activity imply high activity on the zone lock. Taking this lock
 impairs performance by disabling interrupts, dirtying cache lines between
 CPUs and serialising many CPUs.
 
@@ -79,11 +79,11 @@ contention on the lruvec->lru_lock.
   mm_page_pcpu_drain		page=%p pfn=%lu order=%d cpu=%d migratetype=%d
 
 In front of the page allocator is a per-cpu page allocator. It exists only
-for order-0 pages, reduces contention on the zone_lock and reduces the
+for order-0 pages, reduces contention on the zone lock and reduces the
 amount of writing on struct page.
 
 When a per-CPU list is empty or pages of the wrong type are allocated,
-the zone_lock will be taken once and the per-CPU list refilled. The event
+the zone lock will be taken once and the per-CPU list refilled. The event
 triggered is mm_page_alloc_zone_locked for each page allocated with the
 event indicating whether it is for a percpu_refill or not.
 
@@ -92,7 +92,7 @@ which triggers a mm_page_pcpu_drain event.
 
 The individual nature of the events is so that pages can be tracked
 between allocation and freeing. A number of drain or refill pages that occur
-consecutively imply the zone_lock being taken once. Large amounts of per-CPU
+consecutively imply the zone lock being taken once. Large amounts of per-CPU
 refills and drains could imply an imbalance between CPUs where too much work
 is being concentrated in one place. It could also indicate that the per-CPU
 lists should be a larger size. Finally, large amounts of refills on one CPU
mm/compaction.c +1 -1
index 143ead2cb10a..32623894a632 100644
@@ -1419,7 +1419,7 @@ static bool suitable_migration_target(struct compact_control *cc,
 		int order = cc->order > 0 ? cc->order : pageblock_order;
 
 		/*
-		 * We are checking page_order without zone->_lock taken. But
+		 * We are checking page_order without zone lock taken. But
 		 * the only small danger is that we skip a potentially suitable
 		 * pageblock, so it's not worth to check order for valid range.
 		 */
mm/internal.h +1 -1
index f634ac469c87..95b583e7e4f7 100644
@@ -727,7 +727,7 @@ static inline unsigned int buddy_order(struct page *page)
  * (d) a page and its buddy are in the same zone.
  *
  * For recording whether a page is in the buddy system, we set PageBuddy.
- * Setting, clearing, and testing PageBuddy is serialized by zone->_lock.
+ * Setting, clearing, and testing PageBuddy is serialized by zone lock.
  *
  * For recording page's order, we use page_private(page).
  */
mm/page_alloc.c +6 -6
index 4c95364b7063..75ee81445640 100644
@@ -2440,7 +2440,7 @@ enum rmqueue_mode {
 
 /*
  * Do the hard work of removing an element from the buddy allocator.
- * Call me with the zone->_lock already held.
+ * Call me with the zone lock already held.
  */
 static __always_inline struct page *
 __rmqueue(struct zone *zone, unsigned int order, int migratetype,
@@ -2468,7 +2468,7 @@ __rmqueue(struct zone *zone, unsigned int order, int migratetype,
 	 * fallbacks modes with increasing levels of fragmentation risk.
 	 *
 	 * The fallback logic is expensive and rmqueue_bulk() calls in
-	 * a loop with the zone->_lock held, meaning the freelists are
+	 * a loop with the zone lock held, meaning the freelists are
 	 * not subject to any outside changes. Remember in *mode where
 	 * we found pay dirt, to save us the search on the next call.
 	 */
@@ -7046,7 +7046,7 @@ int alloc_contig_frozen_range_noprof(unsigned long start, unsigned long end,
 	 * pages.  Because of this, we reserve the bigger range and
 	 * once this is done free the pages we are not interested in.
 	 *
-	 * We don't have to hold zone->_lock here because the pages are
+	 * We don't have to hold zone lock here because the pages are
 	 * isolated thus they won't get removed from buddy.
 	 */
 	outer_start = find_large_buddy(start);
@@ -7615,7 +7615,7 @@ void accept_page(struct page *page)
 		return;
 	}
 
-	/* Unlocks zone->_lock */
+	/* Unlocks zone lock */
 	__accept_page(zone, &flags, page);
 }
 
@@ -7632,7 +7632,7 @@ static bool try_to_accept_memory_one(struct zone *zone)
 		return false;
 	}
 
-	/* Unlocks zone->_lock */
+	/* Unlocks zone lock */
 	__accept_page(zone, &flags, page);
 
 	return true;
@@ -7773,7 +7773,7 @@ struct page *alloc_frozen_pages_nolock_noprof(gfp_t gfp_flags, int nid, unsigned
 
 	/*
 	 * Best effort allocation from percpu free list.
-	 * If it's empty attempt to spin_trylock zone->_lock.
+	 * If it's empty attempt to spin_trylock zone lock.
 	 */
 	page = get_page_from_freelist(alloc_gfp, order, alloc_flags, &ac);
 
mm/page_isolation.c +2 -2
index cf731370e7a7..e8414e9a718a 100644
@@ -212,7 +212,7 @@ static int set_migratetype_isolate(struct page *page, enum pb_isolate_mode mode,
 	zone_unlock_irqrestore(zone, flags);
 	if (mode == PB_ISOLATE_MODE_MEM_OFFLINE) {
 		/*
-		 * printk() with zone->_lock held will likely trigger a
+		 * printk() with zone lock held will likely trigger a
 		 * lockdep splat, so defer it here.
 		 */
 		dump_page(unmovable, "unmovable page");
@@ -553,7 +553,7 @@ void undo_isolate_page_range(unsigned long start_pfn, unsigned long end_pfn)
 /*
  * Test all pages in the range is free(means isolated) or not.
  * all pages in [start_pfn...end_pfn) must be in the same zone.
- * zone->_lock must be held before call this.
+ * zone lock must be held before call this.
  *
  * Returns the last tested pfn.
  */
mm/page_owner.c +1 -1
index 54a4ba63b14f..109f2f28f5b1 100644
@@ -799,7 +799,7 @@ static void init_pages_in_zone(struct zone *zone)
 				continue;
 
 			/*
-			 * To avoid having to grab zone->_lock, be a little
+			 * To avoid having to grab zone lock, be a little
 			 * careful when reading buddy page order. The only
 			 * danger is that we skip too much and potentially miss
 			 * some early allocated pages, which is better than
Signature · 1 line
2.47.3
Acked-by
#30 lore raw

Re: [PATCH v4 4/5] mm: rename zone->lock to zone->_lock

On Thu, 5 Mar 2026 18:59:43 +0000 Dmitry Ilvokhin <[email protected]> wrote:
[...]

Quoted 13 lines

Following the suggestion from SJ and Vlastimil, I prepared fixup to standardize documentation and comments on the term "zone lock".

The patch is based on top of the current mm-new.

Andrew, please let me know if you would prefer a respin of the series instead.

From 267cda3e0e160f97b346009bc48819bfeed92e52 Mon Sep 17 00:00:00 2001
From: Dmitry Ilvokhin <[email protected]>
Date: Thu, 5 Mar 2026 10:36:17 -0800
Subject: [PATCH] mm: documentation: standardize on "zone lock" terminology

Quoted 8 lines

During review of the zone lock tracing series it was suggested to standardize documentation and comments on the term "zone lock" instead of using zone_lock or referring to the internal field zone->_lock.

Update references accordingly.

THanks,
SJ

[...]

maintainer Acked-by
#31 lore raw

Re: [PATCH v4 4/5] mm: rename zone->lock to zone->_lock

In reply to Dmitry Ilvokhin
Quoted 129 lines · Dmitry Ilvokhin
Quoted 111 lines · Dmitry Ilvokhin
Quoted 101 lines · Vlastimil Babka (SUSE)
Quoted 100 lines · SeongJae Park
Quoted 68 lines · On Wed, 4 Mar 2026 13:01:45 +0000 Dmitry Ilvokhin <[email protected]> wrote:
Quoted 58 lines · SeongJae Park
Quoted 46 lines · On Tue, 3 Mar 2026 14:25:55 +0000 Dmitry Ilvokhin <[email protected]> wrote:
Quoted 16 lines · Andrew Morton
Quoted 14 lines · On Mon, 2 Mar 2026 15:10:03 +0100 "Vlastimil Babka (SUSE)" <[email protected]>…
Quoted 12 lines · Dmitry Ilvokhin

This intentionally breaks direct users of zone->lock at compile time so all call sites are converted to the zone lock wrappers. Without the rename, present and future out-of-tree code could continue using spin_lock(&zone->lock) and bypass the wrappers and tracing infrastructure.

No functional change intended.

Quoted 5 lines
Quoted 3 lines

I see some more instances of 'zone->lock' in comments in include/linux/mmzone.h and under Documentation/ but otherwise LGTM.

I fixed (most of) that in the previous version but my fix was lost.

Thanks for the fixups, Andrew.

I still see a few 'zone->lock' references in Documentation remain on mm-new. This patch cleans them up, as noted by Vlastimil.

I'm happy to adjust this patch if anything else needs attention.

From 9142d5a8b60038fa424a6033253960682e5a51f4 Mon Sep 17 00:00:00 2001
From: Dmitry Ilvokhin <[email protected]>
Date: Tue, 3 Mar 2026 06:13:13 -0800
Subject: [PATCH] mm: fix remaining zone->lock references

---
 Documentation/mm/physical_memory.rst | 4 ++--
 Documentation/trace/events-kmem.rst  | 8 ++++----
 2 files changed, 6 insertions(+), 6 deletions(-)
Documentation/mm/physical_memory.rst +0 -0
index b76183545e5b..e344f93515b6 100644

Documentation/mm/physical_memory.rst General · line 503

Quoted 5 lines
Documentation/mm/physical_memory.rst +1 -1
@@ -500,11 +500,11 @@ General
 ``nr_isolate_pageblock``
   Number of isolated pageblocks. It is used to solve incorrect freepage counting
   problem due to racy retrieving migratetype of pageblock. Protected by
-  ``zone->lock``. Defined only when ``CONFIG_MEMORY_ISOLATION`` is enabled.
+  ``zone_lock``. Defined only when ``CONFIG_MEMORY_ISOLATION`` is enabled.

Dmitry's original patch [1] was doing 's/zone->lock/zone->_lock/', which aligns to my expectation. But this patch is doing 's/zone->lock/zone_lock/'. Same for the rest of this patch.

Quoted 6 lines

I was initially thinking this is just a mistake, but I also found Andrew is doing same change [2], so I'm bit confused. Is this an intentional change?

[1] https://lore.kernel.org/d61500c5784c64e971f4d328c57639303c475f81.1772206930.git.d@ilvokhin.com
[2] https://lore.kernel.org/[email protected]

Good catch, thanks for pointing this out, SJ.

Quoted 7 lines

Originally the mechanical rename was indeed zone->lock -> zone->_lock. However, in Documentation I intentionally switched references to zone_lock instead of zone->_lock. The reasoning is that _lock is now an internal implementation detail, and direct access is discouraged. The intended interface is via the zone_lock_*() / zone_unlock_*() wrappers, so referencing zone_lock in documentation felt more appropriate than mentioning the private struct field (zone->_lock).

Thank you for this nice and kind clarification, Dmitry! I agree mentioning zone_[un]lock_*() helpers instead of the hidden member (zone->_lock) can be better.

But, I'm concerned if people like me might not aware the intention under 'zone_lock'. If there is a well-known convention that allows people to know it is for 'zone_[un]lock_*()' helpers, making it more clear would be nice, in my humble opinion. If there is such a convention but I'm just missing it, please ignore. If I'm not, for eaxmaple,

"protected by ``zone->lock``" could be re-wrote to "protected by ``zone_[un]lock_*()`` locking helpers" or, "protected by zone lock helper functions (``zone_[un]lock_*()``)" ?

Quoted 7 lines

That said, I agree this creates inconsistency with the mechanical rename, and I'm happy to adjust either way: either consistently refer to the wrapper API, or keep documentation aligned with zone->_lock.

I slightly prefer referring to the wrapper API, but don't have a strong preference as long as we're consistent.

Quoted 8 lines
Quoted 2 lines

I also think both approaches are good. But for the wrapper approach, I think giving more contexts rather than just ``zone_lock`` to readers would be nice.

Grep tells me that we also have comments mentioning simply "zone lock", btw. And it's also a term used often in informal conversations. Maybe we could just standardize on that in comments/documentations as it's easier to read. Discovering that the field is called _lock and that wrappers should be used, is hopefully not that difficult.

Quoted 3 lines

Thanks for the suggestion, Vlastimil. That sounds reasonable to me as well. I'll update the comments and documentation to consistently use "zone lock".

Following the suggestion from SJ and Vlastimil, I prepared fixup to standardize documentation and comments on the term "zone lock".

The patch is based on top of the current mm-new.

Andrew, please let me know if you would prefer a respin of the series instead.

From 267cda3e0e160f97b346009bc48819bfeed92e52 Mon Sep 17 00:00:00 2001
From: Dmitry Ilvokhin <[email protected]>
Date: Thu, 5 Mar 2026 10:36:17 -0800
Subject: [PATCH] mm: documentation: standardize on "zone lock" terminology

Quoted 8 lines

During review of the zone lock tracing series it was suggested to standardize documentation and comments on the term "zone lock" instead of using zone_lock or referring to the internal field zone->_lock.

Update references accordingly.

Thanks!

#32 lore raw

Re: [PATCH v4 4/5] mm: rename zone->lock to zone->_lock

In reply to Dmitry Ilvokhin
Quoted 27 lines · Dmitry Ilvokhin

This intentionally breaks direct users of zone->lock at compile time so all call sites are converted to the zone lock wrappers. Without the rename, present and future out-of-tree code could continue using spin_lock(&zone->lock) and bypass the wrappers and tracing infrastructure.

No functional change intended.

---
 include/linux/mmzone.h      |  7 +++++--
 include/linux/mmzone_lock.h | 12 ++++++------
 mm/compaction.c             |  4 ++--
 mm/internal.h               |  2 +-
 mm/page_alloc.c             | 16 ++++++++--------
 mm/page_isolation.c         |  4 ++--
 mm/page_owner.c             |  2 +-
 7 files changed, 25 insertions(+), 22 deletions(-)
include/linux/mmzone.h +0 -0
index 3e51190a55e4..32bca655fce5 100644

include/linux/mmzone.h struct zone { · line 1016

Quoted 10 lines
include/linux/mmzone.h +5 -2
@@ -1009,8 +1009,11 @@ struct zone {
 	/* zone flags, see below */
 	unsigned long		flags;
 
-	/* Primarily protects free_area */
-	spinlock_t		lock;
+	/*
+	 * Primarily protects free_area. Should be accessed via zone_lock_*
+	 * helpers.
+	 */
+	spinlock_t		_lock;

I really don't like this uglification.
Suggestion:

	spinlock_t __private	lock;

include/linux/mmzone_lock.h line 10

Quoted 12 lines
include/linux/mmzone.h +0 -0
 	/* Pages to be freed when next trylock succeeds */
 	struct llist_head	trylock_free_pages;
include/linux/mmzone_lock.h +0 -1
index a1cfba8408d6..62e34d500078 100644
@@ -7,32 +7,32 @@
 
 static inline void zone_lock_init(struct zone *zone)
 {
-	spin_lock_init(&zone->lock);

and then ACCESS_PRIVATE() all over these helpers. This will not make a difference to the compiler, but it will work with sparse.

It's not that I don't understand what you're doing, but we're going to need to look to this code and refer to this code 20 years from now, I would rather not refer to zone->_lock :)

Signature · 1 line
Pedro
#33 lore raw

PATCH v4 5/5 mm: add tracepoints for zone lock

In reply to Dmitry Ilvokhin

Add tracepoint instrumentation to zone lock acquire/release operations via the previously introduced wrappers.

The implementation follows the mmap_lock tracepoint pattern: a lightweight inline helper checks whether the tracepoint is enabled and calls into an out-of-line helper when tracing is active. When CONFIG_TRACING is disabled, helpers compile to empty inline stubs.

The fast path is unaffected when tracing is disabled.

---
 MAINTAINERS                      |  1 +
 include/linux/mmzone_lock.h      | 64 +++++++++++++++++++++++++++++++-
 include/trace/events/zone_lock.h | 64 ++++++++++++++++++++++++++++++++
 mm/Makefile                      |  2 +-
 mm/zone_lock.c                   | 28 ++++++++++++++
 5 files changed, 157 insertions(+), 2 deletions(-)
 create mode 100644 include/trace/events/zone_lock.h
 create mode 100644 mm/zone_lock.c
MAINTAINERS +1 -0
index 947298ecb111..de39e87a4c46 100644
@@ -16681,6 +16681,7 @@ F:	include/linux/pgtable.h
 F:	include/linux/ptdump.h
 F:	include/linux/vmpressure.h
 F:	include/linux/vmstat.h
+F:	include/trace/events/zone_lock.h
 F:	kernel/fork.c
 F:	mm/Kconfig
 F:	mm/debug.c
include/linux/mmzone_lock.h +63 -1

95 lines not on this page · raw message

include/trace/events/zone_lock.h new file +64 -0

64 lines not on this page · raw message

mm/Makefile +1 -1
index 8ad2ab08244e..ffd06cf7a04e 100644
@@ -55,7 +55,7 @@ obj-y			:= filemap.o mempool.o oom_kill.o fadvise.o \
 			   mm_init.o percpu.o slab_common.o \
 			   compaction.o show_mem.o \
 			   interval_tree.o list_lru.o workingset.o \
-			   debug.o gup.o mmap_lock.o vma_init.o $(mmu-y)
+			   debug.o gup.o mmap_lock.o zone_lock.o vma_init.o $(mmu-y)
 
 # Give 'page_alloc' its own module-parameter namespace
 page-alloc-y := page_alloc.o
mm/zone_lock.c new file +28 -0
new file mode 100644
index 000000000000..f4e32220af9a
@@ -0,0 +1,28 @@
+// SPDX-License-Identifier: GPL-2.0
+#define CREATE_TRACE_POINTS
+#include <trace/events/zone_lock.h>
+
+#include <linux/mmzone_lock.h>
+
+EXPORT_TRACEPOINT_SYMBOL(zone_lock_start_locking);
+EXPORT_TRACEPOINT_SYMBOL(zone_lock_acquire_returned);
+EXPORT_TRACEPOINT_SYMBOL(zone_lock_released);
+
+#ifdef CONFIG_TRACING
+
+void __zone_lock_do_trace_start_locking(struct zone *zone)
+{
+	trace_zone_lock_start_locking(zone);
+}
+
+void __zone_lock_do_trace_acquire_returned(struct zone *zone, bool success)
+{
+	trace_zone_lock_acquire_returned(zone, success);
+}
+
+void __zone_lock_do_trace_released(struct zone *zone)
+{
+	trace_zone_lock_released(zone);
+}
+
+#endif /* CONFIG_TRACING */
Signature · 1 line
2.47.3
maintainer
#34 lore raw

Re: [PATCH v4 5/5] mm: add tracepoints for zone lock

include/linux/mmzone_lock.h static inline void zone_lock_init(struct zone *zone) · line 66

Quoted 11 lines · On Fri, 27 Feb 2026 16:00:27 +0000 Dmitry Ilvokhin <[email protected]> wrote:

static inline void zone_lock_init(struct zone *zone) {

include/linux/mmzone_lock.h +4 -0
@@ -12,26 +59,41 @@ static inline void zone_lock_init(struct zone *zone)
 
 #define zone_lock_irqsave(zone, flags)				\
 do {								\
+	bool success = true;					\
+								\
+	__zone_lock_trace_start_locking(zone);			\
 	spin_lock_irqsave(&(zone)->_lock, flags);		\
+	__zone_lock_trace_acquire_returned(zone, success);	\

Why the "success" variable and not just:

	__zone_lock_trace_acquire_returned(zone, true);

?

Quoted 16 lines
include/linux/mmzone_lock.h +7 -1
 } while (0)
 
 #define zone_trylock_irqsave(zone, flags)			\
 ({								\
-	spin_trylock_irqsave(&(zone)->_lock, flags);		\
+	bool success;						\
+								\
+	__zone_lock_trace_start_locking(zone);			\
+	success = spin_trylock_irqsave(&(zone)->_lock, flags);	\
+	__zone_lock_trace_acquire_returned(zone, success);	\
+	success;						\
 })
 
 static inline void zone_unlock_irqrestore(struct zone *zone, unsigned long flags)
 {
+	__zone_lock_trace_released(zone);

include/linux/mmzone_lock.h static inline void zone_lock_init(struct zone *zone) · line 91

Quoted 10 lines
include/linux/mmzone_lock.h +4 -0
 	spin_unlock_irqrestore(&zone->_lock, flags);
 }
 
 static inline void zone_lock_irq(struct zone *zone)
 {
+	bool success = true;
+
+	__zone_lock_trace_start_locking(zone);
 	spin_lock_irq(&zone->_lock);
+	__zone_lock_trace_acquire_returned(zone, success);

Same here.

include/linux/mmzone_lock.h static inline void zone_lock_init(struct zone *zone) · line 99

Quoted 8 lines
include/linux/mmzone_lock.h +1 -0
 }
 
 static inline void zone_unlock_irq(struct zone *zone)
 {
+	__zone_lock_trace_released(zone);
 	spin_unlock_irq(&zone->_lock);
 }
 

-- Steve

#35 lore raw

Re: [PATCH v4 5/5] mm: add tracepoints for zone lock

Quoted 17 lines · Steven Rostedt

include/linux/mmzone_lock.h static inline void zone_lock_init(struct zone *zone) · line 66

Quoted 11 lines · On Fri, 27 Feb 2026 16:00:27 +0000 Dmitry Ilvokhin <[email protected]> wrote:

static inline void zone_lock_init(struct zone *zone) {

include/linux/mmzone_lock.h +4 -0
@@ -12,26 +59,41 @@ static inline void zone_lock_init(struct zone *zone)
 
 #define zone_lock_irqsave(zone, flags)				\
 do {								\
+	bool success = true;					\
+								\
+	__zone_lock_trace_start_locking(zone);			\
 	spin_lock_irqsave(&(zone)->_lock, flags);		\
+	__zone_lock_trace_acquire_returned(zone, success);	\

Why the "success" variable and not just:

Quoted 3 lines
	__zone_lock_trace_acquire_returned(zone, true);

?

Good point, passing true directly is cleaner. Happy to respin if needed.

maintainer Reviewed-by on v4 5/5
#36 lore raw

Re: [PATCH v4 5/5] mm: add tracepoints for zone lock

In reply to Dmitry Ilvokhin
Quoted 8 lines · Dmitry Ilvokhin

Add tracepoint instrumentation to zone lock acquire/release operations via the previously introduced wrappers.

The implementation follows the mmap_lock tracepoint pattern: a lightweight inline helper checks whether the tracepoint is enabled and calls into an out-of-line helper when tracing is active. When CONFIG_TRACING is disabled, helpers compile to empty inline stubs.

Quoted 4 lines

The fast path is unaffected when tracing is disabled.

Agree with Steven; otherwise

#37 lore raw
In reply to Dmitry Ilvokhin
Quoted 18 lines · Dmitry Ilvokhin

Zone lock contention can significantly impact allocation and reclaim latency, as it is a central synchronization point in the page allocator and reclaim paths. Improved visibility into its behavior is therefore important for diagnosing performance issues in memory-intensive workloads.

On some production workloads at Meta, we have observed noticeable zone lock contention. Deeper analysis of lock holders and waiters is currently difficult with existing instrumentation.

While generic lock contention_begin/contention_end tracepoints cover the slow path, they do not provide sufficient visibility into lock hold times. In particular, the lack of a release-side event makes it difficult to identify long lock holders and correlate them with waiters. As a result, distinguishing between short bursts of contention and pathological long hold times requires additional instrumentation.

Quoted 2 lines

This patch series adds dedicated tracepoint instrumentation to zone lock, following the existing mmap_lock tracing model.

I don't like this at all. We have CONFIG_LOCK_STAT. That should be improved insted of coming up with one-offs for every single lock that someone deems "special".

#38 lore raw
Quoted 19 lines · Matthew Wilcox
Quoted 18 lines · Dmitry Ilvokhin

Zone lock contention can significantly impact allocation and reclaim latency, as it is a central synchronization point in the page allocator and reclaim paths. Improved visibility into its behavior is therefore important for diagnosing performance issues in memory-intensive workloads.

On some production workloads at Meta, we have observed noticeable zone lock contention. Deeper analysis of lock holders and waiters is currently difficult with existing instrumentation.

While generic lock contention_begin/contention_end tracepoints cover the slow path, they do not provide sufficient visibility into lock hold times. In particular, the lack of a release-side event makes it difficult to identify long lock holders and correlate them with waiters. As a result, distinguishing between short bursts of contention and pathological long hold times requires additional instrumentation.

Quoted 6 lines
Quoted 2 lines

This patch series adds dedicated tracepoint instrumentation to zone lock, following the existing mmap_lock tracing model.

I don't like this at all. We have CONFIG_LOCK_STAT. That should be improved insted of coming up with one-offs for every single lock that someone deems "special".

Thanks for the feedback, Matthew.

CONFIG_LOCK_STAT provides useful statistics, but it is primarily a debug facility and is generally too heavyweight for the production environments.

The motivation for this series was to provide lightweight observability for the zone lock in production workloads.

I agree that improving generic lock instrumentation would be preferable. I did consider whether something similar could be done generically for spinlocks, but the unlock path there is typically just a single atomic store, so adding generic lightweight instrumentation without affecting the fast path is difficult.

In parallel, I've been experimenting with improving observability for sleepable locks by adding a contended_release tracepoint, which would allow correlating lock holders and waiters in a more generic way. I've posted an RFC here:

https://lore.kernel.org/all/[email protected]/

I'd appreciate feedback on whether that direction makes sense for improving the generic lock tracing infrastructure.

#39 lore raw
Quoted 9 lines · Dmitry Ilvokhin
Quoted 7 lines · Matthew Wilcox
Quoted 2 lines · Dmitry Ilvokhin

This patch series adds dedicated tracepoint instrumentation to zone lock, following the existing mmap_lock tracing model.

I don't like this at all. We have CONFIG_LOCK_STAT. That should be improved insted of coming up with one-offs for every single lock that someone deems "special".

Quoted 5 lines

Thanks for the feedback, Matthew.

CONFIG_LOCK_STAT provides useful statistics, but it is primarily a debug facility and is generally too heavyweight for the production environments.

Yes, agreed. I think that is what needs to change.

Quoted 2 lines

The motivation for this series was to provide lightweight observability for the zone lock in production workloads.

I read that. But first it was the mmap lock. Now it's the zone lock. Which lock will be next? This is too heavyweight a procedure to follow for each lock of interest.

Quoted 5 lines

I agree that improving generic lock instrumentation would be preferable. I did consider whether something similar could be done generically for spinlocks, but the unlock path there is typically just a single atomic store, so adding generic lightweight instrumentation without affecting the fast path is difficult.

This is why we have tracepoint_enabled() and friends. But ... LOCK_STAT doesn't affect the unlock path at all. It only changes the acquire side to call lock_acquired() (and lock_contended() if the trylock failed).

Quoted 9 lines

In parallel, I've been experimenting with improving observability for sleepable locks by adding a contended_release tracepoint, which would allow correlating lock holders and waiters in a more generic way. I've posted an RFC here:

https://lore.kernel.org/all/[email protected]/

I'd appreciate feedback on whether that direction makes sense for improving the generic lock tracing infrastructure.

It seems fine to me, but I don't have the depth in the locking code to give it the thorough review it deserves.

maintainer
#40 lore raw
Quoted 5 lines · On Mon, 9 Mar 2026 14:47:29 +0000 Matthew Wilcox <[email protected]> wrote:
Quoted 3 lines

CONFIG_LOCK_STAT provides useful statistics, but it is primarily a debug facility and is generally too heavyweight for the production environments.

Yes, agreed. I think that is what needs to change.

The biggest issue with making a generic light weight LOCK_STAT is that locks are extremely optimized. Any addition of generic lock encoding will cause a noticeable overhead when compiled in, even when disabled.

Most of the lock code is inlined for the fast path. Now if we want to add lock stats, we would need to add hooks into those inlined paths. This will undoubtedly increase the size of text, which will have an impact on I$.

The other issue is the data we store for the lock. A lock is usually just a word (or long) in size, embedded in a structure. LOCKDEP and LOCK_STAT adds a key per lock. This increases the data size of the kernel.

LOCK_STAT was designed on top of LOCKDEP. Perhaps a lighter version of LOCK_STAT could be designed without LOCKDEP, but it would be a project on its own.

-- Steve

#41 lore raw
Quoted 3 lines · Steven Rostedt

The biggest issue with making a generic light weight LOCK_STAT is that locks are extremely optimized. Any addition of generic lock encoding will cause a noticeable overhead when compiled in, even when disabled.

I'm not sure that's true. Taking the current Debian kernel config leads to a "call" instruction to acquire a spinlock:

void __insert_inode_hash(struct inode *inode, unsigned long hashval)

{
        struct hlist_head *b = inode_hashtable + hash(inode->i_sb, hashval);

        spin_lock(&inode_hash_lock);
        spin_lock(&inode->i_lock);
        hlist_add_head_rcu(&inode->i_hash, b);
        spin_unlock(&inode->i_lock);
        spin_unlock(&inode_hash_lock);
}

compiles to:

[...]

     280:       23 35 00 00 00 00       and    0x0(%rip),%esi        # 286 <__insert_inode_hash+0x56>
                        282: R_X86_64_PC32      .data..ro_after_init+0x10
     286:       48 8d 2c f0             lea    (%rax,%rsi,8),%rbp
     28a:       e8 00 00 00 00          call   28f <__insert_inode_hash+0x5f>
                        28b: R_X86_64_PLT32     _raw_spin_lock-0x4
     28f:       4c 89 e7                mov    %r12,%rdi
     292:       e8 00 00 00 00          call   297 <__insert_inode_hash+0x67>
                        293: R_X86_64_PLT32     _raw_spin_lock-0x4

[...]

Debian doesn't do anything too weird here:

#
# Lock Debugging (spinlocks, mutexes, etc...)
#
CONFIG_LOCK_DEBUGGING_SUPPORT=y
# CONFIG_PROVE_LOCKING is not set
# CONFIG_LOCK_STAT is not set
# CONFIG_DEBUG_RT_MUTEXES is not set
# CONFIG_DEBUG_SPINLOCK is not set
# CONFIG_DEBUG_MUTEXES is not set
# CONFIG_DEBUG_WW_MUTEX_SLOWPATH is not set
# CONFIG_DEBUG_RWSEMS is not set
# CONFIG_DEBUG_LOCK_ALLOC is not set
# CONFIG_DEBUG_ATOMIC_SLEEP is not set
# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set
# CONFIG_LOCK_TORTURE_TEST is not set
# CONFIG_WW_MUTEX_SELFTEST is not set
# CONFIG_SCF_TORTURE_TEST is not set
# CONFIG_CSD_LOCK_WAIT_DEBUG is not set

(The spinlock code is too complex for me to follow what config options influence whether it's a function call; you probably have enough of it in your head that you'd know)

Quoted 3 lines

The other issue is the data we store for the lock. A lock is usually just a word (or long) in size, embedded in a structure. LOCKDEP and LOCK_STAT adds a key per lock. This increases the data size of the kernel.

It does, but perhaps for a light weight lockstat, we could do better than that. For example it could use the return address to look up which lock is being accessed rather than embedding a key in each lock.

maintainer
#42 lore raw
Quoted 30 lines · On Mon, 9 Mar 2026 20:45:31 +0000 Matthew Wilcox <[email protected]> wrote:
Quoted 3 lines · Steven Rostedt

The biggest issue with making a generic light weight LOCK_STAT is that locks are extremely optimized. Any addition of generic lock encoding will cause a noticeable overhead when compiled in, even when disabled.

I'm not sure that's true. Taking the current Debian kernel config leads to a "call" instruction to acquire a spinlock:

void __insert_inode_hash(struct inode *inode, unsigned long hashval)

{
        struct hlist_head *b = inode_hashtable + hash(inode->i_sb, hashval);

        spin_lock(&inode_hash_lock);
        spin_lock(&inode->i_lock);
        hlist_add_head_rcu(&inode->i_hash, b);
        spin_unlock(&inode->i_lock);
        spin_unlock(&inode_hash_lock);
}

compiles to:

[...]

     280:       23 35 00 00 00 00       and    0x0(%rip),%esi        # 286 <__insert_inode_hash+0x56>
                        282: R_X86_64_PC32      .data..ro_after_init+0x10
     286:       48 8d 2c f0             lea    (%rax,%rsi,8),%rbp
     28a:       e8 00 00 00 00          call   28f <__insert_inode_hash+0x5f>
                        28b: R_X86_64_PLT32     _raw_spin_lock-0x4
     28f:       4c 89 e7                mov    %r12,%rdi
     292:       e8 00 00 00 00          call   297 <__insert_inode_hash+0x67>
                        293: R_X86_64_PLT32     _raw_spin_lock-0x4
Quoted 1 line

[...]

Ah, you're correct. Looks like it's an arch specific thing. I was going back to my memory from around 2006, but it appears that only a few archs inline spinlocks anymore. Thomas made it a bit easier to see what does and does not do that (in 2009).

6beb000923882 ("locking: Make inlining decision Kconfig based")

So, perhaps adding code to the spinlocks will not be as much of a hit on I$.

Quoted 3 lines

(The spinlock code is too complex for me to follow what config options influence whether it's a function call; you probably have enough of it in your head that you'd know)

Yeah, I feel like I'm always relearning the code every time I have to jump in and understand it again.

Quoted 8 lines
Quoted 4 lines

The other issue is the data we store for the lock. A lock is usually just a word (or long) in size, embedded in a structure. LOCKDEP and LOCK_STAT adds a key per lock. This increases the data size of the kernel.

It does, but perhaps for a light weight lockstat, we could do better than that. For example it could use the return address to look up which lock is being accessed rather than embedding a key in each lock.

Right.

-- Steve

#43 lore raw

Thanks for the discussion and for the feedback on generic lock instrumentation.

Following up on the earlier points about lightweight LOCK_STAT and tracepoints.

While the thread has focused on spin_lock() calls, the real gap is on the unlock path: there is currently no release-side tracepoint to correlate holders and waiters or measure contended hold times.

A possible generic solution is a trace_contended_release() for spin locks, for example:

    if (trace_contended_release_enabled() &&
        atomic_read(&lock->val) & ~_Q_LOCKED_MASK)
        trace_contended_release(lock);

This might work on x86, but could increase code size and regress performance on arches where spin_unlock() is inlined, such as arm64 under !PREEMPTION.

So even a generic release-side tracepoint has nontrivial downsides (not to mention lightweight LOCK_STAT, since a disabled tracepoint is about as lightweight as it gets, the more stats we add, the less lightweight the mechanism becomes).

The zone lock wrappers provide a practical, production-safe solution to observe this specific high-contention lock, if the generic approach proves impractical.

#44 lore raw

On Mon, Mar 16, 2026 at 05:40:50PM +0000, Dmitry Ilvokhin wrote:

[...]

Quoted 10 lines

A possible generic solution is a trace_contended_release() for spin locks, for example:

    if (trace_contended_release_enabled() &&
        atomic_read(&lock->val) & ~_Q_LOCKED_MASK)
        trace_contended_release(lock);

This might work on x86, but could increase code size and regress performance on arches where spin_unlock() is inlined, such as arm64 under !PREEMPTION.

I took a stab at this idea and submitted an RFC [1].

The implementation builds on your earlier observation from Matthew that _raw_spin_unlock() is not inlined in most configurations. In those cases, when the tracepoint is disabled, this adds a single NOP on the fast path, with the conditional check staying out of line. The measured text size increase in this configuration is +983 bytes.

For configurations where _raw_spin_unlock() is inlined, the instrumentation does increase code size more noticeably (+71 KB in my measurements), since the check and out of line call is replicated at each call site.

This provides a generic release-side signal for contended locks,
allowing: correlation of lock holders with waiters and measurement of contended hold times

This RFC addressing the same visibility gap without introducing per-lock instrumentation.

If this tradeoff is acceptable, this could be a generic alternative to lock-specific tracepoints.

[1]: https://lore.kernel.org/all/51aad0415b78c5a39f2029722118fa01eac77538.1773858853.git.d@ilvokhin.com

maintainer
#45 lore raw
Quoted 32 lines · On Thu, 19 Mar 2026 13:22:54 +0000 Dmitry Ilvokhin <[email protected]> wrote:

On Mon, Mar 16, 2026 at 05:40:50PM +0000, Dmitry Ilvokhin wrote:

[...]

Quoted 10 lines

A possible generic solution is a trace_contended_release() for spin locks, for example:

    if (trace_contended_release_enabled() &&
        atomic_read(&lock->val) & ~_Q_LOCKED_MASK)
        trace_contended_release(lock);

This might work on x86, but could increase code size and regress performance on arches where spin_unlock() is inlined, such as arm64 under !PREEMPTION.

I took a stab at this idea and submitted an RFC [1].

The implementation builds on your earlier observation from Matthew that _raw_spin_unlock() is not inlined in most configurations. In those cases, when the tracepoint is disabled, this adds a single NOP on the fast path, with the conditional check staying out of line. The measured text size increase in this configuration is +983 bytes.

For configurations where _raw_spin_unlock() is inlined, the instrumentation does increase code size more noticeably (+71 KB in my measurements), since the check and out of line call is replicated at each call site.

This provides a generic release-side signal for contended locks,
allowing: correlation of lock holders with waiters and measurement of contended hold times

Quoted 7 lines

This RFC addressing the same visibility gap without introducing per-lock instrumentation.

If this tradeoff is acceptable, this could be a generic alternative to lock-specific tracepoints.

[1]: https://lore.kernel.org/all/51aad0415b78c5a39f2029722118fa01eac77538.1773858853.git.d@ilvokhin.com

That submission has met a disappointing response.

How should I proceed with this series "mm: zone lock tracepoint instrumentation"? It's not urgent so I'm inclined to put this on hold while you pursue "locking: Add contended_release tracepoint to spinning locks"?

Please send that v2 sometime and hopefully Steven can help push it along?

#46 lore raw
Quoted 40 lines · Andrew Morton
Quoted 38 lines · On Thu, 19 Mar 2026 13:22:54 +0000 Dmitry Ilvokhin <[email protected]> wrote:

On Mon, Mar 16, 2026 at 05:40:50PM +0000, Dmitry Ilvokhin wrote:

[...]

Quoted 10 lines

A possible generic solution is a trace_contended_release() for spin locks, for example:

    if (trace_contended_release_enabled() &&
        atomic_read(&lock->val) & ~_Q_LOCKED_MASK)
        trace_contended_release(lock);

This might work on x86, but could increase code size and regress performance on arches where spin_unlock() is inlined, such as arm64 under !PREEMPTION.

I took a stab at this idea and submitted an RFC [1].

The implementation builds on your earlier observation from Matthew that _raw_spin_unlock() is not inlined in most configurations. In those cases, when the tracepoint is disabled, this adds a single NOP on the fast path, with the conditional check staying out of line. The measured text size increase in this configuration is +983 bytes.

For configurations where _raw_spin_unlock() is inlined, the instrumentation does increase code size more noticeably (+71 KB in my measurements), since the check and out of line call is replicated at each call site.

This provides a generic release-side signal for contended locks,
allowing: correlation of lock holders with waiters and measurement of contended hold times

This RFC addressing the same visibility gap without introducing per-lock instrumentation.

If this tradeoff is acceptable, this could be a generic alternative to lock-specific tracepoints.

Quoted 8 lines
Quoted 1 line

[1]: https://lore.kernel.org/all/51aad0415b78c5a39f2029722118fa01eac77538.1773858853.git.d@ilvokhin.com

That submission has met a disappointing response.

How should I proceed with this series "mm: zone lock tracepoint instrumentation"? It's not urgent so I'm inclined to put this on hold while you pursue "locking: Add contended_release tracepoint to spinning locks"?

Thanks for the follow-up, Andrew.

My current plan is to focus on the "locking: Add contended_release tracepoint to spinning locks" work and drive it to a clear conclusion:
either by getting feedback that it's not a good direction, or by getting it into mainline.

In the meantime, it seems reasonable to drop the "mm: zone lock tracepoint instrumentation" patchset from mm-new to avoid confusion until the direction is clearer. I can revisit and respin it if the more generic locking approach doesn't pan out.

Quoted 2 lines

Please send that v2 sometime and hopefully Steven can help push it along?

I'll send the next version of the generic locking series soon. Any help in pushing it along would be appreciated.

maintainer
#47 lore raw
Quoted 4 lines · On Wed, 25 Mar 2026 12:14:35 +0000 Dmitry Ilvokhin <[email protected]> wrote:
Quoted 1 line

Please send that v2 sometime and hopefully Steven can help push it along?

I'll send the next version of the generic locking series soon. Any help in pushing it along would be appreciated.

I'll see what I can do when I see v2!

-- Steve