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