From: John Groves <[email protected]>

This commit adds vm_operations, plus famfs_mmap() and fault handlers.
It is still missing iomap_ops, iomap mapping resolution, and
famfs_ioctl() for setting up file-to-memory mappings.

Note that famfs supports MAP_SYNC (for MMAP_SYNC) basically for free,
because file maps are never mutated after MAP_CREATE.

Signed-off-by: John Groves <[email protected]>
---
v13:
 - famfs_file_mmap(): close an mprotect() bypass of FAMFS_OPT_WRITE. The gate
   checked VM_WRITE only, so a PROT_READ|MAP_SHARED mapping (VM_MAYWRITE set,
   VM_WRITE clear) passed and could then be upgraded via mprotect(PROT_WRITE).
   Now reject an already-writable shared mapping and strip VM_MAYWRITE from a
   read-only one so it cannot be upgraded (Sashiko bot).
 - Added a comment on famfs_filemap_mkwrite() explaining that a
   page_mkwrite/pfn_mkwrite is unconditionally a write fault, so it forces
   write_fault=true rather than consulting famfs_is_write_fault(), per
   Darrick.
 - Dropped .map_pages = filemap_map_pages from famfs_file_vm_ops: it maps
   page-cache folios, which a DAX mapping has none of (the address_space holds
   xarray value entries, skipped via xa_is_value()), so it did nothing but
   cost cycles (Sashiko bot).
 - Advertise FOP_MMAP_SYNC in fop_flags so MAP_SYNC|MAP_SHARED_VALIDATE is
   accepted. famfs fmap metadata is immutable after MAP_CREATE, so the
   MAP_SYNC durability guarantee is trivially met, and the fault path already
   handles VM_FAULT_NEEDDSYNC (Sashiko bot).

 fs/famfs/famfs_file.c | 109 +++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 108 insertions(+), 1 deletion(-)

diff --git a/fs/famfs/famfs_file.c b/fs/famfs/famfs_file.c
index 1369fe1824bc..9c1cd2f67489 100644
--- a/fs/famfs/famfs_file.c
+++ b/fs/famfs/famfs_file.c
@@ -16,6 +16,76 @@
 
 #include "famfs_internal.h"
 
+/*********************************************************************
+ * vm_operations
+ */
+static vm_fault_t
+__famfs_filemap_fault(
+       struct vm_fault *vmf,
+       unsigned int order,
+       bool write_fault)
+{
+       struct inode *inode = file_inode(vmf->vma->vm_file);
+       struct super_block *sb = inode->i_sb;
+       struct famfs_fs_info *fsi = sb->s_fs_info;
+       vm_fault_t ret;
+       unsigned long pfn;
+
+       if (fsi->deverror)
+               return VM_FAULT_SIGBUS;
+
+       if (!IS_DAX(file_inode(vmf->vma->vm_file))) {
+               pr_err("%s: file not marked IS_DAX!!\n", __func__);
+               return VM_FAULT_SIGBUS;
+       }
+
+       if (write_fault) {
+               sb_start_pagefault(inode->i_sb);
+               file_update_time(vmf->vma->vm_file);
+       }
+
+       ret = dax_iomap_fault(vmf, order, &pfn, NULL, NULL /*&famfs_iomap_ops 
*/);
+       if (ret & VM_FAULT_NEEDDSYNC)
+               ret = dax_finish_sync_fault(vmf, order, pfn);
+
+       if (write_fault)
+               sb_end_pagefault(inode->i_sb);
+
+       return ret;
+}
+
+static inline bool
+famfs_is_write_fault(struct vm_fault *vmf)
+{
+       return (vmf->flags & FAULT_FLAG_WRITE) &&
+              (vmf->vma->vm_flags & VM_SHARED);
+}
+
+static vm_fault_t
+famfs_filemap_fault(struct vm_fault *vmf)
+{
+       return __famfs_filemap_fault(vmf, 0, famfs_is_write_fault(vmf));
+}
+
+static vm_fault_t
+famfs_filemap_huge_fault(struct vm_fault *vmf, unsigned int order)
+{
+       return __famfs_filemap_fault(vmf, order, famfs_is_write_fault(vmf));
+}
+
+static vm_fault_t
+famfs_filemap_mkwrite(struct vm_fault *vmf)
+{
+       return __famfs_filemap_fault(vmf, 0, true);
+}
+
+const struct vm_operations_struct famfs_file_vm_ops = {
+       .fault          = famfs_filemap_fault,
+       .huge_fault     = famfs_filemap_huge_fault,
+       .page_mkwrite   = famfs_filemap_mkwrite,
+       .pfn_mkwrite    = famfs_filemap_mkwrite,
+};
+
 /*********************************************************************
  * file_operations
  */
@@ -120,14 +190,51 @@ famfs_dax_write_iter(struct kiocb *iocb, struct iov_iter 
*from)
        return rc;
 }
 
+static int
+famfs_file_mmap(struct file *file, struct vm_area_struct *vma)
+{
+       struct inode *inode = file_inode(file);
+       struct super_block *sb = inode->i_sb;
+       struct famfs_fs_info *fsi = sb->s_fs_info;
+       ssize_t rc;
+
+       if (fsi->deverror)
+               return -ENODEV;
+
+       /*
+        * Gate shared-writable mappings on FAMFS_OPT_WRITE. Reject a mapping
+        * that is already writable, and strip VM_MAYWRITE from a read-only
+        * shared mapping so a later mprotect(PROT_WRITE) cannot upgrade it.
+        * This is best effort: it does not revoke mappings that already exist.
+        */
+       if ((vma->vm_flags & VM_SHARED) &&
+           !famfs_opt_enabled(fsi, FAMFS_OPT_WRITE)) {
+               if (vma->vm_flags & VM_WRITE)
+                       return -EPERM;
+               vm_flags_clear(vma, VM_MAYWRITE);
+       }
+
+       rc = famfs_file_invalid(inode);
+       if (rc)
+               return (int)rc;
+
+       file_accessed(file);
+       vma->vm_ops = &famfs_file_vm_ops;
+       vm_flags_set(vma, VM_HUGEPAGE);
+       return 0;
+}
+
 const struct file_operations famfs_file_operations = {
        .owner             = THIS_MODULE,
 
+       /* fmap metadata is immutable after MAP_CREATE, so MAP_SYNC is free */
+       .fop_flags         = FOP_MMAP_SYNC,
+
        /* Custom famfs operations */
        .write_iter        = famfs_dax_write_iter,
        .read_iter         = famfs_dax_read_iter,
        .unlocked_ioctl    = NULL /*famfs_file_ioctl*/,
-       .mmap              = NULL /* famfs_file_mmap */,
+       .mmap              = famfs_file_mmap,
 
        /* Force PMD alignment for mmap */
        .get_unmapped_area = thp_get_unmapped_area,
-- 
2.53.0



Reply via email to