Commit ce6d9c1c authored by Mike Snitzer's avatar Mike Snitzer Committed by Andrew Morton
Browse files

NFS: fix nfs_release_folio() to not deadlock via kcompactd writeback

Add PF_KCOMPACTD flag and current_is_kcompactd() helper to check for it so
nfs_release_folio() can skip calling nfs_wb_folio() from kcompactd.

Otherwise NFS can deadlock waiting for kcompactd enduced writeback which
recurses back to NFS (which triggers writeback to NFSD via NFS loopback
mount on the same host, NFSD blocks waiting for XFS's call to
__filemap_get_folio):

6070.550357] INFO: task kcompactd0:58 blocked for more than 4435 seconds.

{---
[58] "kcompactd0"
[<0>] folio_wait_bit+0xe8/0x200
[<0>] folio_wait_writeback+0x2b/0x80
[<0>] nfs_wb_folio+0x80/0x1b0 [nfs]
[<0>] nfs_release_folio+0x68/0x130 [nfs]
[<0>] split_huge_page_to_list_to_order+0x362/0x840
[<0>] migrate_pages_batch+0x43d/0xb90
[<0>] migrate_pages_sync+0x9a/0x240
[<0>] migrate_pages+0x93c/0x9f0
[<0>] compact_zone+0x8e2/0x1030
[<0>] compact_node+0xdb/0x120
[<0>] kcompactd+0x121/0x2e0
[<0>] kthread+0xcf/0x100
[<0>] ret_from_fork+0x31/0x40
[<0>] ret_from_fork_asm+0x1a/0x30
---}

[akpm@linux-foundation.org: fix build]
Link: https://lkml.kernel.org/r/20250225022002.26141-1-snitzer@kernel.org


Fixes: 96780ca5 ("NFS: fix up nfs_release_folio() to try to release the page")
Signed-off-by: default avatarMike Snitzer <snitzer@kernel.org>
Cc: Anna Schumaker <anna.schumaker@oracle.com>
Cc: Trond Myklebust <trond.myklebust@hammerspace.com>
Cc: <stable@vger.kernel.org>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
parent 57d910cf
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@
#include <linux/pagemap.h>
#include <linux/gfp.h>
#include <linux/swap.h>
#include <linux/compaction.h>

#include <linux/uaccess.h>
#include <linux/filelock.h>
@@ -457,7 +458,7 @@ static bool nfs_release_folio(struct folio *folio, gfp_t gfp)
	/* If the private flag is set, then the folio is not freeable */
	if (folio_test_private(folio)) {
		if ((current_gfp_context(gfp) & GFP_KERNEL) != GFP_KERNEL ||
		    current_is_kswapd())
		    current_is_kswapd() || current_is_kcompactd())
			return false;
		if (nfs_wb_folio(folio->mapping->host, folio) < 0)
			return false;
+5 −0
Original line number Diff line number Diff line
@@ -80,6 +80,11 @@ static inline unsigned long compact_gap(unsigned int order)
	return 2UL << order;
}

static inline int current_is_kcompactd(void)
{
	return current->flags & PF_KCOMPACTD;
}

#ifdef CONFIG_COMPACTION

extern unsigned int extfrag_for_order(struct zone *zone, unsigned int order);
+1 −1
Original line number Diff line number Diff line
@@ -1701,7 +1701,7 @@ extern struct pid *cad_pid;
#define PF_USED_MATH		0x00002000	/* If unset the fpu must be initialized before use */
#define PF_USER_WORKER		0x00004000	/* Kernel thread cloned from userspace thread */
#define PF_NOFREEZE		0x00008000	/* This thread should not be frozen */
#define PF__HOLE__00010000	0x00010000
#define PF_KCOMPACTD		0x00010000	/* I am kcompactd */
#define PF_KSWAPD		0x00020000	/* I am kswapd */
#define PF_MEMALLOC_NOFS	0x00040000	/* All allocations inherit GFP_NOFS. See memalloc_nfs_save() */
#define PF_MEMALLOC_NOIO	0x00080000	/* All allocations inherit GFP_NOIO. See memalloc_noio_save() */
+3 −0
Original line number Diff line number Diff line
@@ -3181,6 +3181,7 @@ static int kcompactd(void *p)
	long default_timeout = msecs_to_jiffies(HPAGE_FRAG_CHECK_INTERVAL_MSEC);
	long timeout = default_timeout;

	current->flags |= PF_KCOMPACTD;
	set_freezable();

	pgdat->kcompactd_max_order = 0;
@@ -3237,6 +3238,8 @@ static int kcompactd(void *p)
			pgdat->proactive_compact_trigger = false;
	}

	current->flags &= ~PF_KCOMPACTD;

	return 0;
}