From 7879c4e58b7c884ee62b9306721dd1106e624b52 Mon Sep 17 00:00:00 2001 From: Ashish Sangwan Date: Thu, 7 Apr 2016 17:18:11 +0530 Subject: fuse: improve aio directIO write performance for size extending writes While sending the blocking directIO in fuse, the write request is broken into sub-requests, each of default size 128k and all the requests are sent in non-blocking background mode if async_dio mode is supported by libfuse. The process which issue the write wait for the completion of all the sub-requests. Sending multiple requests parallely gives a chance to perform parallel writes in the user space fuse implementation if it is multi-threaded and hence improves the performance. When there is a size extending aio dio write, we switch to blocking mode so that we can properly update the size of the file after completion of the writes. However, in this situation all the sub-requests are sent in serialized manner where the next request is sent only after receiving the reply of the current request. Hence the multi-threaded user space implementation is not utilized properly. This patch changes the size extending aio dio behavior to exactly follow blocking dio. For multi threaded fuse implementation having 10 threads and using buffer size of 64MB to perform async directIO, we are getting double the speed. Signed-off-by: Ashish Sangwan Signed-off-by: Miklos Szeredi --- fs/fuse/file.c | 21 +++++++++------------ fs/fuse/fuse_i.h | 1 + 2 files changed, 10 insertions(+), 12 deletions(-) (limited to 'fs') diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 9154f8679024..7270e89880b5 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -562,7 +562,6 @@ static ssize_t fuse_get_res_by_io(struct fuse_io_priv *io) */ static void fuse_aio_complete(struct fuse_io_priv *io, int err, ssize_t pos) { - bool is_sync = is_sync_kiocb(io->iocb); int left; spin_lock(&io->lock); @@ -572,11 +571,11 @@ static void fuse_aio_complete(struct fuse_io_priv *io, int err, ssize_t pos) io->bytes = pos; left = --io->reqs; - if (!left && is_sync) + if (!left && io->blocking) complete(io->done); spin_unlock(&io->lock); - if (!left && !is_sync) { + if (!left && !io->blocking) { ssize_t res = fuse_get_res_by_io(io); if (res >= 0) { @@ -2850,7 +2849,6 @@ fuse_direct_IO(struct kiocb *iocb, struct iov_iter *iter) size_t count = iov_iter_count(iter); loff_t offset = iocb->ki_pos; struct fuse_io_priv *io; - bool is_sync = is_sync_kiocb(iocb); pos = offset; inode = file->f_mapping->host; @@ -2885,17 +2883,16 @@ fuse_direct_IO(struct kiocb *iocb, struct iov_iter *iter) */ io->async = async_dio; io->iocb = iocb; + io->blocking = is_sync_kiocb(iocb); /* - * We cannot asynchronously extend the size of a file. We have no method - * to wait on real async I/O requests, so we must submit this request - * synchronously. + * We cannot asynchronously extend the size of a file. + * In such case the aio will behave exactly like sync io. */ - if (!is_sync && (offset + count > i_size) && - iov_iter_rw(iter) == WRITE) - io->async = false; + if ((offset + count > i_size) && iov_iter_rw(iter) == WRITE) + io->blocking = true; - if (io->async && is_sync) { + if (io->async && io->blocking) { /* * Additional reference to keep io around after * calling fuse_aio_complete() @@ -2915,7 +2912,7 @@ fuse_direct_IO(struct kiocb *iocb, struct iov_iter *iter) fuse_aio_complete(io, ret < 0 ? ret : 0, -1); /* we have a non-extending, async request, so return */ - if (!is_sync) + if (!io->blocking) return -EIOCBQUEUED; wait_for_completion(&wait); diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 929c383432b0..5db5d24f91a5 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -259,6 +259,7 @@ struct fuse_io_priv { struct kiocb *iocb; struct file *file; struct completion *done; + bool blocking; }; #define FUSE_IO_PRIV_SYNC(f) \ -- cgit v1.2.3 From 7d3a07fcb8a0d5c06718de14fb91fdf1ef20a0e2 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 19 Jul 2016 03:08:27 -0400 Subject: fuse: don't mess with blocking signals just use wait_event_killable{,_exclusive}(). Signed-off-by: Al Viro --- fs/fuse/dev.c | 30 +++--------------------------- 1 file changed, 3 insertions(+), 27 deletions(-) (limited to 'fs') diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index cbece1221417..9406f35d1a1e 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -99,19 +99,6 @@ void fuse_request_free(struct fuse_req *req) kmem_cache_free(fuse_req_cachep, req); } -static void block_sigs(sigset_t *oldset) -{ - sigset_t mask; - - siginitsetinv(&mask, sigmask(SIGKILL)); - sigprocmask(SIG_BLOCK, &mask, oldset); -} - -static void restore_sigs(sigset_t *oldset) -{ - sigprocmask(SIG_SETMASK, oldset, NULL); -} - void __fuse_get_request(struct fuse_req *req) { atomic_inc(&req->count); @@ -151,15 +138,9 @@ static struct fuse_req *__fuse_get_req(struct fuse_conn *fc, unsigned npages, atomic_inc(&fc->num_waiting); if (fuse_block_alloc(fc, for_background)) { - sigset_t oldset; - int intr; - - block_sigs(&oldset); - intr = wait_event_interruptible_exclusive(fc->blocked_waitq, - !fuse_block_alloc(fc, for_background)); - restore_sigs(&oldset); err = -EINTR; - if (intr) + if (wait_event_killable_exclusive(fc->blocked_waitq, + !fuse_block_alloc(fc, for_background))) goto out; } /* Matches smp_wmb() in fuse_set_initialized() */ @@ -446,14 +427,9 @@ static void request_wait_answer(struct fuse_conn *fc, struct fuse_req *req) } if (!test_bit(FR_FORCE, &req->flags)) { - sigset_t oldset; - /* Only fatal signals may interrupt this */ - block_sigs(&oldset); - err = wait_event_interruptible(req->waitq, + err = wait_event_killable(req->waitq, test_bit(FR_FINISHED, &req->flags)); - restore_sigs(&oldset); - if (!err) return; -- cgit v1.2.3 From ac7f052b9e1534c8248f814b6f0068ad8d4a06d2 Mon Sep 17 00:00:00 2001 From: Alexey Kuznetsov Date: Tue, 19 Jul 2016 12:48:01 -0700 Subject: fuse: fsync() did not return IO errors Due to implementation of fuse writeback filemap_write_and_wait_range() does not catch errors. We have to do this directly after fuse_sync_writes() Signed-off-by: Alexey Kuznetsov Signed-off-by: Maxim Patlasov Signed-off-by: Miklos Szeredi Fixes: 4d99ff8f12eb ("fuse: Turn writeback cache on") Cc: # v3.15+ --- fs/fuse/file.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'fs') diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 7270e89880b5..2491f388358b 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -462,6 +462,21 @@ int fuse_fsync_common(struct file *file, loff_t start, loff_t end, goto out; fuse_sync_writes(inode); + + /* + * Due to implementation of fuse writeback + * filemap_write_and_wait_range() does not catch errors. + * We have to do this directly after fuse_sync_writes() + */ + if (test_bit(AS_ENOSPC, &file->f_mapping->flags) && + test_and_clear_bit(AS_ENOSPC, &file->f_mapping->flags)) + err = -ENOSPC; + if (test_bit(AS_EIO, &file->f_mapping->flags) && + test_and_clear_bit(AS_EIO, &file->f_mapping->flags)) + err = -EIO; + if (err) + goto out; + err = sync_inode_metadata(inode, 1); if (err) goto out; -- cgit v1.2.3 From 9ebce595f63a407c5cec98f98f9da8459b73740a Mon Sep 17 00:00:00 2001 From: Maxim Patlasov Date: Tue, 19 Jul 2016 18:12:26 -0700 Subject: fuse: fuse_flush must check mapping->flags for errors fuse_flush() calls write_inode_now() that triggers writeback, but actual writeback will happen later, on fuse_sync_writes(). If an error happens, fuse_writepage_end() will set error bit in mapping->flags. So, we have to check mapping->flags after fuse_sync_writes(). Signed-off-by: Maxim Patlasov Signed-off-by: Miklos Szeredi Fixes: 4d99ff8f12eb ("fuse: Turn writeback cache on") Cc: # v3.15+ --- fs/fuse/file.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'fs') diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 2491f388358b..594fb15fba9b 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -417,6 +417,15 @@ static int fuse_flush(struct file *file, fl_owner_t id) fuse_sync_writes(inode); inode_unlock(inode); + if (test_bit(AS_ENOSPC, &file->f_mapping->flags) && + test_and_clear_bit(AS_ENOSPC, &file->f_mapping->flags)) + err = -ENOSPC; + if (test_bit(AS_EIO, &file->f_mapping->flags) && + test_and_clear_bit(AS_EIO, &file->f_mapping->flags)) + err = -EIO; + if (err) + return err; + req = fuse_get_req_nofail_nopages(fc, file); memset(&inarg, 0, sizeof(inarg)); inarg.fh = ff->fh; -- cgit v1.2.3 From 9446385f05c9af25fed53dbed3cc75763730be52 Mon Sep 17 00:00:00 2001 From: Wei Fang Date: Mon, 25 Jul 2016 21:17:04 +0800 Subject: fuse: fix wrong assignment of ->flags in fuse_send_init() FUSE_HAS_IOCTL_DIR should be assigned to ->flags, it may be a typo. Signed-off-by: Wei Fang Signed-off-by: Miklos Szeredi Fixes: 69fe05c90ed5 ("fuse: add missing INIT flags") Cc: --- fs/fuse/inode.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index 9961d8432ce3..9b7cb37b4ba8 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -942,7 +942,7 @@ static void fuse_send_init(struct fuse_conn *fc, struct fuse_req *req) arg->flags |= FUSE_ASYNC_READ | FUSE_POSIX_LOCKS | FUSE_ATOMIC_O_TRUNC | FUSE_EXPORT_SUPPORT | FUSE_BIG_WRITES | FUSE_DONT_MASK | FUSE_SPLICE_WRITE | FUSE_SPLICE_MOVE | FUSE_SPLICE_READ | - FUSE_FLOCK_LOCKS | FUSE_IOCTL_DIR | FUSE_AUTO_INVAL_DATA | + FUSE_FLOCK_LOCKS | FUSE_HAS_IOCTL_DIR | FUSE_AUTO_INVAL_DATA | FUSE_DO_READDIRPLUS | FUSE_READDIRPLUS_AUTO | FUSE_ASYNC_DIO | FUSE_WRITEBACK_CACHE | FUSE_NO_OPEN_SUPPORT | FUSE_PARALLEL_DIROPS; -- cgit v1.2.3 From 4a7f4e88fef171f989df9538c382d2b5063d21bb Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Fri, 29 Jul 2016 14:10:57 +0200 Subject: fuse: use filemap_check_errors() Signed-off-by: Miklos Szeredi --- fs/fuse/file.c | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) (limited to 'fs') diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 594fb15fba9b..c6b6819d2f53 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -417,12 +417,7 @@ static int fuse_flush(struct file *file, fl_owner_t id) fuse_sync_writes(inode); inode_unlock(inode); - if (test_bit(AS_ENOSPC, &file->f_mapping->flags) && - test_and_clear_bit(AS_ENOSPC, &file->f_mapping->flags)) - err = -ENOSPC; - if (test_bit(AS_EIO, &file->f_mapping->flags) && - test_and_clear_bit(AS_EIO, &file->f_mapping->flags)) - err = -EIO; + err = filemap_check_errors(file->f_mapping); if (err) return err; @@ -477,12 +472,7 @@ int fuse_fsync_common(struct file *file, loff_t start, loff_t end, * filemap_write_and_wait_range() does not catch errors. * We have to do this directly after fuse_sync_writes() */ - if (test_bit(AS_ENOSPC, &file->f_mapping->flags) && - test_and_clear_bit(AS_ENOSPC, &file->f_mapping->flags)) - err = -ENOSPC; - if (test_bit(AS_EIO, &file->f_mapping->flags) && - test_and_clear_bit(AS_EIO, &file->f_mapping->flags)) - err = -EIO; + err = filemap_check_errors(file->f_mapping); if (err) goto out; -- cgit v1.2.3