Subject: [PATCH v2 0/3] split vm_normal_pages for LRU and non-LRU handling

With DEVICE_COHERENT, we'll soon have vm_normal_pages() return
device-managed anonymous pages that are not LRU pages. Although they
behave like normal pages for purposes of mapping in CPU page, and for
COW. They do not support LRU lists, NUMA migration or THP. The
difference between new vm_normal_lru_pages vs vm_normal_pages() is,
the former makes sure to return pages that are LRU handled only.

We also introduced a FOLL_LRU flag that adds the same behaviour to
follow_page and related APIs, to allow callers to specify that they
expect to put pages on an LRU list.

HMM tests were added to selftest to excercise these changes with
device coherent pages. New test called hmm_cow_in_device, will test
pages marked as COW, allocated in device zone. Also, more
configurations were added into hmm_gup_test to test basic get
user pages and get user pages fast paths in device zone pages.

v2:
- Changed the general description for this cover letter.
- Changed commit message for patch 1/3.
- Keep vm_normal_pages and add vm_normal_lru_pages, instead of rename
both.
- Add proper kernel-doc format to new function and minimize code
churn.

TODO: vm_normal_pages with pte_devmap entries still return NULL,
instead of return the actual device page. The reason is
page->_mapcount is never incremented for device pages that are mmap
through DAX mechanism using pmem driver mounted into ext4 filesystem.
When these pages are unmap, zap_pte_range is called and
vm_normal_page return a valid page with page_mapcount() = 0, before
page_remove_rmap is called.

Alex Sierra (3):
mm: add vm_normal_lru_pages for LRU handled pages only
tools: add more gup configs to hmm_gup selftests
tools: add selftests to hmm for COW in device memory

fs/proc/task_mmu.c | 2 +-
include/linux/mm.h | 9 +-
mm/gup.c | 8 +-
mm/huge_memory.c | 2 +-
mm/khugepaged.c | 8 +-
mm/ksm.c | 4 +-
mm/madvise.c | 4 +-
mm/memory.c | 40 ++++++-
mm/mempolicy.c | 4 +-
mm/migrate.c | 2 +-
mm/mlock.c | 6 +-
mm/mprotect.c | 2 +-
tools/testing/selftests/vm/hmm-tests.c | 139 +++++++++++++++++++++----
13 files changed, 187 insertions(+), 43 deletions(-)

--
2.32.0


Subject: [PATCH v2 2/3] tools: add more gup configs to hmm_gup selftests

Test device pages with get_user_pages and get_user_pages_fast.
The motivation is to test device coherent type pages in the gup and
gup fast paths, after vm_normal_pages was split into LRU and non-LRU
handled.

Signed-off-by: Alex Sierra <[email protected]>
Acked-by: Felix Kuehling <[email protected]>
---
tools/testing/selftests/vm/hmm-tests.c | 65 +++++++++++++++++---------
1 file changed, 44 insertions(+), 21 deletions(-)

diff --git a/tools/testing/selftests/vm/hmm-tests.c b/tools/testing/selftests/vm/hmm-tests.c
index 11b83a8084fe..65e30ab6494c 100644
--- a/tools/testing/selftests/vm/hmm-tests.c
+++ b/tools/testing/selftests/vm/hmm-tests.c
@@ -1769,6 +1769,24 @@ TEST_F(hmm, exclusive_cow)
hmm_buffer_free(buffer);
}

+static int gup_test_exec(int gup_fd, unsigned long addr,
+ int cmd, int npages, int size)
+{
+ struct gup_test gup = {
+ .nr_pages_per_call = npages,
+ .addr = addr,
+ .gup_flags = FOLL_WRITE,
+ .size = size,
+ };
+
+ if (ioctl(gup_fd, cmd, &gup)) {
+ perror("ioctl on error\n");
+ return errno;
+ }
+
+ return 0;
+}
+
/*
* Test get user device pages through gup_test. Setting PIN_LONGTERM flag.
* This should trigger a migration back to system memory for both, private
@@ -1779,7 +1797,6 @@ TEST_F(hmm, exclusive_cow)
TEST_F(hmm, hmm_gup_test)
{
struct hmm_buffer *buffer;
- struct gup_test gup;
int gup_fd;
unsigned long npages;
unsigned long size;
@@ -1792,8 +1809,7 @@ TEST_F(hmm, hmm_gup_test)
if (gup_fd == -1)
SKIP(return, "Skipping test, could not find gup_test driver");

- npages = 4;
- ASSERT_NE(npages, 0);
+ npages = 3;
size = npages << self->page_shift;

buffer = malloc(sizeof(*buffer));
@@ -1822,28 +1838,35 @@ TEST_F(hmm, hmm_gup_test)
for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i)
ASSERT_EQ(ptr[i], i);

- gup.nr_pages_per_call = npages;
- gup.addr = (unsigned long)buffer->ptr;
- gup.gup_flags = FOLL_WRITE;
- gup.size = size;
- /*
- * Calling gup_test ioctl. It will try to PIN_LONGTERM these device pages
- * causing a migration back to system memory for both, private and coherent
- * type pages.
- */
- if (ioctl(gup_fd, PIN_LONGTERM_BENCHMARK, &gup)) {
- perror("ioctl on PIN_LONGTERM_BENCHMARK\n");
- goto out_test;
- }
-
- /* Take snapshot to make sure pages have been migrated to sys memory */
+ ASSERT_EQ(gup_test_exec(gup_fd,
+ (unsigned long)buffer->ptr,
+ GUP_BASIC_TEST, 1, self->page_size), 0);
+ ASSERT_EQ(gup_test_exec(gup_fd,
+ (unsigned long)buffer->ptr + 1 * self->page_size,
+ GUP_FAST_BENCHMARK, 1, self->page_size), 0);
+ ASSERT_EQ(gup_test_exec(gup_fd,
+ (unsigned long)buffer->ptr + 2 * self->page_size,
+ PIN_LONGTERM_BENCHMARK, 1, self->page_size), 0);
+
+ /* Take snapshot to CPU pagetables */
ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_SNAPSHOT, buffer, npages);
ASSERT_EQ(ret, 0);
ASSERT_EQ(buffer->cpages, npages);
m = buffer->mirror;
- for (i = 0; i < npages; i++)
- ASSERT_EQ(m[i], HMM_DMIRROR_PROT_WRITE);
-out_test:
+ if (hmm_is_coherent_type(variant->device_number)) {
+ ASSERT_EQ(HMM_DMIRROR_PROT_DEV_COHERENT_LOCAL | HMM_DMIRROR_PROT_WRITE, m[0]);
+ ASSERT_EQ(HMM_DMIRROR_PROT_DEV_COHERENT_LOCAL | HMM_DMIRROR_PROT_WRITE, m[1]);
+ } else {
+ ASSERT_EQ(HMM_DMIRROR_PROT_WRITE, m[0]);
+ ASSERT_EQ(HMM_DMIRROR_PROT_WRITE, m[1]);
+ }
+ ASSERT_EQ(HMM_DMIRROR_PROT_WRITE, m[2]);
+ /* Check again the content on the pages. Make sure there's no
+ * corrupted data.
+ */
+ for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
+ ASSERT_EQ(ptr[i], i);
+
close(gup_fd);
hmm_buffer_free(buffer);
}
--
2.32.0