--- /dev/null
+From 9af86694fd5d387992699ec99007ed374966ce9a Mon Sep 17 00:00:00 2001
+From: Bernd Schubert <bschubert@ddn.com>
+Date: Wed, 6 Sep 2023 17:59:03 +0200
+Subject: btrfs: file_remove_privs needs an exclusive lock in direct io write
+
+From: Bernd Schubert <bschubert@ddn.com>
+
+commit 9af86694fd5d387992699ec99007ed374966ce9a upstream.
+
+This was noticed by Miklos that file_remove_privs might call into
+notify_change(), which requires to hold an exclusive lock. The problem
+exists in FUSE and btrfs. We can fix it without any additional helpers
+from VFS, in case the privileges would need to be dropped, change the
+lock type to be exclusive and redo the loop.
+
+Fixes: e9adabb9712e ("btrfs: use shared lock for direct writes within EOF")
+CC: Miklos Szeredi <miklos@szeredi.hu>
+CC: stable@vger.kernel.org # 5.15+
+Reviewed-by: Christoph Hellwig <hch@lst.de>
+Signed-off-by: Bernd Schubert <bschubert@ddn.com>
+Reviewed-by: David Sterba <dsterba@suse.com>
+Signed-off-by: David Sterba <dsterba@suse.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/btrfs/file.c | 16 ++++++++++++++--
+ 1 file changed, 14 insertions(+), 2 deletions(-)
+
+--- a/fs/btrfs/file.c
++++ b/fs/btrfs/file.c
+@@ -1458,8 +1458,13 @@ static ssize_t btrfs_direct_write(struct
+ 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 by file_remove_privs() called
++ * from btrfs_write_check(). 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:
+@@ -1467,6 +1472,13 @@ relock:
+ if (err < 0)
+ return err;
+
++ /* Shared lock cannot be used with security bits set. */
++ if ((ilock_flags & BTRFS_ILOCK_SHARED) && !IS_NOSEC(inode)) {
++ btrfs_inode_unlock(inode, ilock_flags);
++ ilock_flags &= ~BTRFS_ILOCK_SHARED;
++ goto relock;
++ }
++
+ err = generic_write_checks(iocb, from);
+ if (err <= 0) {
+ btrfs_inode_unlock(inode, ilock_flags);