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