xref: /freebsd/sys/geom/vinum/geom_vinum_subr.c (revision fdafd315)
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