1*d0b020c0Sandvar /* $NetBSD: ccd.c,v 1.192 2025/01/08 08:24:07 andvar Exp $ */ 277d85de2Sthorpej 3ee8a2c00Sthorpej /*- 4481ff5acSad * Copyright (c) 1996, 1997, 1998, 1999, 2007, 2009 The NetBSD Foundation, Inc. 577d85de2Sthorpej * All rights reserved. 677d85de2Sthorpej * 7ee8a2c00Sthorpej * This code is derived from software contributed to The NetBSD Foundation 8481ff5acSad * by Jason R. Thorpe, and by Andrew Doran. 9ee8a2c00Sthorpej * 1077d85de2Sthorpej * Redistribution and use in source and binary forms, with or without 1177d85de2Sthorpej * modification, are permitted provided that the following conditions 1277d85de2Sthorpej * are met: 1377d85de2Sthorpej * 1. Redistributions of source code must retain the above copyright 1477d85de2Sthorpej * notice, this list of conditions and the following disclaimer. 1577d85de2Sthorpej * 2. Redistributions in binary form must reproduce the above copyright 1677d85de2Sthorpej * notice, this list of conditions and the following disclaimer in the 1777d85de2Sthorpej * documentation and/or other materials provided with the distribution. 1877d85de2Sthorpej * 19ee8a2c00Sthorpej * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20ee8a2c00Sthorpej * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21ee8a2c00Sthorpej * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22d6ae2cd7Sjtc * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23d6ae2cd7Sjtc * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24ee8a2c00Sthorpej * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25ee8a2c00Sthorpej * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26ee8a2c00Sthorpej * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27ee8a2c00Sthorpej * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28ee8a2c00Sthorpej * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29ee8a2c00Sthorpej * POSSIBILITY OF SUCH DAMAGE. 3077d85de2Sthorpej */ 31cf92afd6Scgd 326f9db1eeShpeyerl /* 339b6bd2d9Srmind * Copyright (c) 1988 University of Utah. 343214723fShpeyerl * Copyright (c) 1990, 1993 353214723fShpeyerl * The Regents of the University of California. All rights reserved. 366f9db1eeShpeyerl * 376f9db1eeShpeyerl * This code is derived from software contributed to Berkeley by 386f9db1eeShpeyerl * the Systems Programming Group of the University of Utah Computer 396f9db1eeShpeyerl * Science Department. 406f9db1eeShpeyerl * 416f9db1eeShpeyerl * Redistribution and use in source and binary forms, with or without 426f9db1eeShpeyerl * modification, are permitted provided that the following conditions 436f9db1eeShpeyerl * are met: 446f9db1eeShpeyerl * 1. Redistributions of source code must retain the above copyright 456f9db1eeShpeyerl * notice, this list of conditions and the following disclaimer. 466f9db1eeShpeyerl * 2. Redistributions in binary form must reproduce the above copyright 476f9db1eeShpeyerl * notice, this list of conditions and the following disclaimer in the 486f9db1eeShpeyerl * documentation and/or other materials provided with the distribution. 49aad01611Sagc * 3. Neither the name of the University nor the names of its contributors 50aad01611Sagc * may be used to endorse or promote products derived from this software 51aad01611Sagc * without specific prior written permission. 52aad01611Sagc * 53aad01611Sagc * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 54aad01611Sagc * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 55aad01611Sagc * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 56aad01611Sagc * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 57aad01611Sagc * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 58aad01611Sagc * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 59aad01611Sagc * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 60aad01611Sagc * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 61aad01611Sagc * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 62aad01611Sagc * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 63aad01611Sagc * SUCH DAMAGE. 64aad01611Sagc * 65fd1c7fefSriastradh * from: Utah $Hdr: cd.c 1.6 90/11/28$ 66aad01611Sagc * 67aad01611Sagc * @(#)cd.c 8.2 (Berkeley) 11/16/93 68aad01611Sagc */ 69aad01611Sagc 70aad01611Sagc /* 716f9db1eeShpeyerl * "Concatenated" disk driver. 7277d85de2Sthorpej * 73481ff5acSad * Notes on concurrency: 74481ff5acSad * 75481ff5acSad * => sc_dvlock serializes access to the device nodes, excluding block I/O. 76481ff5acSad * 77481ff5acSad * => sc_iolock serializes access to (sc_flags & CCDF_INITED), disk stats, 78481ff5acSad * sc_stop, sc_bufq and b_resid from master buffers. 79481ff5acSad * 80481ff5acSad * => a combination of CCDF_INITED, sc_inflight, and sc_iolock is used to 81481ff5acSad * serialize I/O and configuration changes. 82481ff5acSad * 83481ff5acSad * => the in-core disk label does not change while the device is open. 84481ff5acSad * 85481ff5acSad * On memory consumption: ccd fans out I/O requests and so needs to 86481ff5acSad * allocate memory. If the system is desperately low on memory, we 87481ff5acSad * single thread I/O. 886f9db1eeShpeyerl */ 896f9db1eeShpeyerl 902bbe2de6Slukem #include <sys/cdefs.h> 91*d0b020c0Sandvar __KERNEL_RCSID(0, "$NetBSD: ccd.c,v 1.192 2025/01/08 08:24:07 andvar Exp $"); 922bbe2de6Slukem 936f9db1eeShpeyerl #include <sys/param.h> 946f9db1eeShpeyerl #include <sys/systm.h> 95481ff5acSad #include <sys/kernel.h> 963214723fShpeyerl #include <sys/proc.h> 976f9db1eeShpeyerl #include <sys/errno.h> 986f9db1eeShpeyerl #include <sys/buf.h> 99481ff5acSad #include <sys/kmem.h> 100dd5ae858Sthorpej #include <sys/pool.h> 101b4a3a8f3Sjruoho #include <sys/module.h> 10277d85de2Sthorpej #include <sys/namei.h> 1033214723fShpeyerl #include <sys/stat.h> 1043214723fShpeyerl #include <sys/ioctl.h> 1053214723fShpeyerl #include <sys/disklabel.h> 10677d85de2Sthorpej #include <sys/device.h> 10777d85de2Sthorpej #include <sys/disk.h> 10877d85de2Sthorpej #include <sys/syslog.h> 1093214723fShpeyerl #include <sys/fcntl.h> 11077d85de2Sthorpej #include <sys/vnode.h> 111e1930da2Schristos #include <sys/conf.h> 1129abeea58Sad #include <sys/mutex.h> 113d272bb00Sthorpej #include <sys/queue.h> 1142867b68bSelad #include <sys/kauth.h> 115481ff5acSad #include <sys/kthread.h> 116481ff5acSad #include <sys/bufq.h> 117b08b65c6Schristos #include <sys/sysctl.h> 118d91f98a8Spgoyette #include <sys/compat_stub.h> 1196f9db1eeShpeyerl 12019b39d64Suebayasi #include <uvm/uvm_extern.h> 12119b39d64Suebayasi 1226f9db1eeShpeyerl #include <dev/ccdvar.h> 1231a789da3Schristos #include <dev/dkvar.h> 1246f9db1eeShpeyerl 125292a0c7fShannken #include <miscfs/specfs/specdev.h> /* for v_rdev */ 126292a0c7fShannken 127e7ae23fdSchristos #include "ioconf.h" 128e7ae23fdSchristos 12977d85de2Sthorpej #if defined(CCDDEBUG) && !defined(DEBUG) 13077d85de2Sthorpej #define DEBUG 13177d85de2Sthorpej #endif 13277d85de2Sthorpej 1336f9db1eeShpeyerl #ifdef DEBUG 1343214723fShpeyerl #define CCDB_FOLLOW 0x01 1353214723fShpeyerl #define CCDB_INIT 0x02 1363214723fShpeyerl #define CCDB_IO 0x04 13777d85de2Sthorpej #define CCDB_LABEL 0x08 13877d85de2Sthorpej #define CCDB_VNODE 0x10 139732dd94aSthorpej int ccddebug = 0x00; 1406f9db1eeShpeyerl #endif 1416f9db1eeShpeyerl 142f694af67Scgd #define ccdunit(x) DISKUNIT(x) 1436f9db1eeShpeyerl 144f694af67Scgd struct ccdbuf { 145f694af67Scgd struct buf cb_buf; /* new I/O buf */ 146f694af67Scgd struct buf *cb_obp; /* ptr. to original I/O buf */ 147c6aa25bfSthorpej struct ccd_softc *cb_sc; /* pointer to ccd softc */ 148f694af67Scgd int cb_comp; /* target component */ 149d272bb00Sthorpej SIMPLEQ_ENTRY(ccdbuf) cb_q; /* fifo of component buffers */ 150f694af67Scgd }; 1516f9db1eeShpeyerl 152dd5ae858Sthorpej /* component buffer pool */ 153481ff5acSad static pool_cache_t ccd_cache; 154dd5ae858Sthorpej 15579f4a4f8Shannken #define CCD_GETBUF(wait) pool_cache_get(ccd_cache, wait) 156481ff5acSad #define CCD_PUTBUF(cbp) pool_cache_put(ccd_cache, cbp) 1576f9db1eeShpeyerl 15877d85de2Sthorpej #define CCDLABELDEV(dev) \ 15977d85de2Sthorpej (MAKEDISKDEV(major((dev)), ccdunit((dev)), RAW_PART)) 1606f9db1eeShpeyerl 16177d85de2Sthorpej /* called by main() at boot time */ 16237efed97Schristos void ccddetach(void); 16377d85de2Sthorpej 16477d85de2Sthorpej /* called by biodone() at interrupt time */ 165b8d38500Sthorpej static void ccdiodone(struct buf *); 166f694af67Scgd 167b8d38500Sthorpej static void ccdinterleave(struct ccd_softc *); 168b8d38500Sthorpej static int ccdinit(struct ccd_softc *, char **, struct vnode **, 16995e1ffb1Schristos struct lwp *); 170b8d38500Sthorpej static struct ccdbuf *ccdbuffer(struct ccd_softc *, struct buf *, 17179f4a4f8Shannken daddr_t, void *, long, int); 172b8d38500Sthorpej static void ccdgetdefaultlabel(struct ccd_softc *, struct disklabel *); 173b8d38500Sthorpej static void ccdgetdisklabel(dev_t); 174b8d38500Sthorpej static void ccdmakedisklabel(struct ccd_softc *); 17579f4a4f8Shannken static int ccdstart(struct ccd_softc *, struct buf *, int); 176481ff5acSad static void ccdthread(void *); 1773214723fShpeyerl 178b8d38500Sthorpej static dev_type_open(ccdopen); 179b8d38500Sthorpej static dev_type_close(ccdclose); 180b8d38500Sthorpej static dev_type_read(ccdread); 181b8d38500Sthorpej static dev_type_write(ccdwrite); 182b8d38500Sthorpej static dev_type_ioctl(ccdioctl); 183b8d38500Sthorpej static dev_type_strategy(ccdstrategy); 184b8d38500Sthorpej static dev_type_size(ccdsize); 18577a6b82bSgehenna 18677a6b82bSgehenna const struct bdevsw ccd_bdevsw = { 187481ff5acSad .d_open = ccdopen, 188481ff5acSad .d_close = ccdclose, 189481ff5acSad .d_strategy = ccdstrategy, 190481ff5acSad .d_ioctl = ccdioctl, 191481ff5acSad .d_dump = nodump, 192481ff5acSad .d_psize = ccdsize, 1938c70ef39Sdholland .d_discard = nodiscard, 194481ff5acSad .d_flag = D_DISK | D_MPSAFE 19577a6b82bSgehenna }; 19677a6b82bSgehenna 19777a6b82bSgehenna const struct cdevsw ccd_cdevsw = { 198481ff5acSad .d_open = ccdopen, 199481ff5acSad .d_close = ccdclose, 200481ff5acSad .d_read = ccdread, 201481ff5acSad .d_write = ccdwrite, 202481ff5acSad .d_ioctl = ccdioctl, 203481ff5acSad .d_stop = nostop, 204481ff5acSad .d_tty = notty, 205481ff5acSad .d_poll = nopoll, 206481ff5acSad .d_mmap = nommap, 207481ff5acSad .d_kqfilter = nokqfilter, 208f9228f42Sdholland .d_discard = nodiscard, 209481ff5acSad .d_flag = D_DISK | D_MPSAFE 21077a6b82bSgehenna }; 21177a6b82bSgehenna 2127686d3bdSmlelstv static const struct dkdriver ccddkdriver = { 2137686d3bdSmlelstv .d_strategy = ccdstrategy, 2147686d3bdSmlelstv .d_minphys = minphys 2157686d3bdSmlelstv }; 2167686d3bdSmlelstv 21777d85de2Sthorpej #ifdef DEBUG 218b8d38500Sthorpej static void printiinfo(struct ccdiinfo *); 21977d85de2Sthorpej #endif 22077d85de2Sthorpej 221b08b65c6Schristos static LIST_HEAD(, ccd_softc) ccds = LIST_HEAD_INITIALIZER(ccds); 222b08b65c6Schristos static kmutex_t ccd_lock; 223b08b65c6Schristos 224aeebcff2Spgoyette SYSCTL_SETUP_PROTO(sysctl_kern_ccd_setup); 225aeebcff2Spgoyette 226b08b65c6Schristos static struct ccd_softc * 227b08b65c6Schristos ccdcreate(int unit) { 228b08b65c6Schristos struct ccd_softc *sc = kmem_zalloc(sizeof(*sc), KM_SLEEP); 229fd34ea77Schs 230b08b65c6Schristos /* Initialize per-softc structures. */ 231b08b65c6Schristos snprintf(sc->sc_xname, sizeof(sc->sc_xname), "ccd%d", unit); 232ddeabcf2Schristos sc->sc_unit = unit; 233b08b65c6Schristos mutex_init(&sc->sc_dvlock, MUTEX_DEFAULT, IPL_NONE); 234b08b65c6Schristos sc->sc_iolock = mutex_obj_alloc(MUTEX_DEFAULT, IPL_NONE); 235b08b65c6Schristos cv_init(&sc->sc_stop, "ccdstop"); 236b08b65c6Schristos cv_init(&sc->sc_push, "ccdthr"); 2377686d3bdSmlelstv disk_init(&sc->sc_dkdev, sc->sc_xname, &ccddkdriver); 238b08b65c6Schristos return sc; 239b08b65c6Schristos } 240b08b65c6Schristos 241b08b65c6Schristos static void 242b08b65c6Schristos ccddestroy(struct ccd_softc *sc) { 243b08b65c6Schristos mutex_obj_free(sc->sc_iolock); 24455252202Sjoerg mutex_exit(&sc->sc_dvlock); 245b08b65c6Schristos mutex_destroy(&sc->sc_dvlock); 246b08b65c6Schristos cv_destroy(&sc->sc_stop); 247b08b65c6Schristos cv_destroy(&sc->sc_push); 248b08b65c6Schristos disk_destroy(&sc->sc_dkdev); 249b08b65c6Schristos kmem_free(sc, sizeof(*sc)); 250b08b65c6Schristos } 251b08b65c6Schristos 252b08b65c6Schristos static struct ccd_softc * 253ef781023Schristos ccdget(int unit, int make) { 254b08b65c6Schristos struct ccd_softc *sc; 255b08b65c6Schristos if (unit < 0) { 256b08b65c6Schristos #ifdef DIAGNOSTIC 257b08b65c6Schristos panic("%s: unit %d!", __func__, unit); 258b08b65c6Schristos #endif 259b08b65c6Schristos return NULL; 260b08b65c6Schristos } 261b08b65c6Schristos mutex_enter(&ccd_lock); 262b08b65c6Schristos LIST_FOREACH(sc, &ccds, sc_link) { 263b08b65c6Schristos if (sc->sc_unit == unit) { 264b08b65c6Schristos mutex_exit(&ccd_lock); 265b08b65c6Schristos return sc; 266b08b65c6Schristos } 267b08b65c6Schristos } 268b08b65c6Schristos mutex_exit(&ccd_lock); 269ef781023Schristos if (!make) 270ef781023Schristos return NULL; 271b08b65c6Schristos if ((sc = ccdcreate(unit)) == NULL) 272b08b65c6Schristos return NULL; 273b08b65c6Schristos mutex_enter(&ccd_lock); 274b08b65c6Schristos LIST_INSERT_HEAD(&ccds, sc, sc_link); 275b08b65c6Schristos mutex_exit(&ccd_lock); 276b08b65c6Schristos return sc; 277b08b65c6Schristos } 278b08b65c6Schristos 279b08b65c6Schristos static void 280b08b65c6Schristos ccdput(struct ccd_softc *sc) { 281b08b65c6Schristos mutex_enter(&ccd_lock); 282b08b65c6Schristos LIST_REMOVE(sc, sc_link); 283b08b65c6Schristos mutex_exit(&ccd_lock); 284b08b65c6Schristos ccddestroy(sc); 285b08b65c6Schristos } 2866f9db1eeShpeyerl 2876f9db1eeShpeyerl /* 28877d85de2Sthorpej * Called by main() during pseudo-device attachment. All we need 28977d85de2Sthorpej * to do is allocate enough space for devices to be configured later. 2906f9db1eeShpeyerl */ 2916f9db1eeShpeyerl void 292b8d38500Sthorpej ccdattach(int num) 2936f9db1eeShpeyerl { 294b08b65c6Schristos mutex_init(&ccd_lock, MUTEX_DEFAULT, IPL_NONE); 2950272b2abSthorpej 296dd5ae858Sthorpej /* Initialize the component buffer pool. */ 297481ff5acSad ccd_cache = pool_cache_init(sizeof(struct ccdbuf), 0, 298481ff5acSad 0, 0, "ccdbuf", NULL, IPL_BIO, NULL, NULL, NULL); 2996f9db1eeShpeyerl } 3006f9db1eeShpeyerl 30137efed97Schristos void 30237efed97Schristos ccddetach(void) 30337efed97Schristos { 30437efed97Schristos pool_cache_destroy(ccd_cache); 30537efed97Schristos mutex_destroy(&ccd_lock); 30637efed97Schristos } 30737efed97Schristos 30877d85de2Sthorpej static int 309b8d38500Sthorpej ccdinit(struct ccd_softc *cs, char **cpaths, struct vnode **vpp, 31095e1ffb1Schristos struct lwp *l) 3116f9db1eeShpeyerl { 312169ac5b3Saugustss struct ccdcinfo *ci = NULL; 313169ac5b3Saugustss int ix; 31477d85de2Sthorpej struct ccdgeom *ccg = &cs->sc_geom; 31592d441e0Schristos char *tmppath; 316aa8b5ebdSenami int error, path_alloced; 317e839ec30Schristos uint64_t psize, minsize; 318e839ec30Schristos unsigned secsize, maxsecsize; 319026ad668Sjnemeth struct disk_geom *dg; 3206f9db1eeShpeyerl 3216f9db1eeShpeyerl #ifdef DEBUG 3223214723fShpeyerl if (ccddebug & (CCDB_FOLLOW|CCDB_INIT)) 323c6aa25bfSthorpej printf("%s: ccdinit\n", cs->sc_xname); 3246f9db1eeShpeyerl #endif 32577d85de2Sthorpej 32677d85de2Sthorpej /* Allocate space for the component info. */ 327481ff5acSad cs->sc_cinfo = kmem_alloc(cs->sc_nccdisks * sizeof(*cs->sc_cinfo), 328481ff5acSad KM_SLEEP); 329481ff5acSad tmppath = kmem_alloc(MAXPATHLEN, KM_SLEEP); 33092d441e0Schristos 3310272b2abSthorpej cs->sc_size = 0; 3320272b2abSthorpej 3336f9db1eeShpeyerl /* 3346f9db1eeShpeyerl * Verify that each component piece exists and record 3356f9db1eeShpeyerl * relevant information about it. 3366f9db1eeShpeyerl */ 33777d85de2Sthorpej maxsecsize = 0; 3386f9db1eeShpeyerl minsize = 0; 339aa8b5ebdSenami for (ix = 0, path_alloced = 0; ix < cs->sc_nccdisks; ix++) { 3406f9db1eeShpeyerl ci = &cs->sc_cinfo[ix]; 3410272b2abSthorpej ci->ci_vp = vpp[ix]; 342a44a2765Scgd 34377d85de2Sthorpej /* 34477d85de2Sthorpej * Copy in the pathname of the component. 34577d85de2Sthorpej */ 34625862469Sjoerg memset(tmppath, 0, MAXPATHLEN); /* sanity */ 347d318abdbSchristos error = copyinstr(cpaths[ix], tmppath, 348d318abdbSchristos MAXPATHLEN, &ci->ci_pathlen); 349481ff5acSad if (ci->ci_pathlen == 0) 350481ff5acSad error = EINVAL; 351d318abdbSchristos if (error) { 35277d85de2Sthorpej #ifdef DEBUG 35377d85de2Sthorpej if (ccddebug & (CCDB_FOLLOW|CCDB_INIT)) 35486373f8cSchristos printf("%s: can't copy path, error = %d\n", 3555b39541eSthorpej cs->sc_xname, error); 35677d85de2Sthorpej #endif 357aa8b5ebdSenami goto out; 35877d85de2Sthorpej } 359481ff5acSad ci->ci_path = kmem_alloc(ci->ci_pathlen, KM_SLEEP); 360c8b4ac1bSthorpej memcpy(ci->ci_path, tmppath, ci->ci_pathlen); 361aa8b5ebdSenami path_alloced++; 36277d85de2Sthorpej 36377d85de2Sthorpej /* 36477d85de2Sthorpej * XXX: Cache the component's dev_t. 36577d85de2Sthorpej */ 366292a0c7fShannken ci->ci_dev = vpp[ix]->v_rdev; 36777d85de2Sthorpej 36877d85de2Sthorpej /* 36977d85de2Sthorpej * Get partition information for the component. 37077d85de2Sthorpej */ 371e839ec30Schristos error = getdisksize(vpp[ix], &psize, &secsize); 372d318abdbSchristos if (error) { 37377d85de2Sthorpej #ifdef DEBUG 37477d85de2Sthorpej if (ccddebug & (CCDB_FOLLOW|CCDB_INIT)) 375e839ec30Schristos printf("%s: %s: disksize failed, error = %d\n", 3765b39541eSthorpej cs->sc_xname, ci->ci_path, error); 37777d85de2Sthorpej #endif 378aa8b5ebdSenami goto out; 37977d85de2Sthorpej } 38077d85de2Sthorpej 38177d85de2Sthorpej /* 38277d85de2Sthorpej * Calculate the size, truncating to an interleave 38377d85de2Sthorpej * boundary if necessary. 38477d85de2Sthorpej */ 385e839ec30Schristos maxsecsize = secsize > maxsecsize ? secsize : maxsecsize; 3866f9db1eeShpeyerl if (cs->sc_ileave > 1) 387e839ec30Schristos psize -= psize % cs->sc_ileave; 3883214723fShpeyerl 389e839ec30Schristos if (psize == 0) { 39077d85de2Sthorpej #ifdef DEBUG 39177d85de2Sthorpej if (ccddebug & (CCDB_FOLLOW|CCDB_INIT)) 39286373f8cSchristos printf("%s: %s: size == 0\n", 3935b39541eSthorpej cs->sc_xname, ci->ci_path); 3943214723fShpeyerl #endif 395aa8b5ebdSenami error = ENODEV; 396aa8b5ebdSenami goto out; 39777d85de2Sthorpej } 39877d85de2Sthorpej 399e839ec30Schristos if (minsize == 0 || psize < minsize) 400e839ec30Schristos minsize = psize; 401e839ec30Schristos ci->ci_size = psize; 402e839ec30Schristos cs->sc_size += psize; 4036f9db1eeShpeyerl } 40477d85de2Sthorpej 40577d85de2Sthorpej /* 40677d85de2Sthorpej * Don't allow the interleave to be smaller than 40777d85de2Sthorpej * the biggest component sector. 40877d85de2Sthorpej */ 40977d85de2Sthorpej if ((cs->sc_ileave > 0) && 41077d85de2Sthorpej (cs->sc_ileave < (maxsecsize / DEV_BSIZE))) { 41177d85de2Sthorpej #ifdef DEBUG 41277d85de2Sthorpej if (ccddebug & (CCDB_FOLLOW|CCDB_INIT)) 41386373f8cSchristos printf("%s: interleave must be at least %d\n", 4145b39541eSthorpej cs->sc_xname, (maxsecsize / DEV_BSIZE)); 41577d85de2Sthorpej #endif 416aa8b5ebdSenami error = EINVAL; 417aa8b5ebdSenami goto out; 41877d85de2Sthorpej } 41977d85de2Sthorpej 4206f9db1eeShpeyerl /* 4216f9db1eeShpeyerl * If uniform interleave is desired set all sizes to that of 4226f9db1eeShpeyerl * the smallest component. 4236f9db1eeShpeyerl */ 4240272b2abSthorpej if (cs->sc_flags & CCDF_UNIFORM) { 4256f9db1eeShpeyerl for (ci = cs->sc_cinfo; 4266f9db1eeShpeyerl ci < &cs->sc_cinfo[cs->sc_nccdisks]; ci++) 4276f9db1eeShpeyerl ci->ci_size = minsize; 428732dd94aSthorpej 4296f9db1eeShpeyerl cs->sc_size = cs->sc_nccdisks * minsize; 4306f9db1eeShpeyerl } 43177d85de2Sthorpej 4326f9db1eeShpeyerl /* 43377d85de2Sthorpej * Construct the interleave table. 4346f9db1eeShpeyerl */ 4350272b2abSthorpej ccdinterleave(cs); 43677d85de2Sthorpej 43777d85de2Sthorpej /* 43877d85de2Sthorpej * Create pseudo-geometry based on 1MB cylinders. It's 43977d85de2Sthorpej * pretty close. 44077d85de2Sthorpej */ 44177d85de2Sthorpej ccg->ccg_secsize = DEV_BSIZE; 44289d4987eSthorpej ccg->ccg_ntracks = 1; 44377d85de2Sthorpej ccg->ccg_nsectors = 1024 * (1024 / ccg->ccg_secsize); 44477d85de2Sthorpej ccg->ccg_ncylinders = cs->sc_size / ccg->ccg_nsectors; 44577d85de2Sthorpej 446026ad668Sjnemeth dg = &cs->sc_dkdev.dk_geom; 447026ad668Sjnemeth memset(dg, 0, sizeof(*dg)); 448026ad668Sjnemeth dg->dg_secperunit = cs->sc_size; 449026ad668Sjnemeth dg->dg_secsize = ccg->ccg_secsize; 450026ad668Sjnemeth dg->dg_nsectors = ccg->ccg_nsectors; 451026ad668Sjnemeth dg->dg_ntracks = ccg->ccg_ntracks; 452026ad668Sjnemeth dg->dg_ncylinders = ccg->ccg_ncylinders; 453026ad668Sjnemeth 4547d706cf7Ssborrill if (cs->sc_ileave > 0) 4557d706cf7Ssborrill aprint_normal("%s: Interleaving %d component%s " 4567d706cf7Ssborrill "(%d block interleave)\n", cs->sc_xname, 4577d706cf7Ssborrill cs->sc_nccdisks, (cs->sc_nccdisks != 0 ? "s" : ""), 4587d706cf7Ssborrill cs->sc_ileave); 4597d706cf7Ssborrill else 4607d706cf7Ssborrill aprint_normal("%s: Concatenating %d component%s\n", 4617d706cf7Ssborrill cs->sc_xname, 4627d706cf7Ssborrill cs->sc_nccdisks, (cs->sc_nccdisks != 0 ? "s" : "")); 4637d706cf7Ssborrill for (ix = 0; ix < cs->sc_nccdisks; ix++) { 4647d706cf7Ssborrill ci = &cs->sc_cinfo[ix]; 4657d706cf7Ssborrill aprint_normal("%s: %s (%ju blocks)\n", cs->sc_xname, 4667d706cf7Ssborrill ci->ci_path, (uintmax_t)ci->ci_size); 4677d706cf7Ssborrill } 4687d706cf7Ssborrill aprint_normal("%s: total %ju blocks\n", cs->sc_xname, cs->sc_size); 4697d706cf7Ssborrill 470481ff5acSad /* 471481ff5acSad * Create thread to handle deferred I/O. 472481ff5acSad */ 473481ff5acSad cs->sc_zap = false; 474481ff5acSad error = kthread_create(PRI_BIO, KTHREAD_MPSAFE, NULL, ccdthread, 475481ff5acSad cs, &cs->sc_thread, "%s", cs->sc_xname); 476481ff5acSad if (error) { 477481ff5acSad printf("ccdinit: can't create thread: %d\n", error); 478481ff5acSad goto out; 479481ff5acSad } 480481ff5acSad 481481ff5acSad /* 482481ff5acSad * Only now that everything is set up can we enable the device. 483481ff5acSad */ 484481ff5acSad mutex_enter(cs->sc_iolock); 48577d85de2Sthorpej cs->sc_flags |= CCDF_INITED; 486481ff5acSad mutex_exit(cs->sc_iolock); 487481ff5acSad kmem_free(tmppath, MAXPATHLEN); 48877d85de2Sthorpej return (0); 489aa8b5ebdSenami 490aa8b5ebdSenami out: 491481ff5acSad for (ix = 0; ix < path_alloced; ix++) { 492481ff5acSad kmem_free(cs->sc_cinfo[ix].ci_path, 493481ff5acSad cs->sc_cinfo[ix].ci_pathlen); 494481ff5acSad } 495481ff5acSad kmem_free(cs->sc_cinfo, cs->sc_nccdisks * sizeof(struct ccdcinfo)); 496481ff5acSad kmem_free(tmppath, MAXPATHLEN); 497aa8b5ebdSenami return (error); 4986f9db1eeShpeyerl } 4996f9db1eeShpeyerl 50077d85de2Sthorpej static void 501b8d38500Sthorpej ccdinterleave(struct ccd_softc *cs) 5026f9db1eeShpeyerl { 503169ac5b3Saugustss struct ccdcinfo *ci, *smallci; 504169ac5b3Saugustss struct ccdiinfo *ii; 505169ac5b3Saugustss daddr_t bn, lbn; 506169ac5b3Saugustss int ix; 5076f9db1eeShpeyerl u_long size; 5086f9db1eeShpeyerl 5096f9db1eeShpeyerl #ifdef DEBUG 5103214723fShpeyerl if (ccddebug & CCDB_INIT) 51186373f8cSchristos printf("ccdinterleave(%p): ileave %d\n", cs, cs->sc_ileave); 5126f9db1eeShpeyerl #endif 5136f9db1eeShpeyerl /* 5146f9db1eeShpeyerl * Allocate an interleave table. 5156f9db1eeShpeyerl * Chances are this is too big, but we don't care. 5166f9db1eeShpeyerl */ 5176f9db1eeShpeyerl size = (cs->sc_nccdisks + 1) * sizeof(struct ccdiinfo); 518481ff5acSad cs->sc_itable = kmem_zalloc(size, KM_SLEEP); 51977d85de2Sthorpej 5206f9db1eeShpeyerl /* 5216f9db1eeShpeyerl * Trivial case: no interleave (actually interleave of disk size). 52277d85de2Sthorpej * Each table entry represents a single component in its entirety. 5236f9db1eeShpeyerl */ 5246f9db1eeShpeyerl if (cs->sc_ileave == 0) { 5256f9db1eeShpeyerl bn = 0; 5266f9db1eeShpeyerl ii = cs->sc_itable; 52777d85de2Sthorpej 5286f9db1eeShpeyerl for (ix = 0; ix < cs->sc_nccdisks; ix++) { 52989d4987eSthorpej /* Allocate space for ii_index. */ 530481ff5acSad ii->ii_indexsz = sizeof(int); 531481ff5acSad ii->ii_index = kmem_alloc(ii->ii_indexsz, KM_SLEEP); 5326f9db1eeShpeyerl ii->ii_ndisk = 1; 5336f9db1eeShpeyerl ii->ii_startblk = bn; 5346f9db1eeShpeyerl ii->ii_startoff = 0; 5356f9db1eeShpeyerl ii->ii_index[0] = ix; 5366f9db1eeShpeyerl bn += cs->sc_cinfo[ix].ci_size; 5376f9db1eeShpeyerl ii++; 5386f9db1eeShpeyerl } 5396f9db1eeShpeyerl ii->ii_ndisk = 0; 5406f9db1eeShpeyerl #ifdef DEBUG 5413214723fShpeyerl if (ccddebug & CCDB_INIT) 5426f9db1eeShpeyerl printiinfo(cs->sc_itable); 5436f9db1eeShpeyerl #endif 54477d85de2Sthorpej return; 5456f9db1eeShpeyerl } 54677d85de2Sthorpej 5476f9db1eeShpeyerl /* 5486f9db1eeShpeyerl * The following isn't fast or pretty; it doesn't have to be. 5496f9db1eeShpeyerl */ 5506f9db1eeShpeyerl size = 0; 5516f9db1eeShpeyerl bn = lbn = 0; 5526f9db1eeShpeyerl for (ii = cs->sc_itable; ; ii++) { 55377d85de2Sthorpej /* Allocate space for ii_index. */ 554481ff5acSad ii->ii_indexsz = sizeof(int) * cs->sc_nccdisks; 555481ff5acSad ii->ii_index = kmem_alloc(ii->ii_indexsz, KM_SLEEP); 55677d85de2Sthorpej 5576f9db1eeShpeyerl /* 5586f9db1eeShpeyerl * Locate the smallest of the remaining components 5596f9db1eeShpeyerl */ 5606f9db1eeShpeyerl smallci = NULL; 5616f9db1eeShpeyerl for (ci = cs->sc_cinfo; 5626f9db1eeShpeyerl ci < &cs->sc_cinfo[cs->sc_nccdisks]; ci++) 5636f9db1eeShpeyerl if (ci->ci_size > size && 5646f9db1eeShpeyerl (smallci == NULL || 5656f9db1eeShpeyerl ci->ci_size < smallci->ci_size)) 5666f9db1eeShpeyerl smallci = ci; 56777d85de2Sthorpej 5686f9db1eeShpeyerl /* 5696f9db1eeShpeyerl * Nobody left, all done 5706f9db1eeShpeyerl */ 5716f9db1eeShpeyerl if (smallci == NULL) { 5726f9db1eeShpeyerl ii->ii_ndisk = 0; 5736f9db1eeShpeyerl break; 5746f9db1eeShpeyerl } 57577d85de2Sthorpej 5766f9db1eeShpeyerl /* 5776f9db1eeShpeyerl * Record starting logical block and component offset 5786f9db1eeShpeyerl */ 5796f9db1eeShpeyerl ii->ii_startblk = bn / cs->sc_ileave; 5806f9db1eeShpeyerl ii->ii_startoff = lbn; 58177d85de2Sthorpej 5826f9db1eeShpeyerl /* 5836f9db1eeShpeyerl * Determine how many disks take part in this interleave 5846f9db1eeShpeyerl * and record their indices. 5856f9db1eeShpeyerl */ 5866f9db1eeShpeyerl ix = 0; 5876f9db1eeShpeyerl for (ci = cs->sc_cinfo; 5886f9db1eeShpeyerl ci < &cs->sc_cinfo[cs->sc_nccdisks]; ci++) 5896f9db1eeShpeyerl if (ci->ci_size >= smallci->ci_size) 5906f9db1eeShpeyerl ii->ii_index[ix++] = ci - cs->sc_cinfo; 5916f9db1eeShpeyerl ii->ii_ndisk = ix; 5926f9db1eeShpeyerl bn += ix * (smallci->ci_size - size); 5936f9db1eeShpeyerl lbn = smallci->ci_size / cs->sc_ileave; 5946f9db1eeShpeyerl size = smallci->ci_size; 5956f9db1eeShpeyerl } 5966f9db1eeShpeyerl #ifdef DEBUG 5973214723fShpeyerl if (ccddebug & CCDB_INIT) 5986f9db1eeShpeyerl printiinfo(cs->sc_itable); 5996f9db1eeShpeyerl #endif 6006f9db1eeShpeyerl } 6016f9db1eeShpeyerl 60277d85de2Sthorpej /* ARGSUSED */ 603b8d38500Sthorpej static int 604168cd830Schristos ccdopen(dev_t dev, int flags, int fmt, struct lwp *l) 6056f9db1eeShpeyerl { 6066f9db1eeShpeyerl int unit = ccdunit(dev); 60777d85de2Sthorpej struct ccd_softc *cs; 60877d85de2Sthorpej struct disklabel *lp; 609b8dcfbd3Sthorpej int error = 0, part, pmask; 6106f9db1eeShpeyerl 6116f9db1eeShpeyerl #ifdef DEBUG 6123214723fShpeyerl if (ccddebug & CCDB_FOLLOW) 61308ebead9Scegger printf("ccdopen(0x%"PRIx64", 0x%x)\n", dev, flags); 6146f9db1eeShpeyerl #endif 615ef781023Schristos if ((cs = ccdget(unit, 1)) == NULL) 616b08b65c6Schristos return ENXIO; 61777d85de2Sthorpej 618481ff5acSad mutex_enter(&cs->sc_dvlock); 619b8dcfbd3Sthorpej 6205b39541eSthorpej lp = cs->sc_dkdev.dk_label; 62177d85de2Sthorpej 62277d85de2Sthorpej part = DISKPART(dev); 62377d85de2Sthorpej pmask = (1 << part); 62477d85de2Sthorpej 625b8dcfbd3Sthorpej /* 626b8dcfbd3Sthorpej * If we're initialized, check to see if there are any other 627b8dcfbd3Sthorpej * open partitions. If not, then it's safe to update 6284aef0509Sthorpej * the in-core disklabel. Only read the disklabel if it is 6294aef0509Sthorpej * not already valid. 630b8dcfbd3Sthorpej */ 6314aef0509Sthorpej if ((cs->sc_flags & (CCDF_INITED|CCDF_VLABEL)) == CCDF_INITED && 6324aef0509Sthorpej cs->sc_dkdev.dk_openmask == 0) 633b8dcfbd3Sthorpej ccdgetdisklabel(dev); 634b8dcfbd3Sthorpej 63577d85de2Sthorpej /* Check that the partition exists. */ 63691bd533cSthorpej if (part != RAW_PART) { 63791bd533cSthorpej if (((cs->sc_flags & CCDF_INITED) == 0) || 638bb608224Sthorpej ((part >= lp->d_npartitions) || 639b8dcfbd3Sthorpej (lp->d_partitions[part].p_fstype == FS_UNUSED))) { 640b8dcfbd3Sthorpej error = ENXIO; 641b8dcfbd3Sthorpej goto done; 642b8dcfbd3Sthorpej } 64391bd533cSthorpej } 64477d85de2Sthorpej 64577d85de2Sthorpej /* Prevent our unit from being unconfigured while open. */ 64677d85de2Sthorpej switch (fmt) { 64777d85de2Sthorpej case S_IFCHR: 64877d85de2Sthorpej cs->sc_dkdev.dk_copenmask |= pmask; 64977d85de2Sthorpej break; 65077d85de2Sthorpej 65177d85de2Sthorpej case S_IFBLK: 65277d85de2Sthorpej cs->sc_dkdev.dk_bopenmask |= pmask; 65377d85de2Sthorpej break; 65477d85de2Sthorpej } 65577d85de2Sthorpej cs->sc_dkdev.dk_openmask = 65677d85de2Sthorpej cs->sc_dkdev.dk_copenmask | cs->sc_dkdev.dk_bopenmask; 65777d85de2Sthorpej 658b8dcfbd3Sthorpej done: 659481ff5acSad mutex_exit(&cs->sc_dvlock); 66049c5d29eSthorpej return (error); 6616f9db1eeShpeyerl } 6626f9db1eeShpeyerl 66377d85de2Sthorpej /* ARGSUSED */ 664b8d38500Sthorpej static int 665168cd830Schristos ccdclose(dev_t dev, int flags, int fmt, struct lwp *l) 666a44a2765Scgd { 66777d85de2Sthorpej int unit = ccdunit(dev); 66877d85de2Sthorpej struct ccd_softc *cs; 6699abeea58Sad int part; 67077d85de2Sthorpej 671a44a2765Scgd #ifdef DEBUG 672a44a2765Scgd if (ccddebug & CCDB_FOLLOW) 67308ebead9Scegger printf("ccdclose(0x%"PRIx64", 0x%x)\n", dev, flags); 674a44a2765Scgd #endif 67577d85de2Sthorpej 676ef781023Schristos if ((cs = ccdget(unit, 0)) == NULL) 677b08b65c6Schristos return ENXIO; 678b8dcfbd3Sthorpej 679481ff5acSad mutex_enter(&cs->sc_dvlock); 680b8dcfbd3Sthorpej 68177d85de2Sthorpej part = DISKPART(dev); 68277d85de2Sthorpej 68377d85de2Sthorpej /* ...that much closer to allowing unconfiguration... */ 68477d85de2Sthorpej switch (fmt) { 68577d85de2Sthorpej case S_IFCHR: 68677d85de2Sthorpej cs->sc_dkdev.dk_copenmask &= ~(1 << part); 68777d85de2Sthorpej break; 68877d85de2Sthorpej 68977d85de2Sthorpej case S_IFBLK: 69077d85de2Sthorpej cs->sc_dkdev.dk_bopenmask &= ~(1 << part); 69177d85de2Sthorpej break; 69277d85de2Sthorpej } 69377d85de2Sthorpej cs->sc_dkdev.dk_openmask = 69477d85de2Sthorpej cs->sc_dkdev.dk_copenmask | cs->sc_dkdev.dk_bopenmask; 69577d85de2Sthorpej 6964aef0509Sthorpej if (cs->sc_dkdev.dk_openmask == 0) { 6974aef0509Sthorpej if ((cs->sc_flags & CCDF_KLABEL) == 0) 6984aef0509Sthorpej cs->sc_flags &= ~CCDF_VLABEL; 6994aef0509Sthorpej } 7004aef0509Sthorpej 701481ff5acSad mutex_exit(&cs->sc_dvlock); 702a44a2765Scgd return (0); 703a44a2765Scgd } 704a44a2765Scgd 705481ff5acSad static void 706481ff5acSad ccdthread(void *cookie) 707481ff5acSad { 70879f4a4f8Shannken int error; 709481ff5acSad struct ccd_softc *cs; 71079f4a4f8Shannken struct buf *bp; 711481ff5acSad 712481ff5acSad cs = cookie; 713481ff5acSad 714481ff5acSad #ifdef DEBUG 715481ff5acSad if (ccddebug & CCDB_FOLLOW) 716481ff5acSad printf("ccdthread: hello\n"); 717481ff5acSad #endif 718481ff5acSad 719481ff5acSad mutex_enter(cs->sc_iolock); 720481ff5acSad while (__predict_true(!cs->sc_zap)) { 72179f4a4f8Shannken bp = bufq_get(cs->sc_bufq); 72279f4a4f8Shannken if (bp == NULL) { 723481ff5acSad /* Nothing to do. */ 724481ff5acSad cv_wait(&cs->sc_push, cs->sc_iolock); 725481ff5acSad continue; 726481ff5acSad } 727481ff5acSad #ifdef DEBUG 728481ff5acSad if (ccddebug & CCDB_FOLLOW) 729481ff5acSad printf("ccdthread: dispatching I/O\n"); 730481ff5acSad #endif 73179f4a4f8Shannken error = ccdstart(cs, bp, PR_WAITOK); 73279f4a4f8Shannken KASSERT(error == 0); 733481ff5acSad mutex_enter(cs->sc_iolock); 734481ff5acSad } 735481ff5acSad cs->sc_thread = NULL; 736481ff5acSad mutex_exit(cs->sc_iolock); 737481ff5acSad #ifdef DEBUG 738481ff5acSad if (ccddebug & CCDB_FOLLOW) 739481ff5acSad printf("ccdthread: goodbye\n"); 740481ff5acSad #endif 741481ff5acSad kthread_exit(0); 742481ff5acSad } 743481ff5acSad 744b8d38500Sthorpej static void 745b8d38500Sthorpej ccdstrategy(struct buf *bp) 7466f9db1eeShpeyerl { 747169ac5b3Saugustss int unit = ccdunit(bp->b_dev); 748b08b65c6Schristos struct ccd_softc *cs; 749ef781023Schristos if ((cs = ccdget(unit, 0)) == NULL) 750b08b65c6Schristos return; 7516f9db1eeShpeyerl 752481ff5acSad /* Must be open or reading label. */ 753481ff5acSad KASSERT(cs->sc_dkdev.dk_openmask != 0 || 754481ff5acSad (cs->sc_flags & CCDF_RLABEL) != 0); 755481ff5acSad 756481ff5acSad mutex_enter(cs->sc_iolock); 757481ff5acSad /* Synchronize with device init/uninit. */ 758481ff5acSad if (__predict_false((cs->sc_flags & CCDF_INITED) == 0)) { 759481ff5acSad mutex_exit(cs->sc_iolock); 760c6aa25bfSthorpej #ifdef DEBUG 761c6aa25bfSthorpej if (ccddebug & CCDB_FOLLOW) 762c6aa25bfSthorpej printf("ccdstrategy: unit %d: not inited\n", unit); 763c6aa25bfSthorpej #endif 7646f9db1eeShpeyerl bp->b_error = ENXIO; 765481ff5acSad bp->b_resid = bp->b_bcount; 766481ff5acSad biodone(bp); 767481ff5acSad return; 7686f9db1eeShpeyerl } 76977d85de2Sthorpej 77079f4a4f8Shannken if (ccdstart(cs, bp, PR_NOWAIT) != 0) { 771481ff5acSad /* Defer to thread if system is low on memory. */ 772481ff5acSad bufq_put(cs->sc_bufq, bp); 77379f4a4f8Shannken cv_broadcast(&cs->sc_push); 774481ff5acSad mutex_exit(cs->sc_iolock); 775481ff5acSad } 776481ff5acSad } 777481ff5acSad 77879f4a4f8Shannken static int 77979f4a4f8Shannken ccdstart(struct ccd_softc *cs, struct buf *bp, int wait) 780481ff5acSad { 781481ff5acSad daddr_t blkno; 782481ff5acSad int wlabel; 783481ff5acSad struct disklabel *lp; 784481ff5acSad long bcount, rcount; 785481ff5acSad struct ccdbuf *cbp; 786481ff5acSad char *addr; 787481ff5acSad daddr_t bn; 788481ff5acSad vnode_t *vp; 78979f4a4f8Shannken SIMPLEQ_HEAD(, ccdbuf) cbufq; 790481ff5acSad 791481ff5acSad KASSERT(mutex_owned(cs->sc_iolock)); 792481ff5acSad KASSERT(bp != NULL); 793481ff5acSad 794ba576b71Smlelstv disk_busy(&cs->sc_dkdev); 795ba576b71Smlelstv 796481ff5acSad #ifdef DEBUG 797481ff5acSad if (ccddebug & CCDB_FOLLOW) 798481ff5acSad printf("ccdstart(%s, %p)\n", cs->sc_xname, bp); 799481ff5acSad #endif 800481ff5acSad 80177d85de2Sthorpej /* If it's a nil transfer, wake up the top half now. */ 80277d85de2Sthorpej if (bp->b_bcount == 0) 8036f9db1eeShpeyerl goto done; 80477d85de2Sthorpej 8055b39541eSthorpej lp = cs->sc_dkdev.dk_label; 806b8dcfbd3Sthorpej 80777d85de2Sthorpej /* 808ab0109adSthorpej * Do bounds checking and adjust transfer. If there's an 8092f25411dSthorpej * error, the bounds check will flag that for us. Convert 8102f25411dSthorpej * the partition relative block number to an absolute. 81177d85de2Sthorpej */ 8122f25411dSthorpej blkno = bp->b_blkno; 81377d85de2Sthorpej wlabel = cs->sc_flags & (CCDF_WLABEL|CCDF_LABELLING); 8142f25411dSthorpej if (DISKPART(bp->b_dev) != RAW_PART) { 815e43fecb2Sthorpej if (bounds_check_with_label(&cs->sc_dkdev, bp, wlabel) <= 0) 8166f9db1eeShpeyerl goto done; 8172f25411dSthorpej blkno += lp->d_partitions[DISKPART(bp->b_dev)].p_offset; 8182f25411dSthorpej } 819481ff5acSad mutex_exit(cs->sc_iolock); 8202f25411dSthorpej bp->b_rawblkno = blkno; 82177d85de2Sthorpej 82279f4a4f8Shannken /* Allocate the component buffers. */ 82379f4a4f8Shannken SIMPLEQ_INIT(&cbufq); 8242f25411dSthorpej bp->b_resid = bp->b_bcount; 8252f25411dSthorpej bn = bp->b_rawblkno; 8263214723fShpeyerl addr = bp->b_data; 8276f9db1eeShpeyerl for (bcount = bp->b_bcount; bcount > 0; bcount -= rcount) { 82879f4a4f8Shannken cbp = ccdbuffer(cs, bp, bn, addr, bcount, wait); 82979f4a4f8Shannken KASSERT(cbp != NULL || wait == PR_NOWAIT); 83079f4a4f8Shannken if (cbp == NULL) { 83179f4a4f8Shannken while ((cbp = SIMPLEQ_FIRST(&cbufq)) != NULL) { 83279f4a4f8Shannken SIMPLEQ_REMOVE_HEAD(&cbufq, cb_q); 83379f4a4f8Shannken CCD_PUTBUF(cbp); 83479f4a4f8Shannken } 83579f4a4f8Shannken mutex_enter(cs->sc_iolock); 83679f4a4f8Shannken disk_unbusy(&cs->sc_dkdev, 0, 0); 83779f4a4f8Shannken return ENOMEM; 83879f4a4f8Shannken } 83979f4a4f8Shannken SIMPLEQ_INSERT_TAIL(&cbufq, cbp, cb_q); 840092c2019Sthorpej rcount = cbp->cb_buf.b_bcount; 841d272bb00Sthorpej bn += btodb(rcount); 842d272bb00Sthorpej addr += rcount; 84379f4a4f8Shannken } 84479f4a4f8Shannken 84579f4a4f8Shannken /* All buffers set up, now fire off the requests. */ 84679f4a4f8Shannken while ((cbp = SIMPLEQ_FIRST(&cbufq)) != NULL) { 84779f4a4f8Shannken SIMPLEQ_REMOVE_HEAD(&cbufq, cb_q); 848481ff5acSad vp = cbp->cb_buf.b_vp; 849481ff5acSad if ((cbp->cb_buf.b_flags & B_READ) == 0) { 850e225b7bdSrmind mutex_enter(vp->v_interlock); 851481ff5acSad vp->v_numoutput++; 852e225b7bdSrmind mutex_exit(vp->v_interlock); 853d272bb00Sthorpej } 854481ff5acSad (void)VOP_STRATEGY(vp, &cbp->cb_buf); 855481ff5acSad } 85679f4a4f8Shannken return 0; 857d272bb00Sthorpej 858481ff5acSad done: 859481ff5acSad disk_unbusy(&cs->sc_dkdev, 0, 0); 860481ff5acSad cv_broadcast(&cs->sc_stop); 861481ff5acSad cv_broadcast(&cs->sc_push); 862481ff5acSad mutex_exit(cs->sc_iolock); 863481ff5acSad bp->b_resid = bp->b_bcount; 864481ff5acSad biodone(bp); 86579f4a4f8Shannken return 0; 8662f25411dSthorpej } 8676f9db1eeShpeyerl 8686f9db1eeShpeyerl /* 8696f9db1eeShpeyerl * Build a component buffer header. 8706f9db1eeShpeyerl */ 871092c2019Sthorpej static struct ccdbuf * 87253524e44Schristos ccdbuffer(struct ccd_softc *cs, struct buf *bp, daddr_t bn, void *addr, 87379f4a4f8Shannken long bcount, int wait) 8746f9db1eeShpeyerl { 875169ac5b3Saugustss struct ccdcinfo *ci; 876169ac5b3Saugustss struct ccdbuf *cbp; 877169ac5b3Saugustss daddr_t cbn, cboff; 878169ac5b3Saugustss u_int64_t cbc; 8797770d7c4Sthorpej int ccdisk; 8806f9db1eeShpeyerl 8816f9db1eeShpeyerl #ifdef DEBUG 8823214723fShpeyerl if (ccddebug & CCDB_IO) 8834e0e5333Skleink printf("ccdbuffer(%p, %p, %" PRId64 ", %p, %ld)\n", 8846f9db1eeShpeyerl cs, bp, bn, addr, bcount); 8856f9db1eeShpeyerl #endif 8866f9db1eeShpeyerl /* 8876f9db1eeShpeyerl * Determine which component bn falls in. 8886f9db1eeShpeyerl */ 8896f9db1eeShpeyerl cbn = bn; 8906f9db1eeShpeyerl cboff = 0; 89177d85de2Sthorpej 8926f9db1eeShpeyerl /* 8936f9db1eeShpeyerl * Serially concatenated 8946f9db1eeShpeyerl */ 8956f9db1eeShpeyerl if (cs->sc_ileave == 0) { 896169ac5b3Saugustss daddr_t sblk; 8976f9db1eeShpeyerl 8986f9db1eeShpeyerl sblk = 0; 8997770d7c4Sthorpej for (ccdisk = 0, ci = &cs->sc_cinfo[ccdisk]; 9007770d7c4Sthorpej cbn >= sblk + ci->ci_size; 9017770d7c4Sthorpej ccdisk++, ci = &cs->sc_cinfo[ccdisk]) 9026f9db1eeShpeyerl sblk += ci->ci_size; 9036f9db1eeShpeyerl cbn -= sblk; 9046f9db1eeShpeyerl } 9056f9db1eeShpeyerl /* 9066f9db1eeShpeyerl * Interleaved 9076f9db1eeShpeyerl */ 9086f9db1eeShpeyerl else { 909169ac5b3Saugustss struct ccdiinfo *ii; 9107770d7c4Sthorpej int off; 9116f9db1eeShpeyerl 9126f9db1eeShpeyerl cboff = cbn % cs->sc_ileave; 9136f9db1eeShpeyerl cbn /= cs->sc_ileave; 9146f9db1eeShpeyerl for (ii = cs->sc_itable; ii->ii_ndisk; ii++) 9156f9db1eeShpeyerl if (ii->ii_startblk > cbn) 9166f9db1eeShpeyerl break; 9176f9db1eeShpeyerl ii--; 9186f9db1eeShpeyerl off = cbn - ii->ii_startblk; 9196f9db1eeShpeyerl if (ii->ii_ndisk == 1) { 9206f9db1eeShpeyerl ccdisk = ii->ii_index[0]; 9216f9db1eeShpeyerl cbn = ii->ii_startoff + off; 9226f9db1eeShpeyerl } else { 9236f9db1eeShpeyerl ccdisk = ii->ii_index[off % ii->ii_ndisk]; 9246f9db1eeShpeyerl cbn = ii->ii_startoff + off / ii->ii_ndisk; 9256f9db1eeShpeyerl } 9266f9db1eeShpeyerl cbn *= cs->sc_ileave; 9276f9db1eeShpeyerl ci = &cs->sc_cinfo[ccdisk]; 9286f9db1eeShpeyerl } 92977d85de2Sthorpej 9306f9db1eeShpeyerl /* 9316f9db1eeShpeyerl * Fill in the component buf structure. 9326f9db1eeShpeyerl */ 93379f4a4f8Shannken cbp = CCD_GETBUF(wait); 93479f4a4f8Shannken if (cbp == NULL) 93579f4a4f8Shannken return NULL; 9364a780c9aSad buf_init(&cbp->cb_buf); 9374a780c9aSad cbp->cb_buf.b_flags = bp->b_flags; 9384a780c9aSad cbp->cb_buf.b_oflags = bp->b_oflags; 9394a780c9aSad cbp->cb_buf.b_cflags = bp->b_cflags; 940d318abdbSchristos cbp->cb_buf.b_iodone = ccdiodone; 941f694af67Scgd cbp->cb_buf.b_proc = bp->b_proc; 9423db4e2acShannken cbp->cb_buf.b_dev = ci->ci_dev; 943f694af67Scgd cbp->cb_buf.b_blkno = cbn + cboff; 944f694af67Scgd cbp->cb_buf.b_data = addr; 94577d85de2Sthorpej cbp->cb_buf.b_vp = ci->ci_vp; 946e225b7bdSrmind cbp->cb_buf.b_objlock = ci->ci_vp->v_interlock; 9476f9db1eeShpeyerl if (cs->sc_ileave == 0) 948902855d6Sthorpej cbc = dbtob((u_int64_t)(ci->ci_size - cbn)); 9496f9db1eeShpeyerl else 950902855d6Sthorpej cbc = dbtob((u_int64_t)(cs->sc_ileave - cboff)); 951902855d6Sthorpej cbp->cb_buf.b_bcount = cbc < bcount ? cbc : bcount; 952f694af67Scgd 9536f9db1eeShpeyerl /* 954f694af67Scgd * context for ccdiodone 9556f9db1eeShpeyerl */ 956f694af67Scgd cbp->cb_obp = bp; 957c6aa25bfSthorpej cbp->cb_sc = cs; 9587770d7c4Sthorpej cbp->cb_comp = ccdisk; 959f694af67Scgd 9607266a959Syamt BIO_COPYPRIO(&cbp->cb_buf, bp); 9617266a959Syamt 9626f9db1eeShpeyerl #ifdef DEBUG 9633214723fShpeyerl if (ccddebug & CCDB_IO) 96408ebead9Scegger printf(" dev 0x%"PRIx64"(u%lu): cbp %p bn %" PRId64 " addr %p" 96522399b45Syamt " bcnt %d\n", 966fefcc128Smjacob ci->ci_dev, (unsigned long) (ci-cs->sc_cinfo), cbp, 967fefcc128Smjacob cbp->cb_buf.b_blkno, cbp->cb_buf.b_data, 968fefcc128Smjacob cbp->cb_buf.b_bcount); 9696f9db1eeShpeyerl #endif 970092c2019Sthorpej 971092c2019Sthorpej return (cbp); 9726f9db1eeShpeyerl } 9736f9db1eeShpeyerl 9746f9db1eeShpeyerl /* 97577d85de2Sthorpej * Called at interrupt time. 9766f9db1eeShpeyerl * Mark the component as done and if all components are done, 9776f9db1eeShpeyerl * take a ccd interrupt. 9786f9db1eeShpeyerl */ 979b8d38500Sthorpej static void 980b8d38500Sthorpej ccdiodone(struct buf *vbp) 9816f9db1eeShpeyerl { 982d318abdbSchristos struct ccdbuf *cbp = (struct ccdbuf *) vbp; 983c6aa25bfSthorpej struct buf *bp = cbp->cb_obp; 984c6aa25bfSthorpej struct ccd_softc *cs = cbp->cb_sc; 985481ff5acSad int count; 9866f9db1eeShpeyerl 9876f9db1eeShpeyerl #ifdef DEBUG 9883214723fShpeyerl if (ccddebug & CCDB_FOLLOW) 98986373f8cSchristos printf("ccdiodone(%p)\n", cbp); 9903214723fShpeyerl if (ccddebug & CCDB_IO) { 99122399b45Syamt printf("ccdiodone: bp %p bcount %d resid %d\n", 9926f9db1eeShpeyerl bp, bp->b_bcount, bp->b_resid); 99308ebead9Scegger printf(" dev 0x%"PRIx64"(u%d), cbp %p bn %" PRId64 " addr %p" 99422399b45Syamt " bcnt %d\n", 995f694af67Scgd cbp->cb_buf.b_dev, cbp->cb_comp, cbp, 996f694af67Scgd cbp->cb_buf.b_blkno, cbp->cb_buf.b_data, 997f694af67Scgd cbp->cb_buf.b_bcount); 9986f9db1eeShpeyerl } 9996f9db1eeShpeyerl #endif 10006f9db1eeShpeyerl 1001eb171eaaSad if (cbp->cb_buf.b_error != 0) { 1002eb171eaaSad bp->b_error = cbp->cb_buf.b_error; 100300172c10Sthorpej printf("%s: error %d on component %d\n", 100400172c10Sthorpej cs->sc_xname, bp->b_error, cbp->cb_comp); 10056f9db1eeShpeyerl } 1006f694af67Scgd count = cbp->cb_buf.b_bcount; 10074a780c9aSad buf_destroy(&cbp->cb_buf); 1008dd5ae858Sthorpej CCD_PUTBUF(cbp); 10096f9db1eeShpeyerl 10106f9db1eeShpeyerl /* 10116f9db1eeShpeyerl * If all done, "interrupt". 10126f9db1eeShpeyerl */ 1013481ff5acSad mutex_enter(cs->sc_iolock); 10146f9db1eeShpeyerl bp->b_resid -= count; 10156f9db1eeShpeyerl if (bp->b_resid < 0) 10166f9db1eeShpeyerl panic("ccdiodone: count"); 1017481ff5acSad if (bp->b_resid == 0) { 1018481ff5acSad /* 1019481ff5acSad * Request is done for better or worse, wakeup the top half. 1020481ff5acSad */ 1021481ff5acSad if (bp->b_error != 0) 1022481ff5acSad bp->b_resid = bp->b_bcount; 1023481ff5acSad disk_unbusy(&cs->sc_dkdev, (bp->b_bcount - bp->b_resid), 1024481ff5acSad (bp->b_flags & B_READ)); 1025481ff5acSad if (!disk_isbusy(&cs->sc_dkdev)) { 1026481ff5acSad if (bufq_peek(cs->sc_bufq) != NULL) { 1027481ff5acSad cv_broadcast(&cs->sc_push); 1028481ff5acSad } 1029481ff5acSad cv_broadcast(&cs->sc_stop); 1030481ff5acSad } 1031481ff5acSad mutex_exit(cs->sc_iolock); 1032481ff5acSad biodone(bp); 1033481ff5acSad } else 1034481ff5acSad mutex_exit(cs->sc_iolock); 10356f9db1eeShpeyerl } 10366f9db1eeShpeyerl 103777d85de2Sthorpej /* ARGSUSED */ 1038b8d38500Sthorpej static int 1039168cd830Schristos ccdread(dev_t dev, struct uio *uio, int flags) 10403214723fShpeyerl { 104177d85de2Sthorpej int unit = ccdunit(dev); 104277d85de2Sthorpej struct ccd_softc *cs; 10433214723fShpeyerl 10443214723fShpeyerl #ifdef DEBUG 10453214723fShpeyerl if (ccddebug & CCDB_FOLLOW) 104608ebead9Scegger printf("ccdread(0x%"PRIx64", %p)\n", dev, uio); 10473214723fShpeyerl #endif 1048ef781023Schristos if ((cs = ccdget(unit, 0)) == NULL) 1049b08b65c6Schristos return 0; 105077d85de2Sthorpej 1051481ff5acSad /* Unlocked advisory check, ccdstrategy check is synchronous. */ 105277d85de2Sthorpej if ((cs->sc_flags & CCDF_INITED) == 0) 105377d85de2Sthorpej return (ENXIO); 105477d85de2Sthorpej 10553214723fShpeyerl return (physio(ccdstrategy, NULL, dev, B_READ, minphys, uio)); 10563214723fShpeyerl } 10573214723fShpeyerl 105877d85de2Sthorpej /* ARGSUSED */ 1059b8d38500Sthorpej static int 1060168cd830Schristos ccdwrite(dev_t dev, struct uio *uio, int flags) 10613214723fShpeyerl { 106277d85de2Sthorpej int unit = ccdunit(dev); 106377d85de2Sthorpej struct ccd_softc *cs; 10643214723fShpeyerl 10653214723fShpeyerl #ifdef DEBUG 10663214723fShpeyerl if (ccddebug & CCDB_FOLLOW) 106708ebead9Scegger printf("ccdwrite(0x%"PRIx64", %p)\n", dev, uio); 10683214723fShpeyerl #endif 1069ef781023Schristos if ((cs = ccdget(unit, 0)) == NULL) 1070b08b65c6Schristos return ENOENT; 107177d85de2Sthorpej 1072481ff5acSad /* Unlocked advisory check, ccdstrategy check is synchronous. */ 107377d85de2Sthorpej if ((cs->sc_flags & CCDF_INITED) == 0) 107477d85de2Sthorpej return (ENXIO); 107577d85de2Sthorpej 10763214723fShpeyerl return (physio(ccdstrategy, NULL, dev, B_WRITE, minphys, uio)); 10773214723fShpeyerl } 10783214723fShpeyerl 1079e634efa8Schristos int (*compat_ccd_ioctl_60)(dev_t, u_long, void *, int, struct lwp *, 1080e634efa8Schristos int (*)(dev_t, u_long, void *, int, struct lwp *)) = (void *)enosys; 1081e634efa8Schristos 1082b8d38500Sthorpej static int 108353524e44Schristos ccdioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l) 10846f9db1eeShpeyerl { 108577d85de2Sthorpej int unit = ccdunit(dev); 1086481ff5acSad int i, j, lookedup = 0, error = 0; 1087d91f98a8Spgoyette int part, pmask, make, hook; 108877d85de2Sthorpej struct ccd_softc *cs; 108977d85de2Sthorpej struct ccd_ioctl *ccio = (struct ccd_ioctl *)data; 10902867b68bSelad kauth_cred_t uc; 109177d85de2Sthorpej char **cpp; 10928f6ed30dSdholland struct pathbuf *pb; 109377d85de2Sthorpej struct vnode **vpp; 1094e2d1c1f9Sfvdl #ifdef __HAVE_OLD_DISKLABEL 1095e2d1c1f9Sfvdl struct disklabel newlabel; 1096e2d1c1f9Sfvdl #endif 109777d85de2Sthorpej 1098ef781023Schristos switch (cmd) { 1099ef781023Schristos case CCDIOCSET: 1100ef781023Schristos make = 1; 1101ef781023Schristos break; 1102ef781023Schristos default: 11038c2f80f1Spgoyette MODULE_HOOK_CALL(ccd_ioctl_60_hook, 1104d91f98a8Spgoyette (0, cmd, NULL, 0, NULL, NULL), 1105d91f98a8Spgoyette enosys(), hook); 1106d91f98a8Spgoyette if (hook == 0) 1107e634efa8Schristos make = 1; 1108e634efa8Schristos else 1109ef781023Schristos make = 0; 1110ef781023Schristos break; 1111ef781023Schristos } 1112ef781023Schristos 1113ef781023Schristos if ((cs = ccdget(unit, make)) == NULL) 1114b08b65c6Schristos return ENOENT; 1115481ff5acSad uc = kauth_cred_get(); 111697297f37Sjld 11178c2f80f1Spgoyette MODULE_HOOK_CALL(ccd_ioctl_60_hook, 1118d91f98a8Spgoyette (dev, cmd, data, flag, l, ccdioctl), 1119d91f98a8Spgoyette enosys(), error); 1120e634efa8Schristos if (error != ENOSYS) 11217d706cf7Ssborrill return error; 11227d706cf7Ssborrill 11234cea35ebSthorpej /* Must be open for writes for these commands... */ 11244cea35ebSthorpej switch (cmd) { 11254cea35ebSthorpej case CCDIOCSET: 11264cea35ebSthorpej case CCDIOCCLR: 11274cea35ebSthorpej case DIOCSDINFO: 11284cea35ebSthorpej case DIOCWDINFO: 1129c5ff2ab7Smlelstv case DIOCCACHESYNC: 1130c5ff2ab7Smlelstv case DIOCAWEDGE: 1131c5ff2ab7Smlelstv case DIOCDWEDGE: 1132ad4912fdSmartin case DIOCRMWEDGES: 1133c5ff2ab7Smlelstv case DIOCMWEDGES: 1134e2d1c1f9Sfvdl #ifdef __HAVE_OLD_DISKLABEL 1135e2d1c1f9Sfvdl case ODIOCSDINFO: 1136e2d1c1f9Sfvdl case ODIOCWDINFO: 1137e2d1c1f9Sfvdl #endif 11384aef0509Sthorpej case DIOCKLABEL: 11394cea35ebSthorpej case DIOCWLABEL: 11404cea35ebSthorpej if ((flag & FWRITE) == 0) 11414cea35ebSthorpej return (EBADF); 11424cea35ebSthorpej } 11434cea35ebSthorpej 11444cea35ebSthorpej /* Must be initialized for these... */ 11454cea35ebSthorpej switch (cmd) { 11464cea35ebSthorpej case CCDIOCCLR: 1147d72a3b2dSkleink case DIOCGDINFO: 114822308629Sjdolecek case DIOCGSTRATEGY: 114922308629Sjdolecek case DIOCGCACHE: 1150f64a29d7Sthorpej case DIOCCACHESYNC: 1151c5ff2ab7Smlelstv case DIOCAWEDGE: 1152c5ff2ab7Smlelstv case DIOCDWEDGE: 1153c5ff2ab7Smlelstv case DIOCLWEDGES: 1154c5ff2ab7Smlelstv case DIOCMWEDGES: 11554cea35ebSthorpej case DIOCSDINFO: 11564cea35ebSthorpej case DIOCWDINFO: 11578d10f962Schristos case DIOCGPARTINFO: 11584cea35ebSthorpej case DIOCWLABEL: 11594aef0509Sthorpej case DIOCKLABEL: 1160939e074dSthorpej case DIOCGDEFLABEL: 1161e2d1c1f9Sfvdl #ifdef __HAVE_OLD_DISKLABEL 1162e2d1c1f9Sfvdl case ODIOCGDINFO: 1163e2d1c1f9Sfvdl case ODIOCSDINFO: 1164e2d1c1f9Sfvdl case ODIOCWDINFO: 1165e2d1c1f9Sfvdl case ODIOCGDEFLABEL: 1166e2d1c1f9Sfvdl #endif 11677686d3bdSmlelstv if ((cs->sc_flags & CCDF_INITED) == 0) 11687686d3bdSmlelstv return ENXIO; 11694cea35ebSthorpej } 11704cea35ebSthorpej 1171c60db2e9Schristos error = disk_ioctl(&cs->sc_dkdev, dev, cmd, data, flag, l); 117275320acfSchristos if (error != EPASSTHROUGH) 11737686d3bdSmlelstv return error; 11747686d3bdSmlelstv 11757686d3bdSmlelstv switch (cmd) { 11767686d3bdSmlelstv case DIOCGSTRATEGY: 11777686d3bdSmlelstv { 11787686d3bdSmlelstv struct disk_strategy *dks = (void *)data; 11797686d3bdSmlelstv 11807686d3bdSmlelstv mutex_enter(cs->sc_iolock); 11817686d3bdSmlelstv if (cs->sc_bufq != NULL) 11827686d3bdSmlelstv strlcpy(dks->dks_name, 11837686d3bdSmlelstv bufq_getstrategyname(cs->sc_bufq), 11847686d3bdSmlelstv sizeof(dks->dks_name)); 11857686d3bdSmlelstv else 11867686d3bdSmlelstv error = EINVAL; 11877686d3bdSmlelstv mutex_exit(cs->sc_iolock); 11887686d3bdSmlelstv dks->dks_paramlen = 0; 11897686d3bdSmlelstv break; 11907686d3bdSmlelstv } 11917686d3bdSmlelstv 11927686d3bdSmlelstv case DIOCWDINFO: 11937686d3bdSmlelstv case DIOCSDINFO: 11947686d3bdSmlelstv #ifdef __HAVE_OLD_DISKLABEL 11957686d3bdSmlelstv case ODIOCWDINFO: 11967686d3bdSmlelstv case ODIOCSDINFO: 11977686d3bdSmlelstv #endif 11987686d3bdSmlelstv { 11997686d3bdSmlelstv struct disklabel *lp; 12007686d3bdSmlelstv #ifdef __HAVE_OLD_DISKLABEL 12017686d3bdSmlelstv if (cmd == ODIOCSDINFO || cmd == ODIOCWDINFO) { 12027686d3bdSmlelstv memset(&newlabel, 0, sizeof newlabel); 12037686d3bdSmlelstv memcpy(&newlabel, data, sizeof (struct olddisklabel)); 12047686d3bdSmlelstv lp = &newlabel; 12057686d3bdSmlelstv } else 12067686d3bdSmlelstv #endif 12077686d3bdSmlelstv lp = (struct disklabel *)data; 12087686d3bdSmlelstv 12097686d3bdSmlelstv cs->sc_flags |= CCDF_LABELLING; 12107686d3bdSmlelstv 12117686d3bdSmlelstv error = setdisklabel(cs->sc_dkdev.dk_label, 12127686d3bdSmlelstv lp, 0, cs->sc_dkdev.dk_cpulabel); 12137686d3bdSmlelstv if (error == 0) { 12147686d3bdSmlelstv if (cmd == DIOCWDINFO 12157686d3bdSmlelstv #ifdef __HAVE_OLD_DISKLABEL 12167686d3bdSmlelstv || cmd == ODIOCWDINFO 12177686d3bdSmlelstv #endif 12187686d3bdSmlelstv ) 12197686d3bdSmlelstv error = writedisklabel(CCDLABELDEV(dev), 12207686d3bdSmlelstv ccdstrategy, cs->sc_dkdev.dk_label, 12217686d3bdSmlelstv cs->sc_dkdev.dk_cpulabel); 12227686d3bdSmlelstv } 12237686d3bdSmlelstv 12247686d3bdSmlelstv cs->sc_flags &= ~CCDF_LABELLING; 12257686d3bdSmlelstv break; 12267686d3bdSmlelstv } 12277686d3bdSmlelstv 12287686d3bdSmlelstv case DIOCKLABEL: 12297686d3bdSmlelstv if (*(int *)data != 0) 12307686d3bdSmlelstv cs->sc_flags |= CCDF_KLABEL; 12317686d3bdSmlelstv else 12327686d3bdSmlelstv cs->sc_flags &= ~CCDF_KLABEL; 12337686d3bdSmlelstv break; 12347686d3bdSmlelstv 12357686d3bdSmlelstv case DIOCWLABEL: 12367686d3bdSmlelstv if (*(int *)data != 0) 12377686d3bdSmlelstv cs->sc_flags |= CCDF_WLABEL; 12387686d3bdSmlelstv else 12397686d3bdSmlelstv cs->sc_flags &= ~CCDF_WLABEL; 12407686d3bdSmlelstv break; 12417686d3bdSmlelstv 12427686d3bdSmlelstv case DIOCGDEFLABEL: 12437686d3bdSmlelstv ccdgetdefaultlabel(cs, (struct disklabel *)data); 12447686d3bdSmlelstv break; 12457686d3bdSmlelstv 12467686d3bdSmlelstv #ifdef __HAVE_OLD_DISKLABEL 12477686d3bdSmlelstv case ODIOCGDEFLABEL: 12487686d3bdSmlelstv ccdgetdefaultlabel(cs, &newlabel); 12497686d3bdSmlelstv if (newlabel.d_npartitions > OLDMAXPARTITIONS) 12507686d3bdSmlelstv return ENOTTY; 12517686d3bdSmlelstv memcpy(data, &newlabel, sizeof (struct olddisklabel)); 12527686d3bdSmlelstv break; 12537686d3bdSmlelstv #endif 12547686d3bdSmlelstv default: 12557686d3bdSmlelstv error = ENOTTY; 12567686d3bdSmlelstv break; 12577686d3bdSmlelstv } 12587686d3bdSmlelstv 12597686d3bdSmlelstv if (error != ENOTTY) 12607686d3bdSmlelstv return error; 12617686d3bdSmlelstv 12627686d3bdSmlelstv mutex_enter(&cs->sc_dvlock); 126375320acfSchristos 1264c5ff2ab7Smlelstv error = 0; 126577d85de2Sthorpej switch (cmd) { 126677d85de2Sthorpej case CCDIOCSET: 12670272b2abSthorpej if (cs->sc_flags & CCDF_INITED) { 12680272b2abSthorpej error = EBUSY; 12690272b2abSthorpej goto out; 12700272b2abSthorpej } 1271b8dcfbd3Sthorpej 1272ab27c3f8Sthorpej /* Validate the flags. */ 12730272b2abSthorpej if ((ccio->ccio_flags & CCDF_USERMASK) != ccio->ccio_flags) { 12740272b2abSthorpej error = EINVAL; 12750272b2abSthorpej goto out; 12760272b2abSthorpej } 1277ab27c3f8Sthorpej 1278481ff5acSad if (ccio->ccio_ndisks > CCD_MAXNDISKS || 1279481ff5acSad ccio->ccio_ndisks == 0) { 1280539f3bccSjdolecek error = EINVAL; 1281539f3bccSjdolecek goto out; 1282539f3bccSjdolecek } 1283539f3bccSjdolecek 128477d85de2Sthorpej /* Fill in some important bits. */ 12850272b2abSthorpej cs->sc_ileave = ccio->ccio_ileave; 12860272b2abSthorpej cs->sc_nccdisks = ccio->ccio_ndisks; 12870272b2abSthorpej cs->sc_flags = ccio->ccio_flags & CCDF_USERMASK; 128877d85de2Sthorpej 128977d85de2Sthorpej /* 129077d85de2Sthorpej * Allocate space for and copy in the array of 12917d706cf7Ssborrill * component pathnames and device numbers. 129277d85de2Sthorpej */ 1293481ff5acSad cpp = kmem_alloc(ccio->ccio_ndisks * sizeof(*cpp), KM_SLEEP); 1294481ff5acSad vpp = kmem_alloc(ccio->ccio_ndisks * sizeof(*vpp), KM_SLEEP); 1295bd99e342Sdsl error = copyin(ccio->ccio_disks, cpp, 1296481ff5acSad ccio->ccio_ndisks * sizeof(*cpp)); 129777d85de2Sthorpej if (error) { 1298481ff5acSad kmem_free(vpp, ccio->ccio_ndisks * sizeof(*vpp)); 1299481ff5acSad kmem_free(cpp, ccio->ccio_ndisks * sizeof(*cpp)); 13000272b2abSthorpej goto out; 13016f9db1eeShpeyerl } 13026f9db1eeShpeyerl 130377d85de2Sthorpej #ifdef DEBUG 130477d85de2Sthorpej if (ccddebug & CCDB_INIT) 130577d85de2Sthorpej for (i = 0; i < ccio->ccio_ndisks; ++i) 13067cdea212Schristos printf("ccdioctl: component %d: %p\n", 130777d85de2Sthorpej i, cpp[i]); 130877d85de2Sthorpej #endif 130977d85de2Sthorpej 131077d85de2Sthorpej for (i = 0; i < ccio->ccio_ndisks; ++i) { 131177d85de2Sthorpej #ifdef DEBUG 131277d85de2Sthorpej if (ccddebug & CCDB_INIT) 131386373f8cSchristos printf("ccdioctl: lookedup = %d\n", lookedup); 131477d85de2Sthorpej #endif 13158f6ed30dSdholland error = pathbuf_copyin(cpp[i], &pb); 13160a4a6fb0Sdholland if (error == 0) { 13175a52f2afSmlelstv error = vn_bdev_openpath(pb, &vpp[i], l); 13188f6ed30dSdholland pathbuf_destroy(pb); 13195a9cf8fcSriastradh } 13208f6ed30dSdholland if (error != 0) { 132177d85de2Sthorpej for (j = 0; j < lookedup; ++j) 1322a2db2c18Sthorpej (void)vn_close(vpp[j], FREAD|FWRITE, 1323a9ca7a37Sad uc); 1324481ff5acSad kmem_free(vpp, ccio->ccio_ndisks * 1325481ff5acSad sizeof(*vpp)); 1326481ff5acSad kmem_free(cpp, ccio->ccio_ndisks * 1327481ff5acSad sizeof(*cpp)); 13287686d3bdSmlelstv 13297686d3bdSmlelstv /* 13307686d3bdSmlelstv * No component data is allocated, 13317686d3bdSmlelstv * nothing is to be freed. 13327686d3bdSmlelstv */ 13337686d3bdSmlelstv cs->sc_nccdisks = 0; 13340272b2abSthorpej goto out; 133577d85de2Sthorpej } 133677d85de2Sthorpej ++lookedup; 133777d85de2Sthorpej } 133877d85de2Sthorpej 1339481ff5acSad /* Attach the disk. */ 1340481ff5acSad disk_attach(&cs->sc_dkdev); 1341481ff5acSad bufq_alloc(&cs->sc_bufq, "fcfs", 0); 1342481ff5acSad 134377d85de2Sthorpej /* 134477d85de2Sthorpej * Initialize the ccd. Fills in the softc for us. 134577d85de2Sthorpej */ 134695e1ffb1Schristos if ((error = ccdinit(cs, cpp, vpp, l)) != 0) { 134777d85de2Sthorpej for (j = 0; j < lookedup; ++j) 1348a1bc3740Sthorpej (void)vn_close(vpp[j], FREAD|FWRITE, 1349a9ca7a37Sad uc); 1350481ff5acSad kmem_free(vpp, ccio->ccio_ndisks * sizeof(*vpp)); 1351481ff5acSad kmem_free(cpp, ccio->ccio_ndisks * sizeof(*cpp)); 1352481ff5acSad disk_detach(&cs->sc_dkdev); 1353e38abff0Spgoyette mutex_exit(&cs->sc_dvlock); 1354481ff5acSad bufq_free(cs->sc_bufq); 1355e38abff0Spgoyette return error; 135677d85de2Sthorpej } 135777d85de2Sthorpej 13580272b2abSthorpej /* We can free the temporary variables now. */ 1359481ff5acSad kmem_free(vpp, ccio->ccio_ndisks * sizeof(*vpp)); 1360481ff5acSad kmem_free(cpp, ccio->ccio_ndisks * sizeof(*cpp)); 13610272b2abSthorpej 136277d85de2Sthorpej /* 136377d85de2Sthorpej * The ccd has been successfully initialized, so 13645b39541eSthorpej * we can place it into the array. Don't try to 13655b39541eSthorpej * read the disklabel until the disk has been attached, 13665b39541eSthorpej * because space for the disklabel is allocated 13675b39541eSthorpej * in disk_attach(); 136877d85de2Sthorpej */ 136977d85de2Sthorpej ccio->ccio_unit = unit; 137077d85de2Sthorpej ccio->ccio_size = cs->sc_size; 13715b39541eSthorpej 13725b39541eSthorpej /* Try and read the disklabel. */ 137377d85de2Sthorpej ccdgetdisklabel(dev); 1374c5ff2ab7Smlelstv disk_set_info(NULL, &cs->sc_dkdev, NULL); 1375c5ff2ab7Smlelstv 1376c5ff2ab7Smlelstv /* discover wedges */ 1377c5ff2ab7Smlelstv mutex_exit(&cs->sc_dvlock); 1378c5ff2ab7Smlelstv dkwedge_discover(&cs->sc_dkdev); 1379c5ff2ab7Smlelstv return 0; 138077d85de2Sthorpej 138177d85de2Sthorpej case CCDIOCCLR: 138277d85de2Sthorpej /* 138377d85de2Sthorpej * Don't unconfigure if any other partitions are open 138477d85de2Sthorpej * or if both the character and block flavors of this 138577d85de2Sthorpej * partition are open. 138677d85de2Sthorpej */ 138777d85de2Sthorpej part = DISKPART(dev); 138877d85de2Sthorpej pmask = (1 << part); 138977d85de2Sthorpej if ((cs->sc_dkdev.dk_openmask & ~pmask) || 139077d85de2Sthorpej ((cs->sc_dkdev.dk_bopenmask & pmask) && 1391b8dcfbd3Sthorpej (cs->sc_dkdev.dk_copenmask & pmask))) { 13920272b2abSthorpej error = EBUSY; 13930272b2abSthorpej goto out; 1394b8dcfbd3Sthorpej } 139577d85de2Sthorpej 1396c5ff2ab7Smlelstv /* Delete all of our wedges. */ 1397c5ff2ab7Smlelstv dkwedge_delall(&cs->sc_dkdev); 1398c5ff2ab7Smlelstv 1399481ff5acSad /* Stop new I/O, wait for in-flight I/O to complete. */ 1400481ff5acSad mutex_enter(cs->sc_iolock); 1401481ff5acSad cs->sc_flags &= ~(CCDF_INITED|CCDF_VLABEL); 1402481ff5acSad cs->sc_zap = true; 1403481ff5acSad while (disk_isbusy(&cs->sc_dkdev) || 1404481ff5acSad bufq_peek(cs->sc_bufq) != NULL || 1405481ff5acSad cs->sc_thread != NULL) { 1406481ff5acSad cv_broadcast(&cs->sc_push); 1407481ff5acSad (void)cv_timedwait(&cs->sc_stop, cs->sc_iolock, hz); 1408481ff5acSad } 1409481ff5acSad mutex_exit(cs->sc_iolock); 14102f25411dSthorpej 141177d85de2Sthorpej /* 141277d85de2Sthorpej * Free ccd_softc information and clear entry. 141377d85de2Sthorpej */ 14147268bf55Sthorpej 14157268bf55Sthorpej /* Close the components and free their pathnames. */ 141677d85de2Sthorpej for (i = 0; i < cs->sc_nccdisks; ++i) { 141777d85de2Sthorpej /* 141877d85de2Sthorpej * XXX: this close could potentially fail and 141977d85de2Sthorpej * cause Bad Things. Maybe we need to force 142077d85de2Sthorpej * the close to happen? 142177d85de2Sthorpej */ 142277d85de2Sthorpej #ifdef DEBUG 142377d85de2Sthorpej if (ccddebug & CCDB_VNODE) 142477d85de2Sthorpej vprint("CCDIOCCLR: vnode info", 142577d85de2Sthorpej cs->sc_cinfo[i].ci_vp); 142677d85de2Sthorpej #endif 142777d85de2Sthorpej (void)vn_close(cs->sc_cinfo[i].ci_vp, FREAD|FWRITE, 1428a9ca7a37Sad uc); 1429481ff5acSad kmem_free(cs->sc_cinfo[i].ci_path, 1430481ff5acSad cs->sc_cinfo[i].ci_pathlen); 143177d85de2Sthorpej } 14327268bf55Sthorpej 14337686d3bdSmlelstv if (cs->sc_nccdisks != 0) { 14347268bf55Sthorpej /* Free interleave index. */ 1435481ff5acSad for (i = 0; cs->sc_itable[i].ii_ndisk; ++i) { 1436481ff5acSad kmem_free(cs->sc_itable[i].ii_index, 1437481ff5acSad cs->sc_itable[i].ii_indexsz); 1438481ff5acSad } 14397268bf55Sthorpej /* Free component info and interleave table. */ 1440481ff5acSad kmem_free(cs->sc_cinfo, cs->sc_nccdisks * 1441481ff5acSad sizeof(struct ccdcinfo)); 1442481ff5acSad kmem_free(cs->sc_itable, (cs->sc_nccdisks + 1) * 1443481ff5acSad sizeof(struct ccdiinfo)); 14447686d3bdSmlelstv } 144577d85de2Sthorpej 14467d706cf7Ssborrill aprint_normal("%s: detached\n", cs->sc_xname); 14477d706cf7Ssborrill 14487d706cf7Ssborrill /* Detach the disk. */ 14492af68666Sad disk_detach(&cs->sc_dkdev); 1450481ff5acSad bufq_free(cs->sc_bufq); 14517686d3bdSmlelstv 1452948b2359Sriastradh /* also releases sc_dvlock */ 1453b08b65c6Schristos ccdput(cs); 14547686d3bdSmlelstv 145555252202Sjoerg /* Don't break, otherwise cs is read again. */ 145655252202Sjoerg return 0; 145777d85de2Sthorpej 145822308629Sjdolecek case DIOCGCACHE: 145922308629Sjdolecek { 146022308629Sjdolecek int dkcache = 0; 146122308629Sjdolecek 146222308629Sjdolecek /* 146322308629Sjdolecek * We pass this call down to all components and report 146422308629Sjdolecek * intersection of the flags returned by the components. 146522308629Sjdolecek * If any errors out, we return error. CCD components 146622308629Sjdolecek * can not change unless the device is unconfigured, so 146722308629Sjdolecek * device feature flags will remain static. RCE/WCE can change 146822308629Sjdolecek * of course, if set directly on underlying device. 146922308629Sjdolecek */ 147022308629Sjdolecek for (error = 0, i = 0; i < cs->sc_nccdisks; i++) { 147122308629Sjdolecek error = VOP_IOCTL(cs->sc_cinfo[i].ci_vp, cmd, &j, 147222308629Sjdolecek flag, uc); 147322308629Sjdolecek if (error) 147422308629Sjdolecek break; 147522308629Sjdolecek 147622308629Sjdolecek if (i == 0) 147722308629Sjdolecek dkcache = j; 147822308629Sjdolecek else 14791af402feSjdolecek dkcache = DKCACHE_COMBINE(dkcache, j); 148022308629Sjdolecek } 148122308629Sjdolecek 148222308629Sjdolecek *((int *)data) = dkcache; 148322308629Sjdolecek break; 148422308629Sjdolecek } 148522308629Sjdolecek 1486f64a29d7Sthorpej case DIOCCACHESYNC: 1487f64a29d7Sthorpej /* 1488f64a29d7Sthorpej * We pass this call down to all components and report 1489f64a29d7Sthorpej * the first error we encounter. 1490f64a29d7Sthorpej */ 1491f64a29d7Sthorpej for (error = 0, i = 0; i < cs->sc_nccdisks; i++) { 1492f64a29d7Sthorpej j = VOP_IOCTL(cs->sc_cinfo[i].ci_vp, cmd, data, 149361e8303eSpooka flag, uc); 1494f64a29d7Sthorpej if (j != 0 && error == 0) 1495f64a29d7Sthorpej error = j; 1496f64a29d7Sthorpej } 1497f64a29d7Sthorpej break; 1498f64a29d7Sthorpej 149977d85de2Sthorpej default: 15000272b2abSthorpej error = ENOTTY; 15017686d3bdSmlelstv break; 150277d85de2Sthorpej } 150377d85de2Sthorpej 15040272b2abSthorpej out: 1505481ff5acSad mutex_exit(&cs->sc_dvlock); 15060272b2abSthorpej return (error); 150777d85de2Sthorpej } 150877d85de2Sthorpej 1509b8d38500Sthorpej static int 1510b8d38500Sthorpej ccdsize(dev_t dev) 15116f9db1eeShpeyerl { 151277d85de2Sthorpej struct ccd_softc *cs; 1513e06bb29bSthorpej struct disklabel *lp; 1514e06bb29bSthorpej int part, unit, omask, size; 15156f9db1eeShpeyerl 1516e06bb29bSthorpej unit = ccdunit(dev); 1517ef781023Schristos if ((cs = ccdget(unit, 0)) == NULL) 1518b08b65c6Schristos return -1; 151977d85de2Sthorpej 152077d85de2Sthorpej if ((cs->sc_flags & CCDF_INITED) == 0) 152177d85de2Sthorpej return (-1); 152277d85de2Sthorpej 1523e06bb29bSthorpej part = DISKPART(dev); 1524e06bb29bSthorpej omask = cs->sc_dkdev.dk_openmask & (1 << part); 1525e06bb29bSthorpej lp = cs->sc_dkdev.dk_label; 1526e06bb29bSthorpej 152795e1ffb1Schristos if (omask == 0 && ccdopen(dev, 0, S_IFBLK, curlwp)) 1528e06bb29bSthorpej return (-1); 1529e06bb29bSthorpej 1530e06bb29bSthorpej if (lp->d_partitions[part].p_fstype != FS_SWAP) 153177d85de2Sthorpej size = -1; 153277d85de2Sthorpej else 1533e06bb29bSthorpej size = lp->d_partitions[part].p_size * 1534e06bb29bSthorpej (lp->d_secsize / DEV_BSIZE); 153577d85de2Sthorpej 153695e1ffb1Schristos if (omask == 0 && ccdclose(dev, 0, S_IFBLK, curlwp)) 153777d85de2Sthorpej return (-1); 153877d85de2Sthorpej 153977d85de2Sthorpej return (size); 15406f9db1eeShpeyerl } 15416f9db1eeShpeyerl 154277d85de2Sthorpej static void 1543b8d38500Sthorpej ccdgetdefaultlabel(struct ccd_softc *cs, struct disklabel *lp) 154477d85de2Sthorpej { 154577d85de2Sthorpej struct ccdgeom *ccg = &cs->sc_geom; 154677d85de2Sthorpej 1547c8b4ac1bSthorpej memset(lp, 0, sizeof(*lp)); 154877d85de2Sthorpej 154953385cb9Smlelstv if (cs->sc_size > UINT32_MAX) 155053385cb9Smlelstv lp->d_secperunit = UINT32_MAX; 155153385cb9Smlelstv else 155277d85de2Sthorpej lp->d_secperunit = cs->sc_size; 155377d85de2Sthorpej lp->d_secsize = ccg->ccg_secsize; 155477d85de2Sthorpej lp->d_nsectors = ccg->ccg_nsectors; 155577d85de2Sthorpej lp->d_ntracks = ccg->ccg_ntracks; 155677d85de2Sthorpej lp->d_ncylinders = ccg->ccg_ncylinders; 155789d4987eSthorpej lp->d_secpercyl = lp->d_ntracks * lp->d_nsectors; 155877d85de2Sthorpej 155977d85de2Sthorpej strncpy(lp->d_typename, "ccd", sizeof(lp->d_typename)); 1560c182898bSchristos lp->d_type = DKTYPE_CCD; 156177d85de2Sthorpej strncpy(lp->d_packname, "fictitious", sizeof(lp->d_packname)); 156277d85de2Sthorpej lp->d_rpm = 3600; 156377d85de2Sthorpej lp->d_interleave = 1; 156477d85de2Sthorpej lp->d_flags = 0; 156577d85de2Sthorpej 156677d85de2Sthorpej lp->d_partitions[RAW_PART].p_offset = 0; 156753385cb9Smlelstv lp->d_partitions[RAW_PART].p_size = lp->d_secperunit; 156877d85de2Sthorpej lp->d_partitions[RAW_PART].p_fstype = FS_UNUSED; 156977d85de2Sthorpej lp->d_npartitions = RAW_PART + 1; 157077d85de2Sthorpej 157177d85de2Sthorpej lp->d_magic = DISKMAGIC; 157277d85de2Sthorpej lp->d_magic2 = DISKMAGIC; 15735b39541eSthorpej lp->d_checksum = dkcksum(cs->sc_dkdev.dk_label); 1574939e074dSthorpej } 1575939e074dSthorpej 1576939e074dSthorpej /* 1577939e074dSthorpej * Read the disklabel from the ccd. If one is not present, fake one 1578939e074dSthorpej * up. 1579939e074dSthorpej */ 1580939e074dSthorpej static void 1581b8d38500Sthorpej ccdgetdisklabel(dev_t dev) 1582939e074dSthorpej { 1583939e074dSthorpej int unit = ccdunit(dev); 1584b08b65c6Schristos struct ccd_softc *cs; 1585d91455ceSdsl const char *errstring; 1586b08b65c6Schristos struct disklabel *lp; 1587b08b65c6Schristos struct cpu_disklabel *clp; 1588939e074dSthorpej 1589ef781023Schristos if ((cs = ccdget(unit, 0)) == NULL) 1590b08b65c6Schristos return; 1591b08b65c6Schristos lp = cs->sc_dkdev.dk_label; 1592b08b65c6Schristos clp = cs->sc_dkdev.dk_cpulabel; 1593481ff5acSad KASSERT(mutex_owned(&cs->sc_dvlock)); 1594481ff5acSad 1595c8b4ac1bSthorpej memset(clp, 0, sizeof(*clp)); 1596939e074dSthorpej 1597939e074dSthorpej ccdgetdefaultlabel(cs, lp); 159877d85de2Sthorpej 159977d85de2Sthorpej /* 160077d85de2Sthorpej * Call the generic disklabel extraction routine. 160177d85de2Sthorpej */ 1602481ff5acSad cs->sc_flags |= CCDF_RLABEL; 16030b441623Slukem if ((cs->sc_flags & CCDF_NOLABEL) != 0) 16040b441623Slukem errstring = "CCDF_NOLABEL set; ignoring on-disk label"; 16050b441623Slukem else 1606d318abdbSchristos errstring = readdisklabel(CCDLABELDEV(dev), ccdstrategy, 1607d318abdbSchristos cs->sc_dkdev.dk_label, cs->sc_dkdev.dk_cpulabel); 1608d318abdbSchristos if (errstring) 160977d85de2Sthorpej ccdmakedisklabel(cs); 1610512a0186Senami else { 1611512a0186Senami int i; 1612512a0186Senami struct partition *pp; 1613512a0186Senami 1614512a0186Senami /* 1615512a0186Senami * Sanity check whether the found disklabel is valid. 1616512a0186Senami * 1617512a0186Senami * This is necessary since total size of ccd may vary 1618512a0186Senami * when an interleave is changed even though exactly 1619*d0b020c0Sandvar * same components are used, and old disklabel may used 1620512a0186Senami * if that is found. 1621512a0186Senami */ 162249e3024aSmlelstv if (lp->d_secperunit < UINT32_MAX ? 162349e3024aSmlelstv lp->d_secperunit != cs->sc_size : 162449e3024aSmlelstv lp->d_secperunit > cs->sc_size) 1625512a0186Senami printf("WARNING: %s: " 16267d706cf7Ssborrill "total sector size in disklabel (%ju) != " 16277d706cf7Ssborrill "the size of ccd (%ju)\n", cs->sc_xname, 16287d706cf7Ssborrill (uintmax_t)lp->d_secperunit, 16297d706cf7Ssborrill (uintmax_t)cs->sc_size); 1630512a0186Senami for (i = 0; i < lp->d_npartitions; i++) { 1631512a0186Senami pp = &lp->d_partitions[i]; 1632512a0186Senami if (pp->p_offset + pp->p_size > cs->sc_size) 16339b770726Senami printf("WARNING: %s: end of partition `%c' " 16347d706cf7Ssborrill "exceeds the size of ccd (%ju)\n", 16357d706cf7Ssborrill cs->sc_xname, 'a' + i, (uintmax_t)cs->sc_size); 1636512a0186Senami } 1637512a0186Senami } 163877d85de2Sthorpej 163977d85de2Sthorpej #ifdef DEBUG 164077d85de2Sthorpej /* It's actually extremely common to have unlabeled ccds. */ 164177d85de2Sthorpej if (ccddebug & CCDB_LABEL) 164277d85de2Sthorpej if (errstring != NULL) 164386373f8cSchristos printf("%s: %s\n", cs->sc_xname, errstring); 164477d85de2Sthorpej #endif 16454aef0509Sthorpej 16464aef0509Sthorpej /* In-core label now valid. */ 1647481ff5acSad cs->sc_flags = (cs->sc_flags | CCDF_VLABEL) & ~CCDF_RLABEL; 164877d85de2Sthorpej } 164977d85de2Sthorpej 165077d85de2Sthorpej /* 165177d85de2Sthorpej * Take care of things one might want to take care of in the event 165277d85de2Sthorpej * that a disklabel isn't present. 165377d85de2Sthorpej */ 165477d85de2Sthorpej static void 1655b8d38500Sthorpej ccdmakedisklabel(struct ccd_softc *cs) 165677d85de2Sthorpej { 16575b39541eSthorpej struct disklabel *lp = cs->sc_dkdev.dk_label; 165877d85de2Sthorpej 165977d85de2Sthorpej /* 166077d85de2Sthorpej * For historical reasons, if there's no disklabel present 166177d85de2Sthorpej * the raw partition must be marked FS_BSDFFS. 166277d85de2Sthorpej */ 166377d85de2Sthorpej lp->d_partitions[RAW_PART].p_fstype = FS_BSDFFS; 166477d85de2Sthorpej 166577d85de2Sthorpej strncpy(lp->d_packname, "default label", sizeof(lp->d_packname)); 1666939e074dSthorpej 1667939e074dSthorpej lp->d_checksum = dkcksum(lp); 166877d85de2Sthorpej } 166977d85de2Sthorpej 167077d85de2Sthorpej #ifdef DEBUG 167177d85de2Sthorpej static void 1672b8d38500Sthorpej printiinfo(struct ccdiinfo *ii) 167377d85de2Sthorpej { 1674169ac5b3Saugustss int ix, i; 167577d85de2Sthorpej 167677d85de2Sthorpej for (ix = 0; ii->ii_ndisk; ix++, ii++) { 16774e0e5333Skleink printf(" itab[%d]: #dk %d sblk %" PRId64 " soff %" PRId64, 167877d85de2Sthorpej ix, ii->ii_ndisk, ii->ii_startblk, ii->ii_startoff); 167977d85de2Sthorpej for (i = 0; i < ii->ii_ndisk; i++) 168086373f8cSchristos printf(" %d", ii->ii_index[i]); 168186373f8cSchristos printf("\n"); 168277d85de2Sthorpej } 168377d85de2Sthorpej } 16846f9db1eeShpeyerl #endif 16857147ed32Shaad 16869d1eb470Spgoyette MODULE(MODULE_CLASS_DRIVER, ccd, "dk_subr,bufq_fcfs"); 16877147ed32Shaad 16887147ed32Shaad static int 16897147ed32Shaad ccd_modcmd(modcmd_t cmd, void *arg) 16907147ed32Shaad { 1691cd2a35a0Smartin int error = 0; 1692cd2a35a0Smartin #ifdef _MODULE 1693cd2a35a0Smartin int bmajor = -1, cmajor = -1; 1694cd2a35a0Smartin #endif 1695b4a3a8f3Sjruoho 16967147ed32Shaad 16977147ed32Shaad switch (cmd) { 16987147ed32Shaad case MODULE_CMD_INIT: 1699b4a3a8f3Sjruoho #ifdef _MODULE 1700ef781023Schristos ccdattach(0); 17017147ed32Shaad 1702ef781023Schristos error = devsw_attach("ccd", &ccd_bdevsw, &bmajor, 17037147ed32Shaad &ccd_cdevsw, &cmajor); 1704b4a3a8f3Sjruoho #endif 17057147ed32Shaad break; 17067147ed32Shaad 17077147ed32Shaad case MODULE_CMD_FINI: 1708b4a3a8f3Sjruoho #ifdef _MODULE 1709ef781023Schristos mutex_enter(&ccd_lock); 1710ee984189Spgoyette if (!LIST_EMPTY(&ccds)) { 1711ef781023Schristos mutex_exit(&ccd_lock); 171237efed97Schristos error = EBUSY; 171337efed97Schristos } else { 171437efed97Schristos mutex_exit(&ccd_lock); 1715e7bed289Sriastradh devsw_detach(&ccd_bdevsw, &ccd_cdevsw); 171637efed97Schristos ccddetach(); 171737efed97Schristos } 1718b4a3a8f3Sjruoho #endif 17197147ed32Shaad break; 17207147ed32Shaad 17217147ed32Shaad case MODULE_CMD_STAT: 17227147ed32Shaad return ENOTTY; 17237147ed32Shaad 17247147ed32Shaad default: 17257147ed32Shaad return ENOTTY; 17267147ed32Shaad } 17277147ed32Shaad 17287147ed32Shaad return error; 17297147ed32Shaad } 1730b08b65c6Schristos 1731b08b65c6Schristos static int 1732b08b65c6Schristos ccd_units_sysctl(SYSCTLFN_ARGS) 1733b08b65c6Schristos { 1734b08b65c6Schristos struct sysctlnode node; 1735b08b65c6Schristos struct ccd_softc *sc; 1736b08b65c6Schristos int error, i, nccd, *units; 1737b08b65c6Schristos size_t size; 1738b08b65c6Schristos 1739b08b65c6Schristos nccd = 0; 1740b08b65c6Schristos mutex_enter(&ccd_lock); 1741b08b65c6Schristos LIST_FOREACH(sc, &ccds, sc_link) 1742b08b65c6Schristos nccd++; 1743b08b65c6Schristos mutex_exit(&ccd_lock); 1744b08b65c6Schristos 1745b08b65c6Schristos if (nccd != 0) { 1746b08b65c6Schristos size = nccd * sizeof(*units); 1747b08b65c6Schristos units = kmem_zalloc(size, KM_SLEEP); 1748b08b65c6Schristos i = 0; 1749b08b65c6Schristos mutex_enter(&ccd_lock); 1750b08b65c6Schristos LIST_FOREACH(sc, &ccds, sc_link) { 1751b08b65c6Schristos if (i >= nccd) 1752b08b65c6Schristos break; 1753b08b65c6Schristos units[i] = sc->sc_unit; 1754b08b65c6Schristos } 1755b08b65c6Schristos mutex_exit(&ccd_lock); 1756b08b65c6Schristos } else { 1757b08b65c6Schristos units = NULL; 1758b08b65c6Schristos size = 0; 1759b08b65c6Schristos } 1760b08b65c6Schristos 1761b08b65c6Schristos node = *rnode; 1762b08b65c6Schristos node.sysctl_data = units; 1763b08b65c6Schristos node.sysctl_size = size; 1764b08b65c6Schristos 1765b08b65c6Schristos error = sysctl_lookup(SYSCTLFN_CALL(&node)); 1766b08b65c6Schristos if (units) 1767b08b65c6Schristos kmem_free(units, size); 1768b08b65c6Schristos return error; 1769b08b65c6Schristos } 1770b08b65c6Schristos 1771b08b65c6Schristos static int 1772b08b65c6Schristos ccd_info_sysctl(SYSCTLFN_ARGS) 1773b08b65c6Schristos { 1774b08b65c6Schristos struct sysctlnode node; 1775b08b65c6Schristos struct ccddiskinfo ccd; 1776b08b65c6Schristos struct ccd_softc *sc; 1777c1fde472Spgoyette int unit, error; 1778b08b65c6Schristos 1779b08b65c6Schristos if (newp == NULL || newlen != sizeof(int)) 1780b08b65c6Schristos return EINVAL; 1781b08b65c6Schristos 1782c1fde472Spgoyette error = sysctl_copyin(l, newp, &unit, sizeof unit); 1783c1fde472Spgoyette if (error) 1784c1fde472Spgoyette return error; 1785b08b65c6Schristos newlen = 0; 1786b08b65c6Schristos ccd.ccd_ndisks = ~0; 1787b08b65c6Schristos mutex_enter(&ccd_lock); 1788b08b65c6Schristos LIST_FOREACH(sc, &ccds, sc_link) { 1789b08b65c6Schristos if (sc->sc_unit == unit) { 1790b08b65c6Schristos ccd.ccd_ileave = sc->sc_ileave; 1791b08b65c6Schristos ccd.ccd_size = sc->sc_size; 1792b08b65c6Schristos ccd.ccd_ndisks = sc->sc_nccdisks; 1793b08b65c6Schristos ccd.ccd_flags = sc->sc_flags; 1794b08b65c6Schristos break; 1795b08b65c6Schristos } 1796b08b65c6Schristos } 1797b08b65c6Schristos mutex_exit(&ccd_lock); 1798b08b65c6Schristos 1799b08b65c6Schristos if (ccd.ccd_ndisks == ~0) 1800b08b65c6Schristos return ENOENT; 1801b08b65c6Schristos 1802b08b65c6Schristos node = *rnode; 1803b08b65c6Schristos node.sysctl_data = &ccd; 1804b08b65c6Schristos node.sysctl_size = sizeof(ccd); 1805b08b65c6Schristos 1806b08b65c6Schristos return sysctl_lookup(SYSCTLFN_CALL(&node)); 1807b08b65c6Schristos } 1808b08b65c6Schristos 1809b08b65c6Schristos static int 1810b08b65c6Schristos ccd_components_sysctl(SYSCTLFN_ARGS) 1811b08b65c6Schristos { 1812b08b65c6Schristos struct sysctlnode node; 1813b08b65c6Schristos int error, unit; 1814b08b65c6Schristos size_t size; 1815b08b65c6Schristos char *names, *p, *ep; 1816b08b65c6Schristos struct ccd_softc *sc; 1817b08b65c6Schristos 1818b08b65c6Schristos if (newp == NULL || newlen != sizeof(int)) 1819b08b65c6Schristos return EINVAL; 1820b08b65c6Schristos 1821b08b65c6Schristos size = 0; 1822c1fde472Spgoyette error = sysctl_copyin(l, newp, &unit, sizeof unit); 1823c1fde472Spgoyette if (error) 1824c1fde472Spgoyette return error; 1825b08b65c6Schristos newlen = 0; 1826b08b65c6Schristos mutex_enter(&ccd_lock); 1827b08b65c6Schristos LIST_FOREACH(sc, &ccds, sc_link) 1828b08b65c6Schristos if (sc->sc_unit == unit) { 1829b08b65c6Schristos for (size_t i = 0; i < sc->sc_nccdisks; i++) 1830b08b65c6Schristos size += strlen(sc->sc_cinfo[i].ci_path) + 1; 1831b08b65c6Schristos break; 1832b08b65c6Schristos } 1833b08b65c6Schristos mutex_exit(&ccd_lock); 1834b08b65c6Schristos 1835b08b65c6Schristos if (size == 0) 1836b08b65c6Schristos return ENOENT; 1837b08b65c6Schristos names = kmem_zalloc(size, KM_SLEEP); 1838b08b65c6Schristos p = names; 1839b08b65c6Schristos ep = names + size; 1840b08b65c6Schristos mutex_enter(&ccd_lock); 1841b08b65c6Schristos LIST_FOREACH(sc, &ccds, sc_link) 1842b08b65c6Schristos if (sc->sc_unit == unit) { 1843b08b65c6Schristos for (size_t i = 0; i < sc->sc_nccdisks; i++) { 1844b08b65c6Schristos char *d = sc->sc_cinfo[i].ci_path; 1845b08b65c6Schristos while (p < ep && (*p++ = *d++) != '\0') 1846b08b65c6Schristos continue; 1847b08b65c6Schristos } 1848b08b65c6Schristos break; 1849b08b65c6Schristos } 1850b08b65c6Schristos mutex_exit(&ccd_lock); 1851b08b65c6Schristos 1852b08b65c6Schristos node = *rnode; 1853b08b65c6Schristos node.sysctl_data = names; 1854b08b65c6Schristos node.sysctl_size = ep - names; 1855b08b65c6Schristos 1856b08b65c6Schristos error = sysctl_lookup(SYSCTLFN_CALL(&node)); 1857b08b65c6Schristos kmem_free(names, size); 1858b08b65c6Schristos return error; 1859b08b65c6Schristos } 1860b08b65c6Schristos 1861b08b65c6Schristos SYSCTL_SETUP(sysctl_kern_ccd_setup, "sysctl kern.ccd subtree setup") 1862b08b65c6Schristos { 1863b08b65c6Schristos const struct sysctlnode *node = NULL; 1864b08b65c6Schristos 1865b08b65c6Schristos sysctl_createv(clog, 0, NULL, &node, 1866b08b65c6Schristos CTLFLAG_PERMANENT, 1867b08b65c6Schristos CTLTYPE_NODE, "ccd", 1868b08b65c6Schristos SYSCTL_DESCR("ConCatenated Disk state"), 1869b08b65c6Schristos NULL, 0, NULL, 0, 1870b08b65c6Schristos CTL_KERN, CTL_CREATE, CTL_EOL); 1871b08b65c6Schristos 1872b08b65c6Schristos if (node == NULL) 1873b08b65c6Schristos return; 1874b08b65c6Schristos 1875b08b65c6Schristos sysctl_createv(clog, 0, &node, NULL, 1876b08b65c6Schristos CTLFLAG_PERMANENT | CTLFLAG_READONLY, 1877b08b65c6Schristos CTLTYPE_STRUCT, "units", 1878b08b65c6Schristos SYSCTL_DESCR("List of ccd unit numbers"), 1879b08b65c6Schristos ccd_units_sysctl, 0, NULL, 0, 1880b08b65c6Schristos CTL_CREATE, CTL_EOL); 1881b08b65c6Schristos sysctl_createv(clog, 0, &node, NULL, 1882b08b65c6Schristos CTLFLAG_PERMANENT | CTLFLAG_READWRITE, 1883b08b65c6Schristos CTLTYPE_STRUCT, "info", 1884b08b65c6Schristos SYSCTL_DESCR("Information about a CCD unit"), 1885b08b65c6Schristos ccd_info_sysctl, 0, NULL, 0, 1886b08b65c6Schristos CTL_CREATE, CTL_EOL); 1887b08b65c6Schristos sysctl_createv(clog, 0, &node, NULL, 1888b08b65c6Schristos CTLFLAG_PERMANENT | CTLFLAG_READWRITE, 1889b08b65c6Schristos CTLTYPE_STRUCT, "components", 1890b08b65c6Schristos SYSCTL_DESCR("Information about CCD components"), 1891b08b65c6Schristos ccd_components_sysctl, 0, NULL, 0, 1892b08b65c6Schristos CTL_CREATE, CTL_EOL); 1893b08b65c6Schristos } 1894