13 hotfixes. All are cc:stable. 11 are for MM. All are singletons -

please see the changelogs for details.
 -----BEGIN PGP SIGNATURE-----
 
 iHUEABYKAB0WIQTTMBEPP41GrTpTJgfdBJ7gKXxAjgUCamfLHAAKCRDdBJ7gKXxA
 jrrMAQDQHf+VGi/lZaxtgfLv0OThODhzzckRI2dcTGG66YX/DQD/V4aTndWFUUpM
 j1aSODTTWeKi3Ija4E3dA7SIaZkqSAI=
 =s/6H
 -----END PGP SIGNATURE-----

Merge tag 'mm-hotfixes-stable-2026-07-27-14-18' of git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm

Pull misc fixes from Andrew Morton:
 "13 hotfixes. All are cc:stable. 11 are for MM. All are singletons -
  please see the changelogs for details"

* tag 'mm-hotfixes-stable-2026-07-27-14-18' of git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm:
  fs/proc/task_mmu: fix PAGEMAP_SCAN written state for PMD holes
  mm/hugetlb: fix list corruption in allocate_file_region_entries()
  mm: mglru: fix stale batch updates after memcg reparenting
  selftest: fix headers in fclog.c
  ocfs2: fix boundary check in ocfs2_check_dir_entry() to use buffer offset
  mm/percpu-km: fix bitmap overflow and accounting in pcpu_create_chunk()
  mm/util: don't read __page_2 for order-1 folios in snapshot_page()
  mm/hugetlb: fix swap entry corruption when clearing uffd-wp at fork()
  mm: migrate_device: fix pte_pfn/pte_dirty called on non-present PTE
  fs/proc/task_mmu: fix PAGEMAP_SCAN written state for unpopulated ptes
  userfaultfd: wait on source PMD during UFFDIO_MOVE
  lib: test_hmm: use device devt for coherent device range selection
  mm/vmstat: fold stranded per-cpu node stats when a node comes online
This commit is contained in:
Linus Torvalds 2026-07-27 14:36:26 -07:00
commit 62cc902415
13 changed files with 144 additions and 31 deletions

View File

