1 /* 2 * Copyright (c) 1988 University of Utah. 3 * Copyright (c) 1990 The Regents of the University of California. 4 * All rights reserved. 5 * 6 * This code is derived from software contributed to Berkeley by 7 * the Systems Programming Group of the University of Utah Computer 8 * Science Department. 9 * 10 * %sccs.include.redist.c% 11 * 12 * from: Utah $Hdr: cd.c 1.6 90/11/28$ 13 * 14 * @(#)cd.c 7.4 (Berkeley) 05/07/91 15 */ 16 17 /* 18 * "Concatenated" disk driver. 19 */ 20 #include "cd.h" 21 #if NCD > 0 22 23 #include "sys/param.h" 24 #include "sys/systm.h" 25 #include "sys/errno.h" 26 #include "sys/dkstat.h" 27 #include "sys/buf.h" 28 #include "sys/malloc.h" 29 #include "sys/conf.h" 30 31 #include "cdvar.h" 32 33 #ifdef DEBUG 34 int cddebug = 0x00; 35 #define CDB_FOLLOW 0x01 36 #define CDB_INIT 0x02 37 #define CDB_IO 0x04 38 #endif 39 40 struct buf cdbuf[NCD]; 41 struct buf *cdbuffer(); 42 char *cddevtostr(); 43 int cdiodone(); 44 45 #define cdunit(x) ((minor(x) >> 3) & 0x7) /* for consistency */ 46 47 #define getcbuf() \ 48 ((struct buf *)malloc(sizeof(struct buf), M_DEVBUF, M_WAITOK)) 49 #define putcbuf(bp) \ 50 free((caddr_t)(bp), M_DEVBUF) 51 52 struct cd_softc { 53 int sc_flags; /* flags */ 54 size_t sc_size; /* size of cd */ 55 int sc_ileave; /* interleave */ 56 int sc_ncdisks; /* number of components */ 57 struct cdcinfo sc_cinfo[NCDISKS]; /* component info */ 58 struct cdiinfo *sc_itable; /* interleave table */ 59 int sc_usecnt; /* number of requests active */ 60 struct buf *sc_bp; /* "current" request */ 61 int sc_dk; /* disk index */ 62 } cd_softc[NCD]; 63 64 /* sc_flags */ 65 #define CDF_ALIVE 0x01 66 #define CDF_INITED 0x02 67 68 cdinit(cd) 69 struct cddevice *cd; 70 { 71 register struct cd_softc *cs = &cd_softc[cd->cd_unit]; 72 register struct cdcinfo *ci; 73 register size_t size; 74 register int ix; 75 size_t minsize; 76 dev_t dev; 77 78 #ifdef DEBUG 79 if (cddebug & (CDB_FOLLOW|CDB_INIT)) 80 printf("cdinit: unit %d\n", cd->cd_unit); 81 #endif 82 cs->sc_dk = cd->cd_dk; 83 cs->sc_size = 0; 84 cs->sc_ileave = cd->cd_interleave; 85 cs->sc_ncdisks = 0; 86 /* 87 * Verify that each component piece exists and record 88 * relevant information about it. 89 */ 90 minsize = 0; 91 for (ix = 0; ix < NCDISKS; ix++) { 92 if ((dev = cd->cd_dev[ix]) == NODEV) 93 break; 94 ci = &cs->sc_cinfo[ix]; 95 ci->ci_dev = dev; 96 /* 97 * Calculate size (truncated to interleave boundary 98 * if necessary. 99 */ 100 if (bdevsw[major(dev)].d_psize) { 101 size = (size_t) (*bdevsw[major(dev)].d_psize)(dev); 102 if ((int)size < 0) 103 size = 0; 104 } else 105 size = 0; 106 if (cs->sc_ileave > 1) 107 size -= size % cs->sc_ileave; 108 if (size == 0) { 109 printf("cd%d: not configured (component %s missing)\n", 110 cd->cd_unit, cddevtostr(ci->ci_dev)); 111 return(0); 112 } 113 if (minsize == 0 || size < minsize) 114 minsize = size; 115 ci->ci_size = size; 116 cs->sc_size += size; 117 cs->sc_ncdisks++; 118 } 119 /* 120 * If uniform interleave is desired set all sizes to that of 121 * the smallest component. 122 */ 123 if (cd->cd_flags & CDF_UNIFORM) { 124 for (ci = cs->sc_cinfo; 125 ci < &cs->sc_cinfo[cs->sc_ncdisks]; ci++) 126 ci->ci_size = minsize; 127 cs->sc_size = cs->sc_ncdisks * minsize; 128 } 129 /* 130 * Construct the interleave table 131 */ 132 if (!cdinterleave(cs)) 133 return(0); 134 if (cd->cd_dk >= 0) 135 dk_wpms[cd->cd_dk] = 32 * (60 * DEV_BSIZE / 2); /* XXX */ 136 printf("cd%d: %d components ", cd->cd_unit, cs->sc_ncdisks); 137 for (ix = 0; ix < cs->sc_ncdisks; ix++) 138 printf("%c%s%c", 139 ix == 0 ? '(' : ' ', 140 cddevtostr(cs->sc_cinfo[ix].ci_dev), 141 ix == cs->sc_ncdisks - 1 ? ')' : ','); 142 printf(", %d blocks ", cs->sc_size); 143 if (cs->sc_ileave) 144 printf("interleaved at %d blocks\n", cs->sc_ileave); 145 else 146 printf("concatenated\n"); 147 cs->sc_flags = CDF_ALIVE | CDF_INITED; 148 return(1); 149 } 150 151 /* 152 * XXX not really cd specific. 153 */ 154 char * 155 cddevtostr(dev) 156 dev_t dev; 157 { 158 static char dbuf[5]; 159 160 dbuf[1] = 'd'; 161 switch (major(dev)) { 162 case 2: 163 dbuf[0] = 'r'; 164 break; 165 case 4: 166 dbuf[0] = 's'; 167 break; 168 case 5: 169 dbuf[0] = 'c'; 170 break; 171 default: 172 dbuf[0] = dbuf[1] = '?'; 173 break; 174 } 175 dbuf[2] = (minor(dev) >> 3) + '0'; 176 dbuf[3] = (minor(dev) & 7) + 'a'; 177 dbuf[4] = '\0'; 178 return (dbuf); 179 } 180 181 cdinterleave(cs) 182 register struct cd_softc *cs; 183 { 184 register struct cdcinfo *ci, *smallci; 185 register struct cdiinfo *ii; 186 register daddr_t bn, lbn; 187 register int ix; 188 u_long size; 189 190 #ifdef DEBUG 191 if (cddebug & CDB_INIT) 192 printf("cdinterleave(%x): ileave %d\n", cs, cs->sc_ileave); 193 #endif 194 /* 195 * Allocate an interleave table. 196 * Chances are this is too big, but we don't care. 197 */ 198 size = (cs->sc_ncdisks + 1) * sizeof(struct cdiinfo); 199 cs->sc_itable = (struct cdiinfo *)malloc(size, M_DEVBUF, M_WAITOK); 200 bzero((caddr_t)cs->sc_itable, size); 201 /* 202 * Trivial case: no interleave (actually interleave of disk size). 203 * Each table entry represent a single component in its entirety. 204 */ 205 if (cs->sc_ileave == 0) { 206 bn = 0; 207 ii = cs->sc_itable; 208 for (ix = 0; ix < cs->sc_ncdisks; ix++) { 209 ii->ii_ndisk = 1; 210 ii->ii_startblk = bn; 211 ii->ii_startoff = 0; 212 ii->ii_index[0] = ix; 213 bn += cs->sc_cinfo[ix].ci_size; 214 ii++; 215 } 216 ii->ii_ndisk = 0; 217 #ifdef DEBUG 218 if (cddebug & CDB_INIT) 219 printiinfo(cs->sc_itable); 220 #endif 221 return(1); 222 } 223 /* 224 * The following isn't fast or pretty; it doesn't have to be. 225 */ 226 size = 0; 227 bn = lbn = 0; 228 for (ii = cs->sc_itable; ; ii++) { 229 /* 230 * Locate the smallest of the remaining components 231 */ 232 smallci = NULL; 233 for (ci = cs->sc_cinfo; 234 ci < &cs->sc_cinfo[cs->sc_ncdisks]; ci++) 235 if (ci->ci_size > size && 236 (smallci == NULL || 237 ci->ci_size < smallci->ci_size)) 238 smallci = ci; 239 /* 240 * Nobody left, all done 241 */ 242 if (smallci == NULL) { 243 ii->ii_ndisk = 0; 244 break; 245 } 246 /* 247 * Record starting logical block and component offset 248 */ 249 ii->ii_startblk = bn / cs->sc_ileave; 250 ii->ii_startoff = lbn; 251 /* 252 * Determine how many disks take part in this interleave 253 * and record their indices. 254 */ 255 ix = 0; 256 for (ci = cs->sc_cinfo; 257 ci < &cs->sc_cinfo[cs->sc_ncdisks]; ci++) 258 if (ci->ci_size >= smallci->ci_size) 259 ii->ii_index[ix++] = ci - cs->sc_cinfo; 260 ii->ii_ndisk = ix; 261 bn += ix * (smallci->ci_size - size); 262 lbn = smallci->ci_size / cs->sc_ileave; 263 size = smallci->ci_size; 264 } 265 #ifdef DEBUG 266 if (cddebug & CDB_INIT) 267 printiinfo(cs->sc_itable); 268 #endif 269 return(1); 270 } 271 272 #ifdef DEBUG 273 printiinfo(ii) 274 struct cdiinfo *ii; 275 { 276 register int ix, i; 277 278 for (ix = 0; ii->ii_ndisk; ix++, ii++) { 279 printf(" itab[%d]: #dk %d sblk %d soff %d", 280 ix, ii->ii_ndisk, ii->ii_startblk, ii->ii_startoff); 281 for (i = 0; i < ii->ii_ndisk; i++) 282 printf(" %d", ii->ii_index[i]); 283 printf("\n"); 284 } 285 } 286 #endif 287 288 cdopen(dev, flags) 289 dev_t dev; 290 { 291 int unit = cdunit(dev); 292 register struct cd_softc *cs = &cd_softc[unit]; 293 294 #ifdef DEBUG 295 if (cddebug & CDB_FOLLOW) 296 printf("cdopen(%x, %x)\n", dev, flags); 297 #endif 298 if (unit >= NCD || (cs->sc_flags & CDF_ALIVE) == 0) 299 return(ENXIO); 300 return(0); 301 } 302 303 cdstrategy(bp) 304 register struct buf *bp; 305 { 306 register int unit = cdunit(bp->b_dev); 307 register struct cd_softc *cs = &cd_softc[unit]; 308 register daddr_t bn; 309 register int sz, s; 310 311 #ifdef DEBUG 312 if (cddebug & CDB_FOLLOW) 313 printf("cdstrategy(%x): unit %d\n", bp, unit); 314 #endif 315 if ((cs->sc_flags & CDF_INITED) == 0) { 316 bp->b_error = ENXIO; 317 bp->b_flags |= B_ERROR; 318 goto done; 319 } 320 bn = bp->b_blkno; 321 sz = howmany(bp->b_bcount, DEV_BSIZE); 322 if (bn < 0 || bn + sz > cs->sc_size) { 323 sz = cs->sc_size - bn; 324 if (sz == 0) { 325 bp->b_resid = bp->b_bcount; 326 goto done; 327 } 328 if (sz < 0) { 329 bp->b_error = EINVAL; 330 bp->b_flags |= B_ERROR; 331 goto done; 332 } 333 bp->b_bcount = dbtob(sz); 334 } 335 bp->b_resid = bp->b_bcount; 336 /* 337 * "Start" the unit. 338 * XXX: the use of sc_bp is just to retain the "traditional" 339 * interface to the start routine. 340 */ 341 s = splbio(); 342 cs->sc_bp = bp; 343 cdstart(unit); 344 splx(s); 345 return; 346 done: 347 biodone(bp); 348 } 349 350 cdstart(unit) 351 int unit; 352 { 353 register struct cd_softc *cs = &cd_softc[unit]; 354 register struct buf *bp = cs->sc_bp; 355 register long bcount, rcount; 356 struct buf *cbp; 357 caddr_t addr; 358 daddr_t bn; 359 360 #ifdef DEBUG 361 if (cddebug & CDB_FOLLOW) 362 printf("cdstart(%d)\n", unit); 363 #endif 364 /* 365 * Instumentation (not real meaningful) 366 */ 367 cs->sc_usecnt++; 368 if (cs->sc_dk >= 0) { 369 dk_busy |= 1 << cs->sc_dk; 370 dk_xfer[cs->sc_dk]++; 371 dk_wds[cs->sc_dk] += bp->b_bcount >> 6; 372 } 373 /* 374 * Allocate component buffers and fire off the requests 375 */ 376 bn = bp->b_blkno; 377 addr = bp->b_un.b_addr; 378 for (bcount = bp->b_bcount; bcount > 0; bcount -= rcount) { 379 cbp = cdbuffer(cs, bp, bn, addr, bcount); 380 rcount = cbp->b_bcount; 381 (*bdevsw[major(cbp->b_dev)].d_strategy)(cbp); 382 bn += btodb(rcount); 383 addr += rcount; 384 } 385 } 386 387 /* 388 * Build a component buffer header. 389 */ 390 struct buf * 391 cdbuffer(cs, bp, bn, addr, bcount) 392 register struct cd_softc *cs; 393 struct buf *bp; 394 daddr_t bn; 395 caddr_t addr; 396 long bcount; 397 { 398 register struct cdcinfo *ci; 399 register struct buf *cbp; 400 register daddr_t cbn, cboff; 401 402 #ifdef DEBUG 403 if (cddebug & CDB_IO) 404 printf("cdbuffer(%x, %x, %d, %x, %d)\n", 405 cs, bp, bn, addr, bcount); 406 #endif 407 /* 408 * Determine which component bn falls in. 409 */ 410 cbn = bn; 411 cboff = 0; 412 /* 413 * Serially concatenated 414 */ 415 if (cs->sc_ileave == 0) { 416 register daddr_t sblk; 417 418 sblk = 0; 419 for (ci = cs->sc_cinfo; cbn >= sblk + ci->ci_size; ci++) 420 sblk += ci->ci_size; 421 cbn -= sblk; 422 } 423 /* 424 * Interleaved 425 */ 426 else { 427 register struct cdiinfo *ii; 428 int cdisk, off; 429 430 cboff = cbn % cs->sc_ileave; 431 cbn /= cs->sc_ileave; 432 for (ii = cs->sc_itable; ii->ii_ndisk; ii++) 433 if (ii->ii_startblk > cbn) 434 break; 435 ii--; 436 off = cbn - ii->ii_startblk; 437 if (ii->ii_ndisk == 1) { 438 cdisk = ii->ii_index[0]; 439 cbn = ii->ii_startoff + off; 440 } else { 441 cdisk = ii->ii_index[off % ii->ii_ndisk]; 442 cbn = ii->ii_startoff + off / ii->ii_ndisk; 443 } 444 cbn *= cs->sc_ileave; 445 ci = &cs->sc_cinfo[cdisk]; 446 } 447 /* 448 * Fill in the component buf structure. 449 */ 450 cbp = getcbuf(); 451 cbp->b_flags = bp->b_flags | B_CALL; 452 cbp->b_iodone = cdiodone; 453 cbp->b_proc = bp->b_proc; 454 cbp->b_dev = ci->ci_dev; 455 cbp->b_blkno = cbn + cboff; 456 cbp->b_un.b_addr = addr; 457 cbp->b_vp = 0; 458 if (cs->sc_ileave == 0) 459 cbp->b_bcount = dbtob(ci->ci_size - cbn); 460 else 461 cbp->b_bcount = dbtob(cs->sc_ileave - cboff); 462 if (cbp->b_bcount > bcount) 463 cbp->b_bcount = bcount; 464 /* 465 * XXX: context for cdiodone 466 */ 467 cbp->b_saveaddr = (caddr_t)bp; 468 cbp->b_pfcent = ((cs - cd_softc) << 16) | (ci - cs->sc_cinfo); 469 #ifdef DEBUG 470 if (cddebug & CDB_IO) 471 printf(" dev %x(u%d): cbp %x bn %d addr %x bcnt %d\n", 472 ci->ci_dev, ci-cs->sc_cinfo, cbp, cbp->b_blkno, 473 cbp->b_un.b_addr, cbp->b_bcount); 474 #endif 475 return(cbp); 476 } 477 478 cdintr(unit) 479 int unit; 480 { 481 register struct cd_softc *cs = &cd_softc[unit]; 482 register struct buf *bp = cs->sc_bp; 483 484 #ifdef DEBUG 485 if (cddebug & CDB_FOLLOW) 486 printf("cdintr(%d): bp %x\n", unit, bp); 487 #endif 488 /* 489 * Request is done for better or worse, wakeup the top half. 490 */ 491 if (--cs->sc_usecnt == 0 && cs->sc_dk >= 0) 492 dk_busy &= ~(1 << cs->sc_dk); 493 if (bp->b_flags & B_ERROR) 494 bp->b_resid = bp->b_bcount; 495 biodone(bp); 496 } 497 498 /* 499 * Called by biodone at interrupt time. 500 * Mark the component as done and if all components are done, 501 * take a cd interrupt. 502 */ 503 cdiodone(cbp) 504 register struct buf *cbp; 505 { 506 register struct buf *bp = (struct buf *)cbp->b_saveaddr;/* XXX */ 507 register int unit = (cbp->b_pfcent >> 16) & 0xFFFF; /* XXX */ 508 int count, s; 509 510 s = splbio(); 511 #ifdef DEBUG 512 if (cddebug & CDB_FOLLOW) 513 printf("cdiodone(%x)\n", cbp); 514 if (cddebug & CDB_IO) { 515 printf("cdiodone: bp %x bcount %d resid %d\n", 516 bp, bp->b_bcount, bp->b_resid); 517 printf(" dev %x(u%d), cbp %x bn %d addr %x bcnt %d\n", 518 cbp->b_dev, cbp->b_pfcent & 0xFFFF, cbp, 519 cbp->b_blkno, cbp->b_un.b_addr, cbp->b_bcount); 520 } 521 #endif 522 523 if (cbp->b_flags & B_ERROR) { 524 bp->b_flags |= B_ERROR; 525 bp->b_error = biowait(cbp); 526 #ifdef DEBUG 527 printf("cd%d: error %d on component %d\n", 528 unit, bp->b_error, cbp->b_pfcent & 0xFFFF); 529 #endif 530 } 531 count = cbp->b_bcount; 532 putcbuf(cbp); 533 534 /* 535 * If all done, "interrupt". 536 * Again, sc_bp is only used to preserve the traditional interface. 537 */ 538 bp->b_resid -= count; 539 if (bp->b_resid < 0) 540 panic("cdiodone: count"); 541 if (bp->b_resid == 0) { 542 cd_softc[unit].sc_bp = bp; 543 cdintr(unit); 544 } 545 splx(s); 546 } 547 548 cdread(dev, uio) 549 dev_t dev; 550 struct uio *uio; 551 { 552 register int unit = cdunit(dev); 553 554 #ifdef DEBUG 555 if (cddebug & CDB_FOLLOW) 556 printf("cdread(%x, %x)\n", dev, uio); 557 #endif 558 return(physio(cdstrategy, &cdbuf[unit], dev, B_READ, minphys, uio)); 559 } 560 561 cdwrite(dev, uio) 562 dev_t dev; 563 struct uio *uio; 564 { 565 register int unit = cdunit(dev); 566 567 #ifdef DEBUG 568 if (cddebug & CDB_FOLLOW) 569 printf("cdwrite(%x, %x)\n", dev, uio); 570 #endif 571 return(physio(cdstrategy, &cdbuf[unit], dev, B_WRITE, minphys, uio)); 572 } 573 574 cdioctl(dev, cmd, data, flag) 575 dev_t dev; 576 int cmd; 577 caddr_t data; 578 int flag; 579 { 580 return(EINVAL); 581 } 582 583 cdsize(dev) 584 dev_t dev; 585 { 586 int unit = cdunit(dev); 587 register struct cd_softc *cs = &cd_softc[unit]; 588 589 if (unit >= NCD || (cs->sc_flags & CDF_INITED) == 0) 590 return(-1); 591 return(cs->sc_size); 592 } 593 594 cddump(dev) 595 { 596 return(ENXIO); 597 } 598 #endif 599