geom_slice.c revision 114493
1184610Salfred/*- 2184610Salfred * Copyright (c) 2002 Poul-Henning Kamp 3184610Salfred * Copyright (c) 2002 Networks Associates Technology, Inc. 4184610Salfred * All rights reserved. 5184610Salfred * 6184610Salfred * This software was developed for the FreeBSD Project by Poul-Henning Kamp 7184610Salfred * and NAI Labs, the Security Research Division of Network Associates, Inc. 8184610Salfred * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the 9184610Salfred * DARPA CHATS research program. 10184610Salfred * 11184610Salfred * Redistribution and use in source and binary forms, with or without 12184610Salfred * modification, are permitted provided that the following conditions 13184610Salfred * are met: 14184610Salfred * 1. Redistributions of source code must retain the above copyright 15184610Salfred * notice, this list of conditions and the following disclaimer. 16184610Salfred * 2. Redistributions in binary form must reproduce the above copyright 17184610Salfred * notice, this list of conditions and the following disclaimer in the 18184610Salfred * documentation and/or other materials provided with the distribution. 19184610Salfred * 3. The names of the authors may not be used to endorse or promote 20184610Salfred * products derived from this software without specific prior written 21184610Salfred * permission. 22184610Salfred * 23184610Salfred * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 24184610Salfred * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25184610Salfred * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26184610Salfred * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 27184610Salfred * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28184610Salfred * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29184610Salfred * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30184610Salfred * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31184610Salfred * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32184610Salfred * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33184610Salfred * SUCH DAMAGE. 34184610Salfred * 35184610Salfred * $FreeBSD: head/sys/geom/geom_slice.c 114493 2003-05-02 05:33:27Z phk $ 36184610Salfred */ 37184610Salfred 38184610Salfred 39184610Salfred#include <sys/param.h> 40184610Salfred#include <sys/systm.h> 41184610Salfred#include <sys/kernel.h> 42184610Salfred#include <sys/malloc.h> 43184610Salfred#include <sys/bio.h> 44184610Salfred#include <sys/sysctl.h> 45184610Salfred#include <sys/proc.h> 46184610Salfred#include <sys/kthread.h> 47184610Salfred#include <sys/lock.h> 48184610Salfred#include <sys/mutex.h> 49184610Salfred#include <sys/errno.h> 50184610Salfred#include <sys/sbuf.h> 51184610Salfred#include <geom/geom.h> 52184610Salfred#include <geom/geom_slice.h> 53184610Salfred#include <machine/stdarg.h> 54184610Salfred 55184610Salfredstatic g_orphan_t g_slice_orphan; 56184610Salfredstatic g_access_t g_slice_access; 57184610Salfredstatic g_start_t g_slice_start; 58184610Salfred 59184610Salfredstatic struct g_slicer * 60184610Salfredg_slice_alloc(unsigned nslice, unsigned scsize) 61184610Salfred{ 62184610Salfred struct g_slicer *gsp; 63184610Salfred 64184610Salfred gsp = g_malloc(sizeof *gsp, M_WAITOK | M_ZERO); 65184610Salfred gsp->softc = g_malloc(scsize, M_WAITOK | M_ZERO); 66184610Salfred gsp->slices = g_malloc(nslice * sizeof(struct g_slice), 67194230Sthompsa M_WAITOK | M_ZERO); 68194230Sthompsa gsp->nslice = nslice; 69184610Salfred return (gsp); 70184610Salfred} 71184610Salfred 72184610Salfredstatic void 73230204Shselaskyg_slice_free(struct g_slicer *gsp) 74184610Salfred{ 75184610Salfred 76184610Salfred g_free(gsp->slices); 77184610Salfred if (gsp->hotspot != NULL) 78184610Salfred g_free(gsp->hotspot); 79184610Salfred g_free(gsp->softc); 80184610Salfred g_free(gsp); 81242619Shselasky} 82184610Salfred 83192984Sthompsastatic int 84192984Sthompsag_slice_access(struct g_provider *pp, int dr, int dw, int de) 85192984Sthompsa{ 86184610Salfred int error; 87184610Salfred u_int u; 88188413Sthompsa struct g_geom *gp; 89194228Sthompsa struct g_consumer *cp; 90194228Sthompsa struct g_provider *pp2; 91184610Salfred struct g_slicer *gsp; 92184610Salfred struct g_slice *gsl, *gsl2; 93192984Sthompsa 94194228Sthompsa gp = pp->geom; 95194228Sthompsa cp = LIST_FIRST(&gp->consumer); 96194228Sthompsa KASSERT (cp != NULL, ("g_slice_access but no consumer")); 97194228Sthompsa gsp = gp->softc; 98197570Sthompsa gsl = &gsp->slices[pp->index]; 99194228Sthompsa for (u = 0; u < gsp->nslice; u++) { 100194228Sthompsa gsl2 = &gsp->slices[u]; 101194228Sthompsa if (gsl2->length == 0) 102194228Sthompsa continue; 103194228Sthompsa if (u == pp->index) 104194228Sthompsa continue; 105194228Sthompsa if (gsl->offset + gsl->length <= gsl2->offset) 106194228Sthompsa continue; 107194228Sthompsa if (gsl2->offset + gsl2->length <= gsl->offset) 108194228Sthompsa continue; 109214761Sn_hibma /* overlap */ 110197570Sthompsa pp2 = gsl2->provider; 111239179Shselasky if ((pp->acw + dw) > 0 && pp2->ace > 0) 112184610Salfred return (EPERM); 113184610Salfred if ((pp->ace + de) > 0 && pp2->acw > 0) 114184610Salfred return (EPERM); 115184610Salfred } 116184610Salfred /* On first open, grab an extra "exclusive" bit */ 117184610Salfred if (cp->acr == 0 && cp->acw == 0 && cp->ace == 0) 118184610Salfred de++; 119184610Salfred /* ... and let go of it on last close */ 120184610Salfred if ((cp->acr + dr) == 0 && (cp->acw + dw) == 0 && (cp->ace + de) == 1) 121184610Salfred de--; 122184610Salfred error = g_access_rel(cp, dr, dw, de); 123184610Salfred return (error); 124184610Salfred} 125192984Sthompsa 126192984Sthompsa/* 127192984Sthompsa * XXX: It should be possible to specify here if we should finish all of the 128187176Sthompsa * XXX: bio, or only the non-hot bits. This would get messy if there were 129187176Sthompsa * XXX: two hot spots in the same bio, so for now we simply finish off the 130192984Sthompsa * XXX: entire bio. Modifying hot data on the way to disk is frowned on 131192984Sthompsa * XXX: so making that considerably harder is not a bad idea anyway. 132192984Sthompsa */ 133188413Sthompsavoid 134187176Sthompsag_slice_finish_hot(struct bio *bp) 135187176Sthompsa{ 136192984Sthompsa struct bio *bp2; 137192984Sthompsa struct g_geom *gp; 138230204Shselasky struct g_consumer *cp; 139230204Shselasky struct g_slicer *gsp; 140239179Shselasky struct g_slice *gsl; 141239299Shselasky int idx; 142230209Shselasky 143230204Shselasky KASSERT(bp->bio_to != NULL, 144230209Shselasky ("NULL bio_to in g_slice_finish_hot(%p)", bp)); 145184610Salfred KASSERT(bp->bio_from != NULL, 146184610Salfred ("NULL bio_from in g_slice_finish_hot(%p)", bp)); 147192984Sthompsa gp = bp->bio_to->geom; 148188413Sthompsa gsp = gp->softc; 149188413Sthompsa cp = LIST_FIRST(&gp->consumer); 150188413Sthompsa KASSERT(cp != NULL, ("NULL consumer in g_slice_finish_hot(%p)", bp)); 151188413Sthompsa idx = bp->bio_to->index; 152188413Sthompsa gsl = &gsp->slices[idx]; 153188413Sthompsa 154188413Sthompsa bp2 = g_clone_bio(bp); 155188413Sthompsa if (bp2 == NULL) { 156188413Sthompsa g_io_deliver(bp, ENOMEM); 157188413Sthompsa return; 158188413Sthompsa } 159192984Sthompsa if (bp2->bio_offset + bp2->bio_length > gsl->length) 160192984Sthompsa bp2->bio_length = gsl->length - bp2->bio_offset; 161192984Sthompsa bp2->bio_done = g_std_done; 162192984Sthompsa bp2->bio_offset += gsl->offset; 163192984Sthompsa g_io_request(bp2, cp); 164192984Sthompsa return; 165190742Sthompsa} 166192984Sthompsa 167192984Sthompsastatic void 168192984Sthompsag_slice_start(struct bio *bp) 169184610Salfred{ 170188413Sthompsa struct bio *bp2; 171184610Salfred struct g_provider *pp; 172233774Shselasky struct g_geom *gp; 173242619Shselasky struct g_consumer *cp; 174242619Shselasky struct g_slicer *gsp; 175184610Salfred struct g_slice *gsl; 176197570Sthompsa struct g_slice_hot *ghp; 177184610Salfred int idx, error; 178184610Salfred u_int m_index; 179184610Salfred off_t t; 180184610Salfred 181184610Salfred pp = bp->bio_to; 182184610Salfred gp = pp->geom; 183197570Sthompsa gsp = gp->softc; 184239299Shselasky cp = LIST_FIRST(&gp->consumer); 185239299Shselasky idx = pp->index; 186244489Shselasky gsl = &gsp->slices[idx]; 187184610Salfred switch(bp->bio_cmd) { 188184610Salfred case BIO_READ: 189184610Salfred case BIO_WRITE: 190188413Sthompsa case BIO_DELETE: 191188413Sthompsa if (bp->bio_offset > gsl->length) { 192188413Sthompsa g_io_deliver(bp, EINVAL); /* XXX: EWHAT ? */ 193188413Sthompsa return; 194188413Sthompsa } 195188413Sthompsa /* 196188413Sthompsa * Check if we collide with any hot spaces, and call the 197197570Sthompsa * method once if so. 198242619Shselasky */ 199184610Salfred t = bp->bio_offset + gsl->offset; 200184610Salfred for (m_index = 0; m_index < gsp->nhotspot; m_index++) { 201239179Shselasky ghp = &gsp->hotspot[m_index]; 202239179Shselasky if (t >= ghp->offset + ghp->length) 203239179Shselasky continue; 204239179Shselasky if (t + bp->bio_length <= ghp->offset) 205239179Shselasky continue; 206239179Shselasky switch(bp->bio_cmd) { 207194228Sthompsa case BIO_READ: idx = ghp->ract; break; 208194228Sthompsa case BIO_WRITE: idx = ghp->wact; break; 209188413Sthompsa case BIO_DELETE: idx = ghp->dact; break; 210194228Sthompsa } 211233774Shselasky switch(idx) { 212192984Sthompsa case G_SLICE_HOT_ALLOW: 213214761Sn_hibma /* Fall out and continue normal processing */ 214214843Sn_hibma continue; 215194228Sthompsa case G_SLICE_HOT_DENY: 216194228Sthompsa g_io_deliver(bp, EROFS); 217188413Sthompsa return; 218194228Sthompsa case G_SLICE_HOT_START: 219188413Sthompsa error = gsp->start(bp); 220194228Sthompsa if (error && error != EJUSTRETURN) 221239179Shselasky g_io_deliver(bp, error); 222239179Shselasky return; 223239179Shselasky case G_SLICE_HOT_CALL: 224239179Shselasky error = g_post_event(gsp->hot, bp, M_NOWAIT, 225194230Sthompsa gp, NULL); 226 if (error) 227 g_io_deliver(bp, error); 228 return; 229 } 230 break; 231 } 232 bp2 = g_clone_bio(bp); 233 if (bp2 == NULL) { 234 g_io_deliver(bp, ENOMEM); 235 return; 236 } 237 if (bp2->bio_offset + bp2->bio_length > gsl->length) 238 bp2->bio_length = gsl->length - bp2->bio_offset; 239 bp2->bio_done = g_std_done; 240 bp2->bio_offset += gsl->offset; 241 g_io_request(bp2, cp); 242 return; 243 case BIO_GETATTR: 244 /* Give the real method a chance to override */ 245 if (gsp->start != NULL && gsp->start(bp)) 246 return; 247 if (!strcmp("GEOM::kerneldump", bp->bio_attribute)) { 248 struct g_kerneldump *gkd; 249 250 gkd = (struct g_kerneldump *)bp->bio_data; 251 gkd->offset += gsp->slices[idx].offset; 252 if (gkd->length > gsp->slices[idx].length) 253 gkd->length = gsp->slices[idx].length; 254 /* now, pass it on downwards... */ 255 } 256 bp2 = g_clone_bio(bp); 257 if (bp2 == NULL) { 258 g_io_deliver(bp, ENOMEM); 259 return; 260 } 261 bp2->bio_done = g_std_done; 262 g_io_request(bp2, cp); 263 break; 264 default: 265 g_io_deliver(bp, EOPNOTSUPP); 266 return; 267 } 268} 269 270void 271g_slice_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp, struct g_consumer *cp, struct g_provider *pp) 272{ 273 struct g_slicer *gsp; 274 275 gsp = gp->softc; 276 if (indent == NULL) { 277 sbuf_printf(sb, " i %u", pp->index); 278 sbuf_printf(sb, " o %ju", 279 (uintmax_t)gsp->slices[pp->index].offset); 280 return; 281 } 282 if (pp != NULL) { 283 sbuf_printf(sb, "%s<index>%u</index>\n", indent, pp->index); 284 sbuf_printf(sb, "%s<length>%ju</length>\n", 285 indent, (uintmax_t)gsp->slices[pp->index].length); 286 sbuf_printf(sb, "%s<seclength>%ju</seclength>\n", indent, 287 (uintmax_t)gsp->slices[pp->index].length / 512); 288 sbuf_printf(sb, "%s<offset>%ju</offset>\n", indent, 289 (uintmax_t)gsp->slices[pp->index].offset); 290 sbuf_printf(sb, "%s<secoffset>%ju</secoffset>\n", indent, 291 (uintmax_t)gsp->slices[pp->index].offset / 512); 292 } 293} 294 295int 296g_slice_config(struct g_geom *gp, u_int idx, int how, off_t offset, off_t length, u_int sectorsize, const char *fmt, ...) 297{ 298 struct g_provider *pp, *pp2; 299 struct g_slicer *gsp; 300 struct g_slice *gsl; 301 va_list ap; 302 struct sbuf *sb; 303 int error, acc; 304 305 g_trace(G_T_TOPOLOGY, "g_slice_config(%s, %d, %d)", 306 gp->name, idx, how); 307 g_topology_assert(); 308 gsp = gp->softc; 309 error = 0; 310 if (idx >= gsp->nslice) 311 return(EINVAL); 312 gsl = &gsp->slices[idx]; 313 pp = gsl->provider; 314 if (pp != NULL) 315 acc = pp->acr + pp->acw + pp->ace; 316 else 317 acc = 0; 318 if (acc != 0 && how != G_SLICE_CONFIG_FORCE) { 319 if (length < gsl->length) 320 return(EBUSY); 321 if (offset != gsl->offset) 322 return(EBUSY); 323 } 324 /* XXX: check offset + length <= MEDIASIZE */ 325 if (how == G_SLICE_CONFIG_CHECK) 326 return (0); 327 gsl->length = length; 328 gsl->offset = offset; 329 gsl->sectorsize = sectorsize; 330 if (length == 0) { 331 if (pp == NULL) 332 return (0); 333 if (bootverbose) 334 printf("GEOM: Deconfigure %s\n", pp->name); 335 g_orphan_provider(pp, ENXIO); 336 gsl->provider = NULL; 337 gsp->nprovider--; 338 return (0); 339 } 340 if (pp != NULL) { 341 if (bootverbose) 342 printf("GEOM: Reconfigure %s, start %jd length %jd end %jd\n", 343 pp->name, (intmax_t)offset, (intmax_t)length, 344 (intmax_t)(offset + length - 1)); 345 pp->mediasize = gsl->length; 346 return (0); 347 } 348 va_start(ap, fmt); 349 sb = sbuf_new(NULL, NULL, 0, SBUF_AUTOEXTEND); 350 sbuf_vprintf(sb, fmt, ap); 351 sbuf_finish(sb); 352 pp = g_new_providerf(gp, sbuf_data(sb)); 353 pp2 = LIST_FIRST(&gp->consumer)->provider; 354 pp->flags = pp2->flags & G_PF_CANDELETE; 355 if (pp2->stripesize > 0) { 356 pp->stripesize = pp2->stripesize; 357 pp->stripeoffset = (pp2->stripeoffset + offset) % pp->stripesize; 358 } 359 if (bootverbose) 360 printf("GEOM: Configure %s, start %jd length %jd end %jd\n", 361 pp->name, (intmax_t)offset, (intmax_t)length, 362 (intmax_t)(offset + length - 1)); 363 pp->index = idx; 364 pp->mediasize = gsl->length; 365 pp->sectorsize = gsl->sectorsize; 366 gsl->provider = pp; 367 gsp->nprovider++; 368 g_error_provider(pp, 0); 369 sbuf_delete(sb); 370 return(0); 371} 372 373/* 374 * Configure "hotspots". A hotspot is a piece of the parent device which 375 * this particular slicer cares about for some reason. Typically because 376 * it contains meta-data used to configure the slicer. 377 * A hotspot is identified by its index number. The offset and length are 378 * relative to the parent device, and the three "?act" fields specify 379 * what action to take on BIO_READ, BIO_DELETE and BIO_WRITE. 380 * 381 * XXX: There may be a race relative to g_slice_start() here, if an existing 382 * XXX: hotspot is changed wile I/O is happening. Should this become a problem 383 * XXX: we can protect the hotspot stuff with a mutex. 384 */ 385 386int 387g_slice_conf_hot(struct g_geom *gp, u_int idx, off_t offset, off_t length, int ract, int dact, int wact) 388{ 389 struct g_slicer *gsp; 390 struct g_slice_hot *gsl, *gsl2; 391 392 g_trace(G_T_TOPOLOGY, "g_slice_conf_hot(%s, idx: %d, off: %jd, len: %jd)", 393 gp->name, idx, (intmax_t)offset, (intmax_t)length); 394 g_topology_assert(); 395 gsp = gp->softc; 396 gsl = gsp->hotspot; 397 if(idx >= gsp->nhotspot) { 398 gsl2 = g_malloc((idx + 1) * sizeof *gsl2, M_WAITOK | M_ZERO); 399 if (gsp->hotspot != NULL) 400 bcopy(gsp->hotspot, gsl2, gsp->nhotspot * sizeof *gsl2); 401 gsp->hotspot = gsl2; 402 if (gsp->hotspot != NULL) 403 g_free(gsl); 404 gsl = gsl2; 405 gsp->nhotspot = idx + 1; 406 } 407 gsl[idx].offset = offset; 408 gsl[idx].length = length; 409 KASSERT(!((ract | dact | wact) & G_SLICE_HOT_START) 410 || gsp->start != NULL, ("G_SLICE_HOT_START but no slice->start")); 411 /* XXX: check that we _have_ a start function if HOT_START specified */ 412 gsl[idx].ract = ract; 413 gsl[idx].dact = dact; 414 gsl[idx].wact = wact; 415 return (0); 416} 417 418struct g_geom * 419g_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) 420{ 421 struct g_geom *gp; 422 struct g_slicer *gsp; 423 struct g_consumer *cp; 424 void **vp; 425 int error; 426 427 g_topology_assert(); 428 vp = (void **)extrap; 429 gp = g_new_geomf(mp, "%s", pp->name); 430 gsp = g_slice_alloc(slices, extra); 431 gsp->start = start; 432 gp->access = g_slice_access; 433 gp->orphan = g_slice_orphan; 434 gp->softc = gsp; 435 gp->start = g_slice_start; 436 gp->spoiled = g_std_spoiled; 437 gp->dumpconf = g_slice_dumpconf; 438 cp = g_new_consumer(gp); 439 error = g_attach(cp, pp); 440 if (error == 0) 441 error = g_access_rel(cp, 1, 0, 0); 442 if (error) { 443 if (cp->provider != NULL) 444 g_detach(cp); 445 g_destroy_consumer(cp); 446 g_slice_free(gsp); 447 g_destroy_geom(gp); 448 return (NULL); 449 } 450 *vp = gsp->softc; 451 *cpp = cp; 452 return (gp); 453} 454 455static void 456g_slice_orphan(struct g_consumer *cp) 457{ 458 struct g_geom *gp; 459 struct g_provider *pp; 460 int error; 461 462 g_trace(G_T_TOPOLOGY, "g_slice_orphan(%p/%s)", cp, cp->provider->name); 463 g_topology_assert(); 464 KASSERT(cp->provider->error != 0, 465 ("g_slice_orphan with error == 0")); 466 467 gp = cp->geom; 468 /* XXX: Not good enough we leak the softc and its suballocations */ 469 gp->flags |= G_GEOM_WITHER; 470 error = cp->provider->error; 471 LIST_FOREACH(pp, &gp->provider, provider) 472 g_orphan_provider(pp, error); 473 return; 474} 475