NFS: Avoid races between writebacks and truncation
Currently, there is no serialisation between NFS asynchronous writebacks and truncation at the page level due to the fact that nfs_sync_inode() cannot lock the pages that it is about to write out. This means that it is possible to be flushing out data (and calling something like set_page_writeback()) while the page cache is busy evicting the page. Oops... Use the hooks provided in try_to_release_page() to ensure that dirty pages are always written back to storage before we evict them. Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
This commit is contained in:
parent
b92dccf65b
commit
cd52ed3553
|
@ -316,6 +316,17 @@ static int nfs_commit_write(struct file *file, struct page *page, unsigned offse
|
|||
return status;
|
||||
}
|
||||
|
||||
static int nfs_invalidate_page(struct page *page, unsigned long offset)
|
||||
{
|
||||
/* FIXME: we really should cancel any unstarted writes on this page */
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int nfs_release_page(struct page *page, gfp_t gfp)
|
||||
{
|
||||
return !nfs_wb_page(page->mapping->host, page);
|
||||
}
|
||||
|
||||
struct address_space_operations nfs_file_aops = {
|
||||
.readpage = nfs_readpage,
|
||||
.readpages = nfs_readpages,
|
||||
|
@ -324,6 +335,8 @@ struct address_space_operations nfs_file_aops = {
|
|||
.writepages = nfs_writepages,
|
||||
.prepare_write = nfs_prepare_write,
|
||||
.commit_write = nfs_commit_write,
|
||||
.invalidatepage = nfs_invalidate_page,
|
||||
.releasepage = nfs_release_page,
|
||||
#ifdef CONFIG_NFS_DIRECTIO
|
||||
.direct_IO = nfs_direct_IO,
|
||||
#endif
|
||||
|
|
|
@ -85,6 +85,10 @@ nfs_create_request(struct nfs_open_context *ctx, struct inode *inode,
|
|||
atomic_set(&req->wb_complete, 0);
|
||||
req->wb_index = page->index;
|
||||
page_cache_get(page);
|
||||
BUG_ON(PagePrivate(page));
|
||||
BUG_ON(!PageLocked(page));
|
||||
BUG_ON(page->mapping->host != inode);
|
||||
SetPagePrivate(page);
|
||||
req->wb_offset = offset;
|
||||
req->wb_pgbase = offset;
|
||||
req->wb_bytes = count;
|
||||
|
@ -147,8 +151,10 @@ void nfs_clear_page_writeback(struct nfs_page *req)
|
|||
*/
|
||||
void nfs_clear_request(struct nfs_page *req)
|
||||
{
|
||||
if (req->wb_page) {
|
||||
page_cache_release(req->wb_page);
|
||||
struct page *page = req->wb_page;
|
||||
if (page != NULL) {
|
||||
ClearPagePrivate(page);
|
||||
page_cache_release(page);
|
||||
req->wb_page = NULL;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue