1 /* $FreeBSD: src/sys/dev/ccd/ccd.c,v 1.73.2.1 2001/09/11 09:49:52 kris Exp $ */
2 /* $DragonFly: src/sys/dev/disk/ccd/ccd.c,v 1.24 2006/03/24 18:35:32 dillon Exp $ */
4 /* $NetBSD: ccd.c,v 1.22 1995/12/08 19:13:26 thorpej Exp $ */
7 * Copyright (c) 1995 Jason R. Thorpe.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
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 for the NetBSD Project
22 * 4. The name of the author may not be used to endorse or promote products
23 * derived from this software without specific prior written permission.
25 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
26 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
27 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
28 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
29 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
30 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
31 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
32 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
33 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
39 * Copyright (c) 1988 University of Utah.
40 * Copyright (c) 1990, 1993
41 * The Regents of the University of California. All rights reserved.
43 * This code is derived from software contributed to Berkeley by
44 * the Systems Programming Group of the University of Utah Computer
47 * Redistribution and use in source and binary forms, with or without
48 * modification, are permitted provided that the following conditions
50 * 1. Redistributions of source code must retain the above copyright
51 * notice, this list of conditions and the following disclaimer.
52 * 2. Redistributions in binary form must reproduce the above copyright
53 * notice, this list of conditions and the following disclaimer in the
54 * documentation and/or other materials provided with the distribution.
55 * 3. All advertising materials mentioning features or use of this software
56 * must display the following acknowledgement:
57 * This product includes software developed by the University of
58 * California, Berkeley and its contributors.
59 * 4. Neither the name of the University nor the names of its contributors
60 * may be used to endorse or promote products derived from this software
61 * without specific prior written permission.
63 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
64 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
65 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
66 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
67 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
68 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
69 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
70 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
71 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
72 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
75 * from: Utah $Hdr: cd.c 1.6 90/11/28$
77 * @(#)cd.c 8.2 (Berkeley) 11/16/93
81 * "Concatenated" disk driver.
83 * Dynamic configuration and disklabel support by:
84 * Jason R. Thorpe <thorpej@nas.nasa.gov>
85 * Numerical Aerodynamic Simulation Facility
87 * NASA Ames Research Center
88 * Moffett Field, CA 94035
93 #include <sys/param.h>
94 #include <sys/systm.h>
95 #include <sys/kernel.h>
96 #include <sys/module.h>
99 #include <sys/malloc.h>
100 #include <sys/nlookup.h>
101 #include <sys/conf.h>
102 #include <sys/stat.h>
103 #include <sys/sysctl.h>
104 #include <sys/disklabel.h>
105 #include <vfs/ufs/fs.h>
106 #include <sys/devicestat.h>
107 #include <sys/fcntl.h>
108 #include <sys/vnode.h>
109 #include <sys/buf2.h>
111 #include <sys/ccdvar.h>
113 #include <sys/thread2.h>
115 #include <vm/vm_zone.h>
117 #if defined(CCDDEBUG) && !defined(DEBUG)
122 #define CCDB_FOLLOW 0x01
123 #define CCDB_INIT 0x02
125 #define CCDB_LABEL 0x08
126 #define CCDB_VNODE 0x10
127 static int ccddebug = CCDB_FOLLOW | CCDB_INIT | CCDB_IO | CCDB_LABEL |
129 SYSCTL_INT(_debug, OID_AUTO, ccddebug, CTLFLAG_RW, &ccddebug, 0, "");
133 #define ccdunit(x) dkunit(x)
134 #define ccdpart(x) dkpart(x)
137 This is how mirroring works (only writes are special):
139 When initiating a write, ccdbuffer() returns two "struct ccdbuf *"s
140 linked together by the cb_mirror field. "cb_pflags &
141 CCDPF_MIRROR_DONE" is set to 0 on both of them.
143 When a component returns to ccdiodone(), it checks if "cb_pflags &
144 CCDPF_MIRROR_DONE" is set or not. If not, it sets the partner's
145 flag and returns. If it is, it means its partner has already
146 returned, so it will go to the regular cleanup.
151 struct buf cb_buf; /* new I/O buf */
152 struct bio *cb_obio; /* ptr. to original I/O buf */
153 struct ccdbuf *cb_freenext; /* free list link */
154 int cb_unit; /* target unit */
155 int cb_comp; /* target component */
156 int cb_pflags; /* mirror/parity status flag */
157 struct ccdbuf *cb_mirror; /* mirror counterpart */
160 /* bits in cb_pflags */
161 #define CCDPF_MIRROR_DONE 1 /* if set, mirror counterpart is done */
163 #define CCDLABELDEV(dev) \
164 (make_sub_dev(dev, dkmakeminor(ccdunit((dev)), 0, RAW_PART)))
166 static d_open_t ccdopen;
167 static d_close_t ccdclose;
168 static d_strategy_t ccdstrategy;
169 static d_ioctl_t ccdioctl;
170 static d_dump_t ccddump;
171 static d_psize_t ccdsize;
173 #define NCCDFREEHIWAT 16
175 #define CDEV_MAJOR 74
177 static struct cdevsw ccd_cdevsw = {
179 /* maj */ CDEV_MAJOR,
185 /* close */ ccdclose,
187 /* write */ physwrite,
188 /* ioctl */ ccdioctl,
191 /* strategy */ ccdstrategy,
196 /* called during module initialization */
197 static void ccdattach (void);
198 static int ccd_modevent (module_t, int, void *);
200 /* called by biodone() at interrupt time */
201 static void ccdiodone (struct bio *bio);
203 static void ccdstart (struct ccd_softc *, struct bio *);
204 static void ccdinterleave (struct ccd_softc *, int);
205 static void ccdintr (struct ccd_softc *, struct bio *);
206 static int ccdinit (struct ccddevice *, char **, struct thread *);
207 static int ccdlookup (char *, struct thread *td, struct vnode **);
208 static void ccdbuffer (struct ccdbuf **ret, struct ccd_softc *,
209 struct bio *, off_t, caddr_t, long);
210 static void ccdgetdisklabel (dev_t);
211 static void ccdmakedisklabel (struct ccd_softc *);
212 static int ccdlock (struct ccd_softc *);
213 static void ccdunlock (struct ccd_softc *);
216 static void printiinfo (struct ccdiinfo *);
219 /* Non-private for the benefit of libkvm. */
220 struct ccd_softc *ccd_softc;
221 struct ccddevice *ccddevs;
222 struct ccdbuf *ccdfreebufs;
223 static int numccdfreebufs;
224 static int numccd = 0;
227 * getccdbuf() - Allocate and zero a ccd buffer.
229 * This routine is called at splbio().
239 * Allocate from freelist or malloc as necessary
241 if ((cbp = ccdfreebufs) != NULL) {
242 ccdfreebufs = cbp->cb_freenext;
244 reinitbufbio(&cbp->cb_buf);
246 cbp = malloc(sizeof(struct ccdbuf), M_DEVBUF, M_WAITOK|M_ZERO);
247 initbufbio(&cbp->cb_buf);
251 * independant struct buf initialization
253 LIST_INIT(&cbp->cb_buf.b_dep);
254 BUF_LOCKINIT(&cbp->cb_buf);
255 BUF_LOCK(&cbp->cb_buf, LK_EXCLUSIVE);
256 BUF_KERNPROC(&cbp->cb_buf);
262 * putccdbuf() - Free a ccd buffer.
264 * This routine is called at splbio().
269 putccdbuf(struct ccdbuf *cbp)
271 BUF_UNLOCK(&cbp->cb_buf);
272 BUF_LOCKFREE(&cbp->cb_buf);
274 if (numccdfreebufs < NCCDFREEHIWAT) {
275 cbp->cb_freenext = ccdfreebufs;
279 free((caddr_t)cbp, M_DEVBUF);
285 * Number of blocks to untouched in front of a component partition.
286 * This is to avoid violating its disklabel area when it starts at the
287 * beginning of the slice.
289 #if !defined(CCD_OFFSET)
290 #define CCD_OFFSET 16
294 * Called by main() during pseudo-device attachment. All we need
295 * to do is allocate enough space for devices to be configured later, and
305 printf("ccd0-%d: Concatenated disk drivers\n", num-1);
307 printf("ccd0: Concatenated disk driver\n");
309 ccd_softc = malloc(num * sizeof(struct ccd_softc), M_DEVBUF,
311 ccddevs = malloc(num * sizeof(struct ccddevice), M_DEVBUF,
315 cdevsw_add(&ccd_cdevsw, 0, 0);
316 /* XXX: is this necessary? */
317 for (i = 0; i < numccd; ++i)
318 ccddevs[i].ccd_dk = -1;
322 ccd_modevent(module_t mod, int type, void *data)
332 printf("ccd0: Unload not supported!\n");
336 default: /* MOD_SHUTDOWN etc */
342 DEV_MODULE(ccd, ccd_modevent, NULL);
345 ccdinit(struct ccddevice *ccd, char **cpaths, struct thread *td)
347 struct ccd_softc *cs = &ccd_softc[ccd->ccd_unit];
348 struct ccdcinfo *ci = NULL; /* XXX */
354 struct partinfo dpart;
355 struct ccdgeom *ccg = &cs->sc_geom;
356 char tmppath[MAXPATHLEN];
360 KKASSERT(td->td_proc);
361 cred = td->td_proc->p_ucred;
364 if (ccddebug & (CCDB_FOLLOW|CCDB_INIT))
365 printf("ccdinit: unit %d\n", ccd->ccd_unit);
369 cs->sc_ileave = ccd->ccd_interleave;
370 cs->sc_nccdisks = ccd->ccd_ndev;
372 /* Allocate space for the component info. */
373 cs->sc_cinfo = malloc(cs->sc_nccdisks * sizeof(struct ccdcinfo),
377 * Verify that each component piece exists and record
378 * relevant information about it.
382 for (ix = 0; ix < cs->sc_nccdisks; ix++) {
383 vp = ccd->ccd_vpp[ix];
384 ci = &cs->sc_cinfo[ix];
388 * Copy in the pathname of the component.
390 bzero(tmppath, sizeof(tmppath)); /* sanity */
391 if ((error = copyinstr(cpaths[ix], tmppath,
392 MAXPATHLEN, &ci->ci_pathlen)) != 0) {
394 if (ccddebug & (CCDB_FOLLOW|CCDB_INIT))
395 printf("ccd%d: can't copy path, error = %d\n",
396 ccd->ccd_unit, error);
400 ci->ci_path = malloc(ci->ci_pathlen, M_DEVBUF, M_WAITOK);
401 bcopy(tmppath, ci->ci_path, ci->ci_pathlen);
403 ci->ci_dev = vn_todev(vp);
406 * Get partition information for the component.
408 if ((error = VOP_IOCTL(vp, DIOCGPART, (caddr_t)&dpart,
409 FREAD, cred, td)) != 0) {
411 if (ccddebug & (CCDB_FOLLOW|CCDB_INIT))
412 printf("ccd%d: %s: ioctl failed, error = %d\n",
413 ccd->ccd_unit, ci->ci_path, error);
417 if (dpart.part->p_fstype == FS_BSDFFS) {
419 ((dpart.disklab->d_secsize > maxsecsize) ?
420 dpart.disklab->d_secsize : maxsecsize);
421 size = dpart.part->p_size - CCD_OFFSET;
424 if (ccddebug & (CCDB_FOLLOW|CCDB_INIT))
425 printf("ccd%d: %s: incorrect partition type\n",
426 ccd->ccd_unit, ci->ci_path);
433 * Calculate the size, truncating to an interleave
434 * boundary if necessary.
437 if (cs->sc_ileave > 1)
438 size -= size % cs->sc_ileave;
442 if (ccddebug & (CCDB_FOLLOW|CCDB_INIT))
443 printf("ccd%d: %s: size == 0\n",
444 ccd->ccd_unit, ci->ci_path);
450 if (minsize == 0 || size < minsize)
457 * Don't allow the interleave to be smaller than
458 * the biggest component sector.
460 if ((cs->sc_ileave > 0) &&
461 (cs->sc_ileave < (maxsecsize / DEV_BSIZE))) {
463 if (ccddebug & (CCDB_FOLLOW|CCDB_INIT))
464 printf("ccd%d: interleave must be at least %d\n",
465 ccd->ccd_unit, (maxsecsize / DEV_BSIZE));
472 * If uniform interleave is desired set all sizes to that of
473 * the smallest component. This will guarentee that a single
474 * interleave table is generated.
476 * Lost space must be taken into account when calculating the
477 * overall size. Half the space is lost when CCDF_MIRROR is
478 * specified. One disk is lost when CCDF_PARITY is specified.
480 if (ccd->ccd_flags & CCDF_UNIFORM) {
481 for (ci = cs->sc_cinfo;
482 ci < &cs->sc_cinfo[cs->sc_nccdisks]; ci++) {
483 ci->ci_size = minsize;
485 if (ccd->ccd_flags & CCDF_MIRROR) {
487 * Check to see if an even number of components
488 * have been specified. The interleave must also
489 * be non-zero in order for us to be able to
490 * guarentee the topology.
492 if (cs->sc_nccdisks % 2) {
493 printf("ccd%d: mirroring requires an even number of disks\n", ccd->ccd_unit );
497 if (cs->sc_ileave == 0) {
498 printf("ccd%d: an interleave must be specified when mirroring\n", ccd->ccd_unit);
502 cs->sc_size = (cs->sc_nccdisks/2) * minsize;
503 } else if (ccd->ccd_flags & CCDF_PARITY) {
504 cs->sc_size = (cs->sc_nccdisks-1) * minsize;
506 if (cs->sc_ileave == 0) {
507 printf("ccd%d: an interleave must be specified when using parity\n", ccd->ccd_unit);
511 cs->sc_size = cs->sc_nccdisks * minsize;
516 * Construct the interleave table.
518 ccdinterleave(cs, ccd->ccd_unit);
521 * Create pseudo-geometry based on 1MB cylinders. It's
524 ccg->ccg_secsize = maxsecsize;
525 ccg->ccg_ntracks = 1;
526 ccg->ccg_nsectors = 1024 * 1024 / ccg->ccg_secsize;
527 ccg->ccg_ncylinders = cs->sc_size / ccg->ccg_nsectors;
530 * Add an devstat entry for this device.
532 devstat_add_entry(&cs->device_stats, "ccd", ccd->ccd_unit,
533 ccg->ccg_secsize, DEVSTAT_ALL_SUPPORTED,
534 DEVSTAT_TYPE_STORARRAY |DEVSTAT_TYPE_IF_OTHER,
535 DEVSTAT_PRIORITY_ARRAY);
537 cs->sc_flags |= CCDF_INITED;
538 cs->sc_cflags = ccd->ccd_flags; /* So we can find out later... */
539 cs->sc_unit = ccd->ccd_unit;
542 while (ci > cs->sc_cinfo) {
544 free(ci->ci_path, M_DEVBUF);
546 free(cs->sc_cinfo, M_DEVBUF);
551 ccdinterleave(struct ccd_softc *cs, int unit)
553 struct ccdcinfo *ci, *smallci;
560 if (ccddebug & CCDB_INIT)
561 printf("ccdinterleave(%x): ileave %d\n", cs, cs->sc_ileave);
565 * Allocate an interleave table. The worst case occurs when each
566 * of N disks is of a different size, resulting in N interleave
569 * Chances are this is too big, but we don't care.
571 size = (cs->sc_nccdisks + 1) * sizeof(struct ccdiinfo);
572 cs->sc_itable = (struct ccdiinfo *)malloc(size, M_DEVBUF, M_WAITOK);
573 bzero((caddr_t)cs->sc_itable, size);
576 * Trivial case: no interleave (actually interleave of disk size).
577 * Each table entry represents a single component in its entirety.
579 * An interleave of 0 may not be used with a mirror or parity setup.
581 if (cs->sc_ileave == 0) {
585 for (ix = 0; ix < cs->sc_nccdisks; ix++) {
586 /* Allocate space for ii_index. */
587 ii->ii_index = malloc(sizeof(int), M_DEVBUF, M_WAITOK);
589 ii->ii_startblk = bn;
591 ii->ii_index[0] = ix;
592 bn += cs->sc_cinfo[ix].ci_size;
597 if (ccddebug & CCDB_INIT)
598 printiinfo(cs->sc_itable);
604 * The following isn't fast or pretty; it doesn't have to be.
608 for (ii = cs->sc_itable; ; ii++) {
610 * Allocate space for ii_index. We might allocate more then
613 ii->ii_index = malloc((sizeof(int) * cs->sc_nccdisks),
617 * Locate the smallest of the remaining components
620 for (ci = cs->sc_cinfo; ci < &cs->sc_cinfo[cs->sc_nccdisks];
622 if (ci->ci_size > size &&
624 ci->ci_size < smallci->ci_size)) {
630 * Nobody left, all done
632 if (smallci == NULL) {
638 * Record starting logical block using an sc_ileave blocksize.
640 ii->ii_startblk = bn / cs->sc_ileave;
643 * Record starting comopnent block using an sc_ileave
644 * blocksize. This value is relative to the beginning of
647 ii->ii_startoff = lbn;
650 * Determine how many disks take part in this interleave
651 * and record their indices.
654 for (ci = cs->sc_cinfo;
655 ci < &cs->sc_cinfo[cs->sc_nccdisks]; ci++) {
656 if (ci->ci_size >= smallci->ci_size) {
657 ii->ii_index[ix++] = ci - cs->sc_cinfo;
661 bn += ix * (smallci->ci_size - size);
662 lbn = smallci->ci_size / cs->sc_ileave;
663 size = smallci->ci_size;
666 if (ccddebug & CCDB_INIT)
667 printiinfo(cs->sc_itable);
673 ccdopen(dev_t dev, int flags, int fmt, d_thread_t *td)
675 int unit = ccdunit(dev);
676 struct ccd_softc *cs;
677 struct disklabel *lp;
678 int error = 0, part, pmask;
681 if (ccddebug & CCDB_FOLLOW)
682 printf("ccdopen(%x, %x)\n", dev, flags);
686 cs = &ccd_softc[unit];
688 if ((error = ccdlock(cs)) != 0)
697 * If we're initialized, check to see if there are any other
698 * open partitions. If not, then it's safe to update
699 * the in-core disklabel.
701 if ((cs->sc_flags & CCDF_INITED) && (cs->sc_openmask == 0))
702 ccdgetdisklabel(dev);
704 /* Check that the partition exists. */
705 if (part != RAW_PART && ((part >= lp->d_npartitions) ||
706 (lp->d_partitions[part].p_fstype == FS_UNUSED))) {
711 cs->sc_openmask |= pmask;
719 ccdclose(dev_t dev, int flags, int fmt, d_thread_t *td)
721 int unit = ccdunit(dev);
722 struct ccd_softc *cs;
726 if (ccddebug & CCDB_FOLLOW)
727 printf("ccdclose(%x, %x)\n", dev, flags);
732 cs = &ccd_softc[unit];
734 if ((error = ccdlock(cs)) != 0)
739 /* ...that much closer to allowing unconfiguration... */
740 cs->sc_openmask &= ~(1 << part);
746 ccdstrategy(dev_t dev, struct bio *bio)
748 int unit = ccdunit(dev);
750 struct buf *bp = bio->bio_buf;
751 struct ccd_softc *cs = &ccd_softc[unit];
753 struct disklabel *lp;
756 if (ccddebug & CCDB_FOLLOW)
757 printf("ccdstrategy(%x): unit %d\n", bp, unit);
759 if ((cs->sc_flags & CCDF_INITED) == 0) {
761 bp->b_flags |= B_ERROR;
765 /* If it's a nil transfer, wake up the top half now. */
766 if (bp->b_bcount == 0)
772 * Do bounds checking and adjust transfer. If there's an
773 * error, the bounds check will flag that for us.
775 wlabel = cs->sc_flags & (CCDF_WLABEL|CCDF_LABELLING);
776 if (ccdpart(dev) != RAW_PART) {
777 nbio = bounds_check_with_label(dev, bio, lp, wlabel);
781 int pbn; /* in sc_secsize chunks */
782 long sz; /* in sc_secsize chunks */
784 pbn = (int)(bio->bio_offset / cs->sc_geom.ccg_secsize);
785 sz = howmany(bp->b_bcount, cs->sc_geom.ccg_secsize);
788 * If out of bounds return an error. If at the EOF point,
789 * simply read or write less.
792 if (pbn < 0 || pbn >= cs->sc_size) {
793 bp->b_resid = bp->b_bcount;
794 if (pbn != cs->sc_size) {
795 bp->b_error = EINVAL;
796 bp->b_flags |= B_ERROR | B_INVAL;
802 * If the request crosses EOF, truncate the request.
804 if (pbn + sz > cs->sc_size) {
805 bp->b_bcount = (cs->sc_size - pbn) *
806 cs->sc_geom.ccg_secsize;
811 bp->b_resid = bp->b_bcount;
812 nbio->bio_driver_info = dev;
823 * note: bio, not nbio, is valid at the done label.
830 ccdstart(struct ccd_softc *cs, struct bio *bio)
833 struct ccdbuf *cbp[4];
834 struct buf *bp = bio->bio_buf;
835 dev_t dev = bio->bio_driver_info;
836 /* XXX! : 2 reads and 2 writes for RAID 4/5 */
839 struct partition *pp;
842 if (ccddebug & CCDB_FOLLOW)
843 printf("ccdstart(%x, %x)\n", cs, bp);
846 /* Record the transaction start */
847 devstat_start_transaction(&cs->device_stats);
850 * Translate the partition-relative block number to an absolute.
852 doffset = bio->bio_offset;
853 if (ccdpart(dev) != RAW_PART) {
854 pp = &cs->sc_label.d_partitions[ccdpart(dev)];
855 doffset += pp->p_offset * cs->sc_label.d_secsize;
859 * Allocate component buffers and fire off the requests
862 for (bcount = bp->b_bcount; bcount > 0; bcount -= rcount) {
863 ccdbuffer(cbp, cs, bio, doffset, addr, bcount);
864 rcount = cbp[0]->cb_buf.b_bcount;
866 if (cs->sc_cflags & CCDF_MIRROR) {
868 * Mirroring. Writes go to both disks, reads are
869 * taken from whichever disk seems most appropriate.
871 * We attempt to localize reads to the disk whos arm
872 * is nearest the read request. We ignore seeks due
873 * to writes when making this determination and we
874 * also try to avoid hogging.
876 if ((cbp[0]->cb_buf.b_flags & B_READ) == 0) {
877 vn_strategy(cbp[0]->cb_buf.b_vp,
878 &cbp[0]->cb_buf.b_bio1);
879 vn_strategy(cbp[1]->cb_buf.b_vp,
880 &cbp[1]->cb_buf.b_bio1);
882 int pick = cs->sc_pick;
883 daddr_t range = cs->sc_size / 16 * cs->sc_label.d_secsize;
885 if (doffset < cs->sc_blk[pick] - range ||
886 doffset > cs->sc_blk[pick] + range
888 cs->sc_pick = pick = 1 - pick;
890 cs->sc_blk[pick] = doffset + rcount;
891 vn_strategy(cbp[pick]->cb_buf.b_vp,
892 &cbp[pick]->cb_buf.b_bio1);
898 vn_strategy(cbp[0]->cb_buf.b_vp,
899 &cbp[0]->cb_buf.b_bio1);
907 * Build a component buffer header.
910 ccdbuffer(struct ccdbuf **cb, struct ccd_softc *cs, struct bio *bio,
911 off_t doffset, caddr_t addr, long bcount)
913 struct ccdcinfo *ci, *ci2 = NULL; /* XXX */
915 daddr_t bn, cbn, cboff;
919 if (ccddebug & CCDB_IO)
920 printf("ccdbuffer(%x, %x, %d, %x, %d)\n",
921 cs, bp, bn, addr, bcount);
924 * Determine which component bn falls in.
926 bn = (daddr_t)(doffset / cs->sc_geom.ccg_secsize);
930 if (cs->sc_ileave == 0) {
932 * Serially concatenated and neither a mirror nor a parity
933 * config. This is a special case.
938 for (ci = cs->sc_cinfo; cbn >= sblk + ci->ci_size; ci++)
946 * Calculate cbn, the logical superblock (sc_ileave chunks),
947 * and cboff, a normal block offset (DEV_BSIZE chunks) relative
950 cboff = cbn % cs->sc_ileave; /* DEV_BSIZE gran */
951 cbn = cbn / cs->sc_ileave; /* DEV_BSIZE * ileave gran */
954 * Figure out which interleave table to use.
956 for (ii = cs->sc_itable; ii->ii_ndisk; ii++) {
957 if (ii->ii_startblk > cbn)
963 * off is the logical superblock relative to the beginning
964 * of this interleave block.
966 off = cbn - ii->ii_startblk;
969 * We must calculate which disk component to use (ccdisk),
970 * and recalculate cbn to be the superblock relative to
971 * the beginning of the component. This is typically done by
972 * adding 'off' and ii->ii_startoff together. However, 'off'
973 * must typically be divided by the number of components in
974 * this interleave array to be properly convert it from a
975 * CCD-relative logical superblock number to a
976 * component-relative superblock number.
978 if (ii->ii_ndisk == 1) {
980 * When we have just one disk, it can't be a mirror
981 * or a parity config.
983 ccdisk = ii->ii_index[0];
984 cbn = ii->ii_startoff + off;
986 if (cs->sc_cflags & CCDF_MIRROR) {
988 * We have forced a uniform mapping, resulting
989 * in a single interleave array. We double
990 * up on the first half of the available
991 * components and our mirror is in the second
992 * half. This only works with a single
993 * interleave array because doubling up
994 * doubles the number of sectors, so there
995 * cannot be another interleave array because
996 * the next interleave array's calculations
999 int ndisk2 = ii->ii_ndisk / 2;
1000 ccdisk = ii->ii_index[off % ndisk2];
1001 cbn = ii->ii_startoff + off / ndisk2;
1002 ci2 = &cs->sc_cinfo[ccdisk + ndisk2];
1003 } else if (cs->sc_cflags & CCDF_PARITY) {
1005 * XXX not implemented yet
1007 int ndisk2 = ii->ii_ndisk - 1;
1008 ccdisk = ii->ii_index[off % ndisk2];
1009 cbn = ii->ii_startoff + off / ndisk2;
1010 if (cbn % ii->ii_ndisk <= ccdisk)
1013 ccdisk = ii->ii_index[off % ii->ii_ndisk];
1014 cbn = ii->ii_startoff + off / ii->ii_ndisk;
1018 ci = &cs->sc_cinfo[ccdisk];
1021 * Convert cbn from a superblock to a normal block so it
1022 * can be used to calculate (along with cboff) the normal
1023 * block index into this particular disk.
1025 cbn *= cs->sc_ileave;
1029 * Fill in the component buf structure.
1032 cbp->cb_buf.b_flags = bio->bio_buf->b_flags;
1033 cbp->cb_buf.b_data = addr;
1034 cbp->cb_buf.b_vp = ci->ci_vp;
1035 if (cs->sc_ileave == 0)
1036 cbc = dbtob((off_t)(ci->ci_size - cbn));
1038 cbc = dbtob((off_t)(cs->sc_ileave - cboff));
1039 cbp->cb_buf.b_bcount = (cbc < bcount) ? cbc : bcount;
1040 cbp->cb_buf.b_bufsize = cbp->cb_buf.b_bcount;
1042 cbp->cb_buf.b_bio1.bio_done = ccdiodone;
1043 cbp->cb_buf.b_bio1.bio_caller_info1.ptr = cbp;
1044 cbp->cb_buf.b_bio1.bio_offset = dbtob(cbn + cboff + CCD_OFFSET);
1047 * context for ccdiodone
1050 cbp->cb_unit = cs - ccd_softc;
1051 cbp->cb_comp = ci - cs->sc_cinfo;
1054 if (ccddebug & CCDB_IO)
1055 printf(" dev %x(u%d): cbp %x off %lld addr %x bcnt %d\n",
1056 ci->ci_dev, ci-cs->sc_cinfo, cbp,
1057 cbp->cb_buf.b_bio1.bio_offset,
1058 cbp->cb_buf.b_data, cbp->cb_buf.b_bcount);
1063 * Note: both I/O's setup when reading from mirror, but only one
1066 if (cs->sc_cflags & CCDF_MIRROR) {
1067 /* mirror, setup second I/O */
1070 cbp->cb_buf.b_flags = bio->bio_buf->b_flags;
1071 cbp->cb_buf.b_data = addr;
1072 cbp->cb_buf.b_vp = ci2->ci_vp;
1073 if (cs->sc_ileave == 0)
1074 cbc = dbtob((off_t)(ci->ci_size - cbn));
1076 cbc = dbtob((off_t)(cs->sc_ileave - cboff));
1077 cbp->cb_buf.b_bcount = (cbc < bcount) ? cbc : bcount;
1078 cbp->cb_buf.b_bufsize = cbp->cb_buf.b_bcount;
1080 cbp->cb_buf.b_bio1.bio_done = ccdiodone;
1081 cbp->cb_buf.b_bio1.bio_caller_info1.ptr = cbp;
1082 cbp->cb_buf.b_bio1.bio_offset = dbtob(cbn + cboff + CCD_OFFSET);
1085 * context for ccdiodone
1088 cbp->cb_unit = cs - ccd_softc;
1089 cbp->cb_comp = ci2 - cs->sc_cinfo;
1091 /* link together the ccdbuf's and clear "mirror done" flag */
1092 cb[0]->cb_mirror = cb[1];
1093 cb[1]->cb_mirror = cb[0];
1094 cb[0]->cb_pflags &= ~CCDPF_MIRROR_DONE;
1095 cb[1]->cb_pflags &= ~CCDPF_MIRROR_DONE;
1100 ccdintr(struct ccd_softc *cs, struct bio *bio)
1102 struct buf *bp = bio->bio_buf;
1105 if (ccddebug & CCDB_FOLLOW)
1106 printf("ccdintr(%x, %x)\n", cs, bp);
1109 * Request is done for better or worse, wakeup the top half.
1111 if (bp->b_flags & B_ERROR)
1112 bp->b_resid = bp->b_bcount;
1113 devstat_end_transaction_buf(&cs->device_stats, bp);
1118 * Called at interrupt time.
1119 * Mark the component as done and if all components are done,
1120 * take a ccd interrupt.
1123 ccdiodone(struct bio *bio)
1125 struct ccdbuf *cbp = bio->bio_caller_info1.ptr;
1126 struct bio *obio = cbp->cb_obio;
1127 struct buf *obp = obio->bio_buf;
1128 int unit = cbp->cb_unit;
1132 * Since we do not have exclusive access to underlying devices,
1133 * we can't keep cache translations around.
1135 clearbiocache(bio->bio_next);
1139 if (ccddebug & CCDB_FOLLOW)
1140 printf("ccdiodone(%x)\n", cbp);
1141 if (ccddebug & CCDB_IO) {
1142 printf("ccdiodone: bp %x bcount %d resid %d\n",
1143 obp, obp->b_bcount, obp->b_resid);
1144 printf(" dev %x(u%d), cbp %x off %lld addr %x bcnt %d\n",
1145 cbp->cb_buf.b_dev, cbp->cb_comp, cbp,
1146 cbp->cb_buf.b_loffset, cbp->cb_buf.b_data,
1147 cbp->cb_buf.b_bcount);
1151 * If an error occured, report it. If this is a mirrored
1152 * configuration and the first of two possible reads, do not
1153 * set the error in the bp yet because the second read may
1156 if (cbp->cb_buf.b_flags & B_ERROR) {
1157 const char *msg = "";
1159 if ((ccd_softc[unit].sc_cflags & CCDF_MIRROR) &&
1160 (cbp->cb_buf.b_flags & B_READ) &&
1161 (cbp->cb_pflags & CCDPF_MIRROR_DONE) == 0) {
1163 * We will try our read on the other disk down
1164 * below, also reverse the default pick so if we
1165 * are doing a scan we do not keep hitting the
1168 struct ccd_softc *cs = &ccd_softc[unit];
1170 msg = ", trying other disk";
1171 cs->sc_pick = 1 - cs->sc_pick;
1172 cs->sc_blk[cs->sc_pick] = obio->bio_offset;
1174 obp->b_flags |= B_ERROR;
1175 obp->b_error = cbp->cb_buf.b_error ?
1176 cbp->cb_buf.b_error : EIO;
1178 printf("ccd%d: error %d on component %d offset %lld (ccd offset %lld)%s\n",
1179 unit, obp->b_error, cbp->cb_comp,
1180 cbp->cb_buf.b_bio2.bio_offset,
1181 obio->bio_offset, msg);
1185 * Process mirror. If we are writing, I/O has been initiated on both
1186 * buffers and we fall through only after both are finished.
1188 * If we are reading only one I/O is initiated at a time. If an
1189 * error occurs we initiate the second I/O and return, otherwise
1190 * we free the second I/O without initiating it.
1193 if (ccd_softc[unit].sc_cflags & CCDF_MIRROR) {
1194 if ((cbp->cb_buf.b_flags & B_READ) == 0) {
1196 * When writing, handshake with the second buffer
1197 * to determine when both are done. If both are not
1198 * done, return here.
1200 if ((cbp->cb_pflags & CCDPF_MIRROR_DONE) == 0) {
1201 cbp->cb_mirror->cb_pflags |= CCDPF_MIRROR_DONE;
1208 * When reading, either dispose of the second buffer
1209 * or initiate I/O on the second buffer if an error
1210 * occured with this one.
1212 if ((cbp->cb_pflags & CCDPF_MIRROR_DONE) == 0) {
1213 if (cbp->cb_buf.b_flags & B_ERROR) {
1214 cbp->cb_mirror->cb_pflags |=
1217 cbp->cb_mirror->cb_buf.b_vp,
1218 &cbp->cb_mirror->cb_buf.b_bio1
1224 putccdbuf(cbp->cb_mirror);
1232 * use b_bufsize to determine how big the original request was rather
1233 * then b_bcount, because b_bcount may have been truncated for EOF.
1235 * XXX We check for an error, but we do not test the resid for an
1236 * aligned EOF condition. This may result in character & block
1237 * device access not recognizing EOF properly when read or written
1238 * sequentially, but will not effect filesystems.
1240 count = cbp->cb_buf.b_bufsize;
1244 * If all done, "interrupt".
1246 obp->b_resid -= count;
1247 if (obp->b_resid < 0)
1248 panic("ccdiodone: count");
1249 if (obp->b_resid == 0)
1250 ccdintr(&ccd_softc[unit], obio);
1255 ccdioctl(dev_t dev, u_long cmd, caddr_t data, int flag, d_thread_t *td)
1257 int unit = ccdunit(dev);
1258 int i, j, lookedup = 0, error = 0;
1260 struct ccd_softc *cs;
1261 struct ccd_ioctl *ccio = (struct ccd_ioctl *)data;
1262 struct ccddevice ccd;
1267 KKASSERT(td->td_proc != NULL);
1268 cred = td->td_proc->p_ucred;
1272 cs = &ccd_softc[unit];
1274 bzero(&ccd, sizeof(ccd));
1278 if (cs->sc_flags & CCDF_INITED)
1281 if ((flag & FWRITE) == 0)
1284 if ((error = ccdlock(cs)) != 0)
1287 if (ccio->ccio_ndisks > CCD_MAXNDISKS)
1290 /* Fill in some important bits. */
1291 ccd.ccd_unit = unit;
1292 ccd.ccd_interleave = ccio->ccio_ileave;
1293 if (ccd.ccd_interleave == 0 &&
1294 ((ccio->ccio_flags & CCDF_MIRROR) ||
1295 (ccio->ccio_flags & CCDF_PARITY))) {
1296 printf("ccd%d: disabling mirror/parity, interleave is 0\n", unit);
1297 ccio->ccio_flags &= ~(CCDF_MIRROR | CCDF_PARITY);
1299 if ((ccio->ccio_flags & CCDF_MIRROR) &&
1300 (ccio->ccio_flags & CCDF_PARITY)) {
1301 printf("ccd%d: can't specify both mirror and parity, using mirror\n", unit);
1302 ccio->ccio_flags &= ~CCDF_PARITY;
1304 if ((ccio->ccio_flags & (CCDF_MIRROR | CCDF_PARITY)) &&
1305 !(ccio->ccio_flags & CCDF_UNIFORM)) {
1306 printf("ccd%d: mirror/parity forces uniform flag\n",
1308 ccio->ccio_flags |= CCDF_UNIFORM;
1310 ccd.ccd_flags = ccio->ccio_flags & CCDF_USERMASK;
1313 * Allocate space for and copy in the array of
1314 * componet pathnames and device numbers.
1316 cpp = malloc(ccio->ccio_ndisks * sizeof(char *),
1317 M_DEVBUF, M_WAITOK);
1318 vpp = malloc(ccio->ccio_ndisks * sizeof(struct vnode *),
1319 M_DEVBUF, M_WAITOK);
1321 error = copyin((caddr_t)ccio->ccio_disks, (caddr_t)cpp,
1322 ccio->ccio_ndisks * sizeof(char **));
1324 free(vpp, M_DEVBUF);
1325 free(cpp, M_DEVBUF);
1331 if (ccddebug & CCDB_INIT)
1332 for (i = 0; i < ccio->ccio_ndisks; ++i)
1333 printf("ccdioctl: component %d: 0x%x\n",
1337 for (i = 0; i < ccio->ccio_ndisks; ++i) {
1339 if (ccddebug & CCDB_INIT)
1340 printf("ccdioctl: lookedup = %d\n", lookedup);
1342 if ((error = ccdlookup(cpp[i], td, &vpp[i])) != 0) {
1343 for (j = 0; j < lookedup; ++j)
1344 (void)vn_close(vpp[j], FREAD|FWRITE, td);
1345 free(vpp, M_DEVBUF);
1346 free(cpp, M_DEVBUF);
1354 ccd.ccd_ndev = ccio->ccio_ndisks;
1357 * Initialize the ccd. Fills in the softc for us.
1359 if ((error = ccdinit(&ccd, cpp, td)) != 0) {
1360 for (j = 0; j < lookedup; ++j)
1361 (void)vn_close(vpp[j], FREAD|FWRITE, td);
1362 bzero(&ccd_softc[unit], sizeof(struct ccd_softc));
1363 free(vpp, M_DEVBUF);
1364 free(cpp, M_DEVBUF);
1370 * The ccd has been successfully initialized, so
1371 * we can place it into the array and read the disklabel.
1373 bcopy(&ccd, &ccddevs[unit], sizeof(ccd));
1374 ccio->ccio_unit = unit;
1375 ccio->ccio_size = cs->sc_size;
1376 ccdgetdisklabel(dev);
1383 if ((cs->sc_flags & CCDF_INITED) == 0)
1386 if ((flag & FWRITE) == 0)
1389 if ((error = ccdlock(cs)) != 0)
1392 /* Don't unconfigure if any other partitions are open */
1393 part = ccdpart(dev);
1394 pmask = (1 << part);
1395 if ((cs->sc_openmask & ~pmask)) {
1401 * Free ccd_softc information and clear entry.
1404 /* Close the components and free their pathnames. */
1405 for (i = 0; i < cs->sc_nccdisks; ++i) {
1407 * XXX: this close could potentially fail and
1408 * cause Bad Things. Maybe we need to force
1409 * the close to happen?
1412 if (ccddebug & CCDB_VNODE)
1413 vprint("CCDIOCCLR: vnode info",
1414 cs->sc_cinfo[i].ci_vp);
1416 (void)vn_close(cs->sc_cinfo[i].ci_vp, FREAD|FWRITE, td);
1417 free(cs->sc_cinfo[i].ci_path, M_DEVBUF);
1420 /* Free interleave index. */
1421 for (i = 0; cs->sc_itable[i].ii_ndisk; ++i)
1422 free(cs->sc_itable[i].ii_index, M_DEVBUF);
1424 /* Free component info and interleave table. */
1425 free(cs->sc_cinfo, M_DEVBUF);
1426 free(cs->sc_itable, M_DEVBUF);
1427 cs->sc_flags &= ~CCDF_INITED;
1430 * Free ccddevice information and clear entry.
1432 free(ccddevs[unit].ccd_cpp, M_DEVBUF);
1433 free(ccddevs[unit].ccd_vpp, M_DEVBUF);
1435 bcopy(&ccd, &ccddevs[unit], sizeof(ccd));
1438 * And remove the devstat entry.
1440 devstat_remove_entry(&cs->device_stats);
1442 /* This must be atomic. */
1445 bzero(cs, sizeof(struct ccd_softc));
1451 if ((cs->sc_flags & CCDF_INITED) == 0)
1454 *(struct disklabel *)data = cs->sc_label;
1458 if ((cs->sc_flags & CCDF_INITED) == 0)
1461 ((struct partinfo *)data)->disklab = &cs->sc_label;
1462 ((struct partinfo *)data)->part =
1463 &cs->sc_label.d_partitions[ccdpart(dev)];
1468 if ((cs->sc_flags & CCDF_INITED) == 0)
1471 if ((flag & FWRITE) == 0)
1474 if ((error = ccdlock(cs)) != 0)
1477 cs->sc_flags |= CCDF_LABELLING;
1479 error = setdisklabel(&cs->sc_label,
1480 (struct disklabel *)data, 0);
1482 if (cmd == DIOCWDINFO) {
1483 dev_t cdev = CCDLABELDEV(dev);
1484 error = writedisklabel(cdev, &cs->sc_label);
1488 cs->sc_flags &= ~CCDF_LABELLING;
1497 if ((cs->sc_flags & CCDF_INITED) == 0)
1500 if ((flag & FWRITE) == 0)
1502 if (*(int *)data != 0)
1503 cs->sc_flags |= CCDF_WLABEL;
1505 cs->sc_flags &= ~CCDF_WLABEL;
1518 struct ccd_softc *cs;
1521 if (ccdopen(dev, 0, S_IFCHR, curthread))
1524 cs = &ccd_softc[ccdunit(dev)];
1525 part = ccdpart(dev);
1527 if ((cs->sc_flags & CCDF_INITED) == 0)
1530 if (cs->sc_label.d_partitions[part].p_fstype != FS_SWAP)
1533 size = cs->sc_label.d_partitions[part].p_size;
1535 if (ccdclose(dev, 0, S_IFCHR, curthread))
1542 ccddump(dev_t dev, u_int count, u_int blkno, u_int secsize)
1544 /* Not implemented. */
1549 * Lookup the provided name in the filesystem. If the file exists,
1550 * is a valid block device, and isn't being used by anyone else,
1551 * set *vpp to the file's vnode.
1554 ccdlookup(char *path, struct thread *td, struct vnode **vpp)
1556 struct nlookupdata nd;
1561 KKASSERT(td->td_proc);
1562 cred = td->td_proc->p_ucred;
1565 error = nlookup_init(&nd, path, UIO_USERSPACE, NLC_FOLLOW|NLC_LOCKVP);
1568 if ((error = vn_open(&nd, NULL, FREAD|FWRITE, 0)) != 0) {
1570 if (ccddebug & CCDB_FOLLOW|CCDB_INIT)
1571 printf("ccdlookup: vn_open error = %d\n", error);
1577 if (vp->v_usecount > 1) {
1582 if (!vn_isdisk(vp, &error))
1586 if (ccddebug & CCDB_VNODE)
1587 vprint("ccdlookup: vnode info", vp);
1590 VOP_UNLOCK(vp, 0, td);
1591 nd.nl_open_vp = NULL;
1593 *vpp = vp; /* leave ref intact */
1601 * Read the disklabel from the ccd. If one is not present, fake one
1605 ccdgetdisklabel(dev_t dev)
1607 int unit = ccdunit(dev);
1608 struct ccd_softc *cs = &ccd_softc[unit];
1610 struct disklabel *lp = &cs->sc_label;
1611 struct ccdgeom *ccg = &cs->sc_geom;
1614 bzero(lp, sizeof(*lp));
1616 lp->d_secperunit = cs->sc_size;
1617 lp->d_secsize = ccg->ccg_secsize;
1618 lp->d_nsectors = ccg->ccg_nsectors;
1619 lp->d_ntracks = ccg->ccg_ntracks;
1620 lp->d_ncylinders = ccg->ccg_ncylinders;
1621 lp->d_secpercyl = lp->d_ntracks * lp->d_nsectors;
1623 strncpy(lp->d_typename, "ccd", sizeof(lp->d_typename));
1624 lp->d_type = DTYPE_CCD;
1625 strncpy(lp->d_packname, "fictitious", sizeof(lp->d_packname));
1627 lp->d_interleave = 1;
1630 lp->d_partitions[RAW_PART].p_offset = 0;
1631 lp->d_partitions[RAW_PART].p_size = cs->sc_size;
1632 lp->d_partitions[RAW_PART].p_fstype = FS_UNUSED;
1633 lp->d_npartitions = RAW_PART + 1;
1635 lp->d_bbsize = BBSIZE; /* XXX */
1636 lp->d_sbsize = SBSIZE; /* XXX */
1638 lp->d_magic = DISKMAGIC;
1639 lp->d_magic2 = DISKMAGIC;
1640 lp->d_checksum = dkcksum(&cs->sc_label);
1643 * Call the generic disklabel extraction routine.
1645 cdev = CCDLABELDEV(dev);
1646 errstring = readdisklabel(cdev, &cs->sc_label);
1647 if (errstring != NULL)
1648 ccdmakedisklabel(cs);
1651 /* It's actually extremely common to have unlabeled ccds. */
1652 if (ccddebug & CCDB_LABEL)
1653 if (errstring != NULL)
1654 printf("ccd%d: %s\n", unit, errstring);
1659 * Take care of things one might want to take care of in the event
1660 * that a disklabel isn't present.
1663 ccdmakedisklabel(struct ccd_softc *cs)
1665 struct disklabel *lp = &cs->sc_label;
1668 * For historical reasons, if there's no disklabel present
1669 * the raw partition must be marked FS_BSDFFS.
1671 lp->d_partitions[RAW_PART].p_fstype = FS_BSDFFS;
1673 strncpy(lp->d_packname, "default label", sizeof(lp->d_packname));
1677 * Wait interruptibly for an exclusive lock.
1680 * Several drivers do this; it should be abstracted and made MP-safe.
1683 ccdlock(struct ccd_softc *cs)
1687 while ((cs->sc_flags & CCDF_LOCKED) != 0) {
1688 cs->sc_flags |= CCDF_WANTED;
1689 if ((error = tsleep(cs, PCATCH, "ccdlck", 0)) != 0)
1692 cs->sc_flags |= CCDF_LOCKED;
1697 * Unlock and wake up any waiters.
1700 ccdunlock(struct ccd_softc *cs)
1703 cs->sc_flags &= ~CCDF_LOCKED;
1704 if ((cs->sc_flags & CCDF_WANTED) != 0) {
1705 cs->sc_flags &= ~CCDF_WANTED;
1712 printiinfo(struct ccdiinfo *ii)
1716 for (ix = 0; ii->ii_ndisk; ix++, ii++) {
1717 printf(" itab[%d]: #dk %d sblk %d soff %d",
1718 ix, ii->ii_ndisk, ii->ii_startblk, ii->ii_startoff);
1719 for (i = 0; i < ii->ii_ndisk; i++)
1720 printf(" %d", ii->ii_index[i]);
1727 /* Local Variables: */
1728 /* c-argdecl-indent: 8 */
1729 /* c-continued-statement-offset: 8 */
1730 /* c-indent-level: 8 */