]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/commitdiff
device-dax: fix pmd/pte fault fallback handling
authorDave Jiang <dave.jiang@intel.com>
Fri, 10 Mar 2017 20:24:22 +0000 (13:24 -0700)
committerTim Gardner <tim.gardner@canonical.com>
Thu, 30 Mar 2017 12:35:45 +0000 (06:35 -0600)
BugLink: http://bugs.launchpad.net/bugs/1677589
commit 0134ed4fb9e78672ee9f7b18007114404c81e63f upstream.

Jeff Moyer reports:

    With a device dax alignment of 4KB or 2MB, I get sigbus when running
    the attached fio job file for the current kernel (4.11.0-rc1+).  If
    I specify an alignment of 1GB, it works.

    I turned on debug output, and saw that it was failing in the huge
    fault code.

     dax dax1.0: dax_open
     dax dax1.0: dax_mmap
     dax dax1.0: dax_dev_huge_fault: fio: write (0x7f08f0a00000 -
     dax dax1.0: __dax_dev_pud_fault: phys_to_pgoff(0xffffffffcf60
     dax dax1.0: dax_release

    fio config for reproduce:
    [global]
    ioengine=dev-dax
    direct=0
    filename=/dev/dax0.0
    bs=2m

    [write]
    rw=write

    [read]
    stonewall
    rw=read

The driver fails to fallback when taking a fault that is larger than
the device alignment, or handling a larger fault when a smaller
mapping is already established. While we could support larger
mappings for a device with a smaller alignment, that change is
too large for the immediate fix. The simplest change is to force
fallback until the fault size matches the alignment.

Fixes: dee410792419 ("/dev/dax, core: file operations and dax-mmap")
Cc: <stable@vger.kernel.org>
Reported-by: Jeff Moyer <jmoyer@redhat.com>
Signed-off-by: Dave Jiang <dave.jiang@intel.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Tim Gardner <tim.gardner@canonical.com>
drivers/dax/dax.c

index ed758b74ddf0b74fe3fdbc84f8dde771aead4aca..20ab6bf9d1c71bec5a499c5518c676c55af4bc5b 100644 (file)
@@ -427,6 +427,7 @@ static int __dax_dev_fault(struct dax_dev *dax_dev, struct vm_area_struct *vma,
        int rc = VM_FAULT_SIGBUS;
        phys_addr_t phys;
        pfn_t pfn;
+       unsigned int fault_size = PAGE_SIZE;
 
        if (check_vma(dax_dev, vma, __func__))
                return VM_FAULT_SIGBUS;
@@ -437,6 +438,9 @@ static int __dax_dev_fault(struct dax_dev *dax_dev, struct vm_area_struct *vma,
                return VM_FAULT_SIGBUS;
        }
 
+       if (fault_size != dax_region->align)
+               return VM_FAULT_SIGBUS;
+
        phys = pgoff_to_phys(dax_dev, vmf->pgoff, PAGE_SIZE);
        if (phys == -1) {
                dev_dbg(dev, "%s: phys_to_pgoff(%#lx) failed\n", __func__,
@@ -482,6 +486,7 @@ static int __dax_dev_pmd_fault(struct dax_dev *dax_dev,
        phys_addr_t phys;
        pgoff_t pgoff;
        pfn_t pfn;
+       unsigned int fault_size = PMD_SIZE;
 
        if (check_vma(dax_dev, vma, __func__))
                return VM_FAULT_SIGBUS;
@@ -498,6 +503,16 @@ static int __dax_dev_pmd_fault(struct dax_dev *dax_dev,
                return VM_FAULT_SIGBUS;
        }
 
+       if (fault_size < dax_region->align)
+               return VM_FAULT_SIGBUS;
+       else if (fault_size > dax_region->align)
+               return VM_FAULT_FALLBACK;
+
+       /* if we are outside of the VMA */
+       if (pmd_addr < vma->vm_start ||
+                       (pmd_addr + PMD_SIZE) > vma->vm_end)
+               return VM_FAULT_SIGBUS;
+
        pgoff = linear_page_index(vma, pmd_addr);
        phys = pgoff_to_phys(dax_dev, pgoff, PMD_SIZE);
        if (phys == -1) {