@ -302,10 +302,11 @@ static int ocfs2_check_dir_entry(struct inode *dir,
unsigned long offset)
{
const char *error_msg = NULL;
unsigned long buf_offset = (char *)de - buf;
unsigned long next_offset;
int rlen;
if (offset > size - OCFS2_DIR_REC_LEN(1)) {
if (buf_offset > size || size - buf_offset < OCFS2_DIR_REC_LEN(1)) {
/* Dirent is (maybe partially) beyond the buffer
* boundaries so touching 'de' members is unsafe.
*/
@ -316,7 +317,7 @@ static int ocfs2_check_dir_entry(struct inode *dir,
}
rlen = le16_to_cpu(de->rec_len);
next_offset = ((char *) de - buf) + rlen;
next_offset = buf_offset + rlen;
if (unlikely(rlen < OCFS2_DIR_REC_LEN(1)))
error_msg = "rec_len is smaller than minimal";

View File

@ -2432,8 +2432,18 @@ static unsigned long pagemap_page_category(struct pagemap_scan_private *p,
{
unsigned long categories;
if (pte_none(pte))
return 0;
if (pte_none(pte)) {
/*
* An unpopulated pte carries no uffd-wp marker, i.e. it is not
* write-protected, the same condition under which the present
* and swap cases below report PAGE_IS_WRITTEN. Report it here
* too so this generic path agrees with the PAGE_IS_WRITTEN fast
* path in pagemap_scan_pmd_entry(), which reports pte_none as
* written and, under PM_SCAN_WP_MATCHING, arms a marker. The
* fast path applies no VMA test, so neither does this.
*/
return PAGE_IS_WRITTEN;
}
if (pte_present(pte)) {
struct page *page;
@ -3039,12 +3049,28 @@ static int pagemap_scan_pte_hole(unsigned long addr, unsigned long end,
{
struct pagemap_scan_private *p = walk->private;
struct vm_area_struct *vma = walk->vma;
unsigned long categories;
int ret, err;
if (!vma || !pagemap_scan_is_interesting_page(p->cur_vma_category, p))
if (!vma)
return 0;
ret = pagemap_scan_output(p->cur_vma_category, p, addr, &end);
/*
* In a uffd-wp VMA an unpopulated range is treated as written:
* uffd-wp registration populates page tables and installs markers
* with WP_UNPOPULATED, so a missing marker means the range was
* zapped. See the pte_none() handling in pagemap_page_category().
*
* hugetlb differs, see pagemap_hugetlb_category().
*/
categories = p->cur_vma_category;
if (userfaultfd_wp(vma) && !is_vm_hugetlb_page(vma))
categories |= PAGE_IS_WRITTEN;
if (!pagemap_scan_is_interesting_page(categories, p))
return 0;
ret = pagemap_scan_output(categories, p, addr, &end);
if (addr == end)
return ret;

View File

@ -1493,6 +1493,31 @@ static inline void lruvec_lock_irq(struct lruvec *lruvec)
spin_lock_irq(&lruvec->lru_lock);
}
static inline struct lruvec *lruvec_live_lock_irq(struct lruvec *lruvec)
{
#ifdef CONFIG_MEMCG
struct pglist_data *pgdat = lruvec_pgdat(lruvec);
struct mem_cgroup *memcg = lruvec_memcg(lruvec);
rcu_read_lock();
/*
* The memcg can be NULL when the memory controller is disabled.
* Otherwise, the caller keeps the memcg owning @lruvec alive.
*/
while (unlikely(memcg && css_is_dying(&memcg->css))) {
memcg = parent_mem_cgroup(memcg);
lruvec = mem_cgroup_lruvec(memcg, pgdat);
}
spin_lock_irq(&lruvec->lru_lock);
#else
lruvec_lock_irq(lruvec);
#endif
return lruvec;
}
static inline void lruvec_unlock(struct lruvec *lruvec)
{
spin_unlock(&lruvec->lru_lock);

View File

@ -581,7 +581,7 @@ static int dmirror_allocate_chunk(struct dmirror_device *mdevice,
devmem->pagemap.type = MEMORY_DEVICE_PRIVATE;
break;
case HMM_DMIRROR_MEMORY_DEVICE_COHERENT:
devmem->pagemap.range.start = (MINOR(mdevice->cdevice.dev) - 2) ?
devmem->pagemap.range.start = (MINOR(mdevice->device.devt) - 2) ?
spm_addr_dev0 :
spm_addr_dev1;
devmem->pagemap.range.end = devmem->pagemap.range.start +

View File

@ -2774,7 +2774,7 @@ int move_pages_huge_pmd(struct mm_struct *mm, pmd_t *dst_pmd, pmd_t *src_pmd, pm
if (!pmd_trans_huge(src_pmdval)) {
spin_unlock(src_ptl);
if (pmd_is_migration_entry(src_pmdval)) {
pmd_migration_entry_wait(mm, &src_pmdval);
pmd_migration_entry_wait(mm, src_pmd);
return -EAGAIN;
}
return -ENOENT;

View File

@ -693,7 +693,7 @@ static int allocate_file_region_entries(struct resv_map *resv,
spin_lock(&resv->lock);
list_splice(&allocated_regions, &resv->region_cache);
list_splice_init(&allocated_regions, &resv->region_cache);
resv->region_cache_count += to_allocate;
}
@ -4917,8 +4917,12 @@ int copy_hugetlb_page_range(struct mm_struct *dst, struct mm_struct *src,
softleaf = softleaf_from_pte(entry);
if (unlikely(softleaf_is_hwpoison(softleaf))) {
if (!userfaultfd_wp(dst_vma))
entry = huge_pte_clear_uffd_wp(entry);
/*
* A hwpoison entry never carries the uffd-wp bit: it is
* installed fresh by make_hwpoison_entry() and
* hugetlb_change_protection() leaves it untouched, so
* there is nothing to clear for the child.
*/
set_huge_pte_at(dst, addr, dst_pte, entry, sz);
} else if (unlikely(softleaf_is_migration(softleaf))) {
bool uffd_wp = pte_swp_uffd_wp(entry);
@ -4936,7 +4940,7 @@ int copy_hugetlb_page_range(struct mm_struct *dst, struct mm_struct *src,
set_huge_pte_at(src, addr, src_pte, entry, sz);
}
if (!userfaultfd_wp(dst_vma))
entry = huge_pte_clear_uffd_wp(entry);
entry = pte_swp_clear_uffd_wp(entry);
set_huge_pte_at(dst, addr, dst_pte, entry, sz);
} else if (unlikely(pte_is_marker(entry))) {
const pte_marker marker = copy_pte_marker(softleaf, dst_vma);

View File

@ -401,7 +401,8 @@ static int migrate_vma_collect_pmd(pmd_t *pmdp,
bool anon_exclusive;
pte_t swp_pte;
flush_cache_page(vma, addr, pte_pfn(pte));
if (pte_present(pte))
flush_cache_page(vma, addr, pte_pfn(pte));
anon_exclusive = folio_test_anon(folio) &&
PageAnonExclusive(page);
if (anon_exclusive) {
@ -422,7 +423,7 @@ static int migrate_vma_collect_pmd(pmd_t *pmdp,
migrate->cpages++;
/* Set the dirty flag on the folio now the pte is gone. */
if (pte_dirty(pte))
if (pte_present(pte) && pte_dirty(pte))
folio_mark_dirty(folio);
/* Setup special migration page table entry */

View File

@ -1540,7 +1540,7 @@ void __ref free_area_init_core_hotplug(struct pglist_data *pgdat)
{
int nid = pgdat->node_id;
enum zone_type z;
int cpu;
int cpu, i;
pgdat_init_internals(pgdat);
@ -1558,10 +1558,17 @@ void __ref free_area_init_core_hotplug(struct pglist_data *pgdat)
pgdat->node_start_pfn = 0;
pgdat->node_present_pages = 0;
for_each_online_cpu(cpu) {
struct per_cpu_nodestat *p;
/*
* Hot-unplug can leave per-cpu vmstat deltas unfolded (folders skip
* offline nodes) - reconcile this at online. Foreign access to counters
* is safe: the node is not online yet and we hold the hotplug lock.
*/
for_each_possible_cpu(cpu) {
struct per_cpu_nodestat *p = per_cpu_ptr(pgdat->per_cpu_nodestats, cpu);
p = per_cpu_ptr(pgdat->per_cpu_nodestats, cpu);
for (i = 0; i < NR_VM_NODE_STAT_ITEMS; i++)
if (p->vm_node_stat_diff[i])
node_page_state_add(p->vm_node_stat_diff[i], pgdat, i);
memset(p, 0, sizeof(*p));
}

View File

@ -75,7 +75,7 @@ static struct pcpu_chunk *pcpu_create_chunk(gfp_t gfp)
chunk->base_addr = page_address(pages);
spin_lock_irqsave(&pcpu_lock, flags);
pcpu_chunk_populated(chunk, 0, nr_pages);
pcpu_chunk_populated(chunk, 0, chunk->nr_pages);
spin_unlock_irqrestore(&pcpu_lock, flags);
pcpu_stats_chunk_alloc();

View File

@ -1353,7 +1353,7 @@ void snapshot_page(struct page_snapshot *ps, const struct page *page)
if (ps->idx < MAX_FOLIO_NR_PAGES) {
memcpy(&ps->folio_snapshot, foliop, 2 * sizeof(struct page));
nr_pages = folio_nr_pages(&ps->folio_snapshot);
if (nr_pages > 1)
if (nr_pages > 2)
memcpy(&ps->folio_snapshot.__page_2, &foliop->__page_2,
sizeof(struct page));
set_ps_flags(ps, foliop, page);

View File

@ -3265,7 +3265,7 @@ static void update_batch_size(struct lru_gen_mm_walk *walk, struct folio *folio,
static void reset_batch_size(struct lru_gen_mm_walk *walk)
{
int gen, type, zone;
struct lruvec *lruvec = walk->lruvec;
struct lruvec *lruvec = lruvec_live_lock_irq(walk->lruvec);
struct lru_gen_folio *lrugen = &lruvec->lrugen;
walk->batched = 0;
@ -3285,6 +3285,8 @@ static void reset_batch_size(struct lru_gen_mm_walk *walk)
lru += LRU_ACTIVE;
__update_lru_size(lruvec, lru, zone, delta);
}
lruvec_unlock_irq(lruvec);
}
static int should_skip_vma(unsigned long start, unsigned long end, struct mm_walk *args)
@ -3779,11 +3781,8 @@ static void walk_mm(struct mm_struct *mm, struct lru_gen_mm_walk *walk)
mmap_read_unlock(mm);
}
if (walk->batched) {
lruvec_lock_irq(lruvec);
if (walk->batched)
reset_batch_size(walk);
lruvec_unlock_irq(lruvec);
}
cond_resched();
} while (err == -EAGAIN);
@ -4867,9 +4866,7 @@ static int evict_folios(unsigned long nr_to_scan, struct lruvec *lruvec,
walk = current->reclaim_state->mm_walk;
if (walk && walk->batched) {
walk->lruvec = lruvec;
lruvec_lock_irq(lruvec);
reset_batch_size(walk);
lruvec_unlock_irq(lruvec);
}
mod_lruvec_state(lruvec, PGDEMOTE_KSWAPD + reclaimer_offset(sc),

View File

@ -6,10 +6,8 @@
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/mount.h>

View File

@ -1051,6 +1051,57 @@ static void test_simple(void)
ksft_test_result(i == TEST_ITERATIONS, "Test %s\n", __func__);
}
/*
* A range that was populated and then MADV_DONTNEED'd is genuine pte_none
* with no uffd-wp marker. Such a pte must read the same regardless of which
* PAGEMAP_SCAN path serves the request: both the PAGE_IS_WRITTEN fast path and
* the generic path (reached e.g. via category_anyof_mask) must report every
* page written.
*/
static void unpopulated_scan_test(void)
{
int npages = 16, i;
long mem_size = npages * page_size;
struct page_region regions[16];
long fast = 0, slow = 0, ret;
char *mem;
mem = mmap(NULL, mem_size, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (mem == MAP_FAILED)
ksft_exit_fail_msg("%s mmap failed\n", __func__);
wp_init(mem, mem_size);
/* Populate, then drop: the ptes become pte_none without a marker. */
memset(mem, 1, mem_size);
if (madvise(mem, mem_size, MADV_DONTNEED))
ksft_exit_fail_msg("%s MADV_DONTNEED failed\n", __func__);
/* Fast path: category_mask == return_mask == PAGE_IS_WRITTEN. */
ret = pagemap_ioctl(mem, mem_size, regions, npages, 0, 0,
PAGE_IS_WRITTEN, 0, 0, PAGE_IS_WRITTEN);
if (ret < 0)
ksft_exit_fail_msg("%s fast scan failed\n", __func__);
for (i = 0; i < ret; i++)
fast += LEN(regions[i]);
/* Generic path: same query expressed via category_anyof_mask. */
ret = pagemap_ioctl(mem, mem_size, regions, npages, 0, 0,
0, PAGE_IS_WRITTEN, 0, PAGE_IS_WRITTEN);
if (ret < 0)
ksft_exit_fail_msg("%s generic scan failed\n", __func__);
for (i = 0; i < ret; i++)
slow += LEN(regions[i]);
ksft_test_result(fast == npages && slow == npages,
"%s unpopulated ptes reported written by both paths (%ld, %ld of %d)\n",
__func__, fast, slow, npages);
wp_free(mem, mem_size);
munmap(mem, mem_size);
}
int sanity_tests(void)
{
unsigned long long mem_size, vec_size;
@ -1559,7 +1610,7 @@ int main(int __attribute__((unused)) argc, char *argv[])
if (!hugetlb_setup_default(4))
ksft_print_msg("HugeTLB test will be skipped\n");
ksft_set_plan(117);
ksft_set_plan(118);
page_size = getpagesize();
hpage_size = read_pmd_pagesize();
@ -1737,6 +1788,9 @@ int main(int __attribute__((unused)) argc, char *argv[])
/* 17. ZEROPFN tests */
zeropfn_tests();
/* 18. Unpopulated pte scan-path consistency */
unpopulated_scan_test();
close(pagemap_fd);
ksft_finished();
}