]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/commitdiff
mm/hmm.c: allow VM_MIXEDMAP to work with hmm_range_fault
authorAlistair Popple <apopple@nvidia.com>
Fri, 14 Jan 2022 22:09:31 +0000 (14:09 -0800)
committerPaolo Pisati <paolo.pisati@canonical.com>
Fri, 28 Jan 2022 10:03:43 +0000 (11:03 +0100)
BugLink: https://bugs.launchpad.net/bugs/1959376
commit 87c01d57fa23de82fff593a7d070933d08755801 upstream.

hmm_range_fault() can be used instead of get_user_pages() for devices
which allow faulting however unlike get_user_pages() it will return an
error when used on a VM_MIXEDMAP range.

To make hmm_range_fault() more closely match get_user_pages() remove
this restriction.  This requires dealing with the !ARCH_HAS_PTE_SPECIAL
case in hmm_vma_handle_pte().  Rather than replicating the logic of
vm_normal_page() call it directly and do a check for the zero pfn
similar to what get_user_pages() currently does.

Also add a test to hmm selftest to verify functionality.

Link: https://lkml.kernel.org/r/20211104012001.2555676-1-apopple@nvidia.com
Fixes: da4c3c735ea4 ("mm/hmm/mirror: helper to snapshot CPU page table")
Signed-off-by: Alistair Popple <apopple@nvidia.com>
Reviewed-by: Jason Gunthorpe <jgg@nvidia.com>
Cc: Jerome Glisse <jglisse@redhat.com>
Cc: John Hubbard <jhubbard@nvidia.com>
Cc: Zi Yan <ziy@nvidia.com>
Cc: Ralph Campbell <rcampbell@nvidia.com>
Cc: Felix Kuehling <Felix.Kuehling@amd.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Paolo Pisati <paolo.pisati@canonical.com>
lib/test_hmm.c
mm/hmm.c
tools/testing/selftests/vm/hmm-tests.c

index c259842f6d443c0318eb931e72d585b034e33301..ac794e3540693185fd9dcb040a55926e79635222 100644 (file)
@@ -1087,9 +1087,33 @@ static long dmirror_fops_unlocked_ioctl(struct file *filp,
        return 0;
 }
 
+static int dmirror_fops_mmap(struct file *file, struct vm_area_struct *vma)
+{
+       unsigned long addr;
+
+       for (addr = vma->vm_start; addr < vma->vm_end; addr += PAGE_SIZE) {
+               struct page *page;
+               int ret;
+
+               page = alloc_page(GFP_KERNEL | __GFP_ZERO);
+               if (!page)
+                       return -ENOMEM;
+
+               ret = vm_insert_page(vma, addr, page);
+               if (ret) {
+                       __free_page(page);
+                       return ret;
+               }
+               put_page(page);
+       }
+
+       return 0;
+}
+
 static const struct file_operations dmirror_fops = {
        .open           = dmirror_fops_open,
        .release        = dmirror_fops_release,
+       .mmap           = dmirror_fops_mmap,
        .unlocked_ioctl = dmirror_fops_unlocked_ioctl,
        .llseek         = default_llseek,
        .owner          = THIS_MODULE,
index 842e265992380c801d9f83d8749eca50251aa140..bd56641c79d4eea34a67635b181c410959d3b16e 100644 (file)
--- a/mm/hmm.c
+++ b/mm/hmm.c
@@ -300,7 +300,8 @@ static int hmm_vma_handle_pte(struct mm_walk *walk, unsigned long addr,
         * Since each architecture defines a struct page for the zero page, just
         * fall through and treat it like a normal page.
         */
-       if (pte_special(pte) && !pte_devmap(pte) &&
+       if (!vm_normal_page(walk->vma, addr, pte) &&
+           !pte_devmap(pte) &&
            !is_zero_pfn(pte_pfn(pte))) {
                if (hmm_pte_need_fault(hmm_vma_walk, pfn_req_flags, 0)) {
                        pte_unmap(ptep);
@@ -518,7 +519,7 @@ static int hmm_vma_walk_test(unsigned long start, unsigned long end,
        struct hmm_range *range = hmm_vma_walk->range;
        struct vm_area_struct *vma = walk->vma;
 
-       if (!(vma->vm_flags & (VM_IO | VM_PFNMAP | VM_MIXEDMAP)) &&
+       if (!(vma->vm_flags & (VM_IO | VM_PFNMAP)) &&
            vma->vm_flags & VM_READ)
                return 0;
 
index 864f126ffd78feae499126d3c0b3adcc5c7a4e6c..203323967b507ab724186aeb4cfba11708a0240d 100644 (file)
@@ -1248,6 +1248,48 @@ TEST_F(hmm, anon_teardown)
        }
 }
 
+/*
+ * Test memory snapshot without faulting in pages accessed by the device.
+ */
+TEST_F(hmm, mixedmap)
+{
+       struct hmm_buffer *buffer;
+       unsigned long npages;
+       unsigned long size;
+       unsigned char *m;
+       int ret;
+
+       npages = 1;
+       size = npages << self->page_shift;
+
+       buffer = malloc(sizeof(*buffer));
+       ASSERT_NE(buffer, NULL);
+
+       buffer->fd = -1;
+       buffer->size = size;
+       buffer->mirror = malloc(npages);
+       ASSERT_NE(buffer->mirror, NULL);
+
+
+       /* Reserve a range of addresses. */
+       buffer->ptr = mmap(NULL, size,
+                          PROT_READ | PROT_WRITE,
+                          MAP_PRIVATE,
+                          self->fd, 0);
+       ASSERT_NE(buffer->ptr, MAP_FAILED);
+
+       /* Simulate a device snapshotting CPU pagetables. */
+       ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_SNAPSHOT, buffer, npages);
+       ASSERT_EQ(ret, 0);
+       ASSERT_EQ(buffer->cpages, npages);
+
+       /* Check what the device saw. */
+       m = buffer->mirror;
+       ASSERT_EQ(m[0], HMM_DMIRROR_PROT_READ);
+
+       hmm_buffer_free(buffer);
+}
+
 /*
  * Test memory snapshot without faulting in pages accessed by the device.
  */