orangefs: implement vm_ops->fault
authorMartin Brandenburg <martin@omnibond.com>
Tue, 3 Apr 2018 16:27:12 +0000 (16:27 +0000)
committerMike Marshall <hubcap@omnibond.com>
Wed, 4 Apr 2018 01:55:27 +0000 (21:55 -0400)
Must retrieve size before running filemap_fault so the kernel has
an up-to-date size.

This should have been caught by xfstests generic/246, but it was masked
by orangefs_new_inode, which set i_size to PAGE_SIZE.  When nothing
caused a getattr prior to a pagefault, i_size was still PAGE_SIZE.
Since xfstests only read 10 bytes, it did not catch this bug.

When orangefs_new_inode was modified to perform a getattr instead,
i_size was set to zero, as it was a newly created file.  Then
orangefs_file_write_iter did NOT set i_size.  Instead it invalidated the
attribute cache, which should have caused the next caller to retrieve
i_size.  But the fault handler did not know it was supposed to retrieve
i_size.  So during xfstests, i_size was still zero, and filemap_fault
returned VM_FAULT_SIGBUS.

Fixes xfstests generic/452.

Signed-off-by: Martin Brandenburg <martin@omnibond.com>
Signed-off-by: Mike Marshall <hubcap@omnibond.com>
fs/orangefs/file.c

index 3a7319a1bfdb08d2be0b391316ce487fe88faa9c..26358efbf794a2ca21cd6e94185f84876bfb9a27 100644 (file)
@@ -528,6 +528,28 @@ static long orangefs_ioctl(struct file *file, unsigned int cmd, unsigned long ar
        return ret;
 }
 
+static int orangefs_fault(struct vm_fault *vmf)
+{
+       struct file *file = vmf->vma->vm_file;
+       int rc;
+       rc = orangefs_inode_getattr(file->f_mapping->host, 0, 1,
+           STATX_SIZE);
+       if (rc == -ESTALE)
+               rc = -EIO;
+       if (rc) {
+               gossip_err("%s: orangefs_inode_getattr failed, "
+                   "rc:%d:.\n", __func__, rc);
+               return rc;
+       }
+       return filemap_fault(vmf);
+}
+
+const struct vm_operations_struct orangefs_file_vm_ops = {
+       .fault = orangefs_fault,
+       .map_pages = filemap_map_pages,
+       .page_mkwrite = filemap_page_mkwrite,
+};
+
 /*
  * Memory map a region of a file.
  */
@@ -539,12 +561,16 @@ static int orangefs_file_mmap(struct file *file, struct vm_area_struct *vma)
                        (char *)file->f_path.dentry->d_name.name :
                        (char *)"Unknown"));
 
+       if ((vma->vm_flags & VM_SHARED) && (vma->vm_flags & VM_MAYWRITE))
+               return -EINVAL;
+
        /* set the sequential readahead hint */
        vma->vm_flags |= VM_SEQ_READ;
        vma->vm_flags &= ~VM_RAND_READ;
 
-       /* Use readonly mmap since we cannot support writable maps. */
-       return generic_file_readonly_mmap(file, vma);
+       file_accessed(file);
+       vma->vm_ops = &orangefs_file_vm_ops;
+       return 0;
 }
 
 #define mapping_nrpages(idata) ((idata)->nrpages)