mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-12-27 11:06:41 -05:00
Merge tag 'mm-hotfixes-stable-2025-05-17-09-41' of git://git.kernel.org/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
This commit is contained in:
12
MAINTAINERS
12
MAINTAINERS
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 */
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
28
mm/hugetlb.c
28
mm/hugetlb.c
@@ -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;
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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 */
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
|
||||
/*
|
||||
@@ -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 */
|
||||
@@ -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,
|
||||
@@ -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 */
|
||||
@@ -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);
|
||||
@@ -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")) {
|
||||
@@ -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);
|
||||
@@ -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)
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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:
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -1243,19 +1243,19 @@ void zs_obj_write(struct zs_pool *pool, unsigned long handle,
|
||||
class = zspage_class(pool, zspage);
|
||||
off = offset_in_page(class->size * obj_idx);
|
||||
|
||||
if (off + class->size <= PAGE_SIZE) {
|
||||
if (!ZsHugePage(zspage))
|
||||
off += ZS_HANDLE_SIZE;
|
||||
|
||||
if (off + mem_len <= PAGE_SIZE) {
|
||||
/* this object is contained entirely within a page */
|
||||
void *dst = kmap_local_zpdesc(zpdesc);
|
||||
|
||||
if (!ZsHugePage(zspage))
|
||||
off += ZS_HANDLE_SIZE;
|
||||
memcpy(dst + off, handle_mem, mem_len);
|
||||
kunmap_local(dst);
|
||||
} else {
|
||||
/* this object spans two pages */
|
||||
size_t sizes[2];
|
||||
|
||||
off += ZS_HANDLE_SIZE;
|
||||
sizes[0] = PAGE_SIZE - off;
|
||||
sizes[1] = mem_len - sizes[0];
|
||||
|
||||
|
||||
Reference in New Issue
Block a user