2 * Copyright (c) 2003 Poul-Henning Kamp.
3 * Copyright (c) 1995 Jason R. Thorpe.
4 * Copyright (c) 1990, 1993
5 * The Regents of the University of California. All rights reserved.
7 * Copyright (c) 1988 University of Utah.
9 * This code is derived from software contributed to Berkeley by
10 * the Systems Programming Group of the University of Utah Computer
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
16 * 1. Redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer.
18 * 2. Redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution.
21 * 3. All advertising materials mentioning features or use of this software
22 * must display the following acknowledgement:
23 * This product includes software developed for the NetBSD Project
25 * 4. The names of the authors may not be used to endorse or promote products
26 * derived from this software without specific prior written permission.
28 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
29 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
30 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
31 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
32 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
33 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
34 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
35 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
36 * 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
40 * Dynamic configuration and disklabel support by:
41 * Jason R. Thorpe <thorpej@nas.nasa.gov>
42 * Numerical Aerodynamic Simulation Facility
44 * NASA Ames Research Center
45 * Moffett Field, CA 94035
47 * from: Utah $Hdr: cd.c 1.6 90/11/28$
48 * @(#)cd.c 8.2 (Berkeley) 11/16/93
49 * $NetBSD: ccd.c,v 1.22 1995/12/08 19:13:26 thorpej Exp $
52 #include <sys/cdefs.h>
53 __FBSDID("$FreeBSD$");
55 #include <sys/param.h>
56 #include <sys/systm.h>
57 #include <sys/kernel.h>
58 #include <sys/module.h>
60 #include <sys/malloc.h>
62 #include <geom/geom.h>
65 * Number of blocks to untouched in front of a component partition.
66 * This is to avoid violating its disklabel area when it starts at the
67 * beginning of the slice.
69 #if !defined(CCD_OFFSET)
74 #define CCDF_UNIFORM 0x02 /* use LCCD of sizes for uniform interleave */
75 #define CCDF_MIRROR 0x04 /* use mirroring */
76 #define CCDF_NO_OFFSET 0x08 /* do not leave space in front */
77 #define CCDF_LINUX 0x10 /* use Linux compatibility mode */
79 /* Mask of user-settable ccd flags. */
80 #define CCDF_USERMASK (CCDF_UNIFORM|CCDF_MIRROR)
83 * Interleave description table.
84 * Computed at boot time to speed irregular-interleave lookups.
85 * The idea is that we interleave in "groups". First we interleave
86 * evenly over all component disks up to the size of the smallest
87 * component (the first group), then we interleave evenly over all
88 * remaining disks up to the size of the next-smallest (second group),
91 * Each table entry describes the interleave characteristics of one
92 * of these groups. For example if a concatenated disk consisted of
93 * three components of 5, 3, and 7 DEV_BSIZE blocks interleaved at
94 * DEV_BSIZE (1), the table would have three entries:
96 * ndisk startblk startoff dev
102 * which says that the first nine blocks (0-8) are interleaved over
103 * 3 disks (0, 1, 2) starting at block offset 0 on any component disk,
104 * the next 4 blocks (9-12) are interleaved over 2 disks (0, 2) starting
105 * at component block 3, and the remaining blocks (13-14) are on disk
106 * 2 starting at offset 5.
109 int ii_ndisk
; /* # of disks range is interleaved over */
110 daddr_t ii_startblk
; /* starting scaled block # for range */
111 daddr_t ii_startoff
; /* starting component offset (block #) */
112 int *ii_index
; /* ordered list of components in range */
116 * Component info table.
117 * Describes a single component of a concatenated disk.
120 daddr_t ci_size
; /* size */
121 struct g_provider
*ci_provider
; /* provider */
122 struct g_consumer
*ci_consumer
; /* consumer */
126 * A concatenated disk is described by this structure.
130 LIST_ENTRY(ccd_s
) list
;
132 int sc_unit
; /* logical unit number */
133 int sc_flags
; /* flags */
134 daddr_t sc_size
; /* size of ccd */
135 int sc_ileave
; /* interleave */
136 u_int sc_ndisks
; /* number of components */
137 struct ccdcinfo
*sc_cinfo
; /* component info */
138 struct ccdiinfo
*sc_itable
; /* interleave table */
139 u_int32_t sc_secsize
; /* # bytes per sector */
140 int sc_pick
; /* side of mirror picked */
141 daddr_t sc_blk
[2]; /* mirror localization */
142 u_int32_t sc_offset
; /* actual offset used */
145 static g_start_t g_ccd_start
;
146 static void ccdiodone(struct bio
*bp
);
147 static void ccdinterleave(struct ccd_s
*);
148 static int ccdinit(struct gctl_req
*req
, struct ccd_s
*);
149 static int ccdbuffer(struct bio
**ret
, struct ccd_s
*,
150 struct bio
*, daddr_t
, caddr_t
, long);
153 g_ccd_orphan(struct g_consumer
*cp
)
156 * XXX: We don't do anything here. It is not obvious
157 * XXX: what DTRT would be, so we do what the previous
158 * XXX: code did: ignore it and let the user cope.
163 g_ccd_access(struct g_provider
*pp
, int dr
, int dw
, int de
)
166 struct g_consumer
*cp1
, *cp2
;
174 LIST_FOREACH(cp1
, &gp
->consumer
, consumer
) {
175 error
= g_access(cp1
, dr
, dw
, de
);
177 LIST_FOREACH(cp2
, &gp
->consumer
, consumer
) {
180 g_access(cp2
, -dr
, -dw
, -de
);
189 * Free the softc and its substructures.
192 g_ccd_freesc(struct ccd_s
*sc
)
196 g_free(sc
->sc_cinfo
);
197 if (sc
->sc_itable
!= NULL
) {
198 for (ii
= sc
->sc_itable
; ii
->ii_ndisk
> 0; ii
++)
199 if (ii
->ii_index
!= NULL
)
200 g_free(ii
->ii_index
);
201 g_free(sc
->sc_itable
);
208 ccdinit(struct gctl_req
*req
, struct ccd_s
*cs
)
223 if (cs
->sc_flags
& CCDF_LINUX
) {
226 if (cs
->sc_flags
& CCDF_MIRROR
&& cs
->sc_ndisks
!= 2)
227 gctl_error(req
, "Mirror mode for Linux raids is "
228 "only supported with 2 devices");
230 if (cs
->sc_flags
& CCDF_NO_OFFSET
)
233 cs
->sc_offset
= CCD_OFFSET
;
236 for (ix
= 0; ix
< cs
->sc_ndisks
; ix
++) {
237 ci
= &cs
->sc_cinfo
[ix
];
239 mediasize
= ci
->ci_provider
->mediasize
;
240 sectorsize
= ci
->ci_provider
->sectorsize
;
241 if (sectorsize
> maxsecsize
)
242 maxsecsize
= sectorsize
;
243 size
= mediasize
/ DEV_BSIZE
- cs
->sc_offset
;
245 /* Truncate to interleave boundary */
247 if (cs
->sc_ileave
> 1)
248 size
-= size
% cs
->sc_ileave
;
251 gctl_error(req
, "Component %s has effective size zero",
252 ci
->ci_provider
->name
);
256 if (minsize
== 0 || size
< minsize
)
263 * Don't allow the interleave to be smaller than
264 * the biggest component sector.
266 if ((cs
->sc_ileave
> 0) &&
267 (cs
->sc_ileave
< (maxsecsize
/ DEV_BSIZE
))) {
268 gctl_error(req
, "Interleave to small for sector size");
273 * If uniform interleave is desired set all sizes to that of
274 * the smallest component. This will guarantee that a single
275 * interleave table is generated.
277 * Lost space must be taken into account when calculating the
278 * overall size. Half the space is lost when CCDF_MIRROR is
281 if (cs
->sc_flags
& CCDF_UNIFORM
) {
282 for (ix
= 0; ix
< cs
->sc_ndisks
; ix
++) {
283 ci
= &cs
->sc_cinfo
[ix
];
284 ci
->ci_size
= minsize
;
286 cs
->sc_size
= cs
->sc_ndisks
* minsize
;
289 if (cs
->sc_flags
& CCDF_MIRROR
) {
291 * Check to see if an even number of components
292 * have been specified. The interleave must also
293 * be non-zero in order for us to be able to
294 * guarantee the topology.
296 if (cs
->sc_ndisks
% 2) {
298 "Mirroring requires an even number of disks");
301 if (cs
->sc_ileave
== 0) {
303 "An interleave must be specified when mirroring");
306 cs
->sc_size
= (cs
->sc_ndisks
/2) * minsize
;
310 * Construct the interleave table.
315 * Create pseudo-geometry based on 1MB cylinders. It's
318 cs
->sc_secsize
= maxsecsize
;
324 ccdinterleave(struct ccd_s
*cs
)
326 struct ccdcinfo
*ci
, *smallci
;
334 * Allocate an interleave table. The worst case occurs when each
335 * of N disks is of a different size, resulting in N interleave
338 * Chances are this is too big, but we don't care.
340 size
= (cs
->sc_ndisks
+ 1) * sizeof(struct ccdiinfo
);
341 cs
->sc_itable
= g_malloc(size
, M_WAITOK
| M_ZERO
);
344 * Trivial case: no interleave (actually interleave of disk size).
345 * Each table entry represents a single component in its entirety.
347 * An interleave of 0 may not be used with a mirror setup.
349 if (cs
->sc_ileave
== 0) {
353 for (ix
= 0; ix
< cs
->sc_ndisks
; ix
++) {
354 /* Allocate space for ii_index. */
355 ii
->ii_index
= g_malloc(sizeof(int), M_WAITOK
);
357 ii
->ii_startblk
= bn
;
359 ii
->ii_index
[0] = ix
;
360 bn
+= cs
->sc_cinfo
[ix
].ci_size
;
368 * The following isn't fast or pretty; it doesn't have to be.
372 for (ii
= cs
->sc_itable
; ; ii
++) {
374 * Allocate space for ii_index. We might allocate more then
377 ii
->ii_index
= g_malloc((sizeof(int) * cs
->sc_ndisks
),
381 * Locate the smallest of the remaining components
384 for (ci
= cs
->sc_cinfo
; ci
< &cs
->sc_cinfo
[cs
->sc_ndisks
];
386 if (ci
->ci_size
> size
&&
388 ci
->ci_size
< smallci
->ci_size
)) {
394 * Nobody left, all done
396 if (smallci
== NULL
) {
398 g_free(ii
->ii_index
);
404 * Record starting logical block using an sc_ileave blocksize.
406 ii
->ii_startblk
= bn
/ cs
->sc_ileave
;
409 * Record starting component block using an sc_ileave
410 * blocksize. This value is relative to the beginning of
413 ii
->ii_startoff
= lbn
;
416 * Determine how many disks take part in this interleave
417 * and record their indices.
420 for (ci
= cs
->sc_cinfo
;
421 ci
< &cs
->sc_cinfo
[cs
->sc_ndisks
]; ci
++) {
422 if (ci
->ci_size
>= smallci
->ci_size
) {
423 ii
->ii_index
[ix
++] = ci
- cs
->sc_cinfo
;
427 bn
+= ix
* (smallci
->ci_size
- size
);
428 lbn
= smallci
->ci_size
/ cs
->sc_ileave
;
429 size
= smallci
->ci_size
;
434 g_ccd_start(struct bio
*bp
)
443 cs
= bp
->bio_to
->geom
->softc
;
446 * Block all GETATTR requests, we wouldn't know which of our
447 * subdevices we should ship it off to.
448 * XXX: this may not be the right policy.
450 if(bp
->bio_cmd
== BIO_GETATTR
) {
451 g_io_deliver(bp
, EINVAL
);
456 * Translate the partition-relative block number to an absolute.
458 bn
= bp
->bio_offset
/ cs
->sc_secsize
;
461 * Allocate component buffers and fire off the requests
464 for (bcount
= bp
->bio_length
; bcount
> 0; bcount
-= rcount
) {
465 err
= ccdbuffer(cbp
, cs
, bp
, bn
, addr
, bcount
);
467 bp
->bio_completed
+= bcount
;
468 if (bp
->bio_error
== 0)
470 if (bp
->bio_completed
== bp
->bio_length
)
471 g_io_deliver(bp
, bp
->bio_error
);
474 rcount
= cbp
[0]->bio_length
;
476 if (cs
->sc_flags
& CCDF_MIRROR
) {
478 * Mirroring. Writes go to both disks, reads are
479 * taken from whichever disk seems most appropriate.
481 * We attempt to localize reads to the disk whos arm
482 * is nearest the read request. We ignore seeks due
483 * to writes when making this determination and we
484 * also try to avoid hogging.
486 if (cbp
[0]->bio_cmd
!= BIO_READ
) {
487 g_io_request(cbp
[0], cbp
[0]->bio_from
);
488 g_io_request(cbp
[1], cbp
[1]->bio_from
);
490 int pick
= cs
->sc_pick
;
491 daddr_t range
= cs
->sc_size
/ 16;
493 if (bn
< cs
->sc_blk
[pick
] - range
||
494 bn
> cs
->sc_blk
[pick
] + range
496 cs
->sc_pick
= pick
= 1 - pick
;
498 cs
->sc_blk
[pick
] = bn
+ btodb(rcount
);
499 g_io_request(cbp
[pick
], cbp
[pick
]->bio_from
);
505 g_io_request(cbp
[0], cbp
[0]->bio_from
);
513 * Build a component buffer header.
516 ccdbuffer(struct bio
**cb
, struct ccd_s
*cs
, struct bio
*bp
, daddr_t bn
, caddr_t addr
, long bcount
)
518 struct ccdcinfo
*ci
, *ci2
= NULL
;
524 * Determine which component bn falls in.
529 if (cs
->sc_ileave
== 0) {
531 * Serially concatenated and neither a mirror nor a parity
532 * config. This is a special case.
537 for (ci
= cs
->sc_cinfo
; cbn
>= sblk
+ ci
->ci_size
; ci
++)
545 * Calculate cbn, the logical superblock (sc_ileave chunks),
546 * and cboff, a normal block offset (DEV_BSIZE chunks) relative
549 cboff
= cbn
% cs
->sc_ileave
; /* DEV_BSIZE gran */
550 cbn
= cbn
/ cs
->sc_ileave
; /* DEV_BSIZE * ileave gran */
553 * Figure out which interleave table to use.
555 for (ii
= cs
->sc_itable
; ii
->ii_ndisk
; ii
++) {
556 if (ii
->ii_startblk
> cbn
)
562 * off is the logical superblock relative to the beginning
563 * of this interleave block.
565 off
= cbn
- ii
->ii_startblk
;
568 * We must calculate which disk component to use (ccdisk),
569 * and recalculate cbn to be the superblock relative to
570 * the beginning of the component. This is typically done by
571 * adding 'off' and ii->ii_startoff together. However, 'off'
572 * must typically be divided by the number of components in
573 * this interleave array to be properly convert it from a
574 * CCD-relative logical superblock number to a
575 * component-relative superblock number.
577 if (ii
->ii_ndisk
== 1) {
579 * When we have just one disk, it can't be a mirror
580 * or a parity config.
582 ccdisk
= ii
->ii_index
[0];
583 cbn
= ii
->ii_startoff
+ off
;
585 if (cs
->sc_flags
& CCDF_MIRROR
) {
587 * We have forced a uniform mapping, resulting
588 * in a single interleave array. We double
589 * up on the first half of the available
590 * components and our mirror is in the second
591 * half. This only works with a single
592 * interleave array because doubling up
593 * doubles the number of sectors, so there
594 * cannot be another interleave array because
595 * the next interleave array's calculations
598 int ndisk2
= ii
->ii_ndisk
/ 2;
599 ccdisk
= ii
->ii_index
[off
% ndisk2
];
600 cbn
= ii
->ii_startoff
+ off
/ ndisk2
;
601 ci2
= &cs
->sc_cinfo
[ccdisk
+ ndisk2
];
603 ccdisk
= ii
->ii_index
[off
% ii
->ii_ndisk
];
604 cbn
= ii
->ii_startoff
+ off
/ ii
->ii_ndisk
;
608 ci
= &cs
->sc_cinfo
[ccdisk
];
611 * Convert cbn from a superblock to a normal block so it
612 * can be used to calculate (along with cboff) the normal
613 * block index into this particular disk.
615 cbn
*= cs
->sc_ileave
;
619 * Fill in the component buf structure.
621 cbp
= g_clone_bio(bp
);
624 cbp
->bio_done
= g_std_done
;
625 cbp
->bio_offset
= dbtob(cbn
+ cboff
+ cs
->sc_offset
);
626 cbp
->bio_data
= addr
;
627 if (cs
->sc_ileave
== 0)
628 cbc
= dbtob((off_t
)(ci
->ci_size
- cbn
));
630 cbc
= dbtob((off_t
)(cs
->sc_ileave
- cboff
));
631 cbp
->bio_length
= (cbc
< bcount
) ? cbc
: bcount
;
633 cbp
->bio_from
= ci
->ci_consumer
;
636 if (cs
->sc_flags
& CCDF_MIRROR
) {
637 cbp
= g_clone_bio(bp
);
640 cbp
->bio_done
= cb
[0]->bio_done
= ccdiodone
;
641 cbp
->bio_offset
= cb
[0]->bio_offset
;
642 cbp
->bio_data
= cb
[0]->bio_data
;
643 cbp
->bio_length
= cb
[0]->bio_length
;
644 cbp
->bio_from
= ci2
->ci_consumer
;
645 cbp
->bio_caller1
= cb
[0];
646 cb
[0]->bio_caller1
= cbp
;
653 * Called only for mirrored operations.
656 ccdiodone(struct bio
*cbp
)
658 struct bio
*mbp
, *pbp
;
660 mbp
= cbp
->bio_caller1
;
661 pbp
= cbp
->bio_parent
;
663 if (pbp
->bio_cmd
== BIO_READ
) {
664 if (cbp
->bio_error
== 0) {
665 /* We will not be needing the partner bio */
674 /* Try partner the bio instead */
675 mbp
->bio_caller1
= NULL
;
678 g_io_request(mbp
, mbp
->bio_from
);
680 * XXX: If this comes back OK, we should actually
681 * try to write the good data on the failed mirror
689 mbp
->bio_caller1
= NULL
;
691 if (cbp
->bio_error
!= 0 && pbp
->bio_error
== 0)
692 pbp
->bio_error
= cbp
->bio_error
;
700 g_ccd_create(struct gctl_req
*req
, struct g_class
*mp
)
702 int *unit
, *ileave
, *nprovider
;
704 struct g_consumer
*cp
;
705 struct g_provider
*pp
;
712 unit
= gctl_get_paraml(req
, "unit", sizeof (*unit
));
714 gctl_error(req
, "unit parameter not given");
717 ileave
= gctl_get_paraml(req
, "ileave", sizeof (*ileave
));
718 if (ileave
== NULL
) {
719 gctl_error(req
, "ileave parameter not given");
722 nprovider
= gctl_get_paraml(req
, "nprovider", sizeof (*nprovider
));
723 if (nprovider
== NULL
) {
724 gctl_error(req
, "nprovider parameter not given");
728 /* Check for duplicate unit */
729 LIST_FOREACH(gp
, &mp
->geom
, geom
) {
731 if (sc
!= NULL
&& sc
->sc_unit
== *unit
) {
732 gctl_error(req
, "Unit %d already configured", *unit
);
737 if (*nprovider
<= 0) {
738 gctl_error(req
, "Bogus nprovider argument (= %d)", *nprovider
);
742 /* Check all providers are valid */
743 for (i
= 0; i
< *nprovider
; i
++) {
744 sprintf(buf
, "provider%d", i
);
745 pp
= gctl_get_provider(req
, buf
);
750 gp
= g_new_geomf(mp
, "ccd%d", *unit
);
751 sc
= g_malloc(sizeof *sc
, M_WAITOK
| M_ZERO
);
753 sc
->sc_ndisks
= *nprovider
;
755 /* Allocate space for the component info. */
756 sc
->sc_cinfo
= g_malloc(sc
->sc_ndisks
* sizeof(struct ccdcinfo
),
759 /* Create consumers and attach to all providers */
760 for (i
= 0; i
< *nprovider
; i
++) {
761 sprintf(buf
, "provider%d", i
);
762 pp
= gctl_get_provider(req
, buf
);
763 cp
= g_new_consumer(gp
);
764 error
= g_attach(cp
, pp
);
765 KASSERT(error
== 0, ("attach to %s failed", pp
->name
));
766 sc
->sc_cinfo
[i
].ci_consumer
= cp
;
767 sc
->sc_cinfo
[i
].ci_provider
= pp
;
771 sc
->sc_ileave
= *ileave
;
773 if (gctl_get_param(req
, "no_offset", NULL
))
774 sc
->sc_flags
|= CCDF_NO_OFFSET
;
775 if (gctl_get_param(req
, "linux", NULL
))
776 sc
->sc_flags
|= CCDF_LINUX
;
778 if (gctl_get_param(req
, "uniform", NULL
))
779 sc
->sc_flags
|= CCDF_UNIFORM
;
780 if (gctl_get_param(req
, "mirror", NULL
))
781 sc
->sc_flags
|= CCDF_MIRROR
;
783 if (sc
->sc_ileave
== 0 && (sc
->sc_flags
& CCDF_MIRROR
)) {
784 printf("%s: disabling mirror, interleave is 0\n", gp
->name
);
785 sc
->sc_flags
&= ~(CCDF_MIRROR
);
788 if ((sc
->sc_flags
& CCDF_MIRROR
) && !(sc
->sc_flags
& CCDF_UNIFORM
)) {
789 printf("%s: mirror/parity forces uniform flag\n", gp
->name
);
790 sc
->sc_flags
|= CCDF_UNIFORM
;
793 error
= ccdinit(req
, sc
);
797 g_wither_geom(gp
, ENXIO
);
801 pp
= g_new_providerf(gp
, "%s", gp
->name
);
802 pp
->mediasize
= sc
->sc_size
* (off_t
)sc
->sc_secsize
;
803 pp
->sectorsize
= sc
->sc_secsize
;
804 g_error_provider(pp
, 0);
806 sb
= sbuf_new_auto();
807 sbuf_printf(sb
, "ccd%d: %d components ", sc
->sc_unit
, *nprovider
);
808 for (i
= 0; i
< *nprovider
; i
++) {
809 sbuf_printf(sb
, "%s%s",
811 sc
->sc_cinfo
[i
].ci_provider
->name
);
813 sbuf_printf(sb
, "), %jd blocks ", (off_t
)pp
->mediasize
/ DEV_BSIZE
);
814 if (sc
->sc_ileave
!= 0)
815 sbuf_printf(sb
, "interleaved at %d blocks\n",
818 sbuf_printf(sb
, "concatenated\n");
820 gctl_set_param_err(req
, "output", sbuf_data(sb
), sbuf_len(sb
) + 1);
825 g_ccd_destroy_geom(struct gctl_req
*req
, struct g_class
*mp
, struct g_geom
*gp
)
827 struct g_provider
*pp
;
832 pp
= LIST_FIRST(&gp
->provider
);
833 if (sc
== NULL
|| pp
== NULL
)
835 if (pp
->acr
!= 0 || pp
->acw
!= 0 || pp
->ace
!= 0) {
836 gctl_error(req
, "%s is open(r%dw%de%d)", gp
->name
,
837 pp
->acr
, pp
->acw
, pp
->ace
);
842 g_wither_geom(gp
, ENXIO
);
847 g_ccd_list(struct gctl_req
*req
, struct g_class
*mp
)
854 up
= gctl_get_paraml(req
, "unit", sizeof (*up
));
856 gctl_error(req
, "unit parameter not given");
860 sb
= sbuf_new_auto();
861 LIST_FOREACH(gp
, &mp
->geom
, geom
) {
863 if (cs
== NULL
|| (unit
>= 0 && unit
!= cs
->sc_unit
))
865 sbuf_printf(sb
, "ccd%d\t\t%d\t%d\t",
866 cs
->sc_unit
, cs
->sc_ileave
, cs
->sc_flags
& CCDF_USERMASK
);
868 for (i
= 0; i
< cs
->sc_ndisks
; ++i
) {
869 sbuf_printf(sb
, "%s/dev/%s", i
== 0 ? "" : " ",
870 cs
->sc_cinfo
[i
].ci_provider
->name
);
872 sbuf_printf(sb
, "\n");
875 gctl_set_param_err(req
, "output", sbuf_data(sb
), sbuf_len(sb
) + 1);
880 g_ccd_config(struct gctl_req
*req
, struct g_class
*mp
, char const *verb
)
885 if (!strcmp(verb
, "create geom")) {
886 g_ccd_create(req
, mp
);
887 } else if (!strcmp(verb
, "destroy geom")) {
888 gp
= gctl_get_geom(req
, mp
, "geom");
890 g_ccd_destroy_geom(req
, mp
, gp
);
891 } else if (!strcmp(verb
, "list")) {
894 gctl_error(req
, "unknown verb");
898 static struct g_class g_ccd_class
= {
900 .version
= G_VERSION
,
901 .ctlreq
= g_ccd_config
,
902 .destroy_geom
= g_ccd_destroy_geom
,
903 .start
= g_ccd_start
,
904 .orphan
= g_ccd_orphan
,
905 .access
= g_ccd_access
,
908 DECLARE_GEOM_CLASS(g_ccd_class
, g_ccd
);