file_remove_privs might call into notify_change(), which requires to hold an exclusive lock.
Fixes: e9adabb9712e ("btrfs: use shared lock for direct writes within EOF") Cc: Christoph Hellwig hch@infradead.org Cc: Goldwyn Rodrigues rgoldwyn@suse.com Cc: Miklos Szeredi miklos@szeredi.hu Cc: Dharmendra Singh dsingh@ddn.com Cc: David Sterba dsterba@suse.com Cc: linux-btrfs@vger.kernel.org Cc: linux-fsdevel@vger.kernel.org Cc: stable@vger.kernel.org Signed-off-by: Bernd Schubert bschubert@ddn.com --- fs/btrfs/file.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-)
diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c index fd03e689a6be..c4b304a2948e 100644 --- a/fs/btrfs/file.c +++ b/fs/btrfs/file.c @@ -1466,8 +1466,12 @@ static ssize_t btrfs_direct_write(struct kiocb *iocb, struct iov_iter *from) if (iocb->ki_flags & IOCB_NOWAIT) ilock_flags |= BTRFS_ILOCK_TRY;
- /* If the write DIO is within EOF, use a shared lock */ - if (iocb->ki_pos + iov_iter_count(from) <= i_size_read(inode)) + /* If the write DIO is within EOF, use a shared lock and also only + * if security bits will likely not be dropped. Either will need + * to be rechecked after the lock was acquired. + */ + if (iocb->ki_pos + iov_iter_count(from) <= i_size_read(inode) && + IS_NOSEC(inode)) ilock_flags |= BTRFS_ILOCK_SHARED;
relock: @@ -1475,6 +1479,12 @@ static ssize_t btrfs_direct_write(struct kiocb *iocb, struct iov_iter *from) if (err < 0) return err;
+ if (ilock_flags & BTRFS_ILOCK_SHARED && !IS_NOSEC(inode)) { + btrfs_inode_unlock(BTRFS_I(inode), ilock_flags); + ilock_flags &= ~BTRFS_ILOCK_SHARED; + goto relock; + } + err = generic_write_checks(iocb, from); if (err <= 0) { btrfs_inode_unlock(BTRFS_I(inode), ilock_flags);