* [PATCH v5 1/7] mm: support promotion-only NUMA hinting scans
2026-11-10 15:04 [PATCH v5 0/7] sched/numa: stop VMA scan filters from gating promotion Gregory Price (Meta)
@ 2026-11-10 15:04 ` Gregory Price (Meta)
2026-11-10 15:04 ` [PATCH v5 2/7] mm: allow shared folios to be promoted to a fast tier Gregory Price (Meta)
` (5 subsequent siblings)
6 siblings, 0 replies; 11+ messages in thread
From: Gregory Price (Meta) @ 2026-11-10 15:04 UTC (permalink / raw)
To: linux-mm
Cc: linux-kernel, kernel-team, akpm, david, ljs, liam, vbabka, rppt,
surenb, mhocko, peterz, juri.lelli, vincent.guittot,
dietmar.eggemann, rostedt, bsegall, mgorman, mingo, vschneid,
kprateek.nayak, ziy, baolin.wang, nico.pache, ryan.roberts,
dev.jain, baohua, lance.yang, usama.arif, kas, gourry,
joshua.hahnjy, rakie.kim, ying.huang, matthew.brost, byungchul,
apopple, jannh, pfalcato, hannes, osalvador, raghavendra.kt,
stable
folio_can_map_prot_numa() derives folio eligibility from the global
balancing mode. The mode (normal, tiering, or combined) describes
which balancing mechanisms are enabled (placement vs promotion).
The global setting itself cannot describe the intent of an individual
protection walk because normal-mode tempers its scanning activity based
on a number of heuristics (read-only VMAs, activeness of VMA, etc).
In tiering or combined mode, applying these normal-mode optimizations
to an entire VMA is incorrect and breaks tiering.
- Opting VMAs out of scanning because they became inactive obviously
breaks tiering - because the intent is to identify when a VMA
becomes active. Applying it in tiering modes causes VMAs to become
permanently stranded on lower tiers.
- Opting Read-only file VMAs out of scanning is just incorrect for
tiering modes because its intent is to prevent bouncing between
sockets (east-west), while tiering controls tier migration
(north-south). The result is hot read-only files can overload
lower tier bandwidth.
Add MM_CP_PROT_NUMA_PROMO_ONLY and let task_numa_work() select the type
of walk from a single read of the balancing mode. Build the
change-protection flags there and pass them unchanged through
change_prot_numa() so its PTE/PMD paths use the same decision.
Have folio_can_map_prot_numa() derive the single-threaded private state at
the point of use instead of adding another precomputed boolean to the
protection-walk interface. This keeps the interface focused on scan intent
and avoids plumbing VMA-derived state beside cp_flags.
The tradeoff for the cleaner interface is an atomic mm_users read for each
folio, rather than once per PTE range. Check the promotion-only top-tier
exclusion bit first as a mild optimization.
The scan-intent interface is required by the following memory-tiering
fixes and must accompany them when backported.
Fixes: c574bbe91703 ("NUMA balancing: optimize page placement for memory tiering system")
Cc: stable@vger.kernel.org
Suggested-by: David Hildenbrand <david@kernel.org>
Link: https://lore.kernel.org/r/4d2853c9-edf4-4685-b186-7214ed6abd84@kernel.org
Assisted-by: LLM
Acked-by: David Hildenbrand (Arm) <david@kernel.org>
Signed-off-by: Gregory Price (Meta) <gourry@gourry.net>
---
include/linux/mm.h | 6 ++++--
kernel/sched/fair.c | 9 ++++++++-
mm/huge_memory.c | 3 +--
mm/internal.h | 4 ++--
mm/mempolicy.c | 26 ++++++++++++--------------
mm/mprotect.c | 7 +------
6 files changed, 28 insertions(+), 27 deletions(-)
diff --git a/include/linux/mm.h b/include/linux/mm.h
index 77b5d450fe45b..40de653041f92 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -3584,6 +3584,8 @@ int get_cmdline(struct task_struct *task, char *buffer, int buflen);
#define MM_CP_UFFD_RWP_RESOLVE (1UL << 5) /* resolve rwp */
#define MM_CP_UFFD_RWP_ALL (MM_CP_UFFD_RWP | \
MM_CP_UFFD_RWP_RESOLVE)
+/* Whether a MM_CP_PROT_NUMA change is for promotion only */
+#define MM_CP_PROT_NUMA_PROMO_ONLY (1UL << 6)
bool can_change_pte_writable(struct vm_area_struct *vma, unsigned long addr,
pte_t pte);
@@ -4959,8 +4961,8 @@ static inline void vma_set_page_prot(struct vm_area_struct *vma)
void vma_set_file(struct vm_area_struct *vma, struct file *file);
#ifdef CONFIG_NUMA_BALANCING
-unsigned long change_prot_numa(struct vm_area_struct *vma,
- unsigned long start, unsigned long end);
+unsigned long change_prot_numa(struct vm_area_struct *vma, unsigned long start,
+ unsigned long end, unsigned long cp_flags);
#endif
struct vm_area_struct *find_extend_vma_locked(struct mm_struct *,
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index cfc982205b98c..5d3d7b61d0257 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -4271,11 +4271,14 @@ static bool vma_is_accessed(struct mm_struct *mm, struct vm_area_struct *vma)
*/
static void task_numa_work(struct callback_head *work)
{
+ const unsigned int numab_mode = READ_ONCE(sysctl_numa_balancing_mode);
+ const bool balancing = numab_mode & NUMA_BALANCING_NORMAL;
unsigned long migrate, next_scan, now = jiffies;
struct task_struct *p = current;
struct mm_struct *mm = p->mm;
u64 runtime = p->se.sum_exec_runtime;
struct vm_area_struct *vma;
+ unsigned long cp_flags = MM_CP_PROT_NUMA;
unsigned long start, end;
unsigned long nr_pte_updates = 0;
long pages, virtpages;
@@ -4283,6 +4286,9 @@ static void task_numa_work(struct callback_head *work)
bool vma_pids_skipped;
bool vma_pids_forced = false;
+ if (!balancing)
+ cp_flags |= MM_CP_PROT_NUMA_PROMO_ONLY;
+
WARN_ON_ONCE(p != container_of(work, struct task_struct, numa_work));
work->next = work;
@@ -4459,7 +4465,8 @@ static void task_numa_work(struct callback_head *work)
start = max(start, vma->vm_start);
end = ALIGN(start + (pages << PAGE_SHIFT), HPAGE_SIZE);
end = min(end, vma->vm_end);
- nr_pte_updates = change_prot_numa(vma, start, end);
+ nr_pte_updates = change_prot_numa(vma, start, end,
+ cp_flags);
/*
* Try to scan sysctl_numa_balancing_size worth of
diff --git a/mm/huge_memory.c b/mm/huge_memory.c
index 0d589be136d21..54ece00a457f8 100644
--- a/mm/huge_memory.c
+++ b/mm/huge_memory.c
@@ -2776,8 +2776,7 @@ int change_huge_pmd(struct mmu_gather *tlb, struct vm_area_struct *vma,
if (is_huge_zero_pmd(*pmd))
goto unlock;
- if (!folio_can_map_prot_numa(pmd_folio(*pmd), vma,
- vma_is_single_threaded_private(vma)))
+ if (!folio_can_map_prot_numa(pmd_folio(*pmd), vma, cp_flags))
goto unlock;
}
/*
diff --git a/mm/internal.h b/mm/internal.h
index cd3675c044e0a..6e7c247781a16 100644
--- a/mm/internal.h
+++ b/mm/internal.h
@@ -1256,11 +1256,11 @@ static inline bool vma_is_single_threaded_private(struct vm_area_struct *vma)
#ifdef CONFIG_NUMA_BALANCING
bool folio_can_map_prot_numa(struct folio *folio, struct vm_area_struct *vma,
- bool is_private_single_threaded);
+ unsigned long cp_flags);
#else
static inline bool folio_can_map_prot_numa(struct folio *folio,
- struct vm_area_struct *vma, bool is_private_single_threaded)
+ struct vm_area_struct *vma, unsigned long cp_flags)
{
return false;
}
diff --git a/mm/mempolicy.c b/mm/mempolicy.c
index fb627b37afcbb..f0933e9b209c9 100644
--- a/mm/mempolicy.c
+++ b/mm/mempolicy.c
@@ -849,7 +849,7 @@ static int queue_folios_hugetlb(pte_t *pte, unsigned long hmask,
* folio_can_map_prot_numa() - check whether the folio can map prot numa
* @folio: The folio whose mapping considered for being made NUMA hintable
* @vma: The VMA that the folio belongs to.
- * @is_private_single_threaded: Is this a single-threaded private VMA or not
+ * @cp_flags: Flags describing the protection change
*
* This function checks to see if the folio actually indicates that
* we need to make the mapping one which causes a NUMA hinting fault,
@@ -859,7 +859,7 @@ static int queue_folios_hugetlb(pte_t *pte, unsigned long hmask,
* Return: True if the mapping of the folio needs to be changed, false otherwise.
*/
bool folio_can_map_prot_numa(struct folio *folio, struct vm_area_struct *vma,
- bool is_private_single_threaded)
+ unsigned long cp_flags)
{
int nid;
@@ -882,20 +882,16 @@ bool folio_can_map_prot_numa(struct folio *folio, struct vm_area_struct *vma,
if (folio_is_file_lru(folio) && folio_test_dirty(folio))
return false;
- /*
- * Don't mess with PTEs if folio is already on the node
- * a single-threaded process is running on.
- */
nid = folio_nid(folio);
- if (is_private_single_threaded && (nid == numa_node_id()))
+ /* Promotion-only scans do not mark top-tier folios. */
+ if ((cp_flags & MM_CP_PROT_NUMA_PROMO_ONLY) && node_is_toptier(nid))
return false;
/*
- * Skip scanning top tier node if normal numa
- * balancing is disabled
+ * Don't mess with PTEs if folio is already on the node
+ * a single-threaded process is running on.
*/
- if (!(sysctl_numa_balancing_mode & NUMA_BALANCING_NORMAL) &&
- node_is_toptier(nid))
+ if (vma_is_single_threaded_private(vma) && nid == numa_node_id())
return false;
if (folio_use_access_time(folio))
@@ -909,19 +905,21 @@ bool folio_can_map_prot_numa(struct folio *folio, struct vm_area_struct *vma,
* These are later cleared by a NUMA hinting fault. Depending on these
* faults, pages may be migrated for better NUMA placement.
*
+ * @cp_flags carries the NUMA scan policy through the protection walk.
+ *
* This is assuming that NUMA faults are handled using PROT_NONE. If
* an architecture makes a different choice, it will need further
* changes to the core.
*/
-unsigned long change_prot_numa(struct vm_area_struct *vma,
- unsigned long addr, unsigned long end)
+unsigned long change_prot_numa(struct vm_area_struct *vma, unsigned long addr,
+ unsigned long end, unsigned long cp_flags)
{
struct mmu_gather tlb;
long nr_updated;
tlb_gather_mmu(&tlb, vma->vm_mm);
- nr_updated = change_protection(&tlb, vma, addr, end, MM_CP_PROT_NUMA);
+ nr_updated = change_protection(&tlb, vma, addr, end, cp_flags);
if (nr_updated > 0) {
count_vm_numa_events(NUMA_PTE_UPDATES, nr_updated);
count_memcg_events_mm(vma->vm_mm, NUMA_PTE_UPDATES, nr_updated);
diff --git a/mm/mprotect.c b/mm/mprotect.c
index ef4e7188e24da..22049b265a289 100644
--- a/mm/mprotect.c
+++ b/mm/mprotect.c
@@ -335,7 +335,6 @@ static long change_pte_range(struct mmu_gather *tlb,
pte_t *pte, oldpte;
spinlock_t *ptl;
long pages = 0;
- bool is_private_single_threaded;
bool prot_numa = cp_flags & MM_CP_PROT_NUMA;
bool uffd_rwp = cp_flags & MM_CP_UFFD_RWP;
bool uffd_wp = cp_flags & MM_CP_UFFD_WP;
@@ -346,9 +345,6 @@ static long change_pte_range(struct mmu_gather *tlb,
if (!pte)
return -EAGAIN;
- if (prot_numa)
- is_private_single_threaded = vma_is_single_threaded_private(vma);
-
flush_tlb_batched_pending(vma->vm_mm);
lazy_mmu_mode_enable();
do {
@@ -383,8 +379,7 @@ static long change_pte_range(struct mmu_gather *tlb,
* must set protnone regardless of NUMA placement.
*/
if (prot_numa &&
- !folio_can_map_prot_numa(folio, vma,
- is_private_single_threaded)) {
+ !folio_can_map_prot_numa(folio, vma, cp_flags)) {
/* determine batch to skip */
nr_ptes = mprotect_folio_pte_batch(folio,
--
2.53.0-Meta
^ permalink raw reply [flat|nested] 11+ messages in thread* [PATCH v5 2/7] mm: allow shared folios to be promoted to a fast tier
2026-11-10 15:04 [PATCH v5 0/7] sched/numa: stop VMA scan filters from gating promotion Gregory Price (Meta)
2026-11-10 15:04 ` [PATCH v5 1/7] mm: support promotion-only NUMA hinting scans Gregory Price (Meta)
@ 2026-11-10 15:04 ` Gregory Price (Meta)
2026-11-10 15:04 ` [PATCH v5 3/7] sched/numa: scan read-only file mappings in tiering mode Gregory Price (Meta)
` (4 subsequent siblings)
6 siblings, 0 replies; 11+ messages in thread
From: Gregory Price (Meta) @ 2026-11-10 15:04 UTC (permalink / raw)
To: linux-mm
Cc: linux-kernel, kernel-team, akpm, david, ljs, liam, vbabka, rppt,
surenb, mhocko, peterz, juri.lelli, vincent.guittot,
dietmar.eggemann, rostedt, bsegall, mgorman, mingo, vschneid,
kprateek.nayak, ziy, baolin.wang, nico.pache, ryan.roberts,
dev.jain, baohua, lance.yang, usama.arif, kas, gourry,
joshua.hahnjy, rakie.kim, ying.huang, matthew.brost, byungchul,
apopple, jannh, pfalcato, hannes, osalvador, raghavendra.kt,
stable
NUMA balancing rejects shared copy-on-write folios and executable
file folios mapped by multiple processes to avoid east-west
migration bouncing. These checks break promotion from slow memory.
Allow such folios to participate when the folio being migrated is
a low-tier folio and the destination is top-tier (south->north).
This allows promotion, but prevents east-west or north->south
migrations (north->south is handled by reclaim demotion).
Keep the existing restrictions for ordinary placement and for
migrations that are not slow-to-top-tier promotions.
Rename folio_use_access_time() to folio_numab_promotable() so the name
captures the combined memory-tiering and source-tier test. The helper
retains its existing behavior.
Fixes: c574bbe91703 ("NUMA balancing: optimize page placement for memory tiering system")
Cc: stable@vger.kernel.org
Suggested-by: Zi Yan <ziy@nvidia.com>
Link: https://lore.kernel.org/r/DLHS4KFPQ86I.1J4LN3352RI71@nvidia.com
Assisted-by: LLM
Acked-by: David Hildenbrand (Arm) <david@kernel.org>
Signed-off-by: Gregory Price (Meta) <gourry@gourry.net>
---
include/linux/mm.h | 5 +++--
kernel/sched/fair.c | 2 +-
mm/memory-tiers.c | 9 ++++++---
mm/memory.c | 2 +-
mm/mempolicy.c | 11 ++++++++---
mm/migrate.c | 13 +++++++++----
6 files changed, 28 insertions(+), 14 deletions(-)
diff --git a/include/linux/mm.h b/include/linux/mm.h
index 40de653041f92..55817dea7307f 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -2647,7 +2647,7 @@ static inline void vma_set_access_pid_bit(struct vm_area_struct *vma)
}
}
-bool folio_use_access_time(struct folio *folio);
+bool folio_numab_promotable(struct folio *folio);
#else /* !CONFIG_NUMA_BALANCING */
static inline int folio_xchg_last_cpupid(struct folio *folio, int cpupid)
{
@@ -2701,7 +2701,8 @@ static inline bool cpupid_match_pid(struct task_struct *task, int cpupid)
static inline void vma_set_access_pid_bit(struct vm_area_struct *vma)
{
}
-static inline bool folio_use_access_time(struct folio *folio)
+
+static inline bool folio_numab_promotable(struct folio *folio)
{
return false;
}
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index 5d3d7b61d0257..e353d3d3012ea 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -2877,7 +2877,7 @@ bool should_numa_migrate_memory(struct task_struct *p, struct folio *folio,
* The pages in slow memory node should be migrated according
* to hot/cold instead of private/shared.
*/
- if (folio_use_access_time(folio)) {
+ if (folio_numab_promotable(folio)) {
struct pglist_data *pgdat;
unsigned long rate_limit;
unsigned int latency, th, def_th;
diff --git a/mm/memory-tiers.c b/mm/memory-tiers.c
index 7fc56a8c66518..0df54dc556f19 100644
--- a/mm/memory-tiers.c
+++ b/mm/memory-tiers.c
@@ -53,16 +53,19 @@ static const struct bus_type memory_tier_subsys = {
#ifdef CONFIG_NUMA_BALANCING
/**
- * folio_use_access_time - check if a folio reuses cpupid for page access time
+ * folio_numab_promotable - check if NUMA balancing can promote a folio
* @folio: folio to check
*
* folio's _last_cpupid field is repurposed by memory tiering. In memory
* tiering mode, cpupid of slow memory folio (not toptier memory) is used to
* record page access time.
*
- * Return: the folio _last_cpupid is used to record page access time
+ * If memory tiering is disabled, then lowtier has no appreciable meaning,
+ * so we return false (the folio should not be migrated on this distinction).
+ *
+ * Return: true if memory tiering can promote the folio.
*/
-bool folio_use_access_time(struct folio *folio)
+bool folio_numab_promotable(struct folio *folio)
{
return (sysctl_numa_balancing_mode & NUMA_BALANCING_MEMORY_TIERING) &&
!node_is_toptier(folio_nid(folio));
diff --git a/mm/memory.c b/mm/memory.c
index d477a4f986bc8..e70885653475a 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -6262,7 +6262,7 @@ int numa_migrate_check(struct folio *folio, struct vm_fault *vmf,
* For memory tiering mode, cpupid of slow memory page is used
* to record page access time. So use default value.
*/
- if (folio_use_access_time(folio))
+ if (folio_numab_promotable(folio))
*last_cpupid = (-1 & LAST_CPUPID_MASK);
else
*last_cpupid = folio_last_cpupid(folio);
diff --git a/mm/mempolicy.c b/mm/mempolicy.c
index f0933e9b209c9..49cfeab501b4a 100644
--- a/mm/mempolicy.c
+++ b/mm/mempolicy.c
@@ -866,8 +866,13 @@ bool folio_can_map_prot_numa(struct folio *folio, struct vm_area_struct *vma,
if (!folio || folio_is_zone_device(folio) || folio_test_ksm(folio))
return false;
- /* Also skip shared copy-on-write folios */
- if (vma_is_cow_mapping(vma) && folio_maybe_mapped_shared(folio))
+ /*
+ * Shared copy-on-write folios are poor east-west placement candidates.
+ * When tiering is enabled, folio_numab_promotable() identifies a
+ * low-tier folio that needs a hint fault for promotion.
+ */
+ if (vma_is_cow_mapping(vma) && folio_maybe_mapped_shared(folio) &&
+ !folio_numab_promotable(folio))
return false;
/* Folios are pinned and can't be migrated */
@@ -894,7 +899,7 @@ bool folio_can_map_prot_numa(struct folio *folio, struct vm_area_struct *vma,
if (vma_is_single_threaded_private(vma) && nid == numa_node_id())
return false;
- if (folio_use_access_time(folio))
+ if (folio_numab_promotable(folio))
folio_xchg_access_time(folio, jiffies_to_msecs(jiffies));
return true;
diff --git a/mm/migrate.c b/mm/migrate.c
index 28a8d7734735b..50a0a2e11a353 100644
--- a/mm/migrate.c
+++ b/mm/migrate.c
@@ -2704,14 +2704,19 @@ int migrate_misplaced_folio_prepare(struct folio *folio,
if (folio_is_file_lru(folio)) {
/*
- * Do not migrate file folios that are mapped in multiple
- * processes with execute permissions as they are probably
- * shared libraries.
+ * Limit east-west migration of file folios mapped in
+ * multiple processes with execute permissions as they
+ * are probably shared libraries (limits bouncing).
+ *
+ * If this is a low-tier folio, only migrate if the target
+ * node is toptier (this allows south->north migration while
+ * disallowing east-west migration between slow tiers).
*
* See folio_maybe_mapped_shared() on possible imprecision
* when we cannot easily detect if a folio is shared.
*/
- if ((vma->vm_flags & VM_EXEC) && folio_maybe_mapped_shared(folio))
+ if ((vma->vm_flags & VM_EXEC) && folio_maybe_mapped_shared(folio) &&
+ (!folio_numab_promotable(folio) || !node_is_toptier(node)))
return -EACCES;
/*
--
2.53.0-Meta
^ permalink raw reply [flat|nested] 11+ messages in thread* [PATCH v5 3/7] sched/numa: scan read-only file mappings in tiering mode
2026-11-10 15:04 [PATCH v5 0/7] sched/numa: stop VMA scan filters from gating promotion Gregory Price (Meta)
2026-11-10 15:04 ` [PATCH v5 1/7] mm: support promotion-only NUMA hinting scans Gregory Price (Meta)
2026-11-10 15:04 ` [PATCH v5 2/7] mm: allow shared folios to be promoted to a fast tier Gregory Price (Meta)
@ 2026-11-10 15:04 ` Gregory Price (Meta)
2026-11-10 15:04 ` [PATCH v5 4/7] sched/numa: separate VMA placement from scan continuation Gregory Price (Meta)
` (3 subsequent siblings)
6 siblings, 0 replies; 11+ messages in thread
From: Gregory Price (Meta) @ 2026-11-10 15:04 UTC (permalink / raw)
To: linux-mm
Cc: linux-kernel, kernel-team, akpm, david, ljs, liam, vbabka, rppt,
surenb, mhocko, peterz, juri.lelli, vincent.guittot,
dietmar.eggemann, rostedt, bsegall, mgorman, mingo, vschneid,
kprateek.nayak, ziy, baolin.wang, nico.pache, ryan.roberts,
dev.jain, baohua, lance.yang, usama.arif, kas, gourry,
joshua.hahnjy, rakie.kim, ying.huang, matthew.brost, byungchul,
apopple, jannh, pfalcato, hannes, osalvador, raghavendra.kt,
stable
Commit 4591ce4f2d22 ("sched/numa: Do not trap hinting faults for
shared libraries") excludes file-backed read-only VMAs from NUMA
hint faulting to prevent east-west placement bouncing.
This filter hides hot file folios on slow memory from promotion.
Scan those VMAs when tiering is enabled, but make their scans promotion
only to retain the existing restriction. Keep the historical VMA
predicate unchanged for backport-ability.
Decide the walk type for each VMA from the balancing-mode snapshot
taken in task_numa_work(). A VMA gets a placement scan only when normal
balancing is enabled and it is not a read-only file mapping. Otherwise
the walk is promotion-only, and without tiering the VMA is skipped as
before.
On a host with 768 GB of DRAM and 256 GB of CXL memory running two
database services using ~430GB each, 169 MB of their shared 185 MB
main binary accumulated on CXL and was permanently stuck there.
With the change, the binary tier residency tracks its runtime hotness.
Fixes: c574bbe91703 ("NUMA balancing: optimize page placement for memory tiering system")
Cc: stable@vger.kernel.org
Assisted-by: LLM
Acked-by: David Hildenbrand (Arm) <david@kernel.org>
Signed-off-by: Gregory Price (Meta) <gourry@gourry.net>
---
kernel/sched/fair.c | 32 ++++++++++++++++++++++----------
1 file changed, 22 insertions(+), 10 deletions(-)
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index e353d3d3012ea..c0b7271f1bce8 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -4272,22 +4272,21 @@ static bool vma_is_accessed(struct mm_struct *mm, struct vm_area_struct *vma)
static void task_numa_work(struct callback_head *work)
{
const unsigned int numab_mode = READ_ONCE(sysctl_numa_balancing_mode);
+ const bool tiering = numab_mode & NUMA_BALANCING_MEMORY_TIERING;
const bool balancing = numab_mode & NUMA_BALANCING_NORMAL;
unsigned long migrate, next_scan, now = jiffies;
struct task_struct *p = current;
struct mm_struct *mm = p->mm;
u64 runtime = p->se.sum_exec_runtime;
struct vm_area_struct *vma;
- unsigned long cp_flags = MM_CP_PROT_NUMA;
+ unsigned long cp_flags;
unsigned long start, end;
unsigned long nr_pte_updates = 0;
long pages, virtpages;
struct vma_iterator vmi;
bool vma_pids_skipped;
bool vma_pids_forced = false;
-
- if (!balancing)
- cp_flags |= MM_CP_PROT_NUMA_PROMO_ONLY;
+ bool placement_scan;
WARN_ON_ONCE(p != container_of(work, struct task_struct, numa_work));
@@ -4375,13 +4374,22 @@ static void task_numa_work(struct callback_head *work)
}
/*
- * Shared library pages mapped by multiple processes are not
- * migrated as it is expected they are cache replicated. Avoid
- * hinting faults in read-only file-backed mappings or the vDSO
- * as migrating the pages will be of marginal benefit.
+ * Shared library pages mapped by multiple processes are limited
+ * to south->north migrations as it is expected they are cache
+ * replicated. The benefit of east-west migration in this case
+ * is at best marginal and may be harmful due to TLB/cache
+ * invalidation.
+ *
+ * Allow promotion as a cold page incurring many cache-misses
+ * under cache pressure can drive considerable bandwidth.
*/
- if (!vma->vm_mm ||
- (vma->vm_file && (vma->vm_flags & (VM_READ|VM_WRITE)) == (VM_READ))) {
+ placement_scan = balancing;
+ if (placement_scan && vma->vm_file) {
+ if ((vma->vm_flags & (VM_READ | VM_WRITE)) == VM_READ)
+ placement_scan = false;
+ }
+
+ if (!vma->vm_mm || (!placement_scan && !tiering)) {
trace_sched_skip_vma_numa(mm, vma, NUMAB_SKIP_SHARED_RO);
continue;
}
@@ -4461,6 +4469,10 @@ static void task_numa_work(struct callback_head *work)
continue;
}
+ cp_flags = MM_CP_PROT_NUMA;
+ if (!placement_scan)
+ cp_flags |= MM_CP_PROT_NUMA_PROMO_ONLY;
+
do {
start = max(start, vma->vm_start);
end = ALIGN(start + (pages << PAGE_SHIFT), HPAGE_SIZE);
--
2.53.0-Meta
^ permalink raw reply [flat|nested] 11+ messages in thread* [PATCH v5 4/7] sched/numa: separate VMA placement from scan continuation
2026-11-10 15:04 [PATCH v5 0/7] sched/numa: stop VMA scan filters from gating promotion Gregory Price (Meta)
` (2 preceding siblings ...)
2026-11-10 15:04 ` [PATCH v5 3/7] sched/numa: scan read-only file mappings in tiering mode Gregory Price (Meta)
@ 2026-11-10 15:04 ` Gregory Price (Meta)
2026-11-10 15:04 ` [PATCH v5 5/7] sched/numa: scan PID-inactive VMAs for promotion Gregory Price (Meta)
` (2 subsequent siblings)
6 siblings, 0 replies; 11+ messages in thread
From: Gregory Price (Meta) @ 2026-11-10 15:04 UTC (permalink / raw)
To: linux-mm
Cc: linux-kernel, kernel-team, akpm, david, ljs, liam, vbabka, rppt,
surenb, mhocko, peterz, juri.lelli, vincent.guittot,
dietmar.eggemann, rostedt, bsegall, mgorman, mingo, vschneid,
kprateek.nayak, ziy, baolin.wang, nico.pache, ryan.roberts,
dev.jain, baohua, lance.yang, usama.arif, kas, gourry,
joshua.hahnjy, rakie.kim, ying.huang, matthew.brost, byungchul,
apopple, jannh, pfalcato, hannes, osalvador, raghavendra.kt,
stable
vma_is_accessed() returns true both when a VMA needs placement
(east-west) sampling and when a VMA is already mid-scan across
multiple scan windows.
Callers cannot distinguish placement eligibility from scan progress.
Rename the helper to vma_needs_placement_scan() and handle continuation
state in task_numa_work(). Record whether an in-progress VMA scan is
promotion-only so a scan resumed in a later scan window finishes with
the policy it started with. The zeroed initial state places no
restriction, so a resumed scan that finds freshly allocated NUMAB state
falls back to the default policy.
This separation allows PID-inactive VMAs to be scanned for promotion
without treating scan continuation as evidence of placement eligibility.
It is a prerequisite for the following fix and must accompany it when
backported.
NUMA hint faults always target the faulting CPU's node, which is
top-tier by definition (any node with a CPU is top-tier), so a low-tier
folio can only be promoted, never migrated between slow tiers.
Fixes: fc137c0ddab2 ("sched/numa: enhance vma scanning logic")
Cc: stable@vger.kernel.org
Assisted-by: LLM
Acked-by: David Hildenbrand (Arm) <david@kernel.org>
Signed-off-by: Gregory Price (Meta) <gourry@gourry.net>
---
include/linux/mm_types.h | 6 ++++++
kernel/sched/fair.c | 40 ++++++++++++++++++++++++----------------
2 files changed, 30 insertions(+), 16 deletions(-)
diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
index 1ed35d258d2e3..045b47f4b0463 100644
--- a/include/linux/mm_types.h
+++ b/include/linux/mm_types.h
@@ -803,6 +803,12 @@ struct vma_numab_state {
* A VMA is not eligible for scanning if prev_scan_seq == numa_scan_seq
*/
int prev_scan_seq;
+
+ /*
+ * The in-progress scan of this VMA is promotion-only.
+ * Resumed scans finish with the policy they started with.
+ */
+ bool promo_only;
};
#ifdef __HAVE_PFNMAP_TRACKING
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index c0b7271f1bce8..778ba0ed34fe8 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -4226,8 +4226,9 @@ static void reset_ptenuma_scan(struct task_struct *p)
p->mm->numa_scan_offset = 0;
}
-static bool vma_is_accessed(struct mm_struct *mm, struct vm_area_struct *vma)
+static bool vma_needs_placement_scan(struct vm_area_struct *vma)
{
+ struct mm_struct *mm = vma->vm_mm;
unsigned long pids;
/*
* Allow unconditional access first two times, so that all the (pages)
@@ -4242,15 +4243,6 @@ static bool vma_is_accessed(struct mm_struct *mm, struct vm_area_struct *vma)
if (test_bit(hash_32(current->pid, ilog2(BITS_PER_LONG)), &pids))
return true;
- /*
- * Complete a scan that has already started regardless of PID access, or
- * some VMAs may never be scanned in multi-threaded applications:
- */
- if (mm->numa_scan_offset > vma->vm_start) {
- trace_sched_skip_vma_numa(mm, vma, NUMAB_SKIP_IGNORE_PID);
- return true;
- }
-
/*
* This vma has not been accessed for a while, and if the number
* the threads in the same process is low, which means no other
@@ -4274,6 +4266,7 @@ static void task_numa_work(struct callback_head *work)
const unsigned int numab_mode = READ_ONCE(sysctl_numa_balancing_mode);
const bool tiering = numab_mode & NUMA_BALANCING_MEMORY_TIERING;
const bool balancing = numab_mode & NUMA_BALANCING_NORMAL;
+ bool pid_scan_allowed, placement_due, scan_started;
unsigned long migrate, next_scan, now = jiffies;
struct task_struct *p = current;
struct mm_struct *mm = p->mm;
@@ -4460,15 +4453,29 @@ static void task_numa_work(struct callback_head *work)
}
/*
- * Do not scan the VMA if task has not accessed it, unless no other
- * VMA candidate exists.
+ * Do not scan the VMA if a task has not accessed it, unless no other
+ * VMA candidate exists. If a scan is already in-progress, finish it,
+ * but track continuation separately from starting a new one.
*/
- if (!vma_pids_forced && !vma_is_accessed(mm, vma)) {
- vma_pids_skipped = true;
- trace_sched_skip_vma_numa(mm, vma, NUMAB_SKIP_PID_INACTIVE);
- continue;
+ placement_due = vma_needs_placement_scan(vma);
+ scan_started = mm->numa_scan_offset > vma->vm_start;
+ pid_scan_allowed = vma_pids_forced || placement_due;
+
+ if (!pid_scan_allowed) {
+ if (scan_started) {
+ trace_sched_skip_vma_numa(mm, vma, NUMAB_SKIP_IGNORE_PID);
+ } else {
+ vma_pids_skipped = true;
+ trace_sched_skip_vma_numa(mm, vma, NUMAB_SKIP_PID_INACTIVE);
+ continue;
+ }
}
+ /* Keep scan policy stable while processing a VMA in chunks. */
+ if (scan_started && vma->numab_state->promo_only)
+ placement_scan = false;
+
+ vma->numab_state->promo_only = !placement_scan;
cp_flags = MM_CP_PROT_NUMA;
if (!placement_scan)
cp_flags |= MM_CP_PROT_NUMA_PROMO_ONLY;
@@ -4501,6 +4508,7 @@ static void task_numa_work(struct callback_head *work)
/* VMA scan is complete, do not scan until next sequence. */
vma->numab_state->prev_scan_seq = mm->numa_scan_seq;
+ vma->numab_state->promo_only = false;
/*
* Only force scan within one VMA at a time, to limit the
--
2.53.0-Meta
^ permalink raw reply [flat|nested] 11+ messages in thread* [PATCH v5 5/7] sched/numa: scan PID-inactive VMAs for promotion
2026-11-10 15:04 [PATCH v5 0/7] sched/numa: stop VMA scan filters from gating promotion Gregory Price (Meta)
` (3 preceding siblings ...)
2026-11-10 15:04 ` [PATCH v5 4/7] sched/numa: separate VMA placement from scan continuation Gregory Price (Meta)
@ 2026-11-10 15:04 ` Gregory Price (Meta)
2026-11-10 15:04 ` [PATCH v5 6/7] mm: use BIT() for change_protection() flags Gregory Price (Meta)
2026-11-10 15:04 ` [PATCH v5 7/7] mm: use VMA flag helpers in NUMA balancing Gregory Price (Meta)
6 siblings, 0 replies; 11+ messages in thread
From: Gregory Price (Meta) @ 2026-11-10 15:04 UTC (permalink / raw)
To: linux-mm
Cc: linux-kernel, kernel-team, akpm, david, ljs, liam, vbabka, rppt,
surenb, mhocko, peterz, juri.lelli, vincent.guittot,
dietmar.eggemann, rostedt, bsegall, mgorman, mingo, vschneid,
kprateek.nayak, ziy, baolin.wang, nico.pache, ryan.roberts,
dev.jain, baohua, lance.yang, usama.arif, kas, gourry,
joshua.hahnjy, rakie.kim, ying.huang, matthew.brost, byungchul,
apopple, jannh, pfalcato, hannes, osalvador, raghavendra.kt,
stable
Commit fc137c0ddab2 ("sched/numa: enhance vma scanning logic") skips
VMAs without recent PID activity. Since only NUMA hint faults record
that activity, the filter can suppress the fault needed to promote hot
slow-tier memory.
Let memory tiering bypass the PID scan filter. In combined mode, use the
placement decision to restrict top-tier sampling to VMAs that need it,
while inactive VMAs still receive promotion-only scans.
Make this decision only when a VMA scan starts. placement_due is
evaluated for the current task, and a partially scanned VMA is usually
resumed by a different thread, so re-evaluating it for each chunk would
demote in-progress placement scans and starve large VMAs of placement
sampling in multi-threaded processes.
Track the last completed placement scan separately from scans of any
kind. Promotion-only scans still update prev_scan_seq, but do not
advance the placement-starvation horizon.
On a host with 768 GB of DRAM and 256 GB of CXL memory, one large shmem
VMA consumed most scanning activity, while 2,537 other VMAs covering
84 GB were skipped as inactive. One stand-out result: a hot 20 GB hash
table ended up trapped entirely on CXL and drove CXL bandwidth
utilization beyond sustainable levels - resulting in a large regression.
With this series, the hot hash table ends up split evenly between DRAM
and CXL, tier residency tracks runtime load, and CXL bandwidth
utilization drops from 45GB/s (maxed) to 5-10GB/s, while DRAM bandwidth
utilization increases from ~200GB/s to 250GB/s+, resulting in major
throughput improvements for the database workload.
Fixes: fc137c0ddab2 ("sched/numa: enhance vma scanning logic")
Cc: stable@vger.kernel.org
Assisted-by: LLM
Acked-by: David Hildenbrand (Arm) <david@kernel.org>
Signed-off-by: Gregory Price (Meta) <gourry@gourry.net>
---
include/linux/mm_types.h | 7 +++++++
kernel/sched/fair.c | 31 +++++++++++++++++++++++++------
2 files changed, 32 insertions(+), 6 deletions(-)
diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
index 045b47f4b0463..94350132d1803 100644
--- a/include/linux/mm_types.h
+++ b/include/linux/mm_types.h
@@ -804,6 +804,13 @@ struct vma_numab_state {
*/
int prev_scan_seq;
+ /*
+ * MM scan sequence ID when the VMA was last scanned for placement.
+ * The starvation horizon in vma_needs_placement_scan() counts against
+ * this, so promotion-only scans cannot postpone placement indefinitely.
+ */
+ int prev_placement_scan_seq;
+
/*
* The in-progress scan of this VMA is promotion-only.
* Resumed scans finish with the policy they started with.
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index 778ba0ed34fe8..279bf12b74b64 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -4249,7 +4249,7 @@ static bool vma_needs_placement_scan(struct vm_area_struct *vma)
* threads can help scan this vma, force a vma scan.
*/
if (READ_ONCE(mm->numa_scan_seq) >
- (vma->numab_state->prev_scan_seq + get_nr_threads(current)))
+ (vma->numab_state->prev_placement_scan_seq + get_nr_threads(current)))
return true;
return false;
@@ -4423,7 +4423,8 @@ static void task_numa_work(struct callback_head *work)
* to prevent VMAs being skipped prematurely on the
* first scan:
*/
- vma->numab_state->prev_scan_seq = mm->numa_scan_seq - 1;
+ vma->numab_state->prev_scan_seq = mm->numa_scan_seq - 1;
+ vma->numab_state->prev_placement_scan_seq = mm->numa_scan_seq - 1;
}
/*
@@ -4456,10 +4457,13 @@ static void task_numa_work(struct callback_head *work)
* Do not scan the VMA if a task has not accessed it, unless no other
* VMA candidate exists. If a scan is already in-progress, finish it,
* but track continuation separately from starting a new one.
+ *
+ * The PID filter must not gate promotion. Allow PID-inactive VMAs
+ * to proceed when memory tiering is enabled.
*/
placement_due = vma_needs_placement_scan(vma);
scan_started = mm->numa_scan_offset > vma->vm_start;
- pid_scan_allowed = vma_pids_forced || placement_due;
+ pid_scan_allowed = tiering || vma_pids_forced || placement_due;
if (!pid_scan_allowed) {
if (scan_started) {
@@ -4471,9 +4475,18 @@ static void task_numa_work(struct callback_head *work)
}
}
- /* Keep scan policy stable while processing a VMA in chunks. */
- if (scan_started && vma->numab_state->promo_only)
+ /*
+ * Keep scan policy stable while processing a VMA in chunks.
+ * placement_due is evaluated for the current task, and a
+ * different thread will usually resume the scan. Finish an
+ * in-progress scan with the policy it started with.
+ */
+ if (scan_started) {
+ if (vma->numab_state->promo_only)
+ placement_scan = false;
+ } else if (tiering && !placement_due) {
placement_scan = false;
+ }
vma->numab_state->promo_only = !placement_scan;
cp_flags = MM_CP_PROT_NUMA;
@@ -4506,8 +4519,14 @@ static void task_numa_work(struct callback_head *work)
cond_resched();
} while (end != vma->vm_end);
- /* VMA scan is complete, do not scan until next sequence. */
+ /*
+ * VMA scan is complete, do not scan until next sequence.
+ * A promotion-only scan did not cover top-tier folios, so it
+ * does not count towards the placement-scan starvation check.
+ */
vma->numab_state->prev_scan_seq = mm->numa_scan_seq;
+ if (placement_scan)
+ vma->numab_state->prev_placement_scan_seq = mm->numa_scan_seq;
vma->numab_state->promo_only = false;
/*
--
2.53.0-Meta
^ permalink raw reply [flat|nested] 11+ messages in thread* [PATCH v5 6/7] mm: use BIT() for change_protection() flags
2026-11-10 15:04 [PATCH v5 0/7] sched/numa: stop VMA scan filters from gating promotion Gregory Price (Meta)
` (4 preceding siblings ...)
2026-11-10 15:04 ` [PATCH v5 5/7] sched/numa: scan PID-inactive VMAs for promotion Gregory Price (Meta)
@ 2026-11-10 15:04 ` Gregory Price (Meta)
2026-11-10 15:16 ` Lorenzo Stoakes (ARM)
2026-11-10 15:04 ` [PATCH v5 7/7] mm: use VMA flag helpers in NUMA balancing Gregory Price (Meta)
6 siblings, 1 reply; 11+ messages in thread
From: Gregory Price (Meta) @ 2026-11-10 15:04 UTC (permalink / raw)
To: linux-mm
Cc: linux-kernel, kernel-team, akpm, david, ljs, liam, vbabka, rppt,
surenb, mhocko, peterz, juri.lelli, vincent.guittot,
dietmar.eggemann, rostedt, bsegall, mgorman, mingo, vschneid,
kprateek.nayak, ziy, baolin.wang, nico.pache, ryan.roberts,
dev.jain, baohua, lance.yang, usama.arif, kas, gourry,
joshua.hahnjy, rakie.kim, ying.huang, matthew.brost, byungchul,
apopple, jannh, pfalcato, hannes, osalvador, raghavendra.kt
The MM_CP_* flags are individual bits expressed as open-coded shifts.
Use BIT() consistently for the complete block.
No functional change intended.
Suggested-by: David Hildenbrand <david@kernel.org>
Link: https://lore.kernel.org/r/9b5e1573-6fd8-48a5-a274-a6e7ff83d2fe@kernel.org
Assisted-by: LLM
Acked-by: David Hildenbrand (Arm) <david@kernel.org>
Signed-off-by: Gregory Price (Meta) <gourry@gourry.net>
---
include/linux/mm.h | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/include/linux/mm.h b/include/linux/mm.h
index 55817dea7307f..da22750faddbd 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -3572,21 +3572,21 @@ int get_cmdline(struct task_struct *task, char *buffer, int buflen);
* because something (e.g., COW, uffd-wp) blocks that from happening for all
* PTEs automatically in a writable mapping.
*/
-#define MM_CP_TRY_CHANGE_WRITABLE (1UL << 0)
+#define MM_CP_TRY_CHANGE_WRITABLE BIT(0)
/* Whether this protection change is for NUMA hints */
-#define MM_CP_PROT_NUMA (1UL << 1)
+#define MM_CP_PROT_NUMA BIT(1)
/* Whether this change is for write protecting */
-#define MM_CP_UFFD_WP (1UL << 2) /* do wp */
-#define MM_CP_UFFD_WP_RESOLVE (1UL << 3) /* Resolve wp */
+#define MM_CP_UFFD_WP BIT(2) /* do wp */
+#define MM_CP_UFFD_WP_RESOLVE BIT(3) /* Resolve wp */
#define MM_CP_UFFD_WP_ALL (MM_CP_UFFD_WP | \
MM_CP_UFFD_WP_RESOLVE)
/* Whether this change is for uffd RWP */
-#define MM_CP_UFFD_RWP (1UL << 4) /* do rwp */
-#define MM_CP_UFFD_RWP_RESOLVE (1UL << 5) /* resolve rwp */
+#define MM_CP_UFFD_RWP BIT(4) /* do rwp */
+#define MM_CP_UFFD_RWP_RESOLVE BIT(5) /* resolve rwp */
#define MM_CP_UFFD_RWP_ALL (MM_CP_UFFD_RWP | \
MM_CP_UFFD_RWP_RESOLVE)
/* Whether a MM_CP_PROT_NUMA change is for promotion only */
-#define MM_CP_PROT_NUMA_PROMO_ONLY (1UL << 6)
+#define MM_CP_PROT_NUMA_PROMO_ONLY BIT(6)
bool can_change_pte_writable(struct vm_area_struct *vma, unsigned long addr,
pte_t pte);
--
2.53.0-Meta
^ permalink raw reply [flat|nested] 11+ messages in thread* Re: [PATCH v5 6/7] mm: use BIT() for change_protection() flags
2026-11-10 15:04 ` [PATCH v5 6/7] mm: use BIT() for change_protection() flags Gregory Price (Meta)
@ 2026-11-10 15:16 ` Lorenzo Stoakes (ARM)
0 siblings, 0 replies; 11+ messages in thread
From: Lorenzo Stoakes (ARM) @ 2026-11-10 15:16 UTC (permalink / raw)
To: Gregory Price (Meta)
Cc: linux-mm, linux-kernel, kernel-team, akpm, david, liam, vbabka,
rppt, surenb, mhocko, peterz, juri.lelli, vincent.guittot,
dietmar.eggemann, rostedt, bsegall, mgorman, mingo, vschneid,
kprateek.nayak, ziy, baolin.wang, nico.pache, ryan.roberts,
dev.jain, baohua, lance.yang, usama.arif, kas, joshua.hahnjy,
rakie.kim, ying.huang, matthew.brost, byungchul, apopple, jannh,
pfalcato, hannes, osalvador, raghavendra.kt
On Tue, Nov 10, 2026 at 10:04:45AM -0500, Gregory Price (Meta) wrote:
> The MM_CP_* flags are individual bits expressed as open-coded shifts.
> Use BIT() consistently for the complete block.
>
> No functional change intended.
>
> Suggested-by: David Hildenbrand <david@kernel.org>
> Link: https://lore.kernel.org/r/9b5e1573-6fd8-48a5-a274-a6e7ff83d2fe@kernel.org
> Assisted-by: LLM
> Acked-by: David Hildenbrand (Arm) <david@kernel.org>
> Signed-off-by: Gregory Price (Meta) <gourry@gourry.net>
Reviewed-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
> ---
> include/linux/mm.h | 14 +++++++-------
> 1 file changed, 7 insertions(+), 7 deletions(-)
>
> diff --git a/include/linux/mm.h b/include/linux/mm.h
> index 55817dea7307f..da22750faddbd 100644
> --- a/include/linux/mm.h
> +++ b/include/linux/mm.h
> @@ -3572,21 +3572,21 @@ int get_cmdline(struct task_struct *task, char *buffer, int buflen);
> * because something (e.g., COW, uffd-wp) blocks that from happening for all
> * PTEs automatically in a writable mapping.
> */
> -#define MM_CP_TRY_CHANGE_WRITABLE (1UL << 0)
> +#define MM_CP_TRY_CHANGE_WRITABLE BIT(0)
> /* Whether this protection change is for NUMA hints */
> -#define MM_CP_PROT_NUMA (1UL << 1)
> +#define MM_CP_PROT_NUMA BIT(1)
> /* Whether this change is for write protecting */
> -#define MM_CP_UFFD_WP (1UL << 2) /* do wp */
> -#define MM_CP_UFFD_WP_RESOLVE (1UL << 3) /* Resolve wp */
> +#define MM_CP_UFFD_WP BIT(2) /* do wp */
> +#define MM_CP_UFFD_WP_RESOLVE BIT(3) /* Resolve wp */
> #define MM_CP_UFFD_WP_ALL (MM_CP_UFFD_WP | \
> MM_CP_UFFD_WP_RESOLVE)
> /* Whether this change is for uffd RWP */
> -#define MM_CP_UFFD_RWP (1UL << 4) /* do rwp */
> -#define MM_CP_UFFD_RWP_RESOLVE (1UL << 5) /* resolve rwp */
> +#define MM_CP_UFFD_RWP BIT(4) /* do rwp */
> +#define MM_CP_UFFD_RWP_RESOLVE BIT(5) /* resolve rwp */
> #define MM_CP_UFFD_RWP_ALL (MM_CP_UFFD_RWP | \
> MM_CP_UFFD_RWP_RESOLVE)
> /* Whether a MM_CP_PROT_NUMA change is for promotion only */
> -#define MM_CP_PROT_NUMA_PROMO_ONLY (1UL << 6)
> +#define MM_CP_PROT_NUMA_PROMO_ONLY BIT(6)
>
> bool can_change_pte_writable(struct vm_area_struct *vma, unsigned long addr,
> pte_t pte);
> --
> 2.53.0-Meta
>
--
Cheers, Lorenzo
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v5 7/7] mm: use VMA flag helpers in NUMA balancing
2026-11-10 15:04 [PATCH v5 0/7] sched/numa: stop VMA scan filters from gating promotion Gregory Price (Meta)
` (5 preceding siblings ...)
2026-11-10 15:04 ` [PATCH v5 6/7] mm: use BIT() for change_protection() flags Gregory Price (Meta)
@ 2026-11-10 15:04 ` Gregory Price (Meta)
2026-11-10 15:15 ` Lorenzo Stoakes (ARM)
6 siblings, 1 reply; 11+ messages in thread
From: Gregory Price (Meta) @ 2026-11-10 15:04 UTC (permalink / raw)
To: linux-mm
Cc: linux-kernel, kernel-team, akpm, david, ljs, liam, vbabka, rppt,
surenb, mhocko, peterz, juri.lelli, vincent.guittot,
dietmar.eggemann, rostedt, bsegall, mgorman, mingo, vschneid,
kprateek.nayak, ziy, baolin.wang, nico.pache, ryan.roberts,
dev.jain, baohua, lance.yang, usama.arif, kas, gourry,
joshua.hahnjy, rakie.kim, ying.huang, matthew.brost, byungchul,
apopple, jannh, pfalcato, hannes, osalvador, raghavendra.kt
Use vma_test() instead of accessing vm_flags directly in the NUMA
balancing code touched by the preceding fixes. Keep the existing flag
predicates unchanged.
No functional change intended.
Suggested-by: Lorenzo Stoakes <ljs@kernel.org>
Link: https://lore.kernel.org/r/aq1FDhy00epXXtgd@gremlin
Assisted-by: LLM
Acked-by: David Hildenbrand (Arm) <david@kernel.org>
Signed-off-by: Gregory Price (Meta) <gourry@gourry.net>
---
kernel/sched/fair.c | 2 +-
mm/internal.h | 2 +-
mm/migrate.c | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index 279bf12b74b64..cf3e3657e9533 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -4378,7 +4378,7 @@ static void task_numa_work(struct callback_head *work)
*/
placement_scan = balancing;
if (placement_scan && vma->vm_file) {
- if ((vma->vm_flags & (VM_READ | VM_WRITE)) == VM_READ)
+ if (vma_test(vma, VMA_READ_BIT) && !vma_test(vma, VMA_WRITE_BIT))
placement_scan = false;
}
diff --git a/mm/internal.h b/mm/internal.h
index 6e7c247781a16..7fc34d084697e 100644
--- a/mm/internal.h
+++ b/mm/internal.h
@@ -1248,7 +1248,7 @@ size_t splice_folio_into_pipe(struct pipe_inode_info *pipe,
static inline bool vma_is_single_threaded_private(struct vm_area_struct *vma)
{
- if (vma->vm_flags & VM_SHARED)
+ if (vma_test(vma, VMA_SHARED_BIT))
return false;
return atomic_read(&vma->vm_mm->mm_users) == 1;
diff --git a/mm/migrate.c b/mm/migrate.c
index 50a0a2e11a353..796d98d1c116c 100644
--- a/mm/migrate.c
+++ b/mm/migrate.c
@@ -2715,7 +2715,7 @@ int migrate_misplaced_folio_prepare(struct folio *folio,
* See folio_maybe_mapped_shared() on possible imprecision
* when we cannot easily detect if a folio is shared.
*/
- if ((vma->vm_flags & VM_EXEC) && folio_maybe_mapped_shared(folio) &&
+ if (vma_test(vma, VMA_EXEC_BIT) && folio_maybe_mapped_shared(folio) &&
(!folio_numab_promotable(folio) || !node_is_toptier(node)))
return -EACCES;
--
2.53.0-Meta
^ permalink raw reply [flat|nested] 11+ messages in thread* Re: [PATCH v5 7/7] mm: use VMA flag helpers in NUMA balancing
2026-11-10 15:04 ` [PATCH v5 7/7] mm: use VMA flag helpers in NUMA balancing Gregory Price (Meta)
@ 2026-11-10 15:15 ` Lorenzo Stoakes (ARM)
2026-11-10 16:24 ` Gregory Price (Meta)
0 siblings, 1 reply; 11+ messages in thread
From: Lorenzo Stoakes (ARM) @ 2026-11-10 15:15 UTC (permalink / raw)
To: Gregory Price (Meta)
Cc: linux-mm, linux-kernel, kernel-team, akpm, david, liam, vbabka,
rppt, surenb, mhocko, peterz, juri.lelli, vincent.guittot,
dietmar.eggemann, rostedt, bsegall, mgorman, mingo, vschneid,
kprateek.nayak, ziy, baolin.wang, nico.pache, ryan.roberts,
dev.jain, baohua, lance.yang, usama.arif, kas, joshua.hahnjy,
rakie.kim, ying.huang, matthew.brost, byungchul, apopple, jannh,
pfalcato, hannes, osalvador, raghavendra.kt
On Tue, Nov 10, 2026 at 10:04:46AM -0500, Gregory Price (Meta) wrote:
> Use vma_test() instead of accessing vm_flags directly in the NUMA
> balancing code touched by the preceding fixes. Keep the existing flag
> predicates unchanged.
>
> No functional change intended.
>
> Suggested-by: Lorenzo Stoakes <ljs@kernel.org>
What a legend! :P
> Link: https://lore.kernel.org/r/aq1FDhy00epXXtgd@gremlin
> Assisted-by: LLM
This tag is no longer required, in fact I think checkpatch should moan about it
now.
> Acked-by: David Hildenbrand (Arm) <david@kernel.org>
> Signed-off-by: Gregory Price (Meta) <gourry@gourry.net>
Of course, LGTM ;)
This clashes with my latest VMA flags series, but the resolutions should be
trivial.
Reviewed-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
> ---
> kernel/sched/fair.c | 2 +-
> mm/internal.h | 2 +-
> mm/migrate.c | 2 +-
> 3 files changed, 3 insertions(+), 3 deletions(-)
>
> diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
> index 279bf12b74b64..cf3e3657e9533 100644
> --- a/kernel/sched/fair.c
> +++ b/kernel/sched/fair.c
> @@ -4378,7 +4378,7 @@ static void task_numa_work(struct callback_head *work)
> */
> placement_scan = balancing;
> if (placement_scan && vma->vm_file) {
> - if ((vma->vm_flags & (VM_READ | VM_WRITE)) == VM_READ)
> + if (vma_test(vma, VMA_READ_BIT) && !vma_test(vma, VMA_WRITE_BIT))
> placement_scan = false;
> }
>
> diff --git a/mm/internal.h b/mm/internal.h
> index 6e7c247781a16..7fc34d084697e 100644
> --- a/mm/internal.h
> +++ b/mm/internal.h
> @@ -1248,7 +1248,7 @@ size_t splice_folio_into_pipe(struct pipe_inode_info *pipe,
>
> static inline bool vma_is_single_threaded_private(struct vm_area_struct *vma)
> {
> - if (vma->vm_flags & VM_SHARED)
> + if (vma_test(vma, VMA_SHARED_BIT))
> return false;
>
> return atomic_read(&vma->vm_mm->mm_users) == 1;
> diff --git a/mm/migrate.c b/mm/migrate.c
> index 50a0a2e11a353..796d98d1c116c 100644
> --- a/mm/migrate.c
> +++ b/mm/migrate.c
> @@ -2715,7 +2715,7 @@ int migrate_misplaced_folio_prepare(struct folio *folio,
> * See folio_maybe_mapped_shared() on possible imprecision
> * when we cannot easily detect if a folio is shared.
> */
> - if ((vma->vm_flags & VM_EXEC) && folio_maybe_mapped_shared(folio) &&
> + if (vma_test(vma, VMA_EXEC_BIT) && folio_maybe_mapped_shared(folio) &&
> (!folio_numab_promotable(folio) || !node_is_toptier(node)))
> return -EACCES;
>
> --
> 2.53.0-Meta
>
--
Cheers, Lorenzo
^ permalink raw reply [flat|nested] 11+ messages in thread* Re: [PATCH v5 7/7] mm: use VMA flag helpers in NUMA balancing
2026-11-10 15:15 ` Lorenzo Stoakes (ARM)
@ 2026-11-10 16:24 ` Gregory Price (Meta)
0 siblings, 0 replies; 11+ messages in thread
From: Gregory Price (Meta) @ 2026-11-10 16:24 UTC (permalink / raw)
To: Lorenzo Stoakes (ARM)
Cc: Gregory Price (Meta),
linux-mm, linux-kernel, kernel-team, akpm, david, liam, vbabka,
rppt, surenb, mhocko, peterz, juri.lelli, vincent.guittot,
dietmar.eggemann, rostedt, bsegall, mgorman, mingo, vschneid,
kprateek.nayak, ziy, baolin.wang, nico.pache, ryan.roberts,
dev.jain, baohua, lance.yang, usama.arif, kas, joshua.hahnjy,
rakie.kim, ying.huang, matthew.brost, byungchul, apopple, jannh,
pfalcato, hannes, osalvador, raghavendra.kt
On 2026-11-10 15:15 +0000, Lorenzo Stoakes (ARM) wrote:
> On Tue, Nov 10, 2026 at 10:04:46AM -0500, Gregory Price (Meta) wrote:
> > Use vma_test() instead of accessing vm_flags directly in the NUMA
> > balancing code touched by the preceding fixes. Keep the existing flag
> > predicates unchanged.
> >
> > No functional change intended.
> >
> > Suggested-by: Lorenzo Stoakes <ljs@kernel.org>
>
> What a legend! :P
>
> > Link: https://lore.kernel.org/r/aq1FDhy00epXXtgd@gremlin
> > Assisted-by: LLM
>
> This tag is no longer required, in fact I think checkpatch should moan about it
> now.
>
I cargo-culted it from prior revisions, feel free to drop it.
Cheers :]
--
~Gregory
^ permalink raw reply [flat|nested] 11+ messages in thread