diff options
Diffstat (limited to 'fs/nfs/write.c')
| -rw-r--r-- | fs/nfs/write.c | 95 | 
1 files changed, 62 insertions, 33 deletions
diff --git a/fs/nfs/write.c b/fs/nfs/write.c index 53ff70e2399..3aea3ca98ab 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -201,6 +201,7 @@ static int nfs_set_page_writeback(struct page *page)  		struct inode *inode = page->mapping->host;  		struct nfs_server *nfss = NFS_SERVER(inode); +		page_cache_get(page);  		if (atomic_long_inc_return(&nfss->writeback) >  				NFS_CONGESTION_ON_THRESH) {  			set_bdi_congested(&nfss->backing_dev_info, @@ -216,6 +217,7 @@ static void nfs_end_page_writeback(struct page *page)  	struct nfs_server *nfss = NFS_SERVER(inode);  	end_page_writeback(page); +	page_cache_release(page);  	if (atomic_long_dec_return(&nfss->writeback) < NFS_CONGESTION_OFF_THRESH)  		clear_bdi_congested(&nfss->backing_dev_info, BLK_RW_ASYNC);  } @@ -421,6 +423,7 @@ static void  nfs_mark_request_dirty(struct nfs_page *req)  {  	__set_page_dirty_nobuffers(req->wb_page); +	__mark_inode_dirty(req->wb_page->mapping->host, I_DIRTY_DATASYNC);  }  #if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) @@ -660,9 +663,11 @@ static int nfs_writepage_setup(struct nfs_open_context *ctx, struct page *page,  	req = nfs_setup_write_request(ctx, page, offset, count);  	if (IS_ERR(req))  		return PTR_ERR(req); +	nfs_mark_request_dirty(req);  	/* Update file length */  	nfs_grow_file(page, offset, count);  	nfs_mark_uptodate(page, req->wb_pgbase, req->wb_bytes); +	nfs_mark_request_dirty(req);  	nfs_clear_page_tag_locked(req);  	return 0;  } @@ -739,8 +744,6 @@ int nfs_updatepage(struct file *file, struct page *page,  	status = nfs_writepage_setup(ctx, page, offset, count);  	if (status < 0)  		nfs_set_pageerror(page); -	else -		__set_page_dirty_nobuffers(page);  	dprintk("NFS:       nfs_updatepage returns %d (isize %lld)\n",  			status, (long long)i_size_read(inode)); @@ -749,13 +752,12 @@ int nfs_updatepage(struct file *file, struct page *page,  static void nfs_writepage_release(struct nfs_page *req)  { +	struct page *page = req->wb_page; -	if (PageError(req->wb_page) || !nfs_reschedule_unstable_write(req)) { -		nfs_end_page_writeback(req->wb_page); +	if (PageError(req->wb_page) || !nfs_reschedule_unstable_write(req))  		nfs_inode_remove_request(req); -	} else -		nfs_end_page_writeback(req->wb_page);  	nfs_clear_page_tag_locked(req); +	nfs_end_page_writeback(page);  }  static int flush_task_priority(int how) @@ -779,7 +781,6 @@ static int nfs_write_rpcsetup(struct nfs_page *req,  		int how)  {  	struct inode *inode = req->wb_context->path.dentry->d_inode; -	int flags = (how & FLUSH_SYNC) ? 0 : RPC_TASK_ASYNC;  	int priority = flush_task_priority(how);  	struct rpc_task *task;  	struct rpc_message msg = { @@ -794,9 +795,10 @@ static int nfs_write_rpcsetup(struct nfs_page *req,  		.callback_ops = call_ops,  		.callback_data = data,  		.workqueue = nfsiod_workqueue, -		.flags = flags, +		.flags = RPC_TASK_ASYNC,  		.priority = priority,  	}; +	int ret = 0;  	/* Set up the RPC argument and reply structs  	 * NB: take care not to mess about with data->commit et al. */ @@ -835,10 +837,18 @@ static int nfs_write_rpcsetup(struct nfs_page *req,  		(unsigned long long)data->args.offset);  	task = rpc_run_task(&task_setup_data); -	if (IS_ERR(task)) -		return PTR_ERR(task); +	if (IS_ERR(task)) { +		ret = PTR_ERR(task); +		goto out; +	} +	if (how & FLUSH_SYNC) { +		ret = rpc_wait_for_completion_task(task); +		if (ret == 0) +			ret = task->tk_status; +	}  	rpc_put_task(task); -	return 0; +out: +	return ret;  }  /* If a nfs_flush_* function fails, it should remove reqs from @head and @@ -847,9 +857,11 @@ static int nfs_write_rpcsetup(struct nfs_page *req,   */  static void nfs_redirty_request(struct nfs_page *req)  { +	struct page *page = req->wb_page; +  	nfs_mark_request_dirty(req); -	nfs_end_page_writeback(req->wb_page);  	nfs_clear_page_tag_locked(req); +	nfs_end_page_writeback(page);  }  /* @@ -1084,16 +1096,15 @@ static void nfs_writeback_release_full(void *calldata)  		if (nfs_write_need_commit(data)) {  			memcpy(&req->wb_verf, &data->verf, sizeof(req->wb_verf));  			nfs_mark_request_commit(req); -			nfs_end_page_writeback(page);  			dprintk(" marked for commit\n");  			goto next;  		}  		dprintk(" OK\n");  remove_request: -		nfs_end_page_writeback(page);  		nfs_inode_remove_request(req);  	next:  		nfs_clear_page_tag_locked(req); +		nfs_end_page_writeback(page);  	}  	nfs_writedata_release(calldata);  } @@ -1190,6 +1201,25 @@ int nfs_writeback_done(struct rpc_task *task, struct nfs_write_data *data)  #if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) +static int nfs_commit_set_lock(struct nfs_inode *nfsi, int may_wait) +{ +	if (!test_and_set_bit(NFS_INO_COMMIT, &nfsi->flags)) +		return 1; +	if (may_wait && !out_of_line_wait_on_bit_lock(&nfsi->flags, +				NFS_INO_COMMIT, nfs_wait_bit_killable, +				TASK_KILLABLE)) +		return 1; +	return 0; +} + +static void nfs_commit_clear_lock(struct nfs_inode *nfsi) +{ +	clear_bit(NFS_INO_COMMIT, &nfsi->flags); +	smp_mb__after_clear_bit(); +	wake_up_bit(&nfsi->flags, NFS_INO_COMMIT); +} + +  static void nfs_commitdata_release(void *data)  {  	struct nfs_write_data *wdata = data; @@ -1207,7 +1237,6 @@ static int nfs_commit_rpcsetup(struct list_head *head,  {  	struct nfs_page *first = nfs_list_entry(head->next);  	struct inode *inode = first->wb_context->path.dentry->d_inode; -	int flags = (how & FLUSH_SYNC) ? 0 : RPC_TASK_ASYNC;  	int priority = flush_task_priority(how);  	struct rpc_task *task;  	struct rpc_message msg = { @@ -1222,7 +1251,7 @@ static int nfs_commit_rpcsetup(struct list_head *head,  		.callback_ops = &nfs_commit_ops,  		.callback_data = data,  		.workqueue = nfsiod_workqueue, -		.flags = flags, +		.flags = RPC_TASK_ASYNC,  		.priority = priority,  	}; @@ -1282,6 +1311,7 @@ nfs_commit_list(struct inode *inode, struct list_head *head, int how)  				BDI_RECLAIMABLE);  		nfs_clear_page_tag_locked(req);  	} +	nfs_commit_clear_lock(NFS_I(inode));  	return -ENOMEM;  } @@ -1337,6 +1367,7 @@ static void nfs_commit_release(void *calldata)  	next:  		nfs_clear_page_tag_locked(req);  	} +	nfs_commit_clear_lock(NFS_I(data->inode));  	nfs_commitdata_release(calldata);  } @@ -1351,8 +1382,11 @@ static const struct rpc_call_ops nfs_commit_ops = {  static int nfs_commit_inode(struct inode *inode, int how)  {  	LIST_HEAD(head); -	int res; +	int may_wait = how & FLUSH_SYNC; +	int res = 0; +	if (!nfs_commit_set_lock(NFS_I(inode), may_wait)) +		goto out;  	spin_lock(&inode->i_lock);  	res = nfs_scan_commit(inode, &head, 0, 0);  	spin_unlock(&inode->i_lock); @@ -1360,7 +1394,13 @@ static int nfs_commit_inode(struct inode *inode, int how)  		int error = nfs_commit_list(inode, &head, how);  		if (error < 0)  			return error; -	} +		if (may_wait) +			wait_on_bit(&NFS_I(inode)->flags, NFS_INO_COMMIT, +					nfs_wait_bit_killable, +					TASK_KILLABLE); +	} else +		nfs_commit_clear_lock(NFS_I(inode)); +out:  	return res;  } @@ -1432,6 +1472,7 @@ int nfs_wb_page_cancel(struct inode *inode, struct page *page)  	BUG_ON(!PageLocked(page));  	for (;;) { +		wait_on_page_writeback(page);  		req = nfs_page_find_request(page);  		if (req == NULL)  			break; @@ -1466,30 +1507,18 @@ int nfs_wb_page(struct inode *inode, struct page *page)  		.range_start = range_start,  		.range_end = range_end,  	}; -	struct nfs_page *req; -	int need_commit;  	int ret;  	while(PagePrivate(page)) { +		wait_on_page_writeback(page);  		if (clear_page_dirty_for_io(page)) {  			ret = nfs_writepage_locked(page, &wbc);  			if (ret < 0)  				goto out_error;  		} -		req = nfs_find_and_lock_request(page); -		if (!req) -			break; -		if (IS_ERR(req)) { -			ret = PTR_ERR(req); +		ret = sync_inode(inode, &wbc); +		if (ret < 0)  			goto out_error; -		} -		need_commit = test_bit(PG_CLEAN, &req->wb_flags); -		nfs_clear_page_tag_locked(req); -		if (need_commit) { -			ret = nfs_commit_inode(inode, FLUSH_SYNC); -			if (ret < 0) -				goto out_error; -		}  	}  	return 0;  out_error:  |