Merge tag 'mm-hotfixes-stable-2025-08-21-18-17' of git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm

Pull misc fixes from Andrew Morton:
 "20 hotfixes. 10 are cc:stable and the remainder address post-6.16
  issues or aren't considered necessary for -stable kernels. 17 of these
  fixes are for MM.

  As usual, singletons all over the place, apart from a three-patch
  series of KHO followup work from Pasha which is actually also a bunch
  of singletons"

* tag 'mm-hotfixes-stable-2025-08-21-18-17' of git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm:
  mm/mremap: fix WARN with uffd that has remap events disabled
  mm/damon/sysfs-schemes: put damos dests dir after removing its files
  mm/migrate: fix NULL movable_ops if CONFIG_ZSMALLOC=m
  mm/damon/core: fix damos_commit_filter not changing allow
  mm/memory-failure: fix infinite UCE for VM_PFNMAP pfn
  MAINTAINERS: mark MGLRU as maintained
  mm: rust: add page.rs to MEMORY MANAGEMENT - RUST
  iov_iter: iterate_folioq: fix handling of offset >= folio size
  selftests/damon: fix selftests by installing drgn related script
  .mailmap: add entry for Easwar Hariharan
  selftests/mm: add test for invalid multi VMA operations
  mm/mremap: catch invalid multi VMA moves earlier
  mm/mremap: allow multi-VMA move when filesystem uses thp_get_unmapped_area
  mm/damon/core: fix commit_ops_filters by using correct nth function
  tools/testing: add linux/args.h header and fix radix, VMA tests
  mm/debug_vm_pgtable: clear page table entries at destroy_args()
  squashfs: fix memory leak in squashfs_fill_super
  kho: warn if KHO is disabled due to an error
  kho: mm: don't allow deferred struct page with KHO
  kho: init new_physxa->phys_bits to fix lockdep
This commit is contained in:
Linus Torvalds
2025-08-22 08:54:34 -04:00
19 changed files with 487 additions and 70 deletions

View File

@@ -4,6 +4,7 @@
TEST_GEN_FILES += access_memory access_memory_even
TEST_FILES = _damon_sysfs.py
TEST_FILES += drgn_dump_damon_status.py
# functionality tests
TEST_PROGS += sysfs.sh

View File

