xref: /illumos-gate/usr/src/uts/common/io/vioblk/vioblk.c (revision 94c3dad2979525d0a82595f3d8350a6116aba8ed)
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