aboutsummaryrefslogtreecommitdiffstats
path: root/fs/xfs
diff options
context:
space:
mode:
authorBrian Foster <bfoster@redhat.com>2016-01-04 15:41:16 -0500
committerDave Chinner <david@fromorbit.com>2016-01-04 15:41:16 -0500
commit609adfc2ed5ba16700f125da0b656248bd9d4316 (patch)
tree6612e2f6a7d93cfeeb806abe25def385943544d3 /fs/xfs
parent7088c4136fa1cba26531fde40bdcfcf3d2ccd533 (diff)
xfs: debug mode log record crc error injection
XFS now uses CRC verification over a limited section of the log to detect torn writes prior to a crash. This is difficult to test directly due to the timing and hardware requirements to cause a short write. Add a mechanism to inject CRC errors into log records to facilitate testing torn write detection during log recovery. This mechanism is dangerous and can result in filesystem corruption. Thus, it is only available in DEBUG mode for testing/development purposes. Set a non-zero value to the following sysfs entry to enable error injection: /sys/fs/xfs/<dev>/log/log_badcrc_factor Once enabled, XFS intentionally writes an invalid CRC to a log record at some random point in the future based on the provided frequency. The filesystem immediately shuts down once the record has been written to the physical log to prevent metadata writeback (e.g., AIL insertion) once the log write completes. This helps reasonably simulate a torn write to the log as the affected record must be safe to discard. The next mount after the intentional shutdown requires log recovery and should detect and recover from the torn write. Note again that this _will_ result in data loss or worse. For testing and development purposes only! Signed-off-by: Brian Foster <bfoster@redhat.com> Reviewed-by: Dave Chinner <dchinner@redhat.com> Signed-off-by: Dave Chinner <david@fromorbit.com>
Diffstat (limited to 'fs/xfs')
-rw-r--r--fs/xfs/xfs_log.c45
-rw-r--r--fs/xfs/xfs_log_priv.h3
-rw-r--r--fs/xfs/xfs_sysfs.c36
3 files changed, 77 insertions, 7 deletions
diff --git a/fs/xfs/xfs_log.c b/fs/xfs/xfs_log.c
index f52c72a1a06f..887c44320909 100644
--- a/fs/xfs/xfs_log.c
+++ b/fs/xfs/xfs_log.c
@@ -1188,10 +1188,16 @@ xlog_iodone(xfs_buf_t *bp)
1188 int aborted = 0; 1188 int aborted = 0;
1189 1189
1190 /* 1190 /*
1191 * Race to shutdown the filesystem if we see an error. 1191 * Race to shutdown the filesystem if we see an error or the iclog is in
1192 * IOABORT state. The IOABORT state is only set in DEBUG mode to inject
1193 * CRC errors into log recovery.
1192 */ 1194 */
1193 if (XFS_TEST_ERROR(bp->b_error, l->l_mp, 1195 if (XFS_TEST_ERROR(bp->b_error, l->l_mp, XFS_ERRTAG_IODONE_IOERR,
1194 XFS_ERRTAG_IODONE_IOERR, XFS_RANDOM_IODONE_IOERR)) { 1196 XFS_RANDOM_IODONE_IOERR) ||
1197 iclog->ic_state & XLOG_STATE_IOABORT) {
1198 if (iclog->ic_state & XLOG_STATE_IOABORT)
1199 iclog->ic_state &= ~XLOG_STATE_IOABORT;
1200
1195 xfs_buf_ioerror_alert(bp, __func__); 1201 xfs_buf_ioerror_alert(bp, __func__);
1196 xfs_buf_stale(bp); 1202 xfs_buf_stale(bp);
1197 xfs_force_shutdown(l->l_mp, SHUTDOWN_LOG_IO_ERROR); 1203 xfs_force_shutdown(l->l_mp, SHUTDOWN_LOG_IO_ERROR);
@@ -1838,6 +1844,23 @@ xlog_sync(
1838 /* calculcate the checksum */ 1844 /* calculcate the checksum */
1839 iclog->ic_header.h_crc = xlog_cksum(log, &iclog->ic_header, 1845 iclog->ic_header.h_crc = xlog_cksum(log, &iclog->ic_header,
1840 iclog->ic_datap, size); 1846 iclog->ic_datap, size);
1847#ifdef DEBUG
1848 /*
1849 * Intentionally corrupt the log record CRC based on the error injection
1850 * frequency, if defined. This facilitates testing log recovery in the
1851 * event of torn writes. Hence, set the IOABORT state to abort the log
1852 * write on I/O completion and shutdown the fs. The subsequent mount
1853 * detects the bad CRC and attempts to recover.
1854 */
1855 if (log->l_badcrc_factor &&
1856 (prandom_u32() % log->l_badcrc_factor == 0)) {
1857 iclog->ic_header.h_crc &= 0xAAAAAAAA;
1858 iclog->ic_state |= XLOG_STATE_IOABORT;
1859 xfs_warn(log->l_mp,
1860 "Intentionally corrupted log record at LSN 0x%llx. Shutdown imminent.",
1861 be64_to_cpu(iclog->ic_header.h_lsn));
1862 }
1863#endif
1841 1864
1842 bp->b_io_length = BTOBB(count); 1865 bp->b_io_length = BTOBB(count);
1843 bp->b_fspriv = iclog; 1866 bp->b_fspriv = iclog;
@@ -2791,11 +2814,19 @@ xlog_state_do_callback(
2791 } 2814 }
2792 } while (!ioerrors && loopdidcallbacks); 2815 } while (!ioerrors && loopdidcallbacks);
2793 2816
2817#ifdef DEBUG
2794 /* 2818 /*
2795 * make one last gasp attempt to see if iclogs are being left in 2819 * Make one last gasp attempt to see if iclogs are being left in limbo.
2796 * limbo.. 2820 * If the above loop finds an iclog earlier than the current iclog and
2821 * in one of the syncing states, the current iclog is put into
2822 * DO_CALLBACK and the callbacks are deferred to the completion of the
2823 * earlier iclog. Walk the iclogs in order and make sure that no iclog
2824 * is in DO_CALLBACK unless an earlier iclog is in one of the syncing
2825 * states.
2826 *
2827 * Note that SYNCING|IOABORT is a valid state so we cannot just check
2828 * for ic_state == SYNCING.
2797 */ 2829 */
2798#ifdef DEBUG
2799 if (funcdidcallbacks) { 2830 if (funcdidcallbacks) {
2800 first_iclog = iclog = log->l_iclog; 2831 first_iclog = iclog = log->l_iclog;
2801 do { 2832 do {
@@ -2810,7 +2841,7 @@ xlog_state_do_callback(
2810 * IOERROR - give up hope all ye who enter here 2841 * IOERROR - give up hope all ye who enter here
2811 */ 2842 */
2812 if (iclog->ic_state == XLOG_STATE_WANT_SYNC || 2843 if (iclog->ic_state == XLOG_STATE_WANT_SYNC ||
2813 iclog->ic_state == XLOG_STATE_SYNCING || 2844 iclog->ic_state & XLOG_STATE_SYNCING ||
2814 iclog->ic_state == XLOG_STATE_DONE_SYNC || 2845 iclog->ic_state == XLOG_STATE_DONE_SYNC ||
2815 iclog->ic_state == XLOG_STATE_IOERROR ) 2846 iclog->ic_state == XLOG_STATE_IOERROR )
2816 break; 2847 break;
diff --git a/fs/xfs/xfs_log_priv.h b/fs/xfs/xfs_log_priv.h
index 8daba7491b13..ed8896310c00 100644
--- a/fs/xfs/xfs_log_priv.h
+++ b/fs/xfs/xfs_log_priv.h
@@ -62,6 +62,7 @@ static inline uint xlog_get_client_id(__be32 i)
62#define XLOG_STATE_CALLBACK 0x0020 /* Callback functions now */ 62#define XLOG_STATE_CALLBACK 0x0020 /* Callback functions now */
63#define XLOG_STATE_DIRTY 0x0040 /* Dirty IC log, not ready for ACTIVE status*/ 63#define XLOG_STATE_DIRTY 0x0040 /* Dirty IC log, not ready for ACTIVE status*/
64#define XLOG_STATE_IOERROR 0x0080 /* IO error happened in sync'ing log */ 64#define XLOG_STATE_IOERROR 0x0080 /* IO error happened in sync'ing log */
65#define XLOG_STATE_IOABORT 0x0100 /* force abort on I/O completion (debug) */
65#define XLOG_STATE_ALL 0x7FFF /* All possible valid flags */ 66#define XLOG_STATE_ALL 0x7FFF /* All possible valid flags */
66#define XLOG_STATE_NOTUSED 0x8000 /* This IC log not being used */ 67#define XLOG_STATE_NOTUSED 0x8000 /* This IC log not being used */
67 68
@@ -410,6 +411,8 @@ struct xlog {
410 /* The following field are used for debugging; need to hold icloglock */ 411 /* The following field are used for debugging; need to hold icloglock */
411#ifdef DEBUG 412#ifdef DEBUG
412 void *l_iclog_bak[XLOG_MAX_ICLOGS]; 413 void *l_iclog_bak[XLOG_MAX_ICLOGS];
414 /* log record crc error injection factor */
415 uint32_t l_badcrc_factor;
413#endif 416#endif
414 417
415}; 418};
diff --git a/fs/xfs/xfs_sysfs.c b/fs/xfs/xfs_sysfs.c
index ee70f5dec9dc..641d625eb334 100644
--- a/fs/xfs/xfs_sysfs.c
+++ b/fs/xfs/xfs_sysfs.c
@@ -255,11 +255,47 @@ write_grant_head_show(
255} 255}
256XFS_SYSFS_ATTR_RO(write_grant_head); 256XFS_SYSFS_ATTR_RO(write_grant_head);
257 257
258#ifdef DEBUG
259STATIC ssize_t
260log_badcrc_factor_store(
261 struct kobject *kobject,
262 const char *buf,
263 size_t count)
264{
265 struct xlog *log = to_xlog(kobject);
266 int ret;
267 uint32_t val;
268
269 ret = kstrtouint(buf, 0, &val);
270 if (ret)
271 return ret;
272
273 log->l_badcrc_factor = val;
274
275 return count;
276}
277
278STATIC ssize_t
279log_badcrc_factor_show(
280 struct kobject *kobject,
281 char *buf)
282{
283 struct xlog *log = to_xlog(kobject);
284
285 return snprintf(buf, PAGE_SIZE, "%d\n", log->l_badcrc_factor);
286}
287
288XFS_SYSFS_ATTR_RW(log_badcrc_factor);
289#endif /* DEBUG */
290
258static struct attribute *xfs_log_attrs[] = { 291static struct attribute *xfs_log_attrs[] = {
259 ATTR_LIST(log_head_lsn), 292 ATTR_LIST(log_head_lsn),
260 ATTR_LIST(log_tail_lsn), 293 ATTR_LIST(log_tail_lsn),
261 ATTR_LIST(reserve_grant_head), 294 ATTR_LIST(reserve_grant_head),
262 ATTR_LIST(write_grant_head), 295 ATTR_LIST(write_grant_head),
296#ifdef DEBUG
297 ATTR_LIST(log_badcrc_factor),
298#endif
263 NULL, 299 NULL,
264}; 300};
265 301