]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/geom/vinum/geom_vinum_state.c
MFV r349454:
[FreeBSD/FreeBSD.git] / sys / geom / vinum / geom_vinum_state.c
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
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
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
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
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
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
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
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
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
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 }