ccd.c revision 1.2 1 /* $NetBSD: ccd.c,v 1.2 1994/06/29 06:31:30 cgd Exp $ */
2
3 /*
4 * Copyright (c) 1988 University of Utah.
5 * Copyright (c) 1990 The Regents of the University of California.
6 * All rights reserved.
7 *
8 * This code is derived from software contributed to Berkeley by
9 * the Systems Programming Group of the University of Utah Computer
10 * Science Department.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 * 3. All advertising materials mentioning features or use of this software
21 * must display the following acknowledgement:
22 * This product includes software developed by the University of
23 * California, Berkeley and its contributors.
24 * 4. Neither the name of the University nor the names of its contributors
25 * may be used to endorse or promote products derived from this software
26 * without specific prior written permission.
27 *
28 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
29 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
32 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38 * SUCH DAMAGE.
39 *
40 * from: Utah $Hdr: cd.c 1.6 90/11/28$
41 *
42 * @(#)cd.c 7.4 (Berkeley) 5/7/91
43 */
44
45 /*
46 * "Concatenated" disk driver.
47 */
48 #include "ccd.h"
49 #if NCCD > 0
50
51 #include <sys/param.h>
52 #include <sys/systm.h>
53 #include <sys/errno.h>
54 #include <sys/dkstat.h>
55 #include <sys/buf.h>
56 #include <sys/malloc.h>
57 #include <sys/conf.h>
58
59 #include <dev/ccdvar.h>
60
61 #ifdef DEBUG
62 int ccddebug = 0x00;
63 #define CDB_FOLLOW 0x01
64 #define CDB_INIT 0x02
65 #define CDB_IO 0x04
66 #endif
67
68 struct buf ccdbuf[NCCD];
69 struct buf *ccdbuffer();
70 char *ccddevtostr();
71 int ccdiodone();
72
73 #define ccdunit(x) ((minor(x) >> 3) & 0x7) /* for consistency */
74
75 #define getcbuf() \
76 ((struct buf *)malloc(sizeof(struct buf), M_DEVBUF, M_WAITOK))
77 #define putcbuf(bp) \
78 free((caddr_t)(bp), M_DEVBUF)
79
80 struct ccd_softc {
81 int sc_flags; /* flags */
82 size_t sc_size; /* size of ccd */
83 int sc_ileave; /* interleave */
84 int sc_nccdisks; /* number of components */
85 struct ccdcinfo sc_cinfo[NCCDISKS]; /* component info */
86 struct ccdiinfo *sc_itable; /* interleave table */
87 int sc_usecnt; /* number of requests active */
88 struct buf *sc_bp; /* "current" request */
89 int sc_dk; /* disk index */
90 } ccd_softc[NCCD];
91
92 /* sc_flags */
93 #define CDF_ALIVE 0x01
94 #define CDF_INITED 0x02
95
96 /*
97 * ccdattach() is called at boot time in new systems. We do
98 * nothing here since old systems will not call this.
99 */
100
101 void
102 ccdattach(n)
103 int n;
104 {
105 }
106
107 ccdinit(ccd)
108 struct ccddevice *ccd;
109 {
110 register struct ccd_softc *cs = &ccd_softc[ccd->ccd_unit];
111 register struct ccdcinfo *ci;
112 register size_t size;
113 register int ix;
114 size_t minsize;
115 dev_t dev;
116
117 #ifdef DEBUG
118 if (ccddebug & (CDB_FOLLOW|CDB_INIT))
119 printf("ccdinit: unit %d\n", ccd->ccd_unit);
120 #endif
121 cs->sc_dk = ccd->ccd_dk;
122 cs->sc_size = 0;
123 cs->sc_ileave = ccd->ccd_interleave;
124 cs->sc_nccdisks = 0;
125 /*
126 * Verify that each component piece exists and record
127 * relevant information about it.
128 */
129 minsize = 0;
130 for (ix = 0; ix < NCCDISKS; ix++) {
131 if ((dev = ccd->ccd_dev[ix]) == NODEV)
132 break;
133 ci = &cs->sc_cinfo[ix];
134 ci->ci_dev = dev;
135 /*
136 * Calculate size (truncated to interleave boundary
137 * if necessary.
138 */
139 if (bdevsw[major(dev)].d_psize) {
140 size = (size_t) (*bdevsw[major(dev)].d_psize)(dev);
141 if ((int)size < 0)
142 size = 0;
143 } else
144 size = 0;
145 if (cs->sc_ileave > 1)
146 size -= size % cs->sc_ileave;
147 if (size == 0) {
148 printf("ccd%d: not configured (component %s missing)\n",
149 ccd->ccd_unit, ccddevtostr(ci->ci_dev));
150 return(0);
151 }
152 if (minsize == 0 || size < minsize)
153 minsize = size;
154 ci->ci_size = size;
155 cs->sc_size += size;
156 cs->sc_nccdisks++;
157 }
158 /*
159 * If uniform interleave is desired set all sizes to that of
160 * the smallest component.
161 */
162 if (ccd->ccd_flags & CDF_UNIFORM) {
163 for (ci = cs->sc_cinfo;
164 ci < &cs->sc_cinfo[cs->sc_nccdisks]; ci++)
165 ci->ci_size = minsize;
166 cs->sc_size = cs->sc_nccdisks * minsize;
167 }
168 /*
169 * Construct the interleave table
170 */
171 if (!ccdinterleave(cs))
172 return(0);
173 if (ccd->ccd_dk >= 0)
174 dk_wpms[ccd->ccd_dk] = 32 * (60 * DEV_BSIZE / 2); /* XXX */
175 printf("ccd%d: %d components ", ccd->ccd_unit, cs->sc_nccdisks);
176 for (ix = 0; ix < cs->sc_nccdisks; ix++)
177 printf("%c%s%c",
178 ix == 0 ? '(' : ' ',
179 ccddevtostr(cs->sc_cinfo[ix].ci_dev),
180 ix == cs->sc_nccdisks - 1 ? ')' : ',');
181 printf(", %d blocks ", cs->sc_size);
182 if (cs->sc_ileave)
183 printf("interleaved at %d blocks\n", cs->sc_ileave);
184 else
185 printf("concatenated\n");
186 cs->sc_flags = CDF_ALIVE | CDF_INITED;
187 return(1);
188 }
189
190 /*
191 * XXX not really ccd specific.
192 */
193 char *
194 ccddevtostr(dev)
195 dev_t dev;
196 {
197 static char dbuf[5];
198
199 dbuf[1] = 'd';
200 switch (major(dev)) {
201 case 2:
202 dbuf[0] = 'r';
203 break;
204 case 4:
205 dbuf[0] = 's';
206 break;
207 case 5:
208 dbuf[0] = 'c';
209 break;
210 default:
211 dbuf[0] = dbuf[1] = '?';
212 break;
213 }
214 dbuf[2] = (minor(dev) >> 3) + '0';
215 dbuf[3] = (minor(dev) & 7) + 'a';
216 dbuf[4] = '\0';
217 return (dbuf);
218 }
219
220 ccdinterleave(cs)
221 register struct ccd_softc *cs;
222 {
223 register struct ccdcinfo *ci, *smallci;
224 register struct ccdiinfo *ii;
225 register daddr_t bn, lbn;
226 register int ix;
227 u_long size;
228
229 #ifdef DEBUG
230 if (ccddebug & CDB_INIT)
231 printf("ccdinterleave(%x): ileave %d\n", cs, cs->sc_ileave);
232 #endif
233 /*
234 * Allocate an interleave table.
235 * Chances are this is too big, but we don't care.
236 */
237 size = (cs->sc_nccdisks + 1) * sizeof(struct ccdiinfo);
238 cs->sc_itable = (struct ccdiinfo *)malloc(size, M_DEVBUF, M_WAITOK);
239 bzero((caddr_t)cs->sc_itable, size);
240 /*
241 * Trivial case: no interleave (actually interleave of disk size).
242 * Each table entry represent a single component in its entirety.
243 */
244 if (cs->sc_ileave == 0) {
245 bn = 0;
246 ii = cs->sc_itable;
247 for (ix = 0; ix < cs->sc_nccdisks; ix++) {
248 ii->ii_ndisk = 1;
249 ii->ii_startblk = bn;
250 ii->ii_startoff = 0;
251 ii->ii_index[0] = ix;
252 bn += cs->sc_cinfo[ix].ci_size;
253 ii++;
254 }
255 ii->ii_ndisk = 0;
256 #ifdef DEBUG
257 if (ccddebug & CDB_INIT)
258 printiinfo(cs->sc_itable);
259 #endif
260 return(1);
261 }
262 /*
263 * The following isn't fast or pretty; it doesn't have to be.
264 */
265 size = 0;
266 bn = lbn = 0;
267 for (ii = cs->sc_itable; ; ii++) {
268 /*
269 * Locate the smallest of the remaining components
270 */
271 smallci = NULL;
272 for (ci = cs->sc_cinfo;
273 ci < &cs->sc_cinfo[cs->sc_nccdisks]; ci++)
274 if (ci->ci_size > size &&
275 (smallci == NULL ||
276 ci->ci_size < smallci->ci_size))
277 smallci = ci;
278 /*
279 * Nobody left, all done
280 */
281 if (smallci == NULL) {
282 ii->ii_ndisk = 0;
283 break;
284 }
285 /*
286 * Record starting logical block and component offset
287 */
288 ii->ii_startblk = bn / cs->sc_ileave;
289 ii->ii_startoff = lbn;
290 /*
291 * Determine how many disks take part in this interleave
292 * and record their indices.
293 */
294 ix = 0;
295 for (ci = cs->sc_cinfo;
296 ci < &cs->sc_cinfo[cs->sc_nccdisks]; ci++)
297 if (ci->ci_size >= smallci->ci_size)
298 ii->ii_index[ix++] = ci - cs->sc_cinfo;
299 ii->ii_ndisk = ix;
300 bn += ix * (smallci->ci_size - size);
301 lbn = smallci->ci_size / cs->sc_ileave;
302 size = smallci->ci_size;
303 }
304 #ifdef DEBUG
305 if (ccddebug & CDB_INIT)
306 printiinfo(cs->sc_itable);
307 #endif
308 return(1);
309 }
310
311 #ifdef DEBUG
312 printiinfo(ii)
313 struct ccdiinfo *ii;
314 {
315 register int ix, i;
316
317 for (ix = 0; ii->ii_ndisk; ix++, ii++) {
318 printf(" itab[%d]: #dk %d sblk %d soff %d",
319 ix, ii->ii_ndisk, ii->ii_startblk, ii->ii_startoff);
320 for (i = 0; i < ii->ii_ndisk; i++)
321 printf(" %d", ii->ii_index[i]);
322 printf("\n");
323 }
324 }
325 #endif
326
327 ccdopen(dev, flags)
328 dev_t dev;
329 {
330 int unit = ccdunit(dev);
331 register struct ccd_softc *cs = &ccd_softc[unit];
332
333 #ifdef DEBUG
334 if (ccddebug & CDB_FOLLOW)
335 printf("ccdopen(%x, %x)\n", dev, flags);
336 #endif
337 if (unit >= NCCD || (cs->sc_flags & CDF_ALIVE) == 0)
338 return(ENXIO);
339 return(0);
340 }
341
342 void
343 ccdstrategy(bp)
344 register struct buf *bp;
345 {
346 register int unit = ccdunit(bp->b_dev);
347 register struct ccd_softc *cs = &ccd_softc[unit];
348 register daddr_t bn;
349 register int sz, s;
350
351 #ifdef DEBUG
352 if (ccddebug & CDB_FOLLOW)
353 printf("ccdstrategy(%x): unit %d\n", bp, unit);
354 #endif
355 if ((cs->sc_flags & CDF_INITED) == 0) {
356 bp->b_error = ENXIO;
357 bp->b_flags |= B_ERROR;
358 goto done;
359 }
360 bn = bp->b_blkno;
361 sz = howmany(bp->b_bcount, DEV_BSIZE);
362 if (bn < 0 || bn + sz > cs->sc_size) {
363 sz = cs->sc_size - bn;
364 if (sz == 0) {
365 bp->b_resid = bp->b_bcount;
366 goto done;
367 }
368 if (sz < 0) {
369 bp->b_error = EINVAL;
370 bp->b_flags |= B_ERROR;
371 goto done;
372 }
373 bp->b_bcount = dbtob(sz);
374 }
375 bp->b_resid = bp->b_bcount;
376 /*
377 * "Start" the unit.
378 * XXX: the use of sc_bp is just to retain the "traditional"
379 * interface to the start routine.
380 */
381 s = splbio();
382 cs->sc_bp = bp;
383 ccdstart(unit);
384 splx(s);
385 return;
386 done:
387 biodone(bp);
388 }
389
390 ccdstart(unit)
391 int unit;
392 {
393 register struct ccd_softc *cs = &ccd_softc[unit];
394 register struct buf *bp = cs->sc_bp;
395 register long bcount, rcount;
396 struct buf *cbp;
397 caddr_t addr;
398 daddr_t bn;
399
400 #ifdef DEBUG
401 if (ccddebug & CDB_FOLLOW)
402 printf("ccdstart(%d)\n", unit);
403 #endif
404 /*
405 * Instumentation (not real meaningful)
406 */
407 cs->sc_usecnt++;
408 if (cs->sc_dk >= 0) {
409 dk_busy |= 1 << cs->sc_dk;
410 dk_xfer[cs->sc_dk]++;
411 dk_wds[cs->sc_dk] += bp->b_bcount >> 6;
412 }
413 /*
414 * Allocate component buffers and fire off the requests
415 */
416 bn = bp->b_blkno;
417 addr = bp->b_un.b_addr;
418 for (bcount = bp->b_bcount; bcount > 0; bcount -= rcount) {
419 cbp = ccdbuffer(cs, bp, bn, addr, bcount);
420 rcount = cbp->b_bcount;
421 (*bdevsw[major(cbp->b_dev)].d_strategy)(cbp);
422 bn += btodb(rcount);
423 addr += rcount;
424 }
425 }
426
427 /*
428 * Build a component buffer header.
429 */
430 struct buf *
431 ccdbuffer(cs, bp, bn, addr, bcount)
432 register struct ccd_softc *cs;
433 struct buf *bp;
434 daddr_t bn;
435 caddr_t addr;
436 long bcount;
437 {
438 register struct ccdcinfo *ci;
439 register struct buf *cbp;
440 register daddr_t cbn, cboff;
441
442 #ifdef DEBUG
443 if (ccddebug & CDB_IO)
444 printf("ccdbuffer(%x, %x, %d, %x, %d)\n",
445 cs, bp, bn, addr, bcount);
446 #endif
447 /*
448 * Determine which component bn falls in.
449 */
450 cbn = bn;
451 cboff = 0;
452 /*
453 * Serially concatenated
454 */
455 if (cs->sc_ileave == 0) {
456 register daddr_t sblk;
457
458 sblk = 0;
459 for (ci = cs->sc_cinfo; cbn >= sblk + ci->ci_size; ci++)
460 sblk += ci->ci_size;
461 cbn -= sblk;
462 }
463 /*
464 * Interleaved
465 */
466 else {
467 register struct ccdiinfo *ii;
468 int ccdisk, off;
469
470 cboff = cbn % cs->sc_ileave;
471 cbn /= cs->sc_ileave;
472 for (ii = cs->sc_itable; ii->ii_ndisk; ii++)
473 if (ii->ii_startblk > cbn)
474 break;
475 ii--;
476 off = cbn - ii->ii_startblk;
477 if (ii->ii_ndisk == 1) {
478 ccdisk = ii->ii_index[0];
479 cbn = ii->ii_startoff + off;
480 } else {
481 ccdisk = ii->ii_index[off % ii->ii_ndisk];
482 cbn = ii->ii_startoff + off / ii->ii_ndisk;
483 }
484 cbn *= cs->sc_ileave;
485 ci = &cs->sc_cinfo[ccdisk];
486 }
487 /*
488 * Fill in the component buf structure.
489 */
490 cbp = getcbuf();
491 cbp->b_flags = bp->b_flags | B_CALL;
492 cbp->b_iodone = ccdiodone;
493 cbp->b_proc = bp->b_proc;
494 cbp->b_dev = ci->ci_dev;
495 cbp->b_blkno = cbn + cboff;
496 cbp->b_un.b_addr = addr;
497 cbp->b_vp = 0;
498 if (cs->sc_ileave == 0)
499 cbp->b_bcount = dbtob(ci->ci_size - cbn);
500 else
501 cbp->b_bcount = dbtob(cs->sc_ileave - cboff);
502 if (cbp->b_bcount > bcount)
503 cbp->b_bcount = bcount;
504 /*
505 * XXX: context for ccdiodone
506 */
507 cbp->b_saveaddr = (caddr_t)bp;
508 cbp->b_pfcent = ((cs - ccd_softc) << 16) | (ci - cs->sc_cinfo);
509 #ifdef DEBUG
510 if (ccddebug & CDB_IO)
511 printf(" dev %x(u%d): cbp %x bn %d addr %x bcnt %d\n",
512 ci->ci_dev, ci-cs->sc_cinfo, cbp, cbp->b_blkno,
513 cbp->b_un.b_addr, cbp->b_bcount);
514 #endif
515 return(cbp);
516 }
517
518 ccdintr(unit)
519 int unit;
520 {
521 register struct ccd_softc *cs = &ccd_softc[unit];
522 register struct buf *bp = cs->sc_bp;
523
524 #ifdef DEBUG
525 if (ccddebug & CDB_FOLLOW)
526 printf("ccdintr(%d): bp %x\n", unit, bp);
527 #endif
528 /*
529 * Request is done for better or worse, wakeup the top half.
530 */
531 if (--cs->sc_usecnt == 0 && cs->sc_dk >= 0)
532 dk_busy &= ~(1 << cs->sc_dk);
533 if (bp->b_flags & B_ERROR)
534 bp->b_resid = bp->b_bcount;
535 biodone(bp);
536 }
537
538 /*
539 * Called by biodone at interrupt time.
540 * Mark the component as done and if all components are done,
541 * take a ccd interrupt.
542 */
543 ccdiodone(cbp)
544 register struct buf *cbp;
545 {
546 register struct buf *bp = (struct buf *)cbp->b_saveaddr;/* XXX */
547 register int unit = (cbp->b_pfcent >> 16) & 0xFFFF; /* XXX */
548 int count, s;
549
550 s = splbio();
551 #ifdef DEBUG
552 if (ccddebug & CDB_FOLLOW)
553 printf("ccdiodone(%x)\n", cbp);
554 if (ccddebug & CDB_IO) {
555 printf("ccdiodone: bp %x bcount %d resid %d\n",
556 bp, bp->b_bcount, bp->b_resid);
557 printf(" dev %x(u%d), cbp %x bn %d addr %x bcnt %d\n",
558 cbp->b_dev, cbp->b_pfcent & 0xFFFF, cbp,
559 cbp->b_blkno, cbp->b_un.b_addr, cbp->b_bcount);
560 }
561 #endif
562
563 if (cbp->b_flags & B_ERROR) {
564 bp->b_flags |= B_ERROR;
565 bp->b_error = biowait(cbp);
566 #ifdef DEBUG
567 printf("ccd%d: error %d on component %d\n",
568 unit, bp->b_error, cbp->b_pfcent & 0xFFFF);
569 #endif
570 }
571 count = cbp->b_bcount;
572 putcbuf(cbp);
573
574 /*
575 * If all done, "interrupt".
576 * Again, sc_bp is only used to preserve the traditional interface.
577 */
578 bp->b_resid -= count;
579 if (bp->b_resid < 0)
580 panic("ccdiodone: count");
581 if (bp->b_resid == 0) {
582 ccd_softc[unit].sc_bp = bp;
583 ccdintr(unit);
584 }
585 splx(s);
586 }
587
588 ccdioctl(dev, cmd, data, flag, p)
589 dev_t dev;
590 int cmd;
591 caddr_t data;
592 int flag;
593 struct proc *p;
594 {
595 return(EINVAL);
596 }
597
598 ccdsize(dev)
599 dev_t dev;
600 {
601 int unit = ccdunit(dev);
602 register struct ccd_softc *cs = &ccd_softc[unit];
603
604 if (unit >= NCCD || (cs->sc_flags & CDF_INITED) == 0)
605 return(-1);
606 return(cs->sc_size);
607 }
608
609 ccddump(dev)
610 {
611 return(ENXIO);
612 }
613 #endif
614