This eventually enabled a shared lock for O_DIRECT writes in fuse_cache_write_iter. Missing was to check if privileges need to be dropped - if so, this needs to be done with a exclusive lock. file_needs_remove_privs and file_remove_privs are both sending an getxattr call, assumption is that this is rare - should not have a performance impact. fuse_dio_wr_exclusive_lock() is not using file_needs_remove_privs, as it needs to be called again while lock is hold - it would result in a typical double call into file_needs_remove_privs, with the expensive xattr retrieval. Cc: Hao Xu <howeyxu@xxxxxxxxxxx> Cc: Miklos Szeredi <miklos@xxxxxxxxxx> Cc: Dharmendra Singh <dsingh@xxxxxxx> Cc: Christoph Hellwig <hch@xxxxxxxxxxxxx> Cc: linux-fsdevel@xxxxxxxxxxxxxxx Signed-off-by: Bernd Schubert <bschubert@xxxxxxx> --- fs/fuse/file.c | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 76922a6a0962..feafab9b467f 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -1308,7 +1308,8 @@ static bool fuse_io_past_eof(struct kiocb *iocb, struct iov_iter *iter) /* * @return true if an exclusive lock for direct IO writes is needed */ -static bool fuse_dio_wr_exclusive_lock(struct kiocb *iocb, struct iov_iter *from) +static bool fuse_dio_wr_exclusive_lock(struct kiocb *iocb, struct iov_iter *from, + struct inode *inode) { struct file *file = iocb->ki_filp; struct fuse_file *ff = file->private_data; @@ -1342,7 +1343,8 @@ 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; + bool excl_lock = fuse_dio_wr_exclusive_lock(iocb, from, inode); + int remove_privs = 1; if (fc->writeback_cache && !(iocb->ki_flags & IOCB_DIRECT)) { /* Update size (EOF optimization) and mode (SUID clearing) */ @@ -1380,9 +1382,20 @@ static ssize_t fuse_cache_write_iter(struct kiocb *iocb, struct iov_iter *from) if (err <= 0) goto out; - err = file_remove_privs(file); - if (err) - goto out; + if (!excl_lock) { + remove_privs = file_needs_remove_privs(file); + if (remove_privs) { + inode_unlock_shared(inode); + excl_lock = true; + goto relock; + } + } + + if (remove_privs) { + err = file_remove_privs(file); + if (err) + goto out; + } err = file_update_time(file); if (err) @@ -1623,7 +1636,7 @@ static ssize_t fuse_direct_write_iter(struct kiocb *iocb, struct iov_iter *from) struct inode *inode = file_inode(iocb->ki_filp); struct fuse_io_priv io = FUSE_IO_PRIV_SYNC(iocb); ssize_t res; - bool exclusive_lock = fuse_dio_wr_exclusive_lock(iocb, from); + bool exclusive_lock = fuse_dio_wr_exclusive_lock(iocb, from, inode); /* * Take exclusive lock if -- 2.39.2