[forgot to CC overlayfs list] On Tue, Sep 12, 2023 at 9:54 PM Amir Goldstein <amir73il@xxxxxxxxx> wrote: > > Overlayfs stores its files data in backing files on other filesystems. > > Factor out some common helpers to perform io to backing files, that will > later be reused by fuse passthrough code. > > Suggested-by: Miklos Szeredi <miklos@xxxxxxxxxx> > Link: https://lore.kernel.org/r/CAJfpeguhmZbjP3JLqtUy0AdWaHOkAPWeP827BBWwRFEAUgnUcQ@xxxxxxxxxxxxxx > Signed-off-by: Amir Goldstein <amir73il@xxxxxxxxx> > --- > > Miklos, > > This is the re-factoring that you suggested in the FUSE passthrough > patches discussion linked above. > > This patch is based on the overlayfs prep patch set I just posted [1]. > > Although overlayfs currently is the only user of these backing file > helpers, I am sending this patch to a wider audience in case other > filesystem developers want to comment on the abstraction. > > We could perhaps later considering moving backing_file_open() helper > and related code to backing_file.c. > > In any case, if there are no objections, I plan to queue this work > for 6.7 via the overlayfs tree. > > Thanks, > Amir. > > [1] https://lore.kernel.org/linux-unionfs/20230912173653.3317828-1-amir73il@xxxxxxxxx/ > > > MAINTAINERS | 2 + > fs/Kconfig | 4 + > fs/Makefile | 1 + > fs/backing_file.c | 160 +++++++++++++++++++++++++++++++++++ > fs/overlayfs/Kconfig | 1 + > fs/overlayfs/file.c | 137 ++---------------------------- > fs/overlayfs/overlayfs.h | 2 - > fs/overlayfs/super.c | 11 +-- > include/linux/backing_file.h | 22 +++++ > 9 files changed, 199 insertions(+), 141 deletions(-) > create mode 100644 fs/backing_file.c > create mode 100644 include/linux/backing_file.h > > diff --git a/MAINTAINERS b/MAINTAINERS > index 90f13281d297..4e1d21773e0e 100644 > --- a/MAINTAINERS > +++ b/MAINTAINERS > @@ -16092,7 +16092,9 @@ L: linux-unionfs@xxxxxxxxxxxxxxx > S: Supported > T: git git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs.git > F: Documentation/filesystems/overlayfs.rst > +F: fs/backing_file.c > F: fs/overlayfs/ > +F: include/linux/backing_file.h > > P54 WIRELESS DRIVER > M: Christian Lamparter <chunkeey@xxxxxxxxxxxxxx> > diff --git a/fs/Kconfig b/fs/Kconfig > index aa7e03cc1941..9027a88ffa47 100644 > --- a/fs/Kconfig > +++ b/fs/Kconfig > @@ -26,6 +26,10 @@ config LEGACY_DIRECT_IO > depends on BUFFER_HEAD > bool > > +# Common backing file helpers > +config FS_BACKING_FILE > + bool > + > if BLOCK > > source "fs/ext2/Kconfig" > diff --git a/fs/Makefile b/fs/Makefile > index f9541f40be4e..95ef06cff388 100644 > --- a/fs/Makefile > +++ b/fs/Makefile > @@ -39,6 +39,7 @@ obj-$(CONFIG_COMPAT_BINFMT_ELF) += compat_binfmt_elf.o > obj-$(CONFIG_BINFMT_ELF_FDPIC) += binfmt_elf_fdpic.o > obj-$(CONFIG_BINFMT_FLAT) += binfmt_flat.o > > +obj-$(CONFIG_FS_BACKING_FILE) += backing_file.o > obj-$(CONFIG_FS_MBCACHE) += mbcache.o > obj-$(CONFIG_FS_POSIX_ACL) += posix_acl.o > obj-$(CONFIG_NFS_COMMON) += nfs_common/ > diff --git a/fs/backing_file.c b/fs/backing_file.c > new file mode 100644 > index 000000000000..ea895ca1639d > --- /dev/null > +++ b/fs/backing_file.c > @@ -0,0 +1,160 @@ > +// SPDX-License-Identifier: GPL-2.0-only > +/* > + * Common helpers for backing file io. > + * Forked from fs/overlayfs/file.c. > + * > + * Copyright (C) 2017 Red Hat, Inc. > + * Copyright (C) 2023 CTERA Networks. > + */ > + > +#include <linux/backing_file.h> > + > +struct backing_aio_req { > + struct kiocb iocb; > + refcount_t ref; > + struct kiocb *orig_iocb; > + void (*cleanup)(struct kiocb *, long); > +}; > + > +static struct kmem_cache *backing_aio_req_cachep; > + > +#define BACKING_IOCB_MASK \ > + (IOCB_NOWAIT | IOCB_HIPRI | IOCB_DSYNC | IOCB_SYNC | IOCB_APPEND) > + > +static rwf_t iocb_to_rw_flags(int flags) > +{ > + return (__force rwf_t)(flags & BACKING_IOCB_MASK); > +} > + > +static void backing_aio_put(struct backing_aio_req *aio_req) > +{ > + if (refcount_dec_and_test(&aio_req->ref)) { > + fput(aio_req->iocb.ki_filp); > + kmem_cache_free(backing_aio_req_cachep, aio_req); > + } > +} > + > +/* Completion for submitted/failed async rw io */ > +static void backing_aio_cleanup(struct backing_aio_req *aio_req, long res) > +{ > + struct kiocb *iocb = &aio_req->iocb; > + struct kiocb *orig_iocb = aio_req->orig_iocb; > + > + if (iocb->ki_flags & IOCB_WRITE) > + kiocb_end_write(iocb); > + > + orig_iocb->ki_pos = iocb->ki_pos; > + if (aio_req->cleanup) > + aio_req->cleanup(orig_iocb, res); > + > + backing_aio_put(aio_req); > +} > + > +/* Completion for submitted async rw io */ > +static void backing_aio_rw_complete(struct kiocb *iocb, long res) > +{ > + struct backing_aio_req *aio_req = container_of(iocb, > + struct backing_aio_req, iocb); > + struct kiocb *orig_iocb = aio_req->orig_iocb; > + > + backing_aio_cleanup(aio_req, res); > + orig_iocb->ki_complete(orig_iocb, res); > +} > + > + > +ssize_t backing_file_read_iter(struct file *file, struct iov_iter *iter, > + struct kiocb *iocb, int flags, > + void (*cleanup)(struct kiocb *, long)) > +{ > + struct backing_aio_req *aio_req = NULL; > + ssize_t ret; > + > + if (!iov_iter_count(iter)) > + return 0; > + > + if (iocb->ki_flags & IOCB_DIRECT && > + !(file->f_mode & FMODE_CAN_ODIRECT)) > + return -EINVAL; > + > + if (is_sync_kiocb(iocb)) { > + rwf_t rwf = iocb_to_rw_flags(flags); > + > + ret = vfs_iter_read(file, iter, &iocb->ki_pos, rwf); > + if (cleanup) > + cleanup(iocb, ret); > + } else { > + aio_req = kmem_cache_zalloc(backing_aio_req_cachep, GFP_KERNEL); > + if (!aio_req) > + return -ENOMEM; > + > + aio_req->orig_iocb = iocb; > + aio_req->cleanup = cleanup; > + kiocb_clone(&aio_req->iocb, iocb, get_file(file)); > + aio_req->iocb.ki_complete = backing_aio_rw_complete; > + refcount_set(&aio_req->ref, 2); > + ret = vfs_iocb_iter_read(file, &aio_req->iocb, iter); > + backing_aio_put(aio_req); > + if (ret != -EIOCBQUEUED) > + backing_aio_cleanup(aio_req, ret); > + } > + > + return ret; > +} > +EXPORT_SYMBOL_GPL(backing_file_read_iter); > + > +ssize_t backing_file_write_iter(struct file *file, struct iov_iter *iter, > + struct kiocb *iocb, int flags, > + void (*cleanup)(struct kiocb *, long)) > +{ > + ssize_t ret; > + > + if (!iov_iter_count(iter)) > + return 0; > + > + if (iocb->ki_flags & IOCB_DIRECT && > + !(file->f_mode & FMODE_CAN_ODIRECT)) > + return -EINVAL; > + > + if (is_sync_kiocb(iocb)) { > + rwf_t rwf = iocb_to_rw_flags(flags); > + > + file_start_write(file); > + ret = vfs_iter_write(file, iter, &iocb->ki_pos, rwf); > + file_end_write(file); > + if (cleanup) > + cleanup(iocb, ret); > + } else { > + struct backing_aio_req *aio_req; > + > + aio_req = kmem_cache_zalloc(backing_aio_req_cachep, GFP_KERNEL); > + if (!aio_req) > + return -ENOMEM; > + > + aio_req->orig_iocb = iocb; > + aio_req->cleanup = cleanup; > + kiocb_clone(&aio_req->iocb, iocb, get_file(file)); > + aio_req->iocb.ki_flags = flags; > + aio_req->iocb.ki_complete = backing_aio_rw_complete; > + refcount_set(&aio_req->ref, 2); > + kiocb_start_write(&aio_req->iocb); > + ret = vfs_iocb_iter_write(file, &aio_req->iocb, iter); > + backing_aio_put(aio_req); > + if (ret != -EIOCBQUEUED) > + backing_aio_cleanup(aio_req, ret); > + } > + > + return ret; > +} > +EXPORT_SYMBOL_GPL(backing_file_write_iter); > + > +static int __init backing_aio_init(void) > +{ > + backing_aio_req_cachep = kmem_cache_create("backing_aio_req", > + sizeof(struct backing_aio_req), > + 0, SLAB_HWCACHE_ALIGN, NULL); > + if (!backing_aio_req_cachep) > + return -ENOMEM; > + > + return 0; > +} > +fs_initcall(backing_aio_init); > diff --git a/fs/overlayfs/Kconfig b/fs/overlayfs/Kconfig > index fec5020c3495..7f52d9031cff 100644 > --- a/fs/overlayfs/Kconfig > +++ b/fs/overlayfs/Kconfig > @@ -1,6 +1,7 @@ > # SPDX-License-Identifier: GPL-2.0-only > config OVERLAY_FS > tristate "Overlay filesystem support" > + select FS_BACKING_FILE > select EXPORTFS > help > An overlay filesystem combines two filesystems - an 'upper' filesystem > diff --git a/fs/overlayfs/file.c b/fs/overlayfs/file.c > index 05ec614f7054..81fe6a85cad9 100644 > --- a/fs/overlayfs/file.c > +++ b/fs/overlayfs/file.c > @@ -13,16 +13,9 @@ > #include <linux/security.h> > #include <linux/mm.h> > #include <linux/fs.h> > +#include <linux/backing_file.h> > #include "overlayfs.h" > > -struct ovl_aio_req { > - struct kiocb iocb; > - refcount_t ref; > - struct kiocb *orig_iocb; > -}; > - > -static struct kmem_cache *ovl_aio_request_cachep; > - > static char ovl_whatisit(struct inode *inode, struct inode *realinode) > { > if (realinode != ovl_inode_upper(inode)) > @@ -262,24 +255,8 @@ static void ovl_file_accessed(struct file *file) > touch_atime(&file->f_path); > } > > -#define OVL_IOCB_MASK \ > - (IOCB_NOWAIT | IOCB_HIPRI | IOCB_DSYNC | IOCB_SYNC | IOCB_APPEND) > - > -static rwf_t iocb_to_rw_flags(int flags) > -{ > - return (__force rwf_t)(flags & OVL_IOCB_MASK); > -} > - > -static inline void ovl_aio_put(struct ovl_aio_req *aio_req) > -{ > - if (refcount_dec_and_test(&aio_req->ref)) { > - fput(aio_req->iocb.ki_filp); > - kmem_cache_free(ovl_aio_request_cachep, aio_req); > - } > -} > - > /* Completion for submitted/failed sync/async rw io */ > -static void ovl_rw_complete(struct kiocb *orig_iocb) > +static void ovl_rw_complete(struct kiocb *orig_iocb, long res) > { > struct file *file = orig_iocb->ki_filp; > > @@ -292,32 +269,6 @@ static void ovl_rw_complete(struct kiocb *orig_iocb) > } > } > > -/* Completion for submitted/failed async rw io */ > -static void ovl_aio_cleanup(struct ovl_aio_req *aio_req) > -{ > - struct kiocb *iocb = &aio_req->iocb; > - struct kiocb *orig_iocb = aio_req->orig_iocb; > - > - if (iocb->ki_flags & IOCB_WRITE) > - kiocb_end_write(iocb); > - > - orig_iocb->ki_pos = iocb->ki_pos; > - ovl_rw_complete(orig_iocb); > - > - ovl_aio_put(aio_req); > -} > - > -/* Completion for submitted async rw io */ > -static void ovl_aio_rw_complete(struct kiocb *iocb, long res) > -{ > - struct ovl_aio_req *aio_req = container_of(iocb, > - struct ovl_aio_req, iocb); > - struct kiocb *orig_iocb = aio_req->orig_iocb; > - > - ovl_aio_cleanup(aio_req); > - orig_iocb->ki_complete(orig_iocb, res); > -} > - > static ssize_t ovl_read_iter(struct kiocb *iocb, struct iov_iter *iter) > { > struct file *file = iocb->ki_filp; > @@ -332,38 +283,10 @@ static ssize_t ovl_read_iter(struct kiocb *iocb, struct iov_iter *iter) > if (ret) > return ret; > > - ret = -EINVAL; > - if (iocb->ki_flags & IOCB_DIRECT && > - !(real.file->f_mode & FMODE_CAN_ODIRECT)) > - goto out_fdput; > - > old_cred = ovl_override_creds(file_inode(file)->i_sb); > - if (is_sync_kiocb(iocb)) { > - rwf_t rwf = iocb_to_rw_flags(iocb->ki_flags); > - > - ret = vfs_iter_read(real.file, iter, &iocb->ki_pos, rwf); > - ovl_rw_complete(iocb); > - } else { > - struct ovl_aio_req *aio_req; > - > - ret = -ENOMEM; > - aio_req = kmem_cache_zalloc(ovl_aio_request_cachep, GFP_KERNEL); > - if (!aio_req) > - goto out; > - > - real.flags = 0; > - aio_req->orig_iocb = iocb; > - kiocb_clone(&aio_req->iocb, iocb, get_file(real.file)); > - aio_req->iocb.ki_complete = ovl_aio_rw_complete; > - refcount_set(&aio_req->ref, 2); > - ret = vfs_iocb_iter_read(real.file, &aio_req->iocb, iter); > - ovl_aio_put(aio_req); > - if (ret != -EIOCBQUEUED) > - ovl_aio_cleanup(aio_req); > - } > -out: > + ret = backing_file_read_iter(real.file, iter, iocb, iocb->ki_flags, > + ovl_rw_complete); > revert_creds(old_cred); > -out_fdput: > fdput(real); > > return ret; > @@ -392,45 +315,13 @@ static ssize_t ovl_write_iter(struct kiocb *iocb, struct iov_iter *iter) > if (ret) > goto out_unlock; > > - ret = -EINVAL; > - if (iocb->ki_flags & IOCB_DIRECT && > - !(real.file->f_mode & FMODE_CAN_ODIRECT)) > - goto out_fdput; > - > if (!ovl_should_sync(OVL_FS(inode->i_sb))) > flags &= ~(IOCB_DSYNC | IOCB_SYNC); > > old_cred = ovl_override_creds(inode->i_sb); > - if (is_sync_kiocb(iocb)) { > - rwf_t rwf = iocb_to_rw_flags(flags); > - > - file_start_write(real.file); > - ret = vfs_iter_write(real.file, iter, &iocb->ki_pos, rwf); > - file_end_write(real.file); > - ovl_rw_complete(iocb); > - } else { > - struct ovl_aio_req *aio_req; > - > - ret = -ENOMEM; > - aio_req = kmem_cache_zalloc(ovl_aio_request_cachep, GFP_KERNEL); > - if (!aio_req) > - goto out; > - > - real.flags = 0; > - aio_req->orig_iocb = iocb; > - kiocb_clone(&aio_req->iocb, iocb, get_file(real.file)); > - aio_req->iocb.ki_flags = flags; > - aio_req->iocb.ki_complete = ovl_aio_rw_complete; > - refcount_set(&aio_req->ref, 2); > - kiocb_start_write(&aio_req->iocb); > - ret = vfs_iocb_iter_write(real.file, &aio_req->iocb, iter); > - ovl_aio_put(aio_req); > - if (ret != -EIOCBQUEUED) > - ovl_aio_cleanup(aio_req); > - } > -out: > + ret = backing_file_write_iter(real.file, iter, iocb, flags, > + ovl_rw_complete); > revert_creds(old_cred); > -out_fdput: > fdput(real); > > out_unlock: > @@ -742,19 +633,3 @@ const struct file_operations ovl_file_operations = { > .copy_file_range = ovl_copy_file_range, > .remap_file_range = ovl_remap_file_range, > }; > - > -int __init ovl_aio_request_cache_init(void) > -{ > - ovl_aio_request_cachep = kmem_cache_create("ovl_aio_req", > - sizeof(struct ovl_aio_req), > - 0, SLAB_HWCACHE_ALIGN, NULL); > - if (!ovl_aio_request_cachep) > - return -ENOMEM; > - > - return 0; > -} > - > -void ovl_aio_request_cache_destroy(void) > -{ > - kmem_cache_destroy(ovl_aio_request_cachep); > -} > diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h > index 9817b2dcb132..64b98e67e826 100644 > --- a/fs/overlayfs/overlayfs.h > +++ b/fs/overlayfs/overlayfs.h > @@ -799,8 +799,6 @@ struct dentry *ovl_create_temp(struct ovl_fs *ofs, struct dentry *workdir, > > /* file.c */ > extern const struct file_operations ovl_file_operations; > -int __init ovl_aio_request_cache_init(void); > -void ovl_aio_request_cache_destroy(void); > int ovl_real_fileattr_get(const struct path *realpath, struct fileattr *fa); > int ovl_real_fileattr_set(const struct path *realpath, struct fileattr *fa); > int ovl_fileattr_get(struct dentry *dentry, struct fileattr *fa); > diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c > index def266b5e2a3..8c132467fca1 100644 > --- a/fs/overlayfs/super.c > +++ b/fs/overlayfs/super.c > @@ -1530,14 +1530,10 @@ static int __init ovl_init(void) > if (ovl_inode_cachep == NULL) > return -ENOMEM; > > - err = ovl_aio_request_cache_init(); > - if (!err) { > - err = register_filesystem(&ovl_fs_type); > - if (!err) > - return 0; > + err = register_filesystem(&ovl_fs_type); > + if (!err) > + return 0; > > - ovl_aio_request_cache_destroy(); > - } > kmem_cache_destroy(ovl_inode_cachep); > > return err; > @@ -1553,7 +1549,6 @@ static void __exit ovl_exit(void) > */ > rcu_barrier(); > kmem_cache_destroy(ovl_inode_cachep); > - ovl_aio_request_cache_destroy(); > } > > module_init(ovl_init); > diff --git a/include/linux/backing_file.h b/include/linux/backing_file.h > new file mode 100644 > index 000000000000..1428fe7b26bb > --- /dev/null > +++ b/include/linux/backing_file.h > @@ -0,0 +1,22 @@ > +/* SPDX-License-Identifier: GPL-2.0-only */ > +/* > + * Common helpers for backing file io. > + * > + * Copyright (C) 2023 CTERA Networks. > + */ > + > +#ifndef _LINUX_BACKING_FILE_H > +#define _LINUX_BACKING_FILE_H > + > +#include <linux/file.h> > +#include <linux/uio.h> > +#include <linux/fs.h> > + > +ssize_t backing_file_read_iter(struct file *file, struct iov_iter *iter, > + struct kiocb *iocb, int flags, > + void (*cleanup)(struct kiocb *, long)); > +ssize_t backing_file_write_iter(struct file *file, struct iov_iter *iter, > + struct kiocb *iocb, int flags, > + void (*cleanup)(struct kiocb *, long)); > + > +#endif /* _LINUX_BACKING_FILE_H */ > -- > 2.34.1 >