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