From: Hongbo Li <lihongbo22@huawei.com> hulk inclusion category: feature bugzilla: https://gitee.com/openeuler/release-management/issues/ID5W1P -------------------------------- It is easy to add data operation in MFS include file and directory. In MFS, data is from the cache layer, so we just need to pass the requests to the cache layer. In this step, we just transfer the read io to the under file system. Signed-off-by: Huang Xiaojia <huangxiaojia2@huawei.com> Signed-off-by: Hongbo Li <lihongbo22@huawei.com> --- fs/mfs/Makefile | 2 +- fs/mfs/data.c | 288 ++++++++++++++++++++++++++++++++++++++++++++++ fs/mfs/inode.c | 27 +++-- fs/mfs/internal.h | 29 +++++ 4 files changed, 337 insertions(+), 9 deletions(-) create mode 100644 fs/mfs/data.c diff --git a/fs/mfs/Makefile b/fs/mfs/Makefile index 0c10e447c6b2..546e620daf8d 100644 --- a/fs/mfs/Makefile +++ b/fs/mfs/Makefile @@ -1,4 +1,4 @@ # SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_MFS_FS) += mfs.o -mfs-objs := super.o inode.o +mfs-objs := super.o inode.o data.o diff --git a/fs/mfs/data.c b/fs/mfs/data.c new file mode 100644 index 000000000000..f7fa8f4e7102 --- /dev/null +++ b/fs/mfs/data.c @@ -0,0 +1,288 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* Copyright (C) 2025. Huawei Technologies Co., Ltd */ + +#include "internal.h" + +#include <linux/pagemap.h> +#include <linux/uio.h> +#include <linux/types.h> + +static struct mfs_file_info *mfs_file_info_alloc(struct file *lower, struct file *cache) +{ + struct mfs_file_info *info = kzalloc(sizeof(struct mfs_file_info), GFP_KERNEL); + + if (unlikely(!info)) + return NULL; + + info->lower = lower; + info->cache = cache; + return info; +} + +static void mfs_file_info_free(struct mfs_file_info *info) +{ + fput(info->cache); + fput(info->lower); + kfree(info); +} + +static int mfs_open(struct inode *inode, struct file *file) +{ + struct dentry *dentry = file_dentry(file); + struct mfs_sb_info *sbi = MFS_SB(inode->i_sb); + struct path lpath, cpath; + struct file *lfile, *cfile; + int flags = file->f_flags | MFS_OPEN_FLAGS; + struct mfs_file_info *file_info; + int err = 0; + + mfs_get_path(dentry, &lpath, &cpath); + lfile = dentry_open(&lpath, flags, current_cred()); + if (IS_ERR(lfile)) { + err = PTR_ERR(lfile); + goto put_path; + } + + cfile = dentry_open(&cpath, flags, current_cred()); + if (IS_ERR(cfile)) { + err = PTR_ERR(cfile); + goto lfput; + } + + if (support_event(sbi)) + /* close the default readahead */ + cfile->f_mode |= FMODE_RANDOM; + file_info = mfs_file_info_alloc(lfile, cfile); + if (!file_info) { + err = -ENOMEM; + goto cfput; + } + + file->private_data = file_info; + goto put_path; +cfput: + fput(cfile); +lfput: + fput(lfile); +put_path: + mfs_put_path(&lpath, &cpath); + return err; +} + +static int mfs_release(struct inode *inode, struct file *file) +{ + mfs_file_info_free(file->private_data); + return 0; +} + +static loff_t mfs_llseek(struct file *file, loff_t offset, int whence) +{ + struct mfs_file_info *file_info = file->private_data; + struct inode *inode = file_inode(file); + struct file *lfile, *cfile; + loff_t ret; + + if (offset == 0) { + if (whence == SEEK_CUR) + return file->f_pos; + + if (whence == SEEK_SET) + return vfs_setpos(file, 0, 0); + } + + lfile = file_info->lower; + cfile = file_info->cache; + + mfs_inode_lock(inode); + lfile->f_pos = file->f_pos; + ret = vfs_llseek(lfile, offset, whence); + if (ret < 0) + goto out; + + cfile->f_pos = file->f_pos; + ret = vfs_llseek(cfile, offset, whence); + if (ret < 0) + goto out; + + file->f_pos = lfile->f_pos; +out: + mfs_inode_unlock(inode); + return ret; +} + +static int mfs_flush(struct file *file, fl_owner_t id) +{ + struct mfs_file_info *file_info = file->private_data; + struct file *cfile; + int err = 0; + + cfile = file_info->cache; + if (cfile->f_op->flush) + err = cfile->f_op->flush(cfile, id); + + return err; +} + +static int mfs_readdir(struct file *file, struct dir_context *ctx) +{ + struct file *lfile; + struct mfs_file_info *file_info = file->private_data; + + lfile = file_info->lower; + return iterate_dir(lfile, ctx); +} + +static ssize_t mfs_read_iter(struct kiocb *iocb, struct iov_iter *to) +{ + struct file *cfile, *file = iocb->ki_filp; + struct mfs_file_info *fi = file->private_data; + ssize_t rsize; + + if (!iov_iter_count(to)) + return 0; + + cfile = fi->cache; + if (!cfile->f_op->read_iter) + return -EINVAL; + + (void)get_file(cfile); + + iocb->ki_filp = cfile; + rsize = cfile->f_op->read_iter(iocb, to); + iocb->ki_filp = file; + fput(cfile); + return rsize; +} + +static int mfs_file_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct mfs_file_info *fi = file->private_data; + struct file *cfile = fi->cache; + int err; + + if (!cfile->f_op->mmap) + return -ENODEV; + + (void)get_file(cfile); + vma->vm_file = cfile; + err = call_mmap(vma->vm_file, vma); + vma->vm_file = file; + fput(cfile); + if (err) + return err; + + fi->cache_vm_ops = vma->vm_ops; + vma->vm_ops = &mfs_file_vm_ops; + + return 0; +} + +static vm_fault_t mfs_filemap_fault(struct vm_fault *vmf) +{ + struct file *cfile, *file = vmf->vma->vm_file; + struct mfs_file_info *fi = file->private_data; + const struct vm_operations_struct *cvm_ops; + struct vm_area_struct cvma, *vma, **vma_; + vm_fault_t ret; + + vma = vmf->vma; + memcpy(&cvma, vma, sizeof(struct vm_area_struct)); + cfile = fi->cache; + cvm_ops = fi->cache_vm_ops; + cvma.vm_file = cfile; + + if (unlikely(!cvm_ops->fault)) + return VM_FAULT_SIGBUS; + + (void)get_file(cfile); + + /* + * Dealing fault in mfs will call cachefile's fault eventually, + * hence we will change vmf->vma->vm_file to cachefile. + * When faulting concurrently, changing vmf->vma->vm_file is + * visible to other threads. Hence we use cvma to narrow the + * visibility. vmf->vma is const, so we use **vma_ to change. + */ + vma_ = (struct vm_area_struct **)&vmf->vma; + *vma_ = &cvma; + ret = cvm_ops->fault(vmf); + *vma_ = vma; + fput(cfile); + return ret; +} + +vm_fault_t mfs_filemap_map_pages(struct vm_fault *vmf, + pgoff_t start_pgoff, pgoff_t end_pgoff) +{ + struct file *cfile, *file = vmf->vma->vm_file; + struct mfs_file_info *fi = file->private_data; + const struct vm_operations_struct *cvm_ops; + struct vm_area_struct cvma, *vma, **vma_; + vm_fault_t ret; + + vma = vmf->vma; + memcpy(&cvma, vma, sizeof(struct vm_area_struct)); + cfile = fi->cache; + cvm_ops = fi->cache_vm_ops; + cvma.vm_file = cfile; + (void)get_file(cfile); + if (unlikely(!cvm_ops->map_pages)) { + fput(cfile); + return 0; + } + + vma_ = (struct vm_area_struct **)&vmf->vma; + *vma_ = &cvma; + ret = cvm_ops->map_pages(vmf, start_pgoff, end_pgoff); + *vma_ = vma; + fput(cfile); + + return ret; +} + +static int mfs_fadvise(struct file *file, loff_t offset, loff_t len, int advice) +{ + struct inode *inode = file_inode(file); + struct mfs_sb_info *sbi = MFS_SB(inode->i_sb); + struct mfs_file_info *fi; + struct file *cfile; + int ret; + + /* avoid trigger readahead in event mode */ + if (support_event(sbi)) + return generic_fadvise(file, offset, len, advice); + + fi = file->private_data; + cfile = fi->cache; + (void)get_file(cfile); + + ret = vfs_fadvise(cfile, offset, len, advice); + fput(cfile); + + return ret; +} + +const struct file_operations mfs_dir_fops = { + .open = mfs_open, + .iterate_shared = mfs_readdir, + .release = mfs_release, +}; + +const struct file_operations mfs_file_fops = { + .open = mfs_open, + .release = mfs_release, + .llseek = mfs_llseek, + .read_iter = mfs_read_iter, + .flush = mfs_flush, + .mmap = mfs_file_mmap, + .fadvise = mfs_fadvise, +}; + +const struct vm_operations_struct mfs_file_vm_ops = { + .fault = mfs_filemap_fault, + .map_pages = mfs_filemap_map_pages, +}; + +const struct address_space_operations mfs_aops = { + .direct_IO = noop_direct_IO, +}; diff --git a/fs/mfs/inode.c b/fs/mfs/inode.c index fd87814ed81b..3e4d24c517cb 100644 --- a/fs/mfs/inode.c +++ b/fs/mfs/inode.c @@ -258,20 +258,31 @@ struct inode *mfs_iget(struct super_block *sb, struct inode *lower_inode, /* new inode */ vi = MFS_I(inode); inode->i_ino = lower_inode->i_ino; - vi->lower = lower_inode; - vi->cache = cache_inode; - - if (S_ISDIR(lower_inode->i_mode)) + switch (lower_inode->i_mode & S_IFMT) { + case S_IFREG: + inode->i_op = &mfs_file_iops; + inode->i_fop = &mfs_file_fops; + break; + case S_IFDIR: inode->i_op = &mfs_dir_iops; - else if (S_ISLNK(lower_inode->i_mode)) + inode->i_fop = &mfs_dir_fops; + break; + case S_IFLNK: inode->i_op = &mfs_symlink_iops; - else - inode->i_op = &mfs_file_iops; - + break; + default: + err = -EOPNOTSUPP; + goto err_inode; + } + inode->i_mapping->a_ops = &mfs_aops; + vi->lower = lower_inode; + vi->cache = cache_inode; fsstack_copy_attr_all(inode, lower_inode); fsstack_copy_inode_size(inode, lower_inode); unlock_new_inode(inode); return inode; +err_inode: + iget_failed(inode); err_put_cache: iput(cache_inode); err_put_lower: diff --git a/fs/mfs/internal.h b/fs/mfs/internal.h index cc44af558b26..ca4fc42d578b 100644 --- a/fs/mfs/internal.h +++ b/fs/mfs/internal.h @@ -4,6 +4,7 @@ #ifndef _MFS_INTERNAL_H #define _MFS_INTERNAL_H +#include <linux/file.h> #include <linux/fs.h> #include <linux/mm.h> #include <linux/container_of.h> @@ -12,6 +13,8 @@ #define MFS_NAME "mfs" +#define MFS_OPEN_FLAGS (O_NOATIME) + struct mfs_sb_info { int mode; char *mtree; @@ -31,6 +34,12 @@ struct mfs_inode { struct inode vfs_inode; }; +struct mfs_file_info { + struct file *lower; + struct file *cache; + const struct vm_operations_struct *cache_vm_ops; +}; + struct mfs_dentry_info { spinlock_t lock; struct path lower; @@ -41,6 +50,11 @@ struct mfs_dentry_info { #define MFS_I(ptr) container_of(ptr, struct mfs_inode, vfs_inode) #define MFS_D(dent) ((struct mfs_dentry_info *)(dent)->d_fsdata) +extern const struct file_operations mfs_dir_fops; +extern const struct file_operations mfs_file_fops; +extern const struct address_space_operations mfs_aops; +extern const struct vm_operations_struct mfs_file_vm_ops; + static inline struct inode *mfs_lower_inode(const struct inode *i) { return MFS_I(i)->lower; @@ -101,6 +115,21 @@ static inline void mfs_release_path(const struct dentry *dent) spin_unlock(&MFS_D(dent)->lock); } +static inline void mfs_inode_lock(struct inode *inode) +{ + mutex_lock(&MFS_I(inode)->lock); +} + +static inline void mfs_inode_unlock(struct inode *inode) +{ + mutex_unlock(&MFS_I(inode)->lock); +} + +static inline bool support_event(struct mfs_sb_info *sbi) +{ + return sbi->mode != MFS_MODE_NONE; +} + struct inode *mfs_iget(struct super_block *sb, struct inode *lower_inode, struct path *cache_path); int mfs_alloc_dentry_info(struct dentry *dentry); -- 2.25.1