* [PATCH 0/3] FUSE passthrough fixes
@ 2024-04-07 15:57 Amir Goldstein
2024-04-07 15:57 ` [PATCH 1/3] fuse: fix wrong ff->iomode state changes from parallel dio write Amir Goldstein
` (2 more replies)
0 siblings, 3 replies; 10+ messages in thread
From: Amir Goldstein @ 2024-04-07 15:57 UTC (permalink / raw)
To: Miklos Szeredi; +Cc: Bernd Schubert, Sweet Tea Dorminy, linux-fsdevel
Miklos,
While going over the code to prepare for getattr() passthrough I
experienced a WTF moment that resulted in the two fix patches.
Patch 3/3 is included for reference and to give Sweet Tea a starting
point for getattr() passthrough.
What puzzled me is that some ff->iomode state bugs were so blunt that
I needed to figure out how I did not see any WARN_ON in my tests of rc1.
There are different reasons for different types of bugs.
1. For concurrent dio writes without any passthrough open,
fuse_file_cached_io_start() was supposed to hit
WARN_ON(ff->iomode == IOM_UNCACHED) if there is already a dio write
in-flight.
My conclusion is that the set of fstests that run on passthrough_hp,
on my small test VM do not excercise concurrent dio writes.
2. For dio write, where the file was opened passthrough, every write
was going to hit WARN_ON(ff->iomode == IOM_UNCACHED) and also
fuse_file_cached_io_end() was going to set ff->iomode == IOM_NONE
and leak the fuse_backing object.
However, the bug fixed by patch 2/3 made sure that parallel dio write
would always fallback to exclusive dio if file was open with a backing
file.
Testing:
I ran fstests with passthrough_hp with options:
1) FOPEN_PASSTHROUGH
2) FOPEN_DIRECT_IO | FOPEN_PARALLEL_DIRECT_WRITES
3) FOPEN_PASSTHROUGH | FOPEN_DIRECT_IO | FOPEN_PARALLEL_DIRECT_WRITES
Did not observe any regressions (not any improvments) from rc1.
Ran some multi threads aiodio tests with just patch 2/3 and the
assertions in fuse_evict_inode() from patch 3/3.
First two configs did not hit any assertion.
The passthrough+direct_io+parallel_direct_writes config always
hits the assertion in fuse_file_cached_io_start() and always hits
the leaked fuse_backing assertion in fuse_evict_inode().
Bernd do you have different tests to cover concurrent dio writes in
your setup? Any ideas on how to improve the fstests test coverage?
Thanks,
Amir.
Amir Goldstein (3):
fuse: fix wrong ff->iomode state changes from parallel dio write
fuse: fix parallel dio write on file open in passthrough mode
fuse: prepare for long lived reference on backing file
fs/fuse/file.c | 18 +++++++-----
fs/fuse/fuse_i.h | 10 +++++--
fs/fuse/inode.c | 7 +++++
fs/fuse/iomode.c | 73 +++++++++++++++++++++++++++++++++---------------
4 files changed, 76 insertions(+), 32 deletions(-)
--
2.34.1
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH 1/3] fuse: fix wrong ff->iomode state changes from parallel dio write
2024-04-07 15:57 [PATCH 0/3] FUSE passthrough fixes Amir Goldstein
@ 2024-04-07 15:57 ` Amir Goldstein
2024-04-09 13:33 ` Miklos Szeredi
2024-04-07 15:57 ` [PATCH 2/3] fuse: fix parallel dio write on file open in passthrough mode Amir Goldstein
2024-04-07 15:57 ` [PATCH 3/3] fuse: prepare for long lived reference on backing file Amir Goldstein
2 siblings, 1 reply; 10+ messages in thread
From: Amir Goldstein @ 2024-04-07 15:57 UTC (permalink / raw)
To: Miklos Szeredi; +Cc: Bernd Schubert, Sweet Tea Dorminy, linux-fsdevel
There is a confusion with fuse_file_uncached_io_{start,end} interface.
These helpers do two things when called from passthrough open()/release():
1. Take/drop negative refcount of fi->iocachectr (inode uncached io mode)
2. State change ff->iomode IOM_NONE <-> IOM_UNCACHED (file uncached open)
The calls from parallel dio write path need to take a reference on
fi->iocachectr, but they should not be changing ff->iomode state,
because in this case, the fi->iocachectr reference does not stick around
until file release().
Factor out helpers fuse_inode_uncached_io_{start,end}, to be used from
parallel dio write path and rename fuse_file_*cached_io_{start,end}
helpers to fuse_file_*cached_io_{open,release} to clarify the difference.
Add a check of ff->iomode in mmap(), so that fuse_file_cached_io_open()
is called only on first mmap of direct_io file.
Fixes: 205c1d802683 ("fuse: allow parallel dio writes with FUSE_DIRECT_IO_ALLOW_MMAP")
Signed-off-by: Amir Goldstein <amir73il@gmail.com>
---
fs/fuse/file.c | 18 +++++++++------
fs/fuse/fuse_i.h | 7 +++---
fs/fuse/inode.c | 1 +
fs/fuse/iomode.c | 59 ++++++++++++++++++++++++++++++++----------------
4 files changed, 56 insertions(+), 29 deletions(-)
diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index a56e7bffd000..fcf20b304093 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -1362,7 +1362,7 @@ static void fuse_dio_lock(struct kiocb *iocb, struct iov_iter *from,
bool *exclusive)
{
struct inode *inode = file_inode(iocb->ki_filp);
- struct fuse_file *ff = iocb->ki_filp->private_data;
+ struct fuse_inode *fi = get_fuse_inode(inode);
*exclusive = fuse_dio_wr_exclusive_lock(iocb, from);
if (*exclusive) {
@@ -1377,7 +1377,7 @@ static void fuse_dio_lock(struct kiocb *iocb, struct iov_iter *from,
* have raced, so check it again.
*/
if (fuse_io_past_eof(iocb, from) ||
- fuse_file_uncached_io_start(inode, ff, NULL) != 0) {
+ fuse_inode_uncached_io_start(fi, NULL) != 0) {
inode_unlock_shared(inode);
inode_lock(inode);
*exclusive = true;
@@ -1388,13 +1388,13 @@ 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_file *ff = iocb->ki_filp->private_data;
+ 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_file_uncached_io_end(inode, ff);
+ fuse_inode_uncached_io_end(fi);
inode_unlock_shared(inode);
}
}
@@ -2574,10 +2574,14 @@ static int fuse_file_mmap(struct file *file, struct vm_area_struct *vma)
* First mmap of direct_io file enters caching inode io mode.
* Also waits for parallel dio writers to go into serial mode
* (exclusive instead of shared lock).
+ * After first mmap, the inode stays in caching io mode until
+ * the direct_io file release.
*/
- rc = fuse_file_cached_io_start(inode, ff);
- if (rc)
- return rc;
+ if (READ_ONCE(ff->iomode) != IOM_CACHED) {
+ rc = fuse_file_cached_io_open(inode, ff);
+ if (rc)
+ return rc;
+ }
}
if ((vma->vm_flags & VM_SHARED) && (vma->vm_flags & VM_MAYWRITE))
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index b24084b60864..f23919610313 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -1394,9 +1394,10 @@ int fuse_fileattr_set(struct mnt_idmap *idmap,
struct dentry *dentry, struct fileattr *fa);
/* iomode.c */
-int fuse_file_cached_io_start(struct inode *inode, struct fuse_file *ff);
-int fuse_file_uncached_io_start(struct inode *inode, struct fuse_file *ff, struct fuse_backing *fb);
-void fuse_file_uncached_io_end(struct inode *inode, struct fuse_file *ff);
+int fuse_file_cached_io_open(struct inode *inode, struct fuse_file *ff);
+int fuse_inode_uncached_io_start(struct fuse_inode *fi,
+ struct fuse_backing *fb);
+void fuse_inode_uncached_io_end(struct fuse_inode *fi);
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 3a5d88878335..99e44ea7d875 100644
--- a/fs/fuse/inode.c
+++ b/fs/fuse/inode.c
@@ -175,6 +175,7 @@ static void fuse_evict_inode(struct inode *inode)
}
}
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));
}
diff --git a/fs/fuse/iomode.c b/fs/fuse/iomode.c
index c653ddcf0578..1519f895f0a9 100644
--- a/fs/fuse/iomode.c
+++ b/fs/fuse/iomode.c
@@ -21,12 +21,13 @@ static inline bool fuse_is_io_cache_wait(struct fuse_inode *fi)
}
/*
- * Start cached io mode.
+ * Called on cached file open() and on first mmap() of direct_io file.
+ * Takes cached_io inode mode reference to be dropped on file release.
*
* Blocks new parallel dio writes and waits for the in-progress parallel dio
* writes to complete.
*/
-int fuse_file_cached_io_start(struct inode *inode, struct fuse_file *ff)
+int fuse_file_cached_io_open(struct inode *inode, struct fuse_file *ff)
{
struct fuse_inode *fi = get_fuse_inode(inode);
@@ -56,8 +57,7 @@ int fuse_file_cached_io_start(struct inode *inode, struct fuse_file *ff)
return -ETXTBSY;
}
- WARN_ON(ff->iomode == IOM_UNCACHED);
- if (ff->iomode == IOM_NONE) {
+ if (!WARN_ON(ff->iomode != IOM_NONE)) {
ff->iomode = IOM_CACHED;
if (fi->iocachectr == 0)
set_bit(FUSE_I_CACHE_IO_MODE, &fi->state);
@@ -67,10 +67,9 @@ int fuse_file_cached_io_start(struct inode *inode, struct fuse_file *ff)
return 0;
}
-static void fuse_file_cached_io_end(struct inode *inode, struct fuse_file *ff)
+static void fuse_file_cached_io_release(struct fuse_file *ff,
+ struct fuse_inode *fi)
{
- struct fuse_inode *fi = get_fuse_inode(inode);
-
spin_lock(&fi->lock);
WARN_ON(fi->iocachectr <= 0);
WARN_ON(ff->iomode != IOM_CACHED);
@@ -82,9 +81,8 @@ static void fuse_file_cached_io_end(struct inode *inode, struct fuse_file *ff)
}
/* Start strictly uncached io mode where cache access is not allowed */
-int fuse_file_uncached_io_start(struct inode *inode, struct fuse_file *ff, struct fuse_backing *fb)
+int fuse_inode_uncached_io_start(struct fuse_inode *fi, struct fuse_backing *fb)
{
- struct fuse_inode *fi = get_fuse_inode(inode);
struct fuse_backing *oldfb;
int err = 0;
@@ -99,9 +97,7 @@ int fuse_file_uncached_io_start(struct inode *inode, struct fuse_file *ff, struc
err = -ETXTBSY;
goto unlock;
}
- WARN_ON(ff->iomode != IOM_NONE);
fi->iocachectr--;
- ff->iomode = IOM_UNCACHED;
/* fuse inode holds a single refcount of backing file */
if (!oldfb) {
@@ -115,15 +111,29 @@ int fuse_file_uncached_io_start(struct inode *inode, struct fuse_file *ff, struc
return err;
}
-void fuse_file_uncached_io_end(struct inode *inode, struct fuse_file *ff)
+/* Takes uncached_io inode mode reference to be dropped on file release */
+static int fuse_file_uncached_io_open(struct inode *inode,
+ struct fuse_file *ff,
+ struct fuse_backing *fb)
{
struct fuse_inode *fi = get_fuse_inode(inode);
+ int err;
+
+ err = fuse_inode_uncached_io_start(fi, fb);
+ if (err)
+ return err;
+
+ WARN_ON(ff->iomode != IOM_NONE);
+ ff->iomode = IOM_UNCACHED;
+ return 0;
+}
+
+void fuse_inode_uncached_io_end(struct fuse_inode *fi)
+{
struct fuse_backing *oldfb = NULL;
spin_lock(&fi->lock);
WARN_ON(fi->iocachectr >= 0);
- WARN_ON(ff->iomode != IOM_UNCACHED);
- ff->iomode = IOM_NONE;
fi->iocachectr++;
if (!fi->iocachectr) {
wake_up(&fi->direct_io_waitq);
@@ -134,6 +144,15 @@ void fuse_file_uncached_io_end(struct inode *inode, struct fuse_file *ff)
fuse_backing_put(oldfb);
}
+/* Drop uncached_io reference from passthrough open */
+static void fuse_file_uncached_io_release(struct fuse_file *ff,
+ struct fuse_inode *fi)
+{
+ WARN_ON(ff->iomode != IOM_UNCACHED);
+ ff->iomode = IOM_NONE;
+ fuse_inode_uncached_io_end(fi);
+}
+
/*
* Open flags that are allowed in combination with FOPEN_PASSTHROUGH.
* A combination of FOPEN_PASSTHROUGH and FOPEN_DIRECT_IO means that read/write
@@ -163,7 +182,7 @@ static int fuse_file_passthrough_open(struct inode *inode, struct file *file)
return PTR_ERR(fb);
/* First passthrough file open denies caching inode io mode */
- err = fuse_file_uncached_io_start(inode, ff, fb);
+ err = fuse_file_uncached_io_open(inode, ff, fb);
if (!err)
return 0;
@@ -216,7 +235,7 @@ int fuse_file_io_open(struct file *file, struct inode *inode)
if (ff->open_flags & FOPEN_PASSTHROUGH)
err = fuse_file_passthrough_open(inode, file);
else
- err = fuse_file_cached_io_start(inode, ff);
+ err = fuse_file_cached_io_open(inode, ff);
if (err)
goto fail;
@@ -236,8 +255,10 @@ 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 parallel dio close allows caching inode io mode.
+ * Last passthrough file close allows caching inode io mode.
* Last caching file close exits caching inode io mode.
*/
switch (ff->iomode) {
@@ -245,10 +266,10 @@ void fuse_file_io_release(struct fuse_file *ff, struct inode *inode)
/* Nothing to do */
break;
case IOM_UNCACHED:
- fuse_file_uncached_io_end(inode, ff);
+ fuse_file_uncached_io_release(ff, fi);
break;
case IOM_CACHED:
- fuse_file_cached_io_end(inode, ff);
+ fuse_file_cached_io_release(ff, fi);
break;
}
}
--
2.34.1
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH 2/3] fuse: fix parallel dio write on file open in passthrough mode
2024-04-07 15:57 [PATCH 0/3] FUSE passthrough fixes Amir Goldstein
2024-04-07 15:57 ` [PATCH 1/3] fuse: fix wrong ff->iomode state changes from parallel dio write Amir Goldstein
@ 2024-04-07 15:57 ` Amir Goldstein
2024-04-07 15:57 ` [PATCH 3/3] fuse: prepare for long lived reference on backing file Amir Goldstein
2 siblings, 0 replies; 10+ messages in thread
From: Amir Goldstein @ 2024-04-07 15:57 UTC (permalink / raw)
To: Miklos Szeredi; +Cc: Bernd Schubert, Sweet Tea Dorminy, linux-fsdevel
Paralle dio write takes a negative refcount of fi->iocachectr and so
does open of file in passthrough mode.
The refcount of passthrough mode is associated with attach/detach
of a fuse_backing object to fuse inode.
For parallel dio write, the backing file is irrelevant, so the call to
fuse_inode_uncached_io_start() passes a NULL fuse_backing object.
Passing a NULL fuse_backing will result in false -EBUSY error if
the file is already open in passthrough mode.
Allow taking negative fi->iocachectr refcount with NULL fuse_backing,
because it does not conflict with an already attached fuse_backing
object.
Fixes: 4a90451bbc7f ("fuse: implement open in passthrough mode")
Signed-off-by: Amir Goldstein <amir73il@gmail.com>
---
fs/fuse/iomode.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/fs/fuse/iomode.c b/fs/fuse/iomode.c
index 1519f895f0a9..f9e30c4540af 100644
--- a/fs/fuse/iomode.c
+++ b/fs/fuse/iomode.c
@@ -89,7 +89,7 @@ int fuse_inode_uncached_io_start(struct fuse_inode *fi, struct fuse_backing *fb)
spin_lock(&fi->lock);
/* deny conflicting backing files on same fuse inode */
oldfb = fuse_inode_backing(fi);
- if (oldfb && oldfb != fb) {
+ if (fb && oldfb && oldfb != fb) {
err = -EBUSY;
goto unlock;
}
@@ -100,7 +100,7 @@ int fuse_inode_uncached_io_start(struct fuse_inode *fi, struct fuse_backing *fb)
fi->iocachectr--;
/* fuse inode holds a single refcount of backing file */
- if (!oldfb) {
+ if (fb && !oldfb) {
oldfb = fuse_inode_backing_set(fi, fb);
WARN_ON_ONCE(oldfb != NULL);
} else {
--
2.34.1
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH 3/3] fuse: prepare for long lived reference on backing file
2024-04-07 15:57 [PATCH 0/3] FUSE passthrough fixes Amir Goldstein
2024-04-07 15:57 ` [PATCH 1/3] fuse: fix wrong ff->iomode state changes from parallel dio write Amir Goldstein
2024-04-07 15:57 ` [PATCH 2/3] fuse: fix parallel dio write on file open in passthrough mode Amir Goldstein
@ 2024-04-07 15:57 ` Amir Goldstein
2 siblings, 0 replies; 10+ messages in thread
From: Amir Goldstein @ 2024-04-07 15:57 UTC (permalink / raw)
To: Miklos Szeredi; +Cc: Bernd Schubert, Sweet Tea Dorminy, linux-fsdevel
Currently backing file is associated to fuse inode on the first
passthrough open of the inode and detached on last passthourgh close.
In preparation for attaching a backing file on lookup, allow attaching
a long lived (single) reference on fuse inode backing file that will be
dropped on fuse inode evict.
Signed-off-by: Amir Goldstein <amir73il@gmail.com>
---
fs/fuse/file.c | 2 +-
fs/fuse/fuse_i.h | 5 ++++-
fs/fuse/inode.c | 6 ++++++
fs/fuse/iomode.c | 14 +++++++++++---
4 files changed, 22 insertions(+), 5 deletions(-)
diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index fcf20b304093..347bae2b287f 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -1377,7 +1377,7 @@ static void fuse_dio_lock(struct kiocb *iocb, struct iov_iter *from,
* have raced, so check it again.
*/
if (fuse_io_past_eof(iocb, from) ||
- fuse_inode_uncached_io_start(fi, NULL) != 0) {
+ fuse_inode_uncached_io_start(fi, NULL, false) != 0) {
inode_unlock_shared(inode);
inode_lock(inode);
*exclusive = true;
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index f23919610313..2f340fd05e8a 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -209,6 +209,8 @@ enum {
FUSE_I_BTIME,
/* Wants or already has page cache IO */
FUSE_I_CACHE_IO_MODE,
+ /* Long lived backing file */
+ FUSE_I_BACKING,
};
struct fuse_conn;
@@ -1396,7 +1398,8 @@ 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 fuse_inode *fi,
- struct fuse_backing *fb);
+ struct fuse_backing *fb,
+ bool keep_fb);
void fuse_inode_uncached_io_end(struct fuse_inode *fi);
int fuse_file_io_open(struct file *file, struct inode *inode);
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c
index 99e44ea7d875..989a84f6a825 100644
--- a/fs/fuse/inode.c
+++ b/fs/fuse/inode.c
@@ -175,6 +175,12 @@ static void fuse_evict_inode(struct inode *inode)
}
}
if (S_ISREG(inode->i_mode) && !fuse_is_bad(inode)) {
+ /* fuse inode may have a long lived reference to backing file */
+ if (fuse_inode_backing(fi)) {
+ WARN_ON(!test_bit(FUSE_I_BACKING, &fi->state));
+ fuse_inode_uncached_io_end(fi);
+ }
+
WARN_ON(fi->iocachectr != 0);
WARN_ON(!list_empty(&fi->write_files));
WARN_ON(!list_empty(&fi->queued_writes));
diff --git a/fs/fuse/iomode.c b/fs/fuse/iomode.c
index f9e30c4540af..b1ff43668800 100644
--- a/fs/fuse/iomode.c
+++ b/fs/fuse/iomode.c
@@ -80,8 +80,14 @@ static void fuse_file_cached_io_release(struct fuse_file *ff,
spin_unlock(&fi->lock);
}
-/* Start strictly uncached io mode where cache access is not allowed */
-int fuse_inode_uncached_io_start(struct fuse_inode *fi, struct fuse_backing *fb)
+/*
+ * Start strictly uncached io mode where cache access is not allowed.
+ *
+ * With @keep_fb true, the backing file reference is expected to be dropped
+ * on inode evict.
+ */
+int fuse_inode_uncached_io_start(struct fuse_inode *fi, struct fuse_backing *fb,
+ bool keep_fb)
{
struct fuse_backing *oldfb;
int err = 0;
@@ -103,6 +109,8 @@ int fuse_inode_uncached_io_start(struct fuse_inode *fi, struct fuse_backing *fb)
if (fb && !oldfb) {
oldfb = fuse_inode_backing_set(fi, fb);
WARN_ON_ONCE(oldfb != NULL);
+ if (keep_fb)
+ set_bit(FUSE_I_BACKING, &fi->state);
} else {
fuse_backing_put(fb);
}
@@ -119,7 +127,7 @@ static int fuse_file_uncached_io_open(struct inode *inode,
struct fuse_inode *fi = get_fuse_inode(inode);
int err;
- err = fuse_inode_uncached_io_start(fi, fb);
+ err = fuse_inode_uncached_io_start(fi, fb, false);
if (err)
return err;
--
2.34.1
^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH 1/3] fuse: fix wrong ff->iomode state changes from parallel dio write
2024-04-07 15:57 ` [PATCH 1/3] fuse: fix wrong ff->iomode state changes from parallel dio write Amir Goldstein
@ 2024-04-09 13:33 ` Miklos Szeredi
2024-04-09 15:10 ` Amir Goldstein
0 siblings, 1 reply; 10+ messages in thread
From: Miklos Szeredi @ 2024-04-09 13:33 UTC (permalink / raw)
To: Amir Goldstein; +Cc: Bernd Schubert, Sweet Tea Dorminy, linux-fsdevel
On Sun, 7 Apr 2024 at 17:58, Amir Goldstein <amir73il@gmail.com> wrote:
>
> There is a confusion with fuse_file_uncached_io_{start,end} interface.
> These helpers do two things when called from passthrough open()/release():
> 1. Take/drop negative refcount of fi->iocachectr (inode uncached io mode)
> 2. State change ff->iomode IOM_NONE <-> IOM_UNCACHED (file uncached open)
>
> The calls from parallel dio write path need to take a reference on
> fi->iocachectr, but they should not be changing ff->iomode state,
> because in this case, the fi->iocachectr reference does not stick around
> until file release().
Okay.
>
> Factor out helpers fuse_inode_uncached_io_{start,end}, to be used from
> parallel dio write path and rename fuse_file_*cached_io_{start,end}
> helpers to fuse_file_*cached_io_{open,release} to clarify the difference.
>
> Add a check of ff->iomode in mmap(), so that fuse_file_cached_io_open()
> is called only on first mmap of direct_io file.
Is this supposed to be an optimization? AFAICS it's wrong, because it
moves the check outside of any relevant locks.
> @@ -56,8 +57,7 @@ int fuse_file_cached_io_start(struct inode *inode, struct fuse_file *ff)
> return -ETXTBSY;
> }
>
> - WARN_ON(ff->iomode == IOM_UNCACHED);
> - if (ff->iomode == IOM_NONE) {
> + if (!WARN_ON(ff->iomode != IOM_NONE)) {
This double negation is ugly. Just let the compiler optimize away the
second comparison.
Thanks,
Miklos
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 1/3] fuse: fix wrong ff->iomode state changes from parallel dio write
2024-04-09 13:33 ` Miklos Szeredi
@ 2024-04-09 15:10 ` Amir Goldstein
2024-04-09 15:32 ` Miklos Szeredi
0 siblings, 1 reply; 10+ messages in thread
From: Amir Goldstein @ 2024-04-09 15:10 UTC (permalink / raw)
To: Miklos Szeredi; +Cc: Bernd Schubert, Sweet Tea Dorminy, linux-fsdevel
On Tue, Apr 9, 2024 at 4:33 PM Miklos Szeredi <miklos@szeredi.hu> wrote:
>
> On Sun, 7 Apr 2024 at 17:58, Amir Goldstein <amir73il@gmail.com> wrote:
> >
> > There is a confusion with fuse_file_uncached_io_{start,end} interface.
> > These helpers do two things when called from passthrough open()/release():
> > 1. Take/drop negative refcount of fi->iocachectr (inode uncached io mode)
> > 2. State change ff->iomode IOM_NONE <-> IOM_UNCACHED (file uncached open)
> >
> > The calls from parallel dio write path need to take a reference on
> > fi->iocachectr, but they should not be changing ff->iomode state,
> > because in this case, the fi->iocachectr reference does not stick around
> > until file release().
>
> Okay.
>
> >
> > Factor out helpers fuse_inode_uncached_io_{start,end}, to be used from
> > parallel dio write path and rename fuse_file_*cached_io_{start,end}
> > helpers to fuse_file_*cached_io_{open,release} to clarify the difference.
> >
> > Add a check of ff->iomode in mmap(), so that fuse_file_cached_io_open()
> > is called only on first mmap of direct_io file.
>
> Is this supposed to be an optimization?
No.
The reason I did this is because I wanted to differentiate
the refcount semantics (start/end)
from the state semantics (open/release)
and to make it clearer that there is only one state change
and refcount increment on the first mmap().
> AFAICS it's wrong, because it
> moves the check outside of any relevant locks.
>
Aren't concurrent mmap serialized on some lock?
Anyway, I think that the only "bug" that this can trigger is the
WARN_ON(ff->iomode != IOM_NONE)
so if we ....
>
> > @@ -56,8 +57,7 @@ int fuse_file_cached_io_start(struct inode *inode, struct fuse_file *ff)
> > return -ETXTBSY;
> > }
> >
> > - WARN_ON(ff->iomode == IOM_UNCACHED);
> > - if (ff->iomode == IOM_NONE) {
> > + if (!WARN_ON(ff->iomode != IOM_NONE)) {
>
> This double negation is ugly. Just let the compiler optimize away the
> second comparison.
...drop this change, we should be good.
If you agree, do you need me to re-post?
Thanks,
Amir.
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 1/3] fuse: fix wrong ff->iomode state changes from parallel dio write
2024-04-09 15:10 ` Amir Goldstein
@ 2024-04-09 15:32 ` Miklos Szeredi
2024-04-09 16:18 ` Amir Goldstein
0 siblings, 1 reply; 10+ messages in thread
From: Miklos Szeredi @ 2024-04-09 15:32 UTC (permalink / raw)
To: Amir Goldstein; +Cc: Bernd Schubert, Sweet Tea Dorminy, linux-fsdevel
On Tue, 9 Apr 2024 at 17:10, Amir Goldstein <amir73il@gmail.com> wrote:
>
> On Tue, Apr 9, 2024 at 4:33 PM Miklos Szeredi <miklos@szeredi.hu> wrote:
> >
> > On Sun, 7 Apr 2024 at 17:58, Amir Goldstein <amir73il@gmail.com> wrote:
> > >
> > > There is a confusion with fuse_file_uncached_io_{start,end} interface.
> > > These helpers do two things when called from passthrough open()/release():
> > > 1. Take/drop negative refcount of fi->iocachectr (inode uncached io mode)
> > > 2. State change ff->iomode IOM_NONE <-> IOM_UNCACHED (file uncached open)
> > >
> > > The calls from parallel dio write path need to take a reference on
> > > fi->iocachectr, but they should not be changing ff->iomode state,
> > > because in this case, the fi->iocachectr reference does not stick around
> > > until file release().
> >
> > Okay.
> >
> > >
> > > Factor out helpers fuse_inode_uncached_io_{start,end}, to be used from
> > > parallel dio write path and rename fuse_file_*cached_io_{start,end}
> > > helpers to fuse_file_*cached_io_{open,release} to clarify the difference.
> > >
> > > Add a check of ff->iomode in mmap(), so that fuse_file_cached_io_open()
> > > is called only on first mmap of direct_io file.
> >
> > Is this supposed to be an optimization?
>
> No.
> The reason I did this is because I wanted to differentiate
> the refcount semantics (start/end)
> from the state semantics (open/release)
> and to make it clearer that there is only one state change
> and refcount increment on the first mmap().
>
> > AFAICS it's wrong, because it
> > moves the check outside of any relevant locks.
> >
>
> Aren't concurrent mmap serialized on some lock?
Only on current->mm, which doesn't serialize mmaps of the same file in
different processes.
>
> Anyway, I think that the only "bug" that this can trigger is the
> WARN_ON(ff->iomode != IOM_NONE)
> so if we ....
>
> >
> > > @@ -56,8 +57,7 @@ int fuse_file_cached_io_start(struct inode *inode, struct fuse_file *ff)
> > > return -ETXTBSY;
> > > }
> > >
> > > - WARN_ON(ff->iomode == IOM_UNCACHED);
> > > - if (ff->iomode == IOM_NONE) {
> > > + if (!WARN_ON(ff->iomode != IOM_NONE)) {
> >
> > This double negation is ugly. Just let the compiler optimize away the
> > second comparison.
>
> ...drop this change, we should be good.
>
> If you agree, do you need me to re-post?
Okay, but then what's the point of the unlocked check?
Thanks,
Miklos
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 1/3] fuse: fix wrong ff->iomode state changes from parallel dio write
2024-04-09 15:32 ` Miklos Szeredi
@ 2024-04-09 16:18 ` Amir Goldstein
2024-04-13 6:50 ` Amir Goldstein
0 siblings, 1 reply; 10+ messages in thread
From: Amir Goldstein @ 2024-04-09 16:18 UTC (permalink / raw)
To: Miklos Szeredi; +Cc: Bernd Schubert, Sweet Tea Dorminy, linux-fsdevel
On Tue, Apr 9, 2024 at 6:32 PM Miklos Szeredi <miklos@szeredi.hu> wrote:
>
> On Tue, 9 Apr 2024 at 17:10, Amir Goldstein <amir73il@gmail.com> wrote:
> >
> > On Tue, Apr 9, 2024 at 4:33 PM Miklos Szeredi <miklos@szeredi.hu> wrote:
> > >
> > > On Sun, 7 Apr 2024 at 17:58, Amir Goldstein <amir73il@gmail.com> wrote:
> > > >
> > > > There is a confusion with fuse_file_uncached_io_{start,end} interface.
> > > > These helpers do two things when called from passthrough open()/release():
> > > > 1. Take/drop negative refcount of fi->iocachectr (inode uncached io mode)
> > > > 2. State change ff->iomode IOM_NONE <-> IOM_UNCACHED (file uncached open)
> > > >
> > > > The calls from parallel dio write path need to take a reference on
> > > > fi->iocachectr, but they should not be changing ff->iomode state,
> > > > because in this case, the fi->iocachectr reference does not stick around
> > > > until file release().
> > >
> > > Okay.
> > >
> > > >
> > > > Factor out helpers fuse_inode_uncached_io_{start,end}, to be used from
> > > > parallel dio write path and rename fuse_file_*cached_io_{start,end}
> > > > helpers to fuse_file_*cached_io_{open,release} to clarify the difference.
> > > >
> > > > Add a check of ff->iomode in mmap(), so that fuse_file_cached_io_open()
> > > > is called only on first mmap of direct_io file.
> > >
> > > Is this supposed to be an optimization?
> >
> > No.
> > The reason I did this is because I wanted to differentiate
> > the refcount semantics (start/end)
> > from the state semantics (open/release)
> > and to make it clearer that there is only one state change
> > and refcount increment on the first mmap().
> >
> > > AFAICS it's wrong, because it
> > > moves the check outside of any relevant locks.
> > >
> >
> > Aren't concurrent mmap serialized on some lock?
>
> Only on current->mm, which doesn't serialize mmaps of the same file in
> different processes.
>
> >
> > Anyway, I think that the only "bug" that this can trigger is the
> > WARN_ON(ff->iomode != IOM_NONE)
> > so if we ....
> >
> > >
> > > > @@ -56,8 +57,7 @@ int fuse_file_cached_io_start(struct inode *inode, struct fuse_file *ff)
> > > > return -ETXTBSY;
> > > > }
> > > >
> > > > - WARN_ON(ff->iomode == IOM_UNCACHED);
> > > > - if (ff->iomode == IOM_NONE) {
> > > > + if (!WARN_ON(ff->iomode != IOM_NONE)) {
> > >
> > > This double negation is ugly. Just let the compiler optimize away the
> > > second comparison.
> >
> > ...drop this change, we should be good.
> >
> > If you agree, do you need me to re-post?
>
> Okay, but then what's the point of the unlocked check?
As I wrote, I just did it to emphasize the open-once
semantics.
If you do not like the unlocked check, feel free to remove it.
Thanks,
Amir.
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 1/3] fuse: fix wrong ff->iomode state changes from parallel dio write
2024-04-09 16:18 ` Amir Goldstein
@ 2024-04-13 6:50 ` Amir Goldstein
2024-04-15 8:14 ` Miklos Szeredi
0 siblings, 1 reply; 10+ messages in thread
From: Amir Goldstein @ 2024-04-13 6:50 UTC (permalink / raw)
To: Miklos Szeredi; +Cc: Bernd Schubert, Sweet Tea Dorminy, linux-fsdevel
On Tue, Apr 9, 2024 at 7:18 PM Amir Goldstein <amir73il@gmail.com> wrote:
>
> On Tue, Apr 9, 2024 at 6:32 PM Miklos Szeredi <miklos@szeredi.hu> wrote:
> >
> > On Tue, 9 Apr 2024 at 17:10, Amir Goldstein <amir73il@gmail.com> wrote:
> > >
> > > On Tue, Apr 9, 2024 at 4:33 PM Miklos Szeredi <miklos@szeredi.hu> wrote:
> > > >
> > > > On Sun, 7 Apr 2024 at 17:58, Amir Goldstein <amir73il@gmail.com> wrote:
> > > > >
> > > > > There is a confusion with fuse_file_uncached_io_{start,end} interface.
> > > > > These helpers do two things when called from passthrough open()/release():
> > > > > 1. Take/drop negative refcount of fi->iocachectr (inode uncached io mode)
> > > > > 2. State change ff->iomode IOM_NONE <-> IOM_UNCACHED (file uncached open)
> > > > >
> > > > > The calls from parallel dio write path need to take a reference on
> > > > > fi->iocachectr, but they should not be changing ff->iomode state,
> > > > > because in this case, the fi->iocachectr reference does not stick around
> > > > > until file release().
> > > >
> > > > Okay.
> > > >
> > > > >
> > > > > Factor out helpers fuse_inode_uncached_io_{start,end}, to be used from
> > > > > parallel dio write path and rename fuse_file_*cached_io_{start,end}
> > > > > helpers to fuse_file_*cached_io_{open,release} to clarify the difference.
> > > > >
> > > > > Add a check of ff->iomode in mmap(), so that fuse_file_cached_io_open()
> > > > > is called only on first mmap of direct_io file.
> > > >
> > > > Is this supposed to be an optimization?
> > >
> > > No.
> > > The reason I did this is because I wanted to differentiate
> > > the refcount semantics (start/end)
> > > from the state semantics (open/release)
> > > and to make it clearer that there is only one state change
> > > and refcount increment on the first mmap().
> > >
> > > > AFAICS it's wrong, because it
> > > > moves the check outside of any relevant locks.
> > > >
> > >
> > > Aren't concurrent mmap serialized on some lock?
> >
> > Only on current->mm, which doesn't serialize mmaps of the same file in
> > different processes.
> >
> > >
> > > Anyway, I think that the only "bug" that this can trigger is the
> > > WARN_ON(ff->iomode != IOM_NONE)
> > > so if we ....
> > >
> > > >
> > > > > @@ -56,8 +57,7 @@ int fuse_file_cached_io_start(struct inode *inode, struct fuse_file *ff)
> > > > > return -ETXTBSY;
> > > > > }
> > > > >
> > > > > - WARN_ON(ff->iomode == IOM_UNCACHED);
> > > > > - if (ff->iomode == IOM_NONE) {
> > > > > + if (!WARN_ON(ff->iomode != IOM_NONE)) {
> > > >
> > > > This double negation is ugly. Just let the compiler optimize away the
> > > > second comparison.
> > >
> > > ...drop this change, we should be good.
> > >
> > > If you agree, do you need me to re-post?
> >
> > Okay, but then what's the point of the unlocked check?
>
> As I wrote, I just did it to emphasize the open-once
> semantics.
> If you do not like the unlocked check, feel free to remove it.
Miklos,
I see that you removed the unlocked check in the fix staged for-next.
Please also remove this from commit message:
Add a check of ff->iomode in mmap(), so that fuse_file_cached_io_open() is
called only on first mmap of direct_io file.
Thanks,
Amir.
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 1/3] fuse: fix wrong ff->iomode state changes from parallel dio write
2024-04-13 6:50 ` Amir Goldstein
@ 2024-04-15 8:14 ` Miklos Szeredi
0 siblings, 0 replies; 10+ messages in thread
From: Miklos Szeredi @ 2024-04-15 8:14 UTC (permalink / raw)
To: Amir Goldstein; +Cc: Bernd Schubert, Sweet Tea Dorminy, linux-fsdevel
On Sat, 13 Apr 2024 at 08:50, Amir Goldstein <amir73il@gmail.com> wrote:
> I see that you removed the unlocked check in the fix staged for-next.
> Please also remove this from commit message:
>
> Add a check of ff->iomode in mmap(), so that fuse_file_cached_io_open() is
> called only on first mmap of direct_io file.
Done.
Thanks,
Miklos
^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2024-04-15 8:15 UTC | newest]
Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-04-07 15:57 [PATCH 0/3] FUSE passthrough fixes Amir Goldstein
2024-04-07 15:57 ` [PATCH 1/3] fuse: fix wrong ff->iomode state changes from parallel dio write Amir Goldstein
2024-04-09 13:33 ` Miklos Szeredi
2024-04-09 15:10 ` Amir Goldstein
2024-04-09 15:32 ` Miklos Szeredi
2024-04-09 16:18 ` Amir Goldstein
2024-04-13 6:50 ` Amir Goldstein
2024-04-15 8:14 ` Miklos Szeredi
2024-04-07 15:57 ` [PATCH 2/3] fuse: fix parallel dio write on file open in passthrough mode Amir Goldstein
2024-04-07 15:57 ` [PATCH 3/3] fuse: prepare for long lived reference on backing file Amir Goldstein
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).