]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
NFS: kswapd must not block in nfs_release_page
authorTrond Myklebust <Trond.Myklebust@netapp.com>
Fri, 30 Jul 2010 19:31:54 +0000 (15:31 -0400)
committerGreg Kroah-Hartman <gregkh@suse.de>
Tue, 10 Aug 2010 17:54:05 +0000 (10:54 -0700)
commit b608b283a962caaa280756bc8563016a71712acf upstream.

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>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
fs/nfs/file.c
fs/nfs/write.c
include/linux/nfs_fs.h

index 8d965bddb87e6f5c8d340eec081ac03d88084047..8fc1c55bba48fd7250f02f2334a56e12b9391c88 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/pagemap.h>
 #include <linux/aio.h>
 #include <linux/gfp.h>
+#include <linux/swap.h>
 
 #include <asm/uaccess.h>
 #include <asm/system.h>
@@ -489,11 +490,19 @@ static void nfs_invalidate_page(struct page *page, unsigned long offset)
  */
 static int nfs_release_page(struct page *page, gfp_t gfp)
 {
+       struct address_space *mapping = page->mapping;
+
        dfprintk(PAGECACHE, "NFS: release_page(%p)\n", page);
 
        /* Only do I/O if gfp is a superset of GFP_KERNEL */
-       if ((gfp & GFP_KERNEL) == GFP_KERNEL)
-               nfs_wb_page(page->mapping->host, page);
+       if (mapping && (gfp & GFP_KERNEL) == GFP_KERNEL) {
+               int how = FLUSH_SYNC;
+
+               /* Don't let kswapd deadlock waiting for OOM RPC calls */
+               if (current_is_kswapd())
+                       how = 0;
+               nfs_commit_inode(mapping->host, how);
+       }
        /* If PagePrivate() is set, then the page is not freeable */
        if (PagePrivate(page))
                return 0;
index 91679e2631ee0ebaff79cd96fd6e3019ca5a49d8..0a6c65a1f9d786c6116c2698cde922ae887e8dd5 100644 (file)
@@ -1379,7 +1379,7 @@ static const struct rpc_call_ops nfs_commit_ops = {
        .rpc_release = nfs_commit_release,
 };
 
-static int nfs_commit_inode(struct inode *inode, int how)
+int nfs_commit_inode(struct inode *inode, int how)
 {
        LIST_HEAD(head);
        int may_wait = how & FLUSH_SYNC;
@@ -1443,7 +1443,7 @@ out_mark_dirty:
        return ret;
 }
 #else
-static int nfs_commit_inode(struct inode *inode, int how)
+int nfs_commit_inode(struct inode *inode, int how)
 {
        return 0;
 }
index 07ce4609fe50416fd87e19c592fc8a978aa316bf..d2b552a6012f3776538d551d26259866311c28c6 100644 (file)
@@ -479,6 +479,7 @@ extern int nfs_wb_all(struct inode *inode);
 extern int nfs_wb_page(struct inode *inode, struct page* page);
 extern int nfs_wb_page_cancel(struct inode *inode, struct page* page);
 #if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4)
+extern int  nfs_commit_inode(struct inode *, int);
 extern struct nfs_write_data *nfs_commitdata_alloc(void);
 extern void nfs_commit_free(struct nfs_write_data *wdata);
 #endif