1*49e3024aSmlelstv /* $NetBSD: ccd.c,v 1.154 2014/10/11 12:36:25 mlelstv 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 * 65aad01611Sagc * 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*49e3024aSmlelstv __KERNEL_RCSID(0, "$NetBSD: ccd.c,v 1.154 2014/10/11 12:36:25 mlelstv Exp $"); 927d706cf7Ssborrill 937d706cf7Ssborrill #if defined(_KERNEL_OPT) 947d706cf7Ssborrill #include "opt_compat_netbsd.h" 957d706cf7Ssborrill #endif 962bbe2de6Slukem 976f9db1eeShpeyerl #include <sys/param.h> 986f9db1eeShpeyerl #include <sys/systm.h> 99481ff5acSad #include <sys/kernel.h> 1003214723fShpeyerl #include <sys/proc.h> 1016f9db1eeShpeyerl #include <sys/errno.h> 1026f9db1eeShpeyerl #include <sys/buf.h> 103481ff5acSad #include <sys/kmem.h> 104dd5ae858Sthorpej #include <sys/pool.h> 105b4a3a8f3Sjruoho #include <sys/module.h> 10677d85de2Sthorpej #include <sys/namei.h> 1073214723fShpeyerl #include <sys/stat.h> 1083214723fShpeyerl #include <sys/ioctl.h> 1093214723fShpeyerl #include <sys/disklabel.h> 11077d85de2Sthorpej #include <sys/device.h> 11177d85de2Sthorpej #include <sys/disk.h> 11277d85de2Sthorpej #include <sys/syslog.h> 1133214723fShpeyerl #include <sys/fcntl.h> 11477d85de2Sthorpej #include <sys/vnode.h> 115e1930da2Schristos #include <sys/conf.h> 1169abeea58Sad #include <sys/mutex.h> 117d272bb00Sthorpej #include <sys/queue.h> 1182867b68bSelad #include <sys/kauth.h> 119481ff5acSad #include <sys/kthread.h> 120481ff5acSad #include <sys/bufq.h> 121b08b65c6Schristos #include <sys/sysctl.h> 1226f9db1eeShpeyerl 12319b39d64Suebayasi #include <uvm/uvm_extern.h> 12419b39d64Suebayasi 1256f9db1eeShpeyerl #include <dev/ccdvar.h> 1261a789da3Schristos #include <dev/dkvar.h> 1276f9db1eeShpeyerl 128292a0c7fShannken #include <miscfs/specfs/specdev.h> /* for v_rdev */ 129292a0c7fShannken 13077d85de2Sthorpej #if defined(CCDDEBUG) && !defined(DEBUG) 13177d85de2Sthorpej #define DEBUG 13277d85de2Sthorpej #endif 13377d85de2Sthorpej 1346f9db1eeShpeyerl #ifdef DEBUG 1353214723fShpeyerl #define CCDB_FOLLOW 0x01 1363214723fShpeyerl #define CCDB_INIT 0x02 1373214723fShpeyerl #define CCDB_IO 0x04 13877d85de2Sthorpej #define CCDB_LABEL 0x08 13977d85de2Sthorpej #define CCDB_VNODE 0x10 140732dd94aSthorpej int ccddebug = 0x00; 1416f9db1eeShpeyerl #endif 1426f9db1eeShpeyerl 143f694af67Scgd #define ccdunit(x) DISKUNIT(x) 1446f9db1eeShpeyerl 145f694af67Scgd struct ccdbuf { 146f694af67Scgd struct buf cb_buf; /* new I/O buf */ 147f694af67Scgd struct buf *cb_obp; /* ptr. to original I/O buf */ 148c6aa25bfSthorpej struct ccd_softc *cb_sc; /* pointer to ccd softc */ 149f694af67Scgd int cb_comp; /* target component */ 150d272bb00Sthorpej SIMPLEQ_ENTRY(ccdbuf) cb_q; /* fifo of component buffers */ 151f694af67Scgd }; 1526f9db1eeShpeyerl 153dd5ae858Sthorpej /* component buffer pool */ 154481ff5acSad static pool_cache_t ccd_cache; 155dd5ae858Sthorpej 156481ff5acSad #define CCD_GETBUF() pool_cache_get(ccd_cache, PR_WAITOK) 157481ff5acSad #define CCD_PUTBUF(cbp) pool_cache_put(ccd_cache, cbp) 1586f9db1eeShpeyerl 15977d85de2Sthorpej #define CCDLABELDEV(dev) \ 16077d85de2Sthorpej (MAKEDISKDEV(major((dev)), ccdunit((dev)), RAW_PART)) 1616f9db1eeShpeyerl 16277d85de2Sthorpej /* called by main() at boot time */ 163b8d38500Sthorpej void ccdattach(int); 16477d85de2Sthorpej 16577d85de2Sthorpej /* called by biodone() at interrupt time */ 166b8d38500Sthorpej static void ccdiodone(struct buf *); 167f694af67Scgd 168b8d38500Sthorpej static void ccdinterleave(struct ccd_softc *); 169b8d38500Sthorpej static int ccdinit(struct ccd_softc *, char **, struct vnode **, 17095e1ffb1Schristos struct lwp *); 171b8d38500Sthorpej static struct ccdbuf *ccdbuffer(struct ccd_softc *, struct buf *, 17253524e44Schristos daddr_t, void *, long); 173b8d38500Sthorpej static void ccdgetdefaultlabel(struct ccd_softc *, struct disklabel *); 174b8d38500Sthorpej static void ccdgetdisklabel(dev_t); 175b8d38500Sthorpej static void ccdmakedisklabel(struct ccd_softc *); 176481ff5acSad static void ccdstart(struct ccd_softc *); 177481ff5acSad static void ccdthread(void *); 1783214723fShpeyerl 179b8d38500Sthorpej static dev_type_open(ccdopen); 180b8d38500Sthorpej static dev_type_close(ccdclose); 181b8d38500Sthorpej static dev_type_read(ccdread); 182b8d38500Sthorpej static dev_type_write(ccdwrite); 183b8d38500Sthorpej static dev_type_ioctl(ccdioctl); 184b8d38500Sthorpej static dev_type_strategy(ccdstrategy); 185b8d38500Sthorpej static dev_type_size(ccdsize); 18677a6b82bSgehenna 18777a6b82bSgehenna const struct bdevsw ccd_bdevsw = { 188481ff5acSad .d_open = ccdopen, 189481ff5acSad .d_close = ccdclose, 190481ff5acSad .d_strategy = ccdstrategy, 191481ff5acSad .d_ioctl = ccdioctl, 192481ff5acSad .d_dump = nodump, 193481ff5acSad .d_psize = ccdsize, 1948c70ef39Sdholland .d_discard = nodiscard, 195481ff5acSad .d_flag = D_DISK | D_MPSAFE 19677a6b82bSgehenna }; 19777a6b82bSgehenna 19877a6b82bSgehenna const struct cdevsw ccd_cdevsw = { 199481ff5acSad .d_open = ccdopen, 200481ff5acSad .d_close = ccdclose, 201481ff5acSad .d_read = ccdread, 202481ff5acSad .d_write = ccdwrite, 203481ff5acSad .d_ioctl = ccdioctl, 204481ff5acSad .d_stop = nostop, 205481ff5acSad .d_tty = notty, 206481ff5acSad .d_poll = nopoll, 207481ff5acSad .d_mmap = nommap, 208481ff5acSad .d_kqfilter = nokqfilter, 209f9228f42Sdholland .d_discard = nodiscard, 210481ff5acSad .d_flag = D_DISK | D_MPSAFE 21177a6b82bSgehenna }; 21277a6b82bSgehenna 21377d85de2Sthorpej #ifdef DEBUG 214b8d38500Sthorpej static void printiinfo(struct ccdiinfo *); 21577d85de2Sthorpej #endif 21677d85de2Sthorpej 217b08b65c6Schristos static LIST_HEAD(, ccd_softc) ccds = LIST_HEAD_INITIALIZER(ccds); 218b08b65c6Schristos static kmutex_t ccd_lock; 219b08b65c6Schristos 220b08b65c6Schristos static struct ccd_softc * 221b08b65c6Schristos ccdcreate(int unit) { 222b08b65c6Schristos struct ccd_softc *sc = kmem_zalloc(sizeof(*sc), KM_SLEEP); 223b08b65c6Schristos if (sc == NULL) { 224b08b65c6Schristos #ifdef DIAGNOSTIC 225b08b65c6Schristos printf("%s: out of memory\n", __func__); 226b08b65c6Schristos #endif 227b08b65c6Schristos return NULL; 228b08b65c6Schristos } 229b08b65c6Schristos /* Initialize per-softc structures. */ 230b08b65c6Schristos snprintf(sc->sc_xname, sizeof(sc->sc_xname), "ccd%d", unit); 231b08b65c6Schristos mutex_init(&sc->sc_dvlock, MUTEX_DEFAULT, IPL_NONE); 232b08b65c6Schristos sc->sc_iolock = mutex_obj_alloc(MUTEX_DEFAULT, IPL_NONE); 233b08b65c6Schristos cv_init(&sc->sc_stop, "ccdstop"); 234b08b65c6Schristos cv_init(&sc->sc_push, "ccdthr"); 235b08b65c6Schristos disk_init(&sc->sc_dkdev, sc->sc_xname, NULL); /* XXX */ 236b08b65c6Schristos return sc; 237b08b65c6Schristos } 238b08b65c6Schristos 239b08b65c6Schristos static void 240b08b65c6Schristos ccddestroy(struct ccd_softc *sc) { 241b08b65c6Schristos mutex_obj_free(sc->sc_iolock); 24255252202Sjoerg mutex_exit(&sc->sc_dvlock); 243b08b65c6Schristos mutex_destroy(&sc->sc_dvlock); 244b08b65c6Schristos cv_destroy(&sc->sc_stop); 245b08b65c6Schristos cv_destroy(&sc->sc_push); 246b08b65c6Schristos disk_destroy(&sc->sc_dkdev); 247b08b65c6Schristos kmem_free(sc, sizeof(*sc)); 248b08b65c6Schristos } 249b08b65c6Schristos 250b08b65c6Schristos static struct ccd_softc * 251b08b65c6Schristos ccdget(int unit) { 252b08b65c6Schristos struct ccd_softc *sc; 253b08b65c6Schristos if (unit < 0) { 254b08b65c6Schristos #ifdef DIAGNOSTIC 255b08b65c6Schristos panic("%s: unit %d!", __func__, unit); 256b08b65c6Schristos #endif 257b08b65c6Schristos return NULL; 258b08b65c6Schristos } 259b08b65c6Schristos mutex_enter(&ccd_lock); 260b08b65c6Schristos LIST_FOREACH(sc, &ccds, sc_link) { 261b08b65c6Schristos if (sc->sc_unit == unit) { 262b08b65c6Schristos mutex_exit(&ccd_lock); 263b08b65c6Schristos return sc; 264b08b65c6Schristos } 265b08b65c6Schristos } 266b08b65c6Schristos mutex_exit(&ccd_lock); 267b08b65c6Schristos if ((sc = ccdcreate(unit)) == NULL) 268b08b65c6Schristos return NULL; 269b08b65c6Schristos mutex_enter(&ccd_lock); 270b08b65c6Schristos LIST_INSERT_HEAD(&ccds, sc, sc_link); 271b08b65c6Schristos mutex_exit(&ccd_lock); 272b08b65c6Schristos return sc; 273b08b65c6Schristos } 274b08b65c6Schristos 275b08b65c6Schristos static void 276b08b65c6Schristos ccdput(struct ccd_softc *sc) { 277b08b65c6Schristos mutex_enter(&ccd_lock); 278b08b65c6Schristos LIST_REMOVE(sc, sc_link); 279b08b65c6Schristos mutex_exit(&ccd_lock); 280b08b65c6Schristos ccddestroy(sc); 281b08b65c6Schristos } 2826f9db1eeShpeyerl 2836f9db1eeShpeyerl /* 28477d85de2Sthorpej * Called by main() during pseudo-device attachment. All we need 28577d85de2Sthorpej * to do is allocate enough space for devices to be configured later. 2866f9db1eeShpeyerl */ 2876f9db1eeShpeyerl void 288b8d38500Sthorpej ccdattach(int num) 2896f9db1eeShpeyerl { 290b08b65c6Schristos mutex_init(&ccd_lock, MUTEX_DEFAULT, IPL_NONE); 2910272b2abSthorpej 292dd5ae858Sthorpej /* Initialize the component buffer pool. */ 293481ff5acSad ccd_cache = pool_cache_init(sizeof(struct ccdbuf), 0, 294481ff5acSad 0, 0, "ccdbuf", NULL, IPL_BIO, NULL, NULL, NULL); 2956f9db1eeShpeyerl } 2966f9db1eeShpeyerl 29777d85de2Sthorpej static int 298b8d38500Sthorpej ccdinit(struct ccd_softc *cs, char **cpaths, struct vnode **vpp, 29995e1ffb1Schristos struct lwp *l) 3006f9db1eeShpeyerl { 301169ac5b3Saugustss struct ccdcinfo *ci = NULL; 302169ac5b3Saugustss int ix; 30377d85de2Sthorpej struct ccdgeom *ccg = &cs->sc_geom; 30492d441e0Schristos char *tmppath; 305aa8b5ebdSenami int error, path_alloced; 306e839ec30Schristos uint64_t psize, minsize; 307e839ec30Schristos unsigned secsize, maxsecsize; 3086f9db1eeShpeyerl 3096f9db1eeShpeyerl #ifdef DEBUG 3103214723fShpeyerl if (ccddebug & (CCDB_FOLLOW|CCDB_INIT)) 311c6aa25bfSthorpej printf("%s: ccdinit\n", cs->sc_xname); 3126f9db1eeShpeyerl #endif 31377d85de2Sthorpej 31477d85de2Sthorpej /* Allocate space for the component info. */ 315481ff5acSad cs->sc_cinfo = kmem_alloc(cs->sc_nccdisks * sizeof(*cs->sc_cinfo), 316481ff5acSad KM_SLEEP); 317481ff5acSad tmppath = kmem_alloc(MAXPATHLEN, KM_SLEEP); 31892d441e0Schristos 3190272b2abSthorpej cs->sc_size = 0; 3200272b2abSthorpej 3216f9db1eeShpeyerl /* 3226f9db1eeShpeyerl * Verify that each component piece exists and record 3236f9db1eeShpeyerl * relevant information about it. 3246f9db1eeShpeyerl */ 32577d85de2Sthorpej maxsecsize = 0; 3266f9db1eeShpeyerl minsize = 0; 327aa8b5ebdSenami for (ix = 0, path_alloced = 0; ix < cs->sc_nccdisks; ix++) { 3286f9db1eeShpeyerl ci = &cs->sc_cinfo[ix]; 3290272b2abSthorpej ci->ci_vp = vpp[ix]; 330a44a2765Scgd 33177d85de2Sthorpej /* 33277d85de2Sthorpej * Copy in the pathname of the component. 33377d85de2Sthorpej */ 33425862469Sjoerg memset(tmppath, 0, MAXPATHLEN); /* sanity */ 335d318abdbSchristos error = copyinstr(cpaths[ix], tmppath, 336d318abdbSchristos MAXPATHLEN, &ci->ci_pathlen); 337481ff5acSad if (ci->ci_pathlen == 0) 338481ff5acSad error = EINVAL; 339d318abdbSchristos if (error) { 34077d85de2Sthorpej #ifdef DEBUG 34177d85de2Sthorpej if (ccddebug & (CCDB_FOLLOW|CCDB_INIT)) 34286373f8cSchristos printf("%s: can't copy path, error = %d\n", 3435b39541eSthorpej cs->sc_xname, error); 34477d85de2Sthorpej #endif 345aa8b5ebdSenami goto out; 34677d85de2Sthorpej } 347481ff5acSad ci->ci_path = kmem_alloc(ci->ci_pathlen, KM_SLEEP); 348c8b4ac1bSthorpej memcpy(ci->ci_path, tmppath, ci->ci_pathlen); 349aa8b5ebdSenami path_alloced++; 35077d85de2Sthorpej 35177d85de2Sthorpej /* 35277d85de2Sthorpej * XXX: Cache the component's dev_t. 35377d85de2Sthorpej */ 354292a0c7fShannken ci->ci_dev = vpp[ix]->v_rdev; 35577d85de2Sthorpej 35677d85de2Sthorpej /* 35777d85de2Sthorpej * Get partition information for the component. 35877d85de2Sthorpej */ 359e839ec30Schristos error = getdisksize(vpp[ix], &psize, &secsize); 360d318abdbSchristos if (error) { 36177d85de2Sthorpej #ifdef DEBUG 36277d85de2Sthorpej if (ccddebug & (CCDB_FOLLOW|CCDB_INIT)) 363e839ec30Schristos printf("%s: %s: disksize failed, error = %d\n", 3645b39541eSthorpej cs->sc_xname, ci->ci_path, error); 36577d85de2Sthorpej #endif 366aa8b5ebdSenami goto out; 36777d85de2Sthorpej } 36877d85de2Sthorpej 36977d85de2Sthorpej /* 37077d85de2Sthorpej * Calculate the size, truncating to an interleave 37177d85de2Sthorpej * boundary if necessary. 37277d85de2Sthorpej */ 373e839ec30Schristos maxsecsize = secsize > maxsecsize ? secsize : maxsecsize; 3746f9db1eeShpeyerl if (cs->sc_ileave > 1) 375e839ec30Schristos psize -= psize % cs->sc_ileave; 3763214723fShpeyerl 377e839ec30Schristos if (psize == 0) { 37877d85de2Sthorpej #ifdef DEBUG 37977d85de2Sthorpej if (ccddebug & (CCDB_FOLLOW|CCDB_INIT)) 38086373f8cSchristos printf("%s: %s: size == 0\n", 3815b39541eSthorpej cs->sc_xname, ci->ci_path); 3823214723fShpeyerl #endif 383aa8b5ebdSenami error = ENODEV; 384aa8b5ebdSenami goto out; 38577d85de2Sthorpej } 38677d85de2Sthorpej 387e839ec30Schristos if (minsize == 0 || psize < minsize) 388e839ec30Schristos minsize = psize; 389e839ec30Schristos ci->ci_size = psize; 390e839ec30Schristos cs->sc_size += psize; 3916f9db1eeShpeyerl } 39277d85de2Sthorpej 39377d85de2Sthorpej /* 39477d85de2Sthorpej * Don't allow the interleave to be smaller than 39577d85de2Sthorpej * the biggest component sector. 39677d85de2Sthorpej */ 39777d85de2Sthorpej if ((cs->sc_ileave > 0) && 39877d85de2Sthorpej (cs->sc_ileave < (maxsecsize / DEV_BSIZE))) { 39977d85de2Sthorpej #ifdef DEBUG 40077d85de2Sthorpej if (ccddebug & (CCDB_FOLLOW|CCDB_INIT)) 40186373f8cSchristos printf("%s: interleave must be at least %d\n", 4025b39541eSthorpej cs->sc_xname, (maxsecsize / DEV_BSIZE)); 40377d85de2Sthorpej #endif 404aa8b5ebdSenami error = EINVAL; 405aa8b5ebdSenami goto out; 40677d85de2Sthorpej } 40777d85de2Sthorpej 4086f9db1eeShpeyerl /* 4096f9db1eeShpeyerl * If uniform interleave is desired set all sizes to that of 4106f9db1eeShpeyerl * the smallest component. 4116f9db1eeShpeyerl */ 4120272b2abSthorpej if (cs->sc_flags & CCDF_UNIFORM) { 4136f9db1eeShpeyerl for (ci = cs->sc_cinfo; 4146f9db1eeShpeyerl ci < &cs->sc_cinfo[cs->sc_nccdisks]; ci++) 4156f9db1eeShpeyerl ci->ci_size = minsize; 416732dd94aSthorpej 4176f9db1eeShpeyerl cs->sc_size = cs->sc_nccdisks * minsize; 4186f9db1eeShpeyerl } 41977d85de2Sthorpej 4206f9db1eeShpeyerl /* 42177d85de2Sthorpej * Construct the interleave table. 4226f9db1eeShpeyerl */ 4230272b2abSthorpej ccdinterleave(cs); 42477d85de2Sthorpej 42577d85de2Sthorpej /* 42677d85de2Sthorpej * Create pseudo-geometry based on 1MB cylinders. It's 42777d85de2Sthorpej * pretty close. 42877d85de2Sthorpej */ 42977d85de2Sthorpej ccg->ccg_secsize = DEV_BSIZE; 43089d4987eSthorpej ccg->ccg_ntracks = 1; 43177d85de2Sthorpej ccg->ccg_nsectors = 1024 * (1024 / ccg->ccg_secsize); 43277d85de2Sthorpej ccg->ccg_ncylinders = cs->sc_size / ccg->ccg_nsectors; 43377d85de2Sthorpej 4347d706cf7Ssborrill if (cs->sc_ileave > 0) 4357d706cf7Ssborrill aprint_normal("%s: Interleaving %d component%s " 4367d706cf7Ssborrill "(%d block interleave)\n", cs->sc_xname, 4377d706cf7Ssborrill cs->sc_nccdisks, (cs->sc_nccdisks != 0 ? "s" : ""), 4387d706cf7Ssborrill cs->sc_ileave); 4397d706cf7Ssborrill else 4407d706cf7Ssborrill aprint_normal("%s: Concatenating %d component%s\n", 4417d706cf7Ssborrill cs->sc_xname, 4427d706cf7Ssborrill cs->sc_nccdisks, (cs->sc_nccdisks != 0 ? "s" : "")); 4437d706cf7Ssborrill for (ix = 0; ix < cs->sc_nccdisks; ix++) { 4447d706cf7Ssborrill ci = &cs->sc_cinfo[ix]; 4457d706cf7Ssborrill aprint_normal("%s: %s (%ju blocks)\n", cs->sc_xname, 4467d706cf7Ssborrill ci->ci_path, (uintmax_t)ci->ci_size); 4477d706cf7Ssborrill } 4487d706cf7Ssborrill aprint_normal("%s: total %ju blocks\n", cs->sc_xname, cs->sc_size); 4497d706cf7Ssborrill 450481ff5acSad /* 451481ff5acSad * Create thread to handle deferred I/O. 452481ff5acSad */ 453481ff5acSad cs->sc_zap = false; 454481ff5acSad error = kthread_create(PRI_BIO, KTHREAD_MPSAFE, NULL, ccdthread, 455481ff5acSad cs, &cs->sc_thread, "%s", cs->sc_xname); 456481ff5acSad if (error) { 457481ff5acSad printf("ccdinit: can't create thread: %d\n", error); 458481ff5acSad goto out; 459481ff5acSad } 460481ff5acSad 461481ff5acSad /* 462481ff5acSad * Only now that everything is set up can we enable the device. 463481ff5acSad */ 464481ff5acSad mutex_enter(cs->sc_iolock); 46577d85de2Sthorpej cs->sc_flags |= CCDF_INITED; 466481ff5acSad mutex_exit(cs->sc_iolock); 467481ff5acSad kmem_free(tmppath, MAXPATHLEN); 46877d85de2Sthorpej return (0); 469aa8b5ebdSenami 470aa8b5ebdSenami out: 471481ff5acSad for (ix = 0; ix < path_alloced; ix++) { 472481ff5acSad kmem_free(cs->sc_cinfo[ix].ci_path, 473481ff5acSad cs->sc_cinfo[ix].ci_pathlen); 474481ff5acSad } 475481ff5acSad kmem_free(cs->sc_cinfo, cs->sc_nccdisks * sizeof(struct ccdcinfo)); 476481ff5acSad kmem_free(tmppath, MAXPATHLEN); 477aa8b5ebdSenami return (error); 4786f9db1eeShpeyerl } 4796f9db1eeShpeyerl 48077d85de2Sthorpej static void 481b8d38500Sthorpej ccdinterleave(struct ccd_softc *cs) 4826f9db1eeShpeyerl { 483169ac5b3Saugustss struct ccdcinfo *ci, *smallci; 484169ac5b3Saugustss struct ccdiinfo *ii; 485169ac5b3Saugustss daddr_t bn, lbn; 486169ac5b3Saugustss int ix; 4876f9db1eeShpeyerl u_long size; 4886f9db1eeShpeyerl 4896f9db1eeShpeyerl #ifdef DEBUG 4903214723fShpeyerl if (ccddebug & CCDB_INIT) 49186373f8cSchristos printf("ccdinterleave(%p): ileave %d\n", cs, cs->sc_ileave); 4926f9db1eeShpeyerl #endif 4936f9db1eeShpeyerl /* 4946f9db1eeShpeyerl * Allocate an interleave table. 4956f9db1eeShpeyerl * Chances are this is too big, but we don't care. 4966f9db1eeShpeyerl */ 4976f9db1eeShpeyerl size = (cs->sc_nccdisks + 1) * sizeof(struct ccdiinfo); 498481ff5acSad cs->sc_itable = kmem_zalloc(size, KM_SLEEP); 49977d85de2Sthorpej 5006f9db1eeShpeyerl /* 5016f9db1eeShpeyerl * Trivial case: no interleave (actually interleave of disk size). 50277d85de2Sthorpej * Each table entry represents a single component in its entirety. 5036f9db1eeShpeyerl */ 5046f9db1eeShpeyerl if (cs->sc_ileave == 0) { 5056f9db1eeShpeyerl bn = 0; 5066f9db1eeShpeyerl ii = cs->sc_itable; 50777d85de2Sthorpej 5086f9db1eeShpeyerl for (ix = 0; ix < cs->sc_nccdisks; ix++) { 50989d4987eSthorpej /* Allocate space for ii_index. */ 510481ff5acSad ii->ii_indexsz = sizeof(int); 511481ff5acSad ii->ii_index = kmem_alloc(ii->ii_indexsz, KM_SLEEP); 5126f9db1eeShpeyerl ii->ii_ndisk = 1; 5136f9db1eeShpeyerl ii->ii_startblk = bn; 5146f9db1eeShpeyerl ii->ii_startoff = 0; 5156f9db1eeShpeyerl ii->ii_index[0] = ix; 5166f9db1eeShpeyerl bn += cs->sc_cinfo[ix].ci_size; 5176f9db1eeShpeyerl ii++; 5186f9db1eeShpeyerl } 5196f9db1eeShpeyerl ii->ii_ndisk = 0; 5206f9db1eeShpeyerl #ifdef DEBUG 5213214723fShpeyerl if (ccddebug & CCDB_INIT) 5226f9db1eeShpeyerl printiinfo(cs->sc_itable); 5236f9db1eeShpeyerl #endif 52477d85de2Sthorpej return; 5256f9db1eeShpeyerl } 52677d85de2Sthorpej 5276f9db1eeShpeyerl /* 5286f9db1eeShpeyerl * The following isn't fast or pretty; it doesn't have to be. 5296f9db1eeShpeyerl */ 5306f9db1eeShpeyerl size = 0; 5316f9db1eeShpeyerl bn = lbn = 0; 5326f9db1eeShpeyerl for (ii = cs->sc_itable; ; ii++) { 53377d85de2Sthorpej /* Allocate space for ii_index. */ 534481ff5acSad ii->ii_indexsz = sizeof(int) * cs->sc_nccdisks; 535481ff5acSad ii->ii_index = kmem_alloc(ii->ii_indexsz, KM_SLEEP); 53677d85de2Sthorpej 5376f9db1eeShpeyerl /* 5386f9db1eeShpeyerl * Locate the smallest of the remaining components 5396f9db1eeShpeyerl */ 5406f9db1eeShpeyerl smallci = NULL; 5416f9db1eeShpeyerl for (ci = cs->sc_cinfo; 5426f9db1eeShpeyerl ci < &cs->sc_cinfo[cs->sc_nccdisks]; ci++) 5436f9db1eeShpeyerl if (ci->ci_size > size && 5446f9db1eeShpeyerl (smallci == NULL || 5456f9db1eeShpeyerl ci->ci_size < smallci->ci_size)) 5466f9db1eeShpeyerl smallci = ci; 54777d85de2Sthorpej 5486f9db1eeShpeyerl /* 5496f9db1eeShpeyerl * Nobody left, all done 5506f9db1eeShpeyerl */ 5516f9db1eeShpeyerl if (smallci == NULL) { 5526f9db1eeShpeyerl ii->ii_ndisk = 0; 5536f9db1eeShpeyerl break; 5546f9db1eeShpeyerl } 55577d85de2Sthorpej 5566f9db1eeShpeyerl /* 5576f9db1eeShpeyerl * Record starting logical block and component offset 5586f9db1eeShpeyerl */ 5596f9db1eeShpeyerl ii->ii_startblk = bn / cs->sc_ileave; 5606f9db1eeShpeyerl ii->ii_startoff = lbn; 56177d85de2Sthorpej 5626f9db1eeShpeyerl /* 5636f9db1eeShpeyerl * Determine how many disks take part in this interleave 5646f9db1eeShpeyerl * and record their indices. 5656f9db1eeShpeyerl */ 5666f9db1eeShpeyerl ix = 0; 5676f9db1eeShpeyerl for (ci = cs->sc_cinfo; 5686f9db1eeShpeyerl ci < &cs->sc_cinfo[cs->sc_nccdisks]; ci++) 5696f9db1eeShpeyerl if (ci->ci_size >= smallci->ci_size) 5706f9db1eeShpeyerl ii->ii_index[ix++] = ci - cs->sc_cinfo; 5716f9db1eeShpeyerl ii->ii_ndisk = ix; 5726f9db1eeShpeyerl bn += ix * (smallci->ci_size - size); 5736f9db1eeShpeyerl lbn = smallci->ci_size / cs->sc_ileave; 5746f9db1eeShpeyerl size = smallci->ci_size; 5756f9db1eeShpeyerl } 5766f9db1eeShpeyerl #ifdef DEBUG 5773214723fShpeyerl if (ccddebug & CCDB_INIT) 5786f9db1eeShpeyerl printiinfo(cs->sc_itable); 5796f9db1eeShpeyerl #endif 5806f9db1eeShpeyerl } 5816f9db1eeShpeyerl 58277d85de2Sthorpej /* ARGSUSED */ 583b8d38500Sthorpej static int 584168cd830Schristos ccdopen(dev_t dev, int flags, int fmt, struct lwp *l) 5856f9db1eeShpeyerl { 5866f9db1eeShpeyerl int unit = ccdunit(dev); 58777d85de2Sthorpej struct ccd_softc *cs; 58877d85de2Sthorpej struct disklabel *lp; 589b8dcfbd3Sthorpej int error = 0, part, pmask; 5906f9db1eeShpeyerl 5916f9db1eeShpeyerl #ifdef DEBUG 5923214723fShpeyerl if (ccddebug & CCDB_FOLLOW) 59308ebead9Scegger printf("ccdopen(0x%"PRIx64", 0x%x)\n", dev, flags); 5946f9db1eeShpeyerl #endif 595b08b65c6Schristos if ((cs = ccdget(unit)) == NULL) 596b08b65c6Schristos return ENXIO; 59777d85de2Sthorpej 598481ff5acSad mutex_enter(&cs->sc_dvlock); 599b8dcfbd3Sthorpej 6005b39541eSthorpej lp = cs->sc_dkdev.dk_label; 60177d85de2Sthorpej 60277d85de2Sthorpej part = DISKPART(dev); 60377d85de2Sthorpej pmask = (1 << part); 60477d85de2Sthorpej 605b8dcfbd3Sthorpej /* 606b8dcfbd3Sthorpej * If we're initialized, check to see if there are any other 607b8dcfbd3Sthorpej * open partitions. If not, then it's safe to update 6084aef0509Sthorpej * the in-core disklabel. Only read the disklabel if it is 6094aef0509Sthorpej * not already valid. 610b8dcfbd3Sthorpej */ 6114aef0509Sthorpej if ((cs->sc_flags & (CCDF_INITED|CCDF_VLABEL)) == CCDF_INITED && 6124aef0509Sthorpej cs->sc_dkdev.dk_openmask == 0) 613b8dcfbd3Sthorpej ccdgetdisklabel(dev); 614b8dcfbd3Sthorpej 61577d85de2Sthorpej /* Check that the partition exists. */ 61691bd533cSthorpej if (part != RAW_PART) { 61791bd533cSthorpej if (((cs->sc_flags & CCDF_INITED) == 0) || 618bb608224Sthorpej ((part >= lp->d_npartitions) || 619b8dcfbd3Sthorpej (lp->d_partitions[part].p_fstype == FS_UNUSED))) { 620b8dcfbd3Sthorpej error = ENXIO; 621b8dcfbd3Sthorpej goto done; 622b8dcfbd3Sthorpej } 62391bd533cSthorpej } 62477d85de2Sthorpej 62577d85de2Sthorpej /* Prevent our unit from being unconfigured while open. */ 62677d85de2Sthorpej switch (fmt) { 62777d85de2Sthorpej case S_IFCHR: 62877d85de2Sthorpej cs->sc_dkdev.dk_copenmask |= pmask; 62977d85de2Sthorpej break; 63077d85de2Sthorpej 63177d85de2Sthorpej case S_IFBLK: 63277d85de2Sthorpej cs->sc_dkdev.dk_bopenmask |= pmask; 63377d85de2Sthorpej break; 63477d85de2Sthorpej } 63577d85de2Sthorpej cs->sc_dkdev.dk_openmask = 63677d85de2Sthorpej cs->sc_dkdev.dk_copenmask | cs->sc_dkdev.dk_bopenmask; 63777d85de2Sthorpej 638b8dcfbd3Sthorpej done: 639481ff5acSad mutex_exit(&cs->sc_dvlock); 64049c5d29eSthorpej return (error); 6416f9db1eeShpeyerl } 6426f9db1eeShpeyerl 64377d85de2Sthorpej /* ARGSUSED */ 644b8d38500Sthorpej static int 645168cd830Schristos ccdclose(dev_t dev, int flags, int fmt, struct lwp *l) 646a44a2765Scgd { 64777d85de2Sthorpej int unit = ccdunit(dev); 64877d85de2Sthorpej struct ccd_softc *cs; 6499abeea58Sad int part; 65077d85de2Sthorpej 651a44a2765Scgd #ifdef DEBUG 652a44a2765Scgd if (ccddebug & CCDB_FOLLOW) 65308ebead9Scegger printf("ccdclose(0x%"PRIx64", 0x%x)\n", dev, flags); 654a44a2765Scgd #endif 65577d85de2Sthorpej 656b08b65c6Schristos if ((cs = ccdget(unit)) == NULL) 657b08b65c6Schristos return ENXIO; 658b8dcfbd3Sthorpej 659481ff5acSad mutex_enter(&cs->sc_dvlock); 660b8dcfbd3Sthorpej 66177d85de2Sthorpej part = DISKPART(dev); 66277d85de2Sthorpej 66377d85de2Sthorpej /* ...that much closer to allowing unconfiguration... */ 66477d85de2Sthorpej switch (fmt) { 66577d85de2Sthorpej case S_IFCHR: 66677d85de2Sthorpej cs->sc_dkdev.dk_copenmask &= ~(1 << part); 66777d85de2Sthorpej break; 66877d85de2Sthorpej 66977d85de2Sthorpej case S_IFBLK: 67077d85de2Sthorpej cs->sc_dkdev.dk_bopenmask &= ~(1 << part); 67177d85de2Sthorpej break; 67277d85de2Sthorpej } 67377d85de2Sthorpej cs->sc_dkdev.dk_openmask = 67477d85de2Sthorpej cs->sc_dkdev.dk_copenmask | cs->sc_dkdev.dk_bopenmask; 67577d85de2Sthorpej 6764aef0509Sthorpej if (cs->sc_dkdev.dk_openmask == 0) { 6774aef0509Sthorpej if ((cs->sc_flags & CCDF_KLABEL) == 0) 6784aef0509Sthorpej cs->sc_flags &= ~CCDF_VLABEL; 6794aef0509Sthorpej } 6804aef0509Sthorpej 681481ff5acSad mutex_exit(&cs->sc_dvlock); 682a44a2765Scgd return (0); 683a44a2765Scgd } 684a44a2765Scgd 685481ff5acSad static bool 686481ff5acSad ccdbackoff(struct ccd_softc *cs) 687481ff5acSad { 688481ff5acSad 689481ff5acSad /* XXX Arbitrary, should be a uvm call. */ 690481ff5acSad return uvmexp.free < (uvmexp.freemin >> 1) && 691481ff5acSad disk_isbusy(&cs->sc_dkdev); 692481ff5acSad } 693481ff5acSad 694481ff5acSad static void 695481ff5acSad ccdthread(void *cookie) 696481ff5acSad { 697481ff5acSad struct ccd_softc *cs; 698481ff5acSad 699481ff5acSad cs = cookie; 700481ff5acSad 701481ff5acSad #ifdef DEBUG 702481ff5acSad if (ccddebug & CCDB_FOLLOW) 703481ff5acSad printf("ccdthread: hello\n"); 704481ff5acSad #endif 705481ff5acSad 706481ff5acSad mutex_enter(cs->sc_iolock); 707481ff5acSad while (__predict_true(!cs->sc_zap)) { 708481ff5acSad if (bufq_peek(cs->sc_bufq) == NULL) { 709481ff5acSad /* Nothing to do. */ 710481ff5acSad cv_wait(&cs->sc_push, cs->sc_iolock); 711481ff5acSad continue; 712481ff5acSad } 713481ff5acSad if (ccdbackoff(cs)) { 714481ff5acSad /* Wait for memory to become available. */ 715481ff5acSad (void)cv_timedwait(&cs->sc_push, cs->sc_iolock, 1); 716481ff5acSad continue; 717481ff5acSad } 718481ff5acSad #ifdef DEBUG 719481ff5acSad if (ccddebug & CCDB_FOLLOW) 720481ff5acSad printf("ccdthread: dispatching I/O\n"); 721481ff5acSad #endif 722481ff5acSad ccdstart(cs); 723481ff5acSad mutex_enter(cs->sc_iolock); 724481ff5acSad } 725481ff5acSad cs->sc_thread = NULL; 726481ff5acSad mutex_exit(cs->sc_iolock); 727481ff5acSad #ifdef DEBUG 728481ff5acSad if (ccddebug & CCDB_FOLLOW) 729481ff5acSad printf("ccdthread: goodbye\n"); 730481ff5acSad #endif 731481ff5acSad kthread_exit(0); 732481ff5acSad } 733481ff5acSad 734b8d38500Sthorpej static void 735b8d38500Sthorpej ccdstrategy(struct buf *bp) 7366f9db1eeShpeyerl { 737169ac5b3Saugustss int unit = ccdunit(bp->b_dev); 738b08b65c6Schristos struct ccd_softc *cs; 739b08b65c6Schristos if ((cs = ccdget(unit)) == NULL) 740b08b65c6Schristos return; 7416f9db1eeShpeyerl 742481ff5acSad /* Must be open or reading label. */ 743481ff5acSad KASSERT(cs->sc_dkdev.dk_openmask != 0 || 744481ff5acSad (cs->sc_flags & CCDF_RLABEL) != 0); 745481ff5acSad 746481ff5acSad mutex_enter(cs->sc_iolock); 747481ff5acSad /* Synchronize with device init/uninit. */ 748481ff5acSad if (__predict_false((cs->sc_flags & CCDF_INITED) == 0)) { 749481ff5acSad mutex_exit(cs->sc_iolock); 750c6aa25bfSthorpej #ifdef DEBUG 751c6aa25bfSthorpej if (ccddebug & CCDB_FOLLOW) 752c6aa25bfSthorpej printf("ccdstrategy: unit %d: not inited\n", unit); 753c6aa25bfSthorpej #endif 7546f9db1eeShpeyerl bp->b_error = ENXIO; 755481ff5acSad bp->b_resid = bp->b_bcount; 756481ff5acSad biodone(bp); 757481ff5acSad return; 7586f9db1eeShpeyerl } 75977d85de2Sthorpej 760481ff5acSad /* Defer to thread if system is low on memory. */ 761481ff5acSad bufq_put(cs->sc_bufq, bp); 762481ff5acSad if (__predict_false(ccdbackoff(cs))) { 763481ff5acSad mutex_exit(cs->sc_iolock); 764481ff5acSad #ifdef DEBUG 765481ff5acSad if (ccddebug & CCDB_FOLLOW) 766481ff5acSad printf("ccdstrategy: holding off on I/O\n"); 767481ff5acSad #endif 768481ff5acSad return; 769481ff5acSad } 770481ff5acSad ccdstart(cs); 771481ff5acSad } 772481ff5acSad 773481ff5acSad static void 774481ff5acSad ccdstart(struct ccd_softc *cs) 775481ff5acSad { 776481ff5acSad daddr_t blkno; 777481ff5acSad int wlabel; 778481ff5acSad struct disklabel *lp; 779481ff5acSad long bcount, rcount; 780481ff5acSad struct ccdbuf *cbp; 781481ff5acSad char *addr; 782481ff5acSad daddr_t bn; 783481ff5acSad vnode_t *vp; 784481ff5acSad buf_t *bp; 785481ff5acSad 786481ff5acSad KASSERT(mutex_owned(cs->sc_iolock)); 787481ff5acSad 788481ff5acSad disk_busy(&cs->sc_dkdev); 789481ff5acSad bp = bufq_get(cs->sc_bufq); 790481ff5acSad KASSERT(bp != NULL); 791481ff5acSad 792481ff5acSad #ifdef DEBUG 793481ff5acSad if (ccddebug & CCDB_FOLLOW) 794481ff5acSad printf("ccdstart(%s, %p)\n", cs->sc_xname, bp); 795481ff5acSad #endif 796481ff5acSad 79777d85de2Sthorpej /* If it's a nil transfer, wake up the top half now. */ 79877d85de2Sthorpej if (bp->b_bcount == 0) 7996f9db1eeShpeyerl goto done; 80077d85de2Sthorpej 8015b39541eSthorpej lp = cs->sc_dkdev.dk_label; 802b8dcfbd3Sthorpej 80377d85de2Sthorpej /* 804ab0109adSthorpej * Do bounds checking and adjust transfer. If there's an 8052f25411dSthorpej * error, the bounds check will flag that for us. Convert 8062f25411dSthorpej * the partition relative block number to an absolute. 80777d85de2Sthorpej */ 8082f25411dSthorpej blkno = bp->b_blkno; 80977d85de2Sthorpej wlabel = cs->sc_flags & (CCDF_WLABEL|CCDF_LABELLING); 8102f25411dSthorpej if (DISKPART(bp->b_dev) != RAW_PART) { 811e43fecb2Sthorpej if (bounds_check_with_label(&cs->sc_dkdev, bp, wlabel) <= 0) 8126f9db1eeShpeyerl goto done; 8132f25411dSthorpej blkno += lp->d_partitions[DISKPART(bp->b_dev)].p_offset; 8142f25411dSthorpej } 815481ff5acSad mutex_exit(cs->sc_iolock); 8162f25411dSthorpej bp->b_rawblkno = blkno; 81777d85de2Sthorpej 818481ff5acSad /* Allocate the component buffers and start I/O! */ 8192f25411dSthorpej bp->b_resid = bp->b_bcount; 8202f25411dSthorpej bn = bp->b_rawblkno; 8213214723fShpeyerl addr = bp->b_data; 8226f9db1eeShpeyerl for (bcount = bp->b_bcount; bcount > 0; bcount -= rcount) { 823092c2019Sthorpej cbp = ccdbuffer(cs, bp, bn, addr, bcount); 824092c2019Sthorpej rcount = cbp->cb_buf.b_bcount; 825d272bb00Sthorpej bn += btodb(rcount); 826d272bb00Sthorpej addr += rcount; 827481ff5acSad vp = cbp->cb_buf.b_vp; 828481ff5acSad if ((cbp->cb_buf.b_flags & B_READ) == 0) { 829e225b7bdSrmind mutex_enter(vp->v_interlock); 830481ff5acSad vp->v_numoutput++; 831e225b7bdSrmind mutex_exit(vp->v_interlock); 832d272bb00Sthorpej } 833481ff5acSad (void)VOP_STRATEGY(vp, &cbp->cb_buf); 834481ff5acSad } 835481ff5acSad return; 836d272bb00Sthorpej 837481ff5acSad done: 838481ff5acSad disk_unbusy(&cs->sc_dkdev, 0, 0); 839481ff5acSad cv_broadcast(&cs->sc_stop); 840481ff5acSad cv_broadcast(&cs->sc_push); 841481ff5acSad mutex_exit(cs->sc_iolock); 842481ff5acSad bp->b_resid = bp->b_bcount; 843481ff5acSad biodone(bp); 8442f25411dSthorpej } 8456f9db1eeShpeyerl 8466f9db1eeShpeyerl /* 8476f9db1eeShpeyerl * Build a component buffer header. 8486f9db1eeShpeyerl */ 849092c2019Sthorpej static struct ccdbuf * 85053524e44Schristos ccdbuffer(struct ccd_softc *cs, struct buf *bp, daddr_t bn, void *addr, 851b8d38500Sthorpej long bcount) 8526f9db1eeShpeyerl { 853169ac5b3Saugustss struct ccdcinfo *ci; 854169ac5b3Saugustss struct ccdbuf *cbp; 855169ac5b3Saugustss daddr_t cbn, cboff; 856169ac5b3Saugustss u_int64_t cbc; 8577770d7c4Sthorpej int ccdisk; 8586f9db1eeShpeyerl 8596f9db1eeShpeyerl #ifdef DEBUG 8603214723fShpeyerl if (ccddebug & CCDB_IO) 8614e0e5333Skleink printf("ccdbuffer(%p, %p, %" PRId64 ", %p, %ld)\n", 8626f9db1eeShpeyerl cs, bp, bn, addr, bcount); 8636f9db1eeShpeyerl #endif 8646f9db1eeShpeyerl /* 8656f9db1eeShpeyerl * Determine which component bn falls in. 8666f9db1eeShpeyerl */ 8676f9db1eeShpeyerl cbn = bn; 8686f9db1eeShpeyerl cboff = 0; 86977d85de2Sthorpej 8706f9db1eeShpeyerl /* 8716f9db1eeShpeyerl * Serially concatenated 8726f9db1eeShpeyerl */ 8736f9db1eeShpeyerl if (cs->sc_ileave == 0) { 874169ac5b3Saugustss daddr_t sblk; 8756f9db1eeShpeyerl 8766f9db1eeShpeyerl sblk = 0; 8777770d7c4Sthorpej for (ccdisk = 0, ci = &cs->sc_cinfo[ccdisk]; 8787770d7c4Sthorpej cbn >= sblk + ci->ci_size; 8797770d7c4Sthorpej ccdisk++, ci = &cs->sc_cinfo[ccdisk]) 8806f9db1eeShpeyerl sblk += ci->ci_size; 8816f9db1eeShpeyerl cbn -= sblk; 8826f9db1eeShpeyerl } 8836f9db1eeShpeyerl /* 8846f9db1eeShpeyerl * Interleaved 8856f9db1eeShpeyerl */ 8866f9db1eeShpeyerl else { 887169ac5b3Saugustss struct ccdiinfo *ii; 8887770d7c4Sthorpej int off; 8896f9db1eeShpeyerl 8906f9db1eeShpeyerl cboff = cbn % cs->sc_ileave; 8916f9db1eeShpeyerl cbn /= cs->sc_ileave; 8926f9db1eeShpeyerl for (ii = cs->sc_itable; ii->ii_ndisk; ii++) 8936f9db1eeShpeyerl if (ii->ii_startblk > cbn) 8946f9db1eeShpeyerl break; 8956f9db1eeShpeyerl ii--; 8966f9db1eeShpeyerl off = cbn - ii->ii_startblk; 8976f9db1eeShpeyerl if (ii->ii_ndisk == 1) { 8986f9db1eeShpeyerl ccdisk = ii->ii_index[0]; 8996f9db1eeShpeyerl cbn = ii->ii_startoff + off; 9006f9db1eeShpeyerl } else { 9016f9db1eeShpeyerl ccdisk = ii->ii_index[off % ii->ii_ndisk]; 9026f9db1eeShpeyerl cbn = ii->ii_startoff + off / ii->ii_ndisk; 9036f9db1eeShpeyerl } 9046f9db1eeShpeyerl cbn *= cs->sc_ileave; 9056f9db1eeShpeyerl ci = &cs->sc_cinfo[ccdisk]; 9066f9db1eeShpeyerl } 90777d85de2Sthorpej 9086f9db1eeShpeyerl /* 9096f9db1eeShpeyerl * Fill in the component buf structure. 9106f9db1eeShpeyerl */ 911dd5ae858Sthorpej cbp = CCD_GETBUF(); 912481ff5acSad KASSERT(cbp != NULL); 9134a780c9aSad buf_init(&cbp->cb_buf); 9144a780c9aSad cbp->cb_buf.b_flags = bp->b_flags; 9154a780c9aSad cbp->cb_buf.b_oflags = bp->b_oflags; 9164a780c9aSad cbp->cb_buf.b_cflags = bp->b_cflags; 917d318abdbSchristos cbp->cb_buf.b_iodone = ccdiodone; 918f694af67Scgd cbp->cb_buf.b_proc = bp->b_proc; 9193db4e2acShannken cbp->cb_buf.b_dev = ci->ci_dev; 920f694af67Scgd cbp->cb_buf.b_blkno = cbn + cboff; 921f694af67Scgd cbp->cb_buf.b_data = addr; 92277d85de2Sthorpej cbp->cb_buf.b_vp = ci->ci_vp; 923e225b7bdSrmind cbp->cb_buf.b_objlock = ci->ci_vp->v_interlock; 9246f9db1eeShpeyerl if (cs->sc_ileave == 0) 925902855d6Sthorpej cbc = dbtob((u_int64_t)(ci->ci_size - cbn)); 9266f9db1eeShpeyerl else 927902855d6Sthorpej cbc = dbtob((u_int64_t)(cs->sc_ileave - cboff)); 928902855d6Sthorpej cbp->cb_buf.b_bcount = cbc < bcount ? cbc : bcount; 929f694af67Scgd 9306f9db1eeShpeyerl /* 931f694af67Scgd * context for ccdiodone 9326f9db1eeShpeyerl */ 933f694af67Scgd cbp->cb_obp = bp; 934c6aa25bfSthorpej cbp->cb_sc = cs; 9357770d7c4Sthorpej cbp->cb_comp = ccdisk; 936f694af67Scgd 9377266a959Syamt BIO_COPYPRIO(&cbp->cb_buf, bp); 9387266a959Syamt 9396f9db1eeShpeyerl #ifdef DEBUG 9403214723fShpeyerl if (ccddebug & CCDB_IO) 94108ebead9Scegger printf(" dev 0x%"PRIx64"(u%lu): cbp %p bn %" PRId64 " addr %p" 94222399b45Syamt " bcnt %d\n", 943fefcc128Smjacob ci->ci_dev, (unsigned long) (ci-cs->sc_cinfo), cbp, 944fefcc128Smjacob cbp->cb_buf.b_blkno, cbp->cb_buf.b_data, 945fefcc128Smjacob cbp->cb_buf.b_bcount); 9466f9db1eeShpeyerl #endif 947092c2019Sthorpej 948092c2019Sthorpej return (cbp); 9496f9db1eeShpeyerl } 9506f9db1eeShpeyerl 9516f9db1eeShpeyerl /* 95277d85de2Sthorpej * Called at interrupt time. 9536f9db1eeShpeyerl * Mark the component as done and if all components are done, 9546f9db1eeShpeyerl * take a ccd interrupt. 9556f9db1eeShpeyerl */ 956b8d38500Sthorpej static void 957b8d38500Sthorpej ccdiodone(struct buf *vbp) 9586f9db1eeShpeyerl { 959d318abdbSchristos struct ccdbuf *cbp = (struct ccdbuf *) vbp; 960c6aa25bfSthorpej struct buf *bp = cbp->cb_obp; 961c6aa25bfSthorpej struct ccd_softc *cs = cbp->cb_sc; 962481ff5acSad int count; 9636f9db1eeShpeyerl 9646f9db1eeShpeyerl #ifdef DEBUG 9653214723fShpeyerl if (ccddebug & CCDB_FOLLOW) 96686373f8cSchristos printf("ccdiodone(%p)\n", cbp); 9673214723fShpeyerl if (ccddebug & CCDB_IO) { 96822399b45Syamt printf("ccdiodone: bp %p bcount %d resid %d\n", 9696f9db1eeShpeyerl bp, bp->b_bcount, bp->b_resid); 97008ebead9Scegger printf(" dev 0x%"PRIx64"(u%d), cbp %p bn %" PRId64 " addr %p" 97122399b45Syamt " bcnt %d\n", 972f694af67Scgd cbp->cb_buf.b_dev, cbp->cb_comp, cbp, 973f694af67Scgd cbp->cb_buf.b_blkno, cbp->cb_buf.b_data, 974f694af67Scgd cbp->cb_buf.b_bcount); 9756f9db1eeShpeyerl } 9766f9db1eeShpeyerl #endif 9776f9db1eeShpeyerl 978eb171eaaSad if (cbp->cb_buf.b_error != 0) { 979eb171eaaSad bp->b_error = cbp->cb_buf.b_error; 98000172c10Sthorpej printf("%s: error %d on component %d\n", 98100172c10Sthorpej cs->sc_xname, bp->b_error, cbp->cb_comp); 9826f9db1eeShpeyerl } 983f694af67Scgd count = cbp->cb_buf.b_bcount; 9844a780c9aSad buf_destroy(&cbp->cb_buf); 985dd5ae858Sthorpej CCD_PUTBUF(cbp); 9866f9db1eeShpeyerl 9876f9db1eeShpeyerl /* 9886f9db1eeShpeyerl * If all done, "interrupt". 9896f9db1eeShpeyerl */ 990481ff5acSad mutex_enter(cs->sc_iolock); 9916f9db1eeShpeyerl bp->b_resid -= count; 9926f9db1eeShpeyerl if (bp->b_resid < 0) 9936f9db1eeShpeyerl panic("ccdiodone: count"); 994481ff5acSad if (bp->b_resid == 0) { 995481ff5acSad /* 996481ff5acSad * Request is done for better or worse, wakeup the top half. 997481ff5acSad */ 998481ff5acSad if (bp->b_error != 0) 999481ff5acSad bp->b_resid = bp->b_bcount; 1000481ff5acSad disk_unbusy(&cs->sc_dkdev, (bp->b_bcount - bp->b_resid), 1001481ff5acSad (bp->b_flags & B_READ)); 1002481ff5acSad if (!disk_isbusy(&cs->sc_dkdev)) { 1003481ff5acSad if (bufq_peek(cs->sc_bufq) != NULL) { 1004481ff5acSad cv_broadcast(&cs->sc_push); 1005481ff5acSad } 1006481ff5acSad cv_broadcast(&cs->sc_stop); 1007481ff5acSad } 1008481ff5acSad mutex_exit(cs->sc_iolock); 1009481ff5acSad biodone(bp); 1010481ff5acSad } else 1011481ff5acSad mutex_exit(cs->sc_iolock); 10126f9db1eeShpeyerl } 10136f9db1eeShpeyerl 101477d85de2Sthorpej /* ARGSUSED */ 1015b8d38500Sthorpej static int 1016168cd830Schristos ccdread(dev_t dev, struct uio *uio, int flags) 10173214723fShpeyerl { 101877d85de2Sthorpej int unit = ccdunit(dev); 101977d85de2Sthorpej struct ccd_softc *cs; 10203214723fShpeyerl 10213214723fShpeyerl #ifdef DEBUG 10223214723fShpeyerl if (ccddebug & CCDB_FOLLOW) 102308ebead9Scegger printf("ccdread(0x%"PRIx64", %p)\n", dev, uio); 10243214723fShpeyerl #endif 1025b08b65c6Schristos if ((cs = ccdget(unit)) == NULL) 1026b08b65c6Schristos return 0; 102777d85de2Sthorpej 1028481ff5acSad /* Unlocked advisory check, ccdstrategy check is synchronous. */ 102977d85de2Sthorpej if ((cs->sc_flags & CCDF_INITED) == 0) 103077d85de2Sthorpej return (ENXIO); 103177d85de2Sthorpej 10323214723fShpeyerl return (physio(ccdstrategy, NULL, dev, B_READ, minphys, uio)); 10333214723fShpeyerl } 10343214723fShpeyerl 103577d85de2Sthorpej /* ARGSUSED */ 1036b8d38500Sthorpej static int 1037168cd830Schristos ccdwrite(dev_t dev, struct uio *uio, int flags) 10383214723fShpeyerl { 103977d85de2Sthorpej int unit = ccdunit(dev); 104077d85de2Sthorpej struct ccd_softc *cs; 10413214723fShpeyerl 10423214723fShpeyerl #ifdef DEBUG 10433214723fShpeyerl if (ccddebug & CCDB_FOLLOW) 104408ebead9Scegger printf("ccdwrite(0x%"PRIx64", %p)\n", dev, uio); 10453214723fShpeyerl #endif 1046b08b65c6Schristos if ((cs = ccdget(unit)) == NULL) 1047b08b65c6Schristos return ENOENT; 104877d85de2Sthorpej 1049481ff5acSad /* Unlocked advisory check, ccdstrategy check is synchronous. */ 105077d85de2Sthorpej if ((cs->sc_flags & CCDF_INITED) == 0) 105177d85de2Sthorpej return (ENXIO); 105277d85de2Sthorpej 10533214723fShpeyerl return (physio(ccdstrategy, NULL, dev, B_WRITE, minphys, uio)); 10543214723fShpeyerl } 10553214723fShpeyerl 1056b8d38500Sthorpej static int 105753524e44Schristos ccdioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l) 10586f9db1eeShpeyerl { 105977d85de2Sthorpej int unit = ccdunit(dev); 1060481ff5acSad int i, j, lookedup = 0, error = 0; 10618f8b0d91Smycroft int part, pmask; 106277d85de2Sthorpej struct ccd_softc *cs; 106377d85de2Sthorpej struct ccd_ioctl *ccio = (struct ccd_ioctl *)data; 10642867b68bSelad kauth_cred_t uc; 106577d85de2Sthorpej char **cpp; 10668f6ed30dSdholland struct pathbuf *pb; 106777d85de2Sthorpej struct vnode **vpp; 1068e2d1c1f9Sfvdl #ifdef __HAVE_OLD_DISKLABEL 1069e2d1c1f9Sfvdl struct disklabel newlabel; 1070e2d1c1f9Sfvdl #endif 107177d85de2Sthorpej 1072b08b65c6Schristos if ((cs = ccdget(unit)) == NULL) 1073b08b65c6Schristos return ENOENT; 1074481ff5acSad uc = kauth_cred_get(); 107597297f37Sjld 10767d706cf7Ssborrill /* 10777d706cf7Ssborrill * Compat code must not be called if on a platform where 10787d706cf7Ssborrill * sizeof (size_t) == sizeof (uint64_t) as CCDIOCSET will 10797d706cf7Ssborrill * be the same as CCDIOCSET_60 10807d706cf7Ssborrill */ 10817d706cf7Ssborrill #if defined(COMPAT_60) && !defined(_LP64) 10827d706cf7Ssborrill switch (cmd) { 10837d706cf7Ssborrill case CCDIOCSET_60: { 10847d706cf7Ssborrill struct ccd_ioctl ccionew; 10857d706cf7Ssborrill struct ccd_ioctl_60 *ccio60 = 10867d706cf7Ssborrill (struct ccd_ioctl_60 *)data; 10877d706cf7Ssborrill ccionew.ccio_disks = ccio->ccio_disks; 10887d706cf7Ssborrill ccionew.ccio_ndisks = ccio->ccio_ndisks; 10897d706cf7Ssborrill ccionew.ccio_ileave = ccio->ccio_ileave; 10907d706cf7Ssborrill ccionew.ccio_flags = ccio->ccio_flags; 10917d706cf7Ssborrill ccionew.ccio_unit = ccio->ccio_unit; 10927d706cf7Ssborrill error = ccdioctl(dev, CCDIOCSET, &ccionew, flag, l); 10937d706cf7Ssborrill if (!error) { 10947d706cf7Ssborrill /* Copy data back, adjust types if necessary */ 10957d706cf7Ssborrill ccio60->ccio_disks = ccionew.ccio_disks; 10967d706cf7Ssborrill ccio60->ccio_ndisks = ccionew.ccio_ndisks; 10977d706cf7Ssborrill ccio60->ccio_ileave = ccionew.ccio_ileave; 10987d706cf7Ssborrill ccio60->ccio_flags = ccionew.ccio_flags; 10997d706cf7Ssborrill ccio60->ccio_unit = ccionew.ccio_unit; 11007d706cf7Ssborrill ccio60->ccio_size = (size_t)ccionew.ccio_size; 11017d706cf7Ssborrill } 11027d706cf7Ssborrill return error; 11037d706cf7Ssborrill } 11047d706cf7Ssborrill break; 11057d706cf7Ssborrill 11067d706cf7Ssborrill case CCDIOCCLR_60: 11077d706cf7Ssborrill /* 11087d706cf7Ssborrill * ccio_size member not used, so existing struct OK 11097d706cf7Ssborrill * drop through to existing non-compat version 11107d706cf7Ssborrill */ 11117d706cf7Ssborrill cmd = CCDIOCCLR; 11127d706cf7Ssborrill break; 11137d706cf7Ssborrill } 11147d706cf7Ssborrill #endif /* COMPAT_60 && !_LP64*/ 11157d706cf7Ssborrill 11164cea35ebSthorpej /* Must be open for writes for these commands... */ 11174cea35ebSthorpej switch (cmd) { 11184cea35ebSthorpej case CCDIOCSET: 11194cea35ebSthorpej case CCDIOCCLR: 11204cea35ebSthorpej case DIOCSDINFO: 11214cea35ebSthorpej case DIOCWDINFO: 1122e2d1c1f9Sfvdl #ifdef __HAVE_OLD_DISKLABEL 1123e2d1c1f9Sfvdl case ODIOCSDINFO: 1124e2d1c1f9Sfvdl case ODIOCWDINFO: 1125e2d1c1f9Sfvdl #endif 11264aef0509Sthorpej case DIOCKLABEL: 11274cea35ebSthorpej case DIOCWLABEL: 11284cea35ebSthorpej if ((flag & FWRITE) == 0) 11294cea35ebSthorpej return (EBADF); 11304cea35ebSthorpej } 11314cea35ebSthorpej 1132481ff5acSad mutex_enter(&cs->sc_dvlock); 11330272b2abSthorpej 11344cea35ebSthorpej /* Must be initialized for these... */ 11354cea35ebSthorpej switch (cmd) { 11364cea35ebSthorpej case CCDIOCCLR: 1137d72a3b2dSkleink case DIOCGDINFO: 1138f64a29d7Sthorpej case DIOCCACHESYNC: 11394cea35ebSthorpej case DIOCSDINFO: 11404cea35ebSthorpej case DIOCWDINFO: 11414cea35ebSthorpej case DIOCGPART: 11424cea35ebSthorpej case DIOCWLABEL: 11434aef0509Sthorpej case DIOCKLABEL: 1144939e074dSthorpej case DIOCGDEFLABEL: 1145e2d1c1f9Sfvdl #ifdef __HAVE_OLD_DISKLABEL 1146e2d1c1f9Sfvdl case ODIOCGDINFO: 1147e2d1c1f9Sfvdl case ODIOCSDINFO: 1148e2d1c1f9Sfvdl case ODIOCWDINFO: 1149e2d1c1f9Sfvdl case ODIOCGDEFLABEL: 1150e2d1c1f9Sfvdl #endif 11510272b2abSthorpej if ((cs->sc_flags & CCDF_INITED) == 0) { 11520272b2abSthorpej error = ENXIO; 11530272b2abSthorpej goto out; 11540272b2abSthorpej } 11554cea35ebSthorpej } 11564cea35ebSthorpej 115777d85de2Sthorpej switch (cmd) { 115877d85de2Sthorpej case CCDIOCSET: 11590272b2abSthorpej if (cs->sc_flags & CCDF_INITED) { 11600272b2abSthorpej error = EBUSY; 11610272b2abSthorpej goto out; 11620272b2abSthorpej } 1163b8dcfbd3Sthorpej 1164ab27c3f8Sthorpej /* Validate the flags. */ 11650272b2abSthorpej if ((ccio->ccio_flags & CCDF_USERMASK) != ccio->ccio_flags) { 11660272b2abSthorpej error = EINVAL; 11670272b2abSthorpej goto out; 11680272b2abSthorpej } 1169ab27c3f8Sthorpej 1170481ff5acSad if (ccio->ccio_ndisks > CCD_MAXNDISKS || 1171481ff5acSad ccio->ccio_ndisks == 0) { 1172539f3bccSjdolecek error = EINVAL; 1173539f3bccSjdolecek goto out; 1174539f3bccSjdolecek } 1175539f3bccSjdolecek 117677d85de2Sthorpej /* Fill in some important bits. */ 11770272b2abSthorpej cs->sc_ileave = ccio->ccio_ileave; 11780272b2abSthorpej cs->sc_nccdisks = ccio->ccio_ndisks; 11790272b2abSthorpej cs->sc_flags = ccio->ccio_flags & CCDF_USERMASK; 118077d85de2Sthorpej 118177d85de2Sthorpej /* 118277d85de2Sthorpej * Allocate space for and copy in the array of 11837d706cf7Ssborrill * component pathnames and device numbers. 118477d85de2Sthorpej */ 1185481ff5acSad cpp = kmem_alloc(ccio->ccio_ndisks * sizeof(*cpp), KM_SLEEP); 1186481ff5acSad vpp = kmem_alloc(ccio->ccio_ndisks * sizeof(*vpp), KM_SLEEP); 1187bd99e342Sdsl error = copyin(ccio->ccio_disks, cpp, 1188481ff5acSad ccio->ccio_ndisks * sizeof(*cpp)); 118977d85de2Sthorpej if (error) { 1190481ff5acSad kmem_free(vpp, ccio->ccio_ndisks * sizeof(*vpp)); 1191481ff5acSad kmem_free(cpp, ccio->ccio_ndisks * sizeof(*cpp)); 11920272b2abSthorpej goto out; 11936f9db1eeShpeyerl } 11946f9db1eeShpeyerl 119577d85de2Sthorpej #ifdef DEBUG 119677d85de2Sthorpej if (ccddebug & CCDB_INIT) 119777d85de2Sthorpej for (i = 0; i < ccio->ccio_ndisks; ++i) 11987cdea212Schristos printf("ccdioctl: component %d: %p\n", 119977d85de2Sthorpej i, cpp[i]); 120077d85de2Sthorpej #endif 120177d85de2Sthorpej 120277d85de2Sthorpej for (i = 0; i < ccio->ccio_ndisks; ++i) { 120377d85de2Sthorpej #ifdef DEBUG 120477d85de2Sthorpej if (ccddebug & CCDB_INIT) 120586373f8cSchristos printf("ccdioctl: lookedup = %d\n", lookedup); 120677d85de2Sthorpej #endif 12078f6ed30dSdholland error = pathbuf_copyin(cpp[i], &pb); 12080a4a6fb0Sdholland if (error == 0) { 12098f6ed30dSdholland error = dk_lookup(pb, l, &vpp[i]); 12100a4a6fb0Sdholland } 12118f6ed30dSdholland pathbuf_destroy(pb); 12128f6ed30dSdholland if (error != 0) { 121377d85de2Sthorpej for (j = 0; j < lookedup; ++j) 1214a2db2c18Sthorpej (void)vn_close(vpp[j], FREAD|FWRITE, 1215a9ca7a37Sad uc); 1216481ff5acSad kmem_free(vpp, ccio->ccio_ndisks * 1217481ff5acSad sizeof(*vpp)); 1218481ff5acSad kmem_free(cpp, ccio->ccio_ndisks * 1219481ff5acSad sizeof(*cpp)); 12200272b2abSthorpej goto out; 122177d85de2Sthorpej } 122277d85de2Sthorpej ++lookedup; 122377d85de2Sthorpej } 122477d85de2Sthorpej 1225481ff5acSad /* Attach the disk. */ 1226481ff5acSad disk_attach(&cs->sc_dkdev); 1227481ff5acSad bufq_alloc(&cs->sc_bufq, "fcfs", 0); 1228481ff5acSad 122977d85de2Sthorpej /* 123077d85de2Sthorpej * Initialize the ccd. Fills in the softc for us. 123177d85de2Sthorpej */ 123295e1ffb1Schristos if ((error = ccdinit(cs, cpp, vpp, l)) != 0) { 123377d85de2Sthorpej for (j = 0; j < lookedup; ++j) 1234a1bc3740Sthorpej (void)vn_close(vpp[j], FREAD|FWRITE, 1235a9ca7a37Sad uc); 1236481ff5acSad kmem_free(vpp, ccio->ccio_ndisks * sizeof(*vpp)); 1237481ff5acSad kmem_free(cpp, ccio->ccio_ndisks * sizeof(*cpp)); 1238481ff5acSad disk_detach(&cs->sc_dkdev); 1239481ff5acSad bufq_free(cs->sc_bufq); 12400272b2abSthorpej goto out; 124177d85de2Sthorpej } 124277d85de2Sthorpej 12430272b2abSthorpej /* We can free the temporary variables now. */ 1244481ff5acSad kmem_free(vpp, ccio->ccio_ndisks * sizeof(*vpp)); 1245481ff5acSad kmem_free(cpp, ccio->ccio_ndisks * sizeof(*cpp)); 12460272b2abSthorpej 124777d85de2Sthorpej /* 124877d85de2Sthorpej * The ccd has been successfully initialized, so 12495b39541eSthorpej * we can place it into the array. Don't try to 12505b39541eSthorpej * read the disklabel until the disk has been attached, 12515b39541eSthorpej * because space for the disklabel is allocated 12525b39541eSthorpej * in disk_attach(); 125377d85de2Sthorpej */ 125477d85de2Sthorpej ccio->ccio_unit = unit; 125577d85de2Sthorpej ccio->ccio_size = cs->sc_size; 12565b39541eSthorpej 12575b39541eSthorpej /* Try and read the disklabel. */ 125877d85de2Sthorpej ccdgetdisklabel(dev); 125977d85de2Sthorpej break; 126077d85de2Sthorpej 126177d85de2Sthorpej case CCDIOCCLR: 126277d85de2Sthorpej /* 126377d85de2Sthorpej * Don't unconfigure if any other partitions are open 126477d85de2Sthorpej * or if both the character and block flavors of this 126577d85de2Sthorpej * partition are open. 126677d85de2Sthorpej */ 126777d85de2Sthorpej part = DISKPART(dev); 126877d85de2Sthorpej pmask = (1 << part); 126977d85de2Sthorpej if ((cs->sc_dkdev.dk_openmask & ~pmask) || 127077d85de2Sthorpej ((cs->sc_dkdev.dk_bopenmask & pmask) && 1271b8dcfbd3Sthorpej (cs->sc_dkdev.dk_copenmask & pmask))) { 12720272b2abSthorpej error = EBUSY; 12730272b2abSthorpej goto out; 1274b8dcfbd3Sthorpej } 127577d85de2Sthorpej 1276481ff5acSad /* Stop new I/O, wait for in-flight I/O to complete. */ 1277481ff5acSad mutex_enter(cs->sc_iolock); 1278481ff5acSad cs->sc_flags &= ~(CCDF_INITED|CCDF_VLABEL); 1279481ff5acSad cs->sc_zap = true; 1280481ff5acSad while (disk_isbusy(&cs->sc_dkdev) || 1281481ff5acSad bufq_peek(cs->sc_bufq) != NULL || 1282481ff5acSad cs->sc_thread != NULL) { 1283481ff5acSad cv_broadcast(&cs->sc_push); 1284481ff5acSad (void)cv_timedwait(&cs->sc_stop, cs->sc_iolock, hz); 1285481ff5acSad } 1286481ff5acSad mutex_exit(cs->sc_iolock); 12872f25411dSthorpej 128877d85de2Sthorpej /* 128977d85de2Sthorpej * Free ccd_softc information and clear entry. 129077d85de2Sthorpej */ 12917268bf55Sthorpej 12927268bf55Sthorpej /* Close the components and free their pathnames. */ 129377d85de2Sthorpej for (i = 0; i < cs->sc_nccdisks; ++i) { 129477d85de2Sthorpej /* 129577d85de2Sthorpej * XXX: this close could potentially fail and 129677d85de2Sthorpej * cause Bad Things. Maybe we need to force 129777d85de2Sthorpej * the close to happen? 129877d85de2Sthorpej */ 129977d85de2Sthorpej #ifdef DEBUG 130077d85de2Sthorpej if (ccddebug & CCDB_VNODE) 130177d85de2Sthorpej vprint("CCDIOCCLR: vnode info", 130277d85de2Sthorpej cs->sc_cinfo[i].ci_vp); 130377d85de2Sthorpej #endif 130477d85de2Sthorpej (void)vn_close(cs->sc_cinfo[i].ci_vp, FREAD|FWRITE, 1305a9ca7a37Sad uc); 1306481ff5acSad kmem_free(cs->sc_cinfo[i].ci_path, 1307481ff5acSad cs->sc_cinfo[i].ci_pathlen); 130877d85de2Sthorpej } 13097268bf55Sthorpej 13107268bf55Sthorpej /* Free interleave index. */ 1311481ff5acSad for (i = 0; cs->sc_itable[i].ii_ndisk; ++i) { 1312481ff5acSad kmem_free(cs->sc_itable[i].ii_index, 1313481ff5acSad cs->sc_itable[i].ii_indexsz); 1314481ff5acSad } 13157268bf55Sthorpej 13167268bf55Sthorpej /* Free component info and interleave table. */ 1317481ff5acSad kmem_free(cs->sc_cinfo, cs->sc_nccdisks * 1318481ff5acSad sizeof(struct ccdcinfo)); 1319481ff5acSad kmem_free(cs->sc_itable, (cs->sc_nccdisks + 1) * 1320481ff5acSad sizeof(struct ccdiinfo)); 132177d85de2Sthorpej 13227d706cf7Ssborrill aprint_normal("%s: detached\n", cs->sc_xname); 13237d706cf7Ssborrill 13247d706cf7Ssborrill /* Detach the disk. */ 13252af68666Sad disk_detach(&cs->sc_dkdev); 1326481ff5acSad bufq_free(cs->sc_bufq); 1327b08b65c6Schristos ccdput(cs); 132855252202Sjoerg /* Don't break, otherwise cs is read again. */ 132955252202Sjoerg return 0; 133077d85de2Sthorpej 133177d85de2Sthorpej case DIOCGDINFO: 13325b39541eSthorpej *(struct disklabel *)data = *(cs->sc_dkdev.dk_label); 133377d85de2Sthorpej break; 1334481ff5acSad 1335e2d1c1f9Sfvdl #ifdef __HAVE_OLD_DISKLABEL 1336e2d1c1f9Sfvdl case ODIOCGDINFO: 1337e2d1c1f9Sfvdl newlabel = *(cs->sc_dkdev.dk_label); 1338e2d1c1f9Sfvdl if (newlabel.d_npartitions > OLDMAXPARTITIONS) 1339d040bd59Sfvdl return ENOTTY; 1340e2d1c1f9Sfvdl memcpy(data, &newlabel, sizeof (struct olddisklabel)); 1341e2d1c1f9Sfvdl break; 1342e2d1c1f9Sfvdl #endif 134377d85de2Sthorpej 134477d85de2Sthorpej case DIOCGPART: 13455b39541eSthorpej ((struct partinfo *)data)->disklab = cs->sc_dkdev.dk_label; 134677d85de2Sthorpej ((struct partinfo *)data)->part = 13475b39541eSthorpej &cs->sc_dkdev.dk_label->d_partitions[DISKPART(dev)]; 134877d85de2Sthorpej break; 134977d85de2Sthorpej 1350f64a29d7Sthorpej case DIOCCACHESYNC: 1351f64a29d7Sthorpej /* 1352f64a29d7Sthorpej * XXX Do we really need to care about having a writable 1353f64a29d7Sthorpej * file descriptor here? 1354f64a29d7Sthorpej */ 1355f64a29d7Sthorpej if ((flag & FWRITE) == 0) 1356f64a29d7Sthorpej return (EBADF); 1357f64a29d7Sthorpej 1358f64a29d7Sthorpej /* 1359f64a29d7Sthorpej * We pass this call down to all components and report 1360f64a29d7Sthorpej * the first error we encounter. 1361f64a29d7Sthorpej */ 1362f64a29d7Sthorpej for (error = 0, i = 0; i < cs->sc_nccdisks; i++) { 1363f64a29d7Sthorpej j = VOP_IOCTL(cs->sc_cinfo[i].ci_vp, cmd, data, 136461e8303eSpooka flag, uc); 1365f64a29d7Sthorpej if (j != 0 && error == 0) 1366f64a29d7Sthorpej error = j; 1367f64a29d7Sthorpej } 1368f64a29d7Sthorpej break; 1369f64a29d7Sthorpej 137077d85de2Sthorpej case DIOCWDINFO: 137177d85de2Sthorpej case DIOCSDINFO: 1372e2d1c1f9Sfvdl #ifdef __HAVE_OLD_DISKLABEL 1373e2d1c1f9Sfvdl case ODIOCWDINFO: 1374e2d1c1f9Sfvdl case ODIOCSDINFO: 1375e2d1c1f9Sfvdl #endif 1376e2d1c1f9Sfvdl { 1377e2d1c1f9Sfvdl struct disklabel *lp; 1378e2d1c1f9Sfvdl #ifdef __HAVE_OLD_DISKLABEL 1379e2d1c1f9Sfvdl if (cmd == ODIOCSDINFO || cmd == ODIOCWDINFO) { 1380e2d1c1f9Sfvdl memset(&newlabel, 0, sizeof newlabel); 1381e2d1c1f9Sfvdl memcpy(&newlabel, data, sizeof (struct olddisklabel)); 1382e2d1c1f9Sfvdl lp = &newlabel; 1383e2d1c1f9Sfvdl } else 1384e2d1c1f9Sfvdl #endif 1385e2d1c1f9Sfvdl lp = (struct disklabel *)data; 1386e2d1c1f9Sfvdl 138777d85de2Sthorpej cs->sc_flags |= CCDF_LABELLING; 138877d85de2Sthorpej 13895b39541eSthorpej error = setdisklabel(cs->sc_dkdev.dk_label, 1390e2d1c1f9Sfvdl lp, 0, cs->sc_dkdev.dk_cpulabel); 139177d85de2Sthorpej if (error == 0) { 1392e2d1c1f9Sfvdl if (cmd == DIOCWDINFO 1393e2d1c1f9Sfvdl #ifdef __HAVE_OLD_DISKLABEL 1394e2d1c1f9Sfvdl || cmd == ODIOCWDINFO 1395e2d1c1f9Sfvdl #endif 1396e2d1c1f9Sfvdl ) 139777d85de2Sthorpej error = writedisklabel(CCDLABELDEV(dev), 13985b39541eSthorpej ccdstrategy, cs->sc_dkdev.dk_label, 13995b39541eSthorpej cs->sc_dkdev.dk_cpulabel); 140077d85de2Sthorpej } 140177d85de2Sthorpej 140277d85de2Sthorpej cs->sc_flags &= ~CCDF_LABELLING; 140377d85de2Sthorpej break; 1404e2d1c1f9Sfvdl } 140577d85de2Sthorpej 14064aef0509Sthorpej case DIOCKLABEL: 14074aef0509Sthorpej if (*(int *)data != 0) 14084aef0509Sthorpej cs->sc_flags |= CCDF_KLABEL; 14094aef0509Sthorpej else 14104aef0509Sthorpej cs->sc_flags &= ~CCDF_KLABEL; 14114aef0509Sthorpej break; 14124aef0509Sthorpej 141377d85de2Sthorpej case DIOCWLABEL: 141477d85de2Sthorpej if (*(int *)data != 0) 141577d85de2Sthorpej cs->sc_flags |= CCDF_WLABEL; 141677d85de2Sthorpej else 141777d85de2Sthorpej cs->sc_flags &= ~CCDF_WLABEL; 141877d85de2Sthorpej break; 141977d85de2Sthorpej 1420939e074dSthorpej case DIOCGDEFLABEL: 1421939e074dSthorpej ccdgetdefaultlabel(cs, (struct disklabel *)data); 1422939e074dSthorpej break; 1423939e074dSthorpej 1424e2d1c1f9Sfvdl #ifdef __HAVE_OLD_DISKLABEL 1425e2d1c1f9Sfvdl case ODIOCGDEFLABEL: 1426e2d1c1f9Sfvdl ccdgetdefaultlabel(cs, &newlabel); 1427e2d1c1f9Sfvdl if (newlabel.d_npartitions > OLDMAXPARTITIONS) 1428d040bd59Sfvdl return ENOTTY; 1429e2d1c1f9Sfvdl memcpy(data, &newlabel, sizeof (struct olddisklabel)); 1430e2d1c1f9Sfvdl break; 1431e2d1c1f9Sfvdl #endif 1432e2d1c1f9Sfvdl 143377d85de2Sthorpej default: 14340272b2abSthorpej error = ENOTTY; 143577d85de2Sthorpej } 143677d85de2Sthorpej 14370272b2abSthorpej out: 1438481ff5acSad mutex_exit(&cs->sc_dvlock); 14390272b2abSthorpej return (error); 144077d85de2Sthorpej } 144177d85de2Sthorpej 1442b8d38500Sthorpej static int 1443b8d38500Sthorpej ccdsize(dev_t dev) 14446f9db1eeShpeyerl { 144577d85de2Sthorpej struct ccd_softc *cs; 1446e06bb29bSthorpej struct disklabel *lp; 1447e06bb29bSthorpej int part, unit, omask, size; 14486f9db1eeShpeyerl 1449e06bb29bSthorpej unit = ccdunit(dev); 1450b08b65c6Schristos if ((cs = ccdget(unit)) == NULL) 1451b08b65c6Schristos return -1; 145277d85de2Sthorpej 145377d85de2Sthorpej if ((cs->sc_flags & CCDF_INITED) == 0) 145477d85de2Sthorpej return (-1); 145577d85de2Sthorpej 1456e06bb29bSthorpej part = DISKPART(dev); 1457e06bb29bSthorpej omask = cs->sc_dkdev.dk_openmask & (1 << part); 1458e06bb29bSthorpej lp = cs->sc_dkdev.dk_label; 1459e06bb29bSthorpej 146095e1ffb1Schristos if (omask == 0 && ccdopen(dev, 0, S_IFBLK, curlwp)) 1461e06bb29bSthorpej return (-1); 1462e06bb29bSthorpej 1463e06bb29bSthorpej if (lp->d_partitions[part].p_fstype != FS_SWAP) 146477d85de2Sthorpej size = -1; 146577d85de2Sthorpej else 1466e06bb29bSthorpej size = lp->d_partitions[part].p_size * 1467e06bb29bSthorpej (lp->d_secsize / DEV_BSIZE); 146877d85de2Sthorpej 146995e1ffb1Schristos if (omask == 0 && ccdclose(dev, 0, S_IFBLK, curlwp)) 147077d85de2Sthorpej return (-1); 147177d85de2Sthorpej 147277d85de2Sthorpej return (size); 14736f9db1eeShpeyerl } 14746f9db1eeShpeyerl 147577d85de2Sthorpej static void 1476b8d38500Sthorpej ccdgetdefaultlabel(struct ccd_softc *cs, struct disklabel *lp) 147777d85de2Sthorpej { 147877d85de2Sthorpej struct ccdgeom *ccg = &cs->sc_geom; 147977d85de2Sthorpej 1480c8b4ac1bSthorpej memset(lp, 0, sizeof(*lp)); 148177d85de2Sthorpej 148253385cb9Smlelstv if (cs->sc_size > UINT32_MAX) 148353385cb9Smlelstv lp->d_secperunit = UINT32_MAX; 148453385cb9Smlelstv else 148577d85de2Sthorpej lp->d_secperunit = cs->sc_size; 148677d85de2Sthorpej lp->d_secsize = ccg->ccg_secsize; 148777d85de2Sthorpej lp->d_nsectors = ccg->ccg_nsectors; 148877d85de2Sthorpej lp->d_ntracks = ccg->ccg_ntracks; 148977d85de2Sthorpej lp->d_ncylinders = ccg->ccg_ncylinders; 149089d4987eSthorpej lp->d_secpercyl = lp->d_ntracks * lp->d_nsectors; 149177d85de2Sthorpej 149277d85de2Sthorpej strncpy(lp->d_typename, "ccd", sizeof(lp->d_typename)); 149377d85de2Sthorpej lp->d_type = DTYPE_CCD; 149477d85de2Sthorpej strncpy(lp->d_packname, "fictitious", sizeof(lp->d_packname)); 149577d85de2Sthorpej lp->d_rpm = 3600; 149677d85de2Sthorpej lp->d_interleave = 1; 149777d85de2Sthorpej lp->d_flags = 0; 149877d85de2Sthorpej 149977d85de2Sthorpej lp->d_partitions[RAW_PART].p_offset = 0; 150053385cb9Smlelstv lp->d_partitions[RAW_PART].p_size = lp->d_secperunit; 150177d85de2Sthorpej lp->d_partitions[RAW_PART].p_fstype = FS_UNUSED; 150277d85de2Sthorpej lp->d_npartitions = RAW_PART + 1; 150377d85de2Sthorpej 150477d85de2Sthorpej lp->d_magic = DISKMAGIC; 150577d85de2Sthorpej lp->d_magic2 = DISKMAGIC; 15065b39541eSthorpej lp->d_checksum = dkcksum(cs->sc_dkdev.dk_label); 1507939e074dSthorpej } 1508939e074dSthorpej 1509939e074dSthorpej /* 1510939e074dSthorpej * Read the disklabel from the ccd. If one is not present, fake one 1511939e074dSthorpej * up. 1512939e074dSthorpej */ 1513939e074dSthorpej static void 1514b8d38500Sthorpej ccdgetdisklabel(dev_t dev) 1515939e074dSthorpej { 1516939e074dSthorpej int unit = ccdunit(dev); 1517b08b65c6Schristos struct ccd_softc *cs; 1518d91455ceSdsl const char *errstring; 1519b08b65c6Schristos struct disklabel *lp; 1520b08b65c6Schristos struct cpu_disklabel *clp; 1521939e074dSthorpej 1522b08b65c6Schristos if ((cs = ccdget(unit)) == NULL) 1523b08b65c6Schristos return; 1524b08b65c6Schristos lp = cs->sc_dkdev.dk_label; 1525b08b65c6Schristos clp = cs->sc_dkdev.dk_cpulabel; 1526481ff5acSad KASSERT(mutex_owned(&cs->sc_dvlock)); 1527481ff5acSad 1528c8b4ac1bSthorpej memset(clp, 0, sizeof(*clp)); 1529939e074dSthorpej 1530939e074dSthorpej ccdgetdefaultlabel(cs, lp); 153177d85de2Sthorpej 153277d85de2Sthorpej /* 153377d85de2Sthorpej * Call the generic disklabel extraction routine. 153477d85de2Sthorpej */ 1535481ff5acSad cs->sc_flags |= CCDF_RLABEL; 15360b441623Slukem if ((cs->sc_flags & CCDF_NOLABEL) != 0) 15370b441623Slukem errstring = "CCDF_NOLABEL set; ignoring on-disk label"; 15380b441623Slukem else 1539d318abdbSchristos errstring = readdisklabel(CCDLABELDEV(dev), ccdstrategy, 1540d318abdbSchristos cs->sc_dkdev.dk_label, cs->sc_dkdev.dk_cpulabel); 1541d318abdbSchristos if (errstring) 154277d85de2Sthorpej ccdmakedisklabel(cs); 1543512a0186Senami else { 1544512a0186Senami int i; 1545512a0186Senami struct partition *pp; 1546512a0186Senami 1547512a0186Senami /* 1548512a0186Senami * Sanity check whether the found disklabel is valid. 1549512a0186Senami * 1550512a0186Senami * This is necessary since total size of ccd may vary 1551512a0186Senami * when an interleave is changed even though exactly 1552512a0186Senami * same componets are used, and old disklabel may used 1553512a0186Senami * if that is found. 1554512a0186Senami */ 1555*49e3024aSmlelstv if (lp->d_secperunit < UINT32_MAX ? 1556*49e3024aSmlelstv lp->d_secperunit != cs->sc_size : 1557*49e3024aSmlelstv lp->d_secperunit > cs->sc_size) 1558512a0186Senami printf("WARNING: %s: " 15597d706cf7Ssborrill "total sector size in disklabel (%ju) != " 15607d706cf7Ssborrill "the size of ccd (%ju)\n", cs->sc_xname, 15617d706cf7Ssborrill (uintmax_t)lp->d_secperunit, 15627d706cf7Ssborrill (uintmax_t)cs->sc_size); 1563512a0186Senami for (i = 0; i < lp->d_npartitions; i++) { 1564512a0186Senami pp = &lp->d_partitions[i]; 1565512a0186Senami if (pp->p_offset + pp->p_size > cs->sc_size) 15669b770726Senami printf("WARNING: %s: end of partition `%c' " 15677d706cf7Ssborrill "exceeds the size of ccd (%ju)\n", 15687d706cf7Ssborrill cs->sc_xname, 'a' + i, (uintmax_t)cs->sc_size); 1569512a0186Senami } 1570512a0186Senami } 157177d85de2Sthorpej 157277d85de2Sthorpej #ifdef DEBUG 157377d85de2Sthorpej /* It's actually extremely common to have unlabeled ccds. */ 157477d85de2Sthorpej if (ccddebug & CCDB_LABEL) 157577d85de2Sthorpej if (errstring != NULL) 157686373f8cSchristos printf("%s: %s\n", cs->sc_xname, errstring); 157777d85de2Sthorpej #endif 15784aef0509Sthorpej 15794aef0509Sthorpej /* In-core label now valid. */ 1580481ff5acSad cs->sc_flags = (cs->sc_flags | CCDF_VLABEL) & ~CCDF_RLABEL; 158177d85de2Sthorpej } 158277d85de2Sthorpej 158377d85de2Sthorpej /* 158477d85de2Sthorpej * Take care of things one might want to take care of in the event 158577d85de2Sthorpej * that a disklabel isn't present. 158677d85de2Sthorpej */ 158777d85de2Sthorpej static void 1588b8d38500Sthorpej ccdmakedisklabel(struct ccd_softc *cs) 158977d85de2Sthorpej { 15905b39541eSthorpej struct disklabel *lp = cs->sc_dkdev.dk_label; 159177d85de2Sthorpej 159277d85de2Sthorpej /* 159377d85de2Sthorpej * For historical reasons, if there's no disklabel present 159477d85de2Sthorpej * the raw partition must be marked FS_BSDFFS. 159577d85de2Sthorpej */ 159677d85de2Sthorpej lp->d_partitions[RAW_PART].p_fstype = FS_BSDFFS; 159777d85de2Sthorpej 159877d85de2Sthorpej strncpy(lp->d_packname, "default label", sizeof(lp->d_packname)); 1599939e074dSthorpej 1600939e074dSthorpej lp->d_checksum = dkcksum(lp); 160177d85de2Sthorpej } 160277d85de2Sthorpej 160377d85de2Sthorpej #ifdef DEBUG 160477d85de2Sthorpej static void 1605b8d38500Sthorpej printiinfo(struct ccdiinfo *ii) 160677d85de2Sthorpej { 1607169ac5b3Saugustss int ix, i; 160877d85de2Sthorpej 160977d85de2Sthorpej for (ix = 0; ii->ii_ndisk; ix++, ii++) { 16104e0e5333Skleink printf(" itab[%d]: #dk %d sblk %" PRId64 " soff %" PRId64, 161177d85de2Sthorpej ix, ii->ii_ndisk, ii->ii_startblk, ii->ii_startoff); 161277d85de2Sthorpej for (i = 0; i < ii->ii_ndisk; i++) 161386373f8cSchristos printf(" %d", ii->ii_index[i]); 161486373f8cSchristos printf("\n"); 161577d85de2Sthorpej } 161677d85de2Sthorpej } 16176f9db1eeShpeyerl #endif 16187147ed32Shaad 16198682ae0dSpgoyette MODULE(MODULE_CLASS_DRIVER, ccd, "dk_subr"); 16207147ed32Shaad 16217147ed32Shaad static int 16227147ed32Shaad ccd_modcmd(modcmd_t cmd, void *arg) 16237147ed32Shaad { 1624cd2a35a0Smartin int error = 0; 1625cd2a35a0Smartin #ifdef _MODULE 1626cd2a35a0Smartin int bmajor = -1, cmajor = -1; 1627cd2a35a0Smartin #endif 1628b4a3a8f3Sjruoho 16297147ed32Shaad 16307147ed32Shaad switch (cmd) { 16317147ed32Shaad case MODULE_CMD_INIT: 1632b4a3a8f3Sjruoho #ifdef _MODULE 16337147ed32Shaad ccdattach(4); 16347147ed32Shaad 16357147ed32Shaad return devsw_attach("ccd", &ccd_bdevsw, &bmajor, 16367147ed32Shaad &ccd_cdevsw, &cmajor); 1637b4a3a8f3Sjruoho #endif 16387147ed32Shaad break; 16397147ed32Shaad 16407147ed32Shaad case MODULE_CMD_FINI: 1641b4a3a8f3Sjruoho #ifdef _MODULE 16427147ed32Shaad return devsw_detach(&ccd_bdevsw, &ccd_cdevsw); 1643b4a3a8f3Sjruoho #endif 16447147ed32Shaad break; 16457147ed32Shaad 16467147ed32Shaad case MODULE_CMD_STAT: 16477147ed32Shaad return ENOTTY; 16487147ed32Shaad 16497147ed32Shaad default: 16507147ed32Shaad return ENOTTY; 16517147ed32Shaad } 16527147ed32Shaad 16537147ed32Shaad return error; 16547147ed32Shaad } 1655b08b65c6Schristos 1656b08b65c6Schristos static int 1657b08b65c6Schristos ccd_units_sysctl(SYSCTLFN_ARGS) 1658b08b65c6Schristos { 1659b08b65c6Schristos struct sysctlnode node; 1660b08b65c6Schristos struct ccd_softc *sc; 1661b08b65c6Schristos int error, i, nccd, *units; 1662b08b65c6Schristos size_t size; 1663b08b65c6Schristos 1664b08b65c6Schristos nccd = 0; 1665b08b65c6Schristos mutex_enter(&ccd_lock); 1666b08b65c6Schristos LIST_FOREACH(sc, &ccds, sc_link) 1667b08b65c6Schristos nccd++; 1668b08b65c6Schristos mutex_exit(&ccd_lock); 1669b08b65c6Schristos 1670b08b65c6Schristos if (nccd != 0) { 1671b08b65c6Schristos size = nccd * sizeof(*units); 1672b08b65c6Schristos units = kmem_zalloc(size, KM_SLEEP); 1673b08b65c6Schristos if (units == NULL) 1674b08b65c6Schristos return ENOMEM; 1675b08b65c6Schristos 1676b08b65c6Schristos i = 0; 1677b08b65c6Schristos mutex_enter(&ccd_lock); 1678b08b65c6Schristos LIST_FOREACH(sc, &ccds, sc_link) { 1679b08b65c6Schristos if (i >= nccd) 1680b08b65c6Schristos break; 1681b08b65c6Schristos units[i] = sc->sc_unit; 1682b08b65c6Schristos } 1683b08b65c6Schristos mutex_exit(&ccd_lock); 1684b08b65c6Schristos } else { 1685b08b65c6Schristos units = NULL; 1686b08b65c6Schristos size = 0; 1687b08b65c6Schristos } 1688b08b65c6Schristos 1689b08b65c6Schristos node = *rnode; 1690b08b65c6Schristos node.sysctl_data = units; 1691b08b65c6Schristos node.sysctl_size = size; 1692b08b65c6Schristos 1693b08b65c6Schristos error = sysctl_lookup(SYSCTLFN_CALL(&node)); 1694b08b65c6Schristos if (units) 1695b08b65c6Schristos kmem_free(units, size); 1696b08b65c6Schristos return error; 1697b08b65c6Schristos } 1698b08b65c6Schristos 1699b08b65c6Schristos static int 1700b08b65c6Schristos ccd_info_sysctl(SYSCTLFN_ARGS) 1701b08b65c6Schristos { 1702b08b65c6Schristos struct sysctlnode node; 1703b08b65c6Schristos struct ccddiskinfo ccd; 1704b08b65c6Schristos struct ccd_softc *sc; 1705b08b65c6Schristos int unit; 1706b08b65c6Schristos 1707b08b65c6Schristos if (newp == NULL || newlen != sizeof(int)) 1708b08b65c6Schristos return EINVAL; 1709b08b65c6Schristos 1710b08b65c6Schristos unit = *(const int *)newp; 1711b08b65c6Schristos newp = NULL; 1712b08b65c6Schristos newlen = 0; 1713b08b65c6Schristos ccd.ccd_ndisks = ~0; 1714b08b65c6Schristos mutex_enter(&ccd_lock); 1715b08b65c6Schristos LIST_FOREACH(sc, &ccds, sc_link) { 1716b08b65c6Schristos if (sc->sc_unit == unit) { 1717b08b65c6Schristos ccd.ccd_ileave = sc->sc_ileave; 1718b08b65c6Schristos ccd.ccd_size = sc->sc_size; 1719b08b65c6Schristos ccd.ccd_ndisks = sc->sc_nccdisks; 1720b08b65c6Schristos ccd.ccd_flags = sc->sc_flags; 1721b08b65c6Schristos break; 1722b08b65c6Schristos } 1723b08b65c6Schristos } 1724b08b65c6Schristos mutex_exit(&ccd_lock); 1725b08b65c6Schristos 1726b08b65c6Schristos if (ccd.ccd_ndisks == ~0) 1727b08b65c6Schristos return ENOENT; 1728b08b65c6Schristos 1729b08b65c6Schristos node = *rnode; 1730b08b65c6Schristos node.sysctl_data = &ccd; 1731b08b65c6Schristos node.sysctl_size = sizeof(ccd); 1732b08b65c6Schristos 1733b08b65c6Schristos return sysctl_lookup(SYSCTLFN_CALL(&node)); 1734b08b65c6Schristos } 1735b08b65c6Schristos 1736b08b65c6Schristos static int 1737b08b65c6Schristos ccd_components_sysctl(SYSCTLFN_ARGS) 1738b08b65c6Schristos { 1739b08b65c6Schristos struct sysctlnode node; 1740b08b65c6Schristos int error, unit; 1741b08b65c6Schristos size_t size; 1742b08b65c6Schristos char *names, *p, *ep; 1743b08b65c6Schristos struct ccd_softc *sc; 1744b08b65c6Schristos 1745b08b65c6Schristos if (newp == NULL || newlen != sizeof(int)) 1746b08b65c6Schristos return EINVAL; 1747b08b65c6Schristos 1748b08b65c6Schristos size = 0; 1749b08b65c6Schristos unit = *(const int *)newp; 1750b08b65c6Schristos newp = NULL; 1751b08b65c6Schristos newlen = 0; 1752b08b65c6Schristos mutex_enter(&ccd_lock); 1753b08b65c6Schristos LIST_FOREACH(sc, &ccds, sc_link) 1754b08b65c6Schristos if (sc->sc_unit == unit) { 1755b08b65c6Schristos for (size_t i = 0; i < sc->sc_nccdisks; i++) 1756b08b65c6Schristos size += strlen(sc->sc_cinfo[i].ci_path) + 1; 1757b08b65c6Schristos break; 1758b08b65c6Schristos } 1759b08b65c6Schristos mutex_exit(&ccd_lock); 1760b08b65c6Schristos 1761b08b65c6Schristos if (size == 0) 1762b08b65c6Schristos return ENOENT; 1763b08b65c6Schristos names = kmem_zalloc(size, KM_SLEEP); 1764b08b65c6Schristos if (names == NULL) 1765b08b65c6Schristos return ENOMEM; 1766b08b65c6Schristos 1767b08b65c6Schristos p = names; 1768b08b65c6Schristos ep = names + size; 1769b08b65c6Schristos mutex_enter(&ccd_lock); 1770b08b65c6Schristos LIST_FOREACH(sc, &ccds, sc_link) 1771b08b65c6Schristos if (sc->sc_unit == unit) { 1772b08b65c6Schristos for (size_t i = 0; i < sc->sc_nccdisks; i++) { 1773b08b65c6Schristos char *d = sc->sc_cinfo[i].ci_path; 1774b08b65c6Schristos while (p < ep && (*p++ = *d++) != '\0') 1775b08b65c6Schristos continue; 1776b08b65c6Schristos } 1777b08b65c6Schristos break; 1778b08b65c6Schristos } 1779b08b65c6Schristos mutex_exit(&ccd_lock); 1780b08b65c6Schristos 1781b08b65c6Schristos node = *rnode; 1782b08b65c6Schristos node.sysctl_data = names; 1783b08b65c6Schristos node.sysctl_size = ep - names; 1784b08b65c6Schristos 1785b08b65c6Schristos error = sysctl_lookup(SYSCTLFN_CALL(&node)); 1786b08b65c6Schristos kmem_free(names, size); 1787b08b65c6Schristos return error; 1788b08b65c6Schristos } 1789b08b65c6Schristos 1790b08b65c6Schristos SYSCTL_SETUP(sysctl_kern_ccd_setup, "sysctl kern.ccd subtree setup") 1791b08b65c6Schristos { 1792b08b65c6Schristos const struct sysctlnode *node = NULL; 1793b08b65c6Schristos 1794b08b65c6Schristos sysctl_createv(clog, 0, NULL, &node, 1795b08b65c6Schristos CTLFLAG_PERMANENT, 1796b08b65c6Schristos CTLTYPE_NODE, "ccd", 1797b08b65c6Schristos SYSCTL_DESCR("ConCatenated Disk state"), 1798b08b65c6Schristos NULL, 0, NULL, 0, 1799b08b65c6Schristos CTL_KERN, CTL_CREATE, CTL_EOL); 1800b08b65c6Schristos 1801b08b65c6Schristos if (node == NULL) 1802b08b65c6Schristos return; 1803b08b65c6Schristos 1804b08b65c6Schristos sysctl_createv(clog, 0, &node, NULL, 1805b08b65c6Schristos CTLFLAG_PERMANENT | CTLFLAG_READONLY, 1806b08b65c6Schristos CTLTYPE_STRUCT, "units", 1807b08b65c6Schristos SYSCTL_DESCR("List of ccd unit numbers"), 1808b08b65c6Schristos ccd_units_sysctl, 0, NULL, 0, 1809b08b65c6Schristos CTL_CREATE, CTL_EOL); 1810b08b65c6Schristos sysctl_createv(clog, 0, &node, NULL, 1811b08b65c6Schristos CTLFLAG_PERMANENT | CTLFLAG_READWRITE, 1812b08b65c6Schristos CTLTYPE_STRUCT, "info", 1813b08b65c6Schristos SYSCTL_DESCR("Information about a CCD unit"), 1814b08b65c6Schristos ccd_info_sysctl, 0, NULL, 0, 1815b08b65c6Schristos CTL_CREATE, CTL_EOL); 1816b08b65c6Schristos sysctl_createv(clog, 0, &node, NULL, 1817b08b65c6Schristos CTLFLAG_PERMANENT | CTLFLAG_READWRITE, 1818b08b65c6Schristos CTLTYPE_STRUCT, "components", 1819b08b65c6Schristos SYSCTL_DESCR("Information about CCD components"), 1820b08b65c6Schristos ccd_components_sysctl, 0, NULL, 0, 1821b08b65c6Schristos CTL_CREATE, CTL_EOL); 1822b08b65c6Schristos } 1823