add suggested es_config_timer option to ISH's
[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 *
12 * from: Utah $Hdr: cd.c 1.4 89/09/17$
13 *
14 * @(#)cd.c 7.1 (Berkeley) %G%
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];
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;
299bad:
300 bp->b_flags |= B_ERROR;
301done:
302 iodone(bp);
303}
304
305cdstart(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 */
345struct buf *
346cdbuffer(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
432cdintr(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 */
457cdiodone(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
502cdread(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
515cdwrite(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
528cdioctl(dev, cmd, data, flag)
529 dev_t dev;
530 int cmd;
531 caddr_t data;
532 int flag;
533{
534 return(EINVAL);
535}
536
537cdsize(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
548cddump(dev)
549{
550 return(ENXIO);
551}
552#endif