1 1.52 rin /* $NetBSD: ld_ataraid.c,v 1.52 2025/04/13 02:34:02 rin Exp $ */ 2 1.1 thorpej 3 1.1 thorpej /* 4 1.1 thorpej * Copyright (c) 2003 Wasabi Systems, Inc. 5 1.1 thorpej * All rights reserved. 6 1.1 thorpej * 7 1.1 thorpej * Written by Jason R. Thorpe for Wasabi Systems, Inc. 8 1.1 thorpej * 9 1.1 thorpej * Redistribution and use in source and binary forms, with or without 10 1.1 thorpej * modification, are permitted provided that the following conditions 11 1.1 thorpej * are met: 12 1.1 thorpej * 1. Redistributions of source code must retain the above copyright 13 1.1 thorpej * notice, this list of conditions and the following disclaimer. 14 1.1 thorpej * 2. Redistributions in binary form must reproduce the above copyright 15 1.1 thorpej * notice, this list of conditions and the following disclaimer in the 16 1.1 thorpej * documentation and/or other materials provided with the distribution. 17 1.1 thorpej * 3. All advertising materials mentioning features or use of this software 18 1.1 thorpej * must display the following acknowledgement: 19 1.1 thorpej * This product includes software developed for the NetBSD Project by 20 1.1 thorpej * Wasabi Systems, Inc. 21 1.1 thorpej * 4. The name of Wasabi Systems, Inc. may not be used to endorse 22 1.1 thorpej * or promote products derived from this software without specific prior 23 1.1 thorpej * written permission. 24 1.1 thorpej * 25 1.1 thorpej * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND 26 1.1 thorpej * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 27 1.1 thorpej * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 28 1.1 thorpej * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC 29 1.1 thorpej * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 30 1.1 thorpej * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 31 1.1 thorpej * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 32 1.1 thorpej * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 33 1.1 thorpej * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 34 1.1 thorpej * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 35 1.1 thorpej * POSSIBILITY OF SUCH DAMAGE. 36 1.1 thorpej */ 37 1.1 thorpej 38 1.1 thorpej /* 39 1.1 thorpej * Support for ATA RAID logical disks. 40 1.1 thorpej * 41 1.1 thorpej * Note that all the RAID happens in software here; the ATA RAID 42 1.1 thorpej * controllers we're dealing with (Promise, etc.) only support 43 1.1 thorpej * configuration data on the component disks, with the BIOS supporting 44 1.1 thorpej * booting from the RAID volumes. 45 1.45 martin * 46 1.30 tron * bio(4) support was written by Juan Romero Pardines <xtraeme (at) gmail.com>. 47 1.1 thorpej */ 48 1.8 lukem 49 1.8 lukem #include <sys/cdefs.h> 50 1.52 rin __KERNEL_RCSID(0, "$NetBSD: ld_ataraid.c,v 1.52 2025/04/13 02:34:02 rin Exp $"); 51 1.1 thorpej 52 1.43 pgoyette #if defined(_KERNEL_OPT) 53 1.30 tron #include "bio.h" 54 1.43 pgoyette #endif 55 1.1 thorpej 56 1.1 thorpej #include <sys/param.h> 57 1.1 thorpej #include <sys/systm.h> 58 1.1 thorpej #include <sys/conf.h> 59 1.1 thorpej #include <sys/kernel.h> 60 1.1 thorpej #include <sys/device.h> 61 1.1 thorpej #include <sys/buf.h> 62 1.13 yamt #include <sys/bufq.h> 63 1.1 thorpej #include <sys/dkio.h> 64 1.1 thorpej #include <sys/disk.h> 65 1.1 thorpej #include <sys/disklabel.h> 66 1.1 thorpej #include <sys/fcntl.h> 67 1.1 thorpej #include <sys/vnode.h> 68 1.16 elad #include <sys/kauth.h> 69 1.43 pgoyette #include <sys/module.h> 70 1.30 tron #if NBIO > 0 71 1.30 tron #include <dev/ata/atavar.h> 72 1.30 tron #include <dev/ata/atareg.h> 73 1.30 tron #include <dev/ata/wdvar.h> 74 1.30 tron #include <dev/biovar.h> 75 1.30 tron #include <dev/scsipi/scsipiconf.h> /* for scsipi_strvis() */ 76 1.30 tron #endif 77 1.1 thorpej 78 1.1 thorpej #include <miscfs/specfs/specdev.h> 79 1.1 thorpej 80 1.1 thorpej #include <dev/ldvar.h> 81 1.1 thorpej 82 1.1 thorpej #include <dev/ata/ata_raidvar.h> 83 1.1 thorpej 84 1.43 pgoyette #include "ioconf.h" 85 1.43 pgoyette 86 1.1 thorpej struct ld_ataraid_softc { 87 1.1 thorpej struct ld_softc sc_ld; 88 1.1 thorpej 89 1.1 thorpej struct ataraid_array_info *sc_aai; 90 1.1 thorpej struct vnode *sc_vnodes[ATA_RAID_MAX_DISKS]; 91 1.1 thorpej 92 1.1 thorpej void (*sc_iodone)(struct buf *); 93 1.36 bsh 94 1.45 martin pool_cache_t sc_cbufpool; 95 1.36 bsh 96 1.45 martin SIMPLEQ_HEAD(, cbuf) sc_cbufq; 97 1.36 bsh 98 1.45 martin void *sc_sih_cookie; 99 1.1 thorpej }; 100 1.1 thorpej 101 1.35 cegger static int ld_ataraid_match(device_t, cfdata_t, void *); 102 1.35 cegger static void ld_ataraid_attach(device_t, device_t, void *); 103 1.1 thorpej 104 1.52 rin static int ld_ataraid_dump(struct ld_softc *, void *, daddr_t, int); 105 1.48 jdolecek static int ld_ataraid_ioctl(struct ld_softc *, u_long, void *, int32_t, 106 1.48 jdolecek bool); 107 1.1 thorpej 108 1.36 bsh static int cbufpool_ctor(void *, void *, int); 109 1.36 bsh static void cbufpool_dtor(void *, void *); 110 1.36 bsh 111 1.36 bsh static void ld_ataraid_start_vstrategy(void *); 112 1.1 thorpej static int ld_ataraid_start_span(struct ld_softc *, struct buf *); 113 1.1 thorpej 114 1.1 thorpej static int ld_ataraid_start_raid0(struct ld_softc *, struct buf *); 115 1.1 thorpej static void ld_ataraid_iodone_raid0(struct buf *); 116 1.1 thorpej 117 1.30 tron #if NBIO > 0 118 1.30 tron static int ld_ataraid_bioctl(device_t, u_long, void *); 119 1.30 tron static int ld_ataraid_bioinq(struct ld_ataraid_softc *, struct bioc_inq *); 120 1.30 tron static int ld_ataraid_biovol(struct ld_ataraid_softc *, struct bioc_vol *); 121 1.30 tron static int ld_ataraid_biodisk(struct ld_ataraid_softc *, 122 1.30 tron struct bioc_disk *); 123 1.30 tron #endif 124 1.30 tron 125 1.27 xtraeme CFATTACH_DECL_NEW(ld_ataraid, sizeof(struct ld_ataraid_softc), 126 1.1 thorpej ld_ataraid_match, ld_ataraid_attach, NULL, NULL); 127 1.1 thorpej 128 1.1 thorpej struct cbuf { 129 1.45 martin struct buf cb_buf; /* new I/O buf */ 130 1.1 thorpej struct buf *cb_obp; /* ptr. to original I/O buf */ 131 1.45 martin struct ld_ataraid_softc *cb_sc; /* pointer to ld softc */ 132 1.1 thorpej u_int cb_comp; /* target component */ 133 1.1 thorpej SIMPLEQ_ENTRY(cbuf) cb_q; /* fifo of component buffers */ 134 1.12 enami struct cbuf *cb_other; /* other cbuf in case of mirror */ 135 1.12 enami int cb_flags; 136 1.45 martin #define CBUF_IODONE 0x00000001 /* I/O is already successfully done */ 137 1.1 thorpej }; 138 1.1 thorpej 139 1.36 bsh #define CBUF_GET() pool_cache_get(sc->sc_cbufpool, PR_NOWAIT); 140 1.36 bsh #define CBUF_PUT(cbp) pool_cache_put(sc->sc_cbufpool, (cbp)) 141 1.1 thorpej 142 1.1 thorpej static int 143 1.27 xtraeme ld_ataraid_match(device_t parent, cfdata_t match, void *aux) 144 1.1 thorpej { 145 1.1 thorpej 146 1.1 thorpej return (1); 147 1.1 thorpej } 148 1.1 thorpej 149 1.1 thorpej static void 150 1.27 xtraeme ld_ataraid_attach(device_t parent, device_t self, void *aux) 151 1.1 thorpej { 152 1.27 xtraeme struct ld_ataraid_softc *sc = device_private(self); 153 1.1 thorpej struct ld_softc *ld = &sc->sc_ld; 154 1.1 thorpej struct ataraid_array_info *aai = aux; 155 1.32 tron struct ataraid_disk_info *adi = NULL; 156 1.1 thorpej const char *level; 157 1.1 thorpej struct vnode *vp; 158 1.1 thorpej char unklev[32]; 159 1.1 thorpej u_int i; 160 1.1 thorpej 161 1.29 tron ld->sc_dv = self; 162 1.29 tron 163 1.45 martin sc->sc_cbufpool = pool_cache_init(sizeof(struct cbuf), 0, 164 1.45 martin 0, 0, "ldcbuf", NULL, IPL_BIO, cbufpool_ctor, cbufpool_dtor, sc); 165 1.45 martin sc->sc_sih_cookie = softint_establish(SOFTINT_BIO, 166 1.45 martin ld_ataraid_start_vstrategy, sc); 167 1.1 thorpej 168 1.1 thorpej sc->sc_aai = aai; /* this data persists */ 169 1.1 thorpej 170 1.1 thorpej ld->sc_maxxfer = MAXPHYS * aai->aai_width; /* XXX */ 171 1.1 thorpej ld->sc_secperunit = aai->aai_capacity; 172 1.1 thorpej ld->sc_secsize = 512; /* XXX */ 173 1.1 thorpej ld->sc_maxqueuecnt = 128; /* XXX */ 174 1.1 thorpej ld->sc_dump = ld_ataraid_dump; 175 1.48 jdolecek ld->sc_ioctl = ld_ataraid_ioctl; 176 1.1 thorpej 177 1.1 thorpej switch (aai->aai_level) { 178 1.1 thorpej case AAI_L_SPAN: 179 1.1 thorpej level = "SPAN"; 180 1.1 thorpej ld->sc_start = ld_ataraid_start_span; 181 1.1 thorpej sc->sc_iodone = ld_ataraid_iodone_raid0; 182 1.1 thorpej break; 183 1.1 thorpej 184 1.1 thorpej case AAI_L_RAID0: 185 1.9 thorpej level = "RAID-0"; 186 1.1 thorpej ld->sc_start = ld_ataraid_start_raid0; 187 1.1 thorpej sc->sc_iodone = ld_ataraid_iodone_raid0; 188 1.1 thorpej break; 189 1.1 thorpej 190 1.1 thorpej case AAI_L_RAID1: 191 1.9 thorpej level = "RAID-1"; 192 1.12 enami ld->sc_start = ld_ataraid_start_raid0; 193 1.12 enami sc->sc_iodone = ld_ataraid_iodone_raid0; 194 1.1 thorpej break; 195 1.1 thorpej 196 1.1 thorpej case AAI_L_RAID0 | AAI_L_RAID1: 197 1.9 thorpej level = "RAID-10"; 198 1.12 enami ld->sc_start = ld_ataraid_start_raid0; 199 1.12 enami sc->sc_iodone = ld_ataraid_iodone_raid0; 200 1.1 thorpej break; 201 1.1 thorpej 202 1.1 thorpej default: 203 1.11 itojun snprintf(unklev, sizeof(unklev), "<unknown level 0x%x>", 204 1.11 itojun aai->aai_level); 205 1.1 thorpej level = unklev; 206 1.1 thorpej } 207 1.1 thorpej 208 1.1 thorpej aprint_naive(": ATA %s array\n", level); 209 1.1 thorpej aprint_normal(": %s ATA %s array\n", 210 1.1 thorpej ata_raid_type_name(aai->aai_type), level); 211 1.1 thorpej 212 1.1 thorpej if (ld->sc_start == NULL) { 213 1.29 tron aprint_error_dev(ld->sc_dv, "unsupported array type\n"); 214 1.1 thorpej return; 215 1.1 thorpej } 216 1.1 thorpej 217 1.1 thorpej /* 218 1.1 thorpej * We get a geometry from the device; use it. 219 1.1 thorpej */ 220 1.1 thorpej ld->sc_nheads = aai->aai_heads; 221 1.1 thorpej ld->sc_nsectors = aai->aai_sectors; 222 1.1 thorpej ld->sc_ncylinders = aai->aai_cylinders; 223 1.1 thorpej 224 1.1 thorpej /* 225 1.1 thorpej * Configure all the component disks. 226 1.1 thorpej */ 227 1.1 thorpej for (i = 0; i < aai->aai_ndisks; i++) { 228 1.32 tron adi = &aai->aai_disks[i]; 229 1.51 andvar vp = NULL; 230 1.51 andvar if (adi->adi_status & 231 1.51 andvar (ADI_S_ONLINE | ADI_S_ASSIGNED | ADI_S_SPARE)) 232 1.51 andvar vp = ata_raid_disk_vnode_find(adi); 233 1.51 andvar 234 1.32 tron if (vp == NULL) { 235 1.1 thorpej /* 236 1.1 thorpej * XXX This is bogus. We should just mark the 237 1.1 thorpej * XXX component as FAILED, and write-back new 238 1.1 thorpej * XXX config blocks. 239 1.1 thorpej */ 240 1.3 thorpej break; 241 1.1 thorpej } 242 1.1 thorpej sc->sc_vnodes[i] = vp; 243 1.1 thorpej } 244 1.3 thorpej if (i == aai->aai_ndisks) { 245 1.3 thorpej ld->sc_flags = LDF_ENABLED; 246 1.3 thorpej goto finish; 247 1.3 thorpej } 248 1.1 thorpej 249 1.1 thorpej for (i = 0; i < aai->aai_ndisks; i++) { 250 1.1 thorpej vp = sc->sc_vnodes[i]; 251 1.1 thorpej sc->sc_vnodes[i] = NULL; 252 1.3 thorpej if (vp != NULL) 253 1.25 ad (void) vn_close(vp, FREAD|FWRITE, NOCRED); 254 1.1 thorpej } 255 1.3 thorpej 256 1.3 thorpej finish: 257 1.30 tron #if NBIO > 0 258 1.30 tron if (bio_register(self, ld_ataraid_bioctl) != 0) 259 1.30 tron panic("%s: bioctl registration failed\n", 260 1.30 tron device_xname(ld->sc_dv)); 261 1.30 tron #endif 262 1.42 jdolecek SIMPLEQ_INIT(&sc->sc_cbufq); 263 1.42 jdolecek ldattach(ld, BUFQ_DISK_DEFAULT_STRAT); 264 1.1 thorpej } 265 1.1 thorpej 266 1.36 bsh static int 267 1.36 bsh cbufpool_ctor(void *arg, void *obj, int flags) 268 1.36 bsh { 269 1.45 martin struct ld_ataraid_softc *sc = arg; 270 1.45 martin struct ld_softc *ld = &sc->sc_ld; 271 1.45 martin struct cbuf *cbp = obj; 272 1.45 martin 273 1.45 martin /* We release/reacquire the spinlock before calling buf_init() */ 274 1.45 martin mutex_exit(&ld->sc_mutex); 275 1.45 martin buf_init(&cbp->cb_buf); 276 1.45 martin mutex_enter(&ld->sc_mutex); 277 1.36 bsh 278 1.45 martin return 0; 279 1.36 bsh } 280 1.36 bsh 281 1.36 bsh static void 282 1.36 bsh cbufpool_dtor(void *arg, void *obj) 283 1.36 bsh { 284 1.45 martin struct cbuf *cbp = obj; 285 1.36 bsh 286 1.45 martin buf_destroy(&cbp->cb_buf); 287 1.36 bsh } 288 1.36 bsh 289 1.1 thorpej static struct cbuf * 290 1.1 thorpej ld_ataraid_make_cbuf(struct ld_ataraid_softc *sc, struct buf *bp, 291 1.19 christos u_int comp, daddr_t bn, void *addr, long bcount) 292 1.1 thorpej { 293 1.1 thorpej struct cbuf *cbp; 294 1.1 thorpej 295 1.1 thorpej cbp = CBUF_GET(); 296 1.1 thorpej if (cbp == NULL) 297 1.45 martin return NULL; 298 1.23 ad cbp->cb_buf.b_flags = bp->b_flags; 299 1.23 ad cbp->cb_buf.b_oflags = bp->b_oflags; 300 1.23 ad cbp->cb_buf.b_cflags = bp->b_cflags; 301 1.1 thorpej cbp->cb_buf.b_iodone = sc->sc_iodone; 302 1.1 thorpej cbp->cb_buf.b_proc = bp->b_proc; 303 1.1 thorpej cbp->cb_buf.b_vp = sc->sc_vnodes[comp]; 304 1.38 rmind cbp->cb_buf.b_objlock = sc->sc_vnodes[comp]->v_interlock; 305 1.1 thorpej cbp->cb_buf.b_blkno = bn + sc->sc_aai->aai_offset; 306 1.1 thorpej cbp->cb_buf.b_data = addr; 307 1.1 thorpej cbp->cb_buf.b_bcount = bcount; 308 1.1 thorpej 309 1.1 thorpej /* Context for iodone */ 310 1.1 thorpej cbp->cb_obp = bp; 311 1.1 thorpej cbp->cb_sc = sc; 312 1.1 thorpej cbp->cb_comp = comp; 313 1.12 enami cbp->cb_other = NULL; 314 1.12 enami cbp->cb_flags = 0; 315 1.1 thorpej 316 1.45 martin return cbp; 317 1.36 bsh } 318 1.36 bsh 319 1.36 bsh static void 320 1.36 bsh ld_ataraid_start_vstrategy(void *arg) 321 1.36 bsh { 322 1.45 martin struct ld_ataraid_softc *sc = arg; 323 1.45 martin struct cbuf *cbp; 324 1.36 bsh 325 1.45 martin while ((cbp = SIMPLEQ_FIRST(&sc->sc_cbufq)) != NULL) { 326 1.45 martin SIMPLEQ_REMOVE_HEAD(&sc->sc_cbufq, cb_q); 327 1.45 martin if ((cbp->cb_buf.b_flags & B_READ) == 0) { 328 1.45 martin mutex_enter(cbp->cb_buf.b_vp->v_interlock); 329 1.45 martin cbp->cb_buf.b_vp->v_numoutput++; 330 1.45 martin mutex_exit(cbp->cb_buf.b_vp->v_interlock); 331 1.45 martin } 332 1.45 martin VOP_STRATEGY(cbp->cb_buf.b_vp, &cbp->cb_buf); 333 1.45 martin } 334 1.1 thorpej } 335 1.1 thorpej 336 1.1 thorpej static int 337 1.1 thorpej ld_ataraid_start_span(struct ld_softc *ld, struct buf *bp) 338 1.1 thorpej { 339 1.1 thorpej struct ld_ataraid_softc *sc = (void *) ld; 340 1.1 thorpej struct ataraid_array_info *aai = sc->sc_aai; 341 1.1 thorpej struct ataraid_disk_info *adi; 342 1.1 thorpej struct cbuf *cbp; 343 1.19 christos char *addr; 344 1.1 thorpej daddr_t bn; 345 1.1 thorpej long bcount, rcount; 346 1.1 thorpej u_int comp; 347 1.1 thorpej 348 1.1 thorpej /* Allocate component buffers. */ 349 1.1 thorpej addr = bp->b_data; 350 1.1 thorpej 351 1.1 thorpej /* Find the first component. */ 352 1.1 thorpej comp = 0; 353 1.1 thorpej adi = &aai->aai_disks[comp]; 354 1.1 thorpej bn = bp->b_rawblkno; 355 1.1 thorpej while (bn >= adi->adi_compsize) { 356 1.1 thorpej bn -= adi->adi_compsize; 357 1.1 thorpej adi = &aai->aai_disks[++comp]; 358 1.1 thorpej } 359 1.1 thorpej 360 1.1 thorpej bp->b_resid = bp->b_bcount; 361 1.1 thorpej 362 1.1 thorpej for (bcount = bp->b_bcount; bcount > 0; bcount -= rcount) { 363 1.1 thorpej rcount = bp->b_bcount; 364 1.1 thorpej if ((adi->adi_compsize - bn) < btodb(rcount)) 365 1.1 thorpej rcount = dbtob(adi->adi_compsize - bn); 366 1.1 thorpej 367 1.1 thorpej cbp = ld_ataraid_make_cbuf(sc, bp, comp, bn, addr, rcount); 368 1.1 thorpej if (cbp == NULL) { 369 1.1 thorpej /* Free the already allocated component buffers. */ 370 1.45 martin while ((cbp = SIMPLEQ_FIRST(&sc->sc_cbufq)) != NULL) { 371 1.45 martin SIMPLEQ_REMOVE_HEAD(&sc->sc_cbufq, cb_q); 372 1.1 thorpej CBUF_PUT(cbp); 373 1.1 thorpej } 374 1.45 martin return EAGAIN; 375 1.1 thorpej } 376 1.1 thorpej 377 1.1 thorpej /* 378 1.1 thorpej * For a span, we always know we advance to the next disk, 379 1.1 thorpej * and always start at offset 0 on that disk. 380 1.1 thorpej */ 381 1.1 thorpej adi = &aai->aai_disks[++comp]; 382 1.1 thorpej bn = 0; 383 1.1 thorpej 384 1.45 martin SIMPLEQ_INSERT_TAIL(&sc->sc_cbufq, cbp, cb_q); 385 1.1 thorpej addr += rcount; 386 1.1 thorpej } 387 1.1 thorpej 388 1.1 thorpej /* Now fire off the requests. */ 389 1.45 martin softint_schedule(sc->sc_sih_cookie); 390 1.1 thorpej 391 1.45 martin return 0; 392 1.1 thorpej } 393 1.1 thorpej 394 1.1 thorpej static int 395 1.1 thorpej ld_ataraid_start_raid0(struct ld_softc *ld, struct buf *bp) 396 1.1 thorpej { 397 1.45 martin struct ld_ataraid_softc *sc = (void *)ld; 398 1.1 thorpej struct ataraid_array_info *aai = sc->sc_aai; 399 1.12 enami struct ataraid_disk_info *adi; 400 1.12 enami struct cbuf *cbp, *other_cbp; 401 1.19 christos char *addr; 402 1.1 thorpej daddr_t bn, cbn, tbn, off; 403 1.1 thorpej long bcount, rcount; 404 1.1 thorpej u_int comp; 405 1.12 enami const int read = bp->b_flags & B_READ; 406 1.12 enami const int mirror = aai->aai_level & AAI_L_RAID1; 407 1.45 martin int error = 0; 408 1.1 thorpej 409 1.1 thorpej /* Allocate component buffers. */ 410 1.1 thorpej addr = bp->b_data; 411 1.1 thorpej bn = bp->b_rawblkno; 412 1.1 thorpej 413 1.1 thorpej bp->b_resid = bp->b_bcount; 414 1.1 thorpej 415 1.1 thorpej for (bcount = bp->b_bcount; bcount > 0; bcount -= rcount) { 416 1.1 thorpej tbn = bn / aai->aai_interleave; 417 1.1 thorpej off = bn % aai->aai_interleave; 418 1.1 thorpej 419 1.1 thorpej if (__predict_false(tbn == aai->aai_capacity / 420 1.1 thorpej aai->aai_interleave)) { 421 1.1 thorpej /* Last stripe. */ 422 1.1 thorpej daddr_t sz = (aai->aai_capacity - 423 1.1 thorpej (tbn * aai->aai_interleave)) / 424 1.1 thorpej aai->aai_width; 425 1.1 thorpej comp = off / sz; 426 1.1 thorpej cbn = ((tbn / aai->aai_width) * aai->aai_interleave) + 427 1.1 thorpej (off % sz); 428 1.46 riastrad rcount = uimin(bcount, dbtob(sz)); 429 1.1 thorpej } else { 430 1.1 thorpej comp = tbn % aai->aai_width; 431 1.1 thorpej cbn = ((tbn / aai->aai_width) * aai->aai_interleave) + 432 1.1 thorpej off; 433 1.46 riastrad rcount = uimin(bcount, dbtob(aai->aai_interleave - off)); 434 1.1 thorpej } 435 1.1 thorpej 436 1.12 enami /* 437 1.12 enami * See if a component is valid. 438 1.12 enami */ 439 1.12 enami try_mirror: 440 1.12 enami adi = &aai->aai_disks[comp]; 441 1.12 enami if ((adi->adi_status & ADI_S_ONLINE) == 0) { 442 1.12 enami if (mirror && comp < aai->aai_width) { 443 1.12 enami comp += aai->aai_width; 444 1.12 enami goto try_mirror; 445 1.12 enami } 446 1.12 enami 447 1.12 enami /* 448 1.12 enami * No component available. 449 1.12 enami */ 450 1.12 enami error = EIO; 451 1.12 enami goto free_and_exit; 452 1.12 enami } 453 1.12 enami 454 1.1 thorpej cbp = ld_ataraid_make_cbuf(sc, bp, comp, cbn, addr, rcount); 455 1.1 thorpej if (cbp == NULL) { 456 1.12 enami resource_shortage: 457 1.12 enami error = EAGAIN; 458 1.12 enami free_and_exit: 459 1.1 thorpej /* Free the already allocated component buffers. */ 460 1.45 martin while ((cbp = SIMPLEQ_FIRST(&sc->sc_cbufq)) != NULL) { 461 1.45 martin SIMPLEQ_REMOVE_HEAD(&sc->sc_cbufq, cb_q); 462 1.1 thorpej CBUF_PUT(cbp); 463 1.1 thorpej } 464 1.45 martin return error; 465 1.1 thorpej } 466 1.45 martin SIMPLEQ_INSERT_TAIL(&sc->sc_cbufq, cbp, cb_q); 467 1.12 enami if (mirror && !read && comp < aai->aai_width) { 468 1.12 enami comp += aai->aai_width; 469 1.12 enami adi = &aai->aai_disks[comp]; 470 1.12 enami if (adi->adi_status & ADI_S_ONLINE) { 471 1.12 enami other_cbp = ld_ataraid_make_cbuf(sc, bp, 472 1.12 enami comp, cbn, addr, rcount); 473 1.12 enami if (other_cbp == NULL) 474 1.12 enami goto resource_shortage; 475 1.45 martin SIMPLEQ_INSERT_TAIL(&sc->sc_cbufq, 476 1.45 martin other_cbp, cb_q); 477 1.12 enami other_cbp->cb_other = cbp; 478 1.12 enami cbp->cb_other = other_cbp; 479 1.12 enami } 480 1.12 enami } 481 1.1 thorpej bn += btodb(rcount); 482 1.1 thorpej addr += rcount; 483 1.1 thorpej } 484 1.1 thorpej 485 1.1 thorpej /* Now fire off the requests. */ 486 1.45 martin softint_schedule(sc->sc_sih_cookie); 487 1.1 thorpej 488 1.45 martin return error; 489 1.1 thorpej } 490 1.1 thorpej 491 1.1 thorpej /* 492 1.1 thorpej * Called at interrupt time. Mark the component as done and if all 493 1.1 thorpej * components are done, take an "interrupt". 494 1.1 thorpej */ 495 1.1 thorpej static void 496 1.1 thorpej ld_ataraid_iodone_raid0(struct buf *vbp) 497 1.1 thorpej { 498 1.12 enami struct cbuf *cbp = (struct cbuf *) vbp, *other_cbp; 499 1.1 thorpej struct buf *bp = cbp->cb_obp; 500 1.1 thorpej struct ld_ataraid_softc *sc = cbp->cb_sc; 501 1.12 enami struct ataraid_array_info *aai = sc->sc_aai; 502 1.12 enami struct ataraid_disk_info *adi; 503 1.1 thorpej long count; 504 1.12 enami int s, iodone; 505 1.1 thorpej 506 1.1 thorpej s = splbio(); 507 1.50 ad KERNEL_LOCK(1, NULL); /* XXXSMP */ 508 1.1 thorpej 509 1.12 enami iodone = cbp->cb_flags & CBUF_IODONE; 510 1.12 enami other_cbp = cbp->cb_other; 511 1.12 enami if (other_cbp != NULL) 512 1.12 enami /* You are alone */ 513 1.12 enami other_cbp->cb_other = NULL; 514 1.12 enami 515 1.21 ad if (cbp->cb_buf.b_error != 0) { 516 1.12 enami /* 517 1.12 enami * Mark this component broken. 518 1.12 enami */ 519 1.12 enami adi = &aai->aai_disks[cbp->cb_comp]; 520 1.12 enami adi->adi_status &= ~ADI_S_ONLINE; 521 1.12 enami 522 1.12 enami printf("%s: error %d on component %d (%s)\n", 523 1.29 tron device_xname(sc->sc_ld.sc_dv), bp->b_error, cbp->cb_comp, 524 1.26 cegger device_xname(adi->adi_dev)); 525 1.12 enami 526 1.12 enami /* 527 1.12 enami * If we didn't see an error yet and we are reading 528 1.12 enami * RAID1 disk, try another component. 529 1.12 enami */ 530 1.21 ad if (bp->b_error == 0 && 531 1.12 enami (cbp->cb_buf.b_flags & B_READ) != 0 && 532 1.12 enami (aai->aai_level & AAI_L_RAID1) != 0 && 533 1.12 enami cbp->cb_comp < aai->aai_width) { 534 1.12 enami cbp->cb_comp += aai->aai_width; 535 1.12 enami adi = &aai->aai_disks[cbp->cb_comp]; 536 1.12 enami if (adi->adi_status & ADI_S_ONLINE) { 537 1.21 ad cbp->cb_buf.b_error = 0; 538 1.12 enami VOP_STRATEGY(cbp->cb_buf.b_vp, &cbp->cb_buf); 539 1.12 enami goto out; 540 1.12 enami } 541 1.12 enami } 542 1.12 enami 543 1.12 enami if (iodone || other_cbp != NULL) 544 1.12 enami /* 545 1.12 enami * If I/O on other component successfully done 546 1.12 enami * or the I/O is still in progress, no need 547 1.12 enami * to tell an error to upper layer. 548 1.12 enami */ 549 1.12 enami ; 550 1.12 enami else { 551 1.12 enami bp->b_error = cbp->cb_buf.b_error ? 552 1.12 enami cbp->cb_buf.b_error : EIO; 553 1.12 enami } 554 1.1 thorpej 555 1.1 thorpej /* XXX Update component config blocks. */ 556 1.1 thorpej 557 1.12 enami } else { 558 1.12 enami /* 559 1.12 enami * If other I/O is still in progress, tell it that 560 1.12 enami * our I/O is successfully done. 561 1.12 enami */ 562 1.12 enami if (other_cbp != NULL) 563 1.12 enami other_cbp->cb_flags |= CBUF_IODONE; 564 1.1 thorpej } 565 1.1 thorpej count = cbp->cb_buf.b_bcount; 566 1.1 thorpej CBUF_PUT(cbp); 567 1.1 thorpej 568 1.12 enami if (other_cbp != NULL) 569 1.12 enami goto out; 570 1.12 enami 571 1.1 thorpej /* If all done, "interrupt". */ 572 1.1 thorpej bp->b_resid -= count; 573 1.1 thorpej if (bp->b_resid < 0) 574 1.1 thorpej panic("ld_ataraid_iodone_raid0: count"); 575 1.1 thorpej if (bp->b_resid == 0) 576 1.1 thorpej lddone(&sc->sc_ld, bp); 577 1.12 enami 578 1.12 enami out: 579 1.50 ad KERNEL_UNLOCK_ONE(NULL); /* XXXSMP */ 580 1.1 thorpej splx(s); 581 1.1 thorpej } 582 1.1 thorpej 583 1.1 thorpej static int 584 1.52 rin ld_ataraid_dump(struct ld_softc *sc, void *data, daddr_t blkno, int blkcnt) 585 1.1 thorpej { 586 1.1 thorpej 587 1.1 thorpej return (EIO); 588 1.1 thorpej } 589 1.30 tron 590 1.30 tron #if NBIO > 0 591 1.30 tron static int 592 1.30 tron ld_ataraid_bioctl(device_t self, u_long cmd, void *addr) 593 1.30 tron { 594 1.30 tron struct ld_ataraid_softc *sc = device_private(self); 595 1.30 tron int error = 0; 596 1.30 tron 597 1.30 tron switch (cmd) { 598 1.30 tron case BIOCINQ: 599 1.30 tron error = ld_ataraid_bioinq(sc, (struct bioc_inq *)addr); 600 1.30 tron break; 601 1.30 tron case BIOCVOL: 602 1.30 tron error = ld_ataraid_biovol(sc, (struct bioc_vol *)addr); 603 1.30 tron break; 604 1.30 tron case BIOCDISK: 605 1.30 tron error = ld_ataraid_biodisk(sc, (struct bioc_disk *)addr); 606 1.30 tron break; 607 1.30 tron default: 608 1.30 tron error = ENOTTY; 609 1.30 tron break; 610 1.30 tron } 611 1.30 tron 612 1.30 tron return error; 613 1.30 tron } 614 1.30 tron 615 1.30 tron static int 616 1.30 tron ld_ataraid_bioinq(struct ld_ataraid_softc *sc, struct bioc_inq *bi) 617 1.30 tron { 618 1.30 tron struct ataraid_array_info *aai = sc->sc_aai; 619 1.30 tron 620 1.30 tron /* there's always one volume per ld device */ 621 1.30 tron bi->bi_novol = 1; 622 1.30 tron bi->bi_nodisk = aai->aai_ndisks; 623 1.30 tron 624 1.30 tron return 0; 625 1.30 tron } 626 1.30 tron 627 1.30 tron static int 628 1.30 tron ld_ataraid_biovol(struct ld_ataraid_softc *sc, struct bioc_vol *bv) 629 1.30 tron { 630 1.30 tron struct ataraid_array_info *aai = sc->sc_aai; 631 1.30 tron struct ld_softc *ld = &sc->sc_ld; 632 1.45 martin #define to_kibytes(ld,s) (ld->sc_secsize*(s)/1024) 633 1.30 tron 634 1.30 tron /* Fill in data for _this_ volume */ 635 1.30 tron bv->bv_percent = -1; 636 1.30 tron bv->bv_seconds = 0; 637 1.30 tron 638 1.30 tron switch (aai->aai_status) { 639 1.30 tron case AAI_S_READY: 640 1.30 tron bv->bv_status = BIOC_SVONLINE; 641 1.30 tron break; 642 1.30 tron case AAI_S_DEGRADED: 643 1.30 tron bv->bv_status = BIOC_SVDEGRADED; 644 1.30 tron break; 645 1.30 tron } 646 1.30 tron 647 1.30 tron bv->bv_size = ld->sc_secsize * ld->sc_secperunit; 648 1.30 tron 649 1.30 tron switch (aai->aai_level) { 650 1.30 tron case AAI_L_SPAN: 651 1.30 tron case AAI_L_RAID0: 652 1.37 bsh bv->bv_stripe_size = to_kibytes(ld, aai->aai_interleave); 653 1.30 tron bv->bv_level = 0; 654 1.30 tron break; 655 1.30 tron case AAI_L_RAID1: 656 1.30 tron bv->bv_stripe_size = 0; 657 1.30 tron bv->bv_level = 1; 658 1.30 tron break; 659 1.30 tron case AAI_L_RAID5: 660 1.37 bsh bv->bv_stripe_size = to_kibytes(ld, aai->aai_interleave); 661 1.30 tron bv->bv_level = 5; 662 1.30 tron break; 663 1.30 tron } 664 1.30 tron 665 1.30 tron bv->bv_nodisk = aai->aai_ndisks; 666 1.30 tron strlcpy(bv->bv_dev, device_xname(ld->sc_dv), sizeof(bv->bv_dev)); 667 1.31 tron if (aai->aai_name[0] != '\0') 668 1.31 tron strlcpy(bv->bv_vendor, aai->aai_name, sizeof(bv->bv_vendor)); 669 1.30 tron 670 1.30 tron return 0; 671 1.30 tron } 672 1.30 tron 673 1.30 tron static int 674 1.30 tron ld_ataraid_biodisk(struct ld_ataraid_softc *sc, struct bioc_disk *bd) 675 1.30 tron { 676 1.30 tron struct ataraid_array_info *aai = sc->sc_aai; 677 1.30 tron struct ataraid_disk_info *adi; 678 1.30 tron struct ld_softc *ld = &sc->sc_ld; 679 1.30 tron struct atabus_softc *atabus; 680 1.30 tron struct wd_softc *wd; 681 1.30 tron char model[81], serial[41], rev[17]; 682 1.30 tron 683 1.30 tron /* sanity check */ 684 1.30 tron if (bd->bd_diskid > aai->aai_ndisks) 685 1.30 tron return EINVAL; 686 1.30 tron 687 1.30 tron adi = &aai->aai_disks[bd->bd_diskid]; 688 1.30 tron atabus = device_private(device_parent(adi->adi_dev)); 689 1.30 tron wd = device_private(adi->adi_dev); 690 1.30 tron 691 1.30 tron /* fill in data for _this_ disk */ 692 1.30 tron switch (adi->adi_status) { 693 1.30 tron case ADI_S_ONLINE | ADI_S_ASSIGNED: 694 1.30 tron bd->bd_status = BIOC_SDONLINE; 695 1.30 tron break; 696 1.30 tron case ADI_S_SPARE: 697 1.30 tron bd->bd_status = BIOC_SDHOTSPARE; 698 1.30 tron break; 699 1.30 tron default: 700 1.30 tron bd->bd_status = BIOC_SDOFFLINE; 701 1.30 tron break; 702 1.30 tron } 703 1.30 tron 704 1.30 tron bd->bd_channel = 0; 705 1.30 tron bd->bd_target = atabus->sc_chan->ch_channel; 706 1.30 tron bd->bd_lun = 0; 707 1.30 tron bd->bd_size = (wd->sc_capacity * ld->sc_secsize) - aai->aai_reserved; 708 1.30 tron 709 1.30 tron strlcpy(bd->bd_procdev, device_xname(adi->adi_dev), 710 1.30 tron sizeof(bd->bd_procdev)); 711 1.30 tron 712 1.41 christos strnvisx(serial, sizeof(serial), wd->sc_params.atap_serial, 713 1.41 christos sizeof(wd->sc_params.atap_serial), VIS_TRIM|VIS_SAFE|VIS_OCTAL); 714 1.41 christos strnvisx(model, sizeof(model), wd->sc_params.atap_model, 715 1.41 christos sizeof(wd->sc_params.atap_model), VIS_TRIM|VIS_SAFE|VIS_OCTAL); 716 1.41 christos strnvisx(rev, sizeof(rev), wd->sc_params.atap_revision, 717 1.41 christos sizeof(wd->sc_params.atap_revision), VIS_TRIM|VIS_SAFE|VIS_OCTAL); 718 1.30 tron 719 1.30 tron snprintf(bd->bd_vendor, sizeof(bd->bd_vendor), "%s %s", model, rev); 720 1.30 tron strlcpy(bd->bd_serial, serial, sizeof(bd->bd_serial)); 721 1.30 tron 722 1.30 tron return 0; 723 1.30 tron } 724 1.30 tron #endif /* NBIO > 0 */ 725 1.43 pgoyette 726 1.48 jdolecek static int 727 1.48 jdolecek ld_ataraid_ioctl(struct ld_softc *ld, u_long cmd, void *addr, int32_t flag, 728 1.48 jdolecek bool poll) 729 1.48 jdolecek { 730 1.48 jdolecek struct ld_ataraid_softc *sc = (void *)ld; 731 1.48 jdolecek int error, i, j; 732 1.48 jdolecek kauth_cred_t uc; 733 1.48 jdolecek 734 1.48 jdolecek uc = kauth_cred_get(); 735 1.48 jdolecek 736 1.48 jdolecek switch (cmd) { 737 1.48 jdolecek case DIOCGCACHE: 738 1.48 jdolecek { 739 1.48 jdolecek int dkcache = 0; 740 1.48 jdolecek 741 1.48 jdolecek /* 742 1.48 jdolecek * We pass this call down to all components and report 743 1.48 jdolecek * intersection of the flags returned by the components. 744 1.48 jdolecek * If any errors out, we return error. ATA RAID components 745 1.48 jdolecek * can only change via BIOS, device feature flags will remain 746 1.48 jdolecek * static. RCE/WCE can change if set directly on underlying 747 1.48 jdolecek * device. 748 1.48 jdolecek */ 749 1.48 jdolecek for (error = 0, i = 0; i < sc->sc_aai->aai_ndisks; i++) { 750 1.48 jdolecek KASSERT(sc->sc_vnodes[i] != NULL); 751 1.48 jdolecek 752 1.48 jdolecek error = VOP_IOCTL(sc->sc_vnodes[i], cmd, &j, 753 1.48 jdolecek flag, uc); 754 1.48 jdolecek if (error) 755 1.48 jdolecek break; 756 1.48 jdolecek 757 1.48 jdolecek if (i == 0) 758 1.48 jdolecek dkcache = j; 759 1.48 jdolecek else 760 1.48 jdolecek dkcache = DKCACHE_COMBINE(dkcache, j); 761 1.48 jdolecek } 762 1.48 jdolecek 763 1.48 jdolecek *((int *)addr) = dkcache; 764 1.48 jdolecek break; 765 1.48 jdolecek } 766 1.48 jdolecek 767 1.48 jdolecek case DIOCCACHESYNC: 768 1.48 jdolecek { 769 1.48 jdolecek /* 770 1.48 jdolecek * We pass this call down to all components and report 771 1.48 jdolecek * the first error we encounter. 772 1.48 jdolecek */ 773 1.48 jdolecek for (error = 0, i = 0; i < sc->sc_aai->aai_ndisks; i++) { 774 1.48 jdolecek KASSERT(sc->sc_vnodes[i] != NULL); 775 1.48 jdolecek 776 1.48 jdolecek j = VOP_IOCTL(sc->sc_vnodes[i], cmd, addr, 777 1.48 jdolecek flag, uc); 778 1.48 jdolecek if (j != 0 && error == 0) 779 1.48 jdolecek error = j; 780 1.48 jdolecek } 781 1.48 jdolecek break; 782 1.48 jdolecek } 783 1.48 jdolecek 784 1.48 jdolecek default: 785 1.48 jdolecek error = EPASSTHROUGH; 786 1.48 jdolecek break; 787 1.48 jdolecek } 788 1.48 jdolecek 789 1.48 jdolecek return error; 790 1.48 jdolecek } 791 1.48 jdolecek 792 1.43 pgoyette MODULE(MODULE_CLASS_DRIVER, ld_ataraid, "ld,ataraid"); 793 1.43 pgoyette 794 1.43 pgoyette #ifdef _MODULE 795 1.43 pgoyette /* 796 1.43 pgoyette * XXX Don't allow ioconf.c to redefine the "struct cfdriver ld_ataraid" 797 1.43 pgoyette * XXX it will be defined in the common-code module 798 1.43 pgoyette */ 799 1.45 martin #undef CFDRIVER_DECL 800 1.43 pgoyette #define CFDRIVER_DECL(name, class, attr) 801 1.43 pgoyette #include "ioconf.c" 802 1.43 pgoyette #endif 803 1.43 pgoyette 804 1.43 pgoyette static int 805 1.43 pgoyette ld_ataraid_modcmd(modcmd_t cmd, void *opaque) 806 1.43 pgoyette { 807 1.43 pgoyette #ifdef _MODULE 808 1.43 pgoyette /* 809 1.43 pgoyette * We ignore the cfdriver_vec[] that ioconf provides, since 810 1.43 pgoyette * the cfdrivers are attached already. 811 1.43 pgoyette */ 812 1.43 pgoyette static struct cfdriver * const no_cfdriver_vec[] = { NULL }; 813 1.43 pgoyette #endif 814 1.43 pgoyette int error = 0; 815 1.43 pgoyette 816 1.43 pgoyette #ifdef _MODULE 817 1.43 pgoyette switch (cmd) { 818 1.43 pgoyette case MODULE_CMD_INIT: 819 1.43 pgoyette error = config_init_component(no_cfdriver_vec, 820 1.43 pgoyette cfattach_ioconf_ld_ataraid, cfdata_ioconf_ld_ataraid); 821 1.43 pgoyette break; 822 1.43 pgoyette case MODULE_CMD_FINI: 823 1.43 pgoyette error = config_fini_component(no_cfdriver_vec, 824 1.43 pgoyette cfattach_ioconf_ld_ataraid, cfdata_ioconf_ld_ataraid); 825 1.43 pgoyette break; 826 1.43 pgoyette default: 827 1.43 pgoyette error = ENOTTY; 828 1.43 pgoyette break; 829 1.43 pgoyette } 830 1.43 pgoyette #endif 831 1.43 pgoyette 832 1.43 pgoyette return error; 833 1.43 pgoyette } 834