1*249d29c8SSascha Wildner /*- 2*249d29c8SSascha Wildner * Copyright (c) 2006 IronPort Systems 3*249d29c8SSascha Wildner * All rights reserved. 4*249d29c8SSascha Wildner * 5*249d29c8SSascha Wildner * Redistribution and use in source and binary forms, with or without 6*249d29c8SSascha Wildner * modification, are permitted provided that the following conditions 7*249d29c8SSascha Wildner * are met: 8*249d29c8SSascha Wildner * 1. Redistributions of source code must retain the above copyright 9*249d29c8SSascha Wildner * notice, this list of conditions and the following disclaimer. 10*249d29c8SSascha Wildner * 2. Redistributions in binary form must reproduce the above copyright 11*249d29c8SSascha Wildner * notice, this list of conditions and the following disclaimer in the 12*249d29c8SSascha Wildner * documentation and/or other materials provided with the distribution. 13*249d29c8SSascha Wildner * 14*249d29c8SSascha Wildner * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15*249d29c8SSascha Wildner * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16*249d29c8SSascha Wildner * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17*249d29c8SSascha Wildner * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18*249d29c8SSascha Wildner * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19*249d29c8SSascha Wildner * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20*249d29c8SSascha Wildner * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21*249d29c8SSascha Wildner * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22*249d29c8SSascha Wildner * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23*249d29c8SSascha Wildner * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24*249d29c8SSascha Wildner * SUCH DAMAGE. 25*249d29c8SSascha Wildner * 26*249d29c8SSascha Wildner * $FreeBSD: src/sys/dev/mfi/mfi_disk.c,v 1.8 2008/11/17 23:30:19 jhb Exp $ 27*249d29c8SSascha Wildner */ 28*249d29c8SSascha Wildner 29*249d29c8SSascha Wildner #include "opt_mfi.h" 30*249d29c8SSascha Wildner 31*249d29c8SSascha Wildner #include <sys/param.h> 32*249d29c8SSascha Wildner #include <sys/systm.h> 33*249d29c8SSascha Wildner #include <sys/kernel.h> 34*249d29c8SSascha Wildner #include <sys/module.h> 35*249d29c8SSascha Wildner #include <sys/malloc.h> 36*249d29c8SSascha Wildner #include <sys/uio.h> 37*249d29c8SSascha Wildner 38*249d29c8SSascha Wildner #include <sys/buf2.h> 39*249d29c8SSascha Wildner #include <sys/bus.h> 40*249d29c8SSascha Wildner #include <sys/conf.h> 41*249d29c8SSascha Wildner #include <sys/disk.h> 42*249d29c8SSascha Wildner 43*249d29c8SSascha Wildner #include <vm/vm.h> 44*249d29c8SSascha Wildner #include <vm/pmap.h> 45*249d29c8SSascha Wildner 46*249d29c8SSascha Wildner #include <machine/md_var.h> 47*249d29c8SSascha Wildner #include <sys/rman.h> 48*249d29c8SSascha Wildner 49*249d29c8SSascha Wildner #include <dev/raid/mfi/mfireg.h> 50*249d29c8SSascha Wildner #include <dev/raid/mfi/mfi_ioctl.h> 51*249d29c8SSascha Wildner #include <dev/raid/mfi/mfivar.h> 52*249d29c8SSascha Wildner 53*249d29c8SSascha Wildner static int mfi_disk_probe(device_t dev); 54*249d29c8SSascha Wildner static int mfi_disk_attach(device_t dev); 55*249d29c8SSascha Wildner static int mfi_disk_detach(device_t dev); 56*249d29c8SSascha Wildner 57*249d29c8SSascha Wildner static d_open_t mfi_disk_open; 58*249d29c8SSascha Wildner static d_close_t mfi_disk_close; 59*249d29c8SSascha Wildner static d_strategy_t mfi_disk_strategy; 60*249d29c8SSascha Wildner static d_dump_t mfi_disk_dump; 61*249d29c8SSascha Wildner 62*249d29c8SSascha Wildner static struct dev_ops mfi_disk_ops = { 63*249d29c8SSascha Wildner { "mfid", 0, D_DISK }, 64*249d29c8SSascha Wildner .d_open = mfi_disk_open, 65*249d29c8SSascha Wildner .d_close = mfi_disk_close, 66*249d29c8SSascha Wildner .d_strategy = mfi_disk_strategy, 67*249d29c8SSascha Wildner .d_dump = mfi_disk_dump, 68*249d29c8SSascha Wildner }; 69*249d29c8SSascha Wildner 70*249d29c8SSascha Wildner static devclass_t mfi_disk_devclass; 71*249d29c8SSascha Wildner 72*249d29c8SSascha Wildner static device_method_t mfi_disk_methods[] = { 73*249d29c8SSascha Wildner DEVMETHOD(device_probe, mfi_disk_probe), 74*249d29c8SSascha Wildner DEVMETHOD(device_attach, mfi_disk_attach), 75*249d29c8SSascha Wildner DEVMETHOD(device_detach, mfi_disk_detach), 76*249d29c8SSascha Wildner { 0, 0 } 77*249d29c8SSascha Wildner }; 78*249d29c8SSascha Wildner 79*249d29c8SSascha Wildner static driver_t mfi_disk_driver = { 80*249d29c8SSascha Wildner "mfid", 81*249d29c8SSascha Wildner mfi_disk_methods, 82*249d29c8SSascha Wildner sizeof(struct mfi_disk) 83*249d29c8SSascha Wildner }; 84*249d29c8SSascha Wildner 85*249d29c8SSascha Wildner DRIVER_MODULE(mfid, mfi, mfi_disk_driver, mfi_disk_devclass, 0, 0); 86*249d29c8SSascha Wildner 87*249d29c8SSascha Wildner static int 88*249d29c8SSascha Wildner mfi_disk_probe(device_t dev) 89*249d29c8SSascha Wildner { 90*249d29c8SSascha Wildner 91*249d29c8SSascha Wildner return (0); 92*249d29c8SSascha Wildner } 93*249d29c8SSascha Wildner 94*249d29c8SSascha Wildner static int 95*249d29c8SSascha Wildner mfi_disk_attach(device_t dev) 96*249d29c8SSascha Wildner { 97*249d29c8SSascha Wildner struct mfi_disk *sc; 98*249d29c8SSascha Wildner struct mfi_ld_info *ld_info; 99*249d29c8SSascha Wildner struct disk_info info; 100*249d29c8SSascha Wildner uint64_t sectors; 101*249d29c8SSascha Wildner uint32_t secsize; 102*249d29c8SSascha Wildner char *state; 103*249d29c8SSascha Wildner 104*249d29c8SSascha Wildner sc = device_get_softc(dev); 105*249d29c8SSascha Wildner ld_info = device_get_ivars(dev); 106*249d29c8SSascha Wildner 107*249d29c8SSascha Wildner sc->ld_dev = dev; 108*249d29c8SSascha Wildner sc->ld_id = ld_info->ld_config.properties.ld.v.target_id; 109*249d29c8SSascha Wildner sc->ld_unit = device_get_unit(dev); 110*249d29c8SSascha Wildner sc->ld_info = ld_info; 111*249d29c8SSascha Wildner sc->ld_controller = device_get_softc(device_get_parent(dev)); 112*249d29c8SSascha Wildner sc->ld_flags = 0; 113*249d29c8SSascha Wildner 114*249d29c8SSascha Wildner sectors = ld_info->size; 115*249d29c8SSascha Wildner secsize = MFI_SECTOR_LEN; 116*249d29c8SSascha Wildner lockmgr(&sc->ld_controller->mfi_io_lock, LK_EXCLUSIVE); 117*249d29c8SSascha Wildner TAILQ_INSERT_TAIL(&sc->ld_controller->mfi_ld_tqh, sc, ld_link); 118*249d29c8SSascha Wildner lockmgr(&sc->ld_controller->mfi_io_lock, LK_RELEASE); 119*249d29c8SSascha Wildner 120*249d29c8SSascha Wildner switch (ld_info->ld_config.params.state) { 121*249d29c8SSascha Wildner case MFI_LD_STATE_OFFLINE: 122*249d29c8SSascha Wildner state = "offline"; 123*249d29c8SSascha Wildner break; 124*249d29c8SSascha Wildner case MFI_LD_STATE_PARTIALLY_DEGRADED: 125*249d29c8SSascha Wildner state = "partially degraded"; 126*249d29c8SSascha Wildner break; 127*249d29c8SSascha Wildner case MFI_LD_STATE_DEGRADED: 128*249d29c8SSascha Wildner state = "degraded"; 129*249d29c8SSascha Wildner break; 130*249d29c8SSascha Wildner case MFI_LD_STATE_OPTIMAL: 131*249d29c8SSascha Wildner state = "optimal"; 132*249d29c8SSascha Wildner break; 133*249d29c8SSascha Wildner default: 134*249d29c8SSascha Wildner state = "unknown"; 135*249d29c8SSascha Wildner break; 136*249d29c8SSascha Wildner } 137*249d29c8SSascha Wildner device_printf(dev, "%juMB (%ju sectors) RAID volume '%s' is %s\n", 138*249d29c8SSascha Wildner sectors / (1024 * 1024 / secsize), sectors, 139*249d29c8SSascha Wildner ld_info->ld_config.properties.name, 140*249d29c8SSascha Wildner state); 141*249d29c8SSascha Wildner 142*249d29c8SSascha Wildner sc->ld_disk.d_cdev = disk_create(sc->ld_unit, &sc->ld_disk, 143*249d29c8SSascha Wildner &mfi_disk_ops); 144*249d29c8SSascha Wildner sc->ld_disk.d_cdev->si_drv1 = sc; 145*249d29c8SSascha Wildner sc->ld_disk.d_cdev->si_iosize_max = 146*249d29c8SSascha Wildner min(sc->ld_controller->mfi_max_io * secsize, 147*249d29c8SSascha Wildner (sc->ld_controller->mfi_max_sge - 1) * PAGE_SIZE); 148*249d29c8SSascha Wildner 149*249d29c8SSascha Wildner bzero(&info, sizeof(info)); 150*249d29c8SSascha Wildner info.d_media_blksize = secsize; /* mandatory */ 151*249d29c8SSascha Wildner info.d_media_blocks = sectors; 152*249d29c8SSascha Wildner 153*249d29c8SSascha Wildner if (info.d_media_blocks >= (1 * 1024 * 1024)) { 154*249d29c8SSascha Wildner info.d_nheads = 255; 155*249d29c8SSascha Wildner info.d_secpertrack = 63; 156*249d29c8SSascha Wildner } else { 157*249d29c8SSascha Wildner info.d_nheads = 64; 158*249d29c8SSascha Wildner info.d_secpertrack = 32; 159*249d29c8SSascha Wildner } 160*249d29c8SSascha Wildner 161*249d29c8SSascha Wildner disk_setdiskinfo(&sc->ld_disk, &info); 162*249d29c8SSascha Wildner 163*249d29c8SSascha Wildner return (0); 164*249d29c8SSascha Wildner } 165*249d29c8SSascha Wildner 166*249d29c8SSascha Wildner static int 167*249d29c8SSascha Wildner mfi_disk_detach(device_t dev) 168*249d29c8SSascha Wildner { 169*249d29c8SSascha Wildner struct mfi_disk *sc; 170*249d29c8SSascha Wildner 171*249d29c8SSascha Wildner sc = device_get_softc(dev); 172*249d29c8SSascha Wildner 173*249d29c8SSascha Wildner lockmgr(&sc->ld_controller->mfi_io_lock, LK_EXCLUSIVE); 174*249d29c8SSascha Wildner if (((sc->ld_flags & MFI_DISK_FLAGS_OPEN)) && 175*249d29c8SSascha Wildner (sc->ld_controller->mfi_keep_deleted_volumes || 176*249d29c8SSascha Wildner sc->ld_controller->mfi_detaching)) { 177*249d29c8SSascha Wildner lockmgr(&sc->ld_controller->mfi_io_lock, LK_RELEASE); 178*249d29c8SSascha Wildner return (EBUSY); 179*249d29c8SSascha Wildner } 180*249d29c8SSascha Wildner lockmgr(&sc->ld_controller->mfi_io_lock, LK_RELEASE); 181*249d29c8SSascha Wildner 182*249d29c8SSascha Wildner disk_destroy(&sc->ld_disk); 183*249d29c8SSascha Wildner lockmgr(&sc->ld_controller->mfi_io_lock, LK_EXCLUSIVE); 184*249d29c8SSascha Wildner TAILQ_REMOVE(&sc->ld_controller->mfi_ld_tqh, sc, ld_link); 185*249d29c8SSascha Wildner lockmgr(&sc->ld_controller->mfi_io_lock, LK_RELEASE); 186*249d29c8SSascha Wildner kfree(sc->ld_info, M_MFIBUF); 187*249d29c8SSascha Wildner return (0); 188*249d29c8SSascha Wildner } 189*249d29c8SSascha Wildner 190*249d29c8SSascha Wildner static int 191*249d29c8SSascha Wildner mfi_disk_open(struct dev_open_args *ap) 192*249d29c8SSascha Wildner { 193*249d29c8SSascha Wildner struct mfi_disk *sc = ap->a_head.a_dev->si_drv1; 194*249d29c8SSascha Wildner int error; 195*249d29c8SSascha Wildner 196*249d29c8SSascha Wildner lockmgr(&sc->ld_controller->mfi_io_lock, LK_EXCLUSIVE); 197*249d29c8SSascha Wildner if (sc->ld_flags & MFI_DISK_FLAGS_DISABLED) 198*249d29c8SSascha Wildner error = ENXIO; 199*249d29c8SSascha Wildner else { 200*249d29c8SSascha Wildner sc->ld_flags |= MFI_DISK_FLAGS_OPEN; 201*249d29c8SSascha Wildner error = 0; 202*249d29c8SSascha Wildner } 203*249d29c8SSascha Wildner lockmgr(&sc->ld_controller->mfi_io_lock, LK_RELEASE); 204*249d29c8SSascha Wildner 205*249d29c8SSascha Wildner return (error); 206*249d29c8SSascha Wildner } 207*249d29c8SSascha Wildner 208*249d29c8SSascha Wildner static int 209*249d29c8SSascha Wildner mfi_disk_close(struct dev_close_args *ap) 210*249d29c8SSascha Wildner { 211*249d29c8SSascha Wildner struct mfi_disk *sc = ap->a_head.a_dev->si_drv1; 212*249d29c8SSascha Wildner 213*249d29c8SSascha Wildner lockmgr(&sc->ld_controller->mfi_io_lock, LK_EXCLUSIVE); 214*249d29c8SSascha Wildner sc->ld_flags &= ~MFI_DISK_FLAGS_OPEN; 215*249d29c8SSascha Wildner lockmgr(&sc->ld_controller->mfi_io_lock, LK_RELEASE); 216*249d29c8SSascha Wildner 217*249d29c8SSascha Wildner return (0); 218*249d29c8SSascha Wildner } 219*249d29c8SSascha Wildner 220*249d29c8SSascha Wildner int 221*249d29c8SSascha Wildner mfi_disk_disable(struct mfi_disk *sc) 222*249d29c8SSascha Wildner { 223*249d29c8SSascha Wildner 224*249d29c8SSascha Wildner KKASSERT(lockstatus(&sc->ld_controller->mfi_io_lock, curthread) != 0); 225*249d29c8SSascha Wildner if (sc->ld_flags & MFI_DISK_FLAGS_OPEN) { 226*249d29c8SSascha Wildner if (sc->ld_controller->mfi_delete_busy_volumes) 227*249d29c8SSascha Wildner return (0); 228*249d29c8SSascha Wildner device_printf(sc->ld_dev, "Unable to delete busy device\n"); 229*249d29c8SSascha Wildner return (EBUSY); 230*249d29c8SSascha Wildner } 231*249d29c8SSascha Wildner sc->ld_flags |= MFI_DISK_FLAGS_DISABLED; 232*249d29c8SSascha Wildner return (0); 233*249d29c8SSascha Wildner } 234*249d29c8SSascha Wildner 235*249d29c8SSascha Wildner void 236*249d29c8SSascha Wildner mfi_disk_enable(struct mfi_disk *sc) 237*249d29c8SSascha Wildner { 238*249d29c8SSascha Wildner 239*249d29c8SSascha Wildner KKASSERT(lockstatus(&sc->ld_controller->mfi_io_lock, curthread) != 0); 240*249d29c8SSascha Wildner sc->ld_flags &= ~MFI_DISK_FLAGS_DISABLED; 241*249d29c8SSascha Wildner } 242*249d29c8SSascha Wildner 243*249d29c8SSascha Wildner static int 244*249d29c8SSascha Wildner mfi_disk_strategy(struct dev_strategy_args *ap) 245*249d29c8SSascha Wildner { 246*249d29c8SSascha Wildner struct bio *bio = ap->a_bio; 247*249d29c8SSascha Wildner struct buf *bp = bio->bio_buf; 248*249d29c8SSascha Wildner struct mfi_disk *sc = ap->a_head.a_dev->si_drv1; 249*249d29c8SSascha Wildner struct mfi_softc *controller; 250*249d29c8SSascha Wildner 251*249d29c8SSascha Wildner if (sc == NULL) { 252*249d29c8SSascha Wildner bp->b_error = EINVAL; 253*249d29c8SSascha Wildner bp->b_flags |= B_ERROR; 254*249d29c8SSascha Wildner bp->b_resid = bp->b_bcount; 255*249d29c8SSascha Wildner biodone(bio); 256*249d29c8SSascha Wildner return (0); 257*249d29c8SSascha Wildner } 258*249d29c8SSascha Wildner 259*249d29c8SSascha Wildner /* 260*249d29c8SSascha Wildner * XXX swildner 261*249d29c8SSascha Wildner * 262*249d29c8SSascha Wildner * If it's a null transfer, do nothing. FreeBSD's original driver 263*249d29c8SSascha Wildner * doesn't have this, but that caused hard error messages (even 264*249d29c8SSascha Wildner * though everything else continued to work fine). Interestingly, 265*249d29c8SSascha Wildner * only when HAMMER was used. 266*249d29c8SSascha Wildner * 267*249d29c8SSascha Wildner * Several others of our RAID drivers have this check, such as 268*249d29c8SSascha Wildner * aac(4) and ida(4), so we insert it here, too. 269*249d29c8SSascha Wildner * 270*249d29c8SSascha Wildner * The cause of null transfers is yet unknown. 271*249d29c8SSascha Wildner */ 272*249d29c8SSascha Wildner if (bp->b_bcount == 0) { 273*249d29c8SSascha Wildner bp->b_resid = bp->b_bcount; 274*249d29c8SSascha Wildner biodone(bio); 275*249d29c8SSascha Wildner return (0); 276*249d29c8SSascha Wildner } 277*249d29c8SSascha Wildner 278*249d29c8SSascha Wildner controller = sc->ld_controller; 279*249d29c8SSascha Wildner bio->bio_driver_info = sc; 280*249d29c8SSascha Wildner lockmgr(&controller->mfi_io_lock, LK_EXCLUSIVE); 281*249d29c8SSascha Wildner mfi_enqueue_bio(controller, bio); 282*249d29c8SSascha Wildner mfi_startio(controller); 283*249d29c8SSascha Wildner lockmgr(&controller->mfi_io_lock, LK_RELEASE); 284*249d29c8SSascha Wildner return (0); 285*249d29c8SSascha Wildner } 286*249d29c8SSascha Wildner 287*249d29c8SSascha Wildner void 288*249d29c8SSascha Wildner mfi_disk_complete(struct bio *bio) 289*249d29c8SSascha Wildner { 290*249d29c8SSascha Wildner struct mfi_disk *sc = bio->bio_driver_info; 291*249d29c8SSascha Wildner struct buf *bp = bio->bio_buf; 292*249d29c8SSascha Wildner 293*249d29c8SSascha Wildner if (bp->b_flags & B_ERROR) { 294*249d29c8SSascha Wildner if (bp->b_error == 0) 295*249d29c8SSascha Wildner bp->b_error = EIO; 296*249d29c8SSascha Wildner diskerr(bio, sc->ld_disk.d_cdev, "hard error", -1, 1); 297*249d29c8SSascha Wildner kprintf("\n"); 298*249d29c8SSascha Wildner } else { 299*249d29c8SSascha Wildner bp->b_resid = 0; 300*249d29c8SSascha Wildner } 301*249d29c8SSascha Wildner biodone(bio); 302*249d29c8SSascha Wildner } 303*249d29c8SSascha Wildner 304*249d29c8SSascha Wildner static int 305*249d29c8SSascha Wildner mfi_disk_dump(struct dev_dump_args *ap) 306*249d29c8SSascha Wildner { 307*249d29c8SSascha Wildner cdev_t dev = ap->a_head.a_dev; 308*249d29c8SSascha Wildner off_t offset = ap->a_offset; 309*249d29c8SSascha Wildner void *virt = ap->a_virtual; 310*249d29c8SSascha Wildner size_t len = ap->a_length; 311*249d29c8SSascha Wildner struct mfi_disk *sc; 312*249d29c8SSascha Wildner struct mfi_softc *parent_sc; 313*249d29c8SSascha Wildner int error; 314*249d29c8SSascha Wildner 315*249d29c8SSascha Wildner sc = dev->si_drv1; 316*249d29c8SSascha Wildner parent_sc = sc->ld_controller; 317*249d29c8SSascha Wildner 318*249d29c8SSascha Wildner if (len > 0) { 319*249d29c8SSascha Wildner if ((error = mfi_dump_blocks(parent_sc, sc->ld_id, offset / 320*249d29c8SSascha Wildner MFI_SECTOR_LEN, virt, len)) != 0) 321*249d29c8SSascha Wildner return (error); 322*249d29c8SSascha Wildner } else { 323*249d29c8SSascha Wildner /* mfi_sync_cache(parent_sc, sc->ld_id); */ 324*249d29c8SSascha Wildner } 325*249d29c8SSascha Wildner 326*249d29c8SSascha Wildner return (0); 327*249d29c8SSascha Wildner } 328