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
38void
39gv_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. */
121int
122gv_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
153int
154gv_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
281int
282gv_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
336int
337gv_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. */
374void
375gv_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. */
412void
413gv_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. */
468void
469gv_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. */
500int
501gv_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