/*-1* SPDX-License-Identifier: (BSD-2-Clause AND BSD-3-Clause)2*3* Copyright (c) 2003 Poul-Henning Kamp.4* Copyright (c) 1996, 1997 The NetBSD Foundation, Inc.5* All rights reserved.6*7* This code is derived from software contributed to The NetBSD Foundation8* by Jason R. Thorpe.9*10* Redistribution and use in source and binary forms, with or without11* modification, are permitted provided that the following conditions12* are met:13* 1. Redistributions of source code must retain the above copyright14* notice, this list of conditions and the following disclaimer.15* 2. Redistributions in binary form must reproduce the above copyright16* notice, this list of conditions and the following disclaimer in the17* documentation and/or other materials provided with the distribution.18*19* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS20* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED21* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR22* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS23* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR24* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF25* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS26* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN27* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)28* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE29* POSSIBILITY OF SUCH DAMAGE.30*31* $NetBSD: ccd.c,v 1.22 1995/12/08 19:13:26 thorpej Exp $32*/3334/*-35* Copyright (c) 1988 University of Utah.36* Copyright (c) 1990, 199337* The Regents of the University of California. All rights reserved.38*39* This code is derived from software contributed to Berkeley by40* the Systems Programming Group of the University of Utah Computer41* Science Department.42*43* Redistribution and use in source and binary forms, with or without44* modification, are permitted provided that the following conditions45* are met:46* 1. Redistributions of source code must retain the above copyright47* notice, this list of conditions and the following disclaimer.48* 2. Redistributions in binary form must reproduce the above copyright49* notice, this list of conditions and the following disclaimer in the50* documentation and/or other materials provided with the distribution.51* 3. Neither the name of the University nor the names of its contributors52* may be used to endorse or promote products derived from this software53* without specific prior written permission.54*55* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND56* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE57* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE58* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE59* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL60* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS61* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)62* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT63* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY64* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF65* SUCH DAMAGE.66*67* from: Utah $Hdr: cd.c 1.6 90/11/28$68*/6970/*71* Dynamic configuration and disklabel support by:72* Jason R. Thorpe <[email protected]>73* Numerical Aerodynamic Simulation Facility74* Mail Stop 258-675* NASA Ames Research Center76* Moffett Field, CA 9403577*/7879#include <sys/param.h>80#include <sys/systm.h>81#include <sys/kernel.h>82#include <sys/module.h>83#include <sys/bio.h>84#include <sys/malloc.h>85#include <sys/sbuf.h>86#include <geom/geom.h>8788/*89* Number of blocks to untouched in front of a component partition.90* This is to avoid violating its disklabel area when it starts at the91* beginning of the slice.92*/93#if !defined(CCD_OFFSET)94#define CCD_OFFSET 1695#endif9697/* sc_flags */98#define CCDF_UNIFORM 0x02 /* use LCCD of sizes for uniform interleave */99#define CCDF_MIRROR 0x04 /* use mirroring */100#define CCDF_NO_OFFSET 0x08 /* do not leave space in front */101#define CCDF_LINUX 0x10 /* use Linux compatibility mode */102103/* Mask of user-settable ccd flags. */104#define CCDF_USERMASK (CCDF_UNIFORM|CCDF_MIRROR)105106/*107* Interleave description table.108* Computed at boot time to speed irregular-interleave lookups.109* The idea is that we interleave in "groups". First we interleave110* evenly over all component disks up to the size of the smallest111* component (the first group), then we interleave evenly over all112* remaining disks up to the size of the next-smallest (second group),113* and so on.114*115* Each table entry describes the interleave characteristics of one116* of these groups. For example if a concatenated disk consisted of117* three components of 5, 3, and 7 DEV_BSIZE blocks interleaved at118* DEV_BSIZE (1), the table would have three entries:119*120* ndisk startblk startoff dev121* 3 0 0 0, 1, 2122* 2 9 3 0, 2123* 1 13 5 2124* 0 - - -125*126* which says that the first nine blocks (0-8) are interleaved over127* 3 disks (0, 1, 2) starting at block offset 0 on any component disk,128* the next 4 blocks (9-12) are interleaved over 2 disks (0, 2) starting129* at component block 3, and the remaining blocks (13-14) are on disk130* 2 starting at offset 5.131*/132struct ccdiinfo {133int ii_ndisk; /* # of disks range is interleaved over */134daddr_t ii_startblk; /* starting scaled block # for range */135daddr_t ii_startoff; /* starting component offset (block #) */136int *ii_index; /* ordered list of components in range */137};138139/*140* Component info table.141* Describes a single component of a concatenated disk.142*/143struct ccdcinfo {144daddr_t ci_size; /* size */145struct g_provider *ci_provider; /* provider */146struct g_consumer *ci_consumer; /* consumer */147};148149/*150* A concatenated disk is described by this structure.151*/152153struct ccd_s {154LIST_ENTRY(ccd_s) list;155156int sc_unit; /* logical unit number */157int sc_flags; /* flags */158daddr_t sc_size; /* size of ccd */159int sc_ileave; /* interleave */160u_int sc_ndisks; /* number of components */161struct ccdcinfo *sc_cinfo; /* component info */162struct ccdiinfo *sc_itable; /* interleave table */163uint32_t sc_secsize; /* # bytes per sector */164int sc_pick; /* side of mirror picked */165daddr_t sc_blk[2]; /* mirror localization */166uint32_t sc_offset; /* actual offset used */167};168169static g_start_t g_ccd_start;170static void ccdiodone(struct bio *bp);171static void ccdinterleave(struct ccd_s *);172static int ccdinit(struct gctl_req *req, struct ccd_s *);173static int ccdbuffer(struct bio **ret, struct ccd_s *,174struct bio *, daddr_t, caddr_t, long);175176static void177g_ccd_orphan(struct g_consumer *cp)178{179/*180* XXX: We don't do anything here. It is not obvious181* XXX: what DTRT would be, so we do what the previous182* XXX: code did: ignore it and let the user cope.183*/184}185186static int187g_ccd_access(struct g_provider *pp, int dr, int dw, int de)188{189struct g_geom *gp;190struct g_consumer *cp1, *cp2;191int error;192193de += dr;194de += dw;195196gp = pp->geom;197error = ENXIO;198LIST_FOREACH(cp1, &gp->consumer, consumer) {199error = g_access(cp1, dr, dw, de);200if (error) {201LIST_FOREACH(cp2, &gp->consumer, consumer) {202if (cp1 == cp2)203break;204g_access(cp2, -dr, -dw, -de);205}206break;207}208}209return (error);210}211212/*213* Free the softc and its substructures.214*/215static void216g_ccd_freesc(struct ccd_s *sc)217{218struct ccdiinfo *ii;219220g_free(sc->sc_cinfo);221if (sc->sc_itable != NULL) {222for (ii = sc->sc_itable; ii->ii_ndisk > 0; ii++)223g_free(ii->ii_index);224g_free(sc->sc_itable);225}226g_free(sc);227}228229static int230ccdinit(struct gctl_req *req, struct ccd_s *cs)231{232struct ccdcinfo *ci;233daddr_t size;234int ix;235daddr_t minsize;236int maxsecsize;237off_t mediasize;238u_int sectorsize;239240cs->sc_size = 0;241242maxsecsize = 0;243minsize = 0;244245if (cs->sc_flags & CCDF_LINUX) {246cs->sc_offset = 0;247cs->sc_ileave *= 2;248if (cs->sc_flags & CCDF_MIRROR && cs->sc_ndisks != 2)249gctl_error(req, "Mirror mode for Linux raids is "250"only supported with 2 devices");251} else {252if (cs->sc_flags & CCDF_NO_OFFSET)253cs->sc_offset = 0;254else255cs->sc_offset = CCD_OFFSET;256}257for (ix = 0; ix < cs->sc_ndisks; ix++) {258ci = &cs->sc_cinfo[ix];259260mediasize = ci->ci_provider->mediasize;261sectorsize = ci->ci_provider->sectorsize;262if (sectorsize > maxsecsize)263maxsecsize = sectorsize;264size = mediasize / DEV_BSIZE - cs->sc_offset;265266/* Truncate to interleave boundary */267268if (cs->sc_ileave > 1)269size -= size % cs->sc_ileave;270271if (size == 0) {272gctl_error(req, "Component %s has effective size zero",273ci->ci_provider->name);274return(ENODEV);275}276277if (minsize == 0 || size < minsize)278minsize = size;279ci->ci_size = size;280cs->sc_size += size;281}282283/*284* Don't allow the interleave to be smaller than285* the biggest component sector.286*/287if ((cs->sc_ileave > 0) &&288(cs->sc_ileave < (maxsecsize / DEV_BSIZE))) {289gctl_error(req, "Interleave to small for sector size");290return(EINVAL);291}292293/*294* If uniform interleave is desired set all sizes to that of295* the smallest component. This will guarantee that a single296* interleave table is generated.297*298* Lost space must be taken into account when calculating the299* overall size. Half the space is lost when CCDF_MIRROR is300* specified.301*/302if (cs->sc_flags & CCDF_UNIFORM) {303for (ix = 0; ix < cs->sc_ndisks; ix++) {304ci = &cs->sc_cinfo[ix];305ci->ci_size = minsize;306}307cs->sc_size = cs->sc_ndisks * minsize;308}309310if (cs->sc_flags & CCDF_MIRROR) {311/*312* Check to see if an even number of components313* have been specified. The interleave must also314* be non-zero in order for us to be able to315* guarantee the topology.316*/317if (cs->sc_ndisks % 2) {318gctl_error(req,319"Mirroring requires an even number of disks");320return(EINVAL);321}322if (cs->sc_ileave == 0) {323gctl_error(req,324"An interleave must be specified when mirroring");325return(EINVAL);326}327cs->sc_size = (cs->sc_ndisks/2) * minsize;328}329330/*331* Construct the interleave table.332*/333ccdinterleave(cs);334335/*336* Create pseudo-geometry based on 1MB cylinders. It's337* pretty close.338*/339cs->sc_secsize = maxsecsize;340341return (0);342}343344static void345ccdinterleave(struct ccd_s *cs)346{347struct ccdcinfo *ci, *smallci;348struct ccdiinfo *ii;349daddr_t bn, lbn;350int ix;351daddr_t size;352353/*354* Allocate an interleave table. The worst case occurs when each355* of N disks is of a different size, resulting in N interleave356* tables.357*358* Chances are this is too big, but we don't care.359*/360size = (cs->sc_ndisks + 1) * sizeof(struct ccdiinfo);361cs->sc_itable = g_malloc(size, M_WAITOK | M_ZERO);362363/*364* Trivial case: no interleave (actually interleave of disk size).365* Each table entry represents a single component in its entirety.366*367* An interleave of 0 may not be used with a mirror setup.368*/369if (cs->sc_ileave == 0) {370bn = 0;371ii = cs->sc_itable;372373for (ix = 0; ix < cs->sc_ndisks; ix++) {374/* Allocate space for ii_index. */375ii->ii_index = g_malloc(sizeof(int), M_WAITOK);376ii->ii_ndisk = 1;377ii->ii_startblk = bn;378ii->ii_startoff = 0;379ii->ii_index[0] = ix;380bn += cs->sc_cinfo[ix].ci_size;381ii++;382}383ii->ii_ndisk = 0;384return;385}386387/*388* The following isn't fast or pretty; it doesn't have to be.389*/390size = 0;391bn = lbn = 0;392for (ii = cs->sc_itable; ; ii++) {393/*394* Allocate space for ii_index. We might allocate more then395* we use.396*/397ii->ii_index = g_malloc((sizeof(int) * cs->sc_ndisks),398M_WAITOK);399400/*401* Locate the smallest of the remaining components402*/403smallci = NULL;404for (ci = cs->sc_cinfo; ci < &cs->sc_cinfo[cs->sc_ndisks];405ci++) {406if (ci->ci_size > size &&407(smallci == NULL ||408ci->ci_size < smallci->ci_size)) {409smallci = ci;410}411}412413/*414* Nobody left, all done415*/416if (smallci == NULL) {417ii->ii_ndisk = 0;418g_free(ii->ii_index);419ii->ii_index = NULL;420break;421}422423/*424* Record starting logical block using an sc_ileave blocksize.425*/426ii->ii_startblk = bn / cs->sc_ileave;427428/*429* Record starting component block using an sc_ileave430* blocksize. This value is relative to the beginning of431* a component disk.432*/433ii->ii_startoff = lbn;434435/*436* Determine how many disks take part in this interleave437* and record their indices.438*/439ix = 0;440for (ci = cs->sc_cinfo;441ci < &cs->sc_cinfo[cs->sc_ndisks]; ci++) {442if (ci->ci_size >= smallci->ci_size) {443ii->ii_index[ix++] = ci - cs->sc_cinfo;444}445}446ii->ii_ndisk = ix;447bn += ix * (smallci->ci_size - size);448lbn = smallci->ci_size / cs->sc_ileave;449size = smallci->ci_size;450}451}452453static void454g_ccd_start(struct bio *bp)455{456long bcount, rcount;457struct bio *cbp[2];458caddr_t addr;459daddr_t bn;460int err;461struct ccd_s *cs;462463cs = bp->bio_to->geom->softc;464465/*466* Block all GETATTR requests, we wouldn't know which of our467* subdevices we should ship it off to.468* XXX: this may not be the right policy.469*/470if(bp->bio_cmd == BIO_GETATTR) {471g_io_deliver(bp, EINVAL);472return;473}474475/*476* Translate the partition-relative block number to an absolute.477*/478bn = bp->bio_offset / cs->sc_secsize;479480/*481* Allocate component buffers and fire off the requests482*/483addr = bp->bio_data;484for (bcount = bp->bio_length; bcount > 0; bcount -= rcount) {485err = ccdbuffer(cbp, cs, bp, bn, addr, bcount);486if (err) {487bp->bio_completed += bcount;488if (bp->bio_error == 0)489bp->bio_error = err;490if (bp->bio_completed == bp->bio_length)491g_io_deliver(bp, bp->bio_error);492return;493}494rcount = cbp[0]->bio_length;495496if (cs->sc_flags & CCDF_MIRROR) {497/*498* Mirroring. Writes go to both disks, reads are499* taken from whichever disk seems most appropriate.500*501* We attempt to localize reads to the disk whos arm502* is nearest the read request. We ignore seeks due503* to writes when making this determination and we504* also try to avoid hogging.505*/506if (cbp[0]->bio_cmd != BIO_READ) {507g_io_request(cbp[0], cbp[0]->bio_from);508g_io_request(cbp[1], cbp[1]->bio_from);509} else {510int pick = cs->sc_pick;511daddr_t range = cs->sc_size / 16;512513if (bn < cs->sc_blk[pick] - range ||514bn > cs->sc_blk[pick] + range515) {516cs->sc_pick = pick = 1 - pick;517}518cs->sc_blk[pick] = bn + btodb(rcount);519g_io_request(cbp[pick], cbp[pick]->bio_from);520}521} else {522/*523* Not mirroring524*/525g_io_request(cbp[0], cbp[0]->bio_from);526}527bn += btodb(rcount);528addr += rcount;529}530}531532/*533* Build a component buffer header.534*/535static int536ccdbuffer(struct bio **cb, struct ccd_s *cs, struct bio *bp, daddr_t bn, caddr_t addr, long bcount)537{538struct ccdcinfo *ci, *ci2 = NULL;539struct bio *cbp;540daddr_t cbn, cboff;541off_t cbc;542543/*544* Determine which component bn falls in.545*/546cbn = bn;547cboff = 0;548549if (cs->sc_ileave == 0) {550/*551* Serially concatenated and neither a mirror nor a parity552* config. This is a special case.553*/554daddr_t sblk;555556sblk = 0;557for (ci = cs->sc_cinfo; cbn >= sblk + ci->ci_size; ci++)558sblk += ci->ci_size;559cbn -= sblk;560} else {561struct ccdiinfo *ii;562int ccdisk, off;563564/*565* Calculate cbn, the logical superblock (sc_ileave chunks),566* and cboff, a normal block offset (DEV_BSIZE chunks) relative567* to cbn.568*/569cboff = cbn % cs->sc_ileave; /* DEV_BSIZE gran */570cbn = cbn / cs->sc_ileave; /* DEV_BSIZE * ileave gran */571572/*573* Figure out which interleave table to use.574*/575for (ii = cs->sc_itable; ii->ii_ndisk; ii++) {576if (ii->ii_startblk > cbn)577break;578}579ii--;580581/*582* off is the logical superblock relative to the beginning583* of this interleave block.584*/585off = cbn - ii->ii_startblk;586587/*588* We must calculate which disk component to use (ccdisk),589* and recalculate cbn to be the superblock relative to590* the beginning of the component. This is typically done by591* adding 'off' and ii->ii_startoff together. However, 'off'592* must typically be divided by the number of components in593* this interleave array to be properly convert it from a594* CCD-relative logical superblock number to a595* component-relative superblock number.596*/597if (ii->ii_ndisk == 1) {598/*599* When we have just one disk, it can't be a mirror600* or a parity config.601*/602ccdisk = ii->ii_index[0];603cbn = ii->ii_startoff + off;604} else {605if (cs->sc_flags & CCDF_MIRROR) {606/*607* We have forced a uniform mapping, resulting608* in a single interleave array. We double609* up on the first half of the available610* components and our mirror is in the second611* half. This only works with a single612* interleave array because doubling up613* doubles the number of sectors, so there614* cannot be another interleave array because615* the next interleave array's calculations616* would be off.617*/618int ndisk2 = ii->ii_ndisk / 2;619ccdisk = ii->ii_index[off % ndisk2];620cbn = ii->ii_startoff + off / ndisk2;621ci2 = &cs->sc_cinfo[ccdisk + ndisk2];622} else {623ccdisk = ii->ii_index[off % ii->ii_ndisk];624cbn = ii->ii_startoff + off / ii->ii_ndisk;625}626}627628ci = &cs->sc_cinfo[ccdisk];629630/*631* Convert cbn from a superblock to a normal block so it632* can be used to calculate (along with cboff) the normal633* block index into this particular disk.634*/635cbn *= cs->sc_ileave;636}637638/*639* Fill in the component buf structure.640*/641cbp = g_clone_bio(bp);642if (cbp == NULL)643return (ENOMEM);644cbp->bio_done = g_std_done;645cbp->bio_offset = dbtob(cbn + cboff + cs->sc_offset);646cbp->bio_data = addr;647if (cs->sc_ileave == 0)648cbc = dbtob((off_t)(ci->ci_size - cbn));649else650cbc = dbtob((off_t)(cs->sc_ileave - cboff));651cbp->bio_length = (cbc < bcount) ? cbc : bcount;652653cbp->bio_from = ci->ci_consumer;654cb[0] = cbp;655656if (cs->sc_flags & CCDF_MIRROR) {657cbp = g_clone_bio(bp);658if (cbp == NULL)659return (ENOMEM);660cbp->bio_done = cb[0]->bio_done = ccdiodone;661cbp->bio_offset = cb[0]->bio_offset;662cbp->bio_data = cb[0]->bio_data;663cbp->bio_length = cb[0]->bio_length;664cbp->bio_from = ci2->ci_consumer;665cbp->bio_caller1 = cb[0];666cb[0]->bio_caller1 = cbp;667cb[1] = cbp;668}669return (0);670}671672/*673* Called only for mirrored operations.674*/675static void676ccdiodone(struct bio *cbp)677{678struct bio *mbp, *pbp;679680mbp = cbp->bio_caller1;681pbp = cbp->bio_parent;682683if (pbp->bio_cmd == BIO_READ) {684if (cbp->bio_error == 0) {685/* We will not be needing the partner bio */686if (mbp != NULL) {687pbp->bio_inbed++;688g_destroy_bio(mbp);689}690g_std_done(cbp);691return;692}693if (mbp != NULL) {694/* Try partner the bio instead */695mbp->bio_caller1 = NULL;696pbp->bio_inbed++;697g_destroy_bio(cbp);698g_io_request(mbp, mbp->bio_from);699/*700* XXX: If this comes back OK, we should actually701* try to write the good data on the failed mirror702*/703return;704}705g_std_done(cbp);706return;707}708if (mbp != NULL) {709mbp->bio_caller1 = NULL;710pbp->bio_inbed++;711if (cbp->bio_error != 0 && pbp->bio_error == 0)712pbp->bio_error = cbp->bio_error;713g_destroy_bio(cbp);714return;715}716g_std_done(cbp);717}718719static void720g_ccd_create(struct gctl_req *req, struct g_class *mp)721{722int *unit, *ileave, *nprovider;723struct g_geom *gp;724struct g_consumer *cp;725struct g_provider *pp;726struct ccd_s *sc;727struct sbuf *sb;728char buf[20];729int i, error;730731g_topology_assert();732unit = gctl_get_paraml(req, "unit", sizeof(*unit));733if (unit == NULL) {734gctl_error(req, "unit parameter not given");735return;736}737ileave = gctl_get_paraml(req, "ileave", sizeof(*ileave));738if (ileave == NULL) {739gctl_error(req, "ileave parameter not given");740return;741}742nprovider = gctl_get_paraml(req, "nprovider", sizeof(*nprovider));743if (nprovider == NULL) {744gctl_error(req, "nprovider parameter not given");745return;746}747748/* Check for duplicate unit */749LIST_FOREACH(gp, &mp->geom, geom) {750sc = gp->softc;751if (sc != NULL && sc->sc_unit == *unit) {752gctl_error(req, "Unit %d already configured", *unit);753return;754}755}756757if (*nprovider <= 0) {758gctl_error(req, "Bogus nprovider argument (= %d)", *nprovider);759return;760}761762/* Check all providers are valid */763for (i = 0; i < *nprovider; i++) {764snprintf(buf, sizeof(buf), "provider%d", i);765pp = gctl_get_provider(req, buf);766if (pp == NULL)767return;768}769770gp = g_new_geomf(mp, "ccd%d", *unit);771sc = g_malloc(sizeof(*sc), M_WAITOK | M_ZERO);772gp->softc = sc;773sc->sc_ndisks = *nprovider;774775/* Allocate space for the component info. */776sc->sc_cinfo = g_malloc(sc->sc_ndisks * sizeof(struct ccdcinfo),777M_WAITOK | M_ZERO);778779/* Create consumers and attach to all providers */780for (i = 0; i < *nprovider; i++) {781snprintf(buf, sizeof(buf), "provider%d", i);782pp = gctl_get_provider(req, buf);783cp = g_new_consumer(gp);784error = g_attach(cp, pp);785KASSERT(error == 0, ("attach to %s failed", pp->name));786sc->sc_cinfo[i].ci_consumer = cp;787sc->sc_cinfo[i].ci_provider = pp;788}789790sc->sc_unit = *unit;791sc->sc_ileave = *ileave;792793if (gctl_get_param(req, "no_offset", NULL))794sc->sc_flags |= CCDF_NO_OFFSET;795if (gctl_get_param(req, "linux", NULL))796sc->sc_flags |= CCDF_LINUX;797798if (gctl_get_param(req, "uniform", NULL))799sc->sc_flags |= CCDF_UNIFORM;800if (gctl_get_param(req, "mirror", NULL))801sc->sc_flags |= CCDF_MIRROR;802803if (sc->sc_ileave == 0 && (sc->sc_flags & CCDF_MIRROR)) {804printf("%s: disabling mirror, interleave is 0\n", gp->name);805sc->sc_flags &= ~(CCDF_MIRROR);806}807808if ((sc->sc_flags & CCDF_MIRROR) && !(sc->sc_flags & CCDF_UNIFORM)) {809printf("%s: mirror/parity forces uniform flag\n", gp->name);810sc->sc_flags |= CCDF_UNIFORM;811}812813error = ccdinit(req, sc);814if (error != 0) {815g_ccd_freesc(sc);816gp->softc = NULL;817g_wither_geom(gp, ENXIO);818return;819}820821pp = g_new_providerf(gp, "%s", gp->name);822pp->mediasize = sc->sc_size * (off_t)sc->sc_secsize;823pp->sectorsize = sc->sc_secsize;824g_error_provider(pp, 0);825826sb = sbuf_new_auto();827sbuf_printf(sb, "ccd%d: %d components ", sc->sc_unit, *nprovider);828for (i = 0; i < *nprovider; i++) {829sbuf_printf(sb, "%s%s",830i == 0 ? "(" : ", ",831sc->sc_cinfo[i].ci_provider->name);832}833sbuf_printf(sb, "), %jd blocks ", (off_t)pp->mediasize / DEV_BSIZE);834if (sc->sc_ileave != 0)835sbuf_printf(sb, "interleaved at %d blocks\n",836sc->sc_ileave);837else838sbuf_printf(sb, "concatenated\n");839sbuf_finish(sb);840gctl_set_param_err(req, "output", sbuf_data(sb), sbuf_len(sb) + 1);841sbuf_delete(sb);842}843844static int845g_ccd_destroy_geom(struct gctl_req *req, struct g_class *mp, struct g_geom *gp)846{847struct g_provider *pp;848struct ccd_s *sc;849850g_topology_assert();851sc = gp->softc;852pp = LIST_FIRST(&gp->provider);853if (sc == NULL || pp == NULL)854return (EBUSY);855if (pp->acr != 0 || pp->acw != 0 || pp->ace != 0) {856gctl_error(req, "%s is open(r%dw%de%d)", gp->name,857pp->acr, pp->acw, pp->ace);858return (EBUSY);859}860g_ccd_freesc(sc);861gp->softc = NULL;862g_wither_geom(gp, ENXIO);863return (0);864}865866static void867g_ccd_list(struct gctl_req *req, struct g_class *mp)868{869struct sbuf *sb;870struct ccd_s *cs;871struct g_geom *gp;872int i, unit, *up;873874up = gctl_get_paraml(req, "unit", sizeof(*up));875if (up == NULL) {876gctl_error(req, "unit parameter not given");877return;878}879unit = *up;880sb = sbuf_new_auto();881LIST_FOREACH(gp, &mp->geom, geom) {882cs = gp->softc;883if (cs == NULL || (unit >= 0 && unit != cs->sc_unit))884continue;885sbuf_printf(sb, "ccd%d\t\t%d\t%d\t",886cs->sc_unit, cs->sc_ileave, cs->sc_flags & CCDF_USERMASK);887888for (i = 0; i < cs->sc_ndisks; ++i) {889sbuf_printf(sb, "%s/dev/%s", i == 0 ? "" : " ",890cs->sc_cinfo[i].ci_provider->name);891}892sbuf_printf(sb, "\n");893}894sbuf_finish(sb);895gctl_set_param_err(req, "output", sbuf_data(sb), sbuf_len(sb) + 1);896sbuf_delete(sb);897}898899static void900g_ccd_config(struct gctl_req *req, struct g_class *mp, char const *verb)901{902struct g_geom *gp;903904g_topology_assert();905if (!strcmp(verb, "create geom")) {906g_ccd_create(req, mp);907} else if (!strcmp(verb, "destroy geom")) {908gp = gctl_get_geom(req, mp, "geom");909if (gp != NULL)910g_ccd_destroy_geom(req, mp, gp);911} else if (!strcmp(verb, "list")) {912g_ccd_list(req, mp);913} else {914gctl_error(req, "unknown verb");915}916}917918static struct g_class g_ccd_class = {919.name = "CCD",920.version = G_VERSION,921.ctlreq = g_ccd_config,922.destroy_geom = g_ccd_destroy_geom,923.start = g_ccd_start,924.orphan = g_ccd_orphan,925.access = g_ccd_access,926};927928DECLARE_GEOM_CLASS(g_ccd_class, g_ccd);929MODULE_VERSION(geom_ccd, 0);930931932