From 7fd25d172b0a2a3ef753b9247fe809eff01ddd2c Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Wed, 16 Mar 2022 17:06:55 -0700 Subject: [PATCH] ANDROID: fuse-bpf: fix read_iter and write_iter Properly handle the async case. The existing bpf operations will likely need to be reworked. Given that they don't allow altering anything as is, this change just incrementally moves us in the right direction. Signed-off-by: Daniel Rosenberg Test: generic/467 and fuse_test Bug: 217570523 Change-Id: I31c0b48bf3d674efecad4bff4ea8b482c4e7da45 --- fs/fuse/backing.c | 149 ++++++++++++++++++++++++++++++++++++++++------ fs/fuse/fuse_i.h | 4 ++ 2 files changed, 136 insertions(+), 17 deletions(-) diff --git a/fs/fuse/backing.c b/fs/fuse/backing.c index 70e2fe352371..118bd9d12945 100644 --- a/fs/fuse/backing.c +++ b/fs/fuse/backing.c @@ -13,6 +13,33 @@ #include "../internal.h" +#define FUSE_BPF_IOCB_MASK (IOCB_APPEND | IOCB_DSYNC | IOCB_HIPRI | IOCB_NOWAIT | IOCB_SYNC) + +struct fuse_bpf_aio_req { + struct kiocb iocb; + struct kiocb *iocb_fuse; +}; + +static void fuse_file_accessed(struct file *dst_file, struct file *src_file) +{ + struct inode *dst_inode; + struct inode *src_inode; + + if (dst_file->f_flags & O_NOATIME) + return; + + dst_inode = file_inode(dst_file); + src_inode = file_inode(src_file); + + if ((!timespec64_equal(&dst_inode->i_mtime, &src_inode->i_mtime) || + !timespec64_equal(&dst_inode->i_ctime, &src_inode->i_ctime))) { + dst_inode->i_mtime = src_inode->i_mtime; + dst_inode->i_ctime = src_inode->i_ctime; + } + + touch_atime(&dst_file->f_path); +} + struct bpf_prog *fuse_get_bpf_prog(struct file *file) { struct bpf_prog *bpf_prog = ERR_PTR(-EINVAL); @@ -779,6 +806,33 @@ void *fuse_removexattr_finalize(struct fuse_args *fa, return NULL; } +static void fuse_bpf_aio_cleanup_handler(struct fuse_bpf_aio_req *aio_req) +{ + struct kiocb *iocb = &aio_req->iocb; + struct kiocb *iocb_fuse = aio_req->iocb_fuse; + + if (iocb->ki_flags & IOCB_WRITE) { + __sb_writers_acquired(file_inode(iocb->ki_filp)->i_sb, + SB_FREEZE_WRITE); + file_end_write(iocb->ki_filp); + fuse_copyattr(iocb_fuse->ki_filp, iocb->ki_filp); + } + + iocb_fuse->ki_pos = iocb->ki_pos; + kfree(aio_req); +} + +static void fuse_bpf_aio_rw_complete(struct kiocb *iocb, long res, long res2) +{ + struct fuse_bpf_aio_req *aio_req = + container_of(iocb, struct fuse_bpf_aio_req, iocb); + struct kiocb *iocb_fuse = aio_req->iocb_fuse; + + fuse_bpf_aio_cleanup_handler(aio_req); + iocb_fuse->ki_complete(iocb_fuse, res, res2); +} + + int fuse_file_read_iter_initialize( struct fuse_args *fa, struct fuse_read_in *fri, struct kiocb *iocb, struct iov_iter *to) @@ -820,17 +874,44 @@ int fuse_file_read_iter_backing(struct fuse_args *fa, { struct file *file = iocb->ki_filp; struct fuse_file *ff = file->private_data; - ssize_t result; + ssize_t ret; + + if (!iov_iter_count(to)) + return 0; + + if ((iocb->ki_flags & IOCB_DIRECT) && + (!ff->backing_file->f_mapping->a_ops || + !ff->backing_file->f_mapping->a_ops->direct_IO)) + return -EINVAL; /* TODO This just plain ignores any change to fuse_read_in */ - result = vfs_iter_read(ff->backing_file, to, &iocb->ki_pos, 0); + if (is_sync_kiocb(iocb)) { + ret = vfs_iter_read(ff->backing_file, to, &iocb->ki_pos, + iocb_to_rw_flags(iocb->ki_flags, FUSE_BPF_IOCB_MASK)); + } else { + struct fuse_bpf_aio_req *aio_req; - if (result < 0) - return result; + ret = -ENOMEM; + aio_req = kzalloc(sizeof(struct fuse_bpf_aio_req), GFP_KERNEL); + if (!aio_req) + goto out; + aio_req->iocb_fuse = iocb; + kiocb_clone(&aio_req->iocb, iocb, ff->backing_file); + aio_req->iocb.ki_complete = fuse_bpf_aio_rw_complete; + ret = vfs_iocb_iter_read(ff->backing_file, &aio_req->iocb, to); + if (ret != -EIOCBQUEUED) + fuse_bpf_aio_cleanup_handler(aio_req); + } + + if (ret >= 0) + fa->out_args[0].size = ret; /* TODO Need to point value at the buffer for post-modification */ - fa->out_args[0].size = result; - return result; + +out: + fuse_file_accessed(file, ff->backing_file); + + return ret; } void *fuse_file_read_iter_finalize(struct fuse_args *fa, @@ -862,8 +943,8 @@ int fuse_file_write_iter_initialize( .in_args[1].size = fwio->fwi.size, .in_args[1].value = from->kvec->iov_base, .out_numargs = 1, - .out_args[0].size = sizeof(fwio->fwo), - .out_args[0].value = &fwio->fwo, + .out_args[0].size = sizeof(fwio->fwio), + .out_args[0].value = &fwio->fwio, }; return 0; @@ -874,26 +955,60 @@ int fuse_file_write_iter_backing(struct fuse_args *fa, { struct file *file = iocb->ki_filp; struct fuse_file *ff = file->private_data; - struct fuse_write_out *fwo = fa->out_args[0].value; + struct fuse_write_iter_out *fwio = fa->out_args[0].value; + ssize_t ret; + + if (!iov_iter_count(from)) + return 0; /* TODO This just plain ignores any change to fuse_write_in */ - fwo->size = vfs_iter_write(ff->backing_file, from, &iocb->ki_pos, 0); + /* TODO uint32_t seems smaller than ssize_t.... right? */ + inode_lock(file_inode(file)); - /* Must reflect change in size of backing file to upper file */ - if (fwo->size > 0) - fuse_copyattr(file, ff->backing_file); + fuse_copyattr(file, ff->backing_file); - if (fwo->size < 0) - return fwo->size; + if (is_sync_kiocb(iocb)) { + file_start_write(ff->backing_file); + ret = vfs_iter_write(ff->backing_file, from, &iocb->ki_pos, + iocb_to_rw_flags(iocb->ki_flags, FUSE_BPF_IOCB_MASK)); + file_end_write(ff->backing_file); + + /* Must reflect change in size of backing file to upper file */ + if (ret > 0) + fuse_copyattr(file, ff->backing_file); + } else { + struct fuse_bpf_aio_req *aio_req; + + ret = -ENOMEM; + /* TODO get this from a cache? */ + aio_req = kzalloc(sizeof(struct fuse_bpf_aio_req), GFP_KERNEL); + if (!aio_req) + goto out; + + file_start_write(ff->backing_file); + __sb_writers_release(file_inode(ff->backing_file)->i_sb, SB_FREEZE_WRITE); + aio_req->iocb_fuse = iocb; + kiocb_clone(&aio_req->iocb, iocb, ff->backing_file); + aio_req->iocb.ki_complete = fuse_bpf_aio_rw_complete; + ret = vfs_iocb_iter_write(ff->backing_file, &aio_req->iocb, from); + if (ret != -EIOCBQUEUED) + fuse_bpf_aio_cleanup_handler(aio_req); + } + +out: + inode_unlock(file_inode(file)); + fwio->ret = ret; + if (ret < 0) + return ret; return 0; } void *fuse_file_write_iter_finalize(struct fuse_args *fa, struct kiocb *iocb, struct iov_iter *from) { - struct fuse_write_out *fwo = fa->out_args[0].value; + struct fuse_write_iter_out *fwio = fa->out_args[0].value; - return ERR_PTR(fwo->size); + return ERR_PTR(fwio->ret); } ssize_t fuse_backing_mmap(struct file *file, struct vm_area_struct *vma) diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 84d2f74f9d9f..87f2f2e5c5a8 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -1524,9 +1524,13 @@ int fuse_file_read_iter_backing(struct fuse_args *fa, void *fuse_file_read_iter_finalize(struct fuse_args *fa, struct kiocb *iocb, struct iov_iter *to); +struct fuse_write_iter_out { + uint64_t ret; +}; struct fuse_file_write_iter_io { struct fuse_write_in fwi; struct fuse_write_out fwo; + struct fuse_write_iter_out fwio; }; int fuse_file_write_iter_initialize(