xref: /freebsd/sys/geom/vinum/geom_vinum_subr.c (revision f7c4bd95ba735bd6a5454b4953945a99cefbb80c)
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 static off_t gv_plex_smallest_sd(struct gv_plex *, off_t);
58 
59 /* Find the VINUM class and it's associated geom. */
60 struct g_geom *
61 find_vinum_geom(void)
62 {
63 	struct g_class *mp;
64 	struct g_geom *gp;
65 
66 	g_topology_assert();
67 
68 	gp = NULL;
69 
70 	LIST_FOREACH(mp, &g_classes, class) {
71 		if (!strcmp(mp->name, "VINUM")) {
72 			gp = LIST_FIRST(&mp->geom);
73 			break;
74 		}
75 	}
76 
77 	return (gp);
78 }
79 
80 /*
81  * Parse the vinum config provided in *buf and store it in *gp's softc.
82  * If parameter 'merge' is non-zero, then the given config is merged into
83  * *gp.
84  */
85 void
86 gv_parse_config(struct gv_softc *sc, u_char *buf, int merge)
87 {
88 	char *aptr, *bptr, *cptr;
89 	struct gv_volume *v, *v2;
90 	struct gv_plex *p, *p2;
91 	struct gv_sd *s, *s2;
92 	int tokens;
93 	char *token[GV_MAXARGS];
94 
95 	g_topology_assert();
96 
97 	KASSERT(sc != NULL, ("gv_parse_config: NULL softc"));
98 
99 	/* Until the end of the string *buf. */
100 	for (aptr = buf; *aptr != '\0'; aptr = bptr) {
101 		bptr = aptr;
102 		cptr = aptr;
103 
104 		/* Seperate input lines. */
105 		while (*bptr != '\n')
106 			bptr++;
107 		*bptr = '\0';
108 		bptr++;
109 
110 		tokens = gv_tokenize(cptr, token, GV_MAXARGS);
111 
112 		if (tokens > 0) {
113 			if (!strcmp(token[0], "volume")) {
114 				v = gv_new_volume(tokens, token);
115 				if (v == NULL) {
116 					printf("geom_vinum: failed volume\n");
117 					break;
118 				}
119 
120 				if (merge) {
121 					v2 = gv_find_vol(sc, v->name);
122 					if (v2 != NULL) {
123 						g_free(v);
124 						continue;
125 					}
126 				}
127 
128 				v->vinumconf = sc;
129 				LIST_INIT(&v->plexes);
130 				LIST_INSERT_HEAD(&sc->volumes, v, volume);
131 
132 			} else if (!strcmp(token[0], "plex")) {
133 				p = gv_new_plex(tokens, token);
134 				if (p == NULL) {
135 					printf("geom_vinum: failed plex\n");
136 					break;
137 				}
138 
139 				if (merge) {
140 					p2 = gv_find_plex(sc, p->name);
141 					if (p2 != NULL) {
142 						g_free(p);
143 						continue;
144 					}
145 				}
146 
147 				p->vinumconf = sc;
148 				LIST_INIT(&p->subdisks);
149 				LIST_INSERT_HEAD(&sc->plexes, p, plex);
150 
151 			} else if (!strcmp(token[0], "sd")) {
152 				s = gv_new_sd(tokens, token);
153 
154 				if (s == NULL) {
155 					printf("geom_vinum: failed subdisk\n");
156 					break;
157 				}
158 
159 				if (merge) {
160 					s2 = gv_find_sd(sc, s->name);
161 					if (s2 != NULL) {
162 						g_free(s);
163 						continue;
164 					}
165 				}
166 
167 				s->vinumconf = sc;
168 				LIST_INSERT_HEAD(&sc->subdisks, s, sd);
169 			}
170 		}
171 	}
172 }
173 
174 /*
175  * Format the vinum configuration properly.  If ondisk is non-zero then the
176  * configuration is intended to be written to disk later.
177  */
178 void
179 gv_format_config(struct gv_softc *sc, struct sbuf *sb, int ondisk, char *prefix)
180 {
181 	struct gv_drive *d;
182 	struct gv_sd *s;
183 	struct gv_plex *p;
184 	struct gv_volume *v;
185 
186 	g_topology_assert();
187 
188 	/*
189 	 * We don't need the drive configuration if we're not writing the
190 	 * config to disk.
191 	 */
192 	if (!ondisk) {
193 		LIST_FOREACH(d, &sc->drives, drive) {
194 			sbuf_printf(sb, "%sdrive %s device /dev/%s\n", prefix,
195 			    d->name, d->device);
196 		}
197 	}
198 
199 	LIST_FOREACH(v, &sc->volumes, volume) {
200 		if (!ondisk)
201 			sbuf_printf(sb, "%s", prefix);
202 		sbuf_printf(sb, "volume %s", v->name);
203 		if (ondisk)
204 			sbuf_printf(sb, " state %s", gv_volstate(v->state));
205 		sbuf_printf(sb, "\n");
206 	}
207 
208 	LIST_FOREACH(p, &sc->plexes, plex) {
209 		if (!ondisk)
210 			sbuf_printf(sb, "%s", prefix);
211 		sbuf_printf(sb, "plex name %s org %s ", p->name,
212 		    gv_plexorg(p->org));
213 		if (gv_is_striped(p))
214 			sbuf_printf(sb, "%ds ", p->stripesize / 512);
215 		if (p->vol_sc != NULL)
216 			sbuf_printf(sb, "vol %s", p->volume);
217 		if (ondisk)
218 			sbuf_printf(sb, " state %s", gv_plexstate(p->state));
219 		sbuf_printf(sb, "\n");
220 	}
221 
222 	LIST_FOREACH(s, &sc->subdisks, sd) {
223 		if (!ondisk)
224 			sbuf_printf(sb, "%s", prefix);
225 		sbuf_printf(sb, "sd name %s drive %s len %jds driveoffset "
226 		    "%jds", s->name, s->drive, s->size / 512,
227 		    s->drive_offset / 512);
228 		if (s->plex_sc != NULL) {
229 			sbuf_printf(sb, " plex %s plexoffset %jds", s->plex,
230 			    s->plex_offset / 512);
231 		}
232 		if (ondisk)
233 			sbuf_printf(sb, " state %s", gv_sdstate(s->state));
234 		sbuf_printf(sb, "\n");
235 	}
236 
237 	return;
238 }
239 
240 static off_t
241 gv_plex_smallest_sd(struct gv_plex *p, off_t smallest)
242 {
243 	struct gv_sd *s;
244 
245 	KASSERT(p != NULL, ("gv_plex_smallest_sd: NULL p"));
246 
247 	LIST_FOREACH(s, &p->subdisks, in_plex) {
248 		if (s->size < smallest)
249 			smallest = s->size;
250 	}
251 	return (smallest);
252 }
253 
254 int
255 gv_sd_to_plex(struct gv_plex *p, struct gv_sd *s, int check)
256 {
257 	struct gv_sd *s2;
258 
259 	g_topology_assert();
260 
261 	/* If this subdisk was already given to this plex, do nothing. */
262 	if (s->plex_sc == p)
263 		return (0);
264 
265 	/* Check correct size of this subdisk. */
266 	s2 = LIST_FIRST(&p->subdisks);
267 	if (s2 != NULL && gv_is_striped(p) && (s2->size != s->size)) {
268 		printf("GEOM_VINUM: need equal sized subdisks for "
269 		    "this plex organisation - %s (%jd) <-> %s (%jd)\n",
270 		    s2->name, s2->size, s->name, s->size);
271 		return (-1);
272 	}
273 
274 	/* Find the correct plex offset for this subdisk, if needed. */
275 	if (s->plex_offset == -1) {
276 		if (p->sdcount) {
277 			LIST_FOREACH(s2, &p->subdisks, in_plex) {
278 				if (gv_is_striped(p))
279 					s->plex_offset = p->sdcount *
280 					    p->stripesize;
281 				else
282 					s->plex_offset = s2->plex_offset +
283 					    s2->size;
284 			}
285 		} else
286 			s->plex_offset = 0;
287 	}
288 
289 	p->sdcount++;
290 
291 	/* Adjust the size of our plex. */
292 	switch (p->org) {
293 	case GV_PLEX_CONCAT:
294 	case GV_PLEX_STRIPED:
295 		p->size += s->size;
296 		break;
297 
298 	case GV_PLEX_RAID5:
299 		p->size = (p->sdcount - 1) * gv_plex_smallest_sd(p, s->size);
300 		break;
301 
302 	default:
303 		break;
304 	}
305 
306 	/* There are no subdisks for this plex yet, just insert it. */
307 	if (LIST_EMPTY(&p->subdisks)) {
308 		LIST_INSERT_HEAD(&p->subdisks, s, in_plex);
309 
310 	/* Insert in correct order, depending on plex_offset. */
311 	} else {
312 		LIST_FOREACH(s2, &p->subdisks, in_plex) {
313 			if (s->plex_offset < s2->plex_offset) {
314 				LIST_INSERT_BEFORE(s2, s, in_plex);
315 				break;
316 			} else if (LIST_NEXT(s2, in_plex) == NULL) {
317 				LIST_INSERT_AFTER(s2, s, in_plex);
318 				break;
319 			}
320 		}
321 	}
322 
323 	s->plex_sc = p;
324 
325 	return (0);
326 }
327 
328 void
329 gv_update_vol_size(struct gv_volume *v, off_t size)
330 {
331 	struct g_geom *gp;
332 	struct g_provider *pp;
333 
334 	if (v == NULL)
335 		return;
336 
337 	gp = v->geom;
338 	if (gp == NULL)
339 		return;
340 
341 	LIST_FOREACH(pp, &gp->provider, provider) {
342 		pp->mediasize = size;
343 	}
344 
345 	v->size = size;
346 }
347 
348 /* Calculates the plex size. */
349 off_t
350 gv_plex_size(struct gv_plex *p)
351 {
352 	struct gv_sd *s;
353 	off_t size;
354 
355 	KASSERT(p != NULL, ("gv_plex_size: NULL p"));
356 
357 	if (p->sdcount == 0)
358 		return (0);
359 
360 	/* Adjust the size of our plex. */
361 	size = 0;
362 	switch (p->org) {
363 	case GV_PLEX_CONCAT:
364 		LIST_FOREACH(s, &p->subdisks, in_plex)
365 			size += s->size;
366 		break;
367 	case GV_PLEX_STRIPED:
368 		s = LIST_FIRST(&p->subdisks);
369 		size = p->sdcount * s->size;
370 		break;
371 	case GV_PLEX_RAID5:
372 		s = LIST_FIRST(&p->subdisks);
373 		size = (p->sdcount - 1) * s->size;
374 		break;
375 	}
376 
377 	return (size);
378 }
379 
380 /* Returns the size of a volume. */
381 off_t
382 gv_vol_size(struct gv_volume *v)
383 {
384 	struct gv_plex *p;
385 	off_t minplexsize;
386 
387 	KASSERT(v != NULL, ("gv_vol_size: NULL v"));
388 
389 	p = LIST_FIRST(&v->plexes);
390 	if (p == NULL)
391 		return (0);
392 
393 	minplexsize = p->size;
394 	LIST_FOREACH(p, &v->plexes, plex) {
395 		if (p->size < minplexsize) {
396 			minplexsize = p->size;
397 		}
398 	}
399 	return (minplexsize);
400 }
401 
402 void
403 gv_update_plex_config(struct gv_plex *p)
404 {
405 	struct gv_sd *s, *s2;
406 	off_t remainder;
407 	int required_sds, state;
408 
409 	KASSERT(p != NULL, ("gv_update_plex_config: NULL p"));
410 
411 	/* This is what we want the plex to be. */
412 	state = GV_PLEX_UP;
413 
414 	/* The plex was added to an already running volume. */
415 	if (p->flags & GV_PLEX_ADDED)
416 		state = GV_PLEX_DOWN;
417 
418 	switch (p->org) {
419 	case GV_PLEX_STRIPED:
420 		required_sds = 2;
421 		break;
422 	case GV_PLEX_RAID5:
423 		required_sds = 3;
424 		break;
425 	case GV_PLEX_CONCAT:
426 	default:
427 		required_sds = 0;
428 		break;
429 	}
430 
431 	if (required_sds) {
432 		if (p->sdcount < required_sds) {
433 			state = GV_PLEX_DOWN;
434 		}
435 
436 		/*
437 		 * The subdisks in striped plexes must all have the same size.
438 		 */
439 		s = LIST_FIRST(&p->subdisks);
440 		LIST_FOREACH(s2, &p->subdisks, in_plex) {
441 			if (s->size != s2->size) {
442 				printf("geom_vinum: subdisk size mismatch "
443 				    "%s (%jd) <> %s (%jd)\n", s->name, s->size,
444 				    s2->name, s2->size);
445 				state = GV_PLEX_DOWN;
446 			}
447 		}
448 
449 		/* Trim subdisk sizes so that they match the stripe size. */
450 		LIST_FOREACH(s, &p->subdisks, in_plex) {
451 			remainder = s->size % p->stripesize;
452 			if (remainder) {
453 				printf("gvinum: size of sd %s is not a "
454 				    "multiple of plex stripesize, taking off "
455 				    "%jd bytes\n", s->name,
456 				    (intmax_t)remainder);
457 				gv_adjust_freespace(s, remainder);
458 			}
459 		}
460 	}
461 
462 	/* Adjust the size of our plex. */
463 	if (p->sdcount > 0) {
464 		p->size = 0;
465 		switch (p->org) {
466 		case GV_PLEX_CONCAT:
467 			LIST_FOREACH(s, &p->subdisks, in_plex)
468 				p->size += s->size;
469 			break;
470 
471 		case GV_PLEX_STRIPED:
472 			s = LIST_FIRST(&p->subdisks);
473 			p->size = p->sdcount * s->size;
474 			break;
475 
476 		case GV_PLEX_RAID5:
477 			s = LIST_FIRST(&p->subdisks);
478 			p->size = (p->sdcount - 1) * s->size;
479 			break;
480 
481 		default:
482 			break;
483 		}
484 	}
485 
486 	if (p->sdcount == 0)
487 		state = GV_PLEX_DOWN;
488 	else if ((p->flags & GV_PLEX_ADDED) ||
489 	    ((p->org == GV_PLEX_RAID5) && (p->flags & GV_PLEX_NEWBORN))) {
490 		LIST_FOREACH(s, &p->subdisks, in_plex)
491 			s->state = GV_SD_STALE;
492 		p->flags &= ~GV_PLEX_ADDED;
493 		p->flags &= ~GV_PLEX_NEWBORN;
494 		state = GV_PLEX_DOWN;
495 	}
496 	p->state = state;
497 }
498 
499 /*
500  * Give a subdisk to a drive, check and adjust several parameters, adjust
501  * freelist.
502  */
503 int
504 gv_sd_to_drive(struct gv_softc *sc, struct gv_drive *d, struct gv_sd *s,
505     char *errstr, int errlen)
506 {
507 	struct gv_sd *s2;
508 	struct gv_freelist *fl, *fl2;
509 	off_t tmp;
510 	int i;
511 
512 	g_topology_assert();
513 
514 	fl2 = NULL;
515 
516 	KASSERT(sc != NULL, ("gv_sd_to_drive: NULL softc"));
517 	KASSERT(d != NULL, ("gv_sd_to_drive: NULL drive"));
518 	KASSERT(s != NULL, ("gv_sd_to_drive: NULL subdisk"));
519 	KASSERT(errstr != NULL, ("gv_sd_to_drive: NULL errstr"));
520 	KASSERT(errlen >= ERRBUFSIZ, ("gv_sd_to_drive: short errlen (%d)",
521 	    errlen));
522 
523 	/* Check if this subdisk was already given to this drive. */
524 	if (s->drive_sc == d)
525 		return (0);
526 
527 	/* Preliminary checks. */
528 	if (s->size > d->avail || d->freelist_entries == 0) {
529 		snprintf(errstr, errlen, "not enough space on '%s' for '%s'",
530 		    d->name, s->name);
531 		return (-1);
532 	}
533 
534 	/* No size given, autosize it. */
535 	if (s->size == -1) {
536 		/* Find the largest available slot. */
537 		LIST_FOREACH(fl, &d->freelist, freelist) {
538 			if (fl->size >= s->size) {
539 				s->size = fl->size;
540 				s->drive_offset = fl->offset;
541 				fl2 = fl;
542 			}
543 		}
544 
545 		/* No good slot found? */
546 		if (s->size == -1) {
547 			snprintf(errstr, errlen, "couldn't autosize '%s' on "
548 			    "'%s'", s->name, d->name);
549 			return (-1);
550 		}
551 
552 	/*
553 	 * Check if we have a free slot that's large enough for the given size.
554 	 */
555 	} else {
556 		i = 0;
557 		LIST_FOREACH(fl, &d->freelist, freelist) {
558 			/* Yes, this subdisk fits. */
559 			if (fl->size >= s->size) {
560 				i++;
561 				/* Assign drive offset, if not given. */
562 				if (s->drive_offset == -1)
563 					s->drive_offset = fl->offset;
564 				fl2 = fl;
565 				break;
566 			}
567 		}
568 
569 		/* Couldn't find a good free slot. */
570 		if (i == 0) {
571 			snprintf(errstr, errlen, "free slots to small for '%s' "
572 			    "on '%s'", s->name, d->name);
573 			return (-1);
574 		}
575 	}
576 
577 	/* No drive offset given, try to calculate it. */
578 	if (s->drive_offset == -1) {
579 
580 		/* Add offsets and sizes from other subdisks on this drive. */
581 		LIST_FOREACH(s2, &d->subdisks, from_drive) {
582 			s->drive_offset = s2->drive_offset + s2->size;
583 		}
584 
585 		/*
586 		 * If there are no other subdisks yet, then set the default
587 		 * offset to GV_DATA_START.
588 		 */
589 		if (s->drive_offset == -1)
590 			s->drive_offset = GV_DATA_START;
591 
592 	/* Check if we have a free slot at the given drive offset. */
593 	} else {
594 		i = 0;
595 		LIST_FOREACH(fl, &d->freelist, freelist) {
596 			/* Yes, this subdisk fits. */
597 			if ((fl->offset <= s->drive_offset) &&
598 			    (fl->offset + fl->size >=
599 			    s->drive_offset + s->size)) {
600 				i++;
601 				fl2 = fl;
602 				break;
603 			}
604 		}
605 
606 		/* Couldn't find a good free slot. */
607 		if (i == 0) {
608 			snprintf(errstr, errlen, "given drive_offset for '%s' "
609 			    "won't fit on '%s'", s->name, d->name);
610 			return (-1);
611 		}
612 	}
613 
614 	/*
615 	 * Now that all parameters are checked and set up, we can give the
616 	 * subdisk to the drive and adjust the freelist.
617 	 */
618 
619 	/* First, adjust the freelist. */
620 	LIST_FOREACH(fl, &d->freelist, freelist) {
621 
622 		/* This is the free slot that we have found before. */
623 		if (fl == fl2) {
624 
625 			/*
626 			 * The subdisk starts at the beginning of the free
627 			 * slot.
628 			 */
629 			if (fl->offset == s->drive_offset) {
630 				fl->offset += s->size;
631 				fl->size -= s->size;
632 
633 				/*
634 				 * The subdisk uses the whole slot, so remove
635 				 * it.
636 				 */
637 				if (fl->size == 0) {
638 					d->freelist_entries--;
639 					LIST_REMOVE(fl, freelist);
640 				}
641 			/*
642 			 * The subdisk does not start at the beginning of the
643 			 * free slot.
644 			 */
645 			} else {
646 				tmp = fl->offset + fl->size;
647 				fl->size = s->drive_offset - fl->offset;
648 
649 				/*
650 				 * The subdisk didn't use the complete rest of
651 				 * the free slot, so we need to split it.
652 				 */
653 				if (s->drive_offset + s->size != tmp) {
654 					fl2 = g_malloc(sizeof(*fl2),
655 					    M_WAITOK | M_ZERO);
656 					fl2->offset = s->drive_offset + s->size;
657 					fl2->size = tmp - fl2->offset;
658 					LIST_INSERT_AFTER(fl, fl2, freelist);
659 					d->freelist_entries++;
660 				}
661 			}
662 			break;
663 		}
664 	}
665 
666 	/*
667 	 * This is the first subdisk on this drive, just insert it into the
668 	 * list.
669 	 */
670 	if (LIST_EMPTY(&d->subdisks)) {
671 		LIST_INSERT_HEAD(&d->subdisks, s, from_drive);
672 
673 	/* There are other subdisks, so insert this one in correct order. */
674 	} else {
675 		LIST_FOREACH(s2, &d->subdisks, from_drive) {
676 			if (s->drive_offset < s2->drive_offset) {
677 				LIST_INSERT_BEFORE(s2, s, from_drive);
678 				break;
679 			} else if (LIST_NEXT(s2, from_drive) == NULL) {
680 				LIST_INSERT_AFTER(s2, s, from_drive);
681 				break;
682 			}
683 		}
684 	}
685 
686 	d->sdcount++;
687 	d->avail -= s->size;
688 
689 	/* Link back from the subdisk to this drive. */
690 	s->drive_sc = d;
691 
692 	return (0);
693 }
694 
695 void
696 gv_free_sd(struct gv_sd *s)
697 {
698 	struct gv_drive *d;
699 	struct gv_freelist *fl, *fl2;
700 
701 	KASSERT(s != NULL, ("gv_free_sd: NULL s"));
702 
703 	d = s->drive_sc;
704 	if (d == NULL)
705 		return;
706 
707 	/*
708 	 * First, find the free slot that's immediately before or after this
709 	 * subdisk.
710 	 */
711 	fl = NULL;
712 	LIST_FOREACH(fl, &d->freelist, freelist) {
713 		if (fl->offset == s->drive_offset + s->size)
714 			break;
715 		if (fl->offset + fl->size == s->drive_offset)
716 			break;
717 	}
718 
719 	/* If there is no free slot behind this subdisk, so create one. */
720 	if (fl == NULL) {
721 
722 		fl = g_malloc(sizeof(*fl), M_WAITOK | M_ZERO);
723 		fl->size = s->size;
724 		fl->offset = s->drive_offset;
725 
726 		if (d->freelist_entries == 0) {
727 			LIST_INSERT_HEAD(&d->freelist, fl, freelist);
728 		} else {
729 			LIST_FOREACH(fl2, &d->freelist, freelist) {
730 				if (fl->offset < fl2->offset) {
731 					LIST_INSERT_BEFORE(fl2, fl, freelist);
732 					break;
733 				} else if (LIST_NEXT(fl2, freelist) == NULL) {
734 					LIST_INSERT_AFTER(fl2, fl, freelist);
735 					break;
736 				}
737 			}
738 		}
739 
740 		d->freelist_entries++;
741 
742 	/* Expand the free slot we just found. */
743 	} else {
744 		fl->size += s->size;
745 		if (fl->offset > s->drive_offset)
746 			fl->offset = s->drive_offset;
747 	}
748 
749 	d->avail += s->size;
750 	d->sdcount--;
751 }
752 
753 void
754 gv_adjust_freespace(struct gv_sd *s, off_t remainder)
755 {
756 	struct gv_drive *d;
757 	struct gv_freelist *fl, *fl2;
758 
759 	KASSERT(s != NULL, ("gv_adjust_freespace: NULL s"));
760 	d = s->drive_sc;
761 	KASSERT(d != NULL, ("gv_adjust_freespace: NULL d"));
762 
763 	/* First, find the free slot that's immediately after this subdisk. */
764 	fl = NULL;
765 	LIST_FOREACH(fl, &d->freelist, freelist) {
766 		if (fl->offset == s->drive_offset + s->size)
767 			break;
768 	}
769 
770 	/* If there is no free slot behind this subdisk, so create one. */
771 	if (fl == NULL) {
772 
773 		fl = g_malloc(sizeof(*fl), M_WAITOK | M_ZERO);
774 		fl->size = remainder;
775 		fl->offset = s->drive_offset + s->size - remainder;
776 
777 		if (d->freelist_entries == 0) {
778 			LIST_INSERT_HEAD(&d->freelist, fl, freelist);
779 		} else {
780 			LIST_FOREACH(fl2, &d->freelist, freelist) {
781 				if (fl->offset < fl2->offset) {
782 					LIST_INSERT_BEFORE(fl2, fl, freelist);
783 					break;
784 				} else if (LIST_NEXT(fl2, freelist) == NULL) {
785 					LIST_INSERT_AFTER(fl2, fl, freelist);
786 					break;
787 				}
788 			}
789 		}
790 
791 		d->freelist_entries++;
792 
793 	/* Expand the free slot we just found. */
794 	} else {
795 		fl->offset -= remainder;
796 		fl->size += remainder;
797 	}
798 
799 	s->size -= remainder;
800 	d->avail += remainder;
801 }
802 
803 /* Check if the given plex is a striped one. */
804 int
805 gv_is_striped(struct gv_plex *p)
806 {
807 	KASSERT(p != NULL, ("gv_is_striped: NULL p"));
808 	switch(p->org) {
809 	case GV_PLEX_STRIPED:
810 	case GV_PLEX_RAID5:
811 		return (1);
812 	default:
813 		return (0);
814 	}
815 }
816 
817 /* Find a volume by name. */
818 struct gv_volume *
819 gv_find_vol(struct gv_softc *sc, char *name)
820 {
821 	struct gv_volume *v;
822 
823 	LIST_FOREACH(v, &sc->volumes, volume) {
824 		if (!strncmp(v->name, name, GV_MAXVOLNAME))
825 			return (v);
826 	}
827 
828 	return (NULL);
829 }
830 
831 /* Find a plex by name. */
832 struct gv_plex *
833 gv_find_plex(struct gv_softc *sc, char *name)
834 {
835 	struct gv_plex *p;
836 
837 	LIST_FOREACH(p, &sc->plexes, plex) {
838 		if (!strncmp(p->name, name, GV_MAXPLEXNAME))
839 			return (p);
840 	}
841 
842 	return (NULL);
843 }
844 
845 /* Find a subdisk by name. */
846 struct gv_sd *
847 gv_find_sd(struct gv_softc *sc, char *name)
848 {
849 	struct gv_sd *s;
850 
851 	LIST_FOREACH(s, &sc->subdisks, sd) {
852 		if (!strncmp(s->name, name, GV_MAXSDNAME))
853 			return (s);
854 	}
855 
856 	return (NULL);
857 }
858 
859 /* Find a drive by name. */
860 struct gv_drive *
861 gv_find_drive(struct gv_softc *sc, char *name)
862 {
863 	struct gv_drive *d;
864 
865 	LIST_FOREACH(d, &sc->drives, drive) {
866 		if (!strncmp(d->name, name, GV_MAXDRIVENAME))
867 			return (d);
868 	}
869 
870 	return (NULL);
871 }
872 
873 /* Check if any consumer of the given geom is open. */
874 int
875 gv_is_open(struct g_geom *gp)
876 {
877 	struct g_consumer *cp;
878 
879 	if (gp == NULL)
880 		return (0);
881 
882 	LIST_FOREACH(cp, &gp->consumer, consumer) {
883 		if (cp->acr || cp->acw || cp->ace)
884 			return (1);
885 	}
886 
887 	return (0);
888 }
889 
890 /* Return the type of object identified by string 'name'. */
891 int
892 gv_object_type(struct gv_softc *sc, char *name)
893 {
894 	struct gv_drive *d;
895 	struct gv_plex *p;
896 	struct gv_sd *s;
897 	struct gv_volume *v;
898 
899 	LIST_FOREACH(v, &sc->volumes, volume) {
900 		if (!strncmp(v->name, name, GV_MAXVOLNAME))
901 			return (GV_TYPE_VOL);
902 	}
903 
904 	LIST_FOREACH(p, &sc->plexes, plex) {
905 		if (!strncmp(p->name, name, GV_MAXPLEXNAME))
906 			return (GV_TYPE_PLEX);
907 	}
908 
909 	LIST_FOREACH(s, &sc->subdisks, sd) {
910 		if (!strncmp(s->name, name, GV_MAXSDNAME))
911 			return (GV_TYPE_SD);
912 	}
913 
914 	LIST_FOREACH(d, &sc->drives, drive) {
915 		if (!strncmp(d->name, name, GV_MAXDRIVENAME))
916 			return (GV_TYPE_DRIVE);
917 	}
918 
919 	return (-1);
920 }
921 
922 void
923 gv_kill_drive_thread(struct gv_drive *d)
924 {
925 	if (d->flags & GV_DRIVE_THREAD_ACTIVE) {
926 		d->flags |= GV_DRIVE_THREAD_DIE;
927 		wakeup(d);
928 		while (!(d->flags & GV_DRIVE_THREAD_DEAD))
929 			tsleep(d, PRIBIO, "gv_die", hz);
930 		d->flags &= ~GV_DRIVE_THREAD_ACTIVE;
931 		d->flags &= ~GV_DRIVE_THREAD_DIE;
932 		d->flags &= ~GV_DRIVE_THREAD_DEAD;
933 		g_free(d->bqueue);
934 		d->bqueue = NULL;
935 		mtx_destroy(&d->bqueue_mtx);
936 	}
937 }
938 
939 void
940 gv_kill_plex_thread(struct gv_plex *p)
941 {
942 	if (p->flags & GV_PLEX_THREAD_ACTIVE) {
943 		p->flags |= GV_PLEX_THREAD_DIE;
944 		wakeup(p);
945 		while (!(p->flags & GV_PLEX_THREAD_DEAD))
946 			tsleep(p, PRIBIO, "gv_die", hz);
947 		p->flags &= ~GV_PLEX_THREAD_ACTIVE;
948 		p->flags &= ~GV_PLEX_THREAD_DIE;
949 		p->flags &= ~GV_PLEX_THREAD_DEAD;
950 		g_free(p->bqueue);
951 		g_free(p->wqueue);
952 		p->bqueue = NULL;
953 		p->wqueue = NULL;
954 		mtx_destroy(&p->bqueue_mtx);
955 	}
956 }
957 
958 void
959 gv_kill_vol_thread(struct gv_volume *v)
960 {
961 	if (v->flags & GV_VOL_THREAD_ACTIVE) {
962 		v->flags |= GV_VOL_THREAD_DIE;
963 		wakeup(v);
964 		while (!(v->flags & GV_VOL_THREAD_DEAD))
965 			tsleep(v, PRIBIO, "gv_die", hz);
966 		v->flags &= ~GV_VOL_THREAD_ACTIVE;
967 		v->flags &= ~GV_VOL_THREAD_DIE;
968 		v->flags &= ~GV_VOL_THREAD_DEAD;
969 		g_free(v->bqueue);
970 		v->bqueue = NULL;
971 		mtx_destroy(&v->bqueue_mtx);
972 	}
973 }
974