173679edcSLukas Ertl /*-
2df57947fSPedro F. Giffuni * SPDX-License-Identifier: BSD-4-Clause
3df57947fSPedro F. Giffuni *
4c0b9797aSUlf Lilleengen * Copyright (c) 2004, 2007 Lukas Ertl
5c0b9797aSUlf Lilleengen * Copyright (c) 2007, 2009 Ulf Lilleengen
673679edcSLukas Ertl * Copyright (c) 1997, 1998, 1999
773679edcSLukas Ertl * Nan Yang Computer Services Limited. All rights reserved.
873679edcSLukas Ertl *
973679edcSLukas Ertl * Parts written by Greg Lehey
1073679edcSLukas Ertl *
1173679edcSLukas Ertl * This software is distributed under the so-called ``Berkeley
1273679edcSLukas Ertl * License'':
1373679edcSLukas Ertl *
1473679edcSLukas Ertl * Redistribution and use in source and binary forms, with or without
1573679edcSLukas Ertl * modification, are permitted provided that the following conditions
1673679edcSLukas Ertl * are met:
1773679edcSLukas Ertl * 1. Redistributions of source code must retain the above copyright
1873679edcSLukas Ertl * notice, this list of conditions and the following disclaimer.
1973679edcSLukas Ertl * 2. Redistributions in binary form must reproduce the above copyright
2073679edcSLukas Ertl * notice, this list of conditions and the following disclaimer in the
2173679edcSLukas Ertl * documentation and/or other materials provided with the distribution.
2273679edcSLukas Ertl * 3. All advertising materials mentioning features or use of this software
2373679edcSLukas Ertl * must display the following acknowledgement:
2473679edcSLukas Ertl * This product includes software developed by Nan Yang Computer
2573679edcSLukas Ertl * Services Limited.
2673679edcSLukas Ertl * 4. Neither the name of the Company nor the names of its contributors
2773679edcSLukas Ertl * may be used to endorse or promote products derived from this software
2873679edcSLukas Ertl * without specific prior written permission.
2973679edcSLukas Ertl *
3073679edcSLukas Ertl * This software is provided ``as is'', and any express or implied
3173679edcSLukas Ertl * warranties, including, but not limited to, the implied warranties of
3273679edcSLukas Ertl * merchantability and fitness for a particular purpose are disclaimed.
3373679edcSLukas Ertl * In no event shall the company or contributors be liable for any
3473679edcSLukas Ertl * direct, indirect, incidental, special, exemplary, or consequential
3573679edcSLukas Ertl * damages (including, but not limited to, procurement of substitute
3673679edcSLukas Ertl * goods or services; loss of use, data, or profits; or business
3773679edcSLukas Ertl * interruption) however caused and on any theory of liability, whether
3873679edcSLukas Ertl * in contract, strict liability, or tort (including negligence or
3973679edcSLukas Ertl * otherwise) arising in any way out of the use of this software, even if
4073679edcSLukas Ertl * advised of the possibility of such damage.
4173679edcSLukas Ertl *
4273679edcSLukas Ertl */
4373679edcSLukas Ertl
4473679edcSLukas Ertl #include <sys/param.h>
4573679edcSLukas Ertl #include <sys/malloc.h>
465d807a0eSAndrey V. Elsukov #include <sys/sbuf.h>
4773679edcSLukas Ertl #include <sys/systm.h>
4873679edcSLukas Ertl
4973679edcSLukas Ertl #include <geom/geom.h>
50ac03832eSConrad Meyer #include <geom/geom_dbg.h>
5173679edcSLukas Ertl #include <geom/vinum/geom_vinum_var.h>
5273679edcSLukas Ertl #include <geom/vinum/geom_vinum.h>
5373679edcSLukas Ertl #include <geom/vinum/geom_vinum_share.h>
5473679edcSLukas Ertl
55c0b9797aSUlf Lilleengen int gv_drive_is_newer(struct gv_softc *, struct gv_drive *);
56c0b9797aSUlf Lilleengen static off_t gv_plex_smallest_sd(struct gv_plex *);
57a2237c41SLukas Ertl
5873679edcSLukas Ertl void
gv_parse_config(struct gv_softc * sc,char * buf,struct gv_drive * d)59c0b9797aSUlf Lilleengen gv_parse_config(struct gv_softc *sc, char *buf, struct gv_drive *d)
6073679edcSLukas Ertl {
6173679edcSLukas Ertl char *aptr, *bptr, *cptr;
6273679edcSLukas Ertl struct gv_volume *v, *v2;
6373679edcSLukas Ertl struct gv_plex *p, *p2;
6473679edcSLukas Ertl struct gv_sd *s, *s2;
65c0b9797aSUlf Lilleengen int error, is_newer, tokens;
6673679edcSLukas Ertl char *token[GV_MAXARGS];
6773679edcSLukas Ertl
68c0b9797aSUlf Lilleengen is_newer = gv_drive_is_newer(sc, d);
6973679edcSLukas Ertl
7073679edcSLukas Ertl /* Until the end of the string *buf. */
7173679edcSLukas Ertl for (aptr = buf; *aptr != '\0'; aptr = bptr) {
7273679edcSLukas Ertl bptr = aptr;
7373679edcSLukas Ertl cptr = aptr;
7473679edcSLukas Ertl
75e8d57122SPedro F. Giffuni /* Separate input lines. */
7673679edcSLukas Ertl while (*bptr != '\n')
7773679edcSLukas Ertl bptr++;
7873679edcSLukas Ertl *bptr = '\0';
7973679edcSLukas Ertl bptr++;
8073679edcSLukas Ertl
8173679edcSLukas Ertl tokens = gv_tokenize(cptr, token, GV_MAXARGS);
8273679edcSLukas Ertl
83c0b9797aSUlf Lilleengen if (tokens <= 0)
84c0b9797aSUlf Lilleengen continue;
85c0b9797aSUlf Lilleengen
8673679edcSLukas Ertl if (!strcmp(token[0], "volume")) {
8773679edcSLukas Ertl v = gv_new_volume(tokens, token);
8873679edcSLukas Ertl if (v == NULL) {
89c0b9797aSUlf Lilleengen G_VINUM_DEBUG(0, "config parse failed volume");
9073679edcSLukas Ertl break;
9173679edcSLukas Ertl }
9273679edcSLukas Ertl
9373679edcSLukas Ertl v2 = gv_find_vol(sc, v->name);
9473679edcSLukas Ertl if (v2 != NULL) {
95c0b9797aSUlf Lilleengen if (is_newer) {
96c0b9797aSUlf Lilleengen v2->state = v->state;
97c0b9797aSUlf Lilleengen G_VINUM_DEBUG(2, "newer volume found!");
98c0b9797aSUlf Lilleengen }
9973679edcSLukas Ertl g_free(v);
10073679edcSLukas Ertl continue;
10173679edcSLukas Ertl }
10273679edcSLukas Ertl
103c0b9797aSUlf Lilleengen gv_create_volume(sc, v);
10473679edcSLukas Ertl
10573679edcSLukas Ertl } else if (!strcmp(token[0], "plex")) {
10673679edcSLukas Ertl p = gv_new_plex(tokens, token);
10773679edcSLukas Ertl if (p == NULL) {
108c0b9797aSUlf Lilleengen G_VINUM_DEBUG(0, "config parse failed plex");
10973679edcSLukas Ertl break;
11073679edcSLukas Ertl }
11173679edcSLukas Ertl
11273679edcSLukas Ertl p2 = gv_find_plex(sc, p->name);
11373679edcSLukas Ertl if (p2 != NULL) {
114c0b9797aSUlf Lilleengen /* XXX */
115c0b9797aSUlf Lilleengen if (is_newer) {
116c0b9797aSUlf Lilleengen p2->state = p->state;
117c0b9797aSUlf Lilleengen G_VINUM_DEBUG(2, "newer plex found!");
118c0b9797aSUlf Lilleengen }
11973679edcSLukas Ertl g_free(p);
12073679edcSLukas Ertl continue;
12173679edcSLukas Ertl }
12273679edcSLukas Ertl
123c0b9797aSUlf Lilleengen error = gv_create_plex(sc, p);
124c0b9797aSUlf Lilleengen if (error)
125c0b9797aSUlf Lilleengen continue;
126c0b9797aSUlf Lilleengen /*
127c0b9797aSUlf Lilleengen * These flags were set in gv_create_plex() and are not
128c0b9797aSUlf Lilleengen * needed here (on-disk config parsing).
129c0b9797aSUlf Lilleengen */
130c0b9797aSUlf Lilleengen p->flags &= ~GV_PLEX_ADDED;
13173679edcSLukas Ertl
13273679edcSLukas Ertl } else if (!strcmp(token[0], "sd")) {
13373679edcSLukas Ertl s = gv_new_sd(tokens, token);
13473679edcSLukas Ertl
13573679edcSLukas Ertl if (s == NULL) {
136c0b9797aSUlf Lilleengen G_VINUM_DEBUG(0, "config parse failed subdisk");
13773679edcSLukas Ertl break;
13873679edcSLukas Ertl }
13973679edcSLukas Ertl
14073679edcSLukas Ertl s2 = gv_find_sd(sc, s->name);
14173679edcSLukas Ertl if (s2 != NULL) {
142c0b9797aSUlf Lilleengen /* XXX */
143c0b9797aSUlf Lilleengen if (is_newer) {
144c0b9797aSUlf Lilleengen s2->state = s->state;
145c0b9797aSUlf Lilleengen G_VINUM_DEBUG(2, "newer subdisk found!");
146c0b9797aSUlf Lilleengen }
14773679edcSLukas Ertl g_free(s);
14873679edcSLukas Ertl continue;
14973679edcSLukas Ertl }
15073679edcSLukas Ertl
151c0b9797aSUlf Lilleengen /*
152c0b9797aSUlf Lilleengen * Signal that this subdisk was tasted, and could
153c0b9797aSUlf Lilleengen * possibly reference a drive that isn't in our config
154c0b9797aSUlf Lilleengen * yet.
155c0b9797aSUlf Lilleengen */
156c0b9797aSUlf Lilleengen s->flags |= GV_SD_TASTED;
157c0b9797aSUlf Lilleengen
158c0b9797aSUlf Lilleengen if (s->state == GV_SD_UP)
159c0b9797aSUlf Lilleengen s->flags |= GV_SD_CANGOUP;
160c0b9797aSUlf Lilleengen
161c0b9797aSUlf Lilleengen error = gv_create_sd(sc, s);
162c0b9797aSUlf Lilleengen if (error)
163c0b9797aSUlf Lilleengen continue;
164c0b9797aSUlf Lilleengen
165c0b9797aSUlf Lilleengen /*
166c0b9797aSUlf Lilleengen * This flag was set in gv_create_sd() and is not
167c0b9797aSUlf Lilleengen * needed here (on-disk config parsing).
168c0b9797aSUlf Lilleengen */
169c0b9797aSUlf Lilleengen s->flags &= ~GV_SD_NEWBORN;
170c0b9797aSUlf Lilleengen s->flags &= ~GV_SD_GROW;
17173679edcSLukas Ertl }
17273679edcSLukas Ertl }
17373679edcSLukas Ertl }
17473679edcSLukas Ertl
17573679edcSLukas Ertl /*
17673679edcSLukas Ertl * Format the vinum configuration properly. If ondisk is non-zero then the
17773679edcSLukas Ertl * configuration is intended to be written to disk later.
17873679edcSLukas Ertl */
17973679edcSLukas Ertl void
gv_format_config(struct gv_softc * sc,struct sbuf * sb,int ondisk,char * prefix)18073679edcSLukas Ertl gv_format_config(struct gv_softc *sc, struct sbuf *sb, int ondisk, char *prefix)
18173679edcSLukas Ertl {
18273679edcSLukas Ertl struct gv_drive *d;
18373679edcSLukas Ertl struct gv_sd *s;
18473679edcSLukas Ertl struct gv_plex *p;
18573679edcSLukas Ertl struct gv_volume *v;
18673679edcSLukas Ertl
18773679edcSLukas Ertl /*
18873679edcSLukas Ertl * We don't need the drive configuration if we're not writing the
18973679edcSLukas Ertl * config to disk.
19073679edcSLukas Ertl */
19173679edcSLukas Ertl if (!ondisk) {
19273679edcSLukas Ertl LIST_FOREACH(d, &sc->drives, drive) {
193d9d3a74cSLukas Ertl sbuf_printf(sb, "%sdrive %s device /dev/%s\n", prefix,
19473679edcSLukas Ertl d->name, d->device);
19573679edcSLukas Ertl }
19673679edcSLukas Ertl }
19773679edcSLukas Ertl
19873679edcSLukas Ertl LIST_FOREACH(v, &sc->volumes, volume) {
19973679edcSLukas Ertl if (!ondisk)
20073679edcSLukas Ertl sbuf_printf(sb, "%s", prefix);
20173679edcSLukas Ertl sbuf_printf(sb, "volume %s", v->name);
20273679edcSLukas Ertl if (ondisk)
20373679edcSLukas Ertl sbuf_printf(sb, " state %s", gv_volstate(v->state));
20473679edcSLukas Ertl sbuf_printf(sb, "\n");
20573679edcSLukas Ertl }
20673679edcSLukas Ertl
20773679edcSLukas Ertl LIST_FOREACH(p, &sc->plexes, plex) {
20873679edcSLukas Ertl if (!ondisk)
20973679edcSLukas Ertl sbuf_printf(sb, "%s", prefix);
21073679edcSLukas Ertl sbuf_printf(sb, "plex name %s org %s ", p->name,
21173679edcSLukas Ertl gv_plexorg(p->org));
21273679edcSLukas Ertl if (gv_is_striped(p))
21373679edcSLukas Ertl sbuf_printf(sb, "%ds ", p->stripesize / 512);
21473679edcSLukas Ertl if (p->vol_sc != NULL)
21573679edcSLukas Ertl sbuf_printf(sb, "vol %s", p->volume);
21673679edcSLukas Ertl if (ondisk)
21773679edcSLukas Ertl sbuf_printf(sb, " state %s", gv_plexstate(p->state));
21873679edcSLukas Ertl sbuf_printf(sb, "\n");
21973679edcSLukas Ertl }
22073679edcSLukas Ertl
22173679edcSLukas Ertl LIST_FOREACH(s, &sc->subdisks, sd) {
22273679edcSLukas Ertl if (!ondisk)
22373679edcSLukas Ertl sbuf_printf(sb, "%s", prefix);
22473679edcSLukas Ertl sbuf_printf(sb, "sd name %s drive %s len %jds driveoffset "
22573679edcSLukas Ertl "%jds", s->name, s->drive, s->size / 512,
22673679edcSLukas Ertl s->drive_offset / 512);
22773679edcSLukas Ertl if (s->plex_sc != NULL) {
22873679edcSLukas Ertl sbuf_printf(sb, " plex %s plexoffset %jds", s->plex,
22973679edcSLukas Ertl s->plex_offset / 512);
23073679edcSLukas Ertl }
23173679edcSLukas Ertl if (ondisk)
23273679edcSLukas Ertl sbuf_printf(sb, " state %s", gv_sdstate(s->state));
23373679edcSLukas Ertl sbuf_printf(sb, "\n");
23473679edcSLukas Ertl }
23573679edcSLukas Ertl }
23673679edcSLukas Ertl
237a2237c41SLukas Ertl static off_t
gv_plex_smallest_sd(struct gv_plex * p)238c0b9797aSUlf Lilleengen gv_plex_smallest_sd(struct gv_plex *p)
239a2237c41SLukas Ertl {
240a2237c41SLukas Ertl struct gv_sd *s;
241c0b9797aSUlf Lilleengen off_t smallest;
242a2237c41SLukas Ertl
243a2237c41SLukas Ertl KASSERT(p != NULL, ("gv_plex_smallest_sd: NULL p"));
244a2237c41SLukas Ertl
245c0b9797aSUlf Lilleengen s = LIST_FIRST(&p->subdisks);
246c0b9797aSUlf Lilleengen if (s == NULL)
247c0b9797aSUlf Lilleengen return (-1);
248c0b9797aSUlf Lilleengen smallest = s->size;
249a2237c41SLukas Ertl LIST_FOREACH(s, &p->subdisks, in_plex) {
250a2237c41SLukas Ertl if (s->size < smallest)
251a2237c41SLukas Ertl smallest = s->size;
252a2237c41SLukas Ertl }
253a2237c41SLukas Ertl return (smallest);
254a2237c41SLukas Ertl }
255a2237c41SLukas Ertl
256c0b9797aSUlf Lilleengen /* Walk over plexes in a volume and count how many are down. */
25773679edcSLukas Ertl int
gv_plexdown(struct gv_volume * v)258c0b9797aSUlf Lilleengen gv_plexdown(struct gv_volume *v)
259c0b9797aSUlf Lilleengen {
260c0b9797aSUlf Lilleengen int plexdown;
261c0b9797aSUlf Lilleengen struct gv_plex *p;
262c0b9797aSUlf Lilleengen
263c0b9797aSUlf Lilleengen KASSERT(v != NULL, ("gv_plexdown: NULL v"));
264c0b9797aSUlf Lilleengen
265c0b9797aSUlf Lilleengen plexdown = 0;
266c0b9797aSUlf Lilleengen
267c0b9797aSUlf Lilleengen LIST_FOREACH(p, &v->plexes, plex) {
268c0b9797aSUlf Lilleengen if (p->state == GV_PLEX_DOWN)
269c0b9797aSUlf Lilleengen plexdown++;
270c0b9797aSUlf Lilleengen }
271c0b9797aSUlf Lilleengen return (plexdown);
272c0b9797aSUlf Lilleengen }
273c0b9797aSUlf Lilleengen
274c0b9797aSUlf Lilleengen int
gv_sd_to_plex(struct gv_sd * s,struct gv_plex * p)275c0b9797aSUlf Lilleengen gv_sd_to_plex(struct gv_sd *s, struct gv_plex *p)
27673679edcSLukas Ertl {
27773679edcSLukas Ertl struct gv_sd *s2;
278c0b9797aSUlf Lilleengen off_t psizeorig, remainder, smallest;
27973679edcSLukas Ertl
28073679edcSLukas Ertl /* If this subdisk was already given to this plex, do nothing. */
28173679edcSLukas Ertl if (s->plex_sc == p)
28273679edcSLukas Ertl return (0);
28373679edcSLukas Ertl
284ff91880eSLukas Ertl /* Check correct size of this subdisk. */
285ff91880eSLukas Ertl s2 = LIST_FIRST(&p->subdisks);
286c0b9797aSUlf Lilleengen /* Adjust the subdisk-size if necessary. */
287c0b9797aSUlf Lilleengen if (s2 != NULL && gv_is_striped(p)) {
288c0b9797aSUlf Lilleengen /* First adjust to the stripesize. */
289c0b9797aSUlf Lilleengen remainder = s->size % p->stripesize;
290c0b9797aSUlf Lilleengen
291c0b9797aSUlf Lilleengen if (remainder) {
292c0b9797aSUlf Lilleengen G_VINUM_DEBUG(1, "size of sd %s is not a "
293c0b9797aSUlf Lilleengen "multiple of plex stripesize, taking off "
294c0b9797aSUlf Lilleengen "%jd bytes", s->name,
295c0b9797aSUlf Lilleengen (intmax_t)remainder);
296c0b9797aSUlf Lilleengen gv_adjust_freespace(s, remainder);
297c0b9797aSUlf Lilleengen }
298c0b9797aSUlf Lilleengen
299c0b9797aSUlf Lilleengen smallest = gv_plex_smallest_sd(p);
300c0b9797aSUlf Lilleengen /* Then take off extra if other subdisks are smaller. */
301c0b9797aSUlf Lilleengen remainder = s->size - smallest;
302c0b9797aSUlf Lilleengen
303c0b9797aSUlf Lilleengen /*
304c0b9797aSUlf Lilleengen * Don't allow a remainder below zero for running plexes, it's too
305c0b9797aSUlf Lilleengen * painful, and if someone were to accidentally do this, the
306c0b9797aSUlf Lilleengen * resulting array might be smaller than the original... not god
307c0b9797aSUlf Lilleengen */
308c0b9797aSUlf Lilleengen if (remainder < 0) {
309c0b9797aSUlf Lilleengen if (!(p->flags & GV_PLEX_NEWBORN)) {
310c0b9797aSUlf Lilleengen G_VINUM_DEBUG(0, "sd %s too small for plex %s!",
311c0b9797aSUlf Lilleengen s->name, p->name);
312c0b9797aSUlf Lilleengen return (GV_ERR_BADSIZE);
313c0b9797aSUlf Lilleengen }
314c0b9797aSUlf Lilleengen /* Adjust other subdisks. */
315c0b9797aSUlf Lilleengen LIST_FOREACH(s2, &p->subdisks, in_plex) {
316c0b9797aSUlf Lilleengen G_VINUM_DEBUG(1, "size of sd %s is to big, "
317c0b9797aSUlf Lilleengen "taking off %jd bytes", s->name,
318c0b9797aSUlf Lilleengen (intmax_t)remainder);
319c0b9797aSUlf Lilleengen gv_adjust_freespace(s2, (remainder * -1));
320c0b9797aSUlf Lilleengen }
321c0b9797aSUlf Lilleengen } else if (remainder > 0) {
322c0b9797aSUlf Lilleengen G_VINUM_DEBUG(1, "size of sd %s is to big, "
323c0b9797aSUlf Lilleengen "taking off %jd bytes", s->name,
324c0b9797aSUlf Lilleengen (intmax_t)remainder);
325c0b9797aSUlf Lilleengen gv_adjust_freespace(s, remainder);
326c0b9797aSUlf Lilleengen }
327ff91880eSLukas Ertl }
328ff91880eSLukas Ertl
32973679edcSLukas Ertl /* Find the correct plex offset for this subdisk, if needed. */
33073679edcSLukas Ertl if (s->plex_offset == -1) {
331c0b9797aSUlf Lilleengen /*
332c0b9797aSUlf Lilleengen * First set it to 0 to catch the case where we had a detached
333c0b9797aSUlf Lilleengen * subdisk that didn't get any good offset.
334c0b9797aSUlf Lilleengen */
335c0b9797aSUlf Lilleengen s->plex_offset = 0;
33673679edcSLukas Ertl if (p->sdcount) {
33773679edcSLukas Ertl LIST_FOREACH(s2, &p->subdisks, in_plex) {
33873679edcSLukas Ertl if (gv_is_striped(p))
33973679edcSLukas Ertl s->plex_offset = p->sdcount *
34073679edcSLukas Ertl p->stripesize;
34173679edcSLukas Ertl else
34273679edcSLukas Ertl s->plex_offset = s2->plex_offset +
34373679edcSLukas Ertl s2->size;
34473679edcSLukas Ertl }
34573679edcSLukas Ertl }
34673679edcSLukas Ertl }
34773679edcSLukas Ertl
34873679edcSLukas Ertl /* There are no subdisks for this plex yet, just insert it. */
34973679edcSLukas Ertl if (LIST_EMPTY(&p->subdisks)) {
35073679edcSLukas Ertl LIST_INSERT_HEAD(&p->subdisks, s, in_plex);
35173679edcSLukas Ertl
35273679edcSLukas Ertl /* Insert in correct order, depending on plex_offset. */
35373679edcSLukas Ertl } else {
35473679edcSLukas Ertl LIST_FOREACH(s2, &p->subdisks, in_plex) {
35573679edcSLukas Ertl if (s->plex_offset < s2->plex_offset) {
35673679edcSLukas Ertl LIST_INSERT_BEFORE(s2, s, in_plex);
35773679edcSLukas Ertl break;
35873679edcSLukas Ertl } else if (LIST_NEXT(s2, in_plex) == NULL) {
35973679edcSLukas Ertl LIST_INSERT_AFTER(s2, s, in_plex);
36073679edcSLukas Ertl break;
36173679edcSLukas Ertl }
36273679edcSLukas Ertl }
36373679edcSLukas Ertl }
36473679edcSLukas Ertl
36573679edcSLukas Ertl s->plex_sc = p;
366c0b9797aSUlf Lilleengen /* Adjust the size of our plex. We check if the plex misses a subdisk,
367c0b9797aSUlf Lilleengen * so we don't make the plex smaller than it actually should be.
368c0b9797aSUlf Lilleengen */
369c0b9797aSUlf Lilleengen psizeorig = p->size;
370c0b9797aSUlf Lilleengen p->size = gv_plex_size(p);
371c0b9797aSUlf Lilleengen /* Make sure the size is not changed. */
372c0b9797aSUlf Lilleengen if (p->sddetached > 0) {
373c0b9797aSUlf Lilleengen if (p->size < psizeorig) {
374c0b9797aSUlf Lilleengen p->size = psizeorig;
375c0b9797aSUlf Lilleengen /* We make sure wee need another subdisk. */
376c0b9797aSUlf Lilleengen if (p->sddetached == 1)
377c0b9797aSUlf Lilleengen p->sddetached++;
378c0b9797aSUlf Lilleengen }
379c0b9797aSUlf Lilleengen p->sddetached--;
380c0b9797aSUlf Lilleengen } else {
381c0b9797aSUlf Lilleengen if ((p->org == GV_PLEX_RAID5 ||
382c0b9797aSUlf Lilleengen p->org == GV_PLEX_STRIPED) &&
383c0b9797aSUlf Lilleengen !(p->flags & GV_PLEX_NEWBORN) &&
38441944888SUlf Lilleengen p->state == GV_PLEX_UP) {
385c0b9797aSUlf Lilleengen s->flags |= GV_SD_GROW;
386c0b9797aSUlf Lilleengen }
387c0b9797aSUlf Lilleengen p->sdcount++;
388c0b9797aSUlf Lilleengen }
38973679edcSLukas Ertl
39073679edcSLukas Ertl return (0);
39173679edcSLukas Ertl }
39273679edcSLukas Ertl
39373679edcSLukas Ertl void
gv_update_vol_size(struct gv_volume * v,off_t size)3944b017d0dSLukas Ertl gv_update_vol_size(struct gv_volume *v, off_t size)
3954b017d0dSLukas Ertl {
3964b017d0dSLukas Ertl if (v == NULL)
3974b017d0dSLukas Ertl return;
398c0b9797aSUlf Lilleengen if (v->provider != NULL) {
399c0b9797aSUlf Lilleengen g_topology_lock();
400c0b9797aSUlf Lilleengen v->provider->mediasize = size;
401c0b9797aSUlf Lilleengen g_topology_unlock();
402c0b9797aSUlf Lilleengen }
403c0b9797aSUlf Lilleengen v->size = size;
4044b017d0dSLukas Ertl }
4054b017d0dSLukas Ertl
406c0b9797aSUlf Lilleengen /* Return how many subdisks that constitute the original plex. */
407c0b9797aSUlf Lilleengen int
gv_sdcount(struct gv_plex * p,int growing)408c0b9797aSUlf Lilleengen gv_sdcount(struct gv_plex *p, int growing)
409c0b9797aSUlf Lilleengen {
410c0b9797aSUlf Lilleengen struct gv_sd *s;
411c0b9797aSUlf Lilleengen int sdcount;
412c0b9797aSUlf Lilleengen
413c0b9797aSUlf Lilleengen sdcount = p->sdcount;
414c0b9797aSUlf Lilleengen if (growing) {
415c0b9797aSUlf Lilleengen LIST_FOREACH(s, &p->subdisks, in_plex) {
416c0b9797aSUlf Lilleengen if (s->flags & GV_SD_GROW)
417c0b9797aSUlf Lilleengen sdcount--;
418c0b9797aSUlf Lilleengen }
419c0b9797aSUlf Lilleengen }
420c0b9797aSUlf Lilleengen
421c0b9797aSUlf Lilleengen return (sdcount);
4224b017d0dSLukas Ertl }
4234b017d0dSLukas Ertl
424a2237c41SLukas Ertl /* Calculates the plex size. */
425a2237c41SLukas Ertl off_t
gv_plex_size(struct gv_plex * p)426a2237c41SLukas Ertl gv_plex_size(struct gv_plex *p)
427a2237c41SLukas Ertl {
428a2237c41SLukas Ertl struct gv_sd *s;
429a2237c41SLukas Ertl off_t size;
430c0b9797aSUlf Lilleengen int sdcount;
431a2237c41SLukas Ertl
432a2237c41SLukas Ertl KASSERT(p != NULL, ("gv_plex_size: NULL p"));
433a2237c41SLukas Ertl
434a2237c41SLukas Ertl /* Adjust the size of our plex. */
435a2237c41SLukas Ertl size = 0;
436c0b9797aSUlf Lilleengen sdcount = gv_sdcount(p, 1);
437a2237c41SLukas Ertl switch (p->org) {
438a2237c41SLukas Ertl case GV_PLEX_CONCAT:
439a2237c41SLukas Ertl LIST_FOREACH(s, &p->subdisks, in_plex)
440a2237c41SLukas Ertl size += s->size;
441a2237c41SLukas Ertl break;
442a2237c41SLukas Ertl case GV_PLEX_STRIPED:
443a2237c41SLukas Ertl s = LIST_FIRST(&p->subdisks);
444c0b9797aSUlf Lilleengen size = ((s != NULL) ? (sdcount * s->size) : 0);
445a2237c41SLukas Ertl break;
446a2237c41SLukas Ertl case GV_PLEX_RAID5:
447a2237c41SLukas Ertl s = LIST_FIRST(&p->subdisks);
448c0b9797aSUlf Lilleengen size = ((s != NULL) ? ((sdcount - 1) * s->size) : 0);
449a2237c41SLukas Ertl break;
450a2237c41SLukas Ertl }
451a2237c41SLukas Ertl
452a2237c41SLukas Ertl return (size);
453a2237c41SLukas Ertl }
454a2237c41SLukas Ertl
455a2237c41SLukas Ertl /* Returns the size of a volume. */
456a2237c41SLukas Ertl off_t
gv_vol_size(struct gv_volume * v)457a2237c41SLukas Ertl gv_vol_size(struct gv_volume *v)
458a2237c41SLukas Ertl {
459a2237c41SLukas Ertl struct gv_plex *p;
460a2237c41SLukas Ertl off_t minplexsize;
461a2237c41SLukas Ertl
462a2237c41SLukas Ertl KASSERT(v != NULL, ("gv_vol_size: NULL v"));
463a2237c41SLukas Ertl
464a2237c41SLukas Ertl p = LIST_FIRST(&v->plexes);
465a2237c41SLukas Ertl if (p == NULL)
466a2237c41SLukas Ertl return (0);
467a2237c41SLukas Ertl
468a2237c41SLukas Ertl minplexsize = p->size;
469c0b9797aSUlf Lilleengen LIST_FOREACH(p, &v->plexes, in_volume) {
470a2237c41SLukas Ertl if (p->size < minplexsize) {
471a2237c41SLukas Ertl minplexsize = p->size;
472a2237c41SLukas Ertl }
473a2237c41SLukas Ertl }
474a2237c41SLukas Ertl return (minplexsize);
475a2237c41SLukas Ertl }
476a2237c41SLukas Ertl
4774b017d0dSLukas Ertl void
gv_update_plex_config(struct gv_plex * p)47873679edcSLukas Ertl gv_update_plex_config(struct gv_plex *p)
47973679edcSLukas Ertl {
48073679edcSLukas Ertl struct gv_sd *s, *s2;
48173679edcSLukas Ertl off_t remainder;
48273679edcSLukas Ertl int required_sds, state;
48373679edcSLukas Ertl
48473679edcSLukas Ertl KASSERT(p != NULL, ("gv_update_plex_config: NULL p"));
48573679edcSLukas Ertl
48673679edcSLukas Ertl /* The plex was added to an already running volume. */
48773679edcSLukas Ertl if (p->flags & GV_PLEX_ADDED)
488c0b9797aSUlf Lilleengen gv_set_plex_state(p, GV_PLEX_DOWN, GV_SETSTATE_FORCE);
48973679edcSLukas Ertl
49073679edcSLukas Ertl switch (p->org) {
49173679edcSLukas Ertl case GV_PLEX_STRIPED:
49273679edcSLukas Ertl required_sds = 2;
49373679edcSLukas Ertl break;
49473679edcSLukas Ertl case GV_PLEX_RAID5:
49573679edcSLukas Ertl required_sds = 3;
49673679edcSLukas Ertl break;
49773679edcSLukas Ertl case GV_PLEX_CONCAT:
49873679edcSLukas Ertl default:
49973679edcSLukas Ertl required_sds = 0;
50073679edcSLukas Ertl break;
50173679edcSLukas Ertl }
50273679edcSLukas Ertl
50373679edcSLukas Ertl if (required_sds) {
50473679edcSLukas Ertl if (p->sdcount < required_sds) {
505c0b9797aSUlf Lilleengen gv_set_plex_state(p, GV_PLEX_DOWN, GV_SETSTATE_FORCE);
50673679edcSLukas Ertl }
50773679edcSLukas Ertl
50873679edcSLukas Ertl /*
50973679edcSLukas Ertl * The subdisks in striped plexes must all have the same size.
51073679edcSLukas Ertl */
51173679edcSLukas Ertl s = LIST_FIRST(&p->subdisks);
51273679edcSLukas Ertl LIST_FOREACH(s2, &p->subdisks, in_plex) {
51373679edcSLukas Ertl if (s->size != s2->size) {
51486b3c6f5SUlf Lilleengen G_VINUM_DEBUG(0, "subdisk size mismatch %s"
51586b3c6f5SUlf Lilleengen "(%jd) <> %s (%jd)", s->name, s->size,
51673679edcSLukas Ertl s2->name, s2->size);
517c0b9797aSUlf Lilleengen gv_set_plex_state(p, GV_PLEX_DOWN,
518c0b9797aSUlf Lilleengen GV_SETSTATE_FORCE);
51973679edcSLukas Ertl }
52073679edcSLukas Ertl }
52173679edcSLukas Ertl
52273679edcSLukas Ertl LIST_FOREACH(s, &p->subdisks, in_plex) {
523c0b9797aSUlf Lilleengen /* Trim subdisk sizes to match the stripe size. */
52473679edcSLukas Ertl remainder = s->size % p->stripesize;
52573679edcSLukas Ertl if (remainder) {
52686b3c6f5SUlf Lilleengen G_VINUM_DEBUG(1, "size of sd %s is not a "
52773679edcSLukas Ertl "multiple of plex stripesize, taking off "
52886b3c6f5SUlf Lilleengen "%jd bytes", s->name, (intmax_t)remainder);
52973679edcSLukas Ertl gv_adjust_freespace(s, remainder);
53073679edcSLukas Ertl }
53173679edcSLukas Ertl }
53273679edcSLukas Ertl }
53373679edcSLukas Ertl
534c0b9797aSUlf Lilleengen p->size = gv_plex_size(p);
53573679edcSLukas Ertl if (p->sdcount == 0)
536c0b9797aSUlf Lilleengen gv_set_plex_state(p, GV_PLEX_DOWN, GV_SETSTATE_FORCE);
537c0b9797aSUlf Lilleengen else if (p->org == GV_PLEX_RAID5 && p->flags & GV_PLEX_NEWBORN) {
53873679edcSLukas Ertl LIST_FOREACH(s, &p->subdisks, in_plex)
539c0b9797aSUlf Lilleengen gv_set_sd_state(s, GV_SD_UP, GV_SETSTATE_FORCE);
540c0b9797aSUlf Lilleengen /* If added to a volume, we want the plex to be down. */
541c0b9797aSUlf Lilleengen state = (p->flags & GV_PLEX_ADDED) ? GV_PLEX_DOWN : GV_PLEX_UP;
542c0b9797aSUlf Lilleengen gv_set_plex_state(p, state, GV_SETSTATE_FORCE);
54373679edcSLukas Ertl p->flags &= ~GV_PLEX_ADDED;
544c0b9797aSUlf Lilleengen } else if (p->flags & GV_PLEX_ADDED) {
545c0b9797aSUlf Lilleengen LIST_FOREACH(s, &p->subdisks, in_plex)
546c0b9797aSUlf Lilleengen gv_set_sd_state(s, GV_SD_STALE, GV_SETSTATE_FORCE);
547c0b9797aSUlf Lilleengen gv_set_plex_state(p, GV_PLEX_DOWN, GV_SETSTATE_FORCE);
548c0b9797aSUlf Lilleengen p->flags &= ~GV_PLEX_ADDED;
549c0b9797aSUlf Lilleengen } else if (p->state == GV_PLEX_UP) {
550c0b9797aSUlf Lilleengen LIST_FOREACH(s, &p->subdisks, in_plex) {
551c0b9797aSUlf Lilleengen if (s->flags & GV_SD_GROW) {
552c0b9797aSUlf Lilleengen gv_set_plex_state(p, GV_PLEX_GROWABLE,
553c0b9797aSUlf Lilleengen GV_SETSTATE_FORCE);
554c0b9797aSUlf Lilleengen break;
55573679edcSLukas Ertl }
556c0b9797aSUlf Lilleengen }
557c0b9797aSUlf Lilleengen }
558c0b9797aSUlf Lilleengen /* Our plex is grown up now. */
559c0b9797aSUlf Lilleengen p->flags &= ~GV_PLEX_NEWBORN;
56073679edcSLukas Ertl }
56173679edcSLukas Ertl
56273679edcSLukas Ertl /*
56373679edcSLukas Ertl * Give a subdisk to a drive, check and adjust several parameters, adjust
56473679edcSLukas Ertl * freelist.
56573679edcSLukas Ertl */
56673679edcSLukas Ertl int
gv_sd_to_drive(struct gv_sd * s,struct gv_drive * d)567c0b9797aSUlf Lilleengen gv_sd_to_drive(struct gv_sd *s, struct gv_drive *d)
56873679edcSLukas Ertl {
56973679edcSLukas Ertl struct gv_sd *s2;
57073679edcSLukas Ertl struct gv_freelist *fl, *fl2;
57173679edcSLukas Ertl off_t tmp;
57273679edcSLukas Ertl int i;
57373679edcSLukas Ertl
57473679edcSLukas Ertl fl2 = NULL;
57573679edcSLukas Ertl
576c0b9797aSUlf Lilleengen /* Shortcut for "referenced" drives. */
577c0b9797aSUlf Lilleengen if (d->flags & GV_DRIVE_REFERENCED) {
578c0b9797aSUlf Lilleengen s->drive_sc = d;
57973679edcSLukas Ertl return (0);
58073679edcSLukas Ertl }
58173679edcSLukas Ertl
582c0b9797aSUlf Lilleengen /* Check if this subdisk was already given to this drive. */
583c0b9797aSUlf Lilleengen if (s->drive_sc != NULL) {
584c0b9797aSUlf Lilleengen if (s->drive_sc == d) {
585c0b9797aSUlf Lilleengen if (!(s->flags & GV_SD_TASTED)) {
586c0b9797aSUlf Lilleengen return (0);
587c0b9797aSUlf Lilleengen }
588c0b9797aSUlf Lilleengen } else {
589a8a3cd7dSUlf Lilleengen G_VINUM_DEBUG(0, "error giving subdisk '%s' to '%s' "
590c0b9797aSUlf Lilleengen "(already on '%s')", s->name, d->name,
591c0b9797aSUlf Lilleengen s->drive_sc->name);
592c0b9797aSUlf Lilleengen return (GV_ERR_ISATTACHED);
593c0b9797aSUlf Lilleengen }
594c0b9797aSUlf Lilleengen }
595c0b9797aSUlf Lilleengen
596c0b9797aSUlf Lilleengen /* Preliminary checks. */
597c0b9797aSUlf Lilleengen if ((s->size > d->avail) || (d->freelist_entries == 0)) {
598c0b9797aSUlf Lilleengen G_VINUM_DEBUG(0, "not enough space on '%s' for '%s'", d->name,
599c0b9797aSUlf Lilleengen s->name);
600c0b9797aSUlf Lilleengen return (GV_ERR_NOSPACE);
601c0b9797aSUlf Lilleengen }
602c0b9797aSUlf Lilleengen
603c0b9797aSUlf Lilleengen /* If no size was given for this subdisk, try to auto-size it... */
60473679edcSLukas Ertl if (s->size == -1) {
60573679edcSLukas Ertl /* Find the largest available slot. */
60673679edcSLukas Ertl LIST_FOREACH(fl, &d->freelist, freelist) {
607c0b9797aSUlf Lilleengen if (fl->size < s->size)
608c0b9797aSUlf Lilleengen continue;
60973679edcSLukas Ertl s->size = fl->size;
61073679edcSLukas Ertl s->drive_offset = fl->offset;
61173679edcSLukas Ertl fl2 = fl;
61273679edcSLukas Ertl }
61373679edcSLukas Ertl
61473679edcSLukas Ertl /* No good slot found? */
61573679edcSLukas Ertl if (s->size == -1) {
616a8a3cd7dSUlf Lilleengen G_VINUM_DEBUG(0, "unable to autosize '%s' on '%s'",
617c0b9797aSUlf Lilleengen s->name, d->name);
618c0b9797aSUlf Lilleengen return (GV_ERR_BADSIZE);
61973679edcSLukas Ertl }
62073679edcSLukas Ertl
62173679edcSLukas Ertl /*
622c0b9797aSUlf Lilleengen * ... or check if we have a free slot that's large enough for the
623c0b9797aSUlf Lilleengen * given size.
62473679edcSLukas Ertl */
62573679edcSLukas Ertl } else {
62673679edcSLukas Ertl i = 0;
62773679edcSLukas Ertl LIST_FOREACH(fl, &d->freelist, freelist) {
628c0b9797aSUlf Lilleengen if (fl->size < s->size)
629c0b9797aSUlf Lilleengen continue;
630da8f1aa5SLukas Ertl /* Assign drive offset, if not given. */
631da8f1aa5SLukas Ertl if (s->drive_offset == -1)
63273679edcSLukas Ertl s->drive_offset = fl->offset;
63373679edcSLukas Ertl fl2 = fl;
634c0b9797aSUlf Lilleengen i++;
63573679edcSLukas Ertl break;
63673679edcSLukas Ertl }
63773679edcSLukas Ertl
63873679edcSLukas Ertl /* Couldn't find a good free slot. */
63973679edcSLukas Ertl if (i == 0) {
640c0b9797aSUlf Lilleengen G_VINUM_DEBUG(0, "free slots to small for '%s' on '%s'",
641c0b9797aSUlf Lilleengen s->name, d->name);
642c0b9797aSUlf Lilleengen return (GV_ERR_NOSPACE);
64373679edcSLukas Ertl }
64473679edcSLukas Ertl }
64573679edcSLukas Ertl
64673679edcSLukas Ertl /* No drive offset given, try to calculate it. */
64773679edcSLukas Ertl if (s->drive_offset == -1) {
64873679edcSLukas Ertl /* Add offsets and sizes from other subdisks on this drive. */
64973679edcSLukas Ertl LIST_FOREACH(s2, &d->subdisks, from_drive) {
65073679edcSLukas Ertl s->drive_offset = s2->drive_offset + s2->size;
65173679edcSLukas Ertl }
65273679edcSLukas Ertl
65373679edcSLukas Ertl /*
65473679edcSLukas Ertl * If there are no other subdisks yet, then set the default
65573679edcSLukas Ertl * offset to GV_DATA_START.
65673679edcSLukas Ertl */
657da8f1aa5SLukas Ertl if (s->drive_offset == -1)
65873679edcSLukas Ertl s->drive_offset = GV_DATA_START;
65973679edcSLukas Ertl
66073679edcSLukas Ertl /* Check if we have a free slot at the given drive offset. */
66173679edcSLukas Ertl } else {
66273679edcSLukas Ertl i = 0;
66373679edcSLukas Ertl LIST_FOREACH(fl, &d->freelist, freelist) {
66473679edcSLukas Ertl /* Yes, this subdisk fits. */
66573679edcSLukas Ertl if ((fl->offset <= s->drive_offset) &&
66673679edcSLukas Ertl (fl->offset + fl->size >=
66773679edcSLukas Ertl s->drive_offset + s->size)) {
66873679edcSLukas Ertl i++;
66973679edcSLukas Ertl fl2 = fl;
67073679edcSLukas Ertl break;
67173679edcSLukas Ertl }
67273679edcSLukas Ertl }
67373679edcSLukas Ertl
67473679edcSLukas Ertl /* Couldn't find a good free slot. */
67573679edcSLukas Ertl if (i == 0) {
676c0b9797aSUlf Lilleengen G_VINUM_DEBUG(0, "given drive_offset for '%s' won't fit "
677c0b9797aSUlf Lilleengen "on '%s'", s->name, d->name);
678c0b9797aSUlf Lilleengen return (GV_ERR_NOSPACE);
67973679edcSLukas Ertl }
68073679edcSLukas Ertl }
68173679edcSLukas Ertl
68273679edcSLukas Ertl /*
68373679edcSLukas Ertl * Now that all parameters are checked and set up, we can give the
68473679edcSLukas Ertl * subdisk to the drive and adjust the freelist.
68573679edcSLukas Ertl */
68673679edcSLukas Ertl
68773679edcSLukas Ertl /* First, adjust the freelist. */
68873679edcSLukas Ertl LIST_FOREACH(fl, &d->freelist, freelist) {
689c0b9797aSUlf Lilleengen /* Look for the free slot that we have found before. */
690c0b9797aSUlf Lilleengen if (fl != fl2)
691c0b9797aSUlf Lilleengen continue;
69273679edcSLukas Ertl
693c0b9797aSUlf Lilleengen /* The subdisk starts at the beginning of the free slot. */
69473679edcSLukas Ertl if (fl->offset == s->drive_offset) {
69573679edcSLukas Ertl fl->offset += s->size;
69673679edcSLukas Ertl fl->size -= s->size;
69773679edcSLukas Ertl
698c0b9797aSUlf Lilleengen /* The subdisk uses the whole slot, so remove it. */
69973679edcSLukas Ertl if (fl->size == 0) {
70073679edcSLukas Ertl d->freelist_entries--;
70173679edcSLukas Ertl LIST_REMOVE(fl, freelist);
70273679edcSLukas Ertl }
70373679edcSLukas Ertl /*
704c0b9797aSUlf Lilleengen * The subdisk does not start at the beginning of the free
705c0b9797aSUlf Lilleengen * slot.
70673679edcSLukas Ertl */
70773679edcSLukas Ertl } else {
70873679edcSLukas Ertl tmp = fl->offset + fl->size;
70973679edcSLukas Ertl fl->size = s->drive_offset - fl->offset;
71073679edcSLukas Ertl
71173679edcSLukas Ertl /*
712c0b9797aSUlf Lilleengen * The subdisk didn't use the complete rest of the free
713c0b9797aSUlf Lilleengen * slot, so we need to split it.
71473679edcSLukas Ertl */
71573679edcSLukas Ertl if (s->drive_offset + s->size != tmp) {
716c0b9797aSUlf Lilleengen fl2 = g_malloc(sizeof(*fl2), M_WAITOK | M_ZERO);
71773679edcSLukas Ertl fl2->offset = s->drive_offset + s->size;
71873679edcSLukas Ertl fl2->size = tmp - fl2->offset;
71973679edcSLukas Ertl LIST_INSERT_AFTER(fl, fl2, freelist);
72073679edcSLukas Ertl d->freelist_entries++;
72173679edcSLukas Ertl }
72273679edcSLukas Ertl }
72373679edcSLukas Ertl break;
72473679edcSLukas Ertl }
72573679edcSLukas Ertl
72673679edcSLukas Ertl /*
72773679edcSLukas Ertl * This is the first subdisk on this drive, just insert it into the
72873679edcSLukas Ertl * list.
72973679edcSLukas Ertl */
73073679edcSLukas Ertl if (LIST_EMPTY(&d->subdisks)) {
73173679edcSLukas Ertl LIST_INSERT_HEAD(&d->subdisks, s, from_drive);
73273679edcSLukas Ertl
73373679edcSLukas Ertl /* There are other subdisks, so insert this one in correct order. */
73473679edcSLukas Ertl } else {
73573679edcSLukas Ertl LIST_FOREACH(s2, &d->subdisks, from_drive) {
73673679edcSLukas Ertl if (s->drive_offset < s2->drive_offset) {
73773679edcSLukas Ertl LIST_INSERT_BEFORE(s2, s, from_drive);
73873679edcSLukas Ertl break;
73973679edcSLukas Ertl } else if (LIST_NEXT(s2, from_drive) == NULL) {
74073679edcSLukas Ertl LIST_INSERT_AFTER(s2, s, from_drive);
74173679edcSLukas Ertl break;
74273679edcSLukas Ertl }
74373679edcSLukas Ertl }
74473679edcSLukas Ertl }
74573679edcSLukas Ertl
74673679edcSLukas Ertl d->sdcount++;
74773679edcSLukas Ertl d->avail -= s->size;
74873679edcSLukas Ertl
749c0b9797aSUlf Lilleengen s->flags &= ~GV_SD_TASTED;
750c0b9797aSUlf Lilleengen
75173679edcSLukas Ertl /* Link back from the subdisk to this drive. */
75273679edcSLukas Ertl s->drive_sc = d;
75373679edcSLukas Ertl
75473679edcSLukas Ertl return (0);
75573679edcSLukas Ertl }
75673679edcSLukas Ertl
75773679edcSLukas Ertl void
gv_free_sd(struct gv_sd * s)758ff91880eSLukas Ertl gv_free_sd(struct gv_sd *s)
759ff91880eSLukas Ertl {
760ff91880eSLukas Ertl struct gv_drive *d;
761ff91880eSLukas Ertl struct gv_freelist *fl, *fl2;
762ff91880eSLukas Ertl
763ff91880eSLukas Ertl KASSERT(s != NULL, ("gv_free_sd: NULL s"));
764ff91880eSLukas Ertl
765ff91880eSLukas Ertl d = s->drive_sc;
766ff91880eSLukas Ertl if (d == NULL)
767ff91880eSLukas Ertl return;
768ff91880eSLukas Ertl
769ff91880eSLukas Ertl /*
770ff91880eSLukas Ertl * First, find the free slot that's immediately before or after this
771ff91880eSLukas Ertl * subdisk.
772ff91880eSLukas Ertl */
773ff91880eSLukas Ertl fl = NULL;
774ff91880eSLukas Ertl LIST_FOREACH(fl, &d->freelist, freelist) {
775ff91880eSLukas Ertl if (fl->offset == s->drive_offset + s->size)
776ff91880eSLukas Ertl break;
777ff91880eSLukas Ertl if (fl->offset + fl->size == s->drive_offset)
778ff91880eSLukas Ertl break;
779ff91880eSLukas Ertl }
780ff91880eSLukas Ertl
781ff91880eSLukas Ertl /* If there is no free slot behind this subdisk, so create one. */
782ff91880eSLukas Ertl if (fl == NULL) {
783ff91880eSLukas Ertl fl = g_malloc(sizeof(*fl), M_WAITOK | M_ZERO);
784ff91880eSLukas Ertl fl->size = s->size;
785ff91880eSLukas Ertl fl->offset = s->drive_offset;
786ff91880eSLukas Ertl
787ff91880eSLukas Ertl if (d->freelist_entries == 0) {
788ff91880eSLukas Ertl LIST_INSERT_HEAD(&d->freelist, fl, freelist);
789ff91880eSLukas Ertl } else {
790ff91880eSLukas Ertl LIST_FOREACH(fl2, &d->freelist, freelist) {
791ff91880eSLukas Ertl if (fl->offset < fl2->offset) {
792ff91880eSLukas Ertl LIST_INSERT_BEFORE(fl2, fl, freelist);
793ff91880eSLukas Ertl break;
794ff91880eSLukas Ertl } else if (LIST_NEXT(fl2, freelist) == NULL) {
795ff91880eSLukas Ertl LIST_INSERT_AFTER(fl2, fl, freelist);
796ff91880eSLukas Ertl break;
797ff91880eSLukas Ertl }
798ff91880eSLukas Ertl }
799ff91880eSLukas Ertl }
800ff91880eSLukas Ertl
801ff91880eSLukas Ertl d->freelist_entries++;
802ff91880eSLukas Ertl
803ff91880eSLukas Ertl /* Expand the free slot we just found. */
804ff91880eSLukas Ertl } else {
805ff91880eSLukas Ertl fl->size += s->size;
806ff91880eSLukas Ertl if (fl->offset > s->drive_offset)
807ff91880eSLukas Ertl fl->offset = s->drive_offset;
808ff91880eSLukas Ertl }
809ff91880eSLukas Ertl
810ff91880eSLukas Ertl d->avail += s->size;
811a2237c41SLukas Ertl d->sdcount--;
812ff91880eSLukas Ertl }
813ff91880eSLukas Ertl
814ff91880eSLukas Ertl void
gv_adjust_freespace(struct gv_sd * s,off_t remainder)81573679edcSLukas Ertl gv_adjust_freespace(struct gv_sd *s, off_t remainder)
81673679edcSLukas Ertl {
81773679edcSLukas Ertl struct gv_drive *d;
81873679edcSLukas Ertl struct gv_freelist *fl, *fl2;
81973679edcSLukas Ertl
82073679edcSLukas Ertl KASSERT(s != NULL, ("gv_adjust_freespace: NULL s"));
82173679edcSLukas Ertl d = s->drive_sc;
82273679edcSLukas Ertl KASSERT(d != NULL, ("gv_adjust_freespace: NULL d"));
82373679edcSLukas Ertl
82473679edcSLukas Ertl /* First, find the free slot that's immediately after this subdisk. */
82573679edcSLukas Ertl fl = NULL;
82673679edcSLukas Ertl LIST_FOREACH(fl, &d->freelist, freelist) {
82773679edcSLukas Ertl if (fl->offset == s->drive_offset + s->size)
82873679edcSLukas Ertl break;
82973679edcSLukas Ertl }
83073679edcSLukas Ertl
83173679edcSLukas Ertl /* If there is no free slot behind this subdisk, so create one. */
83273679edcSLukas Ertl if (fl == NULL) {
83373679edcSLukas Ertl fl = g_malloc(sizeof(*fl), M_WAITOK | M_ZERO);
83473679edcSLukas Ertl fl->size = remainder;
83573679edcSLukas Ertl fl->offset = s->drive_offset + s->size - remainder;
83673679edcSLukas Ertl
83773679edcSLukas Ertl if (d->freelist_entries == 0) {
83873679edcSLukas Ertl LIST_INSERT_HEAD(&d->freelist, fl, freelist);
83973679edcSLukas Ertl } else {
84073679edcSLukas Ertl LIST_FOREACH(fl2, &d->freelist, freelist) {
84173679edcSLukas Ertl if (fl->offset < fl2->offset) {
84273679edcSLukas Ertl LIST_INSERT_BEFORE(fl2, fl, freelist);
84373679edcSLukas Ertl break;
84473679edcSLukas Ertl } else if (LIST_NEXT(fl2, freelist) == NULL) {
84573679edcSLukas Ertl LIST_INSERT_AFTER(fl2, fl, freelist);
84673679edcSLukas Ertl break;
84773679edcSLukas Ertl }
84873679edcSLukas Ertl }
84973679edcSLukas Ertl }
85073679edcSLukas Ertl
85173679edcSLukas Ertl d->freelist_entries++;
85273679edcSLukas Ertl
85373679edcSLukas Ertl /* Expand the free slot we just found. */
85473679edcSLukas Ertl } else {
85573679edcSLukas Ertl fl->offset -= remainder;
85673679edcSLukas Ertl fl->size += remainder;
85773679edcSLukas Ertl }
85873679edcSLukas Ertl
85973679edcSLukas Ertl s->size -= remainder;
86073679edcSLukas Ertl d->avail += remainder;
86173679edcSLukas Ertl }
86273679edcSLukas Ertl
86373679edcSLukas Ertl /* Check if the given plex is a striped one. */
86473679edcSLukas Ertl int
gv_is_striped(struct gv_plex * p)86573679edcSLukas Ertl gv_is_striped(struct gv_plex *p)
86673679edcSLukas Ertl {
86773679edcSLukas Ertl KASSERT(p != NULL, ("gv_is_striped: NULL p"));
86873679edcSLukas Ertl switch(p->org) {
86973679edcSLukas Ertl case GV_PLEX_STRIPED:
87073679edcSLukas Ertl case GV_PLEX_RAID5:
87173679edcSLukas Ertl return (1);
87273679edcSLukas Ertl default:
87373679edcSLukas Ertl return (0);
87473679edcSLukas Ertl }
87573679edcSLukas Ertl }
87673679edcSLukas Ertl
87773679edcSLukas Ertl /* Find a volume by name. */
87873679edcSLukas Ertl struct gv_volume *
gv_find_vol(struct gv_softc * sc,char * name)87973679edcSLukas Ertl gv_find_vol(struct gv_softc *sc, char *name)
88073679edcSLukas Ertl {
88173679edcSLukas Ertl struct gv_volume *v;
88273679edcSLukas Ertl
88373679edcSLukas Ertl LIST_FOREACH(v, &sc->volumes, volume) {
88473679edcSLukas Ertl if (!strncmp(v->name, name, GV_MAXVOLNAME))
88573679edcSLukas Ertl return (v);
88673679edcSLukas Ertl }
88773679edcSLukas Ertl
88873679edcSLukas Ertl return (NULL);
88973679edcSLukas Ertl }
89073679edcSLukas Ertl
89173679edcSLukas Ertl /* Find a plex by name. */
89273679edcSLukas Ertl struct gv_plex *
gv_find_plex(struct gv_softc * sc,char * name)89373679edcSLukas Ertl gv_find_plex(struct gv_softc *sc, char *name)
89473679edcSLukas Ertl {
89573679edcSLukas Ertl struct gv_plex *p;
89673679edcSLukas Ertl
89773679edcSLukas Ertl LIST_FOREACH(p, &sc->plexes, plex) {
89873679edcSLukas Ertl if (!strncmp(p->name, name, GV_MAXPLEXNAME))
89973679edcSLukas Ertl return (p);
90073679edcSLukas Ertl }
90173679edcSLukas Ertl
90273679edcSLukas Ertl return (NULL);
90373679edcSLukas Ertl }
90473679edcSLukas Ertl
90573679edcSLukas Ertl /* Find a subdisk by name. */
90673679edcSLukas Ertl struct gv_sd *
gv_find_sd(struct gv_softc * sc,char * name)90773679edcSLukas Ertl gv_find_sd(struct gv_softc *sc, char *name)
90873679edcSLukas Ertl {
90973679edcSLukas Ertl struct gv_sd *s;
91073679edcSLukas Ertl
91173679edcSLukas Ertl LIST_FOREACH(s, &sc->subdisks, sd) {
91273679edcSLukas Ertl if (!strncmp(s->name, name, GV_MAXSDNAME))
91373679edcSLukas Ertl return (s);
91473679edcSLukas Ertl }
91573679edcSLukas Ertl
91673679edcSLukas Ertl return (NULL);
91773679edcSLukas Ertl }
91873679edcSLukas Ertl
91973679edcSLukas Ertl /* Find a drive by name. */
92073679edcSLukas Ertl struct gv_drive *
gv_find_drive(struct gv_softc * sc,char * name)92173679edcSLukas Ertl gv_find_drive(struct gv_softc *sc, char *name)
92273679edcSLukas Ertl {
92373679edcSLukas Ertl struct gv_drive *d;
92473679edcSLukas Ertl
92573679edcSLukas Ertl LIST_FOREACH(d, &sc->drives, drive) {
92673679edcSLukas Ertl if (!strncmp(d->name, name, GV_MAXDRIVENAME))
92773679edcSLukas Ertl return (d);
92873679edcSLukas Ertl }
92973679edcSLukas Ertl
93073679edcSLukas Ertl return (NULL);
93173679edcSLukas Ertl }
93273679edcSLukas Ertl
933c0b9797aSUlf Lilleengen /* Find a drive given a device. */
934c0b9797aSUlf Lilleengen struct gv_drive *
gv_find_drive_device(struct gv_softc * sc,char * device)935c0b9797aSUlf Lilleengen gv_find_drive_device(struct gv_softc *sc, char *device)
936c0b9797aSUlf Lilleengen {
937c0b9797aSUlf Lilleengen struct gv_drive *d;
938c0b9797aSUlf Lilleengen
939c0b9797aSUlf Lilleengen LIST_FOREACH(d, &sc->drives, drive) {
940c0b9797aSUlf Lilleengen if(!strcmp(d->device, device))
941c0b9797aSUlf Lilleengen return (d);
942c0b9797aSUlf Lilleengen }
943c0b9797aSUlf Lilleengen
944c0b9797aSUlf Lilleengen return (NULL);
945c0b9797aSUlf Lilleengen }
946c0b9797aSUlf Lilleengen
94773679edcSLukas Ertl /* Check if any consumer of the given geom is open. */
94873679edcSLukas Ertl int
gv_consumer_is_open(struct g_consumer * cp)949c0b9797aSUlf Lilleengen gv_consumer_is_open(struct g_consumer *cp)
95073679edcSLukas Ertl {
951c0b9797aSUlf Lilleengen if (cp == NULL)
95299b536d8SLukas Ertl return (0);
95373679edcSLukas Ertl
95473679edcSLukas Ertl if (cp->acr || cp->acw || cp->ace)
95573679edcSLukas Ertl return (1);
956c0b9797aSUlf Lilleengen
957c0b9797aSUlf Lilleengen return (0);
958c0b9797aSUlf Lilleengen }
959c0b9797aSUlf Lilleengen
960c0b9797aSUlf Lilleengen int
gv_provider_is_open(struct g_provider * pp)961c0b9797aSUlf Lilleengen gv_provider_is_open(struct g_provider *pp)
962c0b9797aSUlf Lilleengen {
963c0b9797aSUlf Lilleengen if (pp == NULL)
964c0b9797aSUlf Lilleengen return (0);
965c0b9797aSUlf Lilleengen
966c0b9797aSUlf Lilleengen if (pp->acr || pp->acw || pp->ace)
967c0b9797aSUlf Lilleengen return (1);
968c0b9797aSUlf Lilleengen
969c0b9797aSUlf Lilleengen return (0);
970c0b9797aSUlf Lilleengen }
971c0b9797aSUlf Lilleengen
972c0b9797aSUlf Lilleengen /*
973c0b9797aSUlf Lilleengen * Compare the modification dates of the drives.
974c0b9797aSUlf Lilleengen * Return 1 if a > b, 0 otherwise.
975c0b9797aSUlf Lilleengen */
976c0b9797aSUlf Lilleengen int
gv_drive_is_newer(struct gv_softc * sc,struct gv_drive * d)977c0b9797aSUlf Lilleengen gv_drive_is_newer(struct gv_softc *sc, struct gv_drive *d)
978c0b9797aSUlf Lilleengen {
979c0b9797aSUlf Lilleengen struct gv_drive *d2;
980c0b9797aSUlf Lilleengen struct timeval *a, *b;
981c0b9797aSUlf Lilleengen
982c0b9797aSUlf Lilleengen KASSERT(!LIST_EMPTY(&sc->drives),
983c0b9797aSUlf Lilleengen ("gv_is_drive_newer: empty drive list"));
984c0b9797aSUlf Lilleengen
985c0b9797aSUlf Lilleengen a = &d->hdr->label.last_update;
986c0b9797aSUlf Lilleengen LIST_FOREACH(d2, &sc->drives, drive) {
987c0b9797aSUlf Lilleengen if ((d == d2) || (d2->state != GV_DRIVE_UP) ||
988c0b9797aSUlf Lilleengen (d2->hdr == NULL))
989c0b9797aSUlf Lilleengen continue;
990c0b9797aSUlf Lilleengen b = &d2->hdr->label.last_update;
991c0b9797aSUlf Lilleengen if (timevalcmp(a, b, >))
992c0b9797aSUlf Lilleengen return (1);
99373679edcSLukas Ertl }
99473679edcSLukas Ertl
99573679edcSLukas Ertl return (0);
99673679edcSLukas Ertl }
99773679edcSLukas Ertl
99873679edcSLukas Ertl /* Return the type of object identified by string 'name'. */
99973679edcSLukas Ertl int
gv_object_type(struct gv_softc * sc,char * name)100073679edcSLukas Ertl gv_object_type(struct gv_softc *sc, char *name)
100173679edcSLukas Ertl {
100273679edcSLukas Ertl struct gv_drive *d;
100373679edcSLukas Ertl struct gv_plex *p;
100473679edcSLukas Ertl struct gv_sd *s;
100573679edcSLukas Ertl struct gv_volume *v;
100673679edcSLukas Ertl
100773679edcSLukas Ertl LIST_FOREACH(v, &sc->volumes, volume) {
100873679edcSLukas Ertl if (!strncmp(v->name, name, GV_MAXVOLNAME))
100973679edcSLukas Ertl return (GV_TYPE_VOL);
101073679edcSLukas Ertl }
101173679edcSLukas Ertl
101273679edcSLukas Ertl LIST_FOREACH(p, &sc->plexes, plex) {
101373679edcSLukas Ertl if (!strncmp(p->name, name, GV_MAXPLEXNAME))
101473679edcSLukas Ertl return (GV_TYPE_PLEX);
101573679edcSLukas Ertl }
101673679edcSLukas Ertl
101773679edcSLukas Ertl LIST_FOREACH(s, &sc->subdisks, sd) {
101873679edcSLukas Ertl if (!strncmp(s->name, name, GV_MAXSDNAME))
101973679edcSLukas Ertl return (GV_TYPE_SD);
102073679edcSLukas Ertl }
102173679edcSLukas Ertl
102273679edcSLukas Ertl LIST_FOREACH(d, &sc->drives, drive) {
102373679edcSLukas Ertl if (!strncmp(d->name, name, GV_MAXDRIVENAME))
102473679edcSLukas Ertl return (GV_TYPE_DRIVE);
102573679edcSLukas Ertl }
102673679edcSLukas Ertl
1027c0b9797aSUlf Lilleengen return (GV_ERR_NOTFOUND);
102873679edcSLukas Ertl }
102973679edcSLukas Ertl
103073679edcSLukas Ertl void
gv_setup_objects(struct gv_softc * sc)1031c0b9797aSUlf Lilleengen gv_setup_objects(struct gv_softc *sc)
103212653decSLukas Ertl {
1033c0b9797aSUlf Lilleengen struct g_provider *pp;
1034c0b9797aSUlf Lilleengen struct gv_volume *v;
1035c0b9797aSUlf Lilleengen struct gv_plex *p;
1036c0b9797aSUlf Lilleengen struct gv_sd *s;
1037c0b9797aSUlf Lilleengen struct gv_drive *d;
1038c0b9797aSUlf Lilleengen
1039c0b9797aSUlf Lilleengen LIST_FOREACH(s, &sc->subdisks, sd) {
1040c0b9797aSUlf Lilleengen d = gv_find_drive(sc, s->drive);
1041c0b9797aSUlf Lilleengen if (d != NULL)
1042c0b9797aSUlf Lilleengen gv_sd_to_drive(s, d);
1043c0b9797aSUlf Lilleengen p = gv_find_plex(sc, s->plex);
1044c0b9797aSUlf Lilleengen if (p != NULL)
1045c0b9797aSUlf Lilleengen gv_sd_to_plex(s, p);
1046c0b9797aSUlf Lilleengen gv_update_sd_state(s);
1047c0b9797aSUlf Lilleengen }
1048c0b9797aSUlf Lilleengen
1049c0b9797aSUlf Lilleengen LIST_FOREACH(p, &sc->plexes, plex) {
1050c0b9797aSUlf Lilleengen gv_update_plex_config(p);
1051c0b9797aSUlf Lilleengen v = gv_find_vol(sc, p->volume);
1052c0b9797aSUlf Lilleengen if (v != NULL && p->vol_sc != v) {
1053c0b9797aSUlf Lilleengen p->vol_sc = v;
1054c0b9797aSUlf Lilleengen v->plexcount++;
1055c0b9797aSUlf Lilleengen LIST_INSERT_HEAD(&v->plexes, p, in_volume);
1056c0b9797aSUlf Lilleengen }
1057c0b9797aSUlf Lilleengen gv_update_plex_config(p);
1058c0b9797aSUlf Lilleengen }
1059c0b9797aSUlf Lilleengen
1060c0b9797aSUlf Lilleengen LIST_FOREACH(v, &sc->volumes, volume) {
1061c0b9797aSUlf Lilleengen v->size = gv_vol_size(v);
1062c0b9797aSUlf Lilleengen if (v->provider == NULL) {
1063c0b9797aSUlf Lilleengen g_topology_lock();
1064c0b9797aSUlf Lilleengen pp = g_new_providerf(sc->geom, "gvinum/%s", v->name);
1065c0b9797aSUlf Lilleengen pp->mediasize = v->size;
1066c0b9797aSUlf Lilleengen pp->sectorsize = 512; /* XXX */
1067c0b9797aSUlf Lilleengen g_error_provider(pp, 0);
1068c0b9797aSUlf Lilleengen v->provider = pp;
1069c0b9797aSUlf Lilleengen pp->private = v;
1070c0b9797aSUlf Lilleengen g_topology_unlock();
1071c0b9797aSUlf Lilleengen } else if (v->provider->mediasize != v->size) {
1072c0b9797aSUlf Lilleengen g_topology_lock();
1073c0b9797aSUlf Lilleengen v->provider->mediasize = v->size;
1074c0b9797aSUlf Lilleengen g_topology_unlock();
1075c0b9797aSUlf Lilleengen }
1076c0b9797aSUlf Lilleengen v->flags &= ~GV_VOL_NEWBORN;
1077c0b9797aSUlf Lilleengen gv_update_vol_state(v);
107812653decSLukas Ertl }
107912653decSLukas Ertl }
108012653decSLukas Ertl
108112653decSLukas Ertl void
gv_cleanup(struct gv_softc * sc)1082c0b9797aSUlf Lilleengen gv_cleanup(struct gv_softc *sc)
108373679edcSLukas Ertl {
1084c0b9797aSUlf Lilleengen struct gv_volume *v, *v2;
1085c0b9797aSUlf Lilleengen struct gv_plex *p, *p2;
1086c0b9797aSUlf Lilleengen struct gv_sd *s, *s2;
1087c0b9797aSUlf Lilleengen struct gv_drive *d, *d2;
1088c0b9797aSUlf Lilleengen struct gv_freelist *fl, *fl2;
1089c0b9797aSUlf Lilleengen
1090c0b9797aSUlf Lilleengen mtx_lock(&sc->config_mtx);
1091c0b9797aSUlf Lilleengen LIST_FOREACH_SAFE(v, &sc->volumes, volume, v2) {
1092c0b9797aSUlf Lilleengen LIST_REMOVE(v, volume);
1093c0b9797aSUlf Lilleengen g_free(v->wqueue);
1094c0b9797aSUlf Lilleengen g_free(v);
1095c0b9797aSUlf Lilleengen }
1096c0b9797aSUlf Lilleengen LIST_FOREACH_SAFE(p, &sc->plexes, plex, p2) {
1097c0b9797aSUlf Lilleengen LIST_REMOVE(p, plex);
1098d5817a50SLukas Ertl g_free(p->bqueue);
1099c0b9797aSUlf Lilleengen g_free(p->rqueue);
1100d5817a50SLukas Ertl g_free(p->wqueue);
1101c0b9797aSUlf Lilleengen g_free(p);
110267e3ab6eSLukas Ertl }
1103c0b9797aSUlf Lilleengen LIST_FOREACH_SAFE(s, &sc->subdisks, sd, s2) {
1104c0b9797aSUlf Lilleengen LIST_REMOVE(s, sd);
1105c0b9797aSUlf Lilleengen g_free(s);
1106c0b9797aSUlf Lilleengen }
1107c0b9797aSUlf Lilleengen LIST_FOREACH_SAFE(d, &sc->drives, drive, d2) {
1108c0b9797aSUlf Lilleengen LIST_FOREACH_SAFE(fl, &d->freelist, freelist, fl2) {
1109c0b9797aSUlf Lilleengen LIST_REMOVE(fl, freelist);
1110c0b9797aSUlf Lilleengen g_free(fl);
1111c0b9797aSUlf Lilleengen }
1112c0b9797aSUlf Lilleengen LIST_REMOVE(d, drive);
1113c0b9797aSUlf Lilleengen g_free(d->hdr);
1114c0b9797aSUlf Lilleengen g_free(d);
1115c0b9797aSUlf Lilleengen }
1116c0b9797aSUlf Lilleengen mtx_destroy(&sc->config_mtx);
111767e3ab6eSLukas Ertl }
111867e3ab6eSLukas Ertl
1119c0b9797aSUlf Lilleengen /* General 'attach' routine. */
1120c0b9797aSUlf Lilleengen int
gv_attach_plex(struct gv_plex * p,struct gv_volume * v,int rename)1121c0b9797aSUlf Lilleengen gv_attach_plex(struct gv_plex *p, struct gv_volume *v, int rename)
112267e3ab6eSLukas Ertl {
1123c0b9797aSUlf Lilleengen struct gv_sd *s;
1124739a9c51SEdward Tomasz Napierala struct gv_softc *sc __diagused;
1125c0b9797aSUlf Lilleengen
1126c0b9797aSUlf Lilleengen g_topology_assert();
1127c0b9797aSUlf Lilleengen
1128c0b9797aSUlf Lilleengen sc = p->vinumconf;
1129c0b9797aSUlf Lilleengen KASSERT(sc != NULL, ("NULL sc"));
1130c0b9797aSUlf Lilleengen
1131c0b9797aSUlf Lilleengen if (p->vol_sc != NULL) {
1132c0b9797aSUlf Lilleengen G_VINUM_DEBUG(1, "unable to attach %s: already attached to %s",
1133c0b9797aSUlf Lilleengen p->name, p->volume);
1134c0b9797aSUlf Lilleengen return (GV_ERR_ISATTACHED);
113573679edcSLukas Ertl }
1136c0b9797aSUlf Lilleengen
1137c0b9797aSUlf Lilleengen /* Stale all subdisks of this plex. */
1138c0b9797aSUlf Lilleengen LIST_FOREACH(s, &p->subdisks, in_plex) {
1139c0b9797aSUlf Lilleengen if (s->state != GV_SD_STALE)
1140c0b9797aSUlf Lilleengen gv_set_sd_state(s, GV_SD_STALE, GV_SETSTATE_FORCE);
1141c0b9797aSUlf Lilleengen }
1142c0b9797aSUlf Lilleengen /* Attach to volume. Make sure volume is not up and running. */
1143c0b9797aSUlf Lilleengen if (gv_provider_is_open(v->provider)) {
1144c0b9797aSUlf Lilleengen G_VINUM_DEBUG(1, "unable to attach %s: volume %s is busy",
1145c0b9797aSUlf Lilleengen p->name, v->name);
1146c0b9797aSUlf Lilleengen return (GV_ERR_ISBUSY);
1147c0b9797aSUlf Lilleengen }
1148c0b9797aSUlf Lilleengen p->vol_sc = v;
1149c0b9797aSUlf Lilleengen strlcpy(p->volume, v->name, sizeof(p->volume));
1150c0b9797aSUlf Lilleengen v->plexcount++;
1151c0b9797aSUlf Lilleengen if (rename) {
1152c0b9797aSUlf Lilleengen snprintf(p->name, sizeof(p->name), "%s.p%d", v->name,
1153c0b9797aSUlf Lilleengen v->plexcount);
1154c0b9797aSUlf Lilleengen }
1155c0b9797aSUlf Lilleengen LIST_INSERT_HEAD(&v->plexes, p, in_volume);
1156c0b9797aSUlf Lilleengen
1157c0b9797aSUlf Lilleengen /* Get plex up again. */
1158c0b9797aSUlf Lilleengen gv_update_vol_size(v, gv_vol_size(v));
1159c0b9797aSUlf Lilleengen gv_set_plex_state(p, GV_PLEX_UP, 0);
1160c0b9797aSUlf Lilleengen gv_save_config(p->vinumconf);
1161c0b9797aSUlf Lilleengen return (0);
1162c0b9797aSUlf Lilleengen }
1163c0b9797aSUlf Lilleengen
1164c0b9797aSUlf Lilleengen int
gv_attach_sd(struct gv_sd * s,struct gv_plex * p,off_t offset,int rename)1165c0b9797aSUlf Lilleengen gv_attach_sd(struct gv_sd *s, struct gv_plex *p, off_t offset, int rename)
1166c0b9797aSUlf Lilleengen {
1167c0b9797aSUlf Lilleengen struct gv_sd *s2;
1168f3f6e0ebSRobert Wing int error;
1169c0b9797aSUlf Lilleengen
1170c0b9797aSUlf Lilleengen g_topology_assert();
1171c0b9797aSUlf Lilleengen
1172c0b9797aSUlf Lilleengen /* If subdisk is attached, don't do it. */
1173c0b9797aSUlf Lilleengen if (s->plex_sc != NULL) {
1174c0b9797aSUlf Lilleengen G_VINUM_DEBUG(1, "unable to attach %s: already attached to %s",
1175c0b9797aSUlf Lilleengen s->name, s->plex);
1176c0b9797aSUlf Lilleengen return (GV_ERR_ISATTACHED);
1177c0b9797aSUlf Lilleengen }
1178c0b9797aSUlf Lilleengen
1179c0b9797aSUlf Lilleengen gv_set_sd_state(s, GV_SD_STALE, GV_SETSTATE_FORCE);
1180c0b9797aSUlf Lilleengen /* First check that this subdisk has a correct offset. If none other
1181c0b9797aSUlf Lilleengen * starts at the same, and it's correct module stripesize, it is */
1182c0b9797aSUlf Lilleengen if (offset != -1 && offset % p->stripesize != 0)
1183c0b9797aSUlf Lilleengen return (GV_ERR_BADOFFSET);
1184c0b9797aSUlf Lilleengen LIST_FOREACH(s2, &p->subdisks, in_plex) {
1185c0b9797aSUlf Lilleengen if (s2->plex_offset == offset)
1186c0b9797aSUlf Lilleengen return (GV_ERR_BADOFFSET);
1187c0b9797aSUlf Lilleengen }
1188c0b9797aSUlf Lilleengen
1189c0b9797aSUlf Lilleengen /* Attach the subdisk to the plex at given offset. */
1190c0b9797aSUlf Lilleengen s->plex_offset = offset;
1191c0b9797aSUlf Lilleengen strlcpy(s->plex, p->name, sizeof(s->plex));
1192c0b9797aSUlf Lilleengen
1193c0b9797aSUlf Lilleengen error = gv_sd_to_plex(s, p);
1194c0b9797aSUlf Lilleengen if (error)
1195c0b9797aSUlf Lilleengen return (error);
1196c0b9797aSUlf Lilleengen gv_update_plex_config(p);
1197c0b9797aSUlf Lilleengen
1198c0b9797aSUlf Lilleengen if (rename) {
1199c0b9797aSUlf Lilleengen snprintf(s->name, sizeof(s->name), "%s.s%d", s->plex,
1200c0b9797aSUlf Lilleengen p->sdcount);
1201c0b9797aSUlf Lilleengen }
1202c0b9797aSUlf Lilleengen if (p->vol_sc != NULL)
1203c0b9797aSUlf Lilleengen gv_update_vol_size(p->vol_sc, gv_vol_size(p->vol_sc));
1204c0b9797aSUlf Lilleengen gv_save_config(p->vinumconf);
1205c0b9797aSUlf Lilleengen /* We don't update the subdisk state since the user might have to
1206c0b9797aSUlf Lilleengen * initiate a rebuild/sync first. */
1207c0b9797aSUlf Lilleengen return (0);
1208c0b9797aSUlf Lilleengen }
1209c0b9797aSUlf Lilleengen
1210c0b9797aSUlf Lilleengen /* Detach a plex from a volume. */
1211c0b9797aSUlf Lilleengen int
gv_detach_plex(struct gv_plex * p,int flags)1212c0b9797aSUlf Lilleengen gv_detach_plex(struct gv_plex *p, int flags)
1213c0b9797aSUlf Lilleengen {
1214c0b9797aSUlf Lilleengen struct gv_volume *v;
1215c0b9797aSUlf Lilleengen
1216c0b9797aSUlf Lilleengen g_topology_assert();
1217c0b9797aSUlf Lilleengen v = p->vol_sc;
1218c0b9797aSUlf Lilleengen
1219c0b9797aSUlf Lilleengen if (v == NULL) {
1220c0b9797aSUlf Lilleengen G_VINUM_DEBUG(1, "unable to detach %s: already detached",
1221c0b9797aSUlf Lilleengen p->name);
1222c0b9797aSUlf Lilleengen return (0); /* Not an error. */
1223c0b9797aSUlf Lilleengen }
1224c0b9797aSUlf Lilleengen
1225c0b9797aSUlf Lilleengen /*
1226c0b9797aSUlf Lilleengen * Only proceed if forced or volume inactive.
1227c0b9797aSUlf Lilleengen */
1228c0b9797aSUlf Lilleengen if (!(flags & GV_FLAG_F) && (gv_provider_is_open(v->provider) ||
1229c0b9797aSUlf Lilleengen p->state == GV_PLEX_UP)) {
1230c0b9797aSUlf Lilleengen G_VINUM_DEBUG(1, "unable to detach %s: volume %s is busy",
1231c0b9797aSUlf Lilleengen p->name, p->volume);
1232c0b9797aSUlf Lilleengen return (GV_ERR_ISBUSY);
1233c0b9797aSUlf Lilleengen }
1234c0b9797aSUlf Lilleengen v->plexcount--;
1235c0b9797aSUlf Lilleengen /* Make sure someone don't read us when gone. */
1236c0b9797aSUlf Lilleengen v->last_read_plex = NULL;
1237c0b9797aSUlf Lilleengen LIST_REMOVE(p, in_volume);
1238c0b9797aSUlf Lilleengen p->vol_sc = NULL;
1239c0b9797aSUlf Lilleengen memset(p->volume, 0, GV_MAXVOLNAME);
1240c0b9797aSUlf Lilleengen gv_update_vol_size(v, gv_vol_size(v));
1241c0b9797aSUlf Lilleengen gv_save_config(p->vinumconf);
1242c0b9797aSUlf Lilleengen return (0);
1243c0b9797aSUlf Lilleengen }
1244c0b9797aSUlf Lilleengen
1245c0b9797aSUlf Lilleengen /* Detach a subdisk from a plex. */
1246c0b9797aSUlf Lilleengen int
gv_detach_sd(struct gv_sd * s,int flags)1247c0b9797aSUlf Lilleengen gv_detach_sd(struct gv_sd *s, int flags)
1248c0b9797aSUlf Lilleengen {
1249c0b9797aSUlf Lilleengen struct gv_plex *p;
1250c0b9797aSUlf Lilleengen
1251c0b9797aSUlf Lilleengen g_topology_assert();
1252c0b9797aSUlf Lilleengen p = s->plex_sc;
1253c0b9797aSUlf Lilleengen
1254c0b9797aSUlf Lilleengen if (p == NULL) {
1255c0b9797aSUlf Lilleengen G_VINUM_DEBUG(1, "unable to detach %s: already detached",
1256c0b9797aSUlf Lilleengen s->name);
1257c0b9797aSUlf Lilleengen return (0); /* Not an error. */
1258c0b9797aSUlf Lilleengen }
1259c0b9797aSUlf Lilleengen
1260c0b9797aSUlf Lilleengen /*
1261c0b9797aSUlf Lilleengen * Don't proceed if we're not forcing, and the plex is up, or degraded
1262c0b9797aSUlf Lilleengen * with this subdisk up.
1263c0b9797aSUlf Lilleengen */
1264c0b9797aSUlf Lilleengen if (!(flags & GV_FLAG_F) && ((p->state > GV_PLEX_DEGRADED) ||
1265c0b9797aSUlf Lilleengen ((p->state == GV_PLEX_DEGRADED) && (s->state == GV_SD_UP)))) {
1266c0b9797aSUlf Lilleengen G_VINUM_DEBUG(1, "unable to detach %s: plex %s is busy",
1267c0b9797aSUlf Lilleengen s->name, s->plex);
1268c0b9797aSUlf Lilleengen return (GV_ERR_ISBUSY);
1269c0b9797aSUlf Lilleengen }
1270c0b9797aSUlf Lilleengen
1271c0b9797aSUlf Lilleengen LIST_REMOVE(s, in_plex);
1272c0b9797aSUlf Lilleengen s->plex_sc = NULL;
1273c0b9797aSUlf Lilleengen memset(s->plex, 0, GV_MAXPLEXNAME);
1274c0b9797aSUlf Lilleengen p->sddetached++;
1275c0b9797aSUlf Lilleengen gv_save_config(s->vinumconf);
1276c0b9797aSUlf Lilleengen return (0);
127773679edcSLukas Ertl }
1278