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 3 Oct 2006 19:06:34 -0000 @@ -55,6 +55,11 @@ #include "gvinum.h" void gvinum_create(int, char **); +void gvinum_attach(int, char **); +void gvinum_concat(int, char **); +void gvinum_defaultdg(int, char **); +void gvinum_detach(int, char **); +void gvinum_dumpconfig(FILE *, int, char **); void gvinum_help(void); void gvinum_list(int, char **); void gvinum_move(int, char **); @@ -111,6 +116,181 @@ 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; + 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); +} + +/* Create a concatenated volume. */ +void +gvinum_concat(int argc, char **argv) +{ + struct gctl_req *req; + const char *errstr; + char buf[BUFSIZ]; + int drives, flags, i, size; + + flags = 0; + drives = 0; + if (argc < 2) { + warnx("usage:\tconcat [-f] [-n name] [-v] [-g group] [-s size] " + "[drives]\n"); + return; + } + req = gctl_get_handle(); + gctl_ro_param(req, "class", -1, "VINUM"); + gctl_ro_param(req, "verb", -1, "concat"); + printf("Got argc %d, pass\n", argc); + for (i = 1; i < argc; i++) { + printf("Got arg %d, pass %s\n", i, argv[i]); + if (!strcmp(argv[i], "-f")) { + flags |= GV_FLAG_F; + } else if (!strcmp(argv[i], "-g")) { + gctl_ro_param(req, "group", -1, argv[++i]); + } else if (!strcmp(argv[i], "-n")) { + gctl_ro_param(req, "name", -1, argv[++i]); + } else if (!strcmp(argv[i], "-s")) { + size = strtol(argv[++i], NULL, 0); + gctl_ro_param(req, "size", sizeof(int), &size); + } else if (!strcmp(argv[i], "-v")) { + flags |= GV_FLAG_V; + } else { + /* Assume it's a drive. */ + printf("Got arg %d, pass %s\n", i, argv[i]); + snprintf(buf, sizeof(buf), "drive%d", drives++); + gctl_ro_param(req, buf, -1, argv[i]); + + } + } + printf("Sending drives %d\n", drives); + gctl_ro_param(req, "flags", sizeof(int), &flags); + gctl_ro_param(req, "drives", sizeof(int), &drives); + errstr = gctl_issue(req); + if (errstr != NULL) + warnx("creating concat volume failed: %s", errstr); + gctl_free(req); +} + +/* Set default drivegroup in gvinum. */ +void +gvinum_defaultdg(int argc, char **argv) +{ + struct gctl_req *req; + const char *errstr; + char current[GV_MAXGROUPNAME]; + + req = gctl_get_handle(); + gctl_ro_param(req, "class", -1, "VINUM"); + gctl_ro_param(req, "verb", -1, "defaultdg"); + if (argc > 1) + gctl_ro_param(req, "name", -1, argv[1]); + gctl_rw_param(req, "default", sizeof(current), current); + errstr = gctl_issue(req); + if (errstr != NULL) { + warnx("setting defaultdg failed: %s", errstr); + gctl_free(req); + return; + } + gctl_free(req); + printf("%s\n", current); +} + +/* 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); +} + +/* Dump config from specified disk drives */ +void +gvinum_dumpconfig(FILE *of, int argc, char **argv) +{ + char buf[(GV_CFG_LEN * 4) + 1]; + const char *errstr; + struct gctl_req *req; + int i; + + req = gctl_get_handle(); + gctl_ro_param(req, "class", -1, "VINUM"); + gctl_ro_param(req, "verb", -1, "dumpconfig"); + gctl_ro_param(req, "argc", sizeof(int), &argc); + gctl_rw_param(req, "config", sizeof(buf), buf); + + for (i = 1; 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 dump configuration of drive(s): %s", errstr); + gctl_free(req); + fprintf(of, buf); +} + void gvinum_create(int argc, char **argv) { @@ -119,9 +299,10 @@ struct gv_plex *p; struct gv_sd *s; struct gv_volume *v; + struct gv_dg *dg; FILE *tmp; - int drives, errors, fd, line, plexes, plex_in_volume; - int sd_in_plex, status, subdisks, tokens, volumes; + int drives, errors, fd, line, plexes, plex_in_volume, drive_in_group; + int sd_in_plex, status, subdisks, tokens, volumes, drivegroups; const char *errstr; char buf[BUFSIZ], buf1[BUFSIZ], commandline[BUFSIZ], *ed; char original[BUFSIZ], tmpfile[20], *token[GV_MAXARGS]; @@ -168,8 +349,8 @@ gctl_ro_param(req, "class", -1, "VINUM"); gctl_ro_param(req, "verb", -1, "create"); - drives = volumes = plexes = subdisks = 0; - plex_in_volume = sd_in_plex = 0; + drives = volumes = plexes = subdisks = drivegroups = 0; + plex_in_volume = sd_in_plex = drive_in_group = 0; errors = 0; line = 1; while ((fgets(buf, BUFSIZ, tmp)) != NULL) { @@ -235,7 +416,7 @@ sd_in_plex = 0; /* Default name. */ - if (strlen(p->name) == 0) { + if (strlen(p->name) == 0 && strlen(volume) > 0) { snprintf(p->name, GV_MAXPLEXNAME, "%s.p%d", volume, plex_in_volume++); } @@ -269,7 +450,7 @@ } /* Default name. */ - if (strlen(s->name) == 0) { + if (strlen(s->name) == 0 && strlen(plex) > 0) { snprintf(s->name, GV_MAXSDNAME, "%s.s%d", plex, sd_in_plex++); } @@ -282,7 +463,7 @@ gctl_ro_param(req, buf1, sizeof(*s), s); subdisks++; - /* Subdisk definition. */ + /* Drive definition. */ } else if (!strcmp(token[0], "drive")) { d = gv_new_drive(tokens, token); if (d == NULL) { @@ -294,10 +475,28 @@ continue; } + printf("Creating drive %s in group %s\n", d->name, + d->drivegroup); snprintf(buf1, sizeof(buf1), "drive%d", drives); gctl_ro_param(req, buf1, sizeof(*d), d); drives++; + /* Drivegroup definition. */ + } else if (!strcmp(token[0], "group")) { + dg = gv_new_dg(tokens, token); + if (dg == NULL) { + warnx("line %d: invalid drivegroup definition:", + line); + warnx("line %d: '%s'", line, original); + errors++; + line++; + continue; + } + snprintf(buf1, sizeof(buf1), "drivegroup%d", + drivegroups++); + gctl_ro_param(req, buf1, sizeof(*dg), dg); + printf("Created group with groupname %s\n", dg->name); + /* Everything else is bogus. */ } else { warnx("line %d: invalid definition:", line); @@ -310,11 +509,12 @@ fclose(tmp); unlink(tmpfile); - if (!errors && (volumes || plexes || subdisks || drives)) { + if (!errors && (volumes || plexes || subdisks || drives || drivegroups)) { gctl_ro_param(req, "volumes", sizeof(int), &volumes); gctl_ro_param(req, "plexes", sizeof(int), &plexes); gctl_ro_param(req, "subdisks", sizeof(int), &subdisks); gctl_ro_param(req, "drives", sizeof(int), &drives); + gctl_ro_param(req, "drivegroups", sizeof(int), &drivegroups); errstr = gctl_issue(req); if (errstr != NULL) warnx("create failed: %s", errstr); @@ -331,6 +531,18 @@ " 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" + "concat [-f] [-n name] [-v] [-g group] [-s size] [drives]\n" + " Create a concatenated volume on default drivegroup,\n" + " specified drivegroup, or specified drives.\n" + "defaultdg drivegroup\n" + " Set the default drivegroup.\n" + "detach [-f] [plex | subdisk]\n" + " Detach a plex or subdisk from the volume or plex to\n" + " which it is attached.\n" + "dumpconfig [drive ...]\n" "l | list [-r] [-v] [-V] [volume | plex | subdisk]\n" " List information about specified objects.\n" "ld [-r] [-v] [-V] [volume]\n" @@ -357,73 +569,21 @@ "saveconfig\n" " Save vinum configuration to disk after configuration" " failures.\n" - "setstate [-f] state [volume | plex | subdisk | drive]\n" + "setstate [-f] state [volume | plex | subdisk | drive | group]\n" " Set state without influencing other objects, for" " diagnostic pur-\n" " 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; } void -gvinum_setstate(int argc, char **argv) -{ - struct gctl_req *req; - int flags, i; - const char *errstr; - - flags = 0; - - 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 != 2) { - warnx("usage: setstate [-f] "); - return; - } - - /* - * XXX: This hack is needed to avoid tripping over (now) invalid - * 'classic' vinum states and will go away later. - */ - if (strcmp(argv[0], "up") && strcmp(argv[0], "down") && - strcmp(argv[0], "stale")) { - warnx("invalid state '%s'", argv[0]); - return; - } - - req = gctl_get_handle(); - gctl_ro_param(req, "class", -1, "VINUM"); - gctl_ro_param(req, "verb", -1, "setstate"); - gctl_ro_param(req, "state", -1, argv[0]); - gctl_ro_param(req, "object", -1, argv[1]); - gctl_ro_param(req, "flags", sizeof(int), &flags); - - errstr = gctl_issue(req); - if (errstr != NULL) - warnx("%s", errstr); - gctl_free(req); -} - -void gvinum_list(int argc, char **argv) { struct gctl_req *req; @@ -686,6 +846,42 @@ } void +gvinum_resetconfig(void) +{ + struct gctl_req *req; + const char *errstr; + char reply[32]; + + if (!isatty(STDIN_FILENO)) { + warn("Please enter this command from a tty device\n"); + return; + } + printf(" WARNING! This command will completely wipe out your gvinum" + "configuration.\n" + " All data will be lost. If you really want to do this," + " enter the text\n\n" + " NO FUTURE\n" + " Enter text -> "); + fgets(reply, sizeof(reply), stdin); + if (strcmp(reply, "NO FUTURE\n")) { + printf("\n No change\n"); + return; + } + req = gctl_get_handle(); + gctl_ro_param(req, "class", -1, "VINUM"); + gctl_ro_param(req, "verb", -1, "resetconfig"); + errstr = gctl_issue(req); + if (errstr != NULL) { + warnx("can't reset config: %s", errstr); + gctl_free(req); + return; + } + gctl_free(req); + gvinum_list(0, NULL); + printf("gvinum configuration obliterated\n"); +} + +void gvinum_rm(int argc, char **argv) { struct gctl_req *req; @@ -732,53 +928,72 @@ } void -gvinum_resetconfig(void) +gvinum_saveconfig(void) { struct gctl_req *req; const char *errstr; - char reply[32]; - if (!isatty(STDIN_FILENO)) { - warn("Please enter this command from a tty device\n"); - return; - } - printf(" WARNING! This command will completely wipe out your gvinum" - "configuration.\n" - " All data will be lost. If you really want to do this," - " enter the text\n\n" - " NO FUTURE\n" - " Enter text -> "); - fgets(reply, sizeof(reply), stdin); - if (strcmp(reply, "NO FUTURE\n")) { - printf("\n No change\n"); - return; - } req = gctl_get_handle(); gctl_ro_param(req, "class", -1, "VINUM"); - gctl_ro_param(req, "verb", -1, "resetconfig"); + gctl_ro_param(req, "verb", -1, "saveconfig"); errstr = gctl_issue(req); - if (errstr != NULL) { - warnx("can't reset config: %s", errstr); - gctl_free(req); - return; - } + if (errstr != NULL) + warnx("can't save configuration: %s", errstr); gctl_free(req); - gvinum_list(0, NULL); - printf("gvinum configuration obliterated\n"); } void -gvinum_saveconfig(void) +gvinum_setstate(int argc, char **argv) { struct gctl_req *req; + int flags, i; const char *errstr; + flags = 0; + + 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 != 2) { + warnx("usage: setstate [-f] "); + return; + } + + /* + * XXX: This hack is needed to avoid tripping over (now) invalid + * 'classic' vinum states and will go away later. + */ + if (strcmp(argv[0], "up") && strcmp(argv[0], "down") && + strcmp(argv[0], "stale")) { + warnx("invalid state '%s'", argv[0]); + return; + } + req = gctl_get_handle(); gctl_ro_param(req, "class", -1, "VINUM"); - gctl_ro_param(req, "verb", -1, "saveconfig"); + gctl_ro_param(req, "verb", -1, "setstate"); + gctl_ro_param(req, "state", -1, argv[0]); + gctl_ro_param(req, "object", -1, argv[1]); + gctl_ro_param(req, "flags", sizeof(int), &flags); + errstr = gctl_issue(req); if (errstr != NULL) - warnx("can't save configuration: %s", errstr); + warnx("%s", errstr); gctl_free(req); } @@ -839,20 +1054,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 +1124,16 @@ 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], "concat")) + gvinum_concat(argc, argv); + else if (!strcmp(argv[0], "defaultdg")) + gvinum_defaultdg(argc, argv); + else if (!strcmp(argv[0], "detach")) + gvinum_detach(argc, argv); + else if (!strcmp(argv[0], "dumpconfig")) + gvinum_dumpconfig(stdout, 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 3 Oct 2006 19:06:49 -0000 @@ -139,6 +139,7 @@ LIST_INIT(&sc->subdisks); LIST_INIT(&sc->plexes); LIST_INIT(&sc->volumes); + LIST_INIT(&sc->drivegroups); } /* Handle userland requests for creating new objects. */ @@ -149,10 +150,10 @@ struct gv_drive *d, *d2; struct gv_plex *p, *p2; struct gv_sd *s, *s2; + struct gv_dg *dg, *dg2; struct gv_volume *v, *v2; - struct g_consumer *cp; struct g_provider *pp; - int error, i, *drives, *plexes, *subdisks, *volumes; + int error, i, *drives, *plexes, *subdisks, *volumes, *drivegroups; char buf[20], errstr[ERRBUFSIZ]; g_topology_assert(); @@ -164,8 +165,46 @@ plexes = gctl_get_paraml(req, "plexes", sizeof(*plexes)); subdisks = gctl_get_paraml(req, "subdisks", sizeof(*subdisks)); drives = gctl_get_paraml(req, "drives", sizeof(*drives)); + drivegroups = gctl_get_paraml(req, "drivegroups", sizeof(*drivegroups)); + + /* + * If there is no default drivegroup, create a default root drivegroup + */ + if (sc->defaultdg == NULL && *drivegroups == 0) { + dg = g_malloc(sizeof(*dg), M_WAITOK | M_ZERO); + snprintf(dg->name, GV_MAXGROUPNAME, "rootdg"); + dg->drivecount = 0; + dg->vinumconf = sc; + LIST_INIT(&dg->drives); + LIST_INSERT_HEAD(&sc->drivegroups, dg, drivegroup); + sc->defaultdg = dg; + } + + /* First, handle drivegroup definitions ... */ + for (i = 0; i < *drivegroups; i++) { + snprintf(buf, sizeof(buf), "drivegroup%d", i); + dg2 = gctl_get_paraml(req, buf, sizeof(*dg2)); + + dg = gv_find_dg(sc, dg2->name); + if (dg != NULL) { + gctl_error(req, "drivegroup '%s' is already known", + dg->name); + continue; + } + + dg = g_malloc(sizeof(*dg), M_WAITOK | M_ZERO); + bcopy(dg2, dg, sizeof(*dg)); + + dg->drivecount = 0; + dg->vinumconf = sc; + /* Set as default drivegroup if none exists */ + if (sc->defaultdg == NULL) + sc->defaultdg = dg; + LIST_INIT(&dg->drives); + LIST_INSERT_HEAD(&sc->drivegroups, dg, drivegroup); + } - /* First, handle drive definitions ... */ + /* ... then drive definitions ... */ for (i = 0; i < *drives; i++) { snprintf(buf, sizeof(buf), "drive%d", i); d2 = gctl_get_paraml(req, buf, sizeof(*d2)); @@ -190,6 +229,33 @@ g_free(d); return (-1); } + + /* Add to correct drivegroup. Right now, a default drivegroup + * must be given. */ + /* Set name according to groupname if given */ + if (strlen(d->drivegroup) > 0) { + dg = gv_find_dg(sc, d->drivegroup); + + /* If not, use the default drivegroup. */ + } else { + dg = sc->defaultdg; + } + + /* Give error if we don't have a drivegroup. */ + /* XXX: Should _never_ happen. */ + if (dg == NULL) { + gctl_error(req, "No drivegroup exits!"); + g_free(d); + return (-1); + } + + /* XXX: Set correct name on drive? */ + if (strlen(d->drivegroup) > 0) { + snprintf(d->name, GV_MAXDRIVENAME, "%s%d", dg->name, + dg->drivecount); + } + dg->drivecount++; + d->drivegroup_sc = dg; d->size = pp->mediasize - GV_DATA_START; d->avail = d->size; @@ -197,6 +263,8 @@ d->flags |= GV_DRIVE_NEWBORN; LIST_INSERT_HEAD(&sc->drives, d, drive); + LIST_INSERT_HEAD(&dg->drives, d, in_drivegroup); + } /* ... then volume definitions ... */ @@ -242,6 +310,13 @@ g_free(p); continue; } + + /* Set plexname if not set. */ + if (strlen(p->name) == 0) { + snprintf(p->name, GV_MAXPLEXNAME, "%s.p%d", + p->volume, v->plexcount); + } + if (v->plexcount) p->flags |= GV_PLEX_ADDED; p->vol_sc = v; @@ -290,6 +365,12 @@ continue; } + /* Set subdiskname if not set. */ + if (strlen(s->name) == 0) { + snprintf(s->name, GV_MAXSDNAME, "%s.s%d", + s->plex, p->sdcount); + } + /* * First we give the subdisk to the drive, to handle autosized * values ... @@ -348,35 +429,15 @@ gv_update_plex_config(p); LIST_FOREACH(v, &sc->volumes, volume) gv_update_vol_state(v); + LIST_FOREACH(dg, &sc->drivegroups, drivegroup) + gv_update_dg_state(dg); /* * Write out the configuration to each drive. If the drive doesn't * have a valid geom_slice geom yet, attach it temporarily to our VINUM * geom. */ - LIST_FOREACH(d, &sc->drives, drive) { - if (d->geom == NULL) { - /* - * XXX if the provider disapears before we get a chance - * to write the config out to the drive, should this - * be handled any differently? - */ - pp = g_provider_by_name(d->device); - if (pp == NULL) { - printf("geom_vinum: %s: drive disapeared?\n", - d->device); - continue; - } - cp = g_new_consumer(gp); - g_attach(cp, pp); - gv_save_config(cp, d, sc); - g_detach(cp); - g_destroy_consumer(cp); - } else - gv_save_config(NULL, d, sc); - d->flags &= ~GV_DRIVE_NEWBORN; - } - + gv_save_config_fresh(gp); return (0); } @@ -393,7 +454,22 @@ gp = LIST_FIRST(&mp->geom); sc = gp->softc; - if (!strcmp(verb, "list")) { + if (!strcmp(verb, "attach")) { + gv_attach(sc, req); + + } else if (!strcmp(verb, "concat")) { + gv_concat(gp, req); + + } else if (!strcmp(verb, "defaultdg")) { + gv_default_dg(sc, req); + + } else if (!strcmp(verb, "detach")) { + gv_detach(sc, req); + + } else if (!strcmp(verb, "dumpconfig")) { + gv_dump_config(sc, req); + + } else if (!strcmp(verb, "list")) { gv_list(gp, req); /* Save our configuration back to disk. */ @@ -432,9 +508,15 @@ } 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); + } else if (!strcmp(verb, "test")) { + gv_test(sc, req); + } else gctl_error(req, "Unknown verb parameter"); } 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 3 Oct 2006 19:06:49 -0000 @@ -31,12 +31,19 @@ #define ERRBUFSIZ 1024 +/* geom_vinum_attach.c */ +void gv_attach(struct gv_softc *, struct gctl_req *); +/* geom_vinum_detach.c */ +void gv_detach(struct gv_softc *, struct gctl_req *); + /* geom_vinum_drive.c */ void gv_config_new_drive(struct gv_drive *); void gv_drive_modify(struct gv_drive *); void gv_save_config_all(struct gv_softc *); +void gv_read_config(struct gv_drive *, struct sbuf *); void gv_save_config(struct g_consumer *, struct gv_drive *, struct gv_softc *); +void gv_save_config_fresh(struct g_geom *); /* geom_vinum_init.c */ void gv_parityop(struct g_geom *, struct gctl_req *); @@ -47,38 +54,56 @@ void gv_lp(struct g_geom *, struct gctl_req *, struct sbuf *); void gv_ls(struct g_geom *, struct gctl_req *, struct sbuf *); void gv_lv(struct g_geom *, struct gctl_req *, struct sbuf *); +void gv_lg(struct g_geom *, struct gctl_req *, struct sbuf *); void gv_list(struct g_geom *, struct gctl_req *); /* geom_vinum_move.c */ void gv_move(struct g_geom *, struct gctl_req *); +/* geom_vinum_op.c */ +void gv_concat(struct g_geom *, struct gctl_req *); + /* geom_vinum_rename.c */ void gv_rename(struct g_geom *, struct gctl_req *); /* geom_vinum_rm.c */ void gv_remove(struct g_geom *, struct gctl_req *); int gv_resetconfig(struct g_geom *, struct gctl_req *); -int gv_rm_sd(struct gv_softc *sc, struct gctl_req *req, - struct gv_sd *s, int flags); +int gv_rm_sd(struct gv_softc *, struct gctl_req *, + struct gv_sd *, int); +int gv_rm_dg(struct gv_softc *, struct gctl_req *, + struct gv_dg *, int); /* geom_vinum_state.c */ int gv_sdstatemap(struct gv_plex *); +int gv_plexdown(struct gv_volume *); 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_dg_state(struct gv_dg *, int, int); +int gv_set_plex_state(struct gv_plex *, int, int); +int gv_set_vol_state(struct gv_volume *, int, int); void gv_update_sd_state(struct gv_sd *); void gv_update_plex_state(struct gv_plex *); void gv_update_vol_state(struct gv_volume *); +void gv_update_dg_state(struct gv_dg *); + +/* geom_vinum_stop.c */ +void gv_stop_obj(struct g_geom *, struct gctl_req *); /* geom_vinum_subr.c */ void gv_adjust_freespace(struct gv_sd *, off_t); void gv_free_sd(struct gv_sd *); +void gv_free_dg(struct gv_dg *); struct g_geom *find_vinum_geom(void); struct gv_drive *gv_find_drive(struct gv_softc *, char *); struct gv_plex *gv_find_plex(struct gv_softc *, char *); struct gv_sd *gv_find_sd(struct gv_softc *, char *); +struct gv_dg *gv_find_dg(struct gv_softc *, char *); struct gv_volume *gv_find_vol(struct gv_softc *, char *); void gv_format_config(struct gv_softc *, struct sbuf *, int, char *); +void gv_dump_config(struct gv_softc *, struct gctl_req *); +void gv_default_dg(struct gv_softc *, struct gctl_req *); int gv_is_striped(struct gv_plex *); int gv_is_open(struct g_geom *); void gv_kill_drive_thread(struct gv_drive *); @@ -92,4 +117,7 @@ void gv_update_plex_config(struct gv_plex *); void gv_update_vol_size(struct gv_volume *, off_t); +/* geom_vinum_test.c */ +void gv_test(struct gv_softc *, struct gctl_req *); + #endif /* !_GEOM_VINUM_H_ */ Index: sys/geom/vinum/geom_vinum_attach.c =================================================================== RCS file: sys/geom/vinum/geom_vinum_attach.c diff -N sys/geom/vinum/geom_vinum_attach.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ sys/geom/vinum/geom_vinum_attach.c 3 Oct 2006 19:06:49 -0000 @@ -0,0 +1,170 @@ +/*- + * Copyright (c) 2006 Ulf Lilleengen + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include + +#include +#include +#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); + +/* 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); + } + /* Get plex up again */ + gv_set_plex_state(p, GV_PLEX_UP, 0); + /* Attach to volume */ + p->vol_sc = v; + strncpy(p->volume, v->name, GV_MAXVOLNAME); + v->plexcount++; + if (rename) { + snprintf(newplexname, GV_MAXPLEXNAME, "%s.p%d", v->name, + v->plexcount - 1); + strncpy(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) + strncpy(s->plex, newplexname, GV_MAXPLEXNAME); + } + LIST_INSERT_HEAD(&v->plexes, p, in_volume); + + + 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]; + + 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; + } + + /* Can't attach if plexorg is raid5 or striped */ + if (p->org != GV_PLEX_CONCAT) { + gctl_error(req, "Can't attach to plex"); + return; + } + + /* Attach the subdisk to the plex at given offset */ + s->plex_offset = offset; + gv_set_sd_state(s, GV_SD_STALE, GV_SETSTATE_FORCE); + s->plex_sc = p; + strncpy(s->plex, p->name, GV_MAXPLEXNAME); + p->sdcount++; + if (rename) { + snprintf(newsdname, GV_MAXSDNAME, "%s.s%d", s->plex, + p->sdcount - 1); + strncpy(s->name, newsdname, GV_MAXSDNAME); + } + LIST_INSERT_HEAD(&p->subdisks, s, sd); + gv_save_config_all(sc); +} Index: sys/geom/vinum/geom_vinum_detach.c =================================================================== RCS file: sys/geom/vinum/geom_vinum_detach.c diff -N sys/geom/vinum/geom_vinum_detach.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ sys/geom/vinum/geom_vinum_detach.c 3 Oct 2006 19:06:49 -0000 @@ -0,0 +1,142 @@ +/*- + * Copyright (c) 2006 Ulf Lilleengen + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +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); + +/* 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); +} Index: sys/geom/vinum/geom_vinum_drive.c =================================================================== RCS file: /home/ncvs/src/sys/geom/vinum/geom_vinum_drive.c,v retrieving revision 1.25 diff -u -r1.25 geom_vinum_drive.c --- sys/geom/vinum/geom_vinum_drive.c 6 Jan 2006 18:03:17 -0000 1.25 +++ sys/geom/vinum/geom_vinum_drive.c 3 Oct 2006 19:06:49 -0000 @@ -98,6 +98,45 @@ } } +/* Read the vinum configuration from disk */ +void +gv_read_config(struct gv_drive *d, struct sbuf *sb) +{ + struct gv_plex *p; + struct gv_sd *s; + struct gv_volume *v; + struct gv_softc *vinumconf; + + KASSERT(d != NULL, ("gv_read_config: null d")); + vinumconf = d->vinumconf; + sbuf_printf(sb, "Drive %s:\tDevice /dev/%s\n", d->name, d->device); + sbuf_printf(sb, "\t\t\tSize:\t %jd bytes (%jd MB)\n", (intmax_t)d->size, + (intmax_t)d->size / MEGABYTE); + LIST_FOREACH(v, &vinumconf->volumes, volume) + sbuf_printf(sb, "volume %s state %s\n", v->name, + gv_volstate(v->state)); + LIST_FOREACH(p, &vinumconf->plexes, plex) { + sbuf_printf(sb, "plex name %s state %s org %s ", p->name, + gv_plexstate(p->state), gv_plexorg(p->org)); + if (gv_is_striped(p)) + sbuf_printf(sb, "%ds ", p->stripesize / 512); + if (p->vol_sc != NULL) + sbuf_printf(sb, "vol %s", p->volume); + } + LIST_FOREACH(s, &vinumconf->subdisks, sd) { + sbuf_printf(sb, "sd name %s drive %s len %jds " + "driveoffset %jds state %s", s->name, s->drive, + s->size / 512, s->drive_offset / 512, + gv_sdstate(s->state)); + if (s->plex_sc != NULL) + sbuf_printf(sb, " plex %s plexoffset %jds", + s->plex, s->plex_offset / 512); + sbuf_printf(sb, "\n"); + } + sbuf_printf(sb, "\nDrive /dev/%s: %jd MB (%jd bytes)\n", d->device, + (intmax_t)d->size / MEGABYTE, (intmax_t)d->size); +} + /* Save the vinum configuration back to disk. */ void gv_save_config(struct g_consumer *cp, struct gv_drive *d, struct gv_softc *sc) @@ -188,6 +227,46 @@ gv_drive_modify(d); } +/* + * Write out the configuration to each drive. If the drives doesn't have a valid + * geom_slice geom yet, attach it temporarily to our VINUM geom. This should be + * used when new drives are created and the configuration should be written. + */ +void +gv_save_config_fresh(struct g_geom *gp) +{ + struct g_consumer *cp; + struct g_provider *pp; + struct gv_drive *d; + struct gv_softc *sc; + + sc = gp->softc; + + LIST_FOREACH(d, &sc->drives, drive) { + if (d->geom == NULL) { + /* + * XXX if the provider disappears before we get a chance + * to write the config out to the drives, should this be + * handled any differently? + */ + pp = g_provider_by_name(d->device); + if (pp == NULL) { + printf("GEOM_VINUM: %s: drive disappeared?\n", + d->device); + continue; + } + cp = g_new_consumer(gp); + g_attach(cp, pp); + gv_save_config(cp, d, sc); + g_detach(cp); + g_destroy_consumer(cp); + } else { + gv_save_config(NULL, d, sc); + } + d->flags &= ~GV_DRIVE_NEWBORN; + } +} + /* This resembles g_slice_access(). */ static int gv_drive_access(struct g_provider *pp, int dr, int dw, int de) 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 3 Oct 2006 19:06:49 -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_list.c =================================================================== RCS file: /home/ncvs/src/sys/geom/vinum/geom_vinum_list.c,v retrieving revision 1.3 diff -u -r1.3 geom_vinum_list.c --- sys/geom/vinum/geom_vinum_list.c 6 Jan 2005 18:27:30 -0000 1.3 +++ sys/geom/vinum/geom_vinum_list.c 3 Oct 2006 19:06:49 -0000 @@ -41,12 +41,14 @@ void gv_lpi(struct gv_plex *, struct sbuf *, int); void gv_lsi(struct gv_sd *, struct sbuf *, int); void gv_ldi(struct gv_drive *, struct sbuf *, int); +void gv_lgi(struct gv_dg *, struct sbuf *, int); void gv_list(struct g_geom *gp, struct gctl_req *req) { struct gv_softc *sc; struct gv_drive *d; + struct gv_dg *dg; struct gv_plex *p; struct gv_sd *s; struct gv_volume *v; @@ -96,6 +98,10 @@ d = gv_find_drive(sc, arg); gv_ldi(d, sb, *flags); break; + case GV_TYPE_DG: + dg = gv_find_dg(sc, arg); + gv_lgi(dg, sb, *flags); + break; default: gctl_error(req, "unknown object '%s'", arg); @@ -110,6 +116,9 @@ gv_lp(gp, req, sb); sbuf_printf(sb, "\n"); gv_ls(gp, req, sb); + sbuf_printf(sb, "\n"); + gv_lg(gp, req, sb); + sbuf_printf(sb, "\n"); } /* List drives. */ @@ -133,6 +142,26 @@ } else gv_ld(gp, req, sb); + /* List diskgroups. */ + } else if (!strcmp(cmd, "lg")) { + if (*argc) { + for (i = 0; i < *argc; i++) { + snprintf(buf, sizeof(buf), "argv%d", i); + arg = gctl_get_param(req, buf, NULL); + if (arg == NULL) + continue; + type = gv_object_type(sc, arg); + if (type != GV_TYPE_DG) { + gctl_error(req, "'%s' is not a " + "drivegroup", arg); + continue; + } else { + dg = gv_find_dg(sc, arg); + gv_lgi(dg, sb, *flags); + } + } + } + /* List volumes. */ } else if (!strcmp(cmd, "lv")) { if (*argc) { @@ -405,7 +434,7 @@ gv_roughlength(s->size, 0)); } } - + /* List one or more drives. */ void gv_ld(struct g_geom *gp, struct gctl_req *req, struct sbuf *sb) @@ -470,3 +499,34 @@ gv_lsi(s, sb, flags); } } + +/* List one or more drivegroups. */ +void +gv_lg(struct g_geom *gp, struct gctl_req *req, struct sbuf *sb) +{ + struct gv_softc *sc; + struct gv_dg *dg; + int i, *flags; + + sc = gp->softc; + i = 0; + + LIST_FOREACH(dg, &sc->drivegroups, drivegroup) + i++; + + sbuf_printf(sb, "%d drivegroup%s:\n", i, i == 1 ? "" : "s"); + + if (i) { + flags = gctl_get_paraml(req, "flags", sizeof(*flags)); + LIST_FOREACH(dg, &sc->drivegroups, drivegroup) + gv_lgi(dg, sb, *flags); + } +} + +/* List a single group. */ +void +gv_lgi(struct gv_dg *dg, struct sbuf *sb, int flags) +{ + /* XXX: Also list size */ + sbuf_printf(sb, "G %-21s State %s\n", dg->name, gv_groupstate(dg->state)); +} Index: sys/geom/vinum/geom_vinum_op.c =================================================================== RCS file: sys/geom/vinum/geom_vinum_op.c diff -N sys/geom/vinum/geom_vinum_op.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ sys/geom/vinum/geom_vinum_op.c 3 Oct 2006 19:06:49 -0000 @@ -0,0 +1,142 @@ +/*- + * Copyright (c) 2006 Ulf Lilleengen + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +void +gv_concat(struct g_geom *gp, struct gctl_req *req) +{ + struct gv_drive *d; + struct gv_sd *s; + struct gv_volume *v; + struct gv_plex *p; + struct gv_softc *sc; + char *group, name[GV_MAXVOLNAME], *drive, buf[30], *vol, + errstr[ERRBUFSIZ]; + int *drives, error, *flags, dcount; + + sc = gp->softc; + dcount = 0; + vol = gctl_get_param(req, "name", NULL); + if (vol == NULL) /* XXX: USE A COUNTER IN DRIVEGROUP INSTEAD. */ + snprintf(name, sizeof(name), "%s-v0", sc->defaultdg->name); + else + strlcpy(name, vol, GV_MAXVOLNAME); + + group = gctl_get_param(req, "group", NULL); + flags = gctl_get_paraml(req, "flags", sizeof(*flags)); + drives = gctl_get_paraml(req, "drives", sizeof(*drives)); + if (drives == NULL) { /* XXX: Remove when we have support for + drivegroups. */ + gctl_error(req, "Drives not given"); + return; + } + + + /* First we create the volume. */ + v = g_malloc(sizeof(*v), M_WAITOK | M_ZERO); + v->vinumconf = sc; + v->state = GV_VOL_UP; + strlcpy(v->name, name, GV_MAXVOLNAME); + LIST_INIT(&v->plexes); + LIST_INSERT_HEAD(&sc->volumes, v, volume); + + /* Then we create the plex. */ + p = g_malloc(sizeof(*p), M_WAITOK | M_ZERO); + snprintf(p->name, sizeof(p->name), "%s.p0", v->name); + strlcpy(p->volume, v->name, GV_MAXPLEXNAME); + p->org = GV_PLEX_CONCAT; + p->vol_sc = v; + v->plexcount++; + LIST_INSERT_HEAD(&v->plexes, p, in_volume); + p->vinumconf = sc; + p->flags |= GV_PLEX_NEWBORN; + LIST_INIT(&p->subdisks); + LIST_INSERT_HEAD(&sc->plexes, p, plex); + + /* Drives are first (only right now) priority */ + while (dcount < *drives) { + snprintf(buf, sizeof(buf), "drive%d", dcount++); + drive = gctl_get_param(req, buf, NULL); + d = gv_find_drive(sc, drive); + if (d == NULL) { + gctl_error(req, "drive '%s' not found", drive); + return; + } + s = g_malloc(sizeof(*s), M_WAITOK | M_ZERO); + s->plex_offset = -1; + s->drive_offset = -1; + s->size = -1; + snprintf(s->name, sizeof(s->name), "%s.s%d", p->name, + p->sdcount); + snprintf(s->drive, sizeof(s->drive), "%s", drive); + snprintf(s->plex, sizeof(s->plex), "%s", p->name); + + /* First give it to the drive. */ + error = gv_sd_to_drive(sc, d, s, errstr, + sizeof(errstr)); + if (error) { + gctl_error(req, errstr); + g_free(s); + continue; + } + /* + * XXX: Then give it to the plex. We should also check if values are + * correct and maybe adjust them. This could be abstracted out. + */ + error = gv_sd_to_plex(p, s, 1); + if (error) { + gctl_error(req, "GEOM_VINUM: couldn't give sd " + "'%s' to plex '%s'\n", s->name, p->name); + if (s->drive_sc) + LIST_REMOVE(s, from_drive); + gv_free_sd(s); + g_free(s); + } + s->flags |= GV_SD_NEWBORN; + s->vinumconf = sc; + LIST_INSERT_HEAD(&sc->subdisks, s, sd); + } + /*XXX: FOR NOW... */ + + /* Update states. */ + LIST_FOREACH(s, &sc->subdisks, sd) + gv_update_sd_state(s); + LIST_FOREACH(p, &sc->plexes, plex) + gv_update_plex_state(p); + LIST_FOREACH(v, &sc->volumes, volume) + gv_update_vol_state(v); + + gv_save_config_fresh(gp); +} Index: sys/geom/vinum/geom_vinum_rm.c =================================================================== RCS file: /home/ncvs/src/sys/geom/vinum/geom_vinum_rm.c,v retrieving revision 1.12 diff -u -r1.12 geom_vinum_rm.c --- sys/geom/vinum/geom_vinum_rm.c 30 Mar 2006 14:01:25 -0000 1.12 +++ sys/geom/vinum/geom_vinum_rm.c 3 Oct 2006 19:06:49 -0000 @@ -54,6 +54,7 @@ struct gv_plex *p; struct gv_sd *s; struct gv_drive *d; + struct gv_dg *dg; int *argc, *flags; char *argv, buf[20]; int i, type, err; @@ -115,6 +116,17 @@ if (err) return; break; + case GV_TYPE_DG: + dg = gv_find_dg(sc, argv); + if (dg == NULL) { + gctl_error(req, "unknown drivegroup '%s'", + argv); + return; + } + err = gv_rm_dg(sc, req, dg, *flags); + if (err) + return; + break; default: gctl_error(req, "unknown object '%s'", argv); return; @@ -133,6 +145,7 @@ struct gv_volume *v, *v2; struct gv_plex *p, *p2; struct gv_sd *s, *s2; + struct gv_dg *dg, *dg2; int flags; d = NULL; @@ -141,6 +154,8 @@ p2 = NULL; s = NULL; s2 = NULL; + dg = NULL; + dg2 = NULL; flags = GV_FLAG_R; sc = gp->softc; /* First loop through to make sure no volumes are up */ @@ -159,6 +174,9 @@ gv_rm_sd(sc, req, s, flags); LIST_FOREACH_SAFE(d, &sc->drives, drive, d2) gv_rm_drive(sc, req, d, flags); + LIST_FOREACH_SAFE(dg, &sc->drivegroups, drivegroup, dg2) + gv_rm_dg(sc, req, dg, flags); + sc->defaultdg = NULL; gv_save_config_all(sc); return (0); } @@ -303,6 +321,28 @@ return (0); } +/* Remove a drivegroup. */ +int +gv_rm_dg(struct gv_softc *sc, struct gctl_req *req, struct gv_dg *dg, int flags) +{ + + KASSERT(dg != NULL, ("gv_rm_dg: NULL dg")); + + /* XXX: For now, require an empty drivegroup. */ + if (!LIST_EMPTY(&dg->drives)) { + gctl_error(req, "drivegroup '%s' still has drives", dg->name); + return (-1); + } + LIST_REMOVE(dg, drivegroup); + /* If it's the default, remove the default. */ + if (!strncmp(dg->name, sc->defaultdg->name, GV_MAXGROUPNAME)) + sc->defaultdg = NULL; + gv_free_dg(dg); + g_free(dg); + + return (0); +} + /* Remove a drive. */ static int gv_rm_drive(struct gv_softc *sc, struct gctl_req *req, struct gv_drive *d, int flags) @@ -351,6 +391,11 @@ g_topology_lock(); g_access(cp, 0, -1, 0); + /* Decrease number of drives in group and remove from group. */ + d->drivegroup_sc->drivecount--; + LIST_REMOVE(d, in_drivegroup); + d->drivegroup_sc = NULL; + /* Remove all associated subdisks, plexes, volumes. */ if (!LIST_EMPTY(&d->subdisks)) { LIST_FOREACH_SAFE(s, &d->subdisks, from_drive, s2) { Index: sys/geom/vinum/geom_vinum_share.c =================================================================== RCS file: /home/ncvs/src/sys/geom/vinum/geom_vinum_share.c,v retrieving revision 1.4 diff -u -r1.4 geom_vinum_share.c --- sys/geom/vinum/geom_vinum_share.c 15 Nov 2004 12:30:59 -0000 1.4 +++ sys/geom/vinum/geom_vinum_share.c 3 Oct 2006 19:06:49 -0000 @@ -91,7 +91,7 @@ /* End of line. */ if ((*cptr == '\0') || (*cptr == '\n') || (*cptr == '#')) - return tokennr; + return (tokennr); delim = *cptr; token[tokennr] = cptr; /* Point to it. */ @@ -99,7 +99,7 @@ /* Run off the end? */ if (tokennr == maxtoken) - return tokennr; + return (tokennr); /* Quoted? */ if ((delim == '\'') || (delim == '"')) { @@ -112,14 +112,14 @@ /* Space after closing quote needed. */ if (!iswhite(*cptr)) - return -1; + return (-1); /* Delimit. */ *cptr++ = '\0'; /* End-of-line? */ } else if ((*cptr == '\0') || (*cptr == '\n')) - return -1; + return (-1); } /* Not quoted. */ @@ -136,7 +136,7 @@ } /* Can't get here. */ - return maxtoken; + return (maxtoken); } @@ -181,25 +181,25 @@ switch (*s) { case '\0': - return size * sign; + return (size * sign); case 'B': case 'b': case 'S': case 's': - return size * sign * 512; + return (size * sign * 512); case 'K': case 'k': - return size * sign * 1024; + return (size * sign * 1024); case 'M': case 'm': - return size * sign * 1024 * 1024; + return (size * sign * 1024 * 1024); case 'G': case 'g': - return size * sign * 1024 * 1024 * 1024; + return (size * sign * 1024 * 1024 * 1024); } } } @@ -212,11 +212,11 @@ { switch (state) { case GV_DRIVE_DOWN: - return "down"; + return ("down"); case GV_DRIVE_UP: - return "up"; + return ("up"); default: - return "??"; + return ("??"); } } @@ -229,6 +229,27 @@ return (GV_DRIVE_DOWN); } +const char * +gv_groupstate(int state) +{ + switch (state) { + case GV_GROUP_DOWN: + return ("down"); + case GV_GROUP_UP: + return ("up"); + default: + return ("??"); + } +} + +int gv_groupstatei(char *buf) +{ + if (!strcmp(buf, "up")) + return (GV_GROUP_UP); + else + return (GV_GROUP_DOWN); +} + /* Translate from a string to a subdisk state. */ int gv_sdstatei(char *buf) @@ -249,17 +270,17 @@ { switch (state) { case GV_SD_INITIALIZING: - return "initializing"; + return ("initializing"); case GV_SD_STALE: - return "stale"; + return ("stale"); case GV_SD_DOWN: - return "down"; + return ("down"); case GV_SD_REVIVING: - return "reviving"; + return ("reviving"); case GV_SD_UP: - return "up"; + return ("up"); default: - return "??"; + return ("??"); } } @@ -283,15 +304,15 @@ { switch (state) { case GV_PLEX_DOWN: - return "down"; + return ("down"); case GV_PLEX_INITIALIZING: - return "initializing"; + return ("initializing"); case GV_PLEX_DEGRADED: - return "degraded"; + return ("degraded"); case GV_PLEX_UP: - return "up"; + return ("up"); default: - return "??"; + return ("??"); } } @@ -323,11 +344,11 @@ { switch (state) { case GV_VOL_UP: - return "up"; + return ("up"); case GV_VOL_DOWN: - return "down"; + return ("down"); default: - return "??"; + return ("??"); } } @@ -337,15 +358,15 @@ { switch (org) { case GV_PLEX_DISORG: - return "??"; + return ("??"); case GV_PLEX_CONCAT: - return "concat"; + return ("concat"); case GV_PLEX_STRIPED: - return "striped"; + return ("striped"); case GV_PLEX_RAID5: - return "raid5"; + return ("raid5"); default: - return "??"; + return ("??"); } } @@ -354,16 +375,55 @@ { switch (org) { case GV_PLEX_DISORG: - return "??"; + return ("??"); case GV_PLEX_CONCAT: - return "C"; + return ("C"); case GV_PLEX_STRIPED: - return "S"; + return ("S"); case GV_PLEX_RAID5: - return "R5"; + return ("R5"); default: - return "??"; + return ("??"); + } +} + +/* Get a new drivegroup object. */ +struct gv_dg * +gv_new_dg(int max, char *token[]) +{ + struct gv_dg *dg; + int j, errors; + + if (token[1] == NULL || *token[1] == '\0') + return (NULL); + +#ifdef _KERNEL + dg = g_malloc(sizeof(struct gv_dg), M_WAITOK | M_ZERO); + +#else + dg = malloc(sizeof(struct gv_dg)); + if (dg == NULL) + return (NULL); + bzero(dg, sizeof(struct gv_dg)); +#endif + + errors = 0; + for (j = 1; j < max; j++) { + if (!strcmp(token[j], "state")) { + j++; + if (j >= max) { + errors++; + break; + } + dg->state = gv_groupstatei(token[j]); + + /* Assume name. */ + } else { + strncpy(dg->name, token[j], GV_MAXGROUPNAME); + } } + + return (dg); } /* Get a new drive object. */ @@ -410,13 +470,23 @@ ptr++; } strncpy(d->device, ptr, GV_MAXDRIVENAME); + } else if (!strcmp(token[j], "group")) { + j++; + if (j >= max) { + errors++; + break; + } + strncpy(d->drivegroup, token[j], GV_MAXGROUPNAME); } else { /* We assume this is the drive name. */ strncpy(d->name, token[j], GV_MAXDRIVENAME); } } - if (strlen(d->name) == 0 || strlen(d->device) == 0) + /* Only give error if neither groupname or drivename is given. */ + if ((strlen(d->name) == 0 && + strlen(d->drivegroup) == 0) || + strlen(d->device) == 0) errors++; if (errors) { @@ -528,7 +598,8 @@ break; } p->state = gv_plexstatei(token[j]); - } else if (!strcmp(token[j], "vol")) { + } else if (!strcmp(token[j], "vol") || !strcmp(token[j], + "volume")) { j++; if (j >= max) { errors++; @@ -557,14 +628,14 @@ int j, errors; if (token[1] == NULL || *token[1] == '\0') - return NULL; + return (NULL); #ifdef _KERNEL s = g_malloc(sizeof(struct gv_sd), M_WAITOK | M_ZERO); #else s = malloc(sizeof(struct gv_sd)); if (s == NULL) - return NULL; + return (NULL); bzero(s, sizeof(struct gv_sd)); #endif Index: sys/geom/vinum/geom_vinum_share.h =================================================================== RCS file: /home/ncvs/src/sys/geom/vinum/geom_vinum_share.h,v retrieving revision 1.2 diff -u -r1.2 geom_vinum_share.h --- sys/geom/vinum/geom_vinum_share.h 15 Nov 2004 12:30:59 -0000 1.2 +++ sys/geom/vinum/geom_vinum_share.h 3 Oct 2006 19:06:49 -0000 @@ -45,14 +45,17 @@ struct gv_plex *gv_new_plex(int, char **); struct gv_sd *gv_new_sd(int, char **); struct gv_volume *gv_new_volume(int, char **); +struct gv_dg *gv_new_dg(int, char **); int gv_drivestatei(char *); +int gv_groupstatei(char *); int gv_plexorgi(char *); int gv_plexstatei(char *); int gv_sdstatei(char *); int gv_volstatei(char *); const char *gv_drivestate(int); +const char *gv_groupstate(int); const char *gv_plexorg(int); const char *gv_plexorg_short(int); const char *gv_plexstate(int); 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 3 Oct 2006 19:06:49 -0000 @@ -43,6 +43,9 @@ struct gv_softc *sc; struct gv_sd *s; struct gv_drive *d; + struct gv_dg *dg; + struct gv_plex *p; + struct gv_volume *v; char *obj, *state; int err, f, *flags, newstate, type; @@ -72,8 +75,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: @@ -100,6 +122,18 @@ gctl_error(req, "cannot set drive state"); break; + case GV_TYPE_DG: + newstate = gv_groupstatei(state); + if (newstate < 0) { + gctl_error(req, "invalid drivegroup state '%s'", state); + break; + } + dg = gv_find_dg(sc, obj); + err = gv_set_dg_state(dg, newstate, f); + if (err) + gctl_error(req, "cannot set drivegroup state"); + break; + default: gctl_error(req, "unknown object '%s'", obj); break; @@ -108,6 +142,48 @@ return; } +/* Update drivegroup state; return 0 if the state changes, otherwise -1. */ +int +gv_set_dg_state(struct gv_dg *dg, int newstate, int flags) +{ + struct gv_drive *d; + int oldstate; + + KASSERT(dg != NULL, ("gv_set_dg_state: NULL dg")); + + oldstate = dg->state; + if (newstate == oldstate) + return (0); + + /* XXX: Need to check if we can force drives up etc. */ + switch (newstate) { + case GV_GROUP_UP: + /* For the drivegroup to be up, all drives must be up */ + LIST_FOREACH(d, &dg->drives, drive) { + if (d->state == GV_DRIVE_DOWN && + !(flags & GV_SETSTATE_FORCE)) + return (-1); + } + break; + case GV_GROUP_DOWN: + /* For the dg to be down, all drives must be down. */ + LIST_FOREACH(d, &dg->drives, drive) { + if (d->state == GV_DRIVE_UP && + !(flags & GV_SETSTATE_FORCE)) + return (-1); + } + break; + default: + return (-1); + } + + dg->state = newstate; + /* Save the config back to disk. */ + if (flags & GV_SETSTATE_CONFIG) + gv_save_config_all(dg->vinumconf); + return (0); +} + /* Update drive state; return 0 if the state changes, otherwise -1. */ int gv_set_drive_state(struct gv_drive *d, int newstate, int flags) @@ -288,6 +364,104 @@ gv_update_plex_state(s->plex_sc); } +/* Update the state of a drivegroup based on it's drives. */ +void +gv_update_dg_state(struct gv_dg *dg) +{ + struct gv_drive *d; + int oldstate; + + KASSERT(dg != NULL, ("gv_update_dg_state: NULL dg")); + + oldstate = dg->state; + + dg->state = GV_GROUP_UP; + /* Drives need to be up for us to get up. */ + LIST_FOREACH(d, &dg->drives, drive) { + if (d->state == GV_DRIVE_DOWN) + dg->state = GV_GROUP_DOWN; + } + if (dg->state != oldstate) + printf("GEOM_VINUM: drivegroup %s state change: %s -> %s\n", + dg->name, gv_groupstate(oldstate), + gv_groupstate(dg->state)); +} + +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 +506,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 +572,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) Index: sys/geom/vinum/geom_vinum_stop.c =================================================================== RCS file: sys/geom/vinum/geom_vinum_stop.c diff -N sys/geom/vinum/geom_vinum_stop.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ sys/geom/vinum/geom_vinum_stop.c 3 Oct 2006 19:06:49 -0000 @@ -0,0 +1,117 @@ +/*- + * Copyright (c) 2006 Ulf Lilleengen + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +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 3 Oct 2006 19:06:50 -0000 @@ -87,6 +87,7 @@ struct gv_volume *v, *v2; struct gv_plex *p, *p2; struct gv_sd *s, *s2; + struct gv_dg *dg, *dg2; int tokens; char *token[GV_MAXARGS]; @@ -164,11 +165,92 @@ s->vinumconf = sc; LIST_INSERT_HEAD(&sc->subdisks, s, sd); + } else if (!strcmp(token[0], "group")) { + dg = gv_new_dg(tokens, token); + + if (dg == NULL) { + printf("geom_vinum: failed group\n"); + break; + } + + if (merge) { + dg2 = gv_find_dg(sc, dg->name); + if (dg2 != NULL) { + g_free(dg); + continue; + } + } + + dg->vinumconf = sc; + LIST_INIT(&dg->drives); + LIST_INSERT_HEAD(&sc->drivegroups, dg, + drivegroup); } } } } +/* Get configuration from all disks */ +void +gv_dump_config(struct gv_softc *sc, struct gctl_req *req) +{ + struct gv_drive *d; + struct sbuf *sb; + int *argc, drives, i, type; + char *object, buf[64]; + + drives = 0; + i = 0; + argc = gctl_get_paraml(req, "argc", sizeof(*argc)); + if (argc == NULL || *argc == 0) { + gctl_error(req, "No argument given"); + return; + } + + if (*argc > 1) { + /* Only if argument is specified */ + sb = sbuf_new(NULL, NULL, GV_CFG_LEN * (*argc), SBUF_FIXEDLEN); + 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); + if (type == GV_TYPE_DRIVE) { + d = gv_find_drive(sc, object); + gv_read_config(d, sb); + } else { + LIST_FOREACH(d, &sc->drives, drive) { + snprintf(buf, sizeof(buf), "/dev/%s", + d->device); + if (!strcmp(d->device, object) || + !strcmp(buf, object)) { + gv_read_config(d, sb); + } + } + } + } + } else { + /* + * First count number of drives, then allocate enough space for + * them. + */ + LIST_FOREACH(d, &sc->drives, drive) + drives++; + if (drives == 0) { + gctl_error(req, "No drives present"); + return; + } + sb = sbuf_new(NULL, NULL, GV_CFG_LEN * drives, SBUF_FIXEDLEN); + LIST_FOREACH(d, &sc->drives, drive) { + gv_read_config(d, sb); + } + } + sbuf_finish(sb); + gctl_set_param(req, "config", sbuf_data(sb), sbuf_len(sb) + 1); + sbuf_delete(sb); +} + /* * Format the vinum configuration properly. If ondisk is non-zero then the * configuration is intended to be written to disk later. @@ -189,8 +271,10 @@ */ if (!ondisk) { LIST_FOREACH(d, &sc->drives, drive) { - sbuf_printf(sb, "%sdrive %s device /dev/%s\n", prefix, + sbuf_printf(sb, "%sdrive %s device /dev/%s ", prefix, d->name, d->device); + if (d->drivegroup_sc != NULL) + sbuf_printf(sb, "group %s\n", d->drivegroup); } } @@ -232,9 +316,45 @@ sbuf_printf(sb, "\n"); } + LIST_FOREACH(dg, &sc->drivegroups, drivegroup) { + if (|ondisk) + sbuf_printf(sb, "%s", prefix); + sbuf_printf(sb, "group name %s ", dg->name); + if (ondisk) + sbuf_printf(sb, " state %s", gv_groupstate(dg->state)); + sbuf_printf(sb, "\n"); + } return; } +/* + * Set/get a default drivegroup. + */ +void +gv_default_dg(struct gv_softc *sc, struct gctl_req *req) +{ + struct gv_dg *dg; + struct sbuf *sb; + char *drivegroup; + + drivegroup = gctl_get_param(req, "name", NULL); + + /* Set groupname if given. */ + if (drivegroup != NULL) { + dg = gv_find_dg(sc, drivegroup); + if (dg == NULL) { + gctl_error(req, "no such dg '%s'", drivegroup); + return; + } + sc->defaultdg = dg; + } + sb = sbuf_new(NULL, NULL, GV_MAXGROUPNAME, SBUF_FIXEDLEN); + sbuf_printf(sb, "%s", sc->defaultdg->name); + sbuf_finish(sb); + gctl_set_param(req, "default", sbuf_data(sb), sbuf_len(sb) + 1); + sbuf_delete(sb); +} + int gv_sd_to_plex(struct gv_plex *p, struct gv_sd *s, int check) { @@ -679,6 +799,13 @@ } void +gv_free_dg(struct gv_dg *dg) +{ + KASSERT(dg != NULL, ("gv_free_dg: NULL dg")); + +} + +void gv_adjust_freespace(struct gv_sd *s, off_t remainder) { struct gv_drive *d; @@ -798,6 +925,20 @@ return (NULL); } +/* Find a drivegroup by name. */ +struct gv_dg * +gv_find_dg(struct gv_softc *sc, char *name) +{ + struct gv_dg *dg; + + LIST_FOREACH(dg, &sc->drivegroups, drivegroup) { + if (!strncmp(dg->name, name, GV_MAXGROUPNAME)) + return (dg); + } + + return (NULL); +} + /* Check if any consumer of the given geom is open. */ int gv_is_open(struct g_geom *gp) @@ -820,6 +961,7 @@ gv_object_type(struct gv_softc *sc, char *name) { struct gv_drive *d; + struct gv_dg *dg; struct gv_plex *p; struct gv_sd *s; struct gv_volume *v; @@ -844,6 +986,11 @@ return (GV_TYPE_DRIVE); } + LIST_FOREACH(dg, &sc->drivegroups, drivegroup) { + if (!strncmp(dg->name, name, GV_MAXGROUPNAME)) + return (GV_TYPE_DG); + } + return (-1); } Index: sys/geom/vinum/geom_vinum_var.h =================================================================== RCS file: /home/ncvs/src/sys/geom/vinum/geom_vinum_var.h,v retrieving revision 1.11 diff -u -r1.11 geom_vinum_var.h --- sys/geom/vinum/geom_vinum_var.h 6 Jan 2006 18:03:17 -0000 1.11 +++ sys/geom/vinum/geom_vinum_var.h 3 Oct 2006 19:06:50 -0000 @@ -75,6 +75,7 @@ /* #define GV_DATA_START (GV_CFG_LEN * 2 + GV_HDR_LEN) */ #define GV_MAXDRIVENAME 32 /* Maximum length of a device name. */ +#define GV_MAXGROUPNAME 32 /* Maximum length of a group name. */ #define GV_MAXSDNAME 64 /* Maximum length of a subdisk name. */ #define GV_MAXPLEXNAME 64 /* Maximum length of a plex name. */ #define GV_MAXVOLNAME 64 /* Maximum length of a volume name. */ @@ -91,6 +92,7 @@ #define GV_TYPE_PLEX 2 #define GV_TYPE_SD 3 #define GV_TYPE_DRIVE 4 +#define GV_TYPE_DG 5 /* State changing flags. */ #define GV_SETSTATE_FORCE 0x1 @@ -166,10 +168,12 @@ /* Linked lists of all objects in our setup. */ LIST_HEAD(,gv_drive) drives; /* All drives. */ + LIST_HEAD(,gv_dg) drivegroups; /* All groups. */ LIST_HEAD(,gv_plex) plexes; /* All plexes. */ LIST_HEAD(,gv_sd) subdisks; /* All subdisks. */ LIST_HEAD(,gv_volume) volumes; /* All volumes. */ + struct gv_dg *defaultdg; /* Default drivegroup. */ struct g_geom *geom; /* Pointer to our VINUM geom. */ }; @@ -191,12 +195,16 @@ #define GV_DRIVE_THREAD_DEAD 0x04 /* The worker thread has died. */ #define GV_DRIVE_NEWBORN 0x08 /* The drive was just created. */ + char drivegroup[GV_MAXGROUPNAME]; /* The name of it's group. */ + struct gv_dg *drivegroup_sc; /* Pointer to drivegroup. */ + struct gv_hdr *hdr; /* The drive header. */ int freelist_entries; /* Count of freelist entries. */ LIST_HEAD(,gv_freelist) freelist; /* List of freelist entries. */ LIST_HEAD(,gv_sd) subdisks; /* Subdisks on this drive. */ LIST_ENTRY(gv_drive) drive; /* Entry in the vinum config. */ + LIST_ENTRY(gv_drive) in_drivegroup; /* Entry in drivegroup. */ #ifdef _KERNEL struct bio_queue_head *bqueue; /* BIO queue of this drive. */ @@ -209,6 +217,19 @@ struct gv_softc *vinumconf; /* Pointer to the vinum conf. */ }; +struct gv_dg { + char name[GV_MAXGROUPNAME]; + int state; +#define GV_GROUP_DOWN 0 +#define GV_GROUP_UP 1 + + int drivecount; /* Number of drives in group. */ + LIST_HEAD(,gv_drive) drives; /* List of drives in group. */ + LIST_ENTRY(gv_dg) drivegroup; /* Entry in the vinum config. */ + + struct gv_softc *vinumconf; /* Pointer to the vinum conf. */ +}; + /* softc for a subdisk. */ struct gv_sd { char name[GV_MAXSDNAME]; /* The name of this subdisk. */ Index: sys/geom/vinum/geom_vinum_volume.c =================================================================== RCS file: /home/ncvs/src/sys/geom/vinum/geom_vinum_volume.c,v retrieving revision 1.11 diff -u -r1.11 geom_vinum_volume.c --- sys/geom/vinum/geom_vinum_volume.c 6 Jan 2006 18:03:17 -0000 1.11 +++ sys/geom/vinum/geom_vinum_volume.c 3 Oct 2006 19:06:50 -0000 @@ -156,7 +156,6 @@ gv_vol_completed_request(struct gv_volume *v, struct bio *bp) { struct bio *pbp; - struct g_geom *gp; struct g_consumer *cp, *cp2; pbp = bp->bio_parent; @@ -174,7 +173,6 @@ /* Check if we have another plex left. */ cp = bp->bio_from; - gp = cp->geom; cp2 = LIST_NEXT(cp, consumer); if (cp2 == NULL) break; Index: sys/modules/geom/geom_vinum/Makefile =================================================================== RCS file: /home/ncvs/src/sys/modules/geom/geom_vinum/Makefile,v retrieving revision 1.4 diff -u -r1.4 Makefile --- sys/modules/geom/geom_vinum/Makefile 24 Nov 2005 15:11:41 -0000 1.4 +++ sys/modules/geom/geom_vinum/Makefile 3 Oct 2006 19:06:53 -0000 @@ -3,10 +3,11 @@ .PATH: ${.CURDIR}/../../../geom/vinum KMOD= geom_vinum -SRCS= geom_vinum.c geom_vinum_drive.c geom_vinum_plex.c \ +SRCS= geom_vinum.c geom_vinum_attach.c geom_vinum_detach.c \ + geom_vinum_drive.c geom_vinum_plex.c geom_vinum_op.c \ geom_vinum_volume.c geom_vinum_subr.c geom_vinum_raid5.c \ geom_vinum_share.c geom_vinum_list.c geom_vinum_rm.c \ - geom_vinum_init.c geom_vinum_state.c geom_vinum_rename.c \ - geom_vinum_move.c + geom_vinum_init.c geom_vinum_state.c geom_vinum_stop.c \ + geom_vinum_rename.c geom_vinum_move.c geom_vinum_test.c .include