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 7 Dec 2006 19:26:37 -0000 @@ -363,6 +363,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 +842,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; + + optreset = 1; + optind = 1; - warnx(GVINUMMOD " unloaded"); - exit(0); + /* 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 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 7 Dec 2006 19:26:50 -0000 @@ -432,6 +432,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 7 Dec 2006 19:26:50 -0000 @@ -62,10 +62,14 @@ 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 *); 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 7 Dec 2006 19:26:50 -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 7 Dec 2006 19:26:50 -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,81 @@ } 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)); + + /* 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; + } + } +}