svn commit: r328334 - in stable/11: sys/geom/mirror tests/sys/geom/class/mirror
Mark Johnston
markj at FreeBSD.org
Wed Jan 24 15:16:18 UTC 2018
Author: markj
Date: Wed Jan 24 15:16:17 2018
New Revision: 328334
URL: https://svnweb.freebsd.org/changeset/base/328334
Log:
MFC r327779, r327780:
Fix handling of read errors during synchronization.
Added:
stable/11/tests/sys/geom/class/mirror/sync_error.sh
- copied unchanged from r327780, head/tests/sys/geom/class/mirror/sync_error.sh
Modified:
stable/11/sys/geom/mirror/g_mirror.c
stable/11/tests/sys/geom/class/mirror/Makefile
Directory Properties:
stable/11/ (props changed)
Modified: stable/11/sys/geom/mirror/g_mirror.c
==============================================================================
--- stable/11/sys/geom/mirror/g_mirror.c Wed Jan 24 15:15:18 2018 (r328333)
+++ stable/11/sys/geom/mirror/g_mirror.c Wed Jan 24 15:16:17 2018 (r328334)
@@ -108,6 +108,8 @@ static int g_mirror_update_disk(struct g_mirror_disk *
static void g_mirror_update_device(struct g_mirror_softc *sc, bool force);
static void g_mirror_dumpconf(struct sbuf *sb, const char *indent,
struct g_geom *gp, struct g_consumer *cp, struct g_provider *pp);
+static void g_mirror_sync_reinit(const struct g_mirror_disk *disk,
+ struct bio *bp, off_t offset);
static void g_mirror_sync_stop(struct g_mirror_disk *disk, int type);
static void g_mirror_register_request(struct g_mirror_softc *sc,
struct bio *bp);
@@ -1296,10 +1298,11 @@ g_mirror_sync_request_free(struct g_mirror_disk *disk,
/*
* Handle synchronization requests.
- * Every synchronization request is two-steps process: first, READ request is
- * send to active provider and then WRITE request (with read data) to the provider
- * being synchronized. When WRITE is finished, new synchronization request is
- * send.
+ * Every synchronization request is a two-step process: first, a read request is
+ * sent to the mirror provider via the sync consumer. If that request completes
+ * successfully, it is converted to a write and sent to the disk being
+ * synchronized. If the write also completes successfully, the synchronization
+ * offset is advanced and a new read request is submitted.
*/
static void
g_mirror_sync_request(struct g_mirror_softc *sc, struct bio *bp)
@@ -1324,13 +1327,16 @@ g_mirror_sync_request(struct g_mirror_softc *sc, struc
return;
}
+ sync = &disk->d_sync;
+
/*
* Synchronization request.
*/
switch (bp->bio_cmd) {
- case BIO_READ:
- {
+ case BIO_READ: {
+ struct g_mirror_disk *d;
struct g_consumer *cp;
+ int readable;
KFAIL_POINT_ERROR(DEBUG_FP, g_mirror_sync_request_read,
bp->bio_error);
@@ -1339,7 +1345,33 @@ g_mirror_sync_request(struct g_mirror_softc *sc, struc
G_MIRROR_LOGREQ(0, bp,
"Synchronization request failed (error=%d).",
bp->bio_error);
- g_mirror_sync_request_free(disk, bp);
+
+ /*
+ * If there's at least one other disk from which we can
+ * read the block, retry the request.
+ */
+ readable = 0;
+ LIST_FOREACH(d, &sc->sc_disks, d_next)
+ if (d->d_state == G_MIRROR_DISK_STATE_ACTIVE &&
+ !(d->d_flags & G_MIRROR_DISK_FLAG_BROKEN))
+ readable++;
+
+ /*
+ * The read error will trigger a syncid bump, so there's
+ * no need to do that here.
+ *
+ * If we can retry the read from another disk, do so.
+ * Otherwise, all we can do is kick out the new disk.
+ */
+ if (readable == 0) {
+ g_mirror_sync_request_free(disk, bp);
+ g_mirror_event_send(disk,
+ G_MIRROR_DISK_STATE_DISCONNECTED,
+ G_MIRROR_EVENT_DONTWAIT);
+ } else {
+ g_mirror_sync_reinit(disk, bp, bp->bio_offset);
+ goto retry_read;
+ }
return;
}
G_MIRROR_LOGREQ(3, bp,
@@ -1353,12 +1385,10 @@ g_mirror_sync_request(struct g_mirror_softc *sc, struc
cp->index++;
g_io_request(bp, cp);
return;
- }
- case BIO_WRITE:
- {
+ }
+ case BIO_WRITE: {
off_t offset;
- void *data;
- int i, idx;
+ int i;
KFAIL_POINT_ERROR(DEBUG_FP, g_mirror_sync_request_write,
bp->bio_error);
@@ -1375,7 +1405,6 @@ g_mirror_sync_request(struct g_mirror_softc *sc, struc
return;
}
G_MIRROR_LOGREQ(3, bp, "Synchronization request finished.");
- sync = &disk->d_sync;
if (sync->ds_offset >= sc->sc_mediasize ||
sync->ds_consumer == NULL ||
(sc->sc_flags & G_MIRROR_DEVICE_FLAG_DESTROY) != 0) {
@@ -1395,20 +1424,13 @@ g_mirror_sync_request(struct g_mirror_softc *sc, struc
}
/* Send next synchronization request. */
- data = bp->bio_data;
- idx = (int)(uintptr_t)bp->bio_caller1;
- g_reset_bio(bp);
- bp->bio_cmd = BIO_READ;
- bp->bio_offset = sync->ds_offset;
- bp->bio_length = MIN(MAXPHYS, sc->sc_mediasize - bp->bio_offset);
+ g_mirror_sync_reinit(disk, bp, sync->ds_offset);
sync->ds_offset += bp->bio_length;
- bp->bio_done = g_mirror_sync_done;
- bp->bio_data = data;
- bp->bio_from = sync->ds_consumer;
- bp->bio_to = sc->sc_provider;
- bp->bio_caller1 = (void *)(uintptr_t)idx;
+
+retry_read:
G_MIRROR_LOGREQ(3, bp, "Sending synchronization request.");
sync->ds_consumer->index++;
+
/*
* Delay the request if it is colliding with a regular request.
*/
@@ -1434,11 +1456,9 @@ g_mirror_sync_request(struct g_mirror_softc *sc, struc
sync->ds_update_ts = time_uptime;
}
return;
- }
+ }
default:
- KASSERT(1 == 0, ("Invalid command here: %u (device=%s)",
- bp->bio_cmd, sc->sc_name));
- break;
+ panic("Invalid I/O request %p", bp);
}
}
@@ -2029,15 +2049,39 @@ g_mirror_update_idle(struct g_mirror_softc *sc, struct
}
static void
+g_mirror_sync_reinit(const struct g_mirror_disk *disk, struct bio *bp,
+ off_t offset)
+{
+ void *data;
+ int idx;
+
+ data = bp->bio_data;
+ idx = (int)(uintptr_t)bp->bio_caller1;
+ g_reset_bio(bp);
+
+ bp->bio_cmd = BIO_READ;
+ bp->bio_data = data;
+ bp->bio_done = g_mirror_sync_done;
+ bp->bio_from = disk->d_sync.ds_consumer;
+ bp->bio_to = disk->d_softc->sc_provider;
+ bp->bio_caller1 = (void *)(uintptr_t)idx;
+ bp->bio_offset = offset;
+ bp->bio_length = MIN(MAXPHYS,
+ disk->d_softc->sc_mediasize - bp->bio_offset);
+}
+
+static void
g_mirror_sync_start(struct g_mirror_disk *disk)
{
struct g_mirror_softc *sc;
+ struct g_mirror_disk_sync *sync;
struct g_consumer *cp;
struct bio *bp;
int error, i;
g_topology_assert_not();
sc = disk->d_softc;
+ sync = &disk->d_sync;
sx_assert(&sc->sc_lock, SX_LOCKED);
KASSERT(disk->d_state == G_MIRROR_DISK_STATE_SYNCHRONIZING,
@@ -2063,54 +2107,48 @@ g_mirror_sync_start(struct g_mirror_disk *disk)
g_mirror_get_diskname(disk));
if ((sc->sc_flags & G_MIRROR_DEVICE_FLAG_NOFAILSYNC) == 0)
disk->d_flags |= G_MIRROR_DISK_FLAG_DIRTY;
- KASSERT(disk->d_sync.ds_consumer == NULL,
+ KASSERT(sync->ds_consumer == NULL,
("Sync consumer already exists (device=%s, disk=%s).",
sc->sc_name, g_mirror_get_diskname(disk)));
- disk->d_sync.ds_consumer = cp;
- disk->d_sync.ds_consumer->private = disk;
- disk->d_sync.ds_consumer->index = 0;
+ sync->ds_consumer = cp;
+ sync->ds_consumer->private = disk;
+ sync->ds_consumer->index = 0;
/*
* Allocate memory for synchronization bios and initialize them.
*/
- disk->d_sync.ds_bios = malloc(sizeof(struct bio *) * g_mirror_syncreqs,
+ sync->ds_bios = malloc(sizeof(struct bio *) * g_mirror_syncreqs,
M_MIRROR, M_WAITOK);
for (i = 0; i < g_mirror_syncreqs; i++) {
bp = g_alloc_bio();
- disk->d_sync.ds_bios[i] = bp;
- bp->bio_parent = NULL;
- bp->bio_cmd = BIO_READ;
+ sync->ds_bios[i] = bp;
+
bp->bio_data = malloc(MAXPHYS, M_MIRROR, M_WAITOK);
- bp->bio_cflags = 0;
- bp->bio_offset = disk->d_sync.ds_offset;
- bp->bio_length = MIN(MAXPHYS, sc->sc_mediasize - bp->bio_offset);
- disk->d_sync.ds_offset += bp->bio_length;
- bp->bio_done = g_mirror_sync_done;
- bp->bio_from = disk->d_sync.ds_consumer;
- bp->bio_to = sc->sc_provider;
bp->bio_caller1 = (void *)(uintptr_t)i;
+ g_mirror_sync_reinit(disk, bp, sync->ds_offset);
+ sync->ds_offset += bp->bio_length;
}
/* Increase the number of disks in SYNCHRONIZING state. */
sc->sc_sync.ds_ndisks++;
/* Set the number of in-flight synchronization requests. */
- disk->d_sync.ds_inflight = g_mirror_syncreqs;
+ sync->ds_inflight = g_mirror_syncreqs;
/*
* Fire off first synchronization requests.
*/
for (i = 0; i < g_mirror_syncreqs; i++) {
- bp = disk->d_sync.ds_bios[i];
+ bp = sync->ds_bios[i];
G_MIRROR_LOGREQ(3, bp, "Sending synchronization request.");
- disk->d_sync.ds_consumer->index++;
+ sync->ds_consumer->index++;
/*
* Delay the request if it is colliding with a regular request.
*/
if (g_mirror_regular_collision(sc, bp))
g_mirror_sync_delay(sc, bp);
else
- g_io_request(bp, disk->d_sync.ds_consumer);
+ g_io_request(bp, sync->ds_consumer);
}
}
Modified: stable/11/tests/sys/geom/class/mirror/Makefile
==============================================================================
--- stable/11/tests/sys/geom/class/mirror/Makefile Wed Jan 24 15:15:18 2018 (r328333)
+++ stable/11/tests/sys/geom/class/mirror/Makefile Wed Jan 24 15:16:17 2018 (r328334)
@@ -18,6 +18,8 @@ TAP_TESTS_SH+= 11_test
TAP_TESTS_SH+= 12_test
TAP_TESTS_SH+= 13_test
+ATF_TESTS_SH+= sync_error
+
${PACKAGE}FILES+= conf.sh
.for t in ${TAP_TESTS_SH}
Copied: stable/11/tests/sys/geom/class/mirror/sync_error.sh (from r327780, head/tests/sys/geom/class/mirror/sync_error.sh)
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ stable/11/tests/sys/geom/class/mirror/sync_error.sh Wed Jan 24 15:16:17 2018 (r328334, copy of r327780, head/tests/sys/geom/class/mirror/sync_error.sh)
@@ -0,0 +1,110 @@
+# $FreeBSD$
+
+REG_READ_FP=debug.fail_point.g_mirror_regular_request_read
+
+atf_test_case sync_read_error_2_disks cleanup
+sync_read_error_2_disks_head()
+{
+ atf_set "descr" \
+ "Ensure that we properly handle read errors during synchronization."
+ atf_set "require.user" "root"
+}
+sync_read_error_2_disks_body()
+{
+ . $(atf_get_srcdir)/conf.sh
+
+ f1=$(mktemp ${base}.XXXXXX)
+ f2=$(mktemp ${base}.XXXXXX)
+
+ atf_check dd if=/dev/zero bs=1M count=32 of=$f1 status=none
+ atf_check truncate -s 32M $f2
+
+ md1=$(attach_md -t vnode -f ${f1})
+ md2=$(attach_md -t vnode -f ${f2})
+
+ atf_check gmirror label $name $md1
+ devwait
+
+ atf_check -s exit:0 -e empty -o not-empty sysctl ${REG_READ_FP}='1*return(5)'
+
+ # If a read error occurs while synchronizing and the mirror contains
+ # a single active disk, gmirror has no choice but to fail the
+ # synchronization and kick the new disk out of the mirror.
+ atf_check gmirror insert $name $md2
+ sleep 0.1
+ syncwait
+ atf_check [ $(gmirror status -s $name | wc -l) -eq 1 ]
+ atf_check -s exit:0 -o match:"DEGRADED $md1 \(ACTIVE\)" \
+ gmirror status -s $name
+}
+sync_read_error_2_disks_cleanup()
+{
+ . $(atf_get_srcdir)/conf.sh
+
+ atf_check -s exit:0 -e empty -o not-empty sysctl ${REG_READ_FP}='off'
+ gmirror_test_cleanup
+}
+
+atf_test_case sync_read_error_3_disks cleanup
+sync_read_error_3_disks_head()
+{
+ atf_set "descr" \
+ "Ensure that we properly handle read errors during synchronization."
+ atf_set "require.user" "root"
+}
+sync_read_error_3_disks_body()
+{
+ . $(atf_get_srcdir)/conf.sh
+
+ f1=$(mktemp ${base}.XXXXXX)
+ f2=$(mktemp ${base}.XXXXXX)
+ f3=$(mktemp ${base}.XXXXXX)
+
+ atf_check dd if=/dev/random bs=1M count=32 of=$f1 status=none
+ atf_check truncate -s 32M $f2
+ atf_check truncate -s 32M $f3
+
+ md1=$(attach_md -t vnode -f ${f1})
+ md2=$(attach_md -t vnode -f ${f2})
+ md3=$(attach_md -t vnode -f ${f3})
+
+ atf_check gmirror label $name $md1
+ devwait
+
+ atf_check gmirror insert $name $md2
+ syncwait
+
+ atf_check -s exit:0 -e empty -o not-empty sysctl ${REG_READ_FP}='1*return(5)'
+
+ # If a read error occurs while synchronizing a new disk, and we have
+ # multiple active disks, we retry the read after an error. The disk
+ # which returned the read error is kicked out of the mirror.
+ atf_check gmirror insert $name $md3
+ syncwait
+ atf_check [ $(gmirror status -s $name | wc -l) -eq 2 ]
+ atf_check -s exit:0 -o match:"DEGRADED $md3 \(ACTIVE\)" \
+ gmirror status -s $name
+
+ # Make sure that the two active disks are identical. Destroy the
+ # mirror first so that the metadata sectors are wiped.
+ if $(gmirror status -s $name | grep -q $md1); then
+ active=$md1
+ else
+ active=$md2
+ fi
+ atf_check gmirror destroy $name
+ atf_check cmp /dev/$active /dev/$md3
+}
+sync_read_error_3_disks_cleanup()
+{
+ . $(atf_get_srcdir)/conf.sh
+
+ atf_check -s exit:0 -e empty -o not-empty sysctl ${REG_READ_FP}='off'
+ gmirror_test_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case sync_read_error_2_disks
+ atf_add_test_case sync_read_error_3_disks
+}
More information about the svn-src-stable-11
mailing list