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 {