diff --git a/sbin/geom/class/mirror/geom_mirror.c b/sbin/geom/class/mirror/geom_mirror.c
index 9c9facf7ee1e..40c9a4846349 100644
--- a/sbin/geom/class/mirror/geom_mirror.c
+++ b/sbin/geom/class/mirror/geom_mirror.c
@@ -180,6 +180,7 @@ mirror_label(struct gctl_req *req)
md.md_all = *nargs - 1;
md.md_mflags = 0;
md.md_dflags = 0;
+ md.md_genid = 0;
md.md_syncid = 1;
md.md_sync_offset = 0;
valp = gctl_get_paraml(req, "slice", sizeof(*valp));
diff --git a/sys/geom/mirror/g_mirror.c b/sys/geom/mirror/g_mirror.c
index fadf447db936..6e9f4f241678 100644
--- a/sys/geom/mirror/g_mirror.c
+++ b/sys/geom/mirror/g_mirror.c
@@ -218,7 +218,6 @@ g_mirror_event_get(struct g_mirror_softc *sc)
return (ep);
}
-
static void
g_mirror_event_remove(struct g_mirror_softc *sc, struct g_mirror_event *ep)
{
@@ -445,6 +444,7 @@ g_mirror_init_disk(struct g_mirror_softc *sc, struct g_provider *pp,
disk->d_sync.ds_offset = md->md_sync_offset;
disk->d_sync.ds_offset_done = md->md_sync_offset;
disk->d_sync.ds_resync = -1;
+ disk->d_genid = md->md_genid;
disk->d_sync.ds_syncid = md->md_syncid;
if (errorp != NULL)
*errorp = 0;
@@ -544,7 +544,7 @@ g_mirror_orphan(struct g_consumer *cp)
disk = cp->private;
if (disk == NULL)
return;
- disk->d_softc->sc_bump_syncid = G_MIRROR_BUMP_ON_FIRST_WRITE;
+ disk->d_softc->sc_bump_id |= G_MIRROR_BUMP_SYNCID_OFW;
g_mirror_event_send(disk, G_MIRROR_DISK_STATE_DISCONNECTED,
G_MIRROR_EVENT_DONTWAIT);
}
@@ -559,7 +559,7 @@ g_mirror_spoiled(struct g_consumer *cp)
disk = cp->private;
if (disk == NULL)
return;
- disk->d_softc->sc_bump_syncid = G_MIRROR_BUMP_IMMEDIATELY;
+ disk->d_softc->sc_bump_id |= G_MIRROR_BUMP_SYNCID_IMM;
g_mirror_event_send(disk, G_MIRROR_DISK_STATE_DISCONNECTED,
G_MIRROR_EVENT_DONTWAIT);
}
@@ -635,7 +635,7 @@ g_mirror_write_metadata(struct g_mirror_disk *disk,
g_topology_lock();
free(sector, M_MIRROR);
if (error != 0) {
- disk->d_softc->sc_bump_syncid = G_MIRROR_BUMP_IMMEDIATELY;
+ disk->d_softc->sc_bump_id |= G_MIRROR_BUMP_GENID_IMM;
g_mirror_event_send(disk, G_MIRROR_DISK_STATE_DISCONNECTED,
G_MIRROR_EVENT_DONTWAIT);
}
@@ -672,6 +672,7 @@ g_mirror_fill_metadata(struct g_mirror_softc *sc, struct g_mirror_disk *disk,
md->md_all = sc->sc_ndisks;
md->md_slice = sc->sc_slice;
md->md_balance = sc->sc_balance;
+ md->md_genid = sc->sc_genid;
md->md_mediasize = sc->sc_mediasize;
md->md_sectorsize = sc->sc_sectorsize;
md->md_mflags = (sc->sc_flags & G_MIRROR_DEVICE_FLAG_MASK);
@@ -740,6 +741,27 @@ g_mirror_bump_syncid(struct g_mirror_softc *sc)
}
}
+static void
+g_mirror_bump_genid(struct g_mirror_softc *sc)
+{
+ struct g_mirror_disk *disk;
+
+ g_topology_assert();
+ KASSERT(g_mirror_ndisks(sc, G_MIRROR_DISK_STATE_ACTIVE) > 0,
+ ("%s called with no active disks (device=%s).", __func__,
+ sc->sc_name));
+
+ sc->sc_genid++;
+ G_MIRROR_DEBUG(1, "Device %s: genid bumped to %u.", sc->sc_name,
+ sc->sc_genid);
+ LIST_FOREACH(disk, &sc->sc_disks, d_next) {
+ if (disk->d_state == G_MIRROR_DISK_STATE_ACTIVE ||
+ disk->d_state == G_MIRROR_DISK_STATE_SYNCHRONIZING) {
+ g_mirror_update_metadata(disk);
+ }
+ }
+}
+
static void
g_mirror_idle(struct g_mirror_softc *sc)
{
@@ -880,7 +902,7 @@ g_mirror_regular_request(struct bio *bp)
G_MIRROR_LOGREQ(0, bp, "Request failed (error=%d).",
bp->bio_error);
if (disk != NULL) {
- sc->sc_bump_syncid = G_MIRROR_BUMP_IMMEDIATELY;
+ sc->sc_bump_id |= G_MIRROR_BUMP_GENID_IMM;
g_mirror_event_send(disk,
G_MIRROR_DISK_STATE_DISCONNECTED,
G_MIRROR_EVENT_DONTWAIT);
@@ -1062,7 +1084,7 @@ g_mirror_sync_request(struct bio *bp)
"Synchronization request failed (error=%d).",
bp->bio_error);
g_destroy_bio(bp);
- sc->sc_bump_syncid = G_MIRROR_BUMP_IMMEDIATELY;
+ sc->sc_bump_id |= G_MIRROR_BUMP_GENID_IMM;
g_mirror_event_send(disk,
G_MIRROR_DISK_STATE_DISCONNECTED,
G_MIRROR_EVENT_DONTWAIT);
@@ -1381,8 +1403,8 @@ g_mirror_register_request(struct bio *bp)
/*
* Bump syncid on first write.
*/
- if (sc->sc_bump_syncid == G_MIRROR_BUMP_ON_FIRST_WRITE) {
- sc->sc_bump_syncid = 0;
+ if ((sc->sc_bump_id & G_MIRROR_BUMP_SYNCID_OFW) != 0) {
+ sc->sc_bump_id &= ~G_MIRROR_BUMP_SYNCID;
g_topology_lock();
g_mirror_bump_syncid(sc);
g_topology_unlock();
@@ -1422,23 +1444,18 @@ static int
g_mirror_try_destroy(struct g_mirror_softc *sc)
{
+ g_topology_lock();
+ if (!g_mirror_can_destroy(sc)) {
+ g_topology_unlock();
+ return (0);
+ }
if ((sc->sc_flags & G_MIRROR_DEVICE_FLAG_WAIT) != 0) {
- g_topology_lock();
- if (!g_mirror_can_destroy(sc)) {
- g_topology_unlock();
- return (0);
- }
g_topology_unlock();
G_MIRROR_DEBUG(4, "%s: Waking up %p.", __func__,
&sc->sc_worker);
wakeup(&sc->sc_worker);
sc->sc_worker = NULL;
} else {
- g_topology_lock();
- if (!g_mirror_can_destroy(sc)) {
- g_topology_unlock();
- return (0);
- }
g_mirror_destroy_device(sc);
g_topology_unlock();
free(sc, M_MIRROR);
@@ -1861,8 +1878,8 @@ g_mirror_update_device(struct g_mirror_softc *sc, boolean_t force)
switch (sc->sc_state) {
case G_MIRROR_DEVICE_STATE_STARTING:
{
- struct g_mirror_disk *pdisk;
- u_int dirty, ndisks, syncid;
+ struct g_mirror_disk *pdisk, *tdisk;
+ u_int dirty, ndisks, genid, syncid;
KASSERT(sc->sc_provider == NULL,
("Non-NULL provider in STARTING state (%s).", sc->sc_name));
@@ -1911,7 +1928,28 @@ g_mirror_update_device(struct g_mirror_softc *sc, boolean_t force)
}
/*
- * Find disk with the biggest syncid.
+ * Find the biggest genid.
+ */
+ genid = 0;
+ LIST_FOREACH(disk, &sc->sc_disks, d_next) {
+ if (disk->d_genid > genid)
+ genid = disk->d_genid;
+ }
+ sc->sc_genid = genid;
+ /*
+ * Remove all disks without the biggest genid.
+ */
+ LIST_FOREACH_SAFE(disk, &sc->sc_disks, d_next, tdisk) {
+ if (disk->d_genid < genid) {
+ G_MIRROR_DEBUG(0,
+ "Component %s (device %s) broken, skipping.",
+ g_mirror_get_diskname(disk), sc->sc_name);
+ g_mirror_destroy_disk(disk);
+ }
+ }
+
+ /*
+ * Find the biggest syncid.
*/
syncid = 0;
LIST_FOREACH(disk, &sc->sc_disks, d_next) {
@@ -1999,7 +2037,7 @@ g_mirror_update_device(struct g_mirror_softc *sc, boolean_t force)
sc->sc_syncid = syncid;
if (force) {
/* Remember to bump syncid on first write. */
- sc->sc_bump_syncid = G_MIRROR_BUMP_ON_FIRST_WRITE;
+ sc->sc_bump_id |= G_MIRROR_BUMP_SYNCID_OFW;
}
state = G_MIRROR_DEVICE_STATE_RUNNING;
G_MIRROR_DEBUG(1, "Device %s state changed from %s to %s.",
@@ -2010,10 +2048,8 @@ g_mirror_update_device(struct g_mirror_softc *sc, boolean_t force)
state = g_mirror_determine_state(disk);
g_mirror_event_send(disk, state,
G_MIRROR_EVENT_DONTWAIT);
- if (state == G_MIRROR_DISK_STATE_STALE) {
- sc->sc_bump_syncid =
- G_MIRROR_BUMP_ON_FIRST_WRITE;
- }
+ if (state == G_MIRROR_DISK_STATE_STALE)
+ sc->sc_bump_id |= G_MIRROR_BUMP_SYNCID_OFW;
}
wakeup(&g_mirror_class);
break;
@@ -2042,10 +2078,14 @@ g_mirror_update_device(struct g_mirror_softc *sc, boolean_t force)
/*
* Bump syncid here, if we need to do it immediately.
*/
- if (sc->sc_bump_syncid == G_MIRROR_BUMP_IMMEDIATELY) {
- sc->sc_bump_syncid = 0;
+ if ((sc->sc_bump_id & G_MIRROR_BUMP_SYNCID_IMM) != 0) {
+ sc->sc_bump_id &= ~G_MIRROR_BUMP_SYNCID;
g_mirror_bump_syncid(sc);
}
+ if ((sc->sc_bump_id & G_MIRROR_BUMP_GENID_IMM) != 0) {
+ sc->sc_bump_id &= ~G_MIRROR_BUMP_GENID;
+ g_mirror_bump_genid(sc);
+ }
break;
default:
KASSERT(1 == 0, ("Wrong device state (%s, %s).",
@@ -2233,8 +2273,8 @@ again:
* Reset bumping syncid if disk disappeared in STARTING
* state.
*/
- if (sc->sc_bump_syncid == G_MIRROR_BUMP_ON_FIRST_WRITE)
- sc->sc_bump_syncid = 0;
+ if ((sc->sc_bump_id & G_MIRROR_BUMP_SYNCID_OFW) != 0)
+ sc->sc_bump_id &= ~G_MIRROR_BUMP_SYNCID;
#ifdef INVARIANTS
} else {
KASSERT(1 == 0, ("Wrong device state (%s, %s, %s, %s).",
@@ -2296,6 +2336,8 @@ g_mirror_read_metadata(struct g_consumer *cp, struct g_mirror_metadata *md)
g_topology_lock();
g_access(cp, -1, 0, 0);
if (error != 0) {
+ G_MIRROR_DEBUG(1, "Cannot read metadata from %s (error=%d).",
+ cp->provider->name, error);
if (buf != NULL)
g_free(buf);
return (error);
@@ -2306,6 +2348,12 @@ g_mirror_read_metadata(struct g_consumer *cp, struct g_mirror_metadata *md)
g_free(buf);
if (strcmp(md->md_magic, G_MIRROR_MAGIC) != 0)
return (EINVAL);
+ if (md->md_version > G_MIRROR_VERSION) {
+ G_MIRROR_DEBUG(0,
+ "Kernel module is too old to handle metadata from %s.",
+ cp->provider->name);
+ return (EINVAL);
+ }
if (error != 0) {
G_MIRROR_DEBUG(1, "MD5 metadata hash mismatch for provider %s.",
cp->provider->name);
@@ -2395,12 +2443,25 @@ g_mirror_add_disk(struct g_mirror_softc *sc, struct g_provider *pp,
error = g_mirror_check_metadata(sc, pp, md);
if (error != 0)
return (error);
+ if (sc->sc_state == G_MIRROR_DEVICE_STATE_RUNNING &&
+ md->md_genid < sc->sc_genid) {
+ G_MIRROR_DEBUG(0, "Component %s (device %s) broken, skipping.",
+ pp->name, sc->sc_name);
+ return (EINVAL);
+ }
disk = g_mirror_init_disk(sc, pp, md, &error);
if (disk == NULL)
return (error);
error = g_mirror_event_send(disk, G_MIRROR_DISK_STATE_NEW,
G_MIRROR_EVENT_WAIT);
- return (error);
+ if (error != 0)
+ return (error);
+ if (md->md_version < G_MIRROR_VERSION) {
+ G_MIRROR_DEBUG(0, "Upgrading metadata on %s (v%d->v%d).",
+ pp->name, md->md_version, G_MIRROR_VERSION);
+ g_mirror_update_metadata(disk);
+ }
+ return (0);
}
static int
@@ -2481,7 +2542,7 @@ g_mirror_create(struct g_class *mp, const struct g_mirror_metadata *md)
sc->sc_sectorsize = md->md_sectorsize;
sc->sc_ndisks = md->md_all;
sc->sc_flags = md->md_mflags;
- sc->sc_bump_syncid = 0;
+ sc->sc_bump_id = 0;
sc->sc_idle = 0;
bioq_init(&sc->sc_queue);
mtx_init(&sc->sc_queue_mtx, "gmirror:queue", NULL, MTX_DEF);
@@ -2599,11 +2660,6 @@ g_mirror_taste(struct g_class *mp, struct g_provider *pp, int flags __unused)
return (NULL);
gp = NULL;
- if (md.md_version > G_MIRROR_VERSION) {
- printf("geom_mirror.ko module is too old to handle %s.\n",
- pp->name);
- return (NULL);
- }
if (md.md_provider[0] != '\0' && strcmp(md.md_provider, pp->name) != 0)
return (NULL);
if ((md.md_dflags & G_MIRROR_DISK_FLAG_INACTIVE) != 0) {
@@ -2699,6 +2755,8 @@ g_mirror_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp,
}
sbuf_printf(sb, "%s%u\n", indent,
disk->d_sync.ds_syncid);
+ sbuf_printf(sb, "%s%u\n", indent,
+ disk->d_genid);
sbuf_printf(sb, "%s", indent);
if (disk->d_flags == 0)
sbuf_printf(sb, "NONE");
@@ -2730,6 +2788,7 @@ g_mirror_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp,
} else {
sbuf_printf(sb, "%s%u\n", indent, (u_int)sc->sc_id);
sbuf_printf(sb, "%s%u\n", indent, sc->sc_syncid);
+ sbuf_printf(sb, "%s%u\n", indent, sc->sc_genid);
sbuf_printf(sb, "%s", indent);
if (sc->sc_flags == 0)
sbuf_printf(sb, "NONE");
diff --git a/sys/geom/mirror/g_mirror.h b/sys/geom/mirror/g_mirror.h
index a9cb27cf9845..b80308d57848 100644
--- a/sys/geom/mirror/g_mirror.h
+++ b/sys/geom/mirror/g_mirror.h
@@ -35,7 +35,13 @@
#define G_MIRROR_CLASS_NAME "MIRROR"
#define G_MIRROR_MAGIC "GEOM::MIRROR"
-#define G_MIRROR_VERSION 1
+/*
+ * Version history:
+ * 0 - Initial version number.
+ * 1 - Added 'prefer' balance algorithm.
+ * 2 - Added md_genid field to metadata.
+ */
+#define G_MIRROR_VERSION 2
#define G_MIRROR_BALANCE_NONE 0
#define G_MIRROR_BALANCE_ROUND_ROBIN 1
@@ -125,6 +131,7 @@ struct g_mirror_disk {
struct bintime d_delay; /* Disk delay. */
struct bintime d_last_used; /* When disk was last used. */
uint64_t d_flags; /* Additional flags. */
+ u_int d_genid; /* Disk's generation ID. */
struct g_mirror_disk_sync d_sync;/* Sync information. */
LIST_ENTRY(g_mirror_disk) d_next;
};
@@ -148,8 +155,15 @@ struct g_mirror_event {
#define G_MIRROR_DEVICE_STATE_STARTING 0
#define G_MIRROR_DEVICE_STATE_RUNNING 1
-#define G_MIRROR_BUMP_ON_FIRST_WRITE 1
-#define G_MIRROR_BUMP_IMMEDIATELY 2
+/* Bump syncid on first write. */
+#define G_MIRROR_BUMP_SYNCID_OFW 0x1
+/* Bump syncid immediately. */
+#define G_MIRROR_BUMP_SYNCID_IMM 0x2
+#define G_MIRROR_BUMP_SYNCID (G_MIRROR_BUMP_SYNCID_OFW | \
+ G_MIRROR_BUMP_SYNCID_IMM)
+/* Bump genid immediately. */
+#define G_MIRROR_BUMP_GENID_IMM 0x4
+#define G_MIRROR_BUMP_GENID (G_MIRROR_BUMP_GENID_IMM)
struct g_mirror_softc {
u_int sc_state; /* Device state. */
uint32_t sc_slice; /* Slice size. */
@@ -171,8 +185,9 @@ struct g_mirror_softc {
u_int sc_ndisks; /* Number of disks. */
struct g_mirror_disk *sc_hint;
+ u_int sc_genid; /* Generation ID. */
u_int sc_syncid; /* Synchronization ID. */
- int sc_bump_syncid;
+ int sc_bump_id;
struct g_mirror_device_sync sc_sync;
int sc_idle; /* DIRTY flags removed. */
@@ -201,6 +216,7 @@ struct g_mirror_metadata {
uint32_t md_mid; /* Mirror unique ID. */
uint32_t md_did; /* Disk unique ID. */
uint8_t md_all; /* Number of disks in mirror. */
+ uint32_t md_genid; /* Generation ID. */
uint32_t md_syncid; /* Synchronization ID. */
uint8_t md_priority; /* Disk priority. */
uint32_t md_slice; /* Slice size. */
@@ -225,28 +241,27 @@ mirror_metadata_encode(struct g_mirror_metadata *md, u_char *data)
le32enc(data + 36, md->md_mid);
le32enc(data + 40, md->md_did);
*(data + 44) = md->md_all;
- le32enc(data + 45, md->md_syncid);
- *(data + 49) = md->md_priority;
- le32enc(data + 50, md->md_slice);
- *(data + 54) = md->md_balance;
- le64enc(data + 55, md->md_mediasize);
- le32enc(data + 63, md->md_sectorsize);
- le64enc(data + 67, md->md_sync_offset);
- le64enc(data + 75, md->md_mflags);
- le64enc(data + 83, md->md_dflags);
- bcopy(md->md_provider, data + 91, 16);
+ le32enc(data + 45, md->md_genid);
+ le32enc(data + 49, md->md_syncid);
+ *(data + 53) = md->md_priority;
+ le32enc(data + 54, md->md_slice);
+ *(data + 58) = md->md_balance;
+ le64enc(data + 59, md->md_mediasize);
+ le32enc(data + 67, md->md_sectorsize);
+ le64enc(data + 71, md->md_sync_offset);
+ le64enc(data + 79, md->md_mflags);
+ le64enc(data + 87, md->md_dflags);
+ bcopy(md->md_provider, data + 95, 16);
MD5Init(&ctx);
- MD5Update(&ctx, data, 107);
+ MD5Update(&ctx, data, 111);
MD5Final(md->md_hash, &ctx);
- bcopy(md->md_hash, data + 107, 16);
+ bcopy(md->md_hash, data + 111, 16);
}
static __inline int
-mirror_metadata_decode(const u_char *data, struct g_mirror_metadata *md)
+mirror_metadata_decode_v0v1(const u_char *data, struct g_mirror_metadata *md)
{
MD5_CTX ctx;
- bcopy(data, md->md_magic, 16);
- md->md_version = le32dec(data + 16);
bcopy(data + 20, md->md_name, 16);
md->md_mid = le32dec(data + 36);
md->md_did = le32dec(data + 40);
@@ -267,8 +282,61 @@ mirror_metadata_decode(const u_char *data, struct g_mirror_metadata *md)
MD5Final(md->md_hash, &ctx);
if (bcmp(md->md_hash, data + 107, 16) != 0)
return (EINVAL);
+
+ /* New fields. */
+ md->md_genid = 0;
+
return (0);
}
+static __inline int
+mirror_metadata_decode_v2(const u_char *data, struct g_mirror_metadata *md)
+{
+ MD5_CTX ctx;
+
+ bcopy(data + 20, md->md_name, 16);
+ md->md_mid = le32dec(data + 36);
+ md->md_did = le32dec(data + 40);
+ md->md_all = *(data + 44);
+ md->md_genid = le32dec(data + 45);
+ md->md_syncid = le32dec(data + 49);
+ md->md_priority = *(data + 53);
+ md->md_slice = le32dec(data + 54);
+ md->md_balance = *(data + 58);
+ md->md_mediasize = le64dec(data + 59);
+ md->md_sectorsize = le32dec(data + 67);
+ md->md_sync_offset = le64dec(data + 71);
+ md->md_mflags = le64dec(data + 79);
+ md->md_dflags = le64dec(data + 87);
+ bcopy(data + 95, md->md_provider, 16);
+ bcopy(data + 111, md->md_hash, 16);
+ MD5Init(&ctx);
+ MD5Update(&ctx, data, 111);
+ MD5Final(md->md_hash, &ctx);
+ if (bcmp(md->md_hash, data + 111, 16) != 0)
+ return (EINVAL);
+ return (0);
+}
+static __inline int
+mirror_metadata_decode(const u_char *data, struct g_mirror_metadata *md)
+{
+ int error;
+
+ bcopy(data, md->md_magic, 16);
+ md->md_version = le32dec(data + 16);
+ switch (md->md_version) {
+ case 0:
+ case 1:
+ error = mirror_metadata_decode_v0v1(data, md);
+ break;
+ case 2:
+ error = mirror_metadata_decode_v2(data, md);
+ break;
+ default:
+ error = EINVAL;
+ break;
+ }
+ return (error);
+}
static __inline const char *
balance_name(u_int balance)
@@ -320,6 +388,7 @@ mirror_metadata_dump(const struct g_mirror_metadata *md)
printf(" mid: %u\n", (u_int)md->md_mid);
printf(" did: %u\n", (u_int)md->md_did);
printf(" all: %u\n", (u_int)md->md_all);
+ printf(" genid: %u\n", (u_int)md->md_genid);
printf(" syncid: %u\n", (u_int)md->md_syncid);
printf(" priority: %u\n", (u_int)md->md_priority);
printf(" slice: %u\n", (u_int)md->md_slice);
diff --git a/sys/geom/mirror/g_mirror_ctl.c b/sys/geom/mirror/g_mirror_ctl.c
index 2afa1acf023a..65c52bd9f9ec 100644
--- a/sys/geom/mirror/g_mirror_ctl.c
+++ b/sys/geom/mirror/g_mirror_ctl.c
@@ -527,7 +527,7 @@ g_mirror_ctl_deactivate(struct gctl_req *req, struct g_class *mp)
disk->d_flags |= G_MIRROR_DISK_FLAG_INACTIVE;
disk->d_flags &= ~G_MIRROR_DISK_FLAG_FORCE_SYNC;
g_mirror_update_metadata(disk);
- sc->sc_bump_syncid = G_MIRROR_BUMP_ON_FIRST_WRITE;
+ sc->sc_bump_id |= G_MIRROR_BUMP_SYNCID_OFW;
g_mirror_event_send(disk, G_MIRROR_DISK_STATE_DISCONNECTED,
G_MIRROR_EVENT_WAIT);
}