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