geom_vinum_state.c revision 157292
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 157292 2006-03-30 14:01:25Z 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
40void
41gv_setstate(struct g_geom *gp, struct gctl_req *req)
42{
43	struct gv_softc *sc;
44	struct gv_sd *s;
45	struct gv_drive *d;
46	char *obj, *state;
47	int err, f, *flags, newstate, type;
48
49	f = 0;
50	obj = gctl_get_param(req, "object", NULL);
51	if (obj == NULL) {
52		gctl_error(req, "no object given");
53		return;
54	}
55
56	state = gctl_get_param(req, "state", NULL);
57	if (state == NULL) {
58		gctl_error(req, "no state given");
59		return;
60	}
61
62	flags = gctl_get_paraml(req, "flags", sizeof(*flags));
63	if (flags == NULL) {
64		gctl_error(req, "no flags given");
65		return;
66	}
67
68	if (*flags & GV_FLAG_F)
69		f = GV_SETSTATE_FORCE;
70
71	sc = gp->softc;
72	type = gv_object_type(sc, obj);
73	switch (type) {
74	case GV_TYPE_VOL:
75	case GV_TYPE_PLEX:
76		gctl_error(req, "volume or plex state cannot be set currently");
77		break;
78
79	case GV_TYPE_SD:
80		newstate = gv_sdstatei(state);
81		if (newstate < 0) {
82			gctl_error(req, "invalid subdisk state '%s'", state);
83			break;
84		}
85		s = gv_find_sd(sc, obj);
86		err = gv_set_sd_state(s, newstate, f);
87		if (err)
88			gctl_error(req, "cannot set subdisk state");
89		break;
90
91	case GV_TYPE_DRIVE:
92		newstate = gv_drivestatei(state);
93		if (newstate < 0) {
94			gctl_error(req, "invalid drive state '%s'", state);
95			break;
96		}
97		d = gv_find_drive(sc, obj);
98		err = gv_set_drive_state(d, newstate, f);
99		if (err)
100			gctl_error(req, "cannot set drive state");
101		break;
102
103	default:
104		gctl_error(req, "unknown object '%s'", obj);
105		break;
106	}
107
108	return;
109}
110
111/* Update drive state; return 0 if the state changes, otherwise -1. */
112int
113gv_set_drive_state(struct gv_drive *d, int newstate, int flags)
114{
115	struct gv_sd *s;
116	int oldstate;
117
118	KASSERT(d != NULL, ("gv_set_drive_state: NULL d"));
119
120	oldstate = d->state;
121
122	if (newstate == oldstate)
123		return (0);
124
125	/* We allow to take down an open drive only with force. */
126	if ((newstate == GV_DRIVE_DOWN) && gv_is_open(d->geom) &&
127	    (!(flags & GV_SETSTATE_FORCE)))
128		return (-1);
129
130	d->state = newstate;
131
132	if (d->state != oldstate) {
133		LIST_FOREACH(s, &d->subdisks, from_drive)
134			gv_update_sd_state(s);
135	}
136
137	/* Save the config back to disk. */
138	if (flags & GV_SETSTATE_CONFIG)
139		gv_save_config_all(d->vinumconf);
140
141	return (0);
142}
143
144int
145gv_set_sd_state(struct gv_sd *s, int newstate, int flags)
146{
147	struct gv_drive *d;
148	struct gv_plex *p;
149	int oldstate, status;
150
151	KASSERT(s != NULL, ("gv_set_sd_state: NULL s"));
152
153	oldstate = s->state;
154
155	/* We are optimistic and assume it will work. */
156	status = 0;
157
158	if (newstate == oldstate)
159		return (0);
160
161	switch (newstate) {
162	case GV_SD_DOWN:
163		/*
164		 * If we're attached to a plex, we won't go down without use of
165		 * force.
166		 */
167		if ((s->plex_sc != NULL) && !(flags & GV_SETSTATE_FORCE))
168			return (-1);
169		break;
170
171	case GV_SD_UP:
172		/* We can't bring the subdisk up if our drive is dead. */
173		d = s->drive_sc;
174		if ((d == NULL) || (d->state != GV_DRIVE_UP))
175			return (-1);
176
177		/* Check from where we want to be brought up. */
178		switch (s->state) {
179		case GV_SD_REVIVING:
180		case GV_SD_INITIALIZING:
181			/*
182			 * The subdisk was initializing.  We allow it to be
183			 * brought up.
184			 */
185			break;
186
187		case GV_SD_DOWN:
188			/*
189			 * The subdisk is currently down.  We allow it to be
190			 * brought up if it is not attached to a plex.
191			 */
192			p = s->plex_sc;
193			if (p == NULL)
194				break;
195
196			/*
197			 * If this subdisk is attached to a plex, we allow it
198			 * to be brought up if the plex if it's not a RAID5
199			 * plex, otherwise it's made 'stale'.
200			 */
201
202			if (p->org != GV_PLEX_RAID5)
203				break;
204			else if (flags & GV_SETSTATE_FORCE)
205				break;
206			else
207				s->state = GV_SD_STALE;
208
209			status = -1;
210			break;
211
212		case GV_SD_STALE:
213			/*
214			 * A stale subdisk can be brought up only if it's part
215			 * of a concat or striped plex that's the only one in a
216			 * volume, or if the subdisk isn't attached to a plex.
217			 * Otherwise it needs to be revived or initialized
218			 * first.
219			 */
220			p = s->plex_sc;
221			if (p == NULL || flags & GV_SETSTATE_FORCE)
222				break;
223
224			if ((p->org != GV_PLEX_RAID5) &&
225			    (p->vol_sc->plexcount == 1))
226				break;
227			else
228				return (-1);
229
230		default:
231			return (-1);
232		}
233		break;
234
235	/* Other state transitions are only possible with force. */
236	default:
237		if (!(flags & GV_SETSTATE_FORCE))
238			return (-1);
239	}
240
241	/* We can change the state and do it. */
242	if (status == 0)
243		s->state = newstate;
244
245	/* Update our plex, if we're attached to one. */
246	if (s->plex_sc != NULL)
247		gv_update_plex_state(s->plex_sc);
248
249	/* Save the config back to disk. */
250	if (flags & GV_SETSTATE_CONFIG)
251		gv_save_config_all(s->vinumconf);
252
253	return (status);
254}
255
256
257/* Update the state of a subdisk based on its environment. */
258void
259gv_update_sd_state(struct gv_sd *s)
260{
261	struct gv_drive *d;
262	int oldstate;
263
264	KASSERT(s != NULL, ("gv_update_sd_state: NULL s"));
265	d = s->drive_sc;
266	KASSERT(d != NULL, ("gv_update_sd_state: NULL d"));
267
268	oldstate = s->state;
269
270	/* If our drive isn't up we cannot be up either. */
271	if (d->state != GV_DRIVE_UP)
272		s->state = GV_SD_DOWN;
273	/* If this subdisk was just created, we assume it is good.*/
274	else if (s->flags & GV_SD_NEWBORN) {
275		s->state = GV_SD_UP;
276		s->flags &= ~GV_SD_NEWBORN;
277	} else if (s->state != GV_SD_UP)
278		s->state = GV_SD_STALE;
279	else
280		s->state = GV_SD_UP;
281
282	if (s->state != oldstate)
283		printf("GEOM_VINUM: subdisk %s state change: %s -> %s\n",
284		    s->name, gv_sdstate(oldstate), gv_sdstate(s->state));
285
286	/* Update the plex, if we have one. */
287	if (s->plex_sc != NULL)
288		gv_update_plex_state(s->plex_sc);
289}
290
291/* Update the state of a plex based on its environment. */
292void
293gv_update_plex_state(struct gv_plex *p)
294{
295	int sdstates;
296	int oldstate;
297
298	KASSERT(p != NULL, ("gv_update_plex_state: NULL p"));
299
300	oldstate = p->state;
301
302	/* First, check the state of our subdisks. */
303	sdstates = gv_sdstatemap(p);
304
305	/* If all subdisks are up, our plex can be up, too. */
306	if (sdstates == GV_SD_UPSTATE)
307		p->state = GV_PLEX_UP;
308
309	/* One or more of our subdisks are down. */
310	else if (sdstates & GV_SD_DOWNSTATE) {
311		/* A RAID5 plex can handle one dead subdisk. */
312		if ((p->org == GV_PLEX_RAID5) && (p->sddown == 1))
313			p->state = GV_PLEX_DEGRADED;
314		else
315			p->state = GV_PLEX_DOWN;
316
317	/* Some of our subdisks are initializing. */
318	} else if (sdstates & GV_SD_INITSTATE) {
319		if (p->flags & GV_PLEX_SYNCING)
320			p->state = GV_PLEX_DEGRADED;
321		else
322			p->state = GV_PLEX_DOWN;
323	} else
324		p->state = GV_PLEX_DOWN;
325
326	if (p->state != oldstate)
327		printf("GEOM_VINUM: plex %s state change: %s -> %s\n", p->name,
328		    gv_plexstate(oldstate), gv_plexstate(p->state));
329
330	/* Update our volume, if we have one. */
331	if (p->vol_sc != NULL)
332		gv_update_vol_state(p->vol_sc);
333}
334
335/* Update the volume state based on its plexes. */
336void
337gv_update_vol_state(struct gv_volume *v)
338{
339	struct gv_plex *p;
340
341	KASSERT(v != NULL, ("gv_update_vol_state: NULL v"));
342
343	/* The volume can't be up without plexes. */
344	if (v->plexcount == 0) {
345		v->state = GV_VOL_DOWN;
346		return;
347	}
348
349	LIST_FOREACH(p, &v->plexes, in_volume) {
350		/* One of our plexes is accessible, and so are we. */
351		if (p->state > GV_PLEX_DEGRADED) {
352			v->state = GV_VOL_UP;
353			return;
354
355		/* We can handle a RAID5 plex with one dead subdisk as well. */
356		} else if ((p->org == GV_PLEX_RAID5) &&
357		    (p->state == GV_PLEX_DEGRADED)) {
358			v->state = GV_VOL_UP;
359			return;
360		}
361	}
362
363	/* Not one of our plexes is up, so we can't be either. */
364	v->state = GV_VOL_DOWN;
365}
366
367/* Return a state map for the subdisks of a plex. */
368int
369gv_sdstatemap(struct gv_plex *p)
370{
371	struct gv_sd *s;
372	int statemap;
373
374	KASSERT(p != NULL, ("gv_sdstatemap: NULL p"));
375
376	statemap = 0;
377	p->sddown = 0;	/* No subdisks down yet. */
378
379	LIST_FOREACH(s, &p->subdisks, in_plex) {
380		switch (s->state) {
381		case GV_SD_DOWN:
382		case GV_SD_STALE:
383			statemap |= GV_SD_DOWNSTATE;
384			p->sddown++;	/* Another unusable subdisk. */
385			break;
386
387		case GV_SD_UP:
388			statemap |= GV_SD_UPSTATE;
389			break;
390
391		case GV_SD_INITIALIZING:
392			statemap |= GV_SD_INITSTATE;
393			break;
394
395		case GV_SD_REVIVING:
396			statemap |= GV_SD_INITSTATE;
397			p->sddown++;	/* XXX: Another unusable subdisk? */
398			break;
399		}
400	}
401	return (statemap);
402}
403