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 } 403