diff --git a/drivers/dax/dax-private.h b/drivers/dax/dax-private.h index 27cf2daaaa795ad9b8236b985d01aa0e6cea5c40..6f2c48335d0da491b4fd1006d167a52157eef464 100644 --- a/drivers/dax/dax-private.h +++ b/drivers/dax/dax-private.h @@ -114,4 +114,19 @@ static inline bool dax_align_valid(unsigned long align) return align == PAGE_SIZE; } #endif /* CONFIG_TRANSPARENT_HUGEPAGE */ + +#ifndef copy_mc_to_kernel +static inline int dax_test_page_mc(const struct page *page) +{ + return 0; +} +#else +#include +static inline int dax_test_page_mc(const struct page *page) +{ + struct page _p; + + return copy_mc_to_kernel(&_p, page, sizeof(struct page)); +} +#endif #endif diff --git a/drivers/dax/device.c b/drivers/dax/device.c index cfb122b3fee3e1ce9ccc904b1b93cd55eacbcfbd..1e5409ea40fb951c1756c4ee4a4ccdfb5f262d11 100644 --- a/drivers/dax/device.c +++ b/drivers/dax/device.c @@ -73,7 +73,7 @@ __weak phys_addr_t dax_pgoff_to_phys(struct dev_dax *dev_dax, pgoff_t pgoff, return -1; } -static void dax_set_mapping(struct vm_fault *vmf, pfn_t pfn, +static int dax_set_mapping(struct vm_fault *vmf, pfn_t pfn, unsigned long fault_size) { unsigned long i, nr_pages = fault_size / PAGE_SIZE; @@ -90,14 +90,23 @@ static void dax_set_mapping(struct vm_fault *vmf, pfn_t pfn, for (i = 0; i < nr_pages; i++) { struct page *page = pfn_to_page(pfn_t_to_pfn(pfn) + i); + struct page *head = NULL; - page = compound_head(page); - if (page->mapping) + if (dax_test_page_mc(page)) + return -EFAULT; + + head = compound_head(page); + if (page != head && dax_test_page_mc(head)) + return -EFAULT; + + if (head->mapping) continue; - page->mapping = filp->f_mapping; - page->index = pgoff + i; + head->mapping = filp->f_mapping; + head->index = pgoff + i; } + + return 0; } static vm_fault_t __dev_dax_pte_fault(struct dev_dax *dev_dax, @@ -128,7 +137,8 @@ static vm_fault_t __dev_dax_pte_fault(struct dev_dax *dev_dax, pfn = phys_to_pfn_t(phys, PFN_DEV|PFN_MAP); - dax_set_mapping(vmf, pfn, fault_size); + if (dax_set_mapping(vmf, pfn, fault_size)) + return VM_FAULT_SIGBUS; return vmf_insert_mixed(vmf->vma, vmf->address, pfn); } @@ -171,7 +181,8 @@ static vm_fault_t __dev_dax_pmd_fault(struct dev_dax *dev_dax, pfn = phys_to_pfn_t(phys, PFN_DEV|PFN_MAP); - dax_set_mapping(vmf, pfn, fault_size); + if (dax_set_mapping(vmf, pfn, fault_size)) + return VM_FAULT_SIGBUS; return vmf_insert_pfn_pmd(vmf, pfn, vmf->flags & FAULT_FLAG_WRITE); } @@ -216,7 +227,8 @@ static vm_fault_t __dev_dax_pud_fault(struct dev_dax *dev_dax, pfn = phys_to_pfn_t(phys, PFN_DEV|PFN_MAP); - dax_set_mapping(vmf, pfn, fault_size); + if (dax_set_mapping(vmf, pfn, fault_size)) + return VM_FAULT_SIGBUS; return vmf_insert_pfn_pud(vmf, pfn, vmf->flags & FAULT_FLAG_WRITE); }