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