have to unlock after call to vn_open;
[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 *
f7081d19 12 * from: Utah $Hdr: cd.c 1.6 90/11/28$
60f56dfc 13 *
f7081d19 14 * @(#)cd.c 7.4 (Berkeley) %G%
60f56dfc
KM
15 */
16
17/*
18 * "Concatenated" disk driver.
19 */
20#include "cd.h"
21#if NCD > 0
22
b28b3a13
KB
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"
60f56dfc
KM
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();
f7081d19 42char *cddevtostr();
60f56dfc
KM
43int 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
52struct 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
68cdinit(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) {
f7081d19
MH
101 size = (size_t) (*bdevsw[major(dev)].d_psize)(dev);
102 if ((int)size < 0)
60f56dfc
KM
103 size = 0;
104 } else
105 size = 0;
106 if (cs->sc_ileave > 1)
107 size -= size % cs->sc_ileave;
f7081d19
MH
108 if (size == 0) {
109 printf("cd%d: not configured (component %s missing)\n",
110 cd->cd_unit, cddevtostr(ci->ci_dev));
60f56dfc 111 return(0);
f7081d19 112 }
60f56dfc
KM
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 */
f7081d19
MH
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);
60f56dfc 143 if (cs->sc_ileave)
f7081d19 144 printf("interleaved at %d blocks\n", cs->sc_ileave);
60f56dfc 145 else
f7081d19 146 printf("concatenated\n");
60f56dfc
KM
147 cs->sc_flags = CDF_ALIVE | CDF_INITED;
148 return(1);
149}
150
f7081d19
MH
151/*
152 * XXX not really cd specific.
153 */
154char *
155cddevtostr(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
60f56dfc
KM
181cdinterleave(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
273printiinfo(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
288cdopen(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
303cdstrategy(bp)
304 register struct buf *bp;
305{
306 register int unit = cdunit(bp->b_dev);
307 register struct cd_softc *cs = &cd_softc[unit];
0e1872ad
KM
308 register daddr_t bn;
309 register int sz, s;
60f56dfc
KM
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;
0e1872ad
KM
317 bp->b_flags |= B_ERROR;
318 goto done;
60f56dfc
KM
319 }
320 bn = bp->b_blkno;
0e1872ad 321 sz = howmany(bp->b_bcount, DEV_BSIZE);
60f56dfc 322 if (bn < 0 || bn + sz > cs->sc_size) {
0e1872ad
KM
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;
60f56dfc 331 goto done;
0e1872ad
KM
332 }
333 bp->b_bcount = dbtob(sz);
60f56dfc 334 }
0e1872ad 335 bp->b_resid = bp->b_bcount;
60f56dfc
KM
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;
60f56dfc 346done:
0e1872ad 347 biodone(bp);
60f56dfc
KM
348}
349
350cdstart(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 */
390struct buf *
391cdbuffer(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;
0e1872ad 457 cbp->b_vp = 0;
60f56dfc
KM
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 */
0e1872ad 467 cbp->b_saveaddr = (caddr_t)bp;
60f56dfc
KM
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
478cdintr(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)
0e1872ad 486 printf("cdintr(%d): bp %x\n", unit, bp);
60f56dfc
KM
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;
0e1872ad 495 biodone(bp);
60f56dfc
KM
496}
497
498/*
0e1872ad 499 * Called by biodone at interrupt time.
60f56dfc
KM
500 * Mark the component as done and if all components are done,
501 * take a cd interrupt.
502 */
503cdiodone(cbp)
504 register struct buf *cbp;
505{
0e1872ad 506 register struct buf *bp = (struct buf *)cbp->b_saveaddr;/* XXX */
60f56dfc
KM
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;
0e1872ad 525 bp->b_error = biowait(cbp);
60f56dfc
KM
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
548cdread(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
561cdwrite(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
574cdioctl(dev, cmd, data, flag)
575 dev_t dev;
576 int cmd;
577 caddr_t data;
578 int flag;
579{
580 return(EINVAL);
581}
582
583cdsize(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
594cddump(dev)
595{
596 return(ENXIO);
597}
598#endif