xfs: restore nofs context unconditionally in xfs_trans_roll

commit 0241ea5fb0 upstream.

When __xfs_trans_commit() fails in xfs_trans_roll(), the NOFS context
is cleared but only restored in the success path.  This leaves the
error path without nofs protection, causing a circular lock dependency
between xfs_nondir_ilock_class and fs_reclaim:

       CPU0                    CPU1
       ----                    ----
  lock(&xfs_nondir_ilock_class);
                               lock(fs_reclaim);
                               lock(&xfs_nondir_ilock_class);
  lock(fs_reclaim);

Fix this by moving xfs_trans_set_context() before the error check so
that nofs context is always restored on the new transaction.

Reported-by: syzbot+59178abfeb0ea3f0ab20@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=59178abfeb0ea3f0ab20
Fixes: a1ca658d64 ("xfs: fix incorrect context handling in xfs_trans_roll")
Cc: stable@vger.kernel.org
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Yun Zhou <yun.zhou@windriver.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Carlos Maiolino <cem@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Yun Zhou
2026-08-27 14:35:23 +02:00
committed by Greg Kroah-Hartman
parent c35da2bac6
commit 4d00a39c67
+9 -7
View File
@@ -1029,6 +1029,15 @@ xfs_trans_roll(
* duplicate transaction that gets returned.
*/
error = __xfs_trans_commit(tp, true);
tp = *tpp;
/*
* __xfs_trans_commit cleared the NOFS flag by calling into
* xfs_trans_free. Set it again here before doing memory
* allocations.
*/
xfs_trans_set_context(tp);
if (error)
return error;
@@ -1040,13 +1049,6 @@ xfs_trans_roll(
* either nothing be locked across this call, or that anything that is
* locked be logged in the prior and the next transactions.
*/
tp = *tpp;
/*
* __xfs_trans_commit cleared the NOFS flag by calling into
* xfs_trans_free. Set it again here before doing memory
* allocations.
*/
xfs_trans_set_context(tp);
error = xfs_log_regrant(tp->t_mountp, tp->t_ticket);
if (error)
return error;