date and time created 90/11/08 20:14:43 by bill
[unix-history] / usr / src / sys / dev / cd.c
CommitLineData
60f56dfc
KM
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 *
0e1872ad 12 * from: Utah $Hdr: cd.c 1.1 90/07/09$
60f56dfc 13 *
0e1872ad 14 * @(#)cd.c 7.2 (Berkeley) %G%
60f56dfc
KM
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
34int cddebug = 0x00;
35#define CDB_FOLLOW 0x01
36#define CDB_INIT 0x02
37#define CDB_IO 0x04
38#endif
39
40struct buf cdbuf[NCD];
41struct buf *cdbuffer();
42int 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
51struct 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
67cdinit(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
142cdinterleave(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
234printiinfo(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
249cdopen(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
264cdstrategy(bp)
265 register struct buf *bp;
266{
267 register int unit = cdunit(bp->b_dev);
268 register struct cd_softc *cs = &cd_softc[unit];
0e1872ad
KM
269 register daddr_t bn;
270 register int sz, s;
60f56dfc
KM
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;
0e1872ad
KM
278 bp->b_flags |= B_ERROR;
279 goto done;
60f56dfc
KM
280 }
281 bn = bp->b_blkno;
0e1872ad 282 sz = howmany(bp->b_bcount, DEV_BSIZE);
60f56dfc 283 if (bn < 0 || bn + sz > cs->sc_size) {
0e1872ad
KM
284 sz = cs->sc_size - bn;
285 if (sz == 0) {
286 bp->b_resid = bp->b_bcount;
287 goto done;
288 }
289 if (sz < 0) {
290 bp->b_error = EINVAL;
291 bp->b_flags |= B_ERROR;
60f56dfc 292 goto done;
0e1872ad
KM
293 }
294 bp->b_bcount = dbtob(sz);
60f56dfc 295 }
0e1872ad 296 bp->b_resid = bp->b_bcount;
60f56dfc
KM
297 /*
298 * "Start" the unit.
299 * XXX: the use of sc_bp is just to retain the "traditional"
300 * interface to the start routine.
301 */
302 s = splbio();
303 cs->sc_bp = bp;
304 cdstart(unit);
305 splx(s);
306 return;
60f56dfc 307done:
0e1872ad 308 biodone(bp);
60f56dfc
KM
309}
310
311cdstart(unit)
312 int unit;
313{
314 register struct cd_softc *cs = &cd_softc[unit];
315 register struct buf *bp = cs->sc_bp;
316 register long bcount, rcount;
317 struct buf *cbp;
318 caddr_t addr;
319 daddr_t bn;
320
321#ifdef DEBUG
322 if (cddebug & CDB_FOLLOW)
323 printf("cdstart(%d)\n", unit);
324#endif
325 /*
326 * Instumentation (not real meaningful)
327 */
328 cs->sc_usecnt++;
329 if (cs->sc_dk >= 0) {
330 dk_busy |= 1 << cs->sc_dk;
331 dk_xfer[cs->sc_dk]++;
332 dk_wds[cs->sc_dk] += bp->b_bcount >> 6;
333 }
334 /*
335 * Allocate component buffers and fire off the requests
336 */
337 bn = bp->b_blkno;
338 addr = bp->b_un.b_addr;
339 for (bcount = bp->b_bcount; bcount > 0; bcount -= rcount) {
340 cbp = cdbuffer(cs, bp, bn, addr, bcount);
341 rcount = cbp->b_bcount;
342 (*bdevsw[major(cbp->b_dev)].d_strategy)(cbp);
343 bn += btodb(rcount);
344 addr += rcount;
345 }
346}
347
348/*
349 * Build a component buffer header.
350 */
351struct buf *
352cdbuffer(cs, bp, bn, addr, bcount)
353 register struct cd_softc *cs;
354 struct buf *bp;
355 daddr_t bn;
356 caddr_t addr;
357 long bcount;
358{
359 register struct cdcinfo *ci;
360 register struct buf *cbp;
361 register daddr_t cbn, cboff;
362
363#ifdef DEBUG
364 if (cddebug & CDB_IO)
365 printf("cdbuffer(%x, %x, %d, %x, %d)\n",
366 cs, bp, bn, addr, bcount);
367#endif
368 /*
369 * Determine which component bn falls in.
370 */
371 cbn = bn;
372 cboff = 0;
373 /*
374 * Serially concatenated
375 */
376 if (cs->sc_ileave == 0) {
377 register daddr_t sblk;
378
379 sblk = 0;
380 for (ci = cs->sc_cinfo; cbn >= sblk + ci->ci_size; ci++)
381 sblk += ci->ci_size;
382 cbn -= sblk;
383 }
384 /*
385 * Interleaved
386 */
387 else {
388 register struct cdiinfo *ii;
389 int cdisk, off;
390
391 cboff = cbn % cs->sc_ileave;
392 cbn /= cs->sc_ileave;
393 for (ii = cs->sc_itable; ii->ii_ndisk; ii++)
394 if (ii->ii_startblk > cbn)
395 break;
396 ii--;
397 off = cbn - ii->ii_startblk;
398 if (ii->ii_ndisk == 1) {
399 cdisk = ii->ii_index[0];
400 cbn = ii->ii_startoff + off;
401 } else {
402 cdisk = ii->ii_index[off % ii->ii_ndisk];
403 cbn = ii->ii_startoff + off / ii->ii_ndisk;
404 }
405 cbn *= cs->sc_ileave;
406 ci = &cs->sc_cinfo[cdisk];
407 }
408 /*
409 * Fill in the component buf structure.
410 */
411 cbp = getcbuf();
412 cbp->b_flags = bp->b_flags | B_CALL;
413 cbp->b_iodone = cdiodone;
414 cbp->b_proc = bp->b_proc;
415 cbp->b_dev = ci->ci_dev;
416 cbp->b_blkno = cbn + cboff;
417 cbp->b_un.b_addr = addr;
0e1872ad 418 cbp->b_vp = 0;
60f56dfc
KM
419 if (cs->sc_ileave == 0)
420 cbp->b_bcount = dbtob(ci->ci_size - cbn);
421 else
422 cbp->b_bcount = dbtob(cs->sc_ileave - cboff);
423 if (cbp->b_bcount > bcount)
424 cbp->b_bcount = bcount;
425 /*
426 * XXX: context for cdiodone
427 */
0e1872ad 428 cbp->b_saveaddr = (caddr_t)bp;
60f56dfc
KM
429 cbp->b_pfcent = ((cs - cd_softc) << 16) | (ci - cs->sc_cinfo);
430#ifdef DEBUG
431 if (cddebug & CDB_IO)
432 printf(" dev %x(u%d): cbp %x bn %d addr %x bcnt %d\n",
433 ci->ci_dev, ci-cs->sc_cinfo, cbp, cbp->b_blkno,
434 cbp->b_un.b_addr, cbp->b_bcount);
435#endif
436 return(cbp);
437}
438
439cdintr(unit)
440 int unit;
441{
442 register struct cd_softc *cs = &cd_softc[unit];
443 register struct buf *bp = cs->sc_bp;
444
445#ifdef DEBUG
446 if (cddebug & CDB_FOLLOW)
0e1872ad 447 printf("cdintr(%d): bp %x\n", unit, bp);
60f56dfc
KM
448#endif
449 /*
450 * Request is done for better or worse, wakeup the top half.
451 */
452 if (--cs->sc_usecnt == 0 && cs->sc_dk >= 0)
453 dk_busy &= ~(1 << cs->sc_dk);
454 if (bp->b_flags & B_ERROR)
455 bp->b_resid = bp->b_bcount;
0e1872ad 456 biodone(bp);
60f56dfc
KM
457}
458
459/*
0e1872ad 460 * Called by biodone at interrupt time.
60f56dfc
KM
461 * Mark the component as done and if all components are done,
462 * take a cd interrupt.
463 */
464cdiodone(cbp)
465 register struct buf *cbp;
466{
0e1872ad 467 register struct buf *bp = (struct buf *)cbp->b_saveaddr;/* XXX */
60f56dfc
KM
468 register int unit = (cbp->b_pfcent >> 16) & 0xFFFF; /* XXX */
469 int count, s;
470
471 s = splbio();
472#ifdef DEBUG
473 if (cddebug & CDB_FOLLOW)
474 printf("cdiodone(%x)\n", cbp);
475 if (cddebug & CDB_IO) {
476 printf("cdiodone: bp %x bcount %d resid %d\n",
477 bp, bp->b_bcount, bp->b_resid);
478 printf(" dev %x(u%d), cbp %x bn %d addr %x bcnt %d\n",
479 cbp->b_dev, cbp->b_pfcent & 0xFFFF, cbp,
480 cbp->b_blkno, cbp->b_un.b_addr, cbp->b_bcount);
481 }
482#endif
483
484 if (cbp->b_flags & B_ERROR) {
485 bp->b_flags |= B_ERROR;
0e1872ad 486 bp->b_error = biowait(cbp);
60f56dfc
KM
487#ifdef DEBUG
488 printf("cd%d: error %d on component %d\n",
489 unit, bp->b_error, cbp->b_pfcent & 0xFFFF);
490#endif
491 }
492 count = cbp->b_bcount;
493 putcbuf(cbp);
494
495 /*
496 * If all done, "interrupt".
497 * Again, sc_bp is only used to preserve the traditional interface.
498 */
499 bp->b_resid -= count;
500 if (bp->b_resid < 0)
501 panic("cdiodone: count");
502 if (bp->b_resid == 0) {
503 cd_softc[unit].sc_bp = bp;
504 cdintr(unit);
505 }
506 splx(s);
507}
508
509cdread(dev, uio)
510 dev_t dev;
511 struct uio *uio;
512{
513 register int unit = cdunit(dev);
514
515#ifdef DEBUG
516 if (cddebug & CDB_FOLLOW)
517 printf("cdread(%x, %x)\n", dev, uio);
518#endif
519 return(physio(cdstrategy, &cdbuf[unit], dev, B_READ, minphys, uio));
520}
521
522cdwrite(dev, uio)
523 dev_t dev;
524 struct uio *uio;
525{
526 register int unit = cdunit(dev);
527
528#ifdef DEBUG
529 if (cddebug & CDB_FOLLOW)
530 printf("cdwrite(%x, %x)\n", dev, uio);
531#endif
532 return(physio(cdstrategy, &cdbuf[unit], dev, B_WRITE, minphys, uio));
533}
534
535cdioctl(dev, cmd, data, flag)
536 dev_t dev;
537 int cmd;
538 caddr_t data;
539 int flag;
540{
541 return(EINVAL);
542}
543
544cdsize(dev)
545 dev_t dev;
546{
547 int unit = cdunit(dev);
548 register struct cd_softc *cs = &cd_softc[unit];
549
550 if (unit >= NCD || (cs->sc_flags & CDF_INITED) == 0)
551 return(-1);
552 return(cs->sc_size);
553}
554
555cddump(dev)
556{
557 return(ENXIO);
558}
559#endif