1a56bb8a5SSatoshi Asami /* 23b1746dfSPoul-Henning Kamp * Copyright (c) 2003 Poul-Henning Kamp. 3a56bb8a5SSatoshi Asami * Copyright (c) 1995 Jason R. Thorpe. 4a56bb8a5SSatoshi Asami * Copyright (c) 1990, 1993 5a56bb8a5SSatoshi Asami * The Regents of the University of California. All rights reserved. 63b1746dfSPoul-Henning Kamp * All rights reserved. 73b1746dfSPoul-Henning Kamp * Copyright (c) 1988 University of Utah. 8a56bb8a5SSatoshi Asami * 9a56bb8a5SSatoshi Asami * This code is derived from software contributed to Berkeley by 10a56bb8a5SSatoshi Asami * the Systems Programming Group of the University of Utah Computer 11a56bb8a5SSatoshi Asami * Science Department. 12a56bb8a5SSatoshi Asami * 13a56bb8a5SSatoshi Asami * Redistribution and use in source and binary forms, with or without 14a56bb8a5SSatoshi Asami * modification, are permitted provided that the following conditions 15a56bb8a5SSatoshi Asami * are met: 16a56bb8a5SSatoshi Asami * 1. Redistributions of source code must retain the above copyright 17a56bb8a5SSatoshi Asami * notice, this list of conditions and the following disclaimer. 18a56bb8a5SSatoshi Asami * 2. Redistributions in binary form must reproduce the above copyright 19a56bb8a5SSatoshi Asami * notice, this list of conditions and the following disclaimer in the 20a56bb8a5SSatoshi Asami * documentation and/or other materials provided with the distribution. 21a56bb8a5SSatoshi Asami * 3. All advertising materials mentioning features or use of this software 22a56bb8a5SSatoshi Asami * must display the following acknowledgement: 233b1746dfSPoul-Henning Kamp * This product includes software developed for the NetBSD Project 243b1746dfSPoul-Henning Kamp * by Jason R. Thorpe. 253b1746dfSPoul-Henning Kamp * 4. The names of the authors may not be used to endorse or promote products 263b1746dfSPoul-Henning Kamp * derived from this software without specific prior written permission. 27a56bb8a5SSatoshi Asami * 283b1746dfSPoul-Henning Kamp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 293b1746dfSPoul-Henning Kamp * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 303b1746dfSPoul-Henning Kamp * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 313b1746dfSPoul-Henning Kamp * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 323b1746dfSPoul-Henning Kamp * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 333b1746dfSPoul-Henning Kamp * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 343b1746dfSPoul-Henning Kamp * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 353b1746dfSPoul-Henning Kamp * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 363b1746dfSPoul-Henning Kamp * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 37a56bb8a5SSatoshi Asami * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 38a56bb8a5SSatoshi Asami * SUCH DAMAGE. 39a56bb8a5SSatoshi Asami * 40a56bb8a5SSatoshi Asami * Dynamic configuration and disklabel support by: 41a56bb8a5SSatoshi Asami * Jason R. Thorpe <thorpej@nas.nasa.gov> 42a56bb8a5SSatoshi Asami * Numerical Aerodynamic Simulation Facility 43a56bb8a5SSatoshi Asami * Mail Stop 258-6 44a56bb8a5SSatoshi Asami * NASA Ames Research Center 45a56bb8a5SSatoshi Asami * Moffett Field, CA 94035 463b1746dfSPoul-Henning Kamp * 473b1746dfSPoul-Henning Kamp * from: Utah $Hdr: cd.c 1.6 90/11/28$ 483b1746dfSPoul-Henning Kamp * 493b1746dfSPoul-Henning Kamp * @(#)cd.c 8.2 (Berkeley) 11/16/93 503b1746dfSPoul-Henning Kamp * 513b1746dfSPoul-Henning Kamp * $NetBSD: ccd.c,v 1.22 1995/12/08 19:13:26 thorpej Exp $ 523b1746dfSPoul-Henning Kamp * 533b1746dfSPoul-Henning Kamp * $FreeBSD$ 54a56bb8a5SSatoshi Asami */ 55a56bb8a5SSatoshi Asami 56a56bb8a5SSatoshi Asami #include <sys/param.h> 57a56bb8a5SSatoshi Asami #include <sys/systm.h> 58e2a13e8cSSatoshi Asami #include <sys/kernel.h> 59b7b98418SPeter Wemm #include <sys/module.h> 60a56bb8a5SSatoshi Asami #include <sys/proc.h> 619626b608SPoul-Henning Kamp #include <sys/bio.h> 62a56bb8a5SSatoshi Asami #include <sys/malloc.h> 63a56bb8a5SSatoshi Asami #include <sys/namei.h> 64a56bb8a5SSatoshi Asami #include <sys/conf.h> 65a56bb8a5SSatoshi Asami #include <sys/stat.h> 662dd527b3SPoul-Henning Kamp #include <sys/disk.h> 67a56bb8a5SSatoshi Asami #include <sys/fcntl.h> 68a56bb8a5SSatoshi Asami #include <sys/vnode.h> 69189337d8SPoul-Henning Kamp #include <geom/geom.h> 70891619a6SPoul-Henning Kamp #include <geom/geom_disk.h> 71a56bb8a5SSatoshi Asami 72d8594dfbSSatoshi Asami #include <sys/ccdvar.h> 73a56bb8a5SSatoshi Asami 740f557e0aSPoul-Henning Kamp /* 750f557e0aSPoul-Henning Kamp * Component info table. 760f557e0aSPoul-Henning Kamp * Describes a single component of a concatenated disk. 770f557e0aSPoul-Henning Kamp */ 780f557e0aSPoul-Henning Kamp struct ccdcinfo { 790f557e0aSPoul-Henning Kamp struct vnode *ci_vp; /* device's vnode */ 800f557e0aSPoul-Henning Kamp dev_t ci_dev; /* XXX: device's dev_t */ 810f557e0aSPoul-Henning Kamp size_t ci_size; /* size */ 820f557e0aSPoul-Henning Kamp char *ci_path; /* path to component */ 830f557e0aSPoul-Henning Kamp size_t ci_pathlen; /* length of component path */ 840f557e0aSPoul-Henning Kamp }; 850f557e0aSPoul-Henning Kamp 860f557e0aSPoul-Henning Kamp /* 870f557e0aSPoul-Henning Kamp * Interleave description table. 880f557e0aSPoul-Henning Kamp * Computed at boot time to speed irregular-interleave lookups. 890f557e0aSPoul-Henning Kamp * The idea is that we interleave in "groups". First we interleave 900f557e0aSPoul-Henning Kamp * evenly over all component disks up to the size of the smallest 910f557e0aSPoul-Henning Kamp * component (the first group), then we interleave evenly over all 920f557e0aSPoul-Henning Kamp * remaining disks up to the size of the next-smallest (second group), 930f557e0aSPoul-Henning Kamp * and so on. 940f557e0aSPoul-Henning Kamp * 950f557e0aSPoul-Henning Kamp * Each table entry describes the interleave characteristics of one 960f557e0aSPoul-Henning Kamp * of these groups. For example if a concatenated disk consisted of 970f557e0aSPoul-Henning Kamp * three components of 5, 3, and 7 DEV_BSIZE blocks interleaved at 980f557e0aSPoul-Henning Kamp * DEV_BSIZE (1), the table would have three entries: 990f557e0aSPoul-Henning Kamp * 1000f557e0aSPoul-Henning Kamp * ndisk startblk startoff dev 1010f557e0aSPoul-Henning Kamp * 3 0 0 0, 1, 2 1020f557e0aSPoul-Henning Kamp * 2 9 3 0, 2 1030f557e0aSPoul-Henning Kamp * 1 13 5 2 1040f557e0aSPoul-Henning Kamp * 0 - - - 1050f557e0aSPoul-Henning Kamp * 1060f557e0aSPoul-Henning Kamp * which says that the first nine blocks (0-8) are interleaved over 1070f557e0aSPoul-Henning Kamp * 3 disks (0, 1, 2) starting at block offset 0 on any component disk, 1080f557e0aSPoul-Henning Kamp * the next 4 blocks (9-12) are interleaved over 2 disks (0, 2) starting 1090f557e0aSPoul-Henning Kamp * at component block 3, and the remaining blocks (13-14) are on disk 1100f557e0aSPoul-Henning Kamp * 2 starting at offset 5. 1110f557e0aSPoul-Henning Kamp */ 1120f557e0aSPoul-Henning Kamp struct ccdiinfo { 1130f557e0aSPoul-Henning Kamp int ii_ndisk; /* # of disks range is interleaved over */ 1140f557e0aSPoul-Henning Kamp daddr_t ii_startblk; /* starting scaled block # for range */ 1150f557e0aSPoul-Henning Kamp daddr_t ii_startoff; /* starting component offset (block #) */ 1160f557e0aSPoul-Henning Kamp int *ii_index; /* ordered list of components in range */ 1170f557e0aSPoul-Henning Kamp }; 1180f557e0aSPoul-Henning Kamp 1190f557e0aSPoul-Henning Kamp /* 1200f557e0aSPoul-Henning Kamp * Concatenated disk pseudo-geometry information. 1210f557e0aSPoul-Henning Kamp */ 1220f557e0aSPoul-Henning Kamp struct ccdgeom { 1230f557e0aSPoul-Henning Kamp u_int32_t ccg_secsize; /* # bytes per sector */ 1240f557e0aSPoul-Henning Kamp u_int32_t ccg_nsectors; /* # data sectors per track */ 1250f557e0aSPoul-Henning Kamp u_int32_t ccg_ntracks; /* # tracks per cylinder */ 1260f557e0aSPoul-Henning Kamp u_int32_t ccg_ncylinders; /* # cylinders per unit */ 1270f557e0aSPoul-Henning Kamp }; 1280f557e0aSPoul-Henning Kamp 1290f557e0aSPoul-Henning Kamp 1300f557e0aSPoul-Henning Kamp /* 1310f557e0aSPoul-Henning Kamp * A concatenated disk is described by this structure. 1320f557e0aSPoul-Henning Kamp */ 1330f557e0aSPoul-Henning Kamp struct ccd_s { 1340f557e0aSPoul-Henning Kamp LIST_ENTRY(ccd_s) list; 1350f557e0aSPoul-Henning Kamp 1360f557e0aSPoul-Henning Kamp int sc_unit; /* logical unit number */ 1370f557e0aSPoul-Henning Kamp struct vnode **sc_vpp; /* array of component vnodes */ 1380f557e0aSPoul-Henning Kamp int sc_flags; /* flags */ 1390f557e0aSPoul-Henning Kamp int sc_cflags; /* configuration flags */ 1400f557e0aSPoul-Henning Kamp size_t sc_size; /* size of ccd */ 1410f557e0aSPoul-Henning Kamp int sc_ileave; /* interleave */ 1420f557e0aSPoul-Henning Kamp u_int sc_nccdisks; /* number of components */ 1430f557e0aSPoul-Henning Kamp #define CCD_MAXNDISKS 65536 1440f557e0aSPoul-Henning Kamp struct ccdcinfo *sc_cinfo; /* component info */ 1450f557e0aSPoul-Henning Kamp struct ccdiinfo *sc_itable; /* interleave table */ 1460f557e0aSPoul-Henning Kamp struct ccdgeom sc_geom; /* pseudo geometry info */ 1470f557e0aSPoul-Henning Kamp int sc_pick; /* side of mirror picked */ 1480f557e0aSPoul-Henning Kamp daddr_t sc_blk[2]; /* mirror localization */ 1490f557e0aSPoul-Henning Kamp struct disk *sc_disk; 1500f557e0aSPoul-Henning Kamp struct cdev *__remove00; /* XXX: remove when convenient */ 1510f557e0aSPoul-Henning Kamp }; 1520f557e0aSPoul-Henning Kamp 15301706d20SPoul-Henning Kamp MALLOC_DEFINE(M_CCD, "CCD driver", "Concatenated Disk driver"); 15401706d20SPoul-Henning Kamp 155e7322872SSatoshi Asami /* 156e7322872SSatoshi Asami This is how mirroring works (only writes are special): 157e7322872SSatoshi Asami 158e7322872SSatoshi Asami When initiating a write, ccdbuffer() returns two "struct ccdbuf *"s 159e7322872SSatoshi Asami linked together by the cb_mirror field. "cb_pflags & 160e7322872SSatoshi Asami CCDPF_MIRROR_DONE" is set to 0 on both of them. 161e7322872SSatoshi Asami 162e7322872SSatoshi Asami When a component returns to ccdiodone(), it checks if "cb_pflags & 163e7322872SSatoshi Asami CCDPF_MIRROR_DONE" is set or not. If not, it sets the partner's 164e7322872SSatoshi Asami flag and returns. If it is, it means its partner has already 165e7322872SSatoshi Asami returned, so it will go to the regular cleanup. 166e7322872SSatoshi Asami 167e7322872SSatoshi Asami */ 168e7322872SSatoshi Asami 169a56bb8a5SSatoshi Asami struct ccdbuf { 1709d7f7369SPoul-Henning Kamp struct bio cb_buf; /* new I/O buf */ 1719d7f7369SPoul-Henning Kamp struct bio *cb_obp; /* ptr. to original I/O buf */ 1721464240eSMatthew Dillon struct ccdbuf *cb_freenext; /* free list link */ 1730f76d6d8SPoul-Henning Kamp struct ccd_s *cb_softc; 174a56bb8a5SSatoshi Asami int cb_comp; /* target component */ 175e7322872SSatoshi Asami int cb_pflags; /* mirror/parity status flag */ 176e7322872SSatoshi Asami struct ccdbuf *cb_mirror; /* mirror counterpart */ 177a56bb8a5SSatoshi Asami }; 178a56bb8a5SSatoshi Asami 179e7322872SSatoshi Asami /* bits in cb_pflags */ 180e7322872SSatoshi Asami #define CCDPF_MIRROR_DONE 1 /* if set, mirror counterpart is done */ 181e7322872SSatoshi Asami 18201706d20SPoul-Henning Kamp /* convinient macros for often-used statements */ 18301706d20SPoul-Henning Kamp #define IS_ALLOCATED(unit) (ccdfind(unit) != NULL) 18401706d20SPoul-Henning Kamp #define IS_INITED(cs) (((cs)->sc_flags & CCDF_INITED) != 0) 18501706d20SPoul-Henning Kamp 186ddbf51afSPoul-Henning Kamp static dev_t ccdctldev; 187ddbf51afSPoul-Henning Kamp 188ad3467e1SPoul-Henning Kamp static disk_strategy_t ccdstrategy; 1890f76d6d8SPoul-Henning Kamp static d_ioctl_t ccdctlioctl; 190d8594dfbSSatoshi Asami 1911464240eSMatthew Dillon #define NCCDFREEHIWAT 16 1921464240eSMatthew Dillon 193e2a13e8cSSatoshi Asami #define CDEV_MAJOR 74 194a56bb8a5SSatoshi Asami 1950f76d6d8SPoul-Henning Kamp static struct cdevsw ccdctl_cdevsw = { 1967ac40f5fSPoul-Henning Kamp .d_open = nullopen, 1977ac40f5fSPoul-Henning Kamp .d_close = nullclose, 1987ac40f5fSPoul-Henning Kamp .d_ioctl = ccdctlioctl, 1997ac40f5fSPoul-Henning Kamp .d_name = "ccdctl", 2007ac40f5fSPoul-Henning Kamp .d_maj = CDEV_MAJOR, 2010f76d6d8SPoul-Henning Kamp }; 2020f76d6d8SPoul-Henning Kamp 2030f76d6d8SPoul-Henning Kamp static LIST_HEAD(, ccd_s) ccd_softc_list = 2040f76d6d8SPoul-Henning Kamp LIST_HEAD_INITIALIZER(&ccd_softc_list); 20501706d20SPoul-Henning Kamp 20601706d20SPoul-Henning Kamp static struct ccd_s *ccdfind(int); 20701706d20SPoul-Henning Kamp static struct ccd_s *ccdnew(int); 2080f76d6d8SPoul-Henning Kamp static int ccddestroy(struct ccd_s *); 209e2a13e8cSSatoshi Asami 210b7b98418SPeter Wemm /* called during module initialization */ 21101706d20SPoul-Henning Kamp static void ccdattach(void); 21201706d20SPoul-Henning Kamp static int ccd_modevent(module_t, int, void *); 213a56bb8a5SSatoshi Asami 214a56bb8a5SSatoshi Asami /* called by biodone() at interrupt time */ 21501706d20SPoul-Henning Kamp static void ccdiodone(struct bio *bp); 216a56bb8a5SSatoshi Asami 21701706d20SPoul-Henning Kamp static void ccdstart(struct ccd_s *, struct bio *); 21801706d20SPoul-Henning Kamp static void ccdinterleave(struct ccd_s *, int); 219b40ce416SJulian Elischer static int ccdinit(struct ccd_s *, char **, struct thread *); 220b40ce416SJulian Elischer static int ccdlookup(char *, struct thread *p, struct vnode **); 2210f76d6d8SPoul-Henning Kamp static int ccdbuffer(struct ccdbuf **ret, struct ccd_s *, 22201706d20SPoul-Henning Kamp struct bio *, daddr_t, caddr_t, long); 22301706d20SPoul-Henning Kamp static int ccdlock(struct ccd_s *); 22401706d20SPoul-Henning Kamp static void ccdunlock(struct ccd_s *); 225a56bb8a5SSatoshi Asami 226a56bb8a5SSatoshi Asami 2271464240eSMatthew Dillon /* 2280d88ef07SSatoshi Asami * Number of blocks to untouched in front of a component partition. 2290d88ef07SSatoshi Asami * This is to avoid violating its disklabel area when it starts at the 2300d88ef07SSatoshi Asami * beginning of the slice. 2310d88ef07SSatoshi Asami */ 2321af0e025SSatoshi Asami #if !defined(CCD_OFFSET) 2330d88ef07SSatoshi Asami #define CCD_OFFSET 16 2341af0e025SSatoshi Asami #endif 2350d88ef07SSatoshi Asami 23601706d20SPoul-Henning Kamp static struct ccd_s * 23701706d20SPoul-Henning Kamp ccdfind(int unit) 23801706d20SPoul-Henning Kamp { 23901706d20SPoul-Henning Kamp struct ccd_s *sc = NULL; 24001706d20SPoul-Henning Kamp 24101706d20SPoul-Henning Kamp /* XXX: LOCK(unique unit numbers) */ 24201706d20SPoul-Henning Kamp LIST_FOREACH(sc, &ccd_softc_list, list) { 24301706d20SPoul-Henning Kamp if (sc->sc_unit == unit) 24401706d20SPoul-Henning Kamp break; 24501706d20SPoul-Henning Kamp } 24601706d20SPoul-Henning Kamp /* XXX: UNLOCK(unique unit numbers) */ 24701706d20SPoul-Henning Kamp return ((sc == NULL) || (sc->sc_unit != unit) ? NULL : sc); 24801706d20SPoul-Henning Kamp } 24901706d20SPoul-Henning Kamp 25001706d20SPoul-Henning Kamp static struct ccd_s * 25101706d20SPoul-Henning Kamp ccdnew(int unit) 25201706d20SPoul-Henning Kamp { 25301706d20SPoul-Henning Kamp struct ccd_s *sc; 25401706d20SPoul-Henning Kamp 25501706d20SPoul-Henning Kamp /* XXX: LOCK(unique unit numbers) */ 2560f76d6d8SPoul-Henning Kamp if (IS_ALLOCATED(unit) || unit > 32) 25701706d20SPoul-Henning Kamp return (NULL); 25801706d20SPoul-Henning Kamp 259a163d034SWarner Losh MALLOC(sc, struct ccd_s *, sizeof(*sc), M_CCD, M_WAITOK | M_ZERO); 26001706d20SPoul-Henning Kamp sc->sc_unit = unit; 26101706d20SPoul-Henning Kamp LIST_INSERT_HEAD(&ccd_softc_list, sc, list); 26201706d20SPoul-Henning Kamp /* XXX: UNLOCK(unique unit numbers) */ 26301706d20SPoul-Henning Kamp return (sc); 26401706d20SPoul-Henning Kamp } 26501706d20SPoul-Henning Kamp 26601706d20SPoul-Henning Kamp static int 2670f76d6d8SPoul-Henning Kamp ccddestroy(struct ccd_s *sc) 26801706d20SPoul-Henning Kamp { 26901706d20SPoul-Henning Kamp 27001706d20SPoul-Henning Kamp /* XXX: LOCK(unique unit numbers) */ 27101706d20SPoul-Henning Kamp LIST_REMOVE(sc, list); 27201706d20SPoul-Henning Kamp /* XXX: UNLOCK(unique unit numbers) */ 27301706d20SPoul-Henning Kamp FREE(sc, M_CCD); 27401706d20SPoul-Henning Kamp return (0); 27501706d20SPoul-Henning Kamp } 27601706d20SPoul-Henning Kamp 2770d88ef07SSatoshi Asami /* 278a56bb8a5SSatoshi Asami * Called by main() during pseudo-device attachment. All we need 27901706d20SPoul-Henning Kamp * to do is to add devsw entries. 280a56bb8a5SSatoshi Asami */ 281e2738b4fSPoul-Henning Kamp static void 282b7b98418SPeter Wemm ccdattach() 283a56bb8a5SSatoshi Asami { 284a56bb8a5SSatoshi Asami 2850f76d6d8SPoul-Henning Kamp ccdctldev = make_dev(&ccdctl_cdevsw, 0xffff00ff, 286ddbf51afSPoul-Henning Kamp UID_ROOT, GID_OPERATOR, 0640, "ccd.ctl"); 287ddbf51afSPoul-Henning Kamp ccdctldev->si_drv1 = ccdctldev; 288b7b98418SPeter Wemm } 289d8594dfbSSatoshi Asami 290b7b98418SPeter Wemm static int 29101706d20SPoul-Henning Kamp ccd_modevent(module_t mod, int type, void *data) 292b7b98418SPeter Wemm { 293b7b98418SPeter Wemm int error = 0; 294b7b98418SPeter Wemm 295b7b98418SPeter Wemm switch (type) { 296b7b98418SPeter Wemm case MOD_LOAD: 297b7b98418SPeter Wemm ccdattach(); 298b7b98418SPeter Wemm break; 299b7b98418SPeter Wemm 300b7b98418SPeter Wemm case MOD_UNLOAD: 301b7b98418SPeter Wemm printf("ccd0: Unload not supported!\n"); 302b7b98418SPeter Wemm error = EOPNOTSUPP; 303b7b98418SPeter Wemm break; 304b7b98418SPeter Wemm 30555a13f7dSIan Dowse case MOD_SHUTDOWN: 306b7b98418SPeter Wemm break; 30755a13f7dSIan Dowse 30855a13f7dSIan Dowse default: 30955a13f7dSIan Dowse error = EOPNOTSUPP; 310e2a13e8cSSatoshi Asami } 311b7b98418SPeter Wemm return (error); 312e2a13e8cSSatoshi Asami } 313b7b98418SPeter Wemm 314d53dedeeSPoul-Henning Kamp DEV_MODULE(ccd, ccd_modevent, NULL); 315a56bb8a5SSatoshi Asami 316a56bb8a5SSatoshi Asami static int 317b40ce416SJulian Elischer ccdinit(struct ccd_s *cs, char **cpaths, struct thread *td) 318a56bb8a5SSatoshi Asami { 3191464240eSMatthew Dillon struct ccdcinfo *ci = NULL; /* XXX */ 3201464240eSMatthew Dillon size_t size; 3211464240eSMatthew Dillon int ix; 322a56bb8a5SSatoshi Asami struct vnode *vp; 323a56bb8a5SSatoshi Asami size_t minsize; 324a56bb8a5SSatoshi Asami int maxsecsize; 325a56bb8a5SSatoshi Asami struct ccdgeom *ccg = &cs->sc_geom; 326e3f4d3b5SPoul-Henning Kamp char *tmppath = NULL; 3271464240eSMatthew Dillon int error = 0; 328ffee6e99SPoul-Henning Kamp off_t mediasize; 329ffee6e99SPoul-Henning Kamp u_int sectorsize; 330a56bb8a5SSatoshi Asami 331a56bb8a5SSatoshi Asami 332a56bb8a5SSatoshi Asami cs->sc_size = 0; 333a56bb8a5SSatoshi Asami 334a56bb8a5SSatoshi Asami /* Allocate space for the component info. */ 335a56bb8a5SSatoshi Asami cs->sc_cinfo = malloc(cs->sc_nccdisks * sizeof(struct ccdcinfo), 336a163d034SWarner Losh M_CCD, M_WAITOK); 337a56bb8a5SSatoshi Asami 338a56bb8a5SSatoshi Asami /* 339a56bb8a5SSatoshi Asami * Verify that each component piece exists and record 340a56bb8a5SSatoshi Asami * relevant information about it. 341a56bb8a5SSatoshi Asami */ 342a56bb8a5SSatoshi Asami maxsecsize = 0; 343a56bb8a5SSatoshi Asami minsize = 0; 344a163d034SWarner Losh tmppath = malloc(MAXPATHLEN, M_CCD, M_WAITOK); 345a56bb8a5SSatoshi Asami for (ix = 0; ix < cs->sc_nccdisks; ix++) { 34601706d20SPoul-Henning Kamp vp = cs->sc_vpp[ix]; 347a56bb8a5SSatoshi Asami ci = &cs->sc_cinfo[ix]; 348a56bb8a5SSatoshi Asami ci->ci_vp = vp; 349a56bb8a5SSatoshi Asami 350a56bb8a5SSatoshi Asami /* 351a56bb8a5SSatoshi Asami * Copy in the pathname of the component. 352a56bb8a5SSatoshi Asami */ 353b4e36adfSMatthew Dillon if ((error = copyinstr(cpaths[ix], tmppath, 354b4e36adfSMatthew Dillon MAXPATHLEN, &ci->ci_pathlen)) != 0) { 3551464240eSMatthew Dillon goto fail; 356a56bb8a5SSatoshi Asami } 357a163d034SWarner Losh ci->ci_path = malloc(ci->ci_pathlen, M_CCD, M_WAITOK); 358a56bb8a5SSatoshi Asami bcopy(tmppath, ci->ci_path, ci->ci_pathlen); 359a56bb8a5SSatoshi Asami 360684adedeSPoul-Henning Kamp ci->ci_dev = vn_todev(vp); 361a56bb8a5SSatoshi Asami 362a56bb8a5SSatoshi Asami /* 363a56bb8a5SSatoshi Asami * Get partition information for the component. 364a56bb8a5SSatoshi Asami */ 365ffee6e99SPoul-Henning Kamp error = VOP_IOCTL(vp, DIOCGMEDIASIZE, (caddr_t)&mediasize, 366ffee6e99SPoul-Henning Kamp FREAD, td->td_ucred, td); 367ffee6e99SPoul-Henning Kamp if (error != 0) { 3681464240eSMatthew Dillon goto fail; 369a56bb8a5SSatoshi Asami } 370ffee6e99SPoul-Henning Kamp /* 371ffee6e99SPoul-Henning Kamp * Get partition information for the component. 372ffee6e99SPoul-Henning Kamp */ 373ffee6e99SPoul-Henning Kamp error = VOP_IOCTL(vp, DIOCGSECTORSIZE, (caddr_t)§orsize, 374ffee6e99SPoul-Henning Kamp FREAD, td->td_ucred, td); 375ffee6e99SPoul-Henning Kamp if (error != 0) { 3761464240eSMatthew Dillon goto fail; 377a56bb8a5SSatoshi Asami } 378ffee6e99SPoul-Henning Kamp if (sectorsize > maxsecsize) 379ffee6e99SPoul-Henning Kamp maxsecsize = sectorsize; 380ffee6e99SPoul-Henning Kamp size = mediasize / DEV_BSIZE - CCD_OFFSET; 381a56bb8a5SSatoshi Asami 382a56bb8a5SSatoshi Asami /* 383a56bb8a5SSatoshi Asami * Calculate the size, truncating to an interleave 384a56bb8a5SSatoshi Asami * boundary if necessary. 385a56bb8a5SSatoshi Asami */ 386a56bb8a5SSatoshi Asami 387a56bb8a5SSatoshi Asami if (cs->sc_ileave > 1) 388a56bb8a5SSatoshi Asami size -= size % cs->sc_ileave; 389a56bb8a5SSatoshi Asami 390a56bb8a5SSatoshi Asami if (size == 0) { 3911464240eSMatthew Dillon error = ENODEV; 3921464240eSMatthew Dillon goto fail; 393a56bb8a5SSatoshi Asami } 394a56bb8a5SSatoshi Asami 395a56bb8a5SSatoshi Asami if (minsize == 0 || size < minsize) 396a56bb8a5SSatoshi Asami minsize = size; 397a56bb8a5SSatoshi Asami ci->ci_size = size; 398a56bb8a5SSatoshi Asami cs->sc_size += size; 399a56bb8a5SSatoshi Asami } 400a56bb8a5SSatoshi Asami 401b51ea356SPoul-Henning Kamp free(tmppath, M_CCD); 402e3f4d3b5SPoul-Henning Kamp tmppath = NULL; 403e3f4d3b5SPoul-Henning Kamp 404a56bb8a5SSatoshi Asami /* 405a56bb8a5SSatoshi Asami * Don't allow the interleave to be smaller than 406a56bb8a5SSatoshi Asami * the biggest component sector. 407a56bb8a5SSatoshi Asami */ 408a56bb8a5SSatoshi Asami if ((cs->sc_ileave > 0) && 409a56bb8a5SSatoshi Asami (cs->sc_ileave < (maxsecsize / DEV_BSIZE))) { 4101464240eSMatthew Dillon error = EINVAL; 4111464240eSMatthew Dillon goto fail; 412a56bb8a5SSatoshi Asami } 413a56bb8a5SSatoshi Asami 414a56bb8a5SSatoshi Asami /* 415a56bb8a5SSatoshi Asami * If uniform interleave is desired set all sizes to that of 4161464240eSMatthew Dillon * the smallest component. This will guarentee that a single 4171464240eSMatthew Dillon * interleave table is generated. 4181464240eSMatthew Dillon * 4191464240eSMatthew Dillon * Lost space must be taken into account when calculating the 4201464240eSMatthew Dillon * overall size. Half the space is lost when CCDF_MIRROR is 421ddbf51afSPoul-Henning Kamp * specified. 422a56bb8a5SSatoshi Asami */ 42301706d20SPoul-Henning Kamp if (cs->sc_flags & CCDF_UNIFORM) { 424a56bb8a5SSatoshi Asami for (ci = cs->sc_cinfo; 4251464240eSMatthew Dillon ci < &cs->sc_cinfo[cs->sc_nccdisks]; ci++) { 426a56bb8a5SSatoshi Asami ci->ci_size = minsize; 4271464240eSMatthew Dillon } 42801706d20SPoul-Henning Kamp if (cs->sc_flags & CCDF_MIRROR) { 42934f35216SSatoshi Asami /* 43034f35216SSatoshi Asami * Check to see if an even number of components 4311464240eSMatthew Dillon * have been specified. The interleave must also 4321464240eSMatthew Dillon * be non-zero in order for us to be able to 4331464240eSMatthew Dillon * guarentee the topology. 43434f35216SSatoshi Asami */ 43509b59204SSatoshi Asami if (cs->sc_nccdisks % 2) { 43601706d20SPoul-Henning Kamp printf("ccd%d: mirroring requires an even number of disks\n", cs->sc_unit ); 4371464240eSMatthew Dillon error = EINVAL; 4381464240eSMatthew Dillon goto fail; 43934f35216SSatoshi Asami } 4401464240eSMatthew Dillon if (cs->sc_ileave == 0) { 44101706d20SPoul-Henning Kamp printf("ccd%d: an interleave must be specified when mirroring\n", cs->sc_unit); 4421464240eSMatthew Dillon error = EINVAL; 4431464240eSMatthew Dillon goto fail; 44409b59204SSatoshi Asami } 44509b59204SSatoshi Asami cs->sc_size = (cs->sc_nccdisks/2) * minsize; 4461464240eSMatthew Dillon } else { 4471464240eSMatthew Dillon if (cs->sc_ileave == 0) { 44801706d20SPoul-Henning Kamp printf("ccd%d: an interleave must be specified when using parity\n", cs->sc_unit); 4491464240eSMatthew Dillon error = EINVAL; 4501464240eSMatthew Dillon goto fail; 4511464240eSMatthew Dillon } 452a56bb8a5SSatoshi Asami cs->sc_size = cs->sc_nccdisks * minsize; 453a56bb8a5SSatoshi Asami } 4541464240eSMatthew Dillon } 455a56bb8a5SSatoshi Asami 456a56bb8a5SSatoshi Asami /* 457a56bb8a5SSatoshi Asami * Construct the interleave table. 458a56bb8a5SSatoshi Asami */ 45901706d20SPoul-Henning Kamp ccdinterleave(cs, cs->sc_unit); 460a56bb8a5SSatoshi Asami 461a56bb8a5SSatoshi Asami /* 462a56bb8a5SSatoshi Asami * Create pseudo-geometry based on 1MB cylinders. It's 463a56bb8a5SSatoshi Asami * pretty close. 464a56bb8a5SSatoshi Asami */ 465e59f3105SSøren Schmidt ccg->ccg_secsize = maxsecsize; 466a56bb8a5SSatoshi Asami ccg->ccg_ntracks = 1; 467e322ec4cSMatthew Dillon ccg->ccg_nsectors = 1024 * 1024 / ccg->ccg_secsize; 468a56bb8a5SSatoshi Asami ccg->ccg_ncylinders = cs->sc_size / ccg->ccg_nsectors; 469a56bb8a5SSatoshi Asami 470a56bb8a5SSatoshi Asami cs->sc_flags |= CCDF_INITED; 47101706d20SPoul-Henning Kamp cs->sc_cflags = cs->sc_flags; /* So we can find out later... */ 472a56bb8a5SSatoshi Asami return (0); 4731464240eSMatthew Dillon fail: 4741464240eSMatthew Dillon while (ci > cs->sc_cinfo) { 4751464240eSMatthew Dillon ci--; 476b51ea356SPoul-Henning Kamp free(ci->ci_path, M_CCD); 4771464240eSMatthew Dillon } 478e3f4d3b5SPoul-Henning Kamp if (tmppath != NULL) 479b51ea356SPoul-Henning Kamp free(tmppath, M_CCD); 480b51ea356SPoul-Henning Kamp free(cs->sc_cinfo, M_CCD); 4810f76d6d8SPoul-Henning Kamp ccddestroy(cs); 4821464240eSMatthew Dillon return (error); 483a56bb8a5SSatoshi Asami } 484a56bb8a5SSatoshi Asami 485a56bb8a5SSatoshi Asami static void 48601706d20SPoul-Henning Kamp ccdinterleave(struct ccd_s *cs, int unit) 487a56bb8a5SSatoshi Asami { 4881464240eSMatthew Dillon struct ccdcinfo *ci, *smallci; 4891464240eSMatthew Dillon struct ccdiinfo *ii; 4901464240eSMatthew Dillon daddr_t bn, lbn; 4911464240eSMatthew Dillon int ix; 492a56bb8a5SSatoshi Asami u_long size; 493a56bb8a5SSatoshi Asami 4941464240eSMatthew Dillon 495a56bb8a5SSatoshi Asami /* 4961464240eSMatthew Dillon * Allocate an interleave table. The worst case occurs when each 4971464240eSMatthew Dillon * of N disks is of a different size, resulting in N interleave 4981464240eSMatthew Dillon * tables. 4991464240eSMatthew Dillon * 500a56bb8a5SSatoshi Asami * Chances are this is too big, but we don't care. 501a56bb8a5SSatoshi Asami */ 502a56bb8a5SSatoshi Asami size = (cs->sc_nccdisks + 1) * sizeof(struct ccdiinfo); 503b51ea356SPoul-Henning Kamp cs->sc_itable = (struct ccdiinfo *)malloc(size, M_CCD, 504a163d034SWarner Losh M_WAITOK | M_ZERO); 505a56bb8a5SSatoshi Asami 506a56bb8a5SSatoshi Asami /* 507a56bb8a5SSatoshi Asami * Trivial case: no interleave (actually interleave of disk size). 508a56bb8a5SSatoshi Asami * Each table entry represents a single component in its entirety. 5091464240eSMatthew Dillon * 510ddbf51afSPoul-Henning Kamp * An interleave of 0 may not be used with a mirror setup. 511a56bb8a5SSatoshi Asami */ 512a56bb8a5SSatoshi Asami if (cs->sc_ileave == 0) { 513a56bb8a5SSatoshi Asami bn = 0; 514a56bb8a5SSatoshi Asami ii = cs->sc_itable; 515a56bb8a5SSatoshi Asami 516a56bb8a5SSatoshi Asami for (ix = 0; ix < cs->sc_nccdisks; ix++) { 517a56bb8a5SSatoshi Asami /* Allocate space for ii_index. */ 518a163d034SWarner Losh ii->ii_index = malloc(sizeof(int), M_CCD, M_WAITOK); 519a56bb8a5SSatoshi Asami ii->ii_ndisk = 1; 520a56bb8a5SSatoshi Asami ii->ii_startblk = bn; 521a56bb8a5SSatoshi Asami ii->ii_startoff = 0; 522a56bb8a5SSatoshi Asami ii->ii_index[0] = ix; 523a56bb8a5SSatoshi Asami bn += cs->sc_cinfo[ix].ci_size; 524a56bb8a5SSatoshi Asami ii++; 525a56bb8a5SSatoshi Asami } 526a56bb8a5SSatoshi Asami ii->ii_ndisk = 0; 527a56bb8a5SSatoshi Asami return; 528a56bb8a5SSatoshi Asami } 529a56bb8a5SSatoshi Asami 530a56bb8a5SSatoshi Asami /* 531a56bb8a5SSatoshi Asami * The following isn't fast or pretty; it doesn't have to be. 532a56bb8a5SSatoshi Asami */ 533a56bb8a5SSatoshi Asami size = 0; 534a56bb8a5SSatoshi Asami bn = lbn = 0; 535a56bb8a5SSatoshi Asami for (ii = cs->sc_itable; ; ii++) { 5361464240eSMatthew Dillon /* 5371464240eSMatthew Dillon * Allocate space for ii_index. We might allocate more then 5381464240eSMatthew Dillon * we use. 5391464240eSMatthew Dillon */ 540a56bb8a5SSatoshi Asami ii->ii_index = malloc((sizeof(int) * cs->sc_nccdisks), 541a163d034SWarner Losh M_CCD, M_WAITOK); 542a56bb8a5SSatoshi Asami 543a56bb8a5SSatoshi Asami /* 544a56bb8a5SSatoshi Asami * Locate the smallest of the remaining components 545a56bb8a5SSatoshi Asami */ 546a56bb8a5SSatoshi Asami smallci = NULL; 5471464240eSMatthew Dillon for (ci = cs->sc_cinfo; ci < &cs->sc_cinfo[cs->sc_nccdisks]; 5481464240eSMatthew Dillon ci++) { 549a56bb8a5SSatoshi Asami if (ci->ci_size > size && 550a56bb8a5SSatoshi Asami (smallci == NULL || 5511464240eSMatthew Dillon ci->ci_size < smallci->ci_size)) { 552a56bb8a5SSatoshi Asami smallci = ci; 5531464240eSMatthew Dillon } 5541464240eSMatthew Dillon } 555a56bb8a5SSatoshi Asami 556a56bb8a5SSatoshi Asami /* 557a56bb8a5SSatoshi Asami * Nobody left, all done 558a56bb8a5SSatoshi Asami */ 559a56bb8a5SSatoshi Asami if (smallci == NULL) { 560a56bb8a5SSatoshi Asami ii->ii_ndisk = 0; 561e9fe7d1fSPoul-Henning Kamp free(ii->ii_index, M_CCD); 562a56bb8a5SSatoshi Asami break; 563a56bb8a5SSatoshi Asami } 564a56bb8a5SSatoshi Asami 565a56bb8a5SSatoshi Asami /* 5661464240eSMatthew Dillon * Record starting logical block using an sc_ileave blocksize. 567a56bb8a5SSatoshi Asami */ 568a56bb8a5SSatoshi Asami ii->ii_startblk = bn / cs->sc_ileave; 5691464240eSMatthew Dillon 5701464240eSMatthew Dillon /* 5711464240eSMatthew Dillon * Record starting comopnent block using an sc_ileave 5721464240eSMatthew Dillon * blocksize. This value is relative to the beginning of 5731464240eSMatthew Dillon * a component disk. 5741464240eSMatthew Dillon */ 575a56bb8a5SSatoshi Asami ii->ii_startoff = lbn; 576a56bb8a5SSatoshi Asami 577a56bb8a5SSatoshi Asami /* 578a56bb8a5SSatoshi Asami * Determine how many disks take part in this interleave 579a56bb8a5SSatoshi Asami * and record their indices. 580a56bb8a5SSatoshi Asami */ 581a56bb8a5SSatoshi Asami ix = 0; 582a56bb8a5SSatoshi Asami for (ci = cs->sc_cinfo; 5831464240eSMatthew Dillon ci < &cs->sc_cinfo[cs->sc_nccdisks]; ci++) { 5841464240eSMatthew Dillon if (ci->ci_size >= smallci->ci_size) { 585a56bb8a5SSatoshi Asami ii->ii_index[ix++] = ci - cs->sc_cinfo; 5861464240eSMatthew Dillon } 5871464240eSMatthew Dillon } 588a56bb8a5SSatoshi Asami ii->ii_ndisk = ix; 589a56bb8a5SSatoshi Asami bn += ix * (smallci->ci_size - size); 590a56bb8a5SSatoshi Asami lbn = smallci->ci_size / cs->sc_ileave; 591a56bb8a5SSatoshi Asami size = smallci->ci_size; 592a56bb8a5SSatoshi Asami } 593a56bb8a5SSatoshi Asami } 594a56bb8a5SSatoshi Asami 595e2738b4fSPoul-Henning Kamp static void 59601706d20SPoul-Henning Kamp ccdstrategy(struct bio *bp) 597a56bb8a5SSatoshi Asami { 5980f76d6d8SPoul-Henning Kamp struct ccd_s *cs; 59974427f90SMatthew Dillon int pbn; /* in sc_secsize chunks */ 60074427f90SMatthew Dillon long sz; /* in sc_secsize chunks */ 60174427f90SMatthew Dillon 602ad3467e1SPoul-Henning Kamp cs = bp->bio_disk->d_drv1; 6030f76d6d8SPoul-Henning Kamp 6049d7f7369SPoul-Henning Kamp pbn = bp->bio_blkno / (cs->sc_geom.ccg_secsize / DEV_BSIZE); 6059d7f7369SPoul-Henning Kamp sz = howmany(bp->bio_bcount, cs->sc_geom.ccg_secsize); 60674427f90SMatthew Dillon 60774427f90SMatthew Dillon /* 60874427f90SMatthew Dillon * If out of bounds return an error. If at the EOF point, 60974427f90SMatthew Dillon * simply read or write less. 61074427f90SMatthew Dillon */ 61174427f90SMatthew Dillon 61274427f90SMatthew Dillon if (pbn < 0 || pbn >= cs->sc_size) { 6139d7f7369SPoul-Henning Kamp bp->bio_resid = bp->bio_bcount; 614724682d2SPoul-Henning Kamp if (pbn != cs->sc_size) 615724682d2SPoul-Henning Kamp biofinish(bp, NULL, EINVAL); 616724682d2SPoul-Henning Kamp else 617724682d2SPoul-Henning Kamp biodone(bp); 618724682d2SPoul-Henning Kamp return; 61974427f90SMatthew Dillon } 62074427f90SMatthew Dillon 62174427f90SMatthew Dillon /* 62274427f90SMatthew Dillon * If the request crosses EOF, truncate the request. 62374427f90SMatthew Dillon */ 62474427f90SMatthew Dillon if (pbn + sz > cs->sc_size) { 6259d7f7369SPoul-Henning Kamp bp->bio_bcount = (cs->sc_size - pbn) * 62674427f90SMatthew Dillon cs->sc_geom.ccg_secsize; 62774427f90SMatthew Dillon } 628a56bb8a5SSatoshi Asami 6299d7f7369SPoul-Henning Kamp bp->bio_resid = bp->bio_bcount; 630a56bb8a5SSatoshi Asami 631a56bb8a5SSatoshi Asami /* 632a56bb8a5SSatoshi Asami * "Start" the unit. 633a56bb8a5SSatoshi Asami */ 634a56bb8a5SSatoshi Asami ccdstart(cs, bp); 635a56bb8a5SSatoshi Asami return; 636a56bb8a5SSatoshi Asami } 637a56bb8a5SSatoshi Asami 638a56bb8a5SSatoshi Asami static void 63901706d20SPoul-Henning Kamp ccdstart(struct ccd_s *cs, struct bio *bp) 640a56bb8a5SSatoshi Asami { 6411464240eSMatthew Dillon long bcount, rcount; 6420f76d6d8SPoul-Henning Kamp struct ccdbuf *cbp[2]; 643a56bb8a5SSatoshi Asami caddr_t addr; 644a56bb8a5SSatoshi Asami daddr_t bn; 6450f76d6d8SPoul-Henning Kamp int err; 64677154759SPoul-Henning Kamp int sent; 647a56bb8a5SSatoshi Asami 648a56bb8a5SSatoshi Asami /* 649a56bb8a5SSatoshi Asami * Translate the partition-relative block number to an absolute. 650a56bb8a5SSatoshi Asami */ 6519d7f7369SPoul-Henning Kamp bn = bp->bio_blkno; 652a56bb8a5SSatoshi Asami 653a56bb8a5SSatoshi Asami /* 654a56bb8a5SSatoshi Asami * Allocate component buffers and fire off the requests 655a56bb8a5SSatoshi Asami */ 6569d7f7369SPoul-Henning Kamp addr = bp->bio_data; 65777154759SPoul-Henning Kamp sent = 0; 6589d7f7369SPoul-Henning Kamp for (bcount = bp->bio_bcount; bcount > 0; bcount -= rcount) { 6590f76d6d8SPoul-Henning Kamp err = ccdbuffer(cbp, cs, bp, bn, addr, bcount); 6600f76d6d8SPoul-Henning Kamp if (err) { 6610f76d6d8SPoul-Henning Kamp printf("ccdbuffer error %d\n", err); 66277154759SPoul-Henning Kamp if (!sent) 6632f912fc9SPoul-Henning Kamp biofinish(bp, NULL, err); 66477154759SPoul-Henning Kamp else { 66577154759SPoul-Henning Kamp /* 66677154759SPoul-Henning Kamp * XXX: maybe a race where the partners 66777154759SPoul-Henning Kamp * XXX: we sent already have been in 66877154759SPoul-Henning Kamp * XXX: ccdiodone(). Single-threaded g_down 66977154759SPoul-Henning Kamp * XXX: may protect against this. 67077154759SPoul-Henning Kamp */ 67177154759SPoul-Henning Kamp bp->bio_resid -= bcount; 67277154759SPoul-Henning Kamp bp->bio_error = err; 67377154759SPoul-Henning Kamp bp->bio_flags |= BIO_ERROR; 67477154759SPoul-Henning Kamp } 6750f76d6d8SPoul-Henning Kamp return; 6760f76d6d8SPoul-Henning Kamp } 6779d7f7369SPoul-Henning Kamp rcount = cbp[0]->cb_buf.bio_bcount; 6781464240eSMatthew Dillon 6791464240eSMatthew Dillon if (cs->sc_cflags & CCDF_MIRROR) { 6801464240eSMatthew Dillon /* 6811464240eSMatthew Dillon * Mirroring. Writes go to both disks, reads are 6821464240eSMatthew Dillon * taken from whichever disk seems most appropriate. 6831464240eSMatthew Dillon * 6841464240eSMatthew Dillon * We attempt to localize reads to the disk whos arm 6851464240eSMatthew Dillon * is nearest the read request. We ignore seeks due 6861464240eSMatthew Dillon * to writes when making this determination and we 6871464240eSMatthew Dillon * also try to avoid hogging. 6881464240eSMatthew Dillon */ 6899d7f7369SPoul-Henning Kamp if (cbp[0]->cb_buf.bio_cmd == BIO_WRITE) { 690d616ee08SPoul-Henning Kamp BIO_STRATEGY(&cbp[0]->cb_buf); 691d616ee08SPoul-Henning Kamp BIO_STRATEGY(&cbp[1]->cb_buf); 69277154759SPoul-Henning Kamp sent++; 6931464240eSMatthew Dillon } else { 6941464240eSMatthew Dillon int pick = cs->sc_pick; 6951464240eSMatthew Dillon daddr_t range = cs->sc_size / 16; 6961464240eSMatthew Dillon 6971464240eSMatthew Dillon if (bn < cs->sc_blk[pick] - range || 6981464240eSMatthew Dillon bn > cs->sc_blk[pick] + range 6991464240eSMatthew Dillon ) { 7001464240eSMatthew Dillon cs->sc_pick = pick = 1 - pick; 7011464240eSMatthew Dillon } 7021464240eSMatthew Dillon cs->sc_blk[pick] = bn + btodb(rcount); 703d616ee08SPoul-Henning Kamp BIO_STRATEGY(&cbp[pick]->cb_buf); 70477154759SPoul-Henning Kamp sent++; 7051464240eSMatthew Dillon } 7061464240eSMatthew Dillon } else { 7071464240eSMatthew Dillon /* 7081464240eSMatthew Dillon * Not mirroring 7091464240eSMatthew Dillon */ 710d616ee08SPoul-Henning Kamp BIO_STRATEGY(&cbp[0]->cb_buf); 71177154759SPoul-Henning Kamp sent++; 7123bc746beSSatoshi Asami } 713a56bb8a5SSatoshi Asami bn += btodb(rcount); 714a56bb8a5SSatoshi Asami addr += rcount; 715a56bb8a5SSatoshi Asami } 716a56bb8a5SSatoshi Asami } 717a56bb8a5SSatoshi Asami 718a56bb8a5SSatoshi Asami /* 719a56bb8a5SSatoshi Asami * Build a component buffer header. 720a56bb8a5SSatoshi Asami */ 7210f76d6d8SPoul-Henning Kamp static int 72201706d20SPoul-Henning Kamp ccdbuffer(struct ccdbuf **cb, struct ccd_s *cs, struct bio *bp, daddr_t bn, caddr_t addr, long bcount) 723a56bb8a5SSatoshi Asami { 7241464240eSMatthew Dillon struct ccdcinfo *ci, *ci2 = NULL; /* XXX */ 7251464240eSMatthew Dillon struct ccdbuf *cbp; 7261464240eSMatthew Dillon daddr_t cbn, cboff; 7271464240eSMatthew Dillon off_t cbc; 728a56bb8a5SSatoshi Asami 729a56bb8a5SSatoshi Asami /* 730a56bb8a5SSatoshi Asami * Determine which component bn falls in. 731a56bb8a5SSatoshi Asami */ 732a56bb8a5SSatoshi Asami cbn = bn; 733a56bb8a5SSatoshi Asami cboff = 0; 734a56bb8a5SSatoshi Asami 735a56bb8a5SSatoshi Asami if (cs->sc_ileave == 0) { 7361464240eSMatthew Dillon /* 7371464240eSMatthew Dillon * Serially concatenated and neither a mirror nor a parity 7381464240eSMatthew Dillon * config. This is a special case. 7391464240eSMatthew Dillon */ 7401464240eSMatthew Dillon daddr_t sblk; 741a56bb8a5SSatoshi Asami 742a56bb8a5SSatoshi Asami sblk = 0; 743a56bb8a5SSatoshi Asami for (ci = cs->sc_cinfo; cbn >= sblk + ci->ci_size; ci++) 744a56bb8a5SSatoshi Asami sblk += ci->ci_size; 745a56bb8a5SSatoshi Asami cbn -= sblk; 7461464240eSMatthew Dillon } else { 7471464240eSMatthew Dillon struct ccdiinfo *ii; 748a56bb8a5SSatoshi Asami int ccdisk, off; 749a56bb8a5SSatoshi Asami 7501464240eSMatthew Dillon /* 7511464240eSMatthew Dillon * Calculate cbn, the logical superblock (sc_ileave chunks), 7521464240eSMatthew Dillon * and cboff, a normal block offset (DEV_BSIZE chunks) relative 7531464240eSMatthew Dillon * to cbn. 7541464240eSMatthew Dillon */ 7551464240eSMatthew Dillon cboff = cbn % cs->sc_ileave; /* DEV_BSIZE gran */ 7561464240eSMatthew Dillon cbn = cbn / cs->sc_ileave; /* DEV_BSIZE * ileave gran */ 7571464240eSMatthew Dillon 7581464240eSMatthew Dillon /* 7591464240eSMatthew Dillon * Figure out which interleave table to use. 7601464240eSMatthew Dillon */ 7611464240eSMatthew Dillon for (ii = cs->sc_itable; ii->ii_ndisk; ii++) { 762a56bb8a5SSatoshi Asami if (ii->ii_startblk > cbn) 763a56bb8a5SSatoshi Asami break; 7641464240eSMatthew Dillon } 765a56bb8a5SSatoshi Asami ii--; 7661464240eSMatthew Dillon 7671464240eSMatthew Dillon /* 7681464240eSMatthew Dillon * off is the logical superblock relative to the beginning 7691464240eSMatthew Dillon * of this interleave block. 7701464240eSMatthew Dillon */ 771a56bb8a5SSatoshi Asami off = cbn - ii->ii_startblk; 7721464240eSMatthew Dillon 7731464240eSMatthew Dillon /* 7741464240eSMatthew Dillon * We must calculate which disk component to use (ccdisk), 7751464240eSMatthew Dillon * and recalculate cbn to be the superblock relative to 7761464240eSMatthew Dillon * the beginning of the component. This is typically done by 7771464240eSMatthew Dillon * adding 'off' and ii->ii_startoff together. However, 'off' 7781464240eSMatthew Dillon * must typically be divided by the number of components in 7791464240eSMatthew Dillon * this interleave array to be properly convert it from a 7801464240eSMatthew Dillon * CCD-relative logical superblock number to a 7811464240eSMatthew Dillon * component-relative superblock number. 7821464240eSMatthew Dillon */ 783a56bb8a5SSatoshi Asami if (ii->ii_ndisk == 1) { 7841464240eSMatthew Dillon /* 7851464240eSMatthew Dillon * When we have just one disk, it can't be a mirror 7861464240eSMatthew Dillon * or a parity config. 7871464240eSMatthew Dillon */ 788a56bb8a5SSatoshi Asami ccdisk = ii->ii_index[0]; 789a56bb8a5SSatoshi Asami cbn = ii->ii_startoff + off; 790a56bb8a5SSatoshi Asami } else { 79109b59204SSatoshi Asami if (cs->sc_cflags & CCDF_MIRROR) { 7921464240eSMatthew Dillon /* 7931464240eSMatthew Dillon * We have forced a uniform mapping, resulting 7941464240eSMatthew Dillon * in a single interleave array. We double 7951464240eSMatthew Dillon * up on the first half of the available 7961464240eSMatthew Dillon * components and our mirror is in the second 7971464240eSMatthew Dillon * half. This only works with a single 7981464240eSMatthew Dillon * interleave array because doubling up 7991464240eSMatthew Dillon * doubles the number of sectors, so there 8001464240eSMatthew Dillon * cannot be another interleave array because 8011464240eSMatthew Dillon * the next interleave array's calculations 8021464240eSMatthew Dillon * would be off. 8031464240eSMatthew Dillon */ 8041464240eSMatthew Dillon int ndisk2 = ii->ii_ndisk / 2; 8051464240eSMatthew Dillon ccdisk = ii->ii_index[off % ndisk2]; 8061464240eSMatthew Dillon cbn = ii->ii_startoff + off / ndisk2; 8071464240eSMatthew Dillon ci2 = &cs->sc_cinfo[ccdisk + ndisk2]; 8081464240eSMatthew Dillon } else { 809a56bb8a5SSatoshi Asami ccdisk = ii->ii_index[off % ii->ii_ndisk]; 810a56bb8a5SSatoshi Asami cbn = ii->ii_startoff + off / ii->ii_ndisk; 811a56bb8a5SSatoshi Asami } 8127ecb65faSSatoshi Asami } 8131464240eSMatthew Dillon 814a56bb8a5SSatoshi Asami ci = &cs->sc_cinfo[ccdisk]; 8151464240eSMatthew Dillon 8161464240eSMatthew Dillon /* 8171464240eSMatthew Dillon * Convert cbn from a superblock to a normal block so it 8181464240eSMatthew Dillon * can be used to calculate (along with cboff) the normal 8191464240eSMatthew Dillon * block index into this particular disk. 8201464240eSMatthew Dillon */ 8211464240eSMatthew Dillon cbn *= cs->sc_ileave; 822a56bb8a5SSatoshi Asami } 823a56bb8a5SSatoshi Asami 824a56bb8a5SSatoshi Asami /* 825a56bb8a5SSatoshi Asami * Fill in the component buf structure. 826a56bb8a5SSatoshi Asami */ 8270f76d6d8SPoul-Henning Kamp cbp = malloc(sizeof(struct ccdbuf), M_CCD, M_NOWAIT | M_ZERO); 8280f76d6d8SPoul-Henning Kamp if (cbp == NULL) 8290f76d6d8SPoul-Henning Kamp return (ENOMEM); 8309d7f7369SPoul-Henning Kamp cbp->cb_buf.bio_cmd = bp->bio_cmd; 8319d7f7369SPoul-Henning Kamp cbp->cb_buf.bio_done = ccdiodone; 8329d7f7369SPoul-Henning Kamp cbp->cb_buf.bio_dev = ci->ci_dev; /* XXX */ 8339d7f7369SPoul-Henning Kamp cbp->cb_buf.bio_blkno = cbn + cboff + CCD_OFFSET; 8349d7f7369SPoul-Henning Kamp cbp->cb_buf.bio_offset = dbtob(cbn + cboff + CCD_OFFSET); 8359d7f7369SPoul-Henning Kamp cbp->cb_buf.bio_data = addr; 8360f76d6d8SPoul-Henning Kamp cbp->cb_buf.bio_caller2 = cbp; 837a56bb8a5SSatoshi Asami if (cs->sc_ileave == 0) 83840969e38SDavid Greenman cbc = dbtob((off_t)(ci->ci_size - cbn)); 839a56bb8a5SSatoshi Asami else 84040969e38SDavid Greenman cbc = dbtob((off_t)(cs->sc_ileave - cboff)); 8419d7f7369SPoul-Henning Kamp cbp->cb_buf.bio_bcount = (cbc < bcount) ? cbc : bcount; 8429d7f7369SPoul-Henning Kamp cbp->cb_buf.bio_caller1 = (void*)cbp->cb_buf.bio_bcount; 843c0b89506SJohn Dyson 844a56bb8a5SSatoshi Asami /* 845a56bb8a5SSatoshi Asami * context for ccdiodone 846a56bb8a5SSatoshi Asami */ 847a56bb8a5SSatoshi Asami cbp->cb_obp = bp; 8480f76d6d8SPoul-Henning Kamp cbp->cb_softc = cs; 849a56bb8a5SSatoshi Asami cbp->cb_comp = ci - cs->sc_cinfo; 850a56bb8a5SSatoshi Asami 8513bc746beSSatoshi Asami cb[0] = cbp; 8521464240eSMatthew Dillon 8531464240eSMatthew Dillon /* 8541464240eSMatthew Dillon * Note: both I/O's setup when reading from mirror, but only one 8551464240eSMatthew Dillon * will be executed. 8561464240eSMatthew Dillon */ 8571464240eSMatthew Dillon if (cs->sc_cflags & CCDF_MIRROR) { 8581464240eSMatthew Dillon /* mirror, setup second I/O */ 8590f76d6d8SPoul-Henning Kamp cbp = malloc(sizeof(struct ccdbuf), M_CCD, M_NOWAIT); 8600f76d6d8SPoul-Henning Kamp if (cbp == NULL) { 8610f76d6d8SPoul-Henning Kamp free(cb[0], M_CCD); 8620f76d6d8SPoul-Henning Kamp cb[0] = NULL; 8630f76d6d8SPoul-Henning Kamp return (ENOMEM); 8640f76d6d8SPoul-Henning Kamp } 865e9fe7d1fSPoul-Henning Kamp bcopy(cb[0], cbp, sizeof(struct ccdbuf)); 8660238f932SPoul-Henning Kamp cbp->cb_buf.bio_caller2 = cbp; 8679d7f7369SPoul-Henning Kamp cbp->cb_buf.bio_dev = ci2->ci_dev; 8683bc746beSSatoshi Asami cbp->cb_comp = ci2 - cs->sc_cinfo; 8693bc746beSSatoshi Asami cb[1] = cbp; 870e7322872SSatoshi Asami /* link together the ccdbuf's and clear "mirror done" flag */ 871e7322872SSatoshi Asami cb[0]->cb_mirror = cb[1]; 872e7322872SSatoshi Asami cb[1]->cb_mirror = cb[0]; 873e7322872SSatoshi Asami cb[0]->cb_pflags &= ~CCDPF_MIRROR_DONE; 874e7322872SSatoshi Asami cb[1]->cb_pflags &= ~CCDPF_MIRROR_DONE; 8753bc746beSSatoshi Asami } 8760f76d6d8SPoul-Henning Kamp return (0); 877a56bb8a5SSatoshi Asami } 878a56bb8a5SSatoshi Asami 879a56bb8a5SSatoshi Asami /* 880a56bb8a5SSatoshi Asami * Called at interrupt time. 881a56bb8a5SSatoshi Asami * Mark the component as done and if all components are done, 882a56bb8a5SSatoshi Asami * take a ccd interrupt. 883a56bb8a5SSatoshi Asami */ 884e2738b4fSPoul-Henning Kamp static void 88501706d20SPoul-Henning Kamp ccdiodone(struct bio *ibp) 886a56bb8a5SSatoshi Asami { 8870f76d6d8SPoul-Henning Kamp struct ccdbuf *cbp; 8880f76d6d8SPoul-Henning Kamp struct bio *bp; 889360d71d1SPoul-Henning Kamp struct ccd_s *cs; 8900f76d6d8SPoul-Henning Kamp int count; 891a56bb8a5SSatoshi Asami 8920f76d6d8SPoul-Henning Kamp cbp = ibp->bio_caller2; 8930f76d6d8SPoul-Henning Kamp cs = cbp->cb_softc; 8940f76d6d8SPoul-Henning Kamp bp = cbp->cb_obp; 8951464240eSMatthew Dillon /* 8961464240eSMatthew Dillon * If an error occured, report it. If this is a mirrored 8971464240eSMatthew Dillon * configuration and the first of two possible reads, do not 8981464240eSMatthew Dillon * set the error in the bp yet because the second read may 8991464240eSMatthew Dillon * succeed. 9001464240eSMatthew Dillon */ 901a56bb8a5SSatoshi Asami 9029d7f7369SPoul-Henning Kamp if (cbp->cb_buf.bio_flags & BIO_ERROR) { 9031464240eSMatthew Dillon const char *msg = ""; 9041464240eSMatthew Dillon 905360d71d1SPoul-Henning Kamp if ((cs->sc_cflags & CCDF_MIRROR) && 9069d7f7369SPoul-Henning Kamp (cbp->cb_buf.bio_cmd == BIO_READ) && 9071464240eSMatthew Dillon (cbp->cb_pflags & CCDPF_MIRROR_DONE) == 0) { 9081464240eSMatthew Dillon /* 9091464240eSMatthew Dillon * We will try our read on the other disk down 9101464240eSMatthew Dillon * below, also reverse the default pick so if we 9111464240eSMatthew Dillon * are doing a scan we do not keep hitting the 9121464240eSMatthew Dillon * bad disk first. 9131464240eSMatthew Dillon */ 9141464240eSMatthew Dillon 9151464240eSMatthew Dillon msg = ", trying other disk"; 9161464240eSMatthew Dillon cs->sc_pick = 1 - cs->sc_pick; 9179d7f7369SPoul-Henning Kamp cs->sc_blk[cs->sc_pick] = bp->bio_blkno; 9181464240eSMatthew Dillon } else { 9199d7f7369SPoul-Henning Kamp bp->bio_flags |= BIO_ERROR; 9209d7f7369SPoul-Henning Kamp bp->bio_error = cbp->cb_buf.bio_error ? 9219d7f7369SPoul-Henning Kamp cbp->cb_buf.bio_error : EIO; 9221464240eSMatthew Dillon } 92392faa7b5SMaxime Henrion printf("ccd%d: error %d on component %d block %jd " 9240f76d6d8SPoul-Henning Kamp "(ccd block %jd)%s\n", cs->sc_unit, bp->bio_error, 9250f76d6d8SPoul-Henning Kamp cbp->cb_comp, 92692faa7b5SMaxime Henrion (intmax_t)cbp->cb_buf.bio_blkno, (intmax_t)bp->bio_blkno, 92792faa7b5SMaxime Henrion msg); 928a56bb8a5SSatoshi Asami } 929e7322872SSatoshi Asami 9301464240eSMatthew Dillon /* 9311464240eSMatthew Dillon * Process mirror. If we are writing, I/O has been initiated on both 9321464240eSMatthew Dillon * buffers and we fall through only after both are finished. 9331464240eSMatthew Dillon * 9341464240eSMatthew Dillon * If we are reading only one I/O is initiated at a time. If an 9351464240eSMatthew Dillon * error occurs we initiate the second I/O and return, otherwise 9361464240eSMatthew Dillon * we free the second I/O without initiating it. 9371464240eSMatthew Dillon */ 9381464240eSMatthew Dillon 939360d71d1SPoul-Henning Kamp if (cs->sc_cflags & CCDF_MIRROR) { 9409d7f7369SPoul-Henning Kamp if (cbp->cb_buf.bio_cmd == BIO_WRITE) { 9411464240eSMatthew Dillon /* 9421464240eSMatthew Dillon * When writing, handshake with the second buffer 9431464240eSMatthew Dillon * to determine when both are done. If both are not 9441464240eSMatthew Dillon * done, return here. 9451464240eSMatthew Dillon */ 946e7322872SSatoshi Asami if ((cbp->cb_pflags & CCDPF_MIRROR_DONE) == 0) { 947e7322872SSatoshi Asami cbp->cb_mirror->cb_pflags |= CCDPF_MIRROR_DONE; 948e9fe7d1fSPoul-Henning Kamp free(cbp, M_CCD); 949e7322872SSatoshi Asami return; 950e7322872SSatoshi Asami } 9511464240eSMatthew Dillon } else { 9521464240eSMatthew Dillon /* 9531464240eSMatthew Dillon * When reading, either dispose of the second buffer 9541464240eSMatthew Dillon * or initiate I/O on the second buffer if an error 9551464240eSMatthew Dillon * occured with this one. 9561464240eSMatthew Dillon */ 9571464240eSMatthew Dillon if ((cbp->cb_pflags & CCDPF_MIRROR_DONE) == 0) { 9589d7f7369SPoul-Henning Kamp if (cbp->cb_buf.bio_flags & BIO_ERROR) { 9591464240eSMatthew Dillon cbp->cb_mirror->cb_pflags |= 9601464240eSMatthew Dillon CCDPF_MIRROR_DONE; 961d616ee08SPoul-Henning Kamp BIO_STRATEGY(&cbp->cb_mirror->cb_buf); 962e9fe7d1fSPoul-Henning Kamp free(cbp, M_CCD); 9631464240eSMatthew Dillon return; 9641464240eSMatthew Dillon } else { 965e9fe7d1fSPoul-Henning Kamp free(cbp->cb_mirror, M_CCD); 9661464240eSMatthew Dillon } 9671464240eSMatthew Dillon } 9681464240eSMatthew Dillon } 9691464240eSMatthew Dillon } 970e7322872SSatoshi Asami 97125d1a00bSMatthew Dillon /* 9729d7f7369SPoul-Henning Kamp * use bio_caller1 to determine how big the original request was rather 9739d7f7369SPoul-Henning Kamp * then bio_bcount, because bio_bcount may have been truncated for EOF. 97425d1a00bSMatthew Dillon * 97525d1a00bSMatthew Dillon * XXX We check for an error, but we do not test the resid for an 97625d1a00bSMatthew Dillon * aligned EOF condition. This may result in character & block 97725d1a00bSMatthew Dillon * device access not recognizing EOF properly when read or written 97825d1a00bSMatthew Dillon * sequentially, but will not effect filesystems. 97925d1a00bSMatthew Dillon */ 9809d7f7369SPoul-Henning Kamp count = (long)cbp->cb_buf.bio_caller1; 981e9fe7d1fSPoul-Henning Kamp free(cbp, M_CCD); 982a56bb8a5SSatoshi Asami 983a56bb8a5SSatoshi Asami /* 984a56bb8a5SSatoshi Asami * If all done, "interrupt". 985a56bb8a5SSatoshi Asami */ 9869d7f7369SPoul-Henning Kamp bp->bio_resid -= count; 9879d7f7369SPoul-Henning Kamp if (bp->bio_resid < 0) 988a56bb8a5SSatoshi Asami panic("ccdiodone: count"); 989360d71d1SPoul-Henning Kamp if (bp->bio_resid == 0) { 990360d71d1SPoul-Henning Kamp if (bp->bio_flags & BIO_ERROR) 991360d71d1SPoul-Henning Kamp bp->bio_resid = bp->bio_bcount; 99260794e04SPoul-Henning Kamp biodone(bp); 993360d71d1SPoul-Henning Kamp } 994a56bb8a5SSatoshi Asami } 995a56bb8a5SSatoshi Asami 9960f76d6d8SPoul-Henning Kamp static int ccdioctltoo(int unit, u_long cmd, caddr_t data, int flag, struct thread *td); 9970f76d6d8SPoul-Henning Kamp 998e2738b4fSPoul-Henning Kamp static int 9990f76d6d8SPoul-Henning Kamp ccdctlioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct thread *td) 1000a56bb8a5SSatoshi Asami { 1001ddbf51afSPoul-Henning Kamp struct ccd_ioctl *ccio; 1002ddbf51afSPoul-Henning Kamp u_int unit; 1003ddbf51afSPoul-Henning Kamp 1004ddbf51afSPoul-Henning Kamp switch (cmd) { 1005ddbf51afSPoul-Henning Kamp case CCDIOCSET: 1006ddbf51afSPoul-Henning Kamp case CCDIOCCLR: 1007ddbf51afSPoul-Henning Kamp ccio = (struct ccd_ioctl *)data; 1008ddbf51afSPoul-Henning Kamp unit = ccio->ccio_size; 10090f76d6d8SPoul-Henning Kamp return (ccdioctltoo(unit, cmd, data, flag, td)); 1010ddbf51afSPoul-Henning Kamp default: 10110f557e0aSPoul-Henning Kamp return (ENOIOCTL); 1012ddbf51afSPoul-Henning Kamp } 1013ddbf51afSPoul-Henning Kamp } 1014ddbf51afSPoul-Henning Kamp 1015ddbf51afSPoul-Henning Kamp static int 10160f76d6d8SPoul-Henning Kamp ccdioctltoo(int unit, u_long cmd, caddr_t data, int flag, struct thread *td) 1017ddbf51afSPoul-Henning Kamp { 1018a56bb8a5SSatoshi Asami int i, j, lookedup = 0, error = 0; 101901706d20SPoul-Henning Kamp struct ccd_s *cs; 1020a56bb8a5SSatoshi Asami struct ccd_ioctl *ccio = (struct ccd_ioctl *)data; 10210f76d6d8SPoul-Henning Kamp struct ccdgeom *ccg; 1022a56bb8a5SSatoshi Asami char **cpp; 1023a56bb8a5SSatoshi Asami struct vnode **vpp; 1024a56bb8a5SSatoshi Asami 102501706d20SPoul-Henning Kamp cs = ccdfind(unit); 1026a56bb8a5SSatoshi Asami switch (cmd) { 1027a56bb8a5SSatoshi Asami case CCDIOCSET: 10280f76d6d8SPoul-Henning Kamp if (cs == NULL) 10290f76d6d8SPoul-Henning Kamp cs = ccdnew(unit); 103001706d20SPoul-Henning Kamp if (IS_INITED(cs)) 1031a56bb8a5SSatoshi Asami return (EBUSY); 1032a56bb8a5SSatoshi Asami 1033a56bb8a5SSatoshi Asami if ((flag & FWRITE) == 0) 1034a56bb8a5SSatoshi Asami return (EBADF); 1035a56bb8a5SSatoshi Asami 1036b4e36adfSMatthew Dillon if ((error = ccdlock(cs)) != 0) 1037a56bb8a5SSatoshi Asami return (error); 1038a56bb8a5SSatoshi Asami 1039bf61e266SKris Kennaway if (ccio->ccio_ndisks > CCD_MAXNDISKS) 1040bf61e266SKris Kennaway return (EINVAL); 1041bf61e266SKris Kennaway 1042a56bb8a5SSatoshi Asami /* Fill in some important bits. */ 104301706d20SPoul-Henning Kamp cs->sc_ileave = ccio->ccio_ileave; 1044f05f44f0SPoul-Henning Kamp if (cs->sc_ileave == 0 && (ccio->ccio_flags & CCDF_MIRROR)) { 1045f05f44f0SPoul-Henning Kamp printf("ccd%d: disabling mirror, interleave is 0\n", 1046f05f44f0SPoul-Henning Kamp unit); 1047f05f44f0SPoul-Henning Kamp ccio->ccio_flags &= ~(CCDF_MIRROR); 1048b8e29b55SSatoshi Asami } 104909b59204SSatoshi Asami if ((ccio->ccio_flags & CCDF_MIRROR) && 10507ecb65faSSatoshi Asami !(ccio->ccio_flags & CCDF_UNIFORM)) { 105109b59204SSatoshi Asami printf("ccd%d: mirror/parity forces uniform flag\n", 105209b59204SSatoshi Asami unit); 10537ecb65faSSatoshi Asami ccio->ccio_flags |= CCDF_UNIFORM; 10547ecb65faSSatoshi Asami } 105501706d20SPoul-Henning Kamp cs->sc_flags = ccio->ccio_flags & CCDF_USERMASK; 1056a56bb8a5SSatoshi Asami 1057a56bb8a5SSatoshi Asami /* 1058a56bb8a5SSatoshi Asami * Allocate space for and copy in the array of 1059a56bb8a5SSatoshi Asami * componet pathnames and device numbers. 1060a56bb8a5SSatoshi Asami */ 1061a56bb8a5SSatoshi Asami cpp = malloc(ccio->ccio_ndisks * sizeof(char *), 1062a163d034SWarner Losh M_CCD, M_WAITOK); 1063a56bb8a5SSatoshi Asami vpp = malloc(ccio->ccio_ndisks * sizeof(struct vnode *), 1064a163d034SWarner Losh M_CCD, M_WAITOK); 1065a56bb8a5SSatoshi Asami 1066a56bb8a5SSatoshi Asami error = copyin((caddr_t)ccio->ccio_disks, (caddr_t)cpp, 1067a56bb8a5SSatoshi Asami ccio->ccio_ndisks * sizeof(char **)); 1068a56bb8a5SSatoshi Asami if (error) { 1069b51ea356SPoul-Henning Kamp free(vpp, M_CCD); 1070b51ea356SPoul-Henning Kamp free(cpp, M_CCD); 1071a56bb8a5SSatoshi Asami ccdunlock(cs); 1072a56bb8a5SSatoshi Asami return (error); 1073a56bb8a5SSatoshi Asami } 1074a56bb8a5SSatoshi Asami 1075a56bb8a5SSatoshi Asami 1076a56bb8a5SSatoshi Asami for (i = 0; i < ccio->ccio_ndisks; ++i) { 1077b40ce416SJulian Elischer if ((error = ccdlookup(cpp[i], td, &vpp[i])) != 0) { 1078a56bb8a5SSatoshi Asami for (j = 0; j < lookedup; ++j) 1079a56bb8a5SSatoshi Asami (void)vn_close(vpp[j], FREAD|FWRITE, 1080a854ed98SJohn Baldwin td->td_ucred, td); 1081b51ea356SPoul-Henning Kamp free(vpp, M_CCD); 1082b51ea356SPoul-Henning Kamp free(cpp, M_CCD); 1083a56bb8a5SSatoshi Asami ccdunlock(cs); 1084a56bb8a5SSatoshi Asami return (error); 1085a56bb8a5SSatoshi Asami } 1086a56bb8a5SSatoshi Asami ++lookedup; 1087a56bb8a5SSatoshi Asami } 108801706d20SPoul-Henning Kamp cs->sc_vpp = vpp; 108901706d20SPoul-Henning Kamp cs->sc_nccdisks = ccio->ccio_ndisks; 1090a56bb8a5SSatoshi Asami 1091a56bb8a5SSatoshi Asami /* 1092a56bb8a5SSatoshi Asami * Initialize the ccd. Fills in the softc for us. 1093a56bb8a5SSatoshi Asami */ 1094b40ce416SJulian Elischer if ((error = ccdinit(cs, cpp, td)) != 0) { 1095a56bb8a5SSatoshi Asami for (j = 0; j < lookedup; ++j) 1096ba88dfc7SJohn Baldwin (void)vn_close(vpp[j], FREAD|FWRITE, 1097a854ed98SJohn Baldwin td->td_ucred, td); 109801706d20SPoul-Henning Kamp /* 109901706d20SPoul-Henning Kamp * We can't ccddestroy() cs just yet, because nothing 110001706d20SPoul-Henning Kamp * prevents user-level app to do another ioctl() 110101706d20SPoul-Henning Kamp * without closing the device first, therefore 110201706d20SPoul-Henning Kamp * declare unit null and void and let ccdclose() 110301706d20SPoul-Henning Kamp * destroy it when it is safe to do so. 110401706d20SPoul-Henning Kamp */ 110501706d20SPoul-Henning Kamp cs->sc_flags &= (CCDF_WANTED | CCDF_LOCKED); 1106b51ea356SPoul-Henning Kamp free(vpp, M_CCD); 1107b51ea356SPoul-Henning Kamp free(cpp, M_CCD); 1108a56bb8a5SSatoshi Asami ccdunlock(cs); 1109a56bb8a5SSatoshi Asami return (error); 1110a56bb8a5SSatoshi Asami } 11116b267654SPoul-Henning Kamp free(cpp, M_CCD); 1112a56bb8a5SSatoshi Asami 1113a56bb8a5SSatoshi Asami /* 1114a56bb8a5SSatoshi Asami * The ccd has been successfully initialized, so 1115a56bb8a5SSatoshi Asami * we can place it into the array and read the disklabel. 1116a56bb8a5SSatoshi Asami */ 1117a56bb8a5SSatoshi Asami ccio->ccio_unit = unit; 1118a56bb8a5SSatoshi Asami ccio->ccio_size = cs->sc_size; 11190f76d6d8SPoul-Henning Kamp ccg = &cs->sc_geom; 1120a163d034SWarner Losh cs->sc_disk = malloc(sizeof(struct disk), M_CCD, 1121a163d034SWarner Losh M_ZERO | M_WAITOK); 1122806d5cffSPoul-Henning Kamp cs->sc_disk->d_strategy = ccdstrategy; 1123806d5cffSPoul-Henning Kamp cs->sc_disk->d_name = "ccd"; 11240f76d6d8SPoul-Henning Kamp cs->sc_disk->d_sectorsize = ccg->ccg_secsize; 11250f76d6d8SPoul-Henning Kamp cs->sc_disk->d_mediasize = 11260f76d6d8SPoul-Henning Kamp cs->sc_size * (off_t)ccg->ccg_secsize; 11270f76d6d8SPoul-Henning Kamp cs->sc_disk->d_fwsectors = ccg->ccg_nsectors; 11280f76d6d8SPoul-Henning Kamp cs->sc_disk->d_fwheads = ccg->ccg_ntracks; 1129ad3467e1SPoul-Henning Kamp cs->sc_disk->d_drv1 = cs; 1130ad3467e1SPoul-Henning Kamp cs->sc_disk->d_maxsize = MAXPHYS; 1131ad3467e1SPoul-Henning Kamp disk_create(unit, cs->sc_disk, 0, NULL, NULL); 1132a56bb8a5SSatoshi Asami 1133a56bb8a5SSatoshi Asami ccdunlock(cs); 1134a56bb8a5SSatoshi Asami 1135a56bb8a5SSatoshi Asami break; 1136a56bb8a5SSatoshi Asami 1137a56bb8a5SSatoshi Asami case CCDIOCCLR: 11380f76d6d8SPoul-Henning Kamp if (cs == NULL) 11390f76d6d8SPoul-Henning Kamp return (ENXIO); 11400f76d6d8SPoul-Henning Kamp 114101706d20SPoul-Henning Kamp if (!IS_INITED(cs)) 1142a56bb8a5SSatoshi Asami return (ENXIO); 1143a56bb8a5SSatoshi Asami 1144a56bb8a5SSatoshi Asami if ((flag & FWRITE) == 0) 1145a56bb8a5SSatoshi Asami return (EBADF); 1146a56bb8a5SSatoshi Asami 1147b4e36adfSMatthew Dillon if ((error = ccdlock(cs)) != 0) 1148a56bb8a5SSatoshi Asami return (error); 1149a56bb8a5SSatoshi Asami 1150af8862e4SPoul-Henning Kamp /* Don't unconfigure if any other partitions are open */ 1151806d5cffSPoul-Henning Kamp if (cs->sc_disk->d_flags & DISKFLAG_OPEN) { 1152a56bb8a5SSatoshi Asami ccdunlock(cs); 1153a56bb8a5SSatoshi Asami return (EBUSY); 1154a56bb8a5SSatoshi Asami } 1155a56bb8a5SSatoshi Asami 1156b82ff758SPoul-Henning Kamp disk_destroy(cs->sc_disk); 11570f76d6d8SPoul-Henning Kamp free(cs->sc_disk, M_CCD); 11580f76d6d8SPoul-Henning Kamp cs->sc_disk = NULL; 115901706d20SPoul-Henning Kamp /* Declare unit null and void (reset all flags) */ 116001706d20SPoul-Henning Kamp cs->sc_flags &= (CCDF_WANTED | CCDF_LOCKED); 1161a56bb8a5SSatoshi Asami 1162a56bb8a5SSatoshi Asami /* Close the components and free their pathnames. */ 1163a56bb8a5SSatoshi Asami for (i = 0; i < cs->sc_nccdisks; ++i) { 1164a56bb8a5SSatoshi Asami /* 1165a56bb8a5SSatoshi Asami * XXX: this close could potentially fail and 1166a56bb8a5SSatoshi Asami * cause Bad Things. Maybe we need to force 1167a56bb8a5SSatoshi Asami * the close to happen? 1168a56bb8a5SSatoshi Asami */ 1169a56bb8a5SSatoshi Asami (void)vn_close(cs->sc_cinfo[i].ci_vp, FREAD|FWRITE, 1170a854ed98SJohn Baldwin td->td_ucred, td); 1171b51ea356SPoul-Henning Kamp free(cs->sc_cinfo[i].ci_path, M_CCD); 1172a56bb8a5SSatoshi Asami } 1173a56bb8a5SSatoshi Asami 1174a56bb8a5SSatoshi Asami /* Free interleave index. */ 1175a56bb8a5SSatoshi Asami for (i = 0; cs->sc_itable[i].ii_ndisk; ++i) 1176b51ea356SPoul-Henning Kamp free(cs->sc_itable[i].ii_index, M_CCD); 1177a56bb8a5SSatoshi Asami 1178a56bb8a5SSatoshi Asami /* Free component info and interleave table. */ 1179b51ea356SPoul-Henning Kamp free(cs->sc_cinfo, M_CCD); 1180b51ea356SPoul-Henning Kamp free(cs->sc_itable, M_CCD); 1181b51ea356SPoul-Henning Kamp free(cs->sc_vpp, M_CCD); 1182a56bb8a5SSatoshi Asami 1183a56bb8a5SSatoshi Asami /* This must be atomic. */ 1184a56bb8a5SSatoshi Asami ccdunlock(cs); 11850f76d6d8SPoul-Henning Kamp ccddestroy(cs); 1186a56bb8a5SSatoshi Asami 1187a56bb8a5SSatoshi Asami break; 1188a56bb8a5SSatoshi Asami } 1189a56bb8a5SSatoshi Asami 1190a56bb8a5SSatoshi Asami return (0); 1191a56bb8a5SSatoshi Asami } 1192a56bb8a5SSatoshi Asami 1193a56bb8a5SSatoshi Asami 1194a56bb8a5SSatoshi Asami /* 1195a56bb8a5SSatoshi Asami * Lookup the provided name in the filesystem. If the file exists, 1196a56bb8a5SSatoshi Asami * is a valid block device, and isn't being used by anyone else, 1197a56bb8a5SSatoshi Asami * set *vpp to the file's vnode. 1198a56bb8a5SSatoshi Asami */ 1199a56bb8a5SSatoshi Asami static int 1200b40ce416SJulian Elischer ccdlookup(char *path, struct thread *td, struct vnode **vpp) 1201a56bb8a5SSatoshi Asami { 1202a56bb8a5SSatoshi Asami struct nameidata nd; 1203a56bb8a5SSatoshi Asami struct vnode *vp; 1204e6796b67SKirk McKusick int error, flags; 1205a56bb8a5SSatoshi Asami 1206b40ce416SJulian Elischer NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, path, td); 1207e6796b67SKirk McKusick flags = FREAD | FWRITE; 1208e6796b67SKirk McKusick if ((error = vn_open(&nd, &flags, 0)) != 0) { 1209a56bb8a5SSatoshi Asami return (error); 1210a56bb8a5SSatoshi Asami } 1211a56bb8a5SSatoshi Asami vp = nd.ni_vp; 1212a56bb8a5SSatoshi Asami 121337ab0e0dSJeff Roberson if (vrefcnt(vp) > 1) { 1214762e6b85SEivind Eklund error = EBUSY; 1215762e6b85SEivind Eklund goto bad; 1216a56bb8a5SSatoshi Asami } 1217a56bb8a5SSatoshi Asami 1218ba4ad1fcSPoul-Henning Kamp if (!vn_isdisk(vp, &error)) 1219762e6b85SEivind Eklund goto bad; 1220a56bb8a5SSatoshi Asami 1221a56bb8a5SSatoshi Asami 1222b40ce416SJulian Elischer VOP_UNLOCK(vp, 0, td); 1223762e6b85SEivind Eklund NDFREE(&nd, NDF_ONLY_PNBUF); 1224a56bb8a5SSatoshi Asami *vpp = vp; 1225a56bb8a5SSatoshi Asami return (0); 1226762e6b85SEivind Eklund bad: 1227b40ce416SJulian Elischer VOP_UNLOCK(vp, 0, td); 1228762e6b85SEivind Eklund NDFREE(&nd, NDF_ONLY_PNBUF); 1229762e6b85SEivind Eklund /* vn_close does vrele() for vp */ 1230a854ed98SJohn Baldwin (void)vn_close(vp, FREAD|FWRITE, td->td_ucred, td); 1231762e6b85SEivind Eklund return (error); 1232a56bb8a5SSatoshi Asami } 1233a56bb8a5SSatoshi Asami 1234a56bb8a5SSatoshi Asami /* 1235a56bb8a5SSatoshi Asami 1236a56bb8a5SSatoshi Asami * Wait interruptibly for an exclusive lock. 1237a56bb8a5SSatoshi Asami * 1238a56bb8a5SSatoshi Asami * XXX 1239a56bb8a5SSatoshi Asami * Several drivers do this; it should be abstracted and made MP-safe. 1240a56bb8a5SSatoshi Asami */ 1241a56bb8a5SSatoshi Asami static int 124201706d20SPoul-Henning Kamp ccdlock(struct ccd_s *cs) 1243a56bb8a5SSatoshi Asami { 1244a56bb8a5SSatoshi Asami int error; 1245a56bb8a5SSatoshi Asami 1246a56bb8a5SSatoshi Asami while ((cs->sc_flags & CCDF_LOCKED) != 0) { 1247a56bb8a5SSatoshi Asami cs->sc_flags |= CCDF_WANTED; 1248a56bb8a5SSatoshi Asami if ((error = tsleep(cs, PRIBIO | PCATCH, "ccdlck", 0)) != 0) 1249a56bb8a5SSatoshi Asami return (error); 1250a56bb8a5SSatoshi Asami } 1251a56bb8a5SSatoshi Asami cs->sc_flags |= CCDF_LOCKED; 1252a56bb8a5SSatoshi Asami return (0); 1253a56bb8a5SSatoshi Asami } 1254a56bb8a5SSatoshi Asami 1255a56bb8a5SSatoshi Asami /* 1256a56bb8a5SSatoshi Asami * Unlock and wake up any waiters. 1257a56bb8a5SSatoshi Asami */ 1258a56bb8a5SSatoshi Asami static void 125901706d20SPoul-Henning Kamp ccdunlock(struct ccd_s *cs) 1260a56bb8a5SSatoshi Asami { 1261a56bb8a5SSatoshi Asami 1262a56bb8a5SSatoshi Asami cs->sc_flags &= ~CCDF_LOCKED; 1263a56bb8a5SSatoshi Asami if ((cs->sc_flags & CCDF_WANTED) != 0) { 1264a56bb8a5SSatoshi Asami cs->sc_flags &= ~CCDF_WANTED; 1265a56bb8a5SSatoshi Asami wakeup(cs); 1266a56bb8a5SSatoshi Asami } 1267a56bb8a5SSatoshi Asami } 1268189337d8SPoul-Henning Kamp 1269189337d8SPoul-Henning Kamp static struct sbuf * 12700f557e0aSPoul-Henning Kamp g_ccd_list(int unit) 1271189337d8SPoul-Henning Kamp { 1272189337d8SPoul-Henning Kamp struct sbuf *sb; 1273189337d8SPoul-Henning Kamp struct ccd_s *cs; 1274189337d8SPoul-Henning Kamp int i; 1275189337d8SPoul-Henning Kamp 1276189337d8SPoul-Henning Kamp sb = sbuf_new(NULL, NULL, 0, SBUF_AUTOEXTEND); 1277189337d8SPoul-Henning Kamp sbuf_clear(sb); 1278189337d8SPoul-Henning Kamp LIST_FOREACH(cs, &ccd_softc_list, list) { 1279189337d8SPoul-Henning Kamp if (!IS_INITED(cs)) 1280189337d8SPoul-Henning Kamp continue; 12810f557e0aSPoul-Henning Kamp if (unit >= 0 && unit != cs->sc_unit) 12820f557e0aSPoul-Henning Kamp continue; 1283189337d8SPoul-Henning Kamp sbuf_printf(sb, "ccd%d\t\t%d\t%d\t", 1284189337d8SPoul-Henning Kamp cs->sc_unit, cs->sc_ileave, cs->sc_cflags & CCDF_USERMASK); 1285189337d8SPoul-Henning Kamp 1286189337d8SPoul-Henning Kamp for (i = 0; i < cs->sc_nccdisks; ++i) { 1287189337d8SPoul-Henning Kamp sbuf_printf(sb, "%s%s", i == 0 ? "" : " ", 1288189337d8SPoul-Henning Kamp cs->sc_cinfo[i].ci_path); 1289189337d8SPoul-Henning Kamp } 1290189337d8SPoul-Henning Kamp sbuf_printf(sb, "\n"); 1291189337d8SPoul-Henning Kamp } 1292189337d8SPoul-Henning Kamp sbuf_finish(sb); 1293189337d8SPoul-Henning Kamp return (sb); 1294189337d8SPoul-Henning Kamp } 1295189337d8SPoul-Henning Kamp 1296189337d8SPoul-Henning Kamp static void 1297189337d8SPoul-Henning Kamp g_ccd_config(struct gctl_req *req, struct g_class *mp, char const *verb) 1298189337d8SPoul-Henning Kamp { 1299189337d8SPoul-Henning Kamp struct sbuf *sb; 13000f557e0aSPoul-Henning Kamp int u, *up; 1301189337d8SPoul-Henning Kamp 1302189337d8SPoul-Henning Kamp g_topology_assert(); 1303189337d8SPoul-Henning Kamp if (!strcmp(verb, "create geom")) { 1304189337d8SPoul-Henning Kamp gctl_error(req, "TBD"); 1305189337d8SPoul-Henning Kamp } else if (!strcmp(verb, "destroy geom")) { 1306189337d8SPoul-Henning Kamp gctl_error(req, "TBD"); 1307189337d8SPoul-Henning Kamp } else if (!strcmp(verb, "list")) { 13080f557e0aSPoul-Henning Kamp up = gctl_get_paraml(req, "unit", sizeof (int)); 13090f557e0aSPoul-Henning Kamp u = *up; 13100f557e0aSPoul-Henning Kamp sb = g_ccd_list(u); 1311189337d8SPoul-Henning Kamp gctl_set_param(req, "output", sbuf_data(sb), sbuf_len(sb) + 1); 1312189337d8SPoul-Henning Kamp } else { 1313189337d8SPoul-Henning Kamp gctl_error(req, "unknown verb"); 1314189337d8SPoul-Henning Kamp } 1315189337d8SPoul-Henning Kamp } 1316189337d8SPoul-Henning Kamp 1317189337d8SPoul-Henning Kamp static struct g_class g_ccd_class = { 1318189337d8SPoul-Henning Kamp .name = "CCD", 1319189337d8SPoul-Henning Kamp .ctlreq = g_ccd_config, 1320189337d8SPoul-Henning Kamp }; 1321189337d8SPoul-Henning Kamp 1322189337d8SPoul-Henning Kamp DECLARE_GEOM_CLASS(g_ccd_class, g_ccd); 1323