xref: /freebsd/sys/geom/vinum/geom_vinum_subr.c (revision 9336e0699bda8a301cd2bfa37106b6ec5e32012e)
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 		p->state = GV_PLEX_DOWN;
495 	}
496 }
497 
498 /*
499  * Give a subdisk to a drive, check and adjust several parameters, adjust
500  * freelist.
501  */
502 int
503 gv_sd_to_drive(struct gv_softc *sc, struct gv_drive *d, struct gv_sd *s,
504     char *errstr, int errlen)
505 {
506 	struct gv_sd *s2;
507 	struct gv_freelist *fl, *fl2;
508 	off_t tmp;
509 	int i;
510 
511 	g_topology_assert();
512 
513 	fl2 = NULL;
514 
515 	KASSERT(sc != NULL, ("gv_sd_to_drive: NULL softc"));
516 	KASSERT(d != NULL, ("gv_sd_to_drive: NULL drive"));
517 	KASSERT(s != NULL, ("gv_sd_to_drive: NULL subdisk"));
518 	KASSERT(errstr != NULL, ("gv_sd_to_drive: NULL errstr"));
519 	KASSERT(errlen >= ERRBUFSIZ, ("gv_sd_to_drive: short errlen (%d)",
520 	    errlen));
521 
522 	/* Check if this subdisk was already given to this drive. */
523 	if (s->drive_sc == d)
524 		return (0);
525 
526 	/* Preliminary checks. */
527 	if (s->size > d->avail || d->freelist_entries == 0) {
528 		snprintf(errstr, errlen, "not enough space on '%s' for '%s'",
529 		    d->name, s->name);
530 		return (-1);
531 	}
532 
533 	/* No size given, autosize it. */
534 	if (s->size == -1) {
535 		/* Find the largest available slot. */
536 		LIST_FOREACH(fl, &d->freelist, freelist) {
537 			if (fl->size >= s->size) {
538 				s->size = fl->size;
539 				s->drive_offset = fl->offset;
540 				fl2 = fl;
541 			}
542 		}
543 
544 		/* No good slot found? */
545 		if (s->size == -1) {
546 			snprintf(errstr, errlen, "couldn't autosize '%s' on "
547 			    "'%s'", s->name, d->name);
548 			return (-1);
549 		}
550 
551 	/*
552 	 * Check if we have a free slot that's large enough for the given size.
553 	 */
554 	} else {
555 		i = 0;
556 		LIST_FOREACH(fl, &d->freelist, freelist) {
557 			/* Yes, this subdisk fits. */
558 			if (fl->size >= s->size) {
559 				i++;
560 				/* Assign drive offset, if not given. */
561 				if (s->drive_offset == -1)
562 					s->drive_offset = fl->offset;
563 				fl2 = fl;
564 				break;
565 			}
566 		}
567 
568 		/* Couldn't find a good free slot. */
569 		if (i == 0) {
570 			snprintf(errstr, errlen, "free slots to small for '%s' "
571 			    "on '%s'", s->name, d->name);
572 			return (-1);
573 		}
574 	}
575 
576 	/* No drive offset given, try to calculate it. */
577 	if (s->drive_offset == -1) {
578 
579 		/* Add offsets and sizes from other subdisks on this drive. */
580 		LIST_FOREACH(s2, &d->subdisks, from_drive) {
581 			s->drive_offset = s2->drive_offset + s2->size;
582 		}
583 
584 		/*
585 		 * If there are no other subdisks yet, then set the default
586 		 * offset to GV_DATA_START.
587 		 */
588 		if (s->drive_offset == -1)
589 			s->drive_offset = GV_DATA_START;
590 
591 	/* Check if we have a free slot at the given drive offset. */
592 	} else {
593 		i = 0;
594 		LIST_FOREACH(fl, &d->freelist, freelist) {
595 			/* Yes, this subdisk fits. */
596 			if ((fl->offset <= s->drive_offset) &&
597 			    (fl->offset + fl->size >=
598 			    s->drive_offset + s->size)) {
599 				i++;
600 				fl2 = fl;
601 				break;
602 			}
603 		}
604 
605 		/* Couldn't find a good free slot. */
606 		if (i == 0) {
607 			snprintf(errstr, errlen, "given drive_offset for '%s' "
608 			    "won't fit on '%s'", s->name, d->name);
609 			return (-1);
610 		}
611 	}
612 
613 	/*
614 	 * Now that all parameters are checked and set up, we can give the
615 	 * subdisk to the drive and adjust the freelist.
616 	 */
617 
618 	/* First, adjust the freelist. */
619 	LIST_FOREACH(fl, &d->freelist, freelist) {
620 
621 		/* This is the free slot that we have found before. */
622 		if (fl == fl2) {
623 
624 			/*
625 			 * The subdisk starts at the beginning of the free
626 			 * slot.
627 			 */
628 			if (fl->offset == s->drive_offset) {
629 				fl->offset += s->size;
630 				fl->size -= s->size;
631 
632 				/*
633 				 * The subdisk uses the whole slot, so remove
634 				 * it.
635 				 */
636 				if (fl->size == 0) {
637 					d->freelist_entries--;
638 					LIST_REMOVE(fl, freelist);
639 				}
640 			/*
641 			 * The subdisk does not start at the beginning of the
642 			 * free slot.
643 			 */
644 			} else {
645 				tmp = fl->offset + fl->size;
646 				fl->size = s->drive_offset - fl->offset;
647 
648 				/*
649 				 * The subdisk didn't use the complete rest of
650 				 * the free slot, so we need to split it.
651 				 */
652 				if (s->drive_offset + s->size != tmp) {
653 					fl2 = g_malloc(sizeof(*fl2),
654 					    M_WAITOK | M_ZERO);
655 					fl2->offset = s->drive_offset + s->size;
656 					fl2->size = tmp - fl2->offset;
657 					LIST_INSERT_AFTER(fl, fl2, freelist);
658 					d->freelist_entries++;
659 				}
660 			}
661 			break;
662 		}
663 	}
664 
665 	/*
666 	 * This is the first subdisk on this drive, just insert it into the
667 	 * list.
668 	 */
669 	if (LIST_EMPTY(&d->subdisks)) {
670 		LIST_INSERT_HEAD(&d->subdisks, s, from_drive);
671 
672 	/* There are other subdisks, so insert this one in correct order. */
673 	} else {
674 		LIST_FOREACH(s2, &d->subdisks, from_drive) {
675 			if (s->drive_offset < s2->drive_offset) {
676 				LIST_INSERT_BEFORE(s2, s, from_drive);
677 				break;
678 			} else if (LIST_NEXT(s2, from_drive) == NULL) {
679 				LIST_INSERT_AFTER(s2, s, from_drive);
680 				break;
681 			}
682 		}
683 	}
684 
685 	d->sdcount++;
686 	d->avail -= s->size;
687 
688 	/* Link back from the subdisk to this drive. */
689 	s->drive_sc = d;
690 
691 	return (0);
692 }
693 
694 void
695 gv_free_sd(struct gv_sd *s)
696 {
697 	struct gv_drive *d;
698 	struct gv_freelist *fl, *fl2;
699 
700 	KASSERT(s != NULL, ("gv_free_sd: NULL s"));
701 
702 	d = s->drive_sc;
703 	if (d == NULL)
704 		return;
705 
706 	/*
707 	 * First, find the free slot that's immediately before or after this
708 	 * subdisk.
709 	 */
710 	fl = NULL;
711 	LIST_FOREACH(fl, &d->freelist, freelist) {
712 		if (fl->offset == s->drive_offset + s->size)
713 			break;
714 		if (fl->offset + fl->size == s->drive_offset)
715 			break;
716 	}
717 
718 	/* If there is no free slot behind this subdisk, so create one. */
719 	if (fl == NULL) {
720 
721 		fl = g_malloc(sizeof(*fl), M_WAITOK | M_ZERO);
722 		fl->size = s->size;
723 		fl->offset = s->drive_offset;
724 
725 		if (d->freelist_entries == 0) {
726 			LIST_INSERT_HEAD(&d->freelist, fl, freelist);
727 		} else {
728 			LIST_FOREACH(fl2, &d->freelist, freelist) {
729 				if (fl->offset < fl2->offset) {
730 					LIST_INSERT_BEFORE(fl2, fl, freelist);
731 					break;
732 				} else if (LIST_NEXT(fl2, freelist) == NULL) {
733 					LIST_INSERT_AFTER(fl2, fl, freelist);
734 					break;
735 				}
736 			}
737 		}
738 
739 		d->freelist_entries++;
740 
741 	/* Expand the free slot we just found. */
742 	} else {
743 		fl->size += s->size;
744 		if (fl->offset > s->drive_offset)
745 			fl->offset = s->drive_offset;
746 	}
747 
748 	d->avail += s->size;
749 	d->sdcount--;
750 }
751 
752 void
753 gv_adjust_freespace(struct gv_sd *s, off_t remainder)
754 {
755 	struct gv_drive *d;
756 	struct gv_freelist *fl, *fl2;
757 
758 	KASSERT(s != NULL, ("gv_adjust_freespace: NULL s"));
759 	d = s->drive_sc;
760 	KASSERT(d != NULL, ("gv_adjust_freespace: NULL d"));
761 
762 	/* First, find the free slot that's immediately after this subdisk. */
763 	fl = NULL;
764 	LIST_FOREACH(fl, &d->freelist, freelist) {
765 		if (fl->offset == s->drive_offset + s->size)
766 			break;
767 	}
768 
769 	/* If there is no free slot behind this subdisk, so create one. */
770 	if (fl == NULL) {
771 
772 		fl = g_malloc(sizeof(*fl), M_WAITOK | M_ZERO);
773 		fl->size = remainder;
774 		fl->offset = s->drive_offset + s->size - remainder;
775 
776 		if (d->freelist_entries == 0) {
777 			LIST_INSERT_HEAD(&d->freelist, fl, freelist);
778 		} else {
779 			LIST_FOREACH(fl2, &d->freelist, freelist) {
780 				if (fl->offset < fl2->offset) {
781 					LIST_INSERT_BEFORE(fl2, fl, freelist);
782 					break;
783 				} else if (LIST_NEXT(fl2, freelist) == NULL) {
784 					LIST_INSERT_AFTER(fl2, fl, freelist);
785 					break;
786 				}
787 			}
788 		}
789 
790 		d->freelist_entries++;
791 
792 	/* Expand the free slot we just found. */
793 	} else {
794 		fl->offset -= remainder;
795 		fl->size += remainder;
796 	}
797 
798 	s->size -= remainder;
799 	d->avail += remainder;
800 }
801 
802 /* Check if the given plex is a striped one. */
803 int
804 gv_is_striped(struct gv_plex *p)
805 {
806 	KASSERT(p != NULL, ("gv_is_striped: NULL p"));
807 	switch(p->org) {
808 	case GV_PLEX_STRIPED:
809 	case GV_PLEX_RAID5:
810 		return (1);
811 	default:
812 		return (0);
813 	}
814 }
815 
816 /* Find a volume by name. */
817 struct gv_volume *
818 gv_find_vol(struct gv_softc *sc, char *name)
819 {
820 	struct gv_volume *v;
821 
822 	LIST_FOREACH(v, &sc->volumes, volume) {
823 		if (!strncmp(v->name, name, GV_MAXVOLNAME))
824 			return (v);
825 	}
826 
827 	return (NULL);
828 }
829 
830 /* Find a plex by name. */
831 struct gv_plex *
832 gv_find_plex(struct gv_softc *sc, char *name)
833 {
834 	struct gv_plex *p;
835 
836 	LIST_FOREACH(p, &sc->plexes, plex) {
837 		if (!strncmp(p->name, name, GV_MAXPLEXNAME))
838 			return (p);
839 	}
840 
841 	return (NULL);
842 }
843 
844 /* Find a subdisk by name. */
845 struct gv_sd *
846 gv_find_sd(struct gv_softc *sc, char *name)
847 {
848 	struct gv_sd *s;
849 
850 	LIST_FOREACH(s, &sc->subdisks, sd) {
851 		if (!strncmp(s->name, name, GV_MAXSDNAME))
852 			return (s);
853 	}
854 
855 	return (NULL);
856 }
857 
858 /* Find a drive by name. */
859 struct gv_drive *
860 gv_find_drive(struct gv_softc *sc, char *name)
861 {
862 	struct gv_drive *d;
863 
864 	LIST_FOREACH(d, &sc->drives, drive) {
865 		if (!strncmp(d->name, name, GV_MAXDRIVENAME))
866 			return (d);
867 	}
868 
869 	return (NULL);
870 }
871 
872 /* Check if any consumer of the given geom is open. */
873 int
874 gv_is_open(struct g_geom *gp)
875 {
876 	struct g_consumer *cp;
877 
878 	if (gp == NULL)
879 		return (0);
880 
881 	LIST_FOREACH(cp, &gp->consumer, consumer) {
882 		if (cp->acr || cp->acw || cp->ace)
883 			return (1);
884 	}
885 
886 	return (0);
887 }
888 
889 /* Return the type of object identified by string 'name'. */
890 int
891 gv_object_type(struct gv_softc *sc, char *name)
892 {
893 	struct gv_drive *d;
894 	struct gv_plex *p;
895 	struct gv_sd *s;
896 	struct gv_volume *v;
897 
898 	LIST_FOREACH(v, &sc->volumes, volume) {
899 		if (!strncmp(v->name, name, GV_MAXVOLNAME))
900 			return (GV_TYPE_VOL);
901 	}
902 
903 	LIST_FOREACH(p, &sc->plexes, plex) {
904 		if (!strncmp(p->name, name, GV_MAXPLEXNAME))
905 			return (GV_TYPE_PLEX);
906 	}
907 
908 	LIST_FOREACH(s, &sc->subdisks, sd) {
909 		if (!strncmp(s->name, name, GV_MAXSDNAME))
910 			return (GV_TYPE_SD);
911 	}
912 
913 	LIST_FOREACH(d, &sc->drives, drive) {
914 		if (!strncmp(d->name, name, GV_MAXDRIVENAME))
915 			return (GV_TYPE_DRIVE);
916 	}
917 
918 	return (-1);
919 }
920 
921 void
922 gv_kill_drive_thread(struct gv_drive *d)
923 {
924 	if (d->flags & GV_DRIVE_THREAD_ACTIVE) {
925 		d->flags |= GV_DRIVE_THREAD_DIE;
926 		wakeup(d);
927 		while (!(d->flags & GV_DRIVE_THREAD_DEAD))
928 			tsleep(d, PRIBIO, "gv_die", hz);
929 		d->flags &= ~GV_DRIVE_THREAD_ACTIVE;
930 		d->flags &= ~GV_DRIVE_THREAD_DIE;
931 		d->flags &= ~GV_DRIVE_THREAD_DEAD;
932 		g_free(d->bqueue);
933 		d->bqueue = NULL;
934 		mtx_destroy(&d->bqueue_mtx);
935 	}
936 }
937 
938 void
939 gv_kill_plex_thread(struct gv_plex *p)
940 {
941 	if (p->flags & GV_PLEX_THREAD_ACTIVE) {
942 		p->flags |= GV_PLEX_THREAD_DIE;
943 		wakeup(p);
944 		while (!(p->flags & GV_PLEX_THREAD_DEAD))
945 			tsleep(p, PRIBIO, "gv_die", hz);
946 		p->flags &= ~GV_PLEX_THREAD_ACTIVE;
947 		p->flags &= ~GV_PLEX_THREAD_DIE;
948 		p->flags &= ~GV_PLEX_THREAD_DEAD;
949 		g_free(p->bqueue);
950 		g_free(p->wqueue);
951 		p->bqueue = NULL;
952 		p->wqueue = NULL;
953 		mtx_destroy(&p->bqueue_mtx);
954 	}
955 }
956 
957 void
958 gv_kill_vol_thread(struct gv_volume *v)
959 {
960 	if (v->flags & GV_VOL_THREAD_ACTIVE) {
961 		v->flags |= GV_VOL_THREAD_DIE;
962 		wakeup(v);
963 		while (!(v->flags & GV_VOL_THREAD_DEAD))
964 			tsleep(v, PRIBIO, "gv_die", hz);
965 		v->flags &= ~GV_VOL_THREAD_ACTIVE;
966 		v->flags &= ~GV_VOL_THREAD_DIE;
967 		v->flags &= ~GV_VOL_THREAD_DEAD;
968 		g_free(v->bqueue);
969 		v->bqueue = NULL;
970 		mtx_destroy(&v->bqueue_mtx);
971 	}
972 }
973