]> CyberLeo.Net >> Repos - FreeBSD/releng/7.2.git/blob - sys/geom/vinum/geom_vinum_state.c
Create releng/7.2 from stable/7 in preparation for 7.2-RELEASE.
[FreeBSD/releng/7.2.git] / sys / geom / vinum / geom_vinum_state.c
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                 printf("GEOM_VINUM: subdisk %s state change: %s -> %s\n",
284                     s->name, 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                 printf("GEOM_VINUM: plex %s state change: %s -> %s\n", 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 }