]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - sys/geom/vinum/geom_vinum_state.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / sys / geom / vinum / geom_vinum_state.c
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
38 void
39 gv_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. */
121 int
122 gv_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
153 int
154 gv_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
281 int
282 gv_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
336 int
337 gv_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. */
374 void
375 gv_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. */
412 void
413 gv_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. */
468 void
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
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 }