]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
NFS: fix nfs_release_folio() to not deadlock via kcompactd writeback
authorMike Snitzer <snitzer@kernel.org>
Tue, 25 Feb 2025 02:20:02 +0000 (21:20 -0500)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 13 Mar 2025 12:01:58 +0000 (13:01 +0100)
commit ce6d9c1c2b5cc785016faa11b48b6cd317eb367e upstream.

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: 96780ca55e3c ("NFS: fix up nfs_release_folio() to try to release the page")
Signed-off-by: Mike 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: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
fs/nfs/file.c
include/linux/compaction.h
include/linux/sched.h
mm/compaction.c

index 6800ee92d742a86c55363701f72a966a7a83a035..153d25d4b810c50c09a7b50784737f53e2a33ef2 100644 (file)
@@ -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>
@@ -451,7 +452,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;
index e947764960496470aa3d9afc6b811fac05871712..7bf0c521db6340198823de8b8a6cfd670baf9bd8 100644 (file)
@@ -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);
index 8982820dae2131d3da4c22555528491d76609d37..0d1d70aded38f6be700c120c3529096b6a0be1db 100644 (file)
@@ -1682,7 +1682,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() */
index 384e4672998e55aa16c0eeab94a404cca7a66fc7..77dbb9022b47f0e74cf3269159ebde5b516f4181 100644 (file)
@@ -3164,6 +3164,7 @@ static int kcompactd(void *p)
        if (!cpumask_empty(cpumask))
                set_cpus_allowed_ptr(tsk, cpumask);
 
+       current->flags |= PF_KCOMPACTD;
        set_freezable();
 
        pgdat->kcompactd_max_order = 0;
@@ -3220,6 +3221,8 @@ static int kcompactd(void *p)
                        pgdat->proactive_compact_trigger = false;
        }
 
+       current->flags &= ~PF_KCOMPACTD;
+
        return 0;
 }