Index | Thread | Search

From:
Mike Larkin <mlarkin@nested.page>
Subject:
Re: make vmd's vioscsi a child process
To:
Dave Voutila <dv@sisu.io>
Cc:
tech@openbsd.org
Date:
Thu, 12 Feb 2026 13:50:48 -0800

Download raw body.

Thread
On Wed, Feb 11, 2026 at 09:51:42PM -0500, Dave Voutila wrote:
> There are three virtio devices that don't fork/exec into child
> processes: vmmci, the entropy device, and the scsi device (for cdrom
> emulation).
>
> This diff updates vioscsi to follow the fork/exec dance into a
> privsep process. Only two will remain in the vm process (viornd, vmmci).
>
> Design mimics vioblk as they're very similar being single-threaded
> operating on a single fd, but virtio scsi has device specific registers
> to handle reads from/writes to.
>
> Tests definitely welcome as not all virtio scsi drivers exercise all
> of the device specific virtio registers.
>
> oherwise, ok?
>

first read seems ok. I will test a bit this weekend and let you know if I run
into anything.

> -dv
>
> diff refs/heads/master refs/heads/vmd-vioscsi
> commit - b421eb1ff206c81187a06b7792023e34196771b5
> commit + bddff255892ea933c6b0beee6c6ac755e8c0f01a
> blob - 4fcd95f19be0ae4637629852acd8fa9390327bdb
> blob + 8fab755ed3f8e06a032de0a192c3fd504be2608c
> --- usr.sbin/vmd/vioscsi.c
> +++ usr.sbin/vmd/vioscsi.c
> @@ -25,9 +25,13 @@
>  #include <scsi/scsiconf.h>
>  #include <scsi/cd.h>
>
> +#include <errno.h>
> +#include <event.h>
>  #include <stdlib.h>
>  #include <string.h>
> +#include <unistd.h>
>
> +#include "atomicio.h"
>  #include "vmd.h"
>  #include "vioscsi.h"
>  #include "virtio.h"
> @@ -42,6 +46,159 @@
>  #define DPRINTF(x...)	do {} while(0)
>  #endif	/* VIOSCSI_DEBUG */
>
> +extern struct vmd_vm *current_vm;
> +
> +static void dev_dispatch_vm(int, short, void *);
> +static void handle_sync_io(int, short, void *);
> +static uint32_t vioscsi_io_cfg(struct virtio_dev *, int, uint8_t, uint32_t,
> +    uint8_t);
> +static int vioscsi_notifyq(struct virtio_dev *, uint16_t);
> +static uint32_t vioscsi_read(struct virtio_dev *, struct viodev_msg *, int *);
> +static int vioscsi_write(struct virtio_dev *, struct viodev_msg *);
> +
> +__dead void
> +vioscsi_main(int fd, int fd_vmm)
> +{
> +	struct virtio_dev	 dev;
> +	struct vioscsi_dev	*vioscsi = NULL;
> +	struct viodev_msg 	 msg;
> +	struct vmd_vm		 vm;
> +	ssize_t			 sz;
> +	int			 ret;
> +
> +	/*
> +	 * stdio - needed for read/write to disk fds and channels to the vm.
> +	 * vmm + proc - needed to create shared vm mappings.
> +	 */
> +	if (pledge("stdio vmm proc", NULL) == -1)
> +		fatal("pledge");
> +
> +	/* Receive our virtio_dev, mostly preconfigured. */
> +	memset(&dev, 0, sizeof(dev));
> +	sz = atomicio(read, fd, &dev, sizeof(dev));
> +	if (sz != sizeof(dev)) {
> +		ret = errno;
> +		log_warn("failed to receive vioscsi");
> +		goto fail;
> +	}
> +	if (dev.dev_type != VMD_DEVTYPE_SCSI) {
> +		ret = EINVAL;
> +		log_warn("received invalid device type");
> +		goto fail;
> +	}
> +	dev.sync_fd = fd;
> +	vioscsi = &dev.vioscsi;
> +
> +	log_debug("%s: got vioscsi dev. cdrom fd = %d, syncfd = %d, "
> +	    "asyncfd = %d, vmm fd = %d", __func__, vioscsi->cdrom_fd,
> +	    dev.sync_fd, dev.async_fd, fd_vmm);
> +
> +	/* Receive our vm information from the vm process. */
> +	memset(&vm, 0, sizeof(vm));
> +	sz = atomicio(read, dev.sync_fd, &vm, sizeof(vm));
> +	if (sz != sizeof(vm)) {
> +		ret = EIO;
> +		log_warnx("failed to receive vm details");
> +		goto fail;
> +	}
> +	current_vm = &vm;
> +
> +	setproctitle("%s/vioscsi", vm.vm_params.vmc_name);
> +	log_procinit("vm/%s/vioscsi", vm.vm_params.vmc_name);
> +
> +	/* Now that we have our vm information, we can remap memory. */
> +	ret = remap_guest_mem(&vm, fd_vmm);
> +	if (ret) {
> +		log_warnx("failed to remap guest memory");
> +		goto fail;
> +	}
> +
> +	/*
> +	 * We no longer need /dev/vmm access.
> +	 */
> +	close_fd(fd_vmm);
> +	if (pledge("stdio", NULL) == -1)
> +		fatal("pledge2");
> +
> +	/* Initialize the vioscsi backing file. */
> +	ret = virtio_raw_init(&vioscsi->file, &vioscsi->sz,
> +	    &vioscsi->cdrom_fd, 1);
> +	if (ret == -1) {
> +		log_warnx("%s: unable to determine iso format", __func__);
> +		goto fail;
> +	}
> +	vioscsi->n_blocks = vioscsi->sz / VIOSCSI_BLOCK_SIZE_CDROM;
> +
> +	/* Initialize libevent so we can start wiring event handlers. */
> +	event_init();
> +
> +	/* Wire up an async imsg channel. */
> +	log_debug("%s: wiring in async vm event handler (fd=%d)", __func__,
> +		dev.async_fd);
> +	if (vm_device_pipe(&dev, dev_dispatch_vm, NULL)) {
> +		ret = EIO;
> +		log_warnx("vm_device_pipe");
> +		goto fail;
> +	}
> +
> +	/* Configure our sync channel event handler. */
> +	log_debug("%s: wiring in sync channel handler (fd=%d)", __func__,
> +		dev.sync_fd);
> +	if (imsgbuf_init(&dev.sync_iev.ibuf, dev.sync_fd) == -1) {
> +		log_warn("imsgbuf_init");
> +		goto fail;
> +	}
> +	dev.sync_iev.handler = handle_sync_io;
> +	dev.sync_iev.data = &dev;
> +	dev.sync_iev.events = EV_READ;
> +	imsg_event_add(&dev.sync_iev);
> +
> +	/* Send a ready message over the sync channel. */
> +	log_debug("%s: telling vm %s device is ready", __func__,
> +	    vm.vm_params.vmc_name);
> +	memset(&msg, 0, sizeof(msg));
> +	msg.type = VIODEV_MSG_READY;
> +	imsg_compose_event(&dev.sync_iev, IMSG_DEVOP_MSG, 0, 0, -1, &msg,
> +	    sizeof(msg));
> +
> +	/* Send a ready message over the async channel. */
> +	log_debug("%s: sending heartbeat", __func__);
> +	ret = imsg_compose_event(&dev.async_iev, IMSG_DEVOP_MSG, 0, 0, -1,
> +	    &msg, sizeof(msg));
> +	if (ret == -1) {
> +		log_warnx("%s: failed to send async ready message!", __func__);
> +		goto fail;
> +	}
> +
> +	/* Engage the event loop! */
> +	ret = event_dispatch();
> +
> +	if (ret == 0) {
> +		/* Clean shutdown. */
> +		close_fd(dev.sync_fd);
> +		close_fd(dev.async_fd);
> +		close_fd(vioscsi->cdrom_fd);
> +		_exit(0);
> +		/* NOTREACHED */
> +	}
> +
> +fail:
> +	/* Try letting the vm know we've failed something. */
> +	memset(&msg, 0, sizeof(msg));
> +	msg.type = VIODEV_MSG_ERROR;
> +	msg.data = ret;
> +	imsg_compose(&dev.sync_iev.ibuf, IMSG_DEVOP_MSG, 0, 0, -1, &msg,
> +	    sizeof(msg));
> +	imsgbuf_flush(&dev.sync_iev.ibuf);
> +
> +	close_fd(dev.sync_fd);
> +	close_fd(dev.async_fd);
> +	if (vioscsi != NULL)
> +		close_fd(vioscsi->cdrom_fd);
> +	_exit(ret);
> +	/* NOTREACHED */
> +}
> +
>  static void
>  vioscsi_prepare_resp(struct virtio_scsi_res_hdr *resp, uint8_t vio_status,
>      uint8_t scsi_status, uint8_t err_flags, uint8_t add_sense_code,
> @@ -262,7 +419,204 @@ vioscsi_handle_tur(struct virtio_dev *dev, struct virt
>  	return (ret);
>  }
>
> +static void
> +dev_dispatch_vm(int fd, short event, void *arg)
> +{
> +	struct virtio_dev	*dev = (struct virtio_dev *)arg;
> +	struct imsgev		*iev = &dev->async_iev;
> +	struct imsgbuf		*ibuf = &iev->ibuf;
> +	struct imsg		 imsg;
> +	ssize_t			 n = 0;
> +	uint32_t		 type;
> +	int			 verbose;
> +
> +	if (event & EV_READ) {
> +		if ((n = imsgbuf_read(ibuf)) == -1)
> +			fatal("%s: imsgbuf_read", __func__);
> +		if (n == 0) {
> +			/* this pipe is dead, so remove the event handler */
> +			log_debug("%s: vioscsi pipe dead (EV_READ)", __func__);
> +			event_del(&iev->ev);
> +			event_loopexit(NULL);
> +			return;
> +		}
> +	}
> +
> +	if (event & EV_WRITE) {
> +		if (imsgbuf_write(ibuf) == -1) {
> +			if (errno == EPIPE) {
> +				/* this pipe is dead, remove the handler */
> +				log_debug("%s: pipe dead (EV_WRITE)", __func__);
> +				event_del(&iev->ev);
> +				event_loopexit(NULL);
> +				return;
> +			}
> +			fatal("%s: imsgbuf_write", __func__);
> +		}
> +	}
> +
> +	for (;;) {
> +		if ((n = imsg_get(ibuf, &imsg)) == -1)
> +			fatal("%s: imsg_get", __func__);
> +		if (n == 0)
> +			break;
> +
> +		type = imsg_get_type(&imsg);
> +		switch (type) {
> +		case IMSG_VMDOP_PAUSE_VM:
> +			log_debug("%s: pausing", __func__);
> +			break;
> +		case IMSG_VMDOP_UNPAUSE_VM:
> +			log_debug("%s: unpausing", __func__);
> +			break;
> +		case IMSG_CTL_VERBOSE:
> +			verbose = imsg_int_read(&imsg);
> +			log_setverbose(verbose);
> +			break;
> +		default:
> +			log_warnx("%s: unhandled imsg type %d", __func__, type);
> +			break;
> +		}
> +		imsg_free(&imsg);
> +	}
> +	imsg_event_add(iev);
> +}
> +
> +/*
> + * Synchronous IO handler.
> + */
> +static void
> +handle_sync_io(int fd, short event, void *arg)
> +{
> +	struct virtio_dev *dev = (struct virtio_dev *)arg;
> +	struct imsgev *iev = &dev->sync_iev;
> +	struct imsgbuf *ibuf = &iev->ibuf;
> +	struct viodev_msg msg;
> +	struct imsg imsg;
> +	ssize_t n;
> +	int deassert = 0;
> +
> +	if (event & EV_READ) {
> +		if ((n = imsgbuf_read(ibuf)) == -1)
> +			fatal("%s: imsgbuf_read", __func__);
> +		if (n == 0) {
> +			/* this pipe is dead, so remove the event handler */
> +			log_debug("%s: vioscsi pipe dead (EV_READ)", __func__);
> +			event_del(&iev->ev);
> +			event_loopexit(NULL);
> +			return;
> +		}
> +	}
> +
> +	if (event & EV_WRITE) {
> +		if (imsgbuf_write(ibuf) == -1) {
> +			if (errno == EPIPE) {
> +				/* this pipe is dead, remove the handler */
> +				log_debug("%s: pipe dead (EV_WRITE)", __func__);
> +				event_del(&iev->ev);
> +				event_loopexit(NULL);
> +				return;
> +			}
> +			fatal("%s: imsgbuf_write", __func__);
> +		}
> +	}
> +
> +	for (;;) {
> +		if ((n = imsg_get(ibuf, &imsg)) == -1)
> +			fatalx("%s: imsg_get (n=%ld)", __func__, n);
> +		if (n == 0)
> +			break;
> +
> +		/* Unpack our message. They ALL should be dev messages! */
> +		viodev_msg_read(&imsg, &msg);
> +		imsg_free(&imsg);
> +
> +		switch (msg.type) {
> +		case VIODEV_MSG_IO_READ:
> +			/* Read IO: make sure to send a reply */
> +			msg.data = vioscsi_read(dev, &msg, &deassert);
> +			msg.data_valid = 1;
> +			if (deassert) {
> +				/* Inline any interrupt deassertions. */
> +				msg.state = INTR_STATE_DEASSERT;
> +			}
> +			imsg_compose_event(iev, IMSG_DEVOP_MSG, 0, 0, -1, &msg,
> +			    sizeof(msg));
> +			break;
> +		case VIODEV_MSG_IO_WRITE:
> +			/* Write IO: no reply needed, but maybe an irq assert */
> +			if (vioscsi_write(dev, &msg))
> +				virtio_assert_irq(dev, 0);
> +			break;
> +		case VIODEV_MSG_SHUTDOWN:
> +			event_del(&dev->sync_iev.ev);
> +			event_loopbreak();
> +			return;
> +		default:
> +			fatalx("%s: invalid msg type %d", __func__, msg.type);
> +		}
> +	}
> +	imsg_event_add(iev);
> +}
> +
>  static int
> +vioscsi_write(struct virtio_dev *dev, struct viodev_msg *msg)
> +{
> +	uint32_t data = msg->data;
> +	uint16_t reg = msg->reg;
> +	uint8_t sz = msg->io_sz;
> +	int notify = 0;
> +
> +	switch (reg & 0xFF00) {
> +	case VIO1_CFG_BAR_OFFSET:
> +		(void)virtio_io_cfg(dev, VEI_DIR_OUT, (reg & 0xFF), data, sz);
> +		break;
> +	case VIO1_DEV_BAR_OFFSET:
> +		(void)vioscsi_io_cfg(dev, VEI_DIR_OUT, (reg & 0xFF), data, sz);
> +		break;
> +	case VIO1_NOTIFY_BAR_OFFSET:
> +		notify = vioscsi_notifyq(dev, (uint16_t)(msg->data));
> +		break;
> +	case VIO1_ISR_BAR_OFFSET:
> +		/* Ignore writes to ISR. */
> +		break;
> +	default:
> +		log_debug("%s: no handler for reg 0x%04x", __func__, reg);
> +	}
> +
> +	return (notify);
> +}
> +
> +static uint32_t
> +vioscsi_read(struct virtio_dev *dev, struct viodev_msg *msg, int *deassert)
> +{
> +	uint32_t data = 0;
> +	uint16_t reg = msg->reg;
> +	uint8_t sz = msg->io_sz;
> +
> +	switch (reg & 0xFF00) {
> +	case VIO1_CFG_BAR_OFFSET:
> +		data = virtio_io_cfg(dev, VEI_DIR_IN, (reg & 0xFF), 0, sz);
> +		break;
> +	case VIO1_DEV_BAR_OFFSET:
> +		data = vioscsi_io_cfg(dev, VEI_DIR_IN, (reg & 0xFF), 0, sz);
> +		break;
> +	case VIO1_NOTIFY_BAR_OFFSET:
> +		/* Reads of notify register return all 1's. */
> +		break;
> +	case VIO1_ISR_BAR_OFFSET:
> +		data = dev->isr;
> +		dev->isr = 0;
> +		*deassert = 1;
> +		break;
> +	default:
> +		log_debug("%s: no handler for reg 0x%04x", __func__, reg);
> +	}
> +
> +	return (data);
> +}
> +
> +static int
>  vioscsi_handle_inquiry(struct virtio_dev *dev, struct virtio_vq_info *vq_info,
>      struct virtio_scsi_req_hdr *req, struct virtio_vq_acct *acct)
>  {
> @@ -1708,19 +2062,17 @@ get_config_out:
>  	return (ret);
>  }
>
> -int
> -vioscsi_io(int dir, uint16_t reg, uint32_t *data, uint8_t *intr,
> -    void *cookie, uint8_t sz)
> +static uint32_t
> +vioscsi_io_cfg(struct virtio_dev *dev, int dir, uint8_t reg, uint32_t data,
> +    uint8_t sz)
>  {
> -	struct virtio_dev *dev = (struct virtio_dev *)cookie;
>  	struct vioscsi_dev *vioscsi = NULL;
> +	uint32_t res = 0;
>
>  	if (dev->device_id != PCI_PRODUCT_VIRTIO_SCSI)
>  		fatalx("%s: virtio device is not a scsi device", __func__);
>  	vioscsi = &dev->vioscsi;
>
> -	*intr = 0xFF;
> -
>  	DPRINTF("%s: request %s reg %s sz %u", __func__,
>  	    dir ? "READ" : "WRITE", vioscsi_reg_name(reg), sz);
>
> @@ -1728,13 +2080,13 @@ vioscsi_io(int dir, uint16_t reg, uint32_t *data, uint
>  		switch (reg) {
>  		case VIRTIO_SCSI_CONFIG_SENSE_SIZE:
>  			/* Support writing to sense size register. */
> -			if (*data != VIOSCSI_SENSE_LEN)
> +			if (data != VIOSCSI_SENSE_LEN)
>  				log_warnx("%s: guest write to sense size "
>  				    "register ignored", __func__);
>  			break;
>  		case VIRTIO_SCSI_CONFIG_CDB_SIZE:
>  			/* Support writing CDB size. */
> -			if (*data != VIOSCSI_CDB_LEN)
> +			if (data != VIOSCSI_CDB_LEN)
>  				log_warnx("%s: guest write to cdb size "
>  				    "register ignored", __func__);
>  			break;
> @@ -1747,63 +2099,63 @@ vioscsi_io(int dir, uint16_t reg, uint32_t *data, uint
>  		case VIRTIO_SCSI_CONFIG_NUM_QUEUES:
>  			/* Number of request queues, not number of all queues. */
>  			if (sz == 4)
> -				*data = (uint32_t)(VIOSCSI_NUM_REQ_QUEUES);
> +				res = (uint32_t)(VIOSCSI_NUM_REQ_QUEUES);
>  			else
>  				log_warnx("%s: unaligned read of num queues "
>  				    "register", __func__);
>  			break;
>  		case VIRTIO_SCSI_CONFIG_SEG_MAX:
>  			if (sz == 4)
> -				*data = (uint32_t)(VIOSCSI_SEG_MAX);
> +				res = (uint32_t)(VIOSCSI_SEG_MAX);
>  			else
>  				log_warnx("%s: unaligned read of seg max "
>  				    "register", __func__);
>  			break;
>  		case VIRTIO_SCSI_CONFIG_MAX_SECTORS:
>  			if (sz == 4)
> -				*data = (uint32_t)(vioscsi->max_xfer);
> +				res = (uint32_t)(vioscsi->max_xfer);
>  			else
>  				log_warnx("%s: unaligned read of max sectors "
>  				    "register", __func__);
>  			break;
>  		case VIRTIO_SCSI_CONFIG_CMD_PER_LUN:
>  			if (sz == 4)
> -				*data = (uint32_t)(VIOSCSI_CMD_PER_LUN);
> +				res = (uint32_t)(VIOSCSI_CMD_PER_LUN);
>  			else
>  				log_warnx("%s: unaligned read of cmd per lun "
>  				    "register", __func__);
>  			break;
>  		case VIRTIO_SCSI_CONFIG_EVENT_INFO_SIZE:
> -			*data = 0;
> +			res = 0;
>  			break;
>  		case VIRTIO_SCSI_CONFIG_SENSE_SIZE:
>  			if (sz == 4)
> -				*data = (uint32_t)(VIOSCSI_SENSE_LEN);
> +				res = (uint32_t)(VIOSCSI_SENSE_LEN);
>  			else
>  				log_warnx("%s: unaligned read of sense size "
>  				    "register", __func__);
>  			break;
>  		case VIRTIO_SCSI_CONFIG_CDB_SIZE:
>  			if (sz == 4)
> -				*data = (uint32_t)(VIOSCSI_CDB_LEN);
> +				res = (uint32_t)(VIOSCSI_CDB_LEN);
>  			else
>  				log_warnx("%s: unaligned read of cdb size "
>  				    "register", __func__);
>  			break;
>  		case VIRTIO_SCSI_CONFIG_MAX_CHANNEL:
>  			/* defined by standard to be zero */
> -			*data = 0;
> +			res = 0;
>  			break;
>  		case VIRTIO_SCSI_CONFIG_MAX_TARGET:
>  			if (sz == 2)
> -				*data = (uint32_t)(VIOSCSI_MAX_TARGET);
> +				res = (uint32_t)(VIOSCSI_MAX_TARGET);
>  			else
>  				log_warnx("%s: unaligned read of max target "
>  				    "register", __func__);
>  			break;
>  		case VIRTIO_SCSI_CONFIG_MAX_LUN:
>  			if (sz == 4)
> -				*data = (uint32_t)(VIOSCSI_MAX_LUN);
> +				res = (uint32_t)(VIOSCSI_MAX_LUN);
>  			else
>  				log_warnx("%s: unaligned read of max lun "
>  				    "register", __func__);
> @@ -1813,7 +2165,7 @@ vioscsi_io(int dir, uint16_t reg, uint32_t *data, uint
>  		}
>  	}
>
> -	return (0);
> +	return (res);
>  }
>
>  /*
> @@ -1826,7 +2178,7 @@ vioscsi_io(int dir, uint16_t reg, uint32_t *data, uint
>   * Return 1 if an interrupt should be generated (response written)
>   *        0 otherwise
>   */
> -int
> +static int
>  vioscsi_notifyq(struct virtio_dev *dev, uint16_t vq_idx)
>  {
>  	size_t cnt;
> blob - fa0845746230386b1dc3a9a3e7671bd243b21c92
> blob + 5083fa8d2c2246b0b3bebfb066c99b16cbea4edb
> --- usr.sbin/vmd/virtio.c
> +++ usr.sbin/vmd/virtio.c
> @@ -332,9 +332,7 @@ virtio_io_dispatch(int dir, uint16_t reg, uint32_t *da
>  		*data = virtio_io_cfg(dev, dir, actual, *data, sz);
>  		break;
>  	case VIO1_DEV_BAR_OFFSET:
> -		if (dev->device_id == PCI_PRODUCT_VIRTIO_SCSI)
> -			return vioscsi_io(dir, actual, data, intr, arg, sz);
> -		else if (dir == VEI_DIR_IN) {
> +		if (dir == VEI_DIR_IN) {
>  			log_debug("%s: no device specific handler", __func__);
>  			*data = (uint32_t)(-1);
>  		}
> @@ -643,7 +641,8 @@ virtio_io_isr(int dir, uint16_t reg, uint32_t *data, u
>
>  	/* Limit to in-process devices. */
>  	if (dev->device_id == PCI_PRODUCT_VIRTIO_BLOCK ||
> -	    dev->device_id == PCI_PRODUCT_VIRTIO_NETWORK)
> +	    dev->device_id == PCI_PRODUCT_VIRTIO_NETWORK ||
> +	    dev->device_id == PCI_PRODUCT_VIRTIO_SCSI)
>  		fatalx("%s: cannot use on multi-process virtio dev", __func__);
>
>  	if (dir == VEI_DIR_IN) {
> @@ -670,7 +669,8 @@ virtio_io_notify(int dir, uint16_t reg, uint32_t *data
>
>  	/* Limit this handler to in-process devices */
>  	if (dev->device_id == PCI_PRODUCT_VIRTIO_BLOCK ||
> -	    dev->device_id == PCI_PRODUCT_VIRTIO_NETWORK)
> +	    dev->device_id == PCI_PRODUCT_VIRTIO_NETWORK ||
> +	    dev->device_id == PCI_PRODUCT_VIRTIO_SCSI)
>  		fatalx("%s: cannot use on multi-process virtio dev", __func__);
>
>  	if (vq_idx >= dev->num_queues) {
> @@ -688,9 +688,6 @@ virtio_io_notify(int dir, uint16_t reg, uint32_t *data
>  	case PCI_PRODUCT_VIRTIO_ENTROPY:
>  		raise_intr = viornd_notifyq(dev, vq_idx);
>  		break;
> -	case PCI_PRODUCT_VIRTIO_SCSI:
> -		raise_intr = vioscsi_notifyq(dev, vq_idx);
> -		break;
>  	case PCI_PRODUCT_VIRTIO_VMMCI:
>  		/* Does not use a virtqueue. */
>  		break;
> @@ -1138,16 +1135,6 @@ virtio_init(struct vmd_vm *vm, int child_cdrom,
>  		}
>  	}
>
> -	/*
> -	 * Launch virtio devices that support subprocess execution.
> -	 */
> -	SLIST_FOREACH(dev, &virtio_devs, dev_next) {
> -		if (virtio_dev_launch(vm, dev) != 0) {
> -			log_warnx("failed to launch virtio device");
> -			return (1);
> -		}
> -	}
> -
>  	/* Virtio 1.x SCSI CD-ROM */
>  	if (strlen(vmc->vmc_cdrom)) {
>  		dev = malloc(sizeof(struct virtio_dev));
> @@ -1164,7 +1151,7 @@ virtio_init(struct vmd_vm *vm, int child_cdrom,
>  		}
>  		virtio_dev_init(vm, dev, id, VIOSCSI_QUEUE_SIZE_DEFAULT,
>  		    VIRTIO_SCSI_QUEUES, VIRTIO_F_VERSION_1);
> -		if (pci_add_bar(id, PCI_MAPREG_TYPE_IO, virtio_io_dispatch, dev)
> +		if (pci_add_bar(id, PCI_MAPREG_TYPE_IO, virtio_pci_io, dev)
>  		    == -1) {
>  			log_warnx("can't add bar for vioscsi device");
>  			return (1);
> @@ -1175,19 +1162,25 @@ virtio_init(struct vmd_vm *vm, int child_cdrom,
>  		virtio_pci_add_cap(id, VIRTIO_PCI_CAP_NOTIFY_CFG, bar_id, 0);
>
>  		/* Device specific initialization. */
> -		if (virtio_raw_init(&dev->vioscsi.file, &dev->vioscsi.sz,
> -		    &child_cdrom, 1) == -1) {
> -			log_warnx("%s: unable to determine iso format",
> -			    __func__);
> -			return (1);
> -		}
> +		dev->dev_type = VMD_DEVTYPE_SCSI;
> +		dev->vmm_id = vm->vm_vmmid;
> +		dev->vioscsi.cdrom_fd = child_cdrom;
>  		dev->vioscsi.locked = 0;
>  		dev->vioscsi.lba = 0;
> -		dev->vioscsi.n_blocks = dev->vioscsi.sz /
> -		    VIOSCSI_BLOCK_SIZE_CDROM;
>  		dev->vioscsi.max_xfer = VIOSCSI_BLOCK_SIZE_CDROM;
> +		SLIST_INSERT_HEAD(&virtio_devs, dev, dev_next);
>  	}
>
> +	/*
> +	 * Launch virtio devices that support subprocess execution.
> +	 */
> +	SLIST_FOREACH(dev, &virtio_devs, dev_next) {
> +		if (virtio_dev_launch(vm, dev) != 0) {
> +			log_warnx("failed to launch virtio device");
> +			return (1);
> +		}
> +	}
> +
>  	/* Virtio 0.9 VMM Control Interface */
>  	dev = &vmmci;
>  	if (pci_add_device(&id, PCI_VENDOR_OPENBSD, PCI_PRODUCT_OPENBSD_CONTROL,
> @@ -1483,6 +1476,9 @@ virtio_dev_launch(struct vmd_vm *vm, struct virtio_dev
>  		log_debug("%s: launching vioblk%d", vm->vm_params.vmc_name,
>  		    dev->vioblk.idx);
>  		break;
> +	case VMD_DEVTYPE_SCSI:
> +		log_debug("%s: launching vioscsi", vm->vm_params.vmc_name);
> +		break;
>  		/* NOTREACHED */
>  	default:
>  		log_warn("%s: invalid device type", __func__);
> @@ -1595,7 +1591,7 @@ virtio_dev_launch(struct vmd_vm *vm, struct virtio_dev
>  		close_fd(vm->vm_tty);
>  		vm->vm_tty = -1;
>
> -		if (vm->vm_cdrom != -1) {
> +		if (vm->vm_cdrom != -1 && dev->dev_type != VMD_DEVTYPE_SCSI) {
>  			close_fd(vm->vm_cdrom);
>  			vm->vm_cdrom = -1;
>  		}
> @@ -1917,6 +1913,10 @@ virtio_dev_closefds(struct virtio_dev *dev)
>  			close_fd(dev->vionet.data_fd);
>  			dev->vionet.data_fd = -1;
>  			break;
> +		case VMD_DEVTYPE_SCSI:
> +			close_fd(dev->vioscsi.cdrom_fd);
> +			dev->vioscsi.cdrom_fd = -1;
> +			break;
>  	default:
>  		log_warnx("%s: invalid device type", __func__);
>  		return (-1);
> blob - 026ad278a8c22b6e96516e383adf566a083943a5
> blob + eb9212ec7db57e0a565980fa7119a9192d692955
> --- usr.sbin/vmd/virtio.h
> +++ usr.sbin/vmd/virtio.h
> @@ -269,6 +269,7 @@ struct vioblk_dev {
>   */
>  struct vioscsi_dev {
>  	struct virtio_backing file;
> +	int cdrom_fd;		/* fd for iso file */
>
>  	int locked;		/* is the device locked? */
>  	uint64_t sz;		/* size of iso file in bytes */
> @@ -412,10 +413,6 @@ const char *vioblk_cmd_name(uint32_t);
>  /* dhcp.c */
>  ssize_t dhcp_request(struct virtio_dev *, char *, size_t, char **);
>
> -/* vioscsi.c */
> -int vioscsi_io(int, uint16_t, uint32_t *, uint8_t *, void *, uint8_t);
> -int vioscsi_notifyq(struct virtio_dev *, uint16_t);
> -
>  /* imsg handling */
>  void	viodev_msg_read(struct imsg *, struct viodev_msg *);
>  void	vionet_hostmac_read(struct imsg *, struct vionet_dev *);
> blob - de6b15d36231d4d9c3e85f1408a0a6c180d0ffca
> blob + 14a5d12b5ae4ea7a0793907e62661123e12fb90c
> --- usr.sbin/vmd/vmd.c
> +++ usr.sbin/vmd/vmd.c
> @@ -603,6 +603,7 @@ main(int argc, char **argv)
>  			switch (dev_type) {
>  			case VMD_DEVTYPE_NET:
>  			case VMD_DEVTYPE_DISK:
> +			case VMD_DEVTYPE_SCSI:
>  				break;
>  			default: fatalx("invalid device type");
>  			}
> @@ -673,6 +674,10 @@ main(int argc, char **argv)
>  			log_procinit("vm/%s/vioblk", title);
>  			vioblk_main(vm_fd, vmm_fd);
>  			/* NOTREACHED */
> +		} else if (dev_type == VMD_DEVTYPE_SCSI) {
> +			log_procinit("vm/%s/vioscsi", title);
> +			vioscsi_main(vm_fd, vmm_fd);
> +			/* NOTREACHED */
>  		}
>  		fatalx("unsupported device type '%c'", dev_type);
>  	}
> blob - 5ddfd9e4823fe00b29c0489da53503b68405571c
> blob + f7ac0d112ede43b417d185bc920e4f76bb778fcb
> --- usr.sbin/vmd/vmd.h
> +++ usr.sbin/vmd/vmd.h
> @@ -83,6 +83,7 @@
>
>  #define VMD_DEVTYPE_NET		'n'
>  #define VMD_DEVTYPE_DISK	'd'
> +#define VMD_DEVTYPE_SCSI	's'
>
>  /* Rate-limit fast reboots */
>  #define VM_START_RATE_SEC	6	/* min. seconds since last reboot */
> @@ -588,6 +589,8 @@ __dead void vionet_main(int, int);
>
>  /* vioblk.c */
>  __dead void vioblk_main(int, int);
> +/* vioscsi.c */
> +__dead void vioscsi_main(int, int);
>
>  /* psp.c */
>  int	 psp_get_pstate(uint16_t *, uint8_t *, uint8_t *, uint8_t *, uint8_t *);
>