11c5bc425SAlexey Zaytsev /* 21c5bc425SAlexey Zaytsev * CDDL HEADER START 31c5bc425SAlexey Zaytsev * 41c5bc425SAlexey Zaytsev * The contents of this file are subject to the terms of the 51c5bc425SAlexey Zaytsev * Common Development and Distribution License (the "License"). 61c5bc425SAlexey Zaytsev * You may not use this file except in compliance with the License. 71c5bc425SAlexey Zaytsev * 81c5bc425SAlexey Zaytsev * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 91c5bc425SAlexey Zaytsev * or http://www.opensolaris.org/os/licensing. 101c5bc425SAlexey Zaytsev * See the License for the specific language governing permissions 111c5bc425SAlexey Zaytsev * and limitations under the License. 121c5bc425SAlexey Zaytsev * 131c5bc425SAlexey Zaytsev * When distributing Covered Code, include this CDDL HEADER in each 141c5bc425SAlexey Zaytsev * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 151c5bc425SAlexey Zaytsev * If applicable, add the following below this CDDL HEADER, with the 161c5bc425SAlexey Zaytsev * fields enclosed by brackets "[]" replaced with your own identifying 171c5bc425SAlexey Zaytsev * information: Portions Copyright [yyyy] [name of copyright owner] 181c5bc425SAlexey Zaytsev * 191c5bc425SAlexey Zaytsev * CDDL HEADER END 201c5bc425SAlexey Zaytsev */ 211c5bc425SAlexey Zaytsev 221c5bc425SAlexey Zaytsev /* 23*510a6847SHans Rosenfeld * Copyright (c) 2015, Nexenta Systems, Inc. All rights reserved. 241c5bc425SAlexey Zaytsev * Copyright (c) 2012, Alexey Zaytsev <alexey.zaytsev@gmail.com> 251c5bc425SAlexey Zaytsev */ 261c5bc425SAlexey Zaytsev 271c5bc425SAlexey Zaytsev 281c5bc425SAlexey Zaytsev #include <sys/modctl.h> 291c5bc425SAlexey Zaytsev #include <sys/blkdev.h> 301c5bc425SAlexey Zaytsev #include <sys/types.h> 311c5bc425SAlexey Zaytsev #include <sys/errno.h> 321c5bc425SAlexey Zaytsev #include <sys/param.h> 331c5bc425SAlexey Zaytsev #include <sys/stropts.h> 341c5bc425SAlexey Zaytsev #include <sys/stream.h> 351c5bc425SAlexey Zaytsev #include <sys/strsubr.h> 361c5bc425SAlexey Zaytsev #include <sys/kmem.h> 371c5bc425SAlexey Zaytsev #include <sys/conf.h> 381c5bc425SAlexey Zaytsev #include <sys/devops.h> 391c5bc425SAlexey Zaytsev #include <sys/ksynch.h> 401c5bc425SAlexey Zaytsev #include <sys/stat.h> 411c5bc425SAlexey Zaytsev #include <sys/modctl.h> 421c5bc425SAlexey Zaytsev #include <sys/debug.h> 431c5bc425SAlexey Zaytsev #include <sys/pci.h> 441c5bc425SAlexey Zaytsev #include <sys/sysmacros.h> 451c5bc425SAlexey Zaytsev #include "virtiovar.h" 461c5bc425SAlexey Zaytsev #include "virtioreg.h" 471c5bc425SAlexey Zaytsev 481c5bc425SAlexey Zaytsev /* Feature bits */ 491c5bc425SAlexey Zaytsev #define VIRTIO_BLK_F_BARRIER (1<<0) 501c5bc425SAlexey Zaytsev #define VIRTIO_BLK_F_SIZE_MAX (1<<1) 511c5bc425SAlexey Zaytsev #define VIRTIO_BLK_F_SEG_MAX (1<<2) 521c5bc425SAlexey Zaytsev #define VIRTIO_BLK_F_GEOMETRY (1<<4) 531c5bc425SAlexey Zaytsev #define VIRTIO_BLK_F_RO (1<<5) 541c5bc425SAlexey Zaytsev #define VIRTIO_BLK_F_BLK_SIZE (1<<6) 551c5bc425SAlexey Zaytsev #define VIRTIO_BLK_F_SCSI (1<<7) 561c5bc425SAlexey Zaytsev #define VIRTIO_BLK_F_FLUSH (1<<9) 571c5bc425SAlexey Zaytsev #define VIRTIO_BLK_F_TOPOLOGY (1<<10) 581c5bc425SAlexey Zaytsev 591c5bc425SAlexey Zaytsev /* Configuration registers */ 601c5bc425SAlexey Zaytsev #define VIRTIO_BLK_CONFIG_CAPACITY 0 /* 64bit */ 611c5bc425SAlexey Zaytsev #define VIRTIO_BLK_CONFIG_SIZE_MAX 8 /* 32bit */ 621c5bc425SAlexey Zaytsev #define VIRTIO_BLK_CONFIG_SEG_MAX 12 /* 32bit */ 631c5bc425SAlexey Zaytsev #define VIRTIO_BLK_CONFIG_GEOMETRY_C 16 /* 16bit */ 641c5bc425SAlexey Zaytsev #define VIRTIO_BLK_CONFIG_GEOMETRY_H 18 /* 8bit */ 651c5bc425SAlexey Zaytsev #define VIRTIO_BLK_CONFIG_GEOMETRY_S 19 /* 8bit */ 661c5bc425SAlexey Zaytsev #define VIRTIO_BLK_CONFIG_BLK_SIZE 20 /* 32bit */ 673e0831a9SHans Rosenfeld #define VIRTIO_BLK_CONFIG_TOPO_PBEXP 24 /* 8bit */ 683e0831a9SHans Rosenfeld #define VIRTIO_BLK_CONFIG_TOPO_ALIGN 25 /* 8bit */ 693e0831a9SHans Rosenfeld #define VIRTIO_BLK_CONFIG_TOPO_MIN_SZ 26 /* 16bit */ 703e0831a9SHans Rosenfeld #define VIRTIO_BLK_CONFIG_TOPO_OPT_SZ 28 /* 32bit */ 711c5bc425SAlexey Zaytsev 721c5bc425SAlexey Zaytsev /* Command */ 731c5bc425SAlexey Zaytsev #define VIRTIO_BLK_T_IN 0 741c5bc425SAlexey Zaytsev #define VIRTIO_BLK_T_OUT 1 751c5bc425SAlexey Zaytsev #define VIRTIO_BLK_T_SCSI_CMD 2 761c5bc425SAlexey Zaytsev #define VIRTIO_BLK_T_SCSI_CMD_OUT 3 771c5bc425SAlexey Zaytsev #define VIRTIO_BLK_T_FLUSH 4 781c5bc425SAlexey Zaytsev #define VIRTIO_BLK_T_FLUSH_OUT 5 791c5bc425SAlexey Zaytsev #define VIRTIO_BLK_T_GET_ID 8 801c5bc425SAlexey Zaytsev #define VIRTIO_BLK_T_BARRIER 0x80000000 811c5bc425SAlexey Zaytsev 821c5bc425SAlexey Zaytsev #define VIRTIO_BLK_ID_BYTES 20 /* devid */ 831c5bc425SAlexey Zaytsev 841c5bc425SAlexey Zaytsev /* Statuses */ 851c5bc425SAlexey Zaytsev #define VIRTIO_BLK_S_OK 0 861c5bc425SAlexey Zaytsev #define VIRTIO_BLK_S_IOERR 1 871c5bc425SAlexey Zaytsev #define VIRTIO_BLK_S_UNSUPP 2 881c5bc425SAlexey Zaytsev 891c5bc425SAlexey Zaytsev #define DEF_MAXINDIRECT (128) 901c5bc425SAlexey Zaytsev #define DEF_MAXSECTOR (4096) 911c5bc425SAlexey Zaytsev 921c5bc425SAlexey Zaytsev #define VIOBLK_POISON 0xdead0001dead0001 931c5bc425SAlexey Zaytsev 941c5bc425SAlexey Zaytsev /* 951c5bc425SAlexey Zaytsev * Static Variables. 961c5bc425SAlexey Zaytsev */ 971c5bc425SAlexey Zaytsev static char vioblk_ident[] = "VirtIO block driver"; 981c5bc425SAlexey Zaytsev 991c5bc425SAlexey Zaytsev /* Request header structure */ 1001c5bc425SAlexey Zaytsev struct vioblk_req_hdr { 1011c5bc425SAlexey Zaytsev uint32_t type; /* VIRTIO_BLK_T_* */ 1021c5bc425SAlexey Zaytsev uint32_t ioprio; 1031c5bc425SAlexey Zaytsev uint64_t sector; 1041c5bc425SAlexey Zaytsev }; 1051c5bc425SAlexey Zaytsev 1061c5bc425SAlexey Zaytsev struct vioblk_req { 1071c5bc425SAlexey Zaytsev struct vioblk_req_hdr hdr; 1081c5bc425SAlexey Zaytsev uint8_t status; 1091c5bc425SAlexey Zaytsev uint8_t unused[3]; 1101c5bc425SAlexey Zaytsev unsigned int ndmac; 1111c5bc425SAlexey Zaytsev ddi_dma_handle_t dmah; 1121c5bc425SAlexey Zaytsev ddi_dma_handle_t bd_dmah; 1131c5bc425SAlexey Zaytsev ddi_dma_cookie_t dmac; 1141c5bc425SAlexey Zaytsev bd_xfer_t *xfer; 1151c5bc425SAlexey Zaytsev }; 1161c5bc425SAlexey Zaytsev 1171c5bc425SAlexey Zaytsev struct vioblk_stats { 1181c5bc425SAlexey Zaytsev struct kstat_named sts_rw_outofmemory; 1191c5bc425SAlexey Zaytsev struct kstat_named sts_rw_badoffset; 1201c5bc425SAlexey Zaytsev struct kstat_named sts_rw_queuemax; 1211c5bc425SAlexey Zaytsev struct kstat_named sts_rw_cookiesmax; 1221c5bc425SAlexey Zaytsev struct kstat_named sts_rw_cacheflush; 1231c5bc425SAlexey Zaytsev struct kstat_named sts_intr_queuemax; 1241c5bc425SAlexey Zaytsev struct kstat_named sts_intr_total; 1251c5bc425SAlexey Zaytsev struct kstat_named sts_io_errors; 1261c5bc425SAlexey Zaytsev struct kstat_named sts_unsupp_errors; 1271c5bc425SAlexey Zaytsev struct kstat_named sts_nxio_errors; 1281c5bc425SAlexey Zaytsev }; 1291c5bc425SAlexey Zaytsev 1301c5bc425SAlexey Zaytsev struct vioblk_lstats { 1311c5bc425SAlexey Zaytsev uint64_t rw_cacheflush; 1321c5bc425SAlexey Zaytsev uint64_t intr_total; 1331c5bc425SAlexey Zaytsev unsigned int rw_cookiesmax; 1341c5bc425SAlexey Zaytsev unsigned int intr_queuemax; 1351c5bc425SAlexey Zaytsev unsigned int io_errors; 1361c5bc425SAlexey Zaytsev unsigned int unsupp_errors; 1371c5bc425SAlexey Zaytsev unsigned int nxio_errors; 1381c5bc425SAlexey Zaytsev }; 1391c5bc425SAlexey Zaytsev 1401c5bc425SAlexey Zaytsev struct vioblk_softc { 1411c5bc425SAlexey Zaytsev dev_info_t *sc_dev; /* mirrors virtio_softc->sc_dev */ 1421c5bc425SAlexey Zaytsev struct virtio_softc sc_virtio; 1431c5bc425SAlexey Zaytsev struct virtqueue *sc_vq; 1441c5bc425SAlexey Zaytsev bd_handle_t bd_h; 1451c5bc425SAlexey Zaytsev struct vioblk_req *sc_reqs; 1461c5bc425SAlexey Zaytsev struct vioblk_stats *ks_data; 1471c5bc425SAlexey Zaytsev kstat_t *sc_intrstat; 1481c5bc425SAlexey Zaytsev uint64_t sc_capacity; 1491c5bc425SAlexey Zaytsev uint64_t sc_nblks; 1501c5bc425SAlexey Zaytsev struct vioblk_lstats sc_stats; 1511c5bc425SAlexey Zaytsev short sc_blkflags; 1521c5bc425SAlexey Zaytsev boolean_t sc_in_poll_mode; 1531c5bc425SAlexey Zaytsev boolean_t sc_readonly; 1541c5bc425SAlexey Zaytsev int sc_blk_size; 1553e0831a9SHans Rosenfeld int sc_pblk_size; 1561c5bc425SAlexey Zaytsev int sc_seg_max; 1571c5bc425SAlexey Zaytsev int sc_seg_size_max; 1581c5bc425SAlexey Zaytsev kmutex_t lock_devid; 1591c5bc425SAlexey Zaytsev kcondvar_t cv_devid; 1601c5bc425SAlexey Zaytsev char devid[VIRTIO_BLK_ID_BYTES + 1]; 1611c5bc425SAlexey Zaytsev }; 1621c5bc425SAlexey Zaytsev 163*510a6847SHans Rosenfeld static int vioblk_get_id(struct vioblk_softc *sc); 164*510a6847SHans Rosenfeld 1651c5bc425SAlexey Zaytsev static int vioblk_read(void *arg, bd_xfer_t *xfer); 1661c5bc425SAlexey Zaytsev static int vioblk_write(void *arg, bd_xfer_t *xfer); 1671c5bc425SAlexey Zaytsev static int vioblk_flush(void *arg, bd_xfer_t *xfer); 1681c5bc425SAlexey Zaytsev static void vioblk_driveinfo(void *arg, bd_drive_t *drive); 1691c5bc425SAlexey Zaytsev static int vioblk_mediainfo(void *arg, bd_media_t *media); 1701c5bc425SAlexey Zaytsev static int vioblk_devid_init(void *, dev_info_t *, ddi_devid_t *); 1711c5bc425SAlexey Zaytsev uint_t vioblk_int_handler(caddr_t arg1, caddr_t arg2); 1721c5bc425SAlexey Zaytsev 1731c5bc425SAlexey Zaytsev static bd_ops_t vioblk_ops = { 1741c5bc425SAlexey Zaytsev BD_OPS_VERSION_0, 1751c5bc425SAlexey Zaytsev vioblk_driveinfo, 1761c5bc425SAlexey Zaytsev vioblk_mediainfo, 1771c5bc425SAlexey Zaytsev vioblk_devid_init, 1781c5bc425SAlexey Zaytsev vioblk_flush, 1791c5bc425SAlexey Zaytsev vioblk_read, 1801c5bc425SAlexey Zaytsev vioblk_write, 1811c5bc425SAlexey Zaytsev }; 1821c5bc425SAlexey Zaytsev 1831c5bc425SAlexey Zaytsev static int vioblk_quiesce(dev_info_t *); 1841c5bc425SAlexey Zaytsev static int vioblk_attach(dev_info_t *, ddi_attach_cmd_t); 1851c5bc425SAlexey Zaytsev static int vioblk_detach(dev_info_t *, ddi_detach_cmd_t); 1861c5bc425SAlexey Zaytsev 1871c5bc425SAlexey Zaytsev static struct dev_ops vioblk_dev_ops = { 1881c5bc425SAlexey Zaytsev DEVO_REV, 1891c5bc425SAlexey Zaytsev 0, 1901c5bc425SAlexey Zaytsev ddi_no_info, 1911c5bc425SAlexey Zaytsev nulldev, /* identify */ 1921c5bc425SAlexey Zaytsev nulldev, /* probe */ 1931c5bc425SAlexey Zaytsev vioblk_attach, /* attach */ 1941c5bc425SAlexey Zaytsev vioblk_detach, /* detach */ 1951c5bc425SAlexey Zaytsev nodev, /* reset */ 1961c5bc425SAlexey Zaytsev NULL, /* cb_ops */ 1971c5bc425SAlexey Zaytsev NULL, /* bus_ops */ 1981c5bc425SAlexey Zaytsev NULL, /* power */ 1991c5bc425SAlexey Zaytsev vioblk_quiesce /* quiesce */ 2001c5bc425SAlexey Zaytsev }; 2011c5bc425SAlexey Zaytsev 2021c5bc425SAlexey Zaytsev 2031c5bc425SAlexey Zaytsev 2041c5bc425SAlexey Zaytsev /* Standard Module linkage initialization for a Streams driver */ 2051c5bc425SAlexey Zaytsev extern struct mod_ops mod_driverops; 2061c5bc425SAlexey Zaytsev 2071c5bc425SAlexey Zaytsev static struct modldrv modldrv = { 2081c5bc425SAlexey Zaytsev &mod_driverops, /* Type of module. This one is a driver */ 2091c5bc425SAlexey Zaytsev vioblk_ident, /* short description */ 2101c5bc425SAlexey Zaytsev &vioblk_dev_ops /* driver specific ops */ 2111c5bc425SAlexey Zaytsev }; 2121c5bc425SAlexey Zaytsev 2131c5bc425SAlexey Zaytsev static struct modlinkage modlinkage = { 2141c5bc425SAlexey Zaytsev MODREV_1, 2151c5bc425SAlexey Zaytsev { 2161c5bc425SAlexey Zaytsev (void *)&modldrv, 2171c5bc425SAlexey Zaytsev NULL, 2181c5bc425SAlexey Zaytsev }, 2191c5bc425SAlexey Zaytsev }; 2201c5bc425SAlexey Zaytsev 2211c5bc425SAlexey Zaytsev ddi_device_acc_attr_t vioblk_attr = { 2221c5bc425SAlexey Zaytsev DDI_DEVICE_ATTR_V0, 2231c5bc425SAlexey Zaytsev DDI_NEVERSWAP_ACC, /* virtio is always native byte order */ 2241c5bc425SAlexey Zaytsev DDI_STORECACHING_OK_ACC, 2251c5bc425SAlexey Zaytsev DDI_DEFAULT_ACC 2261c5bc425SAlexey Zaytsev }; 2271c5bc425SAlexey Zaytsev 2281c5bc425SAlexey Zaytsev /* DMA attr for the header/status blocks. */ 2291c5bc425SAlexey Zaytsev static ddi_dma_attr_t vioblk_req_dma_attr = { 2301c5bc425SAlexey Zaytsev DMA_ATTR_V0, /* dma_attr version */ 2311c5bc425SAlexey Zaytsev 0, /* dma_attr_addr_lo */ 2321c5bc425SAlexey Zaytsev 0xFFFFFFFFFFFFFFFFull, /* dma_attr_addr_hi */ 2331c5bc425SAlexey Zaytsev 0x00000000FFFFFFFFull, /* dma_attr_count_max */ 2341c5bc425SAlexey Zaytsev 1, /* dma_attr_align */ 2351c5bc425SAlexey Zaytsev 1, /* dma_attr_burstsizes */ 2361c5bc425SAlexey Zaytsev 1, /* dma_attr_minxfer */ 2371c5bc425SAlexey Zaytsev 0xFFFFFFFFull, /* dma_attr_maxxfer */ 2381c5bc425SAlexey Zaytsev 0xFFFFFFFFFFFFFFFFull, /* dma_attr_seg */ 2391c5bc425SAlexey Zaytsev 1, /* dma_attr_sgllen */ 2401c5bc425SAlexey Zaytsev 1, /* dma_attr_granular */ 2411c5bc425SAlexey Zaytsev 0, /* dma_attr_flags */ 2421c5bc425SAlexey Zaytsev }; 2431c5bc425SAlexey Zaytsev 2441c5bc425SAlexey Zaytsev /* DMA attr for the data blocks. */ 2451c5bc425SAlexey Zaytsev static ddi_dma_attr_t vioblk_bd_dma_attr = { 2461c5bc425SAlexey Zaytsev DMA_ATTR_V0, /* dma_attr version */ 2471c5bc425SAlexey Zaytsev 0, /* dma_attr_addr_lo */ 2481c5bc425SAlexey Zaytsev 0xFFFFFFFFFFFFFFFFull, /* dma_attr_addr_hi */ 2491c5bc425SAlexey Zaytsev 0x00000000FFFFFFFFull, /* dma_attr_count_max */ 2501c5bc425SAlexey Zaytsev 1, /* dma_attr_align */ 2511c5bc425SAlexey Zaytsev 1, /* dma_attr_burstsizes */ 2521c5bc425SAlexey Zaytsev 1, /* dma_attr_minxfer */ 2531c5bc425SAlexey Zaytsev 0, /* dma_attr_maxxfer, set in attach */ 2541c5bc425SAlexey Zaytsev 0xFFFFFFFFFFFFFFFFull, /* dma_attr_seg */ 2551c5bc425SAlexey Zaytsev 0, /* dma_attr_sgllen, set in attach */ 2561c5bc425SAlexey Zaytsev 1, /* dma_attr_granular */ 2571c5bc425SAlexey Zaytsev 0, /* dma_attr_flags */ 2581c5bc425SAlexey Zaytsev }; 2591c5bc425SAlexey Zaytsev 2601c5bc425SAlexey Zaytsev static int 2611c5bc425SAlexey Zaytsev vioblk_rw(struct vioblk_softc *sc, bd_xfer_t *xfer, int type, 2621c5bc425SAlexey Zaytsev uint32_t len) 2631c5bc425SAlexey Zaytsev { 2641c5bc425SAlexey Zaytsev struct vioblk_req *req; 2651c5bc425SAlexey Zaytsev struct vq_entry *ve_hdr; 2661c5bc425SAlexey Zaytsev int total_cookies, write; 2671c5bc425SAlexey Zaytsev 2681c5bc425SAlexey Zaytsev write = (type == VIRTIO_BLK_T_OUT || 2691c5bc425SAlexey Zaytsev type == VIRTIO_BLK_T_FLUSH_OUT) ? 1 : 0; 2701c5bc425SAlexey Zaytsev total_cookies = 2; 2711c5bc425SAlexey Zaytsev 2721c5bc425SAlexey Zaytsev if ((xfer->x_blkno + xfer->x_nblks) > sc->sc_nblks) { 2731c5bc425SAlexey Zaytsev sc->ks_data->sts_rw_badoffset.value.ui64++; 2741c5bc425SAlexey Zaytsev return (EINVAL); 2751c5bc425SAlexey Zaytsev } 2761c5bc425SAlexey Zaytsev 2771c5bc425SAlexey Zaytsev /* allocate top entry */ 2781c5bc425SAlexey Zaytsev ve_hdr = vq_alloc_entry(sc->sc_vq); 2791c5bc425SAlexey Zaytsev if (!ve_hdr) { 2801c5bc425SAlexey Zaytsev sc->ks_data->sts_rw_outofmemory.value.ui64++; 2811c5bc425SAlexey Zaytsev return (ENOMEM); 2821c5bc425SAlexey Zaytsev } 2831c5bc425SAlexey Zaytsev 2841c5bc425SAlexey Zaytsev /* getting request */ 2851c5bc425SAlexey Zaytsev req = &sc->sc_reqs[ve_hdr->qe_index]; 2861c5bc425SAlexey Zaytsev req->hdr.type = type; 2871c5bc425SAlexey Zaytsev req->hdr.ioprio = 0; 2881c5bc425SAlexey Zaytsev req->hdr.sector = xfer->x_blkno; 2891c5bc425SAlexey Zaytsev req->xfer = xfer; 2901c5bc425SAlexey Zaytsev 2911c5bc425SAlexey Zaytsev /* Header */ 2921c5bc425SAlexey Zaytsev virtio_ve_add_indirect_buf(ve_hdr, req->dmac.dmac_laddress, 2931c5bc425SAlexey Zaytsev sizeof (struct vioblk_req_hdr), B_TRUE); 2941c5bc425SAlexey Zaytsev 2951c5bc425SAlexey Zaytsev /* Payload */ 2961c5bc425SAlexey Zaytsev if (len > 0) { 2971c5bc425SAlexey Zaytsev virtio_ve_add_cookie(ve_hdr, xfer->x_dmah, xfer->x_dmac, 2981c5bc425SAlexey Zaytsev xfer->x_ndmac, write ? B_TRUE : B_FALSE); 2991c5bc425SAlexey Zaytsev total_cookies += xfer->x_ndmac; 3001c5bc425SAlexey Zaytsev } 3011c5bc425SAlexey Zaytsev 3021c5bc425SAlexey Zaytsev /* Status */ 3031c5bc425SAlexey Zaytsev virtio_ve_add_indirect_buf(ve_hdr, 3041c5bc425SAlexey Zaytsev req->dmac.dmac_laddress + sizeof (struct vioblk_req_hdr), 3051c5bc425SAlexey Zaytsev sizeof (uint8_t), B_FALSE); 3061c5bc425SAlexey Zaytsev 3071c5bc425SAlexey Zaytsev /* sending the whole chain to the device */ 3081c5bc425SAlexey Zaytsev virtio_push_chain(ve_hdr, B_TRUE); 3091c5bc425SAlexey Zaytsev 3101c5bc425SAlexey Zaytsev if (sc->sc_stats.rw_cookiesmax < total_cookies) 3111c5bc425SAlexey Zaytsev sc->sc_stats.rw_cookiesmax = total_cookies; 3121c5bc425SAlexey Zaytsev 3131c5bc425SAlexey Zaytsev return (DDI_SUCCESS); 3141c5bc425SAlexey Zaytsev } 3151c5bc425SAlexey Zaytsev 3161c5bc425SAlexey Zaytsev /* 3171c5bc425SAlexey Zaytsev * Now in polling mode. Interrupts are off, so we 3181c5bc425SAlexey Zaytsev * 1) poll for the already queued requests to complete. 3191c5bc425SAlexey Zaytsev * 2) push our request. 3201c5bc425SAlexey Zaytsev * 3) wait for our request to complete. 3211c5bc425SAlexey Zaytsev */ 3221c5bc425SAlexey Zaytsev static int 3231c5bc425SAlexey Zaytsev vioblk_rw_poll(struct vioblk_softc *sc, bd_xfer_t *xfer, 3241c5bc425SAlexey Zaytsev int type, uint32_t len) 3251c5bc425SAlexey Zaytsev { 3261c5bc425SAlexey Zaytsev clock_t tmout; 3271c5bc425SAlexey Zaytsev int ret; 3281c5bc425SAlexey Zaytsev 3291c5bc425SAlexey Zaytsev ASSERT(xfer->x_flags & BD_XFER_POLL); 3301c5bc425SAlexey Zaytsev 3311c5bc425SAlexey Zaytsev /* Prevent a hard hang. */ 3321c5bc425SAlexey Zaytsev tmout = drv_usectohz(30000000); 3331c5bc425SAlexey Zaytsev 3341c5bc425SAlexey Zaytsev /* Poll for an empty queue */ 3351c5bc425SAlexey Zaytsev while (vq_num_used(sc->sc_vq)) { 3361c5bc425SAlexey Zaytsev /* Check if any pending requests completed. */ 3371c5bc425SAlexey Zaytsev ret = vioblk_int_handler((caddr_t)&sc->sc_virtio, NULL); 3381c5bc425SAlexey Zaytsev if (ret != DDI_INTR_CLAIMED) { 3391c5bc425SAlexey Zaytsev drv_usecwait(10); 3401c5bc425SAlexey Zaytsev tmout -= 10; 3411c5bc425SAlexey Zaytsev return (ETIMEDOUT); 3421c5bc425SAlexey Zaytsev } 3431c5bc425SAlexey Zaytsev } 3441c5bc425SAlexey Zaytsev 3451c5bc425SAlexey Zaytsev ret = vioblk_rw(sc, xfer, type, len); 3461c5bc425SAlexey Zaytsev if (ret) 3471c5bc425SAlexey Zaytsev return (ret); 3481c5bc425SAlexey Zaytsev 3491c5bc425SAlexey Zaytsev tmout = drv_usectohz(30000000); 3501c5bc425SAlexey Zaytsev /* Poll for an empty queue again. */ 3511c5bc425SAlexey Zaytsev while (vq_num_used(sc->sc_vq)) { 3521c5bc425SAlexey Zaytsev /* Check if any pending requests completed. */ 3531c5bc425SAlexey Zaytsev ret = vioblk_int_handler((caddr_t)&sc->sc_virtio, NULL); 3541c5bc425SAlexey Zaytsev if (ret != DDI_INTR_CLAIMED) { 3551c5bc425SAlexey Zaytsev drv_usecwait(10); 3561c5bc425SAlexey Zaytsev tmout -= 10; 3571c5bc425SAlexey Zaytsev return (ETIMEDOUT); 3581c5bc425SAlexey Zaytsev } 3591c5bc425SAlexey Zaytsev } 3601c5bc425SAlexey Zaytsev 3611c5bc425SAlexey Zaytsev return (DDI_SUCCESS); 3621c5bc425SAlexey Zaytsev } 3631c5bc425SAlexey Zaytsev 3641c5bc425SAlexey Zaytsev static int 3651c5bc425SAlexey Zaytsev vioblk_read(void *arg, bd_xfer_t *xfer) 3661c5bc425SAlexey Zaytsev { 3671c5bc425SAlexey Zaytsev int ret; 3681c5bc425SAlexey Zaytsev struct vioblk_softc *sc = (void *)arg; 3691c5bc425SAlexey Zaytsev 3701c5bc425SAlexey Zaytsev if (xfer->x_flags & BD_XFER_POLL) { 3711c5bc425SAlexey Zaytsev if (!sc->sc_in_poll_mode) { 3721c5bc425SAlexey Zaytsev virtio_stop_vq_intr(sc->sc_vq); 3731c5bc425SAlexey Zaytsev sc->sc_in_poll_mode = 1; 3741c5bc425SAlexey Zaytsev } 3751c5bc425SAlexey Zaytsev 3761c5bc425SAlexey Zaytsev ret = vioblk_rw_poll(sc, xfer, VIRTIO_BLK_T_IN, 3771c5bc425SAlexey Zaytsev xfer->x_nblks * DEV_BSIZE); 3781c5bc425SAlexey Zaytsev } else { 3791c5bc425SAlexey Zaytsev if (sc->sc_in_poll_mode) { 3801c5bc425SAlexey Zaytsev virtio_start_vq_intr(sc->sc_vq); 3811c5bc425SAlexey Zaytsev sc->sc_in_poll_mode = 0; 3821c5bc425SAlexey Zaytsev } 3831c5bc425SAlexey Zaytsev 3841c5bc425SAlexey Zaytsev ret = vioblk_rw(sc, xfer, VIRTIO_BLK_T_IN, 3851c5bc425SAlexey Zaytsev xfer->x_nblks * DEV_BSIZE); 3861c5bc425SAlexey Zaytsev } 3871c5bc425SAlexey Zaytsev 3881c5bc425SAlexey Zaytsev return (ret); 3891c5bc425SAlexey Zaytsev } 3901c5bc425SAlexey Zaytsev 3911c5bc425SAlexey Zaytsev static int 3921c5bc425SAlexey Zaytsev vioblk_write(void *arg, bd_xfer_t *xfer) 3931c5bc425SAlexey Zaytsev { 3941c5bc425SAlexey Zaytsev int ret; 3951c5bc425SAlexey Zaytsev struct vioblk_softc *sc = (void *)arg; 3961c5bc425SAlexey Zaytsev 3971c5bc425SAlexey Zaytsev if (xfer->x_flags & BD_XFER_POLL) { 3981c5bc425SAlexey Zaytsev if (!sc->sc_in_poll_mode) { 3991c5bc425SAlexey Zaytsev virtio_stop_vq_intr(sc->sc_vq); 4001c5bc425SAlexey Zaytsev sc->sc_in_poll_mode = 1; 4011c5bc425SAlexey Zaytsev } 4021c5bc425SAlexey Zaytsev 4031c5bc425SAlexey Zaytsev ret = vioblk_rw_poll(sc, xfer, VIRTIO_BLK_T_OUT, 4041c5bc425SAlexey Zaytsev xfer->x_nblks * DEV_BSIZE); 4051c5bc425SAlexey Zaytsev } else { 4061c5bc425SAlexey Zaytsev if (sc->sc_in_poll_mode) { 4071c5bc425SAlexey Zaytsev virtio_start_vq_intr(sc->sc_vq); 4081c5bc425SAlexey Zaytsev sc->sc_in_poll_mode = 0; 4091c5bc425SAlexey Zaytsev } 4101c5bc425SAlexey Zaytsev 4111c5bc425SAlexey Zaytsev ret = vioblk_rw(sc, xfer, VIRTIO_BLK_T_OUT, 4121c5bc425SAlexey Zaytsev xfer->x_nblks * DEV_BSIZE); 4131c5bc425SAlexey Zaytsev } 4141c5bc425SAlexey Zaytsev return (ret); 4151c5bc425SAlexey Zaytsev } 4161c5bc425SAlexey Zaytsev 4171c5bc425SAlexey Zaytsev static int 4181c5bc425SAlexey Zaytsev vioblk_flush(void *arg, bd_xfer_t *xfer) 4191c5bc425SAlexey Zaytsev { 4201c5bc425SAlexey Zaytsev int ret; 4211c5bc425SAlexey Zaytsev struct vioblk_softc *sc = (void *)arg; 4221c5bc425SAlexey Zaytsev 4231c5bc425SAlexey Zaytsev ASSERT((xfer->x_flags & BD_XFER_POLL) == 0); 4241c5bc425SAlexey Zaytsev 4251c5bc425SAlexey Zaytsev ret = vioblk_rw(sc, xfer, VIRTIO_BLK_T_FLUSH_OUT, 4261c5bc425SAlexey Zaytsev xfer->x_nblks * DEV_BSIZE); 4271c5bc425SAlexey Zaytsev 4281c5bc425SAlexey Zaytsev if (!ret) 4291c5bc425SAlexey Zaytsev sc->sc_stats.rw_cacheflush++; 4301c5bc425SAlexey Zaytsev 4311c5bc425SAlexey Zaytsev return (ret); 4321c5bc425SAlexey Zaytsev } 4331c5bc425SAlexey Zaytsev 4341c5bc425SAlexey Zaytsev 4351c5bc425SAlexey Zaytsev static void 4361c5bc425SAlexey Zaytsev vioblk_driveinfo(void *arg, bd_drive_t *drive) 4371c5bc425SAlexey Zaytsev { 4381c5bc425SAlexey Zaytsev struct vioblk_softc *sc = (void *)arg; 4391c5bc425SAlexey Zaytsev 4401c5bc425SAlexey Zaytsev drive->d_qsize = sc->sc_vq->vq_num; 4411c5bc425SAlexey Zaytsev drive->d_removable = B_FALSE; 4421c5bc425SAlexey Zaytsev drive->d_hotpluggable = B_TRUE; 4431c5bc425SAlexey Zaytsev drive->d_target = 0; 4441c5bc425SAlexey Zaytsev drive->d_lun = 0; 445*510a6847SHans Rosenfeld 446*510a6847SHans Rosenfeld drive->d_vendor = "Virtio"; 447*510a6847SHans Rosenfeld drive->d_vendor_len = strlen(drive->d_vendor); 448*510a6847SHans Rosenfeld 449*510a6847SHans Rosenfeld drive->d_product = "Block Device"; 450*510a6847SHans Rosenfeld drive->d_product_len = strlen(drive->d_product); 451*510a6847SHans Rosenfeld 452*510a6847SHans Rosenfeld (void) vioblk_get_id(sc); 453*510a6847SHans Rosenfeld drive->d_serial = sc->devid; 454*510a6847SHans Rosenfeld drive->d_serial_len = strlen(drive->d_serial); 455*510a6847SHans Rosenfeld 456*510a6847SHans Rosenfeld drive->d_revision = "0000"; 457*510a6847SHans Rosenfeld drive->d_revision_len = strlen(drive->d_revision); 4581c5bc425SAlexey Zaytsev } 4591c5bc425SAlexey Zaytsev 4601c5bc425SAlexey Zaytsev static int 4611c5bc425SAlexey Zaytsev vioblk_mediainfo(void *arg, bd_media_t *media) 4621c5bc425SAlexey Zaytsev { 4631c5bc425SAlexey Zaytsev struct vioblk_softc *sc = (void *)arg; 4641c5bc425SAlexey Zaytsev 4651c5bc425SAlexey Zaytsev media->m_nblks = sc->sc_nblks; 4663e0831a9SHans Rosenfeld media->m_blksize = sc->sc_blk_size; 4671c5bc425SAlexey Zaytsev media->m_readonly = sc->sc_readonly; 4683e0831a9SHans Rosenfeld media->m_pblksize = sc->sc_pblk_size; 4691c5bc425SAlexey Zaytsev return (0); 4701c5bc425SAlexey Zaytsev } 4711c5bc425SAlexey Zaytsev 4721c5bc425SAlexey Zaytsev static int 473*510a6847SHans Rosenfeld vioblk_get_id(struct vioblk_softc *sc) 4741c5bc425SAlexey Zaytsev { 4751c5bc425SAlexey Zaytsev clock_t deadline; 4761c5bc425SAlexey Zaytsev int ret; 4771c5bc425SAlexey Zaytsev bd_xfer_t xfer; 4781c5bc425SAlexey Zaytsev 4791c5bc425SAlexey Zaytsev deadline = ddi_get_lbolt() + (clock_t)drv_usectohz(3 * 1000000); 4801c5bc425SAlexey Zaytsev (void) memset(&xfer, 0, sizeof (bd_xfer_t)); 4811c5bc425SAlexey Zaytsev xfer.x_nblks = 1; 4821c5bc425SAlexey Zaytsev 4831c5bc425SAlexey Zaytsev ret = ddi_dma_alloc_handle(sc->sc_dev, &vioblk_bd_dma_attr, 4841c5bc425SAlexey Zaytsev DDI_DMA_SLEEP, NULL, &xfer.x_dmah); 4851c5bc425SAlexey Zaytsev if (ret != DDI_SUCCESS) 4861c5bc425SAlexey Zaytsev goto out_alloc; 4871c5bc425SAlexey Zaytsev 4881c5bc425SAlexey Zaytsev ret = ddi_dma_addr_bind_handle(xfer.x_dmah, NULL, (caddr_t)&sc->devid, 4891c5bc425SAlexey Zaytsev VIRTIO_BLK_ID_BYTES, DDI_DMA_READ | DDI_DMA_CONSISTENT, 4901c5bc425SAlexey Zaytsev DDI_DMA_SLEEP, NULL, &xfer.x_dmac, &xfer.x_ndmac); 4911c5bc425SAlexey Zaytsev if (ret != DDI_DMA_MAPPED) { 4921c5bc425SAlexey Zaytsev ret = DDI_FAILURE; 4931c5bc425SAlexey Zaytsev goto out_map; 4941c5bc425SAlexey Zaytsev } 4951c5bc425SAlexey Zaytsev 4961c5bc425SAlexey Zaytsev mutex_enter(&sc->lock_devid); 4971c5bc425SAlexey Zaytsev 4981c5bc425SAlexey Zaytsev ret = vioblk_rw(sc, &xfer, VIRTIO_BLK_T_GET_ID, 4991c5bc425SAlexey Zaytsev VIRTIO_BLK_ID_BYTES); 5001c5bc425SAlexey Zaytsev if (ret) { 5011c5bc425SAlexey Zaytsev mutex_exit(&sc->lock_devid); 5021c5bc425SAlexey Zaytsev goto out_rw; 5031c5bc425SAlexey Zaytsev } 5041c5bc425SAlexey Zaytsev 5051c5bc425SAlexey Zaytsev /* wait for reply */ 5061c5bc425SAlexey Zaytsev ret = cv_timedwait(&sc->cv_devid, &sc->lock_devid, deadline); 5071c5bc425SAlexey Zaytsev mutex_exit(&sc->lock_devid); 5081c5bc425SAlexey Zaytsev 5091c5bc425SAlexey Zaytsev (void) ddi_dma_unbind_handle(xfer.x_dmah); 5101c5bc425SAlexey Zaytsev ddi_dma_free_handle(&xfer.x_dmah); 5111c5bc425SAlexey Zaytsev 5121c5bc425SAlexey Zaytsev /* timeout */ 5131c5bc425SAlexey Zaytsev if (ret < 0) { 514*510a6847SHans Rosenfeld dev_err(sc->sc_dev, CE_WARN, 515*510a6847SHans Rosenfeld "Cannot get devid from the device"); 5161c5bc425SAlexey Zaytsev return (DDI_FAILURE); 5171c5bc425SAlexey Zaytsev } 5181c5bc425SAlexey Zaytsev 519*510a6847SHans Rosenfeld return (0); 520*510a6847SHans Rosenfeld 521*510a6847SHans Rosenfeld out_rw: 522*510a6847SHans Rosenfeld (void) ddi_dma_unbind_handle(xfer.x_dmah); 523*510a6847SHans Rosenfeld out_map: 524*510a6847SHans Rosenfeld ddi_dma_free_handle(&xfer.x_dmah); 525*510a6847SHans Rosenfeld out_alloc: 526*510a6847SHans Rosenfeld return (ret); 527*510a6847SHans Rosenfeld } 528*510a6847SHans Rosenfeld 529*510a6847SHans Rosenfeld static int 530*510a6847SHans Rosenfeld vioblk_devid_init(void *arg, dev_info_t *devinfo, ddi_devid_t *devid) 531*510a6847SHans Rosenfeld { 532*510a6847SHans Rosenfeld struct vioblk_softc *sc = (void *)arg; 533*510a6847SHans Rosenfeld int ret; 534*510a6847SHans Rosenfeld 535*510a6847SHans Rosenfeld ret = vioblk_get_id(sc); 536*510a6847SHans Rosenfeld if (ret != DDI_SUCCESS) 537*510a6847SHans Rosenfeld return (ret); 538*510a6847SHans Rosenfeld 5391c5bc425SAlexey Zaytsev ret = ddi_devid_init(devinfo, DEVID_ATA_SERIAL, 5401c5bc425SAlexey Zaytsev VIRTIO_BLK_ID_BYTES, sc->devid, devid); 5411c5bc425SAlexey Zaytsev if (ret != DDI_SUCCESS) { 5421c5bc425SAlexey Zaytsev dev_err(devinfo, CE_WARN, "Cannot build devid from the device"); 5431c5bc425SAlexey Zaytsev return (ret); 5441c5bc425SAlexey Zaytsev } 5451c5bc425SAlexey Zaytsev 5461c5bc425SAlexey Zaytsev dev_debug(sc->sc_dev, CE_NOTE, 5471c5bc425SAlexey Zaytsev "devid %x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x", 5481c5bc425SAlexey Zaytsev sc->devid[0], sc->devid[1], sc->devid[2], sc->devid[3], 5491c5bc425SAlexey Zaytsev sc->devid[4], sc->devid[5], sc->devid[6], sc->devid[7], 5501c5bc425SAlexey Zaytsev sc->devid[8], sc->devid[9], sc->devid[10], sc->devid[11], 5511c5bc425SAlexey Zaytsev sc->devid[12], sc->devid[13], sc->devid[14], sc->devid[15], 5521c5bc425SAlexey Zaytsev sc->devid[16], sc->devid[17], sc->devid[18], sc->devid[19]); 5531c5bc425SAlexey Zaytsev 5541c5bc425SAlexey Zaytsev return (0); 5551c5bc425SAlexey Zaytsev } 5561c5bc425SAlexey Zaytsev 5571c5bc425SAlexey Zaytsev static void 5581c5bc425SAlexey Zaytsev vioblk_show_features(struct vioblk_softc *sc, const char *prefix, 5591c5bc425SAlexey Zaytsev uint32_t features) 5601c5bc425SAlexey Zaytsev { 5611c5bc425SAlexey Zaytsev char buf[512]; 5621c5bc425SAlexey Zaytsev char *bufp = buf; 5631c5bc425SAlexey Zaytsev char *bufend = buf + sizeof (buf); 5641c5bc425SAlexey Zaytsev 5651c5bc425SAlexey Zaytsev /* LINTED E_PTRDIFF_OVERFLOW */ 5661c5bc425SAlexey Zaytsev bufp += snprintf(bufp, bufend - bufp, prefix); 5671c5bc425SAlexey Zaytsev 5681c5bc425SAlexey Zaytsev /* LINTED E_PTRDIFF_OVERFLOW */ 5691c5bc425SAlexey Zaytsev bufp += virtio_show_features(features, bufp, bufend - bufp); 5701c5bc425SAlexey Zaytsev 5711c5bc425SAlexey Zaytsev 5721c5bc425SAlexey Zaytsev /* LINTED E_PTRDIFF_OVERFLOW */ 5731c5bc425SAlexey Zaytsev bufp += snprintf(bufp, bufend - bufp, "Vioblk ( "); 5741c5bc425SAlexey Zaytsev 5751c5bc425SAlexey Zaytsev if (features & VIRTIO_BLK_F_BARRIER) 5761c5bc425SAlexey Zaytsev /* LINTED E_PTRDIFF_OVERFLOW */ 5771c5bc425SAlexey Zaytsev bufp += snprintf(bufp, bufend - bufp, "BARRIER "); 5781c5bc425SAlexey Zaytsev if (features & VIRTIO_BLK_F_SIZE_MAX) 5791c5bc425SAlexey Zaytsev /* LINTED E_PTRDIFF_OVERFLOW */ 5801c5bc425SAlexey Zaytsev bufp += snprintf(bufp, bufend - bufp, "SIZE_MAX "); 5811c5bc425SAlexey Zaytsev if (features & VIRTIO_BLK_F_SEG_MAX) 5821c5bc425SAlexey Zaytsev /* LINTED E_PTRDIFF_OVERFLOW */ 5831c5bc425SAlexey Zaytsev bufp += snprintf(bufp, bufend - bufp, "SEG_MAX "); 5841c5bc425SAlexey Zaytsev if (features & VIRTIO_BLK_F_GEOMETRY) 5851c5bc425SAlexey Zaytsev /* LINTED E_PTRDIFF_OVERFLOW */ 5861c5bc425SAlexey Zaytsev bufp += snprintf(bufp, bufend - bufp, "GEOMETRY "); 5871c5bc425SAlexey Zaytsev if (features & VIRTIO_BLK_F_RO) 5881c5bc425SAlexey Zaytsev /* LINTED E_PTRDIFF_OVERFLOW */ 5891c5bc425SAlexey Zaytsev bufp += snprintf(bufp, bufend - bufp, "RO "); 5901c5bc425SAlexey Zaytsev if (features & VIRTIO_BLK_F_BLK_SIZE) 5911c5bc425SAlexey Zaytsev /* LINTED E_PTRDIFF_OVERFLOW */ 5921c5bc425SAlexey Zaytsev bufp += snprintf(bufp, bufend - bufp, "BLK_SIZE "); 5931c5bc425SAlexey Zaytsev if (features & VIRTIO_BLK_F_SCSI) 5941c5bc425SAlexey Zaytsev /* LINTED E_PTRDIFF_OVERFLOW */ 5951c5bc425SAlexey Zaytsev bufp += snprintf(bufp, bufend - bufp, "SCSI "); 5961c5bc425SAlexey Zaytsev if (features & VIRTIO_BLK_F_FLUSH) 5971c5bc425SAlexey Zaytsev /* LINTED E_PTRDIFF_OVERFLOW */ 5981c5bc425SAlexey Zaytsev bufp += snprintf(bufp, bufend - bufp, "FLUSH "); 5991c5bc425SAlexey Zaytsev if (features & VIRTIO_BLK_F_TOPOLOGY) 6001c5bc425SAlexey Zaytsev /* LINTED E_PTRDIFF_OVERFLOW */ 6011c5bc425SAlexey Zaytsev bufp += snprintf(bufp, bufend - bufp, "TOPOLOGY "); 6021c5bc425SAlexey Zaytsev 6031c5bc425SAlexey Zaytsev /* LINTED E_PTRDIFF_OVERFLOW */ 6041c5bc425SAlexey Zaytsev bufp += snprintf(bufp, bufend - bufp, ")"); 6051c5bc425SAlexey Zaytsev *bufp = '\0'; 6061c5bc425SAlexey Zaytsev 6071c5bc425SAlexey Zaytsev dev_debug(sc->sc_dev, CE_NOTE, "%s", buf); 6081c5bc425SAlexey Zaytsev } 6091c5bc425SAlexey Zaytsev 6101c5bc425SAlexey Zaytsev static int 6111c5bc425SAlexey Zaytsev vioblk_dev_features(struct vioblk_softc *sc) 6121c5bc425SAlexey Zaytsev { 6131c5bc425SAlexey Zaytsev uint32_t host_features; 6141c5bc425SAlexey Zaytsev 6151c5bc425SAlexey Zaytsev host_features = virtio_negotiate_features(&sc->sc_virtio, 6161c5bc425SAlexey Zaytsev VIRTIO_BLK_F_RO | 6171c5bc425SAlexey Zaytsev VIRTIO_BLK_F_GEOMETRY | 6181c5bc425SAlexey Zaytsev VIRTIO_BLK_F_BLK_SIZE | 6191c5bc425SAlexey Zaytsev VIRTIO_BLK_F_FLUSH | 6203e0831a9SHans Rosenfeld VIRTIO_BLK_F_TOPOLOGY | 6211c5bc425SAlexey Zaytsev VIRTIO_BLK_F_SEG_MAX | 6221c5bc425SAlexey Zaytsev VIRTIO_BLK_F_SIZE_MAX | 6231c5bc425SAlexey Zaytsev VIRTIO_F_RING_INDIRECT_DESC); 6241c5bc425SAlexey Zaytsev 6251c5bc425SAlexey Zaytsev vioblk_show_features(sc, "Host features: ", host_features); 6261c5bc425SAlexey Zaytsev vioblk_show_features(sc, "Negotiated features: ", 6271c5bc425SAlexey Zaytsev sc->sc_virtio.sc_features); 6281c5bc425SAlexey Zaytsev 6291c5bc425SAlexey Zaytsev if (!(sc->sc_virtio.sc_features & VIRTIO_F_RING_INDIRECT_DESC)) { 6301c5bc425SAlexey Zaytsev dev_err(sc->sc_dev, CE_NOTE, 6311c5bc425SAlexey Zaytsev "Host does not support RING_INDIRECT_DESC, bye."); 6321c5bc425SAlexey Zaytsev return (DDI_FAILURE); 6331c5bc425SAlexey Zaytsev } 6341c5bc425SAlexey Zaytsev 6351c5bc425SAlexey Zaytsev return (DDI_SUCCESS); 6361c5bc425SAlexey Zaytsev } 6371c5bc425SAlexey Zaytsev 6381c5bc425SAlexey Zaytsev /* ARGSUSED */ 6391c5bc425SAlexey Zaytsev uint_t 6401c5bc425SAlexey Zaytsev vioblk_int_handler(caddr_t arg1, caddr_t arg2) 6411c5bc425SAlexey Zaytsev { 6421c5bc425SAlexey Zaytsev struct virtio_softc *vsc = (void *)arg1; 6431c5bc425SAlexey Zaytsev struct vioblk_softc *sc = container_of(vsc, 6441c5bc425SAlexey Zaytsev struct vioblk_softc, sc_virtio); 6451c5bc425SAlexey Zaytsev struct vq_entry *ve; 6461c5bc425SAlexey Zaytsev uint32_t len; 6471c5bc425SAlexey Zaytsev int i = 0, error; 6481c5bc425SAlexey Zaytsev 6491c5bc425SAlexey Zaytsev while ((ve = virtio_pull_chain(sc->sc_vq, &len))) { 6501c5bc425SAlexey Zaytsev struct vioblk_req *req = &sc->sc_reqs[ve->qe_index]; 6511c5bc425SAlexey Zaytsev bd_xfer_t *xfer = req->xfer; 6521c5bc425SAlexey Zaytsev uint8_t status = req->status; 6531c5bc425SAlexey Zaytsev uint32_t type = req->hdr.type; 6541c5bc425SAlexey Zaytsev 6551c5bc425SAlexey Zaytsev if (req->xfer == (void *)VIOBLK_POISON) { 6561c5bc425SAlexey Zaytsev dev_err(sc->sc_dev, CE_WARN, "Poisoned descriptor!"); 6571c5bc425SAlexey Zaytsev virtio_free_chain(ve); 6581c5bc425SAlexey Zaytsev return (DDI_INTR_CLAIMED); 6591c5bc425SAlexey Zaytsev } 6601c5bc425SAlexey Zaytsev 6611c5bc425SAlexey Zaytsev req->xfer = (void *) VIOBLK_POISON; 6621c5bc425SAlexey Zaytsev 6631c5bc425SAlexey Zaytsev /* Note: blkdev tears down the payload mapping for us. */ 6641c5bc425SAlexey Zaytsev virtio_free_chain(ve); 6651c5bc425SAlexey Zaytsev 6661c5bc425SAlexey Zaytsev /* returning payload back to blkdev */ 6671c5bc425SAlexey Zaytsev switch (status) { 6681c5bc425SAlexey Zaytsev case VIRTIO_BLK_S_OK: 6691c5bc425SAlexey Zaytsev error = 0; 6701c5bc425SAlexey Zaytsev break; 6711c5bc425SAlexey Zaytsev case VIRTIO_BLK_S_IOERR: 6721c5bc425SAlexey Zaytsev error = EIO; 6731c5bc425SAlexey Zaytsev sc->sc_stats.io_errors++; 6741c5bc425SAlexey Zaytsev break; 6751c5bc425SAlexey Zaytsev case VIRTIO_BLK_S_UNSUPP: 6761c5bc425SAlexey Zaytsev sc->sc_stats.unsupp_errors++; 6771c5bc425SAlexey Zaytsev error = ENOTTY; 6781c5bc425SAlexey Zaytsev break; 6791c5bc425SAlexey Zaytsev default: 6801c5bc425SAlexey Zaytsev sc->sc_stats.nxio_errors++; 6811c5bc425SAlexey Zaytsev error = ENXIO; 6821c5bc425SAlexey Zaytsev break; 6831c5bc425SAlexey Zaytsev } 6841c5bc425SAlexey Zaytsev 6851c5bc425SAlexey Zaytsev if (type == VIRTIO_BLK_T_GET_ID) { 6861c5bc425SAlexey Zaytsev /* notify devid_init */ 6871c5bc425SAlexey Zaytsev mutex_enter(&sc->lock_devid); 6881c5bc425SAlexey Zaytsev cv_broadcast(&sc->cv_devid); 6891c5bc425SAlexey Zaytsev mutex_exit(&sc->lock_devid); 6901c5bc425SAlexey Zaytsev } else 6911c5bc425SAlexey Zaytsev bd_xfer_done(xfer, error); 6921c5bc425SAlexey Zaytsev 6931c5bc425SAlexey Zaytsev i++; 6941c5bc425SAlexey Zaytsev } 6951c5bc425SAlexey Zaytsev 6961c5bc425SAlexey Zaytsev /* update stats */ 6971c5bc425SAlexey Zaytsev if (sc->sc_stats.intr_queuemax < i) 6981c5bc425SAlexey Zaytsev sc->sc_stats.intr_queuemax = i; 6991c5bc425SAlexey Zaytsev sc->sc_stats.intr_total++; 7001c5bc425SAlexey Zaytsev 7011c5bc425SAlexey Zaytsev return (DDI_INTR_CLAIMED); 7021c5bc425SAlexey Zaytsev } 7031c5bc425SAlexey Zaytsev 7041c5bc425SAlexey Zaytsev /* ARGSUSED */ 7051c5bc425SAlexey Zaytsev uint_t 7061c5bc425SAlexey Zaytsev vioblk_config_handler(caddr_t arg1, caddr_t arg2) 7071c5bc425SAlexey Zaytsev { 7081c5bc425SAlexey Zaytsev return (DDI_INTR_CLAIMED); 7091c5bc425SAlexey Zaytsev } 7101c5bc425SAlexey Zaytsev 7111c5bc425SAlexey Zaytsev static int 7121c5bc425SAlexey Zaytsev vioblk_register_ints(struct vioblk_softc *sc) 7131c5bc425SAlexey Zaytsev { 7141c5bc425SAlexey Zaytsev int ret; 7151c5bc425SAlexey Zaytsev 7161c5bc425SAlexey Zaytsev struct virtio_int_handler vioblk_conf_h = { 7171c5bc425SAlexey Zaytsev vioblk_config_handler 7181c5bc425SAlexey Zaytsev }; 7191c5bc425SAlexey Zaytsev 7201c5bc425SAlexey Zaytsev struct virtio_int_handler vioblk_vq_h[] = { 7211c5bc425SAlexey Zaytsev { vioblk_int_handler }, 7221c5bc425SAlexey Zaytsev { NULL }, 7231c5bc425SAlexey Zaytsev }; 7241c5bc425SAlexey Zaytsev 7251c5bc425SAlexey Zaytsev ret = virtio_register_ints(&sc->sc_virtio, 7261c5bc425SAlexey Zaytsev &vioblk_conf_h, vioblk_vq_h); 7271c5bc425SAlexey Zaytsev 7281c5bc425SAlexey Zaytsev return (ret); 7291c5bc425SAlexey Zaytsev } 7301c5bc425SAlexey Zaytsev 7311c5bc425SAlexey Zaytsev static void 7321c5bc425SAlexey Zaytsev vioblk_free_reqs(struct vioblk_softc *sc) 7331c5bc425SAlexey Zaytsev { 7341c5bc425SAlexey Zaytsev int i, qsize; 7351c5bc425SAlexey Zaytsev 7361c5bc425SAlexey Zaytsev qsize = sc->sc_vq->vq_num; 7371c5bc425SAlexey Zaytsev 7381c5bc425SAlexey Zaytsev for (i = 0; i < qsize; i++) { 7391c5bc425SAlexey Zaytsev struct vioblk_req *req = &sc->sc_reqs[i]; 7401c5bc425SAlexey Zaytsev 7411c5bc425SAlexey Zaytsev if (req->ndmac) 7421c5bc425SAlexey Zaytsev (void) ddi_dma_unbind_handle(req->dmah); 7431c5bc425SAlexey Zaytsev 7441c5bc425SAlexey Zaytsev if (req->dmah) 7451c5bc425SAlexey Zaytsev ddi_dma_free_handle(&req->dmah); 7461c5bc425SAlexey Zaytsev } 7471c5bc425SAlexey Zaytsev 7481c5bc425SAlexey Zaytsev kmem_free(sc->sc_reqs, sizeof (struct vioblk_req) * qsize); 7491c5bc425SAlexey Zaytsev } 7501c5bc425SAlexey Zaytsev 7511c5bc425SAlexey Zaytsev static int 7521c5bc425SAlexey Zaytsev vioblk_alloc_reqs(struct vioblk_softc *sc) 7531c5bc425SAlexey Zaytsev { 7541c5bc425SAlexey Zaytsev int i, qsize; 7551c5bc425SAlexey Zaytsev int ret; 7561c5bc425SAlexey Zaytsev 7571c5bc425SAlexey Zaytsev qsize = sc->sc_vq->vq_num; 7581c5bc425SAlexey Zaytsev 7591c5bc425SAlexey Zaytsev sc->sc_reqs = kmem_zalloc(sizeof (struct vioblk_req) * qsize, KM_SLEEP); 7601c5bc425SAlexey Zaytsev 7611c5bc425SAlexey Zaytsev for (i = 0; i < qsize; i++) { 7621c5bc425SAlexey Zaytsev struct vioblk_req *req = &sc->sc_reqs[i]; 7631c5bc425SAlexey Zaytsev 7641c5bc425SAlexey Zaytsev ret = ddi_dma_alloc_handle(sc->sc_dev, &vioblk_req_dma_attr, 7651c5bc425SAlexey Zaytsev DDI_DMA_SLEEP, NULL, &req->dmah); 7661c5bc425SAlexey Zaytsev if (ret != DDI_SUCCESS) { 7671c5bc425SAlexey Zaytsev 7681c5bc425SAlexey Zaytsev dev_err(sc->sc_dev, CE_WARN, 7691c5bc425SAlexey Zaytsev "Can't allocate dma handle for req " 7701c5bc425SAlexey Zaytsev "buffer %d", i); 7711c5bc425SAlexey Zaytsev goto exit; 7721c5bc425SAlexey Zaytsev } 7731c5bc425SAlexey Zaytsev 7741c5bc425SAlexey Zaytsev ret = ddi_dma_addr_bind_handle(req->dmah, NULL, 7751c5bc425SAlexey Zaytsev (caddr_t)&req->hdr, 7761c5bc425SAlexey Zaytsev sizeof (struct vioblk_req_hdr) + sizeof (uint8_t), 7771c5bc425SAlexey Zaytsev DDI_DMA_RDWR | DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, 7781c5bc425SAlexey Zaytsev NULL, &req->dmac, &req->ndmac); 7791c5bc425SAlexey Zaytsev if (ret != DDI_DMA_MAPPED) { 7801c5bc425SAlexey Zaytsev dev_err(sc->sc_dev, CE_WARN, 7811c5bc425SAlexey Zaytsev "Can't bind req buffer %d", i); 7821c5bc425SAlexey Zaytsev goto exit; 7831c5bc425SAlexey Zaytsev } 7841c5bc425SAlexey Zaytsev } 7851c5bc425SAlexey Zaytsev 7861c5bc425SAlexey Zaytsev return (0); 7871c5bc425SAlexey Zaytsev 7881c5bc425SAlexey Zaytsev exit: 7891c5bc425SAlexey Zaytsev vioblk_free_reqs(sc); 7901c5bc425SAlexey Zaytsev return (ENOMEM); 7911c5bc425SAlexey Zaytsev } 7921c5bc425SAlexey Zaytsev 7931c5bc425SAlexey Zaytsev 7941c5bc425SAlexey Zaytsev static int 7951c5bc425SAlexey Zaytsev vioblk_ksupdate(kstat_t *ksp, int rw) 7961c5bc425SAlexey Zaytsev { 7971c5bc425SAlexey Zaytsev struct vioblk_softc *sc = ksp->ks_private; 7981c5bc425SAlexey Zaytsev 7991c5bc425SAlexey Zaytsev if (rw == KSTAT_WRITE) 8001c5bc425SAlexey Zaytsev return (EACCES); 8011c5bc425SAlexey Zaytsev 8021c5bc425SAlexey Zaytsev sc->ks_data->sts_rw_cookiesmax.value.ui32 = sc->sc_stats.rw_cookiesmax; 8031c5bc425SAlexey Zaytsev sc->ks_data->sts_intr_queuemax.value.ui32 = sc->sc_stats.intr_queuemax; 8041c5bc425SAlexey Zaytsev sc->ks_data->sts_unsupp_errors.value.ui32 = sc->sc_stats.unsupp_errors; 8051c5bc425SAlexey Zaytsev sc->ks_data->sts_nxio_errors.value.ui32 = sc->sc_stats.nxio_errors; 8061c5bc425SAlexey Zaytsev sc->ks_data->sts_io_errors.value.ui32 = sc->sc_stats.io_errors; 8071c5bc425SAlexey Zaytsev sc->ks_data->sts_rw_cacheflush.value.ui64 = sc->sc_stats.rw_cacheflush; 8081c5bc425SAlexey Zaytsev sc->ks_data->sts_intr_total.value.ui64 = sc->sc_stats.intr_total; 8091c5bc425SAlexey Zaytsev 8101c5bc425SAlexey Zaytsev 8111c5bc425SAlexey Zaytsev return (0); 8121c5bc425SAlexey Zaytsev } 8131c5bc425SAlexey Zaytsev 8141c5bc425SAlexey Zaytsev static int 8151c5bc425SAlexey Zaytsev vioblk_attach(dev_info_t *devinfo, ddi_attach_cmd_t cmd) 8161c5bc425SAlexey Zaytsev { 8171c5bc425SAlexey Zaytsev int ret = DDI_SUCCESS; 8181c5bc425SAlexey Zaytsev int instance; 8191c5bc425SAlexey Zaytsev struct vioblk_softc *sc; 8201c5bc425SAlexey Zaytsev struct virtio_softc *vsc; 8211c5bc425SAlexey Zaytsev struct vioblk_stats *ks_data; 8221c5bc425SAlexey Zaytsev 8231c5bc425SAlexey Zaytsev instance = ddi_get_instance(devinfo); 8241c5bc425SAlexey Zaytsev 8251c5bc425SAlexey Zaytsev switch (cmd) { 8261c5bc425SAlexey Zaytsev case DDI_ATTACH: 8271c5bc425SAlexey Zaytsev break; 8281c5bc425SAlexey Zaytsev 8291c5bc425SAlexey Zaytsev case DDI_RESUME: 8301c5bc425SAlexey Zaytsev case DDI_PM_RESUME: 8311c5bc425SAlexey Zaytsev dev_err(devinfo, CE_WARN, "resume not supported yet"); 8321c5bc425SAlexey Zaytsev ret = DDI_FAILURE; 8331c5bc425SAlexey Zaytsev goto exit; 8341c5bc425SAlexey Zaytsev 8351c5bc425SAlexey Zaytsev default: 8361c5bc425SAlexey Zaytsev dev_err(devinfo, CE_WARN, "cmd 0x%x not recognized", cmd); 8371c5bc425SAlexey Zaytsev ret = DDI_FAILURE; 8381c5bc425SAlexey Zaytsev goto exit; 8391c5bc425SAlexey Zaytsev } 8401c5bc425SAlexey Zaytsev 8411c5bc425SAlexey Zaytsev sc = kmem_zalloc(sizeof (struct vioblk_softc), KM_SLEEP); 8421c5bc425SAlexey Zaytsev ddi_set_driver_private(devinfo, sc); 8431c5bc425SAlexey Zaytsev 8441c5bc425SAlexey Zaytsev vsc = &sc->sc_virtio; 8451c5bc425SAlexey Zaytsev 8461c5bc425SAlexey Zaytsev /* Duplicate for faster access / less typing */ 8471c5bc425SAlexey Zaytsev sc->sc_dev = devinfo; 8481c5bc425SAlexey Zaytsev vsc->sc_dev = devinfo; 8491c5bc425SAlexey Zaytsev 8501c5bc425SAlexey Zaytsev cv_init(&sc->cv_devid, NULL, CV_DRIVER, NULL); 8511c5bc425SAlexey Zaytsev mutex_init(&sc->lock_devid, NULL, MUTEX_DRIVER, NULL); 8521c5bc425SAlexey Zaytsev 8531c5bc425SAlexey Zaytsev /* 8541c5bc425SAlexey Zaytsev * Initialize interrupt kstat. This should not normally fail, since 8551c5bc425SAlexey Zaytsev * we don't use a persistent stat. We do it this way to avoid having 8561c5bc425SAlexey Zaytsev * to test for it at run time on the hot path. 8571c5bc425SAlexey Zaytsev */ 8581c5bc425SAlexey Zaytsev sc->sc_intrstat = kstat_create("vioblk", instance, 8591c5bc425SAlexey Zaytsev "intrs", "controller", KSTAT_TYPE_NAMED, 8601c5bc425SAlexey Zaytsev sizeof (struct vioblk_stats) / sizeof (kstat_named_t), 8611c5bc425SAlexey Zaytsev KSTAT_FLAG_PERSISTENT); 8621c5bc425SAlexey Zaytsev if (sc->sc_intrstat == NULL) { 8631c5bc425SAlexey Zaytsev dev_err(devinfo, CE_WARN, "kstat_create failed"); 8641c5bc425SAlexey Zaytsev goto exit_intrstat; 8651c5bc425SAlexey Zaytsev } 8661c5bc425SAlexey Zaytsev ks_data = (struct vioblk_stats *)sc->sc_intrstat->ks_data; 8671c5bc425SAlexey Zaytsev kstat_named_init(&ks_data->sts_rw_outofmemory, 8681c5bc425SAlexey Zaytsev "total_rw_outofmemory", KSTAT_DATA_UINT64); 8691c5bc425SAlexey Zaytsev kstat_named_init(&ks_data->sts_rw_badoffset, 8701c5bc425SAlexey Zaytsev "total_rw_badoffset", KSTAT_DATA_UINT64); 8711c5bc425SAlexey Zaytsev kstat_named_init(&ks_data->sts_intr_total, 8721c5bc425SAlexey Zaytsev "total_intr", KSTAT_DATA_UINT64); 8731c5bc425SAlexey Zaytsev kstat_named_init(&ks_data->sts_io_errors, 8741c5bc425SAlexey Zaytsev "total_io_errors", KSTAT_DATA_UINT32); 8751c5bc425SAlexey Zaytsev kstat_named_init(&ks_data->sts_unsupp_errors, 8761c5bc425SAlexey Zaytsev "total_unsupp_errors", KSTAT_DATA_UINT32); 8771c5bc425SAlexey Zaytsev kstat_named_init(&ks_data->sts_nxio_errors, 8781c5bc425SAlexey Zaytsev "total_nxio_errors", KSTAT_DATA_UINT32); 8791c5bc425SAlexey Zaytsev kstat_named_init(&ks_data->sts_rw_cacheflush, 8801c5bc425SAlexey Zaytsev "total_rw_cacheflush", KSTAT_DATA_UINT64); 8811c5bc425SAlexey Zaytsev kstat_named_init(&ks_data->sts_rw_cookiesmax, 8821c5bc425SAlexey Zaytsev "max_rw_cookies", KSTAT_DATA_UINT32); 8831c5bc425SAlexey Zaytsev kstat_named_init(&ks_data->sts_intr_queuemax, 8841c5bc425SAlexey Zaytsev "max_intr_queue", KSTAT_DATA_UINT32); 8851c5bc425SAlexey Zaytsev sc->ks_data = ks_data; 8861c5bc425SAlexey Zaytsev sc->sc_intrstat->ks_private = sc; 8871c5bc425SAlexey Zaytsev sc->sc_intrstat->ks_update = vioblk_ksupdate; 8881c5bc425SAlexey Zaytsev kstat_install(sc->sc_intrstat); 8891c5bc425SAlexey Zaytsev 8901c5bc425SAlexey Zaytsev /* map BAR0 */ 8911c5bc425SAlexey Zaytsev ret = ddi_regs_map_setup(devinfo, 1, 8921c5bc425SAlexey Zaytsev (caddr_t *)&sc->sc_virtio.sc_io_addr, 8931c5bc425SAlexey Zaytsev 0, 0, &vioblk_attr, &sc->sc_virtio.sc_ioh); 8941c5bc425SAlexey Zaytsev if (ret != DDI_SUCCESS) { 8951c5bc425SAlexey Zaytsev dev_err(devinfo, CE_WARN, "unable to map bar0: [%d]", ret); 8961c5bc425SAlexey Zaytsev goto exit_map; 8971c5bc425SAlexey Zaytsev } 8981c5bc425SAlexey Zaytsev 8991c5bc425SAlexey Zaytsev virtio_device_reset(&sc->sc_virtio); 9001c5bc425SAlexey Zaytsev virtio_set_status(&sc->sc_virtio, VIRTIO_CONFIG_DEVICE_STATUS_ACK); 9011c5bc425SAlexey Zaytsev virtio_set_status(&sc->sc_virtio, VIRTIO_CONFIG_DEVICE_STATUS_DRIVER); 9021c5bc425SAlexey Zaytsev 9031c5bc425SAlexey Zaytsev if (vioblk_register_ints(sc)) { 9041c5bc425SAlexey Zaytsev dev_err(devinfo, CE_WARN, "Unable to add interrupt"); 9051c5bc425SAlexey Zaytsev goto exit_int; 9061c5bc425SAlexey Zaytsev } 9071c5bc425SAlexey Zaytsev 9081c5bc425SAlexey Zaytsev ret = vioblk_dev_features(sc); 9091c5bc425SAlexey Zaytsev if (ret) 9101c5bc425SAlexey Zaytsev goto exit_features; 9111c5bc425SAlexey Zaytsev 9121c5bc425SAlexey Zaytsev if (sc->sc_virtio.sc_features & VIRTIO_BLK_F_RO) 9131c5bc425SAlexey Zaytsev sc->sc_readonly = B_TRUE; 9141c5bc425SAlexey Zaytsev else 9151c5bc425SAlexey Zaytsev sc->sc_readonly = B_FALSE; 9161c5bc425SAlexey Zaytsev 9171c5bc425SAlexey Zaytsev sc->sc_capacity = virtio_read_device_config_8(&sc->sc_virtio, 9181c5bc425SAlexey Zaytsev VIRTIO_BLK_CONFIG_CAPACITY); 9191c5bc425SAlexey Zaytsev sc->sc_nblks = sc->sc_capacity; 9201c5bc425SAlexey Zaytsev 9211c5bc425SAlexey Zaytsev sc->sc_blk_size = DEV_BSIZE; 9221c5bc425SAlexey Zaytsev if (sc->sc_virtio.sc_features & VIRTIO_BLK_F_BLK_SIZE) { 9231c5bc425SAlexey Zaytsev sc->sc_blk_size = virtio_read_device_config_4(&sc->sc_virtio, 9241c5bc425SAlexey Zaytsev VIRTIO_BLK_CONFIG_BLK_SIZE); 9251c5bc425SAlexey Zaytsev } 9261c5bc425SAlexey Zaytsev 9273e0831a9SHans Rosenfeld sc->sc_pblk_size = sc->sc_blk_size; 9283e0831a9SHans Rosenfeld if (sc->sc_virtio.sc_features & VIRTIO_BLK_F_TOPOLOGY) { 9293e0831a9SHans Rosenfeld sc->sc_pblk_size <<= virtio_read_device_config_1(&sc->sc_virtio, 9303e0831a9SHans Rosenfeld VIRTIO_BLK_CONFIG_TOPO_PBEXP); 9313e0831a9SHans Rosenfeld } 9323e0831a9SHans Rosenfeld 9331c5bc425SAlexey Zaytsev /* Flushing is not supported. */ 9341c5bc425SAlexey Zaytsev if (!(sc->sc_virtio.sc_features & VIRTIO_BLK_F_FLUSH)) { 9351c5bc425SAlexey Zaytsev vioblk_ops.o_sync_cache = NULL; 9361c5bc425SAlexey Zaytsev } 9371c5bc425SAlexey Zaytsev 9381c5bc425SAlexey Zaytsev sc->sc_seg_max = DEF_MAXINDIRECT; 9391c5bc425SAlexey Zaytsev /* The max number of segments (cookies) in a request */ 9401c5bc425SAlexey Zaytsev if (sc->sc_virtio.sc_features & VIRTIO_BLK_F_SEG_MAX) { 9411c5bc425SAlexey Zaytsev sc->sc_seg_max = virtio_read_device_config_4(&sc->sc_virtio, 9421c5bc425SAlexey Zaytsev VIRTIO_BLK_CONFIG_SEG_MAX); 9431c5bc425SAlexey Zaytsev 9441c5bc425SAlexey Zaytsev /* That's what Linux does. */ 9451c5bc425SAlexey Zaytsev if (!sc->sc_seg_max) 9461c5bc425SAlexey Zaytsev sc->sc_seg_max = 1; 9471c5bc425SAlexey Zaytsev 9481c5bc425SAlexey Zaytsev /* 9491c5bc425SAlexey Zaytsev * SEG_MAX corresponds to the number of _data_ 9501c5bc425SAlexey Zaytsev * blocks in a request 9511c5bc425SAlexey Zaytsev */ 9521c5bc425SAlexey Zaytsev sc->sc_seg_max += 2; 9531c5bc425SAlexey Zaytsev } 9541c5bc425SAlexey Zaytsev /* 2 descriptors taken for header/status */ 9551c5bc425SAlexey Zaytsev vioblk_bd_dma_attr.dma_attr_sgllen = sc->sc_seg_max - 2; 9561c5bc425SAlexey Zaytsev 9571c5bc425SAlexey Zaytsev 9581c5bc425SAlexey Zaytsev /* The maximum size for a cookie in a request. */ 9591c5bc425SAlexey Zaytsev sc->sc_seg_size_max = DEF_MAXSECTOR; 9601c5bc425SAlexey Zaytsev if (sc->sc_virtio.sc_features & VIRTIO_BLK_F_SIZE_MAX) { 9611c5bc425SAlexey Zaytsev sc->sc_seg_size_max = virtio_read_device_config_4( 9621c5bc425SAlexey Zaytsev &sc->sc_virtio, VIRTIO_BLK_CONFIG_SIZE_MAX); 9631c5bc425SAlexey Zaytsev } 9641c5bc425SAlexey Zaytsev 9651c5bc425SAlexey Zaytsev /* The maximum request size */ 9661c5bc425SAlexey Zaytsev vioblk_bd_dma_attr.dma_attr_maxxfer = 9671c5bc425SAlexey Zaytsev vioblk_bd_dma_attr.dma_attr_sgllen * sc->sc_seg_size_max; 9681c5bc425SAlexey Zaytsev 9691c5bc425SAlexey Zaytsev dev_debug(devinfo, CE_NOTE, 9703e0831a9SHans Rosenfeld "nblks=%" PRIu64 " blksize=%d (%d) num_seg=%d, " 9711c5bc425SAlexey Zaytsev "seg_size=%d, maxxfer=%" PRIu64, 9723e0831a9SHans Rosenfeld sc->sc_nblks, sc->sc_blk_size, sc->sc_pblk_size, 9731c5bc425SAlexey Zaytsev vioblk_bd_dma_attr.dma_attr_sgllen, 9741c5bc425SAlexey Zaytsev sc->sc_seg_size_max, 9751c5bc425SAlexey Zaytsev vioblk_bd_dma_attr.dma_attr_maxxfer); 9761c5bc425SAlexey Zaytsev 9771c5bc425SAlexey Zaytsev 9781c5bc425SAlexey Zaytsev sc->sc_vq = virtio_alloc_vq(&sc->sc_virtio, 0, 0, 9791c5bc425SAlexey Zaytsev sc->sc_seg_max, "I/O request"); 9801c5bc425SAlexey Zaytsev if (sc->sc_vq == NULL) { 9811c5bc425SAlexey Zaytsev goto exit_alloc1; 9821c5bc425SAlexey Zaytsev } 9831c5bc425SAlexey Zaytsev 9841c5bc425SAlexey Zaytsev ret = vioblk_alloc_reqs(sc); 9851c5bc425SAlexey Zaytsev if (ret) { 9861c5bc425SAlexey Zaytsev goto exit_alloc2; 9871c5bc425SAlexey Zaytsev } 9881c5bc425SAlexey Zaytsev 9891c5bc425SAlexey Zaytsev sc->bd_h = bd_alloc_handle(sc, &vioblk_ops, &vioblk_bd_dma_attr, 9901c5bc425SAlexey Zaytsev KM_SLEEP); 9911c5bc425SAlexey Zaytsev 9921c5bc425SAlexey Zaytsev 9931c5bc425SAlexey Zaytsev virtio_set_status(&sc->sc_virtio, 9941c5bc425SAlexey Zaytsev VIRTIO_CONFIG_DEVICE_STATUS_DRIVER_OK); 9951c5bc425SAlexey Zaytsev virtio_start_vq_intr(sc->sc_vq); 9961c5bc425SAlexey Zaytsev 9971c5bc425SAlexey Zaytsev ret = virtio_enable_ints(&sc->sc_virtio); 9981c5bc425SAlexey Zaytsev if (ret) 9991c5bc425SAlexey Zaytsev goto exit_enable_ints; 10001c5bc425SAlexey Zaytsev 10011c5bc425SAlexey Zaytsev ret = bd_attach_handle(devinfo, sc->bd_h); 10021c5bc425SAlexey Zaytsev if (ret != DDI_SUCCESS) { 10031c5bc425SAlexey Zaytsev dev_err(devinfo, CE_WARN, "Failed to attach blkdev"); 10041c5bc425SAlexey Zaytsev goto exit_attach_bd; 10051c5bc425SAlexey Zaytsev } 10061c5bc425SAlexey Zaytsev 10071c5bc425SAlexey Zaytsev return (DDI_SUCCESS); 10081c5bc425SAlexey Zaytsev 10091c5bc425SAlexey Zaytsev exit_attach_bd: 10101c5bc425SAlexey Zaytsev /* 10111c5bc425SAlexey Zaytsev * There is no virtio_disable_ints(), it's done in virtio_release_ints. 10121c5bc425SAlexey Zaytsev * If they ever get split, don't forget to add a call here. 10131c5bc425SAlexey Zaytsev */ 10141c5bc425SAlexey Zaytsev exit_enable_ints: 10151c5bc425SAlexey Zaytsev virtio_stop_vq_intr(sc->sc_vq); 10161c5bc425SAlexey Zaytsev bd_free_handle(sc->bd_h); 10171c5bc425SAlexey Zaytsev vioblk_free_reqs(sc); 10181c5bc425SAlexey Zaytsev exit_alloc2: 10191c5bc425SAlexey Zaytsev virtio_free_vq(sc->sc_vq); 10201c5bc425SAlexey Zaytsev exit_alloc1: 10211c5bc425SAlexey Zaytsev exit_features: 10221c5bc425SAlexey Zaytsev virtio_release_ints(&sc->sc_virtio); 10231c5bc425SAlexey Zaytsev exit_int: 10241c5bc425SAlexey Zaytsev virtio_set_status(&sc->sc_virtio, VIRTIO_CONFIG_DEVICE_STATUS_FAILED); 10251c5bc425SAlexey Zaytsev ddi_regs_map_free(&sc->sc_virtio.sc_ioh); 10261c5bc425SAlexey Zaytsev exit_map: 10271c5bc425SAlexey Zaytsev kstat_delete(sc->sc_intrstat); 10281c5bc425SAlexey Zaytsev exit_intrstat: 10291c5bc425SAlexey Zaytsev mutex_destroy(&sc->lock_devid); 10301c5bc425SAlexey Zaytsev cv_destroy(&sc->cv_devid); 10311c5bc425SAlexey Zaytsev kmem_free(sc, sizeof (struct vioblk_softc)); 10321c5bc425SAlexey Zaytsev exit: 10331c5bc425SAlexey Zaytsev return (ret); 10341c5bc425SAlexey Zaytsev } 10351c5bc425SAlexey Zaytsev 10361c5bc425SAlexey Zaytsev static int 10371c5bc425SAlexey Zaytsev vioblk_detach(dev_info_t *devinfo, ddi_detach_cmd_t cmd) 10381c5bc425SAlexey Zaytsev { 10391c5bc425SAlexey Zaytsev struct vioblk_softc *sc = ddi_get_driver_private(devinfo); 10401c5bc425SAlexey Zaytsev 10411c5bc425SAlexey Zaytsev switch (cmd) { 10421c5bc425SAlexey Zaytsev case DDI_DETACH: 10431c5bc425SAlexey Zaytsev break; 10441c5bc425SAlexey Zaytsev 10451c5bc425SAlexey Zaytsev case DDI_PM_SUSPEND: 10461c5bc425SAlexey Zaytsev cmn_err(CE_WARN, "suspend not supported yet"); 10471c5bc425SAlexey Zaytsev return (DDI_FAILURE); 10481c5bc425SAlexey Zaytsev 10491c5bc425SAlexey Zaytsev default: 10501c5bc425SAlexey Zaytsev cmn_err(CE_WARN, "cmd 0x%x unrecognized", cmd); 10511c5bc425SAlexey Zaytsev return (DDI_FAILURE); 10521c5bc425SAlexey Zaytsev } 10531c5bc425SAlexey Zaytsev 10541c5bc425SAlexey Zaytsev (void) bd_detach_handle(sc->bd_h); 10551c5bc425SAlexey Zaytsev virtio_stop_vq_intr(sc->sc_vq); 10561c5bc425SAlexey Zaytsev virtio_release_ints(&sc->sc_virtio); 10571c5bc425SAlexey Zaytsev vioblk_free_reqs(sc); 10581c5bc425SAlexey Zaytsev virtio_free_vq(sc->sc_vq); 10591c5bc425SAlexey Zaytsev virtio_device_reset(&sc->sc_virtio); 10601c5bc425SAlexey Zaytsev ddi_regs_map_free(&sc->sc_virtio.sc_ioh); 10611c5bc425SAlexey Zaytsev kstat_delete(sc->sc_intrstat); 10621c5bc425SAlexey Zaytsev kmem_free(sc, sizeof (struct vioblk_softc)); 10631c5bc425SAlexey Zaytsev 10641c5bc425SAlexey Zaytsev return (DDI_SUCCESS); 10651c5bc425SAlexey Zaytsev } 10661c5bc425SAlexey Zaytsev 10671c5bc425SAlexey Zaytsev static int 10681c5bc425SAlexey Zaytsev vioblk_quiesce(dev_info_t *devinfo) 10691c5bc425SAlexey Zaytsev { 10701c5bc425SAlexey Zaytsev struct vioblk_softc *sc = ddi_get_driver_private(devinfo); 10711c5bc425SAlexey Zaytsev 10721c5bc425SAlexey Zaytsev virtio_stop_vq_intr(sc->sc_vq); 10731c5bc425SAlexey Zaytsev virtio_device_reset(&sc->sc_virtio); 10741c5bc425SAlexey Zaytsev 10751c5bc425SAlexey Zaytsev return (DDI_SUCCESS); 10761c5bc425SAlexey Zaytsev } 10771c5bc425SAlexey Zaytsev 10781c5bc425SAlexey Zaytsev int 10791c5bc425SAlexey Zaytsev _init(void) 10801c5bc425SAlexey Zaytsev { 10811c5bc425SAlexey Zaytsev int rv; 10821c5bc425SAlexey Zaytsev 10831c5bc425SAlexey Zaytsev bd_mod_init(&vioblk_dev_ops); 10841c5bc425SAlexey Zaytsev 10851c5bc425SAlexey Zaytsev if ((rv = mod_install(&modlinkage)) != 0) { 10861c5bc425SAlexey Zaytsev bd_mod_fini(&vioblk_dev_ops); 10871c5bc425SAlexey Zaytsev } 10881c5bc425SAlexey Zaytsev 10891c5bc425SAlexey Zaytsev return (rv); 10901c5bc425SAlexey Zaytsev } 10911c5bc425SAlexey Zaytsev 10921c5bc425SAlexey Zaytsev int 10931c5bc425SAlexey Zaytsev _fini(void) 10941c5bc425SAlexey Zaytsev { 10951c5bc425SAlexey Zaytsev int rv; 10961c5bc425SAlexey Zaytsev 10971c5bc425SAlexey Zaytsev if ((rv = mod_remove(&modlinkage)) == 0) { 10981c5bc425SAlexey Zaytsev bd_mod_fini(&vioblk_dev_ops); 10991c5bc425SAlexey Zaytsev } 11001c5bc425SAlexey Zaytsev 11011c5bc425SAlexey Zaytsev return (rv); 11021c5bc425SAlexey Zaytsev } 11031c5bc425SAlexey Zaytsev 11041c5bc425SAlexey Zaytsev int 11051c5bc425SAlexey Zaytsev _info(struct modinfo *modinfop) 11061c5bc425SAlexey Zaytsev { 11071c5bc425SAlexey Zaytsev return (mod_info(&modlinkage, modinfop)); 11081c5bc425SAlexey Zaytsev } 1109