1 /*- 2 * Copyright (c) 2004 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 .ctlreq = g_nop_config, 60 .destroy_geom = g_nop_destroy_geom 61 }; 62 63 64 static void 65 g_nop_orphan(struct g_consumer *cp) 66 { 67 68 g_topology_assert(); 69 g_nop_destroy(cp->geom, 1); 70 } 71 72 static void 73 g_nop_start(struct bio *bp) 74 { 75 struct g_geom *gp; 76 struct g_provider *pp; 77 struct bio *cbp; 78 79 gp = bp->bio_to->geom; 80 G_NOP_LOGREQ(bp, "Request received."); 81 cbp = g_clone_bio(bp); 82 if (cbp == NULL) { 83 g_io_deliver(bp, ENOMEM); 84 return; 85 } 86 pp = LIST_FIRST(&gp->provider); 87 KASSERT(pp != NULL, ("NULL pp")); 88 if (pp->index > 0) { 89 u_int rval; 90 91 rval = arc4random() % 100; 92 if (rval < pp->index) { 93 g_io_deliver(bp, EIO); 94 return; 95 } 96 } 97 cbp->bio_done = g_std_done; 98 cbp->bio_offset = bp->bio_offset; 99 cbp->bio_data = bp->bio_data; 100 cbp->bio_length = bp->bio_length; 101 cbp->bio_to = LIST_FIRST(&gp->provider); 102 G_NOP_LOGREQ(cbp, "Sending request."); 103 g_io_request(cbp, LIST_FIRST(&gp->consumer)); 104 } 105 106 static int 107 g_nop_access(struct g_provider *pp, int dr, int dw, int de) 108 { 109 struct g_geom *gp; 110 struct g_consumer *cp; 111 int error; 112 113 gp = pp->geom; 114 cp = LIST_FIRST(&gp->consumer); 115 error = g_access(cp, dr, dw, de); 116 117 return (error); 118 } 119 120 static int 121 g_nop_create(struct gctl_req *req, struct g_class *mp, struct g_provider *pp, 122 u_int failprob) 123 { 124 struct g_geom *gp; 125 struct g_provider *newpp; 126 struct g_consumer *cp; 127 char name[64]; 128 int error; 129 130 g_topology_assert(); 131 132 gp = NULL; 133 newpp = NULL; 134 cp = NULL; 135 136 snprintf(name, sizeof(name), "%s%s", pp->name, G_NOP_SUFFIX); 137 LIST_FOREACH(gp, &mp->geom, geom) { 138 if (strcmp(gp->name, name) == 0) { 139 gctl_error(req, "Provider %s already exists.", name); 140 return (EEXIST); 141 } 142 } 143 gp = g_new_geomf(mp, name); 144 if (gp == NULL) { 145 gctl_error(req, "Cannot create geom %s.", name); 146 return (ENOMEM); 147 } 148 gp->softc = NULL; 149 gp->start = g_nop_start; 150 gp->spoiled = g_nop_orphan; 151 gp->orphan = g_nop_orphan; 152 gp->access = g_nop_access; 153 gp->dumpconf = g_nop_dumpconf; 154 155 newpp = g_new_providerf(gp, gp->name); 156 if (newpp == NULL) { 157 gctl_error(req, "Cannot create provider %s.", name); 158 error = ENOMEM; 159 goto fail; 160 } 161 newpp->mediasize = pp->mediasize; 162 newpp->sectorsize = pp->sectorsize; 163 newpp->index = failprob; 164 165 cp = g_new_consumer(gp); 166 if (cp == NULL) { 167 gctl_error(req, "Cannot create consumer for %s.", gp->name); 168 error = ENOMEM; 169 goto fail; 170 } 171 error = g_attach(cp, pp); 172 if (error != 0) { 173 gctl_error(req, "Cannot attach to provider %s.", pp->name); 174 goto fail; 175 } 176 177 g_error_provider(newpp, 0); 178 G_NOP_DEBUG(0, "Device %s created.", gp->name); 179 return (0); 180 fail: 181 if (cp != NULL) { 182 if (cp->provider != NULL) 183 g_detach(cp); 184 g_destroy_consumer(cp); 185 } 186 if (newpp != NULL) 187 g_destroy_provider(pp); 188 if (gp != NULL) 189 g_destroy_geom(gp); 190 return (error); 191 } 192 193 static int 194 g_nop_destroy(struct g_geom *gp, boolean_t force) 195 { 196 struct g_provider *pp; 197 198 g_topology_assert(); 199 pp = LIST_FIRST(&gp->provider); 200 if (pp != NULL && (pp->acr != 0 || pp->acw != 0 || pp->ace != 0)) { 201 if (force) { 202 G_NOP_DEBUG(0, "Device %s is still open, so it " 203 "can't be definitely removed.", pp->name); 204 } else { 205 G_NOP_DEBUG(1, "Device %s is still open (r%dw%de%d).", 206 pp->name, pp->acr, pp->acw, pp->ace); 207 return (EBUSY); 208 } 209 } else { 210 G_NOP_DEBUG(0, "Device %s removed.", gp->name); 211 } 212 g_wither_geom(gp, ENXIO); 213 214 return (0); 215 } 216 217 static int 218 g_nop_destroy_geom(struct gctl_req *req, struct g_class *mp, struct g_geom *gp) 219 { 220 221 return (g_nop_destroy(gp, 0)); 222 } 223 224 static void 225 g_nop_ctl_create(struct gctl_req *req, struct g_class *mp) 226 { 227 struct g_provider *pp; 228 intmax_t *failprob; 229 const char *name; 230 char param[16]; 231 int i, *nargs; 232 233 g_topology_assert(); 234 235 nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); 236 if (nargs == NULL) { 237 gctl_error(req, "No '%s' argument", "nargs"); 238 return; 239 } 240 if (*nargs <= 0) { 241 gctl_error(req, "Missing device(s)."); 242 return; 243 } 244 failprob = gctl_get_paraml(req, "failprob", sizeof(*failprob)); 245 if (failprob == NULL) { 246 gctl_error(req, "No '%s' argument", "failprob"); 247 return; 248 } 249 if (*failprob < 0 || *failprob > 100) { 250 gctl_error(req, "Invalid '%s' argument", "failprob"); 251 return; 252 } 253 254 for (i = 0; i < *nargs; i++) { 255 snprintf(param, sizeof(param), "arg%d", i); 256 name = gctl_get_asciiparam(req, param); 257 if (name == NULL) { 258 gctl_error(req, "No 'arg%d' argument", i); 259 return; 260 } 261 if (strncmp(name, "/dev/", strlen("/dev/")) == 0) 262 name += strlen("/dev/"); 263 pp = g_provider_by_name(name); 264 if (pp == NULL) { 265 G_NOP_DEBUG(1, "Provider %s is invalid.", name); 266 gctl_error(req, "Provider %s is invalid.", name); 267 return; 268 } 269 if (g_nop_create(req, mp, pp, (u_int)*failprob) != 0) 270 return; 271 } 272 } 273 274 static void 275 g_nop_ctl_configure(struct gctl_req *req, struct g_class *mp) 276 { 277 struct g_provider *pp; 278 intmax_t *failprob; 279 const char *name; 280 char param[16]; 281 int i, *nargs; 282 283 g_topology_assert(); 284 285 nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); 286 if (nargs == NULL) { 287 gctl_error(req, "No '%s' argument", "nargs"); 288 return; 289 } 290 if (*nargs <= 0) { 291 gctl_error(req, "Missing device(s)."); 292 return; 293 } 294 failprob = gctl_get_paraml(req, "failprob", sizeof(*failprob)); 295 if (failprob == NULL) { 296 gctl_error(req, "No '%s' argument", "failprob"); 297 return; 298 } 299 if (*failprob < 0 || *failprob > 100) { 300 gctl_error(req, "Invalid '%s' argument", "failprob"); 301 return; 302 } 303 304 for (i = 0; i < *nargs; i++) { 305 snprintf(param, sizeof(param), "arg%d", i); 306 name = gctl_get_asciiparam(req, param); 307 if (name == NULL) { 308 gctl_error(req, "No 'arg%d' argument", i); 309 return; 310 } 311 if (strncmp(name, "/dev/", strlen("/dev/")) == 0) 312 name += strlen("/dev/"); 313 pp = g_provider_by_name(name); 314 if (pp == NULL || pp->geom->class != mp) { 315 G_NOP_DEBUG(1, "Provider %s is invalid.", name); 316 gctl_error(req, "Provider %s is invalid.", name); 317 return; 318 } 319 pp->index = (u_int)*failprob; 320 } 321 } 322 323 static struct g_geom * 324 g_nop_find_geom(struct g_class *mp, const char *name) 325 { 326 struct g_geom *gp; 327 328 LIST_FOREACH(gp, &mp->geom, geom) { 329 if (strcmp(gp->name, name) == 0) 330 return (gp); 331 } 332 return (NULL); 333 } 334 335 static void 336 g_nop_ctl_destroy(struct gctl_req *req, struct g_class *mp) 337 { 338 int *nargs, *force, error, i; 339 struct g_geom *gp; 340 const char *name; 341 char param[16]; 342 343 g_topology_assert(); 344 345 nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); 346 if (nargs == NULL) { 347 gctl_error(req, "No '%s' argument", "nargs"); 348 return; 349 } 350 if (*nargs <= 0) { 351 gctl_error(req, "Missing device(s)."); 352 return; 353 } 354 force = gctl_get_paraml(req, "force", sizeof(*force)); 355 if (force == NULL) { 356 gctl_error(req, "No 'force' argument"); 357 return; 358 } 359 360 for (i = 0; i < *nargs; i++) { 361 snprintf(param, sizeof(param), "arg%d", i); 362 name = gctl_get_asciiparam(req, param); 363 if (name == NULL) { 364 gctl_error(req, "No 'arg%d' argument", i); 365 return; 366 } 367 if (strncmp(name, "/dev/", strlen("/dev/")) == 0) 368 name += strlen("/dev/"); 369 gp = g_nop_find_geom(mp, name); 370 if (gp == NULL) { 371 G_NOP_DEBUG(1, "Device %s is invalid.", name); 372 gctl_error(req, "Device %s is invalid.", name); 373 return; 374 } 375 error = g_nop_destroy(gp, *force); 376 if (error != 0) { 377 gctl_error(req, "Cannot destroy device %s (error=%d).", 378 gp->name, error); 379 return; 380 } 381 } 382 } 383 384 static void 385 g_nop_config(struct gctl_req *req, struct g_class *mp, const char *verb) 386 { 387 uint32_t *version; 388 389 g_topology_assert(); 390 391 version = gctl_get_paraml(req, "version", sizeof(*version)); 392 if (version == NULL) { 393 gctl_error(req, "No '%s' argument.", "version"); 394 return; 395 } 396 if (*version != G_NOP_VERSION) { 397 gctl_error(req, "Userland and kernel parts are out of sync."); 398 return; 399 } 400 401 if (strcmp(verb, "create") == 0) { 402 g_nop_ctl_create(req, mp); 403 return; 404 } else if (strcmp(verb, "configure") == 0) { 405 g_nop_ctl_configure(req, mp); 406 return; 407 } else if (strcmp(verb, "destroy") == 0) { 408 g_nop_ctl_destroy(req, mp); 409 return; 410 } 411 412 gctl_error(req, "Unknown verb."); 413 } 414 415 static void 416 g_nop_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp, 417 struct g_consumer *cp, struct g_provider *pp) 418 { 419 420 if (pp != NULL) { 421 sbuf_printf(sb, "%s<failprob>%u</failprob>\n", indent, 422 pp->index); 423 } 424 } 425 426 DECLARE_GEOM_CLASS(g_nop_class, g_nop); 427