提交 df309390 编写于 作者: C Carlos Maiolino 提交者: Dave Chinner

xfs: add configurable error support to metadata buffers

With the error configuration handle for async metadata write errors
in place, we can now add initial support to the IO error processing
in xfs_buf_iodone_error().

Add an infrastructure function to look up the configuration handle,
and rearrange the error handling to prepare the way for different
error handling conigurations to be used.
Signed-off-by: NDave Chinner <dchinner@redhat.com>
Signed-off-by: NCarlos Maiolino <cmaiolino@redhat.com>
Reviewed-by: NBrian Foster <bfoster@redhat.com>
Signed-off-by: NDave Chinner <david@fromorbit.com>
上级 ffd40ef6
...@@ -183,6 +183,7 @@ typedef struct xfs_buf { ...@@ -183,6 +183,7 @@ typedef struct xfs_buf {
unsigned int b_page_count; /* size of page array */ unsigned int b_page_count; /* size of page array */
unsigned int b_offset; /* page offset in first page */ unsigned int b_offset; /* page offset in first page */
int b_error; /* error code on I/O */ int b_error; /* error code on I/O */
int b_last_error; /* previous async I/O error */
const struct xfs_buf_ops *b_ops; const struct xfs_buf_ops *b_ops;
#ifdef XFS_BUF_LOCK_TRACKING #ifdef XFS_BUF_LOCK_TRACKING
......
...@@ -1042,35 +1042,22 @@ xfs_buf_do_callbacks( ...@@ -1042,35 +1042,22 @@ xfs_buf_do_callbacks(
} }
} }
/* static bool
* This is the iodone() function for buffers which have had callbacks xfs_buf_iodone_callback_error(
* attached to them by xfs_buf_attach_iodone(). It should remove each
* log item from the buffer's list and call the callback of each in turn.
* When done, the buffer's fsprivate field is set to NULL and the buffer
* is unlocked with a call to iodone().
*/
void
xfs_buf_iodone_callbacks(
struct xfs_buf *bp) struct xfs_buf *bp)
{ {
struct xfs_log_item *lip = bp->b_fspriv; struct xfs_log_item *lip = bp->b_fspriv;
struct xfs_mount *mp = lip->li_mountp; struct xfs_mount *mp = lip->li_mountp;
static ulong lasttime; static ulong lasttime;
static xfs_buftarg_t *lasttarg; static xfs_buftarg_t *lasttarg;
struct xfs_error_cfg *cfg;
if (likely(!bp->b_error))
goto do_callbacks;
/* /*
* If we've already decided to shutdown the filesystem because of * If we've already decided to shutdown the filesystem because of
* I/O errors, there's no point in giving this a retry. * I/O errors, there's no point in giving this a retry.
*/ */
if (XFS_FORCED_SHUTDOWN(mp)) { if (XFS_FORCED_SHUTDOWN(mp))
xfs_buf_stale(bp); goto out_stale;
bp->b_flags |= XBF_DONE;
trace_xfs_buf_item_iodone(bp, _RET_IP_);
goto do_callbacks;
}
if (bp->b_target != lasttarg || if (bp->b_target != lasttarg ||
time_after(jiffies, (lasttime + 5*HZ))) { time_after(jiffies, (lasttime + 5*HZ))) {
...@@ -1079,45 +1066,80 @@ xfs_buf_iodone_callbacks( ...@@ -1079,45 +1066,80 @@ xfs_buf_iodone_callbacks(
} }
lasttarg = bp->b_target; lasttarg = bp->b_target;
/* synchronous writes will have callers process the error */
if (!(bp->b_flags & XBF_ASYNC))
goto out_stale;
trace_xfs_buf_item_iodone_async(bp, _RET_IP_);
ASSERT(bp->b_iodone != NULL);
/* /*
* If the write was asynchronous then no one will be looking for the * If the write was asynchronous then no one will be looking for the
* error. Clear the error state and write the buffer out again. * error. If this is the first failure of this type, clear the error
* * state and write the buffer out again. This means we always retry an
* XXX: This helps against transient write errors, but we need to find * async write failure at least once, but we also need to set the buffer
* a way to shut the filesystem down if the writes keep failing. * up to behave correctly now for repeated failures.
*
* In practice we'll shut the filesystem down soon as non-transient
* errors tend to affect the whole device and a failing log write
* will make us give up. But we really ought to do better here.
*/ */
if (bp->b_flags & XBF_ASYNC) { if (!(bp->b_flags & (XBF_STALE|XBF_WRITE_FAIL)) ||
ASSERT(bp->b_iodone != NULL); bp->b_last_error != bp->b_error) {
bp->b_flags |= (XBF_WRITE | XBF_ASYNC |
trace_xfs_buf_item_iodone_async(bp, _RET_IP_); XBF_DONE | XBF_WRITE_FAIL);
bp->b_last_error = bp->b_error;
xfs_buf_ioerror(bp, 0);
xfs_buf_submit(bp);
return true;
}
xfs_buf_ioerror(bp, 0); /* errno of 0 unsets the flag */ /*
* Repeated failure on an async write. Take action according to the
* error configuration we have been set up to use.
*/
cfg = xfs_error_get_cfg(mp, XFS_ERR_METADATA, bp->b_error);
if (!cfg->max_retries)
goto permanent_error;
if (!(bp->b_flags & (XBF_STALE|XBF_WRITE_FAIL))) { /* still a transient error, higher layers will retry */
bp->b_flags |= XBF_WRITE | XBF_ASYNC | xfs_buf_ioerror(bp, 0);
XBF_DONE | XBF_WRITE_FAIL; xfs_buf_relse(bp);
xfs_buf_submit(bp); return true;
} else {
xfs_buf_relse(bp);
}
return;
}
/* /*
* If the write of the buffer was synchronous, we want to make * Permanent error - we need to trigger a shutdown if we haven't already
* sure to return the error to the caller of xfs_bwrite(). * to indicate that inconsistency will result from this action.
*/ */
permanent_error:
xfs_force_shutdown(mp, SHUTDOWN_META_IO_ERROR);
out_stale:
xfs_buf_stale(bp); xfs_buf_stale(bp);
bp->b_flags |= XBF_DONE; bp->b_flags |= XBF_DONE;
trace_xfs_buf_error_relse(bp, _RET_IP_); trace_xfs_buf_error_relse(bp, _RET_IP_);
return false;
}
/*
* This is the iodone() function for buffers which have had callbacks attached
* to them by xfs_buf_attach_iodone(). We need to iterate the items on the
* callback list, mark the buffer as having no more callbacks and then push the
* buffer through IO completion processing.
*/
void
xfs_buf_iodone_callbacks(
struct xfs_buf *bp)
{
/*
* If there is an error, process it. Some errors require us
* to run callbacks after failure processing is done so we
* detect that and take appropriate action.
*/
if (bp->b_error && xfs_buf_iodone_callback_error(bp))
return;
/*
* Successful IO or permanent error. Either way, we can clear the
* retry state here in preparation for the next error that may occur.
*/
bp->b_last_error = 0;
do_callbacks:
xfs_buf_do_callbacks(bp); xfs_buf_do_callbacks(bp);
bp->b_fspriv = NULL; bp->b_fspriv = NULL;
bp->b_iodone = NULL; bp->b_iodone = NULL;
......
...@@ -387,4 +387,7 @@ extern void xfs_set_low_space_thresholds(struct xfs_mount *); ...@@ -387,4 +387,7 @@ extern void xfs_set_low_space_thresholds(struct xfs_mount *);
int xfs_zero_extent(struct xfs_inode *ip, xfs_fsblock_t start_fsb, int xfs_zero_extent(struct xfs_inode *ip, xfs_fsblock_t start_fsb,
xfs_off_t count_fsb); xfs_off_t count_fsb);
struct xfs_error_cfg * xfs_error_get_cfg(struct xfs_mount *mp,
int error_class, int error);
#endif /* __XFS_MOUNT_H__ */ #endif /* __XFS_MOUNT_H__ */
...@@ -447,3 +447,20 @@ xfs_error_sysfs_del( ...@@ -447,3 +447,20 @@ xfs_error_sysfs_del(
xfs_sysfs_del(&mp->m_error_meta_kobj); xfs_sysfs_del(&mp->m_error_meta_kobj);
xfs_sysfs_del(&mp->m_error_kobj); xfs_sysfs_del(&mp->m_error_kobj);
} }
struct xfs_error_cfg *
xfs_error_get_cfg(
struct xfs_mount *mp,
int error_class,
int error)
{
struct xfs_error_cfg *cfg;
switch (error) {
default:
cfg = &mp->m_error_cfg[error_class][XFS_ERR_DEFAULT];
break;
}
return cfg;
}
...@@ -364,7 +364,6 @@ DEFINE_BUF_EVENT(xfs_buf_delwri_split); ...@@ -364,7 +364,6 @@ DEFINE_BUF_EVENT(xfs_buf_delwri_split);
DEFINE_BUF_EVENT(xfs_buf_get_uncached); DEFINE_BUF_EVENT(xfs_buf_get_uncached);
DEFINE_BUF_EVENT(xfs_bdstrat_shut); DEFINE_BUF_EVENT(xfs_bdstrat_shut);
DEFINE_BUF_EVENT(xfs_buf_item_relse); DEFINE_BUF_EVENT(xfs_buf_item_relse);
DEFINE_BUF_EVENT(xfs_buf_item_iodone);
DEFINE_BUF_EVENT(xfs_buf_item_iodone_async); DEFINE_BUF_EVENT(xfs_buf_item_iodone_async);
DEFINE_BUF_EVENT(xfs_buf_error_relse); DEFINE_BUF_EVENT(xfs_buf_error_relse);
DEFINE_BUF_EVENT(xfs_buf_wait_buftarg); DEFINE_BUF_EVENT(xfs_buf_wait_buftarg);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册