* [PATCH 4/6] fuse: Revalidate positive entries in fuse_atomic_open
2023-08-11 18:37 [PATCH v7 0/6] fuse: full atomic open and atomic-open-revalidate Bernd Schubert
@ 2023-08-11 18:37 ` Bernd Schubert
0 siblings, 0 replies; 10+ messages in thread
From: Bernd Schubert @ 2023-08-11 18:37 UTC (permalink / raw)
To: linux-fsdevel
Cc: bernd.schubert, fuse-devel, Bernd Schubert, Dharmendra Singh,
Horst Birthelmer, Miklos Szeredi, Christian Brauner, Al Viro
From: Dharmendra Singh <dsingh@ddn.com>
This makes use of the vfs changes and fuse_dentry_revalidate()
can now skip revalidate, if the fuse implementation has
atomic_open support, which will has to do the dentry
revalidation.
Skipping revalidate is only possible when we absolutely
know that the implementation supports atomic_open, so
another bit had to be added to struct fuse_conn, which
is set when atomic_open was successful.
Once struct fuse_conn has the positive 'has_open_atomic'
fuse_dentry_revalidate() might set DCACHE_ATOMIC_OPEN.
vfs use that flag to use atomic_open.
If the file was newly created, the previous positive dentry
is invalidated and a new dentry and inode are allocated
and linked (d_splice_alias).
If file was not created, we revalidate the inode. If inode is
stale, current inode is marked as bad. And new inode is allocated
and linked to new dentry(old dentry invalidated). In case of
inode attributes differing with fresh attr, we allocate new
dentry and hook current inode to it and open the file.
For negative dentry, FS just allocate new inode and hook it onto
passed entry from VFS and open the file.
Co-developed-by: Bernd Schubert <bschubert@ddn.com>
Signed-off-by: Dharmendra Singh <dsingh@ddn.com>
Signed-off-by: Horst Birthelmer <hbirthelmer@ddn.com>
Signed-off-by: Bernd Schubert <bschubert@ddn.com>
Cc: Miklos Szeredi <miklos@szeredi.hu>
Cc: Christian Brauner <brauner@kernel.org>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Dharmendra Singh <dsingh@ddn.com>
Cc: linux-fsdevel@vger.kernel.org
---
fs/fuse/dir.c | 207 ++++++++++++++++++++++++++++++++++++++++-------
fs/fuse/fuse_i.h | 3 +
2 files changed, 182 insertions(+), 28 deletions(-)
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
index 8ccd49d63235..d872453a6cd0 100644
--- a/fs/fuse/dir.c
+++ b/fs/fuse/dir.c
@@ -230,6 +230,24 @@ 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;
+ goto out;
+ }
forget = fuse_alloc_forget();
ret = -ENOMEM;
if (!forget)
@@ -769,13 +787,87 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry,
return finish_no_open(file, res);
}
+/**
+ * Revalidate inode hooked into dentry against freshly acquired
+ * attributes. If inode is stale then allocate new dentry and
+ * hook it onto fresh inode.
+ */
+static struct dentry *
+fuse_atomic_open_revalidate(struct fuse_conn *fc, struct dentry *entry,
+ struct inode *inode, int switched,
+ struct fuse_entry_out *outentry,
+ wait_queue_head_t *wq, int *alloc_inode)
+{
+ u64 attr_version;
+ struct dentry *prev = entry;
+
+ WARN_ON(*alloc_inode != 0);
+
+ if (outentry->nodeid != get_node_id(inode) ||
+ (bool) IS_AUTOMOUNT(inode) !=
+ (bool) (outentry->attr.flags & FUSE_ATTR_SUBMOUNT)) {
+ *alloc_inode = 1;
+ }
+ else if (fuse_stale_inode(inode, outentry->generation,
+ &outentry->attr)) {
+ fuse_make_bad(inode);
+ *alloc_inode = 1;
+ }
+
+ if (*alloc_inode) {
+ struct dentry *new = NULL;
+
+ if (!switched && !d_in_lookup(entry)) {
+ d_drop(entry);
+ new = d_alloc_parallel(entry->d_parent, &entry->d_name,
+ wq);
+ if (IS_ERR(new))
+ return new;
+
+ if (unlikely(!d_in_lookup(new))) {
+ dput(new);
+ new = ERR_PTR(-EIO);
+ return new;
+ }
+ }
+
+ fuse_invalidate_entry(entry);
+
+ entry = new;
+ } else if (!*alloc_inode) {
+ attr_version = fuse_get_attr_version(fc);
+ forget_all_cached_acls(inode);
+ fuse_change_attributes(inode, &outentry->attr,
+ entry_attr_timeout(outentry),
+ attr_version);
+ }
+
+ if (prev == entry) {
+ /* nothing changed, atomic-open on the server side
+ * had increased the lookup count - do the same here
+ */
+ struct fuse_inode *fi = get_fuse_inode(inode);
+ spin_lock(&fi->lock);
+ fi->nlookup++;
+ spin_unlock(&fi->lock);
+ }
+
+ return entry;
+}
+
+/**
+ * Does 'lookup + create + open' or 'lookup + open' atomically.
+ * @entry might be positive as well, therefore inode is re-validated.
+ * Positive dentry is invalidated in case inode attributes differ or
+ * we encountered error.
+ */
static int _fuse_atomic_open(struct inode *dir, struct dentry *entry,
struct file *file, unsigned flags,
umode_t mode)
{
int err;
- struct inode *inode;
+ struct inode *inode = d_inode(entry);
struct fuse_mount *fm = get_fuse_mount(dir);
struct fuse_conn *fc = fm->fc;
FUSE_ARGS(args);
@@ -787,10 +879,7 @@ static int _fuse_atomic_open(struct inode *dir, struct dentry *entry,
struct fuse_file *ff;
struct dentry *switched_entry = NULL, *alias = NULL;
DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
-
- /* Expect a negative dentry */
- if (unlikely(d_inode(entry)))
- goto fallback;
+ int alloc_inode = 0;
/* Userspace expects S_IFREG in create mode */
if ((flags & O_CREAT) && (mode & S_IFMT) != S_IFREG)
@@ -842,37 +931,57 @@ static int _fuse_atomic_open(struct inode *dir, struct dentry *entry,
err = fuse_simple_request(fm, &args);
free_ext_value(&args);
- if (err == -ENOSYS) {
- fc->no_open_atomic = 1;
- fuse_file_free(ff);
- kfree(forget);
- goto fallback;
- }
if (!err && !outentry.nodeid)
err = -ENOENT;
- if (err)
- goto out_free_ff;
+ if (err) {
+ if (err == -ENOSYS) {
+ fc->no_open_atomic = 1;
+
+ /* Might come up if userspace tricks us and would
+ * return -ENOSYS for OPEN_ATOMIC after it was
+ * aready working
+ */
+ 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*/
+ if (d_really_is_positive(entry)) {
+ WARN_ON(1);
+ err = -EIO;
+ goto out_free_ff;
+ }
+
+ fuse_file_free(ff);
+ kfree(forget);
+ goto fallback;
+ } else {
+ if (d_really_is_positive(entry)) {
+ if (err != -EINTR && err != -ENOMEM)
+ fuse_invalidate_entry(entry);
+ }
+
+ goto out_free_ff;
+ }
+ }
+
+ if (!err && !fc->has_open_atomic) {
+ /* Only set this flag when atomic open did not return an error,
+ * so that we are absolutely sure it is implemented.
+ */
+ fc->has_open_atomic = 1;
+ }
err = -EIO;
if (invalid_nodeid(outentry.nodeid) || fuse_invalid_attr(&outentry.attr))
goto out_free_ff;
- ff->fh = outopen.fh;
- ff->nodeid = outentry.nodeid;
- ff->open_flags = outopen.open_flags;
- inode = fuse_iget(dir->i_sb, outentry.nodeid, outentry.generation,
- &outentry.attr, entry_attr_timeout(&outentry), 0);
- if (!inode) {
- flags &= ~(O_CREAT | O_EXCL | O_TRUNC);
- fuse_sync_release(NULL, ff, flags);
- fuse_queue_forget(fm->fc, forget, outentry.nodeid, 1);
- err = -ENOMEM;
- goto out_err;
- }
-
- /* prevent racing/parallel lookup on a negative hashed */
+ /* prevent racing/parallel lookup */
if (!(flags & O_CREAT) && !d_in_lookup(entry)) {
d_drop(entry);
switched_entry = d_alloc_parallel(entry->d_parent,
@@ -886,10 +995,52 @@ static int _fuse_atomic_open(struct inode *dir, struct dentry *entry,
/* fall back */
dput(switched_entry);
switched_entry = NULL;
- goto free_and_fallback;
+
+ if (!inode) {
+ goto free_and_fallback;
+ } else {
+ /* XXX can this happen at all and is there a
+ * better way to handle it?
+ */
+ err = PTR_ERR(new);
+ goto out_free_ff;
+ }
+ }
+ }
+
+ if (inode) {
+ struct dentry *new;
+
+ err = -ESTALE;
+ new = fuse_atomic_open_revalidate(fm->fc, entry, inode,
+ !!switched_entry,
+ &outentry, &wq, &alloc_inode);
+ if (IS_ERR(new)) {
+ err = PTR_ERR(new);
+ goto out_free_ff;
}
+ if (new != entry && new != NULL)
+ switched_entry = new;
+ }
+
+ if (switched_entry)
entry = switched_entry;
+
+ ff->fh = outopen.fh;
+ ff->nodeid = outentry.nodeid;
+ ff->open_flags = outopen.open_flags;
+
+ if (!inode || alloc_inode) {
+ inode = fuse_iget(dir->i_sb, outentry.nodeid, outentry.generation,
+ &outentry.attr, entry_attr_timeout(&outentry), 0);
+ if (!inode) {
+ flags &= ~(O_CREAT | O_EXCL | O_TRUNC);
+ fuse_sync_release(NULL, ff, flags);
+ fuse_queue_forget(fm->fc, forget, outentry.nodeid, 1);
+ err = -ENOMEM;
+ goto out_err;
+ }
}
/* modified version of _nfs4_open_and_get_state - nfs does not open
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index 4e2ebcc28912..c0da4fce1a9c 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -675,6 +675,9 @@ struct fuse_conn {
/** Is open atomic not impelmented by fs? */
unsigned no_open_atomic:1;
+ /** Is open atomic is proven to be implemented by fs? */
+ unsigned has_open_atomic;
+
/** Is opendir/releasedir not implemented by fs? */
unsigned no_opendir:1;
--
2.34.1
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH v8 0/6] fuse: full atomic open and atomic-open-revalidate
@ 2023-08-16 14:33 Bernd Schubert
2023-08-16 14:33 ` [PATCH 1/6] fuse: rename fuse_create_open Bernd Schubert
` (5 more replies)
0 siblings, 6 replies; 10+ messages in thread
From: Bernd Schubert @ 2023-08-16 14:33 UTC (permalink / raw)
To: linux-fsdevel
Cc: bernd.schubert, fuse-devel, Bernd Schubert, Miklos Szeredi,
Vivek Goyal, Christian Brauner, Al Viro, Dharmendra Singh,
Horst Birthelmer
In FUSE, as of now, uncached lookups are expensive over the wire.
E.g additional latencies and stressing (meta data) servers from
thousands of clients. With atomic-open lookup before open
can be avoided.
Here is the link to performance numbers
https://lore.kernel.org/linux-fsdevel/20220322121212.5087-1-dharamhans87@gmail.com/
Here is the libfuse pull request
https://github.com/libfuse/libfuse/pull/813
The patches are passing passthrough_hp xfstests (libfuse part applied),
although we had to introduce umount retries into xfstests, as recent
kernels/xfstests fail umount in some tests with
EBUSY - independent of atomic open. (Although outstanding for v7)
I'm especially interested in Al's and Christians opinion about the
atomic open dentry revalidation in v7. If the vfs changes are
acceptable, would it be possible to also look at the other patches
and their vfs/dcache interaction? I __hope__ I got it right and I hope
the vfs can be accepted.
v8: - Another slight indentation fix in _fuse_atomic_open
- Fix compilation error in patch 4 (fuse atomic revalidate)
- Remove LOOKUP_ATOMIC_REVALIDATE
- Switch from DCACHE_ATOMIC_OPEN flag to return value and
and introduce an enum for d_revalidate return values.
- checkpatch fixes
v7: - Indentation and style fixes for _fuse_atomic_open.
- Remodel atomic open to avoid races with parallel lookup, similar
to NFS commit c94c09535c4debcc439f55b5b6d9ebe57bd4665a and what
is done in _nfs4_open_and_get_state()
A WARN_ONCE() and fallback is added to ensure operation is on
negative dentries only.
- Error handling is done via the fallback fuse_create_open()
to reduce complexity and code duplication.
- Remove entry cache invalidation on ENOENT in the atomic-open
patch, as atomic-open so far operates on negative dentries only.
- Remove fuse_advise_use_readdirplus() in _fuse_atomic_open
(Thanks Miklos)
- Add forgotten free_ext_value() (Thanks Miklos).
- Declare struct fuse_inode per condition as the value needs to
be retrieved anyway per condition.
- Added atomic open-revalidation and required vfs changes
- Added myself (Bernd) as Co-developed-by to Dharmendras patches, as
I did substantial modifications.
- More as reminder for myself, so far these tests below are
done manually or with custom scripts, I think we need xfstests
for these.
With updated libfuse /scratch/dest is mounted by:
passthrough_hp -o allow_other --foreground --debug-fuse /scratch/source /scratch/dest
1) Test atomic open (file create) and negative dentry open
rm -f /scratch/source/file # ensure file does not exist
mount /scratch/dest # overlay of /scratch source
echo a > /scratch/dest/file # non-existing file
umount and mount /scratch/test (clear cache)
cat /scratch/dest/file
rm -f /scratch/dest/file
2) Test dir open
mkdir /scratch/source/dir
mount /scratch/dest # overlay of /scratch source
cat /scratch/source/dir
rmdir /scratch/source/dir
3) Test revalidate without file change
mount /scratch/dest
echo "a" > /scratch/dest/file
echo "b" >> /scratch/dest/file
echo "c" >> /scratch/dest/file
cat /scratch/dest/file
rm -f /scratch/dest/file
4) Test revalidate by underlying file change
mount /scratch/dest
echo "a" > /scratch/dest/file
cat /scratch/dest/file
rm -f /scratch/source/file # unknown to dest mount
str="b"
echo "${str}" > /scratch/source/file
reval=$(cat /scratch/dest/file)
if [ "$str" != "reval" ]; then
echo "String mismatch after revalidation"
exit 1
fi
rm -f /scratch/dest/file
5) Test revalidate by underlying file change, but with
O_CREATE included. Tests dentry creation by the atomic
revalidate
mount /scratch/dest
echo "a" >> /scratch/dest/file
rm -f /scratch/source/file
echo "b" > /scratch/source/file
# revalidate includes O_CREATE
echo "c" >> /scratch/dest/file
6) Repeat above tests, but with additional "--nocache"
passthrough_hp option
v6: Addressed Miklos comments and rewrote atomic open into its own
function. Also dropped for now is the revalidate optimization, we
have the code/patch, but it needs more testing. Also easier to
first agree on atomic open and then to land the next optimization.
v5: Addressed comments
v3: Addressed comments
v4: Addressed all comments and refactored the code into 3 separate patches
respectively for Atomic create, Atomic open, optimizing lookup in
d_revalidate().
v3: handle review comments
v2: fixed a memory leak in <fuse_atomic_open_common>
Bernd Schubert (5):
fuse: rename fuse_create_open
fuse: introduce atomic open
fuse: Revalidate positive entries in fuse_atomic_open
fuse: revalidate Set DCACHE_ATOMIC_OPEN for cached dentries
fuse: Avoid code duplication in atomic open
Miklos Szeredi (1):
[RFC] Allow atomic_open() on positive dentry
fs/fuse/dir.c | 398 +++++++++++++++++++++++++++++++++++++-
fs/fuse/fuse_i.h | 6 +
fs/namei.c | 17 +-
include/linux/dcache.h | 6 +
include/linux/namei.h | 1 +
include/uapi/linux/fuse.h | 3 +
6 files changed, 422 insertions(+), 9 deletions(-)
Cc: Miklos Szeredi <miklos@szeredi.hu>
Cc: Vivek Goyal <vgoyal@redhat.com>
Cc: Christian Brauner <brauner@kernel.org>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Dharmendra Singh <dsingh@ddn.com>
Cc: Horst Birthelmer <hbirthelmer@ddn.com>
Cc: linux-fsdevel@vger.kernel.org
--
2.34.1
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH 1/6] fuse: rename fuse_create_open
2023-08-16 14:33 [PATCH v8 0/6] fuse: full atomic open and atomic-open-revalidate Bernd Schubert
@ 2023-08-16 14:33 ` Bernd Schubert
2023-08-16 14:33 ` [PATCH 2/6] fuse: introduce atomic open Bernd Schubert
` (4 subsequent siblings)
5 siblings, 0 replies; 10+ messages in thread
From: Bernd Schubert @ 2023-08-16 14:33 UTC (permalink / raw)
To: linux-fsdevel
Cc: bernd.schubert, fuse-devel, Bernd Schubert, Miklos Szeredi,
Dharmendra Singh
Just preparation work for atomic open.
Signed-off-by: Bernd Schubert <bschubert@ddn.com>
Cc: Miklos Szeredi <miklos@szeredi.hu>
Cc: Dharmendra Singh <dsingh@ddn.com>
Cc: linux-fsdevel@vger.kernel.org
---
fs/fuse/dir.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
index 35bc174f9ba2..6ffc573de470 100644
--- a/fs/fuse/dir.c
+++ b/fs/fuse/dir.c
@@ -613,7 +613,7 @@ static void free_ext_value(struct fuse_args *args)
* If the filesystem doesn't support this, then fall back to separate
* 'mknod' + 'open' requests.
*/
-static int fuse_create_open(struct inode *dir, struct dentry *entry,
+static int _fuse_create_open(struct inode *dir, struct dentry *entry,
struct file *file, unsigned int flags,
umode_t mode, u32 opcode)
{
@@ -753,7 +753,7 @@ static int fuse_atomic_open(struct inode *dir, struct dentry *entry,
if (fc->no_create)
goto mknod;
- err = fuse_create_open(dir, entry, file, flags, mode, FUSE_CREATE);
+ err = _fuse_create_open(dir, entry, file, flags, mode, FUSE_CREATE);
if (err == -ENOSYS) {
fc->no_create = 1;
goto mknod;
@@ -879,7 +879,7 @@ static int fuse_tmpfile(struct mnt_idmap *idmap, struct inode *dir,
if (fc->no_tmpfile)
return -EOPNOTSUPP;
- err = fuse_create_open(dir, file->f_path.dentry, file, file->f_flags, mode, FUSE_TMPFILE);
+ err = _fuse_create_open(dir, file->f_path.dentry, file, file->f_flags, mode, FUSE_TMPFILE);
if (err == -ENOSYS) {
fc->no_tmpfile = 1;
err = -EOPNOTSUPP;
--
2.37.2
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH 2/6] fuse: introduce atomic open
2023-08-16 14:33 [PATCH v8 0/6] fuse: full atomic open and atomic-open-revalidate Bernd Schubert
2023-08-16 14:33 ` [PATCH 1/6] fuse: rename fuse_create_open Bernd Schubert
@ 2023-08-16 14:33 ` Bernd Schubert
2023-08-16 14:33 ` [PATCH 3/6] [RFC] Allow atomic_open() on positive dentry Bernd Schubert
` (3 subsequent siblings)
5 siblings, 0 replies; 10+ messages in thread
From: Bernd Schubert @ 2023-08-16 14:33 UTC (permalink / raw)
To: linux-fsdevel
Cc: bernd.schubert, fuse-devel, Bernd Schubert, Dharmendra Singh,
Horst Birthelmer, Miklos Szeredi, Christian Brauner, Al Viro
From: Dharmendra Singh <dsingh@ddn.com>
This adds full atomic open support, to avoid lookup before open/create.
If the implementation (fuse server/daemon) does not support atomic open
it falls back to non-atomic open.
Co-developed-by: Bernd Schubert <bschubert@ddn.com>
Signed-off-by: Dharmendra Singh <dsingh@ddn.com>
Signed-off-by: Horst Birthelmer <hbirthelmer@ddn.com>
Signed-off-by: Bernd Schubert <bschubert@ddn.com>
Cc: Miklos Szeredi <miklos@szeredi.hu>
Cc: Christian Brauner <brauner@kernel.org>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Dharmendra Singh <dsingh@ddn.com>
Cc: linux-fsdevel@vger.kernel.org
---
fs/fuse/dir.c | 222 +++++++++++++++++++++++++++++++++++++-
fs/fuse/fuse_i.h | 3 +
include/uapi/linux/fuse.h | 3 +
3 files changed, 226 insertions(+), 2 deletions(-)
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
index 6ffc573de470..bb68d911fd80 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)
@@ -724,7 +723,7 @@ static int _fuse_create_open(struct inode *dir, struct dentry *entry,
static int fuse_mknod(struct mnt_idmap *, struct inode *, struct dentry *,
umode_t, dev_t);
-static int fuse_atomic_open(struct inode *dir, struct dentry *entry,
+static int fuse_create_open(struct inode *dir, struct dentry *entry,
struct file *file, unsigned flags,
umode_t mode)
{
@@ -770,6 +769,225 @@ static int fuse_atomic_open(struct inode *dir, struct dentry *entry,
return finish_no_open(file, res);
}
+static int _fuse_atomic_open(struct inode *dir, struct dentry *entry,
+ struct file *file, unsigned flags,
+ umode_t mode)
+{
+ int err;
+ struct inode *inode;
+ struct fuse_mount *fm = get_fuse_mount(dir);
+ struct fuse_conn *fc = fm->fc;
+ FUSE_ARGS(args);
+ struct fuse_forget_link *forget;
+ struct fuse_create_in inarg;
+ struct fuse_open_out outopen;
+ struct fuse_entry_out outentry;
+ struct fuse_inode *fi;
+ struct fuse_file *ff;
+ struct dentry *switched_entry = NULL, *alias = NULL;
+ DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
+
+ /* Expect a negative dentry */
+ if (unlikely(d_inode(entry)))
+ goto fallback;
+
+ /* Userspace expects S_IFREG in create mode */
+ if ((flags & O_CREAT) && (mode & S_IFMT) != S_IFREG)
+ goto fallback;
+
+ forget = fuse_alloc_forget();
+ err = -ENOMEM;
+ if (!forget)
+ goto out_err;
+
+ err = -ENOMEM;
+ ff = fuse_file_alloc(fm);
+ if (!ff)
+ goto out_put_forget_req;
+
+ if (!fc->dont_mask)
+ mode &= ~current_umask();
+
+ flags &= ~O_NOCTTY;
+ memset(&inarg, 0, sizeof(inarg));
+ memset(&outentry, 0, sizeof(outentry));
+ inarg.flags = flags;
+ inarg.mode = mode;
+ inarg.umask = current_umask();
+
+ if (fc->handle_killpriv_v2 && (flags & O_TRUNC) &&
+ !(flags & O_EXCL) && !capable(CAP_FSETID)) {
+ inarg.open_flags |= FUSE_OPEN_KILL_SUIDGID;
+ }
+
+ args.opcode = FUSE_OPEN_ATOMIC;
+ args.nodeid = get_node_id(dir);
+ args.in_numargs = 2;
+ args.in_args[0].size = sizeof(inarg);
+ args.in_args[0].value = &inarg;
+ args.in_args[1].size = entry->d_name.len + 1;
+ args.in_args[1].value = entry->d_name.name;
+ args.out_numargs = 2;
+ args.out_args[0].size = sizeof(outentry);
+ args.out_args[0].value = &outentry;
+ args.out_args[1].size = sizeof(outopen);
+ args.out_args[1].value = &outopen;
+
+ if (flags & O_CREAT) {
+ err = get_create_ext(&args, dir, entry, mode);
+ if (err)
+ goto out_free_ff;
+ }
+
+ err = fuse_simple_request(fm, &args);
+ free_ext_value(&args);
+ if (err == -ENOSYS) {
+ fc->no_open_atomic = 1;
+ fuse_file_free(ff);
+ kfree(forget);
+ goto fallback;
+ }
+
+ if (!err && !outentry.nodeid)
+ err = -ENOENT;
+
+ if (err)
+ goto out_free_ff;
+
+ err = -EIO;
+ if (invalid_nodeid(outentry.nodeid) || fuse_invalid_attr(&outentry.attr))
+ goto out_free_ff;
+
+ ff->fh = outopen.fh;
+ ff->nodeid = outentry.nodeid;
+ ff->open_flags = outopen.open_flags;
+ inode = fuse_iget(dir->i_sb, outentry.nodeid, outentry.generation,
+ &outentry.attr, entry_attr_timeout(&outentry), 0);
+ if (!inode) {
+ flags &= ~(O_CREAT | O_EXCL | O_TRUNC);
+ fuse_sync_release(NULL, ff, flags);
+ fuse_queue_forget(fm->fc, forget, outentry.nodeid, 1);
+ err = -ENOMEM;
+ goto out_err;
+ }
+
+ /* prevent racing/parallel lookup on a negative hashed */
+ if (!(flags & O_CREAT) && !d_in_lookup(entry)) {
+ d_drop(entry);
+ switched_entry = d_alloc_parallel(entry->d_parent,
+ &entry->d_name, &wq);
+ if (IS_ERR(switched_entry)) {
+ err = PTR_ERR(switched_entry);
+ goto out_free_ff;
+ }
+
+ if (unlikely(!d_in_lookup(switched_entry))) {
+ /* fall back */
+ dput(switched_entry);
+ switched_entry = NULL;
+ goto free_and_fallback;
+ }
+
+ entry = switched_entry;
+ }
+
+ /* modified version of _nfs4_open_and_get_state - nfs does not open
+ * dirs, fuse doe
+ * nfs has additional d_really_is_negative condition, which does not
+ * make sense as long as only negative dentries come into this function,
+ * see BUG_ON above and missing revalidate patch - but needed if
+ * we are going to handle revalidate
+ */
+ if (d_really_is_negative(entry)) {
+ d_drop(entry);
+ alias = d_exact_alias(entry, inode);
+ if (!alias) {
+ alias = d_splice_alias(inode, entry);
+ if (IS_ERR(alias)) {
+ /*
+ * Close the file in user space, but do not unlink it,
+ * if it was created - with network file systems other
+ * clients might have already accessed it.
+ */
+ fi = get_fuse_inode(inode);
+ fuse_sync_release(fi, ff, flags);
+ fuse_queue_forget(fm->fc, forget, outentry.nodeid, 1);
+ err = PTR_ERR(alias);
+ goto out_err;
+ }
+ }
+
+ if (alias)
+ entry = alias;
+ }
+
+ fuse_change_entry_timeout(entry, &outentry);
+
+ /* File was indeed created */
+ if (outopen.open_flags & FOPEN_FILE_CREATED) {
+ if (!(flags & O_CREAT)) {
+ pr_debug("Server side bug, FOPEN_FILE_CREATED set "
+ "without O_CREAT, ignoring.");
+ } else {
+ /* This should be always set when the file is created */
+ fuse_dir_changed(dir);
+ file->f_mode |= FMODE_CREATED;
+ }
+ }
+
+ if (S_ISDIR(mode))
+ ff->open_flags &= ~FOPEN_DIRECT_IO;
+ err = finish_open(file, entry, generic_file_open);
+ if (err) {
+ fi = get_fuse_inode(inode);
+ fuse_sync_release(fi, ff, flags);
+ } else {
+ file->private_data = ff;
+ fuse_finish_open(inode, file);
+ }
+
+ kfree(forget);
+
+ if (switched_entry) {
+ d_lookup_done(switched_entry);
+ dput(switched_entry);
+ }
+
+ dput(alias);
+
+ return err;
+
+out_free_ff:
+ fuse_file_free(ff);
+out_put_forget_req:
+ kfree(forget);
+out_err:
+ if (switched_entry) {
+ d_lookup_done(switched_entry);
+ dput(switched_entry);
+ }
+
+ return err;
+
+free_and_fallback:
+ fuse_file_free(ff);
+ kfree(forget);
+fallback:
+ return fuse_create_open(dir, entry, file, flags, mode);
+}
+
+static int fuse_atomic_open(struct inode *dir, struct dentry *entry,
+ struct file *file, unsigned int flags,
+ umode_t mode)
+{
+ struct fuse_conn *fc = get_fuse_conn(dir);
+
+ if (fc->no_open_atomic)
+ return fuse_create_open(dir, entry, file, flags, mode);
+ else
+ return _fuse_atomic_open(dir, entry, file, flags, mode);
+}
+
/*
* Code shared between mknod, mkdir, symlink and link
*/
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index 9b7fc7d3c7f1..4e2ebcc28912 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -672,6 +672,9 @@ struct fuse_conn {
/** Is open/release not implemented by fs? */
unsigned no_open:1;
+ /** Is open atomic not impelmented by fs? */
+ unsigned no_open_atomic:1;
+
/** Is opendir/releasedir not implemented by fs? */
unsigned no_opendir:1;
diff --git a/include/uapi/linux/fuse.h b/include/uapi/linux/fuse.h
index 1b9d0dfae72d..ee36263c0f3a 100644
--- a/include/uapi/linux/fuse.h
+++ b/include/uapi/linux/fuse.h
@@ -314,6 +314,7 @@ struct fuse_file_lock {
* FOPEN_STREAM: the file is stream-like (no file position at all)
* FOPEN_NOFLUSH: don't flush data cache on close (unless FUSE_WRITEBACK_CACHE)
* FOPEN_PARALLEL_DIRECT_WRITES: Allow concurrent direct writes on the same inode
+ * FOPEN_FILE_CREATED: the file was indeed created
*/
#define FOPEN_DIRECT_IO (1 << 0)
#define FOPEN_KEEP_CACHE (1 << 1)
@@ -322,6 +323,7 @@ struct fuse_file_lock {
#define FOPEN_STREAM (1 << 4)
#define FOPEN_NOFLUSH (1 << 5)
#define FOPEN_PARALLEL_DIRECT_WRITES (1 << 6)
+#define FOPEN_FILE_CREATED (1 << 7)
/**
* INIT request/reply flags
@@ -572,6 +574,7 @@ enum fuse_opcode {
FUSE_REMOVEMAPPING = 49,
FUSE_SYNCFS = 50,
FUSE_TMPFILE = 51,
+ FUSE_OPEN_ATOMIC = 52,
/* CUSE specific operations */
CUSE_INIT = 4096,
--
2.37.2
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH 3/6] [RFC] Allow atomic_open() on positive dentry
2023-08-16 14:33 [PATCH v8 0/6] fuse: full atomic open and atomic-open-revalidate Bernd Schubert
2023-08-16 14:33 ` [PATCH 1/6] fuse: rename fuse_create_open Bernd Schubert
2023-08-16 14:33 ` [PATCH 2/6] fuse: introduce atomic open Bernd Schubert
@ 2023-08-16 14:33 ` Bernd Schubert
2023-08-16 15:25 ` Miklos Szeredi
2023-08-16 14:33 ` [PATCH 4/6] fuse: Revalidate positive entries in fuse_atomic_open Bernd Schubert
` (2 subsequent siblings)
5 siblings, 1 reply; 10+ messages in thread
From: Bernd Schubert @ 2023-08-16 14:33 UTC (permalink / raw)
To: linux-fsdevel
Cc: bernd.schubert, fuse-devel, Miklos Szeredi, Bernd Schubert,
Christian Brauner, Al Viro, Dharmendra Singh
From: Miklos Szeredi <miklos@szeredi.hu>
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 (v7 global series) 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.
Changes (v8 global series)
- Introduce enum for d_revalidate return values
- LOOKUP_ATOMIC_REVALIDATE is removed again
- DCACHE_ATOMIC_OPEN flag is replaced by D_REVALIDATE_ATOMIC
return value
Co-developed-by: Bernd Schubert <bschubert@ddn.com>
Signed-off-by: Miklos Szeredi <miklos@szeredi.hu>
Signed-off-by: Bernd Schubert <bschubert@ddn.com>
Cc: Christian Brauner <brauner@kernel.org>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Dharmendra Singh <dsingh@ddn.com>
Cc: linux-fsdevel@vger.kernel.org
---
fs/namei.c | 25 +++++++++++++++++++------
include/linux/namei.h | 6 ++++++
2 files changed, 25 insertions(+), 6 deletions(-)
diff --git a/fs/namei.c b/fs/namei.c
index e4fe0879ae55..8381ec7645f5 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -858,7 +858,7 @@ static inline int d_revalidate(struct dentry *dentry, unsigned int flags)
if (unlikely(dentry->d_flags & DCACHE_OP_REVALIDATE))
return dentry->d_op->d_revalidate(dentry, flags);
else
- return 1;
+ return D_REVALIDATE_VALID;
}
/**
@@ -1611,10 +1611,11 @@ struct dentry *lookup_one_qstr_excl(const struct qstr *name,
}
EXPORT_SYMBOL(lookup_one_qstr_excl);
-static struct dentry *lookup_fast(struct nameidata *nd)
+static struct dentry *lookup_fast(struct nameidata *nd, int *atomic_revalidate)
{
struct dentry *dentry, *parent = nd->path.dentry;
int status = 1;
+ *atomic_revalidate = 0;
/*
* Rename seqlock is not required here because in the off chance
@@ -1656,6 +1657,10 @@ static struct dentry *lookup_fast(struct nameidata *nd)
dput(dentry);
return ERR_PTR(status);
}
+
+ if (status == D_REVALIDATE_ATOMIC)
+ *atomic_revalidate = 1;
+
return dentry;
}
@@ -1981,6 +1986,7 @@ static const char *handle_dots(struct nameidata *nd, int type)
static const char *walk_component(struct nameidata *nd, int flags)
{
struct dentry *dentry;
+ int atomic_revalidate;
/*
* "." and ".." are special - ".." especially so because it has
* to be able to know about the current root directory and
@@ -1991,7 +1997,7 @@ static const char *walk_component(struct nameidata *nd, int flags)
put_link(nd);
return handle_dots(nd, nd->last_type);
}
- dentry = lookup_fast(nd);
+ dentry = lookup_fast(nd, &atomic_revalidate);
if (IS_ERR(dentry))
return ERR_CAST(dentry);
if (unlikely(!dentry)) {
@@ -1999,6 +2005,9 @@ static const char *walk_component(struct nameidata *nd, int flags)
if (IS_ERR(dentry))
return ERR_CAST(dentry);
}
+
+ WARN_ON(atomic_revalidate);
+
if (!(flags & WALK_MORE) && nd->depth)
put_link(nd);
return step_into(nd, flags, dentry);
@@ -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 && error != D_REVALIDATE_ATOMIC) {
/* Cached positive dentry: will open in f_op->open */
return dentry;
}
@@ -3523,15 +3532,19 @@ static const char *open_last_lookups(struct nameidata *nd,
}
if (!(open_flag & O_CREAT)) {
+ int atomic_revalidate;
if (nd->last.name[nd->last.len])
nd->flags |= LOOKUP_FOLLOW | LOOKUP_DIRECTORY;
/* we _can_ be in RCU mode here */
- dentry = lookup_fast(nd);
+ dentry = lookup_fast(nd, &atomic_revalidate);
if (IS_ERR(dentry))
return ERR_CAST(dentry);
+ if (dentry && unlikely(atomic_revalidate)) {
+ dput(dentry);
+ dentry = NULL;
+ }
if (likely(dentry))
goto finish_lookup;
-
BUG_ON(nd->flags & LOOKUP_RCU);
} else {
/* create side of things */
diff --git a/include/linux/namei.h b/include/linux/namei.h
index 1463cbda4888..675fd6c88201 100644
--- a/include/linux/namei.h
+++ b/include/linux/namei.h
@@ -47,6 +47,12 @@ enum {LAST_NORM, LAST_ROOT, LAST_DOT, LAST_DOTDOT};
/* LOOKUP_* flags which do scope-related checks based on the dirfd. */
#define LOOKUP_IS_SCOPED (LOOKUP_BENEATH | LOOKUP_IN_ROOT)
+enum {
+ D_REVALIDATE_INVALID = 0,
+ D_REVALIDATE_VALID = 1,
+ D_REVALIDATE_ATOMIC = 2, /* Not allowed with LOOKUP_RCU */
+};
+
extern int path_pts(struct path *path);
extern int user_path_at_empty(int, const char __user *, unsigned, struct path *, int *empty);
--
2.37.2
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH 4/6] fuse: Revalidate positive entries in fuse_atomic_open
2023-08-16 14:33 [PATCH v8 0/6] fuse: full atomic open and atomic-open-revalidate Bernd Schubert
` (2 preceding siblings ...)
2023-08-16 14:33 ` [PATCH 3/6] [RFC] Allow atomic_open() on positive dentry Bernd Schubert
@ 2023-08-16 14:33 ` Bernd Schubert
2023-08-16 14:33 ` [PATCH 5/6] fuse: revalidate Set DCACHE_ATOMIC_OPEN for cached dentries Bernd Schubert
2023-08-16 14:33 ` [PATCH 6/6] fuse: Avoid code duplication in atomic open Bernd Schubert
5 siblings, 0 replies; 10+ messages in thread
From: Bernd Schubert @ 2023-08-16 14:33 UTC (permalink / raw)
To: linux-fsdevel
Cc: bernd.schubert, fuse-devel, Bernd Schubert, Dharmendra Singh,
Horst Birthelmer, Miklos Szeredi, Christian Brauner, Al Viro
From: Dharmendra Singh <dsingh@ddn.com>
This makes use of the vfs changes and fuse_dentry_revalidate()
can now skip revalidate, if the fuse implementation has
atomic_open support, which will has to do the dentry
revalidation.
Skipping revalidate is only possible when we absolutely
know that the implementation supports atomic_open, so
another bit had to be added to struct fuse_conn, which
is set when atomic_open was successful.
Once struct fuse_conn has the positive 'has_open_atomic'
fuse_dentry_revalidate() might set DCACHE_ATOMIC_OPEN.
vfs use that flag to use atomic_open.
If the file was newly created, the previous positive dentry
is invalidated and a new dentry and inode are allocated
and linked (d_splice_alias).
If file was not created, we revalidate the inode. If inode is
stale, current inode is marked as bad. And new inode is allocated
and linked to new dentry(old dentry invalidated). In case of
inode attributes differing with fresh attr, we allocate new
dentry and hook current inode to it and open the file.
For negative dentry, FS just allocate new inode and hook it onto
passed entry from VFS and open the file.
Co-developed-by: Bernd Schubert <bschubert@ddn.com>
Signed-off-by: Dharmendra Singh <dsingh@ddn.com>
Signed-off-by: Horst Birthelmer <hbirthelmer@ddn.com>
Signed-off-by: Bernd Schubert <bschubert@ddn.com>
Cc: Miklos Szeredi <miklos@szeredi.hu>
Cc: Christian Brauner <brauner@kernel.org>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Dharmendra Singh <dsingh@ddn.com>
Cc: linux-fsdevel@vger.kernel.org
---
fs/fuse/dir.c | 204 ++++++++++++++++++++++++++++++++++++++++-------
fs/fuse/fuse_i.h | 3 +
2 files changed, 177 insertions(+), 30 deletions(-)
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
index bb68d911fd80..701f9c51cdb1 100644
--- a/fs/fuse/dir.c
+++ b/fs/fuse/dir.c
@@ -230,6 +230,19 @@ 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) {
+ ret = D_REVALIDATE_ATOMIC;
+ goto out;
+ }
forget = fuse_alloc_forget();
ret = -ENOMEM;
if (!forget)
@@ -280,12 +293,12 @@ static int fuse_dentry_revalidate(struct dentry *entry, unsigned int flags)
dput(parent);
}
}
- ret = 1;
+ ret = D_REVALIDATE_VALID;
out:
return ret;
invalid:
- ret = 0;
+ ret = D_REVALIDATE_INVALID;
goto out;
}
@@ -769,12 +782,84 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry,
return finish_no_open(file, res);
}
+/**
+ * Revalidate inode hooked into dentry against freshly acquired
+ * attributes. If inode is stale then allocate new dentry and
+ * hook it onto fresh inode.
+ */
+static struct dentry *
+fuse_atomic_open_revalidate(struct fuse_conn *fc, struct dentry *entry,
+ struct inode *inode, int switched,
+ struct fuse_entry_out *outentry,
+ wait_queue_head_t *wq, int *alloc_inode)
+{
+ u64 attr_version;
+ struct dentry *prev = entry;
+
+ if (outentry->nodeid != get_node_id(inode) ||
+ (bool) IS_AUTOMOUNT(inode) !=
+ (bool) (outentry->attr.flags & FUSE_ATTR_SUBMOUNT)) {
+ *alloc_inode = 1;
+ } else if (fuse_stale_inode(inode, outentry->generation,
+ &outentry->attr)) {
+ fuse_make_bad(inode);
+ *alloc_inode = 1;
+ }
+
+ if (*alloc_inode) {
+ struct dentry *new = NULL;
+
+ if (!switched && !d_in_lookup(entry)) {
+ d_drop(entry);
+ new = d_alloc_parallel(entry->d_parent, &entry->d_name,
+ wq);
+ if (IS_ERR(new))
+ return new;
+
+ if (unlikely(!d_in_lookup(new))) {
+ dput(new);
+ new = ERR_PTR(-EIO);
+ return new;
+ }
+ }
+
+ fuse_invalidate_entry(entry);
+
+ entry = new;
+ } else if (!*alloc_inode) {
+ attr_version = fuse_get_attr_version(fc);
+ forget_all_cached_acls(inode);
+ fuse_change_attributes(inode, &outentry->attr,
+ entry_attr_timeout(outentry),
+ attr_version);
+ }
+
+ if (prev == entry) {
+ /* nothing changed, atomic-open on the server side
+ * had increased the lookup count - do the same here
+ */
+ struct fuse_inode *fi = get_fuse_inode(inode);
+
+ spin_lock(&fi->lock);
+ fi->nlookup++;
+ spin_unlock(&fi->lock);
+ }
+
+ return entry;
+}
+
+/**
+ * Does 'lookup + create + open' or 'lookup + open' atomically.
+ * @entry might be positive as well, therefore inode is re-validated.
+ * Positive dentry is invalidated in case inode attributes differ or
+ * we encountered error.
+ */
static int _fuse_atomic_open(struct inode *dir, struct dentry *entry,
struct file *file, unsigned flags,
umode_t mode)
{
int err;
- struct inode *inode;
+ struct inode *inode = d_inode(entry);
struct fuse_mount *fm = get_fuse_mount(dir);
struct fuse_conn *fc = fm->fc;
FUSE_ARGS(args);
@@ -786,10 +871,7 @@ static int _fuse_atomic_open(struct inode *dir, struct dentry *entry,
struct fuse_file *ff;
struct dentry *switched_entry = NULL, *alias = NULL;
DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
-
- /* Expect a negative dentry */
- if (unlikely(d_inode(entry)))
- goto fallback;
+ int alloc_inode = 0;
/* Userspace expects S_IFREG in create mode */
if ((flags & O_CREAT) && (mode & S_IFMT) != S_IFREG)
@@ -841,37 +923,57 @@ static int _fuse_atomic_open(struct inode *dir, struct dentry *entry,
err = fuse_simple_request(fm, &args);
free_ext_value(&args);
- if (err == -ENOSYS) {
- fc->no_open_atomic = 1;
- fuse_file_free(ff);
- kfree(forget);
- goto fallback;
- }
if (!err && !outentry.nodeid)
err = -ENOENT;
- if (err)
- goto out_free_ff;
+ if (err) {
+ if (err == -ENOSYS) {
+ fc->no_open_atomic = 1;
+
+ /* Might come up if userspace tricks us and would
+ * return -ENOSYS for OPEN_ATOMIC after it was
+ * aready working
+ */
+ 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*/
+ if (d_really_is_positive(entry)) {
+ WARN_ON(1);
+ err = -EIO;
+ goto out_free_ff;
+ }
+
+ fuse_file_free(ff);
+ kfree(forget);
+ goto fallback;
+ } else {
+ if (d_really_is_positive(entry)) {
+ if (err != -EINTR && err != -ENOMEM)
+ fuse_invalidate_entry(entry);
+ }
+
+ goto out_free_ff;
+ }
+ }
+
+ if (!err && !fc->has_open_atomic) {
+ /* Only set this flag when atomic open did not return an error,
+ * so that we are absolutely sure it is implemented.
+ */
+ fc->has_open_atomic = 1;
+ }
err = -EIO;
if (invalid_nodeid(outentry.nodeid) || fuse_invalid_attr(&outentry.attr))
goto out_free_ff;
- ff->fh = outopen.fh;
- ff->nodeid = outentry.nodeid;
- ff->open_flags = outopen.open_flags;
- inode = fuse_iget(dir->i_sb, outentry.nodeid, outentry.generation,
- &outentry.attr, entry_attr_timeout(&outentry), 0);
- if (!inode) {
- flags &= ~(O_CREAT | O_EXCL | O_TRUNC);
- fuse_sync_release(NULL, ff, flags);
- fuse_queue_forget(fm->fc, forget, outentry.nodeid, 1);
- err = -ENOMEM;
- goto out_err;
- }
-
- /* prevent racing/parallel lookup on a negative hashed */
+ /* prevent racing/parallel lookup */
if (!(flags & O_CREAT) && !d_in_lookup(entry)) {
d_drop(entry);
switched_entry = d_alloc_parallel(entry->d_parent,
@@ -885,10 +987,52 @@ static int _fuse_atomic_open(struct inode *dir, struct dentry *entry,
/* fall back */
dput(switched_entry);
switched_entry = NULL;
- goto free_and_fallback;
+
+ if (!inode) {
+ goto free_and_fallback;
+ } else {
+ /* XXX can this happen at all and is there a
+ * better way to handle it?
+ */
+ err = -EIO;
+ goto out_free_ff;
+ }
+ }
+ }
+
+ if (inode) {
+ struct dentry *new;
+
+ err = -ESTALE;
+ new = fuse_atomic_open_revalidate(fm->fc, entry, inode,
+ !!switched_entry,
+ &outentry, &wq, &alloc_inode);
+ if (IS_ERR(new)) {
+ err = PTR_ERR(new);
+ goto out_free_ff;
}
+ if (new != entry && new != NULL)
+ switched_entry = new;
+ }
+
+ if (switched_entry)
entry = switched_entry;
+
+ ff->fh = outopen.fh;
+ ff->nodeid = outentry.nodeid;
+ ff->open_flags = outopen.open_flags;
+
+ if (!inode || alloc_inode) {
+ inode = fuse_iget(dir->i_sb, outentry.nodeid, outentry.generation,
+ &outentry.attr, entry_attr_timeout(&outentry), 0);
+ if (!inode) {
+ flags &= ~(O_CREAT | O_EXCL | O_TRUNC);
+ fuse_sync_release(NULL, ff, flags);
+ fuse_queue_forget(fm->fc, forget, outentry.nodeid, 1);
+ err = -ENOMEM;
+ goto out_err;
+ }
}
/* modified version of _nfs4_open_and_get_state - nfs does not open
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index 4e2ebcc28912..6a35f109d214 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -675,6 +675,9 @@ struct fuse_conn {
/** Is open atomic not impelmented by fs? */
unsigned no_open_atomic:1;
+ /** Is open atomic is proven to be implemented by fs? */
+ unsigned has_open_atomic:1;
+
/** Is opendir/releasedir not implemented by fs? */
unsigned no_opendir:1;
--
2.37.2
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH 5/6] fuse: revalidate Set DCACHE_ATOMIC_OPEN for cached dentries
2023-08-16 14:33 [PATCH v8 0/6] fuse: full atomic open and atomic-open-revalidate Bernd Schubert
` (3 preceding siblings ...)
2023-08-16 14:33 ` [PATCH 4/6] fuse: Revalidate positive entries in fuse_atomic_open Bernd Schubert
@ 2023-08-16 14:33 ` Bernd Schubert
2023-08-16 14:33 ` [PATCH 6/6] fuse: Avoid code duplication in atomic open Bernd Schubert
5 siblings, 0 replies; 10+ messages in thread
From: Bernd Schubert @ 2023-08-16 14:33 UTC (permalink / raw)
To: linux-fsdevel
Cc: bernd.schubert, fuse-devel, Bernd Schubert, Miklos Szeredi,
Dharmendra Singh, Horst Birthelmer
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@ddn.com>
Cc: Miklos Szeredi <miklos@szeredi.hu>
Cc: Dharmendra Singh <dsingh@ddn.com>
Cc: Horst Birthelmer <hbirthelmer@ddn.com>
Cc: linux-fsdevel@vger.kernel.org
---
fs/fuse/dir.c | 52 +++++++++++++++++++++++++++++++++++++--------------
1 file changed, 38 insertions(+), 14 deletions(-)
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
index 701f9c51cdb1..1e5e2d46df8a 100644
--- a/fs/fuse/dir.c
+++ b/fs/fuse/dir.c
@@ -193,6 +193,22 @@ 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 && fc->has_open_atomic)
+ ret = D_REVALIDATE_ATOMIC;
+
+ return ret;
+}
+
/*
* Check whether the dentry is still valid
*
@@ -230,19 +246,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) {
- ret = D_REVALIDATE_ATOMIC;
+ ret = fuse_dentry_do_atomic_revalidate(entry, flags, fm->fc);
+ if (ret)
goto out;
- }
+
forget = fuse_alloc_forget();
ret = -ENOMEM;
if (!forget)
@@ -285,6 +292,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)) {
@@ -292,6 +309,14 @@ 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);
+ ret = fuse_dentry_do_atomic_revalidate(entry, flags, fm->fc);
+ if (ret)
+ goto out;
}
ret = D_REVALIDATE_VALID;
out:
@@ -935,11 +960,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.37.2
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH 6/6] fuse: Avoid code duplication in atomic open
2023-08-16 14:33 [PATCH v8 0/6] fuse: full atomic open and atomic-open-revalidate Bernd Schubert
` (4 preceding siblings ...)
2023-08-16 14:33 ` [PATCH 5/6] fuse: revalidate Set DCACHE_ATOMIC_OPEN for cached dentries Bernd Schubert
@ 2023-08-16 14:33 ` Bernd Schubert
5 siblings, 0 replies; 10+ messages in thread
From: Bernd Schubert @ 2023-08-16 14:33 UTC (permalink / raw)
To: linux-fsdevel
Cc: bernd.schubert, fuse-devel, Bernd Schubert, Miklos Szeredi,
Dharmendra Singh, Horst Birthelmer
The same code was used in fuse_atomic_open_revalidate()
_fuse_atomic_open().
(If preferred, this could be merged into the main fuse atomic
revalidate patch). Or adding the function could be moved up
in the series.
Signed-off-by: Bernd Schubert <bschubert@ddn.com>
Cc: Miklos Szeredi <miklos@szeredi.hu>
Cc: Dharmendra Singh <dsingh@ddn.com>
Cc: Horst Birthelmer <hbirthelmer@ddn.com>
Cc: linux-fsdevel@vger.kernel.org
---
fs/fuse/dir.c | 48 +++++++++++++++++++++++-------------------------
1 file changed, 23 insertions(+), 25 deletions(-)
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
index 1e5e2d46df8a..e69dafc89222 100644
--- a/fs/fuse/dir.c
+++ b/fs/fuse/dir.c
@@ -807,6 +807,25 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry,
return finish_no_open(file, res);
}
+static struct dentry *fuse_atomic_open_alloc_dentry(struct dentry *entry,
+ wait_queue_head_t *wq)
+{
+ struct dentry *new;
+
+ d_drop(entry);
+ new = d_alloc_parallel(entry->d_parent, &entry->d_name,
+ wq);
+ if (IS_ERR(new))
+ return new;
+
+ /* XXX Can this happen at all and there a way to handle it? */
+ if (unlikely(!d_in_lookup(new))) {
+ dput(new);
+ new = ERR_PTR(-EIO);
+ }
+ return new;
+}
+
/**
* Revalidate inode hooked into dentry against freshly acquired
* attributes. If inode is stale then allocate new dentry and
@@ -835,17 +854,9 @@ fuse_atomic_open_revalidate(struct fuse_conn *fc, struct dentry *entry,
struct dentry *new = NULL;
if (!switched && !d_in_lookup(entry)) {
- d_drop(entry);
- new = d_alloc_parallel(entry->d_parent, &entry->d_name,
- wq);
+ new = fuse_atomic_open_alloc_dentry(entry, wq);
if (IS_ERR(new))
return new;
-
- if (unlikely(!d_in_lookup(new))) {
- dput(new);
- new = ERR_PTR(-EIO);
- return new;
- }
}
fuse_invalidate_entry(entry);
@@ -999,26 +1010,13 @@ static int _fuse_atomic_open(struct inode *dir, struct dentry *entry,
/* prevent racing/parallel lookup */
if (!(flags & O_CREAT) && !d_in_lookup(entry)) {
- d_drop(entry);
- switched_entry = d_alloc_parallel(entry->d_parent,
- &entry->d_name, &wq);
+ switched_entry = fuse_atomic_open_alloc_dentry(entry, &wq);
if (IS_ERR(switched_entry)) {
- err = PTR_ERR(switched_entry);
- goto out_free_ff;
- }
-
- if (unlikely(!d_in_lookup(switched_entry))) {
- /* fall back */
- dput(switched_entry);
- switched_entry = NULL;
-
if (!inode) {
goto free_and_fallback;
} else {
- /* XXX can this happen at all and is there a
- * better way to handle it?
- */
- err = -EIO;
+ /* XXX Is there a better way to handle it? */
+ err = PTR_ERR(switched_entry);
goto out_free_ff;
}
}
--
2.37.2
^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH 3/6] [RFC] Allow atomic_open() on positive dentry
2023-08-16 14:33 ` [PATCH 3/6] [RFC] Allow atomic_open() on positive dentry Bernd Schubert
@ 2023-08-16 15:25 ` Miklos Szeredi
2023-08-16 15:54 ` Bernd Schubert
0 siblings, 1 reply; 10+ messages in thread
From: Miklos Szeredi @ 2023-08-16 15:25 UTC (permalink / raw)
To: Bernd Schubert
Cc: linux-fsdevel, bernd.schubert, fuse-devel, Christian Brauner,
Al Viro, Dharmendra Singh
On Wed, 16 Aug 2023 at 16:34, Bernd Schubert <bschubert@ddn.com> wrote:
>
> From: Miklos Szeredi <miklos@szeredi.hu>
>
> 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 (v7 global series) 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.
>
> Changes (v8 global series)
> - Introduce enum for d_revalidate return values
> - LOOKUP_ATOMIC_REVALIDATE is removed again
> - DCACHE_ATOMIC_OPEN flag is replaced by D_REVALIDATE_ATOMIC
> return value
>
> Co-developed-by: Bernd Schubert <bschubert@ddn.com>
> Signed-off-by: Miklos Szeredi <miklos@szeredi.hu>
> Signed-off-by: Bernd Schubert <bschubert@ddn.com>
> Cc: Christian Brauner <brauner@kernel.org>
> Cc: Al Viro <viro@zeniv.linux.org.uk>
> Cc: Dharmendra Singh <dsingh@ddn.com>
> Cc: linux-fsdevel@vger.kernel.org
> ---
> fs/namei.c | 25 +++++++++++++++++++------
> include/linux/namei.h | 6 ++++++
> 2 files changed, 25 insertions(+), 6 deletions(-)
>
> diff --git a/fs/namei.c b/fs/namei.c
> index e4fe0879ae55..8381ec7645f5 100644
> --- a/fs/namei.c
> +++ b/fs/namei.c
> @@ -858,7 +858,7 @@ static inline int d_revalidate(struct dentry *dentry, unsigned int flags)
> if (unlikely(dentry->d_flags & DCACHE_OP_REVALIDATE))
> return dentry->d_op->d_revalidate(dentry, flags);
> else
> - return 1;
> + return D_REVALIDATE_VALID;
> }
>
> /**
> @@ -1611,10 +1611,11 @@ struct dentry *lookup_one_qstr_excl(const struct qstr *name,
> }
> EXPORT_SYMBOL(lookup_one_qstr_excl);
>
> -static struct dentry *lookup_fast(struct nameidata *nd)
> +static struct dentry *lookup_fast(struct nameidata *nd, int *atomic_revalidate)
bool?
> {
> struct dentry *dentry, *parent = nd->path.dentry;
> int status = 1;
> + *atomic_revalidate = 0;
>
> /*
> * Rename seqlock is not required here because in the off chance
> @@ -1656,6 +1657,10 @@ static struct dentry *lookup_fast(struct nameidata *nd)
> dput(dentry);
> return ERR_PTR(status);
> }
> +
> + if (status == D_REVALIDATE_ATOMIC)
> + *atomic_revalidate = 1;
> +
> return dentry;
> }
>
> @@ -1981,6 +1986,7 @@ static const char *handle_dots(struct nameidata *nd, int type)
> static const char *walk_component(struct nameidata *nd, int flags)
> {
> struct dentry *dentry;
> + int atomic_revalidate;
> /*
> * "." and ".." are special - ".." especially so because it has
> * to be able to know about the current root directory and
> @@ -1991,7 +1997,7 @@ static const char *walk_component(struct nameidata *nd, int flags)
> put_link(nd);
> return handle_dots(nd, nd->last_type);
> }
> - dentry = lookup_fast(nd);
> + dentry = lookup_fast(nd, &atomic_revalidate);
> if (IS_ERR(dentry))
> return ERR_CAST(dentry);
> if (unlikely(!dentry)) {
> @@ -1999,6 +2005,9 @@ static const char *walk_component(struct nameidata *nd, int flags)
> if (IS_ERR(dentry))
> return ERR_CAST(dentry);
> }
> +
> + WARN_ON(atomic_revalidate);
> +
> if (!(flags & WALK_MORE) && nd->depth)
> put_link(nd);
> return step_into(nd, flags, dentry);
> @@ -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 && error != D_REVALIDATE_ATOMIC) {
> /* Cached positive dentry: will open in f_op->open */
> return dentry;
> }
> @@ -3523,15 +3532,19 @@ static const char *open_last_lookups(struct nameidata *nd,
> }
>
> if (!(open_flag & O_CREAT)) {
> + int atomic_revalidate;
> if (nd->last.name[nd->last.len])
> nd->flags |= LOOKUP_FOLLOW | LOOKUP_DIRECTORY;
> /* we _can_ be in RCU mode here */
> - dentry = lookup_fast(nd);
> + dentry = lookup_fast(nd, &atomic_revalidate);
> if (IS_ERR(dentry))
> return ERR_CAST(dentry);
> + if (dentry && unlikely(atomic_revalidate)) {
Need to assert !LOOKUP_RCU
> + dput(dentry);
> + dentry = NULL;
> + }
Feels a shame to throw away the dentry. May be worth adding a helper
for the plain atomic open, most of the complexity of lookup_open() is
because of O_CREAT, so this should be much simplified.
> if (likely(dentry))
> goto finish_lookup;
> -
Adding/removing empty lines is just a distraction, so it shouldn't be
done unless it serves a real purpose.
> BUG_ON(nd->flags & LOOKUP_RCU);
> } else {
> /* create side of things */
> diff --git a/include/linux/namei.h b/include/linux/namei.h
> index 1463cbda4888..675fd6c88201 100644
> --- a/include/linux/namei.h
> +++ b/include/linux/namei.h
> @@ -47,6 +47,12 @@ enum {LAST_NORM, LAST_ROOT, LAST_DOT, LAST_DOTDOT};
> /* LOOKUP_* flags which do scope-related checks based on the dirfd. */
> #define LOOKUP_IS_SCOPED (LOOKUP_BENEATH | LOOKUP_IN_ROOT)
>
> +enum {
> + D_REVALIDATE_INVALID = 0,
> + D_REVALIDATE_VALID = 1,
> + D_REVALIDATE_ATOMIC = 2, /* Not allowed with LOOKUP_RCU */
> +};
> +
> extern int path_pts(struct path *path);
>
> extern int user_path_at_empty(int, const char __user *, unsigned, struct path *, int *empty);
> --
> 2.37.2
>
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 3/6] [RFC] Allow atomic_open() on positive dentry
2023-08-16 15:25 ` Miklos Szeredi
@ 2023-08-16 15:54 ` Bernd Schubert
0 siblings, 0 replies; 10+ messages in thread
From: Bernd Schubert @ 2023-08-16 15:54 UTC (permalink / raw)
To: Miklos Szeredi, Bernd Schubert
Cc: linux-fsdevel, fuse-devel, Christian Brauner, Al Viro,
Dharmendra Singh
On 8/16/23 17:25, Miklos Szeredi wrote:
> On Wed, 16 Aug 2023 at 16:34, Bernd Schubert <bschubert@ddn.com> wrote:
>>
>> From: Miklos Szeredi <miklos@szeredi.hu>
>>
>> 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 (v7 global series) 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.
>>
>> Changes (v8 global series)
>> - Introduce enum for d_revalidate return values
>> - LOOKUP_ATOMIC_REVALIDATE is removed again
>> - DCACHE_ATOMIC_OPEN flag is replaced by D_REVALIDATE_ATOMIC
>> return value
>>
>> Co-developed-by: Bernd Schubert <bschubert@ddn.com>
>> Signed-off-by: Miklos Szeredi <miklos@szeredi.hu>
>> Signed-off-by: Bernd Schubert <bschubert@ddn.com>
>> Cc: Christian Brauner <brauner@kernel.org>
>> Cc: Al Viro <viro@zeniv.linux.org.uk>
>> Cc: Dharmendra Singh <dsingh@ddn.com>
>> Cc: linux-fsdevel@vger.kernel.org
>> ---
>> fs/namei.c | 25 +++++++++++++++++++------
>> include/linux/namei.h | 6 ++++++
>> 2 files changed, 25 insertions(+), 6 deletions(-)
>>
>> diff --git a/fs/namei.c b/fs/namei.c
>> index e4fe0879ae55..8381ec7645f5 100644
>> --- a/fs/namei.c
>> +++ b/fs/namei.c
>> @@ -858,7 +858,7 @@ static inline int d_revalidate(struct dentry *dentry, unsigned int flags)
>> if (unlikely(dentry->d_flags & DCACHE_OP_REVALIDATE))
>> return dentry->d_op->d_revalidate(dentry, flags);
>> else
>> - return 1;
>> + return D_REVALIDATE_VALID;
>> }
>>
>> /**
>> @@ -1611,10 +1611,11 @@ struct dentry *lookup_one_qstr_excl(const struct qstr *name,
>> }
>> EXPORT_SYMBOL(lookup_one_qstr_excl);
>>
>> -static struct dentry *lookup_fast(struct nameidata *nd)
>> +static struct dentry *lookup_fast(struct nameidata *nd, int *atomic_revalidate)
>
> bool?
>
>> {
>> struct dentry *dentry, *parent = nd->path.dentry;
>> int status = 1;
>> + *atomic_revalidate = 0;
>>
>> /*
>> * Rename seqlock is not required here because in the off chance
>> @@ -1656,6 +1657,10 @@ static struct dentry *lookup_fast(struct nameidata *nd)
>> dput(dentry);
>> return ERR_PTR(status);
>> }
>> +
>> + if (status == D_REVALIDATE_ATOMIC)
>> + *atomic_revalidate = 1;
>> +
>> return dentry;
>> }
>>
>> @@ -1981,6 +1986,7 @@ static const char *handle_dots(struct nameidata *nd, int type)
>> static const char *walk_component(struct nameidata *nd, int flags)
>> {
>> struct dentry *dentry;
>> + int atomic_revalidate;
>> /*
>> * "." and ".." are special - ".." especially so because it has
>> * to be able to know about the current root directory and
>> @@ -1991,7 +1997,7 @@ static const char *walk_component(struct nameidata *nd, int flags)
>> put_link(nd);
>> return handle_dots(nd, nd->last_type);
>> }
>> - dentry = lookup_fast(nd);
>> + dentry = lookup_fast(nd, &atomic_revalidate);
>> if (IS_ERR(dentry))
>> return ERR_CAST(dentry);
>> if (unlikely(!dentry)) {
>> @@ -1999,6 +2005,9 @@ static const char *walk_component(struct nameidata *nd, int flags)
>> if (IS_ERR(dentry))
>> return ERR_CAST(dentry);
>> }
>> +
>> + WARN_ON(atomic_revalidate);
>> +
>> if (!(flags & WALK_MORE) && nd->depth)
>> put_link(nd);
>> return step_into(nd, flags, dentry);
>> @@ -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 && error != D_REVALIDATE_ATOMIC) {
>> /* Cached positive dentry: will open in f_op->open */
>> return dentry;
>> }
>> @@ -3523,15 +3532,19 @@ static const char *open_last_lookups(struct nameidata *nd,
>> }
>>
>> if (!(open_flag & O_CREAT)) {
>> + int atomic_revalidate;
>> if (nd->last.name[nd->last.len])
>> nd->flags |= LOOKUP_FOLLOW | LOOKUP_DIRECTORY;
>> /* we _can_ be in RCU mode here */
>> - dentry = lookup_fast(nd);
>> + dentry = lookup_fast(nd, &atomic_revalidate);
>> if (IS_ERR(dentry))
>> return ERR_CAST(dentry);
>> + if (dentry && unlikely(atomic_revalidate)) {
>
> Need to assert !LOOKUP_RCU
Are you sure? There is the BUG_ON(nd->flags & LOOKUP_RCU) directly after
- should be enough?
>
>> + dput(dentry);
>> + dentry = NULL;
>> + }
>
> Feels a shame to throw away the dentry. May be worth adding a helper
> for the plain atomic open, most of the complexity of lookup_open() is
> because of O_CREAT, so this should be much simplified.
Thanks, I'm going to look into it.
>
>> if (likely(dentry))
>> goto finish_lookup;
>> -
>
> Adding/removing empty lines is just a distraction, so it shouldn't be
> done unless it serves a real purpose.
Ah sorry, accidentally. I'm going to travel the next two days, going to
update it on Monday (or at best over the weekend).
Thanks,
Bernd
^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2023-08-16 15:55 UTC | newest]
Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2023-08-16 14:33 [PATCH v8 0/6] fuse: full atomic open and atomic-open-revalidate Bernd Schubert
2023-08-16 14:33 ` [PATCH 1/6] fuse: rename fuse_create_open Bernd Schubert
2023-08-16 14:33 ` [PATCH 2/6] fuse: introduce atomic open Bernd Schubert
2023-08-16 14:33 ` [PATCH 3/6] [RFC] Allow atomic_open() on positive dentry Bernd Schubert
2023-08-16 15:25 ` Miklos Szeredi
2023-08-16 15:54 ` Bernd Schubert
2023-08-16 14:33 ` [PATCH 4/6] fuse: Revalidate positive entries in fuse_atomic_open Bernd Schubert
2023-08-16 14:33 ` [PATCH 5/6] fuse: revalidate Set DCACHE_ATOMIC_OPEN for cached dentries Bernd Schubert
2023-08-16 14:33 ` [PATCH 6/6] fuse: Avoid code duplication in atomic open Bernd Schubert
-- strict thread matches above, loose matches on Subject: below --
2023-08-11 18:37 [PATCH v7 0/6] fuse: full atomic open and atomic-open-revalidate Bernd Schubert
2023-08-11 18:37 ` [PATCH 4/6] fuse: Revalidate positive entries in fuse_atomic_open Bernd Schubert
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).