OpenVZ / Virtuozzo kernel development (devel@openvz.org)
 help / color / mirror / Atom feed
* [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