From b7a3d0371171f5c8cb972de8476ec59f51eefe26 Mon Sep 17 00:00:00 2001 From: Benjamin LaHaise Date: Mon, 27 Jul 2020 17:03:51 -0400 Subject: [PATCH] Add support for read only mmap() Adds the required memory mapped ops struct and page fault handler for reads. Signed-off-by: Benjamin LaHaise Signed-off-by: Auke Kok --- kmod/src/Makefile.kernelcompat | 16 +++++++ kmod/src/data.c | 78 ++++++++++++++++++++++++++++++++++ kmod/src/kernelcompat.h | 16 +++++++ 3 files changed, 110 insertions(+) diff --git a/kmod/src/Makefile.kernelcompat b/kmod/src/Makefile.kernelcompat index 403a2f66..211ee0d4 100644 --- a/kmod/src/Makefile.kernelcompat +++ b/kmod/src/Makefile.kernelcompat @@ -431,3 +431,19 @@ endif ifneq (,$(shell grep 'struct file.*bdev_file_open_by_path.const char.*path' include/linux/blkdev.h)) ccflags-y += -DKC_BDEV_FILE_OPEN_BY_PATH endif + +# v4.0-rc7-1796-gfe0f07d08ee3 +# +# direct-io changes modify inode_dio_done to now be called inode_dio_end +ifneq (,$(shell grep 'void inode_dio_end.struct inode' include/linux/fs.h)) +ccflags-y += -DKC_INODE_DIO_END +endif + +# +# v5.0-6476-g3d3539018d2c +# +# page fault handlers return a bitmask vm_fault_t instead +# Note: el8's header has a slightly modified prefix here +ifneq (,$(shell grep 'typedef.*__bitwise unsigned.*int vm_fault_t' include/linux/mm_types.h)) +ccflags-y += -DKC_MM_VM_FAULT_T +endif diff --git a/kmod/src/data.c b/kmod/src/data.c index 909167bc..63ed830d 100644 --- a/kmod/src/data.c +++ b/kmod/src/data.c @@ -1914,6 +1914,83 @@ int scoutfs_data_waiting(struct super_block *sb, u64 ino, u64 iblock, return ret; } +#ifdef KC_MM_VM_FAULT_T +static vm_fault_t scoutfs_data_filemap_fault(struct vm_fault *vmf) +{ + struct vm_area_struct *vma = vmf->vma; +#else +static int scoutfs_data_filemap_fault(struct vm_area_struct *vma, struct vm_fault *vmf) +{ +#endif + 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); + loff_t pos; + int err; + vm_fault_t ret = VM_FAULT_SIGBUS; + + pos = vmf->pgoff; + pos <<= PAGE_SHIFT; + +retry: + err = scoutfs_lock_inode(sb, SCOUTFS_LOCK_READ, + SCOUTFS_LKF_REFRESH_INODE, inode, &inode_lock); + if (err < 0) + return vmf_error(err); + + if (scoutfs_per_task_add_excl(&si->pt_data_lock, &pt_ent, inode_lock)) { + /* protect checked extents from stage/release */ + atomic_inc(&inode->i_dio_count); + + err = scoutfs_data_wait_check(inode, pos, PAGE_SIZE, + SEF_OFFLINE, SCOUTFS_IOC_DWO_READ, + &dw, inode_lock); + if (err != 0) { + if (err < 0) + ret = vmf_error(err); + goto out; + } + } + +#ifdef KC_MM_VM_FAULT_T + ret = filemap_fault(vmf); +#else + ret = filemap_fault(vma, vmf); +#endif + +out: + if (scoutfs_per_task_del(&si->pt_data_lock, &pt_ent)) + kc_inode_dio_end(inode); + scoutfs_unlock(sb, inode_lock, SCOUTFS_LOCK_READ); + if (scoutfs_data_wait_found(&dw)) { + err = scoutfs_data_wait(inode, &dw); + if (err == 0) + goto retry; + + ret = VM_FAULT_RETRY; + } + + 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 = { #ifdef KC_MPAGE_READ_FOLIO .dirty_folio = block_dirty_folio, @@ -1945,6 +2022,7 @@ const struct file_operations scoutfs_file_fops = { .splice_read = generic_file_splice_read, .splice_write = iter_file_splice_write, #endif + .mmap = scoutfs_file_mmap, .unlocked_ioctl = scoutfs_ioctl, .fsync = scoutfs_file_fsync, .llseek = scoutfs_file_llseek, diff --git a/kmod/src/kernelcompat.h b/kmod/src/kernelcompat.h index 8c17d31e..74ad0d66 100644 --- a/kmod/src/kernelcompat.h +++ b/kmod/src/kernelcompat.h @@ -438,4 +438,20 @@ static inline int kc_tcp_sock_set_nodelay(struct socket *sock) } #endif +#ifdef KC_INODE_DIO_END +#define kc_inode_dio_end inode_dio_end +#else +#define kc_inode_dio_end inode_dio_done +#endif + +#ifndef KC_MM_VM_FAULT_T +typedef unsigned int vm_fault_t; +static inline vm_fault_t vmf_error(int err) +{ + if (err == -ENOMEM) + return VM_FAULT_OOM; + return VM_FAULT_SIGBUS; +} +#endif + #endif