[PATCH] xfs: fix nofs context corruption in xfs_btree_split_worker

Yun Zhou <[email protected]>
Newsgroups org.kernel.vger.linux-xfs,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
xfs_btree_split_worker() calls xfs_trans_set_context() on the shared
transaction, overwriting tp->t_pflags saved by the submitting thread.
This can cause the submitting thread's NOFS protection to be cleared
prematurely when the transaction is freed, risking deadlocks from
allocations recursing into fs_reclaim.

    Thread A (NOFS set)             Worker
    -------------------             ------
    xfs_trans_alloc()
      xfs_trans_set_context(tp)
        tp->t_pflags = 0  (*)
    ...
    xfs_btree_split()
      queue_work(split_worker)
                                    xfs_trans_set_context(tp)
                                      tp->t_pflags = NOFS  (clobbers!)
                                    __xfs_btree_split()
                                    xfs_trans_clear_context(tp)
      wait_for_completion()
    ...
    xfs_trans_free(tp)
      memalloc_nofs_restore(NOFS)
        -> clears Thread A's NOFS!

    (*) returns 0 because NOFS was already set by outer scope

Fix by using a local memalloc_nofs_save()/restore() in the worker
instead of touching the shared transaction state.

Reported-by: sashiko <[email protected]>
Fixes: 756b1c343333 ("xfs: use current->journal_info for detecting transaction recursion")
Signed-off-by: Yun Zhou <[email protected]>
---
 fs/xfs/libxfs/xfs_btree.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_btree.c b/fs/xfs/libxfs/xfs_btree.c
index 60ef7f08b1d3..1e4c43b7fa3d 100644
--- a/fs/xfs/libxfs/xfs_btree.c
+++ b/fs/xfs/libxfs/xfs_btree.c
@@ -3010,6 +3010,7 @@ xfs_btree_split_worker(
 						struct xfs_btree_split_args, work);
 	unsigned long		pflags;
 	unsigned long		new_pflags = 0;
+	unsigned int		nofs_flags;
 
 	/*
 	 * we are in a transaction context here, but may also be doing work
@@ -3021,12 +3022,12 @@ xfs_btree_split_worker(
 		new_pflags |= PF_MEMALLOC | PF_KSWAPD;
 
 	current_set_flags_nested(&pflags, new_pflags);
-	xfs_trans_set_context(args->cur->bc_tp);
+	nofs_flags = memalloc_nofs_save();
 
 	args->result = __xfs_btree_split(args->cur, args->level, args->ptrp,
 					 args->key, args->curp, args->stat);
 
-	xfs_trans_clear_context(args->cur->bc_tp);
+	memalloc_nofs_restore(nofs_flags);
 	current_restore_flags_nested(&pflags, new_pflags);
 
 	/*
-- 
2.43.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.