1132904Spjd/*- 2196879Spjd * Copyright (c) 2004-2009 Pawel Jakub Dawidek <pjd@FreeBSD.org> 3132904Spjd * All rights reserved. 4132904Spjd * 5132904Spjd * Redistribution and use in source and binary forms, with or without 6132904Spjd * modification, are permitted provided that the following conditions 7132904Spjd * are met: 8132904Spjd * 1. Redistributions of source code must retain the above copyright 9132904Spjd * notice, this list of conditions and the following disclaimer. 10132904Spjd * 2. Redistributions in binary form must reproduce the above copyright 11132904Spjd * notice, this list of conditions and the following disclaimer in the 12132904Spjd * documentation and/or other materials provided with the distribution. 13155174Spjd * 14132904Spjd * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 15132904Spjd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16132904Spjd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17132904Spjd * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 18132904Spjd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19132904Spjd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20132904Spjd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21132904Spjd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22132904Spjd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23132904Spjd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24132904Spjd * SUCH DAMAGE. 25132904Spjd */ 26132904Spjd 27132904Spjd#include <sys/cdefs.h> 28132904Spjd__FBSDID("$FreeBSD: releng/10.3/sys/geom/mirror/g_mirror_ctl.c 260503 2014-01-10 07:48:36Z ae $"); 29132904Spjd 30132904Spjd#include <sys/param.h> 31132904Spjd#include <sys/systm.h> 32132904Spjd#include <sys/kernel.h> 33132904Spjd#include <sys/module.h> 34260502Sae#include <sys/limits.h> 35132904Spjd#include <sys/lock.h> 36132904Spjd#include <sys/mutex.h> 37132904Spjd#include <sys/bio.h> 38132904Spjd#include <sys/sysctl.h> 39132904Spjd#include <sys/malloc.h> 40132904Spjd#include <sys/bitstring.h> 41132904Spjd#include <vm/uma.h> 42132904Spjd#include <machine/atomic.h> 43132904Spjd#include <geom/geom.h> 44260502Sae#include <geom/geom_int.h> 45132904Spjd#include <sys/proc.h> 46132904Spjd#include <sys/kthread.h> 47132904Spjd#include <geom/mirror/g_mirror.h> 48132904Spjd 49132904Spjd 50132904Spjdstatic struct g_mirror_softc * 51132904Spjdg_mirror_find_device(struct g_class *mp, const char *name) 52132904Spjd{ 53132904Spjd struct g_mirror_softc *sc; 54132904Spjd struct g_geom *gp; 55132904Spjd 56156610Spjd g_topology_lock(); 57132904Spjd LIST_FOREACH(gp, &mp->geom, geom) { 58132904Spjd sc = gp->softc; 59132904Spjd if (sc == NULL) 60132904Spjd continue; 61132904Spjd if ((sc->sc_flags & G_MIRROR_DEVICE_FLAG_DESTROY) != 0) 62132904Spjd continue; 63132904Spjd if (strcmp(gp->name, name) == 0 || 64132904Spjd strcmp(sc->sc_name, name) == 0) { 65156610Spjd g_topology_unlock(); 66156610Spjd sx_xlock(&sc->sc_lock); 67132904Spjd return (sc); 68132904Spjd } 69132904Spjd } 70156610Spjd g_topology_unlock(); 71132904Spjd return (NULL); 72132904Spjd} 73132904Spjd 74132904Spjdstatic struct g_mirror_disk * 75132904Spjdg_mirror_find_disk(struct g_mirror_softc *sc, const char *name) 76132904Spjd{ 77132904Spjd struct g_mirror_disk *disk; 78132904Spjd 79156610Spjd sx_assert(&sc->sc_lock, SX_XLOCKED); 80160330Spjd if (strncmp(name, "/dev/", 5) == 0) 81160330Spjd name += 5; 82132904Spjd LIST_FOREACH(disk, &sc->sc_disks, d_next) { 83132904Spjd if (disk->d_consumer == NULL) 84132904Spjd continue; 85132904Spjd if (disk->d_consumer->provider == NULL) 86132904Spjd continue; 87132904Spjd if (strcmp(disk->d_consumer->provider->name, name) == 0) 88132904Spjd return (disk); 89132904Spjd } 90132904Spjd return (NULL); 91132904Spjd} 92132904Spjd 93132904Spjdstatic void 94132904Spjdg_mirror_ctl_configure(struct gctl_req *req, struct g_class *mp) 95132904Spjd{ 96132904Spjd struct g_mirror_softc *sc; 97132904Spjd struct g_mirror_disk *disk; 98196879Spjd const char *name, *balancep, *prov; 99196879Spjd intmax_t *slicep, *priority; 100132904Spjd uint32_t slice; 101132904Spjd uint8_t balance; 102163888Spjd int *autosync, *noautosync, *failsync, *nofailsync, *hardcode, *dynamic; 103196879Spjd int *nargs, do_sync = 0, dirty = 1, do_priority = 0; 104132904Spjd 105132904Spjd nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); 106144142Spjd if (nargs == NULL) { 107144142Spjd gctl_error(req, "No '%s' argument.", "nargs"); 108144142Spjd return; 109144142Spjd } 110196879Spjd if (*nargs != 1 && *nargs != 2) { 111132904Spjd gctl_error(req, "Invalid number of arguments."); 112132904Spjd return; 113132904Spjd } 114132904Spjd name = gctl_get_asciiparam(req, "arg0"); 115144142Spjd if (name == NULL) { 116144142Spjd gctl_error(req, "No 'arg%u' argument.", 0); 117144142Spjd return; 118144142Spjd } 119132904Spjd balancep = gctl_get_asciiparam(req, "balance"); 120144142Spjd if (balancep == NULL) { 121144142Spjd gctl_error(req, "No '%s' argument.", "balance"); 122144142Spjd return; 123144142Spjd } 124132904Spjd autosync = gctl_get_paraml(req, "autosync", sizeof(*autosync)); 125132904Spjd if (autosync == NULL) { 126132904Spjd gctl_error(req, "No '%s' argument.", "autosync"); 127132904Spjd return; 128132904Spjd } 129132904Spjd noautosync = gctl_get_paraml(req, "noautosync", sizeof(*noautosync)); 130132904Spjd if (noautosync == NULL) { 131132904Spjd gctl_error(req, "No '%s' argument.", "noautosync"); 132132904Spjd return; 133132904Spjd } 134163888Spjd failsync = gctl_get_paraml(req, "failsync", sizeof(*failsync)); 135163888Spjd if (failsync == NULL) { 136163888Spjd gctl_error(req, "No '%s' argument.", "failsync"); 137163888Spjd return; 138163888Spjd } 139163888Spjd nofailsync = gctl_get_paraml(req, "nofailsync", sizeof(*nofailsync)); 140163888Spjd if (nofailsync == NULL) { 141163888Spjd gctl_error(req, "No '%s' argument.", "nofailsync"); 142163888Spjd return; 143163888Spjd } 144133447Spjd hardcode = gctl_get_paraml(req, "hardcode", sizeof(*hardcode)); 145133447Spjd if (hardcode == NULL) { 146133447Spjd gctl_error(req, "No '%s' argument.", "hardcode"); 147133447Spjd return; 148133447Spjd } 149133447Spjd dynamic = gctl_get_paraml(req, "dynamic", sizeof(*dynamic)); 150133447Spjd if (dynamic == NULL) { 151133447Spjd gctl_error(req, "No '%s' argument.", "dynamic"); 152133447Spjd return; 153133447Spjd } 154196879Spjd priority = gctl_get_paraml(req, "priority", sizeof(*priority)); 155196879Spjd if (priority == NULL) { 156196879Spjd gctl_error(req, "No '%s' argument.", "priority"); 157196879Spjd return; 158196879Spjd } 159196879Spjd if (*priority < -1 || *priority > 255) { 160196879Spjd gctl_error(req, "Priority range is 0 to 255, %jd given", 161196879Spjd *priority); 162196879Spjd return; 163196879Spjd } 164196879Spjd /* 165196879Spjd * Since we have a priority, we also need a provider now. 166196879Spjd * Note: be WARNS safe, by always assigning prov and only throw an 167196879Spjd * error if *priority != -1. 168196879Spjd */ 169196879Spjd prov = gctl_get_asciiparam(req, "arg1"); 170196879Spjd if (*priority > -1) { 171196879Spjd if (prov == NULL) { 172196879Spjd gctl_error(req, "Priority needs a disk name"); 173196879Spjd return; 174196879Spjd } 175196879Spjd do_priority = 1; 176196879Spjd } 177132904Spjd if (*autosync && *noautosync) { 178132904Spjd gctl_error(req, "'%s' and '%s' specified.", "autosync", 179132904Spjd "noautosync"); 180132904Spjd return; 181132904Spjd } 182163888Spjd if (*failsync && *nofailsync) { 183163888Spjd gctl_error(req, "'%s' and '%s' specified.", "failsync", 184163888Spjd "nofailsync"); 185163888Spjd return; 186163888Spjd } 187133447Spjd if (*hardcode && *dynamic) { 188133447Spjd gctl_error(req, "'%s' and '%s' specified.", "hardcode", 189133447Spjd "dynamic"); 190133447Spjd return; 191133447Spjd } 192156610Spjd sc = g_mirror_find_device(mp, name); 193156610Spjd if (sc == NULL) { 194156610Spjd gctl_error(req, "No such device: %s.", name); 195156610Spjd return; 196156610Spjd } 197212547Spjd if (*balancep == '\0') 198156610Spjd balance = sc->sc_balance; 199156610Spjd else { 200156610Spjd if (balance_id(balancep) == -1) { 201156610Spjd gctl_error(req, "Invalid balance algorithm."); 202156610Spjd sx_xunlock(&sc->sc_lock); 203156610Spjd return; 204156610Spjd } 205156610Spjd balance = balance_id(balancep); 206156610Spjd } 207156610Spjd slicep = gctl_get_paraml(req, "slice", sizeof(*slicep)); 208156610Spjd if (slicep == NULL) { 209156610Spjd gctl_error(req, "No '%s' argument.", "slice"); 210156610Spjd sx_xunlock(&sc->sc_lock); 211156610Spjd return; 212156610Spjd } 213156610Spjd if (*slicep == -1) 214156610Spjd slice = sc->sc_slice; 215156610Spjd else 216156610Spjd slice = *slicep; 217196879Spjd /* Enforce usage() of -p not allowing any other options. */ 218196879Spjd if (do_priority && (*autosync || *noautosync || *failsync || 219196879Spjd *nofailsync || *hardcode || *dynamic || *slicep != -1 || 220212547Spjd *balancep != '\0')) { 221156610Spjd sx_xunlock(&sc->sc_lock); 222196879Spjd gctl_error(req, "only -p accepted when setting priority"); 223156610Spjd return; 224156610Spjd } 225156610Spjd if (sc->sc_balance == balance && sc->sc_slice == slice && !*autosync && 226163888Spjd !*noautosync && !*failsync && !*nofailsync && !*hardcode && 227196879Spjd !*dynamic && !do_priority) { 228156610Spjd sx_xunlock(&sc->sc_lock); 229156610Spjd gctl_error(req, "Nothing has changed."); 230156610Spjd return; 231156610Spjd } 232196879Spjd if ((!do_priority && *nargs != 1) || (do_priority && *nargs != 2)) { 233196879Spjd sx_xunlock(&sc->sc_lock); 234196879Spjd gctl_error(req, "Invalid number of arguments."); 235196879Spjd return; 236196879Spjd } 237196879Spjd if (g_mirror_ndisks(sc, -1) < sc->sc_ndisks) { 238196879Spjd sx_xunlock(&sc->sc_lock); 239196879Spjd gctl_error(req, "Not all disks connected. Try 'forget' command " 240196879Spjd "first."); 241196879Spjd return; 242196879Spjd } 243132904Spjd sc->sc_balance = balance; 244132904Spjd sc->sc_slice = slice; 245132904Spjd if ((sc->sc_flags & G_MIRROR_DEVICE_FLAG_NOAUTOSYNC) != 0) { 246132904Spjd if (*autosync) { 247132904Spjd sc->sc_flags &= ~G_MIRROR_DEVICE_FLAG_NOAUTOSYNC; 248132904Spjd do_sync = 1; 249132904Spjd } 250132904Spjd } else { 251132904Spjd if (*noautosync) 252132904Spjd sc->sc_flags |= G_MIRROR_DEVICE_FLAG_NOAUTOSYNC; 253132904Spjd } 254163888Spjd if ((sc->sc_flags & G_MIRROR_DEVICE_FLAG_NOFAILSYNC) != 0) { 255163888Spjd if (*failsync) 256163888Spjd sc->sc_flags &= ~G_MIRROR_DEVICE_FLAG_NOFAILSYNC; 257163888Spjd } else { 258163888Spjd if (*nofailsync) { 259163888Spjd sc->sc_flags |= G_MIRROR_DEVICE_FLAG_NOFAILSYNC; 260163888Spjd dirty = 0; 261163888Spjd } 262163888Spjd } 263132904Spjd LIST_FOREACH(disk, &sc->sc_disks, d_next) { 264196879Spjd /* 265196879Spjd * Handle priority first, since we only need one disk, do one 266196879Spjd * operation on it and then we're done. No need to check other 267196879Spjd * flags, as usage doesn't allow it. 268196879Spjd */ 269196879Spjd if (do_priority) { 270196879Spjd if (strcmp(disk->d_name, prov) == 0) { 271196879Spjd if (disk->d_priority == *priority) 272196879Spjd gctl_error(req, "Nothing has changed."); 273196879Spjd else { 274196879Spjd disk->d_priority = *priority; 275196879Spjd g_mirror_update_metadata(disk); 276196879Spjd } 277196879Spjd break; 278196879Spjd } 279196879Spjd continue; 280196879Spjd } 281132904Spjd if (do_sync) { 282132904Spjd if (disk->d_state == G_MIRROR_DISK_STATE_SYNCHRONIZING) 283132904Spjd disk->d_flags &= ~G_MIRROR_DISK_FLAG_FORCE_SYNC; 284132904Spjd } 285133447Spjd if (*hardcode) 286133447Spjd disk->d_flags |= G_MIRROR_DISK_FLAG_HARDCODED; 287133447Spjd else if (*dynamic) 288133447Spjd disk->d_flags &= ~G_MIRROR_DISK_FLAG_HARDCODED; 289163888Spjd if (!dirty) 290163888Spjd disk->d_flags &= ~G_MIRROR_DISK_FLAG_DIRTY; 291132904Spjd g_mirror_update_metadata(disk); 292132904Spjd if (do_sync) { 293132904Spjd if (disk->d_state == G_MIRROR_DISK_STATE_STALE) { 294132904Spjd g_mirror_event_send(disk, 295132904Spjd G_MIRROR_DISK_STATE_DISCONNECTED, 296132904Spjd G_MIRROR_EVENT_DONTWAIT); 297132904Spjd } 298132904Spjd } 299132904Spjd } 300156610Spjd sx_xunlock(&sc->sc_lock); 301132904Spjd} 302132904Spjd 303132904Spjdstatic void 304132904Spjdg_mirror_ctl_rebuild(struct gctl_req *req, struct g_class *mp) 305132904Spjd{ 306139650Spjd struct g_mirror_metadata md; 307132904Spjd struct g_mirror_softc *sc; 308132904Spjd struct g_mirror_disk *disk; 309139650Spjd struct g_provider *pp; 310132904Spjd const char *name; 311132904Spjd char param[16]; 312139650Spjd int error, *nargs; 313132904Spjd u_int i; 314132904Spjd 315132904Spjd nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); 316132904Spjd if (nargs == NULL) { 317132904Spjd gctl_error(req, "No '%s' argument.", "nargs"); 318132904Spjd return; 319132904Spjd } 320132904Spjd if (*nargs < 2) { 321132904Spjd gctl_error(req, "Too few arguments."); 322132904Spjd return; 323132904Spjd } 324132904Spjd name = gctl_get_asciiparam(req, "arg0"); 325132904Spjd if (name == NULL) { 326132904Spjd gctl_error(req, "No 'arg%u' argument.", 0); 327132904Spjd return; 328132904Spjd } 329132904Spjd sc = g_mirror_find_device(mp, name); 330132904Spjd if (sc == NULL) { 331132904Spjd gctl_error(req, "No such device: %s.", name); 332132904Spjd return; 333132904Spjd } 334132904Spjd for (i = 1; i < (u_int)*nargs; i++) { 335132904Spjd snprintf(param, sizeof(param), "arg%u", i); 336132904Spjd name = gctl_get_asciiparam(req, param); 337132904Spjd if (name == NULL) { 338132904Spjd gctl_error(req, "No 'arg%u' argument.", i); 339139050Spjd continue; 340132904Spjd } 341132904Spjd disk = g_mirror_find_disk(sc, name); 342132904Spjd if (disk == NULL) { 343132904Spjd gctl_error(req, "No such provider: %s.", name); 344139050Spjd continue; 345132904Spjd } 346132904Spjd if (g_mirror_ndisks(sc, G_MIRROR_DISK_STATE_ACTIVE) == 1 && 347132904Spjd disk->d_state == G_MIRROR_DISK_STATE_ACTIVE) { 348132904Spjd /* 349132904Spjd * This is the last active disk. There will be nothing 350132904Spjd * to rebuild it from, so deny this request. 351132904Spjd */ 352132904Spjd gctl_error(req, 353132904Spjd "Provider %s is the last active provider in %s.", 354132904Spjd name, sc->sc_geom->name); 355156610Spjd break; 356132904Spjd } 357132904Spjd /* 358139650Spjd * Do rebuild by resetting syncid, disconnecting the disk and 359139650Spjd * connecting it again. 360132904Spjd */ 361132904Spjd disk->d_sync.ds_syncid = 0; 362132904Spjd if ((sc->sc_flags & G_MIRROR_DEVICE_FLAG_NOAUTOSYNC) != 0) 363132904Spjd disk->d_flags |= G_MIRROR_DISK_FLAG_FORCE_SYNC; 364132904Spjd g_mirror_update_metadata(disk); 365139650Spjd pp = disk->d_consumer->provider; 366156610Spjd g_topology_lock(); 367139650Spjd error = g_mirror_read_metadata(disk->d_consumer, &md); 368156610Spjd g_topology_unlock(); 369132904Spjd g_mirror_event_send(disk, G_MIRROR_DISK_STATE_DISCONNECTED, 370132904Spjd G_MIRROR_EVENT_WAIT); 371139650Spjd if (error != 0) { 372139650Spjd gctl_error(req, "Cannot read metadata from %s.", 373139650Spjd pp->name); 374139650Spjd continue; 375139650Spjd } 376139650Spjd error = g_mirror_add_disk(sc, pp, &md); 377139650Spjd if (error != 0) { 378139650Spjd gctl_error(req, "Cannot reconnect component %s.", 379139650Spjd pp->name); 380139650Spjd continue; 381139650Spjd } 382132904Spjd } 383156610Spjd sx_xunlock(&sc->sc_lock); 384132904Spjd} 385132904Spjd 386132904Spjdstatic void 387132904Spjdg_mirror_ctl_insert(struct gctl_req *req, struct g_class *mp) 388132904Spjd{ 389132904Spjd struct g_mirror_softc *sc; 390132904Spjd struct g_mirror_disk *disk; 391132904Spjd struct g_mirror_metadata md; 392132904Spjd struct g_provider *pp; 393132904Spjd struct g_consumer *cp; 394132909Spjd intmax_t *priority; 395132904Spjd const char *name; 396132904Spjd char param[16]; 397132904Spjd u_char *sector; 398132904Spjd u_int i, n; 399133447Spjd int error, *nargs, *hardcode, *inactive; 400132904Spjd struct { 401132904Spjd struct g_provider *provider; 402132904Spjd struct g_consumer *consumer; 403132904Spjd } *disks; 404132904Spjd 405132904Spjd nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); 406132904Spjd if (nargs == NULL) { 407132904Spjd gctl_error(req, "No '%s' argument.", "nargs"); 408132904Spjd return; 409132904Spjd } 410132904Spjd if (*nargs < 2) { 411132904Spjd gctl_error(req, "Too few arguments."); 412132904Spjd return; 413132904Spjd } 414132909Spjd priority = gctl_get_paraml(req, "priority", sizeof(*priority)); 415132909Spjd if (priority == NULL) { 416132909Spjd gctl_error(req, "No '%s' argument.", "priority"); 417132909Spjd return; 418132909Spjd } 419132904Spjd inactive = gctl_get_paraml(req, "inactive", sizeof(*inactive)); 420132904Spjd if (inactive == NULL) { 421132904Spjd gctl_error(req, "No '%s' argument.", "inactive"); 422132904Spjd return; 423132904Spjd } 424133447Spjd hardcode = gctl_get_paraml(req, "hardcode", sizeof(*hardcode)); 425133447Spjd if (hardcode == NULL) { 426133447Spjd gctl_error(req, "No '%s' argument.", "hardcode"); 427133447Spjd return; 428133447Spjd } 429132904Spjd name = gctl_get_asciiparam(req, "arg0"); 430132904Spjd if (name == NULL) { 431132904Spjd gctl_error(req, "No 'arg%u' argument.", 0); 432132904Spjd return; 433132904Spjd } 434132904Spjd sc = g_mirror_find_device(mp, name); 435132904Spjd if (sc == NULL) { 436132904Spjd gctl_error(req, "No such device: %s.", name); 437132904Spjd return; 438132904Spjd } 439132904Spjd if (g_mirror_ndisks(sc, -1) < sc->sc_ndisks) { 440132904Spjd gctl_error(req, "Not all disks connected."); 441156610Spjd sx_xunlock(&sc->sc_lock); 442132904Spjd return; 443132904Spjd } 444132904Spjd 445132904Spjd disks = g_malloc(sizeof(*disks) * (*nargs), M_WAITOK | M_ZERO); 446156610Spjd g_topology_lock(); 447132904Spjd for (i = 1, n = 0; i < (u_int)*nargs; i++) { 448132904Spjd snprintf(param, sizeof(param), "arg%u", i); 449132904Spjd name = gctl_get_asciiparam(req, param); 450132904Spjd if (name == NULL) { 451132904Spjd gctl_error(req, "No 'arg%u' argument.", i); 452132904Spjd continue; 453132904Spjd } 454132908Spjd if (g_mirror_find_disk(sc, name) != NULL) { 455132908Spjd gctl_error(req, "Provider %s already inserted.", name); 456132908Spjd continue; 457132908Spjd } 458160330Spjd if (strncmp(name, "/dev/", 5) == 0) 459160330Spjd name += 5; 460132904Spjd pp = g_provider_by_name(name); 461132904Spjd if (pp == NULL) { 462132904Spjd gctl_error(req, "Unknown provider %s.", name); 463132904Spjd continue; 464132904Spjd } 465145502Spjd if (sc->sc_provider->mediasize > 466145502Spjd pp->mediasize - pp->sectorsize) { 467132904Spjd gctl_error(req, "Provider %s too small.", name); 468132904Spjd continue; 469132904Spjd } 470132904Spjd if ((sc->sc_provider->sectorsize % pp->sectorsize) != 0) { 471132904Spjd gctl_error(req, "Invalid sectorsize of provider %s.", 472132904Spjd name); 473132904Spjd continue; 474132904Spjd } 475132904Spjd cp = g_new_consumer(sc->sc_geom); 476132904Spjd if (g_attach(cp, pp) != 0) { 477132904Spjd g_destroy_consumer(cp); 478132904Spjd gctl_error(req, "Cannot attach to provider %s.", name); 479132904Spjd continue; 480132904Spjd } 481132904Spjd if (g_access(cp, 0, 1, 1) != 0) { 482132904Spjd g_detach(cp); 483132904Spjd g_destroy_consumer(cp); 484132904Spjd gctl_error(req, "Cannot access provider %s.", name); 485132904Spjd continue; 486132904Spjd } 487132904Spjd disks[n].provider = pp; 488132904Spjd disks[n].consumer = cp; 489132904Spjd n++; 490132904Spjd } 491132904Spjd if (n == 0) { 492156610Spjd g_topology_unlock(); 493156610Spjd sx_xunlock(&sc->sc_lock); 494132904Spjd g_free(disks); 495132904Spjd return; 496132904Spjd } 497132904Spjd sc->sc_ndisks += n; 498132904Spjdagain: 499132904Spjd for (i = 0; i < n; i++) { 500132904Spjd if (disks[i].consumer == NULL) 501132904Spjd continue; 502132904Spjd g_mirror_fill_metadata(sc, NULL, &md); 503132909Spjd md.md_priority = *priority; 504132904Spjd if (*inactive) 505132904Spjd md.md_dflags |= G_MIRROR_DISK_FLAG_INACTIVE; 506132904Spjd pp = disks[i].provider; 507133447Spjd if (*hardcode) { 508133447Spjd strlcpy(md.md_provider, pp->name, 509133447Spjd sizeof(md.md_provider)); 510133447Spjd } else { 511133447Spjd bzero(md.md_provider, sizeof(md.md_provider)); 512133447Spjd } 513156527Spjd md.md_provsize = pp->mediasize; 514132904Spjd sector = g_malloc(pp->sectorsize, M_WAITOK); 515132904Spjd mirror_metadata_encode(&md, sector); 516132904Spjd error = g_write_data(disks[i].consumer, 517132904Spjd pp->mediasize - pp->sectorsize, sector, pp->sectorsize); 518132904Spjd g_free(sector); 519132904Spjd if (error != 0) { 520132904Spjd gctl_error(req, "Cannot store metadata on %s.", 521132904Spjd pp->name); 522132904Spjd g_access(disks[i].consumer, 0, -1, -1); 523132904Spjd g_detach(disks[i].consumer); 524132904Spjd g_destroy_consumer(disks[i].consumer); 525132904Spjd disks[i].consumer = NULL; 526132904Spjd disks[i].provider = NULL; 527132904Spjd sc->sc_ndisks--; 528132904Spjd goto again; 529132904Spjd } 530132904Spjd } 531156610Spjd g_topology_unlock(); 532132904Spjd if (i == 0) { 533132904Spjd /* All writes failed. */ 534156610Spjd sx_xunlock(&sc->sc_lock); 535132904Spjd g_free(disks); 536132904Spjd return; 537132904Spjd } 538132904Spjd LIST_FOREACH(disk, &sc->sc_disks, d_next) { 539132904Spjd g_mirror_update_metadata(disk); 540132904Spjd } 541132904Spjd /* 542132904Spjd * Release provider and wait for retaste. 543132904Spjd */ 544156610Spjd g_topology_lock(); 545132904Spjd for (i = 0; i < n; i++) { 546132904Spjd if (disks[i].consumer == NULL) 547132904Spjd continue; 548132904Spjd g_access(disks[i].consumer, 0, -1, -1); 549132904Spjd g_detach(disks[i].consumer); 550132904Spjd g_destroy_consumer(disks[i].consumer); 551132904Spjd } 552156610Spjd g_topology_unlock(); 553156610Spjd sx_xunlock(&sc->sc_lock); 554132904Spjd g_free(disks); 555132904Spjd} 556132904Spjd 557132904Spjdstatic void 558132904Spjdg_mirror_ctl_remove(struct gctl_req *req, struct g_class *mp) 559132904Spjd{ 560132904Spjd struct g_mirror_softc *sc; 561132904Spjd struct g_mirror_disk *disk; 562132904Spjd const char *name; 563132904Spjd char param[16]; 564132904Spjd int *nargs; 565235600Sae u_int i, active; 566132904Spjd 567132904Spjd nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); 568132904Spjd if (nargs == NULL) { 569132904Spjd gctl_error(req, "No '%s' argument.", "nargs"); 570132904Spjd return; 571132904Spjd } 572132904Spjd if (*nargs < 2) { 573132904Spjd gctl_error(req, "Too few arguments."); 574132904Spjd return; 575132904Spjd } 576132904Spjd name = gctl_get_asciiparam(req, "arg0"); 577132904Spjd if (name == NULL) { 578132904Spjd gctl_error(req, "No 'arg%u' argument.", 0); 579132904Spjd return; 580132904Spjd } 581132904Spjd sc = g_mirror_find_device(mp, name); 582132904Spjd if (sc == NULL) { 583132904Spjd gctl_error(req, "No such device: %s.", name); 584132904Spjd return; 585132904Spjd } 586132904Spjd if (g_mirror_ndisks(sc, -1) < sc->sc_ndisks) { 587156610Spjd sx_xunlock(&sc->sc_lock); 588156610Spjd gctl_error(req, "Not all disks connected. Try 'forget' command " 589156610Spjd "first."); 590132904Spjd return; 591132904Spjd } 592235600Sae active = g_mirror_ndisks(sc, G_MIRROR_DISK_STATE_ACTIVE); 593132904Spjd for (i = 1; i < (u_int)*nargs; i++) { 594132904Spjd snprintf(param, sizeof(param), "arg%u", i); 595132904Spjd name = gctl_get_asciiparam(req, param); 596132904Spjd if (name == NULL) { 597132904Spjd gctl_error(req, "No 'arg%u' argument.", i); 598139050Spjd continue; 599132904Spjd } 600132904Spjd disk = g_mirror_find_disk(sc, name); 601132904Spjd if (disk == NULL) { 602132904Spjd gctl_error(req, "No such provider: %s.", name); 603139050Spjd continue; 604132904Spjd } 605235600Sae if (disk->d_state == G_MIRROR_DISK_STATE_ACTIVE) { 606235600Sae if (active > 1) 607235600Sae active--; 608235600Sae else { 609235600Sae gctl_error(req, "%s: Can't remove the last " 610235600Sae "ACTIVE component %s.", sc->sc_geom->name, 611235600Sae name); 612235600Sae continue; 613235600Sae } 614235600Sae } 615132904Spjd g_mirror_event_send(disk, G_MIRROR_DISK_STATE_DESTROY, 616156610Spjd G_MIRROR_EVENT_DONTWAIT); 617132904Spjd } 618156610Spjd sx_xunlock(&sc->sc_lock); 619132904Spjd} 620132904Spjd 621132904Spjdstatic void 622260502Saeg_mirror_ctl_resize(struct gctl_req *req, struct g_class *mp) 623260502Sae{ 624260502Sae struct g_mirror_softc *sc; 625260502Sae struct g_mirror_disk *disk; 626260502Sae uint64_t mediasize; 627260502Sae const char *name, *s; 628260502Sae char *x; 629260502Sae int *nargs; 630260502Sae 631260502Sae nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); 632260502Sae if (nargs == NULL) { 633260502Sae gctl_error(req, "No '%s' argument.", "nargs"); 634260502Sae return; 635260502Sae } 636260502Sae if (*nargs != 1) { 637260502Sae gctl_error(req, "Missing device."); 638260502Sae return; 639260502Sae } 640260502Sae name = gctl_get_asciiparam(req, "arg0"); 641260502Sae if (name == NULL) { 642260502Sae gctl_error(req, "No 'arg%u' argument.", 0); 643260502Sae return; 644260502Sae } 645260502Sae s = gctl_get_asciiparam(req, "size"); 646260502Sae if (s == NULL) { 647260502Sae gctl_error(req, "No '%s' argument.", "size"); 648260502Sae return; 649260502Sae } 650260502Sae mediasize = strtouq(s, &x, 0); 651260502Sae if (*x != '\0' || mediasize == 0) { 652260502Sae gctl_error(req, "Invalid '%s' argument.", "size"); 653260502Sae return; 654260502Sae } 655260502Sae sc = g_mirror_find_device(mp, name); 656260502Sae if (sc == NULL) { 657260502Sae gctl_error(req, "No such device: %s.", name); 658260502Sae return; 659260502Sae } 660260502Sae /* Deny shrinking of an opened provider */ 661260502Sae if ((g_debugflags & 16) == 0 && (sc->sc_provider->acr > 0 || 662260502Sae sc->sc_provider->acw > 0 || sc->sc_provider->ace > 0)) { 663260502Sae if (sc->sc_mediasize > mediasize) { 664260502Sae gctl_error(req, "Device %s is busy.", 665260502Sae sc->sc_provider->name); 666260502Sae sx_xunlock(&sc->sc_lock); 667260502Sae return; 668260502Sae } 669260502Sae } 670260502Sae LIST_FOREACH(disk, &sc->sc_disks, d_next) { 671260502Sae if (mediasize > disk->d_consumer->provider->mediasize - 672260502Sae disk->d_consumer->provider->sectorsize) { 673260502Sae gctl_error(req, "Provider %s is too small.", 674260502Sae disk->d_name); 675260502Sae sx_xunlock(&sc->sc_lock); 676260502Sae return; 677260502Sae } 678260502Sae } 679260502Sae /* Update the size. */ 680260502Sae sc->sc_mediasize = mediasize; 681260502Sae LIST_FOREACH(disk, &sc->sc_disks, d_next) { 682260502Sae g_mirror_update_metadata(disk); 683260502Sae } 684260502Sae g_topology_lock(); 685260502Sae g_resize_provider(sc->sc_provider, mediasize); 686260502Sae g_topology_unlock(); 687260502Sae sx_xunlock(&sc->sc_lock); 688260502Sae} 689260502Sae 690260502Saestatic void 691132904Spjdg_mirror_ctl_deactivate(struct gctl_req *req, struct g_class *mp) 692132904Spjd{ 693132904Spjd struct g_mirror_softc *sc; 694132904Spjd struct g_mirror_disk *disk; 695132904Spjd const char *name; 696132904Spjd char param[16]; 697132904Spjd int *nargs; 698260503Sae u_int i, active; 699132904Spjd 700132904Spjd nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); 701132904Spjd if (nargs == NULL) { 702132904Spjd gctl_error(req, "No '%s' argument.", "nargs"); 703132904Spjd return; 704132904Spjd } 705132904Spjd if (*nargs < 2) { 706132904Spjd gctl_error(req, "Too few arguments."); 707132904Spjd return; 708132904Spjd } 709132904Spjd name = gctl_get_asciiparam(req, "arg0"); 710132904Spjd if (name == NULL) { 711132904Spjd gctl_error(req, "No 'arg%u' argument.", 0); 712132904Spjd return; 713132904Spjd } 714132904Spjd sc = g_mirror_find_device(mp, name); 715132904Spjd if (sc == NULL) { 716132904Spjd gctl_error(req, "No such device: %s.", name); 717132904Spjd return; 718132904Spjd } 719260503Sae active = g_mirror_ndisks(sc, G_MIRROR_DISK_STATE_ACTIVE); 720132904Spjd for (i = 1; i < (u_int)*nargs; i++) { 721132904Spjd snprintf(param, sizeof(param), "arg%u", i); 722132904Spjd name = gctl_get_asciiparam(req, param); 723132904Spjd if (name == NULL) { 724132904Spjd gctl_error(req, "No 'arg%u' argument.", i); 725139050Spjd continue; 726132904Spjd } 727132904Spjd disk = g_mirror_find_disk(sc, name); 728132904Spjd if (disk == NULL) { 729132904Spjd gctl_error(req, "No such provider: %s.", name); 730139050Spjd continue; 731132904Spjd } 732260503Sae if (disk->d_state == G_MIRROR_DISK_STATE_ACTIVE) { 733260503Sae if (active > 1) 734260503Sae active--; 735260503Sae else { 736260503Sae gctl_error(req, "%s: Can't deactivate the " 737260503Sae "last ACTIVE component %s.", 738260503Sae sc->sc_geom->name, name); 739260503Sae continue; 740260503Sae } 741260503Sae } 742132904Spjd disk->d_flags |= G_MIRROR_DISK_FLAG_INACTIVE; 743132904Spjd disk->d_flags &= ~G_MIRROR_DISK_FLAG_FORCE_SYNC; 744132904Spjd g_mirror_update_metadata(disk); 745139670Spjd sc->sc_bump_id |= G_MIRROR_BUMP_SYNCID; 746132904Spjd g_mirror_event_send(disk, G_MIRROR_DISK_STATE_DISCONNECTED, 747156610Spjd G_MIRROR_EVENT_DONTWAIT); 748132904Spjd } 749156610Spjd sx_xunlock(&sc->sc_lock); 750132904Spjd} 751132904Spjd 752132904Spjdstatic void 753132904Spjdg_mirror_ctl_forget(struct gctl_req *req, struct g_class *mp) 754132904Spjd{ 755132904Spjd struct g_mirror_softc *sc; 756132904Spjd struct g_mirror_disk *disk; 757132904Spjd const char *name; 758132904Spjd char param[16]; 759132904Spjd int *nargs; 760132904Spjd u_int i; 761132904Spjd 762132904Spjd nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); 763132904Spjd if (nargs == NULL) { 764132904Spjd gctl_error(req, "No '%s' argument.", "nargs"); 765132904Spjd return; 766132904Spjd } 767132904Spjd if (*nargs < 1) { 768132904Spjd gctl_error(req, "Missing device(s)."); 769132904Spjd return; 770132904Spjd } 771132904Spjd 772132904Spjd for (i = 0; i < (u_int)*nargs; i++) { 773132904Spjd snprintf(param, sizeof(param), "arg%u", i); 774132904Spjd name = gctl_get_asciiparam(req, param); 775132904Spjd if (name == NULL) { 776132904Spjd gctl_error(req, "No 'arg%u' argument.", i); 777132904Spjd return; 778132904Spjd } 779132904Spjd sc = g_mirror_find_device(mp, name); 780132904Spjd if (sc == NULL) { 781132904Spjd gctl_error(req, "No such device: %s.", name); 782132904Spjd return; 783132904Spjd } 784132904Spjd if (g_mirror_ndisks(sc, -1) == sc->sc_ndisks) { 785156610Spjd sx_xunlock(&sc->sc_lock); 786132904Spjd G_MIRROR_DEBUG(1, 787132904Spjd "All disks connected in %s, skipping.", 788132904Spjd sc->sc_name); 789132904Spjd continue; 790132904Spjd } 791132904Spjd sc->sc_ndisks = g_mirror_ndisks(sc, -1); 792132904Spjd LIST_FOREACH(disk, &sc->sc_disks, d_next) { 793132904Spjd g_mirror_update_metadata(disk); 794132904Spjd } 795156610Spjd sx_xunlock(&sc->sc_lock); 796132904Spjd } 797132904Spjd} 798132904Spjd 799132904Spjdstatic void 800260503Saeg_mirror_ctl_stop(struct gctl_req *req, struct g_class *mp, int wipe) 801132904Spjd{ 802132904Spjd struct g_mirror_softc *sc; 803132904Spjd int *force, *nargs, error; 804132904Spjd const char *name; 805132904Spjd char param[16]; 806132904Spjd u_int i; 807157630Spjd int how; 808132904Spjd 809132904Spjd nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); 810132904Spjd if (nargs == NULL) { 811132904Spjd gctl_error(req, "No '%s' argument.", "nargs"); 812132904Spjd return; 813132904Spjd } 814132904Spjd if (*nargs < 1) { 815132904Spjd gctl_error(req, "Missing device(s)."); 816132904Spjd return; 817132904Spjd } 818132904Spjd force = gctl_get_paraml(req, "force", sizeof(*force)); 819132904Spjd if (force == NULL) { 820132904Spjd gctl_error(req, "No '%s' argument.", "force"); 821132904Spjd return; 822132904Spjd } 823157630Spjd if (*force) 824157630Spjd how = G_MIRROR_DESTROY_HARD; 825157630Spjd else 826157630Spjd how = G_MIRROR_DESTROY_SOFT; 827132904Spjd 828132904Spjd for (i = 0; i < (u_int)*nargs; i++) { 829132904Spjd snprintf(param, sizeof(param), "arg%u", i); 830132904Spjd name = gctl_get_asciiparam(req, param); 831132904Spjd if (name == NULL) { 832132904Spjd gctl_error(req, "No 'arg%u' argument.", i); 833132904Spjd return; 834132904Spjd } 835132904Spjd sc = g_mirror_find_device(mp, name); 836132904Spjd if (sc == NULL) { 837132904Spjd gctl_error(req, "No such device: %s.", name); 838132904Spjd return; 839132904Spjd } 840157630Spjd g_cancel_event(sc); 841260503Sae if (wipe) 842260503Sae sc->sc_flags |= G_MIRROR_DEVICE_FLAG_WIPE; 843157630Spjd error = g_mirror_destroy(sc, how); 844132904Spjd if (error != 0) { 845132904Spjd gctl_error(req, "Cannot destroy device %s (error=%d).", 846132904Spjd sc->sc_geom->name, error); 847260503Sae if (wipe) 848260503Sae sc->sc_flags &= ~G_MIRROR_DEVICE_FLAG_WIPE; 849156610Spjd sx_xunlock(&sc->sc_lock); 850132904Spjd return; 851132904Spjd } 852156610Spjd /* No need to unlock, because lock is already dead. */ 853132904Spjd } 854132904Spjd} 855132904Spjd 856132904Spjdvoid 857132904Spjdg_mirror_config(struct gctl_req *req, struct g_class *mp, const char *verb) 858132904Spjd{ 859132904Spjd uint32_t *version; 860132904Spjd 861132904Spjd g_topology_assert(); 862132904Spjd 863132904Spjd version = gctl_get_paraml(req, "version", sizeof(*version)); 864132904Spjd if (version == NULL) { 865132904Spjd gctl_error(req, "No '%s' argument.", "version"); 866132904Spjd return; 867132904Spjd } 868132904Spjd if (*version != G_MIRROR_VERSION) { 869132904Spjd gctl_error(req, "Userland and kernel parts are out of sync."); 870132904Spjd return; 871132904Spjd } 872132904Spjd 873156610Spjd g_topology_unlock(); 874132904Spjd if (strcmp(verb, "configure") == 0) 875132904Spjd g_mirror_ctl_configure(req, mp); 876132904Spjd else if (strcmp(verb, "rebuild") == 0) 877132904Spjd g_mirror_ctl_rebuild(req, mp); 878132904Spjd else if (strcmp(verb, "insert") == 0) 879132904Spjd g_mirror_ctl_insert(req, mp); 880132904Spjd else if (strcmp(verb, "remove") == 0) 881132904Spjd g_mirror_ctl_remove(req, mp); 882260502Sae else if (strcmp(verb, "resize") == 0) 883260502Sae g_mirror_ctl_resize(req, mp); 884132904Spjd else if (strcmp(verb, "deactivate") == 0) 885132904Spjd g_mirror_ctl_deactivate(req, mp); 886132904Spjd else if (strcmp(verb, "forget") == 0) 887132904Spjd g_mirror_ctl_forget(req, mp); 888132904Spjd else if (strcmp(verb, "stop") == 0) 889260503Sae g_mirror_ctl_stop(req, mp, 0); 890260503Sae else if (strcmp(verb, "destroy") == 0) 891260503Sae g_mirror_ctl_stop(req, mp, 1); 892132904Spjd else 893132904Spjd gctl_error(req, "Unknown verb."); 894156610Spjd g_topology_lock(); 895132904Spjd} 896