Skip to content

Commit

Permalink
Merge tag 'mm-hotfixes-stable-2025-05-17-09-41' of git://git.kernel.o…
Browse files Browse the repository at this point in the history
…rg/pub/scm/linux/kernel/git/akpm/mm

Pull hotfixes from Andrew Morton:
 "Nine singleton hotfixes, all MM.  Four are cc:stable"

* tag 'mm-hotfixes-stable-2025-05-17-09-41' of git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm:
  mm: userfaultfd: correct dirty flags set for both present and swap pte
  zsmalloc: don't underflow size calculation in zs_obj_write()
  mm/page_alloc: fix race condition in unaccepted memory handling
  mm/page_alloc: ensure try_alloc_pages() plays well with unaccepted memory
  MAINTAINERS: add mm GUP section
  mm/codetag: move tag retrieval back upfront in __free_pages()
  mm/memory: fix mapcount / refcount sanity check for mTHP reuse
  kernel/fork: only call untrack_pfn_clear() on VMAs duplicated for fork()
  mm: hugetlb: fix incorrect fallback for subpool
  • Loading branch information
Linus Torvalds committed May 17, 2025
2 parents 205b2bd + 75cb1cc commit e72e784
Show file tree
Hide file tree
Showing 10 changed files with 82 additions and 87 deletions.
12 changes: 12 additions & 0 deletions MAINTAINERS
Original file line number Diff line number Diff line change
Expand Up @@ -15549,6 +15549,18 @@ S: Maintained
F: include/linux/execmem.h
F: mm/execmem.c

MEMORY MANAGEMENT - GUP (GET USER PAGES)
M: Andrew Morton <akpm@linux-foundation.org>
M: David Hildenbrand <david@redhat.com>
R: Jason Gunthorpe <jgg@nvidia.com>
R: John Hubbard <jhubbard@nvidia.com>
R: Peter Xu <peterx@redhat.com>
L: linux-mm@kvack.org
S: Maintained
W: http://www.linux-mm.org
T: git git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm
F: mm/gup.c

MEMORY MANAGEMENT - NUMA MEMBLOCKS AND NUMA EMULATION
M: Andrew Morton <akpm@linux-foundation.org>
M: Mike Rapoport <rppt@kernel.org>
Expand Down
8 changes: 8 additions & 0 deletions include/linux/pgalloc_tag.h
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,13 @@ static inline struct alloc_tag *__pgalloc_tag_get(struct page *page)
return tag;
}

static inline struct alloc_tag *pgalloc_tag_get(struct page *page)
{
if (mem_alloc_profiling_enabled())
return __pgalloc_tag_get(page);
return NULL;
}

void pgalloc_tag_split(struct folio *folio, int old_order, int new_order);
void pgalloc_tag_swap(struct folio *new, struct folio *old);

Expand All @@ -199,6 +206,7 @@ static inline void clear_page_tag_ref(struct page *page) {}
static inline void alloc_tag_sec_init(void) {}
static inline void pgalloc_tag_split(struct folio *folio, int old_order, int new_order) {}
static inline void pgalloc_tag_swap(struct folio *new, struct folio *old) {}
static inline struct alloc_tag *pgalloc_tag_get(struct page *page) { return NULL; }

#endif /* CONFIG_MEM_ALLOC_PROFILING */

Expand Down
9 changes: 5 additions & 4 deletions kernel/fork.c
Original file line number Diff line number Diff line change
Expand Up @@ -498,10 +498,6 @@ struct vm_area_struct *vm_area_dup(struct vm_area_struct *orig)
vma_numab_state_init(new);
dup_anon_vma_name(orig, new);

/* track_pfn_copy() will later take care of copying internal state. */
if (unlikely(new->vm_flags & VM_PFNMAP))
untrack_pfn_clear(new);

return new;
}

