geom_vinum_state.c revision 135434
1/*-
2 * Copyright (c) 2004 Lukas Ertl
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD: head/sys/geom/vinum/geom_vinum_state.c 135434 2004-09-18 18:03:20Z le $");
29
30#include <sys/param.h>
31#include <sys/kernel.h>
32#include <sys/libkern.h>
33#include <sys/malloc.h>
34
35#include <geom/geom.h>
36#include <geom/vinum/geom_vinum_var.h>
37#include <geom/vinum/geom_vinum.h>
38#include <geom/vinum/geom_vinum_share.h>
39
40/* Update drive state; return 1 if the state changes, otherwise 0. */
41int
42gv_set_drive_state(struct gv_drive *d, int newstate, int flags)
43{
44	struct gv_sd *s;
45	int oldstate;
46
47	KASSERT(d != NULL, ("gv_set_drive_state: NULL d"));
48
49	oldstate = d->state;
50
51	if (newstate == oldstate)
52		return (1);
53
54	/* We allow to take down an open drive only with force. */
55	if ((newstate == GV_DRIVE_DOWN) && gv_is_open(d->geom) &&
56	    (!(flags & GV_SETSTATE_FORCE)))
57		return (0);
58
59	d->state = newstate;
60
61	if (d->state != oldstate) {
62		LIST_FOREACH(s, &d->subdisks, from_drive)
63			gv_update_sd_state(s);
64	}
65
66	/* Save the config back to disk. */
67	if (flags & GV_SETSTATE_CONFIG)
68		gv_save_config_all(d->vinumconf);
69
70	return (1);
71}
72
73int
74gv_set_sd_state(struct gv_sd *s, int newstate, int flags)
75{
76	struct gv_drive *d;
77	struct gv_plex *p;
78	int oldstate, status;
79
80	KASSERT(s != NULL, ("gv_set_sd_state: NULL s"));
81
82	oldstate = s->state;
83
84	/* We are optimistic and assume it will work. */
85	status = 0;
86
87	if (newstate == oldstate)
88		return (0);
89
90	switch (newstate) {
91	case GV_SD_DOWN:
92		/*
93		 * If we're attached to a plex, we won't go down without use of
94		 * force.
95		 */
96		if ((s->plex_sc != NULL) && !(flags & GV_SETSTATE_FORCE))
97			return (-1);
98		break;
99
100	case GV_SD_UP:
101		/* We can't bring the subdisk up if our drive is dead. */
102		d = s->drive_sc;
103		if ((d == NULL) || (d->state != GV_DRIVE_UP))
104			return (-1);
105
106		/* Check from where we want to be brought up. */
107		switch (s->state) {
108		case GV_SD_REVIVING:
109		case GV_SD_INITIALIZING:
110			/*
111			 * The subdisk was initializing.  We allow it to be
112			 * brought up.
113			 */
114			break;
115
116		case GV_SD_DOWN:
117			/*
118			 * The subdisk is currently down.  We allow it to be
119			 * brought up if it is not attached to a plex.
120			 */
121			p = s->plex_sc;
122			if (p == NULL)
123				break;
124
125			/*
126			 * If this subdisk is attached to a plex, we allow it
127			 * to be brought up if the plex if it's not a RAID5
128			 * plex, otherwise it's made 'stale'.
129			 */
130
131			if (p->org != GV_PLEX_RAID5)
132				break;
133			else
134				s->state = GV_SD_STALE;
135
136			status = -1;
137			break;
138
139		case GV_SD_STALE:
140			/*
141			 * A stale subdisk can be brought up only if it's part
142			 * of a concat or striped plex that's the only one in a
143			 * volume, or if the subdisk isn't attached to a plex.
144			 * Otherwise it needs to be revived or initialized
145			 * first.
146			 */
147			p = s->plex_sc;
148			if (p == NULL)
149				break;
150
151			if ((p->org != GV_PLEX_RAID5) &&
152			    (p->vol_sc->plexcount == 1))
153				break;
154			else
155				return (-1);
156
157		default:
158			return (-1);
159		}
160		break;
161
162	/* Other state transitions are only possible with force. */
163	default:
164		if (!(flags & GV_SETSTATE_FORCE))
165			return (-1);
166	}
167
168	/* We can change the state and do it. */
169	if (status == 0)
170		s->state = newstate;
171
172	/* Update our plex, if we're attached to one. */
173	if (s->plex_sc != NULL)
174		gv_update_plex_state(s->plex_sc);
175
176	/* Save the config back to disk. */
177	if (flags & GV_SETSTATE_CONFIG)
178		gv_save_config_all(s->vinumconf);
179
180	return (status);
181}
182
183
184/* Update the state of a subdisk based on its environment. */
185void
186gv_update_sd_state(struct gv_sd *s)
187{
188	struct gv_drive *d;
189
190	KASSERT(s != NULL, ("gv_update_sd_state: NULL s"));
191	d = s->drive_sc;
192	KASSERT(d != NULL, ("gv_update_sd_state: NULL d"));
193
194	/* If our drive isn't up we cannot be up either. */
195	if (d->state != GV_DRIVE_UP)
196		s->state = GV_SD_DOWN;
197	/* If this subdisk was just created, we assume it is good.*/
198	else if (s->flags & GV_SD_NEWBORN) {
199		s->state = GV_SD_UP;
200		s->flags &= ~GV_SD_NEWBORN;
201	} else if (s->state != GV_SD_UP)
202		s->state = GV_SD_STALE;
203	else
204		s->state = GV_SD_UP;
205
206	printf("GEOM_VINUM: subdisk %s is %s\n", s->name, gv_sdstate(s->state));
207	/* Update the plex, if we have one. */
208	if (s->plex_sc != NULL)
209		gv_update_plex_state(s->plex_sc);
210}
211
212/* Update the state of a plex based on its environment. */
213void
214gv_update_plex_state(struct gv_plex *p)
215{
216	int sdstates;
217
218	KASSERT(p != NULL, ("gv_update_plex_state: NULL p"));
219
220	/* First, check the state of our subdisks. */
221	sdstates = gv_sdstatemap(p);
222
223	/* If all subdisks are up, our plex can be up, too. */
224	if (sdstates == GV_SD_UPSTATE)
225		p->state = GV_PLEX_UP;
226
227	/* One or more of our subdisks are down. */
228	else if (sdstates & GV_SD_DOWNSTATE) {
229		/* A RAID5 plex can handle one dead subdisk. */
230		if ((p->org == GV_PLEX_RAID5) && (p->sddown == 1))
231			p->state = GV_PLEX_DEGRADED;
232		else
233			p->state = GV_PLEX_DOWN;
234
235	/* Some of our subdisks are initializing. */
236	} else if (sdstates & GV_SD_INITSTATE) {
237		if (p->flags & GV_PLEX_SYNCING)
238			p->state = GV_PLEX_DEGRADED;
239		else
240			p->state = GV_PLEX_DOWN;
241	} else
242		p->state = GV_PLEX_DOWN;
243
244	printf("GEOM_VINUM: plex %s is %s\n", p->name, gv_plexstate(p->state));
245	/* Update our volume, if we have one. */
246	if (p->vol_sc != NULL)
247		gv_update_vol_state(p->vol_sc);
248}
249
250/* Update the volume state based on its plexes. */
251void
252gv_update_vol_state(struct gv_volume *v)
253{
254	struct gv_plex *p;
255
256	KASSERT(v != NULL, ("gv_update_vol_state: NULL v"));
257
258	LIST_FOREACH(p, &v->plexes, in_volume) {
259		/* One of our plexes is accessible, and so are we. */
260		if (p->state > GV_PLEX_DEGRADED) {
261			v->state = GV_VOL_UP;
262			return;
263
264		/* We can handle a RAID5 plex with one dead subdisk as well. */
265		} else if ((p->org == GV_PLEX_RAID5) &&
266		    (p->state == GV_PLEX_DEGRADED)) {
267			v->state = GV_VOL_UP;
268			return;
269		}
270	}
271
272	/* Not one of our plexes is up, so we can't be either. */
273	v->state = GV_VOL_DOWN;
274}
275
276/* Return a state map for the subdisks of a plex. */
277int
278gv_sdstatemap(struct gv_plex *p)
279{
280	struct gv_sd *s;
281	int statemap;
282
283	KASSERT(p != NULL, ("gv_sdstatemap: NULL p"));
284
285	statemap = 0;
286	p->sddown = 0;	/* No subdisks down yet. */
287
288	LIST_FOREACH(s, &p->subdisks, in_plex) {
289		switch (s->state) {
290		case GV_SD_DOWN:
291		case GV_SD_STALE:
292			statemap |= GV_SD_DOWNSTATE;
293			p->sddown++;	/* Another unusable subdisk. */
294			break;
295
296		case GV_SD_UP:
297			statemap |= GV_SD_UPSTATE;
298			break;
299
300		case GV_SD_INITIALIZING:
301			statemap |= GV_SD_INITSTATE;
302			break;
303
304		case GV_SD_REVIVING:
305			statemap |= GV_SD_INITSTATE;
306			p->sddown++;	/* XXX: Another unusable subdisk? */
307			break;
308		}
309	}
310	return (statemap);
311}
312