From: Alexey Kuznetsov <[email protected]> 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 <[email protected]> --- fs/fuse/file.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 40cded85037f..c812420cc109 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -18,6 +18,7 @@ #include <linux/falloc.h> #include <linux/uio.h> #include <linux/fs.h> +#include <linux/file.h> #include <linux/filelock.h> #include <linux/splice.h> #include <linux/task_io_accounting_ops.h> @@ -971,6 +972,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 +991,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); -- 2.54.0 _______________________________________________ Devel mailing list [email protected] https://lists.openvz.org/mailman/listinfo/devel