@@ -5,10 +5,14 @@
#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <linux/userfaultfd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <syscall.h>
#include <time.h>
#include <stdbool.h>
@@ -168,6 +172,7 @@ static bool is_range_mapped(FILE *maps_fp, unsigned long start,
if (first_val <= start && second_val >= end) {
success = true;
fflush(maps_fp);
break;
}
}
@@ -175,6 +180,15 @@ static bool is_range_mapped(FILE *maps_fp, unsigned long start,
return success;
}
/* Check if [ptr, ptr + size) mapped in /proc/self/maps. */
static bool is_ptr_mapped(FILE *maps_fp, void *ptr, unsigned long size)
{
unsigned long start = (unsigned long)ptr;
unsigned long end = start + size;
return is_range_mapped(maps_fp, start, end);
}
/*
* Returns the start address of the mapping on success, else returns
* NULL on failure.
@@ -733,6 +747,249 @@ out:
dont_unmap ? " [dontunnmap]" : "");
}
#ifdef __NR_userfaultfd
static void mremap_move_multi_invalid_vmas(FILE *maps_fp,
unsigned long page_size)
{
char *test_name = "mremap move multiple invalid vmas";
const size_t size = 10 * page_size;
bool success = true;
char *ptr, *tgt_ptr;
int uffd, err, i;
void *res;
struct uffdio_api api = {
.api = UFFD_API,
.features = UFFD_EVENT_PAGEFAULT,
};
uffd = syscall(__NR_userfaultfd, O_NONBLOCK);
if (uffd == -1) {
err = errno;
perror("userfaultfd");
if (err == EPERM) {
ksft_test_result_skip("%s - missing uffd", test_name);
return;
}
success = false;
goto out;
}
if (ioctl(uffd, UFFDIO_API, &api)) {
perror("ioctl UFFDIO_API");
success = false;
goto out_close_uffd;
}
ptr = mmap(NULL, size, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANON, -1, 0);
if (ptr == MAP_FAILED) {
perror("mmap");
success = false;
goto out_close_uffd;
}
tgt_ptr = mmap(NULL, size, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0);
if (tgt_ptr == MAP_FAILED) {
perror("mmap");
success = false;
goto out_close_uffd;
}
if (munmap(tgt_ptr, size)) {
perror("munmap");
success = false;
goto out_unmap;
}
/*
* Unmap so we end up with:
*
* 0 2 4 6 8 10 offset in buffer
* |*| |*| |*| |*| |*|
* |*| |*| |*| |*| |*|
*
* Additionally, register each with UFFD.
*/
for (i = 0; i < 10; i += 2) {
void *unmap_ptr = &ptr[(i + 1) * page_size];
unsigned long start = (unsigned long)&ptr[i * page_size];
struct uffdio_register reg = {
.range = {
.start = start,
.len = page_size,
},
.mode = UFFDIO_REGISTER_MODE_MISSING,
};
if (ioctl(uffd, UFFDIO_REGISTER, &reg) == -1) {
perror("ioctl UFFDIO_REGISTER");
success = false;
goto out_unmap;
}
if (munmap(unmap_ptr, page_size)) {
perror("munmap");
success = false;
goto out_unmap;
}
}
/*
* Now try to move the entire range which is invalid for multi VMA move.
*
* This will fail, and no VMA should be moved, as we check this ahead of
* time.
*/
res = mremap(ptr, size, size, MREMAP_MAYMOVE | MREMAP_FIXED, tgt_ptr);
err = errno;
if (res != MAP_FAILED) {
fprintf(stderr, "mremap() succeeded for multi VMA uffd armed\n");
success = false;
goto out_unmap;
}
if (err != EFAULT) {
errno = err;
perror("mrmeap() unexpected error");
success = false;
goto out_unmap;
}
if (is_ptr_mapped(maps_fp, tgt_ptr, page_size)) {
fprintf(stderr,
"Invalid uffd-armed VMA at start of multi range moved\n");
success = false;
goto out_unmap;
}
/*
* Now try to move a single VMA, this should succeed as not multi VMA
* move.
*/
res = mremap(ptr, page_size, page_size,
MREMAP_MAYMOVE | MREMAP_FIXED, tgt_ptr);
if (res == MAP_FAILED) {
perror("mremap single invalid-multi VMA");
success = false;
goto out_unmap;
}
/*
* Unmap the VMA, and remap a non-uffd registered (therefore, multi VMA
* move valid) VMA at the start of ptr range.
*/
if (munmap(tgt_ptr, page_size)) {
perror("munmap");
success = false;
goto out_unmap;
}
res = mmap(ptr, page_size, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);
if (res == MAP_FAILED) {
perror("mmap");
success = false;
goto out_unmap;
}
/*
* Now try to move the entire range, we should succeed in moving the
* first VMA, but no others, and report a failure.
*/
res = mremap(ptr, size, size, MREMAP_MAYMOVE | MREMAP_FIXED, tgt_ptr);
err = errno;
if (res != MAP_FAILED) {
fprintf(stderr, "mremap() succeeded for multi VMA uffd armed\n");
success = false;
goto out_unmap;
}
if (err != EFAULT) {
errno = err;
perror("mrmeap() unexpected error");
success = false;
goto out_unmap;
}
if (!is_ptr_mapped(maps_fp, tgt_ptr, page_size)) {
fprintf(stderr, "Valid VMA not moved\n");
success = false;
goto out_unmap;
}
/*
* Unmap the VMA, and map valid VMA at start of ptr range, and replace
* all existing multi-move invalid VMAs, except the last, with valid
* multi-move VMAs.
*/
if (munmap(tgt_ptr, page_size)) {
perror("munmap");
success = false;
goto out_unmap;
}
if (munmap(ptr, size - 2 * page_size)) {
perror("munmap");
success = false;
goto out_unmap;
}
for (i = 0; i < 8; i += 2) {
res = mmap(&ptr[i * page_size], page_size,
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);
if (res == MAP_FAILED) {
perror("mmap");
success = false;
goto out_unmap;
}
}
/*
* Now try to move the entire range, we should succeed in moving all but
* the last VMA, and report a failure.
*/
res = mremap(ptr, size, size, MREMAP_MAYMOVE | MREMAP_FIXED, tgt_ptr);
err = errno;
if (res != MAP_FAILED) {
fprintf(stderr, "mremap() succeeded for multi VMA uffd armed\n");
success = false;
goto out_unmap;
}
if (err != EFAULT) {
errno = err;
perror("mrmeap() unexpected error");
success = false;
goto out_unmap;
}
for (i = 0; i < 10; i += 2) {
bool is_mapped = is_ptr_mapped(maps_fp,
&tgt_ptr[i * page_size], page_size);
if (i < 8 && !is_mapped) {
fprintf(stderr, "Valid VMA not moved at %d\n", i);
success = false;
goto out_unmap;
} else if (i == 8 && is_mapped) {
fprintf(stderr, "Invalid VMA moved at %d\n", i);
success = false;
goto out_unmap;
}
}
out_unmap:
if (munmap(tgt_ptr, size))
perror("munmap tgt");
if (munmap(ptr, size))
perror("munmap src");
out_close_uffd:
close(uffd);
out:
if (success)
ksft_test_result_pass("%s\n", test_name);
else
ksft_test_result_fail("%s\n", test_name);
}
#else
static void mremap_move_multi_invalid_vmas(FILE *maps_fp, unsigned long page_size)
{
char *test_name = "mremap move multiple invalid vmas";
ksft_test_result_skip("%s - missing uffd", test_name);
}
#endif /* __NR_userfaultfd */
/* Returns the time taken for the remap on success else returns -1. */
static long long remap_region(struct config c, unsigned int threshold_mb,
char *rand_addr)
@@ -1074,7 +1331,7 @@ int main(int argc, char **argv)
char *rand_addr;
size_t rand_size;
int num_expand_tests = 2;
int num_misc_tests = 8;
int num_misc_tests = 9;
struct test test_cases[MAX_TEST] = {};
struct test perf_test_cases[MAX_PERF_TEST];
int page_size;
@@ -1197,8 +1454,6 @@ int main(int argc, char **argv)
mremap_expand_merge(maps_fp, page_size);
mremap_expand_merge_offset(maps_fp, page_size);
fclose(maps_fp);
mremap_move_within_range(pattern_seed, rand_addr);
mremap_move_1mb_from_start(pattern_seed, rand_addr);
mremap_shrink_multiple_vmas(page_size, /* inplace= */true);
@@ -1207,6 +1462,9 @@ int main(int argc, char **argv)
mremap_move_multiple_vmas(pattern_seed, page_size, /* dontunmap= */ true);
mremap_move_multiple_vmas_split(pattern_seed, page_size, /* dontunmap= */ false);
mremap_move_multiple_vmas_split(pattern_seed, page_size, /* dontunmap= */ true);
mremap_move_multi_invalid_vmas(maps_fp, page_size);
fclose(maps_fp);
if (run_perf_tests) {
ksft_print_msg("\n%s\n",

View File

@@ -1 +1,5 @@
/* Avoid duplicate definitions due to system headers. */
#ifdef __CONCAT
#undef __CONCAT
#endif
#include "../../../../include/linux/idr.h"