xref: /original-bsd/sys/dev/cd.c (revision 8fbb78b3)
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