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
41void
42gv_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. */
124int
125gv_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
156int
157gv_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
284int
285gv_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
339int
340gv_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. */
377void
378gv_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. */
415void
416gv_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. */
470void
471gv_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. */
502int
503gv_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