[Devel] [PATCH VZ10] fs/fuse: force offload of final fput in aio completion to workqueue
Pavel Tikhomirov
ptikhomirov at virtuozzo.com
Fri Jun 26 11:43:42 MSK 2026
Merged to rh10-6.12.0-211.16.1.12.7.vz10.
On 6/22/26 18:22, Alexey Kuznetsov wrote:
> 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.
>
> Signed-off-by: Alexey Kuznetsov <kuznet at virtuozzo.com>
> ---
> fs/fuse/file.c | 14 ++++++++++++++
> 1 file changed, 14 insertions(+)
>
> diff --git a/fs/fuse/file.c b/fs/fuse/file.c
> index 40cded8..95a3352 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,20 @@ 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);
--
Best regards, Pavel Tikhomirov
Senior Software Developer, Virtuozzo.
More information about the Devel
mailing list