geom_vinum_state.c revision 135162
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 135162 2004-09-13 17:33:52Z 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't be brought up directly, it
142			 * needs to be revived or initialized first.
143			 */
144			/* FALLTHROUGH */
145		default:
146			return (-1);
147		}
148		break;
149
150	/* Other state transitions are only possible with force. */
151	default:
152		if (!(flags & GV_SETSTATE_FORCE))
153			return (-1);
154	}
155
156	/* We can change the state and do it. */
157	if (status == 0)
158		s->state = newstate;
159
160	/* Update our plex, if we're attached to one. */
161	if (s->plex_sc != NULL)
162		gv_update_plex_state(s->plex_sc);
163
164	/* Save the config back to disk. */
165	if (flags & GV_SETSTATE_CONFIG)
166		gv_save_config_all(s->vinumconf);
167
168	return (status);
169}
170
171
172/* Update the state of a subdisk based on its environment. */
173void
174gv_update_sd_state(struct gv_sd *s)
175{
176	struct gv_drive *d;
177
178	KASSERT(s != NULL, ("gv_update_sd_state: NULL s"));
179	d = s->drive_sc;
180	KASSERT(d != NULL, ("gv_update_sd_state: NULL d"));
181
182	/* If our drive isn't up we cannot be up either. */
183	if (d->state != GV_DRIVE_UP)
184		s->state = GV_SD_DOWN;
185	/* If this subdisk was just created, we assume it is good.*/
186	else if (s->flags & GV_SD_NEWBORN) {
187		s->state = GV_SD_UP;
188		s->flags &= ~GV_SD_NEWBORN;
189	} else if (s->state != GV_SD_UP)
190		s->state = GV_SD_STALE;
191	else
192		s->state = GV_SD_UP;
193
194	printf("GEOM_VINUM: subdisk %s is %s\n", s->name, gv_sdstate(s->state));
195	/* Update the plex, if we have one. */
196	if (s->plex_sc != NULL)
197		gv_update_plex_state(s->plex_sc);
198}
199
200/* Update the state of a plex based on its environment. */
201void
202gv_update_plex_state(struct gv_plex *p)
203{
204	int sdstates;
205
206	KASSERT(p != NULL, ("gv_update_plex_state: NULL p"));
207
208	/* First, check the state of our subdisks. */
209	sdstates = gv_sdstatemap(p);
210
211	/* If all subdisks are up, our plex can be up, too. */
212	if (sdstates == GV_SD_UPSTATE)
213		p->state = GV_PLEX_UP;
214
215	/* One or more of our subdisks are down. */
216	else if (sdstates & GV_SD_DOWNSTATE) {
217		/* A RAID5 plex can handle one dead subdisk. */
218		if ((p->org == GV_PLEX_RAID5) && (p->sddown == 1))
219			p->state = GV_PLEX_DEGRADED;
220		else
221			p->state = GV_PLEX_DOWN;
222
223	/* Some of our subdisks are initializing. */
224	} else if (sdstates & GV_SD_INITSTATE) {
225		if (p->flags & GV_PLEX_SYNCING)
226			p->state = GV_PLEX_DEGRADED;
227		else
228			p->state = GV_PLEX_DOWN;
229	} else
230		p->state = GV_PLEX_DOWN;
231
232	printf("GEOM_VINUM: plex %s is %s\n", p->name, gv_plexstate(p->state));
233	/* Update our volume, if we have one. */
234	if (p->vol_sc != NULL)
235		gv_update_vol_state(p->vol_sc);
236}
237
238/* Update the volume state based on its plexes. */
239void
240gv_update_vol_state(struct gv_volume *v)
241{
242	struct gv_plex *p;
243
244	KASSERT(v != NULL, ("gv_update_vol_state: NULL v"));
245
246	LIST_FOREACH(p, &v->plexes, in_volume) {
247		/* One of our plexes is accessible, and so are we. */
248		if (p->state > GV_PLEX_DEGRADED) {
249			v->state = GV_VOL_UP;
250			return;
251
252		/* We can handle a RAID5 plex with one dead subdisk as well. */
253		} else if ((p->org == GV_PLEX_RAID5) &&
254		    (p->state == GV_PLEX_DEGRADED)) {
255			v->state = GV_VOL_UP;
256			return;
257		}
258	}
259
260	/* Not one of our plexes is up, so we can't be either. */
261	v->state = GV_VOL_DOWN;
262}
263
264/* Return a state map for the subdisks of a plex. */
265int
266gv_sdstatemap(struct gv_plex *p)
267{
268	struct gv_sd *s;
269	int statemap;
270
271	KASSERT(p != NULL, ("gv_sdstatemap: NULL p"));
272
273	statemap = 0;
274	p->sddown = 0;	/* No subdisks down yet. */
275
276	LIST_FOREACH(s, &p->subdisks, in_plex) {
277		switch (s->state) {
278		case GV_SD_DOWN:
279		case GV_SD_STALE:
280			statemap |= GV_SD_DOWNSTATE;
281			p->sddown++;	/* Another unusable subdisk. */
282			break;
283
284		case GV_SD_UP:
285			statemap |= GV_SD_UPSTATE;
286			break;
287
288		case GV_SD_INITIALIZING:
289			statemap |= GV_SD_INITSTATE;
290			break;
291
292		case GV_SD_REVIVING:
293			statemap |= GV_SD_INITSTATE;
294			p->sddown++;	/* XXX: Another unusable subdisk? */
295			break;
296		}
297	}
298	return (statemap);
299}
300