NFSv4: Recovery of recalled read delegations is broken
[deliverable/linux.git] / fs / dax.c
index 57bb70b4af70f75f422dff8beff13ce7545e2b61..93bf2f990ace462b31dba2ee239084e112b1dfd0 100644 (file)
--- a/fs/dax.c
+++ b/fs/dax.c
@@ -17,6 +17,7 @@
 #include <linux/atomic.h>
 #include <linux/blkdev.h>
 #include <linux/buffer_head.h>
+#include <linux/dax.h>
 #include <linux/fs.h>
 #include <linux/genhd.h>
 #include <linux/highmem.h>
@@ -283,7 +284,6 @@ static int copy_user_bh(struct page *to, struct buffer_head *bh,
 static int dax_insert_mapping(struct inode *inode, struct buffer_head *bh,
                        struct vm_area_struct *vma, struct vm_fault *vmf)
 {
-       struct address_space *mapping = inode->i_mapping;
        sector_t sector = bh->b_blocknr << (inode->i_blkbits - 9);
        unsigned long vaddr = (unsigned long)vmf->virtual_address;
        void __pmem *addr;
@@ -291,8 +291,6 @@ static int dax_insert_mapping(struct inode *inode, struct buffer_head *bh,
        pgoff_t size;
        int error;
 
-       i_mmap_lock_read(mapping);
-
        /*
         * Check truncate didn't happen while we were allocating a block.
         * If it did, this block may or may not be still allocated to the
@@ -322,8 +320,6 @@ static int dax_insert_mapping(struct inode *inode, struct buffer_head *bh,
        error = vm_insert_mixed(vma, vaddr, pfn);
 
  out:
-       i_mmap_unlock_read(mapping);
-
        return error;
 }
 
@@ -385,15 +381,17 @@ int __dax_fault(struct vm_area_struct *vma, struct vm_fault *vmf,
                         * from a read fault and we've raced with a truncate
                         */
                        error = -EIO;
-                       goto unlock_page;
+                       goto unlock;
                }
+       } else {
+               i_mmap_lock_write(mapping);
        }
 
        error = get_block(inode, block, &bh, 0);
        if (!error && (bh.b_size < PAGE_SIZE))
                error = -EIO;           /* fs corruption? */
        if (error)
-               goto unlock_page;
+               goto unlock;
 
        if (!buffer_mapped(&bh) && !buffer_unwritten(&bh) && !vmf->cow_page) {
                if (vmf->flags & FAULT_FLAG_WRITE) {
@@ -404,8 +402,9 @@ int __dax_fault(struct vm_area_struct *vma, struct vm_fault *vmf,
                        if (!error && (bh.b_size < PAGE_SIZE))
                                error = -EIO;
                        if (error)
-                               goto unlock_page;
+                               goto unlock;
                } else {
+                       i_mmap_unlock_write(mapping);
                        return dax_load_hole(mapping, page, vmf);
                }
        }
@@ -417,17 +416,15 @@ int __dax_fault(struct vm_area_struct *vma, struct vm_fault *vmf,
                else
                        clear_user_highpage(new_page, vaddr);
                if (error)
-                       goto unlock_page;
+                       goto unlock;
                vmf->page = page;
                if (!page) {
-                       i_mmap_lock_read(mapping);
                        /* Check we didn't race with truncate */
                        size = (i_size_read(inode) + PAGE_SIZE - 1) >>
                                                                PAGE_SHIFT;
                        if (vmf->pgoff >= size) {
-                               i_mmap_unlock_read(mapping);
                                error = -EIO;
-                               goto out;
+                               goto unlock;
                        }
                }
                return VM_FAULT_LOCKED;
@@ -463,6 +460,8 @@ int __dax_fault(struct vm_area_struct *vma, struct vm_fault *vmf,
                        WARN_ON_ONCE(!(vmf->flags & FAULT_FLAG_WRITE));
        }
 
+       if (!page)
+               i_mmap_unlock_write(mapping);
  out:
        if (error == -ENOMEM)
                return VM_FAULT_OOM | major;
@@ -471,11 +470,14 @@ int __dax_fault(struct vm_area_struct *vma, struct vm_fault *vmf,
                return VM_FAULT_SIGBUS | major;
        return VM_FAULT_NOPAGE | major;
 
- unlock_page:
+ unlock:
        if (page) {
                unlock_page(page);
                page_cache_release(page);
+       } else {
+               i_mmap_unlock_write(mapping);
        }
+
        goto out;
 }
 EXPORT_SYMBOL(__dax_fault);
@@ -507,6 +509,177 @@ int dax_fault(struct vm_area_struct *vma, struct vm_fault *vmf,
 }
 EXPORT_SYMBOL_GPL(dax_fault);
 
+#ifdef CONFIG_TRANSPARENT_HUGEPAGE
+/*
+ * The 'colour' (ie low bits) within a PMD of a page offset.  This comes up
+ * more often than one might expect in the below function.
+ */
+#define PG_PMD_COLOUR  ((PMD_SIZE >> PAGE_SHIFT) - 1)
+
+int __dax_pmd_fault(struct vm_area_struct *vma, unsigned long address,
+               pmd_t *pmd, unsigned int flags, get_block_t get_block,
+               dax_iodone_t complete_unwritten)
+{
+       struct file *file = vma->vm_file;
+       struct address_space *mapping = file->f_mapping;
+       struct inode *inode = mapping->host;
+       struct buffer_head bh;
+       unsigned blkbits = inode->i_blkbits;
+       unsigned long pmd_addr = address & PMD_MASK;
+       bool write = flags & FAULT_FLAG_WRITE;
+       long length;
+       void __pmem *kaddr;
+       pgoff_t size, pgoff;
+       sector_t block, sector;
+       unsigned long pfn;
+       int result = 0;
+
+       /* Fall back to PTEs if we're going to COW */
+       if (write && !(vma->vm_flags & VM_SHARED))
+               return VM_FAULT_FALLBACK;
+       /* If the PMD would extend outside the VMA */
+       if (pmd_addr < vma->vm_start)
+               return VM_FAULT_FALLBACK;
+       if ((pmd_addr + PMD_SIZE) > vma->vm_end)
+               return VM_FAULT_FALLBACK;
+
+       pgoff = linear_page_index(vma, pmd_addr);
+       size = (i_size_read(inode) + PAGE_SIZE - 1) >> PAGE_SHIFT;
+       if (pgoff >= size)
+               return VM_FAULT_SIGBUS;
+       /* If the PMD would cover blocks out of the file */
+       if ((pgoff | PG_PMD_COLOUR) >= size)
+               return VM_FAULT_FALLBACK;
+
+       memset(&bh, 0, sizeof(bh));
+       block = (sector_t)pgoff << (PAGE_SHIFT - blkbits);
+
+       bh.b_size = PMD_SIZE;
+       i_mmap_lock_write(mapping);
+       length = get_block(inode, block, &bh, write);
+       if (length)
+               return VM_FAULT_SIGBUS;
+
+       /*
+        * If the filesystem isn't willing to tell us the length of a hole,
+        * just fall back to PTEs.  Calling get_block 512 times in a loop
+        * would be silly.
+        */
+       if (!buffer_size_valid(&bh) || bh.b_size < PMD_SIZE)
+               goto fallback;
+
+       if (buffer_unwritten(&bh) || buffer_new(&bh)) {
+               int i;
+               for (i = 0; i < PTRS_PER_PMD; i++)
+                       clear_pmem(kaddr + i * PAGE_SIZE, PAGE_SIZE);
+               wmb_pmem();
+               count_vm_event(PGMAJFAULT);
+               mem_cgroup_count_vm_event(vma->vm_mm, PGMAJFAULT);
+               result |= VM_FAULT_MAJOR;
+       }
+
+       /*
+        * If we allocated new storage, make sure no process has any
+        * zero pages covering this hole
+        */
+       if (buffer_new(&bh)) {
+               i_mmap_unlock_write(mapping);
+               unmap_mapping_range(mapping, pgoff << PAGE_SHIFT, PMD_SIZE, 0);
+               i_mmap_lock_write(mapping);
+       }
+
+       /*
+        * If a truncate happened while we were allocating blocks, we may
+        * leave blocks allocated to the file that are beyond EOF.  We can't
+        * take i_mutex here, so just leave them hanging; they'll be freed
+        * when the file is deleted.
+        */
+       size = (i_size_read(inode) + PAGE_SIZE - 1) >> PAGE_SHIFT;
+       if (pgoff >= size) {
+               result = VM_FAULT_SIGBUS;
+               goto out;
+       }
+       if ((pgoff | PG_PMD_COLOUR) >= size)
+               goto fallback;
+
+       if (!write && !buffer_mapped(&bh) && buffer_uptodate(&bh)) {
+               spinlock_t *ptl;
+               pmd_t entry;
+               struct page *zero_page = get_huge_zero_page();
+
+               if (unlikely(!zero_page))
+                       goto fallback;
+
+               ptl = pmd_lock(vma->vm_mm, pmd);
+               if (!pmd_none(*pmd)) {
+                       spin_unlock(ptl);
+                       goto fallback;
+               }
+
+               entry = mk_pmd(zero_page, vma->vm_page_prot);
+               entry = pmd_mkhuge(entry);
+               set_pmd_at(vma->vm_mm, pmd_addr, pmd, entry);
+               result = VM_FAULT_NOPAGE;
+               spin_unlock(ptl);
+       } else {
+               sector = bh.b_blocknr << (blkbits - 9);
+               length = bdev_direct_access(bh.b_bdev, sector, &kaddr, &pfn,
+                                               bh.b_size);
+               if (length < 0) {
+                       result = VM_FAULT_SIGBUS;
+                       goto out;
+               }
+               if ((length < PMD_SIZE) || (pfn & PG_PMD_COLOUR))
+                       goto fallback;
+
+               result |= vmf_insert_pfn_pmd(vma, address, pmd, pfn, write);
+       }
+
+ out:
+       if (buffer_unwritten(&bh))
+               complete_unwritten(&bh, !(result & VM_FAULT_ERROR));
+
+       i_mmap_unlock_write(mapping);
+
+       return result;
+
+ fallback:
+       count_vm_event(THP_FAULT_FALLBACK);
+       result = VM_FAULT_FALLBACK;
+       goto out;
+}
+EXPORT_SYMBOL_GPL(__dax_pmd_fault);
+
+/**
+ * dax_pmd_fault - handle a PMD fault on a DAX file
+ * @vma: The virtual memory area where the fault occurred
+ * @vmf: The description of the fault
+ * @get_block: The filesystem method used to translate file offsets to blocks
+ *
+ * When a page fault occurs, filesystems may call this helper in their
+ * pmd_fault handler for DAX files.
+ */
+int dax_pmd_fault(struct vm_area_struct *vma, unsigned long address,
+                       pmd_t *pmd, unsigned int flags, get_block_t get_block,
+                       dax_iodone_t complete_unwritten)
+{
+       int result;
+       struct super_block *sb = file_inode(vma->vm_file)->i_sb;
+
+       if (flags & FAULT_FLAG_WRITE) {
+               sb_start_pagefault(sb);
+               file_update_time(vma->vm_file);
+       }
+       result = __dax_pmd_fault(vma, address, pmd, flags, get_block,
+                               complete_unwritten);
+       if (flags & FAULT_FLAG_WRITE)
+               sb_end_pagefault(sb);
+
+       return result;
+}
+EXPORT_SYMBOL_GPL(dax_pmd_fault);
+#endif /* CONFIG_TRANSPARENT_HUGEPAGE */
+
 /**
  * dax_pfn_mkwrite - handle first write to DAX page
  * @vma: The virtual memory area where the fault occurred
This page took 0.032519 seconds and 5 git commands to generate.