Index: sbin/gvinum/gvinum.c =================================================================== RCS file: /home/ncvs/src/sbin/gvinum/gvinum.c,v retrieving revision 1.8 diff -u -r1.8 gvinum.c --- sbin/gvinum/gvinum.c 23 Mar 2006 19:58:43 -0000 1.8 +++ sbin/gvinum/gvinum.c 24 Nov 2006 19:42:31 -0000 @@ -54,7 +54,9 @@ #include "gvinum.h" +void gvinum_attach(int, char **); void gvinum_create(int, char **); +void gvinum_detach(int, char **); void gvinum_help(void); void gvinum_list(int, char **); void gvinum_move(int, char **); @@ -111,6 +113,42 @@ exit(0); } +/* Attach a plex to a volume or a plex to a subdisk */ +void +gvinum_attach(int argc, char **argv) +{ + struct gctl_req *req; + const char *errstr; + int rename, offset; + + rename = 0; + offset = -1; + if (argc < 3) { + warnx("usage:\tattach [rename]" + "[]\n" + "\tattach [rename]"); + } + if (argc > 3) { + if (!strcmp(argv[3], "rename")) { + rename = 1; + if (argc == 5) + offset = strtol(argv[4], NULL, 0); + } else + offset = strtol(argv[3], NULL, 0); + } + req = gctl_get_handle(); + gctl_ro_param(req, "class", -1, "VINUM"); + gctl_ro_param(req, "verb", -1 , "attach"); + gctl_ro_param(req, "child", -1, argv[1]); + gctl_ro_param(req, "parent", -1, argv[2]); + gctl_ro_param(req, "offset", sizeof(int), &offset); + gctl_ro_param(req, "rename", sizeof(int), &rename); + errstr = gctl_issue(req); + if (errstr != NULL) + warnx("attach failed: %s", errstr); + gctl_free(req); +} + void gvinum_create(int argc, char **argv) { @@ -323,6 +361,46 @@ gvinum_list(0, NULL); } +/* Detach plex from volume or subdisk from plex */ +void +gvinum_detach(int argc, char **argv) +{ + const char *errstr; + struct gctl_req *req; + int flags, i; + + optreset = 1; + optind = 1; + while ((i = getopt(argc, argv, "f")) != -1) { + switch(i) { + case 'f': + flags |= GV_FLAG_F; + break; + case '?': + default: + warn("invalid flag: %c", i); + return; + } + } + argc -= optind; + argv += optind; + if (argc != 1) { + warnx("usage: detach [-f] | "); + return; + } + + req = gctl_get_handle(); + gctl_ro_param(req, "class", -1, "VINUM"); + gctl_ro_param(req, "verb", -1, "detach"); + gctl_ro_param(req, "object", -1, argv[0]); + gctl_ro_param(req, "flags", sizeof(int), &flags); + + errstr = gctl_issue(req); + if (errstr != NULL) + warnx("detach failed: %s", errstr); + gctl_free(req); +} + void gvinum_help(void) { @@ -331,6 +409,12 @@ " Check the parity blocks of a RAID-5 plex.\n" "create description-file\n" " Create as per description-file or open editor.\n" + "attach plex volume [rename]\n" + "attach subdisk plex [offset] [rename]\n" + " Attach a plex to a volume, or a subdisk to a plex.\n" + "detach [-f] [plex | subdisk]\n" + " Detach a plex or a subdisk from the volume or plex to\n" + " which it is attached.\n" "l | list [-r] [-v] [-V] [volume | plex | subdisk]\n" " List information about specified objects.\n" "ld [-r] [-v] [-V] [volume]\n" @@ -363,6 +447,9 @@ " poses only.\n" "start [-S size] volume | plex | subdisk\n" " Allow the system to access the objects.\n" + "stop [-f] volume | plex | subdisk\n" + " Terminate access to these objects, or stop gvinum if no" + " parameters are specified\n" ); return; @@ -839,20 +926,64 @@ void gvinum_stop(int argc, char **argv) { + struct gctl_req *req; + int flags, i; + const char *errstr; + char buf[30]; int fileid; - fileid = kldfind(GVINUMMOD); - if (fileid == -1) { - warn("cannot find " GVINUMMOD); - return; - } - if (kldunload(fileid) != 0) { - warn("cannot unload " GVINUMMOD); - return; - } + flags = 0; + i = 0; + req = NULL; - warnx(GVINUMMOD " unloaded"); - exit(0); + optreset = 1; + optind = 1; + + /* Do not unload the kernel module if we have arguments. */ + if (argc > 1) { + while ((i = getopt(argc, argv, "f")) != -1) { + switch(i) { + case 'f': + flags |= GV_FLAG_F; + break; + case '?': + default: + warn("invalid flag: %c", i); + return; + } + } + + argc -= optind; + argv += optind; + + req = gctl_get_handle(); + gctl_ro_param(req, "class", -1, "VINUM"); + gctl_ro_param(req, "verb", -1, "stop"); + gctl_ro_param(req, "argc", sizeof(int), &argc); + gctl_ro_param(req, "flags", sizeof(int), &flags); + for (i = 0; i < argc; i++) { + snprintf(buf, sizeof(buf), "argv%d", i); + gctl_ro_param(req, buf, -1, argv[i]); + } + errstr = gctl_issue(req); + if (errstr != NULL) + warnx("can't stop object(s): %s", errstr); + gctl_free(req); + + } else { + fileid = kldfind(GVINUMMOD); + if (fileid == -1) { + warn("cannot find " GVINUMMOD); + return; + } + if (kldunload(fileid) != 0) { + warn("cannot unload " GVINUMMOD); + return; + } + + warnx(GVINUMMOD " unloaded"); + exit(0); + } } void @@ -865,6 +996,10 @@ gvinum_create(argc, argv); else if (!strcmp(argv[0], "exit") || !strcmp(argv[0], "quit")) exit(0); + else if (!strcmp(argv[0], "attach")) + gvinum_attach(argc, argv); + else if (!strcmp(argv[0], "detach")) + gvinum_detach(argc, argv); else if (!strcmp(argv[0], "help")) gvinum_help(); else if (!strcmp(argv[0], "list") || !strcmp(argv[0], "l")) Index: sys/geom/vinum/geom_vinum.c =================================================================== RCS file: /home/ncvs/src/sys/geom/vinum/geom_vinum.c,v retrieving revision 1.21 diff -u -r1.21 geom_vinum.c --- sys/geom/vinum/geom_vinum.c 30 Mar 2006 14:01:25 -0000 1.21 +++ sys/geom/vinum/geom_vinum.c 24 Nov 2006 19:42:40 -0000 @@ -393,7 +393,10 @@ gp = LIST_FIRST(&mp->geom); sc = gp->softc; - if (!strcmp(verb, "list")) { + if (!strcmp(verb, "attach")) { + gv_attach(sc, req); + + } else if (!strcmp(verb, "list")) { gv_list(gp, req); /* Save our configuration back to disk. */ @@ -414,6 +417,9 @@ } else if (!strcmp(verb, "create")) { gv_create(gp, req); + } else if (!strcmp(verb, "detach")) { + gv_detach(sc, req); + } else if (!strcmp(verb, "move")) { gv_move(gp, req); @@ -432,6 +438,9 @@ } else if (!strcmp(verb, "start")) { gv_start_obj(gp, req); + } else if (!strcmp(verb, "stop")) { + gv_stop_obj(gp, req); + } else if (!strcmp(verb, "setstate")) { gv_setstate(gp, req); Index: sys/geom/vinum/geom_vinum.h =================================================================== RCS file: /home/ncvs/src/sys/geom/vinum/geom_vinum.h,v retrieving revision 1.12 diff -u -r1.12 geom_vinum.h --- sys/geom/vinum/geom_vinum.h 30 Mar 2006 14:01:25 -0000 1.12 +++ sys/geom/vinum/geom_vinum.h 24 Nov 2006 19:42:40 -0000 @@ -62,16 +62,22 @@ struct gv_sd *s, int flags); /* geom_vinum_state.c */ +int gv_plexdown(struct gv_volume *); int gv_sdstatemap(struct gv_plex *); void gv_setstate(struct g_geom *, struct gctl_req *); int gv_set_drive_state(struct gv_drive *, int, int); int gv_set_sd_state(struct gv_sd *, int, int); +int gv_set_plex_state(struct gv_plex *, int, int); +int gv_set_vol_state(struct gv_volume *, int, int); +void gv_stop_obj(struct g_geom *, struct gctl_req *); void gv_update_sd_state(struct gv_sd *); void gv_update_plex_state(struct gv_plex *); void gv_update_vol_state(struct gv_volume *); /* geom_vinum_subr.c */ void gv_adjust_freespace(struct gv_sd *, off_t); +void gv_attach(struct gv_softc *, struct gctl_req *); +void gv_detach(struct gv_softc *, struct gctl_req *); void gv_free_sd(struct gv_sd *); struct g_geom *find_vinum_geom(void); struct gv_drive *gv_find_drive(struct gv_softc *, char *); Index: sys/geom/vinum/geom_vinum_init.c =================================================================== RCS file: /home/ncvs/src/sys/geom/vinum/geom_vinum_init.c,v retrieving revision 1.11 diff -u -r1.11 geom_vinum_init.c --- sys/geom/vinum/geom_vinum_init.c 28 Aug 2005 18:16:31 -0000 1.11 +++ sys/geom/vinum/geom_vinum_init.c 24 Nov 2006 19:42:40 -0000 @@ -271,6 +271,7 @@ error = gv_init_plex(p); } + gv_set_plex_state(p, GV_PLEX_UP, GV_SETSTATE_CONFIG); return (error); } @@ -311,6 +312,7 @@ } else error = gv_sync(v); + gv_set_vol_state(v, GV_VOL_UP, GV_SETSTATE_CONFIG); return (error); } Index: sys/geom/vinum/geom_vinum_state.c =================================================================== RCS file: /home/ncvs/src/sys/geom/vinum/geom_vinum_state.c,v retrieving revision 1.8 diff -u -r1.8 geom_vinum_state.c --- sys/geom/vinum/geom_vinum_state.c 30 Mar 2006 14:01:25 -0000 1.8 +++ sys/geom/vinum/geom_vinum_state.c 24 Nov 2006 19:42:41 -0000 @@ -43,6 +43,8 @@ struct gv_softc *sc; struct gv_sd *s; struct gv_drive *d; + struct gv_plex *p; + struct gv_volume *v; char *obj, *state; int err, f, *flags, newstate, type; @@ -72,8 +74,27 @@ type = gv_object_type(sc, obj); switch (type) { case GV_TYPE_VOL: + newstate = gv_volstatei(state); + if (newstate < 0) { + gctl_error(req, "invalid volume state '%s'", state); + break; + } + v = gv_find_vol(sc, obj); + err = gv_set_vol_state(v, newstate, f); + if (err) + gctl_error(req, "cannot set volume state"); + break; + case GV_TYPE_PLEX: - gctl_error(req, "volume or plex state cannot be set currently"); + newstate = gv_plexstatei(state); + if (newstate < 0) { + gctl_error(req, "invalid plex state '%s'", state); + break; + } + p = gv_find_plex(sc, obj); + err = gv_set_plex_state(p, newstate, f); + if (err) + gctl_error(req, "cannot set plex state"); break; case GV_TYPE_SD: @@ -288,6 +309,81 @@ gv_update_plex_state(s->plex_sc); } +int +gv_set_plex_state(struct gv_plex *p, int newstate, int flags) +{ + struct gv_volume *v; + struct gv_sd *s; + int oldstate, plexdown; + + KASSERT(p != NULL, ("gv_set_plex_state: NULL p")); + + oldstate = p->state; + v = p->vol_sc; + plexdown = 0; + + if (newstate == oldstate) + return (0); + + switch (newstate) { + case GV_PLEX_UP: + /* Let update_plex handle if the plex can come up */ + gv_update_plex_state(p); /* XXX: Should return error if fail */ + break; + + case GV_PLEX_DOWN: + if (v != NULL) { + /* If the only one, or only one up, force is needed. */ + plexdown = gv_plexdown(v); + if (((v->plexcount == 1) || + ((v->plexcount - plexdown) == 1)) && + ((flags & GV_SETSTATE_FORCE) == 0)) + return (-1); + } + p->state = newstate; + /* Force down all subdisks. */ + LIST_FOREACH(s, &p->subdisks, sd) { + if ((s->state == GV_SD_UP || + s->state == GV_SD_REVIVING) && + s->plex_sc == p) { + gv_set_sd_state(s, GV_SD_DOWN, flags); + } + } + break; + + /* + * Added this for potential use internally as in vinum. We + * really do trust ourselves here. + */ + case GV_PLEX_DEGRADED: + p->state = newstate; + LIST_FOREACH(s, &p->subdisks, sd) { + if ((s->state == GV_SD_UP || + s->state == GV_SD_REVIVING) && + s->plex_sc == p) { + gv_set_sd_state(s, GV_SD_DOWN, flags); + } + } + break; + + case GV_PLEX_INITIALIZING: + /* XXX: As with vinum, consider what safeguards we need here */ + if ((flags & GV_SETSTATE_FORCE) == 0) + return (-1); + p->state = newstate; + break; + } + + /* Update our volume, if we have one. */ + if (v != NULL) + gv_update_vol_state(v); + + /* Save config */ + if (flags & GV_SETSTATE_CONFIG) + gv_save_config_all(v->vinumconf); + return (0); +} + /* Update the state of a plex based on its environment. */ void gv_update_plex_state(struct gv_plex *p) @@ -332,6 +428,40 @@ gv_update_vol_state(p->vol_sc); } +int +gv_set_vol_state(struct gv_volume *v, int newstate, int flags) +{ + int oldstate; + + KASSERT(v != NULL, ("gv_set_vol_state: NULL v")); + + oldstate = v->state; + + if (newstate == oldstate) + return (0); + + switch (newstate) { + case GV_VOL_UP: + /* Let update handle if the volume can come up */ + gv_update_vol_state(v); /* XXX: Should return error for this. */ + break; + case GV_VOL_DOWN: + /* + * Set state to GV_VOL_DOWN only if noone is using the volume, + * or if the state should be forced. + */ + if ((gv_is_open(v->geom) != 0) && + ((flags & GV_SETSTATE_FORCE) == 0)) + return (-1); + v->state = newstate; + break; + } + /* Save config */ + if (flags & GV_SETSTATE_CONFIG) + gv_save_config_all(v->vinumconf); + return (0); +} + /* Update the volume state based on its plexes. */ void gv_update_vol_state(struct gv_volume *v) @@ -364,6 +494,32 @@ v->state = GV_VOL_DOWN; } +/* Walk over plexes in a volume and count how many are down. */ +int +gv_plexdown(struct gv_volume *v) +{ + int plexdown; + struct gv_plex *p; + + KASSERT(v != NULL, ("gv_plexdown: NULL v")); + + plexdown = 0; + + LIST_FOREACH(p, &v->plexes, plex) { + switch (p->state) { + case GV_PLEX_DOWN: + plexdown++; + break; + case GV_PLEX_DEGRADED: + case GV_PLEX_UP: + plexdown--; + break; + } + } + + return (plexdown); +} + /* Return a state map for the subdisks of a plex. */ int gv_sdstatemap(struct gv_plex *p) @@ -400,3 +556,85 @@ } return (statemap); } + +void +gv_stop_obj(struct g_geom *gp, struct gctl_req *req) +{ + struct gv_softc *sc; + struct gv_volume *v; + struct gv_plex *p; + struct gv_drive *d; + struct gv_sd *s; + char buf[30], *object; + int *argc, i, type, err, *flags, object_flags; + + object_flags = GV_SETSTATE_CONFIG; /* Save configuration. */ + sc = gp->softc; + argc = gctl_get_paraml(req, "argc", sizeof(*argc)); + if (argc == NULL || *argc == 0) { + gctl_error(req, "No arguments given"); + return; + } + + /* Check if we have the -f flag. */ + flags = gctl_get_paraml(req, "flags", sizeof(*flags)); + if (*flags & GV_FLAG_F) + object_flags |= GV_SETSTATE_FORCE; + + for (i = 0; i < *argc; i++) { + snprintf(buf, sizeof(buf), "argv%d", i); + object = gctl_get_param(req, buf, NULL); + if (object == NULL) + continue; + type = gv_object_type(sc, object); + + switch (type) { + case GV_TYPE_VOL: + v = gv_find_vol(sc, object); + err = gv_set_vol_state(v, GV_VOL_DOWN, + object_flags); + if (err) { + gctl_error(req, "cannot stop volume " + "'%s'", object); + return; + } + break; + + case GV_TYPE_PLEX: + p = gv_find_plex(sc, object); + err = gv_set_plex_state(p, GV_PLEX_DOWN, + object_flags); + if (err) { + gctl_error(req, "cannot stop plex " + "'%s'", object); + return; + } + break; + + case GV_TYPE_SD: + s = gv_find_sd(sc, object); + err = gv_set_sd_state(s, GV_SD_DOWN, + object_flags); + if (err) { + gctl_error(req, "cannot stop sd " + "'%s'", object); + return; + } + break; + case GV_TYPE_DRIVE: + d = gv_find_drive(sc, object); + err = gv_set_drive_state(d, GV_DRIVE_DOWN, + object_flags); + if (err) { + gctl_error(req, "cannot stop drive " + "'%s'", object); + return; + } + break; + + default: + gctl_error(req, "unknown object '%s'", object); + break; + } + } +} Index: sys/geom/vinum/geom_vinum_subr.c =================================================================== RCS file: /home/ncvs/src/sys/geom/vinum/geom_vinum_subr.c,v retrieving revision 1.15 diff -u -r1.15 geom_vinum_subr.c --- sys/geom/vinum/geom_vinum_subr.c 30 Mar 2006 14:01:25 -0000 1.15 +++ sys/geom/vinum/geom_vinum_subr.c 24 Nov 2006 19:42:41 -0000 @@ -54,6 +54,14 @@ #include #include +static void gv_attach_plex(struct gv_softc *, struct gctl_req *, char *, + char *, int); +static void gv_attach_sd(struct gv_softc *, struct gctl_req *, char *, + char *, int, int); +static void gv_detach_plex(struct gv_softc *, struct gctl_req *, char *, + int); +static void gv_detach_sd(struct gv_softc *, struct gctl_req *, char *, int); + /* Find the VINUM class and it's associated geom. */ struct g_geom * find_vinum_geom(void) @@ -899,3 +907,233 @@ mtx_destroy(&v->bqueue_mtx); } } + +/* General 'attach' routine */ +void +gv_attach(struct gv_softc *sc, struct gctl_req *req) +{ + char *child, *parent; + int *offset, *rename, type_child, type_parent; + + child = gctl_get_param(req, "child", NULL); + if (child == NULL) { + gctl_error(req, "no child given"); + return; + } + parent = gctl_get_param(req, "parent", NULL); + if (parent == NULL) { + gctl_error(req, "no parent given"); + return; + } + + offset = gctl_get_paraml(req, "offset", sizeof(*offset)); + rename = gctl_get_paraml(req, "rename", sizeof(*rename)); + type_child = gv_object_type(sc, child); + type_parent = gv_object_type(sc, parent); + + switch (type_child) { + case GV_TYPE_PLEX: + if (type_parent != GV_TYPE_VOL) { + gctl_error(req, "no such volume to attach to"); + return; + } + gv_attach_plex(sc, req, child, parent, *rename); + break; + case GV_TYPE_SD: + if (type_parent != GV_TYPE_PLEX) { + gctl_error(req, "no such plex to attach to"); + return; + } + gv_attach_sd(sc, req, child, parent, *offset, *rename); + break; + default: + gctl_error(req, "invalid child type"); + break; + } +} + +static void +gv_attach_plex(struct gv_softc *sc, struct gctl_req *req, char *plex, + char *volume, int rename) +{ + struct gv_volume *v; + struct gv_plex *p; + struct gv_sd *s, *s2; + char newplexname[GV_MAXPLEXNAME]; + + g_topology_assert(); + p = gv_find_plex(sc, plex); + v = gv_find_vol(sc, volume); + + /* If plex is attached, force is needed. */ + if (p->vol_sc != NULL) { + gctl_error(req, "Plex %s already attached", p->name); + return; + } + + /* Stale all subdisks of this plex */ + LIST_FOREACH(s2, &p->subdisks, sd) { + if (s2->state != GV_SD_DOWN) + gv_set_sd_state(s2, GV_SD_STALE, GV_SETSTATE_FORCE); + } + /* Attach to volume */ + p->vol_sc = v; + strlcpy(p->volume, v->name, GV_MAXVOLNAME); + v->plexcount++; + if (rename) { + snprintf(newplexname, GV_MAXPLEXNAME, "%s.p%d", v->name, + v->plexcount - 1); + strlcpy(p->name, newplexname, GV_MAXPLEXNAME); + /* XXX: + * Rename subdisks? Vinum does not do so, so we don't do it + * yet. + */ + LIST_FOREACH(s, &p->subdisks, in_plex) + strlcpy(s->plex, newplexname, GV_MAXPLEXNAME); + } + LIST_INSERT_HEAD(&v->plexes, p, in_volume); + + /* Get plex up again */ + gv_set_plex_state(p, GV_PLEX_UP, 0); + gv_save_config_all(sc); +} + +static void +gv_attach_sd(struct gv_softc *sc, struct gctl_req *req, char *subdisk, + char *plex, int offset, int rename) +{ + struct gv_plex *p; + struct gv_sd *s; + char newsdname[GV_MAXSDNAME]; + int error; + + g_topology_assert(); + p = gv_find_plex(sc, plex); + s = gv_find_sd(sc, subdisk); + + /* If subdisk is attached, force is needed. */ + if (s->plex_sc != NULL) { + gctl_error(req, "Subdisk %s already attached", s->name); + return; + } + + /* Attach the subdisk to the plex at given offset */ + gv_set_sd_state(s, GV_SD_STALE, GV_SETSTATE_FORCE); + s->plex_offset = offset; + strlcpy(s->plex, p->name, GV_MAXPLEXNAME); + error = gv_sd_to_plex(p, s, 1); + if (error) { + gctl_error(req, "Can't attach to plex"); + return; + } + if (rename) { + snprintf(newsdname, GV_MAXSDNAME, "%s.s%d", s->plex, + p->sdcount - 1); + strlcpy(s->name, newsdname, GV_MAXSDNAME); + } + gv_save_config_all(sc); +} + + +/* General 'detach' routine */ +void +gv_detach(struct gv_softc *sc, struct gctl_req *req) +{ + char *object; + int *flags, type; + + object = gctl_get_param(req, "object", NULL); + if (object == NULL) { + gctl_error(req, "no argument given"); + return; + } + + flags = gctl_get_paraml(req, "flags", sizeof(*flags)); + type = gv_object_type(sc, object); + switch (type) { + case GV_TYPE_PLEX: + gv_detach_plex(sc, req, object, *flags); + break; + case GV_TYPE_SD: + gv_detach_sd(sc, req, object, *flags); + break; + default: + gctl_error(req, "invalid object type"); + break; + } +} + +/* Detach a plex from a volume */ +static void +gv_detach_plex(struct gv_softc *sc, struct gctl_req *req, char *object, + int flags) +{ + struct g_geom *gp; + struct gv_plex *p; + struct gv_volume *v; + + g_topology_assert(); + p = gv_find_plex(sc, object); + gp = p->geom; + v = p->vol_sc; + + if (p->vol_sc == NULL) { + gctl_error(req, "Plex already detached"); + return; + } + /* + * Don't proceed if this plex if not forcing, and volume is up and this + * is the last plex. + */ + if ((flags & GV_FLAG_F) == 0 && ((v->state == GV_VOL_UP) && + (v->plexcount == 1))) { + gctl_error(req, "Unable to detach plex"); + return; + } + v->plexcount--; + LIST_REMOVE(p, in_volume); + p->vol_sc = NULL; + memset(p->volume, 0, GV_MAXVOLNAME); + gv_save_config_all(sc); +} + +/* Detach a subdisk from a plex */ +static void +gv_detach_sd(struct gv_softc *sc, struct gctl_req *req, char *object, int flags) +{ + struct g_consumer *cp; + struct gv_sd *s; + struct gv_plex *p; + + g_topology_assert(); + s = gv_find_sd(sc, object); + cp = s->consumer; + p = s->plex_sc; + + if (s->plex_sc == NULL) { + gctl_error(req, "Subdisk already detached"); + return; + } + + /* + * Don't proceed if we're not forcing, and the plex is up, or degraded + * with this subdisk up. + */ + if ((flags & GV_FLAG_F) == 0 && ((p->state == GV_PLEX_UP) || + ((p->state == GV_PLEX_DEGRADED) && + s->state == GV_SD_UP))) { + gctl_error(req, "Unable to detach subdisk"); + return; + } + + p->sdcount--; + LIST_REMOVE(s, in_plex); + s->plex_sc = NULL; + memset(s->plex, 0, GV_MAXPLEXNAME); + /* If this plex is striped, we need to take down the plex */ + if (p->org == GV_PLEX_STRIPED) { + gv_set_plex_state(p, GV_PLEX_DOWN, GV_SETSTATE_FORCE | + GV_SETSTATE_CONFIG); + } + gv_save_config_all(sc); +}