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
gv_setstate(struct g_geom * gp,struct gctl_req * req)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
gv_set_drive_state(struct gv_drive * d,int newstate,int flags)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
gv_set_sd_state(struct gv_sd * s,int newstate,int flags)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
gv_set_plex_state(struct gv_plex * p,int newstate,int flags)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
gv_set_vol_state(struct gv_volume * v,int newstate,int flags)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
gv_update_sd_state(struct gv_sd * s)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
gv_update_plex_state(struct gv_plex * p)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
gv_update_vol_state(struct gv_volume * v)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
gv_sdstatemap(struct gv_plex * p)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