]> CyberLeo.Net >> Repos - FreeBSD/releng/7.2.git/blob - sys/geom/vinum/geom_vinum_subr.c
Create releng/7.2 from stable/7 in preparation for 7.2-RELEASE.
[FreeBSD/releng/7.2.git] / sys / geom / vinum / geom_vinum_subr.c
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 }