linux-mm.kvack.org archive mirror
 help / color / mirror / Atom feed
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


  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