geom_vinum_state.c revision 190507
1234353Sdim/*-
2198090Srdivacky * Copyright (c) 2004, 2007 Lukas Ertl
3198090Srdivacky * All rights reserved.
4198090Srdivacky *
5198090Srdivacky * Redistribution and use in source and binary forms, with or without
6198090Srdivacky * modification, are permitted provided that the following conditions
7198090Srdivacky * are met:
8198090Srdivacky * 1. Redistributions of source code must retain the above copyright
9198090Srdivacky *    notice, this list of conditions and the following disclaimer.
10198090Srdivacky * 2. Redistributions in binary form must reproduce the above copyright
11198090Srdivacky *    notice, this list of conditions and the following disclaimer in the
12198090Srdivacky *    documentation and/or other materials provided with the distribution.
13198090Srdivacky *
14198090Srdivacky * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15198090Srdivacky * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16234353Sdim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17199481Srdivacky * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18218893Sdim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19198090Srdivacky * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20226633Sdim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21249423Sdim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22198090Srdivacky * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23199481Srdivacky * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24198090Srdivacky * SUCH DAMAGE.
25198090Srdivacky */
26198090Srdivacky
27198090Srdivacky#include <sys/cdefs.h>
28208599Srdivacky__FBSDID("$FreeBSD: head/sys/geom/vinum/geom_vinum_state.c 190507 2009-03-28 17:20:08Z lulf $");
29226633Sdim
30249423Sdim#include <sys/libkern.h>
31249423Sdim#include <sys/malloc.h>
32249423Sdim
33198090Srdivacky#include <geom/geom.h>
34224145Sdim#include <geom/vinum/geom_vinum_var.h>
35198090Srdivacky#include <geom/vinum/geom_vinum.h>
36198892Srdivacky#include <geom/vinum/geom_vinum_share.h>
37198090Srdivacky
38224145Sdimvoid
39224145Sdimgv_setstate(struct g_geom *gp, struct gctl_req *req)
40224145Sdim{
41224145Sdim	struct gv_softc *sc;
42198090Srdivacky	struct gv_sd *s;
43198090Srdivacky	struct gv_drive *d;
44198090Srdivacky	struct gv_volume *v;
45198090Srdivacky	struct gv_plex *p;
46198090Srdivacky	char *obj, *state;
47198090Srdivacky	int f, *flags, type;
48226633Sdim
49234353Sdim	f = 0;
50226633Sdim	obj = gctl_get_param(req, "object", NULL);
51226633Sdim	if (obj == NULL) {
52243830Sdim		gctl_error(req, "no object given");
53243830Sdim		return;
54243830Sdim	}
55243830Sdim
56243830Sdim	state = gctl_get_param(req, "state", NULL);
57218893Sdim	if (state == NULL) {
58218893Sdim		gctl_error(req, "no state given");
59239462Sdim		return;
60239462Sdim	}
61239462Sdim
62218893Sdim	flags = gctl_get_paraml(req, "flags", sizeof(*flags));
63218893Sdim	if (flags == NULL) {
64218893Sdim		gctl_error(req, "no flags given");
65218893Sdim		return;
66218893Sdim	}
67218893Sdim
68218893Sdim	if (*flags & GV_FLAG_F)
69218893Sdim		f = GV_SETSTATE_FORCE;
70218893Sdim
71218893Sdim	sc = gp->softc;
72218893Sdim	type = gv_object_type(sc, obj);
73218893Sdim	switch (type) {
74218893Sdim	case GV_TYPE_VOL:
75218893Sdim		if (gv_volstatei(state) < 0) {
76218893Sdim			gctl_error(req, "invalid volume state '%s'", state);
77218893Sdim			break;
78218893Sdim		}
79218893Sdim		v = gv_find_vol(sc, obj);
80218893Sdim		gv_post_event(sc, GV_EVENT_SET_VOL_STATE, v, NULL,
81218893Sdim		    gv_volstatei(state), f);
82218893Sdim		break;
83218893Sdim
84218893Sdim	case GV_TYPE_PLEX:
85218893Sdim		if (gv_plexstatei(state) < 0) {
86218893Sdim			gctl_error(req, "invalid plex state '%s'", state);
87218893Sdim			break;
88218893Sdim		}
89198892Srdivacky		p = gv_find_plex(sc, obj);
90224145Sdim		gv_post_event(sc, GV_EVENT_SET_PLEX_STATE, p, NULL,
91198892Srdivacky		    gv_plexstatei(state), f);
92218893Sdim		break;
93218893Sdim
94218893Sdim	case GV_TYPE_SD:
95218893Sdim		if (gv_sdstatei(state) < 0) {
96218893Sdim			gctl_error(req, "invalid subdisk state '%s'", state);
97218893Sdim			break;
98198090Srdivacky		}
99198090Srdivacky		s = gv_find_sd(sc, obj);
100218893Sdim		gv_post_event(sc, GV_EVENT_SET_SD_STATE, s, NULL,
101218893Sdim		    gv_sdstatei(state), f);
102218893Sdim		break;
103218893Sdim
104218893Sdim	case GV_TYPE_DRIVE:
105218893Sdim		if (gv_drivestatei(state) < 0) {
106218893Sdim			gctl_error(req, "invalid drive state '%s'", state);
107218893Sdim			break;
108218893Sdim		}
109249423Sdim		d = gv_find_drive(sc, obj);
110218893Sdim		gv_post_event(sc, GV_EVENT_SET_DRIVE_STATE, d, NULL,
111218893Sdim		    gv_drivestatei(state), f);
112218893Sdim		break;
113218893Sdim
114218893Sdim	default:
115218893Sdim		gctl_error(req, "unknown object '%s'", obj);
116218893Sdim		break;
117218893Sdim	}
118249423Sdim}
119218893Sdim
120218893Sdim/* Update drive state; return 0 if the state changes, otherwise error. */
121198090Srdivackyint
122198090Srdivackygv_set_drive_state(struct gv_drive *d, int newstate, int flags)
123198090Srdivacky{
124198090Srdivacky	struct gv_sd *s;
125198090Srdivacky	int oldstate;
126198090Srdivacky
127198090Srdivacky	KASSERT(d != NULL, ("gv_set_drive_state: NULL d"));
128198090Srdivacky
129198090Srdivacky	oldstate = d->state;
130198090Srdivacky
131198090Srdivacky	if (newstate == oldstate)
132210299Sed		return (0);
133198090Srdivacky
134198090Srdivacky	/* We allow to take down an open drive only with force. */
135198090Srdivacky	if ((newstate == GV_DRIVE_DOWN) && gv_consumer_is_open(d->consumer) &&
136198090Srdivacky	    (!(flags & GV_SETSTATE_FORCE)))
137198090Srdivacky		return (GV_ERR_ISBUSY);
138198090Srdivacky
139198090Srdivacky	d->state = newstate;
140198090Srdivacky
141198090Srdivacky	if (d->state != oldstate) {
142198090Srdivacky		LIST_FOREACH(s, &d->subdisks, from_drive)
143198090Srdivacky			gv_update_sd_state(s);
144198090Srdivacky	}
145198090Srdivacky
146198090Srdivacky	/* Save the config back to disk. */
147198090Srdivacky	if (flags & GV_SETSTATE_CONFIG)
148198090Srdivacky		gv_save_config(d->vinumconf);
149198090Srdivacky
150198090Srdivacky	return (0);
151198090Srdivacky}
152224145Sdim
153224145Sdimint
154234353Sdimgv_set_sd_state(struct gv_sd *s, int newstate, int flags)
155198090Srdivacky{
156198090Srdivacky	struct gv_drive *d;
157198090Srdivacky	struct gv_plex *p;
158198090Srdivacky	int oldstate, status;
159198090Srdivacky
160198090Srdivacky	KASSERT(s != NULL, ("gv_set_sd_state: NULL s"));
161198090Srdivacky
162198090Srdivacky	oldstate = s->state;
163198090Srdivacky
164234353Sdim	/* We are optimistic and assume it will work. */
165198090Srdivacky	status = 0;
166198090Srdivacky
167198090Srdivacky	if (newstate == oldstate)
168198090Srdivacky		return (0);
169198090Srdivacky
170198090Srdivacky	switch (newstate) {
171198090Srdivacky	case GV_SD_DOWN:
172198090Srdivacky		/*
173198090Srdivacky		 * If we're attached to a plex, we won't go down without use of
174198090Srdivacky		 * force.
175198090Srdivacky		 */
176198090Srdivacky		if ((s->plex_sc != NULL) && !(flags & GV_SETSTATE_FORCE))
177198090Srdivacky			return (GV_ERR_ISATTACHED);
178198090Srdivacky		break;
179198090Srdivacky
180198090Srdivacky	case GV_SD_REVIVING:
181226633Sdim	case GV_SD_INITIALIZING:
182198090Srdivacky		/*
183198090Srdivacky		 * Only do this if we're forced, since it usually is done
184198090Srdivacky		 * internally, and then we do use the force flag.
185198090Srdivacky		 */
186198090Srdivacky		if (!flags & GV_SETSTATE_FORCE)
187198090Srdivacky			return (GV_ERR_SETSTATE);
188198090Srdivacky		break;
189198090Srdivacky
190198090Srdivacky	case GV_SD_UP:
191198090Srdivacky		/* We can't bring the subdisk up if our drive is dead. */
192198090Srdivacky		d = s->drive_sc;
193198090Srdivacky		if ((d == NULL) || (d->state != GV_DRIVE_UP))
194198090Srdivacky			return (GV_ERR_SETSTATE);
195198090Srdivacky
196198090Srdivacky		/* Check from where we want to be brought up. */
197198090Srdivacky		switch (s->state) {
198198090Srdivacky		case GV_SD_REVIVING:
199198090Srdivacky		case GV_SD_INITIALIZING:
200198090Srdivacky			/*
201198090Srdivacky			 * The subdisk was initializing.  We allow it to be
202198090Srdivacky			 * brought up.
203198090Srdivacky			 */
204198090Srdivacky			break;
205198090Srdivacky
206198090Srdivacky		case GV_SD_DOWN:
207198090Srdivacky			/*
208198090Srdivacky			 * The subdisk is currently down.  We allow it to be
209198090Srdivacky			 * brought up if it is not attached to a plex.
210198090Srdivacky			 */
211198090Srdivacky			p = s->plex_sc;
212198090Srdivacky			if (p == NULL)
213198090Srdivacky				break;
214218893Sdim
215198090Srdivacky			/*
216198090Srdivacky			 * If this subdisk is attached to a plex, we allow it
217198090Srdivacky			 * to be brought up if the plex if it's not a RAID5
218198090Srdivacky			 * plex, otherwise it's made 'stale'.
219198090Srdivacky			 */
220198090Srdivacky
221198090Srdivacky			if (p->org != GV_PLEX_RAID5)
222198090Srdivacky				break;
223198090Srdivacky			else if (s->flags & GV_SD_CANGOUP) {
224198090Srdivacky				s->flags &= ~GV_SD_CANGOUP;
225218893Sdim				break;
226198090Srdivacky			} else if (flags & GV_SETSTATE_FORCE)
227198090Srdivacky				break;
228198090Srdivacky			else
229198090Srdivacky				s->state = GV_SD_STALE;
230198090Srdivacky
231198090Srdivacky			status = GV_ERR_SETSTATE;
232198090Srdivacky			break;
233198090Srdivacky
234198090Srdivacky		case GV_SD_STALE:
235198090Srdivacky			/*
236198090Srdivacky			 * A stale subdisk can be brought up only if it's part
237198090Srdivacky			 * of a concat or striped plex that's the only one in a
238198090Srdivacky			 * volume, or if the subdisk isn't attached to a plex.
239198090Srdivacky			 * Otherwise it needs to be revived or initialized
240218893Sdim			 * first.
241198090Srdivacky			 */
242198090Srdivacky			p = s->plex_sc;
243198090Srdivacky			if (p == NULL || flags & GV_SETSTATE_FORCE)
244198090Srdivacky				break;
245198090Srdivacky
246198090Srdivacky			if ((p->org != GV_PLEX_RAID5 &&
247198090Srdivacky			    p->vol_sc->plexcount == 1) ||
248198090Srdivacky			    (p->flags & GV_PLEX_SYNCING &&
249198090Srdivacky			    p->synced > 0 &&
250198090Srdivacky			    p->org == GV_PLEX_RAID5))
251198090Srdivacky				break;
252198090Srdivacky			else
253198090Srdivacky				return (GV_ERR_SETSTATE);
254198090Srdivacky
255198090Srdivacky		default:
256198090Srdivacky			return (GV_ERR_INVSTATE);
257198090Srdivacky		}
258198090Srdivacky		break;
259198090Srdivacky
260198090Srdivacky	/* Other state transitions are only possible with force. */
261198090Srdivacky	default:
262198090Srdivacky		if (!(flags & GV_SETSTATE_FORCE))
263198090Srdivacky			return (GV_ERR_SETSTATE);
264198090Srdivacky	}
265198090Srdivacky
266198090Srdivacky	/* We can change the state and do it. */
267198090Srdivacky	if (status == 0)
268198090Srdivacky		s->state = newstate;
269198090Srdivacky
270198090Srdivacky	/* Update our plex, if we're attached to one. */
271198090Srdivacky	if (s->plex_sc != NULL)
272198090Srdivacky		gv_update_plex_state(s->plex_sc);
273198090Srdivacky
274198090Srdivacky	/* Save the config back to disk. */
275198090Srdivacky	if (flags & GV_SETSTATE_CONFIG)
276198090Srdivacky		gv_save_config(s->vinumconf);
277198090Srdivacky
278206083Srdivacky	return (status);
279198090Srdivacky}
280206083Srdivacky
281206083Srdivackyint
282206083Srdivackygv_set_plex_state(struct gv_plex *p, int newstate, int flags)
283206083Srdivacky{
284206083Srdivacky	struct gv_volume *v;
285206083Srdivacky	int oldstate, plexdown;
286198090Srdivacky
287198090Srdivacky	KASSERT(p != NULL, ("gv_set_plex_state: NULL p"));
288198090Srdivacky
289251662Sdim	oldstate = p->state;
290198090Srdivacky	v = p->vol_sc;
291251662Sdim	plexdown = 0;
292251662Sdim
293251662Sdim	if (newstate == oldstate)
294251662Sdim		return (0);
295251662Sdim
296251662Sdim	switch (newstate) {
297251662Sdim	case GV_PLEX_UP:
298251662Sdim		/* Let update_plex handle if the plex can come up */
299198090Srdivacky		gv_update_plex_state(p);
300198090Srdivacky		if (p->state != GV_PLEX_UP && !(flags & GV_SETSTATE_FORCE))
301198090Srdivacky			return (GV_ERR_SETSTATE);
302198090Srdivacky		p->state = newstate;
303198090Srdivacky		break;
304198090Srdivacky	case GV_PLEX_DOWN:
305198090Srdivacky		/*
306198090Srdivacky		 * Set state to GV_PLEX_DOWN only if no-one is using the plex,
307198090Srdivacky		 * or if the state is forced.
308198090Srdivacky		 */
309198090Srdivacky		if (v != NULL) {
310198090Srdivacky			/* If the only one up, force is needed. */
311198090Srdivacky			plexdown = gv_plexdown(v);
312198090Srdivacky			if ((v->plexcount == 1 ||
313198090Srdivacky			    (v->plexcount - plexdown == 1)) &&
314198090Srdivacky			    ((flags & GV_SETSTATE_FORCE) == 0))
315198090Srdivacky				return (GV_ERR_SETSTATE);
316198090Srdivacky		}
317218893Sdim		p->state = newstate;
318198090Srdivacky		break;
319218893Sdim	case GV_PLEX_DEGRADED:
320218893Sdim		/* Only used internally, so we have to be forced. */
321218893Sdim		if (flags & GV_SETSTATE_FORCE)
322218893Sdim			p->state = newstate;
323218893Sdim		break;
324218893Sdim	}
325218893Sdim
326218893Sdim	/* Update our volume if we have one. */
327218893Sdim	if (v != NULL)
328218893Sdim		gv_update_vol_state(v);
329218893Sdim
330218893Sdim	/* Save config. */
331218893Sdim	if (flags & GV_SETSTATE_CONFIG)
332218893Sdim		gv_save_config(p->vinumconf);
333218893Sdim	return (0);
334218893Sdim}
335218893Sdim
336218893Sdimint
337198090Srdivackygv_set_vol_state(struct gv_volume *v, int newstate, int flags)
338198090Srdivacky{
339198090Srdivacky	int oldstate;
340198090Srdivacky
341198090Srdivacky	KASSERT(v != NULL, ("gv_set_vol_state: NULL v"));
342198090Srdivacky
343198090Srdivacky	oldstate = v->state;
344198090Srdivacky
345198090Srdivacky	if (newstate == oldstate)
346198090Srdivacky		return (0);
347198090Srdivacky
348198090Srdivacky	switch (newstate) {
349198090Srdivacky	case GV_VOL_UP:
350198090Srdivacky		/* Let update handle if the volume can come up. */
351198090Srdivacky		gv_update_vol_state(v);
352198090Srdivacky		if (v->state != GV_VOL_UP && !(flags & GV_SETSTATE_FORCE))
353198090Srdivacky			return (GV_ERR_SETSTATE);
354198090Srdivacky		v->state = newstate;
355198090Srdivacky		break;
356198090Srdivacky	case GV_VOL_DOWN:
357198090Srdivacky		/*
358198090Srdivacky		 * Set state to GV_VOL_DOWN only if no-one is using the volume,
359198090Srdivacky		 * or if the state should be forced.
360198090Srdivacky		 */
361198090Srdivacky		if (!gv_provider_is_open(v->provider) &&
362198090Srdivacky		    !(flags & GV_SETSTATE_FORCE))
363198892Srdivacky			return (GV_ERR_ISBUSY);
364198892Srdivacky		v->state = newstate;
365198090Srdivacky		break;
366198090Srdivacky	}
367198090Srdivacky	/* Save config */
368198090Srdivacky	if (flags & GV_SETSTATE_CONFIG)
369198090Srdivacky		gv_save_config(v->vinumconf);
370198090Srdivacky	return (0);
371198090Srdivacky}
372198090Srdivacky
373198090Srdivacky/* Update the state of a subdisk based on its environment. */
374198090Srdivackyvoid
375198090Srdivackygv_update_sd_state(struct gv_sd *s)
376198090Srdivacky{
377198090Srdivacky	struct gv_drive *d;
378198090Srdivacky	int oldstate;
379198090Srdivacky
380198090Srdivacky	KASSERT(s != NULL, ("gv_update_sd_state: NULL s"));
381206083Srdivacky	d = s->drive_sc;
382206083Srdivacky	KASSERT(d != NULL, ("gv_update_sd_state: NULL d"));
383206083Srdivacky
384206083Srdivacky	oldstate = s->state;
385206083Srdivacky
386198090Srdivacky	/* If our drive isn't up we cannot be up either. */
387198090Srdivacky	if (d->state != GV_DRIVE_UP) {
388198090Srdivacky		s->state = GV_SD_DOWN;
389198090Srdivacky	/* If this subdisk was just created, we assume it is good.*/
390198090Srdivacky	} else if (s->flags & GV_SD_NEWBORN) {
391198090Srdivacky		s->state = GV_SD_UP;
392198090Srdivacky		s->flags &= ~GV_SD_NEWBORN;
393198090Srdivacky	} else if (s->state != GV_SD_UP) {
394198090Srdivacky		if (s->flags & GV_SD_CANGOUP) {
395198090Srdivacky			s->state = GV_SD_UP;
396198090Srdivacky			s->flags &= ~GV_SD_CANGOUP;
397198090Srdivacky		} else
398198090Srdivacky			s->state = GV_SD_STALE;
399198090Srdivacky	} else
400198090Srdivacky		s->state = GV_SD_UP;
401198090Srdivacky
402198090Srdivacky	if (s->state != oldstate)
403198090Srdivacky		G_VINUM_DEBUG(1, "subdisk %s state change: %s -> %s", s->name,
404198090Srdivacky		    gv_sdstate(oldstate), gv_sdstate(s->state));
405198090Srdivacky
406198090Srdivacky	/* Update the plex, if we have one. */
407198090Srdivacky	if (s->plex_sc != NULL)
408210299Sed		gv_update_plex_state(s->plex_sc);
409210299Sed}
410198090Srdivacky
411198090Srdivacky/* Update the state of a plex based on its environment. */
412198090Srdivackyvoid
413198090Srdivackygv_update_plex_state(struct gv_plex *p)
414198090Srdivacky{
415226633Sdim	struct gv_sd *s;
416198090Srdivacky	int sdstates;
417198090Srdivacky	int oldstate;
418198090Srdivacky
419198090Srdivacky	KASSERT(p != NULL, ("gv_update_plex_state: NULL p"));
420198090Srdivacky
421198090Srdivacky	oldstate = p->state;
422198090Srdivacky
423226633Sdim	/* First, check the state of our subdisks. */
424226633Sdim	sdstates = gv_sdstatemap(p);
425226633Sdim
426226633Sdim	/* If all subdisks are up, our plex can be up, too. */
427226633Sdim	if (sdstates == GV_SD_UPSTATE)
428226633Sdim		p->state = GV_PLEX_UP;
429210299Sed
430198090Srdivacky	/* One or more of our subdisks are down. */
431198090Srdivacky	else if (sdstates & GV_SD_DOWNSTATE) {
432198090Srdivacky		/* A RAID5 plex can handle one dead subdisk. */
433198090Srdivacky		if ((p->org == GV_PLEX_RAID5) && (p->sddown == 1))
434198090Srdivacky			p->state = GV_PLEX_DEGRADED;
435210299Sed		else
436198090Srdivacky			p->state = GV_PLEX_DOWN;
437226633Sdim
438226633Sdim	/* Some of our subdisks are initializing. */
439226633Sdim	} else if (sdstates & GV_SD_INITSTATE) {
440226633Sdim
441198090Srdivacky		if (p->flags & GV_PLEX_SYNCING ||
442198090Srdivacky		    p->flags & GV_PLEX_REBUILDING)
443198090Srdivacky			p->state = GV_PLEX_DEGRADED;
444198090Srdivacky		else
445198090Srdivacky			p->state = GV_PLEX_DOWN;
446198090Srdivacky	} else
447198090Srdivacky		p->state = GV_PLEX_DOWN;
448198090Srdivacky
449198090Srdivacky	if (p->state == GV_PLEX_UP) {
450198090Srdivacky		LIST_FOREACH(s, &p->subdisks, in_plex) {
451234353Sdim			if (s->flags & GV_SD_GROW) {
452234353Sdim				p->state = GV_PLEX_GROWABLE;
453234353Sdim				break;
454234353Sdim			}
455234353Sdim		}
456234353Sdim	}
457234353Sdim
458234353Sdim	if (p->state != oldstate)
459234353Sdim		G_VINUM_DEBUG(1, "plex %s state change: %s -> %s", p->name,
460234353Sdim		    gv_plexstate(oldstate), gv_plexstate(p->state));
461234353Sdim
462234353Sdim	/* Update our volume, if we have one. */
463234353Sdim	if (p->vol_sc != NULL)
464234353Sdim		gv_update_vol_state(p->vol_sc);
465234353Sdim}
466234353Sdim
467198090Srdivacky/* Update the volume state based on its plexes. */
468198090Srdivackyvoid
469198090Srdivackygv_update_vol_state(struct gv_volume *v)
470198090Srdivacky{
471198090Srdivacky	struct gv_plex *p;
472198090Srdivacky
473249423Sdim	KASSERT(v != NULL, ("gv_update_vol_state: NULL v"));
474249423Sdim
475249423Sdim	/* The volume can't be up without plexes. */
476198090Srdivacky	if (v->plexcount == 0) {
477198090Srdivacky		v->state = GV_VOL_DOWN;
478198090Srdivacky		return;
479198090Srdivacky	}
480198090Srdivacky
481198090Srdivacky	LIST_FOREACH(p, &v->plexes, in_volume) {
482198090Srdivacky		/* One of our plexes is accessible, and so are we. */
483198090Srdivacky		if (p->state > GV_PLEX_DEGRADED) {
484198090Srdivacky			v->state = GV_VOL_UP;
485198090Srdivacky			return;
486198090Srdivacky
487198090Srdivacky		/* We can handle a RAID5 plex with one dead subdisk as well. */
488198090Srdivacky		} else if ((p->org == GV_PLEX_RAID5) &&
489198090Srdivacky		    (p->state == GV_PLEX_DEGRADED)) {
490198090Srdivacky			v->state = GV_VOL_UP;
491198090Srdivacky			return;
492198090Srdivacky		}
493198090Srdivacky	}
494198090Srdivacky
495198090Srdivacky	/* Not one of our plexes is up, so we can't be either. */
496198090Srdivacky	v->state = GV_VOL_DOWN;
497198090Srdivacky}
498198090Srdivacky
499198090Srdivacky/* Return a state map for the subdisks of a plex. */
500198090Srdivackyint
501198090Srdivackygv_sdstatemap(struct gv_plex *p)
502198090Srdivacky{
503198090Srdivacky	struct gv_sd *s;
504198090Srdivacky	int statemap;
505198090Srdivacky
506198090Srdivacky	KASSERT(p != NULL, ("gv_sdstatemap: NULL p"));
507198090Srdivacky
508198090Srdivacky	statemap = 0;
509198090Srdivacky	p->sddown = 0;	/* No subdisks down yet. */
510198090Srdivacky
511198090Srdivacky	LIST_FOREACH(s, &p->subdisks, in_plex) {
512198090Srdivacky		switch (s->state) {
513198090Srdivacky		case GV_SD_DOWN:
514198090Srdivacky		case GV_SD_STALE:
515198090Srdivacky			statemap |= GV_SD_DOWNSTATE;
516198090Srdivacky			p->sddown++;	/* Another unusable subdisk. */
517198090Srdivacky			break;
518198090Srdivacky
519198090Srdivacky		case GV_SD_UP:
520198090Srdivacky			statemap |= GV_SD_UPSTATE;
521234353Sdim			break;
522234353Sdim
523198090Srdivacky		case GV_SD_INITIALIZING:
524198090Srdivacky			statemap |= GV_SD_INITSTATE;
525198090Srdivacky			break;
526198090Srdivacky
527198090Srdivacky		case GV_SD_REVIVING:
528198090Srdivacky			statemap |= GV_SD_INITSTATE;
529198090Srdivacky			p->sddown++;	/* XXX: Another unusable subdisk? */
530198090Srdivacky			break;
531199989Srdivacky		}
532199989Srdivacky	}
533199989Srdivacky	return (statemap);
534199989Srdivacky}
535234353Sdim