* [Devel] [PATCH hci-8.1 0/3] fs/fuse: rework close_wait handling
@ 2026-09-30 5:44 Liu Kui
2026-09-30 5:44 ` [Devel] [PATCH hci-8.1 1/3] fs/fuse: give num_openers and i_size_unstable each a single role Liu Kui
` (3 more replies)
0 siblings, 4 replies; 8+ messages in thread
From: Liu Kui @ 2026-09-30 5:44 UTC (permalink / raw)
To: Konstantin Khorenko; +Cc: azaitsev, devel, Liu Kui
Rework the close_wait handling around fuse file open/close:
- give num_openers and i_size_unstable each a single role: the flag
alone decides when attributes are taken from the server, the
counter only drives kio's file_open/file_close pairing
- move all the close_wait handling of fuse_release() into a new
helper and restore the !close_wait case to the mainline behavior;
the atomic last-open detection under the inode lock fixes two
racing releasers both calling kio's file_close
- decouple the releaser's wait for ff->count from fi->page_waitq:
switch it to wait_var_event() and fold the matching wake_up_var()
into fuse_file_put(), so the wakeup can never be separated from
the reference drop again (see commit 3b2cc738990e ("fs/fuse: wake
close_wait waiters when fuse_writepage_locked() bails out") for
the most recent instance of a drop site missing the wakeup),
removing __fuse_file_put() and fuse_release_ff()
https://virtuozzo.atlassian.net/browse/VSTOR-147878
---
Liu Kui (3):
fs/fuse: give num_openers and i_size_unstable each a single role
fs/fuse: move the close_wait handling of fuse_release() into a helper
fs/fuse: use wait_var_event to wait for fuse file references
fs/fuse/file.c | 177 +++++++++++++++++++------------------
fs/fuse/fuse_i.h | 6 +-
fs/fuse/inode.c | 5 +-
fs/fuse/kio/pcs/pcs_fuse_kdirect.c | 3 +-
4 files changed, 101 insertions(+), 90 deletions(-)
---
base-commit: ca54f152160587a74ff17cb52feef16f7b9d4e2b
change-id: 20260928-close-wait-rework-v2-7cac44e6a295
Best regards,
--
Liu Kui <kui.liu@virtuozzo.com>
_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel
^ permalink raw reply [flat|nested] 8+ messages in thread
* [Devel] [PATCH hci-8.1 1/3] fs/fuse: give num_openers and i_size_unstable each a single role
2026-09-30 5:44 [Devel] [PATCH hci-8.1 0/3] fs/fuse: rework close_wait handling Liu Kui
@ 2026-09-30 5:44 ` Liu Kui
2026-09-30 5:50 ` no-reply
2026-09-30 5:44 ` [Devel] [PATCH hci-8.1 2/3] fs/fuse: move the close_wait handling of fuse_release() into a helper Liu Kui
` (2 subsequent siblings)
3 siblings, 1 reply; 8+ messages in thread
From: Liu Kui @ 2026-09-30 5:44 UTC (permalink / raw)
To: Konstantin Khorenko; +Cc: azaitsev, devel, Liu Kui
Currently fi->num_openers serves two purposes: it helps decide whether
the i_size attribute needs to be taken from the server, and it manages
when kio's file_open/file_close methods need to be called. The former
role already requires the extra i_size_unstable flag to handle lease
revokes, which makes num_openers redundant for that purpose.
Give each field a single role: use i_size_unstable as the "take the
attributes from the server" flag, and make num_openers a pure open
count managing the calls of kio's file_open/file_close. A new inode
starts with the i_size_unstable flag set, an open finding it set
refreshes the attributes and clears it on success, and the last
release sets it back after writeback has drained.
Also fix a bug where a failed refresh could re-trust stale local
attributes.
https://virtuozzo.atlassian.net/browse/VSTOR-147878
Signed-off-by: Liu Kui <kui.liu@virtuozzo.com>
---
fs/fuse/file.c | 21 ++++++++++++++++-----
fs/fuse/fuse_i.h | 4 ++--
fs/fuse/inode.c | 5 +++--
3 files changed, 21 insertions(+), 9 deletions(-)
diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index e0efb524cda7..c41d6445c7e5 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -293,8 +293,14 @@ static int fuse_open_close_wait(struct inode *inode, struct file *file)
inode_lock(inode);
spin_lock(&fi->lock);
- if (++fi->num_openers == 1 || fi->i_size_unstable) {
- fi->i_size_unstable = 1;
+ fi->num_openers++;
+ /*
+ * i_size_unstable set means the attributes must be taken from the
+ * server: no open has refreshed them yet (first open), the last
+ * opener is gone, or the files were invalidated by a lease revoke.
+ * Refresh the attributes so ours can be trusted again.
+ */
+ if (fi->i_size_unstable) {
fi->inval_mask = ~0;
spin_unlock(&fi->lock);
err = fuse_update_attributes(inode, file, ~0);
@@ -303,9 +309,10 @@ static int fuse_open_close_wait(struct inode *inode, struct file *file)
err = fc->kio.op->file_open(file, inode);
spin_lock(&fi->lock);
- fi->i_size_unstable = 0;
if (err)
fi->num_openers--;
+ else
+ fi->i_size_unstable = 0;
}
file->f_mode |= FMODE_NOWAIT;
@@ -583,8 +590,12 @@ static int fuse_release(struct inode *inode, struct file *file)
wait_event(fi->page_waitq, refcount_read(&ff->count) == 1);
spin_lock(&fi->lock);
- /* since now we can trust userspace attr.size */
- fi->num_openers--;
+ /*
+ * Last opener is gone and its writeback has drained: from
+ * now on take the attributes from the server again.
+ */
+ if (--fi->num_openers == 0)
+ fi->i_size_unstable = 1;
spin_unlock(&fi->lock);
} else if (fc->close_wait)
wait_event(fi->page_waitq, refcount_read(&ff->count) == 1);
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index 21f873337627..f6da643b9c86 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -200,10 +200,10 @@ struct fuse_inode {
/** Reference to backing file in passthrough mode */
struct fuse_backing *fb;
#endif
- /** Mostly to detect very first open */
+ /** Number of opens, drives kio file_open/file_close pairing */
int num_openers;
- /** Even though num_openers>0, trust server i_size */
+ /** Take attributes (i_size) from the server, don't trust ours */
int i_size_unstable;
/** Private kdirect io context */
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c
index eb2e9a4f8756..4eaa89d9c46c 100644
--- a/fs/fuse/inode.c
+++ b/fs/fuse/inode.c
@@ -116,7 +116,8 @@ static struct inode *fuse_alloc_inode(struct super_block *sb)
fi->orig_ino = 0;
fi->state = 0;
fi->submount_lookup = NULL;
- fi->i_size_unstable = 0;
+ /* take attributes from the server until the first open */
+ fi->i_size_unstable = 1;
fi->private = NULL;
fi->inval_files_seq = 0;
INIT_LIST_HEAD(&fi->rw_files);
@@ -328,7 +329,7 @@ u32 fuse_get_cache_mask(struct inode *inode)
if (fc->close_wait) {
struct fuse_inode *fi = get_fuse_inode(inode);
- if (!fi->num_openers || fi->i_size_unstable)
+ if (fi->i_size_unstable)
return 0;
}
--
Git-155)
_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel
^ permalink raw reply [flat|nested] 8+ messages in thread
* [Devel] [PATCH hci-8.1 2/3] fs/fuse: move the close_wait handling of fuse_release() into a helper
2026-09-30 5:44 [Devel] [PATCH hci-8.1 0/3] fs/fuse: rework close_wait handling Liu Kui
2026-09-30 5:44 ` [Devel] [PATCH hci-8.1 1/3] fs/fuse: give num_openers and i_size_unstable each a single role Liu Kui
@ 2026-09-30 5:44 ` Liu Kui
2026-09-30 5:49 ` no-reply
2026-09-30 5:44 ` [Devel] [PATCH hci-8.1 3/3] fs/fuse: use wait_var_event to wait for fuse file references Liu Kui
2026-09-30 15:39 ` [Devel] [PATCH hci-8.1 0/3] fs/fuse: rework close_wait handling Konstantin Khorenko
3 siblings, 1 reply; 8+ messages in thread
From: Liu Kui @ 2026-09-30 5:44 UTC (permalink / raw)
To: Konstantin Khorenko; +Cc: azaitsev, devel, Liu Kui
Move all the close_wait handling of fuse_release() into a new helper
fuse_release_close_wait(), and restore the !close_wait case to the
mainline behavior except the FMODE_WRITE check.
Unify the code paths: unconditionally remove the file from
fi->write_files and wait until we hold the last reference. Decrement
fi->num_openers under the inode lock, which detects the last open.
This fixes two racing releasers both reading num_openers == 0 and
calling kio's file_close twice, possible because the decrement was
done early under fi->lock while the check ran later under the inode
lock:
releaser A releaser B
---------- ----------
--fi->num_openers (2 -> 1)
--fi->num_openers (1 -> 0)
inode_lock()
num_openers == 0 -> file_close()
inode_unlock()
inode_lock()
num_openers == 0 -> file_close()
The atomic decrement under the inode lock also matches the increment
in fuse_open_close_wait() for !writeback_cache files.
https://virtuozzo.atlassian.net/browse/VSTOR-147878
Signed-off-by: Liu Kui <kui.liu@virtuozzo.com>
---
fs/fuse/file.c | 103 ++++++++++++++++++++++++++-------------------------------
1 file changed, 46 insertions(+), 57 deletions(-)
diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index c41d6445c7e5..d7c9ea8a7c45 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -537,82 +537,71 @@ void fuse_file_release(struct inode *inode, struct fuse_file *ff,
fuse_file_put(ff, ff->fm->fc->destroy || ff->fm->fc->close_wait);
}
-void fuse_release_common(struct file *file, bool isdir)
-{
- fuse_file_release(file_inode(file), file->private_data, file->f_flags,
- (fl_owner_t) file, isdir);
-}
-
static void fuse_sync_writes(struct inode *inode);
-static int fuse_release(struct inode *inode, struct file *file)
+static void fuse_release_close_wait(struct inode *inode, struct file *file)
{
struct fuse_file *ff = file->private_data;
struct fuse_inode *fi = get_fuse_inode(inode);
struct fuse_conn *fc = ff->fm->fc;
- set_bit(FUSE_S_CLOSING, &ff->ff_state);
+ /*
+ * Stop the write rerouting from picking this file, otherwise
+ * continuous writeback on the inode can keep the reference count
+ * elevated and the wait below may never finish.
+ */
spin_lock(&fi->lock);
+ list_del_init(&ff->write_entry);
spin_unlock(&fi->lock);
/*
- * Dirty pages might remain despite write_inode_now() call from
- * fuse_flush() due to writes racing with the close.
+ * Wait until we are the last one holding a reference, so that all
+ * in-flight requests on this file have finished and FUSE_RELEASE
+ * is sent by the releaser itself.
*/
- if (fc->writeback_cache) {
- if (file->f_mode & FMODE_WRITE) {
- write_inode_now(inode, 1);
+ wait_event(fi->page_waitq, refcount_read(&ff->count) == 1);
- /* Must remove file from write list. Otherwise it is possible this
- * file will get more writeback from another files rerouted via write_files
- */
- spin_lock(&fi->lock);
- list_del_init(&ff->write_entry);
- spin_unlock(&fi->lock);
-
- /* A writeback from another fuse file might come after
- * filemap_write_and_wait() above
- */
- if (!fc->close_wait)
- filemap_write_and_wait(file->f_mapping);
- } else
- BUG_ON(!list_empty(&ff->write_entry));
-
- /* This can livelock. Inode can be open via another file
- * and that file can generate continuous writeback.
- * I think i_mutex could be taken around this.
- *
- * For now we replace this with waiting on ff->count,
- * it is safe, because we essentially wait only for writeback (and readahead)
- * enqueued on this file and it is not going to get new one: it is closing.
- */
- if (fc->close_wait)
- wait_event(fi->page_waitq, refcount_read(&ff->count) == 1);
+ inode_lock(inode);
+ fuse_sync_writes(inode);
+ fuse_read_dio_wait(fi);
+ /* num_openers is serialized by the inode lock, no fi->lock needed */
+ if (--fi->num_openers == 0) {
+ /* take the attributes from the server again */
spin_lock(&fi->lock);
- /*
- * Last opener is gone and its writeback has drained: from
- * now on take the attributes from the server again.
- */
- if (--fi->num_openers == 0)
- fi->i_size_unstable = 1;
+ fi->i_size_unstable = 1;
spin_unlock(&fi->lock);
- } else if (fc->close_wait)
- wait_event(fi->page_waitq, refcount_read(&ff->count) == 1);
-
- if (fc->kio.op) {
- /*
- * Flush pending requests before FUSE_RELEASE makes userspace
- * to drop the lease of the file. Otherwise, they never finish.
- */
- inode_lock(inode);
- fuse_sync_writes(inode);
- fuse_read_dio_wait(fi);
- if (fi->num_openers == 0 && fc->kio.op->file_close)
+ if (fc->kio.op && fc->kio.op->file_close)
fc->kio.op->file_close(file, inode);
- inode_unlock(inode);
}
+ inode_unlock(inode);
+}
+
+void fuse_release_common(struct file *file, bool isdir)
+{
+ fuse_file_release(file_inode(file), file->private_data, file->f_flags,
+ (fl_owner_t) file, isdir);
+}
+
+static int fuse_release(struct inode *inode, struct file *file)
+{
+ struct fuse_file *ff = file->private_data;
+ struct fuse_conn *fc = get_fuse_conn(inode);
+
+ /* stop fiemap from picking this file before flushing starts */
+ if (fc->close_wait)
+ set_bit(FUSE_S_CLOSING, &ff->ff_state);
+
+ /*
+ * Dirty pages might remain despite write_inode_now() call from
+ * fuse_flush() due to writes racing with the close.
+ */
+ if (fc->writeback_cache && (file->f_mode & FMODE_WRITE))
+ write_inode_now(inode, 1);
+
+ if (fc->close_wait)
+ fuse_release_close_wait(inode, file);
fuse_release_common(file, false);
--
Git-155)
_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel
^ permalink raw reply [flat|nested] 8+ messages in thread
* [Devel] [PATCH hci-8.1 3/3] fs/fuse: use wait_var_event to wait for fuse file references
2026-09-30 5:44 [Devel] [PATCH hci-8.1 0/3] fs/fuse: rework close_wait handling Liu Kui
2026-09-30 5:44 ` [Devel] [PATCH hci-8.1 1/3] fs/fuse: give num_openers and i_size_unstable each a single role Liu Kui
2026-09-30 5:44 ` [Devel] [PATCH hci-8.1 2/3] fs/fuse: move the close_wait handling of fuse_release() into a helper Liu Kui
@ 2026-09-30 5:44 ` Liu Kui
2026-09-30 5:51 ` no-reply
2026-09-30 15:39 ` [Devel] [PATCH hci-8.1 0/3] fs/fuse: rework close_wait handling Konstantin Khorenko
3 siblings, 1 reply; 8+ messages in thread
From: Liu Kui @ 2026-09-30 5:44 UTC (permalink / raw)
To: Konstantin Khorenko; +Cc: azaitsev, devel, Liu Kui
On close_wait mounts, fuse_release_close_wait() needs to wait for all
asynchronous references to the fuse file to be dropped before sending
FUSE_RELEASE. This wait on fi->page_waitq was inherited from the past
implementation and is no longer appropriate as the wait condition
evolved:
- fi->page_waitq is meant to serve page-under-writeback waiters and
fuse_set_nowrite()'s writectr waiters; now it also has to serve the
ff reference waiter, so every wakeup can wake unrelated waiters.
- the wake is decoupled from the decrement, so every path dropping an
ff reference must remember to wake fi->page_waitq, and because of
this we grew two variants of the stock fuse_file_put():
__fuse_file_put() and fuse_release_ff(). This is bug prone, and a
misuse can leave the releaser blocked forever.
Instead of waiting on fi->page_waitq, a simple wait_var_event() on
&ff->count serves the purpose best: the matching wake_up_var() uses
the address purely as a hash key and never dereferences it, so the
wakeup can be folded into fuse_file_put() itself. With that, both
__fuse_file_put() and fuse_release_ff() become redundant and are
removed. This cleanup also makes the code less divergent from
mainline.
https://virtuozzo.atlassian.net/browse/VSTOR-147878
Signed-off-by: Liu Kui <kui.liu@virtuozzo.com>
---
fs/fuse/file.c | 69 +++++++++++++++++++++-----------------
fs/fuse/fuse_i.h | 2 +-
fs/fuse/kio/pcs/pcs_fuse_kdirect.c | 3 +-
3 files changed, 42 insertions(+), 32 deletions(-)
diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index d7c9ea8a7c45..74c8d925e2fd 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -24,6 +24,7 @@
#include <linux/fiemap.h>
#include <linux/jhash.h>
#include <linux/file.h>
+#include <linux/wait_bit.h>
unsigned int hash_seed_r;
module_param(hash_seed_r, uint, 0644);
@@ -140,12 +141,23 @@ static void fuse_release_end(struct fuse_mount *fm, struct fuse_args *args,
kfree(ra);
}
-static void fuse_file_put(struct fuse_file *ff, bool sync)
+void fuse_file_put(struct fuse_file *ff, bool sync)
{
+ /* read before the drop: ff may not be touched after a non-final one */
+ bool close_wait = ff->fm->fc->close_wait;
+
if (refcount_dec_and_test(&ff->count)) {
struct fuse_release_args *ra = &ff->args->release_args;
struct fuse_args *args = (ra ? &ra->args : NULL);
+ /*
+ * On close_wait the final put must come from a synchronous
+ * caller: fuse_release() waits for ff->count == 1 while
+ * holding its own reference, so an asynchronous completion
+ * can never drop the last one.
+ */
+ WARN_ON(!sync && close_wait);
+
if (ra && ra->inode)
fuse_file_io_release(ff, ra->inode);
@@ -165,14 +177,25 @@ static void fuse_file_put(struct fuse_file *ff, bool sync)
fuse_release_end(ff->fm, args, -ENOTCONN);
}
kfree(ff);
+ return;
}
-}
-static void __fuse_file_put(struct fuse_file *ff)
-{
- if (refcount_dec_and_test(&ff->count))
- BUG();
+ /*
+ * ff may already be freed by a racing final put -- don't touch it
+ * past this point.
+ */
+ if (close_wait) {
+ /*
+ * Pairs with wait_var_event() in fuse_release_close_wait():
+ * the wake_up_var() contract requires a full barrier between
+ * the counter update and the wakeup, and refcount_dec()
+ * alone is only release-ordered.
+ */
+ smp_mb__after_atomic();
+ wake_up_var(&ff->count);
+ }
}
+EXPORT_SYMBOL_GPL(fuse_file_put);
struct fuse_file *fuse_file_open(struct fuse_mount *fm, u64 nodeid,
unsigned int open_flags, bool isdir)
@@ -557,9 +580,10 @@ static void fuse_release_close_wait(struct inode *inode, struct file *file)
/*
* Wait until we are the last one holding a reference, so that all
* in-flight requests on this file have finished and FUSE_RELEASE
- * is sent by the releaser itself.
+ * is sent by the releaser itself. Woken by the wake_up_var() in
+ * fuse_file_put().
*/
- wait_event(fi->page_waitq, refcount_read(&ff->count) == 1);
+ wait_var_event(&ff->count, refcount_read(&ff->count) == 1);
inode_lock(inode);
fuse_sync_writes(inode);
@@ -1252,22 +1276,6 @@ static int fuse_read_folio(struct file *file, struct folio *folio)
return err;
}
-void fuse_release_ff(struct inode *inode, struct fuse_file *ff)
-{
- if (ff) {
- if (ff->fm->fc->close_wait) {
- struct fuse_inode *fi = get_fuse_inode(inode);
- spin_lock(&fi->lock);
- __fuse_file_put(ff);
- wake_up(&fi->page_waitq);
- spin_unlock(&fi->lock);
- } else {
- fuse_file_put(ff, false);
- }
- }
-}
-EXPORT_SYMBOL_GPL(fuse_release_ff);
-
void fuse_revoke_readpages(struct fuse_file *ff)
{
struct fuse_io_args *ia;
@@ -1330,7 +1338,7 @@ static void fuse_readpages_end(struct fuse_mount *fm, struct fuse_args *args,
fuse_invalidate_atime(inode);
if (ff)
- fuse_release_ff(inode, ff);
+ fuse_file_put(ff, false);
fuse_io_free(ia);
}
@@ -2189,7 +2197,7 @@ static void fuse_writepage_free(struct fuse_writepage_args *wpa)
for (i = 0; i < ap->num_pages; i++)
__free_page(ap->pages[i]);
- fuse_release_ff(wpa->inode, wpa->ia.ff);
+ fuse_file_put(wpa->ia.ff, false);
kfree(ap->pages);
kfree(wpa);
@@ -2572,7 +2580,7 @@ static int fuse_writepage_locked(struct folio *folio)
return 0;
err_writepage_args:
- fuse_release_ff(inode, ff);
+ fuse_file_put(ff, false);
err_nofile:
folio_put(tmp_folio);
err:
@@ -2859,7 +2867,8 @@ static int fuse_writepages(struct address_space *mapping,
WARN_ON(!data.wpa->ia.ap.num_pages);
fuse_writepages_send(&data);
}
- fuse_release_ff(inode, data.ff);
+ if (data.ff)
+ fuse_file_put(data.ff, false);
kfree(data.orig_pages);
out:
@@ -3929,7 +3938,7 @@ static int fuse_request_fiemap(struct inode *inode, u32 cur_max,
ap.pages = fuse_pages_alloc(fc->max_pages, GFP_KERNEL, &ap.descs);
if (!ap.pages) {
- fuse_release_ff(inode, ff);
+ fuse_file_put(ff, false);
return -ENOMEM;
}
@@ -4021,7 +4030,7 @@ static int fuse_request_fiemap(struct inode *inode, u32 cur_max,
ap.pages[allocated] = NULL;
}
kfree(ap.pages);
- fuse_release_ff(inode, ff);
+ fuse_file_put(ff, false);
return err;
}
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index f6da643b9c86..e94d40458af3 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -1673,7 +1673,7 @@ int fuse_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
struct fuse_file *__fuse_write_file_get(struct fuse_inode *fi);
struct fuse_file *fuse_file_get(struct fuse_file *ff);
-void fuse_release_ff(struct inode *inode, struct fuse_file *ff);
+void fuse_file_put(struct fuse_file *ff, bool sync);
struct fuse_req *fuse_dev_find_request(unsigned int fd, u64 unique);
void fuse_revoke_readpages(struct fuse_file *ff);
diff --git a/fs/fuse/kio/pcs/pcs_fuse_kdirect.c b/fs/fuse/kio/pcs/pcs_fuse_kdirect.c
index efbab75654c8..5064198a7b4c 100644
--- a/fs/fuse/kio/pcs/pcs_fuse_kdirect.c
+++ b/fs/fuse/kio/pcs/pcs_fuse_kdirect.c
@@ -728,7 +728,8 @@ static int submit_size_grow(struct inode *inode, unsigned long long size)
args.out_args[0].value = &outarg;
err = fuse_simple_request(fm, &args);
- fuse_release_ff(inode, ff);
+ if (ff)
+ fuse_file_put(ff, false);
return err;
--
Git-155)
_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [Devel] [PATCH hci-8.1 2/3] fs/fuse: move the close_wait handling of fuse_release() into a helper
2026-09-30 5:44 ` [Devel] [PATCH hci-8.1 2/3] fs/fuse: move the close_wait handling of fuse_release() into a helper Liu Kui
@ 2026-09-30 5:49 ` no-reply
0 siblings, 0 replies; 8+ messages in thread
From: no-reply @ 2026-09-30 5:49 UTC (permalink / raw)
To: Konstantin Khorenko, Liu Kui; +Cc: azaitsev, devel
> Move all the close_wait handling of fuse_release() into a new helper
> fuse_release_close_wait(), and restore the !close_wait case to the
> mainline behavior except the FMODE_WRITE check.
>
> Unify the code paths: unconditionally remove the file from
> fi->write_files and wait until we hold the last reference. Decrement
> fi->num_openers under the inode lock, which detects the last open.
> This fixes two racing releasers both reading num_openers == 0 and
> calling kio's file_close twice, possible because the decrement was
> done early under fi->lock while the check ran later under the inode
> lock:
>
> releaser A releaser B
> ---------- ----------
> --fi->num_openers (2 -> 1)
> --fi->num_openers (1 -> 0)
> inode_lock()
> num_openers == 0 -> file_close()
> inode_unlock()
> inode_lock()
> num_openers == 0 -> file_close()
>
> The atomic decrement under the inode lock also matches the increment
> in fuse_open_close_wait() for !writeback_cache files.
>
> https://virtuozzo.atlassian.net/browse/VSTOR-147878
>
> Signed-off-by: Liu Kui <kui.liu@virtuozzo.com>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.vzint.dev/#/patchset/20260930-close-wait-rework-v2-v1-0-f8062178544a@virtuozzo.com?part=2
_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [Devel] [PATCH hci-8.1 1/3] fs/fuse: give num_openers and i_size_unstable each a single role
2026-09-30 5:44 ` [Devel] [PATCH hci-8.1 1/3] fs/fuse: give num_openers and i_size_unstable each a single role Liu Kui
@ 2026-09-30 5:50 ` no-reply
0 siblings, 0 replies; 8+ messages in thread
From: no-reply @ 2026-09-30 5:50 UTC (permalink / raw)
To: Liu Kui, Konstantin Khorenko; +Cc: devel, azaitsev
> Currently fi->num_openers serves two purposes: it helps decide whether
> the i_size attribute needs to be taken from the server, and it manages
> when kio's file_open/file_close methods need to be called. The former
> role already requires the extra i_size_unstable flag to handle lease
> revokes, which makes num_openers redundant for that purpose.
>
> Give each field a single role: use i_size_unstable as the "take the
> attributes from the server" flag, and make num_openers a pure open
> count managing the calls of kio's file_open/file_close. A new inode
> starts with the i_size_unstable flag set, an open finding it set
> refreshes the attributes and clears it on success, and the last
> release sets it back after writeback has drained.
>
> Also fix a bug where a failed refresh could re-trust stale local
> attributes.
>
> https://virtuozzo.atlassian.net/browse/VSTOR-147878
>
> Signed-off-by: Liu Kui <kui.liu@virtuozzo.com>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.vzint.dev/#/patchset/20260930-close-wait-rework-v2-v1-0-f8062178544a@virtuozzo.com?part=1
_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [Devel] [PATCH hci-8.1 3/3] fs/fuse: use wait_var_event to wait for fuse file references
2026-09-30 5:44 ` [Devel] [PATCH hci-8.1 3/3] fs/fuse: use wait_var_event to wait for fuse file references Liu Kui
@ 2026-09-30 5:51 ` no-reply
0 siblings, 0 replies; 8+ messages in thread
From: no-reply @ 2026-09-30 5:51 UTC (permalink / raw)
To: Konstantin Khorenko, Liu Kui; +Cc: devel, azaitsev
> On close_wait mounts, fuse_release_close_wait() needs to wait for all
> asynchronous references to the fuse file to be dropped before sending
> FUSE_RELEASE. This wait on fi->page_waitq was inherited from the past
> implementation and is no longer appropriate as the wait condition
> evolved:
>
> - fi->page_waitq is meant to serve page-under-writeback waiters and
> fuse_set_nowrite()'s writectr waiters; now it also has to serve the
> ff reference waiter, so every wakeup can wake unrelated waiters.
>
> - the wake is decoupled from the decrement, so every path dropping an
> ff reference must remember to wake fi->page_waitq, and because of
> this we grew two variants of the stock fuse_file_put():
> __fuse_file_put() and fuse_release_ff(). This is bug prone, and a
> misuse can leave the releaser blocked forever.
>
> Instead of waiting on fi->page_waitq, a simple wait_var_event() on
> &ff->count serves the purpose best: the matching wake_up_var() uses
> the address purely as a hash key and never dereferences it, so the
> wakeup can be folded into fuse_file_put() itself. With that, both
> __fuse_file_put() and fuse_release_ff() become redundant and are
> removed. This cleanup also makes the code less divergent from
> mainline.
>
> https://virtuozzo.atlassian.net/browse/VSTOR-147878
>
> Signed-off-by: Liu Kui <kui.liu@virtuozzo.com>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.vzint.dev/#/patchset/20260930-close-wait-rework-v2-v1-0-f8062178544a@virtuozzo.com?part=3
_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [Devel] [PATCH hci-8.1 0/3] fs/fuse: rework close_wait handling
2026-09-30 5:44 [Devel] [PATCH hci-8.1 0/3] fs/fuse: rework close_wait handling Liu Kui
` (2 preceding siblings ...)
2026-09-30 5:44 ` [Devel] [PATCH hci-8.1 3/3] fs/fuse: use wait_var_event to wait for fuse file references Liu Kui
@ 2026-09-30 15:39 ` Konstantin Khorenko
3 siblings, 0 replies; 8+ messages in thread
From: Konstantin Khorenko @ 2026-09-30 15:39 UTC (permalink / raw)
To: Liu Kui; +Cc: devel, azaitsev
On Wed, 30 Sep 2026 13:44:47 +0800, Liu Kui wrote:
> fs/fuse: rework close_wait handling
>
> Rework the close_wait handling around fuse file open/close:
>
> - give num_openers and i_size_unstable each a single role: the flag
> alone decides when attributes are taken from the server, the
> counter only drives kio's file_open/file_close pairing
>
> [...]
Applied to branch-rh10-6.12.0-211.52.1.22.x.vz10-ovz, will be released in rh10-6.12.0-211.52.1.22.5.vz10, thanks!
[1/3] fs/fuse: give num_openers and i_size_unstable each a single role
https://bitbucket.org/virtuozzocore/vzkernel.vzs/commits/7c8c17dcba69
[2/3] fs/fuse: move the close_wait handling of fuse_release() into a helper
https://bitbucket.org/virtuozzocore/vzkernel.vzs/commits/e2a2db8bef2a
[3/3] fs/fuse: use wait_var_event to wait for fuse file references
https://bitbucket.org/virtuozzocore/vzkernel.vzs/commits/a31b002a5327
--
Konstantin Khorenko <khorenko@virtuozzo.com>
_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2026-09-30 15:40 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-30 5:44 [Devel] [PATCH hci-8.1 0/3] fs/fuse: rework close_wait handling Liu Kui
2026-09-30 5:44 ` [Devel] [PATCH hci-8.1 1/3] fs/fuse: give num_openers and i_size_unstable each a single role Liu Kui
2026-09-30 5:50 ` no-reply
2026-09-30 5:44 ` [Devel] [PATCH hci-8.1 2/3] fs/fuse: move the close_wait handling of fuse_release() into a helper Liu Kui
2026-09-30 5:49 ` no-reply
2026-09-30 5:44 ` [Devel] [PATCH hci-8.1 3/3] fs/fuse: use wait_var_event to wait for fuse file references Liu Kui
2026-09-30 5:51 ` no-reply
2026-09-30 15:39 ` [Devel] [PATCH hci-8.1 0/3] fs/fuse: rework close_wait handling Konstantin Khorenko
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox