mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	xfs: fix and streamline error handling in xfs_end_io
There are two different cases of buffered I/O errors: - first we can have an already shutdown fs. In that case we should skip any on-disk operations and just clean up the appen transaction if present and destroy the ioend - a real I/O error. In that case we should cleanup any lingering COW blocks. This gets skipped in the current code and is fixed by this patch. Signed-off-by: Christoph Hellwig <hch@lst.de> Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
This commit is contained in:
		
							parent
							
								
									3802a34532
								
							
						
					
					
						commit
						787eb48550
					
				
					 1 changed files with 31 additions and 36 deletions
				
			
		| 
						 | 
					@ -274,54 +274,49 @@ xfs_end_io(
 | 
				
			||||||
	struct xfs_ioend	*ioend =
 | 
						struct xfs_ioend	*ioend =
 | 
				
			||||||
		container_of(work, struct xfs_ioend, io_work);
 | 
							container_of(work, struct xfs_ioend, io_work);
 | 
				
			||||||
	struct xfs_inode	*ip = XFS_I(ioend->io_inode);
 | 
						struct xfs_inode	*ip = XFS_I(ioend->io_inode);
 | 
				
			||||||
 | 
						xfs_off_t		offset = ioend->io_offset;
 | 
				
			||||||
 | 
						size_t			size = ioend->io_size;
 | 
				
			||||||
	int			error = ioend->io_bio->bi_error;
 | 
						int			error = ioend->io_bio->bi_error;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
	 * Set an error if the mount has shut down and proceed with end I/O
 | 
						 * Just clean up the in-memory strutures if the fs has been shut down.
 | 
				
			||||||
	 * processing so it can perform whatever cleanups are necessary.
 | 
					 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	if (XFS_FORCED_SHUTDOWN(ip->i_mount))
 | 
						if (XFS_FORCED_SHUTDOWN(ip->i_mount)) {
 | 
				
			||||||
		error = -EIO;
 | 
							error = -EIO;
 | 
				
			||||||
 | 
							goto done;
 | 
				
			||||||
	/*
 | 
					 | 
				
			||||||
	 * For a CoW extent, we need to move the mapping from the CoW fork
 | 
					 | 
				
			||||||
	 * to the data fork.  If instead an error happened, just dump the
 | 
					 | 
				
			||||||
	 * new blocks.
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	if (ioend->io_type == XFS_IO_COW) {
 | 
					 | 
				
			||||||
		if (error)
 | 
					 | 
				
			||||||
			goto done;
 | 
					 | 
				
			||||||
		if (ioend->io_bio->bi_error) {
 | 
					 | 
				
			||||||
			error = xfs_reflink_cancel_cow_range(ip,
 | 
					 | 
				
			||||||
					ioend->io_offset, ioend->io_size, true);
 | 
					 | 
				
			||||||
			goto done;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		error = xfs_reflink_end_cow(ip, ioend->io_offset,
 | 
					 | 
				
			||||||
				ioend->io_size);
 | 
					 | 
				
			||||||
		if (error)
 | 
					 | 
				
			||||||
			goto done;
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
	 * For unwritten extents we need to issue transactions to convert a
 | 
						 * Clean up any COW blocks on an I/O error.
 | 
				
			||||||
	 * range to normal written extens after the data I/O has finished.
 | 
					 | 
				
			||||||
	 * Detecting and handling completion IO errors is done individually
 | 
					 | 
				
			||||||
	 * for each case as different cleanup operations need to be performed
 | 
					 | 
				
			||||||
	 * on error.
 | 
					 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	if (ioend->io_type == XFS_IO_UNWRITTEN) {
 | 
						if (unlikely(error)) {
 | 
				
			||||||
		if (error)
 | 
							switch (ioend->io_type) {
 | 
				
			||||||
			goto done;
 | 
							case XFS_IO_COW:
 | 
				
			||||||
		error = xfs_iomap_write_unwritten(ip, ioend->io_offset,
 | 
								xfs_reflink_cancel_cow_range(ip, offset, size, true);
 | 
				
			||||||
						  ioend->io_size);
 | 
								break;
 | 
				
			||||||
	} else if (ioend->io_append_trans) {
 | 
							}
 | 
				
			||||||
		error = xfs_setfilesize_ioend(ioend, error);
 | 
					
 | 
				
			||||||
	} else {
 | 
							goto done;
 | 
				
			||||||
		ASSERT(!xfs_ioend_is_append(ioend) ||
 | 
						}
 | 
				
			||||||
		       ioend->io_type == XFS_IO_COW);
 | 
					
 | 
				
			||||||
 | 
						/*
 | 
				
			||||||
 | 
						 * Success:  commit the COW or unwritten blocks if needed.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						switch (ioend->io_type) {
 | 
				
			||||||
 | 
						case XFS_IO_COW:
 | 
				
			||||||
 | 
							error = xfs_reflink_end_cow(ip, offset, size);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case XFS_IO_UNWRITTEN:
 | 
				
			||||||
 | 
							error = xfs_iomap_write_unwritten(ip, offset, size);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							ASSERT(!xfs_ioend_is_append(ioend) || ioend->io_append_trans);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
done:
 | 
					done:
 | 
				
			||||||
 | 
						if (ioend->io_append_trans)
 | 
				
			||||||
 | 
							error = xfs_setfilesize_ioend(ioend, error);
 | 
				
			||||||
	xfs_destroy_ioend(ioend, error);
 | 
						xfs_destroy_ioend(ioend, error);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue