summaryrefslogtreecommitdiff
path: root/fs/nfs/write.c
diff options
context:
space:
mode:
authorTrond Myklebust <trond.myklebust@hammerspace.com>2025-12-31 11:42:31 -0500
committerTrond Myklebust <trond.myklebust@hammerspace.com>2026-01-04 23:03:24 -0500
commitcce0be6eb4971456b703aaeafd571650d314bcca (patch)
tree2d3741ca0b4b4c6e1f538534ac603da5d74834c6 /fs/nfs/write.c
parent857bf9056291a16785ae3be1d291026b2437fc48 (diff)
downloadlwn-cce0be6eb4971456b703aaeafd571650d314bcca.tar.gz
lwn-cce0be6eb4971456b703aaeafd571650d314bcca.zip
NFS: Fix a deadlock involving nfs_release_folio()
Wang Zhaolong reports a deadlock involving NFSv4.1 state recovery waiting on kthreadd, which is attempting to reclaim memory by calling nfs_release_folio(). The latter cannot make progress due to state recovery being needed. It seems that the only safe thing to do here is to kick off a writeback of the folio, without waiting for completion, or else kicking off an asynchronous commit. Reported-by: Wang Zhaolong <wangzhaolong@huaweicloud.com> Fixes: 96780ca55e3c ("NFS: fix up nfs_release_folio() to try to release the page") Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
Diffstat (limited to 'fs/nfs/write.c')
-rw-r--r--fs/nfs/write.c33
1 files changed, 33 insertions, 0 deletions
diff --git a/fs/nfs/write.c b/fs/nfs/write.c
index 336c510f3750..bf412455e8ed 100644
--- a/fs/nfs/write.c
+++ b/fs/nfs/write.c
@@ -2025,6 +2025,39 @@ int nfs_wb_folio_cancel(struct inode *inode, struct folio *folio)
}
/**
+ * nfs_wb_folio_reclaim - Write back all requests on one page
+ * @inode: pointer to page
+ * @folio: pointer to folio
+ *
+ * Assumes that the folio has been locked by the caller
+ */
+int nfs_wb_folio_reclaim(struct inode *inode, struct folio *folio)
+{
+ loff_t range_start = folio_pos(folio);
+ size_t len = folio_size(folio);
+ struct writeback_control wbc = {
+ .sync_mode = WB_SYNC_ALL,
+ .nr_to_write = 0,
+ .range_start = range_start,
+ .range_end = range_start + len - 1,
+ .for_sync = 1,
+ };
+ int ret;
+
+ if (folio_test_writeback(folio))
+ return -EBUSY;
+ if (folio_clear_dirty_for_io(folio)) {
+ trace_nfs_writeback_folio_reclaim(inode, range_start, len);
+ ret = nfs_writepage_locked(folio, &wbc);
+ trace_nfs_writeback_folio_reclaim_done(inode, range_start, len,
+ ret);
+ return ret;
+ }
+ nfs_commit_inode(inode, 0);
+ return 0;
+}
+
+/**
* nfs_wb_folio - Write back all requests on one page
* @inode: pointer to page
* @folio: pointer to folio