From: Alistair Popple <apopple@nvidia.com>
To: linux-mm@kvack.org
Cc: linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org,
Alistair Popple <apopple@nvidia.com>
Subject: [PATCH RFC 5/6] selftests/hmm: Add file-backed migration tests
Date: Sun, 16 Mar 2025 15:29:28 +1100 [thread overview]
Message-ID: <e4789b78bf932984c321469e5a7d2bee583653b3.1742099301.git-series.apopple@nvidia.com> (raw)
In-Reply-To: <cover.24b48fced909fe1414e83b58aa468d4393dd06de.1742099301.git-series.apopple@nvidia.com>
Add tests of file-backed migration to hmm-tests.
Signed-off-by: Alistair Popple <apopple@nvidia.com>
---
lib/test_hmm.c | 27 ++-
tools/testing/selftests/mm/hmm-tests.c | 252 +++++++++++++++++++++++++-
2 files changed, 277 insertions(+), 2 deletions(-)
diff --git a/lib/test_hmm.c b/lib/test_hmm.c
index 056f2e4..bd8cd29 100644
--- a/lib/test_hmm.c
+++ b/lib/test_hmm.c
@@ -979,6 +979,8 @@ static int dmirror_migrate_to_device(struct dmirror *dmirror,
mmap_read_lock(mm);
for (addr = start; addr < end; addr = next) {
+ int i, retried = 0;
+
vma = vma_lookup(mm, addr);
if (!vma || !(vma->vm_flags & VM_READ)) {
ret = -EINVAL;
@@ -987,7 +989,7 @@ static int dmirror_migrate_to_device(struct dmirror *dmirror,
next = min(end, addr + (ARRAY_SIZE(src_pfns) << PAGE_SHIFT));
if (next > vma->vm_end)
next = vma->vm_end;
-
+retry:
args.vma = vma;
args.src = src_pfns;
args.dst = dst_pfns;
@@ -1004,6 +1006,16 @@ static int dmirror_migrate_to_device(struct dmirror *dmirror,
migrate_vma_pages(&args);
dmirror_migrate_finalize_and_map(&args, dmirror);
migrate_vma_finalize(&args);
+
+ for (i = 0; i < ((next - addr) >> PAGE_SHIFT); i++) {
+ if (!(src_pfns[i] & MIGRATE_PFN_MIGRATE)
+ && migrate_pfn_to_page(src_pfns[i])
+ && retried++ < 3) {
+ wait_on_page_writeback(
+ migrate_pfn_to_page(src_pfns[i]));
+ goto retry;
+ }
+ }
}
mmap_read_unlock(mm);
mmput(mm);
@@ -1404,6 +1416,10 @@ static void dmirror_devmem_free(struct page *page)
if (rpage != page)
__free_page(rpage);
+ /* Page has been freed so reinitialize these fields */
+ ClearPageDirty(page);
+ folio_clear_swapbacked(page_folio(page));
+
mdevice = dmirror_page_to_device(page);
spin_lock(&mdevice->lock);
@@ -1459,9 +1475,18 @@ static vm_fault_t dmirror_devmem_fault(struct vm_fault *vmf)
return 0;
}
+static int dmirror_devmem_pagecache(struct page *page, struct page *newpage)
+{
+ set_page_dirty(newpage);
+ copy_highpage(newpage, BACKING_PAGE(page));
+
+ return 0;
+}
+
static const struct dev_pagemap_ops dmirror_devmem_ops = {
.page_free = dmirror_devmem_free,
.migrate_to_ram = dmirror_devmem_fault,
+ .migrate_to_pagecache = dmirror_devmem_pagecache,
};
static int dmirror_device_init(struct dmirror_device *mdevice, int id)
diff --git a/tools/testing/selftests/mm/hmm-tests.c b/tools/testing/selftests/mm/hmm-tests.c
index 141bf63..4b77edd 100644
--- a/tools/testing/selftests/mm/hmm-tests.c
+++ b/tools/testing/selftests/mm/hmm-tests.c
@@ -999,6 +999,254 @@ TEST_F(hmm, migrate)
}
/*
+ * Migrate file memory to device private memory.
+ */
+TEST_F(hmm, migrate_file)
+{
+ struct hmm_buffer *buffer;
+ unsigned long npages;
+ unsigned long size;
+ unsigned long i;
+ int *ptr;
+ int ret;
+
+ 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 = hmm_create_file(size);
+ buffer->size = size;
+ buffer->mirror = malloc(size);
+ ASSERT_NE(buffer->mirror, NULL);
+
+ buffer->ptr = mmap(NULL, size,
+ PROT_READ | PROT_WRITE,
+ MAP_SHARED,
+ 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] = i;
+
+ /*
+ * TODO: Migration code should try and clean the pages, but it's not
+ * working.
+ */
+ fsync(buffer->fd);
+
+ /* Migrate memory to device. */
+ ret = hmm_migrate_sys_to_dev(self->fd, buffer, npages);
+ ASSERT_EQ(ret, 0);
+ ASSERT_EQ(buffer->cpages, npages);
+
+ /* Check what the device read. */
+ for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i)
+ ASSERT_EQ(ptr[i], i);
+
+ hmm_buffer_free(buffer);
+}
+
+TEST_F(hmm, migrate_file_fault)
+{
+ struct hmm_buffer *buffer;
+ unsigned long npages;
+ unsigned long size;
+ unsigned long i;
+ int *ptr;
+ int ret;
+
+ 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 = hmm_create_file(size);
+ buffer->size = size;
+ buffer->mirror = malloc(size);
+ ASSERT_NE(buffer->mirror, NULL);
+
+ buffer->ptr = mmap(NULL, size,
+ PROT_READ | PROT_WRITE,
+ MAP_SHARED,
+ 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] = i;
+
+ /*
+ * TODO: Migration code should try and clean the pages, but it's not
+ * working.
+ */
+ fsync(buffer->fd);
+
+ /* Migrate memory to device. */
+ ret = hmm_migrate_sys_to_dev(self->fd, buffer, npages);
+ ASSERT_EQ(ret, 0);
+ ASSERT_EQ(buffer->cpages, npages);
+
+ /* Check what the device read. */
+ for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i)
+ ASSERT_EQ(ptr[i], i);
+
+ /* Fault half the pages back to system memory and check them. */
+ for (i = 0, ptr = buffer->ptr; i < size / (2 * sizeof(*ptr)); ++i)
+ ASSERT_EQ(ptr[i], i);
+
+ /* Migrate memory to the device again. */
+ ret = hmm_migrate_sys_to_dev(self->fd, buffer, npages);
+ ASSERT_EQ(ret, 0);
+ ASSERT_EQ(buffer->cpages, npages);
+
+ /* Check what the device read. */
+ for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i)
+ ASSERT_EQ(ptr[i], i);
+
+ hmm_buffer_free(buffer);
+}
+
+TEST_F(hmm, migrate_fault_read_buf)
+{
+ struct hmm_buffer *buffer;
+ unsigned long npages;
+ unsigned long size;
+ unsigned long i;
+ int *ptr;
+ int ret;
+
+ 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 = hmm_create_file(size);
+ buffer->size = size;
+ buffer->mirror = malloc(size);
+ ASSERT_NE(buffer->mirror, NULL);
+
+ buffer->ptr = mmap(NULL, size,
+ PROT_READ | PROT_WRITE,
+ MAP_SHARED,
+ 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] = i;
+
+ /*
+ * TODO: Migration code should try and clean the pages, but it's not
+ * working.
+ */
+ fsync(buffer->fd);
+
+ /* Migrate memory to device. */
+ ret = hmm_migrate_sys_to_dev(self->fd, buffer, npages);
+ ASSERT_EQ(ret, 0);
+ ASSERT_EQ(buffer->cpages, npages);
+
+ /* Check what the device read. */
+ for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i)
+ ASSERT_EQ(ptr[i], i);
+
+ /* Use read and check what we read */
+ read(buffer->fd, buffer->mirror, size);
+ for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i)
+ ASSERT_EQ(ptr[i], i);
+
+ hmm_buffer_free(buffer);
+}
+
+TEST_F(hmm, migrate_fault_write_buf)
+{
+ struct hmm_buffer *buffer;
+ unsigned long npages;
+ unsigned long size;
+ unsigned long i;
+ int *ptr;
+ int ret;
+
+ 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 = hmm_create_file(size);
+ buffer->size = size;
+ buffer->mirror = malloc(size);
+ ASSERT_NE(buffer->mirror, NULL);
+
+ buffer->ptr = mmap(NULL, size,
+ PROT_READ | PROT_WRITE,
+ MAP_SHARED,
+ 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] = i;
+
+ /*
+ * TODO: Migration code should try and clean the pages, but it's not
+ * working.
+ */
+ fsync(buffer->fd);
+
+ /* Migrate memory to device. */
+ ret = hmm_migrate_sys_to_dev(self->fd, buffer, npages);
+ ASSERT_EQ(ret, 0);
+ ASSERT_EQ(buffer->cpages, npages);
+
+ /* Check what the device read and update to write to the device. */
+ for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i)
+ ASSERT_EQ(ptr[i]++, i);
+
+ /* Write to the buffer from the device */
+ ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_WRITE, buffer, npages);
+ ASSERT_EQ(ret, 0);
+
+ /* Truncate half the file */
+ size >>= 1;
+ ret = truncate("hmm-test-file", size);
+ ASSERT_EQ(ret, 0);
+
+ /* Use read and check what we read */
+ ret = read(buffer->fd, buffer->mirror, size);
+ ASSERT_EQ(ret, size);
+ for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i)
+ ASSERT_EQ(ptr[i], i + 1);
+
+ /* Should see the same in the mmap */
+ for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
+ ASSERT_EQ(ptr[i], i + 1);
+
+ /* And check we get zeros in the second half */
+ size <<= 1;
+ ret = truncate("hmm-test-file", size);
+ ASSERT_EQ(ret, 0);
+
+ for (i = 0, ptr = buffer->ptr; i < size / (2*sizeof(*ptr)); ++i)
+ ASSERT_EQ(ptr[i], i + 1);
+
+ for (i = size/(2*sizeof(*ptr)), ptr = buffer->ptr;
+ i < size / sizeof(*ptr); ++i)
+ ASSERT_EQ(ptr[i], 0);
+
+ hmm_buffer_free(buffer);
+}
+
+/*
* Migrate anonymous memory to device private memory and fault some of it back
* to system memory, then try migrating the resulting mix of system and device
* private memory to the device.
@@ -1040,8 +1288,10 @@ TEST_F(hmm, migrate_fault)
ASSERT_EQ(buffer->cpages, npages);
/* Check what the device read. */
- for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i)
+ for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i) {
ASSERT_EQ(ptr[i], i);
+ ptr[i]++;
+ }
/* Fault half the pages back to system memory and check them. */
for (i = 0, ptr = buffer->ptr; i < size / (2 * sizeof(*ptr)); ++i)
--
git-series 0.9.1
next prev parent reply other threads:[~2025-03-16 4:30 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-03-16 4:29 [PATCH RFC 0/6] Allow file-backed or shared device private pages Alistair Popple
2025-03-16 4:29 ` [PATCH RFC 1/6] mm/migrate_device.c: Don't read dirty bit of non-present PTEs Alistair Popple
2025-03-16 4:29 ` [PATCH RFC 2/6] mm/migrate: Support file-backed pages with migrate_vma Alistair Popple
2025-03-16 4:29 ` [PATCH RFC 3/6] mm: Allow device private pages to exist in page cache Alistair Popple
2025-03-16 4:29 ` [PATCH RFC 4/6] mm: Implement writeback for share device private pages Alistair Popple
2025-03-16 4:29 ` Alistair Popple [this message]
2025-03-16 4:29 ` [PATCH RFC 6/6] nouveau: Add SVM support for migrating file-backed pages to the GPU Alistair Popple
2025-03-17 6:04 ` [PATCH RFC 0/6] Allow file-backed or shared device private pages Christoph Hellwig
2025-03-26 2:14 ` Matthew Wilcox
2025-03-27 14:49 ` Alistair Popple
2025-03-27 16:47 ` Matthew Wilcox
2025-04-07 9:15 ` Christoph Hellwig
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=e4789b78bf932984c321469e5a7d2bee583653b3.1742099301.git-series.apopple@nvidia.com \
--to=apopple@nvidia.com \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mm@kvack.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox