xref: /freebsd-src/sys/dev/virtio/scsi/virtio_scsi.c (revision fdafd315ad0d0f28a11b9fb4476a9ab059c62b92)
12f001371SPeter Grehan /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
3718cf2ccSPedro F. Giffuni  *
4abd6790cSBryan Venteicher  * Copyright (c) 2012, Bryan Venteicher <bryanv@FreeBSD.org>
52f001371SPeter Grehan  * All rights reserved.
62f001371SPeter Grehan  *
72f001371SPeter Grehan  * Redistribution and use in source and binary forms, with or without
82f001371SPeter Grehan  * modification, are permitted provided that the following conditions
92f001371SPeter Grehan  * are met:
102f001371SPeter Grehan  * 1. Redistributions of source code must retain the above copyright
112f001371SPeter Grehan  *    notice unmodified, this list of conditions, and the following
122f001371SPeter Grehan  *    disclaimer.
132f001371SPeter Grehan  * 2. Redistributions in binary form must reproduce the above copyright
142f001371SPeter Grehan  *    notice, this list of conditions and the following disclaimer in the
152f001371SPeter Grehan  *    documentation and/or other materials provided with the distribution.
162f001371SPeter Grehan  *
172f001371SPeter Grehan  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
182f001371SPeter Grehan  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
192f001371SPeter Grehan  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
202f001371SPeter Grehan  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
212f001371SPeter Grehan  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
222f001371SPeter Grehan  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
232f001371SPeter Grehan  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
242f001371SPeter Grehan  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
252f001371SPeter Grehan  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
262f001371SPeter Grehan  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
272f001371SPeter Grehan  */
282f001371SPeter Grehan 
292f001371SPeter Grehan /* Driver for VirtIO SCSI devices. */
302f001371SPeter Grehan 
312f001371SPeter Grehan #include <sys/param.h>
322f001371SPeter Grehan #include <sys/systm.h>
332f001371SPeter Grehan #include <sys/kernel.h>
342f001371SPeter Grehan #include <sys/kthread.h>
352f001371SPeter Grehan #include <sys/malloc.h>
362f001371SPeter Grehan #include <sys/module.h>
372f001371SPeter Grehan #include <sys/sglist.h>
382f001371SPeter Grehan #include <sys/sysctl.h>
392f001371SPeter Grehan #include <sys/lock.h>
402f001371SPeter Grehan #include <sys/mutex.h>
412f001371SPeter Grehan #include <sys/callout.h>
422f001371SPeter Grehan #include <sys/queue.h>
432f001371SPeter Grehan #include <sys/sbuf.h>
442f001371SPeter Grehan 
452f001371SPeter Grehan #include <machine/stdarg.h>
462f001371SPeter Grehan 
472f001371SPeter Grehan #include <machine/bus.h>
482f001371SPeter Grehan #include <machine/resource.h>
492f001371SPeter Grehan #include <sys/bus.h>
502f001371SPeter Grehan #include <sys/rman.h>
512f001371SPeter Grehan 
522f001371SPeter Grehan #include <cam/cam.h>
532f001371SPeter Grehan #include <cam/cam_ccb.h>
542f001371SPeter Grehan #include <cam/cam_sim.h>
552f001371SPeter Grehan #include <cam/cam_periph.h>
562f001371SPeter Grehan #include <cam/cam_xpt_sim.h>
572f001371SPeter Grehan #include <cam/cam_debug.h>
582f001371SPeter Grehan #include <cam/scsi/scsi_all.h>
592f001371SPeter Grehan #include <cam/scsi/scsi_message.h>
602f001371SPeter Grehan 
612f001371SPeter Grehan #include <dev/virtio/virtio.h>
622f001371SPeter Grehan #include <dev/virtio/virtqueue.h>
632f001371SPeter Grehan #include <dev/virtio/scsi/virtio_scsi.h>
642f001371SPeter Grehan #include <dev/virtio/scsi/virtio_scsivar.h>
652f001371SPeter Grehan 
662f001371SPeter Grehan #include "virtio_if.h"
672f001371SPeter Grehan 
682f001371SPeter Grehan static int	vtscsi_modevent(module_t, int, void *);
692f001371SPeter Grehan 
702f001371SPeter Grehan static int	vtscsi_probe(device_t);
712f001371SPeter Grehan static int	vtscsi_attach(device_t);
722f001371SPeter Grehan static int	vtscsi_detach(device_t);
732f001371SPeter Grehan static int	vtscsi_suspend(device_t);
742f001371SPeter Grehan static int	vtscsi_resume(device_t);
752f001371SPeter Grehan 
76e6cc42f1SBryan Venteicher static int	vtscsi_negotiate_features(struct vtscsi_softc *);
77e6cc42f1SBryan Venteicher static int	vtscsi_setup_features(struct vtscsi_softc *);
788c457c88SBryan Venteicher static void	vtscsi_read_config(struct vtscsi_softc *,
798c457c88SBryan Venteicher 		    struct virtio_scsi_config *);
802f001371SPeter Grehan static int	vtscsi_maximum_segments(struct vtscsi_softc *, int);
812f001371SPeter Grehan static int	vtscsi_alloc_virtqueues(struct vtscsi_softc *);
82df840654SEric van Gyzen static void	vtscsi_check_sizes(struct vtscsi_softc *);
832f001371SPeter Grehan static void	vtscsi_write_device_config(struct vtscsi_softc *);
842f001371SPeter Grehan static int	vtscsi_reinit(struct vtscsi_softc *);
852f001371SPeter Grehan 
862f001371SPeter Grehan static int	vtscsi_alloc_cam(struct vtscsi_softc *);
872f001371SPeter Grehan static int	vtscsi_register_cam(struct vtscsi_softc *);
882f001371SPeter Grehan static void	vtscsi_free_cam(struct vtscsi_softc *);
892f001371SPeter Grehan static void	vtscsi_cam_async(void *, uint32_t, struct cam_path *, void *);
902f001371SPeter Grehan static int	vtscsi_register_async(struct vtscsi_softc *);
912f001371SPeter Grehan static void	vtscsi_deregister_async(struct vtscsi_softc *);
922f001371SPeter Grehan static void	vtscsi_cam_action(struct cam_sim *, union ccb *);
932f001371SPeter Grehan static void	vtscsi_cam_poll(struct cam_sim *);
942f001371SPeter Grehan 
952f001371SPeter Grehan static void	vtscsi_cam_scsi_io(struct vtscsi_softc *, struct cam_sim *,
962f001371SPeter Grehan 		    union ccb *);
972f001371SPeter Grehan static void	vtscsi_cam_get_tran_settings(struct vtscsi_softc *,
982f001371SPeter Grehan 		    union ccb *);
992f001371SPeter Grehan static void	vtscsi_cam_reset_bus(struct vtscsi_softc *, union ccb *);
1002f001371SPeter Grehan static void	vtscsi_cam_reset_dev(struct vtscsi_softc *, union ccb *);
1012f001371SPeter Grehan static void	vtscsi_cam_abort(struct vtscsi_softc *, union ccb *);
1022f001371SPeter Grehan static void	vtscsi_cam_path_inquiry(struct vtscsi_softc *,
1032f001371SPeter Grehan 		    struct cam_sim *, union ccb *);
1042f001371SPeter Grehan 
1052f001371SPeter Grehan static int	vtscsi_sg_append_scsi_buf(struct vtscsi_softc *,
1062f001371SPeter Grehan 		    struct sglist *, struct ccb_scsiio *);
1072f001371SPeter Grehan static int	vtscsi_fill_scsi_cmd_sglist(struct vtscsi_softc *,
1082f001371SPeter Grehan 		    struct vtscsi_request *, int *, int *);
1092f001371SPeter Grehan static int	vtscsi_execute_scsi_cmd(struct vtscsi_softc *,
1102f001371SPeter Grehan 		    struct vtscsi_request *);
1112f001371SPeter Grehan static int	vtscsi_start_scsi_cmd(struct vtscsi_softc *, union ccb *);
1122f001371SPeter Grehan static void	vtscsi_complete_abort_timedout_scsi_cmd(struct vtscsi_softc *,
1132f001371SPeter Grehan 		    struct vtscsi_request *);
1142f001371SPeter Grehan static int	vtscsi_abort_timedout_scsi_cmd(struct vtscsi_softc *,
1152f001371SPeter Grehan 		    struct vtscsi_request *);
1162f001371SPeter Grehan static void	vtscsi_timedout_scsi_cmd(void *);
1172f001371SPeter Grehan static cam_status vtscsi_scsi_cmd_cam_status(struct virtio_scsi_cmd_resp *);
1182f001371SPeter Grehan static cam_status vtscsi_complete_scsi_cmd_response(struct vtscsi_softc *,
1192f001371SPeter Grehan 		    struct ccb_scsiio *, struct virtio_scsi_cmd_resp *);
1202f001371SPeter Grehan static void	vtscsi_complete_scsi_cmd(struct vtscsi_softc *,
1212f001371SPeter Grehan 		    struct vtscsi_request *);
1222f001371SPeter Grehan 
1232f001371SPeter Grehan static void	vtscsi_poll_ctrl_req(struct vtscsi_softc *,
1242f001371SPeter Grehan 		    struct vtscsi_request *);
1252f001371SPeter Grehan static int	vtscsi_execute_ctrl_req(struct vtscsi_softc *,
1262f001371SPeter Grehan 		    struct vtscsi_request *, struct sglist *, int, int, int);
1272f001371SPeter Grehan static void	vtscsi_complete_abort_task_cmd(struct vtscsi_softc *c,
1282f001371SPeter Grehan 		    struct vtscsi_request *);
1292f001371SPeter Grehan static int	vtscsi_execute_abort_task_cmd(struct vtscsi_softc *,
1302f001371SPeter Grehan 		    struct vtscsi_request *);
1312f001371SPeter Grehan static int	vtscsi_execute_reset_dev_cmd(struct vtscsi_softc *,
1322f001371SPeter Grehan 		    struct vtscsi_request *);
1332f001371SPeter Grehan 
1342aaf349cSBryan Venteicher static void	vtscsi_get_request_lun(uint8_t [], target_id_t *, lun_id_t *);
1352f001371SPeter Grehan static void	vtscsi_set_request_lun(struct ccb_hdr *, uint8_t []);
13615be4953SBryan Venteicher static void	vtscsi_init_scsi_cmd_req(struct vtscsi_softc *,
13715be4953SBryan Venteicher 		    struct ccb_scsiio *, struct virtio_scsi_cmd_req *);
13815be4953SBryan Venteicher static void	vtscsi_init_ctrl_tmf_req(struct vtscsi_softc *, struct ccb_hdr *,
13915be4953SBryan Venteicher 		    uint32_t, uintptr_t, struct virtio_scsi_ctrl_tmf_req *);
1402f001371SPeter Grehan 
1412f001371SPeter Grehan static void	vtscsi_freeze_simq(struct vtscsi_softc *, int);
1422f001371SPeter Grehan static int	vtscsi_thaw_simq(struct vtscsi_softc *, int);
1432f001371SPeter Grehan 
1442f001371SPeter Grehan static void	vtscsi_announce(struct vtscsi_softc *, uint32_t, target_id_t,
1452f001371SPeter Grehan 		    lun_id_t);
1462f001371SPeter Grehan static void	vtscsi_execute_rescan(struct vtscsi_softc *, target_id_t,
1472f001371SPeter Grehan 		    lun_id_t);
1482f001371SPeter Grehan static void	vtscsi_execute_rescan_bus(struct vtscsi_softc *);
1492f001371SPeter Grehan 
1502f001371SPeter Grehan static void	vtscsi_handle_event(struct vtscsi_softc *,
1512f001371SPeter Grehan 		    struct virtio_scsi_event *);
1522f001371SPeter Grehan static int	vtscsi_enqueue_event_buf(struct vtscsi_softc *,
1532f001371SPeter Grehan 		    struct virtio_scsi_event *);
1542f001371SPeter Grehan static int	vtscsi_init_event_vq(struct vtscsi_softc *);
1552f001371SPeter Grehan static void	vtscsi_reinit_event_vq(struct vtscsi_softc *);
1562f001371SPeter Grehan static void	vtscsi_drain_event_vq(struct vtscsi_softc *);
1572f001371SPeter Grehan 
1582f001371SPeter Grehan static void	vtscsi_complete_vqs_locked(struct vtscsi_softc *);
1592f001371SPeter Grehan static void	vtscsi_complete_vqs(struct vtscsi_softc *);
1602f001371SPeter Grehan static void	vtscsi_drain_vqs(struct vtscsi_softc *);
1612f001371SPeter Grehan static void	vtscsi_cancel_request(struct vtscsi_softc *,
1622f001371SPeter Grehan 		    struct vtscsi_request *);
1632f001371SPeter Grehan static void	vtscsi_drain_vq(struct vtscsi_softc *, struct virtqueue *);
1642f001371SPeter Grehan static void	vtscsi_stop(struct vtscsi_softc *);
1652f001371SPeter Grehan static int	vtscsi_reset_bus(struct vtscsi_softc *);
1662f001371SPeter Grehan 
1672f001371SPeter Grehan static void	vtscsi_init_request(struct vtscsi_softc *,
1682f001371SPeter Grehan 		    struct vtscsi_request *);
1692f001371SPeter Grehan static int	vtscsi_alloc_requests(struct vtscsi_softc *);
1702f001371SPeter Grehan static void	vtscsi_free_requests(struct vtscsi_softc *);
1712f001371SPeter Grehan static void	vtscsi_enqueue_request(struct vtscsi_softc *,
1722f001371SPeter Grehan 		    struct vtscsi_request *);
1732f001371SPeter Grehan static struct vtscsi_request * vtscsi_dequeue_request(struct vtscsi_softc *);
1742f001371SPeter Grehan 
1752f001371SPeter Grehan static void	vtscsi_complete_request(struct vtscsi_request *);
1762f001371SPeter Grehan static void	vtscsi_complete_vq(struct vtscsi_softc *, struct virtqueue *);
1772f001371SPeter Grehan 
1786632efe4SBryan Venteicher static void	vtscsi_control_vq_intr(void *);
1796632efe4SBryan Venteicher static void	vtscsi_event_vq_intr(void *);
1806632efe4SBryan Venteicher static void	vtscsi_request_vq_intr(void *);
1812f001371SPeter Grehan static void	vtscsi_disable_vqs_intr(struct vtscsi_softc *);
1822f001371SPeter Grehan static void	vtscsi_enable_vqs_intr(struct vtscsi_softc *);
1832f001371SPeter Grehan 
1842f001371SPeter Grehan static void	vtscsi_get_tunables(struct vtscsi_softc *);
185e6cc42f1SBryan Venteicher static void	vtscsi_setup_sysctl(struct vtscsi_softc *);
1862f001371SPeter Grehan 
1872f001371SPeter Grehan static void	vtscsi_printf_req(struct vtscsi_request *, const char *,
1882f001371SPeter Grehan 		    const char *, ...);
1892f001371SPeter Grehan 
19015be4953SBryan Venteicher #define vtscsi_modern(_sc) (((_sc)->vtscsi_features & VIRTIO_F_VERSION_1) != 0)
19115be4953SBryan Venteicher #define vtscsi_htog16(_sc, _val)	virtio_htog16(vtscsi_modern(_sc), _val)
19215be4953SBryan Venteicher #define vtscsi_htog32(_sc, _val)	virtio_htog32(vtscsi_modern(_sc), _val)
19315be4953SBryan Venteicher #define vtscsi_htog64(_sc, _val)	virtio_htog64(vtscsi_modern(_sc), _val)
19415be4953SBryan Venteicher #define vtscsi_gtoh16(_sc, _val)	virtio_gtoh16(vtscsi_modern(_sc), _val)
19515be4953SBryan Venteicher #define vtscsi_gtoh32(_sc, _val)	virtio_gtoh32(vtscsi_modern(_sc), _val)
19615be4953SBryan Venteicher #define vtscsi_gtoh64(_sc, _val)	virtio_gtoh64(vtscsi_modern(_sc), _val)
19715be4953SBryan Venteicher 
1982f001371SPeter Grehan /* Global tunables. */
1992f001371SPeter Grehan /*
2002f001371SPeter Grehan  * The current QEMU VirtIO SCSI implementation does not cancel in-flight
2012f001371SPeter Grehan  * IO during virtio_stop(). So in-flight requests still complete after the
2022f001371SPeter Grehan  * device reset. We would have to wait for all the in-flight IO to complete,
2032f001371SPeter Grehan  * which defeats the typical purpose of a bus reset. We could simulate the
2042f001371SPeter Grehan  * bus reset with either I_T_NEXUS_RESET of all the targets, or with
2052f001371SPeter Grehan  * LOGICAL_UNIT_RESET of all the LUNs (assuming there is space in the
2062f001371SPeter Grehan  * control virtqueue). But this isn't very useful if things really go off
2072f001371SPeter Grehan  * the rails, so default to disabled for now.
2082f001371SPeter Grehan  */
2092f001371SPeter Grehan static int vtscsi_bus_reset_disable = 1;
2102f001371SPeter Grehan TUNABLE_INT("hw.vtscsi.bus_reset_disable", &vtscsi_bus_reset_disable);
2112f001371SPeter Grehan 
2122f001371SPeter Grehan static struct virtio_feature_desc vtscsi_feature_desc[] = {
2132f001371SPeter Grehan 	{ VIRTIO_SCSI_F_INOUT,		"InOut"		},
2142f001371SPeter Grehan 	{ VIRTIO_SCSI_F_HOTPLUG,	"Hotplug"	},
21515be4953SBryan Venteicher 	{ VIRTIO_SCSI_F_CHANGE,		"ChangeEvent"	},
21615be4953SBryan Venteicher 	{ VIRTIO_SCSI_F_T10_PI, 	"T10PI"		},
21715be4953SBryan Venteicher 
2182f001371SPeter Grehan 	{ 0, NULL }
2192f001371SPeter Grehan };
2202f001371SPeter Grehan 
2212f001371SPeter Grehan static device_method_t vtscsi_methods[] = {
2222f001371SPeter Grehan 	/* Device methods. */
2232f001371SPeter Grehan 	DEVMETHOD(device_probe,		vtscsi_probe),
2242f001371SPeter Grehan 	DEVMETHOD(device_attach,	vtscsi_attach),
2252f001371SPeter Grehan 	DEVMETHOD(device_detach,	vtscsi_detach),
2262f001371SPeter Grehan 	DEVMETHOD(device_suspend,	vtscsi_suspend),
2272f001371SPeter Grehan 	DEVMETHOD(device_resume,	vtscsi_resume),
2282f001371SPeter Grehan 
2292f001371SPeter Grehan 	DEVMETHOD_END
2302f001371SPeter Grehan };
2312f001371SPeter Grehan 
2322f001371SPeter Grehan static driver_t vtscsi_driver = {
2332f001371SPeter Grehan 	"vtscsi",
2342f001371SPeter Grehan 	vtscsi_methods,
2352f001371SPeter Grehan 	sizeof(struct vtscsi_softc)
2362f001371SPeter Grehan };
2372f001371SPeter Grehan 
2385c4c96d3SJohn Baldwin VIRTIO_DRIVER_MODULE(virtio_scsi, vtscsi_driver, vtscsi_modevent, NULL);
2392f001371SPeter Grehan MODULE_VERSION(virtio_scsi, 1);
2402f001371SPeter Grehan MODULE_DEPEND(virtio_scsi, virtio, 1, 1, 1);
2412f001371SPeter Grehan MODULE_DEPEND(virtio_scsi, cam, 1, 1, 1);
2422f001371SPeter Grehan 
243633218eeSJessica Clarke VIRTIO_SIMPLE_PNPINFO(virtio_scsi, VIRTIO_ID_SCSI, "VirtIO SCSI Adapter");
2440f6040f0SConrad Meyer 
2452f001371SPeter Grehan static int
vtscsi_modevent(module_t mod,int type,void * unused)2462f001371SPeter Grehan vtscsi_modevent(module_t mod, int type, void *unused)
2472f001371SPeter Grehan {
2482f001371SPeter Grehan 	int error;
2492f001371SPeter Grehan 
2502f001371SPeter Grehan 	switch (type) {
2512f001371SPeter Grehan 	case MOD_LOAD:
2522f001371SPeter Grehan 	case MOD_QUIESCE:
2532f001371SPeter Grehan 	case MOD_UNLOAD:
2542f001371SPeter Grehan 	case MOD_SHUTDOWN:
2552f001371SPeter Grehan 		error = 0;
2562f001371SPeter Grehan 		break;
2572f001371SPeter Grehan 	default:
2582f001371SPeter Grehan 		error = EOPNOTSUPP;
2592f001371SPeter Grehan 		break;
2602f001371SPeter Grehan 	}
2612f001371SPeter Grehan 
2622f001371SPeter Grehan 	return (error);
2632f001371SPeter Grehan }
2642f001371SPeter Grehan 
2652f001371SPeter Grehan static int
vtscsi_probe(device_t dev)2662f001371SPeter Grehan vtscsi_probe(device_t dev)
2672f001371SPeter Grehan {
2680f6040f0SConrad Meyer 	return (VIRTIO_SIMPLE_PROBE(dev, virtio_scsi));
2692f001371SPeter Grehan }
2702f001371SPeter Grehan 
2712f001371SPeter Grehan static int
vtscsi_attach(device_t dev)2722f001371SPeter Grehan vtscsi_attach(device_t dev)
2732f001371SPeter Grehan {
2742f001371SPeter Grehan 	struct vtscsi_softc *sc;
2752f001371SPeter Grehan 	struct virtio_scsi_config scsicfg;
2762f001371SPeter Grehan 	int error;
2772f001371SPeter Grehan 
2782f001371SPeter Grehan 	sc = device_get_softc(dev);
2792f001371SPeter Grehan 	sc->vtscsi_dev = dev;
280e6cc42f1SBryan Venteicher 	virtio_set_feature_desc(dev, vtscsi_feature_desc);
2812f001371SPeter Grehan 
2822f001371SPeter Grehan 	VTSCSI_LOCK_INIT(sc, device_get_nameunit(dev));
2832f001371SPeter Grehan 	TAILQ_INIT(&sc->vtscsi_req_free);
2842f001371SPeter Grehan 
2852f001371SPeter Grehan 	vtscsi_get_tunables(sc);
286e6cc42f1SBryan Venteicher 	vtscsi_setup_sysctl(sc);
2872f001371SPeter Grehan 
288e6cc42f1SBryan Venteicher 	error = vtscsi_setup_features(sc);
289e6cc42f1SBryan Venteicher 	if (error) {
290e6cc42f1SBryan Venteicher 		device_printf(dev, "cannot setup features\n");
291e6cc42f1SBryan Venteicher 		goto fail;
292e6cc42f1SBryan Venteicher 	}
2932f001371SPeter Grehan 
2948c457c88SBryan Venteicher 	vtscsi_read_config(sc, &scsicfg);
2952f001371SPeter Grehan 
2962f001371SPeter Grehan 	sc->vtscsi_max_channel = scsicfg.max_channel;
2972f001371SPeter Grehan 	sc->vtscsi_max_target = scsicfg.max_target;
2982f001371SPeter Grehan 	sc->vtscsi_max_lun = scsicfg.max_lun;
2992f001371SPeter Grehan 	sc->vtscsi_event_buf_size = scsicfg.event_info_size;
3002f001371SPeter Grehan 
3012f001371SPeter Grehan 	vtscsi_write_device_config(sc);
3022f001371SPeter Grehan 
3032f001371SPeter Grehan 	sc->vtscsi_max_nsegs = vtscsi_maximum_segments(sc, scsicfg.seg_max);
3042f001371SPeter Grehan 	sc->vtscsi_sglist = sglist_alloc(sc->vtscsi_max_nsegs, M_NOWAIT);
3052f001371SPeter Grehan 	if (sc->vtscsi_sglist == NULL) {
3062f001371SPeter Grehan 		error = ENOMEM;
3072f001371SPeter Grehan 		device_printf(dev, "cannot allocate sglist\n");
3082f001371SPeter Grehan 		goto fail;
3092f001371SPeter Grehan 	}
3102f001371SPeter Grehan 
3112f001371SPeter Grehan 	error = vtscsi_alloc_virtqueues(sc);
3122f001371SPeter Grehan 	if (error) {
3132f001371SPeter Grehan 		device_printf(dev, "cannot allocate virtqueues\n");
3142f001371SPeter Grehan 		goto fail;
3152f001371SPeter Grehan 	}
3162f001371SPeter Grehan 
317df840654SEric van Gyzen 	vtscsi_check_sizes(sc);
318df840654SEric van Gyzen 
3192f001371SPeter Grehan 	error = vtscsi_init_event_vq(sc);
3202f001371SPeter Grehan 	if (error) {
3212f001371SPeter Grehan 		device_printf(dev, "cannot populate the eventvq\n");
3222f001371SPeter Grehan 		goto fail;
3232f001371SPeter Grehan 	}
3242f001371SPeter Grehan 
3252f001371SPeter Grehan 	error = vtscsi_alloc_requests(sc);
3262f001371SPeter Grehan 	if (error) {
3272f001371SPeter Grehan 		device_printf(dev, "cannot allocate requests\n");
3282f001371SPeter Grehan 		goto fail;
3292f001371SPeter Grehan 	}
3302f001371SPeter Grehan 
3312f001371SPeter Grehan 	error = vtscsi_alloc_cam(sc);
3322f001371SPeter Grehan 	if (error) {
3332f001371SPeter Grehan 		device_printf(dev, "cannot allocate CAM structures\n");
3342f001371SPeter Grehan 		goto fail;
3352f001371SPeter Grehan 	}
3362f001371SPeter Grehan 
3372f001371SPeter Grehan 	error = virtio_setup_intr(dev, INTR_TYPE_CAM);
3382f001371SPeter Grehan 	if (error) {
3392f001371SPeter Grehan 		device_printf(dev, "cannot setup virtqueue interrupts\n");
3402f001371SPeter Grehan 		goto fail;
3412f001371SPeter Grehan 	}
3422f001371SPeter Grehan 
3432f001371SPeter Grehan 	vtscsi_enable_vqs_intr(sc);
3442f001371SPeter Grehan 
3452f001371SPeter Grehan 	/*
3462f001371SPeter Grehan 	 * Register with CAM after interrupts are enabled so we will get
3472f001371SPeter Grehan 	 * notified of the probe responses.
3482f001371SPeter Grehan 	 */
3492f001371SPeter Grehan 	error = vtscsi_register_cam(sc);
3502f001371SPeter Grehan 	if (error) {
3512f001371SPeter Grehan 		device_printf(dev, "cannot register with CAM\n");
3522f001371SPeter Grehan 		goto fail;
3532f001371SPeter Grehan 	}
3542f001371SPeter Grehan 
3552f001371SPeter Grehan fail:
3562f001371SPeter Grehan 	if (error)
3572f001371SPeter Grehan 		vtscsi_detach(dev);
3582f001371SPeter Grehan 
3592f001371SPeter Grehan 	return (error);
3602f001371SPeter Grehan }
3612f001371SPeter Grehan 
3622f001371SPeter Grehan static int
vtscsi_detach(device_t dev)3632f001371SPeter Grehan vtscsi_detach(device_t dev)
3642f001371SPeter Grehan {
3652f001371SPeter Grehan 	struct vtscsi_softc *sc;
3662f001371SPeter Grehan 
3672f001371SPeter Grehan 	sc = device_get_softc(dev);
3682f001371SPeter Grehan 
3692f001371SPeter Grehan 	VTSCSI_LOCK(sc);
3702f001371SPeter Grehan 	sc->vtscsi_flags |= VTSCSI_FLAG_DETACH;
3712f001371SPeter Grehan 	if (device_is_attached(dev))
3722f001371SPeter Grehan 		vtscsi_stop(sc);
3732f001371SPeter Grehan 	VTSCSI_UNLOCK(sc);
3742f001371SPeter Grehan 
3752f001371SPeter Grehan 	vtscsi_complete_vqs(sc);
3762f001371SPeter Grehan 	vtscsi_drain_vqs(sc);
3772f001371SPeter Grehan 
3782f001371SPeter Grehan 	vtscsi_free_cam(sc);
3792f001371SPeter Grehan 	vtscsi_free_requests(sc);
3802f001371SPeter Grehan 
3812f001371SPeter Grehan 	if (sc->vtscsi_sglist != NULL) {
3822f001371SPeter Grehan 		sglist_free(sc->vtscsi_sglist);
3832f001371SPeter Grehan 		sc->vtscsi_sglist = NULL;
3842f001371SPeter Grehan 	}
3852f001371SPeter Grehan 
3862f001371SPeter Grehan 	VTSCSI_LOCK_DESTROY(sc);
3872f001371SPeter Grehan 
3882f001371SPeter Grehan 	return (0);
3892f001371SPeter Grehan }
3902f001371SPeter Grehan 
3912f001371SPeter Grehan static int
vtscsi_suspend(device_t dev)3922f001371SPeter Grehan vtscsi_suspend(device_t dev)
3932f001371SPeter Grehan {
3942f001371SPeter Grehan 
3952f001371SPeter Grehan 	return (0);
3962f001371SPeter Grehan }
3972f001371SPeter Grehan 
3982f001371SPeter Grehan static int
vtscsi_resume(device_t dev)3992f001371SPeter Grehan vtscsi_resume(device_t dev)
4002f001371SPeter Grehan {
4012f001371SPeter Grehan 
4022f001371SPeter Grehan 	return (0);
4032f001371SPeter Grehan }
4042f001371SPeter Grehan 
405e6cc42f1SBryan Venteicher static int
vtscsi_negotiate_features(struct vtscsi_softc * sc)4062f001371SPeter Grehan vtscsi_negotiate_features(struct vtscsi_softc *sc)
4072f001371SPeter Grehan {
4082f001371SPeter Grehan 	device_t dev;
4092f001371SPeter Grehan 	uint64_t features;
4102f001371SPeter Grehan 
4112f001371SPeter Grehan 	dev = sc->vtscsi_dev;
41215be4953SBryan Venteicher 	features = VTSCSI_FEATURES;
41315be4953SBryan Venteicher 
41415be4953SBryan Venteicher 	sc->vtscsi_features = virtio_negotiate_features(dev, features);
415e6cc42f1SBryan Venteicher 	return (virtio_finalize_features(dev));
416e6cc42f1SBryan Venteicher }
417e6cc42f1SBryan Venteicher 
418e6cc42f1SBryan Venteicher static int
vtscsi_setup_features(struct vtscsi_softc * sc)419e6cc42f1SBryan Venteicher vtscsi_setup_features(struct vtscsi_softc *sc)
420e6cc42f1SBryan Venteicher {
421e6cc42f1SBryan Venteicher 	device_t dev;
422e6cc42f1SBryan Venteicher 	int error;
423e6cc42f1SBryan Venteicher 
424e6cc42f1SBryan Venteicher 	dev = sc->vtscsi_dev;
425e6cc42f1SBryan Venteicher 
426e6cc42f1SBryan Venteicher 	error = vtscsi_negotiate_features(sc);
427e6cc42f1SBryan Venteicher 	if (error)
428e6cc42f1SBryan Venteicher 		return (error);
429e6cc42f1SBryan Venteicher 
430e6cc42f1SBryan Venteicher 	if (virtio_with_feature(dev, VIRTIO_RING_F_INDIRECT_DESC))
431e6cc42f1SBryan Venteicher 		sc->vtscsi_flags |= VTSCSI_FLAG_INDIRECT;
432e6cc42f1SBryan Venteicher 	if (virtio_with_feature(dev, VIRTIO_SCSI_F_INOUT))
433e6cc42f1SBryan Venteicher 		sc->vtscsi_flags |= VTSCSI_FLAG_BIDIRECTIONAL;
434e6cc42f1SBryan Venteicher 	if (virtio_with_feature(dev, VIRTIO_SCSI_F_HOTPLUG))
435e6cc42f1SBryan Venteicher 		sc->vtscsi_flags |= VTSCSI_FLAG_HOTPLUG;
436e6cc42f1SBryan Venteicher 
437e6cc42f1SBryan Venteicher 	return (0);
4382f001371SPeter Grehan }
4392f001371SPeter Grehan 
4408c457c88SBryan Venteicher #define VTSCSI_GET_CONFIG(_dev, _field, _cfg)			\
4418c457c88SBryan Venteicher 	virtio_read_device_config(_dev,				\
4428c457c88SBryan Venteicher 	    offsetof(struct virtio_scsi_config, _field),	\
4438c457c88SBryan Venteicher 	    &(_cfg)->_field, sizeof((_cfg)->_field))		\
4448c457c88SBryan Venteicher 
4458c457c88SBryan Venteicher static void
vtscsi_read_config(struct vtscsi_softc * sc,struct virtio_scsi_config * scsicfg)4468c457c88SBryan Venteicher vtscsi_read_config(struct vtscsi_softc *sc,
4478c457c88SBryan Venteicher     struct virtio_scsi_config *scsicfg)
4488c457c88SBryan Venteicher {
4498c457c88SBryan Venteicher 	device_t dev;
4508c457c88SBryan Venteicher 
4518c457c88SBryan Venteicher 	dev = sc->vtscsi_dev;
4528c457c88SBryan Venteicher 
4538c457c88SBryan Venteicher 	bzero(scsicfg, sizeof(struct virtio_scsi_config));
4548c457c88SBryan Venteicher 
4558c457c88SBryan Venteicher 	VTSCSI_GET_CONFIG(dev, num_queues, scsicfg);
4568c457c88SBryan Venteicher 	VTSCSI_GET_CONFIG(dev, seg_max, scsicfg);
4578c457c88SBryan Venteicher 	VTSCSI_GET_CONFIG(dev, max_sectors, scsicfg);
4588c457c88SBryan Venteicher 	VTSCSI_GET_CONFIG(dev, cmd_per_lun, scsicfg);
4598c457c88SBryan Venteicher 	VTSCSI_GET_CONFIG(dev, event_info_size, scsicfg);
4608c457c88SBryan Venteicher 	VTSCSI_GET_CONFIG(dev, sense_size, scsicfg);
4618c457c88SBryan Venteicher 	VTSCSI_GET_CONFIG(dev, cdb_size, scsicfg);
4628c457c88SBryan Venteicher 	VTSCSI_GET_CONFIG(dev, max_channel, scsicfg);
4638c457c88SBryan Venteicher 	VTSCSI_GET_CONFIG(dev, max_target, scsicfg);
4648c457c88SBryan Venteicher 	VTSCSI_GET_CONFIG(dev, max_lun, scsicfg);
4658c457c88SBryan Venteicher }
4668c457c88SBryan Venteicher 
4678c457c88SBryan Venteicher #undef VTSCSI_GET_CONFIG
4688c457c88SBryan Venteicher 
4692f001371SPeter Grehan static int
vtscsi_maximum_segments(struct vtscsi_softc * sc,int seg_max)4702f001371SPeter Grehan vtscsi_maximum_segments(struct vtscsi_softc *sc, int seg_max)
4712f001371SPeter Grehan {
4722f001371SPeter Grehan 	int nsegs;
4732f001371SPeter Grehan 
4742f001371SPeter Grehan 	nsegs = VTSCSI_MIN_SEGMENTS;
4752f001371SPeter Grehan 
4762f001371SPeter Grehan 	if (seg_max > 0) {
477cd853791SKonstantin Belousov 		nsegs += MIN(seg_max, maxphys / PAGE_SIZE + 1);
4782f001371SPeter Grehan 		if (sc->vtscsi_flags & VTSCSI_FLAG_INDIRECT)
4792f001371SPeter Grehan 			nsegs = MIN(nsegs, VIRTIO_MAX_INDIRECT);
4802f001371SPeter Grehan 	} else
4812f001371SPeter Grehan 		nsegs += 1;
4822f001371SPeter Grehan 
4832f001371SPeter Grehan 	return (nsegs);
4842f001371SPeter Grehan }
4852f001371SPeter Grehan 
4862f001371SPeter Grehan static int
vtscsi_alloc_virtqueues(struct vtscsi_softc * sc)4872f001371SPeter Grehan vtscsi_alloc_virtqueues(struct vtscsi_softc *sc)
4882f001371SPeter Grehan {
4892f001371SPeter Grehan 	device_t dev;
4902f001371SPeter Grehan 	struct vq_alloc_info vq_info[3];
4912f001371SPeter Grehan 	int nvqs;
4922f001371SPeter Grehan 
4932f001371SPeter Grehan 	dev = sc->vtscsi_dev;
4942f001371SPeter Grehan 	nvqs = 3;
4952f001371SPeter Grehan 
4962f001371SPeter Grehan 	VQ_ALLOC_INFO_INIT(&vq_info[0], 0, vtscsi_control_vq_intr, sc,
4972f001371SPeter Grehan 	    &sc->vtscsi_control_vq, "%s control", device_get_nameunit(dev));
4982f001371SPeter Grehan 
4992f001371SPeter Grehan 	VQ_ALLOC_INFO_INIT(&vq_info[1], 0, vtscsi_event_vq_intr, sc,
5002f001371SPeter Grehan 	    &sc->vtscsi_event_vq, "%s event", device_get_nameunit(dev));
5012f001371SPeter Grehan 
5022f001371SPeter Grehan 	VQ_ALLOC_INFO_INIT(&vq_info[2], sc->vtscsi_max_nsegs,
5032f001371SPeter Grehan 	    vtscsi_request_vq_intr, sc, &sc->vtscsi_request_vq,
5042f001371SPeter Grehan 	    "%s request", device_get_nameunit(dev));
5052f001371SPeter Grehan 
506180c0240SMina Galić 	return (virtio_alloc_virtqueues(dev, nvqs, vq_info));
5072f001371SPeter Grehan }
5082f001371SPeter Grehan 
5092f001371SPeter Grehan static void
vtscsi_check_sizes(struct vtscsi_softc * sc)510df840654SEric van Gyzen vtscsi_check_sizes(struct vtscsi_softc *sc)
511df840654SEric van Gyzen {
512df840654SEric van Gyzen 	int rqsize;
513df840654SEric van Gyzen 
514df840654SEric van Gyzen 	if ((sc->vtscsi_flags & VTSCSI_FLAG_INDIRECT) == 0) {
515df840654SEric van Gyzen 		/*
516df840654SEric van Gyzen 		 * Ensure the assertions in virtqueue_enqueue(),
517df840654SEric van Gyzen 		 * even if the hypervisor reports a bad seg_max.
518df840654SEric van Gyzen 		 */
519df840654SEric van Gyzen 		rqsize = virtqueue_size(sc->vtscsi_request_vq);
520df840654SEric van Gyzen 		if (sc->vtscsi_max_nsegs > rqsize) {
521df840654SEric van Gyzen 			device_printf(sc->vtscsi_dev,
522df840654SEric van Gyzen 			    "clamping seg_max (%d %d)\n", sc->vtscsi_max_nsegs,
523df840654SEric van Gyzen 			    rqsize);
524df840654SEric van Gyzen 			sc->vtscsi_max_nsegs = rqsize;
525df840654SEric van Gyzen 		}
526df840654SEric van Gyzen 	}
527df840654SEric van Gyzen }
528df840654SEric van Gyzen 
529df840654SEric van Gyzen static void
vtscsi_write_device_config(struct vtscsi_softc * sc)5302f001371SPeter Grehan vtscsi_write_device_config(struct vtscsi_softc *sc)
5312f001371SPeter Grehan {
5322f001371SPeter Grehan 
5332f001371SPeter Grehan 	virtio_write_dev_config_4(sc->vtscsi_dev,
5342f001371SPeter Grehan 	    offsetof(struct virtio_scsi_config, sense_size),
5352f001371SPeter Grehan 	    VIRTIO_SCSI_SENSE_SIZE);
5362f001371SPeter Grehan 
5372f001371SPeter Grehan 	/*
5382f001371SPeter Grehan 	 * This is the size in the virtio_scsi_cmd_req structure. Note
5392f001371SPeter Grehan 	 * this value (32) is larger than the maximum CAM CDB size (16).
5402f001371SPeter Grehan 	 */
5412f001371SPeter Grehan 	virtio_write_dev_config_4(sc->vtscsi_dev,
5422f001371SPeter Grehan 	    offsetof(struct virtio_scsi_config, cdb_size),
5432f001371SPeter Grehan 	    VIRTIO_SCSI_CDB_SIZE);
5442f001371SPeter Grehan }
5452f001371SPeter Grehan 
5462f001371SPeter Grehan static int
vtscsi_reinit(struct vtscsi_softc * sc)5472f001371SPeter Grehan vtscsi_reinit(struct vtscsi_softc *sc)
5482f001371SPeter Grehan {
5492f001371SPeter Grehan 	device_t dev;
5502f001371SPeter Grehan 	int error;
5512f001371SPeter Grehan 
5522f001371SPeter Grehan 	dev = sc->vtscsi_dev;
5532f001371SPeter Grehan 
5542f001371SPeter Grehan 	error = virtio_reinit(dev, sc->vtscsi_features);
5552f001371SPeter Grehan 	if (error == 0) {
5562f001371SPeter Grehan 		vtscsi_write_device_config(sc);
5572f001371SPeter Grehan 		virtio_reinit_complete(dev);
55815be4953SBryan Venteicher 		vtscsi_reinit_event_vq(sc);
5592f001371SPeter Grehan 
5602f001371SPeter Grehan 		vtscsi_enable_vqs_intr(sc);
5612f001371SPeter Grehan 	}
5622f001371SPeter Grehan 
5632f001371SPeter Grehan 	vtscsi_dprintf(sc, VTSCSI_TRACE, "error=%d\n", error);
5642f001371SPeter Grehan 
5652f001371SPeter Grehan 	return (error);
5662f001371SPeter Grehan }
5672f001371SPeter Grehan 
5682f001371SPeter Grehan static int
vtscsi_alloc_cam(struct vtscsi_softc * sc)5692f001371SPeter Grehan vtscsi_alloc_cam(struct vtscsi_softc *sc)
5702f001371SPeter Grehan {
5712f001371SPeter Grehan 	device_t dev;
5722f001371SPeter Grehan 	struct cam_devq *devq;
5732f001371SPeter Grehan 	int openings;
5742f001371SPeter Grehan 
5752f001371SPeter Grehan 	dev = sc->vtscsi_dev;
5762f001371SPeter Grehan 	openings = sc->vtscsi_nrequests - VTSCSI_RESERVED_REQUESTS;
5772f001371SPeter Grehan 
5782f001371SPeter Grehan 	devq = cam_simq_alloc(openings);
5792f001371SPeter Grehan 	if (devq == NULL) {
5802f001371SPeter Grehan 		device_printf(dev, "cannot allocate SIM queue\n");
5812f001371SPeter Grehan 		return (ENOMEM);
5822f001371SPeter Grehan 	}
5832f001371SPeter Grehan 
5842f001371SPeter Grehan 	sc->vtscsi_sim = cam_sim_alloc(vtscsi_cam_action, vtscsi_cam_poll,
5852f001371SPeter Grehan 	    "vtscsi", sc, device_get_unit(dev), VTSCSI_MTX(sc), 1,
5862f001371SPeter Grehan 	    openings, devq);
5872f001371SPeter Grehan 	if (sc->vtscsi_sim == NULL) {
5882f001371SPeter Grehan 		cam_simq_free(devq);
5892f001371SPeter Grehan 		device_printf(dev, "cannot allocate SIM\n");
5902f001371SPeter Grehan 		return (ENOMEM);
5912f001371SPeter Grehan 	}
5922f001371SPeter Grehan 
5932f001371SPeter Grehan 	return (0);
5942f001371SPeter Grehan }
5952f001371SPeter Grehan 
5962f001371SPeter Grehan static int
vtscsi_register_cam(struct vtscsi_softc * sc)5972f001371SPeter Grehan vtscsi_register_cam(struct vtscsi_softc *sc)
5982f001371SPeter Grehan {
5992f001371SPeter Grehan 	device_t dev;
6002f001371SPeter Grehan 	int registered, error;
6012f001371SPeter Grehan 
6022f001371SPeter Grehan 	dev = sc->vtscsi_dev;
6032f001371SPeter Grehan 	registered = 0;
6042f001371SPeter Grehan 
6052f001371SPeter Grehan 	VTSCSI_LOCK(sc);
6062f001371SPeter Grehan 
6072f001371SPeter Grehan 	if (xpt_bus_register(sc->vtscsi_sim, dev, 0) != CAM_SUCCESS) {
6082f001371SPeter Grehan 		error = ENOMEM;
6092f001371SPeter Grehan 		device_printf(dev, "cannot register XPT bus\n");
6102f001371SPeter Grehan 		goto fail;
6112f001371SPeter Grehan 	}
6122f001371SPeter Grehan 
6132f001371SPeter Grehan 	registered = 1;
6142f001371SPeter Grehan 
6152f001371SPeter Grehan 	if (xpt_create_path(&sc->vtscsi_path, NULL,
6162f001371SPeter Grehan 	    cam_sim_path(sc->vtscsi_sim), CAM_TARGET_WILDCARD,
6172f001371SPeter Grehan 	    CAM_LUN_WILDCARD) != CAM_REQ_CMP) {
6182f001371SPeter Grehan 		error = ENOMEM;
6192f001371SPeter Grehan 		device_printf(dev, "cannot create bus path\n");
6202f001371SPeter Grehan 		goto fail;
6212f001371SPeter Grehan 	}
6222f001371SPeter Grehan 
6232f001371SPeter Grehan 	if (vtscsi_register_async(sc) != CAM_REQ_CMP) {
6242f001371SPeter Grehan 		error = EIO;
6252f001371SPeter Grehan 		device_printf(dev, "cannot register async callback\n");
6262f001371SPeter Grehan 		goto fail;
6272f001371SPeter Grehan 	}
6282f001371SPeter Grehan 
6294d5919ecSBryan Venteicher 	VTSCSI_UNLOCK(sc);
6304d5919ecSBryan Venteicher 
6312f001371SPeter Grehan 	return (0);
6322f001371SPeter Grehan 
6332f001371SPeter Grehan fail:
6342f001371SPeter Grehan 	if (sc->vtscsi_path != NULL) {
6352f001371SPeter Grehan 		xpt_free_path(sc->vtscsi_path);
6362f001371SPeter Grehan 		sc->vtscsi_path = NULL;
6372f001371SPeter Grehan 	}
6382f001371SPeter Grehan 
6392f001371SPeter Grehan 	if (registered != 0)
6402f001371SPeter Grehan 		xpt_bus_deregister(cam_sim_path(sc->vtscsi_sim));
6412f001371SPeter Grehan 
6422f001371SPeter Grehan 	VTSCSI_UNLOCK(sc);
6432f001371SPeter Grehan 
6442f001371SPeter Grehan 	return (error);
6452f001371SPeter Grehan }
6462f001371SPeter Grehan 
6472f001371SPeter Grehan static void
vtscsi_free_cam(struct vtscsi_softc * sc)6482f001371SPeter Grehan vtscsi_free_cam(struct vtscsi_softc *sc)
6492f001371SPeter Grehan {
6502f001371SPeter Grehan 
6512f001371SPeter Grehan 	VTSCSI_LOCK(sc);
6522f001371SPeter Grehan 
6532f001371SPeter Grehan 	if (sc->vtscsi_path != NULL) {
6542f001371SPeter Grehan 		vtscsi_deregister_async(sc);
6552f001371SPeter Grehan 
6562f001371SPeter Grehan 		xpt_free_path(sc->vtscsi_path);
6572f001371SPeter Grehan 		sc->vtscsi_path = NULL;
6582f001371SPeter Grehan 
6592f001371SPeter Grehan 		xpt_bus_deregister(cam_sim_path(sc->vtscsi_sim));
6602f001371SPeter Grehan 	}
6612f001371SPeter Grehan 
6622f001371SPeter Grehan 	if (sc->vtscsi_sim != NULL) {
6632f001371SPeter Grehan 		cam_sim_free(sc->vtscsi_sim, 1);
6642f001371SPeter Grehan 		sc->vtscsi_sim = NULL;
6652f001371SPeter Grehan 	}
6662f001371SPeter Grehan 
6672f001371SPeter Grehan 	VTSCSI_UNLOCK(sc);
6682f001371SPeter Grehan }
6692f001371SPeter Grehan 
6702f001371SPeter Grehan static void
vtscsi_cam_async(void * cb_arg,uint32_t code,struct cam_path * path,void * arg)6712f001371SPeter Grehan vtscsi_cam_async(void *cb_arg, uint32_t code, struct cam_path *path, void *arg)
6722f001371SPeter Grehan {
6732f001371SPeter Grehan 	struct cam_sim *sim;
6742f001371SPeter Grehan 	struct vtscsi_softc *sc;
6752f001371SPeter Grehan 
6762f001371SPeter Grehan 	sim = cb_arg;
6772f001371SPeter Grehan 	sc = cam_sim_softc(sim);
6782f001371SPeter Grehan 
6792f001371SPeter Grehan 	vtscsi_dprintf(sc, VTSCSI_TRACE, "code=%u\n", code);
6802f001371SPeter Grehan 
6812f001371SPeter Grehan 	/*
6822f001371SPeter Grehan 	 * TODO Once QEMU supports event reporting, we should
6832f001371SPeter Grehan 	 *      (un)subscribe to events here.
6842f001371SPeter Grehan 	 */
6852f001371SPeter Grehan 	switch (code) {
6862f001371SPeter Grehan 	case AC_FOUND_DEVICE:
6872f001371SPeter Grehan 		break;
6882f001371SPeter Grehan 	case AC_LOST_DEVICE:
6892f001371SPeter Grehan 		break;
6902f001371SPeter Grehan 	}
6912f001371SPeter Grehan }
6922f001371SPeter Grehan 
6932f001371SPeter Grehan static int
vtscsi_register_async(struct vtscsi_softc * sc)6942f001371SPeter Grehan vtscsi_register_async(struct vtscsi_softc *sc)
6952f001371SPeter Grehan {
6962f001371SPeter Grehan 	struct ccb_setasync csa;
6972f001371SPeter Grehan 
6985b81e2e1SMark Johnston 	memset(&csa, 0, sizeof(csa));
6992f001371SPeter Grehan 	xpt_setup_ccb(&csa.ccb_h, sc->vtscsi_path, 5);
7002f001371SPeter Grehan 	csa.ccb_h.func_code = XPT_SASYNC_CB;
7012f001371SPeter Grehan 	csa.event_enable = AC_LOST_DEVICE | AC_FOUND_DEVICE;
7022f001371SPeter Grehan 	csa.callback = vtscsi_cam_async;
7032f001371SPeter Grehan 	csa.callback_arg = sc->vtscsi_sim;
7042f001371SPeter Grehan 
7052f001371SPeter Grehan 	xpt_action((union ccb *) &csa);
7062f001371SPeter Grehan 
7072f001371SPeter Grehan 	return (csa.ccb_h.status);
7082f001371SPeter Grehan }
7092f001371SPeter Grehan 
7102f001371SPeter Grehan static void
vtscsi_deregister_async(struct vtscsi_softc * sc)7112f001371SPeter Grehan vtscsi_deregister_async(struct vtscsi_softc *sc)
7122f001371SPeter Grehan {
7132f001371SPeter Grehan 	struct ccb_setasync csa;
7142f001371SPeter Grehan 
7155b81e2e1SMark Johnston 	memset(&csa, 0, sizeof(csa));
7162f001371SPeter Grehan 	xpt_setup_ccb(&csa.ccb_h, sc->vtscsi_path, 5);
7172f001371SPeter Grehan 	csa.ccb_h.func_code = XPT_SASYNC_CB;
7182f001371SPeter Grehan 	csa.event_enable = 0;
7192f001371SPeter Grehan 	csa.callback = vtscsi_cam_async;
7202f001371SPeter Grehan 	csa.callback_arg = sc->vtscsi_sim;
7212f001371SPeter Grehan 
7222f001371SPeter Grehan 	xpt_action((union ccb *) &csa);
7232f001371SPeter Grehan }
7242f001371SPeter Grehan 
7252f001371SPeter Grehan static void
vtscsi_cam_action(struct cam_sim * sim,union ccb * ccb)7262f001371SPeter Grehan vtscsi_cam_action(struct cam_sim *sim, union ccb *ccb)
7272f001371SPeter Grehan {
7282f001371SPeter Grehan 	struct vtscsi_softc *sc;
7292f001371SPeter Grehan 	struct ccb_hdr *ccbh;
7302f001371SPeter Grehan 
7312f001371SPeter Grehan 	sc = cam_sim_softc(sim);
7322f001371SPeter Grehan 	ccbh = &ccb->ccb_h;
7332f001371SPeter Grehan 
7342f001371SPeter Grehan 	VTSCSI_LOCK_OWNED(sc);
7352f001371SPeter Grehan 
7362f001371SPeter Grehan 	if (sc->vtscsi_flags & VTSCSI_FLAG_DETACH) {
7372f001371SPeter Grehan 		/*
7382f001371SPeter Grehan 		 * The VTSCSI_MTX is briefly dropped between setting
7392f001371SPeter Grehan 		 * VTSCSI_FLAG_DETACH and deregistering with CAM, so
7402f001371SPeter Grehan 		 * drop any CCBs that come in during that window.
7412f001371SPeter Grehan 		 */
7422f001371SPeter Grehan 		ccbh->status = CAM_NO_HBA;
7432f001371SPeter Grehan 		xpt_done(ccb);
7442f001371SPeter Grehan 		return;
7452f001371SPeter Grehan 	}
7462f001371SPeter Grehan 
7472f001371SPeter Grehan 	switch (ccbh->func_code) {
7482f001371SPeter Grehan 	case XPT_SCSI_IO:
7492f001371SPeter Grehan 		vtscsi_cam_scsi_io(sc, sim, ccb);
7502f001371SPeter Grehan 		break;
7512f001371SPeter Grehan 
7522f001371SPeter Grehan 	case XPT_SET_TRAN_SETTINGS:
7532f001371SPeter Grehan 		ccbh->status = CAM_FUNC_NOTAVAIL;
7542f001371SPeter Grehan 		xpt_done(ccb);
7552f001371SPeter Grehan 		break;
7562f001371SPeter Grehan 
7572f001371SPeter Grehan 	case XPT_GET_TRAN_SETTINGS:
7582f001371SPeter Grehan 		vtscsi_cam_get_tran_settings(sc, ccb);
7592f001371SPeter Grehan 		break;
7602f001371SPeter Grehan 
7612f001371SPeter Grehan 	case XPT_RESET_BUS:
7622f001371SPeter Grehan 		vtscsi_cam_reset_bus(sc, ccb);
7632f001371SPeter Grehan 		break;
7642f001371SPeter Grehan 
7652f001371SPeter Grehan 	case XPT_RESET_DEV:
7662f001371SPeter Grehan 		vtscsi_cam_reset_dev(sc, ccb);
7672f001371SPeter Grehan 		break;
7682f001371SPeter Grehan 
7692f001371SPeter Grehan 	case XPT_ABORT:
7702f001371SPeter Grehan 		vtscsi_cam_abort(sc, ccb);
7712f001371SPeter Grehan 		break;
7722f001371SPeter Grehan 
7732f001371SPeter Grehan 	case XPT_CALC_GEOMETRY:
7742f001371SPeter Grehan 		cam_calc_geometry(&ccb->ccg, 1);
7752f001371SPeter Grehan 		xpt_done(ccb);
7762f001371SPeter Grehan 		break;
7772f001371SPeter Grehan 
7782f001371SPeter Grehan 	case XPT_PATH_INQ:
7792f001371SPeter Grehan 		vtscsi_cam_path_inquiry(sc, sim, ccb);
7802f001371SPeter Grehan 		break;
7812f001371SPeter Grehan 
7822f001371SPeter Grehan 	default:
7832f001371SPeter Grehan 		vtscsi_dprintf(sc, VTSCSI_ERROR,
7842f001371SPeter Grehan 		    "invalid ccb=%p func=%#x\n", ccb, ccbh->func_code);
7852f001371SPeter Grehan 
7862f001371SPeter Grehan 		ccbh->status = CAM_REQ_INVALID;
7872f001371SPeter Grehan 		xpt_done(ccb);
7882f001371SPeter Grehan 		break;
7892f001371SPeter Grehan 	}
7902f001371SPeter Grehan }
7912f001371SPeter Grehan 
7922f001371SPeter Grehan static void
vtscsi_cam_poll(struct cam_sim * sim)7932f001371SPeter Grehan vtscsi_cam_poll(struct cam_sim *sim)
7942f001371SPeter Grehan {
7952f001371SPeter Grehan 	struct vtscsi_softc *sc;
7962f001371SPeter Grehan 
7972f001371SPeter Grehan 	sc = cam_sim_softc(sim);
7982f001371SPeter Grehan 
7992f001371SPeter Grehan 	vtscsi_complete_vqs_locked(sc);
8002f001371SPeter Grehan }
8012f001371SPeter Grehan 
8022f001371SPeter Grehan static void
vtscsi_cam_scsi_io(struct vtscsi_softc * sc,struct cam_sim * sim,union ccb * ccb)8032f001371SPeter Grehan vtscsi_cam_scsi_io(struct vtscsi_softc *sc, struct cam_sim *sim,
8042f001371SPeter Grehan     union ccb *ccb)
8052f001371SPeter Grehan {
8062f001371SPeter Grehan 	struct ccb_hdr *ccbh;
8072f001371SPeter Grehan 	struct ccb_scsiio *csio;
8082f001371SPeter Grehan 	int error;
8092f001371SPeter Grehan 
8102f001371SPeter Grehan 	ccbh = &ccb->ccb_h;
8112f001371SPeter Grehan 	csio = &ccb->csio;
8122f001371SPeter Grehan 
8132f001371SPeter Grehan 	if (csio->cdb_len > VIRTIO_SCSI_CDB_SIZE) {
8142f001371SPeter Grehan 		error = EINVAL;
8152f001371SPeter Grehan 		ccbh->status = CAM_REQ_INVALID;
8162f001371SPeter Grehan 		goto done;
8172f001371SPeter Grehan 	}
8182f001371SPeter Grehan 
8192f001371SPeter Grehan 	if ((ccbh->flags & CAM_DIR_MASK) == CAM_DIR_BOTH &&
8202f001371SPeter Grehan 	    (sc->vtscsi_flags & VTSCSI_FLAG_BIDIRECTIONAL) == 0) {
8212f001371SPeter Grehan 		error = EINVAL;
8222f001371SPeter Grehan 		ccbh->status = CAM_REQ_INVALID;
8232f001371SPeter Grehan 		goto done;
8242f001371SPeter Grehan 	}
8252f001371SPeter Grehan 
8262f001371SPeter Grehan 	error = vtscsi_start_scsi_cmd(sc, ccb);
8272f001371SPeter Grehan 
8282f001371SPeter Grehan done:
8292f001371SPeter Grehan 	if (error) {
8302f001371SPeter Grehan 		vtscsi_dprintf(sc, VTSCSI_ERROR,
8312f001371SPeter Grehan 		    "error=%d ccb=%p status=%#x\n", error, ccb, ccbh->status);
8322f001371SPeter Grehan 		xpt_done(ccb);
8332f001371SPeter Grehan 	}
8342f001371SPeter Grehan }
8352f001371SPeter Grehan 
8362f001371SPeter Grehan static void
vtscsi_cam_get_tran_settings(struct vtscsi_softc * sc,union ccb * ccb)8372f001371SPeter Grehan vtscsi_cam_get_tran_settings(struct vtscsi_softc *sc, union ccb *ccb)
8382f001371SPeter Grehan {
8392f001371SPeter Grehan 	struct ccb_trans_settings *cts;
8402f001371SPeter Grehan 	struct ccb_trans_settings_scsi *scsi;
8412f001371SPeter Grehan 
8422f001371SPeter Grehan 	cts = &ccb->cts;
8432f001371SPeter Grehan 	scsi = &cts->proto_specific.scsi;
8442f001371SPeter Grehan 
8452f001371SPeter Grehan 	cts->protocol = PROTO_SCSI;
8462f001371SPeter Grehan 	cts->protocol_version = SCSI_REV_SPC3;
8472f001371SPeter Grehan 	cts->transport = XPORT_SAS;
8482f001371SPeter Grehan 	cts->transport_version = 0;
8492f001371SPeter Grehan 
8502f001371SPeter Grehan 	scsi->valid = CTS_SCSI_VALID_TQ;
8512f001371SPeter Grehan 	scsi->flags = CTS_SCSI_FLAGS_TAG_ENB;
8522f001371SPeter Grehan 
8532f001371SPeter Grehan 	ccb->ccb_h.status = CAM_REQ_CMP;
8542f001371SPeter Grehan 	xpt_done(ccb);
8552f001371SPeter Grehan }
8562f001371SPeter Grehan 
8572f001371SPeter Grehan static void
vtscsi_cam_reset_bus(struct vtscsi_softc * sc,union ccb * ccb)8582f001371SPeter Grehan vtscsi_cam_reset_bus(struct vtscsi_softc *sc, union ccb *ccb)
8592f001371SPeter Grehan {
8602f001371SPeter Grehan 	int error;
8612f001371SPeter Grehan 
8622f001371SPeter Grehan 	error = vtscsi_reset_bus(sc);
8632f001371SPeter Grehan 	if (error == 0)
8642f001371SPeter Grehan 		ccb->ccb_h.status = CAM_REQ_CMP;
8652f001371SPeter Grehan 	else
8662f001371SPeter Grehan 		ccb->ccb_h.status = CAM_REQ_CMP_ERR;
8672f001371SPeter Grehan 
8682f001371SPeter Grehan 	vtscsi_dprintf(sc, VTSCSI_TRACE, "error=%d ccb=%p status=%#x\n",
8692f001371SPeter Grehan 	    error, ccb, ccb->ccb_h.status);
8702f001371SPeter Grehan 
8712f001371SPeter Grehan 	xpt_done(ccb);
8722f001371SPeter Grehan }
8732f001371SPeter Grehan 
8742f001371SPeter Grehan static void
vtscsi_cam_reset_dev(struct vtscsi_softc * sc,union ccb * ccb)8752f001371SPeter Grehan vtscsi_cam_reset_dev(struct vtscsi_softc *sc, union ccb *ccb)
8762f001371SPeter Grehan {
8772f001371SPeter Grehan 	struct ccb_hdr *ccbh;
8782f001371SPeter Grehan 	struct vtscsi_request *req;
8792f001371SPeter Grehan 	int error;
8802f001371SPeter Grehan 
8812f001371SPeter Grehan 	ccbh = &ccb->ccb_h;
8822f001371SPeter Grehan 
8832f001371SPeter Grehan 	req = vtscsi_dequeue_request(sc);
8842f001371SPeter Grehan 	if (req == NULL) {
8852f001371SPeter Grehan 		error = EAGAIN;
8862f001371SPeter Grehan 		vtscsi_freeze_simq(sc, VTSCSI_REQUEST);
8872f001371SPeter Grehan 		goto fail;
8882f001371SPeter Grehan 	}
8892f001371SPeter Grehan 
8902f001371SPeter Grehan 	req->vsr_ccb = ccb;
8912f001371SPeter Grehan 
8922f001371SPeter Grehan 	error = vtscsi_execute_reset_dev_cmd(sc, req);
8932f001371SPeter Grehan 	if (error == 0)
8942f001371SPeter Grehan 		return;
8952f001371SPeter Grehan 
8962f001371SPeter Grehan 	vtscsi_enqueue_request(sc, req);
8972f001371SPeter Grehan 
8982f001371SPeter Grehan fail:
8992f001371SPeter Grehan 	vtscsi_dprintf(sc, VTSCSI_ERROR, "error=%d req=%p ccb=%p\n",
9002f001371SPeter Grehan 	    error, req, ccb);
9012f001371SPeter Grehan 
9022f001371SPeter Grehan 	if (error == EAGAIN)
9032f001371SPeter Grehan 		ccbh->status = CAM_RESRC_UNAVAIL;
9042f001371SPeter Grehan 	else
9052f001371SPeter Grehan 		ccbh->status = CAM_REQ_CMP_ERR;
9062f001371SPeter Grehan 
9072f001371SPeter Grehan 	xpt_done(ccb);
9082f001371SPeter Grehan }
9092f001371SPeter Grehan 
9102f001371SPeter Grehan static void
vtscsi_cam_abort(struct vtscsi_softc * sc,union ccb * ccb)9112f001371SPeter Grehan vtscsi_cam_abort(struct vtscsi_softc *sc, union ccb *ccb)
9122f001371SPeter Grehan {
9132f001371SPeter Grehan 	struct vtscsi_request *req;
9142f001371SPeter Grehan 	struct ccb_hdr *ccbh;
9152f001371SPeter Grehan 	int error;
9162f001371SPeter Grehan 
9172f001371SPeter Grehan 	ccbh = &ccb->ccb_h;
9182f001371SPeter Grehan 
9192f001371SPeter Grehan 	req = vtscsi_dequeue_request(sc);
9202f001371SPeter Grehan 	if (req == NULL) {
9212f001371SPeter Grehan 		error = EAGAIN;
9222f001371SPeter Grehan 		vtscsi_freeze_simq(sc, VTSCSI_REQUEST);
9232f001371SPeter Grehan 		goto fail;
9242f001371SPeter Grehan 	}
9252f001371SPeter Grehan 
9262f001371SPeter Grehan 	req->vsr_ccb = ccb;
9272f001371SPeter Grehan 
9282f001371SPeter Grehan 	error = vtscsi_execute_abort_task_cmd(sc, req);
9292f001371SPeter Grehan 	if (error == 0)
9302f001371SPeter Grehan 		return;
9312f001371SPeter Grehan 
9322f001371SPeter Grehan 	vtscsi_enqueue_request(sc, req);
9332f001371SPeter Grehan 
9342f001371SPeter Grehan fail:
9352f001371SPeter Grehan 	vtscsi_dprintf(sc, VTSCSI_ERROR, "error=%d req=%p ccb=%p\n",
9362f001371SPeter Grehan 	    error, req, ccb);
9372f001371SPeter Grehan 
9382f001371SPeter Grehan 	if (error == EAGAIN)
9392f001371SPeter Grehan 		ccbh->status = CAM_RESRC_UNAVAIL;
9402f001371SPeter Grehan 	else
9412f001371SPeter Grehan 		ccbh->status = CAM_REQ_CMP_ERR;
9422f001371SPeter Grehan 
9432f001371SPeter Grehan 	xpt_done(ccb);
9442f001371SPeter Grehan }
9452f001371SPeter Grehan 
9462f001371SPeter Grehan static void
vtscsi_cam_path_inquiry(struct vtscsi_softc * sc,struct cam_sim * sim,union ccb * ccb)9472f001371SPeter Grehan vtscsi_cam_path_inquiry(struct vtscsi_softc *sc, struct cam_sim *sim,
9482f001371SPeter Grehan     union ccb *ccb)
9492f001371SPeter Grehan {
9502f001371SPeter Grehan 	device_t dev;
9512f001371SPeter Grehan 	struct ccb_pathinq *cpi;
9522f001371SPeter Grehan 
9532f001371SPeter Grehan 	dev = sc->vtscsi_dev;
9542f001371SPeter Grehan 	cpi = &ccb->cpi;
9552f001371SPeter Grehan 
9562f001371SPeter Grehan 	vtscsi_dprintf(sc, VTSCSI_TRACE, "sim=%p ccb=%p\n", sim, ccb);
9572f001371SPeter Grehan 
9582f001371SPeter Grehan 	cpi->version_num = 1;
9592f001371SPeter Grehan 	cpi->hba_inquiry = PI_TAG_ABLE;
9602f001371SPeter Grehan 	cpi->target_sprt = 0;
96122525db5SBryan Venteicher 	cpi->hba_misc = PIM_SEQSCAN | PIM_UNMAPPED;
9622f001371SPeter Grehan 	if (vtscsi_bus_reset_disable != 0)
9632f001371SPeter Grehan 		cpi->hba_misc |= PIM_NOBUSRESET;
9642f001371SPeter Grehan 	cpi->hba_eng_cnt = 0;
9652f001371SPeter Grehan 
9662f001371SPeter Grehan 	cpi->max_target = sc->vtscsi_max_target;
9672f001371SPeter Grehan 	cpi->max_lun = sc->vtscsi_max_lun;
968adbf6af7SAndriy Gapon 	cpi->initiator_id = cpi->max_target + 1;
9692f001371SPeter Grehan 
9704195c7deSAlan Somers 	strlcpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN);
9714195c7deSAlan Somers 	strlcpy(cpi->hba_vid, "VirtIO", HBA_IDLEN);
9724195c7deSAlan Somers 	strlcpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN);
9732f001371SPeter Grehan 
9742f001371SPeter Grehan 	cpi->unit_number = cam_sim_unit(sim);
9752f001371SPeter Grehan 	cpi->bus_id = cam_sim_bus(sim);
9762f001371SPeter Grehan 
9772f001371SPeter Grehan 	cpi->base_transfer_speed = 300000;
9782f001371SPeter Grehan 
9792f001371SPeter Grehan 	cpi->protocol = PROTO_SCSI;
9802f001371SPeter Grehan 	cpi->protocol_version = SCSI_REV_SPC3;
9812f001371SPeter Grehan 	cpi->transport = XPORT_SAS;
9822f001371SPeter Grehan 	cpi->transport_version = 0;
9832f001371SPeter Grehan 
9842f001371SPeter Grehan 	cpi->maxio = (sc->vtscsi_max_nsegs - VTSCSI_MIN_SEGMENTS - 1) *
9852f001371SPeter Grehan 	    PAGE_SIZE;
9862f001371SPeter Grehan 
9872f001371SPeter Grehan 	cpi->hba_vendor = virtio_get_vendor(dev);
9882f001371SPeter Grehan 	cpi->hba_device = virtio_get_device(dev);
9892f001371SPeter Grehan 	cpi->hba_subvendor = virtio_get_subvendor(dev);
9902f001371SPeter Grehan 	cpi->hba_subdevice = virtio_get_subdevice(dev);
9912f001371SPeter Grehan 
9922f001371SPeter Grehan 	ccb->ccb_h.status = CAM_REQ_CMP;
9932f001371SPeter Grehan 	xpt_done(ccb);
9942f001371SPeter Grehan }
9952f001371SPeter Grehan 
9962f001371SPeter Grehan static int
vtscsi_sg_append_scsi_buf(struct vtscsi_softc * sc,struct sglist * sg,struct ccb_scsiio * csio)9972f001371SPeter Grehan vtscsi_sg_append_scsi_buf(struct vtscsi_softc *sc, struct sglist *sg,
9982f001371SPeter Grehan     struct ccb_scsiio *csio)
9992f001371SPeter Grehan {
10002f001371SPeter Grehan 	struct ccb_hdr *ccbh;
10012f001371SPeter Grehan 	struct bus_dma_segment *dseg;
10022f001371SPeter Grehan 	int i, error;
10032f001371SPeter Grehan 
10042f001371SPeter Grehan 	ccbh = &csio->ccb_h;
10052f001371SPeter Grehan 	error = 0;
10062f001371SPeter Grehan 
1007dd0b4fb6SKonstantin Belousov 	switch ((ccbh->flags & CAM_DATA_MASK)) {
1008dd0b4fb6SKonstantin Belousov 	case CAM_DATA_VADDR:
1009dd0b4fb6SKonstantin Belousov 		error = sglist_append(sg, csio->data_ptr, csio->dxfer_len);
1010dd0b4fb6SKonstantin Belousov 		break;
1011dd0b4fb6SKonstantin Belousov 	case CAM_DATA_PADDR:
10122f001371SPeter Grehan 		error = sglist_append_phys(sg,
1013dd0b4fb6SKonstantin Belousov 		    (vm_paddr_t)(vm_offset_t) csio->data_ptr, csio->dxfer_len);
1014dd0b4fb6SKonstantin Belousov 		break;
1015dd0b4fb6SKonstantin Belousov 	case CAM_DATA_SG:
10162f001371SPeter Grehan 		for (i = 0; i < csio->sglist_cnt && error == 0; i++) {
10172f001371SPeter Grehan 			dseg = &((struct bus_dma_segment *)csio->data_ptr)[i];
10182f001371SPeter Grehan 			error = sglist_append(sg,
1019dd0b4fb6SKonstantin Belousov 			    (void *)(vm_offset_t) dseg->ds_addr, dseg->ds_len);
1020dd0b4fb6SKonstantin Belousov 		}
1021dd0b4fb6SKonstantin Belousov 		break;
1022dd0b4fb6SKonstantin Belousov 	case CAM_DATA_SG_PADDR:
1023dd0b4fb6SKonstantin Belousov 		for (i = 0; i < csio->sglist_cnt && error == 0; i++) {
1024dd0b4fb6SKonstantin Belousov 			dseg = &((struct bus_dma_segment *)csio->data_ptr)[i];
10252f001371SPeter Grehan 			error = sglist_append_phys(sg,
10262f001371SPeter Grehan 			    (vm_paddr_t) dseg->ds_addr, dseg->ds_len);
10272f001371SPeter Grehan 		}
1028dd0b4fb6SKonstantin Belousov 		break;
102922525db5SBryan Venteicher 	case CAM_DATA_BIO:
103022525db5SBryan Venteicher 		error = sglist_append_bio(sg, (struct bio *) csio->data_ptr);
103122525db5SBryan Venteicher 		break;
1032dd0b4fb6SKonstantin Belousov 	default:
1033dd0b4fb6SKonstantin Belousov 		error = EINVAL;
1034dd0b4fb6SKonstantin Belousov 		break;
10352f001371SPeter Grehan 	}
10362f001371SPeter Grehan 
10372f001371SPeter Grehan 	return (error);
10382f001371SPeter Grehan }
10392f001371SPeter Grehan 
10402f001371SPeter Grehan static int
vtscsi_fill_scsi_cmd_sglist(struct vtscsi_softc * sc,struct vtscsi_request * req,int * readable,int * writable)10412f001371SPeter Grehan vtscsi_fill_scsi_cmd_sglist(struct vtscsi_softc *sc, struct vtscsi_request *req,
10422f001371SPeter Grehan     int *readable, int *writable)
10432f001371SPeter Grehan {
10442f001371SPeter Grehan 	struct sglist *sg;
10452f001371SPeter Grehan 	struct ccb_hdr *ccbh;
10462f001371SPeter Grehan 	struct ccb_scsiio *csio;
10472f001371SPeter Grehan 	struct virtio_scsi_cmd_req *cmd_req;
10482f001371SPeter Grehan 	struct virtio_scsi_cmd_resp *cmd_resp;
10492f001371SPeter Grehan 	int error;
10502f001371SPeter Grehan 
10512f001371SPeter Grehan 	sg = sc->vtscsi_sglist;
10522f001371SPeter Grehan 	csio = &req->vsr_ccb->csio;
10532f001371SPeter Grehan 	ccbh = &csio->ccb_h;
10542f001371SPeter Grehan 	cmd_req = &req->vsr_cmd_req;
10552f001371SPeter Grehan 	cmd_resp = &req->vsr_cmd_resp;
10562f001371SPeter Grehan 
10572f001371SPeter Grehan 	sglist_reset(sg);
10582f001371SPeter Grehan 
10592f001371SPeter Grehan 	sglist_append(sg, cmd_req, sizeof(struct virtio_scsi_cmd_req));
10602f001371SPeter Grehan 	if ((ccbh->flags & CAM_DIR_MASK) == CAM_DIR_OUT) {
10612f001371SPeter Grehan 		error = vtscsi_sg_append_scsi_buf(sc, sg, csio);
10622f001371SPeter Grehan 		/* At least one segment must be left for the response. */
10632f001371SPeter Grehan 		if (error || sg->sg_nseg == sg->sg_maxseg)
10642f001371SPeter Grehan 			goto fail;
10652f001371SPeter Grehan 	}
10662f001371SPeter Grehan 
10672f001371SPeter Grehan 	*readable = sg->sg_nseg;
10682f001371SPeter Grehan 
10692f001371SPeter Grehan 	sglist_append(sg, cmd_resp, sizeof(struct virtio_scsi_cmd_resp));
10702f001371SPeter Grehan 	if ((ccbh->flags & CAM_DIR_MASK) == CAM_DIR_IN) {
10712f001371SPeter Grehan 		error = vtscsi_sg_append_scsi_buf(sc, sg, csio);
10722f001371SPeter Grehan 		if (error)
10732f001371SPeter Grehan 			goto fail;
10742f001371SPeter Grehan 	}
10752f001371SPeter Grehan 
10762f001371SPeter Grehan 	*writable = sg->sg_nseg - *readable;
10772f001371SPeter Grehan 
10782f001371SPeter Grehan 	vtscsi_dprintf(sc, VTSCSI_TRACE, "req=%p ccb=%p readable=%d "
10792f001371SPeter Grehan 	    "writable=%d\n", req, ccbh, *readable, *writable);
10802f001371SPeter Grehan 
10812f001371SPeter Grehan 	return (0);
10822f001371SPeter Grehan 
10832f001371SPeter Grehan fail:
10842f001371SPeter Grehan 	/*
10852f001371SPeter Grehan 	 * This should never happen unless maxio was incorrectly set.
10862f001371SPeter Grehan 	 */
10872f001371SPeter Grehan 	vtscsi_set_ccb_status(ccbh, CAM_REQ_TOO_BIG, 0);
10882f001371SPeter Grehan 
10892f001371SPeter Grehan 	vtscsi_dprintf(sc, VTSCSI_ERROR, "error=%d req=%p ccb=%p "
10902f001371SPeter Grehan 	    "nseg=%d maxseg=%d\n",
10912f001371SPeter Grehan 	    error, req, ccbh, sg->sg_nseg, sg->sg_maxseg);
10922f001371SPeter Grehan 
10932f001371SPeter Grehan 	return (EFBIG);
10942f001371SPeter Grehan }
10952f001371SPeter Grehan 
10962f001371SPeter Grehan static int
vtscsi_execute_scsi_cmd(struct vtscsi_softc * sc,struct vtscsi_request * req)10972f001371SPeter Grehan vtscsi_execute_scsi_cmd(struct vtscsi_softc *sc, struct vtscsi_request *req)
10982f001371SPeter Grehan {
10992f001371SPeter Grehan 	struct sglist *sg;
11002f001371SPeter Grehan 	struct virtqueue *vq;
11012f001371SPeter Grehan 	struct ccb_scsiio *csio;
11022f001371SPeter Grehan 	struct ccb_hdr *ccbh;
11032f001371SPeter Grehan 	struct virtio_scsi_cmd_req *cmd_req;
11042f001371SPeter Grehan 	struct virtio_scsi_cmd_resp *cmd_resp;
11052f001371SPeter Grehan 	int readable, writable, error;
11062f001371SPeter Grehan 
11072f001371SPeter Grehan 	sg = sc->vtscsi_sglist;
11082f001371SPeter Grehan 	vq = sc->vtscsi_request_vq;
11092f001371SPeter Grehan 	csio = &req->vsr_ccb->csio;
11102f001371SPeter Grehan 	ccbh = &csio->ccb_h;
11112f001371SPeter Grehan 	cmd_req = &req->vsr_cmd_req;
11122f001371SPeter Grehan 	cmd_resp = &req->vsr_cmd_resp;
11132f001371SPeter Grehan 
111415be4953SBryan Venteicher 	vtscsi_init_scsi_cmd_req(sc, csio, cmd_req);
11152f001371SPeter Grehan 
11162f001371SPeter Grehan 	error = vtscsi_fill_scsi_cmd_sglist(sc, req, &readable, &writable);
11172f001371SPeter Grehan 	if (error)
11182f001371SPeter Grehan 		return (error);
11192f001371SPeter Grehan 
11202f001371SPeter Grehan 	req->vsr_complete = vtscsi_complete_scsi_cmd;
11212f001371SPeter Grehan 	cmd_resp->response = -1;
11222f001371SPeter Grehan 
11232f001371SPeter Grehan 	error = virtqueue_enqueue(vq, req, sg, readable, writable);
11242f001371SPeter Grehan 	if (error) {
11252f001371SPeter Grehan 		vtscsi_dprintf(sc, VTSCSI_ERROR,
11262f001371SPeter Grehan 		    "enqueue error=%d req=%p ccb=%p\n", error, req, ccbh);
11272f001371SPeter Grehan 
11282f001371SPeter Grehan 		ccbh->status = CAM_REQUEUE_REQ;
11292f001371SPeter Grehan 		vtscsi_freeze_simq(sc, VTSCSI_REQUEST_VQ);
11302f001371SPeter Grehan 		return (error);
11312f001371SPeter Grehan 	}
11322f001371SPeter Grehan 
11332f001371SPeter Grehan 	ccbh->status |= CAM_SIM_QUEUED;
11342f001371SPeter Grehan 	ccbh->ccbh_vtscsi_req = req;
11352f001371SPeter Grehan 
11362f001371SPeter Grehan 	virtqueue_notify(vq);
11372f001371SPeter Grehan 
11382f001371SPeter Grehan 	if (ccbh->timeout != CAM_TIME_INFINITY) {
11392f001371SPeter Grehan 		req->vsr_flags |= VTSCSI_REQ_FLAG_TIMEOUT_SET;
114085c9dd9dSSteven Hartland 		callout_reset_sbt(&req->vsr_callout, SBT_1MS * ccbh->timeout,
114185c9dd9dSSteven Hartland 		    0, vtscsi_timedout_scsi_cmd, req, 0);
11422f001371SPeter Grehan 	}
11432f001371SPeter Grehan 
11442f001371SPeter Grehan 	vtscsi_dprintf_req(req, VTSCSI_TRACE, "enqueued req=%p ccb=%p\n",
11452f001371SPeter Grehan 	    req, ccbh);
11462f001371SPeter Grehan 
11472f001371SPeter Grehan 	return (0);
11482f001371SPeter Grehan }
11492f001371SPeter Grehan 
11502f001371SPeter Grehan static int
vtscsi_start_scsi_cmd(struct vtscsi_softc * sc,union ccb * ccb)11512f001371SPeter Grehan vtscsi_start_scsi_cmd(struct vtscsi_softc *sc, union ccb *ccb)
11522f001371SPeter Grehan {
11532f001371SPeter Grehan 	struct vtscsi_request *req;
11542f001371SPeter Grehan 	int error;
11552f001371SPeter Grehan 
11562f001371SPeter Grehan 	req = vtscsi_dequeue_request(sc);
11572f001371SPeter Grehan 	if (req == NULL) {
11582f001371SPeter Grehan 		ccb->ccb_h.status = CAM_REQUEUE_REQ;
11592f001371SPeter Grehan 		vtscsi_freeze_simq(sc, VTSCSI_REQUEST);
11602f001371SPeter Grehan 		return (ENOBUFS);
11612f001371SPeter Grehan 	}
11622f001371SPeter Grehan 
11632f001371SPeter Grehan 	req->vsr_ccb = ccb;
11642f001371SPeter Grehan 
11652f001371SPeter Grehan 	error = vtscsi_execute_scsi_cmd(sc, req);
11662f001371SPeter Grehan 	if (error)
11672f001371SPeter Grehan 		vtscsi_enqueue_request(sc, req);
11682f001371SPeter Grehan 
11692f001371SPeter Grehan 	return (error);
11702f001371SPeter Grehan }
11712f001371SPeter Grehan 
11722f001371SPeter Grehan static void
vtscsi_complete_abort_timedout_scsi_cmd(struct vtscsi_softc * sc,struct vtscsi_request * req)11732f001371SPeter Grehan vtscsi_complete_abort_timedout_scsi_cmd(struct vtscsi_softc *sc,
11742f001371SPeter Grehan     struct vtscsi_request *req)
11752f001371SPeter Grehan {
11762f001371SPeter Grehan 	struct virtio_scsi_ctrl_tmf_resp *tmf_resp;
11772f001371SPeter Grehan 	struct vtscsi_request *to_req;
11782f001371SPeter Grehan 	uint8_t response;
11792f001371SPeter Grehan 
11802f001371SPeter Grehan 	tmf_resp = &req->vsr_tmf_resp;
11812f001371SPeter Grehan 	response = tmf_resp->response;
11822f001371SPeter Grehan 	to_req = req->vsr_timedout_req;
11832f001371SPeter Grehan 
11842f001371SPeter Grehan 	vtscsi_dprintf(sc, VTSCSI_TRACE, "req=%p to_req=%p response=%d\n",
11852f001371SPeter Grehan 	    req, to_req, response);
11862f001371SPeter Grehan 
11872f001371SPeter Grehan 	vtscsi_enqueue_request(sc, req);
11882f001371SPeter Grehan 
11892f001371SPeter Grehan 	/*
11902f001371SPeter Grehan 	 * The timedout request could have completed between when the
11912f001371SPeter Grehan 	 * abort task was sent and when the host processed it.
11922f001371SPeter Grehan 	 */
11932f001371SPeter Grehan 	if (to_req->vsr_state != VTSCSI_REQ_STATE_TIMEDOUT)
11942f001371SPeter Grehan 		return;
11952f001371SPeter Grehan 
11962f001371SPeter Grehan 	/* The timedout request was successfully aborted. */
11972f001371SPeter Grehan 	if (response == VIRTIO_SCSI_S_FUNCTION_COMPLETE)
11982f001371SPeter Grehan 		return;
11992f001371SPeter Grehan 
12002f001371SPeter Grehan 	/* Don't bother if the device is going away. */
12012f001371SPeter Grehan 	if (sc->vtscsi_flags & VTSCSI_FLAG_DETACH)
12022f001371SPeter Grehan 		return;
12032f001371SPeter Grehan 
12042f001371SPeter Grehan 	/* The timedout request will be aborted by the reset. */
12052f001371SPeter Grehan 	if (sc->vtscsi_flags & VTSCSI_FLAG_RESET)
12062f001371SPeter Grehan 		return;
12072f001371SPeter Grehan 
12082f001371SPeter Grehan 	vtscsi_reset_bus(sc);
12092f001371SPeter Grehan }
12102f001371SPeter Grehan 
12112f001371SPeter Grehan static int
vtscsi_abort_timedout_scsi_cmd(struct vtscsi_softc * sc,struct vtscsi_request * to_req)12122f001371SPeter Grehan vtscsi_abort_timedout_scsi_cmd(struct vtscsi_softc *sc,
12132f001371SPeter Grehan     struct vtscsi_request *to_req)
12142f001371SPeter Grehan {
12152f001371SPeter Grehan 	struct sglist *sg;
12162f001371SPeter Grehan 	struct ccb_hdr *to_ccbh;
12172f001371SPeter Grehan 	struct vtscsi_request *req;
12182f001371SPeter Grehan 	struct virtio_scsi_ctrl_tmf_req *tmf_req;
12192f001371SPeter Grehan 	struct virtio_scsi_ctrl_tmf_resp *tmf_resp;
12202f001371SPeter Grehan 	int error;
12212f001371SPeter Grehan 
12222f001371SPeter Grehan 	sg = sc->vtscsi_sglist;
12232f001371SPeter Grehan 	to_ccbh = &to_req->vsr_ccb->ccb_h;
12242f001371SPeter Grehan 
12252f001371SPeter Grehan 	req = vtscsi_dequeue_request(sc);
12262f001371SPeter Grehan 	if (req == NULL) {
12272f001371SPeter Grehan 		error = ENOBUFS;
12282f001371SPeter Grehan 		goto fail;
12292f001371SPeter Grehan 	}
12302f001371SPeter Grehan 
12312f001371SPeter Grehan 	tmf_req = &req->vsr_tmf_req;
12322f001371SPeter Grehan 	tmf_resp = &req->vsr_tmf_resp;
12332f001371SPeter Grehan 
123415be4953SBryan Venteicher 	vtscsi_init_ctrl_tmf_req(sc, to_ccbh, VIRTIO_SCSI_T_TMF_ABORT_TASK,
12352f001371SPeter Grehan 	    (uintptr_t) to_ccbh, tmf_req);
12362f001371SPeter Grehan 
12372f001371SPeter Grehan 	sglist_reset(sg);
12382f001371SPeter Grehan 	sglist_append(sg, tmf_req, sizeof(struct virtio_scsi_ctrl_tmf_req));
12392f001371SPeter Grehan 	sglist_append(sg, tmf_resp, sizeof(struct virtio_scsi_ctrl_tmf_resp));
12402f001371SPeter Grehan 
12412f001371SPeter Grehan 	req->vsr_timedout_req = to_req;
12422f001371SPeter Grehan 	req->vsr_complete = vtscsi_complete_abort_timedout_scsi_cmd;
12432f001371SPeter Grehan 	tmf_resp->response = -1;
12442f001371SPeter Grehan 
12452f001371SPeter Grehan 	error = vtscsi_execute_ctrl_req(sc, req, sg, 1, 1,
12462f001371SPeter Grehan 	    VTSCSI_EXECUTE_ASYNC);
12472f001371SPeter Grehan 	if (error == 0)
12482f001371SPeter Grehan 		return (0);
12492f001371SPeter Grehan 
12502f001371SPeter Grehan 	vtscsi_enqueue_request(sc, req);
12512f001371SPeter Grehan 
12522f001371SPeter Grehan fail:
12532f001371SPeter Grehan 	vtscsi_dprintf(sc, VTSCSI_ERROR, "error=%d req=%p "
12542f001371SPeter Grehan 	    "timedout req=%p ccb=%p\n", error, req, to_req, to_ccbh);
12552f001371SPeter Grehan 
12562f001371SPeter Grehan 	return (error);
12572f001371SPeter Grehan }
12582f001371SPeter Grehan 
12592f001371SPeter Grehan static void
vtscsi_timedout_scsi_cmd(void * xreq)12602f001371SPeter Grehan vtscsi_timedout_scsi_cmd(void *xreq)
12612f001371SPeter Grehan {
12622f001371SPeter Grehan 	struct vtscsi_softc *sc;
12632f001371SPeter Grehan 	struct vtscsi_request *to_req;
12642f001371SPeter Grehan 
12652f001371SPeter Grehan 	to_req = xreq;
12662f001371SPeter Grehan 	sc = to_req->vsr_softc;
12672f001371SPeter Grehan 
12682f001371SPeter Grehan 	vtscsi_dprintf(sc, VTSCSI_INFO, "timedout req=%p ccb=%p state=%#x\n",
12692f001371SPeter Grehan 	    to_req, to_req->vsr_ccb, to_req->vsr_state);
12702f001371SPeter Grehan 
12712f001371SPeter Grehan 	/* Don't bother if the device is going away. */
12722f001371SPeter Grehan 	if (sc->vtscsi_flags & VTSCSI_FLAG_DETACH)
12732f001371SPeter Grehan 		return;
12742f001371SPeter Grehan 
12752f001371SPeter Grehan 	/*
12762f001371SPeter Grehan 	 * Bail if the request is not in use. We likely raced when
12772f001371SPeter Grehan 	 * stopping the callout handler or it has already been aborted.
12782f001371SPeter Grehan 	 */
12792f001371SPeter Grehan 	if (to_req->vsr_state != VTSCSI_REQ_STATE_INUSE ||
12802f001371SPeter Grehan 	    (to_req->vsr_flags & VTSCSI_REQ_FLAG_TIMEOUT_SET) == 0)
12812f001371SPeter Grehan 		return;
12822f001371SPeter Grehan 
12832f001371SPeter Grehan 	/*
12842f001371SPeter Grehan 	 * Complete the request queue in case the timedout request is
12852f001371SPeter Grehan 	 * actually just pending.
12862f001371SPeter Grehan 	 */
12872f001371SPeter Grehan 	vtscsi_complete_vq(sc, sc->vtscsi_request_vq);
12882f001371SPeter Grehan 	if (to_req->vsr_state == VTSCSI_REQ_STATE_FREE)
12892f001371SPeter Grehan 		return;
12902f001371SPeter Grehan 
12912f001371SPeter Grehan 	sc->vtscsi_stats.scsi_cmd_timeouts++;
12922f001371SPeter Grehan 	to_req->vsr_state = VTSCSI_REQ_STATE_TIMEDOUT;
12932f001371SPeter Grehan 
12942f001371SPeter Grehan 	if (vtscsi_abort_timedout_scsi_cmd(sc, to_req) == 0)
12952f001371SPeter Grehan 		return;
12962f001371SPeter Grehan 
12972f001371SPeter Grehan 	vtscsi_dprintf(sc, VTSCSI_ERROR, "resetting bus\n");
12982f001371SPeter Grehan 	vtscsi_reset_bus(sc);
12992f001371SPeter Grehan }
13002f001371SPeter Grehan 
13012f001371SPeter Grehan static cam_status
vtscsi_scsi_cmd_cam_status(struct virtio_scsi_cmd_resp * cmd_resp)13022f001371SPeter Grehan vtscsi_scsi_cmd_cam_status(struct virtio_scsi_cmd_resp *cmd_resp)
13032f001371SPeter Grehan {
13042f001371SPeter Grehan 	cam_status status;
13052f001371SPeter Grehan 
13062f001371SPeter Grehan 	switch (cmd_resp->response) {
13072f001371SPeter Grehan 	case VIRTIO_SCSI_S_OK:
13082f001371SPeter Grehan 		status = CAM_REQ_CMP;
13092f001371SPeter Grehan 		break;
13102f001371SPeter Grehan 	case VIRTIO_SCSI_S_OVERRUN:
13112f001371SPeter Grehan 		status = CAM_DATA_RUN_ERR;
13122f001371SPeter Grehan 		break;
13132f001371SPeter Grehan 	case VIRTIO_SCSI_S_ABORTED:
13142f001371SPeter Grehan 		status = CAM_REQ_ABORTED;
13152f001371SPeter Grehan 		break;
13162f001371SPeter Grehan 	case VIRTIO_SCSI_S_BAD_TARGET:
13174d5919ecSBryan Venteicher 		status = CAM_SEL_TIMEOUT;
13182f001371SPeter Grehan 		break;
13192f001371SPeter Grehan 	case VIRTIO_SCSI_S_RESET:
13202f001371SPeter Grehan 		status = CAM_SCSI_BUS_RESET;
13212f001371SPeter Grehan 		break;
13222f001371SPeter Grehan 	case VIRTIO_SCSI_S_BUSY:
13232f001371SPeter Grehan 		status = CAM_SCSI_BUSY;
13242f001371SPeter Grehan 		break;
13252f001371SPeter Grehan 	case VIRTIO_SCSI_S_TRANSPORT_FAILURE:
13262f001371SPeter Grehan 	case VIRTIO_SCSI_S_TARGET_FAILURE:
13272f001371SPeter Grehan 	case VIRTIO_SCSI_S_NEXUS_FAILURE:
13282f001371SPeter Grehan 		status = CAM_SCSI_IT_NEXUS_LOST;
13292f001371SPeter Grehan 		break;
13302f001371SPeter Grehan 	default: /* VIRTIO_SCSI_S_FAILURE */
13312f001371SPeter Grehan 		status = CAM_REQ_CMP_ERR;
13322f001371SPeter Grehan 		break;
13332f001371SPeter Grehan 	}
13342f001371SPeter Grehan 
13352f001371SPeter Grehan 	return (status);
13362f001371SPeter Grehan }
13372f001371SPeter Grehan 
13382f001371SPeter Grehan static cam_status
vtscsi_complete_scsi_cmd_response(struct vtscsi_softc * sc,struct ccb_scsiio * csio,struct virtio_scsi_cmd_resp * cmd_resp)13392f001371SPeter Grehan vtscsi_complete_scsi_cmd_response(struct vtscsi_softc *sc,
13402f001371SPeter Grehan     struct ccb_scsiio *csio, struct virtio_scsi_cmd_resp *cmd_resp)
13412f001371SPeter Grehan {
134215be4953SBryan Venteicher 	uint32_t resp_sense_length;
13432f001371SPeter Grehan 	cam_status status;
13442f001371SPeter Grehan 
13452f001371SPeter Grehan 	csio->scsi_status = cmd_resp->status;
134615be4953SBryan Venteicher 	csio->resid = vtscsi_htog32(sc, cmd_resp->resid);
13472f001371SPeter Grehan 
13482f001371SPeter Grehan 	if (csio->scsi_status == SCSI_STATUS_OK)
13492f001371SPeter Grehan 		status = CAM_REQ_CMP;
13502f001371SPeter Grehan 	else
13512f001371SPeter Grehan 		status = CAM_SCSI_STATUS_ERROR;
13522f001371SPeter Grehan 
135315be4953SBryan Venteicher 	resp_sense_length = vtscsi_htog32(sc, cmd_resp->sense_len);
135415be4953SBryan Venteicher 
135515be4953SBryan Venteicher 	if (resp_sense_length > 0) {
13562f001371SPeter Grehan 		status |= CAM_AUTOSNS_VALID;
13572f001371SPeter Grehan 
135815be4953SBryan Venteicher 		if (resp_sense_length < csio->sense_len)
135915be4953SBryan Venteicher 			csio->sense_resid = csio->sense_len - resp_sense_length;
13602f001371SPeter Grehan 		else
13612f001371SPeter Grehan 			csio->sense_resid = 0;
13622f001371SPeter Grehan 
1363e5355d33SAlexander Motin 		memcpy(&csio->sense_data, cmd_resp->sense,
13642f001371SPeter Grehan 		    csio->sense_len - csio->sense_resid);
13652f001371SPeter Grehan 	}
13662f001371SPeter Grehan 
13672f001371SPeter Grehan 	vtscsi_dprintf(sc, status == CAM_REQ_CMP ? VTSCSI_TRACE : VTSCSI_ERROR,
13682f001371SPeter Grehan 	    "ccb=%p scsi_status=%#x resid=%u sense_resid=%u\n",
13692f001371SPeter Grehan 	    csio, csio->scsi_status, csio->resid, csio->sense_resid);
13702f001371SPeter Grehan 
13712f001371SPeter Grehan 	return (status);
13722f001371SPeter Grehan }
13732f001371SPeter Grehan 
13742f001371SPeter Grehan static void
vtscsi_complete_scsi_cmd(struct vtscsi_softc * sc,struct vtscsi_request * req)13752f001371SPeter Grehan vtscsi_complete_scsi_cmd(struct vtscsi_softc *sc, struct vtscsi_request *req)
13762f001371SPeter Grehan {
13772f001371SPeter Grehan 	struct ccb_hdr *ccbh;
13782f001371SPeter Grehan 	struct ccb_scsiio *csio;
13792f001371SPeter Grehan 	struct virtio_scsi_cmd_resp *cmd_resp;
13802f001371SPeter Grehan 	cam_status status;
13812f001371SPeter Grehan 
13822f001371SPeter Grehan 	csio = &req->vsr_ccb->csio;
13832f001371SPeter Grehan 	ccbh = &csio->ccb_h;
13842f001371SPeter Grehan 	cmd_resp = &req->vsr_cmd_resp;
13852f001371SPeter Grehan 
13862f001371SPeter Grehan 	KASSERT(ccbh->ccbh_vtscsi_req == req,
13872f001371SPeter Grehan 	    ("ccb %p req mismatch %p/%p", ccbh, ccbh->ccbh_vtscsi_req, req));
13882f001371SPeter Grehan 
13892f001371SPeter Grehan 	if (req->vsr_flags & VTSCSI_REQ_FLAG_TIMEOUT_SET)
13902f001371SPeter Grehan 		callout_stop(&req->vsr_callout);
13912f001371SPeter Grehan 
13922f001371SPeter Grehan 	status = vtscsi_scsi_cmd_cam_status(cmd_resp);
13932f001371SPeter Grehan 	if (status == CAM_REQ_ABORTED) {
13942f001371SPeter Grehan 		if (req->vsr_state == VTSCSI_REQ_STATE_TIMEDOUT)
13952f001371SPeter Grehan 			status = CAM_CMD_TIMEOUT;
13962f001371SPeter Grehan 	} else if (status == CAM_REQ_CMP)
13972f001371SPeter Grehan 		status = vtscsi_complete_scsi_cmd_response(sc, csio, cmd_resp);
13982f001371SPeter Grehan 
13992f001371SPeter Grehan 	if ((status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
14002f001371SPeter Grehan 		status |= CAM_DEV_QFRZN;
14012f001371SPeter Grehan 		xpt_freeze_devq(ccbh->path, 1);
14022f001371SPeter Grehan 	}
14032f001371SPeter Grehan 
14042f001371SPeter Grehan 	if (vtscsi_thaw_simq(sc, VTSCSI_REQUEST | VTSCSI_REQUEST_VQ) != 0)
14052f001371SPeter Grehan 		status |= CAM_RELEASE_SIMQ;
14062f001371SPeter Grehan 
14072f001371SPeter Grehan 	vtscsi_dprintf(sc, VTSCSI_TRACE, "req=%p ccb=%p status=%#x\n",
14082f001371SPeter Grehan 	    req, ccbh, status);
14092f001371SPeter Grehan 
14102f001371SPeter Grehan 	ccbh->status = status;
14112f001371SPeter Grehan 	xpt_done(req->vsr_ccb);
14122f001371SPeter Grehan 	vtscsi_enqueue_request(sc, req);
14132f001371SPeter Grehan }
14142f001371SPeter Grehan 
14152f001371SPeter Grehan static void
vtscsi_poll_ctrl_req(struct vtscsi_softc * sc,struct vtscsi_request * req)14162f001371SPeter Grehan vtscsi_poll_ctrl_req(struct vtscsi_softc *sc, struct vtscsi_request *req)
14172f001371SPeter Grehan {
14182f001371SPeter Grehan 
14192f001371SPeter Grehan 	/* XXX We probably shouldn't poll forever. */
14202f001371SPeter Grehan 	req->vsr_flags |= VTSCSI_REQ_FLAG_POLLED;
14212f001371SPeter Grehan 	do
14222f001371SPeter Grehan 		vtscsi_complete_vq(sc, sc->vtscsi_control_vq);
14232f001371SPeter Grehan 	while ((req->vsr_flags & VTSCSI_REQ_FLAG_COMPLETE) == 0);
14242f001371SPeter Grehan 
14252f001371SPeter Grehan 	req->vsr_flags &= ~VTSCSI_REQ_FLAG_POLLED;
14262f001371SPeter Grehan }
14272f001371SPeter Grehan 
14282f001371SPeter Grehan static int
vtscsi_execute_ctrl_req(struct vtscsi_softc * sc,struct vtscsi_request * req,struct sglist * sg,int readable,int writable,int flag)14292f001371SPeter Grehan vtscsi_execute_ctrl_req(struct vtscsi_softc *sc, struct vtscsi_request *req,
14302f001371SPeter Grehan     struct sglist *sg, int readable, int writable, int flag)
14312f001371SPeter Grehan {
14322f001371SPeter Grehan 	struct virtqueue *vq;
14332f001371SPeter Grehan 	int error;
14342f001371SPeter Grehan 
14352f001371SPeter Grehan 	vq = sc->vtscsi_control_vq;
14362f001371SPeter Grehan 
14372f001371SPeter Grehan 	MPASS(flag == VTSCSI_EXECUTE_POLL || req->vsr_complete != NULL);
14382f001371SPeter Grehan 
14392f001371SPeter Grehan 	error = virtqueue_enqueue(vq, req, sg, readable, writable);
14402f001371SPeter Grehan 	if (error) {
14412f001371SPeter Grehan 		/*
14422f001371SPeter Grehan 		 * Return EAGAIN when the virtqueue does not have enough
14432f001371SPeter Grehan 		 * descriptors available.
14442f001371SPeter Grehan 		 */
14452f001371SPeter Grehan 		if (error == ENOSPC || error == EMSGSIZE)
14462f001371SPeter Grehan 			error = EAGAIN;
14472f001371SPeter Grehan 
14482f001371SPeter Grehan 		return (error);
14492f001371SPeter Grehan 	}
14502f001371SPeter Grehan 
14512f001371SPeter Grehan 	virtqueue_notify(vq);
14522f001371SPeter Grehan 	if (flag == VTSCSI_EXECUTE_POLL)
14532f001371SPeter Grehan 		vtscsi_poll_ctrl_req(sc, req);
14542f001371SPeter Grehan 
14552f001371SPeter Grehan 	return (0);
14562f001371SPeter Grehan }
14572f001371SPeter Grehan 
14582f001371SPeter Grehan static void
vtscsi_complete_abort_task_cmd(struct vtscsi_softc * sc,struct vtscsi_request * req)14592f001371SPeter Grehan vtscsi_complete_abort_task_cmd(struct vtscsi_softc *sc,
14602f001371SPeter Grehan     struct vtscsi_request *req)
14612f001371SPeter Grehan {
14622f001371SPeter Grehan 	union ccb *ccb;
14632f001371SPeter Grehan 	struct ccb_hdr *ccbh;
14642f001371SPeter Grehan 	struct virtio_scsi_ctrl_tmf_resp *tmf_resp;
14652f001371SPeter Grehan 
14662f001371SPeter Grehan 	ccb = req->vsr_ccb;
14672f001371SPeter Grehan 	ccbh = &ccb->ccb_h;
14682f001371SPeter Grehan 	tmf_resp = &req->vsr_tmf_resp;
14692f001371SPeter Grehan 
14702f001371SPeter Grehan 	switch (tmf_resp->response) {
14712f001371SPeter Grehan 	case VIRTIO_SCSI_S_FUNCTION_COMPLETE:
14722f001371SPeter Grehan 		ccbh->status = CAM_REQ_CMP;
14732f001371SPeter Grehan 		break;
14742f001371SPeter Grehan 	case VIRTIO_SCSI_S_FUNCTION_REJECTED:
14752f001371SPeter Grehan 		ccbh->status = CAM_UA_ABORT;
14762f001371SPeter Grehan 		break;
14772f001371SPeter Grehan 	default:
14782f001371SPeter Grehan 		ccbh->status = CAM_REQ_CMP_ERR;
14792f001371SPeter Grehan 		break;
14802f001371SPeter Grehan 	}
14812f001371SPeter Grehan 
14822f001371SPeter Grehan 	xpt_done(ccb);
14832f001371SPeter Grehan 	vtscsi_enqueue_request(sc, req);
14842f001371SPeter Grehan }
14852f001371SPeter Grehan 
14862f001371SPeter Grehan static int
vtscsi_execute_abort_task_cmd(struct vtscsi_softc * sc,struct vtscsi_request * req)14872f001371SPeter Grehan vtscsi_execute_abort_task_cmd(struct vtscsi_softc *sc,
14882f001371SPeter Grehan     struct vtscsi_request *req)
14892f001371SPeter Grehan {
14902f001371SPeter Grehan 	struct sglist *sg;
14912f001371SPeter Grehan 	struct ccb_abort *cab;
14922f001371SPeter Grehan 	struct ccb_hdr *ccbh;
14932f001371SPeter Grehan 	struct ccb_hdr *abort_ccbh;
14942f001371SPeter Grehan 	struct vtscsi_request *abort_req;
14952f001371SPeter Grehan 	struct virtio_scsi_ctrl_tmf_req *tmf_req;
14962f001371SPeter Grehan 	struct virtio_scsi_ctrl_tmf_resp *tmf_resp;
14972f001371SPeter Grehan 	int error;
14982f001371SPeter Grehan 
14992f001371SPeter Grehan 	sg = sc->vtscsi_sglist;
15002f001371SPeter Grehan 	cab = &req->vsr_ccb->cab;
15012f001371SPeter Grehan 	ccbh = &cab->ccb_h;
15022f001371SPeter Grehan 	tmf_req = &req->vsr_tmf_req;
15032f001371SPeter Grehan 	tmf_resp = &req->vsr_tmf_resp;
15042f001371SPeter Grehan 
15052f001371SPeter Grehan 	/* CCB header and request that's to be aborted. */
15062f001371SPeter Grehan 	abort_ccbh = &cab->abort_ccb->ccb_h;
15072f001371SPeter Grehan 	abort_req = abort_ccbh->ccbh_vtscsi_req;
15082f001371SPeter Grehan 
15092f001371SPeter Grehan 	if (abort_ccbh->func_code != XPT_SCSI_IO || abort_req == NULL) {
15102f001371SPeter Grehan 		error = EINVAL;
15112f001371SPeter Grehan 		goto fail;
15122f001371SPeter Grehan 	}
15132f001371SPeter Grehan 
15142f001371SPeter Grehan 	/* Only attempt to abort requests that could be in-flight. */
15152f001371SPeter Grehan 	if (abort_req->vsr_state != VTSCSI_REQ_STATE_INUSE) {
15162f001371SPeter Grehan 		error = EALREADY;
15172f001371SPeter Grehan 		goto fail;
15182f001371SPeter Grehan 	}
15192f001371SPeter Grehan 
15202f001371SPeter Grehan 	abort_req->vsr_state = VTSCSI_REQ_STATE_ABORTED;
15212f001371SPeter Grehan 	if (abort_req->vsr_flags & VTSCSI_REQ_FLAG_TIMEOUT_SET)
15222f001371SPeter Grehan 		callout_stop(&abort_req->vsr_callout);
15232f001371SPeter Grehan 
152415be4953SBryan Venteicher 	vtscsi_init_ctrl_tmf_req(sc, ccbh, VIRTIO_SCSI_T_TMF_ABORT_TASK,
15252f001371SPeter Grehan 	    (uintptr_t) abort_ccbh, tmf_req);
15262f001371SPeter Grehan 
15272f001371SPeter Grehan 	sglist_reset(sg);
15282f001371SPeter Grehan 	sglist_append(sg, tmf_req, sizeof(struct virtio_scsi_ctrl_tmf_req));
15292f001371SPeter Grehan 	sglist_append(sg, tmf_resp, sizeof(struct virtio_scsi_ctrl_tmf_resp));
15302f001371SPeter Grehan 
15312f001371SPeter Grehan 	req->vsr_complete = vtscsi_complete_abort_task_cmd;
15322f001371SPeter Grehan 	tmf_resp->response = -1;
15332f001371SPeter Grehan 
15342f001371SPeter Grehan 	error = vtscsi_execute_ctrl_req(sc, req, sg, 1, 1,
15352f001371SPeter Grehan 	    VTSCSI_EXECUTE_ASYNC);
15362f001371SPeter Grehan 
15372f001371SPeter Grehan fail:
15382f001371SPeter Grehan 	vtscsi_dprintf(sc, VTSCSI_TRACE, "error=%d req=%p abort_ccb=%p "
15392f001371SPeter Grehan 	    "abort_req=%p\n", error, req, abort_ccbh, abort_req);
15402f001371SPeter Grehan 
15412f001371SPeter Grehan 	return (error);
15422f001371SPeter Grehan }
15432f001371SPeter Grehan 
15442f001371SPeter Grehan static void
vtscsi_complete_reset_dev_cmd(struct vtscsi_softc * sc,struct vtscsi_request * req)15452f001371SPeter Grehan vtscsi_complete_reset_dev_cmd(struct vtscsi_softc *sc,
15462f001371SPeter Grehan     struct vtscsi_request *req)
15472f001371SPeter Grehan {
15482f001371SPeter Grehan 	union ccb *ccb;
15492f001371SPeter Grehan 	struct ccb_hdr *ccbh;
15502f001371SPeter Grehan 	struct virtio_scsi_ctrl_tmf_resp *tmf_resp;
15512f001371SPeter Grehan 
15522f001371SPeter Grehan 	ccb = req->vsr_ccb;
15532f001371SPeter Grehan 	ccbh = &ccb->ccb_h;
15542f001371SPeter Grehan 	tmf_resp = &req->vsr_tmf_resp;
15552f001371SPeter Grehan 
15562f001371SPeter Grehan 	vtscsi_dprintf(sc, VTSCSI_TRACE, "req=%p ccb=%p response=%d\n",
15572f001371SPeter Grehan 	    req, ccb, tmf_resp->response);
15582f001371SPeter Grehan 
15592f001371SPeter Grehan 	if (tmf_resp->response == VIRTIO_SCSI_S_FUNCTION_COMPLETE) {
15602f001371SPeter Grehan 		ccbh->status = CAM_REQ_CMP;
15612f001371SPeter Grehan 		vtscsi_announce(sc, AC_SENT_BDR, ccbh->target_id,
15622f001371SPeter Grehan 		    ccbh->target_lun);
15632f001371SPeter Grehan 	} else
15642f001371SPeter Grehan 		ccbh->status = CAM_REQ_CMP_ERR;
15652f001371SPeter Grehan 
15662f001371SPeter Grehan 	xpt_done(ccb);
15672f001371SPeter Grehan 	vtscsi_enqueue_request(sc, req);
15682f001371SPeter Grehan }
15692f001371SPeter Grehan 
15702f001371SPeter Grehan static int
vtscsi_execute_reset_dev_cmd(struct vtscsi_softc * sc,struct vtscsi_request * req)15712f001371SPeter Grehan vtscsi_execute_reset_dev_cmd(struct vtscsi_softc *sc,
15722f001371SPeter Grehan     struct vtscsi_request *req)
15732f001371SPeter Grehan {
15742f001371SPeter Grehan 	struct sglist *sg;
15752f001371SPeter Grehan 	struct ccb_resetdev *crd;
15762f001371SPeter Grehan 	struct ccb_hdr *ccbh;
15772f001371SPeter Grehan 	struct virtio_scsi_ctrl_tmf_req *tmf_req;
15782f001371SPeter Grehan 	struct virtio_scsi_ctrl_tmf_resp *tmf_resp;
15792f001371SPeter Grehan 	uint32_t subtype;
15802f001371SPeter Grehan 	int error;
15812f001371SPeter Grehan 
15822f001371SPeter Grehan 	sg = sc->vtscsi_sglist;
15832f001371SPeter Grehan 	crd = &req->vsr_ccb->crd;
15842f001371SPeter Grehan 	ccbh = &crd->ccb_h;
15852f001371SPeter Grehan 	tmf_req = &req->vsr_tmf_req;
15862f001371SPeter Grehan 	tmf_resp = &req->vsr_tmf_resp;
15872f001371SPeter Grehan 
15882f001371SPeter Grehan 	if (ccbh->target_lun == CAM_LUN_WILDCARD)
15892f001371SPeter Grehan 		subtype = VIRTIO_SCSI_T_TMF_I_T_NEXUS_RESET;
15902f001371SPeter Grehan 	else
15912f001371SPeter Grehan 		subtype = VIRTIO_SCSI_T_TMF_LOGICAL_UNIT_RESET;
15922f001371SPeter Grehan 
159315be4953SBryan Venteicher 	vtscsi_init_ctrl_tmf_req(sc, ccbh, subtype, 0, tmf_req);
15942f001371SPeter Grehan 
15952f001371SPeter Grehan 	sglist_reset(sg);
15962f001371SPeter Grehan 	sglist_append(sg, tmf_req, sizeof(struct virtio_scsi_ctrl_tmf_req));
15972f001371SPeter Grehan 	sglist_append(sg, tmf_resp, sizeof(struct virtio_scsi_ctrl_tmf_resp));
15982f001371SPeter Grehan 
15992f001371SPeter Grehan 	req->vsr_complete = vtscsi_complete_reset_dev_cmd;
16002f001371SPeter Grehan 	tmf_resp->response = -1;
16012f001371SPeter Grehan 
16022f001371SPeter Grehan 	error = vtscsi_execute_ctrl_req(sc, req, sg, 1, 1,
16032f001371SPeter Grehan 	    VTSCSI_EXECUTE_ASYNC);
16042f001371SPeter Grehan 
16052f001371SPeter Grehan 	vtscsi_dprintf(sc, VTSCSI_TRACE, "error=%d req=%p ccb=%p\n",
16062f001371SPeter Grehan 	    error, req, ccbh);
16072f001371SPeter Grehan 
16082f001371SPeter Grehan 	return (error);
16092f001371SPeter Grehan }
16102f001371SPeter Grehan 
16112f001371SPeter Grehan static void
vtscsi_get_request_lun(uint8_t lun[],target_id_t * target_id,lun_id_t * lun_id)16122f001371SPeter Grehan vtscsi_get_request_lun(uint8_t lun[], target_id_t *target_id, lun_id_t *lun_id)
16132f001371SPeter Grehan {
16142f001371SPeter Grehan 
16152f001371SPeter Grehan 	*target_id = lun[1];
16162f001371SPeter Grehan 	*lun_id = (lun[2] << 8) | lun[3];
16172f001371SPeter Grehan }
16182f001371SPeter Grehan 
16192f001371SPeter Grehan static void
vtscsi_set_request_lun(struct ccb_hdr * ccbh,uint8_t lun[])16202f001371SPeter Grehan vtscsi_set_request_lun(struct ccb_hdr *ccbh, uint8_t lun[])
16212f001371SPeter Grehan {
16222f001371SPeter Grehan 
16232f001371SPeter Grehan 	lun[0] = 1;
16242f001371SPeter Grehan 	lun[1] = ccbh->target_id;
16252f001371SPeter Grehan 	lun[2] = 0x40 | ((ccbh->target_lun >> 8) & 0x3F);
1626bf51187bSBryan Venteicher 	lun[3] = ccbh->target_lun & 0xFF;
16272f001371SPeter Grehan }
16282f001371SPeter Grehan 
16292f001371SPeter Grehan static void
vtscsi_init_scsi_cmd_req(struct vtscsi_softc * sc,struct ccb_scsiio * csio,struct virtio_scsi_cmd_req * cmd_req)163015be4953SBryan Venteicher vtscsi_init_scsi_cmd_req(struct vtscsi_softc *sc, struct ccb_scsiio *csio,
16312f001371SPeter Grehan     struct virtio_scsi_cmd_req *cmd_req)
16322f001371SPeter Grehan {
16332f001371SPeter Grehan 	uint8_t attr;
16342f001371SPeter Grehan 
16352f001371SPeter Grehan 	switch (csio->tag_action) {
16362f001371SPeter Grehan 	case MSG_HEAD_OF_Q_TAG:
16372f001371SPeter Grehan 		attr = VIRTIO_SCSI_S_HEAD;
16382f001371SPeter Grehan 		break;
16392f001371SPeter Grehan 	case MSG_ORDERED_Q_TAG:
16402f001371SPeter Grehan 		attr = VIRTIO_SCSI_S_ORDERED;
16412f001371SPeter Grehan 		break;
16422f001371SPeter Grehan 	case MSG_ACA_TASK:
16432f001371SPeter Grehan 		attr = VIRTIO_SCSI_S_ACA;
16442f001371SPeter Grehan 		break;
16452f001371SPeter Grehan 	default: /* MSG_SIMPLE_Q_TAG */
16462f001371SPeter Grehan 		attr = VIRTIO_SCSI_S_SIMPLE;
16472f001371SPeter Grehan 		break;
16482f001371SPeter Grehan 	}
16492f001371SPeter Grehan 
16502f001371SPeter Grehan 	vtscsi_set_request_lun(&csio->ccb_h, cmd_req->lun);
165115be4953SBryan Venteicher 	cmd_req->tag = vtscsi_gtoh64(sc, (uintptr_t) csio);
16522f001371SPeter Grehan 	cmd_req->task_attr = attr;
16532f001371SPeter Grehan 
16542f001371SPeter Grehan 	memcpy(cmd_req->cdb,
16552f001371SPeter Grehan 	    csio->ccb_h.flags & CAM_CDB_POINTER ?
16562f001371SPeter Grehan 	        csio->cdb_io.cdb_ptr : csio->cdb_io.cdb_bytes,
16572f001371SPeter Grehan 	    csio->cdb_len);
16582f001371SPeter Grehan }
16592f001371SPeter Grehan 
16602f001371SPeter Grehan static void
vtscsi_init_ctrl_tmf_req(struct vtscsi_softc * sc,struct ccb_hdr * ccbh,uint32_t subtype,uintptr_t tag,struct virtio_scsi_ctrl_tmf_req * tmf_req)166115be4953SBryan Venteicher vtscsi_init_ctrl_tmf_req(struct vtscsi_softc *sc, struct ccb_hdr *ccbh,
166215be4953SBryan Venteicher     uint32_t subtype, uintptr_t tag, struct virtio_scsi_ctrl_tmf_req *tmf_req)
16632f001371SPeter Grehan {
16642f001371SPeter Grehan 
16652f001371SPeter Grehan 	vtscsi_set_request_lun(ccbh, tmf_req->lun);
16662f001371SPeter Grehan 
166715be4953SBryan Venteicher 	tmf_req->type = vtscsi_gtoh32(sc, VIRTIO_SCSI_T_TMF);
166815be4953SBryan Venteicher 	tmf_req->subtype = vtscsi_gtoh32(sc, subtype);
166915be4953SBryan Venteicher 	tmf_req->tag = vtscsi_gtoh64(sc, tag);
16702f001371SPeter Grehan }
16712f001371SPeter Grehan 
16722f001371SPeter Grehan static void
vtscsi_freeze_simq(struct vtscsi_softc * sc,int reason)16732f001371SPeter Grehan vtscsi_freeze_simq(struct vtscsi_softc *sc, int reason)
16742f001371SPeter Grehan {
16752f001371SPeter Grehan 	int frozen;
16762f001371SPeter Grehan 
16772f001371SPeter Grehan 	frozen = sc->vtscsi_frozen;
16782f001371SPeter Grehan 
16792f001371SPeter Grehan 	if (reason & VTSCSI_REQUEST &&
16802f001371SPeter Grehan 	    (sc->vtscsi_frozen & VTSCSI_FROZEN_NO_REQUESTS) == 0)
16812f001371SPeter Grehan 		sc->vtscsi_frozen |= VTSCSI_FROZEN_NO_REQUESTS;
16822f001371SPeter Grehan 
16832f001371SPeter Grehan 	if (reason & VTSCSI_REQUEST_VQ &&
16842f001371SPeter Grehan 	    (sc->vtscsi_frozen & VTSCSI_FROZEN_REQUEST_VQ_FULL) == 0)
16852f001371SPeter Grehan 		sc->vtscsi_frozen |= VTSCSI_FROZEN_REQUEST_VQ_FULL;
16862f001371SPeter Grehan 
16872f001371SPeter Grehan 	/* Freeze the SIMQ if transitioned to frozen. */
16882f001371SPeter Grehan 	if (frozen == 0 && sc->vtscsi_frozen != 0) {
16892f001371SPeter Grehan 		vtscsi_dprintf(sc, VTSCSI_INFO, "SIMQ frozen\n");
16902f001371SPeter Grehan 		xpt_freeze_simq(sc->vtscsi_sim, 1);
16912f001371SPeter Grehan 	}
16922f001371SPeter Grehan }
16932f001371SPeter Grehan 
16942f001371SPeter Grehan static int
vtscsi_thaw_simq(struct vtscsi_softc * sc,int reason)16952f001371SPeter Grehan vtscsi_thaw_simq(struct vtscsi_softc *sc, int reason)
16962f001371SPeter Grehan {
16972f001371SPeter Grehan 	int thawed;
16982f001371SPeter Grehan 
16992f001371SPeter Grehan 	if (sc->vtscsi_frozen == 0 || reason == 0)
17002f001371SPeter Grehan 		return (0);
17012f001371SPeter Grehan 
17022f001371SPeter Grehan 	if (reason & VTSCSI_REQUEST &&
17032f001371SPeter Grehan 	    sc->vtscsi_frozen & VTSCSI_FROZEN_NO_REQUESTS)
17042f001371SPeter Grehan 		sc->vtscsi_frozen &= ~VTSCSI_FROZEN_NO_REQUESTS;
17052f001371SPeter Grehan 
17062f001371SPeter Grehan 	if (reason & VTSCSI_REQUEST_VQ &&
17072f001371SPeter Grehan 	    sc->vtscsi_frozen & VTSCSI_FROZEN_REQUEST_VQ_FULL)
17082f001371SPeter Grehan 		sc->vtscsi_frozen &= ~VTSCSI_FROZEN_REQUEST_VQ_FULL;
17092f001371SPeter Grehan 
17102f001371SPeter Grehan 	thawed = sc->vtscsi_frozen == 0;
17112f001371SPeter Grehan 	if (thawed != 0)
17122f001371SPeter Grehan 		vtscsi_dprintf(sc, VTSCSI_INFO, "SIMQ thawed\n");
17132f001371SPeter Grehan 
17142f001371SPeter Grehan 	return (thawed);
17152f001371SPeter Grehan }
17162f001371SPeter Grehan 
17172f001371SPeter Grehan static void
vtscsi_announce(struct vtscsi_softc * sc,uint32_t ac_code,target_id_t target_id,lun_id_t lun_id)17182f001371SPeter Grehan vtscsi_announce(struct vtscsi_softc *sc, uint32_t ac_code,
17192f001371SPeter Grehan     target_id_t target_id, lun_id_t lun_id)
17202f001371SPeter Grehan {
17212f001371SPeter Grehan 	struct cam_path *path;
17222f001371SPeter Grehan 
17232f001371SPeter Grehan 	/* Use the wildcard path from our softc for bus announcements. */
17242f001371SPeter Grehan 	if (target_id == CAM_TARGET_WILDCARD && lun_id == CAM_LUN_WILDCARD) {
17252f001371SPeter Grehan 		xpt_async(ac_code, sc->vtscsi_path, NULL);
17262f001371SPeter Grehan 		return;
17272f001371SPeter Grehan 	}
17282f001371SPeter Grehan 
17292f001371SPeter Grehan 	if (xpt_create_path(&path, NULL, cam_sim_path(sc->vtscsi_sim),
17302f001371SPeter Grehan 	    target_id, lun_id) != CAM_REQ_CMP) {
17312f001371SPeter Grehan 		vtscsi_dprintf(sc, VTSCSI_ERROR, "cannot create path\n");
17322f001371SPeter Grehan 		return;
17332f001371SPeter Grehan 	}
17342f001371SPeter Grehan 
17352f001371SPeter Grehan 	xpt_async(ac_code, path, NULL);
17362f001371SPeter Grehan 	xpt_free_path(path);
17372f001371SPeter Grehan }
17382f001371SPeter Grehan 
17392f001371SPeter Grehan static void
vtscsi_execute_rescan(struct vtscsi_softc * sc,target_id_t target_id,lun_id_t lun_id)17402f001371SPeter Grehan vtscsi_execute_rescan(struct vtscsi_softc *sc, target_id_t target_id,
17412f001371SPeter Grehan     lun_id_t lun_id)
17422f001371SPeter Grehan {
17432f001371SPeter Grehan 	union ccb *ccb;
17442f001371SPeter Grehan 	cam_status status;
17452f001371SPeter Grehan 
17462f001371SPeter Grehan 	ccb = xpt_alloc_ccb_nowait();
17472f001371SPeter Grehan 	if (ccb == NULL) {
17482f001371SPeter Grehan 		vtscsi_dprintf(sc, VTSCSI_ERROR, "cannot allocate CCB\n");
17492f001371SPeter Grehan 		return;
17502f001371SPeter Grehan 	}
17512f001371SPeter Grehan 
1752e5dfa058SAlexander Motin 	status = xpt_create_path(&ccb->ccb_h.path, NULL,
17532f001371SPeter Grehan 	    cam_sim_path(sc->vtscsi_sim), target_id, lun_id);
17542f001371SPeter Grehan 	if (status != CAM_REQ_CMP) {
17552f001371SPeter Grehan 		xpt_free_ccb(ccb);
17562f001371SPeter Grehan 		return;
17572f001371SPeter Grehan 	}
17582f001371SPeter Grehan 
17592f001371SPeter Grehan 	xpt_rescan(ccb);
17602f001371SPeter Grehan }
17612f001371SPeter Grehan 
17622f001371SPeter Grehan static void
vtscsi_execute_rescan_bus(struct vtscsi_softc * sc)17632f001371SPeter Grehan vtscsi_execute_rescan_bus(struct vtscsi_softc *sc)
17642f001371SPeter Grehan {
17652f001371SPeter Grehan 
17662f001371SPeter Grehan 	vtscsi_execute_rescan(sc, CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD);
17672f001371SPeter Grehan }
17682f001371SPeter Grehan 
17692f001371SPeter Grehan static void
vtscsi_transport_reset_event(struct vtscsi_softc * sc,struct virtio_scsi_event * event)17702f001371SPeter Grehan vtscsi_transport_reset_event(struct vtscsi_softc *sc,
17712f001371SPeter Grehan     struct virtio_scsi_event *event)
17722f001371SPeter Grehan {
17732f001371SPeter Grehan 	target_id_t target_id;
17742f001371SPeter Grehan 	lun_id_t lun_id;
17752f001371SPeter Grehan 
17762f001371SPeter Grehan 	vtscsi_get_request_lun(event->lun, &target_id, &lun_id);
17772f001371SPeter Grehan 
17782f001371SPeter Grehan 	switch (event->reason) {
17792f001371SPeter Grehan 	case VIRTIO_SCSI_EVT_RESET_RESCAN:
17802f001371SPeter Grehan 	case VIRTIO_SCSI_EVT_RESET_REMOVED:
17812f001371SPeter Grehan 		vtscsi_execute_rescan(sc, target_id, lun_id);
17822f001371SPeter Grehan 		break;
17832f001371SPeter Grehan 	default:
17842f001371SPeter Grehan 		device_printf(sc->vtscsi_dev,
17852f001371SPeter Grehan 		    "unhandled transport event reason: %d\n", event->reason);
17862f001371SPeter Grehan 		break;
17872f001371SPeter Grehan 	}
17882f001371SPeter Grehan }
17892f001371SPeter Grehan 
17902f001371SPeter Grehan static void
vtscsi_handle_event(struct vtscsi_softc * sc,struct virtio_scsi_event * event)17912f001371SPeter Grehan vtscsi_handle_event(struct vtscsi_softc *sc, struct virtio_scsi_event *event)
17922f001371SPeter Grehan {
1793b25ddb78SJohn Baldwin 	int error __diagused;
17942f001371SPeter Grehan 
17952f001371SPeter Grehan 	if ((event->event & VIRTIO_SCSI_T_EVENTS_MISSED) == 0) {
17962f001371SPeter Grehan 		switch (event->event) {
17972f001371SPeter Grehan 		case VIRTIO_SCSI_T_TRANSPORT_RESET:
17982f001371SPeter Grehan 			vtscsi_transport_reset_event(sc, event);
17992f001371SPeter Grehan 			break;
18002f001371SPeter Grehan 		default:
18012f001371SPeter Grehan 			device_printf(sc->vtscsi_dev,
18022f001371SPeter Grehan 			    "unhandled event: %d\n", event->event);
18032f001371SPeter Grehan 			break;
18042f001371SPeter Grehan 		}
18052f001371SPeter Grehan 	} else
18062f001371SPeter Grehan 		vtscsi_execute_rescan_bus(sc);
18072f001371SPeter Grehan 
18082f001371SPeter Grehan 	/*
18092f001371SPeter Grehan 	 * This should always be successful since the buffer
18102f001371SPeter Grehan 	 * was just dequeued.
18112f001371SPeter Grehan 	 */
18122f001371SPeter Grehan 	error = vtscsi_enqueue_event_buf(sc, event);
18132f001371SPeter Grehan 	KASSERT(error == 0,
18142f001371SPeter Grehan 	    ("cannot requeue event buffer: %d", error));
18152f001371SPeter Grehan }
18162f001371SPeter Grehan 
18172f001371SPeter Grehan static int
vtscsi_enqueue_event_buf(struct vtscsi_softc * sc,struct virtio_scsi_event * event)18182f001371SPeter Grehan vtscsi_enqueue_event_buf(struct vtscsi_softc *sc,
18192f001371SPeter Grehan     struct virtio_scsi_event *event)
18202f001371SPeter Grehan {
18212f001371SPeter Grehan 	struct sglist *sg;
18222f001371SPeter Grehan 	struct virtqueue *vq;
18232f001371SPeter Grehan 	int size, error;
18242f001371SPeter Grehan 
18252f001371SPeter Grehan 	sg = sc->vtscsi_sglist;
18262f001371SPeter Grehan 	vq = sc->vtscsi_event_vq;
18272f001371SPeter Grehan 	size = sc->vtscsi_event_buf_size;
18282f001371SPeter Grehan 
18292f001371SPeter Grehan 	bzero(event, size);
18302f001371SPeter Grehan 
18312f001371SPeter Grehan 	sglist_reset(sg);
18322f001371SPeter Grehan 	error = sglist_append(sg, event, size);
18332f001371SPeter Grehan 	if (error)
18342f001371SPeter Grehan 		return (error);
18352f001371SPeter Grehan 
18362f001371SPeter Grehan 	error = virtqueue_enqueue(vq, event, sg, 0, sg->sg_nseg);
18372f001371SPeter Grehan 	if (error)
18382f001371SPeter Grehan 		return (error);
18392f001371SPeter Grehan 
18402f001371SPeter Grehan 	virtqueue_notify(vq);
18412f001371SPeter Grehan 
18422f001371SPeter Grehan 	return (0);
18432f001371SPeter Grehan }
18442f001371SPeter Grehan 
18452f001371SPeter Grehan static int
vtscsi_init_event_vq(struct vtscsi_softc * sc)18462f001371SPeter Grehan vtscsi_init_event_vq(struct vtscsi_softc *sc)
18472f001371SPeter Grehan {
18482f001371SPeter Grehan 	struct virtio_scsi_event *event;
18492f001371SPeter Grehan 	int i, size, error;
18502f001371SPeter Grehan 
18512f001371SPeter Grehan 	/*
18522f001371SPeter Grehan 	 * The first release of QEMU with VirtIO SCSI support would crash
18532f001371SPeter Grehan 	 * when attempting to notify the event virtqueue. This was fixed
18542f001371SPeter Grehan 	 * when hotplug support was added.
18552f001371SPeter Grehan 	 */
18562f001371SPeter Grehan 	if (sc->vtscsi_flags & VTSCSI_FLAG_HOTPLUG)
18572f001371SPeter Grehan 		size = sc->vtscsi_event_buf_size;
18582f001371SPeter Grehan 	else
18592f001371SPeter Grehan 		size = 0;
18602f001371SPeter Grehan 
18612f001371SPeter Grehan 	if (size < sizeof(struct virtio_scsi_event))
18622f001371SPeter Grehan 		return (0);
18632f001371SPeter Grehan 
18642f001371SPeter Grehan 	for (i = 0; i < VTSCSI_NUM_EVENT_BUFS; i++) {
18652f001371SPeter Grehan 		event = &sc->vtscsi_event_bufs[i];
18662f001371SPeter Grehan 
18672f001371SPeter Grehan 		error = vtscsi_enqueue_event_buf(sc, event);
18682f001371SPeter Grehan 		if (error)
18692f001371SPeter Grehan 			break;
18702f001371SPeter Grehan 	}
18712f001371SPeter Grehan 
18722f001371SPeter Grehan 	/*
18732f001371SPeter Grehan 	 * Even just one buffer is enough. Missed events are
18742f001371SPeter Grehan 	 * denoted with the VIRTIO_SCSI_T_EVENTS_MISSED flag.
18752f001371SPeter Grehan 	 */
18762f001371SPeter Grehan 	if (i > 0)
18772f001371SPeter Grehan 		error = 0;
18782f001371SPeter Grehan 
18792f001371SPeter Grehan 	return (error);
18802f001371SPeter Grehan }
18812f001371SPeter Grehan 
18822f001371SPeter Grehan static void
vtscsi_reinit_event_vq(struct vtscsi_softc * sc)18832f001371SPeter Grehan vtscsi_reinit_event_vq(struct vtscsi_softc *sc)
18842f001371SPeter Grehan {
18852f001371SPeter Grehan 	struct virtio_scsi_event *event;
18862f001371SPeter Grehan 	int i, error;
18872f001371SPeter Grehan 
18882f001371SPeter Grehan 	if ((sc->vtscsi_flags & VTSCSI_FLAG_HOTPLUG) == 0 ||
18892f001371SPeter Grehan 	    sc->vtscsi_event_buf_size < sizeof(struct virtio_scsi_event))
18902f001371SPeter Grehan 		return;
18912f001371SPeter Grehan 
18922f001371SPeter Grehan 	for (i = 0; i < VTSCSI_NUM_EVENT_BUFS; i++) {
18932f001371SPeter Grehan 		event = &sc->vtscsi_event_bufs[i];
18942f001371SPeter Grehan 
18952f001371SPeter Grehan 		error = vtscsi_enqueue_event_buf(sc, event);
18962f001371SPeter Grehan 		if (error)
18972f001371SPeter Grehan 			break;
18982f001371SPeter Grehan 	}
18992f001371SPeter Grehan 
19002f001371SPeter Grehan 	KASSERT(i > 0, ("cannot reinit event vq: %d", error));
19012f001371SPeter Grehan }
19022f001371SPeter Grehan 
19032f001371SPeter Grehan static void
vtscsi_drain_event_vq(struct vtscsi_softc * sc)19042f001371SPeter Grehan vtscsi_drain_event_vq(struct vtscsi_softc *sc)
19052f001371SPeter Grehan {
19062f001371SPeter Grehan 	struct virtqueue *vq;
19072f001371SPeter Grehan 	int last;
19082f001371SPeter Grehan 
19092f001371SPeter Grehan 	vq = sc->vtscsi_event_vq;
19102f001371SPeter Grehan 	last = 0;
19112f001371SPeter Grehan 
19122f001371SPeter Grehan 	while (virtqueue_drain(vq, &last) != NULL)
19132f001371SPeter Grehan 		;
19142f001371SPeter Grehan 
19152f001371SPeter Grehan 	KASSERT(virtqueue_empty(vq), ("eventvq not empty"));
19162f001371SPeter Grehan }
19172f001371SPeter Grehan 
19182f001371SPeter Grehan static void
vtscsi_complete_vqs_locked(struct vtscsi_softc * sc)19192f001371SPeter Grehan vtscsi_complete_vqs_locked(struct vtscsi_softc *sc)
19202f001371SPeter Grehan {
19212f001371SPeter Grehan 
19222f001371SPeter Grehan 	VTSCSI_LOCK_OWNED(sc);
19232f001371SPeter Grehan 
19242f001371SPeter Grehan 	if (sc->vtscsi_request_vq != NULL)
19252f001371SPeter Grehan 		vtscsi_complete_vq(sc, sc->vtscsi_request_vq);
19262f001371SPeter Grehan 	if (sc->vtscsi_control_vq != NULL)
19272f001371SPeter Grehan 		vtscsi_complete_vq(sc, sc->vtscsi_control_vq);
19282f001371SPeter Grehan }
19292f001371SPeter Grehan 
19302f001371SPeter Grehan static void
vtscsi_complete_vqs(struct vtscsi_softc * sc)19312f001371SPeter Grehan vtscsi_complete_vqs(struct vtscsi_softc *sc)
19322f001371SPeter Grehan {
19332f001371SPeter Grehan 
19342f001371SPeter Grehan 	VTSCSI_LOCK(sc);
19352f001371SPeter Grehan 	vtscsi_complete_vqs_locked(sc);
19362f001371SPeter Grehan 	VTSCSI_UNLOCK(sc);
19372f001371SPeter Grehan }
19382f001371SPeter Grehan 
19392f001371SPeter Grehan static void
vtscsi_cancel_request(struct vtscsi_softc * sc,struct vtscsi_request * req)19402f001371SPeter Grehan vtscsi_cancel_request(struct vtscsi_softc *sc, struct vtscsi_request *req)
19412f001371SPeter Grehan {
19422f001371SPeter Grehan 	union ccb *ccb;
19432f001371SPeter Grehan 	int detach;
19442f001371SPeter Grehan 
19452f001371SPeter Grehan 	ccb = req->vsr_ccb;
19462f001371SPeter Grehan 
19472f001371SPeter Grehan 	vtscsi_dprintf(sc, VTSCSI_TRACE, "req=%p ccb=%p\n", req, ccb);
19482f001371SPeter Grehan 
19492f001371SPeter Grehan 	/*
19502f001371SPeter Grehan 	 * The callout must be drained when detaching since the request is
19512f001371SPeter Grehan 	 * about to be freed. The VTSCSI_MTX must not be held for this in
19522f001371SPeter Grehan 	 * case the callout is pending because there is a deadlock potential.
19532f001371SPeter Grehan 	 * Otherwise, the virtqueue is being drained because of a bus reset
19542f001371SPeter Grehan 	 * so we only need to attempt to stop the callouts.
19552f001371SPeter Grehan 	 */
19562f001371SPeter Grehan 	detach = (sc->vtscsi_flags & VTSCSI_FLAG_DETACH) != 0;
19572f001371SPeter Grehan 	if (detach != 0)
19582f001371SPeter Grehan 		VTSCSI_LOCK_NOTOWNED(sc);
19592f001371SPeter Grehan 	else
19602f001371SPeter Grehan 		VTSCSI_LOCK_OWNED(sc);
19612f001371SPeter Grehan 
19622f001371SPeter Grehan 	if (req->vsr_flags & VTSCSI_REQ_FLAG_TIMEOUT_SET) {
19632f001371SPeter Grehan 		if (detach != 0)
19642f001371SPeter Grehan 			callout_drain(&req->vsr_callout);
19652f001371SPeter Grehan 		else
19662f001371SPeter Grehan 			callout_stop(&req->vsr_callout);
19672f001371SPeter Grehan 	}
19682f001371SPeter Grehan 
19692f001371SPeter Grehan 	if (ccb != NULL) {
19702f001371SPeter Grehan 		if (detach != 0) {
19712f001371SPeter Grehan 			VTSCSI_LOCK(sc);
19722f001371SPeter Grehan 			ccb->ccb_h.status = CAM_NO_HBA;
19732f001371SPeter Grehan 		} else
19742f001371SPeter Grehan 			ccb->ccb_h.status = CAM_REQUEUE_REQ;
19752f001371SPeter Grehan 		xpt_done(ccb);
19762f001371SPeter Grehan 		if (detach != 0)
19772f001371SPeter Grehan 			VTSCSI_UNLOCK(sc);
19782f001371SPeter Grehan 	}
19792f001371SPeter Grehan 
19802f001371SPeter Grehan 	vtscsi_enqueue_request(sc, req);
19812f001371SPeter Grehan }
19822f001371SPeter Grehan 
19832f001371SPeter Grehan static void
vtscsi_drain_vq(struct vtscsi_softc * sc,struct virtqueue * vq)19842f001371SPeter Grehan vtscsi_drain_vq(struct vtscsi_softc *sc, struct virtqueue *vq)
19852f001371SPeter Grehan {
19862f001371SPeter Grehan 	struct vtscsi_request *req;
19872f001371SPeter Grehan 	int last;
19882f001371SPeter Grehan 
19892f001371SPeter Grehan 	last = 0;
19902f001371SPeter Grehan 
19912f001371SPeter Grehan 	vtscsi_dprintf(sc, VTSCSI_TRACE, "vq=%p\n", vq);
19922f001371SPeter Grehan 
19932f001371SPeter Grehan 	while ((req = virtqueue_drain(vq, &last)) != NULL)
19942f001371SPeter Grehan 		vtscsi_cancel_request(sc, req);
19952f001371SPeter Grehan 
19962f001371SPeter Grehan 	KASSERT(virtqueue_empty(vq), ("virtqueue not empty"));
19972f001371SPeter Grehan }
19982f001371SPeter Grehan 
19992f001371SPeter Grehan static void
vtscsi_drain_vqs(struct vtscsi_softc * sc)20002f001371SPeter Grehan vtscsi_drain_vqs(struct vtscsi_softc *sc)
20012f001371SPeter Grehan {
20022f001371SPeter Grehan 
20032f001371SPeter Grehan 	if (sc->vtscsi_control_vq != NULL)
20042f001371SPeter Grehan 		vtscsi_drain_vq(sc, sc->vtscsi_control_vq);
20052f001371SPeter Grehan 	if (sc->vtscsi_request_vq != NULL)
20062f001371SPeter Grehan 		vtscsi_drain_vq(sc, sc->vtscsi_request_vq);
20072f001371SPeter Grehan 	if (sc->vtscsi_event_vq != NULL)
20082f001371SPeter Grehan 		vtscsi_drain_event_vq(sc);
20092f001371SPeter Grehan }
20102f001371SPeter Grehan 
20112f001371SPeter Grehan static void
vtscsi_stop(struct vtscsi_softc * sc)20122f001371SPeter Grehan vtscsi_stop(struct vtscsi_softc *sc)
20132f001371SPeter Grehan {
20142f001371SPeter Grehan 
20152f001371SPeter Grehan 	vtscsi_disable_vqs_intr(sc);
20162f001371SPeter Grehan 	virtio_stop(sc->vtscsi_dev);
20172f001371SPeter Grehan }
20182f001371SPeter Grehan 
20192f001371SPeter Grehan static int
vtscsi_reset_bus(struct vtscsi_softc * sc)20202f001371SPeter Grehan vtscsi_reset_bus(struct vtscsi_softc *sc)
20212f001371SPeter Grehan {
20222f001371SPeter Grehan 	int error;
20232f001371SPeter Grehan 
20242f001371SPeter Grehan 	VTSCSI_LOCK_OWNED(sc);
20252f001371SPeter Grehan 
20262f001371SPeter Grehan 	if (vtscsi_bus_reset_disable != 0) {
20272f001371SPeter Grehan 		device_printf(sc->vtscsi_dev, "bus reset disabled\n");
20282f001371SPeter Grehan 		return (0);
20292f001371SPeter Grehan 	}
20302f001371SPeter Grehan 
20312f001371SPeter Grehan 	sc->vtscsi_flags |= VTSCSI_FLAG_RESET;
20322f001371SPeter Grehan 
20332f001371SPeter Grehan 	/*
20342f001371SPeter Grehan 	 * vtscsi_stop() will cause the in-flight requests to be canceled.
20352f001371SPeter Grehan 	 * Those requests are then completed here so CAM will retry them
20362f001371SPeter Grehan 	 * after the reset is complete.
20372f001371SPeter Grehan 	 */
20382f001371SPeter Grehan 	vtscsi_stop(sc);
20392f001371SPeter Grehan 	vtscsi_complete_vqs_locked(sc);
20402f001371SPeter Grehan 
20412f001371SPeter Grehan 	/* Rid the virtqueues of any remaining requests. */
20422f001371SPeter Grehan 	vtscsi_drain_vqs(sc);
20432f001371SPeter Grehan 
20442f001371SPeter Grehan 	/*
20452f001371SPeter Grehan 	 * Any resource shortage that froze the SIMQ cannot persist across
20462f001371SPeter Grehan 	 * a bus reset so ensure it gets thawed here.
20472f001371SPeter Grehan 	 */
20482f001371SPeter Grehan 	if (vtscsi_thaw_simq(sc, VTSCSI_REQUEST | VTSCSI_REQUEST_VQ) != 0)
20492f001371SPeter Grehan 		xpt_release_simq(sc->vtscsi_sim, 0);
20502f001371SPeter Grehan 
20512f001371SPeter Grehan 	error = vtscsi_reinit(sc);
20522f001371SPeter Grehan 	if (error) {
20532f001371SPeter Grehan 		device_printf(sc->vtscsi_dev,
20542f001371SPeter Grehan 		    "reinitialization failed, stopping device...\n");
20552f001371SPeter Grehan 		vtscsi_stop(sc);
20562f001371SPeter Grehan 	} else
20572f001371SPeter Grehan 		vtscsi_announce(sc, AC_BUS_RESET, CAM_TARGET_WILDCARD,
20582f001371SPeter Grehan 		    CAM_LUN_WILDCARD);
20592f001371SPeter Grehan 
20602f001371SPeter Grehan 	sc->vtscsi_flags &= ~VTSCSI_FLAG_RESET;
20612f001371SPeter Grehan 
20622f001371SPeter Grehan 	return (error);
20632f001371SPeter Grehan }
20642f001371SPeter Grehan 
20652f001371SPeter Grehan static void
vtscsi_init_request(struct vtscsi_softc * sc,struct vtscsi_request * req)20662f001371SPeter Grehan vtscsi_init_request(struct vtscsi_softc *sc, struct vtscsi_request *req)
20672f001371SPeter Grehan {
20682f001371SPeter Grehan 
20692f001371SPeter Grehan #ifdef INVARIANTS
20702f001371SPeter Grehan 	int req_nsegs, resp_nsegs;
20712f001371SPeter Grehan 
20722f001371SPeter Grehan 	req_nsegs = sglist_count(&req->vsr_ureq, sizeof(req->vsr_ureq));
20732f001371SPeter Grehan 	resp_nsegs = sglist_count(&req->vsr_uresp, sizeof(req->vsr_uresp));
20742f001371SPeter Grehan 
20752f001371SPeter Grehan 	KASSERT(req_nsegs == 1, ("request crossed page boundary"));
20762f001371SPeter Grehan 	KASSERT(resp_nsegs == 1, ("response crossed page boundary"));
20772f001371SPeter Grehan #endif
20782f001371SPeter Grehan 
20792f001371SPeter Grehan 	req->vsr_softc = sc;
20802f001371SPeter Grehan 	callout_init_mtx(&req->vsr_callout, VTSCSI_MTX(sc), 0);
20812f001371SPeter Grehan }
20822f001371SPeter Grehan 
20832f001371SPeter Grehan static int
vtscsi_alloc_requests(struct vtscsi_softc * sc)20842f001371SPeter Grehan vtscsi_alloc_requests(struct vtscsi_softc *sc)
20852f001371SPeter Grehan {
20862f001371SPeter Grehan 	struct vtscsi_request *req;
20872f001371SPeter Grehan 	int i, nreqs;
20882f001371SPeter Grehan 
20892f001371SPeter Grehan 	/*
20902f001371SPeter Grehan 	 * Commands destined for either the request or control queues come
20912f001371SPeter Grehan 	 * from the same SIM queue. Use the size of the request virtqueue
20922f001371SPeter Grehan 	 * as it (should) be much more frequently used. Some additional
20932f001371SPeter Grehan 	 * requests are allocated for internal (TMF) use.
20942f001371SPeter Grehan 	 */
20952f001371SPeter Grehan 	nreqs = virtqueue_size(sc->vtscsi_request_vq);
20962f001371SPeter Grehan 	if ((sc->vtscsi_flags & VTSCSI_FLAG_INDIRECT) == 0)
20972f001371SPeter Grehan 		nreqs /= VTSCSI_MIN_SEGMENTS;
20982f001371SPeter Grehan 	nreqs += VTSCSI_RESERVED_REQUESTS;
20992f001371SPeter Grehan 
21002f001371SPeter Grehan 	for (i = 0; i < nreqs; i++) {
21012f001371SPeter Grehan 		req = malloc(sizeof(struct vtscsi_request), M_DEVBUF,
21022f001371SPeter Grehan 		    M_NOWAIT);
21032f001371SPeter Grehan 		if (req == NULL)
21042f001371SPeter Grehan 			return (ENOMEM);
21052f001371SPeter Grehan 
21062f001371SPeter Grehan 		vtscsi_init_request(sc, req);
21072f001371SPeter Grehan 
21082f001371SPeter Grehan 		sc->vtscsi_nrequests++;
21092f001371SPeter Grehan 		vtscsi_enqueue_request(sc, req);
21102f001371SPeter Grehan 	}
21112f001371SPeter Grehan 
21122f001371SPeter Grehan 	return (0);
21132f001371SPeter Grehan }
21142f001371SPeter Grehan 
21152f001371SPeter Grehan static void
vtscsi_free_requests(struct vtscsi_softc * sc)21162f001371SPeter Grehan vtscsi_free_requests(struct vtscsi_softc *sc)
21172f001371SPeter Grehan {
21182f001371SPeter Grehan 	struct vtscsi_request *req;
21192f001371SPeter Grehan 
21202f001371SPeter Grehan 	while ((req = vtscsi_dequeue_request(sc)) != NULL) {
21212f001371SPeter Grehan 		KASSERT(callout_active(&req->vsr_callout) == 0,
21222f001371SPeter Grehan 		    ("request callout still active"));
21232f001371SPeter Grehan 
21242f001371SPeter Grehan 		sc->vtscsi_nrequests--;
21252f001371SPeter Grehan 		free(req, M_DEVBUF);
21262f001371SPeter Grehan 	}
21272f001371SPeter Grehan 
21282f001371SPeter Grehan 	KASSERT(sc->vtscsi_nrequests == 0, ("leaked requests: %d",
21292f001371SPeter Grehan 	    sc->vtscsi_nrequests));
21302f001371SPeter Grehan }
21312f001371SPeter Grehan 
21322f001371SPeter Grehan static void
vtscsi_enqueue_request(struct vtscsi_softc * sc,struct vtscsi_request * req)21332f001371SPeter Grehan vtscsi_enqueue_request(struct vtscsi_softc *sc, struct vtscsi_request *req)
21342f001371SPeter Grehan {
21352f001371SPeter Grehan 
21362f001371SPeter Grehan 	KASSERT(req->vsr_softc == sc,
21372f001371SPeter Grehan 	    ("non-matching request vsr_softc %p/%p", req->vsr_softc, sc));
21382f001371SPeter Grehan 
21392f001371SPeter Grehan 	vtscsi_dprintf(sc, VTSCSI_TRACE, "req=%p\n", req);
21402f001371SPeter Grehan 
21412f001371SPeter Grehan 	/* A request is available so the SIMQ could be released. */
21422f001371SPeter Grehan 	if (vtscsi_thaw_simq(sc, VTSCSI_REQUEST) != 0)
21432f001371SPeter Grehan 		xpt_release_simq(sc->vtscsi_sim, 1);
21442f001371SPeter Grehan 
21452f001371SPeter Grehan 	req->vsr_ccb = NULL;
21462f001371SPeter Grehan 	req->vsr_complete = NULL;
21472f001371SPeter Grehan 	req->vsr_ptr0 = NULL;
21482f001371SPeter Grehan 	req->vsr_state = VTSCSI_REQ_STATE_FREE;
21492f001371SPeter Grehan 	req->vsr_flags = 0;
21502f001371SPeter Grehan 
21512f001371SPeter Grehan 	bzero(&req->vsr_ureq, sizeof(req->vsr_ureq));
21522f001371SPeter Grehan 	bzero(&req->vsr_uresp, sizeof(req->vsr_uresp));
21532f001371SPeter Grehan 
21542f001371SPeter Grehan 	/*
21552f001371SPeter Grehan 	 * We insert at the tail of the queue in order to make it
21562f001371SPeter Grehan 	 * very unlikely a request will be reused if we race with
21572f001371SPeter Grehan 	 * stopping its callout handler.
21582f001371SPeter Grehan 	 */
21592f001371SPeter Grehan 	TAILQ_INSERT_TAIL(&sc->vtscsi_req_free, req, vsr_link);
21602f001371SPeter Grehan }
21612f001371SPeter Grehan 
21622f001371SPeter Grehan static struct vtscsi_request *
vtscsi_dequeue_request(struct vtscsi_softc * sc)21632f001371SPeter Grehan vtscsi_dequeue_request(struct vtscsi_softc *sc)
21642f001371SPeter Grehan {
21652f001371SPeter Grehan 	struct vtscsi_request *req;
21662f001371SPeter Grehan 
21672f001371SPeter Grehan 	req = TAILQ_FIRST(&sc->vtscsi_req_free);
21682f001371SPeter Grehan 	if (req != NULL) {
21692f001371SPeter Grehan 		req->vsr_state = VTSCSI_REQ_STATE_INUSE;
21702f001371SPeter Grehan 		TAILQ_REMOVE(&sc->vtscsi_req_free, req, vsr_link);
21712f001371SPeter Grehan 	} else
21722f001371SPeter Grehan 		sc->vtscsi_stats.dequeue_no_requests++;
21732f001371SPeter Grehan 
21742f001371SPeter Grehan 	vtscsi_dprintf(sc, VTSCSI_TRACE, "req=%p\n", req);
21752f001371SPeter Grehan 
21762f001371SPeter Grehan 	return (req);
21772f001371SPeter Grehan }
21782f001371SPeter Grehan 
21792f001371SPeter Grehan static void
vtscsi_complete_request(struct vtscsi_request * req)21802f001371SPeter Grehan vtscsi_complete_request(struct vtscsi_request *req)
21812f001371SPeter Grehan {
21822f001371SPeter Grehan 
21832f001371SPeter Grehan 	if (req->vsr_flags & VTSCSI_REQ_FLAG_POLLED)
21842f001371SPeter Grehan 		req->vsr_flags |= VTSCSI_REQ_FLAG_COMPLETE;
21852f001371SPeter Grehan 
21862f001371SPeter Grehan 	if (req->vsr_complete != NULL)
21872f001371SPeter Grehan 		req->vsr_complete(req->vsr_softc, req);
21882f001371SPeter Grehan }
21892f001371SPeter Grehan 
21902f001371SPeter Grehan static void
vtscsi_complete_vq(struct vtscsi_softc * sc,struct virtqueue * vq)21912f001371SPeter Grehan vtscsi_complete_vq(struct vtscsi_softc *sc, struct virtqueue *vq)
21922f001371SPeter Grehan {
21932f001371SPeter Grehan 	struct vtscsi_request *req;
21942f001371SPeter Grehan 
21952f001371SPeter Grehan 	VTSCSI_LOCK_OWNED(sc);
21962f001371SPeter Grehan 
21972f001371SPeter Grehan 	while ((req = virtqueue_dequeue(vq, NULL)) != NULL)
21982f001371SPeter Grehan 		vtscsi_complete_request(req);
21992f001371SPeter Grehan }
22002f001371SPeter Grehan 
22012f001371SPeter Grehan static void
vtscsi_control_vq_intr(void * xsc)22026632efe4SBryan Venteicher vtscsi_control_vq_intr(void *xsc)
22032f001371SPeter Grehan {
22042f001371SPeter Grehan 	struct vtscsi_softc *sc;
22052f001371SPeter Grehan 	struct virtqueue *vq;
22062f001371SPeter Grehan 
22076632efe4SBryan Venteicher 	sc = xsc;
22082f001371SPeter Grehan 	vq = sc->vtscsi_control_vq;
22092f001371SPeter Grehan 
22106632efe4SBryan Venteicher again:
22112f001371SPeter Grehan 	VTSCSI_LOCK(sc);
22122f001371SPeter Grehan 
22132f001371SPeter Grehan 	vtscsi_complete_vq(sc, sc->vtscsi_control_vq);
22142f001371SPeter Grehan 
22152f001371SPeter Grehan 	if (virtqueue_enable_intr(vq) != 0) {
22162f001371SPeter Grehan 		virtqueue_disable_intr(vq);
22172f001371SPeter Grehan 		VTSCSI_UNLOCK(sc);
22186632efe4SBryan Venteicher 		goto again;
22192f001371SPeter Grehan 	}
22202f001371SPeter Grehan 
22212f001371SPeter Grehan 	VTSCSI_UNLOCK(sc);
22222f001371SPeter Grehan }
22232f001371SPeter Grehan 
22242f001371SPeter Grehan static void
vtscsi_event_vq_intr(void * xsc)22256632efe4SBryan Venteicher vtscsi_event_vq_intr(void *xsc)
22262f001371SPeter Grehan {
22272f001371SPeter Grehan 	struct vtscsi_softc *sc;
22282f001371SPeter Grehan 	struct virtqueue *vq;
22292f001371SPeter Grehan 	struct virtio_scsi_event *event;
22302f001371SPeter Grehan 
22316632efe4SBryan Venteicher 	sc = xsc;
22322f001371SPeter Grehan 	vq = sc->vtscsi_event_vq;
22332f001371SPeter Grehan 
22346632efe4SBryan Venteicher again:
22352f001371SPeter Grehan 	VTSCSI_LOCK(sc);
22362f001371SPeter Grehan 
22372f001371SPeter Grehan 	while ((event = virtqueue_dequeue(vq, NULL)) != NULL)
22382f001371SPeter Grehan 		vtscsi_handle_event(sc, event);
22392f001371SPeter Grehan 
22402f001371SPeter Grehan 	if (virtqueue_enable_intr(vq) != 0) {
22412f001371SPeter Grehan 		virtqueue_disable_intr(vq);
22422f001371SPeter Grehan 		VTSCSI_UNLOCK(sc);
22436632efe4SBryan Venteicher 		goto again;
22442f001371SPeter Grehan 	}
22452f001371SPeter Grehan 
22462f001371SPeter Grehan 	VTSCSI_UNLOCK(sc);
22472f001371SPeter Grehan }
22482f001371SPeter Grehan 
22492f001371SPeter Grehan static void
vtscsi_request_vq_intr(void * xsc)22506632efe4SBryan Venteicher vtscsi_request_vq_intr(void *xsc)
22512f001371SPeter Grehan {
22522f001371SPeter Grehan 	struct vtscsi_softc *sc;
22532f001371SPeter Grehan 	struct virtqueue *vq;
22542f001371SPeter Grehan 
22556632efe4SBryan Venteicher 	sc = xsc;
22562f001371SPeter Grehan 	vq = sc->vtscsi_request_vq;
22572f001371SPeter Grehan 
22586632efe4SBryan Venteicher again:
22592f001371SPeter Grehan 	VTSCSI_LOCK(sc);
22602f001371SPeter Grehan 
22612f001371SPeter Grehan 	vtscsi_complete_vq(sc, sc->vtscsi_request_vq);
22622f001371SPeter Grehan 
22632f001371SPeter Grehan 	if (virtqueue_enable_intr(vq) != 0) {
22642f001371SPeter Grehan 		virtqueue_disable_intr(vq);
22652f001371SPeter Grehan 		VTSCSI_UNLOCK(sc);
22666632efe4SBryan Venteicher 		goto again;
22672f001371SPeter Grehan 	}
22682f001371SPeter Grehan 
22692f001371SPeter Grehan 	VTSCSI_UNLOCK(sc);
22702f001371SPeter Grehan }
22712f001371SPeter Grehan 
22722f001371SPeter Grehan static void
vtscsi_disable_vqs_intr(struct vtscsi_softc * sc)22732f001371SPeter Grehan vtscsi_disable_vqs_intr(struct vtscsi_softc *sc)
22742f001371SPeter Grehan {
22752f001371SPeter Grehan 
22762f001371SPeter Grehan 	virtqueue_disable_intr(sc->vtscsi_control_vq);
22772f001371SPeter Grehan 	virtqueue_disable_intr(sc->vtscsi_event_vq);
22782f001371SPeter Grehan 	virtqueue_disable_intr(sc->vtscsi_request_vq);
22792f001371SPeter Grehan }
22802f001371SPeter Grehan 
22812f001371SPeter Grehan static void
vtscsi_enable_vqs_intr(struct vtscsi_softc * sc)22822f001371SPeter Grehan vtscsi_enable_vqs_intr(struct vtscsi_softc *sc)
22832f001371SPeter Grehan {
22842f001371SPeter Grehan 
22852f001371SPeter Grehan 	virtqueue_enable_intr(sc->vtscsi_control_vq);
22862f001371SPeter Grehan 	virtqueue_enable_intr(sc->vtscsi_event_vq);
22872f001371SPeter Grehan 	virtqueue_enable_intr(sc->vtscsi_request_vq);
22882f001371SPeter Grehan }
22892f001371SPeter Grehan 
22902f001371SPeter Grehan static void
vtscsi_get_tunables(struct vtscsi_softc * sc)22912f001371SPeter Grehan vtscsi_get_tunables(struct vtscsi_softc *sc)
22922f001371SPeter Grehan {
22932f001371SPeter Grehan 	char tmpstr[64];
22942f001371SPeter Grehan 
22952f001371SPeter Grehan 	TUNABLE_INT_FETCH("hw.vtscsi.debug_level", &sc->vtscsi_debug);
22962f001371SPeter Grehan 
22972f001371SPeter Grehan 	snprintf(tmpstr, sizeof(tmpstr), "dev.vtscsi.%d.debug_level",
22982f001371SPeter Grehan 	    device_get_unit(sc->vtscsi_dev));
22992f001371SPeter Grehan 	TUNABLE_INT_FETCH(tmpstr, &sc->vtscsi_debug);
23002f001371SPeter Grehan }
23012f001371SPeter Grehan 
23022f001371SPeter Grehan static void
vtscsi_setup_sysctl(struct vtscsi_softc * sc)2303e6cc42f1SBryan Venteicher vtscsi_setup_sysctl(struct vtscsi_softc *sc)
23042f001371SPeter Grehan {
23052f001371SPeter Grehan 	device_t dev;
23062f001371SPeter Grehan 	struct vtscsi_statistics *stats;
23072f001371SPeter Grehan         struct sysctl_ctx_list *ctx;
23082f001371SPeter Grehan 	struct sysctl_oid *tree;
23092f001371SPeter Grehan 	struct sysctl_oid_list *child;
23102f001371SPeter Grehan 
23112f001371SPeter Grehan 	dev = sc->vtscsi_dev;
23122f001371SPeter Grehan 	stats = &sc->vtscsi_stats;
23132f001371SPeter Grehan 	ctx = device_get_sysctl_ctx(dev);
23142f001371SPeter Grehan 	tree = device_get_sysctl_tree(dev);
23152f001371SPeter Grehan 	child = SYSCTL_CHILDREN(tree);
23162f001371SPeter Grehan 
23172f001371SPeter Grehan 	SYSCTL_ADD_INT(ctx, child, OID_AUTO, "debug_level",
23182f001371SPeter Grehan 	    CTLFLAG_RW, &sc->vtscsi_debug, 0,
23192f001371SPeter Grehan 	    "Debug level");
23202f001371SPeter Grehan 
23212f001371SPeter Grehan 	SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "scsi_cmd_timeouts",
23222f001371SPeter Grehan 	    CTLFLAG_RD, &stats->scsi_cmd_timeouts,
23232f001371SPeter Grehan 	    "SCSI command timeouts");
23242f001371SPeter Grehan 	SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "dequeue_no_requests",
23252f001371SPeter Grehan 	    CTLFLAG_RD, &stats->dequeue_no_requests,
23262f001371SPeter Grehan 	    "No available requests to dequeue");
23272f001371SPeter Grehan }
23282f001371SPeter Grehan 
23292f001371SPeter Grehan static void
vtscsi_printf_req(struct vtscsi_request * req,const char * func,const char * fmt,...)23302f001371SPeter Grehan vtscsi_printf_req(struct vtscsi_request *req, const char *func,
23312f001371SPeter Grehan     const char *fmt, ...)
23322f001371SPeter Grehan {
23332f001371SPeter Grehan 	struct vtscsi_softc *sc;
23342f001371SPeter Grehan 	union ccb *ccb;
23352f001371SPeter Grehan 	struct sbuf sb;
23362f001371SPeter Grehan 	va_list ap;
23372f001371SPeter Grehan 	char str[192];
23382f001371SPeter Grehan 
23392f001371SPeter Grehan 	if (req == NULL)
23402f001371SPeter Grehan 		return;
23412f001371SPeter Grehan 
23422f001371SPeter Grehan 	sc = req->vsr_softc;
23432f001371SPeter Grehan 	ccb = req->vsr_ccb;
23442f001371SPeter Grehan 
23452f001371SPeter Grehan 	va_start(ap, fmt);
23462f001371SPeter Grehan 	sbuf_new(&sb, str, sizeof(str), 0);
23472f001371SPeter Grehan 
23482f001371SPeter Grehan 	if (ccb == NULL) {
23492f001371SPeter Grehan 		sbuf_printf(&sb, "(noperiph:%s%d:%u): ",
23502f001371SPeter Grehan 		    cam_sim_name(sc->vtscsi_sim), cam_sim_unit(sc->vtscsi_sim),
23512f001371SPeter Grehan 		    cam_sim_bus(sc->vtscsi_sim));
23522f001371SPeter Grehan 	} else {
2353*8c4ee0b2SAlexander Motin 		xpt_path_sbuf(ccb->ccb_h.path, &sb);
23542f001371SPeter Grehan 		if (ccb->ccb_h.func_code == XPT_SCSI_IO) {
23552f001371SPeter Grehan 			scsi_command_string(&ccb->csio, &sb);
23562f001371SPeter Grehan 			sbuf_printf(&sb, "length %d ", ccb->csio.dxfer_len);
23572f001371SPeter Grehan 		}
23582f001371SPeter Grehan 	}
23592f001371SPeter Grehan 
23602f001371SPeter Grehan 	sbuf_vprintf(&sb, fmt, ap);
23612f001371SPeter Grehan 	va_end(ap);
23622f001371SPeter Grehan 
23632f001371SPeter Grehan 	sbuf_finish(&sb);
23642f001371SPeter Grehan 	printf("%s: %s: %s", device_get_nameunit(sc->vtscsi_dev), func,
23652f001371SPeter Grehan 	    sbuf_data(&sb));
23662f001371SPeter Grehan }
2367