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*eb587f61SHans 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 */
674a9fc876SHans Rosenfeld #define VIRTIO_BLK_CONFIG_TOPO_PBEXP 24 /* 8bit */
684a9fc876SHans Rosenfeld #define VIRTIO_BLK_CONFIG_TOPO_ALIGN 25 /* 8bit */
694a9fc876SHans Rosenfeld #define VIRTIO_BLK_CONFIG_TOPO_MIN_SZ 26 /* 16bit */
704a9fc876SHans 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;
1554a9fc876SHans 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*eb587f61SHans Rosenfeld static int vioblk_get_id(struct vioblk_softc *sc);
164*eb587f61SHans 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
vioblk_rw(struct vioblk_softc * sc,bd_xfer_t * xfer,int type,uint32_t len)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
vioblk_rw_poll(struct vioblk_softc * sc,bd_xfer_t * xfer,int type,uint32_t len)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
vioblk_read(void * arg,bd_xfer_t * xfer)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
vioblk_write(void * arg,bd_xfer_t * xfer)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
vioblk_flush(void * arg,bd_xfer_t * xfer)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
vioblk_driveinfo(void * arg,bd_drive_t * drive)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*eb587f61SHans Rosenfeld
446*eb587f61SHans Rosenfeld drive->d_vendor = "Virtio";
447*eb587f61SHans Rosenfeld drive->d_vendor_len = strlen(drive->d_vendor);
448*eb587f61SHans Rosenfeld
449*eb587f61SHans Rosenfeld drive->d_product = "Block Device";
450*eb587f61SHans Rosenfeld drive->d_product_len = strlen(drive->d_product);
451*eb587f61SHans Rosenfeld
452*eb587f61SHans Rosenfeld (void) vioblk_get_id(sc);
453*eb587f61SHans Rosenfeld drive->d_serial = sc->devid;
454*eb587f61SHans Rosenfeld drive->d_serial_len = strlen(drive->d_serial);
455*eb587f61SHans Rosenfeld
456*eb587f61SHans Rosenfeld drive->d_revision = "0000";
457*eb587f61SHans Rosenfeld drive->d_revision_len = strlen(drive->d_revision);
4581c5bc425SAlexey Zaytsev }
4591c5bc425SAlexey Zaytsev
4601c5bc425SAlexey Zaytsev static int
vioblk_mediainfo(void * arg,bd_media_t * media)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;
4664a9fc876SHans Rosenfeld media->m_blksize = sc->sc_blk_size;
4671c5bc425SAlexey Zaytsev media->m_readonly = sc->sc_readonly;
4684a9fc876SHans Rosenfeld media->m_pblksize = sc->sc_pblk_size;
4691c5bc425SAlexey Zaytsev return (0);
4701c5bc425SAlexey Zaytsev }
4711c5bc425SAlexey Zaytsev
4721c5bc425SAlexey Zaytsev static int
vioblk_get_id(struct vioblk_softc * sc)473*eb587f61SHans 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*eb587f61SHans Rosenfeld dev_err(sc->sc_dev, CE_WARN,
515*eb587f61SHans Rosenfeld "Cannot get devid from the device");
5161c5bc425SAlexey Zaytsev return (DDI_FAILURE);
5171c5bc425SAlexey Zaytsev }
5181c5bc425SAlexey Zaytsev
519*eb587f61SHans Rosenfeld return (0);
520*eb587f61SHans Rosenfeld
521*eb587f61SHans Rosenfeld out_rw:
522*eb587f61SHans Rosenfeld (void) ddi_dma_unbind_handle(xfer.x_dmah);
523*eb587f61SHans Rosenfeld out_map:
524*eb587f61SHans Rosenfeld ddi_dma_free_handle(&xfer.x_dmah);
525*eb587f61SHans Rosenfeld out_alloc:
526*eb587f61SHans Rosenfeld return (ret);
527*eb587f61SHans Rosenfeld }
528*eb587f61SHans Rosenfeld
529*eb587f61SHans Rosenfeld static int
vioblk_devid_init(void * arg,dev_info_t * devinfo,ddi_devid_t * devid)530*eb587f61SHans Rosenfeld vioblk_devid_init(void *arg, dev_info_t *devinfo, ddi_devid_t *devid)
531*eb587f61SHans Rosenfeld {
532*eb587f61SHans Rosenfeld struct vioblk_softc *sc = (void *)arg;
533*eb587f61SHans Rosenfeld int ret;
534*eb587f61SHans Rosenfeld
535*eb587f61SHans Rosenfeld ret = vioblk_get_id(sc);
536*eb587f61SHans Rosenfeld if (ret != DDI_SUCCESS)
537*eb587f61SHans Rosenfeld return (ret);
538*eb587f61SHans 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
vioblk_show_features(struct vioblk_softc * sc,const char * prefix,uint32_t features)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
vioblk_dev_features(struct vioblk_softc * sc)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 |
6204a9fc876SHans 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
vioblk_int_handler(caddr_t arg1,caddr_t arg2)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
vioblk_config_handler(caddr_t arg1,caddr_t arg2)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
vioblk_register_ints(struct vioblk_softc * sc)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
vioblk_free_reqs(struct vioblk_softc * sc)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
vioblk_alloc_reqs(struct vioblk_softc * sc)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
vioblk_ksupdate(kstat_t * ksp,int rw)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
vioblk_attach(dev_info_t * devinfo,ddi_attach_cmd_t cmd)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
9274a9fc876SHans Rosenfeld sc->sc_pblk_size = sc->sc_blk_size;
9284a9fc876SHans Rosenfeld if (sc->sc_virtio.sc_features & VIRTIO_BLK_F_TOPOLOGY) {
9294a9fc876SHans Rosenfeld sc->sc_pblk_size <<= virtio_read_device_config_1(&sc->sc_virtio,
9304a9fc876SHans Rosenfeld VIRTIO_BLK_CONFIG_TOPO_PBEXP);
9314a9fc876SHans Rosenfeld }
9324a9fc876SHans 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,
9704a9fc876SHans Rosenfeld "nblks=%" PRIu64 " blksize=%d (%d) num_seg=%d, "
9711c5bc425SAlexey Zaytsev "seg_size=%d, maxxfer=%" PRIu64,
9724a9fc876SHans 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
vioblk_detach(dev_info_t * devinfo,ddi_detach_cmd_t cmd)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
vioblk_quiesce(dev_info_t * devinfo)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
_init(void)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
_fini(void)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
_info(struct modinfo * modinfop)11051c5bc425SAlexey Zaytsev _info(struct modinfo *modinfop)
11061c5bc425SAlexey Zaytsev {
11071c5bc425SAlexey Zaytsev return (mod_info(&modlinkage, modinfop));
11081c5bc425SAlexey Zaytsev }
1109