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