xref: /original-bsd/sys/dev/cd.c (revision 00534a39)
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.6 (Berkeley) 02/18/93
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 #include <sys/stat.h>
31 #ifdef COMPAT_NOLABEL
32 #include <sys/ioctl.h>
33 #include <sys/disklabel.h>
34 #include <sys/fcntl.h>
35 #endif
36 
37 #include <dev/cdvar.h>
38 
39 #ifdef DEBUG
40 int cddebug = 0x00;
41 #define CDB_FOLLOW	0x01
42 #define CDB_INIT	0x02
43 #define CDB_IO		0x04
44 #endif
45 
46 struct	buf cdbuf[NCD];
47 struct	buf *cdbuffer();
48 char	*cddevtostr();
49 int	cdiodone();
50 
51 #define	cdunit(x)	((minor(x) >> 3) & 0x7)	/* for consistency */
52 
53 #define	getcbuf()	\
54 	((struct buf *)malloc(sizeof(struct buf), M_DEVBUF, M_WAITOK))
55 #define putcbuf(bp)	\
56 	free((caddr_t)(bp), M_DEVBUF)
57 
58 struct cd_softc {
59 	int		 sc_flags;		/* flags */
60 	size_t		 sc_size;		/* size of cd */
61 	int		 sc_ileave;		/* interleave */
62 	int		 sc_ncdisks;		/* number of components */
63 	struct cdcinfo	 sc_cinfo[NCDISKS];	/* component info */
64 	struct cdiinfo	 *sc_itable;		/* interleave table */
65 	int		 sc_usecnt;		/* number of requests active */
66 	struct buf	 *sc_bp;		/* "current" request */
67 	int		 sc_dk;			/* disk index */
68 } cd_softc[NCD];
69 
70 /* sc_flags */
71 #define	CDF_ALIVE	0x01
72 #define CDF_INITED	0x02
73 
74 cdinit(cd)
75 	struct cddevice *cd;
76 {
77 	register struct cd_softc *cs = &cd_softc[cd->cd_unit];
78 	register struct cdcinfo *ci;
79 	register size_t size;
80 	register int ix;
81 	size_t minsize;
82 	dev_t dev;
83 	struct bdevsw *bsw;
84 	int error;
85 
86 #ifdef DEBUG
87 	if (cddebug & (CDB_FOLLOW|CDB_INIT))
88 		printf("cdinit: unit %d\n", cd->cd_unit);
89 #endif
90 	cs->sc_dk = cd->cd_dk;
91 	cs->sc_size = 0;
92 	cs->sc_ileave = cd->cd_interleave;
93 	cs->sc_ncdisks = 0;
94 	/*
95 	 * Verify that each component piece exists and record
96 	 * relevant information about it.
97 	 */
98 	minsize = 0;
99 	for (ix = 0; ix < NCDISKS; ix++) {
100 		if ((dev = cd->cd_dev[ix]) == NODEV)
101 			break;
102 		ci = &cs->sc_cinfo[ix];
103 		ci->ci_dev = dev;
104 		bsw = &bdevsw[major(dev)];
105 		/*
106 		 * Open the partition
107 		 */
108 		if (bsw->d_open && (error = (*bsw->d_open)(dev, 0, S_IFBLK))) {
109 			printf("cd%d: component %s open failed, error = %d\n",
110 			       cd->cd_unit, cddevtostr(dev), error);
111 			return(0);
112 		}
113 		/*
114 		 * Calculate size (truncated to interleave boundary
115 		 * if necessary.
116 		 */
117 		if (bsw->d_psize) {
118 			size = (size_t) (*bsw->d_psize)(dev);
119 			if ((int)size < 0)
120 				size = 0;
121 		} else
122 			size = 0;
123 		if (cs->sc_ileave > 1)
124 			size -= size % cs->sc_ileave;
125 		if (size == 0) {
126 			printf("cd%d: not configured (component %s missing)\n",
127 			       cd->cd_unit, cddevtostr(dev));
128 			return(0);
129 		}
130 #ifdef COMPAT_NOLABEL
131 		/*
132 		 * XXX if this is a 'c' partition then we need to mark the
133 		 * label area writeable since there cannot be a label.
134 		 */
135 		if ((minor(dev) & 7) == 2 && bsw->d_open) {
136 			int i, flag;
137 
138 			for (i = 0; i < nchrdev; i++)
139 				if (cdevsw[i].d_open == bsw->d_open)
140 					break;
141 			if (i != nchrdev && cdevsw[i].d_ioctl) {
142 				flag = 1;
143 				(void)(*cdevsw[i].d_ioctl)(dev, DIOCWLABEL,
144 							   &flag, FWRITE);
145 			}
146 		}
147 #endif
148 		if (minsize == 0 || size < minsize)
149 			minsize = size;
150 		ci->ci_size = size;
151 		cs->sc_size += size;
152 		cs->sc_ncdisks++;
153 	}
154 	/*
155 	 * If uniform interleave is desired set all sizes to that of
156 	 * the smallest component.
157 	 */
158 	if (cd->cd_flags & CDF_UNIFORM) {
159 		for (ci = cs->sc_cinfo;
160 		     ci < &cs->sc_cinfo[cs->sc_ncdisks]; ci++)
161 			ci->ci_size = minsize;
162 		cs->sc_size = cs->sc_ncdisks * minsize;
163 	}
164 	/*
165 	 * Construct the interleave table
166 	 */
167 	if (!cdinterleave(cs))
168 		return(0);
169 	if (cd->cd_dk >= 0)
170 		dk_wpms[cd->cd_dk] = 32 * (60 * DEV_BSIZE / 2);	/* XXX */
171 	printf("cd%d: %d components ", cd->cd_unit, cs->sc_ncdisks);
172 	for (ix = 0; ix < cs->sc_ncdisks; ix++)
173 		printf("%c%s%c",
174 		       ix == 0 ? '(' : ' ',
175 		       cddevtostr(cs->sc_cinfo[ix].ci_dev),
176 		       ix == cs->sc_ncdisks - 1 ? ')' : ',');
177 	printf(", %d blocks ", cs->sc_size);
178 	if (cs->sc_ileave)
179 		printf("interleaved at %d blocks\n", cs->sc_ileave);
180 	else
181 		printf("concatenated\n");
182 	cs->sc_flags = CDF_ALIVE | CDF_INITED;
183 	return(1);
184 }
185 
186 /*
187  * XXX not really cd specific.
188  */
189 char *
190 cddevtostr(dev)
191 	dev_t dev;
192 {
193 	static char dbuf[5];
194 
195 	dbuf[1] = 'd';
196 	switch (major(dev)) {
197 	case 2:
198 		dbuf[0] = 'r';
199 		break;
200 	case 4:
201 		dbuf[0] = 's';
202 		break;
203 	case 5:
204 		dbuf[0] = 'c';
205 		break;
206 	default:
207 		dbuf[0] = dbuf[1] = '?';
208 		break;
209 	}
210 	dbuf[2] = (minor(dev) >> 3) + '0';
211 	dbuf[3] = (minor(dev) & 7) + 'a';
212 	dbuf[4] = '\0';
213 	return (dbuf);
214 }
215 
216 cdinterleave(cs)
217 	register struct cd_softc *cs;
218 {
219 	register struct cdcinfo *ci, *smallci;
220 	register struct cdiinfo *ii;
221 	register daddr_t bn, lbn;
222 	register int ix;
223 	u_long size;
224 
225 #ifdef DEBUG
226 	if (cddebug & CDB_INIT)
227 		printf("cdinterleave(%x): ileave %d\n", cs, cs->sc_ileave);
228 #endif
229 	/*
230 	 * Allocate an interleave table.
231 	 * Chances are this is too big, but we don't care.
232 	 */
233 	size = (cs->sc_ncdisks + 1) * sizeof(struct cdiinfo);
234 	cs->sc_itable = (struct cdiinfo *)malloc(size, M_DEVBUF, M_WAITOK);
235 	bzero((caddr_t)cs->sc_itable, size);
236 	/*
237 	 * Trivial case: no interleave (actually interleave of disk size).
238 	 * Each table entry represent a single component in its entirety.
239 	 */
240 	if (cs->sc_ileave == 0) {
241 		bn = 0;
242 		ii = cs->sc_itable;
243 		for (ix = 0; ix < cs->sc_ncdisks; ix++) {
244 			ii->ii_ndisk = 1;
245 			ii->ii_startblk = bn;
246 			ii->ii_startoff = 0;
247 			ii->ii_index[0] = ix;
248 			bn += cs->sc_cinfo[ix].ci_size;
249 			ii++;
250 		}
251 		ii->ii_ndisk = 0;
252 #ifdef DEBUG
253 		if (cddebug & CDB_INIT)
254 			printiinfo(cs->sc_itable);
255 #endif
256 		return(1);
257 	}
258 	/*
259 	 * The following isn't fast or pretty; it doesn't have to be.
260 	 */
261 	size = 0;
262 	bn = lbn = 0;
263 	for (ii = cs->sc_itable; ; ii++) {
264 		/*
265 		 * Locate the smallest of the remaining components
266 		 */
267 		smallci = NULL;
268 		for (ci = cs->sc_cinfo;
269 		     ci < &cs->sc_cinfo[cs->sc_ncdisks]; ci++)
270 			if (ci->ci_size > size &&
271 			    (smallci == NULL ||
272 			     ci->ci_size < smallci->ci_size))
273 				smallci = ci;
274 		/*
275 		 * Nobody left, all done
276 		 */
277 		if (smallci == NULL) {
278 			ii->ii_ndisk = 0;
279 			break;
280 		}
281 		/*
282 		 * Record starting logical block and component offset
283 		 */
284 		ii->ii_startblk = bn / cs->sc_ileave;
285 		ii->ii_startoff = lbn;
286 		/*
287 		 * Determine how many disks take part in this interleave
288 		 * and record their indices.
289 		 */
290 		ix = 0;
291 		for (ci = cs->sc_cinfo;
292 		     ci < &cs->sc_cinfo[cs->sc_ncdisks]; ci++)
293 			if (ci->ci_size >= smallci->ci_size)
294 				ii->ii_index[ix++] = ci - cs->sc_cinfo;
295 		ii->ii_ndisk = ix;
296 		bn += ix * (smallci->ci_size - size);
297 		lbn = smallci->ci_size / cs->sc_ileave;
298 		size = smallci->ci_size;
299 	}
300 #ifdef DEBUG
301 	if (cddebug & CDB_INIT)
302 		printiinfo(cs->sc_itable);
303 #endif
304 	return(1);
305 }
306 
307 #ifdef DEBUG
308 printiinfo(ii)
309 	struct cdiinfo *ii;
310 {
311 	register int ix, i;
312 
313 	for (ix = 0; ii->ii_ndisk; ix++, ii++) {
314 		printf(" itab[%d]: #dk %d sblk %d soff %d",
315 		       ix, ii->ii_ndisk, ii->ii_startblk, ii->ii_startoff);
316 		for (i = 0; i < ii->ii_ndisk; i++)
317 			printf(" %d", ii->ii_index[i]);
318 		printf("\n");
319 	}
320 }
321 #endif
322 
323 cdopen(dev, flags)
324 	dev_t dev;
325 {
326 	int unit = cdunit(dev);
327 	register struct cd_softc *cs = &cd_softc[unit];
328 
329 #ifdef DEBUG
330 	if (cddebug & CDB_FOLLOW)
331 		printf("cdopen(%x, %x)\n", dev, flags);
332 #endif
333 	if (unit >= NCD || (cs->sc_flags & CDF_ALIVE) == 0)
334 		return(ENXIO);
335 	return(0);
336 }
337 
338 cdstrategy(bp)
339 	register struct buf *bp;
340 {
341 	register int unit = cdunit(bp->b_dev);
342 	register struct cd_softc *cs = &cd_softc[unit];
343 	register daddr_t bn;
344 	register int sz, s;
345 
346 #ifdef DEBUG
347 	if (cddebug & CDB_FOLLOW)
348 		printf("cdstrategy(%x): unit %d\n", bp, unit);
349 #endif
350 	if ((cs->sc_flags & CDF_INITED) == 0) {
351 		bp->b_error = ENXIO;
352 		bp->b_flags |= B_ERROR;
353 		goto done;
354 	}
355 	bn = bp->b_blkno;
356 	sz = howmany(bp->b_bcount, DEV_BSIZE);
357 	if (bn < 0 || bn + sz > cs->sc_size) {
358 		sz = cs->sc_size - bn;
359 		if (sz == 0) {
360 			bp->b_resid = bp->b_bcount;
361 			goto done;
362 		}
363 		if (sz < 0) {
364 			bp->b_error = EINVAL;
365 			bp->b_flags |= B_ERROR;
366 			goto done;
367 		}
368 		bp->b_bcount = dbtob(sz);
369 	}
370 	bp->b_resid = bp->b_bcount;
371 	/*
372 	 * "Start" the unit.
373 	 * XXX: the use of sc_bp is just to retain the "traditional"
374 	 * interface to the start routine.
375 	 */
376 	s = splbio();
377 	cs->sc_bp = bp;
378 	cdstart(unit);
379 	splx(s);
380 	return;
381 done:
382 	biodone(bp);
383 }
384 
385 cdstart(unit)
386 	int unit;
387 {
388 	register struct cd_softc *cs = &cd_softc[unit];
389 	register struct buf *bp = cs->sc_bp;
390 	register long bcount, rcount;
391 	struct buf *cbp;
392 	caddr_t addr;
393 	daddr_t bn;
394 
395 #ifdef DEBUG
396 	if (cddebug & CDB_FOLLOW)
397 		printf("cdstart(%d)\n", unit);
398 #endif
399 	/*
400 	 * Instumentation (not real meaningful)
401 	 */
402 	cs->sc_usecnt++;
403 	if (cs->sc_dk >= 0) {
404 		dk_busy |= 1 << cs->sc_dk;
405 		dk_xfer[cs->sc_dk]++;
406 		dk_wds[cs->sc_dk] += bp->b_bcount >> 6;
407 	}
408 	/*
409 	 * Allocate component buffers and fire off the requests
410 	 */
411 	bn = bp->b_blkno;
412 	addr = bp->b_un.b_addr;
413 	for (bcount = bp->b_bcount; bcount > 0; bcount -= rcount) {
414 		cbp = cdbuffer(cs, bp, bn, addr, bcount);
415 		rcount = cbp->b_bcount;
416 		(*bdevsw[major(cbp->b_dev)].d_strategy)(cbp);
417 		bn += btodb(rcount);
418 		addr += rcount;
419 	}
420 }
421 
422 /*
423  * Build a component buffer header.
424  */
425 struct buf *
426 cdbuffer(cs, bp, bn, addr, bcount)
427 	register struct cd_softc *cs;
428 	struct buf *bp;
429 	daddr_t bn;
430 	caddr_t addr;
431 	long bcount;
432 {
433 	register struct cdcinfo *ci;
434 	register struct buf *cbp;
435 	register daddr_t cbn, cboff;
436 
437 #ifdef DEBUG
438 	if (cddebug & CDB_IO)
439 		printf("cdbuffer(%x, %x, %d, %x, %d)\n",
440 		       cs, bp, bn, addr, bcount);
441 #endif
442 	/*
443 	 * Determine which component bn falls in.
444 	 */
445 	cbn = bn;
446 	cboff = 0;
447 	/*
448 	 * Serially concatenated
449 	 */
450 	if (cs->sc_ileave == 0) {
451 		register daddr_t sblk;
452 
453 		sblk = 0;
454 		for (ci = cs->sc_cinfo; cbn >= sblk + ci->ci_size; ci++)
455 			sblk += ci->ci_size;
456 		cbn -= sblk;
457 	}
458 	/*
459 	 * Interleaved
460 	 */
461 	else {
462 		register struct cdiinfo *ii;
463 		int cdisk, off;
464 
465 		cboff = cbn % cs->sc_ileave;
466 		cbn /= cs->sc_ileave;
467 		for (ii = cs->sc_itable; ii->ii_ndisk; ii++)
468 			if (ii->ii_startblk > cbn)
469 				break;
470 		ii--;
471 		off = cbn - ii->ii_startblk;
472 		if (ii->ii_ndisk == 1) {
473 			cdisk = ii->ii_index[0];
474 			cbn = ii->ii_startoff + off;
475 		} else {
476 			cdisk = ii->ii_index[off % ii->ii_ndisk];
477 			cbn = ii->ii_startoff + off / ii->ii_ndisk;
478 		}
479 		cbn *= cs->sc_ileave;
480 		ci = &cs->sc_cinfo[cdisk];
481 	}
482 	/*
483 	 * Fill in the component buf structure.
484 	 */
485 	cbp = getcbuf();
486 	cbp->b_flags = bp->b_flags | B_CALL;
487 	cbp->b_iodone = cdiodone;
488 	cbp->b_proc = bp->b_proc;
489 	cbp->b_dev = ci->ci_dev;
490 	cbp->b_blkno = cbn + cboff;
491 	cbp->b_un.b_addr = addr;
492 	cbp->b_vp = 0;
493 	if (cs->sc_ileave == 0)
494 		cbp->b_bcount = dbtob(ci->ci_size - cbn);
495 	else
496 		cbp->b_bcount = dbtob(cs->sc_ileave - cboff);
497 	if (cbp->b_bcount > bcount)
498 		cbp->b_bcount = bcount;
499 	/*
500 	 * XXX: context for cdiodone
501 	 */
502 	cbp->b_saveaddr = (caddr_t)bp;
503 	cbp->b_pfcent = ((cs - cd_softc) << 16) | (ci - cs->sc_cinfo);
504 #ifdef DEBUG
505 	if (cddebug & CDB_IO)
506 		printf(" dev %x(u%d): cbp %x bn %d addr %x bcnt %d\n",
507 		       ci->ci_dev, ci-cs->sc_cinfo, cbp, cbp->b_blkno,
508 		       cbp->b_un.b_addr, cbp->b_bcount);
509 #endif
510 	return(cbp);
511 }
512 
513 cdintr(unit)
514 	int unit;
515 {
516 	register struct cd_softc *cs = &cd_softc[unit];
517 	register struct buf *bp = cs->sc_bp;
518 
519 #ifdef DEBUG
520 	if (cddebug & CDB_FOLLOW)
521 		printf("cdintr(%d): bp %x\n", unit, bp);
522 #endif
523 	/*
524 	 * Request is done for better or worse, wakeup the top half.
525 	 */
526 	if (--cs->sc_usecnt == 0 && cs->sc_dk >= 0)
527 		dk_busy &= ~(1 << cs->sc_dk);
528 	if (bp->b_flags & B_ERROR)
529 		bp->b_resid = bp->b_bcount;
530 	biodone(bp);
531 }
532 
533 /*
534  * Called by biodone at interrupt time.
535  * Mark the component as done and if all components are done,
536  * take a cd interrupt.
537  */
538 cdiodone(cbp)
539 	register struct buf *cbp;
540 {
541 	register struct buf *bp = (struct buf *)cbp->b_saveaddr;/* XXX */
542 	register int unit = (cbp->b_pfcent >> 16) & 0xFFFF;	/* XXX */
543 	int count, s;
544 
545 	s = splbio();
546 #ifdef DEBUG
547 	if (cddebug & CDB_FOLLOW)
548 		printf("cdiodone(%x)\n", cbp);
549 	if (cddebug & CDB_IO) {
550 		printf("cdiodone: bp %x bcount %d resid %d\n",
551 		       bp, bp->b_bcount, bp->b_resid);
552 		printf(" dev %x(u%d), cbp %x bn %d addr %x bcnt %d\n",
553 		       cbp->b_dev, cbp->b_pfcent & 0xFFFF, cbp,
554 		       cbp->b_blkno, cbp->b_un.b_addr, cbp->b_bcount);
555 	}
556 #endif
557 
558 	if (cbp->b_flags & B_ERROR) {
559 		bp->b_flags |= B_ERROR;
560 		bp->b_error = biowait(cbp);
561 #ifdef DEBUG
562 		printf("cd%d: error %d on component %d\n",
563 		       unit, bp->b_error, cbp->b_pfcent & 0xFFFF);
564 #endif
565 	}
566 	count = cbp->b_bcount;
567 	putcbuf(cbp);
568 
569 	/*
570 	 * If all done, "interrupt".
571 	 * Again, sc_bp is only used to preserve the traditional interface.
572 	 */
573 	bp->b_resid -= count;
574 	if (bp->b_resid < 0)
575 		panic("cdiodone: count");
576 	if (bp->b_resid == 0) {
577 		cd_softc[unit].sc_bp = bp;
578 		cdintr(unit);
579 	}
580 	splx(s);
581 }
582 
583 cdread(dev, uio)
584 	dev_t dev;
585 	struct uio *uio;
586 {
587 	register int unit = cdunit(dev);
588 
589 #ifdef DEBUG
590 	if (cddebug & CDB_FOLLOW)
591 		printf("cdread(%x, %x)\n", dev, uio);
592 #endif
593 	return(physio(cdstrategy, &cdbuf[unit], dev, B_READ, minphys, uio));
594 }
595 
596 cdwrite(dev, uio)
597 	dev_t dev;
598 	struct uio *uio;
599 {
600 	register int unit = cdunit(dev);
601 
602 #ifdef DEBUG
603 	if (cddebug & CDB_FOLLOW)
604 		printf("cdwrite(%x, %x)\n", dev, uio);
605 #endif
606 	return(physio(cdstrategy, &cdbuf[unit], dev, B_WRITE, minphys, uio));
607 }
608 
609 cdioctl(dev, cmd, data, flag)
610 	dev_t dev;
611 	int cmd;
612 	caddr_t data;
613 	int flag;
614 {
615 	return(EINVAL);
616 }
617 
618 cdsize(dev)
619 	dev_t dev;
620 {
621 	int unit = cdunit(dev);
622 	register struct cd_softc *cs = &cd_softc[unit];
623 
624 	if (unit >= NCD || (cs->sc_flags & CDF_INITED) == 0)
625 		return(-1);
626 	return(cs->sc_size);
627 }
628 
629 cddump(dev)
630 {
631 	return(ENXIO);
632 }
633 #endif
634