xref: /freebsd/sys/geom/vinum/geom_vinum_subr.c (revision c0b9f4fe659b6839541970eb5675e57f4d814969)
1 /*-
2  * Copyright (c) 2004 Lukas Ertl
3  * Copyright (c) 1997, 1998, 1999
4  *      Nan Yang Computer Services Limited.  All rights reserved.
5  *
6  *  Parts written by Greg Lehey
7  *
8  *  This software is distributed under the so-called ``Berkeley
9  *  License'':
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  * 3. All advertising materials mentioning features or use of this software
20  *    must display the following acknowledgement:
21  *      This product includes software developed by Nan Yang Computer
22  *      Services Limited.
23  * 4. Neither the name of the Company nor the names of its contributors
24  *    may be used to endorse or promote products derived from this software
25  *    without specific prior written permission.
26  *
27  * This software is provided ``as is'', and any express or implied
28  * warranties, including, but not limited to, the implied warranties of
29  * merchantability and fitness for a particular purpose are disclaimed.
30  * In no event shall the company or contributors be liable for any
31  * direct, indirect, incidental, special, exemplary, or consequential
32  * damages (including, but not limited to, procurement of substitute
33  * goods or services; loss of use, data, or profits; or business
34  * interruption) however caused and on any theory of liability, whether
35  * in contract, strict liability, or tort (including negligence or
36  * otherwise) arising in any way out of the use of this software, even if
37  * advised of the possibility of such damage.
38  *
39  */
40 
41 #include <sys/cdefs.h>
42 __FBSDID("$FreeBSD$");
43 
44 #include <sys/param.h>
45 #include <sys/conf.h>
46 #include <sys/kernel.h>
47 #include <sys/libkern.h>
48 #include <sys/malloc.h>
49 #include <sys/systm.h>
50 
51 #include <geom/geom.h>
52 #include <geom/geom_int.h>
53 #include <geom/vinum/geom_vinum_var.h>
54 #include <geom/vinum/geom_vinum.h>
55 #include <geom/vinum/geom_vinum_share.h>
56 
57 /* Find the VINUM class and it's associated geom. */
58 struct g_geom *
59 find_vinum_geom(void)
60 {
61 	struct g_class *mp;
62 	struct g_geom *gp;
63 
64 	g_topology_assert();
65 
66 	gp = NULL;
67 
68 	LIST_FOREACH(mp, &g_classes, class) {
69 		if (!strcmp(mp->name, "VINUM")) {
70 			gp = LIST_FIRST(&mp->geom);
71 			break;
72 		}
73 	}
74 
75 	return (gp);
76 }
77 
78 /*
79  * Parse the vinum config provided in *buf and store it in *gp's softc.
80  * If parameter 'merge' is non-zero, then the given config is merged into
81  * *gp.
82  */
83 void
84 gv_parse_config(struct gv_softc *sc, u_char *buf, int merge)
85 {
86 	char *aptr, *bptr, *cptr;
87 	struct gv_volume *v, *v2;
88 	struct gv_plex *p, *p2;
89 	struct gv_sd *s, *s2;
90 	int tokens;
91 	char *token[GV_MAXARGS];
92 
93 	g_topology_assert();
94 
95 	KASSERT(sc != NULL, ("gv_parse_config: NULL softc"));
96 
97 	/* Until the end of the string *buf. */
98 	for (aptr = buf; *aptr != '\0'; aptr = bptr) {
99 		bptr = aptr;
100 		cptr = aptr;
101 
102 		/* Seperate input lines. */
103 		while (*bptr != '\n')
104 			bptr++;
105 		*bptr = '\0';
106 		bptr++;
107 
108 		tokens = gv_tokenize(cptr, token, GV_MAXARGS);
109 
110 		if (tokens > 0) {
111 			if (!strcmp(token[0], "volume")) {
112 				v = gv_new_volume(tokens, token);
113 				if (v == NULL) {
114 					printf("geom_vinum: failed volume\n");
115 					break;
116 				}
117 
118 				if (merge) {
119 					v2 = gv_find_vol(sc, v->name);
120 					if (v2 != NULL) {
121 						g_free(v);
122 						continue;
123 					}
124 				}
125 
126 				v->vinumconf = sc;
127 				LIST_INIT(&v->plexes);
128 				LIST_INSERT_HEAD(&sc->volumes, v, volume);
129 
130 			} else if (!strcmp(token[0], "plex")) {
131 				p = gv_new_plex(tokens, token);
132 				if (p == NULL) {
133 					printf("geom_vinum: failed plex\n");
134 					break;
135 				}
136 
137 				if (merge) {
138 					p2 = gv_find_plex(sc, p->name);
139 					if (p2 != NULL) {
140 						g_free(p);
141 						continue;
142 					}
143 				}
144 
145 				p->vinumconf = sc;
146 				LIST_INIT(&p->subdisks);
147 				LIST_INSERT_HEAD(&sc->plexes, p, plex);
148 
149 			} else if (!strcmp(token[0], "sd")) {
150 				s = gv_new_sd(tokens, token);
151 
152 				if (s == NULL) {
153 					printf("geom_vinum: failed subdisk\n");
154 					break;
155 				}
156 
157 				if (merge) {
158 					s2 = gv_find_sd(sc, s->name);
159 					if (s2 != NULL) {
160 						g_free(s);
161 						continue;
162 					}
163 				}
164 
165 				s->vinumconf = sc;
166 				LIST_INSERT_HEAD(&sc->subdisks, s, sd);
167 			}
168 		}
169 	}
170 }
171 
172 /*
173  * Format the vinum configuration properly.  If ondisk is non-zero then the
174  * configuration is intended to be written to disk later.
175  */
176 void
177 gv_format_config(struct gv_softc *sc, struct sbuf *sb, int ondisk, char *prefix)
178 {
179 	struct gv_drive *d;
180 	struct gv_sd *s;
181 	struct gv_plex *p;
182 	struct gv_volume *v;
183 
184 	g_topology_assert();
185 
186 	/*
187 	 * We don't need the drive configuration if we're not writing the
188 	 * config to disk.
189 	 */
190 	if (!ondisk) {
191 		LIST_FOREACH(d, &sc->drives, drive) {
192 			sbuf_printf(sb, "%sdrive %s device /dev/%s\n", prefix,
193 			    d->name, d->device);
194 		}
195 	}
196 
197 	LIST_FOREACH(v, &sc->volumes, volume) {
198 		if (!ondisk)
199 			sbuf_printf(sb, "%s", prefix);
200 		sbuf_printf(sb, "volume %s", v->name);
201 		if (ondisk)
202 			sbuf_printf(sb, " state %s", gv_volstate(v->state));
203 		sbuf_printf(sb, "\n");
204 	}
205 
206 	LIST_FOREACH(p, &sc->plexes, plex) {
207 		if (!ondisk)
208 			sbuf_printf(sb, "%s", prefix);
209 		sbuf_printf(sb, "plex name %s org %s ", p->name,
210 		    gv_plexorg(p->org));
211 		if (gv_is_striped(p))
212 			sbuf_printf(sb, "%ds ", p->stripesize / 512);
213 		if (p->vol_sc != NULL)
214 			sbuf_printf(sb, "vol %s", p->volume);
215 		if (ondisk)
216 			sbuf_printf(sb, " state %s", gv_plexstate(p->state));
217 		sbuf_printf(sb, "\n");
218 	}
219 
220 	LIST_FOREACH(s, &sc->subdisks, sd) {
221 		if (!ondisk)
222 			sbuf_printf(sb, "%s", prefix);
223 		sbuf_printf(sb, "sd name %s drive %s len %jds driveoffset "
224 		    "%jds", s->name, s->drive, s->size / 512,
225 		    s->drive_offset / 512);
226 		if (s->plex_sc != NULL) {
227 			sbuf_printf(sb, " plex %s plexoffset %jds", s->plex,
228 			    s->plex_offset / 512);
229 		}
230 		if (ondisk)
231 			sbuf_printf(sb, " state %s", gv_sdstate(s->state));
232 		sbuf_printf(sb, "\n");
233 	}
234 
235 	return;
236 }
237 
238 int
239 gv_sd_to_plex(struct gv_plex *p, struct gv_sd *s, int check)
240 {
241 	struct gv_sd *s2;
242 
243 	g_topology_assert();
244 
245 	/* If this subdisk was already given to this plex, do nothing. */
246 	if (s->plex_sc == p)
247 		return (0);
248 
249 	/* Find the correct plex offset for this subdisk, if needed. */
250 	if (s->plex_offset == -1) {
251 		if (p->sdcount) {
252 			LIST_FOREACH(s2, &p->subdisks, in_plex) {
253 				if (gv_is_striped(p))
254 					s->plex_offset = p->sdcount *
255 					    p->stripesize;
256 				else
257 					s->plex_offset = s2->plex_offset +
258 					    s2->size;
259 			}
260 		} else
261 			s->plex_offset = 0;
262 	}
263 
264 	p->sdcount++;
265 
266 	/* Adjust the size of our plex. */
267 	switch (p->org) {
268 	case GV_PLEX_CONCAT:
269 	case GV_PLEX_STRIPED:
270 		p->size += s->size;
271 		break;
272 
273 	case GV_PLEX_RAID5:
274 		p->size = (p->sdcount - 1) * s->size;
275 		break;
276 
277 	default:
278 		break;
279 	}
280 
281 	/* There are no subdisks for this plex yet, just insert it. */
282 	if (LIST_EMPTY(&p->subdisks)) {
283 		LIST_INSERT_HEAD(&p->subdisks, s, in_plex);
284 
285 	/* Insert in correct order, depending on plex_offset. */
286 	} else {
287 		LIST_FOREACH(s2, &p->subdisks, in_plex) {
288 			if (s->plex_offset < s2->plex_offset) {
289 				LIST_INSERT_BEFORE(s2, s, in_plex);
290 				break;
291 			} else if (LIST_NEXT(s2, in_plex) == NULL) {
292 				LIST_INSERT_AFTER(s2, s, in_plex);
293 				break;
294 			}
295 		}
296 	}
297 
298 	s->plex_sc = p;
299 
300 	return (0);
301 }
302 
303 void
304 gv_update_vol_size(struct gv_volume *v, off_t size)
305 {
306 	struct g_geom *gp;
307 	struct g_provider *pp;
308 
309 	if (v == NULL)
310 		return;
311 
312 	gp = v->geom;
313 	if (gp == NULL)
314 		return;
315 
316 	LIST_FOREACH(pp, &gp->provider, provider) {
317 		pp->mediasize = size;
318 	}
319 
320 	v->size = size;
321 }
322 
323 void
324 gv_update_plex_config(struct gv_plex *p)
325 {
326 	struct gv_sd *s, *s2;
327 	off_t remainder;
328 	int required_sds, state;
329 
330 	KASSERT(p != NULL, ("gv_update_plex_config: NULL p"));
331 
332 	/* This is what we want the plex to be. */
333 	state = GV_PLEX_UP;
334 
335 	/* The plex was added to an already running volume. */
336 	if (p->flags & GV_PLEX_ADDED)
337 		state = GV_PLEX_DOWN;
338 
339 	switch (p->org) {
340 	case GV_PLEX_STRIPED:
341 		required_sds = 2;
342 		break;
343 	case GV_PLEX_RAID5:
344 		required_sds = 3;
345 		break;
346 	case GV_PLEX_CONCAT:
347 	default:
348 		required_sds = 0;
349 		break;
350 	}
351 
352 	if (required_sds) {
353 		if (p->sdcount < required_sds) {
354 			state = GV_PLEX_DOWN;
355 		}
356 
357 		/*
358 		 * The subdisks in striped plexes must all have the same size.
359 		 */
360 		s = LIST_FIRST(&p->subdisks);
361 		LIST_FOREACH(s2, &p->subdisks, in_plex) {
362 			if (s->size != s2->size) {
363 				printf("geom_vinum: subdisk size mismatch "
364 				    "%s (%jd) <> %s (%jd)\n", s->name, s->size,
365 				    s2->name, s2->size);
366 				state = GV_PLEX_DOWN;
367 			}
368 		}
369 
370 		/* Trim subdisk sizes so that they match the stripe size. */
371 		LIST_FOREACH(s, &p->subdisks, in_plex) {
372 			remainder = s->size % p->stripesize;
373 			if (remainder) {
374 				printf("gvinum: size of sd %s is not a "
375 				    "multiple of plex stripesize, taking off "
376 				    "%jd bytes\n", s->name,
377 				    (intmax_t)remainder);
378 				gv_adjust_freespace(s, remainder);
379 			}
380 		}
381 	}
382 
383 	/* Adjust the size of our plex. */
384 	if (p->sdcount > 0) {
385 		p->size = 0;
386 		switch (p->org) {
387 		case GV_PLEX_CONCAT:
388 			LIST_FOREACH(s, &p->subdisks, in_plex)
389 				p->size += s->size;
390 			break;
391 
392 		case GV_PLEX_STRIPED:
393 			s = LIST_FIRST(&p->subdisks);
394 			p->size = p->sdcount * s->size;
395 			break;
396 
397 		case GV_PLEX_RAID5:
398 			s = LIST_FIRST(&p->subdisks);
399 			p->size = (p->sdcount - 1) * s->size;
400 			break;
401 
402 		default:
403 			break;
404 		}
405 	}
406 
407 	if (p->sdcount == 0)
408 		state = GV_PLEX_DOWN;
409 	else if ((p->flags & GV_PLEX_ADDED) ||
410 	    ((p->org == GV_PLEX_RAID5) && (p->flags & GV_PLEX_NEWBORN))) {
411 		LIST_FOREACH(s, &p->subdisks, in_plex)
412 			s->state = GV_SD_STALE;
413 		p->flags &= ~GV_PLEX_ADDED;
414 		p->flags &= ~GV_PLEX_NEWBORN;
415 		p->state = GV_PLEX_DOWN;
416 	}
417 }
418 
419 /*
420  * Give a subdisk to a drive, check and adjust several parameters, adjust
421  * freelist.
422  */
423 int
424 gv_sd_to_drive(struct gv_softc *sc, struct gv_drive *d, struct gv_sd *s,
425     char *errstr, int errlen)
426 {
427 	struct gv_sd *s2;
428 	struct gv_freelist *fl, *fl2;
429 	off_t tmp;
430 	int i;
431 
432 	g_topology_assert();
433 
434 	fl2 = NULL;
435 
436 	KASSERT(sc != NULL, ("gv_sd_to_drive: NULL softc"));
437 	KASSERT(d != NULL, ("gv_sd_to_drive: NULL drive"));
438 	KASSERT(s != NULL, ("gv_sd_to_drive: NULL subdisk"));
439 	KASSERT(errstr != NULL, ("gv_sd_to_drive: NULL errstr"));
440 	KASSERT(errlen >= ERRBUFSIZ, ("gv_sd_to_drive: short errlen (%d)",
441 	    errlen));
442 
443 	/* Check if this subdisk was already given to this drive. */
444 	if (s->drive_sc == d)
445 		return (0);
446 
447 	/* Preliminary checks. */
448 	if (s->size > d->avail || d->freelist_entries == 0) {
449 		snprintf(errstr, errlen, "not enough space on '%s' for '%s'",
450 		    d->name, s->name);
451 		return (-1);
452 	}
453 
454 	/* No size given, autosize it. */
455 	if (s->size == -1) {
456 		/* Find the largest available slot. */
457 		LIST_FOREACH(fl, &d->freelist, freelist) {
458 			if (fl->size >= s->size) {
459 				s->size = fl->size;
460 				s->drive_offset = fl->offset;
461 				fl2 = fl;
462 			}
463 		}
464 
465 		/* No good slot found? */
466 		if (s->size == -1) {
467 			snprintf(errstr, errlen, "couldn't autosize '%s' on "
468 			    "'%s'", s->name, d->name);
469 			return (-1);
470 		}
471 
472 	/*
473 	 * Check if we have a free slot that's large enough for the given size.
474 	 */
475 	} else {
476 		i = 0;
477 		LIST_FOREACH(fl, &d->freelist, freelist) {
478 			/* Yes, this subdisk fits. */
479 			if (fl->size >= s->size) {
480 				i++;
481 				/* Assign drive offset, if not given. */
482 				if (s->drive_offset == -1)
483 					s->drive_offset = fl->offset;
484 				fl2 = fl;
485 				break;
486 			}
487 		}
488 
489 		/* Couldn't find a good free slot. */
490 		if (i == 0) {
491 			snprintf(errstr, errlen, "free slots to small for '%s' "
492 			    "on '%s'", s->name, d->name);
493 			return (-1);
494 		}
495 	}
496 
497 	/* No drive offset given, try to calculate it. */
498 	if (s->drive_offset == -1) {
499 
500 		/* Add offsets and sizes from other subdisks on this drive. */
501 		LIST_FOREACH(s2, &d->subdisks, from_drive) {
502 			s->drive_offset = s2->drive_offset + s2->size;
503 		}
504 
505 		/*
506 		 * If there are no other subdisks yet, then set the default
507 		 * offset to GV_DATA_START.
508 		 */
509 		if (s->drive_offset == -1)
510 			s->drive_offset = GV_DATA_START;
511 
512 	/* Check if we have a free slot at the given drive offset. */
513 	} else {
514 		i = 0;
515 		LIST_FOREACH(fl, &d->freelist, freelist) {
516 			/* Yes, this subdisk fits. */
517 			if ((fl->offset <= s->drive_offset) &&
518 			    (fl->offset + fl->size >=
519 			    s->drive_offset + s->size)) {
520 				i++;
521 				fl2 = fl;
522 				break;
523 			}
524 		}
525 
526 		/* Couldn't find a good free slot. */
527 		if (i == 0) {
528 			snprintf(errstr, errlen, "given drive_offset for '%s' "
529 			    "won't fit on '%s'", s->name, d->name);
530 			return (-1);
531 		}
532 	}
533 
534 	/*
535 	 * Now that all parameters are checked and set up, we can give the
536 	 * subdisk to the drive and adjust the freelist.
537 	 */
538 
539 	/* First, adjust the freelist. */
540 	LIST_FOREACH(fl, &d->freelist, freelist) {
541 
542 		/* This is the free slot that we have found before. */
543 		if (fl == fl2) {
544 
545 			/*
546 			 * The subdisk starts at the beginning of the free
547 			 * slot.
548 			 */
549 			if (fl->offset == s->drive_offset) {
550 				fl->offset += s->size;
551 				fl->size -= s->size;
552 
553 				/*
554 				 * The subdisk uses the whole slot, so remove
555 				 * it.
556 				 */
557 				if (fl->size == 0) {
558 					d->freelist_entries--;
559 					LIST_REMOVE(fl, freelist);
560 				}
561 			/*
562 			 * The subdisk does not start at the beginning of the
563 			 * free slot.
564 			 */
565 			} else {
566 				tmp = fl->offset + fl->size;
567 				fl->size = s->drive_offset - fl->offset;
568 
569 				/*
570 				 * The subdisk didn't use the complete rest of
571 				 * the free slot, so we need to split it.
572 				 */
573 				if (s->drive_offset + s->size != tmp) {
574 					fl2 = g_malloc(sizeof(*fl2),
575 					    M_WAITOK | M_ZERO);
576 					fl2->offset = s->drive_offset + s->size;
577 					fl2->size = tmp - fl2->offset;
578 					LIST_INSERT_AFTER(fl, fl2, freelist);
579 					d->freelist_entries++;
580 				}
581 			}
582 			break;
583 		}
584 	}
585 
586 	/*
587 	 * This is the first subdisk on this drive, just insert it into the
588 	 * list.
589 	 */
590 	if (LIST_EMPTY(&d->subdisks)) {
591 		LIST_INSERT_HEAD(&d->subdisks, s, from_drive);
592 
593 	/* There are other subdisks, so insert this one in correct order. */
594 	} else {
595 		LIST_FOREACH(s2, &d->subdisks, from_drive) {
596 			if (s->drive_offset < s2->drive_offset) {
597 				LIST_INSERT_BEFORE(s2, s, from_drive);
598 				break;
599 			} else if (LIST_NEXT(s2, from_drive) == NULL) {
600 				LIST_INSERT_AFTER(s2, s, from_drive);
601 				break;
602 			}
603 		}
604 	}
605 
606 	d->sdcount++;
607 	d->avail -= s->size;
608 
609 	/* Link back from the subdisk to this drive. */
610 	s->drive_sc = d;
611 
612 	return (0);
613 }
614 
615 void
616 gv_adjust_freespace(struct gv_sd *s, off_t remainder)
617 {
618 	struct gv_drive *d;
619 	struct gv_freelist *fl, *fl2;
620 
621 	KASSERT(s != NULL, ("gv_adjust_freespace: NULL s"));
622 	d = s->drive_sc;
623 	KASSERT(d != NULL, ("gv_adjust_freespace: NULL d"));
624 
625 	/* First, find the free slot that's immediately after this subdisk. */
626 	fl = NULL;
627 	LIST_FOREACH(fl, &d->freelist, freelist) {
628 		if (fl->offset == s->drive_offset + s->size)
629 			break;
630 	}
631 
632 	/* If there is no free slot behind this subdisk, so create one. */
633 	if (fl == NULL) {
634 
635 		fl = g_malloc(sizeof(*fl), M_WAITOK | M_ZERO);
636 		fl->size = remainder;
637 		fl->offset = s->drive_offset + s->size - remainder;
638 
639 		if (d->freelist_entries == 0) {
640 			LIST_INSERT_HEAD(&d->freelist, fl, freelist);
641 		} else {
642 			LIST_FOREACH(fl2, &d->freelist, freelist) {
643 				if (fl->offset < fl2->offset) {
644 					LIST_INSERT_BEFORE(fl2, fl, freelist);
645 					break;
646 				} else if (LIST_NEXT(fl2, freelist) == NULL) {
647 					LIST_INSERT_AFTER(fl2, fl, freelist);
648 					break;
649 				}
650 			}
651 		}
652 
653 		d->freelist_entries++;
654 
655 	/* Expand the free slot we just found. */
656 	} else {
657 		fl->offset -= remainder;
658 		fl->size += remainder;
659 	}
660 
661 	s->size -= remainder;
662 	d->avail += remainder;
663 }
664 
665 /* Check if the given plex is a striped one. */
666 int
667 gv_is_striped(struct gv_plex *p)
668 {
669 	KASSERT(p != NULL, ("gv_is_striped: NULL p"));
670 	switch(p->org) {
671 	case GV_PLEX_STRIPED:
672 	case GV_PLEX_RAID5:
673 		return (1);
674 	default:
675 		return (0);
676 	}
677 }
678 
679 /* Find a volume by name. */
680 struct gv_volume *
681 gv_find_vol(struct gv_softc *sc, char *name)
682 {
683 	struct gv_volume *v;
684 
685 	LIST_FOREACH(v, &sc->volumes, volume) {
686 		if (!strncmp(v->name, name, GV_MAXVOLNAME))
687 			return (v);
688 	}
689 
690 	return (NULL);
691 }
692 
693 /* Find a plex by name. */
694 struct gv_plex *
695 gv_find_plex(struct gv_softc *sc, char *name)
696 {
697 	struct gv_plex *p;
698 
699 	LIST_FOREACH(p, &sc->plexes, plex) {
700 		if (!strncmp(p->name, name, GV_MAXPLEXNAME))
701 			return (p);
702 	}
703 
704 	return (NULL);
705 }
706 
707 /* Find a subdisk by name. */
708 struct gv_sd *
709 gv_find_sd(struct gv_softc *sc, char *name)
710 {
711 	struct gv_sd *s;
712 
713 	LIST_FOREACH(s, &sc->subdisks, sd) {
714 		if (!strncmp(s->name, name, GV_MAXSDNAME))
715 			return (s);
716 	}
717 
718 	return (NULL);
719 }
720 
721 /* Find a drive by name. */
722 struct gv_drive *
723 gv_find_drive(struct gv_softc *sc, char *name)
724 {
725 	struct gv_drive *d;
726 
727 	LIST_FOREACH(d, &sc->drives, drive) {
728 		if (!strncmp(d->name, name, GV_MAXDRIVENAME))
729 			return (d);
730 	}
731 
732 	return (NULL);
733 }
734 
735 /* Check if any consumer of the given geom is open. */
736 int
737 gv_is_open(struct g_geom *gp)
738 {
739 	struct g_consumer *cp;
740 
741 	if (gp == NULL)
742 		return (0);
743 
744 	LIST_FOREACH(cp, &gp->consumer, consumer) {
745 		if (cp->acr || cp->acw || cp->ace)
746 			return (1);
747 	}
748 
749 	return (0);
750 }
751 
752 /* Return the type of object identified by string 'name'. */
753 int
754 gv_object_type(struct gv_softc *sc, char *name)
755 {
756 	struct gv_drive *d;
757 	struct gv_plex *p;
758 	struct gv_sd *s;
759 	struct gv_volume *v;
760 
761 	LIST_FOREACH(v, &sc->volumes, volume) {
762 		if (!strncmp(v->name, name, GV_MAXVOLNAME))
763 			return (GV_TYPE_VOL);
764 	}
765 
766 	LIST_FOREACH(p, &sc->plexes, plex) {
767 		if (!strncmp(p->name, name, GV_MAXPLEXNAME))
768 			return (GV_TYPE_PLEX);
769 	}
770 
771 	LIST_FOREACH(s, &sc->subdisks, sd) {
772 		if (!strncmp(s->name, name, GV_MAXSDNAME))
773 			return (GV_TYPE_SD);
774 	}
775 
776 	LIST_FOREACH(d, &sc->drives, drive) {
777 		if (!strncmp(d->name, name, GV_MAXDRIVENAME))
778 			return (GV_TYPE_DRIVE);
779 	}
780 
781 	return (-1);
782 }
783 
784 void
785 gv_kill_drive_thread(struct gv_drive *d)
786 {
787 	if (d->flags & GV_DRIVE_THREAD_ACTIVE) {
788 		d->flags |= GV_DRIVE_THREAD_DIE;
789 		wakeup(d);
790 		while (!(d->flags & GV_DRIVE_THREAD_DEAD))
791 			tsleep(d, PRIBIO, "gv_die", hz);
792 		d->flags &= ~GV_DRIVE_THREAD_ACTIVE;
793 		d->flags &= ~GV_DRIVE_THREAD_DIE;
794 		d->flags &= ~GV_DRIVE_THREAD_DEAD;
795 		mtx_destroy(&d->bqueue_mtx);
796 	}
797 }
798 
799 void
800 gv_kill_plex_thread(struct gv_plex *p)
801 {
802 	if (p->flags & GV_PLEX_THREAD_ACTIVE) {
803 		p->flags |= GV_PLEX_THREAD_DIE;
804 		wakeup(p);
805 		while (!(p->flags & GV_PLEX_THREAD_DEAD))
806 			tsleep(p, PRIBIO, "gv_die", hz);
807 		p->flags &= ~GV_PLEX_THREAD_ACTIVE;
808 		p->flags &= ~GV_PLEX_THREAD_DIE;
809 		p->flags &= ~GV_PLEX_THREAD_DEAD;
810 		mtx_destroy(&p->bqueue_mtx);
811 	}
812 }
813 
814 void
815 gv_kill_vol_thread(struct gv_volume *v)
816 {
817 	if (v->flags & GV_VOL_THREAD_ACTIVE) {
818 		v->flags |= GV_VOL_THREAD_DIE;
819 		wakeup(v);
820 		while (!(v->flags & GV_VOL_THREAD_DEAD))
821 			tsleep(v, PRIBIO, "gv_die", hz);
822 		v->flags &= ~GV_VOL_THREAD_ACTIVE;
823 		v->flags &= ~GV_VOL_THREAD_DIE;
824 		v->flags &= ~GV_VOL_THREAD_DEAD;
825 		mtx_destroy(&v->bqueue_mtx);
826 	}
827 }
828