* linux-next: manual merge of the mm-nonmm-unstable tree with the mm tree
@ 2026-09-15 13:32 Mark Brown
[not found] ` <CACXp-XqfPNgOU2=s0sDOUer9U9MXzwCVLZ1hK=tP8m-q_eNu3w@mail.gmail.com>
0 siblings, 1 reply; 9+ messages in thread
From: Mark Brown @ 2026-09-15 13:32 UTC (permalink / raw)
To: Andrew Morton
Cc: Ackerley Tng, Hongfu Li, Huaisheng Ye, Jaewook You, Jinmeng Zhou,
Jinmeng Zhou, Kaitao Cheng, Linux Kernel Mailing List,
Linux Next Mailing List, Longlong Xia, Muchun Song, Muchun Song,
Zhao Li
[-- Attachment #1: Type: text/plain, Size: 18095 bytes --]
Hi all,
Today's linux-next merge of the mm-nonmm-unstable tree got a conflict in:
mm/hugetlb.c
between commits:
e66eed4176426 ("mm/hugetlb: do not dissolve gigantic pages without runtime support")
f7b1987904bf9 ("mm/hugetlb: fix max-only subpool accounting on alloc_hugetlb_folio failure")
6121655f693f7 ("mm/hugetlb: fix resv_huge_pages double decrement in memfd error path")
ed62771da8508 ("mm/hugetlb: use hugetlb_vmemmap_optimizable() in boolean contexts")
e1b6218149da3 ("mm/sparse-vmemmap: rename HVO order macros")
06f8705bfa9f1 ("mm/hugetlb: switch HugeTLB to section-based vmemmap optimization")
27da1e7e0591e ("mm/hugetlb: remove HUGE_BOOTMEM_HVO")
becc26de43819 ("mm/hugetlb: remove HUGE_BOOTMEM_CMA")
661924944464c ("mm/hugetlb: localize struct huge_bootmem_page")
0906df87f1c72 ("mm/hugetlb: localize HUGE_BOOTMEM_ZONES_VALID")
e5215e10fed91 ("mm/hugetlb: warn instead of silently bailing gigantic pages without runtime support")
858fd521da151 ("mm/hugetlb: preserve source surplus accounting during demotion")
ff2738512b84e ("mm/hugetlb: cap demotion at currently available free pages")
2abb1820003ec ("mm: hugetlb: return -ENOSPC on memcg charge failure")
4387fdebcd7cb ("mm: hugetlb: drop refcount before freeing on memcg charge failure")
1767ef423cd19 ("mm/hugetlb: charge folios to the target mm's memcg")
23becfb18688d ("mm/hugetlb: fix subpool minimum reservation rollback")
0bd66c5296ed7 ("mm/hugetlb: account for allowed nodes when gathering surplus pages")
755b5b6349080 ("mm/sparse-vmemmap: move vmemmap optimization helpers to a public header")
fc451c02dc6af ("mm/hugetlb: preserve mremap address delta when skipping page tables")
from the mm tree and commits:
09a2c8a46cf62 ("mm/hugetlb: do not dissolve gigantic pages without runtime support")
1ff1504af83d9 ("mm/hugetlb: fix max-only subpool accounting on alloc_hugetlb_folio failure")
770243d49fe62 ("mm/hugetlb: preserve mremap address delta when skipping page tables")
from the mm-nonmm-unstable tree (more duplicated commits).
I fixed it up (see below) and can carry the fix as necessary. This
is now fixed as far as linux-next is concerned, but any non trivial
conflicts should be mentioned to your upstream maintainer when your tree
is submitted for merging. You may also want to consider cooperating
with the maintainer of the conflicting tree to minimise any particularly
complex conflicts.
diff --combined mm/hugetlb.c
index d3a0650ff6905,49ffbcb54f8c0..0000000000000
--- a/mm/hugetlb.c
+++ b/mm/hugetlb.c
@@@ -38,7 -38,6 +38,7 @@@
#include <linux/mm_inline.h>
#include <linux/padata.h>
#include <linux/pgalloc.h>
+#include <linux/vmemmap-optimization.h>
#include <asm/page.h>
#include <asm/tlb.h>
@@@ -55,20 -54,12 +55,20 @@@
#include "mm_init.h"
#include <linux/page-isolation.h>
+#define HUGE_BOOTMEM_ZONES_VALID BIT(0)
+
+struct huge_bootmem_page {
+ struct list_head list;
+ struct hstate *hstate;
+ unsigned long flags;
+};
+
int hugetlb_max_hstate __read_mostly;
unsigned int default_hstate_idx;
struct hstate hstates[HUGE_MAX_HSTATE];
__initdata nodemask_t hugetlb_bootmem_nodes;
-__initdata struct list_head huge_boot_pages[MAX_NUMNODES];
+static struct list_head huge_boot_pages[MAX_NUMNODES] __initdata;
/*
* Due to ordering constraints across the init code for various
@@@ -125,7 -116,6 +125,7 @@@ struct mutex *hugetlb_fault_mutex_tabl
/* Forward declaration */
static int hugetlb_acct_memory(struct hstate *h, long delta);
+static unsigned int allowed_mems_nr(struct hstate *h);
static void hugetlb_vma_lock_free(struct vm_area_struct *vma);
static void hugetlb_vma_lock_alloc(struct vm_area_struct *vma);
static void __hugetlb_vma_unlock_write_free(struct vm_area_struct *vma);
@@@ -1397,11 -1387,8 +1397,11 @@@ void remove_hugetlb_folio(struct hstat
VM_BUG_ON_FOLIO(hugetlb_cgroup_from_folio_rsvd(folio), folio);
lockdep_assert_held(&hugetlb_lock);
- if (hstate_is_gigantic_no_runtime(h))
+ if (hstate_is_gigantic_no_runtime(h)) {
+ /* Callers must filter gigantic_no_runtime upstream. */
+ VM_WARN_ON_ONCE(1);
return;
+ }
list_del(&folio->lru);
@@@ -1462,11 -1449,8 +1462,11 @@@ static void __update_and_free_hugetlb_f
{
bool clear_flag = folio_test_hugetlb_vmemmap_optimized(folio);
- if (hstate_is_gigantic_no_runtime(h))
+ if (hstate_is_gigantic_no_runtime(h)) {
+ /* Callers must filter gigantic_no_runtime upstream. */
+ VM_WARN_ON_ONCE(1);
return;
+ }
/*
* If we don't know which subpages are hwpoisoned, we can't free
@@@ -2203,10 -2187,8 +2203,10 @@@ struct folio *alloc_hugetlb_folio_reser
folio = dequeue_hugetlb_folio_nodemask(h, gfp_mask, preferred_nid,
nmask);
- if (folio)
+ if (folio) {
+ folio_set_hugetlb_restore_reserve(folio);
h->resv_huge_pages--;
+ }
spin_unlock_irq(&hugetlb_lock);
return folio;
@@@ -2253,19 -2235,6 +2253,19 @@@ static nodemask_t *policy_mbind_nodemas
return NULL;
}
+/*
+ * Reservations are globally accounted, but they must also be backed by free
+ * pages on nodes allowed by the current cpuset and MPOL_BIND policy.
+ */
+static long surplus_pages_needed(struct hstate *h, long delta, long allocated)
+{
+ long global_free = (long)h->free_huge_pages + allocated;
+ long allowed_free = (long)allowed_mems_nr(h) + allocated;
+
+ return max((long)h->resv_huge_pages + delta - global_free,
+ delta - allowed_free);
+}
+
/*
* Increase the hugetlb pool such that it can accommodate a reservation
* of size 'delta'.
@@@ -2288,7 -2257,7 +2288,7 @@@ static int gather_surplus_pages(struct
alloc_nodemask = cpuset_current_mems_allowed;
lockdep_assert_held(&hugetlb_lock);
- needed = (h->resv_huge_pages + delta) - h->free_huge_pages;
+ needed = surplus_pages_needed(h, delta, 0);
if (needed <= 0) {
h->resv_huge_pages += delta;
return 0;
@@@ -2319,10 -2288,11 +2319,10 @@@ retry
/*
* After retaking hugetlb_lock, we need to recalculate 'needed'
- * because either resv_huge_pages or free_huge_pages may have changed.
+ * because either resv_huge_pages or the free page counts may have changed.
*/
spin_lock_irq(&hugetlb_lock);
- needed = (h->resv_huge_pages + delta) -
- (h->free_huge_pages + allocated);
+ needed = surplus_pages_needed(h, delta, allocated);
if (needed > 0) {
if (alloc_ok)
goto retry;
@@@ -2857,7 -2827,6 +2857,7 @@@ void wait_for_freed_hugetlb_folios(void
* hugetlb_alloc_folio - Allocate a hugetlb folio.
* @h: Hugetlb state control block.
* @mpoli: Interpreted memory policy to use for allocation.
+ * @mm: Memory descriptor of the allocation target.
* @alloc_flags: Flags controlling the allocation behavior.
*
* Allocates a hugetlb folio and handles cgroup charging and global hstate
@@@ -2865,10 -2834,10 +2865,10 @@@
*
* Return: A pointer to the allocated folio, or an ERR_PTR on failure.
* -ENOSPC if cgroup charging fails or no folio is available.
- * -ENOMEM if mem cgroup charging fails.
*/
struct folio *hugetlb_alloc_folio(struct hstate *h,
- struct mempolicy_interpreted *mpoli, u8 alloc_flags)
+ struct mempolicy_interpreted *mpoli, struct mm_struct *mm,
+ u8 alloc_flags)
{
bool charge_hugetlb_cgroup_rsvd = alloc_flags &
HUGETLB_ALLOC_CHARG_CGROUP_RSVD;
@@@ -2923,8 -2892,7 +2923,8 @@@
spin_unlock_irq(&hugetlb_lock);
- ret = mem_cgroup_charge_hugetlb(folio, gfp | __GFP_RETRY_MAYFAIL);
+ ret = mem_cgroup_charge_hugetlb(folio, mm,
+ gfp | __GFP_RETRY_MAYFAIL);
/*
* Unconditionally increment NR_HUGETLB here because if
* mem_cgroup_charge_hugetlb failed, freeing the page will
@@@ -2933,17 -2901,13 +2933,17 @@@
lruvec_stat_mod_folio(folio, NR_HUGETLB, nr_pages);
if (ret == -ENOMEM) {
- free_huge_folio(folio);
+ folio_put(folio);
/*
* Skip uncharging hugetlb_cgroup since the charges
* were committed to the folio and freeing the folio
* would have cleared those up.
*/
- return ERR_PTR(ret);
+ /*
+ * Return -ENOSPC, since retrying the fault is futile:
+ * the OOM killer is not triggered for HugeTLB.
+ */
+ return ERR_PTR(-ENOSPC);
}
return folio;
@@@ -3067,7 -3031,7 +3067,7 @@@ struct folio *alloc_hugetlb_folio(struc
.nodemask = nodemask,
};
- folio = hugetlb_alloc_folio(h, &mpoli, alloc_flags);
+ folio = hugetlb_alloc_folio(h, &mpoli, vma->vm_mm, alloc_flags);
mpol_cond_put(mpol);
@@@ -3177,7 -3141,7 +3177,7 @@@ static bool __init alloc_bootmem_huge_p
*/
INIT_LIST_HEAD(&m->list);
m->hstate = h;
- m->flags = hugetlb_early_cma(h) ? HUGE_BOOTMEM_CMA : 0;
+ m->flags = 0;
/* CMA pages: zone-crossing is validated in hugetlb_cma_reserve(). */
if (!hugetlb_early_cma(h) &&
@@@ -3195,7 -3159,6 +3195,7 @@@
} else {
list_add_tail(&m->list, &huge_boot_pages[nid]);
m->flags |= HUGE_BOOTMEM_ZONES_VALID;
+ hugetlb_vmemmap_optimize_bootmem_page(pfn, huge_page_order(h));
/*
* Only initialize the head struct page in memmap_init_reserved_pages,
* rest of the struct pages will be initialized by the HugeTLB
@@@ -3253,6 -3216,16 +3253,6 @@@ static void __init hugetlb_folio_init_v
prep_compound_head(&folio->page, huge_page_order(h));
}
-static bool __init hugetlb_bootmem_page_prehvo(struct huge_bootmem_page *m)
-{
- return m->flags & HUGE_BOOTMEM_HVO;
-}
-
-static bool __init hugetlb_bootmem_page_earlycma(struct huge_bootmem_page *m)
-{
- return m->flags & HUGE_BOOTMEM_CMA;
-}
-
/*
* memblock-allocated pageblocks might not have the migrate type set
* if marked with the 'noinit' flag. Set it to the default (MIGRATE_MOVABLE)
@@@ -3322,7 -3295,6 +3322,7 @@@ static void __init gather_bootmem_preal
list_for_each_entry_safe(m, tm, &huge_boot_pages[nid], list) {
struct page *page = virt_to_page(m);
struct folio *folio = (void *)page;
+ const unsigned long pfn = folio_pfn(folio);
h = m->hstate;
/*
@@@ -3340,9 -3312,16 +3340,9 @@@
HUGETLB_VMEMMAP_RESERVE_PAGES);
init_new_hugetlb_folio(folio);
- if (hugetlb_bootmem_page_prehvo(m))
- /*
- * If pre-HVO was done, just set the
- * flag, the HVO code will then skip
- * this folio.
- */
+ if (vmemmap_optimizable_order(pfn_to_section_compound_order(pfn)))
folio_set_hugetlb_vmemmap_optimized(folio);
-
- if (hugetlb_bootmem_page_earlycma(m))
- folio_set_hugetlb_cma(folio);
+ section_set_compound_order_range(pfn, folio_nr_pages(folio), 0);
list_add(&folio->lru, &folio_list);
@@@ -3354,9 -3333,7 +3354,9 @@@
* For CMA pages, this is done in init_cma_pageblock
* (via hugetlb_bootmem_init_migratetype), so skip it here.
*/
- if (!folio_test_hugetlb_cma(folio))
+ if (hugetlb_early_cma(h))
+ folio_set_hugetlb_cma(folio);
+ else
adjust_managed_page_count(page, pages_per_huge_page(h));
cond_resched();
}
@@@ -3385,6 -3362,31 +3385,6 @@@ void __init hugetlb_bootmem_struct_page
.max_threads = num_node_state(N_MEMORY),
.numa_aware = true,
};
-#ifdef CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP
- struct zone *zone;
-
- for_each_zone(zone) {
- for (int i = 0; i < NR_VMEMMAP_TAILS; i++) {
- struct page *tail, *p;
- unsigned int order;
-
- tail = zone->vmemmap_tails[i];
- if (!tail)
- continue;
-
- order = i + VMEMMAP_TAIL_MIN_ORDER;
- p = page_to_virt(tail);
- /*
- * prep_and_add_bootmem_folios() can access pageblock
- * flags on bootmem HugeTLB pages, so initialize the
- * shared tail struct pages here before bootmem folios
- * start using them.
- */
- for (int j = 0; j < PAGE_SIZE / sizeof(struct page); j++)
- init_compound_tail(p + j, NULL, order, zone);
- }
- }
-#endif
padata_do_multithreaded(&job);
}
@@@ -3434,7 -3436,7 +3434,7 @@@ static void __init hugetlb_hstate_alloc
folio = only_alloc_fresh_hugetlb_folio(h, gfp_mask, nid,
&node_states[N_MEMORY], NULL);
if (!folio && !list_empty(&folio_list) &&
- hugetlb_vmemmap_optimizable_size(h)) {
+ hugetlb_vmemmap_optimizable(h)) {
prep_and_add_allocated_folios(h, &folio_list);
INIT_LIST_HEAD(&folio_list);
folio = only_alloc_fresh_hugetlb_folio(h, gfp_mask, nid,
@@@ -3503,7 -3505,7 +3503,7 @@@ static void __init hugetlb_pages_alloc_
for (i = 0; i < num; ++i) {
struct folio *folio;
- if (hugetlb_vmemmap_optimizable_size(h) &&
+ if (hugetlb_vmemmap_optimizable(h) &&
(si_mem_available() == 0) && !list_empty(&folio_list)) {
prep_and_add_allocated_folios(h, &folio_list);
INIT_LIST_HEAD(&folio_list);
@@@ -4030,7 -4032,6 +4030,7 @@@ long demote_pool_huge_page(struct hstat
struct hstate *dst;
long rc = 0;
long nr_demoted = 0;
+ long nr_persistent = 0;
lockdep_assert_held(&hugetlb_lock);
@@@ -4043,60 -4044,22 +4043,60 @@@
for_each_node_mask_to_free(src, nr_nodes, node, nodes_allowed) {
LIST_HEAD(list);
+ LIST_HEAD(surplus_list);
struct folio *folio, *next;
+ unsigned long nr_available, nr_target;
+
+ /*
+ * Re-check available each node batch: the previous
+ * batch released hugetlb_lock for vmemmap restore/split,
+ * and a new reservation could have been added in that
+ * window, shrinking the budget. available is global
+ * (resv is not per-node), so 0 means no node can
+ * contribute -- stop the whole scan.
+ */
+ nr_available = available_huge_pages(src);
+ if (!nr_available)
+ break;
+
+ /*
+ * Cap this batch at the current budget; expressed as a
+ * cumulative stop point because nr_demoted is running.
+ */
+ nr_target = nr_demoted + min_t(unsigned long,
+ nr_to_demote - nr_demoted, nr_available);
list_for_each_entry_safe(folio, next, &src->hugepage_freelists[node], lru) {
+ bool adjust_surplus;
+
if (folio_test_hwpoison(folio))
continue;
- remove_hugetlb_folio(src, folio, false);
- list_add(&folio->lru, &list);
+ /* Surplus accounting is maintained per node, not per folio. */
+ adjust_surplus = src->surplus_huge_pages_node[node] > 0;
+ remove_hugetlb_folio(src, folio, adjust_surplus);
+ list_add(&folio->lru, adjust_surplus ? &surplus_list : &list);
+ if (!adjust_surplus)
+ nr_persistent++;
- if (++nr_demoted == nr_to_demote)
+ if (++nr_demoted == nr_target)
break;
}
+ if (list_empty(&list) && list_empty(&surplus_list))
+ continue;
+
spin_unlock_irq(&hugetlb_lock);
- rc = demote_free_hugetlb_folios(src, dst, &list);
+ if (!list_empty(&list))
+ rc = demote_free_hugetlb_folios(src, dst, &list);
+ if (!list_empty(&surplus_list)) {
+ long tmp_rc;
+
+ tmp_rc = demote_free_hugetlb_folios(src, dst, &surplus_list);
+ if (rc >= 0)
+ rc = tmp_rc;
+ }
spin_lock_irq(&hugetlb_lock);
@@@ -4104,14 -4067,6 +4104,14 @@@
list_del(&folio->lru);
add_hugetlb_folio(src, folio, false);
+ nr_demoted--;
+ nr_persistent--;
+ }
+
+ list_for_each_entry_safe(folio, next, &surplus_list, lru) {
+ list_del(&folio->lru);
+ add_hugetlb_folio(src, folio, true);
+
nr_demoted--;
}
@@@ -4123,7 -4078,7 +4123,7 @@@
* Not absolutely necessary, but for consistency update max_huge_pages
* based on pool changes for the demoted page.
*/
- src->max_huge_pages -= nr_demoted;
+ src->max_huge_pages -= nr_persistent;
dst->max_huge_pages += nr_demoted << (huge_page_order(src) - huge_page_order(dst));
if (rc < 0)
@@@ -5226,21 -5181,21 +5226,21 @@@ int move_hugetlb_page_tables(struct vm_
hugetlb_vma_lock_write(vma);
i_mmap_lock_write(mapping);
for (; old_addr < old_end; old_addr += sz, new_addr += sz) {
- const unsigned long offset_to_last_entry =
+ const unsigned long remaining_size =
(old_addr | last_addr_mask) - old_addr;
src_pte = hugetlb_walk(vma, old_addr, sz);
if (!src_pte) {
- old_addr += offset_to_last_entry;
- new_addr += offset_to_last_entry;
+ old_addr += remaining_size;
+ new_addr += remaining_size;
continue;
}
if (huge_pte_none(huge_ptep_get(mm, old_addr, src_pte)))
continue;
if (huge_pmd_unshare(&tlb, vma, old_addr, src_pte)) {
- old_addr += offset_to_last_entry;
- new_addr += offset_to_last_entry;
+ old_addr += remaining_size;
+ new_addr += remaining_size;
continue;
}
@@@ -6878,6 -6833,15 +6878,6 @@@ long hugetlb_reserve_pages(struct inod
out_put_pages:
spool_resv = chg - gbl_reserve;
- if (spool_resv) {
- /* put sub pool's reservation back, chg - gbl_reserve */
- gbl_resv = hugepage_subpool_put_pages(spool, spool_resv);
- /*
- * subpool's reserved pages can not be put back due to race,
- * return to hstate.
- */
- hugetlb_acct_memory(h, -gbl_resv);
- }
/* Restore used_hpages for pages that failed global reservation */
if (gbl_reserve && spool) {
unsigned long flags;
@@@ -6887,15 -6851,6 +6887,15 @@@
spool->used_hpages -= gbl_reserve;
unlock_or_release_subpool(spool, flags);
}
+ if (spool_resv) {
+ /* put sub pool's reservation back, chg - gbl_reserve */
+ gbl_resv = hugepage_subpool_put_pages(spool, spool_resv);
+ /*
+ * subpool's reserved pages can not be put back due to race,
+ * return to hstate.
+ */
+ hugetlb_acct_memory(h, -gbl_resv);
+ }
out_uncharge_cgroup:
hugetlb_cgroup_uncharge_cgroup_rsvd(hstate_index(h),
chg * pages_per_huge_page(h), h_cg);
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: linux-next: manual merge of the mm-nonmm-unstable tree with the mm tree
[not found] ` <CACXp-XqfPNgOU2=s0sDOUer9U9MXzwCVLZ1hK=tP8m-q_eNu3w@mail.gmail.com>
@ 2026-09-15 19:03 ` David Hildenbrand (Arm)
0 siblings, 0 replies; 9+ messages in thread
From: David Hildenbrand (Arm) @ 2026-09-15 19:03 UTC (permalink / raw)
To: arang, Mark Brown; +Cc: Andrew Morton, Muchun Song, linux-next, linux-kernel
On 9/15/26 20:41, arang wrote:
> Hi Mark,
>
> Thanks for resolving the conflict.
>
> Adding David, since the conflicting commits are different revisions of
> the same fix. The latest public revision is v3:
>
> https://lore.kernel.org/linux-mm/20260914132352.472-1-jaewook376@gmail.com/
> <https://lore.kernel.org/linux-mm/20260914132352.472-1-jaewook376@gmail.com/>
>
> Following David's review, v3 renames remaining_size to
> offset_to_last_entry. David also provided:
>
> Acked-by: David Hildenbrand (Arm) <david@kernel.org <mailto:david@kernel.org>>
>
> The current linux-next merge result appears to retain remaining_size from
> the older revision. There is no functional difference, but please
> preserve the v3 offset_to_last_entry name and David's Ack in the final
> version.
It's a temporary glitch, because we have the mm-unstable tree coming into -next
in two differently dated versions. Tomorrow it should be resolved.
And we're discussing how to avoid that in the future :)
--
Cheers,
David
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: linux-next: manual merge of the mm-nonmm-unstable tree with the mm tree
2026-09-15 18:23 ` David Hildenbrand (Arm)
@ 2026-09-15 18:41 ` Mark Brown
0 siblings, 0 replies; 9+ messages in thread
From: Mark Brown @ 2026-09-15 18:41 UTC (permalink / raw)
To: David Hildenbrand (Arm)
Cc: Andrew Morton, Krishna Iyer, Linux Kernel Mailing List,
Linux Next Mailing List, SJ Park, Mike Rapoport, Vlastimil Babka
[-- Attachment #1: Type: text/plain, Size: 746 bytes --]
On Tue, Sep 15, 2026 at 08:23:27PM +0200, David Hildenbrand (Arm) wrote:
> I think the problem is that
> akpm/mm-nonmm-unstable
> is based on
> akpm/mm-hotfixes-unstable (and akpm/mm-hotfixes-stable)
> Which only go into the MM-next tree.
> So if a commit in akpm/mm-hotfixes-unstable changes (and a stale one is in
> mm/for-next-fixes), it comes it through two trees in different forms.
Yes, rebasing a branch that's being merged elsewhere is going to cause
issues like this.
> Likely we'd need a separate akpm branch for NON-MM hotfixes ... or I'll take
> care of all MM hotfixes through the mm/core tree.
Either of those should work well, I can easily add an extra nonmm
hotfixes branch - let me know if that's what you decide to do.
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: linux-next: manual merge of the mm-nonmm-unstable tree with the mm tree
2026-09-15 17:59 ` Mark Brown
@ 2026-09-15 18:23 ` David Hildenbrand (Arm)
2026-09-15 18:41 ` Mark Brown
0 siblings, 1 reply; 9+ messages in thread
From: David Hildenbrand (Arm) @ 2026-09-15 18:23 UTC (permalink / raw)
To: Mark Brown, Andrew Morton
Cc: Krishna Iyer, Linux Kernel Mailing List, Linux Next Mailing List,
SJ Park, Mike Rapoport, Vlastimil Babka
On 9/15/26 19:59, Mark Brown wrote:
> On Tue, Sep 15, 2026 at 10:54:13AM -0700, Andrew Morton wrote:
>> On Tue, 15 Sep 2026 14:32:22 +0100 Mark Brown <broonie@kernel.org> wrote:
>
>>> from the mm tree and commit:
>
>> hm, "mm tree" isn't a branch name. I assume "mm-unstable" was meant.
>
> No, this is David's tree at:
>
> https://git.kernel.org/pub/scm/linux/kernel/git/mm/linux.git for-next
>
> As requested your nonmm branches are the only ones left from your tree,
> they were replaced by the above and the fixes branch at:
>
> https://git.kernel.org/pub/scm/linux/kernel/git/mm/linux.git for-next-fixes
>
Yes, only the MM portion go through the MM tree.
I think the problem is that
akpm/mm-nonmm-unstable
is based on
akpm/mm-hotfixes-unstable (and akpm/mm-hotfixes-stable)
Which only go into the MM-next tree.
So if a commit in akpm/mm-hotfixes-unstable changes (and a stale one is in
mm/for-next-fixes), it comes it through two trees in different forms.
Likely we'd need a separate akpm branch for NON-MM hotfixes ... or I'll take
care of all MM hotfixes through the mm/core tree.
--
Cheers,
David
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: linux-next: manual merge of the mm-nonmm-unstable tree with the mm tree
2026-09-15 17:54 ` Andrew Morton
@ 2026-09-15 17:59 ` Mark Brown
2026-09-15 18:23 ` David Hildenbrand (Arm)
0 siblings, 1 reply; 9+ messages in thread
From: Mark Brown @ 2026-09-15 17:59 UTC (permalink / raw)
To: Andrew Morton
Cc: Krishna Iyer, Linux Kernel Mailing List, Linux Next Mailing List,
SJ Park, David Hildenbrand, Mike Rapoport, Vlastimil Babka
[-- Attachment #1: Type: text/plain, Size: 722 bytes --]
On Tue, Sep 15, 2026 at 10:54:13AM -0700, Andrew Morton wrote:
> On Tue, 15 Sep 2026 14:32:22 +0100 Mark Brown <broonie@kernel.org> wrote:
> > from the mm tree and commit:
> hm, "mm tree" isn't a branch name. I assume "mm-unstable" was meant.
No, this is David's tree at:
https://git.kernel.org/pub/scm/linux/kernel/git/mm/linux.git for-next
As requested your nonmm branches are the only ones left from your tree,
they were replaced by the above and the fixes branch at:
https://git.kernel.org/pub/scm/linux/kernel/git/mm/linux.git for-next-fixes
Something got messed up with the CCs there, there were two other similar
conflicts today. I'll double check what the scripts are doing next time
this comes up.
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: linux-next: manual merge of the mm-nonmm-unstable tree with the mm tree
2026-09-15 13:32 Mark Brown
@ 2026-09-15 17:54 ` Andrew Morton
2026-09-15 17:59 ` Mark Brown
0 siblings, 1 reply; 9+ messages in thread
From: Andrew Morton @ 2026-09-15 17:54 UTC (permalink / raw)
To: Mark Brown
Cc: Krishna Iyer, Linux Kernel Mailing List, Linux Next Mailing List,
SJ Park
On Tue, 15 Sep 2026 14:32:22 +0100 Mark Brown <broonie@kernel.org> wrote:
> Hi all,
>
> Today's linux-next merge of the mm-nonmm-unstable tree got a conflict in:
>
> mm/damon/vaddr.c
>
> between commits:
>
> 6acc47b87e9f0 ("mm/damon/vaddr: avoid hw-driven pte updates during damon_hugetlb_mkold()")
> 24f6b1a0ab44a ("mm/damon/vaddr: respect folio end for DAMOS_STAT")
> f256012bb6a1c ("mm/damon/vaddr: respect folio end for DAMOS_MIGRATE_{HOT,COLD}")
> fa5b61fcf5c9d ("mm/damon/vaddr: remove a debug message")
> ed6700504e3f5 ("mm/damon: move damon_hugetlb_mkold() from vaddr to ops-common")
> 88ac1f509a7e6 ("mm/damon/vaddr: support prep_probes")
> 68ce5caa141c4 ("mm/damon/vaddr: support apply_probe")
> 45e6bd6ac2868 ("mm/damon/vaddr: extend apply_probes() for hugetlb")
> 4144f08e72a48 ("mm/damon/vaddr: support pgidle_unset probe filter type")
> 6a00e5447c45e ("mm/damon/vaddr: support DAMON_FILTER_TYPE_PGIDLE_SET")
> 3dabc621c0cfd ("mm/damon/core: extend probe_hits_wsum() for moving sum based calculation")
>
> from the mm tree and commit:
hm, "mm tree" isn't a branch name. I assume "mm-unstable" was meant.
>
> 566380a1497eb ("mm/damon/vaddr: avoid hw-driven pte updates during damon_hugetlb_mkold()")
>
> from the mm-nonmm-unstable tree.
>
Something went wrong here. "mm/damon/vaddr: avoid hw-driven pte
updates during damon_hugetlb_mkold()" is in mm-hotfixes-unstable.
mm-hotfixes-unstable is merged into mm-unstable and mm-nonmm-unstable.
^ permalink raw reply [flat|nested] 9+ messages in thread
* linux-next: manual merge of the mm-nonmm-unstable tree with the mm tree
@ 2026-09-15 13:32 Mark Brown
0 siblings, 0 replies; 9+ messages in thread
From: Mark Brown @ 2026-09-15 13:32 UTC (permalink / raw)
To: Andrew Morton
Cc: JonasZhou, Linux Kernel Mailing List, Linux Next Mailing List,
Uladzislau Rezki
[-- Attachment #1: Type: text/plain, Size: 6284 bytes --]
Hi all,
Today's linux-next merge of the mm-nonmm-unstable tree got a conflict in:
mm/vmalloc.c
between commits:
f71e94f83c2a2 ("mm/vmalloc: ase dedicated unbound workqueues for vmap drain")
662d2275549a8 ("mm/vmalloc: avoid false sharing with drain_vmap_work")
from the mm tree and commit:
75b41adb019c4 ("mm/vmalloc: ase dedicated unbound workqueues for vmap drain")
from the mm-nonmm-unstable tree.
I fixed it up (see below) and can carry the fix as necessary. This
is now fixed as far as linux-next is concerned, but any non trivial
conflicts should be mentioned to your upstream maintainer when your tree
is submitted for merging. You may also want to consider cooperating
with the maintainer of the conflicting tree to minimise any particularly
complex conflicts.
diff --combined mm/vmalloc.c
index 859e6d2d57a38,89c327a6ce7d9..0000000000000
--- a/mm/vmalloc.c
+++ b/mm/vmalloc.c
@@@ -1090,12 -1090,7 +1090,12 @@@ RB_DECLARE_CALLBACKS_MAX(static, free_v
static void reclaim_and_purge_vmap_areas(void);
static BLOCKING_NOTIFIER_HEAD(vmap_notify_list);
static void drain_vmap_area_work(struct work_struct *work);
-static DECLARE_WORK(drain_vmap_work, drain_vmap_area_work);
+/*
+ * Keep the work item, whose pending bit is updated by freeing CPUs,
+ * away from vmap metadata read by allocation and free paths.
+ */
+static __cacheline_aligned_in_smp
+DECLARE_WORK(drain_vmap_work, drain_vmap_area_work);
static struct workqueue_struct *drain_vmap_helpers_wq;
static struct workqueue_struct *drain_vmap_wq;
@@@ -2452,8 -2447,7 +2452,8 @@@ static bool __purge_vmap_area_lazy(unsi
static void reclaim_and_purge_vmap_areas(void)
{
- mutex_lock(&vmap_purge_lock);
+ if (!mutex_trylock(&vmap_purge_lock))
+ return;
purge_fragmented_blocks_allcpus();
__purge_vmap_area_lazy(ULONG_MAX, 0, true);
mutex_unlock(&vmap_purge_lock);
@@@ -3138,7 -3132,7 +3138,7 @@@ EXPORT_SYMBOL(vm_map_ram)
static struct vm_struct *vmlist __initdata;
-static inline unsigned int vm_area_page_order(struct vm_struct *vm)
+static inline unsigned int vm_area_page_order(const struct vm_struct *vm)
{
#ifdef CONFIG_HAVE_ARCH_HUGE_VMALLOC
return vm->page_order;
@@@ -3147,7 -3141,7 +3147,7 @@@
#endif
}
-unsigned int get_vm_area_page_order(struct vm_struct *vm)
+unsigned int get_vm_area_page_order(const struct vm_struct *vm)
{
return vm_area_page_order(vm);
}
@@@ -3372,18 -3366,14 +3372,18 @@@ struct vm_struct *remove_vm_area(const
}
static inline void set_area_direct_map(const struct vm_struct *area,
- int (*set_direct_map)(struct page *page))
+ int (*set_direct_map)(struct page *page,
+ unsigned int nr))
{
- unsigned long i;
+ unsigned int nr = (1U << vm_area_page_order(area));
- /* HUGE_VMALLOC passes small pages to set_direct_map */
- for (i = 0; i < area->nr_pages; i++)
- if (page_address(area->pages[i]))
- set_direct_map(area->pages[i]);
+ for (unsigned long i = 0; i < area->nr_pages; i += nr) {
+ if (page_address(area->pages[i])) {
+ int err = set_direct_map(area->pages[i], nr);
+
+ WARN_ON_ONCE(err);
+ }
+ }
}
/*
@@@ -3890,7 -3880,7 +3890,7 @@@ static void *__vmalloc_area_node(struc
unsigned long size = get_vm_area_size(area);
unsigned long array_size;
unsigned long nr_small_pages = size >> PAGE_SHIFT;
- unsigned int page_order;
+ unsigned int page_order = page_shift - PAGE_SHIFT;
unsigned int flags;
int ret;
@@@ -3918,6 -3908,9 +3918,6 @@@
goto fail;
}
- set_vm_area_page_order(area, page_shift - PAGE_SHIFT);
- page_order = vm_area_page_order(area);
-
/*
* High-order nofail allocations are really expensive and
* potentially dangerous (pre-mature OOM, disruptive reclaim
@@@ -3972,7 -3965,6 +3972,7 @@@
goto fail;
}
+ set_vm_area_page_order(area, page_order);
return area->addr;
fail:
@@@ -4035,12 -4027,6 +4035,12 @@@ static gfp_t vmalloc_fix_flags(gfp_t fl
* %__GFP_SKIP_KASAN can be used to skip unpoisoning of mapped pages
* (when prot=%PAGE_KERNEL).
*
+ * %VM_ALLOW_HUGE_VMAP allocates huge pages when possible and falls back to
+ * base pages if huge page allocation fails.
+ *
+ * %VM_REQUIRE_HUGE_VMAP implies %VM_ALLOW_HUGE_VMAP and fails instead of
+ * silently falling back to base pages.
+ *
* Can not be called from interrupt nor NMI contexts.
* Return: the address of the area or %NULL on failure
*/
@@@ -4066,10 -4052,6 +4066,10 @@@ void *__vmalloc_node_range_noprof(unsig
return NULL;
}
+ /* VM_REQUIRE_HUGE_VMAP implies VM_ALLOW_HUGE_VMAP */
+ if (vm_flags & VM_REQUIRE_HUGE_VMAP)
+ vm_flags |= VM_ALLOW_HUGE_VMAP;
+
if (vmap_allow_huge && (vm_flags & VM_ALLOW_HUGE_VMAP)) {
/*
* Try huge pages. Only try for PAGE_KERNEL allocations,
@@@ -4086,9 -4068,6 +4086,9 @@@
align = max(original_align, 1UL << shift);
}
+ if ((vm_flags & VM_REQUIRE_HUGE_VMAP) && shift == PAGE_SHIFT)
+ return NULL;
+
again:
area = __get_vm_area_node(size, align, shift, VM_ALLOC |
VM_UNINITIALIZED | vm_flags, start, end, node,
@@@ -4163,7 -4142,7 +4163,7 @@@
return area->addr;
fail:
- if (shift > PAGE_SHIFT) {
+ if (shift > PAGE_SHIFT && !(vm_flags & VM_REQUIRE_HUGE_VMAP)) {
shift = PAGE_SHIFT;
align = original_align;
goto again;
@@@ -5548,20 -5527,10 +5548,20 @@@ vmap_node_shrink_scan(struct shrinker *
{
struct vmap_node *vn;
- guard(mutex)(&vmap_purge_lock);
+ /*
+ * This shrinker is invoked from direct reclaim where memory
+ * pressure is already high. Blocking on vmap_purge_lock here
+ * can deadlock the system: the lock holder may be blocked in
+ * flush_work() waiting for a worker that is stuck in this same
+ * reclaim path trying to acquire the same lock. Use trylock
+ * to avoid this; skipping a pool decay cycle is harmless.
+ */
+ if (!mutex_trylock(&vmap_purge_lock))
+ return SHRINK_STOP;
for_each_vmap_node(vn)
decay_va_pool_node(vn, true);
+ mutex_unlock(&vmap_purge_lock);
return SHRINK_STOP;
}
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 9+ messages in thread
* linux-next: manual merge of the mm-nonmm-unstable tree with the mm tree
@ 2026-09-15 13:32 Mark Brown
2026-09-15 17:54 ` Andrew Morton
0 siblings, 1 reply; 9+ messages in thread
From: Mark Brown @ 2026-09-15 13:32 UTC (permalink / raw)
To: Andrew Morton
Cc: Krishna Iyer, Linux Kernel Mailing List, Linux Next Mailing List,
SJ Park
[-- Attachment #1: Type: text/plain, Size: 11735 bytes --]
Hi all,
Today's linux-next merge of the mm-nonmm-unstable tree got a conflict in:
mm/damon/vaddr.c
between commits:
6acc47b87e9f0 ("mm/damon/vaddr: avoid hw-driven pte updates during damon_hugetlb_mkold()")
24f6b1a0ab44a ("mm/damon/vaddr: respect folio end for DAMOS_STAT")
f256012bb6a1c ("mm/damon/vaddr: respect folio end for DAMOS_MIGRATE_{HOT,COLD}")
fa5b61fcf5c9d ("mm/damon/vaddr: remove a debug message")
ed6700504e3f5 ("mm/damon: move damon_hugetlb_mkold() from vaddr to ops-common")
88ac1f509a7e6 ("mm/damon/vaddr: support prep_probes")
68ce5caa141c4 ("mm/damon/vaddr: support apply_probe")
45e6bd6ac2868 ("mm/damon/vaddr: extend apply_probes() for hugetlb")
4144f08e72a48 ("mm/damon/vaddr: support pgidle_unset probe filter type")
6a00e5447c45e ("mm/damon/vaddr: support DAMON_FILTER_TYPE_PGIDLE_SET")
3dabc621c0cfd ("mm/damon/core: extend probe_hits_wsum() for moving sum based calculation")
from the mm tree and commit:
566380a1497eb ("mm/damon/vaddr: avoid hw-driven pte updates during damon_hugetlb_mkold()")
from the mm-nonmm-unstable tree.
I fixed it up (see below) and can carry the fix as necessary. This
is now fixed as far as linux-next is concerned, but any non trivial
conflicts should be mentioned to your upstream maintainer when your tree
is submitted for merging. You may also want to consider cooperating
with the maintainer of the conflicting tree to minimise any particularly
complex conflicts.
diff --combined mm/damon/vaddr.c
index d5dde97b3cd0d,04ee2a2c6a4d6..0000000000000
--- a/mm/damon/vaddr.c
+++ b/mm/damon/vaddr.c
@@@ -189,12 -189,22 +189,12 @@@ static int damon_va_three_regions(struc
* <BIG UNMAPPED REGION 2>
* <stack>
*/
-static void __damon_va_init_regions(struct damon_ctx *ctx,
- struct damon_target *t)
+static void __damon_va_init_regions(struct damon_target *t)
{
- struct damon_target *ti;
struct damon_addr_range regions[3];
- int tidx = 0;
- if (damon_va_three_regions(t, regions)) {
- damon_for_each_target(ti, ctx) {
- if (ti == t)
- break;
- tidx++;
- }
- pr_debug("Failed to get three regions of %dth target\n", tidx);
+ if (damon_va_three_regions(t, regions))
return;
- }
damon_set_regions(t, regions, 3, DAMON_MIN_REGION_SZ);
}
@@@ -207,7 -217,7 +207,7 @@@ static void damon_va_init(struct damon_
damon_for_each_target(t, ctx) {
/* the user may set the target regions as they want */
if (!damon_nr_regions(t))
- __damon_va_init_regions(ctx, t);
+ __damon_va_init_regions(t);
}
}
@@@ -283,6 -293,40 +283,6 @@@ out
}
#ifdef CONFIG_HUGETLB_PAGE
-static bool damon_hugetlb_ptep_mkold(pte_t *pte, struct mm_struct *mm,
- struct vm_area_struct *vma, unsigned long addr, pte_t *entry)
-{
- unsigned long psize = huge_page_size(hstate_vma(vma));
-
- if (!pte_young(*entry))
- return false;
- *entry = huge_ptep_get_and_clear(mm, addr, pte, psize);
- *entry = pte_mkold(*entry);
- set_huge_pte_at(mm, addr, pte, *entry, psize);
- return true;
-}
-
-static void damon_hugetlb_mkold(pte_t *pte, struct mm_struct *mm,
- struct vm_area_struct *vma, unsigned long addr)
-{
- bool referenced = false;
- pte_t entry = huge_ptep_get(mm, addr, pte);
- struct folio *folio = pfn_folio(pte_pfn(entry));
-
- folio_get(folio);
-
- referenced = damon_hugetlb_ptep_mkold(pte, mm, vma, addr, &entry);
- if (mmu_notifier_clear_young(mm, addr,
- addr + huge_page_size(hstate_vma(vma))))
- referenced = true;
-
- if (referenced)
- folio_set_young(folio);
-
- folio_set_idle(folio);
- folio_put(folio);
-}
-
static int damon_mkold_hugetlb_entry(pte_t *pte, unsigned long hmask,
unsigned long addr, unsigned long end,
struct mm_walk *walk)
@@@ -483,243 -527,6 +483,243 @@@ static unsigned int damon_va_check_acce
return max_nr_accesses;
}
+static void damon_va_prep_probe_region(struct damon_ctx *ctx,
+ struct mm_struct *mm, struct damon_region *r,
+ struct damon_probe *probe)
+{
+ struct damon_prep *p;
+
+ damon_for_each_prep(p, probe) {
+ switch (p->action) {
+ case DAMON_PREP_SET_PGIDLE:
+ damon_va_mkold(mm, r->sampling_addr);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+static void damon_va_prep_probes(struct damon_ctx *ctx, bool set_samples)
+{
+ struct damon_target *t;
+ struct mm_struct *mm;
+ struct damon_region *r;
+ struct damon_probe *p;
+
+ damon_for_each_target(t, ctx) {
+ mm = damon_get_mm(t);
+ if (!mm)
+ continue;
+ damon_for_each_region(r, t) {
+ if (set_samples)
+ r->sampling_addr = damon_rand(ctx, r->ar.start,
+ r->ar.end);
+ damon_for_each_probe(p, ctx)
+ damon_va_prep_probe_region(ctx, mm, r, p);
+ }
+ mmput(mm);
+ }
+}
+
+static bool damon_va_young_addr(struct folio *folio, pte_t *pte, pmd_t *pmd,
+ struct mm_struct *mm, unsigned long addr)
+{
+ bool young = false;
+
+ if (pte)
+ young = pte_young(*pte);
+ else if (pmd)
+ young = pmd_young(*pmd);
+ young = young || !folio_test_idle(folio) ||
+ mmu_notifier_test_young(mm, addr);
+ return young;
+}
+
+static bool damon_va_filter_match(struct damon_filter *filter,
+ struct folio *folio, pte_t *pte, pmd_t *pmd,
+ struct mm_struct *mm, unsigned long addr)
+{
+ bool matched = false;
+
+ switch (filter->type) {
+ case DAMON_FILTER_TYPE_PGIDLE_UNSET:
+ if (!folio)
+ matched = false;
+ else
+ matched = damon_va_young_addr(folio, pte, pmd, mm,
+ addr);
+ break;
+ case DAMON_FILTER_TYPE_PGIDLE_SET:
+ if (!folio)
+ matched = false;
+ else
+ matched = !damon_va_young_addr(folio, pte, pmd, mm,
+ addr);
+ break;
+ default:
+ return damon_ops_filter_match(filter, folio);
+ }
+ return matched == filter->matching;
+}
+
+static bool damon_va_filter_pass(struct folio *folio, struct damon_probe *p,
+ pte_t *pte, pmd_t *pmd, struct mm_struct *mm,
+ unsigned long addr)
+{
+ struct damon_filter *f;
+ bool pass = true;
+
+ damon_for_each_filter(f, p) {
+ if (damon_va_filter_match(f, folio, pte, pmd, mm, addr)) {
+ pass = f->allow;
+ break;
+ }
+ pass = !f->allow;
+ }
+ return pass;
+}
+
+struct damon_va_probe_walk_private {
+ struct damon_ctx *ctx;
+ struct damon_region *r;
+};
+
+static void damon_va_probe_folio(struct damon_ctx *ctx,
+ struct damon_region *r, struct folio *folio,
+ pte_t *pte, pmd_t *pmd, struct mm_struct *mm)
+{
+ struct damon_probe *probe;
+ int i = 0;
+
+ damon_for_each_probe(probe, ctx) {
+ if (damon_va_filter_pass(folio, probe, pte, pmd, mm,
+ r->sampling_addr))
+ r->probe_hits[i]++;
+ i++;
+ }
+}
+
+static int damon_va_probe_pmd_entry(pmd_t *pmd, unsigned long addr,
+ unsigned long next, struct mm_walk *walk)
+{
+ pte_t *pte;
+ pte_t ptent;
+ spinlock_t *ptl;
+ struct folio *folio;
+ struct damon_va_probe_walk_private *priv = walk->private;
+
+#ifdef CONFIG_TRANSPARENT_HUGEPAGE
+ ptl = pmd_trans_huge_lock(pmd, walk->vma);
+ if (ptl) {
+ pmd_t pmde = pmdp_get(pmd);
+
+ if (!pmd_present(pmde))
+ goto huge_out;
+ folio = vm_normal_folio_pmd(walk->vma, addr, pmde);
+ if (!folio)
+ goto huge_out;
+ damon_va_probe_folio(priv->ctx, priv->r, folio, NULL, &pmde,
+ walk->vma->vm_mm);
+
+huge_out:
+ spin_unlock(ptl);
+ return 0;
+ }
+#endif /* CONFIG_TRANSPARENT_HUGEPAGE */
+
+ pte = pte_offset_map_lock(walk->mm, pmd, addr, &ptl);
+ if (!pte)
+ return 0;
+ ptent = ptep_get(pte);
+ if (!pte_present(ptent))
+ goto out;
+ folio = vm_normal_folio(walk->vma, addr, ptent);
+ if (!folio)
+ goto out;
+ damon_va_probe_folio(priv->ctx, priv->r, folio, &ptent, NULL,
+ walk->vma->vm_mm);
+
+out:
+ pte_unmap_unlock(pte, ptl);
+ return 0;
+}
+
+#ifdef CONFIG_HUGETLB_PAGE
+static int damon_va_probe_hugetlb_entry(pte_t *pte, unsigned long hmask,
+ unsigned long addr, unsigned long end, struct mm_walk *walk)
+{
+ struct damon_va_probe_walk_private *priv = walk->private;
+ struct hstate *h = hstate_vma(walk->vma);
+ struct folio *folio;
+ spinlock_t *ptl;
+ pte_t entry;
+
+ ptl = huge_pte_lock(h, walk->mm, pte);
+ entry = huge_ptep_get(walk->mm, addr, pte);
+ if (!pte_present(entry))
+ goto out;
+
+ folio = pfn_folio(pte_pfn(entry));
+ folio_get(folio);
+ damon_va_probe_folio(priv->ctx, priv->r, folio, &entry, NULL,
+ walk->vma->vm_mm);
+ folio_put(folio);
+
+out:
+ spin_unlock(ptl);
+ return 0;
+}
+#else
+#define damon_va_probe_hugetlb_entry NULL
+#endif /* CONFIG_HUGETLB_PAGE */
+
+static void __damon_va_apply_probes(struct damon_ctx *ctx,
+ struct mm_struct *mm, struct damon_region *r)
+{
+ struct damon_va_probe_walk_private arg = {
+ .ctx = ctx,
+ .r = r,
+ };
+ struct mm_walk_ops damon_probe_walk_ops = {
+ .pmd_entry = damon_va_probe_pmd_entry,
+ .hugetlb_entry = damon_va_probe_hugetlb_entry,
+ };
+ unsigned long addr = r->sampling_addr;
+
+ if (!mm)
+ return;
+
+ damon_va_walk_page_range(mm, addr, addr + 1, &damon_probe_walk_ops,
+ &arg);
+}
+
+static unsigned int damon_va_apply_probes(struct damon_ctx *ctx,
+ bool set_samples, bool return_max_wsum)
+{
+ struct damon_target *t;
+ struct mm_struct *mm;
+ struct damon_region *r;
+ unsigned int max_wsum = 0;
+
+ damon_for_each_target(t, ctx) {
+ mm = damon_get_mm(t);
+ damon_for_each_region(r, t) {
+ if (set_samples)
+ r->sampling_addr = damon_rand(ctx, r->ar.start,
+ r->ar.end);
+ __damon_va_apply_probes(ctx, mm, r);
+ if (return_max_wsum)
+ max_wsum = max(damon_probe_hits_wsum(r, false,
+ false, ctx), max_wsum);
+ }
+ if (mm)
+ mmput(mm);
+ }
+
+ return max_wsum;
+}
+
static bool damos_va_filter_young_match(struct damos_filter *filter,
struct folio *folio, struct vm_area_struct *vma,
unsigned long addr, pte_t *ptep, pmd_t *pmdp)
@@@ -869,8 -676,6 +869,8 @@@ huge_out
return 0;
for (; addr < next; pte += nr, addr += nr * PAGE_SIZE) {
+ unsigned long page_idx;
+
nr = 1;
ptent = ptep_get(pte);
@@@ -883,8 -688,7 +883,8 @@@
continue;
damos_va_migrate_dests_add(folio, walk->vma, addr, dests,
migration_lists);
- nr = folio_nr_pages(folio);
+ page_idx = folio_page_idx(folio, pte_page(ptent));
+ nr = folio_nr_pages(folio) - page_idx;
}
pte_unmap_unlock(start_pte, ptl);
return 0;
@@@ -1034,8 -838,6 +1034,8 @@@ huge_unlock
return 0;
for (; addr < next; pte += nr, addr += nr * PAGE_SIZE) {
+ unsigned long page_idx;
+
nr = 1;
ptent = ptep_get(pte);
@@@ -1049,8 -851,7 +1049,8 @@@
if (!damos_va_filter_out(s, folio, vma, addr, pte, NULL))
*sz_filter_passed += folio_size(folio);
- nr = folio_nr_pages(folio);
+ page_idx = folio_page_idx(folio, pte_page(ptent));
+ nr = folio_nr_pages(folio) - page_idx;
s->last_applied = folio;
}
pte_unmap_unlock(start_pte, ptl);
@@@ -1148,8 -949,6 +1148,8 @@@ static int __init damon_va_initcall(voi
.update = damon_va_update,
.prepare_access_checks = damon_va_prepare_access_checks,
.check_accesses = damon_va_check_accesses,
+ .prep_probes = damon_va_prep_probes,
+ .apply_probes = damon_va_apply_probes,
.target_valid = damon_va_target_valid,
.cleanup_target = damon_va_cleanup_target,
.apply_scheme = damon_va_apply_scheme,
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 9+ messages in thread
* linux-next: manual merge of the mm-nonmm-unstable tree with the mm tree
@ 2026-09-02 10:18 Mark Brown
0 siblings, 0 replies; 9+ messages in thread
From: Mark Brown @ 2026-09-02 10:18 UTC (permalink / raw)
To: Andrew Morton
Cc: Eric Dumazet, Gregory Price, Linux Kernel Mailing List,
Linux Next Mailing List
[-- Attachment #1: Type: text/plain, Size: 11417 bytes --]
Hi all,
Today's linux-next merge of the mm-nonmm-unstable tree got a conflict in:
mm/mempolicy.c
between commits:
61f04edb01700 ("mm/mempolicy: fix sleeping allocation in alloc_pages_bulk_weighted_interleave()")
5da38ed988084 ("mm/mempolicy: use SRCU for the weighted interleave state")
from the mm tree and commit:
540e583b66d64 ("mm/mempolicy: fix sleeping allocation in alloc_pages_bulk_weighted_interleave()")
from the mm-nonmm-unstable tree.
I fixed it up (see below) and can carry the fix as necessary. This
is now fixed as far as linux-next is concerned, but any non trivial
conflicts should be mentioned to your upstream maintainer when your tree
is submitted for merging. You may also want to consider cooperating
with the maintainer of the conflicting tree to minimise any particularly
complex conflicts.
diff --combined mm/mempolicy.c
index 2ad0a5f18280a,79053ece02cd4..0000000000000
--- a/mm/mempolicy.c
+++ b/mm/mempolicy.c
@@@ -112,7 -112,6 +112,7 @@@
#include <linux/printk.h>
#include <linux/leafops.h>
#include <linux/gcd.h>
+#include <linux/srcu.h>
#include <asm/tlbflush.h>
#include <asm/tlb.h>
@@@ -158,7 -157,6 +158,7 @@@ static const int weightiness = 32
*/
struct weighted_interleave_state {
bool mode_auto;
+ struct rcu_head rcu;
u8 iw_table[];
};
static struct weighted_interleave_state __rcu *wi_state;
@@@ -170,24 -168,6 +170,24 @@@ static unsigned int *node_bw_table
*/
static DEFINE_MUTEX(wi_state_lock);
+/* Readers that sleep while walking iw_table hold this instead */
+DEFINE_STATIC_SRCU_FAST(wi_srcu);
+
+static void wi_state_free_rcu(struct rcu_head *head)
+{
+ struct weighted_interleave_state *state =
+ container_of(head, struct weighted_interleave_state, rcu);
+
+ kfree_rcu(state, rcu);
+}
+
+/* Retire through both flavors: sleeping readers use SRCU, the rest RCU */
+static void wi_state_retire(struct weighted_interleave_state *state)
+{
+ if (state)
+ call_srcu(&wi_srcu, &state->rcu, wi_state_free_rcu);
+}
+
static u8 get_il_weight(int node)
{
struct weighted_interleave_state *state;
@@@ -286,7 -266,10 +286,7 @@@ int mempolicy_set_node_perf(unsigned in
rcu_assign_pointer(wi_state, new_wi_state);
mutex_unlock(&wi_state_lock);
- if (old_wi_state) {
- synchronize_rcu();
- kfree(old_wi_state);
- }
+ wi_state_retire(old_wi_state);
out:
kfree(old_bw);
return 0;
@@@ -679,8 -662,6 +679,8 @@@ static void queue_folios_pmd(pmd_t *pmd
return;
}
folio = pmd_folio(pmdval);
+ if (folio_is_zone_device(folio))
+ return;
if (is_huge_zero_folio(folio)) {
walk->action = ACTION_CONTINUE;
return;
@@@ -2199,15 -2180,34 +2199,15 @@@ unsigned int mempolicy_slab_node(void
}
}
-static unsigned int read_once_policy_nodemask(struct mempolicy *pol,
- nodemask_t *mask)
-{
- /*
- * barrier stabilizes the nodemask locally so that it can be iterated
- * over safely without concern for changes. Allocators validate node
- * selection does not violate mems_allowed, so this is safe.
- */
- barrier();
- memcpy(mask, &pol->nodes, sizeof(nodemask_t));
- barrier();
- return nodes_weight(*mask);
-}
-
static unsigned int weighted_interleave_nid(struct mempolicy *pol, pgoff_t ilx)
{
struct weighted_interleave_state *state;
- nodemask_t nodemask;
- unsigned int target, nr_nodes;
+ unsigned int target, nnodes = 0;
u8 *table = NULL;
unsigned int weight_total = 0;
u8 weight;
int nid = 0;
- nr_nodes = read_once_policy_nodemask(pol, &nodemask);
- if (!nr_nodes)
- return numa_node_id();
-
rcu_read_lock();
state = rcu_dereference(wi_state);
@@@ -2215,40 -2215,22 +2215,40 @@@
if (state)
table = state->iw_table;
- /* calculate the total weight */
- for_each_node_mask(nid, nodemask)
+ /* calculate the total weight and the node count */
+ for_each_node_mask(nid, pol->nodes) {
weight_total += table ? table[nid] : 1;
+ nnodes++;
+ }
+
+ /* the mask is empty */
+ if (!weight_total) {
+ rcu_read_unlock();
+ return numa_node_id();
+ }
/* Calculate the node offset based on totals */
target = ilx % weight_total;
- nid = first_node(nodemask);
- while (target) {
+ nid = first_node(pol->nodes);
+
+ /*
+ * The target was calculated in a separate loop, and a concurrent
+ * rebind can change the total number of nodes. Clamp this loop to
+ * a single pass (nnodes) to keep the walk bounded by node count.
+ */
+ while (target && nnodes-- && nid < MAX_NUMNODES) {
/* detect system default usage */
weight = table ? table[nid] : 1;
if (target < weight)
break;
target -= weight;
- nid = next_node_in(nid, nodemask);
+ nid = next_node_in(nid, pol->nodes);
}
rcu_read_unlock();
+
+ /* the mask emptied under the walk */
+ if (nid >= MAX_NUMNODES)
+ return numa_node_id();
return nid;
}
@@@ -2259,21 -2241,18 +2259,21 @@@
*/
static unsigned int interleave_nid(struct mempolicy *pol, pgoff_t ilx)
{
- nodemask_t nodemask;
unsigned int target, nnodes;
int i;
int nid;
- nnodes = read_once_policy_nodemask(pol, &nodemask);
+ nnodes = nodes_weight(pol->nodes);
if (!nnodes)
return numa_node_id();
target = ilx % nnodes;
- nid = first_node(nodemask);
- for (i = 0; i < target; i++)
- nid = next_node(nid, nodemask);
+ nid = first_node(pol->nodes);
+ for (i = 0; i < target && nid < MAX_NUMNODES; i++)
+ nid = next_node_in(nid, pol->nodes);
+
+ /* the mask emptied under the walk */
+ if (nid >= MAX_NUMNODES)
+ return numa_node_id();
return nid;
}
@@@ -2613,7 -2592,6 +2613,7 @@@ static unsigned long alloc_pages_bulk_i
struct mempolicy *pol, unsigned long nr_pages,
struct page **page_array)
{
+ unsigned int cpuset_mems_cookie;
int nodes;
unsigned long nr_pages_per_node;
int delta;
@@@ -2621,16 -2599,7 +2621,16 @@@
unsigned long nr_allocated;
unsigned long total_allocated = 0;
- nodes = nodes_weight(pol->nodes);
+ /* count the nodes, retry if a rebind happened during the read */
+ do {
+ cpuset_mems_cookie = read_mems_allowed_begin();
+ nodes = nodes_weight(pol->nodes);
+ } while (read_mems_allowed_retry(cpuset_mems_cookie));
+
+ /* if the nodemask has become invalid, we cannot do anything */
+ if (!nodes)
+ return 0;
+
nr_pages_per_node = nr_pages / nodes;
delta = nr_pages - nodes * nr_pages_per_node;
@@@ -2665,10 -2634,10 +2665,10 @@@ static unsigned long alloc_pages_bulk_w
unsigned long nr_allocated = 0;
unsigned long rounds;
unsigned long node_pages, delta;
- u8 *weights, weight;
+ struct srcu_ctr __percpu *scp;
+ u8 *table, weight;
unsigned int weight_total = 0;
unsigned long rem_pages = nr_pages;
- nodemask_t nodes;
int nnodes, node;
int resume_node = MAX_NUMNODES - 1;
u8 resume_weight = 0;
@@@ -2678,10 -2647,10 +2678,10 @@@
if (!nr_pages)
return 0;
- /* read the nodes onto the stack, retry if done during rebind */
+ /* count the nodes, retry if a rebind happened during the read */
do {
cpuset_mems_cookie = read_mems_allowed_begin();
- nnodes = read_once_policy_nodemask(pol, &nodes);
+ nnodes = nodes_weight(pol->nodes);
} while (read_mems_allowed_retry(cpuset_mems_cookie));
/* if the nodemask has become invalid, we cannot do anything */
@@@ -2691,7 -2660,7 +2691,7 @@@
/* Continue allocating from most recent node and adjust the nr_pages */
node = me->il_prev;
weight = me->il_weight;
- if (weight && node_isset(node, nodes)) {
+ if (weight && node_isset(node, pol->nodes)) {
node_pages = min(rem_pages, weight);
nr_allocated = __alloc_pages_bulk(gfp, node, NULL, node_pages,
page_array);
@@@ -2709,18 -2678,25 +2709,18 @@@
me->il_weight = 0;
prev_node = node;
- /* create a local copy of node weights to operate on outside rcu */
- weights = kmalloc(nr_node_ids, gfp & GFP_RECLAIM_MASK);
- if (!weights)
- return total_allocated;
-
- rcu_read_lock();
- state = rcu_dereference(wi_state);
- if (state) {
- memcpy(weights, state->iw_table, nr_node_ids * sizeof(u8));
- rcu_read_unlock();
- } else {
- rcu_read_unlock();
- for (i = 0; i < nr_node_ids; i++)
- weights[i] = 1;
- }
+ /* The page allocator may sleep, pin the weight table with SRCU */
+ scp = srcu_read_lock_fast(&wi_srcu);
+ state = srcu_dereference(wi_state, &wi_srcu);
+ table = state ? state->iw_table : NULL;
/* calculate total, detect system default usage */
- for_each_node_mask(node, nodes)
- weight_total += weights[node];
+ for_each_node_mask(node, pol->nodes)
+ weight_total += table ? table[node] : 1;
+
+ /* the mask emptied since it was counted */
+ if (!weight_total)
+ goto out;
/*
* Calculate rounds/partial rounds to minimize __alloc_pages_bulk calls.
@@@ -2731,15 -2707,11 +2731,15 @@@
*/
rounds = rem_pages / weight_total;
delta = rem_pages % weight_total;
- resume_node = next_node_in(prev_node, nodes);
- resume_weight = weights[resume_node];
+ resume_node = next_node_in(prev_node, pol->nodes);
+ if (resume_node >= MAX_NUMNODES)
+ goto out;
+ resume_weight = table ? table[resume_node] : 1;
for (i = 0; i < nnodes; i++) {
- node = next_node_in(prev_node, nodes);
- weight = weights[node];
+ node = next_node_in(prev_node, pol->nodes);
+ if (node >= MAX_NUMNODES)
+ break;
+ weight = table ? table[node] : 1;
node_pages = weight * rounds;
/* If a delta exists, add this node's portion of the delta */
if (delta > weight) {
@@@ -2755,8 -2727,6 +2755,8 @@@
/* node_pages can be 0 if an allocation fails and rounds == 0 */
if (!node_pages)
break;
+ /* a rebind can invalidate the counts: never overrun page_array */
+ node_pages = min(node_pages, nr_pages - total_allocated);
nr_allocated = __alloc_pages_bulk(gfp, node, NULL, node_pages,
page_array);
page_array += nr_allocated;
@@@ -2767,8 -2737,7 +2767,8 @@@
}
me->il_prev = resume_node;
me->il_weight = resume_weight;
- kfree(weights);
+out:
+ srcu_read_unlock_fast(&wi_srcu, scp);
return total_allocated;
}
@@@ -3694,7 -3663,10 +3694,7 @@@ static ssize_t node_store(struct kobjec
rcu_assign_pointer(wi_state, new_wi_state);
mutex_unlock(&wi_state_lock);
- if (old_wi_state) {
- synchronize_rcu();
- kfree(old_wi_state);
- }
+ wi_state_retire(old_wi_state);
return count;
}
@@@ -3760,7 -3732,10 +3760,7 @@@ static ssize_t weighted_interleave_auto
update_wi_state:
rcu_assign_pointer(wi_state, new_wi_state);
mutex_unlock(&wi_state_lock);
- if (old_wi_state) {
- synchronize_rcu();
- kfree(old_wi_state);
- }
+ wi_state_retire(old_wi_state);
return count;
}
@@@ -3804,7 -3779,10 +3804,7 @@@ static void wi_state_free(void
rcu_assign_pointer(wi_state, NULL);
mutex_unlock(&wi_state_lock);
- if (old_wi_state) {
- synchronize_rcu();
- kfree(old_wi_state);
- }
+ wi_state_retire(old_wi_state);
}
static struct kobj_attribute wi_auto_attr = {
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2026-09-15 19:04 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-15 13:32 linux-next: manual merge of the mm-nonmm-unstable tree with the mm tree Mark Brown
[not found] ` <CACXp-XqfPNgOU2=s0sDOUer9U9MXzwCVLZ1hK=tP8m-q_eNu3w@mail.gmail.com>
2026-09-15 19:03 ` David Hildenbrand (Arm)
-- strict thread matches above, loose matches on Subject: below --
2026-09-15 13:32 Mark Brown
2026-09-15 13:32 Mark Brown
2026-09-15 17:54 ` Andrew Morton
2026-09-15 17:59 ` Mark Brown
2026-09-15 18:23 ` David Hildenbrand (Arm)
2026-09-15 18:41 ` Mark Brown
2026-09-02 10:18 Mark Brown
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®