]> git.proxmox.com Git - mirror_ubuntu-disco-kernel.git/blobdiff - fs/ext4/file.c
Merge branch 'work.mount' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
[mirror_ubuntu-disco-kernel.git] / fs / ext4 / file.c
index b33ec8a236782eb8fed7b5d475efce0c5fb64b32..da9c6948ad25dcd16ff2ed894b46c431b72fc91d 100644 (file)
@@ -279,7 +279,20 @@ static int ext4_dax_huge_fault(struct vm_fault *vmf,
        handle_t *handle = NULL;
        struct inode *inode = file_inode(vmf->vma->vm_file);
        struct super_block *sb = inode->i_sb;
-       bool write = vmf->flags & FAULT_FLAG_WRITE;
+
+       /*
+        * We have to distinguish real writes from writes which will result in a
+        * COW page; COW writes should *not* poke the journal (the file will not
+        * be changed). Doing so would cause unintended failures when mounted
+        * read-only.
+        *
+        * We check for VM_SHARED rather than vmf->cow_page since the latter is
+        * unset for pe_size != PE_SIZE_PTE (i.e. only in do_cow_fault); for
+        * other sizes, dax_iomap_fault will handle splitting / fallback so that
+        * we eventually come back with a COW page.
+        */
+       bool write = (vmf->flags & FAULT_FLAG_WRITE) &&
+               (vmf->vma->vm_flags & VM_SHARED);
 
        if (write) {
                sb_start_pagefault(sb);
@@ -311,41 +324,11 @@ static int ext4_dax_fault(struct vm_fault *vmf)
        return ext4_dax_huge_fault(vmf, PE_SIZE_PTE);
 }
 
-/*
- * Handle write fault for VM_MIXEDMAP mappings. Similarly to ext4_dax_fault()
- * handler we check for races agaist truncate. Note that since we cycle through
- * i_mmap_sem, we are sure that also any hole punching that began before we
- * were called is finished by now and so if it included part of the file we
- * are working on, our pte will get unmapped and the check for pte_same() in
- * wp_pfn_shared() fails. Thus fault gets retried and things work out as
- * desired.
- */
-static int ext4_dax_pfn_mkwrite(struct vm_fault *vmf)
-{
-       struct inode *inode = file_inode(vmf->vma->vm_file);
-       struct super_block *sb = inode->i_sb;
-       loff_t size;
-       int ret;
-
-       sb_start_pagefault(sb);
-       file_update_time(vmf->vma->vm_file);
-       down_read(&EXT4_I(inode)->i_mmap_sem);
-       size = (i_size_read(inode) + PAGE_SIZE - 1) >> PAGE_SHIFT;
-       if (vmf->pgoff >= size)
-               ret = VM_FAULT_SIGBUS;
-       else
-               ret = dax_pfn_mkwrite(vmf);
-       up_read(&EXT4_I(inode)->i_mmap_sem);
-       sb_end_pagefault(sb);
-
-       return ret;
-}
-
 static const struct vm_operations_struct ext4_dax_vm_ops = {
        .fault          = ext4_dax_fault,
        .huge_fault     = ext4_dax_huge_fault,
        .page_mkwrite   = ext4_dax_fault,
-       .pfn_mkwrite    = ext4_dax_pfn_mkwrite,
+       .pfn_mkwrite    = ext4_dax_fault,
 };
 #else
 #define ext4_dax_vm_ops        ext4_file_vm_ops
@@ -494,12 +477,11 @@ static int ext4_find_unwritten_pgoff(struct inode *inode,
 
        pagevec_init(&pvec, 0);
        do {
-               int i, num;
+               int i;
                unsigned long nr_pages;
 
-               num = min_t(pgoff_t, end - index, PAGEVEC_SIZE - 1) + 1;
-               nr_pages = pagevec_lookup(&pvec, inode->i_mapping, index,
-                                         (pgoff_t)num);
+               nr_pages = pagevec_lookup_range(&pvec, inode->i_mapping,
+                                       &index, end);
                if (nr_pages == 0)
                        break;
 
@@ -518,9 +500,6 @@ static int ext4_find_unwritten_pgoff(struct inode *inode,
                                goto out;
                        }
 
-                       if (page->index > end)
-                               goto out;
-
                        lock_page(page);
 
                        if (unlikely(page->mapping != inode->i_mapping)) {
@@ -537,6 +516,8 @@ static int ext4_find_unwritten_pgoff(struct inode *inode,
                                lastoff = page_offset(page);
                                bh = head = page_buffers(page);
                                do {
+                                       if (lastoff + bh->b_size <= startoff)
+                                               goto next;
                                        if (buffer_uptodate(bh) ||
                                            buffer_unwritten(bh)) {
                                                if (whence == SEEK_DATA)
@@ -551,6 +532,7 @@ static int ext4_find_unwritten_pgoff(struct inode *inode,
                                                unlock_page(page);
                                                goto out;
                                        }
+next:
                                        lastoff += bh->b_size;
                                        bh = bh->b_this_page;
                                } while (bh != head);
@@ -560,14 +542,10 @@ static int ext4_find_unwritten_pgoff(struct inode *inode,
                        unlock_page(page);
                }
 
-               /* The no. of pages is less than our desired, we are done. */
-               if (nr_pages < num)
-                       break;
-
-               index = pvec.pages[i - 1]->index + 1;
                pagevec_release(&pvec);
        } while (index <= end);
 
+       /* There are no pages upto endoff - that would be a hole in there. */
        if (whence == SEEK_HOLE && lastoff < endoff) {
                found = 1;
                *offset = lastoff;
@@ -592,7 +570,7 @@ static loff_t ext4_seek_data(struct file *file, loff_t offset, loff_t maxsize)
        inode_lock(inode);
 
        isize = i_size_read(inode);
-       if (offset >= isize) {
+       if (offset < 0 || offset >= isize) {
                inode_unlock(inode);
                return -ENXIO;
        }
@@ -655,7 +633,7 @@ static loff_t ext4_seek_hole(struct file *file, loff_t offset, loff_t maxsize)
        inode_lock(inode);
 
        isize = i_size_read(inode);
-       if (offset >= isize) {
+       if (offset < 0 || offset >= isize) {
                inode_unlock(inode);
                return -ENXIO;
        }