Commit a1ca658d authored by Wenwu Hou's avatar Wenwu Hou Committed by Carlos Maiolino
Browse files

xfs: fix incorrect context handling in xfs_trans_roll

The memalloc_nofs_save() and memalloc_nofs_restore() calls are
incorrectly paired in xfs_trans_roll.

Call path:
xfs_trans_alloc()
    __xfs_trans_alloc()
	// tp->t_pflags = memalloc_nofs_save();
	xfs_trans_set_context()
...
xfs_defer_trans_roll()
    xfs_trans_roll()
        xfs_trans_dup()
            // old_tp->t_pflags = 0;
            xfs_trans_switch_context()
        __xfs_trans_commit()
            xfs_trans_free()
                // memalloc_nofs_restore(tp->t_pflags);
                xfs_trans_clear_context()

The code passes 0 to memalloc_nofs_restore() when committing the original
transaction, but memalloc_nofs_restore() should always receive the
flags returned from the paired memalloc_nofs_save() call.

Before commit 3f6d5e6a ("mm: introduce memalloc_flags_{save,restore}"),
calling memalloc_nofs_restore(0) would unset the PF_MEMALLOC_NOFS flag,
which could cause memory allocation deadlocks[1].
Fortunately, after that commit, memalloc_nofs_restore(0) does nothing,
so this issue is currently harmless.

Fixes: 756b1c34 ("xfs: use current->journal_info for detecting transaction recursion")
Link: https://lore.kernel.org/linux-xfs/20251104131857.1587584-1-leo.lilong@huawei.com

 [1]
Signed-off-by: default avatarWenwu Hou <hwenwur@gmail.com>
Reviewed-by: default avatarChristoph Hellwig <hch@lst.de>
Signed-off-by: default avatarCarlos Maiolino <cem@kernel.org>
parent 01a28961
Loading
Loading
Loading
Loading
+6 −2
Original line number Diff line number Diff line
@@ -124,8 +124,6 @@ xfs_trans_dup(
	ntp->t_rtx_res = tp->t_rtx_res - tp->t_rtx_res_used;
	tp->t_rtx_res = tp->t_rtx_res_used;

	xfs_trans_switch_context(tp, ntp);

	/* move deferred ops over to the new tp */
	xfs_defer_move(ntp, tp);

@@ -1043,6 +1041,12 @@ xfs_trans_roll(
	 * 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;
+0 −9
Original line number Diff line number Diff line
@@ -280,13 +280,4 @@ xfs_trans_clear_context(
	memalloc_nofs_restore(tp->t_pflags);
}

static inline void
xfs_trans_switch_context(
	struct xfs_trans	*old_tp,
	struct xfs_trans	*new_tp)
{
	new_tp->t_pflags = old_tp->t_pflags;
	old_tp->t_pflags = 0;
}

#endif	/* __XFS_TRANS_H__ */