Take a shared lock in fuse_cache_write_iter. This was already done for FOPEN_DIRECT_IO in commit 153524053bbb ("fuse: allow non-extending parallel direct writes on the same file") but so far missing for plain O_DIRECT. Server side needs to set FOPEN_PARALLEL_DIRECT_WRITES in order to signal that it supports parallel dio writes. >From style point of view another goto target is introduced, although the existing writethrough target would be sufficient. This is just done to make the code easier to read. In this commit the exclusive lock still enforced by an '|| 1'. For readability this will be solved in a follow up commit. Cc: Hao Xu <howeyxu@xxxxxxxxxxx> Cc: Miklos Szeredi <miklos@xxxxxxxxxx> Cc: Dharmendra Singh <dsingh@xxxxxxx> Cc: linux-fsdevel@xxxxxxxxxxxxxxx Signed-off-by: Bernd Schubert <bschubert@xxxxxxx> --- fs/fuse/file.c | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 7606cf376ec3..76922a6a0962 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -1313,6 +1313,10 @@ static bool fuse_dio_wr_exclusive_lock(struct kiocb *iocb, struct iov_iter *from struct file *file = iocb->ki_filp; struct fuse_file *ff = file->private_data; + /* the shared lock is about direct IO only */ + if (!(iocb->ki_flags & IOCB_DIRECT)) + return true; + /* server side has to advise that it supports parallel dio writes */ if (!(ff->open_flags & FOPEN_PARALLEL_DIRECT_WRITES)) return true; @@ -1338,6 +1342,7 @@ static ssize_t fuse_cache_write_iter(struct kiocb *iocb, struct iov_iter *from) struct inode *inode = mapping->host; ssize_t err; struct fuse_conn *fc = get_fuse_conn(inode); + bool excl_lock = fuse_dio_wr_exclusive_lock(iocb, from) || 1; if (fc->writeback_cache && !(iocb->ki_flags & IOCB_DIRECT)) { /* Update size (EOF optimization) and mode (SUID clearing) */ @@ -1356,7 +1361,20 @@ static ssize_t fuse_cache_write_iter(struct kiocb *iocb, struct iov_iter *from) } writethrough: - inode_lock(inode); +relock: + if (excl_lock) + inode_lock(inode); + else { + inode_lock_shared(inode); + if (fuse_io_past_eof(iocb, from)) { + /* file extending writes will trigger i_size_write, + * exclusive lock is needed + */ + inode_unlock_shared(inode); + excl_lock = true; + goto relock; + } + } err = generic_write_checks(iocb, from); if (err <= 0) @@ -1374,13 +1392,24 @@ static ssize_t fuse_cache_write_iter(struct kiocb *iocb, struct iov_iter *from) written = generic_file_direct_write(iocb, from); if (written < 0 || !iov_iter_count(from)) goto out; + + if (!excl_lock) { + /* fallback to page IO needs the exclusive lock */ + inode_unlock_shared(inode); + excl_lock = true; + goto relock; + } + written = direct_write_fallback(iocb, from, written, fuse_perform_write(iocb, from)); } else { written = fuse_perform_write(iocb, from); } out: - inode_unlock(inode); + if (excl_lock) + inode_unlock(inode); + else + inode_unlock_shared(inode); if (written > 0) written = generic_write_sync(iocb, written); -- 2.39.2