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
3f6d5e6a468d ("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: 756b1c343333 ("xfs: use current->journal_info for detecting transaction recursion")
Link: https://lore.kernel.org/linux-xfs/20251104131857.1587584-1-leo.lilong@huawei.com
Signed-off-by: Wenwu Hou <hwenwur@gmail.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Carlos Maiolino <cem@kernel.org>
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);
* 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;
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__ */