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