--- /dev/null
+From 105726f68000d334f6017dcdc30ad1ec06cd66bc Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 1 Mar 2023 15:10:04 +0100
+Subject: ext4: Fix deadlock during directory rename
+
+From: Jan Kara <jack@suse.cz>
+
+[ Upstream commit 3c92792da8506a295afb6d032b4476e46f979725 ]
+
+As lockdep properly warns, we should not be locking i_rwsem while having
+transactions started as the proper lock ordering used by all directory
+handling operations is i_rwsem -> transaction start. Fix the lock
+ordering by moving the locking of the directory earlier in
+ext4_rename().
+
+Reported-by: syzbot+9d16c39efb5fade84574@syzkaller.appspotmail.com
+Fixes: 0813299c586b ("ext4: Fix possible corruption when moving a directory")
+Link: https://syzkaller.appspot.com/bug?extid=9d16c39efb5fade84574
+Signed-off-by: Jan Kara <jack@suse.cz>
+Link: https://lore.kernel.org/r/20230301141004.15087-1-jack@suse.cz
+Signed-off-by: Theodore Ts'o <tytso@mit.edu>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/ext4/namei.c | 26 +++++++++++++++++---------
+ 1 file changed, 17 insertions(+), 9 deletions(-)
+
+diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
+index f9d11f59df7d2..b708b437b3e36 100644
+--- a/fs/ext4/namei.c
++++ b/fs/ext4/namei.c
+@@ -3795,10 +3795,20 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry,
+ return retval;
+ }
+
++ /*
++ * We need to protect against old.inode directory getting converted
++ * from inline directory format into a normal one.
++ */
++ if (S_ISDIR(old.inode->i_mode))
++ inode_lock_nested(old.inode, I_MUTEX_NONDIR2);
++
+ old.bh = ext4_find_entry(old.dir, &old.dentry->d_name, &old.de,
+ &old.inlined);
+- if (IS_ERR(old.bh))
+- return PTR_ERR(old.bh);
++ if (IS_ERR(old.bh)) {
++ retval = PTR_ERR(old.bh);
++ goto unlock_moved_dir;
++ }
++
+ /*
+ * Check for inode number is _not_ due to possible IO errors.
+ * We might rmdir the source, keep it as pwd of some process
+@@ -3855,11 +3865,6 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry,
+ if (new.dir != old.dir && EXT4_DIR_LINK_MAX(new.dir))
+ goto end_rename;
+ }
+- /*
+- * We need to protect against old.inode directory getting
+- * converted from inline directory format into a normal one.
+- */
+- inode_lock_nested(old.inode, I_MUTEX_NONDIR2);
+ retval = ext4_rename_dir_prepare(handle, &old);
+ if (retval) {
+ inode_unlock(old.inode);
+@@ -3960,12 +3965,15 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry,
+ } else {
+ ext4_journal_stop(handle);
+ }
+- if (old.dir_bh)
+- inode_unlock(old.inode);
+ release_bh:
+ brelse(old.dir_bh);
+ brelse(old.bh);
+ brelse(new.bh);
++
++unlock_moved_dir:
++ if (S_ISDIR(old.inode->i_mode))
++ inode_unlock(old.inode);
++
+ return retval;
+ }
+
+--
+2.39.2
+