Expand Down Expand Up @@ -672,6 +668,11 @@ static __latent_entropy int dup_mmap(struct mm_struct *mm,
tmp = vm_area_dup(mpnt);
if (!tmp)
goto fail_nomem;

/* track_pfn_copy() will later take care of copying internal state. */
if (unlikely(tmp->vm_flags & VM_PFNMAP))
untrack_pfn_clear(tmp);

retval = vma_dup_policy(mpnt, tmp);
if (retval)
goto fail_nomem_policy;
Expand Down
28 changes: 22 additions & 6 deletions mm/hugetlb.c
Original file line number Diff line number Diff line change
Expand Up @@ -3010,7 +3010,7 @@ struct folio *alloc_hugetlb_folio(struct vm_area_struct *vma,
struct hugepage_subpool *spool = subpool_vma(vma);
struct hstate *h = hstate_vma(vma);
struct folio *folio;
long retval, gbl_chg;
long retval, gbl_chg, gbl_reserve;
map_chg_state map_chg;
int ret, idx;
struct hugetlb_cgroup *h_cg = NULL;
Expand Down Expand Up @@ -3163,8 +3163,16 @@ struct folio *alloc_hugetlb_folio(struct vm_area_struct *vma,
hugetlb_cgroup_uncharge_cgroup_rsvd(idx, pages_per_huge_page(h),
h_cg);
out_subpool_put:
if (map_chg)
hugepage_subpool_put_pages(spool, 1);
/*
* put page to subpool iff the quota of subpool's rsv_hpages is used
* during hugepage_subpool_get_pages.
*/
if (map_chg && !gbl_chg) {
gbl_reserve = hugepage_subpool_put_pages(spool, 1);
hugetlb_acct_memory(h, -gbl_reserve);
}


out_end_reservation:
if (map_chg != MAP_CHG_ENFORCED)
vma_end_reservation(h, vma, addr);
Expand Down Expand Up @@ -7239,7 +7247,7 @@ bool hugetlb_reserve_pages(struct inode *inode,
struct vm_area_struct *vma,
vm_flags_t vm_flags)
{
long chg = -1, add = -1;
long chg = -1, add = -1, spool_resv, gbl_resv;
struct hstate *h = hstate_inode(inode);
struct hugepage_subpool *spool = subpool_inode(inode);
struct resv_map *resv_map;
Expand Down Expand Up @@ -7374,8 +7382,16 @@ bool hugetlb_reserve_pages(struct inode *inode,
return true;

out_put_pages:
/* put back original number of pages, chg */
(void)hugepage_subpool_put_pages(spool, chg);
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);
}
out_uncharge_cgroup:
hugetlb_cgroup_uncharge_cgroup_rsvd(hstate_index(h),
chg * pages_per_huge_page(h), h_cg);
Expand Down
1 change: 0 additions & 1 deletion mm/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -1590,7 +1590,6 @@ unsigned long move_page_tables(struct pagetable_move_control *pmc);

#ifdef CONFIG_UNACCEPTED_MEMORY
void accept_page(struct page *page);
void unaccepted_cleanup_work(struct work_struct *work);
#else /* CONFIG_UNACCEPTED_MEMORY */
static inline void accept_page(struct page *page)
{
Expand Down
2 changes: 1 addition & 1 deletion mm/memory.c
Original file line number Diff line number Diff line change
Expand Up @@ -3751,7 +3751,7 @@ static bool __wp_can_reuse_large_anon_folio(struct folio *folio,

/* Stabilize the mapcount vs. refcount and recheck. */
folio_lock_large_mapcount(folio);
VM_WARN_ON_ONCE(folio_large_mapcount(folio) < folio_ref_count(folio));
VM_WARN_ON_ONCE_FOLIO(folio_large_mapcount(folio) > folio_ref_count(folio), folio);

if (folio_test_large_maybe_mapped_shared(folio))
goto unlock;
Expand Down
1 change: 0 additions & 1 deletion mm/mm_init.c
Original file line number Diff line number Diff line change
Expand Up @@ -1441,7 +1441,6 @@ static void __meminit zone_init_free_lists(struct zone *zone)

#ifdef CONFIG_UNACCEPTED_MEMORY
INIT_LIST_HEAD(&zone->unaccepted_pages);
INIT_WORK(&zone->unaccepted_cleanup, unaccepted_cleanup_work);
#endif
}

Expand Down
88 changes: 20 additions & 68 deletions mm/page_alloc.c
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,8 @@ EXPORT_SYMBOL(nr_online_nodes);
#endif

static bool page_contains_unaccepted(struct page *page, unsigned int order);
static bool cond_accept_memory(struct zone *zone, unsigned int order);
static bool cond_accept_memory(struct zone *zone, unsigned int order,
int alloc_flags);
static bool __free_unaccepted(struct page *page);

int page_group_by_mobility_disabled __read_mostly;
Expand Down Expand Up @@ -1151,14 +1152,9 @@ static inline void pgalloc_tag_sub(struct page *page, unsigned int nr)
__pgalloc_tag_sub(page, nr);
}

static inline void pgalloc_tag_sub_pages(struct page *page, unsigned int nr)
/* When tag is not NULL, assuming mem_alloc_profiling_enabled */
static inline void pgalloc_tag_sub_pages(struct alloc_tag *tag, unsigned int nr)
{
struct alloc_tag *tag;

if (!mem_alloc_profiling_enabled())
return;

tag = __pgalloc_tag_get(page);
if (tag)
this_cpu_sub(tag->counters->bytes, PAGE_SIZE * nr);
}
Expand All @@ -1168,7 +1164,7 @@ static inline void pgalloc_tag_sub_pages(struct page *page, unsigned int nr)
static inline void pgalloc_tag_add(struct page *page, struct task_struct *task,
unsigned int nr) {}
static inline void pgalloc_tag_sub(struct page *page, unsigned int nr) {}
static inline void pgalloc_tag_sub_pages(struct page *page, unsigned int nr) {}
static inline void pgalloc_tag_sub_pages(struct alloc_tag *tag, unsigned int nr) {}

#endif /* CONFIG_MEM_ALLOC_PROFILING */

Expand Down Expand Up @@ -3616,7 +3612,7 @@ get_page_from_freelist(gfp_t gfp_mask, unsigned int order, int alloc_flags,
}
}

cond_accept_memory(zone, order);
cond_accept_memory(zone, order, alloc_flags);

/*
* Detect whether the number of free pages is below high
Expand All @@ -3643,7 +3639,7 @@ get_page_from_freelist(gfp_t gfp_mask, unsigned int order, int alloc_flags,
gfp_mask)) {
int ret;

if (cond_accept_memory(zone, order))
if (cond_accept_memory(zone, order, alloc_flags))
goto try_this_zone;

/*
Expand Down Expand Up @@ -3696,7 +3692,7 @@ get_page_from_freelist(gfp_t gfp_mask, unsigned int order, int alloc_flags,

return page;
} else {
if (cond_accept_memory(zone, order))
if (cond_accept_memory(zone, order, alloc_flags))
goto try_this_zone;

/* Try again if zone has deferred pages */
Expand Down Expand Up @@ -4849,7 +4845,7 @@ unsigned long alloc_pages_bulk_noprof(gfp_t gfp, int preferred_nid,
goto failed;
}

cond_accept_memory(zone, 0);
cond_accept_memory(zone, 0, alloc_flags);
retry_this_zone:
mark = wmark_pages(zone, alloc_flags & ALLOC_WMARK_MASK) + nr_pages;
if (zone_watermark_fast(zone, 0, mark,
Expand All @@ -4858,7 +4854,7 @@ unsigned long alloc_pages_bulk_noprof(gfp_t gfp, int preferred_nid,
break;
}

if (cond_accept_memory(zone, 0))
if (cond_accept_memory(zone, 0, alloc_flags))
goto retry_this_zone;

/* Try again if zone has deferred pages */
Expand Down Expand Up @@ -5065,11 +5061,13 @@ static void ___free_pages(struct page *page, unsigned int order,
{
/* get PageHead before we drop reference */
int head = PageHead(page);
/* get alloc tag in case the page is released by others */
struct alloc_tag *tag = pgalloc_tag_get(page);

if (put_page_testzero(page))
__free_frozen_pages(page, order, fpi_flags);
else if (!head) {
pgalloc_tag_sub_pages(page, (1 << order) - 1);
pgalloc_tag_sub_pages(tag, (1 << order) - 1);
while (order-- > 0)
__free_frozen_pages(page + (1 << order), order,
fpi_flags);
Expand Down Expand Up @@ -7174,16 +7172,8 @@ bool has_managed_dma(void)

#ifdef CONFIG_UNACCEPTED_MEMORY

/* Counts number of zones with unaccepted pages. */
static DEFINE_STATIC_KEY_FALSE(zones_with_unaccepted_pages);

static bool lazy_accept = true;

void unaccepted_cleanup_work(struct work_struct *work)
{
static_branch_dec(&zones_with_unaccepted_pages);
}

static int __init accept_memory_parse(char *p)
{
if (!strcmp(p, "lazy")) {
Expand All @@ -7208,11 +7198,7 @@ static bool page_contains_unaccepted(struct page *page, unsigned int order)
static void __accept_page(struct zone *zone, unsigned long *flags,
struct page *page)
{
bool last;

list_del(&page->lru);
last = list_empty(&zone->unaccepted_pages);

account_freepages(zone, -MAX_ORDER_NR_PAGES, MIGRATE_MOVABLE);
__mod_zone_page_state(zone, NR_UNACCEPTED, -MAX_ORDER_NR_PAGES);
__ClearPageUnaccepted(page);
Expand All @@ -7221,28 +7207,6 @@ static void __accept_page(struct zone *zone, unsigned long *flags,
accept_memory(page_to_phys(page), PAGE_SIZE << MAX_PAGE_ORDER);

__free_pages_ok(page, MAX_PAGE_ORDER, FPI_TO_TAIL);

if (last) {
/*
* There are two corner cases:
*
* - If allocation occurs during the CPU bring up,
* static_branch_dec() cannot be used directly as
* it causes a deadlock on cpu_hotplug_lock.
*
* Instead, use schedule_work() to prevent deadlock.
*
* - If allocation occurs before workqueues are initialized,
* static_branch_dec() should be called directly.
*
* Workqueues are initialized before CPU bring up, so this
* will not conflict with the first scenario.
*/
if (system_wq)
schedule_work(&zone->unaccepted_cleanup);
else
unaccepted_cleanup_work(&zone->unaccepted_cleanup);
}
}

void accept_page(struct page *page)
Expand Down Expand Up @@ -7279,20 +7243,17 @@ static bool try_to_accept_memory_one(struct zone *zone)
return true;
}

static inline bool has_unaccepted_memory(void)
{
return static_branch_unlikely(&zones_with_unaccepted_pages);
}

static bool cond_accept_memory(struct zone *zone, unsigned int order)
static bool cond_accept_memory(struct zone *zone, unsigned int order,
int alloc_flags)
{
long to_accept, wmark;
bool ret = false;

if (!has_unaccepted_memory())
if (list_empty(&zone->unaccepted_pages))
return false;

if (list_empty(&zone->unaccepted_pages))
/* Bailout, since try_to_accept_memory_one() needs to take a lock */
if (alloc_flags & ALLOC_TRYLOCK)
return false;

wmark = promo_wmark_pages(zone);
Expand Down Expand Up @@ -7325,22 +7286,17 @@ static bool __free_unaccepted(struct page *page)
{
struct zone *zone = page_zone(page);
unsigned long flags;
bool first = false;

if (!lazy_accept)
return false;

spin_lock_irqsave(&zone->lock, flags);
first = list_empty(&zone->unaccepted_pages);
list_add_tail(&page->lru, &zone->unaccepted_pages);
account_freepages(zone, MAX_ORDER_NR_PAGES, MIGRATE_MOVABLE);
__mod_zone_page_state(zone, NR_UNACCEPTED, MAX_ORDER_NR_PAGES);
__SetPageUnaccepted(page);
spin_unlock_irqrestore(&zone->lock, flags);

if (first)
static_branch_inc(&zones_with_unaccepted_pages);

return true;
}

Expand All @@ -7351,7 +7307,8 @@ static bool page_contains_unaccepted(struct page *page, unsigned int order)
return false;
}

static bool cond_accept_memory(struct zone *zone, unsigned int order)
static bool cond_accept_memory(struct zone *zone, unsigned int order,
int alloc_flags)
{
return false;
}
Expand Down Expand Up @@ -7422,11 +7379,6 @@ struct page *try_alloc_pages_noprof(int nid, unsigned int order)
if (!pcp_allowed_order(order))
return NULL;

#ifdef CONFIG_UNACCEPTED_MEMORY
/* Bailout, since try_to_accept_memory_one() needs to take a lock */
if (has_unaccepted_memory())
return NULL;
#endif
/* Bailout, since _deferred_grow_zone() needs to take a lock */
if (deferred_pages_enabled())
return NULL;
Expand Down
12 changes: 10 additions & 2 deletions mm/userfaultfd.c
Original file line number Diff line number Diff line change
Expand Up @@ -1064,8 +1064,13 @@ static int move_present_pte(struct mm_struct *mm,
src_folio->index = linear_page_index(dst_vma, dst_addr);

orig_dst_pte = mk_pte(&src_folio->page, dst_vma->vm_page_prot);
/* Follow mremap() behavior and treat the entry dirty after the move */
orig_dst_pte = pte_mkwrite(pte_mkdirty(orig_dst_pte), dst_vma);
/* Set soft dirty bit so userspace can notice the pte was moved */
#ifdef CONFIG_MEM_SOFT_DIRTY
orig_dst_pte = pte_mksoft_dirty(orig_dst_pte);
#endif
if (pte_dirty(orig_src_pte))
orig_dst_pte = pte_mkdirty(orig_dst_pte);
orig_dst_pte = pte_mkwrite(orig_dst_pte, dst_vma);

set_pte_at(mm, dst_addr, dst_pte, orig_dst_pte);
out:
Expand Down Expand Up @@ -1100,6 +1105,9 @@ static int move_swap_pte(struct mm_struct *mm, struct vm_area_struct *dst_vma,
}

orig_src_pte = ptep_get_and_clear(mm, src_addr, src_pte);
#ifdef CONFIG_MEM_SOFT_DIRTY
orig_src_pte = pte_swp_mksoft_dirty(orig_src_pte);
#endif
set_pte_at(mm, dst_addr, dst_pte, orig_src_pte);
double_pt_unlock(dst_ptl, src_ptl);

Expand Down
Loading

0 comments on commit e72e784

Please sign in to comment.