xref: /freebsd/sys/geom/vinum/geom_vinum_state.c (revision f4b37ed0f8b307b1f3f0f630ca725d68f1dff30d)
1 /*-
2  * Copyright (c) 2004, 2007 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$");
29 
30 #include <sys/libkern.h>
31 #include <sys/malloc.h>
32 
33 #include <geom/geom.h>
34 #include <geom/vinum/geom_vinum_var.h>
35 #include <geom/vinum/geom_vinum.h>
36 #include <geom/vinum/geom_vinum_share.h>
37 
38 void
39 gv_setstate(struct g_geom *gp, struct gctl_req *req)
40 {
41 	struct gv_softc *sc;
42 	struct gv_sd *s;
43 	struct gv_drive *d;
44 	struct gv_volume *v;
45 	struct gv_plex *p;
46 	char *obj, *state;
47 	int f, *flags, 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 		if (gv_volstatei(state) < 0) {
76 			gctl_error(req, "invalid volume state '%s'", state);
77 			break;
78 		}
79 		v = gv_find_vol(sc, obj);
80 		gv_post_event(sc, GV_EVENT_SET_VOL_STATE, v, NULL,
81 		    gv_volstatei(state), f);
82 		break;
83 
84 	case GV_TYPE_PLEX:
85 		if (gv_plexstatei(state) < 0) {
86 			gctl_error(req, "invalid plex state '%s'", state);
87 			break;
88 		}
89 		p = gv_find_plex(sc, obj);
90 		gv_post_event(sc, GV_EVENT_SET_PLEX_STATE, p, NULL,
91 		    gv_plexstatei(state), f);
92 		break;
93 
94 	case GV_TYPE_SD:
95 		if (gv_sdstatei(state) < 0) {
96 			gctl_error(req, "invalid subdisk state '%s'", state);
97 			break;
98 		}
99 		s = gv_find_sd(sc, obj);
100 		gv_post_event(sc, GV_EVENT_SET_SD_STATE, s, NULL,
101 		    gv_sdstatei(state), f);
102 		break;
103 
104 	case GV_TYPE_DRIVE:
105 		if (gv_drivestatei(state) < 0) {
106 			gctl_error(req, "invalid drive state '%s'", state);
107 			break;
108 		}
109 		d = gv_find_drive(sc, obj);
110 		gv_post_event(sc, GV_EVENT_SET_DRIVE_STATE, d, NULL,
111 		    gv_drivestatei(state), f);
112 		break;
113 
114 	default:
115 		gctl_error(req, "unknown object '%s'", obj);
116 		break;
117 	}
118 }
119 
120 /* Update drive state; return 0 if the state changes, otherwise error. */
121 int
122 gv_set_drive_state(struct gv_drive *d, int newstate, int flags)
123 {
124 	struct gv_sd *s;
125 	int oldstate;
126 
127 	KASSERT(d != NULL, ("gv_set_drive_state: NULL d"));
128 
129 	oldstate = d->state;
130 
131 	if (newstate == oldstate)
132 		return (0);
133 
134 	/* We allow to take down an open drive only with force. */
135 	if ((newstate == GV_DRIVE_DOWN) && gv_consumer_is_open(d->consumer) &&
136 	    (!(flags & GV_SETSTATE_FORCE)))
137 		return (GV_ERR_ISBUSY);
138 
139 	d->state = newstate;
140 
141 	if (d->state != oldstate) {
142 		LIST_FOREACH(s, &d->subdisks, from_drive)
143 			gv_update_sd_state(s);
144 	}
145 
146 	/* Save the config back to disk. */
147 	if (flags & GV_SETSTATE_CONFIG)
148 		gv_save_config(d->vinumconf);
149 
150 	return (0);
151 }
152 
153 int
154 gv_set_sd_state(struct gv_sd *s, int newstate, int flags)
155 {
156 	struct gv_drive *d;
157 	struct gv_plex *p;
158 	int oldstate, status;
159 
160 	KASSERT(s != NULL, ("gv_set_sd_state: NULL s"));
161 
162 	oldstate = s->state;
163 
164 	/* We are optimistic and assume it will work. */
165 	status = 0;
166 
167 	if (newstate == oldstate)
168 		return (0);
169 
170 	switch (newstate) {
171 	case GV_SD_DOWN:
172 		/*
173 		 * If we're attached to a plex, we won't go down without use of
174 		 * force.
175 		 */
176 		if ((s->plex_sc != NULL) && !(flags & GV_SETSTATE_FORCE))
177 			return (GV_ERR_ISATTACHED);
178 		break;
179 
180 	case GV_SD_REVIVING:
181 	case GV_SD_INITIALIZING:
182 		/*
183 		 * Only do this if we're forced, since it usually is done
184 		 * internally, and then we do use the force flag.
185 		 */
186 		if (!flags & GV_SETSTATE_FORCE)
187 			return (GV_ERR_SETSTATE);
188 		break;
189 
190 	case GV_SD_UP:
191 		/* We can't bring the subdisk up if our drive is dead. */
192 		d = s->drive_sc;
193 		if ((d == NULL) || (d->state != GV_DRIVE_UP))
194 			return (GV_ERR_SETSTATE);
195 
196 		/* Check from where we want to be brought up. */
197 		switch (s->state) {
198 		case GV_SD_REVIVING:
199 		case GV_SD_INITIALIZING:
200 			/*
201 			 * The subdisk was initializing.  We allow it to be
202 			 * brought up.
203 			 */
204 			break;
205 
206 		case GV_SD_DOWN:
207 			/*
208 			 * The subdisk is currently down.  We allow it to be
209 			 * brought up if it is not attached to a plex.
210 			 */
211 			p = s->plex_sc;
212 			if (p == NULL)
213 				break;
214 
215 			/*
216 			 * If this subdisk is attached to a plex, we allow it
217 			 * to be brought up if the plex if it's not a RAID5
218 			 * plex, otherwise it's made 'stale'.
219 			 */
220 
221 			if (p->org != GV_PLEX_RAID5)
222 				break;
223 			else if (s->flags & GV_SD_CANGOUP) {
224 				s->flags &= ~GV_SD_CANGOUP;
225 				break;
226 			} else if (flags & GV_SETSTATE_FORCE)
227 				break;
228 			else
229 				s->state = GV_SD_STALE;
230 
231 			status = GV_ERR_SETSTATE;
232 			break;
233 
234 		case GV_SD_STALE:
235 			/*
236 			 * A stale subdisk can be brought up only if it's part
237 			 * of a concat or striped plex that's the only one in a
238 			 * volume, or if the subdisk isn't attached to a plex.
239 			 * Otherwise it needs to be revived or initialized
240 			 * first.
241 			 */
242 			p = s->plex_sc;
243 			if (p == NULL || flags & GV_SETSTATE_FORCE)
244 				break;
245 
246 			if ((p->org != GV_PLEX_RAID5 &&
247 			    p->vol_sc->plexcount == 1) ||
248 			    (p->flags & GV_PLEX_SYNCING &&
249 			    p->synced > 0 &&
250 			    p->org == GV_PLEX_RAID5))
251 				break;
252 			else
253 				return (GV_ERR_SETSTATE);
254 
255 		default:
256 			return (GV_ERR_INVSTATE);
257 		}
258 		break;
259 
260 	/* Other state transitions are only possible with force. */
261 	default:
262 		if (!(flags & GV_SETSTATE_FORCE))
263 			return (GV_ERR_SETSTATE);
264 	}
265 
266 	/* We can change the state and do it. */
267 	if (status == 0)
268 		s->state = newstate;
269 
270 	/* Update our plex, if we're attached to one. */
271 	if (s->plex_sc != NULL)
272 		gv_update_plex_state(s->plex_sc);
273 
274 	/* Save the config back to disk. */
275 	if (flags & GV_SETSTATE_CONFIG)
276 		gv_save_config(s->vinumconf);
277 
278 	return (status);
279 }
280 
281 int
282 gv_set_plex_state(struct gv_plex *p, int newstate, int flags)
283 {
284 	struct gv_volume *v;
285 	int oldstate, plexdown;
286 
287 	KASSERT(p != NULL, ("gv_set_plex_state: NULL p"));
288 
289 	oldstate = p->state;
290 	v = p->vol_sc;
291 	plexdown = 0;
292 
293 	if (newstate == oldstate)
294 		return (0);
295 
296 	switch (newstate) {
297 	case GV_PLEX_UP:
298 		/* Let update_plex handle if the plex can come up */
299 		gv_update_plex_state(p);
300 		if (p->state != GV_PLEX_UP && !(flags & GV_SETSTATE_FORCE))
301 			return (GV_ERR_SETSTATE);
302 		p->state = newstate;
303 		break;
304 	case GV_PLEX_DOWN:
305 		/*
306 		 * Set state to GV_PLEX_DOWN only if no-one is using the plex,
307 		 * or if the state is forced.
308 		 */
309 		if (v != NULL) {
310 			/* If the only one up, force is needed. */
311 			plexdown = gv_plexdown(v);
312 			if ((v->plexcount == 1 ||
313 			    (v->plexcount - plexdown == 1)) &&
314 			    ((flags & GV_SETSTATE_FORCE) == 0))
315 				return (GV_ERR_SETSTATE);
316 		}
317 		p->state = newstate;
318 		break;
319 	case GV_PLEX_DEGRADED:
320 		/* Only used internally, so we have to be forced. */
321 		if (flags & GV_SETSTATE_FORCE)
322 			p->state = newstate;
323 		break;
324 	}
325 
326 	/* Update our volume if we have one. */
327 	if (v != NULL)
328 		gv_update_vol_state(v);
329 
330 	/* Save config. */
331 	if (flags & GV_SETSTATE_CONFIG)
332 		gv_save_config(p->vinumconf);
333 	return (0);
334 }
335 
336 int
337 gv_set_vol_state(struct gv_volume *v, int newstate, int flags)
338 {
339 	int oldstate;
340 
341 	KASSERT(v != NULL, ("gv_set_vol_state: NULL v"));
342 
343 	oldstate = v->state;
344 
345 	if (newstate == oldstate)
346 		return (0);
347 
348 	switch (newstate) {
349 	case GV_VOL_UP:
350 		/* Let update handle if the volume can come up. */
351 		gv_update_vol_state(v);
352 		if (v->state != GV_VOL_UP && !(flags & GV_SETSTATE_FORCE))
353 			return (GV_ERR_SETSTATE);
354 		v->state = newstate;
355 		break;
356 	case GV_VOL_DOWN:
357 		/*
358 		 * Set state to GV_VOL_DOWN only if no-one is using the volume,
359 		 * or if the state should be forced.
360 		 */
361 		if (!gv_provider_is_open(v->provider) &&
362 		    !(flags & GV_SETSTATE_FORCE))
363 			return (GV_ERR_ISBUSY);
364 		v->state = newstate;
365 		break;
366 	}
367 	/* Save config */
368 	if (flags & GV_SETSTATE_CONFIG)
369 		gv_save_config(v->vinumconf);
370 	return (0);
371 }
372 
373 /* Update the state of a subdisk based on its environment. */
374 void
375 gv_update_sd_state(struct gv_sd *s)
376 {
377 	struct gv_drive *d;
378 	int oldstate;
379 
380 	KASSERT(s != NULL, ("gv_update_sd_state: NULL s"));
381 	d = s->drive_sc;
382 	KASSERT(d != NULL, ("gv_update_sd_state: NULL d"));
383 
384 	oldstate = s->state;
385 
386 	/* If our drive isn't up we cannot be up either. */
387 	if (d->state != GV_DRIVE_UP) {
388 		s->state = GV_SD_DOWN;
389 	/* If this subdisk was just created, we assume it is good.*/
390 	} else if (s->flags & GV_SD_NEWBORN) {
391 		s->state = GV_SD_UP;
392 		s->flags &= ~GV_SD_NEWBORN;
393 	} else if (s->state != GV_SD_UP) {
394 		if (s->flags & GV_SD_CANGOUP) {
395 			s->state = GV_SD_UP;
396 			s->flags &= ~GV_SD_CANGOUP;
397 		} else
398 			s->state = GV_SD_STALE;
399 	} else
400 		s->state = GV_SD_UP;
401 
402 	if (s->state != oldstate)
403 		G_VINUM_DEBUG(1, "subdisk %s state change: %s -> %s", s->name,
404 		    gv_sdstate(oldstate), gv_sdstate(s->state));
405 
406 	/* Update the plex, if we have one. */
407 	if (s->plex_sc != NULL)
408 		gv_update_plex_state(s->plex_sc);
409 }
410 
411 /* Update the state of a plex based on its environment. */
412 void
413 gv_update_plex_state(struct gv_plex *p)
414 {
415 	struct gv_sd *s;
416 	int sdstates;
417 	int oldstate;
418 
419 	KASSERT(p != NULL, ("gv_update_plex_state: NULL p"));
420 
421 	oldstate = p->state;
422 
423 	/* First, check the state of our subdisks. */
424 	sdstates = gv_sdstatemap(p);
425 
426 	/* If all subdisks are up, our plex can be up, too. */
427 	if (sdstates == GV_SD_UPSTATE)
428 		p->state = GV_PLEX_UP;
429 
430 	/* One or more of our subdisks are down. */
431 	else if (sdstates & GV_SD_DOWNSTATE) {
432 		/* A RAID5 plex can handle one dead subdisk. */
433 		if ((p->org == GV_PLEX_RAID5) && (p->sddown == 1))
434 			p->state = GV_PLEX_DEGRADED;
435 		else
436 			p->state = GV_PLEX_DOWN;
437 
438 	/* Some of our subdisks are initializing. */
439 	} else if (sdstates & GV_SD_INITSTATE) {
440 
441 		if (p->flags & GV_PLEX_SYNCING ||
442 		    p->flags & GV_PLEX_REBUILDING)
443 			p->state = GV_PLEX_DEGRADED;
444 		else
445 			p->state = GV_PLEX_DOWN;
446 	} else
447 		p->state = GV_PLEX_DOWN;
448 
449 	if (p->state == GV_PLEX_UP) {
450 		LIST_FOREACH(s, &p->subdisks, in_plex) {
451 			if (s->flags & GV_SD_GROW) {
452 				p->state = GV_PLEX_GROWABLE;
453 				break;
454 			}
455 		}
456 	}
457 
458 	if (p->state != oldstate)
459 		G_VINUM_DEBUG(1, "plex %s state change: %s -> %s", p->name,
460 		    gv_plexstate(oldstate), gv_plexstate(p->state));
461 
462 	/* Update our volume, if we have one. */
463 	if (p->vol_sc != NULL)
464 		gv_update_vol_state(p->vol_sc);
465 }
466 
467 /* Update the volume state based on its plexes. */
468 void
469 gv_update_vol_state(struct gv_volume *v)
470 {
471 	struct gv_plex *p;
472 
473 	KASSERT(v != NULL, ("gv_update_vol_state: NULL v"));
474 
475 	/* The volume can't be up without plexes. */
476 	if (v->plexcount == 0) {
477 		v->state = GV_VOL_DOWN;
478 		return;
479 	}
480 
481 	LIST_FOREACH(p, &v->plexes, in_volume) {
482 		/* One of our plexes is accessible, and so are we. */
483 		if (p->state > GV_PLEX_DEGRADED) {
484 			v->state = GV_VOL_UP;
485 			return;
486 
487 		/* We can handle a RAID5 plex with one dead subdisk as well. */
488 		} else if ((p->org == GV_PLEX_RAID5) &&
489 		    (p->state == GV_PLEX_DEGRADED)) {
490 			v->state = GV_VOL_UP;
491 			return;
492 		}
493 	}
494 
495 	/* Not one of our plexes is up, so we can't be either. */
496 	v->state = GV_VOL_DOWN;
497 }
498 
499 /* Return a state map for the subdisks of a plex. */
500 int
501 gv_sdstatemap(struct gv_plex *p)
502 {
503 	struct gv_sd *s;
504 	int statemap;
505 
506 	KASSERT(p != NULL, ("gv_sdstatemap: NULL p"));
507 
508 	statemap = 0;
509 	p->sddown = 0;	/* No subdisks down yet. */
510 
511 	LIST_FOREACH(s, &p->subdisks, in_plex) {
512 		switch (s->state) {
513 		case GV_SD_DOWN:
514 		case GV_SD_STALE:
515 			statemap |= GV_SD_DOWNSTATE;
516 			p->sddown++;	/* Another unusable subdisk. */
517 			break;
518 
519 		case GV_SD_UP:
520 			statemap |= GV_SD_UPSTATE;
521 			break;
522 
523 		case GV_SD_INITIALIZING:
524 			statemap |= GV_SD_INITSTATE;
525 			break;
526 
527 		case GV_SD_REVIVING:
528 			statemap |= GV_SD_INITSTATE;
529 			p->sddown++;	/* XXX: Another unusable subdisk? */
530 			break;
531 		}
532 	}
533 	return (statemap);
534 }
535