NFS: Fix a write request leak in nfs_invalidate_page()

Ryusuke Konishi says:

The recent truncate_complete_page() clears the dirty flag from a page
before calling a_ops->invalidatepage(),
^^^^^^
static void
truncate_complete_page(struct address_space *mapping, struct page *page)
{
...
cancel_dirty_page(page, PAGE_CACHE_SIZE); <--- Inserted here at
kernel 2.6.20

if (PagePrivate(page))
do_invalidatepage(page, 0); ---> will call
a_ops->invalidatepage()
...
}

and this is disturbing nfs_wb_page_priority() from calling
nfs_writepage_locked() that is expected to handle the pending
request (=nfs_page) associated with the page.

int nfs_wb_page_priority(struct inode *inode, struct page *page, int how)
{
...
if (clear_page_dirty_for_io(page)) {
ret = nfs_writepage_locked(page, &wbc);
if (ret < 0)
goto out;
}
...
}

Since truncate_complete_page() will get rid of the page after
a_ops->invalidatepage() returns, the request (=nfs_page) associated
with the page becomes a garbage in nfs_inode->nfs_page_tree.
------------------------

Fix this by ensuring that nfs_wb_page_priority() recognises that it may
also need to clear out non-dirty pages that have an nfs_page associated
with them.

Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>

+46 -1
+1 -1
fs/nfs/file.c
··· 316 316 if (offset != 0) 317 317 return; 318 318 /* Cancel any unstarted writes on this page */ 319 - nfs_wb_page_priority(page->mapping->host, page, FLUSH_INVALIDATE); 319 + nfs_wb_page_cancel(page->mapping->host, page); 320 320 } 321 321 322 322 static int nfs_release_page(struct page *page, gfp_t gfp)
+44
fs/nfs/write.c
··· 1396 1396 return ret; 1397 1397 } 1398 1398 1399 + int nfs_wb_page_cancel(struct inode *inode, struct page *page) 1400 + { 1401 + struct nfs_page *req; 1402 + loff_t range_start = page_offset(page); 1403 + loff_t range_end = range_start + (loff_t)(PAGE_CACHE_SIZE - 1); 1404 + struct writeback_control wbc = { 1405 + .bdi = page->mapping->backing_dev_info, 1406 + .sync_mode = WB_SYNC_ALL, 1407 + .nr_to_write = LONG_MAX, 1408 + .range_start = range_start, 1409 + .range_end = range_end, 1410 + }; 1411 + int ret = 0; 1412 + 1413 + BUG_ON(!PageLocked(page)); 1414 + for (;;) { 1415 + req = nfs_page_find_request(page); 1416 + if (req == NULL) 1417 + goto out; 1418 + if (test_bit(PG_NEED_COMMIT, &req->wb_flags)) { 1419 + nfs_release_request(req); 1420 + break; 1421 + } 1422 + if (nfs_lock_request_dontget(req)) { 1423 + nfs_inode_remove_request(req); 1424 + /* 1425 + * In case nfs_inode_remove_request has marked the 1426 + * page as being dirty 1427 + */ 1428 + cancel_dirty_page(page, PAGE_CACHE_SIZE); 1429 + nfs_unlock_request(req); 1430 + break; 1431 + } 1432 + ret = nfs_wait_on_request(req); 1433 + if (ret < 0) 1434 + goto out; 1435 + } 1436 + if (!PagePrivate(page)) 1437 + return 0; 1438 + ret = nfs_sync_mapping_wait(page->mapping, &wbc, FLUSH_INVALIDATE); 1439 + out: 1440 + return ret; 1441 + } 1442 + 1399 1443 int nfs_wb_page_priority(struct inode *inode, struct page *page, int how) 1400 1444 { 1401 1445 loff_t range_start = page_offset(page);
+1
include/linux/nfs_fs.h
··· 431 431 extern int nfs_wb_all(struct inode *inode); 432 432 extern int nfs_wb_page(struct inode *inode, struct page* page); 433 433 extern int nfs_wb_page_priority(struct inode *inode, struct page* page, int how); 434 + extern int nfs_wb_page_cancel(struct inode *inode, struct page* page); 434 435 #if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) 435 436 extern int nfs_commit_inode(struct inode *, int); 436 437 extern struct nfs_write_data *nfs_commit_alloc(void);