]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/geom/vinum/geom_vinum_subr.c
add -n option to suppress clearing the build tree and add -DNO_CLEAN
[FreeBSD/FreeBSD.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                 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 }