diff options
Diffstat (limited to 'fs/xfs/xfs_buf.c')
| -rw-r--r-- | fs/xfs/xfs_buf.c | 212 | 
1 files changed, 73 insertions, 139 deletions
| diff --git a/fs/xfs/xfs_buf.c b/fs/xfs/xfs_buf.c index e9c058e3761c..e839907e8492 100644 --- a/fs/xfs/xfs_buf.c +++ b/fs/xfs/xfs_buf.c @@ -34,16 +34,6 @@  static kmem_zone_t *xfs_buf_zone; -#ifdef XFS_BUF_LOCK_TRACKING -# define XB_SET_OWNER(bp)	((bp)->b_last_holder = current->pid) -# define XB_CLEAR_OWNER(bp)	((bp)->b_last_holder = -1) -# define XB_GET_OWNER(bp)	((bp)->b_last_holder) -#else -# define XB_SET_OWNER(bp)	do { } while (0) -# define XB_CLEAR_OWNER(bp)	do { } while (0) -# define XB_GET_OWNER(bp)	do { } while (0) -#endif -  #define xb_to_gfp(flags) \  	((((flags) & XBF_READ_AHEAD) ? __GFP_NORETRY : GFP_NOFS) | __GFP_NOWARN) @@ -226,7 +216,6 @@ _xfs_buf_alloc(  	INIT_LIST_HEAD(&bp->b_li_list);  	sema_init(&bp->b_sema, 0); /* held, no waiters */  	spin_lock_init(&bp->b_lock); -	XB_SET_OWNER(bp);  	bp->b_target = target;  	bp->b_flags = flags; @@ -757,11 +746,7 @@ _xfs_buf_read(  	bp->b_flags &= ~(XBF_WRITE | XBF_ASYNC | XBF_READ_AHEAD);  	bp->b_flags |= flags & (XBF_READ | XBF_ASYNC | XBF_READ_AHEAD); -	if (flags & XBF_ASYNC) { -		xfs_buf_submit(bp); -		return 0; -	} -	return xfs_buf_submit_wait(bp); +	return xfs_buf_submit(bp);  }  xfs_buf_t * @@ -846,7 +831,7 @@ xfs_buf_read_uncached(  	bp->b_flags |= XBF_READ;  	bp->b_ops = ops; -	xfs_buf_submit_wait(bp); +	xfs_buf_submit(bp);  	if (bp->b_error) {  		int	error = bp->b_error;  		xfs_buf_relse(bp); @@ -1095,12 +1080,10 @@ xfs_buf_trylock(  	int			locked;  	locked = down_trylock(&bp->b_sema) == 0; -	if (locked) { -		XB_SET_OWNER(bp); +	if (locked)  		trace_xfs_buf_trylock(bp, _RET_IP_); -	} else { +	else  		trace_xfs_buf_trylock_fail(bp, _RET_IP_); -	}  	return locked;  } @@ -1122,7 +1105,6 @@ xfs_buf_lock(  	if (atomic_read(&bp->b_pin_count) && (bp->b_flags & XBF_STALE))  		xfs_log_force(bp->b_target->bt_mount, 0);  	down(&bp->b_sema); -	XB_SET_OWNER(bp);  	trace_xfs_buf_lock_done(bp, _RET_IP_);  } @@ -1133,9 +1115,7 @@ xfs_buf_unlock(  {  	ASSERT(xfs_buf_islocked(bp)); -	XB_CLEAR_OWNER(bp);  	up(&bp->b_sema); -  	trace_xfs_buf_unlock(bp, _RET_IP_);  } @@ -1249,7 +1229,7 @@ xfs_bwrite(  	bp->b_flags &= ~(XBF_ASYNC | XBF_READ | _XBF_DELWRI_Q |  			 XBF_WRITE_FAIL | XBF_DONE); -	error = xfs_buf_submit_wait(bp); +	error = xfs_buf_submit(bp);  	if (error) {  		xfs_force_shutdown(bp->b_target->bt_mount,  				   SHUTDOWN_META_IO_ERROR); @@ -1453,29 +1433,55 @@ _xfs_buf_ioapply(  }  /* - * Asynchronous IO submission path. This transfers the buffer lock ownership and - * the current reference to the IO. It is not safe to reference the buffer after - * a call to this function unless the caller holds an additional reference - * itself. + * Wait for I/O completion of a sync buffer and return the I/O error code.   */ -void -xfs_buf_submit( +static int +xfs_buf_iowait(  	struct xfs_buf	*bp)  { +	ASSERT(!(bp->b_flags & XBF_ASYNC)); + +	trace_xfs_buf_iowait(bp, _RET_IP_); +	wait_for_completion(&bp->b_iowait); +	trace_xfs_buf_iowait_done(bp, _RET_IP_); + +	return bp->b_error; +} + +/* + * Buffer I/O submission path, read or write. Asynchronous submission transfers + * the buffer lock ownership and the current reference to the IO. It is not + * safe to reference the buffer after a call to this function unless the caller + * holds an additional reference itself. + */ +int +__xfs_buf_submit( +	struct xfs_buf	*bp, +	bool		wait) +{ +	int		error = 0; +  	trace_xfs_buf_submit(bp, _RET_IP_);  	ASSERT(!(bp->b_flags & _XBF_DELWRI_Q)); -	ASSERT(bp->b_flags & XBF_ASYNC);  	/* on shutdown we stale and complete the buffer immediately */  	if (XFS_FORCED_SHUTDOWN(bp->b_target->bt_mount)) {  		xfs_buf_ioerror(bp, -EIO);  		bp->b_flags &= ~XBF_DONE;  		xfs_buf_stale(bp); -		xfs_buf_ioend(bp); -		return; +		if (bp->b_flags & XBF_ASYNC) +			xfs_buf_ioend(bp); +		return -EIO;  	} +	/* +	 * Grab a reference so the buffer does not go away underneath us. For +	 * async buffers, I/O completion drops the callers reference, which +	 * could occur before submission returns. +	 */ +	xfs_buf_hold(bp); +  	if (bp->b_flags & XBF_WRITE)  		xfs_buf_wait_unpin(bp); @@ -1483,22 +1489,13 @@ xfs_buf_submit(  	bp->b_io_error = 0;  	/* -	 * The caller's reference is released during I/O completion. -	 * This occurs some time after the last b_io_remaining reference is -	 * released, so after we drop our Io reference we have to have some -	 * other reference to ensure the buffer doesn't go away from underneath -	 * us. Take a direct reference to ensure we have safe access to the -	 * buffer until we are finished with it. -	 */ -	xfs_buf_hold(bp); - -	/*  	 * Set the count to 1 initially, this will stop an I/O completion  	 * callout which happens before we have started all the I/O from calling  	 * xfs_buf_ioend too early.  	 */  	atomic_set(&bp->b_io_remaining, 1); -	xfs_buf_ioacct_inc(bp); +	if (bp->b_flags & XBF_ASYNC) +		xfs_buf_ioacct_inc(bp);  	_xfs_buf_ioapply(bp);  	/* @@ -1507,74 +1504,19 @@ xfs_buf_submit(  	 * that we don't return to the caller with completion still pending.  	 */  	if (atomic_dec_and_test(&bp->b_io_remaining) == 1) { -		if (bp->b_error) +		if (bp->b_error || !(bp->b_flags & XBF_ASYNC))  			xfs_buf_ioend(bp);  		else  			xfs_buf_ioend_async(bp);  	} -	xfs_buf_rele(bp); -	/* Note: it is not safe to reference bp now we've dropped our ref */ -} - -/* - * Synchronous buffer IO submission path, read or write. - */ -int -xfs_buf_submit_wait( -	struct xfs_buf	*bp) -{ -	int		error; - -	trace_xfs_buf_submit_wait(bp, _RET_IP_); - -	ASSERT(!(bp->b_flags & (_XBF_DELWRI_Q | XBF_ASYNC))); - -	if (XFS_FORCED_SHUTDOWN(bp->b_target->bt_mount)) { -		xfs_buf_ioerror(bp, -EIO); -		xfs_buf_stale(bp); -		bp->b_flags &= ~XBF_DONE; -		return -EIO; -	} - -	if (bp->b_flags & XBF_WRITE) -		xfs_buf_wait_unpin(bp); - -	/* clear the internal error state to avoid spurious errors */ -	bp->b_io_error = 0; +	if (wait) +		error = xfs_buf_iowait(bp);  	/* -	 * For synchronous IO, the IO does not inherit the submitters reference -	 * count, nor the buffer lock. Hence we cannot release the reference we -	 * are about to take until we've waited for all IO completion to occur, -	 * including any xfs_buf_ioend_async() work that may be pending. -	 */ -	xfs_buf_hold(bp); - -	/* -	 * Set the count to 1 initially, this will stop an I/O completion -	 * callout which happens before we have started all the I/O from calling -	 * xfs_buf_ioend too early. -	 */ -	atomic_set(&bp->b_io_remaining, 1); -	_xfs_buf_ioapply(bp); - -	/* -	 * make sure we run completion synchronously if it raced with us and is -	 * already complete. -	 */ -	if (atomic_dec_and_test(&bp->b_io_remaining) == 1) -		xfs_buf_ioend(bp); - -	/* wait for completion before gathering the error from the buffer */ -	trace_xfs_buf_iowait(bp, _RET_IP_); -	wait_for_completion(&bp->b_iowait); -	trace_xfs_buf_iowait_done(bp, _RET_IP_); -	error = bp->b_error; - -	/* -	 * all done now, we can release the hold that keeps the buffer -	 * referenced for the entire IO. +	 * Release the hold that keeps the buffer referenced for the entire +	 * I/O. Note that if the buffer is async, it is not safe to reference +	 * after this release.  	 */  	xfs_buf_rele(bp);  	return error; @@ -1972,16 +1914,11 @@ xfs_buf_cmp(  }  /* - * submit buffers for write. - * - * When we have a large buffer list, we do not want to hold all the buffers - * locked while we block on the request queue waiting for IO dispatch. To avoid - * this problem, we lock and submit buffers in groups of 50, thereby minimising - * the lock hold times for lists which may contain thousands of objects. - * - * To do this, we sort the buffer list before we walk the list to lock and - * submit buffers, and we plug and unplug around each group of buffers we - * submit. + * Submit buffers for write. If wait_list is specified, the buffers are + * submitted using sync I/O and placed on the wait list such that the caller can + * iowait each buffer. Otherwise async I/O is used and the buffers are released + * at I/O completion time. In either case, buffers remain locked until I/O + * completes and the buffer is released from the queue.   */  static int  xfs_buf_delwri_submit_buffers( @@ -2023,21 +1960,21 @@ xfs_buf_delwri_submit_buffers(  		trace_xfs_buf_delwri_split(bp, _RET_IP_);  		/* -		 * We do all IO submission async. This means if we need -		 * to wait for IO completion we need to take an extra -		 * reference so the buffer is still valid on the other -		 * side. We need to move the buffer onto the io_list -		 * at this point so the caller can still access it. +		 * If we have a wait list, each buffer (and associated delwri +		 * queue reference) transfers to it and is submitted +		 * synchronously. Otherwise, drop the buffer from the delwri +		 * queue and submit async.  		 */  		bp->b_flags &= ~(_XBF_DELWRI_Q | XBF_WRITE_FAIL); -		bp->b_flags |= XBF_WRITE | XBF_ASYNC; +		bp->b_flags |= XBF_WRITE;  		if (wait_list) { -			xfs_buf_hold(bp); +			bp->b_flags &= ~XBF_ASYNC;  			list_move_tail(&bp->b_list, wait_list); -		} else +		} else { +			bp->b_flags |= XBF_ASYNC;  			list_del_init(&bp->b_list); - -		xfs_buf_submit(bp); +		} +		__xfs_buf_submit(bp, false);  	}  	blk_finish_plug(&plug); @@ -2084,9 +2021,11 @@ xfs_buf_delwri_submit(  		list_del_init(&bp->b_list); -		/* locking the buffer will wait for async IO completion. */ -		xfs_buf_lock(bp); -		error2 = bp->b_error; +		/* +		 * Wait on the locked buffer, check for errors and unlock and +		 * release the delwri queue reference. +		 */ +		error2 = xfs_buf_iowait(bp);  		xfs_buf_relse(bp);  		if (!error)  			error = error2; @@ -2132,23 +2071,18 @@ xfs_buf_delwri_pushbuf(  	/*  	 * Delwri submission clears the DELWRI_Q buffer flag and returns with -	 * the buffer on the wait list with an associated reference. Rather than +	 * the buffer on the wait list with the original reference. Rather than  	 * bounce the buffer from a local wait list back to the original list  	 * after I/O completion, reuse the original list as the wait list.  	 */  	xfs_buf_delwri_submit_buffers(&submit_list, buffer_list);  	/* -	 * The buffer is now under I/O and wait listed as during typical delwri -	 * submission. Lock the buffer to wait for I/O completion. Rather than -	 * remove the buffer from the wait list and release the reference, we -	 * want to return with the buffer queued to the original list. The -	 * buffer already sits on the original list with a wait list reference, -	 * however. If we let the queue inherit that wait list reference, all we -	 * need to do is reset the DELWRI_Q flag. +	 * The buffer is now locked, under I/O and wait listed on the original +	 * delwri queue. Wait for I/O completion, restore the DELWRI_Q flag and +	 * return with the buffer unlocked and on the original queue.  	 */ -	xfs_buf_lock(bp); -	error = bp->b_error; +	error = xfs_buf_iowait(bp);  	bp->b_flags |= _XBF_DELWRI_Q;  	xfs_buf_unlock(bp); |