1249d29c8SSascha Wildner /*- 2249d29c8SSascha Wildner * Copyright (c) 2006 IronPort Systems 3249d29c8SSascha Wildner * All rights reserved. 4249d29c8SSascha Wildner * 5249d29c8SSascha Wildner * Redistribution and use in source and binary forms, with or without 6249d29c8SSascha Wildner * modification, are permitted provided that the following conditions 7249d29c8SSascha Wildner * are met: 8249d29c8SSascha Wildner * 1. Redistributions of source code must retain the above copyright 9249d29c8SSascha Wildner * notice, this list of conditions and the following disclaimer. 10249d29c8SSascha Wildner * 2. Redistributions in binary form must reproduce the above copyright 11249d29c8SSascha Wildner * notice, this list of conditions and the following disclaimer in the 12249d29c8SSascha Wildner * documentation and/or other materials provided with the distribution. 13249d29c8SSascha Wildner * 14249d29c8SSascha Wildner * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15249d29c8SSascha Wildner * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16249d29c8SSascha Wildner * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17249d29c8SSascha Wildner * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18249d29c8SSascha Wildner * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19249d29c8SSascha Wildner * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20249d29c8SSascha Wildner * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21249d29c8SSascha Wildner * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22249d29c8SSascha Wildner * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23249d29c8SSascha Wildner * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24249d29c8SSascha Wildner * SUCH DAMAGE. 25249d29c8SSascha Wildner * 26249d29c8SSascha Wildner * $FreeBSD: src/sys/dev/mfi/mfi_disk.c,v 1.8 2008/11/17 23:30:19 jhb Exp $ 27*590ba11dSSascha Wildner * FreeBSD projects/head_mfi/ r232888 28249d29c8SSascha Wildner */ 29249d29c8SSascha Wildner 30249d29c8SSascha Wildner #include "opt_mfi.h" 31249d29c8SSascha Wildner 32249d29c8SSascha Wildner #include <sys/param.h> 33249d29c8SSascha Wildner #include <sys/systm.h> 34249d29c8SSascha Wildner #include <sys/kernel.h> 35249d29c8SSascha Wildner #include <sys/module.h> 36249d29c8SSascha Wildner #include <sys/malloc.h> 37249d29c8SSascha Wildner #include <sys/uio.h> 38249d29c8SSascha Wildner 39249d29c8SSascha Wildner #include <sys/buf2.h> 40249d29c8SSascha Wildner #include <sys/bus.h> 41249d29c8SSascha Wildner #include <sys/conf.h> 42249d29c8SSascha Wildner #include <sys/disk.h> 43249d29c8SSascha Wildner 44249d29c8SSascha Wildner #include <vm/vm.h> 45249d29c8SSascha Wildner #include <vm/pmap.h> 46249d29c8SSascha Wildner 47249d29c8SSascha Wildner #include <machine/md_var.h> 48249d29c8SSascha Wildner #include <sys/rman.h> 49249d29c8SSascha Wildner 50249d29c8SSascha Wildner #include <dev/raid/mfi/mfireg.h> 51249d29c8SSascha Wildner #include <dev/raid/mfi/mfi_ioctl.h> 52249d29c8SSascha Wildner #include <dev/raid/mfi/mfivar.h> 53249d29c8SSascha Wildner 54249d29c8SSascha Wildner static int mfi_disk_probe(device_t dev); 55249d29c8SSascha Wildner static int mfi_disk_attach(device_t dev); 56249d29c8SSascha Wildner static int mfi_disk_detach(device_t dev); 57249d29c8SSascha Wildner 58249d29c8SSascha Wildner static d_open_t mfi_disk_open; 59249d29c8SSascha Wildner static d_close_t mfi_disk_close; 60249d29c8SSascha Wildner static d_strategy_t mfi_disk_strategy; 61249d29c8SSascha Wildner static d_dump_t mfi_disk_dump; 62249d29c8SSascha Wildner 63249d29c8SSascha Wildner static struct dev_ops mfi_disk_ops = { 64249d29c8SSascha Wildner { "mfid", 0, D_DISK }, 65249d29c8SSascha Wildner .d_open = mfi_disk_open, 66249d29c8SSascha Wildner .d_close = mfi_disk_close, 67249d29c8SSascha Wildner .d_strategy = mfi_disk_strategy, 68249d29c8SSascha Wildner .d_dump = mfi_disk_dump, 69249d29c8SSascha Wildner }; 70249d29c8SSascha Wildner 71249d29c8SSascha Wildner static devclass_t mfi_disk_devclass; 72249d29c8SSascha Wildner 73249d29c8SSascha Wildner static device_method_t mfi_disk_methods[] = { 74249d29c8SSascha Wildner DEVMETHOD(device_probe, mfi_disk_probe), 75249d29c8SSascha Wildner DEVMETHOD(device_attach, mfi_disk_attach), 76249d29c8SSascha Wildner DEVMETHOD(device_detach, mfi_disk_detach), 77249d29c8SSascha Wildner { 0, 0 } 78249d29c8SSascha Wildner }; 79249d29c8SSascha Wildner 80249d29c8SSascha Wildner static driver_t mfi_disk_driver = { 81249d29c8SSascha Wildner "mfid", 82249d29c8SSascha Wildner mfi_disk_methods, 83249d29c8SSascha Wildner sizeof(struct mfi_disk) 84249d29c8SSascha Wildner }; 85249d29c8SSascha Wildner 86aa2b9d05SSascha Wildner DRIVER_MODULE(mfid, mfi, mfi_disk_driver, mfi_disk_devclass, NULL, NULL); 87249d29c8SSascha Wildner 88249d29c8SSascha Wildner static int 89249d29c8SSascha Wildner mfi_disk_probe(device_t dev) 90249d29c8SSascha Wildner { 91249d29c8SSascha Wildner 92249d29c8SSascha Wildner return (0); 93249d29c8SSascha Wildner } 94249d29c8SSascha Wildner 95249d29c8SSascha Wildner static int 96249d29c8SSascha Wildner mfi_disk_attach(device_t dev) 97249d29c8SSascha Wildner { 98249d29c8SSascha Wildner struct mfi_disk *sc; 99249d29c8SSascha Wildner struct mfi_ld_info *ld_info; 100249d29c8SSascha Wildner struct disk_info info; 101249d29c8SSascha Wildner uint64_t sectors; 102249d29c8SSascha Wildner uint32_t secsize; 103249d29c8SSascha Wildner char *state; 104249d29c8SSascha Wildner 105249d29c8SSascha Wildner sc = device_get_softc(dev); 106249d29c8SSascha Wildner ld_info = device_get_ivars(dev); 107249d29c8SSascha Wildner 108249d29c8SSascha Wildner sc->ld_dev = dev; 109249d29c8SSascha Wildner sc->ld_id = ld_info->ld_config.properties.ld.v.target_id; 110249d29c8SSascha Wildner sc->ld_unit = device_get_unit(dev); 111249d29c8SSascha Wildner sc->ld_info = ld_info; 112249d29c8SSascha Wildner sc->ld_controller = device_get_softc(device_get_parent(dev)); 113249d29c8SSascha Wildner sc->ld_flags = 0; 114249d29c8SSascha Wildner 115249d29c8SSascha Wildner sectors = ld_info->size; 116249d29c8SSascha Wildner secsize = MFI_SECTOR_LEN; 117249d29c8SSascha Wildner lockmgr(&sc->ld_controller->mfi_io_lock, LK_EXCLUSIVE); 118249d29c8SSascha Wildner TAILQ_INSERT_TAIL(&sc->ld_controller->mfi_ld_tqh, sc, ld_link); 119249d29c8SSascha Wildner lockmgr(&sc->ld_controller->mfi_io_lock, LK_RELEASE); 120249d29c8SSascha Wildner 121249d29c8SSascha Wildner switch (ld_info->ld_config.params.state) { 122249d29c8SSascha Wildner case MFI_LD_STATE_OFFLINE: 123249d29c8SSascha Wildner state = "offline"; 124249d29c8SSascha Wildner break; 125249d29c8SSascha Wildner case MFI_LD_STATE_PARTIALLY_DEGRADED: 126249d29c8SSascha Wildner state = "partially degraded"; 127249d29c8SSascha Wildner break; 128249d29c8SSascha Wildner case MFI_LD_STATE_DEGRADED: 129249d29c8SSascha Wildner state = "degraded"; 130249d29c8SSascha Wildner break; 131249d29c8SSascha Wildner case MFI_LD_STATE_OPTIMAL: 132249d29c8SSascha Wildner state = "optimal"; 133249d29c8SSascha Wildner break; 134249d29c8SSascha Wildner default: 135249d29c8SSascha Wildner state = "unknown"; 136249d29c8SSascha Wildner break; 137249d29c8SSascha Wildner } 138249d29c8SSascha Wildner device_printf(dev, "%juMB (%ju sectors) RAID volume '%s' is %s\n", 139249d29c8SSascha Wildner sectors / (1024 * 1024 / secsize), sectors, 140249d29c8SSascha Wildner ld_info->ld_config.properties.name, 141249d29c8SSascha Wildner state); 142249d29c8SSascha Wildner 14391c61028SSascha Wildner devstat_add_entry(&sc->ld_devstat, "mfid", device_get_unit(dev), 14491c61028SSascha Wildner MFI_SECTOR_LEN, DEVSTAT_NO_ORDERED_TAGS, 14591c61028SSascha Wildner DEVSTAT_TYPE_STORARRAY | DEVSTAT_TYPE_IF_OTHER, 14691c61028SSascha Wildner DEVSTAT_PRIORITY_ARRAY); 14791c61028SSascha Wildner 1489049decfSSascha Wildner sc->ld_dev_t = disk_create(sc->ld_unit, &sc->ld_disk, &mfi_disk_ops); 1499049decfSSascha Wildner sc->ld_dev_t->si_drv1 = sc; 1509049decfSSascha Wildner sc->ld_dev_t->si_iosize_max = 151249d29c8SSascha Wildner min(sc->ld_controller->mfi_max_io * secsize, 152249d29c8SSascha Wildner (sc->ld_controller->mfi_max_sge - 1) * PAGE_SIZE); 153249d29c8SSascha Wildner 154249d29c8SSascha Wildner bzero(&info, sizeof(info)); 155249d29c8SSascha Wildner info.d_media_blksize = secsize; /* mandatory */ 156249d29c8SSascha Wildner info.d_media_blocks = sectors; 157249d29c8SSascha Wildner 158249d29c8SSascha Wildner if (info.d_media_blocks >= (1 * 1024 * 1024)) { 159249d29c8SSascha Wildner info.d_nheads = 255; 160249d29c8SSascha Wildner info.d_secpertrack = 63; 161249d29c8SSascha Wildner } else { 162249d29c8SSascha Wildner info.d_nheads = 64; 163249d29c8SSascha Wildner info.d_secpertrack = 32; 164249d29c8SSascha Wildner } 165249d29c8SSascha Wildner 166249d29c8SSascha Wildner disk_setdiskinfo(&sc->ld_disk, &info); 167249d29c8SSascha Wildner 168249d29c8SSascha Wildner return (0); 169249d29c8SSascha Wildner } 170249d29c8SSascha Wildner 171249d29c8SSascha Wildner static int 172249d29c8SSascha Wildner mfi_disk_detach(device_t dev) 173249d29c8SSascha Wildner { 174249d29c8SSascha Wildner struct mfi_disk *sc; 175249d29c8SSascha Wildner 176249d29c8SSascha Wildner sc = device_get_softc(dev); 177249d29c8SSascha Wildner 178249d29c8SSascha Wildner lockmgr(&sc->ld_controller->mfi_io_lock, LK_EXCLUSIVE); 17917566092SSascha Wildner if ((sc->ld_flags & MFI_DISK_FLAGS_OPEN) && 180249d29c8SSascha Wildner (sc->ld_controller->mfi_keep_deleted_volumes || 181249d29c8SSascha Wildner sc->ld_controller->mfi_detaching)) { 182249d29c8SSascha Wildner lockmgr(&sc->ld_controller->mfi_io_lock, LK_RELEASE); 183249d29c8SSascha Wildner return (EBUSY); 184249d29c8SSascha Wildner } 185249d29c8SSascha Wildner lockmgr(&sc->ld_controller->mfi_io_lock, LK_RELEASE); 186249d29c8SSascha Wildner 187249d29c8SSascha Wildner disk_destroy(&sc->ld_disk); 18891c61028SSascha Wildner devstat_remove_entry(&sc->ld_devstat); 189249d29c8SSascha Wildner lockmgr(&sc->ld_controller->mfi_io_lock, LK_EXCLUSIVE); 190249d29c8SSascha Wildner TAILQ_REMOVE(&sc->ld_controller->mfi_ld_tqh, sc, ld_link); 191249d29c8SSascha Wildner lockmgr(&sc->ld_controller->mfi_io_lock, LK_RELEASE); 192249d29c8SSascha Wildner kfree(sc->ld_info, M_MFIBUF); 193249d29c8SSascha Wildner return (0); 194249d29c8SSascha Wildner } 195249d29c8SSascha Wildner 196249d29c8SSascha Wildner static int 197249d29c8SSascha Wildner mfi_disk_open(struct dev_open_args *ap) 198249d29c8SSascha Wildner { 199249d29c8SSascha Wildner struct mfi_disk *sc = ap->a_head.a_dev->si_drv1; 200249d29c8SSascha Wildner int error; 201249d29c8SSascha Wildner 202249d29c8SSascha Wildner lockmgr(&sc->ld_controller->mfi_io_lock, LK_EXCLUSIVE); 203249d29c8SSascha Wildner if (sc->ld_flags & MFI_DISK_FLAGS_DISABLED) 204249d29c8SSascha Wildner error = ENXIO; 205249d29c8SSascha Wildner else { 206249d29c8SSascha Wildner sc->ld_flags |= MFI_DISK_FLAGS_OPEN; 207249d29c8SSascha Wildner error = 0; 208249d29c8SSascha Wildner } 209249d29c8SSascha Wildner lockmgr(&sc->ld_controller->mfi_io_lock, LK_RELEASE); 210249d29c8SSascha Wildner 211249d29c8SSascha Wildner return (error); 212249d29c8SSascha Wildner } 213249d29c8SSascha Wildner 214249d29c8SSascha Wildner static int 215249d29c8SSascha Wildner mfi_disk_close(struct dev_close_args *ap) 216249d29c8SSascha Wildner { 217249d29c8SSascha Wildner struct mfi_disk *sc = ap->a_head.a_dev->si_drv1; 218249d29c8SSascha Wildner 219249d29c8SSascha Wildner lockmgr(&sc->ld_controller->mfi_io_lock, LK_EXCLUSIVE); 220249d29c8SSascha Wildner sc->ld_flags &= ~MFI_DISK_FLAGS_OPEN; 221249d29c8SSascha Wildner lockmgr(&sc->ld_controller->mfi_io_lock, LK_RELEASE); 222249d29c8SSascha Wildner 223249d29c8SSascha Wildner return (0); 224249d29c8SSascha Wildner } 225249d29c8SSascha Wildner 226249d29c8SSascha Wildner int 227249d29c8SSascha Wildner mfi_disk_disable(struct mfi_disk *sc) 228249d29c8SSascha Wildner { 229249d29c8SSascha Wildner 230249d29c8SSascha Wildner KKASSERT(lockstatus(&sc->ld_controller->mfi_io_lock, curthread) != 0); 231249d29c8SSascha Wildner if (sc->ld_flags & MFI_DISK_FLAGS_OPEN) { 232249d29c8SSascha Wildner if (sc->ld_controller->mfi_delete_busy_volumes) 233249d29c8SSascha Wildner return (0); 23417566092SSascha Wildner device_printf(sc->ld_dev, "Unable to delete busy ld device\n"); 235249d29c8SSascha Wildner return (EBUSY); 236249d29c8SSascha Wildner } 237249d29c8SSascha Wildner sc->ld_flags |= MFI_DISK_FLAGS_DISABLED; 238249d29c8SSascha Wildner return (0); 239249d29c8SSascha Wildner } 240249d29c8SSascha Wildner 241249d29c8SSascha Wildner void 242249d29c8SSascha Wildner mfi_disk_enable(struct mfi_disk *sc) 243249d29c8SSascha Wildner { 244249d29c8SSascha Wildner 245249d29c8SSascha Wildner KKASSERT(lockstatus(&sc->ld_controller->mfi_io_lock, curthread) != 0); 246249d29c8SSascha Wildner sc->ld_flags &= ~MFI_DISK_FLAGS_DISABLED; 247249d29c8SSascha Wildner } 248249d29c8SSascha Wildner 249249d29c8SSascha Wildner static int 250249d29c8SSascha Wildner mfi_disk_strategy(struct dev_strategy_args *ap) 251249d29c8SSascha Wildner { 252249d29c8SSascha Wildner struct bio *bio = ap->a_bio; 253249d29c8SSascha Wildner struct buf *bp = bio->bio_buf; 254249d29c8SSascha Wildner struct mfi_disk *sc = ap->a_head.a_dev->si_drv1; 255*590ba11dSSascha Wildner struct mfi_softc *controller = sc->ld_controller; 256249d29c8SSascha Wildner 257249d29c8SSascha Wildner if (sc == NULL) { 258249d29c8SSascha Wildner bp->b_error = EINVAL; 259249d29c8SSascha Wildner bp->b_flags |= B_ERROR; 260249d29c8SSascha Wildner bp->b_resid = bp->b_bcount; 261249d29c8SSascha Wildner biodone(bio); 262249d29c8SSascha Wildner return (0); 263249d29c8SSascha Wildner } 264249d29c8SSascha Wildner 265*590ba11dSSascha Wildner if (controller->hw_crit_error) { 266*590ba11dSSascha Wildner bp->b_error = EBUSY; 267*590ba11dSSascha Wildner return (0); 268*590ba11dSSascha Wildner } 269*590ba11dSSascha Wildner 270*590ba11dSSascha Wildner if (controller->issuepend_done == 0) { 271*590ba11dSSascha Wildner bp->b_error = EBUSY; 272*590ba11dSSascha Wildner return (0); 273*590ba11dSSascha Wildner } 274*590ba11dSSascha Wildner 275249d29c8SSascha Wildner /* 276249d29c8SSascha Wildner * XXX swildner 277249d29c8SSascha Wildner * 278249d29c8SSascha Wildner * If it's a null transfer, do nothing. FreeBSD's original driver 279249d29c8SSascha Wildner * doesn't have this, but that caused hard error messages (even 280249d29c8SSascha Wildner * though everything else continued to work fine). Interestingly, 281249d29c8SSascha Wildner * only when HAMMER was used. 282249d29c8SSascha Wildner * 283249d29c8SSascha Wildner * Several others of our RAID drivers have this check, such as 284249d29c8SSascha Wildner * aac(4) and ida(4), so we insert it here, too. 285249d29c8SSascha Wildner * 286249d29c8SSascha Wildner * The cause of null transfers is yet unknown. 287249d29c8SSascha Wildner */ 288249d29c8SSascha Wildner if (bp->b_bcount == 0) { 289249d29c8SSascha Wildner bp->b_resid = bp->b_bcount; 290249d29c8SSascha Wildner biodone(bio); 291249d29c8SSascha Wildner return (0); 292249d29c8SSascha Wildner } 293249d29c8SSascha Wildner 294249d29c8SSascha Wildner bio->bio_driver_info = sc; 295249d29c8SSascha Wildner lockmgr(&controller->mfi_io_lock, LK_EXCLUSIVE); 296249d29c8SSascha Wildner mfi_enqueue_bio(controller, bio); 29791c61028SSascha Wildner devstat_start_transaction(&sc->ld_devstat); 298249d29c8SSascha Wildner mfi_startio(controller); 299249d29c8SSascha Wildner lockmgr(&controller->mfi_io_lock, LK_RELEASE); 300249d29c8SSascha Wildner return (0); 301249d29c8SSascha Wildner } 302249d29c8SSascha Wildner 303249d29c8SSascha Wildner void 304249d29c8SSascha Wildner mfi_disk_complete(struct bio *bio) 305249d29c8SSascha Wildner { 306249d29c8SSascha Wildner struct mfi_disk *sc = bio->bio_driver_info; 307249d29c8SSascha Wildner struct buf *bp = bio->bio_buf; 308249d29c8SSascha Wildner 30991c61028SSascha Wildner devstat_end_transaction_buf(&sc->ld_devstat, bp); 310249d29c8SSascha Wildner if (bp->b_flags & B_ERROR) { 311249d29c8SSascha Wildner if (bp->b_error == 0) 312249d29c8SSascha Wildner bp->b_error = EIO; 313249d29c8SSascha Wildner diskerr(bio, sc->ld_disk.d_cdev, "hard error", -1, 1); 314249d29c8SSascha Wildner kprintf("\n"); 315249d29c8SSascha Wildner } else { 316249d29c8SSascha Wildner bp->b_resid = 0; 317249d29c8SSascha Wildner } 318249d29c8SSascha Wildner biodone(bio); 319249d29c8SSascha Wildner } 320249d29c8SSascha Wildner 321249d29c8SSascha Wildner static int 322249d29c8SSascha Wildner mfi_disk_dump(struct dev_dump_args *ap) 323249d29c8SSascha Wildner { 324249d29c8SSascha Wildner cdev_t dev = ap->a_head.a_dev; 325249d29c8SSascha Wildner off_t offset = ap->a_offset; 326249d29c8SSascha Wildner void *virt = ap->a_virtual; 327249d29c8SSascha Wildner size_t len = ap->a_length; 328249d29c8SSascha Wildner struct mfi_disk *sc; 329249d29c8SSascha Wildner struct mfi_softc *parent_sc; 330249d29c8SSascha Wildner int error; 331249d29c8SSascha Wildner 332249d29c8SSascha Wildner sc = dev->si_drv1; 333249d29c8SSascha Wildner parent_sc = sc->ld_controller; 334249d29c8SSascha Wildner 335249d29c8SSascha Wildner if (len > 0) { 336249d29c8SSascha Wildner if ((error = mfi_dump_blocks(parent_sc, sc->ld_id, offset / 337249d29c8SSascha Wildner MFI_SECTOR_LEN, virt, len)) != 0) 338249d29c8SSascha Wildner return (error); 339249d29c8SSascha Wildner } else { 340249d29c8SSascha Wildner /* mfi_sync_cache(parent_sc, sc->ld_id); */ 341249d29c8SSascha Wildner } 342249d29c8SSascha Wildner 343249d29c8SSascha Wildner return (0); 344249d29c8SSascha Wildner } 345