1 /* 2 * Copyright (c) 2004 Lukas Ertl 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 */ 27 28 #include <sys/cdefs.h> 29 __FBSDID("$FreeBSD$"); 30 31 #include <sys/param.h> 32 #include <sys/libkern.h> 33 #include <sys/kernel.h> 34 #include <sys/malloc.h> 35 36 #include <geom/geom.h> 37 #include <geom/vinum/geom_vinum_var.h> 38 #include <geom/vinum/geom_vinum.h> 39 #include <geom/vinum/geom_vinum_share.h> 40 41 static void gv_cleanup_pp(void *, int); 42 static void gv_free_sd(struct gv_sd *); 43 static int gv_rm_drive(struct gv_softc *, struct gctl_req *, 44 struct gv_drive *, int); 45 static int gv_rm_plex(struct gv_softc *, struct gctl_req *, 46 struct gv_plex *, int); 47 static int gv_rm_sd(struct gv_softc *, struct gctl_req *, struct gv_sd *, 48 int); 49 static int gv_rm_vol(struct gv_softc *, struct gctl_req *, 50 struct gv_volume *, int); 51 52 /* General 'remove' routine. */ 53 void 54 gv_remove(struct g_geom *gp, struct gctl_req *req) 55 { 56 struct gv_softc *sc; 57 struct gv_volume *v; 58 struct gv_plex *p; 59 struct gv_sd *s; 60 struct gv_drive *d; 61 int *argc, *flags; 62 char *argv, buf[20]; 63 int i, type, err; 64 65 argc = gctl_get_paraml(req, "argc", sizeof(*argc)); 66 flags = gctl_get_paraml(req, "flags", sizeof(*flags)); 67 68 if (argc == NULL || *argc == 0) { 69 gctl_error(req, "no arguments given"); 70 return; 71 } 72 73 sc = gp->softc; 74 75 for (i = 0; i < *argc; i++) { 76 snprintf(buf, sizeof(buf), "argv%d", i); 77 argv = gctl_get_param(req, buf, NULL); 78 if (argv == NULL) 79 continue; 80 type = gv_object_type(sc, argv); 81 switch (type) { 82 case GV_TYPE_VOL: 83 v = gv_find_vol(sc, argv); 84 if (v == NULL) { 85 gctl_error(req, "unknown volume '%s'", argv); 86 return; 87 } 88 err = gv_rm_vol(sc, req, v, *flags); 89 if (err) 90 return; 91 break; 92 case GV_TYPE_PLEX: 93 p = gv_find_plex(sc, argv); 94 if (p == NULL) { 95 gctl_error(req, "unknown plex '%s'", argv); 96 return; 97 } 98 err = gv_rm_plex(sc, req, p, *flags); 99 if (err) 100 return; 101 break; 102 case GV_TYPE_SD: 103 s = gv_find_sd(sc, argv); 104 if (s == NULL) { 105 gctl_error(req, "unknown subdisk '%s'", argv); 106 return; 107 } 108 err = gv_rm_sd(sc, req, s, *flags); 109 if (err) 110 return; 111 break; 112 case GV_TYPE_DRIVE: 113 d = gv_find_drive(sc, argv); 114 if (d == NULL) { 115 gctl_error(req, "unknown drive '%s'", argv); 116 return; 117 } 118 err = gv_rm_drive(sc, req, d, *flags); 119 if (err) 120 return; 121 break; 122 default: 123 gctl_error(req, "unknown object '%s'", argv); 124 return; 125 } 126 } 127 128 gv_save_config_all(sc); 129 } 130 131 /* Remove a volume. */ 132 static int 133 gv_rm_vol(struct gv_softc *sc, struct gctl_req *req, struct gv_volume *v, int flags) 134 { 135 struct g_geom *gp; 136 struct gv_plex *p, *p2; 137 int err; 138 139 g_topology_assert(); 140 KASSERT(v != NULL, ("gv_rm_vol: NULL v")); 141 142 /* If this volume has plexes, we want a recursive removal. */ 143 if (!LIST_EMPTY(&v->plexes) && !(flags & GV_FLAG_R)) { 144 gctl_error(req, "volume '%s' has attached plexes", v->name); 145 return (-1); 146 } 147 148 gp = v->geom; 149 150 /* Check if any of our consumers is open. */ 151 if (gp != NULL && gv_is_open(gp)) { 152 gctl_error(req, "volume '%s' is busy", v->name); 153 return (-1); 154 } 155 156 /* Remove the plexes our volume has. */ 157 LIST_FOREACH_SAFE(p, &v->plexes, in_volume, p2) { 158 v->plexcount--; 159 LIST_REMOVE(p, in_volume); 160 p->vol_sc = NULL; 161 162 err = gv_rm_plex(sc, req, p, flags); 163 if (err) 164 return (err); 165 } 166 167 /* Clean up and let our geom fade away. */ 168 LIST_REMOVE(v, volume); 169 gv_kill_vol_thread(v); 170 g_free(v); 171 if (gp != NULL) { 172 gp->softc = NULL; 173 g_wither_geom(gp, ENXIO); 174 } 175 176 return (0); 177 } 178 179 /* Remove a plex. */ 180 static int 181 gv_rm_plex(struct gv_softc *sc, struct gctl_req *req, struct gv_plex *p, int flags) 182 { 183 struct g_geom *gp; 184 struct gv_sd *s, *s2; 185 int err; 186 187 g_topology_assert(); 188 189 KASSERT(p != NULL, ("gv_rm_plex: NULL p")); 190 191 /* If this plex has subdisks, we want a recursive removal. */ 192 if (!LIST_EMPTY(&p->subdisks) && !(flags & GV_FLAG_R)) { 193 gctl_error(req, "plex '%s' has attached subdisks", p->name); 194 return (-1); 195 } 196 197 if (p->vol_sc != NULL && p->vol_sc->plexcount == 1) { 198 gctl_error(req, "plex '%s' is still attached to volume '%s'", 199 p->name, p->volume); 200 return (-1); 201 } 202 203 gp = p->geom; 204 205 /* Check if any of our consumers is open. */ 206 if (gp != NULL && gv_is_open(gp)) { 207 gctl_error(req, "plex '%s' is busy", p->name); 208 return (-1); 209 } 210 211 /* Remove the subdisks our plex has. */ 212 LIST_FOREACH_SAFE(s, &p->subdisks, in_plex, s2) { 213 p->sdcount--; 214 #if 0 215 LIST_REMOVE(s, in_plex); 216 s->plex_sc = NULL; 217 #endif 218 219 err = gv_rm_sd(sc, req, s, flags); 220 if (err) 221 return (err); 222 } 223 224 /* Clean up and let our geom fade away. */ 225 LIST_REMOVE(p, plex); 226 if (p->vol_sc != NULL) { 227 p->vol_sc->plexcount--; 228 LIST_REMOVE(p, in_volume); 229 p->vol_sc = NULL; 230 } 231 232 gv_kill_plex_thread(p); 233 g_free(p); 234 235 if (gp != NULL) { 236 gp->softc = NULL; 237 g_wither_geom(gp, ENXIO); 238 } 239 240 return (0); 241 } 242 243 /* Remove a subdisk. */ 244 static int 245 gv_rm_sd(struct gv_softc *sc, struct gctl_req *req, struct gv_sd *s, int flags) 246 { 247 struct gv_drive *d; 248 struct g_geom *gp; 249 struct g_provider *pp; 250 251 KASSERT(s != NULL, ("gv_rm_sd: NULL s")); 252 d = s->drive_sc; 253 KASSERT(d != NULL, ("gv_rm_sd: NULL d")); 254 gp = d->geom; 255 KASSERT(gp != NULL, ("gv_rm_sd: NULL gp")); 256 257 pp = s->provider; 258 259 /* Clean up. */ 260 LIST_REMOVE(s, in_plex); 261 LIST_REMOVE(s, from_drive); 262 LIST_REMOVE(s, sd); 263 gv_free_sd(s); 264 g_free(s); 265 266 /* If the subdisk has a provider we need to clean up this one too. */ 267 if (pp != NULL) { 268 g_orphan_provider(pp, ENXIO); 269 if (LIST_EMPTY(&pp->consumers)) 270 g_destroy_provider(pp); 271 else 272 /* Schedule this left-over provider for destruction. */ 273 g_post_event(gv_cleanup_pp, pp, M_WAITOK, pp, NULL); 274 } 275 276 return (0); 277 } 278 279 /* Remove a drive. */ 280 static int 281 gv_rm_drive(struct gv_softc *sc, struct gctl_req *req, struct gv_drive *d, int flags) 282 { 283 struct g_geom *gp; 284 struct g_consumer *cp; 285 struct gv_freelist *fl, *fl2; 286 struct gv_plex *p; 287 struct gv_sd *s, *s2; 288 struct gv_volume *v; 289 int err; 290 291 KASSERT(d != NULL, ("gv_rm_drive: NULL d")); 292 gp = d->geom; 293 KASSERT(gp != NULL, ("gv_rm_drive: NULL gp")); 294 295 /* We don't allow to remove open drives. */ 296 if (gv_is_open(gp)) { 297 gctl_error(req, "drive '%s' is open", d->name); 298 return (-1); 299 } 300 301 /* A drive with subdisks needs a recursive removal. */ 302 if (!LIST_EMPTY(&d->subdisks) && !(flags & GV_FLAG_R)) { 303 gctl_error(req, "drive '%s' still has subdisks", d->name); 304 return (-1); 305 } 306 307 cp = LIST_FIRST(&gp->consumer); 308 err = g_access(cp, 0, 1, 0); 309 if (err) { 310 printf("GEOM_VINUM: gv_rm_drive: couldn't access '%s', errno: " 311 "%d\n", cp->provider->name, err); 312 return (err); 313 } 314 315 /* Clear the Vinum Magic. */ 316 d->hdr->magic = GV_NOMAGIC; 317 g_topology_unlock(); 318 err = g_write_data(cp, GV_HDR_OFFSET, d->hdr, GV_HDR_LEN); 319 if (err) { 320 printf("GEOM_VINUM: gv_rm_drive: couldn't write header to '%s'" 321 ", errno: %d\n", cp->provider->name, err); 322 d->hdr->magic = GV_MAGIC; 323 } 324 g_topology_lock(); 325 g_access(cp, 0, -1, 0); 326 327 /* Remove all associated subdisks, plexes, volumes. */ 328 if (!LIST_EMPTY(&d->subdisks)) { 329 LIST_FOREACH_SAFE(s, &d->subdisks, from_drive, s2) { 330 p = s->plex_sc; 331 if (p != NULL) { 332 v = p->vol_sc; 333 if (v != NULL) 334 gv_rm_vol(sc, req, v, flags); 335 } 336 } 337 } 338 339 /* Clean up. */ 340 LIST_FOREACH_SAFE(fl, &d->freelist, freelist, fl2) { 341 LIST_REMOVE(fl, freelist); 342 g_free(fl); 343 } 344 LIST_REMOVE(d, drive); 345 346 gv_kill_drive_thread(d); 347 gp = d->geom; 348 d->geom = NULL; 349 g_free(d->hdr); 350 g_free(d); 351 gv_save_config_all(sc); 352 g_wither_geom(gp, ENXIO); 353 354 return (err); 355 } 356 357 /* 358 * This function is called from the event queue to clean up left-over subdisk 359 * providers. 360 */ 361 static void 362 gv_cleanup_pp(void *arg, int flag) 363 { 364 struct g_provider *pp; 365 366 g_topology_assert(); 367 368 if (flag == EV_CANCEL) 369 return; 370 371 pp = arg; 372 if (pp == NULL) { 373 printf("gv_cleanup_pp: provider has gone\n"); 374 return; 375 } 376 377 if (!LIST_EMPTY(&pp->consumers)) { 378 printf("gv_cleanup_pp: provider still not empty\n"); 379 return; 380 } 381 382 g_destroy_provider(pp); 383 } 384 385 static void 386 gv_free_sd(struct gv_sd *s) 387 { 388 struct gv_drive *d; 389 struct gv_freelist *fl, *fl2; 390 391 KASSERT(s != NULL, ("gv_free_sd: NULL s")); 392 d = s->drive_sc; 393 KASSERT(d != NULL, ("gv_free_sd: NULL d")); 394 395 /* 396 * First, find the free slot that's immediately before or after this 397 * subdisk. 398 */ 399 fl = NULL; 400 LIST_FOREACH(fl, &d->freelist, freelist) { 401 if (fl->offset == s->drive_offset + s->size) 402 break; 403 if (fl->offset + fl->size == s->drive_offset) 404 break; 405 } 406 407 /* If there is no free slot behind this subdisk, so create one. */ 408 if (fl == NULL) { 409 410 fl = g_malloc(sizeof(*fl), M_WAITOK | M_ZERO); 411 fl->size = s->size; 412 fl->offset = s->drive_offset; 413 414 if (d->freelist_entries == 0) { 415 LIST_INSERT_HEAD(&d->freelist, fl, freelist); 416 } else { 417 LIST_FOREACH(fl2, &d->freelist, freelist) { 418 if (fl->offset < fl2->offset) { 419 LIST_INSERT_BEFORE(fl2, fl, freelist); 420 break; 421 } else if (LIST_NEXT(fl2, freelist) == NULL) { 422 LIST_INSERT_AFTER(fl2, fl, freelist); 423 break; 424 } 425 } 426 } 427 428 d->freelist_entries++; 429 430 /* Expand the free slot we just found. */ 431 } else { 432 fl->size += s->size; 433 if (fl->offset > s->drive_offset) 434 fl->offset = s->drive_offset; 435 } 436 437 d->avail += s->size; 438 } 439