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