device-dax: fix pmd/pte fault fallback handling
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: dee4107924
("/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>
This commit is contained in:
parent
c1ae3cfa0e
commit
0134ed4fb9
@ -427,6 +427,7 @@ static int __dax_dev_pte_fault(struct dax_dev *dax_dev, struct vm_fault *vmf)
|
|||||||
int rc = VM_FAULT_SIGBUS;
|
int rc = VM_FAULT_SIGBUS;
|
||||||
phys_addr_t phys;
|
phys_addr_t phys;
|
||||||
pfn_t pfn;
|
pfn_t pfn;
|
||||||
|
unsigned int fault_size = PAGE_SIZE;
|
||||||
|
|
||||||
if (check_vma(dax_dev, vmf->vma, __func__))
|
if (check_vma(dax_dev, vmf->vma, __func__))
|
||||||
return VM_FAULT_SIGBUS;
|
return VM_FAULT_SIGBUS;
|
||||||
@ -437,6 +438,9 @@ static int __dax_dev_pte_fault(struct dax_dev *dax_dev, struct vm_fault *vmf)
|
|||||||
return VM_FAULT_SIGBUS;
|
return VM_FAULT_SIGBUS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (fault_size != dax_region->align)
|
||||||
|
return VM_FAULT_SIGBUS;
|
||||||
|
|
||||||
phys = pgoff_to_phys(dax_dev, vmf->pgoff, PAGE_SIZE);
|
phys = pgoff_to_phys(dax_dev, vmf->pgoff, PAGE_SIZE);
|
||||||
if (phys == -1) {
|
if (phys == -1) {
|
||||||
dev_dbg(dev, "%s: phys_to_pgoff(%#lx) failed\n", __func__,
|
dev_dbg(dev, "%s: phys_to_pgoff(%#lx) failed\n", __func__,
|
||||||
@ -464,6 +468,7 @@ static int __dax_dev_pmd_fault(struct dax_dev *dax_dev, struct vm_fault *vmf)
|
|||||||
phys_addr_t phys;
|
phys_addr_t phys;
|
||||||
pgoff_t pgoff;
|
pgoff_t pgoff;
|
||||||
pfn_t pfn;
|
pfn_t pfn;
|
||||||
|
unsigned int fault_size = PMD_SIZE;
|
||||||
|
|
||||||
if (check_vma(dax_dev, vmf->vma, __func__))
|
if (check_vma(dax_dev, vmf->vma, __func__))
|
||||||
return VM_FAULT_SIGBUS;
|
return VM_FAULT_SIGBUS;
|
||||||
@ -480,6 +485,16 @@ static int __dax_dev_pmd_fault(struct dax_dev *dax_dev, struct vm_fault *vmf)
|
|||||||
return VM_FAULT_SIGBUS;
|
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 < vmf->vma->vm_start ||
|
||||||
|
(pmd_addr + PMD_SIZE) > vmf->vma->vm_end)
|
||||||
|
return VM_FAULT_SIGBUS;
|
||||||
|
|
||||||
pgoff = linear_page_index(vmf->vma, pmd_addr);
|
pgoff = linear_page_index(vmf->vma, pmd_addr);
|
||||||
phys = pgoff_to_phys(dax_dev, pgoff, PMD_SIZE);
|
phys = pgoff_to_phys(dax_dev, pgoff, PMD_SIZE);
|
||||||
if (phys == -1) {
|
if (phys == -1) {
|
||||||
|
Loading…
Reference in New Issue
Block a user