Commit b608b283a962caaa280756bc8563016a71712acf
1 parent
674b222292
NFS: kswapd must not block in nfs_release_page
See https://bugzilla.kernel.org/show_bug.cgi?id=16056 If other processes are blocked waiting for kswapd to free up some memory so that they can make progress, then we cannot allow kswapd to block on those processes. Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com> Cc: stable@kernel.org
Showing 3 changed files with 14 additions and 4 deletions Side-by-side Diff
fs/nfs/file.c
... | ... | @@ -27,6 +27,7 @@ |
27 | 27 | #include <linux/pagemap.h> |
28 | 28 | #include <linux/aio.h> |
29 | 29 | #include <linux/gfp.h> |
30 | +#include <linux/swap.h> | |
30 | 31 | |
31 | 32 | #include <asm/uaccess.h> |
32 | 33 | #include <asm/system.h> |
33 | 34 | |
... | ... | @@ -493,11 +494,19 @@ |
493 | 494 | */ |
494 | 495 | static int nfs_release_page(struct page *page, gfp_t gfp) |
495 | 496 | { |
497 | + struct address_space *mapping = page->mapping; | |
498 | + | |
496 | 499 | dfprintk(PAGECACHE, "NFS: release_page(%p)\n", page); |
497 | 500 | |
498 | 501 | /* Only do I/O if gfp is a superset of GFP_KERNEL */ |
499 | - if ((gfp & GFP_KERNEL) == GFP_KERNEL) | |
500 | - nfs_wb_page(page->mapping->host, page); | |
502 | + if (mapping && (gfp & GFP_KERNEL) == GFP_KERNEL) { | |
503 | + int how = FLUSH_SYNC; | |
504 | + | |
505 | + /* Don't let kswapd deadlock waiting for OOM RPC calls */ | |
506 | + if (current_is_kswapd()) | |
507 | + how = 0; | |
508 | + nfs_commit_inode(mapping->host, how); | |
509 | + } | |
501 | 510 | /* If PagePrivate() is set, then the page is not freeable */ |
502 | 511 | if (PagePrivate(page)) |
503 | 512 | return 0; |
fs/nfs/write.c
... | ... | @@ -1379,7 +1379,7 @@ |
1379 | 1379 | .rpc_release = nfs_commit_release, |
1380 | 1380 | }; |
1381 | 1381 | |
1382 | -static int nfs_commit_inode(struct inode *inode, int how) | |
1382 | +int nfs_commit_inode(struct inode *inode, int how) | |
1383 | 1383 | { |
1384 | 1384 | LIST_HEAD(head); |
1385 | 1385 | int may_wait = how & FLUSH_SYNC; |
... | ... | @@ -1443,7 +1443,7 @@ |
1443 | 1443 | return ret; |
1444 | 1444 | } |
1445 | 1445 | #else |
1446 | -static int nfs_commit_inode(struct inode *inode, int how) | |
1446 | +int nfs_commit_inode(struct inode *inode, int how) | |
1447 | 1447 | { |
1448 | 1448 | return 0; |
1449 | 1449 | } |
include/linux/nfs_fs.h
... | ... | @@ -493,6 +493,7 @@ |
493 | 493 | extern int nfs_wb_page(struct inode *inode, struct page* page); |
494 | 494 | extern int nfs_wb_page_cancel(struct inode *inode, struct page* page); |
495 | 495 | #if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) |
496 | +extern int nfs_commit_inode(struct inode *, int); | |
496 | 497 | extern struct nfs_write_data *nfs_commitdata_alloc(void); |
497 | 498 | extern void nfs_commit_free(struct nfs_write_data *wdata); |
498 | 499 | #endif |