Download raw body.
make vmd's vioscsi a child process
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 *);
>
make vmd's vioscsi a child process