1 /*- 2 * Copyright (c) 2004-2006 Pawel Jakub Dawidek <pjd@FreeBSD.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27 #include <sys/cdefs.h> 28 __FBSDID("$FreeBSD$"); 29 30 #include <sys/param.h> 31 #include <sys/systm.h> 32 #include <sys/kernel.h> 33 #include <sys/module.h> 34 #include <sys/lock.h> 35 #include <sys/mutex.h> 36 #include <sys/bio.h> 37 #include <sys/sysctl.h> 38 #include <sys/malloc.h> 39 #include <geom/geom.h> 40 #include <geom/nop/g_nop.h> 41 42 43 SYSCTL_DECL(_kern_geom); 44 SYSCTL_NODE(_kern_geom, OID_AUTO, nop, CTLFLAG_RW, 0, "GEOM_NOP stuff"); 45 static u_int g_nop_debug = 0; 46 SYSCTL_UINT(_kern_geom_nop, OID_AUTO, debug, CTLFLAG_RW, &g_nop_debug, 0, 47 "Debug level"); 48 49 static int g_nop_destroy(struct g_geom *gp, boolean_t force); 50 static int g_nop_destroy_geom(struct gctl_req *req, struct g_class *mp, 51 struct g_geom *gp); 52 static void g_nop_config(struct gctl_req *req, struct g_class *mp, 53 const char *verb); 54 static void g_nop_dumpconf(struct sbuf *sb, const char *indent, 55 struct g_geom *gp, struct g_consumer *cp, struct g_provider *pp); 56 57 struct g_class g_nop_class = { 58 .name = G_NOP_CLASS_NAME, 59 .version = G_VERSION, 60 .ctlreq = g_nop_config, 61 .destroy_geom = g_nop_destroy_geom 62 }; 63 64 65 static void 66 g_nop_orphan(struct g_consumer *cp) 67 { 68 69 g_topology_assert(); 70 g_nop_destroy(cp->geom, 1); 71 } 72 73 static void 74 g_nop_start(struct bio *bp) 75 { 76 struct g_nop_softc *sc; 77 struct g_geom *gp; 78 struct g_provider *pp; 79 struct bio *cbp; 80 u_int failprob = 0; 81 82 gp = bp->bio_to->geom; 83 sc = gp->softc; 84 G_NOP_LOGREQ(bp, "Request received."); 85 switch (bp->bio_cmd) { 86 case BIO_READ: 87 sc->sc_reads++; 88 sc->sc_readbytes += bp->bio_length; 89 failprob = sc->sc_rfailprob; 90 break; 91 case BIO_WRITE: 92 sc->sc_writes++; 93 sc->sc_wrotebytes += bp->bio_length; 94 failprob = sc->sc_wfailprob; 95 break; 96 } 97 if (failprob > 0) { 98 u_int rval; 99 100 rval = arc4random() % 100; 101 if (rval < failprob) { 102 G_NOP_LOGREQ(bp, "Returning error=%d.", sc->sc_error); 103 g_io_deliver(bp, sc->sc_error); 104 return; 105 } 106 } 107 cbp = g_clone_bio(bp); 108 if (cbp == NULL) { 109 g_io_deliver(bp, ENOMEM); 110 return; 111 } 112 cbp->bio_done = g_std_done; 113 cbp->bio_offset = bp->bio_offset + sc->sc_offset; 114 cbp->bio_data = bp->bio_data; 115 cbp->bio_length = bp->bio_length; 116 pp = LIST_FIRST(&gp->provider); 117 KASSERT(pp != NULL, ("NULL pp")); 118 cbp->bio_to = pp; 119 G_NOP_LOGREQ(cbp, "Sending request."); 120 g_io_request(cbp, LIST_FIRST(&gp->consumer)); 121 } 122 123 static int 124 g_nop_access(struct g_provider *pp, int dr, int dw, int de) 125 { 126 struct g_geom *gp; 127 struct g_consumer *cp; 128 int error; 129 130 gp = pp->geom; 131 cp = LIST_FIRST(&gp->consumer); 132 error = g_access(cp, dr, dw, de); 133 134 return (error); 135 } 136 137 static int 138 g_nop_create(struct gctl_req *req, struct g_class *mp, struct g_provider *pp, 139 int ioerror, u_int rfailprob, u_int wfailprob, off_t offset, off_t size, 140 u_int secsize) 141 { 142 struct g_nop_softc *sc; 143 struct g_geom *gp; 144 struct g_provider *newpp; 145 struct g_consumer *cp; 146 char name[64]; 147 int error; 148 149 g_topology_assert(); 150 151 gp = NULL; 152 newpp = NULL; 153 cp = NULL; 154 155 if ((offset % pp->sectorsize) != 0) { 156 gctl_error(req, "Invalid offset for provider %s.", pp->name); 157 return (EINVAL); 158 } 159 if ((size % pp->sectorsize) != 0) { 160 gctl_error(req, "Invalid size for provider %s.", pp->name); 161 return (EINVAL); 162 } 163 if (offset >= pp->mediasize) { 164 gctl_error(req, "Invalid offset for provider %s.", pp->name); 165 return (EINVAL); 166 } 167 if (size == 0) 168 size = pp->mediasize - offset; 169 if (offset + size > pp->mediasize) { 170 gctl_error(req, "Invalid size for provider %s.", pp->name); 171 return (EINVAL); 172 } 173 if (secsize == 0) 174 secsize = pp->sectorsize; 175 else if ((secsize % pp->sectorsize) != 0) { 176 gctl_error(req, "Invalid secsize for provider %s.", pp->name); 177 return (EINVAL); 178 } 179 if (secsize > MAXPHYS) { 180 gctl_error(req, "secsize is too big."); 181 return (EINVAL); 182 } 183 size -= size % secsize; 184 snprintf(name, sizeof(name), "%s%s", pp->name, G_NOP_SUFFIX); 185 LIST_FOREACH(gp, &mp->geom, geom) { 186 if (strcmp(gp->name, name) == 0) { 187 gctl_error(req, "Provider %s already exists.", name); 188 return (EEXIST); 189 } 190 } 191 gp = g_new_geomf(mp, name); 192 if (gp == NULL) { 193 gctl_error(req, "Cannot create geom %s.", name); 194 return (ENOMEM); 195 } 196 sc = g_malloc(sizeof(*sc), M_WAITOK); 197 sc->sc_offset = offset; 198 sc->sc_error = ioerror; 199 sc->sc_rfailprob = rfailprob; 200 sc->sc_wfailprob = wfailprob; 201 sc->sc_reads = 0; 202 sc->sc_writes = 0; 203 sc->sc_readbytes = 0; 204 sc->sc_wrotebytes = 0; 205 gp->softc = sc; 206 gp->start = g_nop_start; 207 gp->orphan = g_nop_orphan; 208 gp->access = g_nop_access; 209 gp->dumpconf = g_nop_dumpconf; 210 211 newpp = g_new_providerf(gp, gp->name); 212 if (newpp == NULL) { 213 gctl_error(req, "Cannot create provider %s.", name); 214 error = ENOMEM; 215 goto fail; 216 } 217 newpp->mediasize = size; 218 newpp->sectorsize = secsize; 219 220 cp = g_new_consumer(gp); 221 if (cp == NULL) { 222 gctl_error(req, "Cannot create consumer for %s.", gp->name); 223 error = ENOMEM; 224 goto fail; 225 } 226 error = g_attach(cp, pp); 227 if (error != 0) { 228 gctl_error(req, "Cannot attach to provider %s.", pp->name); 229 goto fail; 230 } 231 232 g_error_provider(newpp, 0); 233 G_NOP_DEBUG(0, "Device %s created.", gp->name); 234 return (0); 235 fail: 236 if (cp != NULL) { 237 if (cp->provider != NULL) 238 g_detach(cp); 239 g_destroy_consumer(cp); 240 } 241 if (newpp != NULL) 242 g_destroy_provider(newpp); 243 if (gp != NULL) { 244 if (gp->softc != NULL) 245 g_free(gp->softc); 246 g_destroy_geom(gp); 247 } 248 return (error); 249 } 250 251 static int 252 g_nop_destroy(struct g_geom *gp, boolean_t force) 253 { 254 struct g_provider *pp; 255 256 g_topology_assert(); 257 if (gp->softc == NULL) 258 return (ENXIO); 259 pp = LIST_FIRST(&gp->provider); 260 if (pp != NULL && (pp->acr != 0 || pp->acw != 0 || pp->ace != 0)) { 261 if (force) { 262 G_NOP_DEBUG(0, "Device %s is still open, so it " 263 "can't be definitely removed.", pp->name); 264 } else { 265 G_NOP_DEBUG(1, "Device %s is still open (r%dw%de%d).", 266 pp->name, pp->acr, pp->acw, pp->ace); 267 return (EBUSY); 268 } 269 } else { 270 G_NOP_DEBUG(0, "Device %s removed.", gp->name); 271 } 272 g_free(gp->softc); 273 gp->softc = NULL; 274 g_wither_geom(gp, ENXIO); 275 276 return (0); 277 } 278 279 static int 280 g_nop_destroy_geom(struct gctl_req *req, struct g_class *mp, struct g_geom *gp) 281 { 282 283 return (g_nop_destroy(gp, 0)); 284 } 285 286 static void 287 g_nop_ctl_create(struct gctl_req *req, struct g_class *mp) 288 { 289 struct g_provider *pp; 290 intmax_t *error, *rfailprob, *wfailprob, *offset, *secsize, *size; 291 const char *name; 292 char param[16]; 293 int i, *nargs; 294 295 g_topology_assert(); 296 297 nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); 298 if (nargs == NULL) { 299 gctl_error(req, "No '%s' argument", "nargs"); 300 return; 301 } 302 if (*nargs <= 0) { 303 gctl_error(req, "Missing device(s)."); 304 return; 305 } 306 error = gctl_get_paraml(req, "error", sizeof(*error)); 307 if (error == NULL) { 308 gctl_error(req, "No '%s' argument", "error"); 309 return; 310 } 311 rfailprob = gctl_get_paraml(req, "rfailprob", sizeof(*rfailprob)); 312 if (rfailprob == NULL) { 313 gctl_error(req, "No '%s' argument", "rfailprob"); 314 return; 315 } 316 if (*rfailprob < -1 || *rfailprob > 100) { 317 gctl_error(req, "Invalid '%s' argument", "rfailprob"); 318 return; 319 } 320 wfailprob = gctl_get_paraml(req, "wfailprob", sizeof(*wfailprob)); 321 if (wfailprob == NULL) { 322 gctl_error(req, "No '%s' argument", "wfailprob"); 323 return; 324 } 325 if (*wfailprob < -1 || *wfailprob > 100) { 326 gctl_error(req, "Invalid '%s' argument", "wfailprob"); 327 return; 328 } 329 offset = gctl_get_paraml(req, "offset", sizeof(*offset)); 330 if (offset == NULL) { 331 gctl_error(req, "No '%s' argument", "offset"); 332 return; 333 } 334 if (*offset < 0) { 335 gctl_error(req, "Invalid '%s' argument", "offset"); 336 return; 337 } 338 size = gctl_get_paraml(req, "size", sizeof(*size)); 339 if (size == NULL) { 340 gctl_error(req, "No '%s' argument", "size"); 341 return; 342 } 343 if (*size < 0) { 344 gctl_error(req, "Invalid '%s' argument", "size"); 345 return; 346 } 347 secsize = gctl_get_paraml(req, "secsize", sizeof(*secsize)); 348 if (secsize == NULL) { 349 gctl_error(req, "No '%s' argument", "secsize"); 350 return; 351 } 352 if (*secsize < 0) { 353 gctl_error(req, "Invalid '%s' argument", "secsize"); 354 return; 355 } 356 357 for (i = 0; i < *nargs; i++) { 358 snprintf(param, sizeof(param), "arg%d", i); 359 name = gctl_get_asciiparam(req, param); 360 if (name == NULL) { 361 gctl_error(req, "No 'arg%d' argument", i); 362 return; 363 } 364 if (strncmp(name, "/dev/", strlen("/dev/")) == 0) 365 name += strlen("/dev/"); 366 pp = g_provider_by_name(name); 367 if (pp == NULL) { 368 G_NOP_DEBUG(1, "Provider %s is invalid.", name); 369 gctl_error(req, "Provider %s is invalid.", name); 370 return; 371 } 372 if (g_nop_create(req, mp, pp, 373 *error == -1 ? EIO : (int)*error, 374 *rfailprob == -1 ? 0 : (u_int)*rfailprob, 375 *wfailprob == -1 ? 0 : (u_int)*wfailprob, 376 (off_t)*offset, (off_t)*size, (u_int)*secsize) != 0) { 377 return; 378 } 379 } 380 } 381 382 static void 383 g_nop_ctl_configure(struct gctl_req *req, struct g_class *mp) 384 { 385 struct g_nop_softc *sc; 386 struct g_provider *pp; 387 intmax_t *error, *rfailprob, *wfailprob; 388 const char *name; 389 char param[16]; 390 int i, *nargs; 391 392 g_topology_assert(); 393 394 nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); 395 if (nargs == NULL) { 396 gctl_error(req, "No '%s' argument", "nargs"); 397 return; 398 } 399 if (*nargs <= 0) { 400 gctl_error(req, "Missing device(s)."); 401 return; 402 } 403 error = gctl_get_paraml(req, "error", sizeof(*error)); 404 if (error == NULL) { 405 gctl_error(req, "No '%s' argument", "error"); 406 return; 407 } 408 rfailprob = gctl_get_paraml(req, "rfailprob", sizeof(*rfailprob)); 409 if (rfailprob == NULL) { 410 gctl_error(req, "No '%s' argument", "rfailprob"); 411 return; 412 } 413 if (*rfailprob < -1 || *rfailprob > 100) { 414 gctl_error(req, "Invalid '%s' argument", "rfailprob"); 415 return; 416 } 417 wfailprob = gctl_get_paraml(req, "wfailprob", sizeof(*wfailprob)); 418 if (wfailprob == NULL) { 419 gctl_error(req, "No '%s' argument", "wfailprob"); 420 return; 421 } 422 if (*wfailprob < -1 || *wfailprob > 100) { 423 gctl_error(req, "Invalid '%s' argument", "wfailprob"); 424 return; 425 } 426 427 for (i = 0; i < *nargs; i++) { 428 snprintf(param, sizeof(param), "arg%d", i); 429 name = gctl_get_asciiparam(req, param); 430 if (name == NULL) { 431 gctl_error(req, "No 'arg%d' argument", i); 432 return; 433 } 434 if (strncmp(name, "/dev/", strlen("/dev/")) == 0) 435 name += strlen("/dev/"); 436 pp = g_provider_by_name(name); 437 if (pp == NULL || pp->geom->class != mp) { 438 G_NOP_DEBUG(1, "Provider %s is invalid.", name); 439 gctl_error(req, "Provider %s is invalid.", name); 440 return; 441 } 442 sc = pp->geom->softc; 443 if (*error != -1) 444 sc->sc_error = (int)*error; 445 if (*rfailprob != -1) 446 sc->sc_rfailprob = (u_int)*rfailprob; 447 if (*wfailprob != -1) 448 sc->sc_wfailprob = (u_int)*wfailprob; 449 } 450 } 451 452 static struct g_geom * 453 g_nop_find_geom(struct g_class *mp, const char *name) 454 { 455 struct g_geom *gp; 456 457 LIST_FOREACH(gp, &mp->geom, geom) { 458 if (strcmp(gp->name, name) == 0) 459 return (gp); 460 } 461 return (NULL); 462 } 463 464 static void 465 g_nop_ctl_destroy(struct gctl_req *req, struct g_class *mp) 466 { 467 int *nargs, *force, error, i; 468 struct g_geom *gp; 469 const char *name; 470 char param[16]; 471 472 g_topology_assert(); 473 474 nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); 475 if (nargs == NULL) { 476 gctl_error(req, "No '%s' argument", "nargs"); 477 return; 478 } 479 if (*nargs <= 0) { 480 gctl_error(req, "Missing device(s)."); 481 return; 482 } 483 force = gctl_get_paraml(req, "force", sizeof(*force)); 484 if (force == NULL) { 485 gctl_error(req, "No 'force' argument"); 486 return; 487 } 488 489 for (i = 0; i < *nargs; i++) { 490 snprintf(param, sizeof(param), "arg%d", i); 491 name = gctl_get_asciiparam(req, param); 492 if (name == NULL) { 493 gctl_error(req, "No 'arg%d' argument", i); 494 return; 495 } 496 if (strncmp(name, "/dev/", strlen("/dev/")) == 0) 497 name += strlen("/dev/"); 498 gp = g_nop_find_geom(mp, name); 499 if (gp == NULL) { 500 G_NOP_DEBUG(1, "Device %s is invalid.", name); 501 gctl_error(req, "Device %s is invalid.", name); 502 return; 503 } 504 error = g_nop_destroy(gp, *force); 505 if (error != 0) { 506 gctl_error(req, "Cannot destroy device %s (error=%d).", 507 gp->name, error); 508 return; 509 } 510 } 511 } 512 513 static void 514 g_nop_ctl_reset(struct gctl_req *req, struct g_class *mp) 515 { 516 struct g_nop_softc *sc; 517 struct g_provider *pp; 518 const char *name; 519 char param[16]; 520 int i, *nargs; 521 522 g_topology_assert(); 523 524 nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); 525 if (nargs == NULL) { 526 gctl_error(req, "No '%s' argument", "nargs"); 527 return; 528 } 529 if (*nargs <= 0) { 530 gctl_error(req, "Missing device(s)."); 531 return; 532 } 533 534 for (i = 0; i < *nargs; i++) { 535 snprintf(param, sizeof(param), "arg%d", i); 536 name = gctl_get_asciiparam(req, param); 537 if (name == NULL) { 538 gctl_error(req, "No 'arg%d' argument", i); 539 return; 540 } 541 if (strncmp(name, "/dev/", strlen("/dev/")) == 0) 542 name += strlen("/dev/"); 543 pp = g_provider_by_name(name); 544 if (pp == NULL || pp->geom->class != mp) { 545 G_NOP_DEBUG(1, "Provider %s is invalid.", name); 546 gctl_error(req, "Provider %s is invalid.", name); 547 return; 548 } 549 sc = pp->geom->softc; 550 sc->sc_reads = 0; 551 sc->sc_writes = 0; 552 sc->sc_readbytes = 0; 553 sc->sc_wrotebytes = 0; 554 } 555 } 556 557 static void 558 g_nop_config(struct gctl_req *req, struct g_class *mp, const char *verb) 559 { 560 uint32_t *version; 561 562 g_topology_assert(); 563 564 version = gctl_get_paraml(req, "version", sizeof(*version)); 565 if (version == NULL) { 566 gctl_error(req, "No '%s' argument.", "version"); 567 return; 568 } 569 if (*version != G_NOP_VERSION) { 570 gctl_error(req, "Userland and kernel parts are out of sync."); 571 return; 572 } 573 574 if (strcmp(verb, "create") == 0) { 575 g_nop_ctl_create(req, mp); 576 return; 577 } else if (strcmp(verb, "configure") == 0) { 578 g_nop_ctl_configure(req, mp); 579 return; 580 } else if (strcmp(verb, "destroy") == 0) { 581 g_nop_ctl_destroy(req, mp); 582 return; 583 } else if (strcmp(verb, "reset") == 0) { 584 g_nop_ctl_reset(req, mp); 585 return; 586 } 587 588 gctl_error(req, "Unknown verb."); 589 } 590 591 static void 592 g_nop_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp, 593 struct g_consumer *cp, struct g_provider *pp) 594 { 595 struct g_nop_softc *sc; 596 597 if (pp != NULL || cp != NULL) 598 return; 599 sc = gp->softc; 600 sbuf_printf(sb, "%s<Offset>%jd</Offset>\n", indent, 601 (intmax_t)sc->sc_offset); 602 sbuf_printf(sb, "%s<ReadFailProb>%u</ReadFailProb>\n", indent, 603 sc->sc_rfailprob); 604 sbuf_printf(sb, "%s<WriteFailProb>%u</WriteFailProb>\n", indent, 605 sc->sc_wfailprob); 606 sbuf_printf(sb, "%s<Error>%d</Error>\n", indent, sc->sc_error); 607 sbuf_printf(sb, "%s<Reads>%ju</Reads>\n", indent, sc->sc_reads); 608 sbuf_printf(sb, "%s<Writes>%ju</Writes>\n", indent, sc->sc_writes); 609 sbuf_printf(sb, "%s<ReadBytes>%ju</ReadBytes>\n", indent, 610 sc->sc_readbytes); 611 sbuf_printf(sb, "%s<WroteBytes>%ju</WroteBytes>\n", indent, 612 sc->sc_wrotebytes); 613 } 614 615 DECLARE_GEOM_CLASS(g_nop_class, g_nop); 616