Skip to content
/ linux Public

Commit a4810f8

Browse files
Trond Myklebustgregkh
authored andcommitted
NFS: Fix a deadlock involving nfs_release_folio()
[ Upstream commit cce0be6 ] 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: 96780ca ("NFS: fix up nfs_release_folio() to try to release the page") Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com> [ Minor conflict resolved. ] Signed-off-by: Li hongliang <1468888505@139.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent 1562138 commit a4810f8

File tree

4 files changed

+39
-1
lines changed

4 files changed

+39
-1
lines changed

fs/nfs/file.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -459,7 +459,8 @@ static bool nfs_release_folio(struct folio *folio, gfp_t gfp)
459459
if ((current_gfp_context(gfp) & GFP_KERNEL) != GFP_KERNEL ||
460460
current_is_kswapd() || current_is_kcompactd())
461461
return false;
462-
if (nfs_wb_folio(folio_file_mapping(folio)->host, folio) < 0)
462+
if (nfs_wb_folio_reclaim(folio_file_mapping(folio)->host, folio) < 0 ||
463+
folio_test_private(folio))
463464
return false;
464465
}
465466
return nfs_fscache_release_folio(folio, gfp);

fs/nfs/nfstrace.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1033,6 +1033,9 @@ DECLARE_EVENT_CLASS(nfs_folio_event_done,
10331033
DEFINE_NFS_FOLIO_EVENT(nfs_aop_readpage);
10341034
DEFINE_NFS_FOLIO_EVENT_DONE(nfs_aop_readpage_done);
10351035

1036+
DEFINE_NFS_FOLIO_EVENT(nfs_writeback_folio_reclaim);
1037+
DEFINE_NFS_FOLIO_EVENT_DONE(nfs_writeback_folio_reclaim_done);
1038+
10361039
DEFINE_NFS_FOLIO_EVENT(nfs_writeback_folio);
10371040
DEFINE_NFS_FOLIO_EVENT_DONE(nfs_writeback_folio_done);
10381041

fs/nfs/write.c

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2121,6 +2121,39 @@ int nfs_wb_folio_cancel(struct inode *inode, struct folio *folio)
21212121
return ret;
21222122
}
21232123

2124+
/**
2125+
* nfs_wb_folio_reclaim - Write back all requests on one page
2126+
* @inode: pointer to page
2127+
* @folio: pointer to folio
2128+
*
2129+
* Assumes that the folio has been locked by the caller
2130+
*/
2131+
int nfs_wb_folio_reclaim(struct inode *inode, struct folio *folio)
2132+
{
2133+
loff_t range_start = folio_pos(folio);
2134+
size_t len = folio_size(folio);
2135+
struct writeback_control wbc = {
2136+
.sync_mode = WB_SYNC_ALL,
2137+
.nr_to_write = 0,
2138+
.range_start = range_start,
2139+
.range_end = range_start + len - 1,
2140+
.for_sync = 1,
2141+
};
2142+
int ret;
2143+
2144+
if (folio_test_writeback(folio))
2145+
return -EBUSY;
2146+
if (folio_clear_dirty_for_io(folio)) {
2147+
trace_nfs_writeback_folio_reclaim(inode, range_start, len);
2148+
ret = nfs_writepage_locked(folio, &wbc);
2149+
trace_nfs_writeback_folio_reclaim_done(inode, range_start, len,
2150+
ret);
2151+
return ret;
2152+
}
2153+
nfs_commit_inode(inode, 0);
2154+
return 0;
2155+
}
2156+
21242157
/**
21252158
* nfs_wb_folio - Write back all requests on one page
21262159
* @inode: pointer to page

include/linux/nfs_fs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -608,6 +608,7 @@ extern int nfs_update_folio(struct file *file, struct folio *folio,
608608
extern int nfs_sync_inode(struct inode *inode);
609609
extern int nfs_wb_all(struct inode *inode);
610610
extern int nfs_wb_folio(struct inode *inode, struct folio *folio);
611+
extern int nfs_wb_folio_reclaim(struct inode *inode, struct folio *folio);
611612
int nfs_wb_folio_cancel(struct inode *inode, struct folio *folio);
612613
extern int nfs_commit_inode(struct inode *, int);
613614
extern struct nfs_commit_data *nfs_commitdata_alloc(void);

0 commit comments

Comments
 (0)