forked from mirrors/linux
		
	NFS: Fix up fsync() when the server rebooted
Don't clear the NFS_CONTEXT_RESEND_WRITES flag until after calling nfs_commit_inode(). Otherwise, if nfs_commit_inode() returns an error, we end up with dirty pages in the page cache, but no tag to tell us that those pages need resending. Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com> Signed-off-by: Anna Schumaker <Anna.Schumaker@Netapp.com>
This commit is contained in:
		
							parent
							
								
									b32d285539
								
							
						
					
					
						commit
						2197e9b06c
					
				
					 1 changed files with 16 additions and 21 deletions
				
			
		|  | @ -204,44 +204,39 @@ EXPORT_SYMBOL_GPL(nfs_file_mmap); | ||||||
| static int | static int | ||||||
| nfs_file_fsync_commit(struct file *file, int datasync) | nfs_file_fsync_commit(struct file *file, int datasync) | ||||||
| { | { | ||||||
| 	struct nfs_open_context *ctx = nfs_file_open_context(file); |  | ||||||
| 	struct inode *inode = file_inode(file); | 	struct inode *inode = file_inode(file); | ||||||
| 	int do_resend, status; | 	int ret; | ||||||
| 	int ret = 0; |  | ||||||
| 
 | 
 | ||||||
| 	dprintk("NFS: fsync file(%pD2) datasync %d\n", file, datasync); | 	dprintk("NFS: fsync file(%pD2) datasync %d\n", file, datasync); | ||||||
| 
 | 
 | ||||||
| 	nfs_inc_stats(inode, NFSIOS_VFSFSYNC); | 	nfs_inc_stats(inode, NFSIOS_VFSFSYNC); | ||||||
| 	do_resend = test_and_clear_bit(NFS_CONTEXT_RESEND_WRITES, &ctx->flags); | 	ret = nfs_commit_inode(inode, FLUSH_SYNC); | ||||||
| 	status = nfs_commit_inode(inode, FLUSH_SYNC); | 	if (ret < 0) | ||||||
| 	if (status == 0) | 		return ret; | ||||||
| 		status = file_check_and_advance_wb_err(file); | 	return file_check_and_advance_wb_err(file); | ||||||
| 	if (status < 0) { |  | ||||||
| 		ret = status; |  | ||||||
| 		goto out; |  | ||||||
| 	} |  | ||||||
| 	do_resend |= test_bit(NFS_CONTEXT_RESEND_WRITES, &ctx->flags); |  | ||||||
| 	if (do_resend) |  | ||||||
| 		ret = -EAGAIN; |  | ||||||
| out: |  | ||||||
| 	return ret; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| int | int | ||||||
| nfs_file_fsync(struct file *file, loff_t start, loff_t end, int datasync) | nfs_file_fsync(struct file *file, loff_t start, loff_t end, int datasync) | ||||||
| { | { | ||||||
| 	int ret; | 	struct nfs_open_context *ctx = nfs_file_open_context(file); | ||||||
| 	struct inode *inode = file_inode(file); | 	struct inode *inode = file_inode(file); | ||||||
|  | 	int ret; | ||||||
| 
 | 
 | ||||||
| 	trace_nfs_fsync_enter(inode); | 	trace_nfs_fsync_enter(inode); | ||||||
| 
 | 
 | ||||||
| 	do { | 	for (;;) { | ||||||
| 		ret = file_write_and_wait_range(file, start, end); | 		ret = file_write_and_wait_range(file, start, end); | ||||||
| 		if (ret != 0) | 		if (ret != 0) | ||||||
| 			break; | 			break; | ||||||
| 		ret = nfs_file_fsync_commit(file, datasync); | 		ret = nfs_file_fsync_commit(file, datasync); | ||||||
| 		if (!ret) | 		if (ret != 0) | ||||||
| 			ret = pnfs_sync_inode(inode, !!datasync); | 			break; | ||||||
|  | 		ret = pnfs_sync_inode(inode, !!datasync); | ||||||
|  | 		if (ret != 0) | ||||||
|  | 			break; | ||||||
|  | 		if (!test_and_clear_bit(NFS_CONTEXT_RESEND_WRITES, &ctx->flags)) | ||||||
|  | 			break; | ||||||
| 		/*
 | 		/*
 | ||||||
| 		 * If nfs_file_fsync_commit detected a server reboot, then | 		 * If nfs_file_fsync_commit detected a server reboot, then | ||||||
| 		 * resend all dirty pages that might have been covered by | 		 * resend all dirty pages that might have been covered by | ||||||
|  | @ -249,7 +244,7 @@ nfs_file_fsync(struct file *file, loff_t start, loff_t end, int datasync) | ||||||
| 		 */ | 		 */ | ||||||
| 		start = 0; | 		start = 0; | ||||||
| 		end = LLONG_MAX; | 		end = LLONG_MAX; | ||||||
| 	} while (ret == -EAGAIN); | 	} | ||||||
| 
 | 
 | ||||||
| 	trace_nfs_fsync_exit(inode, ret); | 	trace_nfs_fsync_exit(inode, ret); | ||||||
| 	return ret; | 	return ret; | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Trond Myklebust
						Trond Myklebust