xref: /freebsd/sys/geom/vinum/geom_vinum_state.c (revision 39beb93c)
1 /*-
2  * Copyright (c) 2004 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/param.h>
31 #include <sys/kernel.h>
32 #include <sys/libkern.h>
33 #include <sys/malloc.h>
34 
35 #include <geom/geom.h>
36 #include <geom/vinum/geom_vinum_var.h>
37 #include <geom/vinum/geom_vinum.h>
38 #include <geom/vinum/geom_vinum_share.h>
39 
40 void
41 gv_setstate(struct g_geom *gp, struct gctl_req *req)
42 {
43 	struct gv_softc *sc;
44 	struct gv_sd *s;
45 	struct gv_drive *d;
46 	char *obj, *state;
47 	int err, f, *flags, newstate, 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 	case GV_TYPE_PLEX:
76 		gctl_error(req, "volume or plex state cannot be set currently");
77 		break;
78 
79 	case GV_TYPE_SD:
80 		newstate = gv_sdstatei(state);
81 		if (newstate < 0) {
82 			gctl_error(req, "invalid subdisk state '%s'", state);
83 			break;
84 		}
85 		s = gv_find_sd(sc, obj);
86 		err = gv_set_sd_state(s, newstate, f);
87 		if (err)
88 			gctl_error(req, "cannot set subdisk state");
89 		break;
90 
91 	case GV_TYPE_DRIVE:
92 		newstate = gv_drivestatei(state);
93 		if (newstate < 0) {
94 			gctl_error(req, "invalid drive state '%s'", state);
95 			break;
96 		}
97 		d = gv_find_drive(sc, obj);
98 		err = gv_set_drive_state(d, newstate, f);
99 		if (err)
100 			gctl_error(req, "cannot set drive state");
101 		break;
102 
103 	default:
104 		gctl_error(req, "unknown object '%s'", obj);
105 		break;
106 	}
107 
108 	return;
109 }
110 
111 /* Update drive state; return 0 if the state changes, otherwise -1. */
112 int
113 gv_set_drive_state(struct gv_drive *d, int newstate, int flags)
114 {
115 	struct gv_sd *s;
116 	int oldstate;
117 
118 	KASSERT(d != NULL, ("gv_set_drive_state: NULL d"));
119 
120 	oldstate = d->state;
121 
122 	if (newstate == oldstate)
123 		return (0);
124 
125 	/* We allow to take down an open drive only with force. */
126 	if ((newstate == GV_DRIVE_DOWN) && gv_is_open(d->geom) &&
127 	    (!(flags & GV_SETSTATE_FORCE)))
128 		return (-1);
129 
130 	d->state = newstate;
131 
132 	if (d->state != oldstate) {
133 		LIST_FOREACH(s, &d->subdisks, from_drive)
134 			gv_update_sd_state(s);
135 	}
136 
137 	/* Save the config back to disk. */
138 	if (flags & GV_SETSTATE_CONFIG)
139 		gv_save_config_all(d->vinumconf);
140 
141 	return (0);
142 }
143 
144 int
145 gv_set_sd_state(struct gv_sd *s, int newstate, int flags)
146 {
147 	struct gv_drive *d;
148 	struct gv_plex *p;
149 	int oldstate, status;
150 
151 	KASSERT(s != NULL, ("gv_set_sd_state: NULL s"));
152 
153 	oldstate = s->state;
154 
155 	/* We are optimistic and assume it will work. */
156 	status = 0;
157 
158 	if (newstate == oldstate)
159 		return (0);
160 
161 	switch (newstate) {
162 	case GV_SD_DOWN:
163 		/*
164 		 * If we're attached to a plex, we won't go down without use of
165 		 * force.
166 		 */
167 		if ((s->plex_sc != NULL) && !(flags & GV_SETSTATE_FORCE))
168 			return (-1);
169 		break;
170 
171 	case GV_SD_UP:
172 		/* We can't bring the subdisk up if our drive is dead. */
173 		d = s->drive_sc;
174 		if ((d == NULL) || (d->state != GV_DRIVE_UP))
175 			return (-1);
176 
177 		/* Check from where we want to be brought up. */
178 		switch (s->state) {
179 		case GV_SD_REVIVING:
180 		case GV_SD_INITIALIZING:
181 			/*
182 			 * The subdisk was initializing.  We allow it to be
183 			 * brought up.
184 			 */
185 			break;
186 
187 		case GV_SD_DOWN:
188 			/*
189 			 * The subdisk is currently down.  We allow it to be
190 			 * brought up if it is not attached to a plex.
191 			 */
192 			p = s->plex_sc;
193 			if (p == NULL)
194 				break;
195 
196 			/*
197 			 * If this subdisk is attached to a plex, we allow it
198 			 * to be brought up if the plex if it's not a RAID5
199 			 * plex, otherwise it's made 'stale'.
200 			 */
201 
202 			if (p->org != GV_PLEX_RAID5)
203 				break;
204 			else if (flags & GV_SETSTATE_FORCE)
205 				break;
206 			else
207 				s->state = GV_SD_STALE;
208 
209 			status = -1;
210 			break;
211 
212 		case GV_SD_STALE:
213 			/*
214 			 * A stale subdisk can be brought up only if it's part
215 			 * of a concat or striped plex that's the only one in a
216 			 * volume, or if the subdisk isn't attached to a plex.
217 			 * Otherwise it needs to be revived or initialized
218 			 * first.
219 			 */
220 			p = s->plex_sc;
221 			if (p == NULL || flags & GV_SETSTATE_FORCE)
222 				break;
223 
224 			if ((p->org != GV_PLEX_RAID5) &&
225 			    (p->vol_sc->plexcount == 1))
226 				break;
227 			else
228 				return (-1);
229 
230 		default:
231 			return (-1);
232 		}
233 		break;
234 
235 	/* Other state transitions are only possible with force. */
236 	default:
237 		if (!(flags & GV_SETSTATE_FORCE))
238 			return (-1);
239 	}
240 
241 	/* We can change the state and do it. */
242 	if (status == 0)
243 		s->state = newstate;
244 
245 	/* Update our plex, if we're attached to one. */
246 	if (s->plex_sc != NULL)
247 		gv_update_plex_state(s->plex_sc);
248 
249 	/* Save the config back to disk. */
250 	if (flags & GV_SETSTATE_CONFIG)
251 		gv_save_config_all(s->vinumconf);
252 
253 	return (status);
254 }
255 
256 
257 /* Update the state of a subdisk based on its environment. */
258 void
259 gv_update_sd_state(struct gv_sd *s)
260 {
261 	struct gv_drive *d;
262 	int oldstate;
263 
264 	KASSERT(s != NULL, ("gv_update_sd_state: NULL s"));
265 	d = s->drive_sc;
266 	KASSERT(d != NULL, ("gv_update_sd_state: NULL d"));
267 
268 	oldstate = s->state;
269 
270 	/* If our drive isn't up we cannot be up either. */
271 	if (d->state != GV_DRIVE_UP)
272 		s->state = GV_SD_DOWN;
273 	/* If this subdisk was just created, we assume it is good.*/
274 	else if (s->flags & GV_SD_NEWBORN) {
275 		s->state = GV_SD_UP;
276 		s->flags &= ~GV_SD_NEWBORN;
277 	} else if (s->state != GV_SD_UP)
278 		s->state = GV_SD_STALE;
279 	else
280 		s->state = GV_SD_UP;
281 
282 	if (s->state != oldstate)
283 		G_VINUM_DEBUG(1, "subdisk %s state change: %s -> %s", s->name,
284 		    gv_sdstate(oldstate), gv_sdstate(s->state));
285 
286 	/* Update the plex, if we have one. */
287 	if (s->plex_sc != NULL)
288 		gv_update_plex_state(s->plex_sc);
289 }
290 
291 /* Update the state of a plex based on its environment. */
292 void
293 gv_update_plex_state(struct gv_plex *p)
294 {
295 	int sdstates;
296 	int oldstate;
297 
298 	KASSERT(p != NULL, ("gv_update_plex_state: NULL p"));
299 
300 	oldstate = p->state;
301 
302 	/* First, check the state of our subdisks. */
303 	sdstates = gv_sdstatemap(p);
304 
305 	/* If all subdisks are up, our plex can be up, too. */
306 	if (sdstates == GV_SD_UPSTATE)
307 		p->state = GV_PLEX_UP;
308 
309 	/* One or more of our subdisks are down. */
310 	else if (sdstates & GV_SD_DOWNSTATE) {
311 		/* A RAID5 plex can handle one dead subdisk. */
312 		if ((p->org == GV_PLEX_RAID5) && (p->sddown == 1))
313 			p->state = GV_PLEX_DEGRADED;
314 		else
315 			p->state = GV_PLEX_DOWN;
316 
317 	/* Some of our subdisks are initializing. */
318 	} else if (sdstates & GV_SD_INITSTATE) {
319 		if (p->flags & GV_PLEX_SYNCING)
320 			p->state = GV_PLEX_DEGRADED;
321 		else
322 			p->state = GV_PLEX_DOWN;
323 	} else
324 		p->state = GV_PLEX_DOWN;
325 
326 	if (p->state != oldstate)
327 		G_VINUM_DEBUG(1, "plex %s state change: %s -> %s", p->name,
328 		    gv_plexstate(oldstate), gv_plexstate(p->state));
329 
330 	/* Update our volume, if we have one. */
331 	if (p->vol_sc != NULL)
332 		gv_update_vol_state(p->vol_sc);
333 }
334 
335 /* Update the volume state based on its plexes. */
336 void
337 gv_update_vol_state(struct gv_volume *v)
338 {
339 	struct gv_plex *p;
340 
341 	KASSERT(v != NULL, ("gv_update_vol_state: NULL v"));
342 
343 	/* The volume can't be up without plexes. */
344 	if (v->plexcount == 0) {
345 		v->state = GV_VOL_DOWN;
346 		return;
347 	}
348 
349 	LIST_FOREACH(p, &v->plexes, in_volume) {
350 		/* One of our plexes is accessible, and so are we. */
351 		if (p->state > GV_PLEX_DEGRADED) {
352 			v->state = GV_VOL_UP;
353 			return;
354 
355 		/* We can handle a RAID5 plex with one dead subdisk as well. */
356 		} else if ((p->org == GV_PLEX_RAID5) &&
357 		    (p->state == GV_PLEX_DEGRADED)) {
358 			v->state = GV_VOL_UP;
359 			return;
360 		}
361 	}
362 
363 	/* Not one of our plexes is up, so we can't be either. */
364 	v->state = GV_VOL_DOWN;
365 }
366 
367 /* Return a state map for the subdisks of a plex. */
368 int
369 gv_sdstatemap(struct gv_plex *p)
370 {
371 	struct gv_sd *s;
372 	int statemap;
373 
374 	KASSERT(p != NULL, ("gv_sdstatemap: NULL p"));
375 
376 	statemap = 0;
377 	p->sddown = 0;	/* No subdisks down yet. */
378 
379 	LIST_FOREACH(s, &p->subdisks, in_plex) {
380 		switch (s->state) {
381 		case GV_SD_DOWN:
382 		case GV_SD_STALE:
383 			statemap |= GV_SD_DOWNSTATE;
384 			p->sddown++;	/* Another unusable subdisk. */
385 			break;
386 
387 		case GV_SD_UP:
388 			statemap |= GV_SD_UPSTATE;
389 			break;
390 
391 		case GV_SD_INITIALIZING:
392 			statemap |= GV_SD_INITSTATE;
393 			break;
394 
395 		case GV_SD_REVIVING:
396 			statemap |= GV_SD_INITSTATE;
397 			p->sddown++;	/* XXX: Another unusable subdisk? */
398 			break;
399 		}
400 	}
401 	return (statemap);
402 }
403