1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2004-2009 Pawel Jakub Dawidek <pjd@FreeBSD.org> 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 AUTHORS 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 AUTHORS 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/param.h> 33 #include <sys/systm.h> 34 #include <sys/bio.h> 35 #include <sys/kernel.h> 36 #include <sys/limits.h> 37 #include <sys/lock.h> 38 #include <sys/malloc.h> 39 #include <sys/sbuf.h> 40 #include <sys/sx.h> 41 42 #include <geom/geom.h> 43 #include <geom/geom_dbg.h> 44 #include <geom/geom_int.h> 45 #include <geom/mirror/g_mirror.h> 46 47 /* 48 * Configure, Rebuild, Remove, Deactivate, Forget, and Stop operations do not 49 * seem to depend on any particular g_mirror initialization state. 50 */ 51 static struct g_mirror_softc * 52 g_mirror_find_device(struct g_class *mp, const char *name) 53 { 54 struct g_mirror_softc *sc; 55 struct g_geom *gp; 56 57 g_topology_lock(); 58 LIST_FOREACH(gp, &mp->geom, geom) { 59 sc = gp->softc; 60 if (sc == NULL) 61 continue; 62 if ((sc->sc_flags & G_MIRROR_DEVICE_FLAG_DESTROY) != 0) 63 continue; 64 if (strcmp(gp->name, name) == 0 || 65 strcmp(sc->sc_name, name) == 0) { 66 g_topology_unlock(); 67 sx_xlock(&sc->sc_lock); 68 if ((sc->sc_flags & G_MIRROR_DEVICE_FLAG_DESTROY) != 0) { 69 sx_xunlock(&sc->sc_lock); 70 return (NULL); 71 } 72 return (sc); 73 } 74 } 75 g_topology_unlock(); 76 return (NULL); 77 } 78 79 /* Insert and Resize operations depend on a launched GEOM (sc_provider). */ 80 #define GMFL_VALID_FLAGS (M_WAITOK | M_NOWAIT) 81 static struct g_mirror_softc * 82 g_mirror_find_launched_device(struct g_class *mp, const char *name, int flags) 83 { 84 struct g_mirror_softc *sc; 85 int error; 86 87 KASSERT((flags & ~GMFL_VALID_FLAGS) == 0 && 88 flags != GMFL_VALID_FLAGS && flags != 0, 89 ("%s: Invalid flags %x\n", __func__, (unsigned)flags)); 90 #undef GMFL_VALID_FLAGS 91 92 while (true) { 93 sc = g_mirror_find_device(mp, name); 94 if (sc == NULL) 95 return (NULL); 96 if (sc->sc_provider != NULL) 97 return (sc); 98 if (flags & M_NOWAIT) { 99 sx_xunlock(&sc->sc_lock); 100 return (NULL); 101 } 102 103 /* 104 * This is a dumb hack. G_mirror does not expose any real 105 * wakeup API for observing state changes, and even if it did, 106 * its "RUNNING" state does not actually reflect all softc 107 * elements being initialized. 108 * 109 * Revamping g_mirror to have a 3rd, ACTUALLY_RUNNING state and 110 * updating all assertions and sc_state checks is a large work 111 * and would be easy to introduce regressions. 112 * 113 * Revamping g_mirror to have a wakeup for state changes would 114 * be difficult if one wanted to capture more than just 115 * sc_state and sc_provider. 116 * 117 * For now, just dummy sleep-poll until sc_provider shows up, 118 * the user cancels, or the g_mirror is destroyed. 119 */ 120 error = sx_sleep(&sc, &sc->sc_lock, PRIBIO | PCATCH | PDROP, 121 "GM:launched", 1); 122 if (error != 0 && error != EWOULDBLOCK) 123 return (NULL); 124 } 125 __unreachable(); 126 } 127 128 static struct g_mirror_disk * 129 g_mirror_find_disk(struct g_mirror_softc *sc, const char *name) 130 { 131 struct g_mirror_disk *disk; 132 133 sx_assert(&sc->sc_lock, SX_XLOCKED); 134 if (strncmp(name, _PATH_DEV, 5) == 0) 135 name += 5; 136 LIST_FOREACH(disk, &sc->sc_disks, d_next) { 137 if (disk->d_consumer == NULL) 138 continue; 139 if (disk->d_consumer->provider == NULL) 140 continue; 141 if (strcmp(disk->d_consumer->provider->name, name) == 0) 142 return (disk); 143 } 144 return (NULL); 145 } 146 147 static void 148 g_mirror_ctl_configure(struct gctl_req *req, struct g_class *mp) 149 { 150 struct g_mirror_softc *sc; 151 struct g_mirror_disk *disk; 152 const char *name, *balancep, *prov; 153 intmax_t *slicep, *priority; 154 uint32_t slice; 155 uint8_t balance; 156 int *autosync, *noautosync, *failsync, *nofailsync, *hardcode, *dynamic; 157 int *nargs, do_sync = 0, dirty = 1, do_priority = 0; 158 159 nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); 160 if (nargs == NULL) { 161 gctl_error(req, "No '%s' argument.", "nargs"); 162 return; 163 } 164 if (*nargs != 1 && *nargs != 2) { 165 gctl_error(req, "Invalid number of arguments."); 166 return; 167 } 168 name = gctl_get_asciiparam(req, "arg0"); 169 if (name == NULL) { 170 gctl_error(req, "No 'arg%u' argument.", 0); 171 return; 172 } 173 balancep = gctl_get_asciiparam(req, "balance"); 174 if (balancep == NULL) { 175 gctl_error(req, "No '%s' argument.", "balance"); 176 return; 177 } 178 autosync = gctl_get_paraml(req, "autosync", sizeof(*autosync)); 179 if (autosync == NULL) { 180 gctl_error(req, "No '%s' argument.", "autosync"); 181 return; 182 } 183 noautosync = gctl_get_paraml(req, "noautosync", sizeof(*noautosync)); 184 if (noautosync == NULL) { 185 gctl_error(req, "No '%s' argument.", "noautosync"); 186 return; 187 } 188 failsync = gctl_get_paraml(req, "failsync", sizeof(*failsync)); 189 if (failsync == NULL) { 190 gctl_error(req, "No '%s' argument.", "failsync"); 191 return; 192 } 193 nofailsync = gctl_get_paraml(req, "nofailsync", sizeof(*nofailsync)); 194 if (nofailsync == NULL) { 195 gctl_error(req, "No '%s' argument.", "nofailsync"); 196 return; 197 } 198 hardcode = gctl_get_paraml(req, "hardcode", sizeof(*hardcode)); 199 if (hardcode == NULL) { 200 gctl_error(req, "No '%s' argument.", "hardcode"); 201 return; 202 } 203 dynamic = gctl_get_paraml(req, "dynamic", sizeof(*dynamic)); 204 if (dynamic == NULL) { 205 gctl_error(req, "No '%s' argument.", "dynamic"); 206 return; 207 } 208 priority = gctl_get_paraml(req, "priority", sizeof(*priority)); 209 if (priority == NULL) { 210 gctl_error(req, "No '%s' argument.", "priority"); 211 return; 212 } 213 if (*priority < -1 || *priority > 255) { 214 gctl_error(req, "Priority range is 0 to 255, %jd given", 215 *priority); 216 return; 217 } 218 /* 219 * Since we have a priority, we also need a provider now. 220 * Note: be WARNS safe, by always assigning prov and only throw an 221 * error if *priority != -1. 222 */ 223 prov = gctl_get_asciiparam(req, "arg1"); 224 if (*priority > -1) { 225 if (prov == NULL) { 226 gctl_error(req, "Priority needs a disk name"); 227 return; 228 } 229 do_priority = 1; 230 } 231 if (*autosync && *noautosync) { 232 gctl_error(req, "'%s' and '%s' specified.", "autosync", 233 "noautosync"); 234 return; 235 } 236 if (*failsync && *nofailsync) { 237 gctl_error(req, "'%s' and '%s' specified.", "failsync", 238 "nofailsync"); 239 return; 240 } 241 if (*hardcode && *dynamic) { 242 gctl_error(req, "'%s' and '%s' specified.", "hardcode", 243 "dynamic"); 244 return; 245 } 246 sc = g_mirror_find_device(mp, name); 247 if (sc == NULL) { 248 gctl_error(req, "No such device: %s.", name); 249 return; 250 } 251 if (*balancep == '\0') 252 balance = sc->sc_balance; 253 else { 254 if (balance_id(balancep) == -1) { 255 gctl_error(req, "Invalid balance algorithm."); 256 sx_xunlock(&sc->sc_lock); 257 return; 258 } 259 balance = balance_id(balancep); 260 } 261 slicep = gctl_get_paraml(req, "slice", sizeof(*slicep)); 262 if (slicep == NULL) { 263 gctl_error(req, "No '%s' argument.", "slice"); 264 sx_xunlock(&sc->sc_lock); 265 return; 266 } 267 if (*slicep == -1) 268 slice = sc->sc_slice; 269 else 270 slice = *slicep; 271 /* Enforce usage() of -p not allowing any other options. */ 272 if (do_priority && (*autosync || *noautosync || *failsync || 273 *nofailsync || *hardcode || *dynamic || *slicep != -1 || 274 *balancep != '\0')) { 275 sx_xunlock(&sc->sc_lock); 276 gctl_error(req, "only -p accepted when setting priority"); 277 return; 278 } 279 if (sc->sc_balance == balance && sc->sc_slice == slice && !*autosync && 280 !*noautosync && !*failsync && !*nofailsync && !*hardcode && 281 !*dynamic && !do_priority) { 282 sx_xunlock(&sc->sc_lock); 283 gctl_error(req, "Nothing has changed."); 284 return; 285 } 286 if ((!do_priority && *nargs != 1) || (do_priority && *nargs != 2)) { 287 sx_xunlock(&sc->sc_lock); 288 gctl_error(req, "Invalid number of arguments."); 289 return; 290 } 291 if (g_mirror_ndisks(sc, -1) < sc->sc_ndisks) { 292 sx_xunlock(&sc->sc_lock); 293 gctl_error(req, "Not all disks connected. Try 'forget' command " 294 "first."); 295 return; 296 } 297 sc->sc_balance = balance; 298 sc->sc_slice = slice; 299 if ((sc->sc_flags & G_MIRROR_DEVICE_FLAG_NOAUTOSYNC) != 0) { 300 if (*autosync) { 301 sc->sc_flags &= ~G_MIRROR_DEVICE_FLAG_NOAUTOSYNC; 302 do_sync = 1; 303 } 304 } else { 305 if (*noautosync) 306 sc->sc_flags |= G_MIRROR_DEVICE_FLAG_NOAUTOSYNC; 307 } 308 if ((sc->sc_flags & G_MIRROR_DEVICE_FLAG_NOFAILSYNC) != 0) { 309 if (*failsync) 310 sc->sc_flags &= ~G_MIRROR_DEVICE_FLAG_NOFAILSYNC; 311 } else { 312 if (*nofailsync) { 313 sc->sc_flags |= G_MIRROR_DEVICE_FLAG_NOFAILSYNC; 314 dirty = 0; 315 } 316 } 317 LIST_FOREACH(disk, &sc->sc_disks, d_next) { 318 /* 319 * Handle priority first, since we only need one disk, do one 320 * operation on it and then we're done. No need to check other 321 * flags, as usage doesn't allow it. 322 */ 323 if (do_priority) { 324 if (strcmp(disk->d_name, prov) == 0) { 325 if (disk->d_priority == *priority) 326 gctl_error(req, "Nothing has changed."); 327 else { 328 disk->d_priority = *priority; 329 g_mirror_update_metadata(disk); 330 } 331 break; 332 } 333 continue; 334 } 335 if (do_sync) { 336 if (disk->d_state == G_MIRROR_DISK_STATE_SYNCHRONIZING) 337 disk->d_flags &= ~G_MIRROR_DISK_FLAG_FORCE_SYNC; 338 } 339 if (*hardcode) 340 disk->d_flags |= G_MIRROR_DISK_FLAG_HARDCODED; 341 else if (*dynamic) 342 disk->d_flags &= ~G_MIRROR_DISK_FLAG_HARDCODED; 343 if (!dirty) 344 disk->d_flags &= ~G_MIRROR_DISK_FLAG_DIRTY; 345 g_mirror_update_metadata(disk); 346 if (do_sync) { 347 if (disk->d_state == G_MIRROR_DISK_STATE_STALE) { 348 g_mirror_event_send(disk, 349 G_MIRROR_DISK_STATE_DISCONNECTED, 350 G_MIRROR_EVENT_DONTWAIT); 351 } 352 } 353 } 354 sx_xunlock(&sc->sc_lock); 355 } 356 357 static void 358 g_mirror_create_orphan(struct g_consumer *cp) 359 { 360 361 KASSERT(1 == 0, ("%s called while creating %s.", __func__, 362 cp->provider->name)); 363 } 364 365 static void 366 g_mirror_ctl_create(struct gctl_req *req, struct g_class *mp) 367 { 368 struct g_mirror_metadata md; 369 struct g_geom *gp; 370 struct g_consumer *cp; 371 struct g_provider *pp; 372 struct g_mirror_softc *sc; 373 struct sbuf *sb; 374 const char *name; 375 char param[16]; 376 int *nargs; 377 intmax_t *val; 378 int *ival; 379 const char *sval; 380 int bal; 381 unsigned attached, no, sectorsize; 382 off_t mediasize; 383 384 nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); 385 if (nargs == NULL) { 386 gctl_error(req, "No '%s' argument.", "nargs"); 387 return; 388 } 389 if (*nargs <= 2) { 390 gctl_error(req, "Too few arguments."); 391 return; 392 } 393 394 strlcpy(md.md_magic, G_MIRROR_MAGIC, sizeof(md.md_magic)); 395 md.md_version = G_MIRROR_VERSION; 396 name = gctl_get_asciiparam(req, "arg0"); 397 if (name == NULL) { 398 gctl_error(req, "No 'arg%u' argument.", 0); 399 return; 400 } 401 strlcpy(md.md_name, name, sizeof(md.md_name)); 402 md.md_mid = arc4random(); 403 md.md_all = *nargs - 1; 404 md.md_genid = 0; 405 md.md_syncid = 1; 406 md.md_sync_offset = 0; 407 val = gctl_get_paraml(req, "slice", sizeof(*val)); 408 if (val == NULL) { 409 gctl_error(req, "No slice argument."); 410 return; 411 } 412 md.md_slice = *val; 413 sval = gctl_get_asciiparam(req, "balance"); 414 if (sval == NULL) { 415 gctl_error(req, "No balance argument."); 416 return; 417 } 418 bal = balance_id(sval); 419 if (bal < 0) { 420 gctl_error(req, "Invalid balance algorithm."); 421 return; 422 } 423 md.md_balance = bal; 424 md.md_mflags = 0; 425 md.md_dflags = 0; 426 ival = gctl_get_paraml(req, "noautosync", sizeof(*ival)); 427 if (ival != NULL && *ival) 428 md.md_mflags |= G_MIRROR_DEVICE_FLAG_NOAUTOSYNC; 429 ival = gctl_get_paraml(req, "nofailsync", sizeof(*ival)); 430 if (ival != NULL && *ival) 431 md.md_mflags |= G_MIRROR_DEVICE_FLAG_NOFAILSYNC; 432 /* These fields not used in manual mode. */ 433 bzero(md.md_provider, sizeof(md.md_provider)); 434 md.md_provsize = 0; 435 436 g_topology_lock(); 437 mediasize = OFF_MAX; 438 sectorsize = 0; 439 gp = g_new_geomf(mp, "%s", md.md_name); 440 gp->orphan = g_mirror_create_orphan; 441 cp = g_new_consumer(gp); 442 for (no = 1; no < *nargs; no++) { 443 snprintf(param, sizeof(param), "arg%u", no); 444 pp = gctl_get_provider(req, param); 445 if (pp == NULL) { 446 err: 447 g_destroy_consumer(cp); 448 g_destroy_geom(gp); 449 g_topology_unlock(); 450 return; 451 } 452 if (g_attach(cp, pp) != 0) { 453 G_MIRROR_DEBUG(1, "Can't attach disk %s.", pp->name); 454 gctl_error(req, "Can't attach disk %s.", pp->name); 455 goto err; 456 } 457 if (g_access(cp, 1, 0, 0) != 0) { 458 G_MIRROR_DEBUG(1, "Can't open disk %s.", pp->name); 459 gctl_error(req, "Can't open disk %s.", pp->name); 460 err2: 461 g_detach(cp); 462 goto err; 463 } 464 if (pp->mediasize == 0 || pp->sectorsize == 0) { 465 G_MIRROR_DEBUG(1, "Disk %s has no media.", pp->name); 466 gctl_error(req, "Disk %s has no media.", pp->name); 467 g_access(cp, -1, 0, 0); 468 goto err2; 469 } 470 if (pp->mediasize < mediasize) 471 mediasize = pp->mediasize; 472 if (pp->sectorsize > sectorsize) 473 sectorsize = pp->sectorsize; 474 g_access(cp, -1, 0, 0); 475 g_detach(cp); 476 } 477 g_destroy_consumer(cp); 478 g_destroy_geom(gp); 479 md.md_mediasize = mediasize; 480 md.md_sectorsize = sectorsize; 481 md.md_mediasize -= (md.md_mediasize % md.md_sectorsize); 482 483 gp = g_mirror_create(mp, &md, G_MIRROR_TYPE_MANUAL); 484 if (gp == NULL) { 485 gctl_error(req, "Can't create %s.", md.md_name); 486 g_topology_unlock(); 487 return; 488 } 489 490 sc = gp->softc; 491 g_topology_unlock(); 492 sx_xlock(&sc->sc_lock); 493 sc->sc_flags |= G_MIRROR_DEVICE_FLAG_TASTING; 494 sb = sbuf_new_auto(); 495 sbuf_printf(sb, "Can't attach disk(s) to %s:", gp->name); 496 for (attached = 0, no = 1; no < *nargs; no++) { 497 snprintf(param, sizeof(param), "arg%u", no); 498 pp = gctl_get_provider(req, param); 499 if (pp == NULL) { 500 name = gctl_get_asciiparam(req, param); 501 MPASS(name != NULL); 502 sbuf_printf(sb, " %s", name); 503 continue; 504 } 505 md.md_did = arc4random(); 506 md.md_priority = no - 1; 507 if (g_mirror_add_disk(sc, pp, &md) != 0) { 508 G_MIRROR_DEBUG(1, "Disk %u (%s) not attached to %s.", 509 no, pp->name, gp->name); 510 sbuf_printf(sb, " %s", pp->name); 511 continue; 512 } 513 attached++; 514 } 515 sbuf_finish(sb); 516 sc->sc_flags &= ~G_MIRROR_DEVICE_FLAG_TASTING; 517 if (md.md_all != attached || 518 (sc->sc_flags & G_MIRROR_DEVICE_FLAG_DESTROY) != 0) { 519 g_mirror_destroy(gp->softc, G_MIRROR_DESTROY_HARD); 520 gctl_error(req, "%s", sbuf_data(sb)); 521 } else 522 sx_xunlock(&sc->sc_lock); 523 sbuf_delete(sb); 524 } 525 526 static void 527 g_mirror_ctl_rebuild(struct gctl_req *req, struct g_class *mp) 528 { 529 struct g_mirror_metadata md; 530 struct g_mirror_softc *sc; 531 struct g_mirror_disk *disk; 532 struct g_provider *pp; 533 const char *name; 534 char param[16]; 535 int error, *nargs; 536 u_int i; 537 538 nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); 539 if (nargs == NULL) { 540 gctl_error(req, "No '%s' argument.", "nargs"); 541 return; 542 } 543 if (*nargs < 2) { 544 gctl_error(req, "Too few arguments."); 545 return; 546 } 547 name = gctl_get_asciiparam(req, "arg0"); 548 if (name == NULL) { 549 gctl_error(req, "No 'arg%u' argument.", 0); 550 return; 551 } 552 sc = g_mirror_find_device(mp, name); 553 if (sc == NULL) { 554 gctl_error(req, "No such device: %s.", name); 555 return; 556 } 557 for (i = 1; i < (u_int)*nargs; i++) { 558 snprintf(param, sizeof(param), "arg%u", i); 559 name = gctl_get_asciiparam(req, param); 560 if (name == NULL) { 561 gctl_error(req, "No 'arg%u' argument.", i); 562 continue; 563 } 564 disk = g_mirror_find_disk(sc, name); 565 if (disk == NULL) { 566 gctl_error(req, "No such provider: %s.", name); 567 continue; 568 } 569 if (g_mirror_ndisks(sc, G_MIRROR_DISK_STATE_ACTIVE) == 1 && 570 disk->d_state == G_MIRROR_DISK_STATE_ACTIVE) { 571 /* 572 * This is the last active disk. There will be nothing 573 * to rebuild it from, so deny this request. 574 */ 575 gctl_error(req, 576 "Provider %s is the last active provider in %s.", 577 name, sc->sc_geom->name); 578 break; 579 } 580 /* 581 * Do rebuild by resetting syncid, disconnecting the disk and 582 * connecting it again. 583 */ 584 disk->d_sync.ds_syncid = 0; 585 if ((sc->sc_flags & G_MIRROR_DEVICE_FLAG_NOAUTOSYNC) != 0) 586 disk->d_flags |= G_MIRROR_DISK_FLAG_FORCE_SYNC; 587 g_mirror_update_metadata(disk); 588 pp = disk->d_consumer->provider; 589 g_topology_lock(); 590 error = g_mirror_read_metadata(disk->d_consumer, &md); 591 g_topology_unlock(); 592 g_mirror_event_send(disk, G_MIRROR_DISK_STATE_DISCONNECTED, 593 G_MIRROR_EVENT_WAIT); 594 if (error != 0) { 595 gctl_error(req, "Cannot read metadata from %s.", 596 pp->name); 597 continue; 598 } 599 error = g_mirror_add_disk(sc, pp, &md); 600 if (error != 0) { 601 gctl_error(req, "Cannot reconnect component %s.", 602 pp->name); 603 continue; 604 } 605 } 606 sx_xunlock(&sc->sc_lock); 607 } 608 609 static void 610 g_mirror_ctl_insert(struct gctl_req *req, struct g_class *mp) 611 { 612 struct g_mirror_softc *sc; 613 struct g_mirror_disk *disk; 614 struct g_mirror_metadata md; 615 struct g_provider *pp; 616 struct g_consumer *cp; 617 intmax_t *priority; 618 const char *name; 619 char param[16]; 620 u_char *sector; 621 u_int i, n; 622 int error, *nargs, *hardcode, *inactive; 623 struct { 624 struct g_provider *provider; 625 struct g_consumer *consumer; 626 } *disks; 627 off_t mdsize; 628 629 nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); 630 if (nargs == NULL) { 631 gctl_error(req, "No '%s' argument.", "nargs"); 632 return; 633 } 634 if (*nargs < 2) { 635 gctl_error(req, "Too few arguments."); 636 return; 637 } 638 priority = gctl_get_paraml(req, "priority", sizeof(*priority)); 639 if (priority == NULL) { 640 gctl_error(req, "No '%s' argument.", "priority"); 641 return; 642 } 643 inactive = gctl_get_paraml(req, "inactive", sizeof(*inactive)); 644 if (inactive == NULL) { 645 gctl_error(req, "No '%s' argument.", "inactive"); 646 return; 647 } 648 hardcode = gctl_get_paraml(req, "hardcode", sizeof(*hardcode)); 649 if (hardcode == NULL) { 650 gctl_error(req, "No '%s' argument.", "hardcode"); 651 return; 652 } 653 name = gctl_get_asciiparam(req, "arg0"); 654 if (name == NULL) { 655 gctl_error(req, "No 'arg%u' argument.", 0); 656 return; 657 } 658 sc = g_mirror_find_launched_device(mp, name, M_WAITOK); 659 if (sc == NULL) { 660 gctl_error(req, "No such device: %s.", name); 661 return; 662 } 663 if (g_mirror_ndisks(sc, -1) < sc->sc_ndisks) { 664 gctl_error(req, "Not all disks connected."); 665 sx_xunlock(&sc->sc_lock); 666 return; 667 } 668 669 disks = g_malloc(sizeof(*disks) * (*nargs), M_WAITOK | M_ZERO); 670 g_topology_lock(); 671 for (i = 1, n = 0; i < (u_int)*nargs; i++) { 672 snprintf(param, sizeof(param), "arg%u", i); 673 pp = gctl_get_provider(req, param); 674 if (pp == NULL) 675 continue; 676 if (g_mirror_find_disk(sc, pp->name) != NULL) { 677 gctl_error(req, "Provider %s already inserted.", pp->name); 678 continue; 679 } 680 cp = g_new_consumer(sc->sc_geom); 681 if (g_attach(cp, pp) != 0) { 682 g_destroy_consumer(cp); 683 gctl_error(req, "Cannot attach to provider %s.", pp->name); 684 continue; 685 } 686 if (g_access(cp, 0, 1, 1) != 0) { 687 gctl_error(req, "Cannot access provider %s.", pp->name); 688 err: 689 g_detach(cp); 690 g_destroy_consumer(cp); 691 continue; 692 } 693 mdsize = (sc->sc_type == G_MIRROR_TYPE_AUTOMATIC) ? 694 pp->sectorsize : 0; 695 if (sc->sc_provider->mediasize > pp->mediasize - mdsize) { 696 gctl_error(req, "Provider %s too small.", pp->name); 697 err2: 698 g_access(cp, 0, -1, -1); 699 goto err; 700 } 701 if ((sc->sc_provider->sectorsize % pp->sectorsize) != 0) { 702 gctl_error(req, "Invalid sectorsize of provider %s.", 703 pp->name); 704 goto err2; 705 } 706 if (sc->sc_type != G_MIRROR_TYPE_AUTOMATIC) { 707 g_access(cp, 0, -1, -1); 708 g_detach(cp); 709 g_destroy_consumer(cp); 710 g_topology_unlock(); 711 sc->sc_ndisks++; 712 g_mirror_fill_metadata(sc, NULL, &md); 713 md.md_priority = *priority; 714 if (*inactive) 715 md.md_dflags |= G_MIRROR_DISK_FLAG_INACTIVE; 716 if (g_mirror_add_disk(sc, pp, &md) != 0) { 717 sc->sc_ndisks--; 718 gctl_error(req, "Disk %s not inserted.", pp->name); 719 } 720 g_topology_lock(); 721 continue; 722 } 723 disks[n].provider = pp; 724 disks[n].consumer = cp; 725 n++; 726 } 727 if (n == 0) { 728 g_topology_unlock(); 729 sx_xunlock(&sc->sc_lock); 730 g_free(disks); 731 return; 732 } 733 sc->sc_ndisks += n; 734 again: 735 for (i = 0; i < n; i++) { 736 if (disks[i].consumer == NULL) 737 continue; 738 g_mirror_fill_metadata(sc, NULL, &md); 739 md.md_priority = *priority; 740 if (*inactive) 741 md.md_dflags |= G_MIRROR_DISK_FLAG_INACTIVE; 742 pp = disks[i].provider; 743 if (*hardcode) { 744 strlcpy(md.md_provider, pp->name, 745 sizeof(md.md_provider)); 746 } else { 747 bzero(md.md_provider, sizeof(md.md_provider)); 748 } 749 md.md_provsize = pp->mediasize; 750 sector = g_malloc(pp->sectorsize, M_WAITOK | M_ZERO); 751 mirror_metadata_encode(&md, sector); 752 error = g_write_data(disks[i].consumer, 753 pp->mediasize - pp->sectorsize, sector, pp->sectorsize); 754 g_free(sector); 755 if (error != 0) { 756 gctl_error(req, "Cannot store metadata on %s.", 757 pp->name); 758 g_access(disks[i].consumer, 0, -1, -1); 759 g_detach(disks[i].consumer); 760 g_destroy_consumer(disks[i].consumer); 761 disks[i].consumer = NULL; 762 disks[i].provider = NULL; 763 sc->sc_ndisks--; 764 goto again; 765 } 766 } 767 g_topology_unlock(); 768 if (i == 0) { 769 /* All writes failed. */ 770 sx_xunlock(&sc->sc_lock); 771 g_free(disks); 772 return; 773 } 774 LIST_FOREACH(disk, &sc->sc_disks, d_next) { 775 g_mirror_update_metadata(disk); 776 } 777 /* 778 * Release provider and wait for retaste. 779 */ 780 g_topology_lock(); 781 for (i = 0; i < n; i++) { 782 if (disks[i].consumer == NULL) 783 continue; 784 g_access(disks[i].consumer, 0, -1, -1); 785 g_detach(disks[i].consumer); 786 g_destroy_consumer(disks[i].consumer); 787 } 788 g_topology_unlock(); 789 sx_xunlock(&sc->sc_lock); 790 g_free(disks); 791 } 792 793 static void 794 g_mirror_ctl_remove(struct gctl_req *req, struct g_class *mp) 795 { 796 struct g_mirror_softc *sc; 797 struct g_mirror_disk *disk; 798 const char *name; 799 char param[16]; 800 int *nargs; 801 u_int i, active; 802 803 nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); 804 if (nargs == NULL) { 805 gctl_error(req, "No '%s' argument.", "nargs"); 806 return; 807 } 808 if (*nargs < 2) { 809 gctl_error(req, "Too few arguments."); 810 return; 811 } 812 name = gctl_get_asciiparam(req, "arg0"); 813 if (name == NULL) { 814 gctl_error(req, "No 'arg%u' argument.", 0); 815 return; 816 } 817 sc = g_mirror_find_device(mp, name); 818 if (sc == NULL) { 819 gctl_error(req, "No such device: %s.", name); 820 return; 821 } 822 if (g_mirror_ndisks(sc, -1) < sc->sc_ndisks) { 823 sx_xunlock(&sc->sc_lock); 824 gctl_error(req, "Not all disks connected. Try 'forget' command " 825 "first."); 826 return; 827 } 828 active = g_mirror_ndisks(sc, G_MIRROR_DISK_STATE_ACTIVE); 829 for (i = 1; i < (u_int)*nargs; i++) { 830 snprintf(param, sizeof(param), "arg%u", i); 831 name = gctl_get_asciiparam(req, param); 832 if (name == NULL) { 833 gctl_error(req, "No 'arg%u' argument.", i); 834 continue; 835 } 836 disk = g_mirror_find_disk(sc, name); 837 if (disk == NULL) { 838 gctl_error(req, "No such provider: %s.", name); 839 continue; 840 } 841 if (disk->d_state == G_MIRROR_DISK_STATE_ACTIVE) { 842 if (active > 1) 843 active--; 844 else { 845 gctl_error(req, "%s: Can't remove the last " 846 "ACTIVE component %s.", sc->sc_geom->name, 847 name); 848 continue; 849 } 850 } 851 g_mirror_event_send(disk, G_MIRROR_DISK_STATE_DESTROY, 852 G_MIRROR_EVENT_DONTWAIT); 853 } 854 sx_xunlock(&sc->sc_lock); 855 } 856 857 static void 858 g_mirror_ctl_resize(struct gctl_req *req, struct g_class *mp) 859 { 860 struct g_mirror_softc *sc; 861 struct g_mirror_disk *disk; 862 uint64_t mediasize; 863 const char *name, *s; 864 char *x; 865 int *nargs; 866 867 nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); 868 if (nargs == NULL) { 869 gctl_error(req, "No '%s' argument.", "nargs"); 870 return; 871 } 872 if (*nargs != 1) { 873 gctl_error(req, "Missing device."); 874 return; 875 } 876 name = gctl_get_asciiparam(req, "arg0"); 877 if (name == NULL) { 878 gctl_error(req, "No 'arg%u' argument.", 0); 879 return; 880 } 881 s = gctl_get_asciiparam(req, "size"); 882 if (s == NULL) { 883 gctl_error(req, "No '%s' argument.", "size"); 884 return; 885 } 886 mediasize = strtouq(s, &x, 0); 887 if (*x != '\0' || mediasize == 0) { 888 gctl_error(req, "Invalid '%s' argument.", "size"); 889 return; 890 } 891 sc = g_mirror_find_launched_device(mp, name, M_WAITOK); 892 if (sc == NULL) { 893 gctl_error(req, "No such device: %s.", name); 894 return; 895 } 896 /* Deny shrinking of an opened provider */ 897 if ((g_debugflags & G_F_FOOTSHOOTING) == 0 && sc->sc_provider_open > 0) { 898 if (sc->sc_mediasize > mediasize) { 899 gctl_error(req, "Device %s is busy.", 900 sc->sc_provider->name); 901 sx_xunlock(&sc->sc_lock); 902 return; 903 } 904 } 905 LIST_FOREACH(disk, &sc->sc_disks, d_next) { 906 if (mediasize > disk->d_consumer->provider->mediasize - 907 disk->d_consumer->provider->sectorsize) { 908 gctl_error(req, "Provider %s is too small.", 909 disk->d_name); 910 sx_xunlock(&sc->sc_lock); 911 return; 912 } 913 } 914 /* Update the size. */ 915 sc->sc_mediasize = mediasize; 916 LIST_FOREACH(disk, &sc->sc_disks, d_next) { 917 g_mirror_update_metadata(disk); 918 } 919 g_topology_lock(); 920 g_resize_provider(sc->sc_provider, mediasize); 921 g_topology_unlock(); 922 sx_xunlock(&sc->sc_lock); 923 } 924 925 static void 926 g_mirror_ctl_deactivate(struct gctl_req *req, struct g_class *mp) 927 { 928 struct g_mirror_softc *sc; 929 struct g_mirror_disk *disk; 930 const char *name; 931 char param[16]; 932 int *nargs; 933 u_int i, active; 934 935 nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); 936 if (nargs == NULL) { 937 gctl_error(req, "No '%s' argument.", "nargs"); 938 return; 939 } 940 if (*nargs < 2) { 941 gctl_error(req, "Too few arguments."); 942 return; 943 } 944 name = gctl_get_asciiparam(req, "arg0"); 945 if (name == NULL) { 946 gctl_error(req, "No 'arg%u' argument.", 0); 947 return; 948 } 949 sc = g_mirror_find_device(mp, name); 950 if (sc == NULL) { 951 gctl_error(req, "No such device: %s.", name); 952 return; 953 } 954 active = g_mirror_ndisks(sc, G_MIRROR_DISK_STATE_ACTIVE); 955 for (i = 1; i < (u_int)*nargs; i++) { 956 snprintf(param, sizeof(param), "arg%u", i); 957 name = gctl_get_asciiparam(req, param); 958 if (name == NULL) { 959 gctl_error(req, "No 'arg%u' argument.", i); 960 continue; 961 } 962 disk = g_mirror_find_disk(sc, name); 963 if (disk == NULL) { 964 gctl_error(req, "No such provider: %s.", name); 965 continue; 966 } 967 if (disk->d_state == G_MIRROR_DISK_STATE_ACTIVE) { 968 if (active > 1) 969 active--; 970 else { 971 gctl_error(req, "%s: Can't deactivate the " 972 "last ACTIVE component %s.", 973 sc->sc_geom->name, name); 974 continue; 975 } 976 } 977 disk->d_flags |= G_MIRROR_DISK_FLAG_INACTIVE; 978 disk->d_flags &= ~G_MIRROR_DISK_FLAG_FORCE_SYNC; 979 g_mirror_update_metadata(disk); 980 sc->sc_bump_id |= G_MIRROR_BUMP_SYNCID; 981 g_mirror_event_send(disk, G_MIRROR_DISK_STATE_DISCONNECTED, 982 G_MIRROR_EVENT_DONTWAIT); 983 } 984 sx_xunlock(&sc->sc_lock); 985 } 986 987 static void 988 g_mirror_ctl_forget(struct gctl_req *req, struct g_class *mp) 989 { 990 struct g_mirror_softc *sc; 991 struct g_mirror_disk *disk; 992 const char *name; 993 char param[16]; 994 int *nargs; 995 u_int i; 996 997 nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); 998 if (nargs == NULL) { 999 gctl_error(req, "No '%s' argument.", "nargs"); 1000 return; 1001 } 1002 if (*nargs < 1) { 1003 gctl_error(req, "Missing device(s)."); 1004 return; 1005 } 1006 1007 for (i = 0; i < (u_int)*nargs; i++) { 1008 snprintf(param, sizeof(param), "arg%u", i); 1009 name = gctl_get_asciiparam(req, param); 1010 if (name == NULL) { 1011 gctl_error(req, "No 'arg%u' argument.", i); 1012 return; 1013 } 1014 sc = g_mirror_find_device(mp, name); 1015 if (sc == NULL) { 1016 gctl_error(req, "No such device: %s.", name); 1017 return; 1018 } 1019 if (g_mirror_ndisks(sc, -1) == sc->sc_ndisks) { 1020 sx_xunlock(&sc->sc_lock); 1021 G_MIRROR_DEBUG(1, 1022 "All disks connected in %s, skipping.", 1023 sc->sc_name); 1024 continue; 1025 } 1026 sc->sc_ndisks = g_mirror_ndisks(sc, -1); 1027 LIST_FOREACH(disk, &sc->sc_disks, d_next) { 1028 g_mirror_update_metadata(disk); 1029 } 1030 sx_xunlock(&sc->sc_lock); 1031 } 1032 } 1033 1034 static void 1035 g_mirror_ctl_stop(struct gctl_req *req, struct g_class *mp, int wipe) 1036 { 1037 struct g_mirror_softc *sc; 1038 int *force, *nargs, error; 1039 const char *name; 1040 char param[16]; 1041 u_int i; 1042 int how; 1043 1044 nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); 1045 if (nargs == NULL) { 1046 gctl_error(req, "No '%s' argument.", "nargs"); 1047 return; 1048 } 1049 if (*nargs < 1) { 1050 gctl_error(req, "Missing device(s)."); 1051 return; 1052 } 1053 force = gctl_get_paraml(req, "force", sizeof(*force)); 1054 if (force == NULL) { 1055 gctl_error(req, "No '%s' argument.", "force"); 1056 return; 1057 } 1058 if (*force) 1059 how = G_MIRROR_DESTROY_HARD; 1060 else 1061 how = G_MIRROR_DESTROY_SOFT; 1062 1063 for (i = 0; i < (u_int)*nargs; i++) { 1064 snprintf(param, sizeof(param), "arg%u", i); 1065 name = gctl_get_asciiparam(req, param); 1066 if (name == NULL) { 1067 gctl_error(req, "No 'arg%u' argument.", i); 1068 return; 1069 } 1070 sc = g_mirror_find_device(mp, name); 1071 if (sc == NULL) { 1072 gctl_error(req, "No such device: %s.", name); 1073 return; 1074 } 1075 g_cancel_event(sc); 1076 if (wipe) 1077 sc->sc_flags |= G_MIRROR_DEVICE_FLAG_WIPE; 1078 error = g_mirror_destroy(sc, how); 1079 if (error != 0) { 1080 gctl_error(req, "Cannot destroy device %s (error=%d).", 1081 sc->sc_geom->name, error); 1082 if (wipe) 1083 sc->sc_flags &= ~G_MIRROR_DEVICE_FLAG_WIPE; 1084 sx_xunlock(&sc->sc_lock); 1085 return; 1086 } 1087 /* No need to unlock, because lock is already dead. */ 1088 } 1089 } 1090 1091 void 1092 g_mirror_config(struct gctl_req *req, struct g_class *mp, const char *verb) 1093 { 1094 uint32_t *version; 1095 1096 g_topology_assert(); 1097 1098 version = gctl_get_paraml(req, "version", sizeof(*version)); 1099 if (version == NULL) { 1100 gctl_error(req, "No '%s' argument.", "version"); 1101 return; 1102 } 1103 if (*version != G_MIRROR_VERSION) { 1104 gctl_error(req, "Userland and kernel parts are out of sync."); 1105 return; 1106 } 1107 1108 g_topology_unlock(); 1109 if (strcmp(verb, "configure") == 0) 1110 g_mirror_ctl_configure(req, mp); 1111 else if (strcmp(verb, "create") == 0) 1112 g_mirror_ctl_create(req, mp); 1113 else if (strcmp(verb, "rebuild") == 0) 1114 g_mirror_ctl_rebuild(req, mp); 1115 else if (strcmp(verb, "insert") == 0) 1116 g_mirror_ctl_insert(req, mp); 1117 else if (strcmp(verb, "remove") == 0) 1118 g_mirror_ctl_remove(req, mp); 1119 else if (strcmp(verb, "resize") == 0) 1120 g_mirror_ctl_resize(req, mp); 1121 else if (strcmp(verb, "deactivate") == 0) 1122 g_mirror_ctl_deactivate(req, mp); 1123 else if (strcmp(verb, "forget") == 0) 1124 g_mirror_ctl_forget(req, mp); 1125 else if (strcmp(verb, "stop") == 0) 1126 g_mirror_ctl_stop(req, mp, 0); 1127 else if (strcmp(verb, "destroy") == 0) 1128 g_mirror_ctl_stop(req, mp, 1); 1129 else 1130 gctl_error(req, "Unknown verb."); 1131 g_topology_lock(); 1132 } 1133