]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/geom/vinum/geom_vinum_subr.c
openssh: cherry-pick OpenSSL 1.1.1 compatibility
[FreeBSD/FreeBSD.git] / sys / geom / vinum / geom_vinum_subr.c
1 /*-
2  * SPDX-License-Identifier: BSD-4-Clause
3  *
4  * Copyright (c) 2004, 2007 Lukas Ertl
5  * Copyright (c) 2007, 2009 Ulf Lilleengen
6  * Copyright (c) 1997, 1998, 1999
7  *      Nan Yang Computer Services Limited.  All rights reserved.
8  *
9  *  Parts written by Greg Lehey
10  *
11  *  This software is distributed under the so-called ``Berkeley
12  *  License'':
13  *
14  * Redistribution and use in source and binary forms, with or without
15  * modification, are permitted provided that the following conditions
16  * are met:
17  * 1. Redistributions of source code must retain the above copyright
18  *    notice, this list of conditions and the following disclaimer.
19  * 2. Redistributions in binary form must reproduce the above copyright
20  *    notice, this list of conditions and the following disclaimer in the
21  *    documentation and/or other materials provided with the distribution.
22  * 3. All advertising materials mentioning features or use of this software
23  *    must display the following acknowledgement:
24  *      This product includes software developed by Nan Yang Computer
25  *      Services Limited.
26  * 4. Neither the name of the Company nor the names of its contributors
27  *    may be used to endorse or promote products derived from this software
28  *    without specific prior written permission.
29  *
30  * This software is provided ``as is'', and any express or implied
31  * warranties, including, but not limited to, the implied warranties of
32  * merchantability and fitness for a particular purpose are disclaimed.
33  * In no event shall the company or contributors be liable for any
34  * direct, indirect, incidental, special, exemplary, or consequential
35  * damages (including, but not limited to, procurement of substitute
36  * goods or services; loss of use, data, or profits; or business
37  * interruption) however caused and on any theory of liability, whether
38  * in contract, strict liability, or tort (including negligence or
39  * otherwise) arising in any way out of the use of this software, even if
40  * advised of the possibility of such damage.
41  *
42  */
43
44 #include <sys/cdefs.h>
45 __FBSDID("$FreeBSD$");
46
47 #include <sys/param.h>
48 #include <sys/malloc.h>
49 #include <sys/sbuf.h>
50 #include <sys/systm.h>
51
52 #include <geom/geom.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 int     gv_drive_is_newer(struct gv_softc *, struct gv_drive *);
58 static off_t gv_plex_smallest_sd(struct gv_plex *);
59
60 void
61 gv_parse_config(struct gv_softc *sc, char *buf, struct gv_drive *d)
62 {
63         char *aptr, *bptr, *cptr;
64         struct gv_volume *v, *v2;
65         struct gv_plex *p, *p2;
66         struct gv_sd *s, *s2;
67         int error, is_newer, tokens;
68         char *token[GV_MAXARGS];
69
70         is_newer = gv_drive_is_newer(sc, d);
71
72         /* Until the end of the string *buf. */
73         for (aptr = buf; *aptr != '\0'; aptr = bptr) {
74                 bptr = aptr;
75                 cptr = aptr;
76
77                 /* Separate input lines. */
78                 while (*bptr != '\n')
79                         bptr++;
80                 *bptr = '\0';
81                 bptr++;
82
83                 tokens = gv_tokenize(cptr, token, GV_MAXARGS);
84
85                 if (tokens <= 0)
86                         continue;
87
88                 if (!strcmp(token[0], "volume")) {
89                         v = gv_new_volume(tokens, token);
90                         if (v == NULL) {
91                                 G_VINUM_DEBUG(0, "config parse failed volume");
92                                 break;
93                         }
94
95                         v2 = gv_find_vol(sc, v->name);
96                         if (v2 != NULL) {
97                                 if (is_newer) {
98                                         v2->state = v->state;
99                                         G_VINUM_DEBUG(2, "newer volume found!");
100                                 }
101                                 g_free(v);
102                                 continue;
103                         }
104
105                         gv_create_volume(sc, v);
106
107                 } else if (!strcmp(token[0], "plex")) {
108                         p = gv_new_plex(tokens, token);
109                         if (p == NULL) {
110                                 G_VINUM_DEBUG(0, "config parse failed plex");
111                                 break;
112                         }
113
114                         p2 = gv_find_plex(sc, p->name);
115                         if (p2 != NULL) {
116                                 /* XXX */
117                                 if (is_newer) {
118                                         p2->state = p->state;
119                                         G_VINUM_DEBUG(2, "newer plex found!");
120                                 }
121                                 g_free(p);
122                                 continue;
123                         }
124
125                         error = gv_create_plex(sc, p);
126                         if (error)
127                                 continue;
128                         /*
129                          * These flags were set in gv_create_plex() and are not
130                          * needed here (on-disk config parsing).
131                          */
132                         p->flags &= ~GV_PLEX_ADDED;
133
134                 } else if (!strcmp(token[0], "sd")) {
135                         s = gv_new_sd(tokens, token);
136
137                         if (s == NULL) {
138                                 G_VINUM_DEBUG(0, "config parse failed subdisk");
139                                 break;
140                         }
141
142                         s2 = gv_find_sd(sc, s->name);
143                         if (s2 != NULL) {
144                                 /* XXX */
145                                 if (is_newer) {
146                                         s2->state = s->state;
147                                         G_VINUM_DEBUG(2, "newer subdisk found!");
148                                 }
149                                 g_free(s);
150                                 continue;
151                         }
152
153                         /*
154                          * Signal that this subdisk was tasted, and could
155                          * possibly reference a drive that isn't in our config
156                          * yet.
157                          */
158                         s->flags |= GV_SD_TASTED;
159
160                         if (s->state == GV_SD_UP)
161                                 s->flags |= GV_SD_CANGOUP;
162
163                         error = gv_create_sd(sc, s);
164                         if (error)
165                                 continue;
166
167                         /*
168                          * This flag was set in gv_create_sd() and is not
169                          * needed here (on-disk config parsing).
170                          */
171                         s->flags &= ~GV_SD_NEWBORN;
172                         s->flags &= ~GV_SD_GROW;
173                 }
174         }
175 }
176
177 /*
178  * Format the vinum configuration properly.  If ondisk is non-zero then the
179  * configuration is intended to be written to disk later.
180  */
181 void
182 gv_format_config(struct gv_softc *sc, struct sbuf *sb, int ondisk, char *prefix)
183 {
184         struct gv_drive *d;
185         struct gv_sd *s;
186         struct gv_plex *p;
187         struct gv_volume *v;
188
189         /*
190          * We don't need the drive configuration if we're not writing the
191          * config to disk.
192          */
193         if (!ondisk) {
194                 LIST_FOREACH(d, &sc->drives, drive) {
195                         sbuf_printf(sb, "%sdrive %s device /dev/%s\n", prefix,
196                             d->name, d->device);
197                 }
198         }
199
200         LIST_FOREACH(v, &sc->volumes, volume) {
201                 if (!ondisk)
202                         sbuf_printf(sb, "%s", prefix);
203                 sbuf_printf(sb, "volume %s", v->name);
204                 if (ondisk)
205                         sbuf_printf(sb, " state %s", gv_volstate(v->state));
206                 sbuf_printf(sb, "\n");
207         }
208
209         LIST_FOREACH(p, &sc->plexes, plex) {
210                 if (!ondisk)
211                         sbuf_printf(sb, "%s", prefix);
212                 sbuf_printf(sb, "plex name %s org %s ", p->name,
213                     gv_plexorg(p->org));
214                 if (gv_is_striped(p))
215                         sbuf_printf(sb, "%ds ", p->stripesize / 512);
216                 if (p->vol_sc != NULL)
217                         sbuf_printf(sb, "vol %s", p->volume);
218                 if (ondisk)
219                         sbuf_printf(sb, " state %s", gv_plexstate(p->state));
220                 sbuf_printf(sb, "\n");
221         }
222
223         LIST_FOREACH(s, &sc->subdisks, sd) {
224                 if (!ondisk)
225                         sbuf_printf(sb, "%s", prefix);
226                 sbuf_printf(sb, "sd name %s drive %s len %jds driveoffset "
227                     "%jds", s->name, s->drive, s->size / 512,
228                     s->drive_offset / 512);
229                 if (s->plex_sc != NULL) {
230                         sbuf_printf(sb, " plex %s plexoffset %jds", s->plex,
231                             s->plex_offset / 512);
232                 }
233                 if (ondisk)
234                         sbuf_printf(sb, " state %s", gv_sdstate(s->state));
235                 sbuf_printf(sb, "\n");
236         }
237 }
238
239 static off_t
240 gv_plex_smallest_sd(struct gv_plex *p)
241 {
242         struct gv_sd *s;
243         off_t smallest;
244
245         KASSERT(p != NULL, ("gv_plex_smallest_sd: NULL p"));
246
247         s = LIST_FIRST(&p->subdisks);
248         if (s == NULL)
249                 return (-1);
250         smallest = s->size;
251         LIST_FOREACH(s, &p->subdisks, in_plex) {
252                 if (s->size < smallest)
253                         smallest = s->size;
254         }
255         return (smallest);
256 }
257
258 /* Walk over plexes in a volume and count how many are down. */
259 int
260 gv_plexdown(struct gv_volume *v)
261 {
262         int plexdown;
263         struct gv_plex *p;
264
265         KASSERT(v != NULL, ("gv_plexdown: NULL v"));
266
267         plexdown = 0;
268
269         LIST_FOREACH(p, &v->plexes, plex) {
270                 if (p->state == GV_PLEX_DOWN)
271                         plexdown++;
272         }
273         return (plexdown);
274 }
275
276 int
277 gv_sd_to_plex(struct gv_sd *s, struct gv_plex *p)
278 {
279         struct gv_sd *s2;
280         off_t psizeorig, remainder, smallest;
281
282         /* If this subdisk was already given to this plex, do nothing. */
283         if (s->plex_sc == p)
284                 return (0);
285
286         /* Check correct size of this subdisk. */
287         s2 = LIST_FIRST(&p->subdisks);
288         /* Adjust the subdisk-size if necessary. */
289         if (s2 != NULL && gv_is_striped(p)) {
290                 /* First adjust to the stripesize. */
291                 remainder = s->size % p->stripesize;
292
293                 if (remainder) {
294                         G_VINUM_DEBUG(1, "size of sd %s is not a "
295                             "multiple of plex stripesize, taking off "
296                             "%jd bytes", s->name,
297                             (intmax_t)remainder);
298                         gv_adjust_freespace(s, remainder);
299                 }
300
301                 smallest = gv_plex_smallest_sd(p);
302                 /* Then take off extra if other subdisks are smaller. */
303                 remainder = s->size - smallest;
304
305                 /*
306                  * Don't allow a remainder below zero for running plexes, it's too
307                  * painful, and if someone were to accidentally do this, the
308                  * resulting array might be smaller than the original... not god 
309                  */
310                 if (remainder < 0) {
311                         if (!(p->flags & GV_PLEX_NEWBORN)) {
312                                 G_VINUM_DEBUG(0, "sd %s too small for plex %s!",
313                                     s->name, p->name);
314                                 return (GV_ERR_BADSIZE);
315                         }
316                         /* Adjust other subdisks. */
317                         LIST_FOREACH(s2, &p->subdisks, in_plex) {
318                                 G_VINUM_DEBUG(1, "size of sd %s is to big, "
319                                     "taking off %jd bytes", s->name,
320                                     (intmax_t)remainder);
321                                 gv_adjust_freespace(s2, (remainder * -1));
322                         }
323                 } else if (remainder > 0) {
324                         G_VINUM_DEBUG(1, "size of sd %s is to big, "
325                             "taking off %jd bytes", s->name,
326                             (intmax_t)remainder);
327                         gv_adjust_freespace(s, remainder);
328                 }
329         }
330
331         /* Find the correct plex offset for this subdisk, if needed. */
332         if (s->plex_offset == -1) {
333                 /* 
334                  * First set it to 0 to catch the case where we had a detached
335                  * subdisk that didn't get any good offset.
336                  */
337                 s->plex_offset = 0;
338                 if (p->sdcount) {
339                         LIST_FOREACH(s2, &p->subdisks, in_plex) {
340                                 if (gv_is_striped(p))
341                                         s->plex_offset = p->sdcount *
342                                             p->stripesize;
343                                 else
344                                         s->plex_offset = s2->plex_offset +
345                                             s2->size;
346                         }
347                 }
348         }
349
350         /* There are no subdisks for this plex yet, just insert it. */
351         if (LIST_EMPTY(&p->subdisks)) {
352                 LIST_INSERT_HEAD(&p->subdisks, s, in_plex);
353
354         /* Insert in correct order, depending on plex_offset. */
355         } else {
356                 LIST_FOREACH(s2, &p->subdisks, in_plex) {
357                         if (s->plex_offset < s2->plex_offset) {
358                                 LIST_INSERT_BEFORE(s2, s, in_plex);
359                                 break;
360                         } else if (LIST_NEXT(s2, in_plex) == NULL) {
361                                 LIST_INSERT_AFTER(s2, s, in_plex);
362                                 break;
363                         }
364                 }
365         }
366
367         s->plex_sc = p;
368         /* Adjust the size of our plex. We check if the plex misses a subdisk,
369          * so we don't make the plex smaller than it actually should be.
370          */
371         psizeorig = p->size;
372         p->size = gv_plex_size(p);
373         /* Make sure the size is not changed. */
374         if (p->sddetached > 0) {
375                 if (p->size < psizeorig) {
376                         p->size = psizeorig;
377                         /* We make sure wee need another subdisk. */
378                         if (p->sddetached == 1)
379                                 p->sddetached++;
380                 }
381                 p->sddetached--;
382         } else {
383                 if ((p->org == GV_PLEX_RAID5 ||
384                     p->org == GV_PLEX_STRIPED) &&
385                     !(p->flags & GV_PLEX_NEWBORN) && 
386                     p->state == GV_PLEX_UP) {
387                         s->flags |= GV_SD_GROW;
388                 }
389                 p->sdcount++;
390         }
391
392         return (0);
393 }
394
395 void
396 gv_update_vol_size(struct gv_volume *v, off_t size)
397 {
398         if (v == NULL)
399                 return;
400         if (v->provider != NULL) {
401                 g_topology_lock();
402                 v->provider->mediasize = size;
403                 g_topology_unlock();
404         }
405         v->size = size;
406 }
407
408 /* Return how many subdisks that constitute the original plex. */
409 int
410 gv_sdcount(struct gv_plex *p, int growing)
411 {
412         struct gv_sd *s;
413         int sdcount;
414
415         sdcount = p->sdcount;
416         if (growing) {
417                 LIST_FOREACH(s, &p->subdisks, in_plex) {
418                         if (s->flags & GV_SD_GROW)
419                                 sdcount--;
420                 }
421         }
422
423         return (sdcount);
424 }
425
426 /* Calculates the plex size. */
427 off_t
428 gv_plex_size(struct gv_plex *p)
429 {
430         struct gv_sd *s;
431         off_t size;
432         int sdcount;
433
434         KASSERT(p != NULL, ("gv_plex_size: NULL p"));
435
436         /* Adjust the size of our plex. */
437         size = 0;
438         sdcount = gv_sdcount(p, 1);
439         switch (p->org) {
440         case GV_PLEX_CONCAT:
441                 LIST_FOREACH(s, &p->subdisks, in_plex)
442                         size += s->size;
443                 break;
444         case GV_PLEX_STRIPED:
445                 s = LIST_FIRST(&p->subdisks);
446                 size = ((s != NULL) ? (sdcount * s->size) : 0);
447                 break;
448         case GV_PLEX_RAID5:
449                 s = LIST_FIRST(&p->subdisks);
450                 size = ((s != NULL) ? ((sdcount - 1) * s->size) : 0);
451                 break;
452         }
453
454         return (size);
455 }
456
457 /* Returns the size of a volume. */
458 off_t
459 gv_vol_size(struct gv_volume *v)
460 {
461         struct gv_plex *p;
462         off_t minplexsize;
463
464         KASSERT(v != NULL, ("gv_vol_size: NULL v"));
465
466         p = LIST_FIRST(&v->plexes);
467         if (p == NULL)
468                 return (0);
469
470         minplexsize = p->size;
471         LIST_FOREACH(p, &v->plexes, in_volume) {
472                 if (p->size < minplexsize) {
473                         minplexsize = p->size;
474                 }
475         }
476         return (minplexsize);
477 }
478
479 void
480 gv_update_plex_config(struct gv_plex *p)
481 {
482         struct gv_sd *s, *s2;
483         off_t remainder;
484         int required_sds, state;
485
486         KASSERT(p != NULL, ("gv_update_plex_config: NULL p"));
487
488         /* The plex was added to an already running volume. */
489         if (p->flags & GV_PLEX_ADDED)
490                 gv_set_plex_state(p, GV_PLEX_DOWN, GV_SETSTATE_FORCE);
491
492         switch (p->org) {
493         case GV_PLEX_STRIPED:
494                 required_sds = 2;
495                 break;
496         case GV_PLEX_RAID5:
497                 required_sds = 3;
498                 break;
499         case GV_PLEX_CONCAT:
500         default:
501                 required_sds = 0;
502                 break;
503         }
504
505         if (required_sds) {
506                 if (p->sdcount < required_sds) {
507                         gv_set_plex_state(p, GV_PLEX_DOWN, GV_SETSTATE_FORCE);
508                 }
509
510                 /*
511                  * The subdisks in striped plexes must all have the same size.
512                  */
513                 s = LIST_FIRST(&p->subdisks);
514                 LIST_FOREACH(s2, &p->subdisks, in_plex) {
515                         if (s->size != s2->size) {
516                                 G_VINUM_DEBUG(0, "subdisk size mismatch %s"
517                                     "(%jd) <> %s (%jd)", s->name, s->size,
518                                     s2->name, s2->size);
519                                 gv_set_plex_state(p, GV_PLEX_DOWN,
520                                     GV_SETSTATE_FORCE);
521                         }
522                 }
523
524                 LIST_FOREACH(s, &p->subdisks, in_plex) {
525                         /* Trim subdisk sizes to match the stripe size. */
526                         remainder = s->size % p->stripesize;
527                         if (remainder) {
528                                 G_VINUM_DEBUG(1, "size of sd %s is not a "
529                                     "multiple of plex stripesize, taking off "
530                                     "%jd bytes", s->name, (intmax_t)remainder);
531                                 gv_adjust_freespace(s, remainder);
532                         }
533                 }
534         }
535
536         p->size = gv_plex_size(p);
537         if (p->sdcount == 0)
538                 gv_set_plex_state(p, GV_PLEX_DOWN, GV_SETSTATE_FORCE);
539         else if (p->org == GV_PLEX_RAID5 && p->flags & GV_PLEX_NEWBORN) {
540                 LIST_FOREACH(s, &p->subdisks, in_plex)
541                         gv_set_sd_state(s, GV_SD_UP, GV_SETSTATE_FORCE);
542                 /* If added to a volume, we want the plex to be down. */
543                 state = (p->flags & GV_PLEX_ADDED) ? GV_PLEX_DOWN : GV_PLEX_UP;
544                 gv_set_plex_state(p, state, GV_SETSTATE_FORCE);
545                 p->flags &= ~GV_PLEX_ADDED;
546         } else if (p->flags & GV_PLEX_ADDED) {
547                 LIST_FOREACH(s, &p->subdisks, in_plex)
548                         gv_set_sd_state(s, GV_SD_STALE, GV_SETSTATE_FORCE);
549                 gv_set_plex_state(p, GV_PLEX_DOWN, GV_SETSTATE_FORCE);
550                 p->flags &= ~GV_PLEX_ADDED;
551         } else if (p->state == GV_PLEX_UP) {
552                 LIST_FOREACH(s, &p->subdisks, in_plex) {
553                         if (s->flags & GV_SD_GROW) {
554                                 gv_set_plex_state(p, GV_PLEX_GROWABLE,
555                                     GV_SETSTATE_FORCE);
556                                 break;
557                         }
558                 }
559         }
560         /* Our plex is grown up now. */
561         p->flags &= ~GV_PLEX_NEWBORN;
562 }
563
564 /*
565  * Give a subdisk to a drive, check and adjust several parameters, adjust
566  * freelist.
567  */
568 int
569 gv_sd_to_drive(struct gv_sd *s, struct gv_drive *d)
570 {
571         struct gv_sd *s2;
572         struct gv_freelist *fl, *fl2;
573         off_t tmp;
574         int i;
575
576         fl2 = NULL;
577
578         /* Shortcut for "referenced" drives. */
579         if (d->flags & GV_DRIVE_REFERENCED) {
580                 s->drive_sc = d;
581                 return (0);
582         }
583
584         /* Check if this subdisk was already given to this drive. */
585         if (s->drive_sc != NULL) {
586                 if (s->drive_sc == d) {
587                         if (!(s->flags & GV_SD_TASTED)) {
588                                 return (0);
589                         }
590                 } else {
591                         G_VINUM_DEBUG(0, "error giving subdisk '%s' to '%s' "
592                             "(already on '%s')", s->name, d->name,
593                             s->drive_sc->name);
594                         return (GV_ERR_ISATTACHED);
595                 }
596         }
597
598         /* Preliminary checks. */
599         if ((s->size > d->avail) || (d->freelist_entries == 0)) {
600                 G_VINUM_DEBUG(0, "not enough space on '%s' for '%s'", d->name,
601                     s->name);
602                 return (GV_ERR_NOSPACE);
603         }
604
605         /* If no size was given for this subdisk, try to auto-size it... */
606         if (s->size == -1) {
607                 /* Find the largest available slot. */
608                 LIST_FOREACH(fl, &d->freelist, freelist) {
609                         if (fl->size < s->size)
610                                 continue;
611                         s->size = fl->size;
612                         s->drive_offset = fl->offset;
613                         fl2 = fl;
614                 }
615
616                 /* No good slot found? */
617                 if (s->size == -1) {
618                         G_VINUM_DEBUG(0, "unable to autosize '%s' on '%s'",
619                             s->name, d->name);
620                         return (GV_ERR_BADSIZE);
621                 }
622
623         /*
624          * ... or check if we have a free slot that's large enough for the
625          * given size.
626          */
627         } else {
628                 i = 0;
629                 LIST_FOREACH(fl, &d->freelist, freelist) {
630                         if (fl->size < s->size)
631                                 continue;
632                         /* Assign drive offset, if not given. */
633                         if (s->drive_offset == -1)
634                                 s->drive_offset = fl->offset;
635                         fl2 = fl;
636                         i++;
637                         break;
638                 }
639
640                 /* Couldn't find a good free slot. */
641                 if (i == 0) {
642                         G_VINUM_DEBUG(0, "free slots to small for '%s' on '%s'",
643                             s->name, d->name);
644                         return (GV_ERR_NOSPACE);
645                 }
646         }
647
648         /* No drive offset given, try to calculate it. */
649         if (s->drive_offset == -1) {
650
651                 /* Add offsets and sizes from other subdisks on this drive. */
652                 LIST_FOREACH(s2, &d->subdisks, from_drive) {
653                         s->drive_offset = s2->drive_offset + s2->size;
654                 }
655
656                 /*
657                  * If there are no other subdisks yet, then set the default
658                  * offset to GV_DATA_START.
659                  */
660                 if (s->drive_offset == -1)
661                         s->drive_offset = GV_DATA_START;
662
663         /* Check if we have a free slot at the given drive offset. */
664         } else {
665                 i = 0;
666                 LIST_FOREACH(fl, &d->freelist, freelist) {
667                         /* Yes, this subdisk fits. */
668                         if ((fl->offset <= s->drive_offset) &&
669                             (fl->offset + fl->size >=
670                             s->drive_offset + s->size)) {
671                                 i++;
672                                 fl2 = fl;
673                                 break;
674                         }
675                 }
676
677                 /* Couldn't find a good free slot. */
678                 if (i == 0) {
679                         G_VINUM_DEBUG(0, "given drive_offset for '%s' won't fit "
680                             "on '%s'", s->name, d->name);
681                         return (GV_ERR_NOSPACE);
682                 }
683         }
684
685         /*
686          * Now that all parameters are checked and set up, we can give the
687          * subdisk to the drive and adjust the freelist.
688          */
689
690         /* First, adjust the freelist. */
691         LIST_FOREACH(fl, &d->freelist, freelist) {
692                 /* Look for the free slot that we have found before. */
693                 if (fl != fl2)
694                         continue;
695
696                 /* The subdisk starts at the beginning of the free slot. */
697                 if (fl->offset == s->drive_offset) {
698                         fl->offset += s->size;
699                         fl->size -= s->size;
700
701                         /* The subdisk uses the whole slot, so remove it. */
702                         if (fl->size == 0) {
703                                 d->freelist_entries--;
704                                 LIST_REMOVE(fl, freelist);
705                         }
706                 /*
707                  * The subdisk does not start at the beginning of the free
708                  * slot.
709                  */
710                 } else {
711                         tmp = fl->offset + fl->size;
712                         fl->size = s->drive_offset - fl->offset;
713
714                         /*
715                          * The subdisk didn't use the complete rest of the free
716                          * slot, so we need to split it.
717                          */
718                         if (s->drive_offset + s->size != tmp) {
719                                 fl2 = g_malloc(sizeof(*fl2), M_WAITOK | M_ZERO);
720                                 fl2->offset = s->drive_offset + s->size;
721                                 fl2->size = tmp - fl2->offset;
722                                 LIST_INSERT_AFTER(fl, fl2, freelist);
723                                 d->freelist_entries++;
724                         }
725                 }
726                 break;
727         }
728
729         /*
730          * This is the first subdisk on this drive, just insert it into the
731          * list.
732          */
733         if (LIST_EMPTY(&d->subdisks)) {
734                 LIST_INSERT_HEAD(&d->subdisks, s, from_drive);
735
736         /* There are other subdisks, so insert this one in correct order. */
737         } else {
738                 LIST_FOREACH(s2, &d->subdisks, from_drive) {
739                         if (s->drive_offset < s2->drive_offset) {
740                                 LIST_INSERT_BEFORE(s2, s, from_drive);
741                                 break;
742                         } else if (LIST_NEXT(s2, from_drive) == NULL) {
743                                 LIST_INSERT_AFTER(s2, s, from_drive);
744                                 break;
745                         }
746                 }
747         }
748
749         d->sdcount++;
750         d->avail -= s->size;
751
752         s->flags &= ~GV_SD_TASTED;
753
754         /* Link back from the subdisk to this drive. */
755         s->drive_sc = d;
756
757         return (0);
758 }
759
760 void
761 gv_free_sd(struct gv_sd *s)
762 {
763         struct gv_drive *d;
764         struct gv_freelist *fl, *fl2;
765
766         KASSERT(s != NULL, ("gv_free_sd: NULL s"));
767
768         d = s->drive_sc;
769         if (d == NULL)
770                 return;
771
772         /*
773          * First, find the free slot that's immediately before or after this
774          * subdisk.
775          */
776         fl = NULL;
777         LIST_FOREACH(fl, &d->freelist, freelist) {
778                 if (fl->offset == s->drive_offset + s->size)
779                         break;
780                 if (fl->offset + fl->size == s->drive_offset)
781                         break;
782         }
783
784         /* If there is no free slot behind this subdisk, so create one. */
785         if (fl == NULL) {
786
787                 fl = g_malloc(sizeof(*fl), M_WAITOK | M_ZERO);
788                 fl->size = s->size;
789                 fl->offset = s->drive_offset;
790
791                 if (d->freelist_entries == 0) {
792                         LIST_INSERT_HEAD(&d->freelist, fl, freelist);
793                 } else {
794                         LIST_FOREACH(fl2, &d->freelist, freelist) {
795                                 if (fl->offset < fl2->offset) {
796                                         LIST_INSERT_BEFORE(fl2, fl, freelist);
797                                         break;
798                                 } else if (LIST_NEXT(fl2, freelist) == NULL) {
799                                         LIST_INSERT_AFTER(fl2, fl, freelist);
800                                         break;
801                                 }
802                         }
803                 }
804
805                 d->freelist_entries++;
806
807         /* Expand the free slot we just found. */
808         } else {
809                 fl->size += s->size;
810                 if (fl->offset > s->drive_offset)
811                         fl->offset = s->drive_offset;
812         }
813
814         d->avail += s->size;
815         d->sdcount--;
816 }
817
818 void
819 gv_adjust_freespace(struct gv_sd *s, off_t remainder)
820 {
821         struct gv_drive *d;
822         struct gv_freelist *fl, *fl2;
823
824         KASSERT(s != NULL, ("gv_adjust_freespace: NULL s"));
825         d = s->drive_sc;
826         KASSERT(d != NULL, ("gv_adjust_freespace: NULL d"));
827
828         /* First, find the free slot that's immediately after this subdisk. */
829         fl = NULL;
830         LIST_FOREACH(fl, &d->freelist, freelist) {
831                 if (fl->offset == s->drive_offset + s->size)
832                         break;
833         }
834
835         /* If there is no free slot behind this subdisk, so create one. */
836         if (fl == NULL) {
837
838                 fl = g_malloc(sizeof(*fl), M_WAITOK | M_ZERO);
839                 fl->size = remainder;
840                 fl->offset = s->drive_offset + s->size - remainder;
841
842                 if (d->freelist_entries == 0) {
843                         LIST_INSERT_HEAD(&d->freelist, fl, freelist);
844                 } else {
845                         LIST_FOREACH(fl2, &d->freelist, freelist) {
846                                 if (fl->offset < fl2->offset) {
847                                         LIST_INSERT_BEFORE(fl2, fl, freelist);
848                                         break;
849                                 } else if (LIST_NEXT(fl2, freelist) == NULL) {
850                                         LIST_INSERT_AFTER(fl2, fl, freelist);
851                                         break;
852                                 }
853                         }
854                 }
855
856                 d->freelist_entries++;
857
858         /* Expand the free slot we just found. */
859         } else {
860                 fl->offset -= remainder;
861                 fl->size += remainder;
862         }
863
864         s->size -= remainder;
865         d->avail += remainder;
866 }
867
868 /* Check if the given plex is a striped one. */
869 int
870 gv_is_striped(struct gv_plex *p)
871 {
872         KASSERT(p != NULL, ("gv_is_striped: NULL p"));
873         switch(p->org) {
874         case GV_PLEX_STRIPED:
875         case GV_PLEX_RAID5:
876                 return (1);
877         default:
878                 return (0);
879         }
880 }
881
882 /* Find a volume by name. */
883 struct gv_volume *
884 gv_find_vol(struct gv_softc *sc, char *name)
885 {
886         struct gv_volume *v;
887
888         LIST_FOREACH(v, &sc->volumes, volume) {
889                 if (!strncmp(v->name, name, GV_MAXVOLNAME))
890                         return (v);
891         }
892
893         return (NULL);
894 }
895
896 /* Find a plex by name. */
897 struct gv_plex *
898 gv_find_plex(struct gv_softc *sc, char *name)
899 {
900         struct gv_plex *p;
901
902         LIST_FOREACH(p, &sc->plexes, plex) {
903                 if (!strncmp(p->name, name, GV_MAXPLEXNAME))
904                         return (p);
905         }
906
907         return (NULL);
908 }
909
910 /* Find a subdisk by name. */
911 struct gv_sd *
912 gv_find_sd(struct gv_softc *sc, char *name)
913 {
914         struct gv_sd *s;
915
916         LIST_FOREACH(s, &sc->subdisks, sd) {
917                 if (!strncmp(s->name, name, GV_MAXSDNAME))
918                         return (s);
919         }
920
921         return (NULL);
922 }
923
924 /* Find a drive by name. */
925 struct gv_drive *
926 gv_find_drive(struct gv_softc *sc, char *name)
927 {
928         struct gv_drive *d;
929
930         LIST_FOREACH(d, &sc->drives, drive) {
931                 if (!strncmp(d->name, name, GV_MAXDRIVENAME))
932                         return (d);
933         }
934
935         return (NULL);
936 }
937
938 /* Find a drive given a device. */
939 struct gv_drive *
940 gv_find_drive_device(struct gv_softc *sc, char *device)
941 {
942         struct gv_drive *d;
943
944         LIST_FOREACH(d, &sc->drives, drive) {
945                 if(!strcmp(d->device, device))
946                         return (d);
947         }
948
949         return (NULL);
950 }
951
952 /* Check if any consumer of the given geom is open. */
953 int
954 gv_consumer_is_open(struct g_consumer *cp)
955 {
956         if (cp == NULL)
957                 return (0);
958
959         if (cp->acr || cp->acw || cp->ace)
960                 return (1);
961
962         return (0);
963 }
964
965 int
966 gv_provider_is_open(struct g_provider *pp)
967 {
968         if (pp == NULL)
969                 return (0);
970
971         if (pp->acr || pp->acw || pp->ace)
972                 return (1);
973
974         return (0);
975 }
976
977 /*
978  * Compare the modification dates of the drives.
979  * Return 1 if a > b, 0 otherwise.
980  */
981 int
982 gv_drive_is_newer(struct gv_softc *sc, struct gv_drive *d)
983 {
984         struct gv_drive *d2;
985         struct timeval *a, *b;
986
987         KASSERT(!LIST_EMPTY(&sc->drives),
988             ("gv_is_drive_newer: empty drive list"));
989
990         a = &d->hdr->label.last_update;
991         LIST_FOREACH(d2, &sc->drives, drive) {
992                 if ((d == d2) || (d2->state != GV_DRIVE_UP) ||
993                     (d2->hdr == NULL))
994                         continue;
995                 b = &d2->hdr->label.last_update;
996                 if (timevalcmp(a, b, >))
997                         return (1);
998         }
999
1000         return (0);
1001 }
1002
1003 /* Return the type of object identified by string 'name'. */
1004 int
1005 gv_object_type(struct gv_softc *sc, char *name)
1006 {
1007         struct gv_drive *d;
1008         struct gv_plex *p;
1009         struct gv_sd *s;
1010         struct gv_volume *v;
1011
1012         LIST_FOREACH(v, &sc->volumes, volume) {
1013                 if (!strncmp(v->name, name, GV_MAXVOLNAME))
1014                         return (GV_TYPE_VOL);
1015         }
1016
1017         LIST_FOREACH(p, &sc->plexes, plex) {
1018                 if (!strncmp(p->name, name, GV_MAXPLEXNAME))
1019                         return (GV_TYPE_PLEX);
1020         }
1021
1022         LIST_FOREACH(s, &sc->subdisks, sd) {
1023                 if (!strncmp(s->name, name, GV_MAXSDNAME))
1024                         return (GV_TYPE_SD);
1025         }
1026
1027         LIST_FOREACH(d, &sc->drives, drive) {
1028                 if (!strncmp(d->name, name, GV_MAXDRIVENAME))
1029                         return (GV_TYPE_DRIVE);
1030         }
1031
1032         return (GV_ERR_NOTFOUND);
1033 }
1034
1035 void
1036 gv_setup_objects(struct gv_softc *sc)
1037 {
1038         struct g_provider *pp;
1039         struct gv_volume *v;
1040         struct gv_plex *p;
1041         struct gv_sd *s;
1042         struct gv_drive *d;
1043
1044         LIST_FOREACH(s, &sc->subdisks, sd) {
1045                 d = gv_find_drive(sc, s->drive);
1046                 if (d != NULL)
1047                         gv_sd_to_drive(s, d);
1048                 p = gv_find_plex(sc, s->plex);
1049                 if (p != NULL)
1050                         gv_sd_to_plex(s, p);
1051                 gv_update_sd_state(s);
1052         }
1053
1054         LIST_FOREACH(p, &sc->plexes, plex) {
1055                 gv_update_plex_config(p);
1056                 v = gv_find_vol(sc, p->volume);
1057                 if (v != NULL && p->vol_sc != v) {
1058                         p->vol_sc = v;
1059                         v->plexcount++;
1060                         LIST_INSERT_HEAD(&v->plexes, p, in_volume);
1061                 }
1062                 gv_update_plex_config(p);
1063         }
1064
1065         LIST_FOREACH(v, &sc->volumes, volume) {
1066                 v->size = gv_vol_size(v);
1067                 if (v->provider == NULL) {
1068                         g_topology_lock();
1069                         pp = g_new_providerf(sc->geom, "gvinum/%s", v->name);
1070                         pp->mediasize = v->size;
1071                         pp->sectorsize = 512;    /* XXX */
1072                         g_error_provider(pp, 0);
1073                         v->provider = pp;
1074                         pp->private = v;
1075                         g_topology_unlock();
1076                 } else if (v->provider->mediasize != v->size) {
1077                         g_topology_lock();
1078                         v->provider->mediasize = v->size;
1079                         g_topology_unlock();
1080                 }
1081                 v->flags &= ~GV_VOL_NEWBORN;
1082                 gv_update_vol_state(v);
1083         }
1084 }
1085
1086 void
1087 gv_cleanup(struct gv_softc *sc)
1088 {
1089         struct gv_volume *v, *v2;
1090         struct gv_plex *p, *p2;
1091         struct gv_sd *s, *s2;
1092         struct gv_drive *d, *d2;
1093         struct gv_freelist *fl, *fl2;
1094
1095         mtx_lock(&sc->config_mtx);
1096         LIST_FOREACH_SAFE(v, &sc->volumes, volume, v2) {
1097                 LIST_REMOVE(v, volume);
1098                 g_free(v->wqueue);
1099                 g_free(v);
1100         }
1101         LIST_FOREACH_SAFE(p, &sc->plexes, plex, p2) {
1102                 LIST_REMOVE(p, plex);
1103                 g_free(p->bqueue);
1104                 g_free(p->rqueue);
1105                 g_free(p->wqueue);
1106                 g_free(p);
1107         }
1108         LIST_FOREACH_SAFE(s, &sc->subdisks, sd, s2) {
1109                 LIST_REMOVE(s, sd);
1110                 g_free(s);
1111         }
1112         LIST_FOREACH_SAFE(d, &sc->drives, drive, d2) {
1113                 LIST_FOREACH_SAFE(fl, &d->freelist, freelist, fl2) {
1114                         LIST_REMOVE(fl, freelist);
1115                         g_free(fl);
1116                 }
1117                 LIST_REMOVE(d, drive);
1118                 g_free(d->hdr);
1119                 g_free(d);
1120         }
1121         mtx_destroy(&sc->config_mtx);
1122 }
1123
1124 /* General 'attach' routine. */
1125 int
1126 gv_attach_plex(struct gv_plex *p, struct gv_volume *v, int rename)
1127 {
1128         struct gv_sd *s;
1129         struct gv_softc *sc;
1130
1131         g_topology_assert();
1132
1133         sc = p->vinumconf;
1134         KASSERT(sc != NULL, ("NULL sc"));
1135
1136         if (p->vol_sc != NULL) {
1137                 G_VINUM_DEBUG(1, "unable to attach %s: already attached to %s",
1138                     p->name, p->volume);
1139                 return (GV_ERR_ISATTACHED);
1140         }
1141
1142         /* Stale all subdisks of this plex. */
1143         LIST_FOREACH(s, &p->subdisks, in_plex) {
1144                 if (s->state != GV_SD_STALE)
1145                         gv_set_sd_state(s, GV_SD_STALE, GV_SETSTATE_FORCE);
1146         }
1147         /* Attach to volume. Make sure volume is not up and running. */
1148         if (gv_provider_is_open(v->provider)) {
1149                 G_VINUM_DEBUG(1, "unable to attach %s: volume %s is busy",
1150                     p->name, v->name);
1151                 return (GV_ERR_ISBUSY);
1152         }
1153         p->vol_sc = v;
1154         strlcpy(p->volume, v->name, sizeof(p->volume));
1155         v->plexcount++;
1156         if (rename) {
1157                 snprintf(p->name, sizeof(p->name), "%s.p%d", v->name,
1158                     v->plexcount);
1159         }
1160         LIST_INSERT_HEAD(&v->plexes, p, in_volume);
1161
1162         /* Get plex up again. */
1163         gv_update_vol_size(v, gv_vol_size(v));
1164         gv_set_plex_state(p, GV_PLEX_UP, 0);
1165         gv_save_config(p->vinumconf);
1166         return (0);
1167 }
1168
1169 int
1170 gv_attach_sd(struct gv_sd *s, struct gv_plex *p, off_t offset, int rename)
1171 {
1172         struct gv_sd *s2;
1173         int error, sdcount;
1174
1175         g_topology_assert();
1176
1177         /* If subdisk is attached, don't do it. */
1178         if (s->plex_sc != NULL) {
1179                 G_VINUM_DEBUG(1, "unable to attach %s: already attached to %s",
1180                     s->name, s->plex);
1181                 return (GV_ERR_ISATTACHED);
1182         }
1183
1184         gv_set_sd_state(s, GV_SD_STALE, GV_SETSTATE_FORCE);
1185         /* First check that this subdisk has a correct offset. If none other
1186          * starts at the same, and it's correct module stripesize, it is */
1187         if (offset != -1 && offset % p->stripesize != 0)
1188                 return (GV_ERR_BADOFFSET);
1189         LIST_FOREACH(s2, &p->subdisks, in_plex) {
1190                 if (s2->plex_offset == offset)
1191                         return (GV_ERR_BADOFFSET);
1192         }
1193
1194         /* Attach the subdisk to the plex at given offset. */
1195         s->plex_offset = offset;
1196         strlcpy(s->plex, p->name, sizeof(s->plex));
1197
1198         sdcount = p->sdcount;
1199         error = gv_sd_to_plex(s, p);
1200         if (error)
1201                 return (error);
1202         gv_update_plex_config(p);
1203
1204         if (rename) {
1205                 snprintf(s->name, sizeof(s->name), "%s.s%d", s->plex,
1206                     p->sdcount);
1207         }
1208         if (p->vol_sc != NULL)
1209                 gv_update_vol_size(p->vol_sc, gv_vol_size(p->vol_sc));
1210         gv_save_config(p->vinumconf);
1211         /* We don't update the subdisk state since the user might have to
1212          * initiate a rebuild/sync first. */
1213         return (0);
1214 }
1215
1216 /* Detach a plex from a volume. */
1217 int
1218 gv_detach_plex(struct gv_plex *p, int flags)
1219 {
1220         struct gv_volume *v;
1221
1222         g_topology_assert();
1223         v = p->vol_sc;
1224
1225         if (v == NULL) {
1226                 G_VINUM_DEBUG(1, "unable to detach %s: already detached",
1227                     p->name);
1228                 return (0); /* Not an error. */
1229         }
1230
1231         /*
1232          * Only proceed if forced or volume inactive.
1233          */
1234         if (!(flags & GV_FLAG_F) && (gv_provider_is_open(v->provider) ||
1235             p->state == GV_PLEX_UP)) {
1236                 G_VINUM_DEBUG(1, "unable to detach %s: volume %s is busy",
1237                     p->name, p->volume);
1238                 return (GV_ERR_ISBUSY);
1239         }
1240         v->plexcount--;
1241         /* Make sure someone don't read us when gone. */
1242         v->last_read_plex = NULL; 
1243         LIST_REMOVE(p, in_volume);
1244         p->vol_sc = NULL;
1245         memset(p->volume, 0, GV_MAXVOLNAME);
1246         gv_update_vol_size(v, gv_vol_size(v));
1247         gv_save_config(p->vinumconf);
1248         return (0);
1249 }
1250
1251 /* Detach a subdisk from a plex. */
1252 int
1253 gv_detach_sd(struct gv_sd *s, int flags)
1254 {
1255         struct gv_plex *p;
1256
1257         g_topology_assert();
1258         p = s->plex_sc;
1259
1260         if (p == NULL) {
1261                 G_VINUM_DEBUG(1, "unable to detach %s: already detached",
1262                     s->name);
1263                 return (0); /* Not an error. */
1264         }
1265
1266         /*
1267          * Don't proceed if we're not forcing, and the plex is up, or degraded
1268          * with this subdisk up.
1269          */
1270         if (!(flags & GV_FLAG_F) && ((p->state > GV_PLEX_DEGRADED) ||
1271             ((p->state == GV_PLEX_DEGRADED) && (s->state == GV_SD_UP)))) {
1272                 G_VINUM_DEBUG(1, "unable to detach %s: plex %s is busy",
1273                     s->name, s->plex);
1274                 return (GV_ERR_ISBUSY);
1275         }
1276
1277         LIST_REMOVE(s, in_plex);
1278         s->plex_sc = NULL;
1279         memset(s->plex, 0, GV_MAXPLEXNAME);
1280         p->sddetached++;
1281         gv_save_config(s->vinumconf);
1282         return (0);
1283 }