xref: /original-bsd/sys/dev/cd.c (revision acda45c0)
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.1 90/07/09$
13  *
14  *	@(#)cd.c	7.3 (Berkeley) 12/16/90
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 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 daddr_t bn;
270 	register int sz, 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 		bp->b_flags |= B_ERROR;
279 		goto done;
280 	}
281 	bn = bp->b_blkno;
282 	sz = howmany(bp->b_bcount, DEV_BSIZE);
283 	if (bn < 0 || bn + sz > cs->sc_size) {
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;
292 			goto done;
293 		}
294 		bp->b_bcount = dbtob(sz);
295 	}
296 	bp->b_resid = bp->b_bcount;
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;
307 done:
308 	biodone(bp);
309 }
310 
311 cdstart(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  */
351 struct buf *
352 cdbuffer(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;
418 	cbp->b_vp = 0;
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 	 */
428 	cbp->b_saveaddr = (caddr_t)bp;
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 
439 cdintr(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)
447 		printf("cdintr(%d): bp %x\n", unit, bp);
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;
456 	biodone(bp);
457 }
458 
459 /*
460  * Called by biodone at interrupt time.
461  * Mark the component as done and if all components are done,
462  * take a cd interrupt.
463  */
464 cdiodone(cbp)
465 	register struct buf *cbp;
466 {
467 	register struct buf *bp = (struct buf *)cbp->b_saveaddr;/* XXX */
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;
486 		bp->b_error = biowait(cbp);
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 
509 cdread(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 
522 cdwrite(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 
535 cdioctl(dev, cmd, data, flag)
536 	dev_t dev;
537 	int cmd;
538 	caddr_t data;
539 	int flag;
540 {
541 	return(EINVAL);
542 }
543 
544 cdsize(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 
555 cddump(dev)
556 {
557 	return(ENXIO);
558 }
559 #endif
560