From: Amir Goldstein In preparation for readdir passthrough, allow the inode iomode state to be applicable to directory inodes and prepare the helper fuse_sync_release() for directories. Directory inodes will support cached mode, "direct" uncached readdir mode and readdir passthrough mode, but will not need to wait for parallel dio like regular files. Reviewed-by: Joanne Koong Signed-off-by: Amir Goldstein --- fs/fuse/cuse.c | 2 +- fs/fuse/dir.c | 4 ++-- fs/fuse/file.c | 11 ++++++----- fs/fuse/fuse_i.h | 15 +++++++++------ fs/fuse/inode.c | 2 +- fs/fuse/iomode.c | 31 ++++++++++++++++++------------- 6 files changed, 37 insertions(+), 28 deletions(-) diff --git a/fs/fuse/cuse.c b/fs/fuse/cuse.c index bac327cfc7f1..9c4d7a1993e8 100644 --- a/fs/fuse/cuse.c +++ b/fs/fuse/cuse.c @@ -148,7 +148,7 @@ static int cuse_release(struct inode *inode, struct file *file) struct fuse_file *ff = file->private_data; struct fuse_mount *fm = ff->fm; - fuse_sync_release(NULL, ff, file->f_flags); + fuse_sync_release(NULL, ff, file->f_flags, false); fuse_conn_put(fm->fc); return 0; diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index be41c14ef329..937165788e79 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -893,7 +893,7 @@ static int fuse_create_open(struct mnt_idmap *idmap, struct inode *dir, &outentry.attr, ATTR_TIMEOUT(&outentry), 0, 0); if (!inode) { flags &= ~(O_CREAT | O_EXCL | O_TRUNC); - fuse_sync_release(NULL, ff, flags); + fuse_sync_release(NULL, ff, flags, false); fuse_chan_queue_forget(fm->fc->chan, forget, outentry.nodeid, 1); err = -ENOMEM; goto out_err; @@ -910,7 +910,7 @@ static int fuse_create_open(struct mnt_idmap *idmap, struct inode *dir, } if (err) { fi = get_fuse_inode(inode); - fuse_sync_release(fi, ff, flags); + fuse_sync_release(fi, ff, flags, false); } else { if (fm->fc->atomic_o_trunc && trunc) truncate_pagecache(inode, 0); diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 06dc8cfe3c7b..332032cd5622 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -285,7 +285,7 @@ static int fuse_open(struct inode *inode, struct file *file) ff = file->private_data; err = fuse_finish_open(inode, file); if (err) - fuse_sync_release(fi, ff, file->f_flags); + fuse_sync_release(fi, ff, file->f_flags, false); else if (is_truncate) fuse_truncate_update_attr(inode, file); } @@ -408,10 +408,12 @@ static int fuse_release(struct inode *inode, struct file *file) } void fuse_sync_release(struct fuse_inode *fi, struct fuse_file *ff, - unsigned int flags) + unsigned int flags, bool isdir) { + int opcode = isdir ? FUSE_RELEASEDIR : FUSE_RELEASE; + WARN_ON(refcount_read(&ff->count) > 1); - fuse_prepare_release(fi, ff, flags, FUSE_RELEASE, true); + fuse_prepare_release(fi, ff, flags, opcode, true); fuse_file_put(ff, true); } EXPORT_SYMBOL_GPL(fuse_sync_release); @@ -1454,13 +1456,12 @@ static void fuse_dio_lock(struct kiocb *iocb, struct iov_iter *from, static void fuse_dio_unlock(struct kiocb *iocb, bool exclusive) { struct inode *inode = file_inode(iocb->ki_filp); - struct fuse_inode *fi = get_fuse_inode(inode); if (exclusive) { inode_unlock(inode); } else { /* Allow opens in caching mode after last parallel dio end */ - fuse_inode_uncached_io_end(fi); + fuse_inode_uncached_io_end(inode); inode_unlock_shared(inode); } } diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 8e54dc7d56af..2436219338b3 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -160,9 +160,6 @@ struct fuse_inode { */ int writectr; - /** @iocachectr: Number of files/maps using page cache */ - int iocachectr; - /** @page_waitq: Waitq for writepage completion */ wait_queue_head_t page_waitq; @@ -216,6 +213,12 @@ struct fuse_inode { /** @lock: Lock to protect write-related fields */ spinlock_t lock; + /** + * @iocachectr: Number of files/maps using page cache (negative for + * passthrough) + */ + int iocachectr; + #ifdef CONFIG_FUSE_DAX /** * @dax: Dax specific inode data @@ -248,7 +251,7 @@ enum { FUSE_I_BAD, /* Has btime */ FUSE_I_BTIME, - /* Wants or already has page cache IO */ + /* Regular file wants or already has page cache IO */ FUSE_I_CACHE_IO_MODE, /* * Client has exclusive access to the inode, either because fs is local @@ -946,7 +949,7 @@ void fuse_file_free(struct fuse_file *ff); int fuse_finish_open(struct inode *inode, struct file *file); void fuse_sync_release(struct fuse_inode *fi, struct fuse_file *ff, - unsigned int flags); + unsigned int flags, bool isdir); /* * Send RELEASE or RELEASEDIR request @@ -1244,7 +1247,7 @@ int fuse_fileattr_set(struct mnt_idmap *idmap, /* iomode.c */ int fuse_file_cached_io_open(struct inode *inode, struct fuse_file *ff); int fuse_inode_uncached_io_start(struct inode *inode, struct fuse_backing *fb); -void fuse_inode_uncached_io_end(struct fuse_inode *fi); +void fuse_inode_uncached_io_end(struct inode *inode); int fuse_file_io_open(struct file *file, struct inode *inode); void fuse_file_io_release(struct fuse_file *ff, struct inode *inode); diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index b2a5892a4dc3..cef8b853b3d8 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -180,10 +180,10 @@ static void fuse_evict_inode(struct inode *inode) atomic64_inc(&fc->evict_ctr); } if (S_ISREG(inode->i_mode) && !fuse_is_bad(inode)) { - WARN_ON(fi->iocachectr != 0); WARN_ON(!list_empty(&fi->write_files)); WARN_ON(!list_empty(&fi->queued_writes)); } + WARN_ON(fi->iocachectr != 0); } static int fuse_reconfigure(struct fs_context *fsc) diff --git a/fs/fuse/iomode.c b/fs/fuse/iomode.c index 5517711a3eca..e2e7fbaac9af 100644 --- a/fs/fuse/iomode.c +++ b/fs/fuse/iomode.c @@ -15,9 +15,12 @@ /* * Return true if need to wait for new opens in caching mode. */ -static inline bool fuse_is_io_cache_wait(struct fuse_inode *fi) +static inline bool fuse_is_io_cache_wait(struct inode *inode) { - return READ_ONCE(fi->iocachectr) < 0 && !fuse_inode_backing(fi); + struct fuse_inode *fi = get_fuse_inode(inode); + + return S_ISREG(inode->i_mode) && + READ_ONCE(fi->iocachectr) < 0 && !fuse_inode_backing(fi); } /* @@ -40,10 +43,10 @@ int fuse_file_cached_io_open(struct inode *inode, struct fuse_file *ff) * Setting the bit advises new direct-io writes to use an exclusive * lock - without it the wait below might be forever. */ - while (fuse_is_io_cache_wait(fi)) { + while (fuse_is_io_cache_wait(inode)) { set_bit(FUSE_I_CACHE_IO_MODE, &fi->state); spin_unlock(&fi->lock); - wait_event(fi->direct_io_waitq, !fuse_is_io_cache_wait(fi)); + wait_event(fi->direct_io_waitq, !fuse_is_io_cache_wait(inode)); spin_lock(&fi->lock); } @@ -69,8 +72,10 @@ int fuse_file_cached_io_open(struct inode *inode, struct fuse_file *ff) } static void fuse_file_cached_io_release(struct fuse_file *ff, - struct fuse_inode *fi) + struct inode *inode) { + struct fuse_inode *fi = get_fuse_inode(inode); + spin_lock(&fi->lock); WARN_ON(fi->iocachectr <= 0); WARN_ON(ff->iomode != IOM_CACHED); @@ -141,15 +146,17 @@ static int fuse_file_uncached_io_open(struct inode *inode, return 0; } -void fuse_inode_uncached_io_end(struct fuse_inode *fi) +void fuse_inode_uncached_io_end(struct inode *inode) { + struct fuse_inode *fi = get_fuse_inode(inode); struct fuse_backing *oldfb = NULL; spin_lock(&fi->lock); WARN_ON(fi->iocachectr >= 0); fi->iocachectr++; if (!fi->iocachectr) { - wake_up(&fi->direct_io_waitq); + if (S_ISREG(inode->i_mode)) + wake_up(&fi->direct_io_waitq); oldfb = fuse_inode_backing_set(fi, NULL); } spin_unlock(&fi->lock); @@ -159,11 +166,11 @@ void fuse_inode_uncached_io_end(struct fuse_inode *fi) /* Drop uncached_io reference from passthrough open */ static void fuse_file_uncached_io_release(struct fuse_file *ff, - struct fuse_inode *fi) + struct inode *inode) { WARN_ON(ff->iomode != IOM_UNCACHED); ff->iomode = IOM_NONE; - fuse_inode_uncached_io_end(fi); + fuse_inode_uncached_io_end(inode); } /* @@ -278,8 +285,6 @@ int fuse_file_io_open(struct file *file, struct inode *inode) /* No more pending io and no new io possible to inode via open/mmapped file */ void fuse_file_io_release(struct fuse_file *ff, struct inode *inode) { - struct fuse_inode *fi = get_fuse_inode(inode); - /* * Last passthrough file close allows caching inode io mode. * Last caching file close exits caching inode io mode. @@ -289,10 +294,10 @@ void fuse_file_io_release(struct fuse_file *ff, struct inode *inode) /* Nothing to do */ break; case IOM_UNCACHED: - fuse_file_uncached_io_release(ff, fi); + fuse_file_uncached_io_release(ff, inode); break; case IOM_CACHED: - fuse_file_cached_io_release(ff, fi); + fuse_file_cached_io_release(ff, inode); break; } } -- 2.52.0