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); }