From 91e68b1f83b930706b64bb72673bc40e0e76c784 Mon Sep 17 00:00:00 2001 From: Benjamin LaHaise Date: Mon, 27 Jul 2020 17:03:51 -0400 Subject: [PATCH] mmap: add support for read only mmap() Add support for read only mmap(). Signed-off-by: Benjamin LaHaise --- kmod/src/data.c | 78 ++++++++++++++++++++++++++++++++++++++++++++++++ kmod/src/inode.c | 1 + kmod/src/inode.h | 6 ++++ kmod/src/ioctl.c | 3 ++ 4 files changed, 88 insertions(+) diff --git a/kmod/src/data.c b/kmod/src/data.c index b129aa42..ba399000 100644 --- a/kmod/src/data.c +++ b/kmod/src/data.c @@ -1441,6 +1441,7 @@ out: long scoutfs_fallocate(struct file *file, int mode, loff_t offset, loff_t len) { struct inode *inode = file_inode(file); + struct scoutfs_inode_info *si = SCOUTFS_I(inode); struct super_block *sb = inode->i_sb; const u64 ino = scoutfs_ino(inode); struct scoutfs_lock *lock = NULL; @@ -1451,6 +1452,7 @@ long scoutfs_fallocate(struct file *file, int mode, loff_t offset, loff_t len) int ret; mutex_lock(&inode->i_mutex); + mutex_lock(&si->s_i_mutex); /* XXX support more flags */ if (mode & ~(FALLOC_FL_KEEP_SIZE)) { @@ -1516,6 +1518,7 @@ long scoutfs_fallocate(struct file *file, int mode, loff_t offset, loff_t len) out: scoutfs_unlock(sb, lock, SCOUTFS_LOCK_WRITE); + mutex_unlock(&si->s_i_mutex); mutex_unlock(&inode->i_mutex); trace_scoutfs_data_fallocate(sb, ino, mode, offset, len, ret); @@ -1999,6 +2002,80 @@ int scoutfs_data_waiting(struct super_block *sb, u64 ino, u64 iblock, return ret; } +int scoutfs_data_filemap_fault(struct vm_area_struct *vma, struct vm_fault *vmf) +{ + struct file *file = vma->vm_file; + struct inode *inode = file_inode(file); + struct scoutfs_inode_info *si = SCOUTFS_I(inode); + struct super_block *sb = inode->i_sb; + struct scoutfs_lock *inode_lock = NULL; + SCOUTFS_DECLARE_PER_TASK_ENTRY(pt_ent); + DECLARE_DATA_WAIT(dw); + bool have_ret = false; + loff_t pos; + int ret; + + pos = vmf->pgoff; + pos <<= PAGE_CACHE_SHIFT; + +retry: + ret = scoutfs_lock_inode(sb, SCOUTFS_LOCK_READ, + SCOUTFS_LKF_REFRESH_INODE, inode, &inode_lock); + if (ret < 0) + return VM_FAULT_SIGBUS; + + if (scoutfs_per_task_add_excl(&si->pt_data_lock, &pt_ent, inode_lock)) { + /* protect checked extents from stage/release */ + mutex_lock(&si->s_i_mutex); + atomic_inc(&inode->i_dio_count); + mutex_unlock(&si->s_i_mutex); + + ret = scoutfs_data_wait_check(inode, pos, PAGE_SIZE, + SEF_OFFLINE, SCOUTFS_IOC_DWO_READ, + &dw, inode_lock); + if (ret != 0) { + goto out; + } + } + + ret = filemap_fault(vma, vmf); + have_ret = true; + +out: + if (scoutfs_per_task_del(&si->pt_data_lock, &pt_ent)) + inode_dio_done(inode); + scoutfs_unlock(sb, inode_lock, SCOUTFS_LOCK_READ); + if (scoutfs_data_wait_found(&dw)) { + int err = scoutfs_data_wait(inode, &dw); + if (!have_ret) { + if (err == 0) + goto retry; + up_read(&vma->vm_mm->mmap_sem); + ret = VM_FAULT_RETRY; + have_ret = true; + } + } + if (!have_ret) + ret = VM_FAULT_SIGBUS; + + return ret; +} + + +static const struct vm_operations_struct scoutfs_data_file_vm_ops = { + .fault = scoutfs_data_filemap_fault, + .remap_pages = generic_file_remap_pages, +}; + +static int scoutfs_file_mmap(struct file *file, struct vm_area_struct *vma) +{ + if ((vma->vm_flags & VM_SHARED) && (vma->vm_flags & VM_MAYWRITE)) + return -EINVAL; + file_accessed(file); + vma->vm_ops = &scoutfs_data_file_vm_ops; + return 0; +} + const struct address_space_operations scoutfs_file_aops = { .readpage = scoutfs_readpage, .readpages = scoutfs_readpages, @@ -2013,6 +2090,7 @@ const struct file_operations scoutfs_file_fops = { .write = do_sync_write, .aio_read = scoutfs_file_aio_read, .aio_write = scoutfs_file_aio_write, + .mmap = scoutfs_file_mmap, .unlocked_ioctl = scoutfs_ioctl, .fsync = scoutfs_file_fsync, .llseek = scoutfs_file_llseek, diff --git a/kmod/src/inode.c b/kmod/src/inode.c index 32ed084e..dfbb198f 100644 --- a/kmod/src/inode.c +++ b/kmod/src/inode.c @@ -66,6 +66,7 @@ static void scoutfs_inode_ctor(void *obj) { struct scoutfs_inode_info *ci = obj; + mutex_init(&ci->s_i_mutex); mutex_init(&ci->item_mutex); seqcount_init(&ci->seqcount); ci->staging = false; diff --git a/kmod/src/inode.h b/kmod/src/inode.h index 719fb391..0ef5e809 100644 --- a/kmod/src/inode.h +++ b/kmod/src/inode.h @@ -28,6 +28,12 @@ struct scoutfs_inode_info { u64 offline_blocks; u32 flags; + /* We can't use inode->i_mutex to protect i_dio_count due to lock + * ordering in the kernel between i_mutex and mmap_sem. Use this + * as an inner lock. + */ + struct mutex s_i_mutex; + /* * The in-memory item info caches the current index item values * so that we can decide to update them with comparisons instead diff --git a/kmod/src/ioctl.c b/kmod/src/ioctl.c index a321a1db..7f833203 100644 --- a/kmod/src/ioctl.c +++ b/kmod/src/ioctl.c @@ -268,6 +268,7 @@ out: static long scoutfs_ioc_release(struct file *file, unsigned long arg) { struct inode *inode = file_inode(file); + struct scoutfs_inode_info *si = SCOUTFS_I(inode); struct super_block *sb = inode->i_sb; struct scoutfs_ioctl_release args; struct scoutfs_lock *lock = NULL; @@ -294,6 +295,7 @@ static long scoutfs_ioc_release(struct file *file, unsigned long arg) return ret; mutex_lock(&inode->i_mutex); + mutex_lock(&si->s_i_mutex); ret = scoutfs_lock_inode(sb, SCOUTFS_LOCK_WRITE, SCOUTFS_LKF_REFRESH_INODE, inode, &lock); @@ -341,6 +343,7 @@ static long scoutfs_ioc_release(struct file *file, unsigned long arg) out: scoutfs_unlock(sb, lock, SCOUTFS_LOCK_WRITE); + mutex_unlock(&si->s_i_mutex); mutex_unlock(&inode->i_mutex); mnt_drop_write_file(file);