[Devel] [PATCH RHEL10 COMMIT] fs/fuse: force offload of final fput in aio completion to workqueue
Konstantin Khorenko
khorenko at virtuozzo.com
Mon Jun 29 17:31:01 MSK 2026
The commit is pushed to "branch-rh10-6.12.0-211.16.1.12.x.vz10-ovz" and will appear at git at bitbucket.org:openvz/vzkernel.git
after rh10-6.12.0-211.16.1.12.5.vz10
------>
commit 4dc73eb6adc3d37cfde6a83345900a837679ba8c
Author: Alexey Kuznetsov <kuznet at virtuozzo.com>
Date: Tue Jun 23 00:22:26 2026 +0800
fs/fuse: force offload of final fput in aio completion to workqueue
The patch is known as "fuse: queue work for aio_complete (v3)",
here is its simpler form.
Aio completion is executed in context of userspace worker thread.
Final fput might make lots of additional work like updating mtime,
releasing file, which could be routed to the same thread.
The problem is not specific to vstorage, all decent fuse fses
suffer of this. The code as is works only with low performance
fses based on libfuse. Also, current version of vstorage
is not supposed suffer if this problem because aio completions
are guaranteed to be done in threads different of threads
working on metadata. Yet, it can be configured, deliberately
or by mistake to route everything to one thread.
Use workqueue supplied by vfs, all kernels in use (rh7,9,10)
offload final fput to workqueue when fput() is called from
interrupt or kthread context. So, we grab reference to file
before ki_complete() and release it afterwards surrounded
with local_bh_disable/local_bh_enable pair. It looks like
a good pragmatic solution with minimal impact on performance
and maintanance cost.
https://virtuozzo.atlassian.net/browse/VSTOR-135017
Reviewed-by: Pavel Tikhomirov <ptikhomirov at virtuozzo.com>
Signed-off-by: Alexey Kuznetsov <kuznet at virtuozzo.com>
Feature: vStorage
---
fs/fuse/file.c | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index 40cded85037f0..d11aaac99f03c 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -971,6 +971,7 @@ static void fuse_aio_complete(struct fuse_io_priv *io, int err, ssize_t pos)
if (!left && !io->blocking) {
ssize_t res = fuse_get_res_by_io(io);
+ struct file *file = io->iocb->ki_filp;
if (res >= 0) {
struct inode *inode = file_inode(io->iocb->ki_filp);
@@ -989,7 +990,24 @@ static void fuse_aio_complete(struct fuse_io_priv *io, int err, ssize_t pos)
io->iocb->ki_flags, io->iocb->ki_pos);
}
+ /*
+ * We have to bump f_count here to avoid deadlock for
+ * single-threaded fuse daemon: if the process that generated
+ * AIO is already close(2) the file, fput() called from
+ * aio_complete will be the last fput(); hence, it will send
+ * flush_mtime (or release) request to userspace who is busy
+ * now writing ACK for given AIO to in-kernel fuse
+ */
+ get_file(file);
io->iocb->ki_complete(io->iocb, res);
+
+ /*
+ * local_bh_disable() forces fput() to offload final
+ * release to workqueue
+ */
+ local_bh_disable();
+ fput(file);
+ local_bh_enable();
}
kref_put(&io->refcnt, fuse_io_release);
More information about the Devel
mailing list