The quilt patch titled Subject: selftests/hmm-tests: add test for dirty bits has been removed from the -mm tree. Its filename was selftests-hmm-tests-add-test-for-dirty-bits.patch This patch was dropped because an updated version will be merged ------------------------------------------------------ From: Alistair Popple <apopple@xxxxxxxxxx> Subject: selftests/hmm-tests: add test for dirty bits Date: Tue, 16 Aug 2022 17:39:25 +1000 We were not correctly copying PTE dirty bits to pages during migrate_vma_setup() calls. This could potentially lead to data loss, so add a test for this. [apopple@xxxxxxxxxx: fix migrate_dirty_page test] Link: https://lkml.kernel.org/r/20220914091205.557676-1-apopple@xxxxxxxxxx Link: https://lkml.kernel.org/r/23069a5c6e07d16d4c4f0951ff003591ffc4f656.1660635033.git-series.apopple@xxxxxxxxxx Link: https://lkml.kernel.org/r/68cf1d70f3fb8ce4e3c1a4899c19df4f6c382a13.1662078528.git-series.apopple@xxxxxxxxxx Signed-off-by: Alistair Popple <apopple@xxxxxxxxxx> Reviewed-by: Mika Penttilä <mpenttil@xxxxxxxxxx> Cc: Peter Xu <peterx@xxxxxxxxxx> Cc: Huang Ying <ying.huang@xxxxxxxxx> Cc: Alex Sierra <alex.sierra@xxxxxxx> Cc: Ben Skeggs <bskeggs@xxxxxxxxxx> Cc: David Hildenbrand <david@xxxxxxxxxx> Cc: Felix Kuehling <felix.kuehling@xxxxxxx> Cc: Jason Gunthorpe <jgg@xxxxxxxxxx> Cc: John Hubbard <jhubbard@xxxxxxxxxx> Cc: Karol Herbst <kherbst@xxxxxxxxxx> Cc: Logan Gunthorpe <logang@xxxxxxxxxxxx> Cc: Lyude Paul <lyude@xxxxxxxxxx> Cc: Matthew Wilcox (Oracle) <willy@xxxxxxxxxxxxx> Cc: Paul Mackerras <paulus@xxxxxxxxxx> Cc: Ralph Campbell <rcampbell@xxxxxxxxxx> Signed-off-by: Andrew Morton <akpm@xxxxxxxxxxxxxxxxxxxx> --- tools/testing/selftests/vm/Makefile | 1 tools/testing/selftests/vm/hmm-tests.c | 144 +++++++++++++++++++++++ tools/testing/selftests/vm/vm_util.c | 22 +++ tools/testing/selftests/vm/vm_util.h | 2 4 files changed, 169 insertions(+) --- a/tools/testing/selftests/vm/hmm-tests.c~selftests-hmm-tests-add-test-for-dirty-bits +++ a/tools/testing/selftests/vm/hmm-tests.c @@ -33,6 +33,7 @@ */ #include "../../../../lib/test_hmm_uapi.h" #include "../../../../mm/gup_test.h" +#include "vm_util.h" struct hmm_buffer { void *ptr; @@ -1240,6 +1241,149 @@ TEST_F(hmm, migrate_multiple) } } +static char cgroup[] = "/sys/fs/cgroup/hmm-test-XXXXXX"; +static int write_cgroup_param(char *cgroup_path, char *param, long value) +{ + int ret; + FILE *f; + char *filename; + + if (asprintf(&filename, "%s/%s", cgroup_path, param) < 0) + return -1; + + f = fopen(filename, "w"); + if (!f) { + ret = -1; + goto out; + } + + ret = fprintf(f, "%ld\n", value); + if (ret < 0) + goto out1; + + ret = 0; + +out1: + fclose(f); +out: + free(filename); + + return ret; +} + +static int setup_cgroup(void) +{ + pid_t pid = getpid(); + int ret; + + if (!mkdtemp(cgroup)) + return -1; + + ret = write_cgroup_param(cgroup, "cgroup.procs", pid); + if (ret) + return ret; + + return 0; +} + +static int destroy_cgroup(void) +{ + pid_t pid = getpid(); + int ret; + + ret = write_cgroup_param("/sys/fs/cgroup/cgroup.procs", + "cgroup.proc", pid); + if (ret) + return ret; + + if (rmdir(cgroup)) + return -1; + + return 0; +} + +/* Returns true if at least one page in the range is on swap */ +static bool pages_swapped(void *ptr, size_t size) +{ + int fd = open("/proc/self/pagemap", O_RDONLY); + bool ret; + + if (fd < 0) + return false; + + ret = pagemap_range_some_swapped(fd, ptr, size); + close(fd); + + return ret; +} + +/* + * Try and migrate a dirty page that has previously been swapped to disk. This + * checks that we don't lose dirty bits. + */ +TEST_F(hmm, migrate_dirty_page) +{ + struct hmm_buffer *buffer; + unsigned long npages; + unsigned long size; + unsigned long i; + int *ptr; + int tmp = 0; + + npages = ALIGN(HMM_BUFFER_SIZE, self->page_size) >> self->page_shift; + ASSERT_NE(npages, 0); + size = npages << self->page_shift; + + buffer = malloc(sizeof(*buffer)); + ASSERT_NE(buffer, NULL); + + buffer->fd = -1; + buffer->size = size; + buffer->mirror = malloc(size); + ASSERT_NE(buffer->mirror, NULL); + + ASSERT_EQ(setup_cgroup(), 0); + + buffer->ptr = mmap(NULL, size, + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, + buffer->fd, 0); + ASSERT_NE(buffer->ptr, MAP_FAILED); + + /* Initialize buffer in system memory. */ + for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i) + ptr[i] = 0; + + ASSERT_FALSE(write_cgroup_param(cgroup, "memory.reclaim", 1UL<<30)); + + /* Make sure at least some pages got paged to disk. */ + if (!pages_swapped(buffer->ptr, size)) + SKIP(return, "Pages weren't swapped when they should have been"); + + /* Fault pages back in from swap as clean pages */ + for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i) + tmp += ptr[i]; + + /* Dirty the pte */ + for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i) + ptr[i] = i; + + /* + * Attempt to migrate memory to device. This might fail if some pages + * are/were backed by swap but that's ok. + */ + hmm_migrate_sys_to_dev(self->fd, buffer, npages); + + ASSERT_FALSE(write_cgroup_param(cgroup, "memory.reclaim", 1UL<<30)); + + /* Check we still see the updated data after restoring from swap. */ + for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i) + ASSERT_EQ(ptr[i], i); + + hmm_buffer_free(buffer); + destroy_cgroup(); +} + /* * Read anonymous memory multiple times. */ --- a/tools/testing/selftests/vm/Makefile~selftests-hmm-tests-add-test-for-dirty-bits +++ a/tools/testing/selftests/vm/Makefile @@ -98,6 +98,7 @@ include ../lib.mk $(OUTPUT)/madv_populate: vm_util.c $(OUTPUT)/soft-dirty: vm_util.c $(OUTPUT)/split_huge_page_test: vm_util.c +$(OUTPUT)/hmm-tests: vm_util.c ifeq ($(MACHINE),x86_64) BINARIES_32 := $(patsubst %,$(OUTPUT)/%,$(BINARIES_32)) --- a/tools/testing/selftests/vm/vm_util.c~selftests-hmm-tests-add-test-for-dirty-bits +++ a/tools/testing/selftests/vm/vm_util.c @@ -1,4 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 +#include <unistd.h> #include <string.h> #include <fcntl.h> #include "../kselftest.h" @@ -20,6 +21,14 @@ uint64_t pagemap_get_entry(int fd, char return entry; } +bool pagemap_is_swapped(int fd, char *start) +{ + uint64_t entry = pagemap_get_entry(fd, start); + + // Check if swap entry bit (62nd bit) is set + return entry & 0x4000000000000000ull; +} + bool pagemap_is_softdirty(int fd, char *start) { uint64_t entry = pagemap_get_entry(fd, start); @@ -28,6 +37,19 @@ bool pagemap_is_softdirty(int fd, char * return entry & 0x0080000000000000ull; } +/* Returns true if at least one page in the range is in swap */ +bool pagemap_range_some_swapped(int fd, char *start, size_t len) +{ + unsigned long i; + unsigned long npages = len / getpagesize(); + + for (i = 0; i < npages; i++) + if (pagemap_is_swapped(fd, start + i * getpagesize())) + return true; + + return false; +} + void clear_softdirty(void) { int ret; --- a/tools/testing/selftests/vm/vm_util.h~selftests-hmm-tests-add-test-for-dirty-bits +++ a/tools/testing/selftests/vm/vm_util.h @@ -4,6 +4,8 @@ uint64_t pagemap_get_entry(int fd, char *start); bool pagemap_is_softdirty(int fd, char *start); +bool pagemap_is_swapped(int fd, char *start); +bool pagemap_range_some_swapped(int fd, char *start, size_t len); void clear_softdirty(void); uint64_t read_pmd_pagesize(void); uint64_t check_huge(void *addr); _ Patches currently in -mm which might be from apopple@xxxxxxxxxx are