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