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