diff options
Diffstat (limited to 'fs/nfs/write.c')
| -rw-r--r-- | fs/nfs/write.c | 121 | 
1 files changed, 56 insertions, 65 deletions
diff --git a/fs/nfs/write.c b/fs/nfs/write.c index cc341fc7fd44..db7ba542559e 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -60,14 +60,28 @@ static mempool_t *nfs_wdata_mempool;  static struct kmem_cache *nfs_cdata_cachep;  static mempool_t *nfs_commit_mempool; -struct nfs_commit_data *nfs_commitdata_alloc(void) +struct nfs_commit_data *nfs_commitdata_alloc(bool never_fail)  { -	struct nfs_commit_data *p = mempool_alloc(nfs_commit_mempool, GFP_NOIO); +	struct nfs_commit_data *p; -	if (p) { -		memset(p, 0, sizeof(*p)); -		INIT_LIST_HEAD(&p->pages); +	if (never_fail) +		p = mempool_alloc(nfs_commit_mempool, GFP_NOIO); +	else { +		/* It is OK to do some reclaim, not no safe to wait +		 * for anything to be returned to the pool. +		 * mempool_alloc() cannot handle that particular combination, +		 * so we need two separate attempts. +		 */ +		p = mempool_alloc(nfs_commit_mempool, GFP_NOWAIT); +		if (!p) +			p = kmem_cache_alloc(nfs_cdata_cachep, GFP_NOIO | +					     __GFP_NOWARN | __GFP_NORETRY); +		if (!p) +			return NULL;  	} + +	memset(p, 0, sizeof(*p)); +	INIT_LIST_HEAD(&p->pages);  	return p;  }  EXPORT_SYMBOL_GPL(nfs_commitdata_alloc); @@ -82,8 +96,10 @@ static struct nfs_pgio_header *nfs_writehdr_alloc(void)  {  	struct nfs_pgio_header *p = mempool_alloc(nfs_wdata_mempool, GFP_NOIO); -	if (p) +	if (p) {  		memset(p, 0, sizeof(*p)); +		p->rw_mode = FMODE_WRITE; +	}  	return p;  } @@ -547,9 +563,21 @@ static void nfs_write_error_remove_page(struct nfs_page *req)  {  	nfs_unlock_request(req);  	nfs_end_page_writeback(req); -	nfs_release_request(req);  	generic_error_remove_page(page_file_mapping(req->wb_page),  				  req->wb_page); +	nfs_release_request(req); +} + +static bool +nfs_error_is_fatal_on_server(int err) +{ +	switch (err) { +	case 0: +	case -ERESTARTSYS: +	case -EINTR: +		return false; +	} +	return nfs_error_is_fatal(err);  }  /* @@ -557,8 +585,7 @@ static void nfs_write_error_remove_page(struct nfs_page *req)   * May return an error if the user signalled nfs_wait_on_request().   */  static int nfs_page_async_flush(struct nfs_pageio_descriptor *pgio, -				struct page *page, bool nonblock, -				bool launder) +				struct page *page, bool nonblock)  {  	struct nfs_page *req;  	int ret = 0; @@ -574,19 +601,19 @@ static int nfs_page_async_flush(struct nfs_pageio_descriptor *pgio,  	WARN_ON_ONCE(test_bit(PG_CLEAN, &req->wb_flags));  	ret = 0; +	/* If there is a fatal error that covers this write, just exit */ +	if (nfs_error_is_fatal_on_server(req->wb_context->error)) +		goto out_launder; +  	if (!nfs_pageio_add_request(pgio, req)) {  		ret = pgio->pg_error;  		/* -		 * Remove the problematic req upon fatal errors -		 * in launder case, while other dirty pages can -		 * still be around until they get flushed. +		 * Remove the problematic req upon fatal errors on the server  		 */  		if (nfs_error_is_fatal(ret)) {  			nfs_context_set_write_error(req->wb_context, ret); -			if (launder) { -				nfs_write_error_remove_page(req); -				goto out; -			} +			if (nfs_error_is_fatal_on_server(ret)) +				goto out_launder;  		}  		nfs_redirty_request(req);  		ret = -EAGAIN; @@ -595,16 +622,18 @@ static int nfs_page_async_flush(struct nfs_pageio_descriptor *pgio,  				NFSIOS_WRITEPAGES, 1);  out:  	return ret; +out_launder: +	nfs_write_error_remove_page(req); +	return ret;  }  static int nfs_do_writepage(struct page *page, struct writeback_control *wbc, -			    struct nfs_pageio_descriptor *pgio, bool launder) +			    struct nfs_pageio_descriptor *pgio)  {  	int ret;  	nfs_pageio_cond_complete(pgio, page_index(page)); -	ret = nfs_page_async_flush(pgio, page, wbc->sync_mode == WB_SYNC_NONE, -				   launder); +	ret = nfs_page_async_flush(pgio, page, wbc->sync_mode == WB_SYNC_NONE);  	if (ret == -EAGAIN) {  		redirty_page_for_writepage(wbc, page);  		ret = 0; @@ -616,8 +645,7 @@ static int nfs_do_writepage(struct page *page, struct writeback_control *wbc,   * Write an mmapped page to the server.   */  static int nfs_writepage_locked(struct page *page, -				struct writeback_control *wbc, -				bool launder) +				struct writeback_control *wbc)  {  	struct nfs_pageio_descriptor pgio;  	struct inode *inode = page_file_mapping(page)->host; @@ -626,7 +654,7 @@ static int nfs_writepage_locked(struct page *page,  	nfs_inc_stats(inode, NFSIOS_VFSWRITEPAGE);  	nfs_pageio_init_write(&pgio, inode, 0,  				false, &nfs_async_write_completion_ops); -	err = nfs_do_writepage(page, wbc, &pgio, launder); +	err = nfs_do_writepage(page, wbc, &pgio);  	nfs_pageio_complete(&pgio);  	if (err < 0)  		return err; @@ -639,7 +667,7 @@ int nfs_writepage(struct page *page, struct writeback_control *wbc)  {  	int ret; -	ret = nfs_writepage_locked(page, wbc, false); +	ret = nfs_writepage_locked(page, wbc);  	unlock_page(page);  	return ret;  } @@ -648,7 +676,7 @@ static int nfs_writepages_callback(struct page *page, struct writeback_control *  {  	int ret; -	ret = nfs_do_writepage(page, wbc, data, false); +	ret = nfs_do_writepage(page, wbc, data);  	unlock_page(page);  	return ret;  } @@ -1367,7 +1395,7 @@ void nfs_pageio_init_write(struct nfs_pageio_descriptor *pgio,  		pg_ops = server->pnfs_curr_ld->pg_write_ops;  #endif  	nfs_pageio_init(pgio, inode, pg_ops, compl_ops, &nfs_rw_write_ops, -			server->wsize, ioflags); +			server->wsize, ioflags, GFP_NOIO);  }  EXPORT_SYMBOL_GPL(nfs_pageio_init_write); @@ -1704,50 +1732,14 @@ nfs_commit_list(struct inode *inode, struct list_head *head, int how,  	if (list_empty(head))  		return 0; -	data = nfs_commitdata_alloc(); - -	if (!data) -		goto out_bad; +	data = nfs_commitdata_alloc(true);  	/* Set up the argument struct */  	nfs_init_commit(data, head, NULL, cinfo);  	atomic_inc(&cinfo->mds->rpcs_out);  	return nfs_initiate_commit(NFS_CLIENT(inode), data, NFS_PROTO(inode),  				   data->mds_ops, how, 0); - out_bad: -	nfs_retry_commit(head, NULL, cinfo, 0); -	return -ENOMEM; -} - -int nfs_commit_file(struct file *file, struct nfs_write_verifier *verf) -{ -	struct inode *inode = file_inode(file); -	struct nfs_open_context *open; -	struct nfs_commit_info cinfo; -	struct nfs_page *req; -	int ret; - -	open = get_nfs_open_context(nfs_file_open_context(file)); -	req  = nfs_create_request(open, NULL, NULL, 0, i_size_read(inode)); -	if (IS_ERR(req)) { -		ret = PTR_ERR(req); -		goto out_put; -	} - -	nfs_init_cinfo_from_inode(&cinfo, inode); - -	memcpy(&req->wb_verf, verf, sizeof(struct nfs_write_verifier)); -	nfs_request_add_commit_list(req, &cinfo); -	ret = nfs_commit_inode(inode, FLUSH_SYNC); -	if (ret > 0) -		ret = 0; - -	nfs_free_request(req); -out_put: -	put_nfs_open_context(open); -	return ret;  } -EXPORT_SYMBOL_GPL(nfs_commit_file);  /*   * COMMIT call returned @@ -1985,7 +1977,7 @@ int nfs_wb_page_cancel(struct inode *inode, struct page *page)  /*   * Write back all requests on one page - we do this before reading it.   */ -int nfs_wb_single_page(struct inode *inode, struct page *page, bool launder) +int nfs_wb_page(struct inode *inode, struct page *page)  {  	loff_t range_start = page_file_offset(page);  	loff_t range_end = range_start + (loff_t)(PAGE_SIZE - 1); @@ -2002,7 +1994,7 @@ int nfs_wb_single_page(struct inode *inode, struct page *page, bool launder)  	for (;;) {  		wait_on_page_writeback(page);  		if (clear_page_dirty_for_io(page)) { -			ret = nfs_writepage_locked(page, &wbc, launder); +			ret = nfs_writepage_locked(page, &wbc);  			if (ret < 0)  				goto out_error;  			continue; @@ -2107,7 +2099,6 @@ void nfs_destroy_writepagecache(void)  }  static const struct nfs_rw_ops nfs_rw_write_ops = { -	.rw_mode		= FMODE_WRITE,  	.rw_alloc_header	= nfs_writehdr_alloc,  	.rw_free_header		= nfs_writehdr_free,  	.rw_done		= nfs_writeback_done,  |