From: Miklos Szeredi <miklos@xxxxxxxxxx> atomic_open() will do an open-by-name or create-and-open depending on the flags. If file was created, then the old positive dentry is obviously stale, so it will be invalidated and a new one will be allocated. If not created, then check whether it's the same inode (same as in ->d_revalidate()) and if not, invalidate & allocate new dentry. Changes from Miklos initial patch (by Bernd): - LOOKUP_ATOMIC_REVALIDATE was added and is set for revalidate calls into the file system when revalidate by atomic open is supported - this is to avoid that ->d_revalidate() would skip revalidate and set DCACHE_ATOMIC_OPEN, although vfs does not supported it in the given code path (for example when LOOKUP_RCU is set)). - Support atomic-open-revalidate in lookup_fast() - allow atomic open for positive dentries without O_CREAT being set. Signed-off-by: Miklos Szeredi <miklos@xxxxxxxxxx> Signed-off-by: Bernd Schubert <bschubert@xxxxxxx> Cc: Christian Brauner <brauner@xxxxxxxxxx> Cc: Al Viro <viro@xxxxxxxxxxxxxxxxxx> Cc: Dharmendra Singh <dsingh@xxxxxxx> Cc: linux-fsdevel@xxxxxxxxxxxxxxx --- fs/fuse/dir.c | 5 ++--- fs/namei.c | 17 +++++++++++++---- include/linux/dcache.h | 6 ++++++ include/linux/namei.h | 1 + 4 files changed, 22 insertions(+), 7 deletions(-) diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index c02b63fe91ca..8ccd49d63235 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -380,7 +380,6 @@ int fuse_lookup_name(struct super_block *sb, u64 nodeid, const struct qstr *name if (name->len > FUSE_NAME_MAX) goto out; - forget = fuse_alloc_forget(); err = -ENOMEM; if (!forget) @@ -771,8 +770,8 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry, } static int _fuse_atomic_open(struct inode *dir, struct dentry *entry, - struct file *file, unsigned flags, - umode_t mode) + struct file *file, unsigned flags, + umode_t mode) { int err; diff --git a/fs/namei.c b/fs/namei.c index e4fe0879ae55..5dae1b1afd0e 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1643,12 +1643,14 @@ static struct dentry *lookup_fast(struct nameidata *nd) return ERR_PTR(-ECHILD); if (status == -ECHILD) /* we'd been told to redo it in non-rcu mode */ - status = d_revalidate(dentry, nd->flags); + status = d_revalidate(dentry, + nd->flags | LOOKUP_ATOMIC_REVALIDATE); } else { dentry = __d_lookup(parent, &nd->last); if (unlikely(!dentry)) return NULL; - status = d_revalidate(dentry, nd->flags); + status = d_revalidate(dentry, + nd->flags | LOOKUP_ATOMIC_REVALIDATE); } if (unlikely(status <= 0)) { if (!status) @@ -1656,6 +1658,12 @@ static struct dentry *lookup_fast(struct nameidata *nd) dput(dentry); return ERR_PTR(status); } + + if (unlikely(d_atomic_open(dentry))) { + dput(dentry); + return NULL; + } + return dentry; } @@ -3421,7 +3429,8 @@ static struct dentry *lookup_open(struct nameidata *nd, struct file *file, if (d_in_lookup(dentry)) break; - error = d_revalidate(dentry, nd->flags); + error = d_revalidate(dentry, + nd->flags | LOOKUP_ATOMIC_REVALIDATE); if (likely(error > 0)) break; if (error) @@ -3430,7 +3439,7 @@ static struct dentry *lookup_open(struct nameidata *nd, struct file *file, dput(dentry); dentry = NULL; } - if (dentry->d_inode) { + if (dentry->d_inode && !d_atomic_open(dentry)) { /* Cached positive dentry: will open in f_op->open */ return dentry; } diff --git a/include/linux/dcache.h b/include/linux/dcache.h index 6b351e009f59..f90eec22691c 100644 --- a/include/linux/dcache.h +++ b/include/linux/dcache.h @@ -208,6 +208,7 @@ struct dentry_operations { #define DCACHE_FALLTHRU 0x01000000 /* Fall through to lower layer */ #define DCACHE_NOKEY_NAME 0x02000000 /* Encrypted name encoded without key */ #define DCACHE_OP_REAL 0x04000000 +#define DCACHE_ATOMIC_OPEN 0x08000000 /* Always use ->atomic_open() to open this file */ #define DCACHE_PAR_LOOKUP 0x10000000 /* being looked up (with parent locked shared) */ #define DCACHE_DENTRY_CURSOR 0x20000000 @@ -496,6 +497,11 @@ static inline bool d_is_fallthru(const struct dentry *dentry) return dentry->d_flags & DCACHE_FALLTHRU; } +static inline bool d_atomic_open(const struct dentry *dentry) +{ + return dentry->d_flags & DCACHE_ATOMIC_OPEN; +} + extern int sysctl_vfs_cache_pressure; diff --git a/include/linux/namei.h b/include/linux/namei.h index 1463cbda4888..7eec6c06b192 100644 --- a/include/linux/namei.h +++ b/include/linux/namei.h @@ -33,6 +33,7 @@ enum {LAST_NORM, LAST_ROOT, LAST_DOT, LAST_DOTDOT}; #define LOOKUP_CREATE 0x0200 /* ... in object creation */ #define LOOKUP_EXCL 0x0400 /* ... in exclusive creation */ #define LOOKUP_RENAME_TARGET 0x0800 /* ... in destination of rename() */ +#define LOOKUP_ATOMIC_REVALIDATE 0x1000 /* atomic revalidate possible */ /* internal use only */ #define LOOKUP_PARENT 0x0010 -- 2.34.1