Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > by-pkgid > 27922b4260f65d317aabda37e42bbbff > files > 1137

kernel-2.6.18-238.el5.src.rpm

From: Jeff Layton <jlayton@redhat.com>
Date: Fri, 10 Dec 2010 18:07:02 -0500
Subject: [fs] nfs: remove problematic calls to nfs_clear_request
Message-id: <1292004422-17475-4-git-send-email-jlayton@redhat.com>
Patchwork-id: 30074
O-Subject: [RHEL5.6 PATCH 3/3] BZ#656492: nfs: remove extraneous and problematic
	calls to nfs_clear_request
Bugzilla: 656492
RH-Acked-by: J. Bruce Fields <bfields@redhat.com>

This patch is backported from the upstream patch with the same title. The only
real difference is that RHEL5 does not have nfs_set_page_tag_locked or
nfs_clear_page_tag_locked. It does have nfs_clear_page_writeback however
which checks whether wb_page is NULL, so this patch converts that to a
check for PG_MAPPED.

---------------------------[snip]--------------------------

When a nfs_page is freed, nfs_free_request is called which also calls
nfs_clear_request to clean out the lock and open contexts and free the
pagecache page.

However, a couple of places in the nfs code call nfs_clear_request
themselves. What happens here if the refcount on the request is still high?
We'll be releasing contexts and freeing pointers while the request is
possibly still in use.

Remove those bare calls to nfs_clear_context. That should only be done when
the request is being freed.

Note that when doing this, we need to watch out for tests of req->wb_page.
Previously, nfs_set_page_tag_locked() and nfs_clear_page_tag_locked()
check the value of req->wb_page to figure out if the page is mapped
into the nfsi->nfs_page_tree. We now indicate the page is mapped using
the new bit PG_MAPPED in req->wb_flags.

Reported-by: Jeff Layton <jlayton@redhat.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>

diff --git a/fs/nfs/pagelist.c b/fs/nfs/pagelist.c
index c5dcdf4..895d567 100644
--- a/fs/nfs/pagelist.c
+++ b/fs/nfs/pagelist.c
@@ -131,7 +131,7 @@ void nfs_clear_page_writeback(struct nfs_page *req)
 {
 	struct nfs_inode *nfsi = NFS_I(req->wb_context->path.dentry->d_inode);
 
-	if (req->wb_page != NULL) {
+	if (test_bit(PG_MAPPED, &req->wb_flags)) {
 		spin_lock(&nfsi->req_lock);
 		radix_tree_tag_clear(&nfsi->nfs_page_tree, req->wb_index, NFS_PAGE_TAG_WRITEBACK);
 		spin_unlock(&nfsi->req_lock);
diff --git a/fs/nfs/read.c b/fs/nfs/read.c
index 6cec3d8..7bb2e7c 100644
--- a/fs/nfs/read.c
+++ b/fs/nfs/read.c
@@ -268,7 +268,6 @@ static void nfs_readpage_release(struct nfs_page *req)
 			(long long)NFS_FILEID(req->wb_context->path.dentry->d_inode),
 			req->wb_bytes,
 			(long long)req_offset(req));
-	nfs_clear_request(req);
 	nfs_release_request(req);
 }
 
diff --git a/fs/nfs/write.c b/fs/nfs/write.c
index c69be8c..b6b3b61 100644
--- a/fs/nfs/write.c
+++ b/fs/nfs/write.c
@@ -436,6 +436,7 @@ static int nfs_inode_add_request(struct inode *inode, struct nfs_page *req)
 		if (nfs_have_delegation(inode, FMODE_WRITE))
 			nfsi->change_attr++;
 	}
+	set_bit(PG_MAPPED, &req->wb_flags);
 	SetPageNfsWriting(req->wb_page);
 	nfsi->npages++;
 	atomic_inc(&req->wb_count);
@@ -454,6 +455,7 @@ static void nfs_inode_remove_request(struct nfs_page *req)
 
 	spin_lock(&nfsi->req_lock);
 	ClearPageNfsWriting(req->wb_page);
+	clear_bit(PG_MAPPED, &req->wb_flags);
 	radix_tree_delete(&nfsi->nfs_page_tree, req->wb_index);
 	nfsi->npages--;
 	if (!nfsi->npages) {
@@ -461,7 +463,6 @@ static void nfs_inode_remove_request(struct nfs_page *req)
 		iput(inode);
 	} else
 		spin_unlock(&nfsi->req_lock);
-	nfs_clear_request(req);
 	nfs_release_request(req);
 }
 
diff --git a/include/linux/nfs_page.h b/include/linux/nfs_page.h
index 6591a44..7eb96ec 100644
--- a/include/linux/nfs_page.h
+++ b/include/linux/nfs_page.h
@@ -30,6 +30,7 @@
 #define PG_BUSY			0
 #define PG_NEED_COMMIT		1
 #define PG_NEED_RESCHED		2
+#define PG_MAPPED		3
 
 struct nfs_inode;
 struct nfs_page {