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