Cached dentries do not get revalidate, but open will result in open + getattr, but we want one call only. libfuse logs (passthrough_hp): Unpatched: ---------- unique: 22, opcode: OPEN (14), nodeid: 140698229673544, insize: 48, pid: 3434 unique: 22, success, outsize: 32 unique: 24, opcode: GETATTR (3), nodeid: 140698229673544, insize: 56, pid: 3434 unique: 24, success, outsize: 120 unique: 26, opcode: FLUSH (25), nodeid: 140698229673544, insize: 64, pid: 3434 unique: 26, success, outsize: 16 unique: 28, opcode: RELEASE (18), nodeid: 140698229673544, insize: 64, pid: 0 unique: 28, success, outsize: 16 Patched: ---------- unique: 20, opcode: OPEN_ATOMIC (52), nodeid: 1, insize: 63, pid: 3397 unique: 20, success, outsize: 160 unique: 22, opcode: FLUSH (25), nodeid: 140024188243528, insize: 64, pid: 3397 unique: 22, success, outsize: 16 unique: 24, opcode: RELEASE (18), nodeid: 140024188243528, insize: 64, pid: 0 unique: 24, success, outsize: 16 Signed-off-by: Bernd Schubert <bschubert@xxxxxxx> Cc: Miklos Szeredi <miklos@xxxxxxxxxx> Cc: Dharmendra Singh <dsingh@xxxxxxx> Cc: Horst Birthelmer <hbirthelmer@xxxxxxx> Cc: linux-fsdevel@xxxxxxxxxxxxxxx --- fs/fuse/dir.c | 58 ++++++++++++++++++++++++++++++++++----------------- 1 file changed, 39 insertions(+), 19 deletions(-) diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index d872453a6cd0..067e1a2fb23a 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -193,6 +193,25 @@ static void fuse_lookup_init(struct fuse_conn *fc, struct fuse_args *args, args->out_args[0].value = outarg; } +/* + * If open atomic is supported by FUSE then use this opportunity + * to avoid this lookup and combine lookup + open into a single call. + */ +static int fuse_dentry_do_atomic_revalidate(struct dentry *entry, + unsigned int flags, + struct fuse_conn *fc) +{ + int ret = 0; + if (flags & LOOKUP_OPEN && flags & LOOKUP_ATOMIC_REVALIDATE && + fc->has_open_atomic) { + spin_lock(&entry->d_lock); + entry->d_flags |= DCACHE_ATOMIC_OPEN; + ret = 1; + spin_unlock(&entry->d_lock); + } + return ret; +} + /* * Check whether the dentry is still valid * @@ -230,24 +249,10 @@ static int fuse_dentry_revalidate(struct dentry *entry, unsigned int flags) fm = get_fuse_mount(inode); - /* If open atomic is supported by FUSE then use this opportunity - * to avoid this lookup and combine lookup + open into a single call. - * - * Note: Fuse detects open atomic implementation automatically. - * Therefore first few call would go into open atomic code path - * , detects that open atomic is implemented or not by setting - * fc->no_open_atomic. In case open atomic is not implemented, - * calls fall back to non-atomic open. - */ - if (fm->fc->has_open_atomic && flags & LOOKUP_OPEN && - flags & LOOKUP_ATOMIC_REVALIDATE) { - spin_lock(&entry->d_lock); - entry->d_flags |= DCACHE_ATOMIC_OPEN; - spin_unlock(&entry->d_lock); - - ret = 1; + ret = fuse_dentry_do_atomic_revalidate(entry, flags, fm->fc); + if (ret) goto out; - } + forget = fuse_alloc_forget(); ret = -ENOMEM; if (!forget) @@ -290,6 +295,16 @@ static int fuse_dentry_revalidate(struct dentry *entry, unsigned int flags) } else if (inode) { fi = get_fuse_inode(inode); if (flags & LOOKUP_RCU) { + fm = get_fuse_mount(inode); + if (fm->fc->has_open_atomic) { + /* Atomic open is preferred, as it does entry + * revalidate and attribute refresh, but + * DCACHE_ATOMIC_OPEN cannot be set in RCU mode + */ + if (flags & LOOKUP_OPEN) + return -ECHILD; + } + if (test_bit(FUSE_I_INIT_RDPLUS, &fi->state)) return -ECHILD; } else if (test_and_clear_bit(FUSE_I_INIT_RDPLUS, &fi->state)) { @@ -297,6 +312,12 @@ static int fuse_dentry_revalidate(struct dentry *entry, unsigned int flags) fuse_advise_use_readdirplus(d_inode(parent)); dput(parent); } + + /* revalidate is skipped, but we still want atomic open to + * update attributes during open + */ + fm = get_fuse_mount(inode); + fuse_dentry_do_atomic_revalidate(entry, flags, fm->fc); } ret = 1; out: @@ -943,11 +964,10 @@ static int _fuse_atomic_open(struct inode *dir, struct dentry *entry, * return -ENOSYS for OPEN_ATOMIC after it was * aready working */ - if (unlikely(fc->has_open_atomic == 1)) { + if (unlikely(fc->has_open_atomic == 1)) pr_info("fuse server/daemon bug, atomic open " "got -ENOSYS although it was already " "succeeding before."); - } /* This should better never happen, revalidate * is missing for this entry*/ -- 2.34.1