| From stable-bounces@linux.kernel.org Fri Oct 19 14:26:57 2007 |
| From: Trond Myklebust <Trond.Myklebust@netapp.com> |
| Date: Fri, 19 Oct 2007 17:26:11 -0400 |
| Subject: NFS: Fix a writeback race... |
| To: stable <stable@kernel.org> |
| Message-ID: <1192829171.7466.21.camel@heimdal.trondhjem.org> |
| |
| From: Trond Myklebust <Trond.Myklebust@netapp.com> |
| |
| patch 61e930a904966cc37e0a3404276f0b73037e57ca in mainline |
| |
| This patch fixes a regression that was introduced by commit |
| 44dd151d5c21234cc534c47d7382f5c28c3143cd |
| |
| We cannot zero the user page in nfs_mark_uptodate() any more, since |
| |
| a) We'd be modifying the page without holding the page lock |
| b) We can race with other updates of the page, most notably |
| because of the call to nfs_wb_page() in nfs_writepage_setup(). |
| |
| Instead, we do the zeroing in nfs_update_request() if we see that we're |
| creating a request that might potentially be marked as up to date. |
| |
| Thanks to Olivier Paquet for reporting the bug and providing a test-case. |
| |
| Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> |
| |
| --- |
| fs/nfs/write.c | 17 +++++++++++++---- |
| 1 file changed, 13 insertions(+), 4 deletions(-) |
| |
| --- a/fs/nfs/write.c |
| +++ b/fs/nfs/write.c |
| @@ -167,8 +167,6 @@ static void nfs_mark_uptodate(struct pag |
| return; |
| if (count != nfs_page_length(page)) |
| return; |
| - if (count != PAGE_CACHE_SIZE) |
| - zero_user_page(page, count, PAGE_CACHE_SIZE - count, KM_USER0); |
| SetPageUptodate(page); |
| } |
| |
| @@ -643,7 +641,8 @@ static struct nfs_page * nfs_update_requ |
| return ERR_PTR(error); |
| } |
| spin_unlock(&inode->i_lock); |
| - return new; |
| + req = new; |
| + goto zero_page; |
| } |
| spin_unlock(&inode->i_lock); |
| |
| @@ -671,13 +670,23 @@ static struct nfs_page * nfs_update_requ |
| if (offset < req->wb_offset) { |
| req->wb_offset = offset; |
| req->wb_pgbase = offset; |
| - req->wb_bytes = rqend - req->wb_offset; |
| + req->wb_bytes = max(end, rqend) - req->wb_offset; |
| + goto zero_page; |
| } |
| |
| if (end > rqend) |
| req->wb_bytes = end - req->wb_offset; |
| |
| return req; |
| +zero_page: |
| + /* If this page might potentially be marked as up to date, |
| + * then we need to zero any uninitalised data. */ |
| + if (req->wb_pgbase == 0 && req->wb_bytes != PAGE_CACHE_SIZE |
| + && !PageUptodate(req->wb_page)) |
| + zero_user_page(req->wb_page, req->wb_bytes, |
| + PAGE_CACHE_SIZE - req->wb_bytes, |
| + KM_USER0); |
| + return req; |
| } |
| |
| int nfs_flush_incompatible(struct file *file, struct page *page) |