Compaction uses compact_lock_irqsave(), which currently operates
on a raw spinlock_t pointer so that it can be used for both
zone->lock and lru_lock. Since zone lock operations are now wrapped,
compact_lock_irqsave() can no longer operate directly on a spinlock_t
when the lock belongs to a zone.
Introduce struct compact_lock to abstract the underlying lock type. The
structure carries a lock type enum and a union holding either a zone
pointer or a raw spinlock_t pointer, and dispatches to the appropriate
lock/unlock helper.
No functional change intended.
Signed-off-by: Dmitry Ilvokhin <d@ilvokhin.com>
---
mm/compaction.c | 73 +++++++++++++++++++++++++++++++++++++++++++------
1 file changed, 64 insertions(+), 9 deletions(-)
diff --git a/mm/compaction.c b/mm/compaction.c
index 47b26187a5df..c3b97379a963 100644
--- a/mm/compaction.c
+++ b/mm/compaction.c
@@ -494,6 +494,53 @@ static bool test_and_set_skip(struct compact_control *cc, struct page *page)
}
#endif /* CONFIG_COMPACTION */
+enum compact_lock_type {
+ COMPACT_LOCK_ZONE,
+ COMPACT_LOCK_RAW_SPINLOCK,
+};
+
+struct compact_lock {
+ enum compact_lock_type type;
+ union {
+ struct zone *zone;
+ spinlock_t *lock; /* Reference to lru lock */
+ };
+};
+
+static bool compact_do_trylock_irqsave(struct compact_lock lock,
+ unsigned long *flags)
+{
+ if (lock.type == COMPACT_LOCK_ZONE)
+ return zone_trylock_irqsave(lock.zone, *flags);
+
+ return spin_trylock_irqsave(lock.lock, *flags);
+}
+
+static void compact_do_zone_lock_irqsave(struct zone *zone,
+ unsigned long *flags)
+__acquires(zone->lock)
+{
+ zone_lock_irqsave(zone, *flags);
+}
+
+static void compact_do_raw_lock_irqsave(spinlock_t *lock,
+ unsigned long *flags)
+__acquires(lock)
+{
+ spin_lock_irqsave(lock, *flags);
+}
+
+static void compact_do_lock_irqsave(struct compact_lock lock,
+ unsigned long *flags)
+{
+ if (lock.type == COMPACT_LOCK_ZONE) {
+ compact_do_zone_lock_irqsave(lock.zone, flags);
+ return;
+ }
+
+ compact_do_raw_lock_irqsave(lock.lock, flags);
+}
+
/*
* Compaction requires the taking of some coarse locks that are potentially
* very heavily contended. For async compaction, trylock and record if the
@@ -503,19 +550,19 @@ 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_lock_irqsave(struct compact_lock lock,
+ unsigned long *flags,
+ struct compact_control *cc)
{
/* Track if the lock is contended in async mode */
if (cc->mode == MIGRATE_ASYNC && !cc->contended) {
- if (spin_trylock_irqsave(lock, *flags))
+ if (compact_do_trylock_irqsave(lock, flags))
return true;
cc->contended = true;
}
- spin_lock_irqsave(lock, *flags);
+ compact_do_lock_irqsave(lock, flags);
return true;
}
@@ -531,7 +578,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 +662,12 @@ 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);
+ struct compact_lock zol = {
+ .type = COMPACT_LOCK_ZONE,
+ .zone = cc->zone,
+ };
+
+ locked = compact_lock_irqsave(zol, &flags, cc);
/* Recheck this is a buddy page under lock */
if (!PageBuddy(page))
@@ -1160,10 +1210,15 @@ isolate_migratepages_block(struct compact_control *cc, unsigned long low_pfn,
/* If we already hold the lock, we can skip some rechecking */
if (lruvec != locked) {
+ struct compact_lock zol = {
+ .type = COMPACT_LOCK_RAW_SPINLOCK,
+ .lock = &lruvec->lru_lock,
+ };
+
if (locked)
unlock_page_lruvec_irqrestore(locked, flags);
- compact_lock_irqsave(&lruvec->lru_lock, &flags, cc);
+ compact_lock_irqsave(zol, &flags, cc);
locked = lruvec;
lruvec_memcg_debug(lruvec, folio);
--
2.47.3
On Wed, 25 Feb 2026 14:43:05 +0000 Dmitry Ilvokhin <d@ilvokhin.com> wrote: > Compaction uses compact_lock_irqsave(), which currently operates > on a raw spinlock_t pointer so that it can be used for both > zone->lock and lru_lock. Since zone lock operations are now wrapped, > compact_lock_irqsave() can no longer operate directly on a spinlock_t > when the lock belongs to a zone. > > Introduce struct compact_lock to abstract the underlying lock type. The > structure carries a lock type enum and a union holding either a zone > pointer or a raw spinlock_t pointer, and dispatches to the appropriate > lock/unlock helper. It's regrettable that adds overhead - increased .text, increased instructions. Thing is, compact_lock_irqsave() has only two callsites. One knows that it's dealing with the zone lock, the other knows that it's dealing with the lruvec lock. Would it not be simpler and more efficient to copy/paste/edit two versions of compact_lock_irqsave()? A compact_zone_lock_irqsave() and a compact_lruvec_lock_irqsave()?
On Wed, Feb 25, 2026 at 12:12:52PM -0800, Andrew Morton wrote: > On Wed, 25 Feb 2026 14:43:05 +0000 Dmitry Ilvokhin <d@ilvokhin.com> wrote: > > > Compaction uses compact_lock_irqsave(), which currently operates > > on a raw spinlock_t pointer so that it can be used for both > > zone->lock and lru_lock. Since zone lock operations are now wrapped, > > compact_lock_irqsave() can no longer operate directly on a spinlock_t > > when the lock belongs to a zone. > > > > Introduce struct compact_lock to abstract the underlying lock type. The > > structure carries a lock type enum and a union holding either a zone > > pointer or a raw spinlock_t pointer, and dispatches to the appropriate > > lock/unlock helper. > > It's regrettable that adds overhead - increased .text, increased > instructions. > > Thing is, compact_lock_irqsave() has only two callsites. One knows > that it's dealing with the zone lock, the other knows that it's dealing > with the lruvec lock. > > Would it not be simpler and more efficient to copy/paste/edit two > versions of compact_lock_irqsave()? A compact_zone_lock_irqsave() and a > compact_lruvec_lock_irqsave()? > Thanks for the feedback, Andrew. My initial goal was to reduce code duplication by keeping the logic centralized, but your rationale makes sense. Given that there are only two call sites and both statically know the lock type, splitting the helper avoids unnecessary abstraction. I'll introduce compact_zone_lock_irqsave() and compact_lruvec_lock_irqsave() in v3.
© 2016 - 2026 Red Hat, Inc.