1 /*- 2 * Copyright (c) 2002 Poul-Henning Kamp 3 * Copyright (c) 2002 Networks Associates Technology, Inc. 4 * All rights reserved. 5 * 6 * This software was developed for the FreeBSD Project by Poul-Henning Kamp 7 * and NAI Labs, the Security Research Division of Network Associates, Inc. 8 * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the 9 * DARPA CHATS research program. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 3. The names of the authors may not be used to endorse or promote 20 * products derived from this software without specific prior written 21 * permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36 #include <sys/cdefs.h> 37 __FBSDID("$FreeBSD$"); 38 39 #include <sys/param.h> 40 #include <sys/systm.h> 41 #include <sys/kernel.h> 42 #include <sys/malloc.h> 43 #include <sys/bio.h> 44 #include <sys/sysctl.h> 45 #include <sys/proc.h> 46 #include <sys/lock.h> 47 #include <sys/mutex.h> 48 #include <sys/errno.h> 49 #include <sys/sbuf.h> 50 #include <geom/geom.h> 51 #include <geom/geom_slice.h> 52 #include <machine/stdarg.h> 53 54 static g_access_t g_slice_access; 55 static g_start_t g_slice_start; 56 57 static struct g_slicer * 58 g_slice_alloc(unsigned nslice, unsigned scsize) 59 { 60 struct g_slicer *gsp; 61 62 gsp = g_malloc(sizeof *gsp, M_WAITOK | M_ZERO); 63 if (scsize > 0) 64 gsp->softc = g_malloc(scsize, M_WAITOK | M_ZERO); 65 else 66 gsp->softc = NULL; 67 gsp->slices = g_malloc(nslice * sizeof(struct g_slice), 68 M_WAITOK | M_ZERO); 69 gsp->nslice = nslice; 70 return (gsp); 71 } 72 73 static void 74 g_slice_free(struct g_geom *gp) 75 { 76 struct g_slicer *gsp; 77 78 gsp = gp->softc; 79 gp->softc = NULL; 80 81 /* 82 * We can get multiple spoiled events before wither-washer 83 * detaches our consumer, so this can get called multiple 84 * times. 85 */ 86 if (gsp == NULL) 87 return; 88 g_free(gsp->slices); 89 if (gsp->hotspot != NULL) 90 g_free(gsp->hotspot); 91 if (gsp->softc != NULL) 92 g_free(gsp->softc); 93 g_free(gsp); 94 } 95 96 static int 97 g_slice_access(struct g_provider *pp, int dr, int dw, int de) 98 { 99 int error; 100 u_int u; 101 struct g_geom *gp; 102 struct g_consumer *cp; 103 struct g_provider *pp2; 104 struct g_slicer *gsp; 105 struct g_slice *gsl, *gsl2; 106 107 gp = pp->geom; 108 cp = LIST_FIRST(&gp->consumer); 109 KASSERT (cp != NULL, ("g_slice_access but no consumer")); 110 gsp = gp->softc; 111 if (dr > 0 || dw > 0 || de > 0) { 112 gsl = &gsp->slices[pp->index]; 113 for (u = 0; u < gsp->nslice; u++) { 114 gsl2 = &gsp->slices[u]; 115 if (gsl2->length == 0) 116 continue; 117 if (u == pp->index) 118 continue; 119 if (gsl->offset + gsl->length <= gsl2->offset) 120 continue; 121 if (gsl2->offset + gsl2->length <= gsl->offset) 122 continue; 123 /* overlap */ 124 pp2 = gsl2->provider; 125 if ((pp->acw + dw) > 0 && pp2->ace > 0) 126 return (EPERM); 127 if ((pp->ace + de) > 0 && pp2->acw > 0) 128 return (EPERM); 129 } 130 } 131 /* On first open, grab an extra "exclusive" bit */ 132 if (cp->acr == 0 && cp->acw == 0 && cp->ace == 0) 133 de++; 134 /* ... and let go of it on last close */ 135 if ((cp->acr + dr) == 0 && (cp->acw + dw) == 0 && (cp->ace + de) == 1) 136 de--; 137 error = g_access(cp, dr, dw, de); 138 139 /* 140 * Free the softc if all providers have been closed and this geom 141 * is being removed. 142 */ 143 if (error == 0 && (gp->flags & G_GEOM_WITHER) != 0 && 144 (cp->acr + cp->acw + cp->ace) == 0) 145 g_slice_free(gp); 146 147 return (error); 148 } 149 150 /* 151 * XXX: It should be possible to specify here if we should finish all of the 152 * XXX: bio, or only the non-hot bits. This would get messy if there were 153 * XXX: two hot spots in the same bio, so for now we simply finish off the 154 * XXX: entire bio. Modifying hot data on the way to disk is frowned on 155 * XXX: so making that considerably harder is not a bad idea anyway. 156 */ 157 void 158 g_slice_finish_hot(struct bio *bp) 159 { 160 struct bio *bp2; 161 struct g_geom *gp; 162 struct g_consumer *cp; 163 struct g_slicer *gsp; 164 struct g_slice *gsl; 165 int idx; 166 167 KASSERT(bp->bio_to != NULL, 168 ("NULL bio_to in g_slice_finish_hot(%p)", bp)); 169 KASSERT(bp->bio_from != NULL, 170 ("NULL bio_from in g_slice_finish_hot(%p)", bp)); 171 gp = bp->bio_to->geom; 172 gsp = gp->softc; 173 cp = LIST_FIRST(&gp->consumer); 174 KASSERT(cp != NULL, ("NULL consumer in g_slice_finish_hot(%p)", bp)); 175 idx = bp->bio_to->index; 176 gsl = &gsp->slices[idx]; 177 178 bp2 = g_clone_bio(bp); 179 if (bp2 == NULL) { 180 g_io_deliver(bp, ENOMEM); 181 return; 182 } 183 if (bp2->bio_offset + bp2->bio_length > gsl->length) 184 bp2->bio_length = gsl->length - bp2->bio_offset; 185 bp2->bio_done = g_std_done; 186 bp2->bio_offset += gsl->offset; 187 g_io_request(bp2, cp); 188 return; 189 } 190 191 static void 192 g_slice_done(struct bio *bp) 193 { 194 195 KASSERT(bp->bio_cmd == BIO_GETATTR && 196 strcmp(bp->bio_attribute, "GEOM::ident") == 0, 197 ("bio_cmd=0x%x bio_attribute=%s", bp->bio_cmd, bp->bio_attribute)); 198 199 if (bp->bio_error == 0 && bp->bio_data[0] != '\0') { 200 char idx[8]; 201 202 /* Add index to the ident received. */ 203 snprintf(idx, sizeof(idx), "s%d", 204 bp->bio_parent->bio_to->index); 205 if (strlcat(bp->bio_data, idx, bp->bio_length) >= 206 bp->bio_length) { 207 bp->bio_error = EFAULT; 208 } 209 } 210 g_std_done(bp); 211 } 212 213 static void 214 g_slice_start(struct bio *bp) 215 { 216 struct bio *bp2; 217 struct g_provider *pp; 218 struct g_geom *gp; 219 struct g_consumer *cp; 220 struct g_slicer *gsp; 221 struct g_slice *gsl; 222 struct g_slice_hot *ghp; 223 int idx, error; 224 u_int m_index; 225 off_t t; 226 227 pp = bp->bio_to; 228 gp = pp->geom; 229 gsp = gp->softc; 230 cp = LIST_FIRST(&gp->consumer); 231 idx = pp->index; 232 gsl = &gsp->slices[idx]; 233 switch(bp->bio_cmd) { 234 case BIO_READ: 235 case BIO_WRITE: 236 case BIO_DELETE: 237 if (bp->bio_offset > gsl->length) { 238 g_io_deliver(bp, EINVAL); /* XXX: EWHAT ? */ 239 return; 240 } 241 /* 242 * Check if we collide with any hot spaces, and call the 243 * method once if so. 244 */ 245 t = bp->bio_offset + gsl->offset; 246 for (m_index = 0; m_index < gsp->nhotspot; m_index++) { 247 ghp = &gsp->hotspot[m_index]; 248 if (t >= ghp->offset + ghp->length) 249 continue; 250 if (t + bp->bio_length <= ghp->offset) 251 continue; 252 switch(bp->bio_cmd) { 253 case BIO_READ: idx = ghp->ract; break; 254 case BIO_WRITE: idx = ghp->wact; break; 255 case BIO_DELETE: idx = ghp->dact; break; 256 } 257 switch(idx) { 258 case G_SLICE_HOT_ALLOW: 259 /* Fall out and continue normal processing */ 260 continue; 261 case G_SLICE_HOT_DENY: 262 g_io_deliver(bp, EROFS); 263 return; 264 case G_SLICE_HOT_START: 265 error = gsp->start(bp); 266 if (error && error != EJUSTRETURN) 267 g_io_deliver(bp, error); 268 return; 269 case G_SLICE_HOT_CALL: 270 error = g_post_event(gsp->hot, bp, M_NOWAIT, 271 gp, NULL); 272 if (error) 273 g_io_deliver(bp, error); 274 return; 275 } 276 break; 277 } 278 bp2 = g_clone_bio(bp); 279 if (bp2 == NULL) { 280 g_io_deliver(bp, ENOMEM); 281 return; 282 } 283 if (bp2->bio_offset + bp2->bio_length > gsl->length) 284 bp2->bio_length = gsl->length - bp2->bio_offset; 285 bp2->bio_done = g_std_done; 286 bp2->bio_offset += gsl->offset; 287 g_io_request(bp2, cp); 288 return; 289 case BIO_GETATTR: 290 /* Give the real method a chance to override */ 291 if (gsp->start != NULL && gsp->start(bp)) 292 return; 293 if (!strcmp("GEOM::ident", bp->bio_attribute)) { 294 bp2 = g_clone_bio(bp); 295 if (bp2 == NULL) { 296 g_io_deliver(bp, ENOMEM); 297 return; 298 } 299 bp2->bio_done = g_slice_done; 300 g_io_request(bp2, cp); 301 return; 302 } 303 if (!strcmp("GEOM::kerneldump", bp->bio_attribute)) { 304 struct g_kerneldump *gkd; 305 306 gkd = (struct g_kerneldump *)bp->bio_data; 307 gkd->offset += gsp->slices[idx].offset; 308 if (gkd->length > gsp->slices[idx].length) 309 gkd->length = gsp->slices[idx].length; 310 /* now, pass it on downwards... */ 311 } 312 /* FALLTHROUGH */ 313 case BIO_FLUSH: 314 bp2 = g_clone_bio(bp); 315 if (bp2 == NULL) { 316 g_io_deliver(bp, ENOMEM); 317 return; 318 } 319 bp2->bio_done = g_std_done; 320 g_io_request(bp2, cp); 321 break; 322 default: 323 g_io_deliver(bp, EOPNOTSUPP); 324 return; 325 } 326 } 327 328 void 329 g_slice_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp, struct g_consumer *cp, struct g_provider *pp) 330 { 331 struct g_slicer *gsp; 332 333 gsp = gp->softc; 334 if (indent == NULL) { 335 sbuf_printf(sb, " i %u", pp->index); 336 sbuf_printf(sb, " o %ju", 337 (uintmax_t)gsp->slices[pp->index].offset); 338 return; 339 } 340 if (pp != NULL) { 341 sbuf_printf(sb, "%s<index>%u</index>\n", indent, pp->index); 342 sbuf_printf(sb, "%s<length>%ju</length>\n", 343 indent, (uintmax_t)gsp->slices[pp->index].length); 344 sbuf_printf(sb, "%s<seclength>%ju</seclength>\n", indent, 345 (uintmax_t)gsp->slices[pp->index].length / 512); 346 sbuf_printf(sb, "%s<offset>%ju</offset>\n", indent, 347 (uintmax_t)gsp->slices[pp->index].offset); 348 sbuf_printf(sb, "%s<secoffset>%ju</secoffset>\n", indent, 349 (uintmax_t)gsp->slices[pp->index].offset / 512); 350 } 351 } 352 353 int 354 g_slice_config(struct g_geom *gp, u_int idx, int how, off_t offset, off_t length, u_int sectorsize, const char *fmt, ...) 355 { 356 struct g_provider *pp, *pp2; 357 struct g_slicer *gsp; 358 struct g_slice *gsl; 359 va_list ap; 360 struct sbuf *sb; 361 int acc; 362 363 g_trace(G_T_TOPOLOGY, "g_slice_config(%s, %d, %d)", 364 gp->name, idx, how); 365 g_topology_assert(); 366 gsp = gp->softc; 367 if (idx >= gsp->nslice) 368 return(EINVAL); 369 gsl = &gsp->slices[idx]; 370 pp = gsl->provider; 371 if (pp != NULL) 372 acc = pp->acr + pp->acw + pp->ace; 373 else 374 acc = 0; 375 if (acc != 0 && how != G_SLICE_CONFIG_FORCE) { 376 if (length < gsl->length) 377 return(EBUSY); 378 if (offset != gsl->offset) 379 return(EBUSY); 380 } 381 /* XXX: check offset + length <= MEDIASIZE */ 382 if (how == G_SLICE_CONFIG_CHECK) 383 return (0); 384 gsl->length = length; 385 gsl->offset = offset; 386 gsl->sectorsize = sectorsize; 387 if (length == 0) { 388 if (pp == NULL) 389 return (0); 390 if (bootverbose) 391 printf("GEOM: Deconfigure %s\n", pp->name); 392 g_wither_provider(pp, ENXIO); 393 gsl->provider = NULL; 394 gsp->nprovider--; 395 return (0); 396 } 397 if (pp != NULL) { 398 if (bootverbose) 399 printf("GEOM: Reconfigure %s, start %jd length %jd end %jd\n", 400 pp->name, (intmax_t)offset, (intmax_t)length, 401 (intmax_t)(offset + length - 1)); 402 g_resize_provider(pp, gsl->length); 403 return (0); 404 } 405 sb = sbuf_new_auto(); 406 va_start(ap, fmt); 407 sbuf_vprintf(sb, fmt, ap); 408 va_end(ap); 409 sbuf_finish(sb); 410 pp = g_new_providerf(gp, "%s", sbuf_data(sb)); 411 pp2 = LIST_FIRST(&gp->consumer)->provider; 412 pp->stripesize = pp2->stripesize; 413 pp->stripeoffset = pp2->stripeoffset + offset; 414 if (pp->stripesize > 0) 415 pp->stripeoffset %= pp->stripesize; 416 if (gsp->nhotspot == 0) { 417 pp->flags |= pp2->flags & G_PF_ACCEPT_UNMAPPED; 418 pp->flags |= G_PF_DIRECT_SEND | G_PF_DIRECT_RECEIVE; 419 } 420 if (0 && bootverbose) 421 printf("GEOM: Configure %s, start %jd length %jd end %jd\n", 422 pp->name, (intmax_t)offset, (intmax_t)length, 423 (intmax_t)(offset + length - 1)); 424 pp->index = idx; 425 pp->mediasize = gsl->length; 426 pp->sectorsize = gsl->sectorsize; 427 gsl->provider = pp; 428 gsp->nprovider++; 429 g_error_provider(pp, 0); 430 sbuf_delete(sb); 431 return(0); 432 } 433 434 /* 435 * Configure "hotspots". A hotspot is a piece of the parent device which 436 * this particular slicer cares about for some reason. Typically because 437 * it contains meta-data used to configure the slicer. 438 * A hotspot is identified by its index number. The offset and length are 439 * relative to the parent device, and the three "?act" fields specify 440 * what action to take on BIO_READ, BIO_DELETE and BIO_WRITE. 441 * 442 * XXX: There may be a race relative to g_slice_start() here, if an existing 443 * XXX: hotspot is changed wile I/O is happening. Should this become a problem 444 * XXX: we can protect the hotspot stuff with a mutex. 445 */ 446 447 int 448 g_slice_conf_hot(struct g_geom *gp, u_int idx, off_t offset, off_t length, int ract, int dact, int wact) 449 { 450 struct g_slicer *gsp; 451 struct g_slice_hot *gsl, *gsl2; 452 struct g_consumer *cp; 453 struct g_provider *pp; 454 455 g_trace(G_T_TOPOLOGY, "g_slice_conf_hot(%s, idx: %d, off: %jd, len: %jd)", 456 gp->name, idx, (intmax_t)offset, (intmax_t)length); 457 g_topology_assert(); 458 gsp = gp->softc; 459 /* Deny unmapped I/O and direct dispatch if hotspots are used. */ 460 if (gsp->nhotspot == 0) { 461 LIST_FOREACH(pp, &gp->provider, provider) 462 pp->flags &= ~(G_PF_ACCEPT_UNMAPPED | 463 G_PF_DIRECT_SEND | G_PF_DIRECT_RECEIVE); 464 LIST_FOREACH(cp, &gp->consumer, consumer) 465 cp->flags &= ~(G_CF_DIRECT_SEND | G_CF_DIRECT_RECEIVE); 466 } 467 gsl = gsp->hotspot; 468 if(idx >= gsp->nhotspot) { 469 gsl2 = g_malloc((idx + 1) * sizeof *gsl2, M_WAITOK | M_ZERO); 470 if (gsp->hotspot != NULL) 471 bcopy(gsp->hotspot, gsl2, gsp->nhotspot * sizeof *gsl2); 472 gsp->hotspot = gsl2; 473 if (gsp->hotspot != NULL) 474 g_free(gsl); 475 gsl = gsl2; 476 gsp->nhotspot = idx + 1; 477 } 478 gsl[idx].offset = offset; 479 gsl[idx].length = length; 480 KASSERT(!((ract | dact | wact) & G_SLICE_HOT_START) 481 || gsp->start != NULL, ("G_SLICE_HOT_START but no slice->start")); 482 /* XXX: check that we _have_ a start function if HOT_START specified */ 483 gsl[idx].ract = ract; 484 gsl[idx].dact = dact; 485 gsl[idx].wact = wact; 486 return (0); 487 } 488 489 void 490 g_slice_orphan(struct g_consumer *cp) 491 { 492 struct g_geom *gp; 493 494 g_topology_assert(); 495 gp = cp->geom; 496 g_trace(G_T_TOPOLOGY, "%s(%p/%s)", __func__, cp, gp->name); 497 g_wither_geom(gp, ENXIO); 498 499 /* 500 * We can safely free the softc now if there are no accesses, 501 * otherwise g_slice_access() will do that after the last close. 502 */ 503 if ((cp->acr + cp->acw + cp->ace) == 0) 504 g_slice_free(gp); 505 } 506 507 void 508 g_slice_spoiled(struct g_consumer *cp) 509 { 510 511 g_trace(G_T_TOPOLOGY, "%s(%p/%s)", __func__, cp, cp->geom->name); 512 cp->flags |= G_CF_ORPHAN; 513 g_slice_orphan(cp); 514 } 515 516 int 517 g_slice_destroy_geom(struct gctl_req *req, struct g_class *mp, struct g_geom *gp) 518 { 519 520 g_slice_spoiled(LIST_FIRST(&gp->consumer)); 521 return (0); 522 } 523 524 struct g_geom * 525 g_slice_new(struct g_class *mp, u_int slices, struct g_provider *pp, struct g_consumer **cpp, void *extrap, int extra, g_slice_start_t *start) 526 { 527 struct g_geom *gp; 528 struct g_slicer *gsp; 529 struct g_consumer *cp; 530 void **vp; 531 int error; 532 533 g_topology_assert(); 534 vp = (void **)extrap; 535 gp = g_new_geomf(mp, "%s", pp->name); 536 gsp = g_slice_alloc(slices, extra); 537 gsp->start = start; 538 gp->softc = gsp; 539 gp->start = g_slice_start; 540 gp->access = g_slice_access; 541 gp->orphan = g_slice_orphan; 542 gp->spoiled = g_slice_spoiled; 543 if (gp->dumpconf == NULL) 544 gp->dumpconf = g_slice_dumpconf; 545 if (gp->class->destroy_geom == NULL) 546 gp->class->destroy_geom = g_slice_destroy_geom; 547 cp = g_new_consumer(gp); 548 cp->flags |= G_CF_DIRECT_SEND | G_CF_DIRECT_RECEIVE; 549 error = g_attach(cp, pp); 550 if (error == 0) 551 error = g_access(cp, 1, 0, 0); 552 if (error) { 553 g_wither_geom(gp, ENXIO); 554 return (NULL); 555 } 556 if (extrap != NULL) 557 *vp = gsp->softc; 558 *cpp = cp; 559 return (gp); 560 } 561