diff options
author | Dave Chinner <dchinner@redhat.com> | 2010-07-20 17:52:59 +1000 |
---|---|---|
committer | Alex Elder <aelder@sgi.com> | 2010-07-26 13:16:48 -0500 |
commit | ec53d1dbb3ca960e7b552397613358ba1dbd12bd (patch) | |
tree | 9b04fd4e4f1149ce0b5927c9f9a89d26fa6a3d4a | |
parent | a4190f90b4e22bde8b01b0086e00dd95439e2edd (diff) | |
download | linux-stable-ec53d1dbb3ca960e7b552397613358ba1dbd12bd.tar.gz linux-stable-ec53d1dbb3ca960e7b552397613358ba1dbd12bd.tar.bz2 linux-stable-ec53d1dbb3ca960e7b552397613358ba1dbd12bd.zip |
xfs: don't block on buffer read errors
xfs_buf_read() fails to detect dispatch errors before attempting to
wait on sychronous IO. If there was an error, it will get stuck
forever, waiting for an I/O that was never started. Make sure the
error is detected correctly.
Further, such a failure can leave locked pages in the page cache
which will cause a later operation to hang on the page. Ensure that
we correctly process pages in the buffers when we get a dispatch
error.
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Dave Chinner <david@fromorbit.com>
-rw-r--r-- | fs/xfs/linux-2.6/xfs_buf.c | 19 |
1 files changed, 15 insertions, 4 deletions
diff --git a/fs/xfs/linux-2.6/xfs_buf.c b/fs/xfs/linux-2.6/xfs_buf.c index efce8abb375c..f4d4e708a8d6 100644 --- a/fs/xfs/linux-2.6/xfs_buf.c +++ b/fs/xfs/linux-2.6/xfs_buf.c @@ -578,9 +578,9 @@ _xfs_buf_read( XBF_READ_AHEAD | _XBF_RUN_QUEUES); status = xfs_buf_iorequest(bp); - if (!status && !(flags & XBF_ASYNC)) - status = xfs_buf_iowait(bp); - return status; + if (status || XFS_BUF_ISERROR(bp) || (flags & XBF_ASYNC)) + return status; + return xfs_buf_iowait(bp); } xfs_buf_t * @@ -1280,8 +1280,19 @@ submit_io: if (size) goto next_chunk; } else { - bio_put(bio); + /* + * if we get here, no pages were added to the bio. However, + * we can't just error out here - if the pages are locked then + * we have to unlock them otherwise we can hang on a later + * access to the page. + */ xfs_buf_ioerror(bp, EIO); + if (bp->b_flags & _XBF_PAGE_LOCKED) { + int i; + for (i = 0; i < bp->b_page_count; i++) + unlock_page(bp->b_pages[i]); + } + bio_put(bio); } } |