2 * SPDX-License-Identifier: BSD-4-Clause
4 * Copyright (c) 2004, 2007 Lukas Ertl
5 * Copyright (c) 2007, 2009 Ulf Lilleengen
6 * Copyright (c) 1997, 1998, 1999
7 * Nan Yang Computer Services Limited. All rights reserved.
9 * Parts written by Greg Lehey
11 * This software is distributed under the so-called ``Berkeley
14 * Redistribution and use in source and binary forms, with or without
15 * modification, are permitted provided that the following conditions
17 * 1. Redistributions of source code must retain the above copyright
18 * notice, this list of conditions and the following disclaimer.
19 * 2. Redistributions in binary form must reproduce the above copyright
20 * notice, this list of conditions and the following disclaimer in the
21 * documentation and/or other materials provided with the distribution.
22 * 3. All advertising materials mentioning features or use of this software
23 * must display the following acknowledgement:
24 * This product includes software developed by Nan Yang Computer
26 * 4. Neither the name of the Company nor the names of its contributors
27 * may be used to endorse or promote products derived from this software
28 * without specific prior written permission.
30 * This software is provided ``as is'', and any express or implied
31 * warranties, including, but not limited to, the implied warranties of
32 * merchantability and fitness for a particular purpose are disclaimed.
33 * In no event shall the company or contributors be liable for any
34 * direct, indirect, incidental, special, exemplary, or consequential
35 * damages (including, but not limited to, procurement of substitute
36 * goods or services; loss of use, data, or profits; or business
37 * interruption) however caused and on any theory of liability, whether
38 * in contract, strict liability, or tort (including negligence or
39 * otherwise) arising in any way out of the use of this software, even if
40 * advised of the possibility of such damage.
44 #include <sys/cdefs.h>
45 __FBSDID("$FreeBSD$");
47 #include <sys/param.h>
48 #include <sys/malloc.h>
50 #include <sys/systm.h>
52 #include <geom/geom.h>
53 #include <geom/vinum/geom_vinum_var.h>
54 #include <geom/vinum/geom_vinum.h>
55 #include <geom/vinum/geom_vinum_share.h>
57 int gv_drive_is_newer(struct gv_softc *, struct gv_drive *);
58 static off_t gv_plex_smallest_sd(struct gv_plex *);
61 gv_parse_config(struct gv_softc *sc, char *buf, struct gv_drive *d)
63 char *aptr, *bptr, *cptr;
64 struct gv_volume *v, *v2;
65 struct gv_plex *p, *p2;
67 int error, is_newer, tokens;
68 char *token[GV_MAXARGS];
70 is_newer = gv_drive_is_newer(sc, d);
72 /* Until the end of the string *buf. */
73 for (aptr = buf; *aptr != '\0'; aptr = bptr) {
77 /* Separate input lines. */
83 tokens = gv_tokenize(cptr, token, GV_MAXARGS);
88 if (!strcmp(token[0], "volume")) {
89 v = gv_new_volume(tokens, token);
91 G_VINUM_DEBUG(0, "config parse failed volume");
95 v2 = gv_find_vol(sc, v->name);
99 G_VINUM_DEBUG(2, "newer volume found!");
105 gv_create_volume(sc, v);
107 } else if (!strcmp(token[0], "plex")) {
108 p = gv_new_plex(tokens, token);
110 G_VINUM_DEBUG(0, "config parse failed plex");
114 p2 = gv_find_plex(sc, p->name);
118 p2->state = p->state;
119 G_VINUM_DEBUG(2, "newer plex found!");
125 error = gv_create_plex(sc, p);
129 * These flags were set in gv_create_plex() and are not
130 * needed here (on-disk config parsing).
132 p->flags &= ~GV_PLEX_ADDED;
134 } else if (!strcmp(token[0], "sd")) {
135 s = gv_new_sd(tokens, token);
138 G_VINUM_DEBUG(0, "config parse failed subdisk");
142 s2 = gv_find_sd(sc, s->name);
146 s2->state = s->state;
147 G_VINUM_DEBUG(2, "newer subdisk found!");
154 * Signal that this subdisk was tasted, and could
155 * possibly reference a drive that isn't in our config
158 s->flags |= GV_SD_TASTED;
160 if (s->state == GV_SD_UP)
161 s->flags |= GV_SD_CANGOUP;
163 error = gv_create_sd(sc, s);
168 * This flag was set in gv_create_sd() and is not
169 * needed here (on-disk config parsing).
171 s->flags &= ~GV_SD_NEWBORN;
172 s->flags &= ~GV_SD_GROW;
178 * Format the vinum configuration properly. If ondisk is non-zero then the
179 * configuration is intended to be written to disk later.
182 gv_format_config(struct gv_softc *sc, struct sbuf *sb, int ondisk, char *prefix)
190 * We don't need the drive configuration if we're not writing the
194 LIST_FOREACH(d, &sc->drives, drive) {
195 sbuf_printf(sb, "%sdrive %s device /dev/%s\n", prefix,
200 LIST_FOREACH(v, &sc->volumes, volume) {
202 sbuf_printf(sb, "%s", prefix);
203 sbuf_printf(sb, "volume %s", v->name);
205 sbuf_printf(sb, " state %s", gv_volstate(v->state));
206 sbuf_printf(sb, "\n");
209 LIST_FOREACH(p, &sc->plexes, plex) {
211 sbuf_printf(sb, "%s", prefix);
212 sbuf_printf(sb, "plex name %s org %s ", p->name,
214 if (gv_is_striped(p))
215 sbuf_printf(sb, "%ds ", p->stripesize / 512);
216 if (p->vol_sc != NULL)
217 sbuf_printf(sb, "vol %s", p->volume);
219 sbuf_printf(sb, " state %s", gv_plexstate(p->state));
220 sbuf_printf(sb, "\n");
223 LIST_FOREACH(s, &sc->subdisks, sd) {
225 sbuf_printf(sb, "%s", prefix);
226 sbuf_printf(sb, "sd name %s drive %s len %jds driveoffset "
227 "%jds", s->name, s->drive, s->size / 512,
228 s->drive_offset / 512);
229 if (s->plex_sc != NULL) {
230 sbuf_printf(sb, " plex %s plexoffset %jds", s->plex,
231 s->plex_offset / 512);
234 sbuf_printf(sb, " state %s", gv_sdstate(s->state));
235 sbuf_printf(sb, "\n");
240 gv_plex_smallest_sd(struct gv_plex *p)
245 KASSERT(p != NULL, ("gv_plex_smallest_sd: NULL p"));
247 s = LIST_FIRST(&p->subdisks);
251 LIST_FOREACH(s, &p->subdisks, in_plex) {
252 if (s->size < smallest)
258 /* Walk over plexes in a volume and count how many are down. */
260 gv_plexdown(struct gv_volume *v)
265 KASSERT(v != NULL, ("gv_plexdown: NULL v"));
269 LIST_FOREACH(p, &v->plexes, plex) {
270 if (p->state == GV_PLEX_DOWN)
277 gv_sd_to_plex(struct gv_sd *s, struct gv_plex *p)
280 off_t psizeorig, remainder, smallest;
282 /* If this subdisk was already given to this plex, do nothing. */
286 /* Check correct size of this subdisk. */
287 s2 = LIST_FIRST(&p->subdisks);
288 /* Adjust the subdisk-size if necessary. */
289 if (s2 != NULL && gv_is_striped(p)) {
290 /* First adjust to the stripesize. */
291 remainder = s->size % p->stripesize;
294 G_VINUM_DEBUG(1, "size of sd %s is not a "
295 "multiple of plex stripesize, taking off "
296 "%jd bytes", s->name,
297 (intmax_t)remainder);
298 gv_adjust_freespace(s, remainder);
301 smallest = gv_plex_smallest_sd(p);
302 /* Then take off extra if other subdisks are smaller. */
303 remainder = s->size - smallest;
306 * Don't allow a remainder below zero for running plexes, it's too
307 * painful, and if someone were to accidentally do this, the
308 * resulting array might be smaller than the original... not god
311 if (!(p->flags & GV_PLEX_NEWBORN)) {
312 G_VINUM_DEBUG(0, "sd %s too small for plex %s!",
314 return (GV_ERR_BADSIZE);
316 /* Adjust other subdisks. */
317 LIST_FOREACH(s2, &p->subdisks, in_plex) {
318 G_VINUM_DEBUG(1, "size of sd %s is to big, "
319 "taking off %jd bytes", s->name,
320 (intmax_t)remainder);
321 gv_adjust_freespace(s2, (remainder * -1));
323 } else if (remainder > 0) {
324 G_VINUM_DEBUG(1, "size of sd %s is to big, "
325 "taking off %jd bytes", s->name,
326 (intmax_t)remainder);
327 gv_adjust_freespace(s, remainder);
331 /* Find the correct plex offset for this subdisk, if needed. */
332 if (s->plex_offset == -1) {
334 * First set it to 0 to catch the case where we had a detached
335 * subdisk that didn't get any good offset.
339 LIST_FOREACH(s2, &p->subdisks, in_plex) {
340 if (gv_is_striped(p))
341 s->plex_offset = p->sdcount *
344 s->plex_offset = s2->plex_offset +
350 /* There are no subdisks for this plex yet, just insert it. */
351 if (LIST_EMPTY(&p->subdisks)) {
352 LIST_INSERT_HEAD(&p->subdisks, s, in_plex);
354 /* Insert in correct order, depending on plex_offset. */
356 LIST_FOREACH(s2, &p->subdisks, in_plex) {
357 if (s->plex_offset < s2->plex_offset) {
358 LIST_INSERT_BEFORE(s2, s, in_plex);
360 } else if (LIST_NEXT(s2, in_plex) == NULL) {
361 LIST_INSERT_AFTER(s2, s, in_plex);
368 /* Adjust the size of our plex. We check if the plex misses a subdisk,
369 * so we don't make the plex smaller than it actually should be.
372 p->size = gv_plex_size(p);
373 /* Make sure the size is not changed. */
374 if (p->sddetached > 0) {
375 if (p->size < psizeorig) {
377 /* We make sure wee need another subdisk. */
378 if (p->sddetached == 1)
383 if ((p->org == GV_PLEX_RAID5 ||
384 p->org == GV_PLEX_STRIPED) &&
385 !(p->flags & GV_PLEX_NEWBORN) &&
386 p->state == GV_PLEX_UP) {
387 s->flags |= GV_SD_GROW;
396 gv_update_vol_size(struct gv_volume *v, off_t size)
400 if (v->provider != NULL) {
402 v->provider->mediasize = size;
408 /* Return how many subdisks that constitute the original plex. */
410 gv_sdcount(struct gv_plex *p, int growing)
415 sdcount = p->sdcount;
417 LIST_FOREACH(s, &p->subdisks, in_plex) {
418 if (s->flags & GV_SD_GROW)
426 /* Calculates the plex size. */
428 gv_plex_size(struct gv_plex *p)
434 KASSERT(p != NULL, ("gv_plex_size: NULL p"));
436 /* Adjust the size of our plex. */
438 sdcount = gv_sdcount(p, 1);
441 LIST_FOREACH(s, &p->subdisks, in_plex)
444 case GV_PLEX_STRIPED:
445 s = LIST_FIRST(&p->subdisks);
446 size = ((s != NULL) ? (sdcount * s->size) : 0);
449 s = LIST_FIRST(&p->subdisks);
450 size = ((s != NULL) ? ((sdcount - 1) * s->size) : 0);
457 /* Returns the size of a volume. */
459 gv_vol_size(struct gv_volume *v)
464 KASSERT(v != NULL, ("gv_vol_size: NULL v"));
466 p = LIST_FIRST(&v->plexes);
470 minplexsize = p->size;
471 LIST_FOREACH(p, &v->plexes, in_volume) {
472 if (p->size < minplexsize) {
473 minplexsize = p->size;
476 return (minplexsize);
480 gv_update_plex_config(struct gv_plex *p)
482 struct gv_sd *s, *s2;
484 int required_sds, state;
486 KASSERT(p != NULL, ("gv_update_plex_config: NULL p"));
488 /* The plex was added to an already running volume. */
489 if (p->flags & GV_PLEX_ADDED)
490 gv_set_plex_state(p, GV_PLEX_DOWN, GV_SETSTATE_FORCE);
493 case GV_PLEX_STRIPED:
506 if (p->sdcount < required_sds) {
507 gv_set_plex_state(p, GV_PLEX_DOWN, GV_SETSTATE_FORCE);
511 * The subdisks in striped plexes must all have the same size.
513 s = LIST_FIRST(&p->subdisks);
514 LIST_FOREACH(s2, &p->subdisks, in_plex) {
515 if (s->size != s2->size) {
516 G_VINUM_DEBUG(0, "subdisk size mismatch %s"
517 "(%jd) <> %s (%jd)", s->name, s->size,
519 gv_set_plex_state(p, GV_PLEX_DOWN,
524 LIST_FOREACH(s, &p->subdisks, in_plex) {
525 /* Trim subdisk sizes to match the stripe size. */
526 remainder = s->size % p->stripesize;
528 G_VINUM_DEBUG(1, "size of sd %s is not a "
529 "multiple of plex stripesize, taking off "
530 "%jd bytes", s->name, (intmax_t)remainder);
531 gv_adjust_freespace(s, remainder);
536 p->size = gv_plex_size(p);
538 gv_set_plex_state(p, GV_PLEX_DOWN, GV_SETSTATE_FORCE);
539 else if (p->org == GV_PLEX_RAID5 && p->flags & GV_PLEX_NEWBORN) {
540 LIST_FOREACH(s, &p->subdisks, in_plex)
541 gv_set_sd_state(s, GV_SD_UP, GV_SETSTATE_FORCE);
542 /* If added to a volume, we want the plex to be down. */
543 state = (p->flags & GV_PLEX_ADDED) ? GV_PLEX_DOWN : GV_PLEX_UP;
544 gv_set_plex_state(p, state, GV_SETSTATE_FORCE);
545 p->flags &= ~GV_PLEX_ADDED;
546 } else if (p->flags & GV_PLEX_ADDED) {
547 LIST_FOREACH(s, &p->subdisks, in_plex)
548 gv_set_sd_state(s, GV_SD_STALE, GV_SETSTATE_FORCE);
549 gv_set_plex_state(p, GV_PLEX_DOWN, GV_SETSTATE_FORCE);
550 p->flags &= ~GV_PLEX_ADDED;
551 } else if (p->state == GV_PLEX_UP) {
552 LIST_FOREACH(s, &p->subdisks, in_plex) {
553 if (s->flags & GV_SD_GROW) {
554 gv_set_plex_state(p, GV_PLEX_GROWABLE,
560 /* Our plex is grown up now. */
561 p->flags &= ~GV_PLEX_NEWBORN;
565 * Give a subdisk to a drive, check and adjust several parameters, adjust
569 gv_sd_to_drive(struct gv_sd *s, struct gv_drive *d)
572 struct gv_freelist *fl, *fl2;
578 /* Shortcut for "referenced" drives. */
579 if (d->flags & GV_DRIVE_REFERENCED) {
584 /* Check if this subdisk was already given to this drive. */
585 if (s->drive_sc != NULL) {
586 if (s->drive_sc == d) {
587 if (!(s->flags & GV_SD_TASTED)) {
591 G_VINUM_DEBUG(0, "error giving subdisk '%s' to '%s' "
592 "(already on '%s')", s->name, d->name,
594 return (GV_ERR_ISATTACHED);
598 /* Preliminary checks. */
599 if ((s->size > d->avail) || (d->freelist_entries == 0)) {
600 G_VINUM_DEBUG(0, "not enough space on '%s' for '%s'", d->name,
602 return (GV_ERR_NOSPACE);
605 /* If no size was given for this subdisk, try to auto-size it... */
607 /* Find the largest available slot. */
608 LIST_FOREACH(fl, &d->freelist, freelist) {
609 if (fl->size < s->size)
612 s->drive_offset = fl->offset;
616 /* No good slot found? */
618 G_VINUM_DEBUG(0, "unable to autosize '%s' on '%s'",
620 return (GV_ERR_BADSIZE);
624 * ... or check if we have a free slot that's large enough for the
629 LIST_FOREACH(fl, &d->freelist, freelist) {
630 if (fl->size < s->size)
632 /* Assign drive offset, if not given. */
633 if (s->drive_offset == -1)
634 s->drive_offset = fl->offset;
640 /* Couldn't find a good free slot. */
642 G_VINUM_DEBUG(0, "free slots to small for '%s' on '%s'",
644 return (GV_ERR_NOSPACE);
648 /* No drive offset given, try to calculate it. */
649 if (s->drive_offset == -1) {
651 /* Add offsets and sizes from other subdisks on this drive. */
652 LIST_FOREACH(s2, &d->subdisks, from_drive) {
653 s->drive_offset = s2->drive_offset + s2->size;
657 * If there are no other subdisks yet, then set the default
658 * offset to GV_DATA_START.
660 if (s->drive_offset == -1)
661 s->drive_offset = GV_DATA_START;
663 /* Check if we have a free slot at the given drive offset. */
666 LIST_FOREACH(fl, &d->freelist, freelist) {
667 /* Yes, this subdisk fits. */
668 if ((fl->offset <= s->drive_offset) &&
669 (fl->offset + fl->size >=
670 s->drive_offset + s->size)) {
677 /* Couldn't find a good free slot. */
679 G_VINUM_DEBUG(0, "given drive_offset for '%s' won't fit "
680 "on '%s'", s->name, d->name);
681 return (GV_ERR_NOSPACE);
686 * Now that all parameters are checked and set up, we can give the
687 * subdisk to the drive and adjust the freelist.
690 /* First, adjust the freelist. */
691 LIST_FOREACH(fl, &d->freelist, freelist) {
692 /* Look for the free slot that we have found before. */
696 /* The subdisk starts at the beginning of the free slot. */
697 if (fl->offset == s->drive_offset) {
698 fl->offset += s->size;
701 /* The subdisk uses the whole slot, so remove it. */
703 d->freelist_entries--;
704 LIST_REMOVE(fl, freelist);
707 * The subdisk does not start at the beginning of the free
711 tmp = fl->offset + fl->size;
712 fl->size = s->drive_offset - fl->offset;
715 * The subdisk didn't use the complete rest of the free
716 * slot, so we need to split it.
718 if (s->drive_offset + s->size != tmp) {
719 fl2 = g_malloc(sizeof(*fl2), M_WAITOK | M_ZERO);
720 fl2->offset = s->drive_offset + s->size;
721 fl2->size = tmp - fl2->offset;
722 LIST_INSERT_AFTER(fl, fl2, freelist);
723 d->freelist_entries++;
730 * This is the first subdisk on this drive, just insert it into the
733 if (LIST_EMPTY(&d->subdisks)) {
734 LIST_INSERT_HEAD(&d->subdisks, s, from_drive);
736 /* There are other subdisks, so insert this one in correct order. */
738 LIST_FOREACH(s2, &d->subdisks, from_drive) {
739 if (s->drive_offset < s2->drive_offset) {
740 LIST_INSERT_BEFORE(s2, s, from_drive);
742 } else if (LIST_NEXT(s2, from_drive) == NULL) {
743 LIST_INSERT_AFTER(s2, s, from_drive);
752 s->flags &= ~GV_SD_TASTED;
754 /* Link back from the subdisk to this drive. */
761 gv_free_sd(struct gv_sd *s)
764 struct gv_freelist *fl, *fl2;
766 KASSERT(s != NULL, ("gv_free_sd: NULL s"));
773 * First, find the free slot that's immediately before or after this
777 LIST_FOREACH(fl, &d->freelist, freelist) {
778 if (fl->offset == s->drive_offset + s->size)
780 if (fl->offset + fl->size == s->drive_offset)
784 /* If there is no free slot behind this subdisk, so create one. */
787 fl = g_malloc(sizeof(*fl), M_WAITOK | M_ZERO);
789 fl->offset = s->drive_offset;
791 if (d->freelist_entries == 0) {
792 LIST_INSERT_HEAD(&d->freelist, fl, freelist);
794 LIST_FOREACH(fl2, &d->freelist, freelist) {
795 if (fl->offset < fl2->offset) {
796 LIST_INSERT_BEFORE(fl2, fl, freelist);
798 } else if (LIST_NEXT(fl2, freelist) == NULL) {
799 LIST_INSERT_AFTER(fl2, fl, freelist);
805 d->freelist_entries++;
807 /* Expand the free slot we just found. */
810 if (fl->offset > s->drive_offset)
811 fl->offset = s->drive_offset;
819 gv_adjust_freespace(struct gv_sd *s, off_t remainder)
822 struct gv_freelist *fl, *fl2;
824 KASSERT(s != NULL, ("gv_adjust_freespace: NULL s"));
826 KASSERT(d != NULL, ("gv_adjust_freespace: NULL d"));
828 /* First, find the free slot that's immediately after this subdisk. */
830 LIST_FOREACH(fl, &d->freelist, freelist) {
831 if (fl->offset == s->drive_offset + s->size)
835 /* If there is no free slot behind this subdisk, so create one. */
838 fl = g_malloc(sizeof(*fl), M_WAITOK | M_ZERO);
839 fl->size = remainder;
840 fl->offset = s->drive_offset + s->size - remainder;
842 if (d->freelist_entries == 0) {
843 LIST_INSERT_HEAD(&d->freelist, fl, freelist);
845 LIST_FOREACH(fl2, &d->freelist, freelist) {
846 if (fl->offset < fl2->offset) {
847 LIST_INSERT_BEFORE(fl2, fl, freelist);
849 } else if (LIST_NEXT(fl2, freelist) == NULL) {
850 LIST_INSERT_AFTER(fl2, fl, freelist);
856 d->freelist_entries++;
858 /* Expand the free slot we just found. */
860 fl->offset -= remainder;
861 fl->size += remainder;
864 s->size -= remainder;
865 d->avail += remainder;
868 /* Check if the given plex is a striped one. */
870 gv_is_striped(struct gv_plex *p)
872 KASSERT(p != NULL, ("gv_is_striped: NULL p"));
874 case GV_PLEX_STRIPED:
882 /* Find a volume by name. */
884 gv_find_vol(struct gv_softc *sc, char *name)
888 LIST_FOREACH(v, &sc->volumes, volume) {
889 if (!strncmp(v->name, name, GV_MAXVOLNAME))
896 /* Find a plex by name. */
898 gv_find_plex(struct gv_softc *sc, char *name)
902 LIST_FOREACH(p, &sc->plexes, plex) {
903 if (!strncmp(p->name, name, GV_MAXPLEXNAME))
910 /* Find a subdisk by name. */
912 gv_find_sd(struct gv_softc *sc, char *name)
916 LIST_FOREACH(s, &sc->subdisks, sd) {
917 if (!strncmp(s->name, name, GV_MAXSDNAME))
924 /* Find a drive by name. */
926 gv_find_drive(struct gv_softc *sc, char *name)
930 LIST_FOREACH(d, &sc->drives, drive) {
931 if (!strncmp(d->name, name, GV_MAXDRIVENAME))
938 /* Find a drive given a device. */
940 gv_find_drive_device(struct gv_softc *sc, char *device)
944 LIST_FOREACH(d, &sc->drives, drive) {
945 if(!strcmp(d->device, device))
952 /* Check if any consumer of the given geom is open. */
954 gv_consumer_is_open(struct g_consumer *cp)
959 if (cp->acr || cp->acw || cp->ace)
966 gv_provider_is_open(struct g_provider *pp)
971 if (pp->acr || pp->acw || pp->ace)
978 * Compare the modification dates of the drives.
979 * Return 1 if a > b, 0 otherwise.
982 gv_drive_is_newer(struct gv_softc *sc, struct gv_drive *d)
985 struct timeval *a, *b;
987 KASSERT(!LIST_EMPTY(&sc->drives),
988 ("gv_is_drive_newer: empty drive list"));
990 a = &d->hdr->label.last_update;
991 LIST_FOREACH(d2, &sc->drives, drive) {
992 if ((d == d2) || (d2->state != GV_DRIVE_UP) ||
995 b = &d2->hdr->label.last_update;
996 if (timevalcmp(a, b, >))
1003 /* Return the type of object identified by string 'name'. */
1005 gv_object_type(struct gv_softc *sc, char *name)
1010 struct gv_volume *v;
1012 LIST_FOREACH(v, &sc->volumes, volume) {
1013 if (!strncmp(v->name, name, GV_MAXVOLNAME))
1014 return (GV_TYPE_VOL);
1017 LIST_FOREACH(p, &sc->plexes, plex) {
1018 if (!strncmp(p->name, name, GV_MAXPLEXNAME))
1019 return (GV_TYPE_PLEX);
1022 LIST_FOREACH(s, &sc->subdisks, sd) {
1023 if (!strncmp(s->name, name, GV_MAXSDNAME))
1024 return (GV_TYPE_SD);
1027 LIST_FOREACH(d, &sc->drives, drive) {
1028 if (!strncmp(d->name, name, GV_MAXDRIVENAME))
1029 return (GV_TYPE_DRIVE);
1032 return (GV_ERR_NOTFOUND);
1036 gv_setup_objects(struct gv_softc *sc)
1038 struct g_provider *pp;
1039 struct gv_volume *v;
1044 LIST_FOREACH(s, &sc->subdisks, sd) {
1045 d = gv_find_drive(sc, s->drive);
1047 gv_sd_to_drive(s, d);
1048 p = gv_find_plex(sc, s->plex);
1050 gv_sd_to_plex(s, p);
1051 gv_update_sd_state(s);
1054 LIST_FOREACH(p, &sc->plexes, plex) {
1055 gv_update_plex_config(p);
1056 v = gv_find_vol(sc, p->volume);
1057 if (v != NULL && p->vol_sc != v) {
1060 LIST_INSERT_HEAD(&v->plexes, p, in_volume);
1062 gv_update_plex_config(p);
1065 LIST_FOREACH(v, &sc->volumes, volume) {
1066 v->size = gv_vol_size(v);
1067 if (v->provider == NULL) {
1069 pp = g_new_providerf(sc->geom, "gvinum/%s", v->name);
1070 pp->mediasize = v->size;
1071 pp->sectorsize = 512; /* XXX */
1072 g_error_provider(pp, 0);
1075 g_topology_unlock();
1076 } else if (v->provider->mediasize != v->size) {
1078 v->provider->mediasize = v->size;
1079 g_topology_unlock();
1081 v->flags &= ~GV_VOL_NEWBORN;
1082 gv_update_vol_state(v);
1087 gv_cleanup(struct gv_softc *sc)
1089 struct gv_volume *v, *v2;
1090 struct gv_plex *p, *p2;
1091 struct gv_sd *s, *s2;
1092 struct gv_drive *d, *d2;
1093 struct gv_freelist *fl, *fl2;
1095 mtx_lock(&sc->config_mtx);
1096 LIST_FOREACH_SAFE(v, &sc->volumes, volume, v2) {
1097 LIST_REMOVE(v, volume);
1101 LIST_FOREACH_SAFE(p, &sc->plexes, plex, p2) {
1102 LIST_REMOVE(p, plex);
1108 LIST_FOREACH_SAFE(s, &sc->subdisks, sd, s2) {
1112 LIST_FOREACH_SAFE(d, &sc->drives, drive, d2) {
1113 LIST_FOREACH_SAFE(fl, &d->freelist, freelist, fl2) {
1114 LIST_REMOVE(fl, freelist);
1117 LIST_REMOVE(d, drive);
1121 mtx_destroy(&sc->config_mtx);
1124 /* General 'attach' routine. */
1126 gv_attach_plex(struct gv_plex *p, struct gv_volume *v, int rename)
1129 struct gv_softc *sc;
1131 g_topology_assert();
1134 KASSERT(sc != NULL, ("NULL sc"));
1136 if (p->vol_sc != NULL) {
1137 G_VINUM_DEBUG(1, "unable to attach %s: already attached to %s",
1138 p->name, p->volume);
1139 return (GV_ERR_ISATTACHED);
1142 /* Stale all subdisks of this plex. */
1143 LIST_FOREACH(s, &p->subdisks, in_plex) {
1144 if (s->state != GV_SD_STALE)
1145 gv_set_sd_state(s, GV_SD_STALE, GV_SETSTATE_FORCE);
1147 /* Attach to volume. Make sure volume is not up and running. */
1148 if (gv_provider_is_open(v->provider)) {
1149 G_VINUM_DEBUG(1, "unable to attach %s: volume %s is busy",
1151 return (GV_ERR_ISBUSY);
1154 strlcpy(p->volume, v->name, sizeof(p->volume));
1157 snprintf(p->name, sizeof(p->name), "%s.p%d", v->name,
1160 LIST_INSERT_HEAD(&v->plexes, p, in_volume);
1162 /* Get plex up again. */
1163 gv_update_vol_size(v, gv_vol_size(v));
1164 gv_set_plex_state(p, GV_PLEX_UP, 0);
1165 gv_save_config(p->vinumconf);
1170 gv_attach_sd(struct gv_sd *s, struct gv_plex *p, off_t offset, int rename)
1175 g_topology_assert();
1177 /* If subdisk is attached, don't do it. */
1178 if (s->plex_sc != NULL) {
1179 G_VINUM_DEBUG(1, "unable to attach %s: already attached to %s",
1181 return (GV_ERR_ISATTACHED);
1184 gv_set_sd_state(s, GV_SD_STALE, GV_SETSTATE_FORCE);
1185 /* First check that this subdisk has a correct offset. If none other
1186 * starts at the same, and it's correct module stripesize, it is */
1187 if (offset != -1 && offset % p->stripesize != 0)
1188 return (GV_ERR_BADOFFSET);
1189 LIST_FOREACH(s2, &p->subdisks, in_plex) {
1190 if (s2->plex_offset == offset)
1191 return (GV_ERR_BADOFFSET);
1194 /* Attach the subdisk to the plex at given offset. */
1195 s->plex_offset = offset;
1196 strlcpy(s->plex, p->name, sizeof(s->plex));
1198 sdcount = p->sdcount;
1199 error = gv_sd_to_plex(s, p);
1202 gv_update_plex_config(p);
1205 snprintf(s->name, sizeof(s->name), "%s.s%d", s->plex,
1208 if (p->vol_sc != NULL)
1209 gv_update_vol_size(p->vol_sc, gv_vol_size(p->vol_sc));
1210 gv_save_config(p->vinumconf);
1211 /* We don't update the subdisk state since the user might have to
1212 * initiate a rebuild/sync first. */
1216 /* Detach a plex from a volume. */
1218 gv_detach_plex(struct gv_plex *p, int flags)
1220 struct gv_volume *v;
1222 g_topology_assert();
1226 G_VINUM_DEBUG(1, "unable to detach %s: already detached",
1228 return (0); /* Not an error. */
1232 * Only proceed if forced or volume inactive.
1234 if (!(flags & GV_FLAG_F) && (gv_provider_is_open(v->provider) ||
1235 p->state == GV_PLEX_UP)) {
1236 G_VINUM_DEBUG(1, "unable to detach %s: volume %s is busy",
1237 p->name, p->volume);
1238 return (GV_ERR_ISBUSY);
1241 /* Make sure someone don't read us when gone. */
1242 v->last_read_plex = NULL;
1243 LIST_REMOVE(p, in_volume);
1245 memset(p->volume, 0, GV_MAXVOLNAME);
1246 gv_update_vol_size(v, gv_vol_size(v));
1247 gv_save_config(p->vinumconf);
1251 /* Detach a subdisk from a plex. */
1253 gv_detach_sd(struct gv_sd *s, int flags)
1257 g_topology_assert();
1261 G_VINUM_DEBUG(1, "unable to detach %s: already detached",
1263 return (0); /* Not an error. */
1267 * Don't proceed if we're not forcing, and the plex is up, or degraded
1268 * with this subdisk up.
1270 if (!(flags & GV_FLAG_F) && ((p->state > GV_PLEX_DEGRADED) ||
1271 ((p->state == GV_PLEX_DEGRADED) && (s->state == GV_SD_UP)))) {
1272 G_VINUM_DEBUG(1, "unable to detach %s: plex %s is busy",
1274 return (GV_ERR_ISBUSY);
1277 LIST_REMOVE(s, in_plex);
1279 memset(s->plex, 0, GV_MAXPLEXNAME);
1281 gv_save_config(s->vinumconf);