/* Plug the VirtIODevice */ int virtio_bus_plug_device(VirtIODevice *vdev) { DeviceState *qdev = DEVICE(vdev); BusState *qbus = BUS(qdev_get_parent_bus(qdev)); VirtioBusState *bus = VIRTIO_BUS(qbus); VirtioBusClass *klass = VIRTIO_BUS_GET_CLASS(bus); DPRINTF("%s: plug device.\n", qbus->name); bus->vdev = vdev; /* * The lines below will disappear when we drop VirtIOBindings, at the end * of the series. */ bus->bindings.notify = klass->notify; bus->bindings.save_config = klass->save_config; bus->bindings.save_queue = klass->save_queue; bus->bindings.load_config = klass->load_config; bus->bindings.load_queue = klass->load_queue; bus->bindings.load_done = klass->load_done; bus->bindings.get_features = klass->get_features; bus->bindings.query_guest_notifiers = klass->query_guest_notifiers; bus->bindings.set_guest_notifiers = klass->set_guest_notifiers; bus->bindings.set_host_notifier = klass->set_host_notifier; bus->bindings.vmstate_change = klass->vmstate_change; virtio_bind_device(bus->vdev, &bus->bindings, qbus->parent); if (klass->device_plugged != NULL) { klass->device_plugged(qbus->parent); } return 0; }
static void vhost_net_stop_one(struct vhost_net *net, VirtIODevice *dev) { struct vhost_vring_file file = { .fd = -1 }; if (!net->dev.started) { return; } for (file.index = 0; file.index < net->dev.nvqs; ++file.index) { int r = ioctl(net->dev.control, VHOST_NET_SET_BACKEND, &file); assert(r >= 0); } net->nc->info->poll(net->nc, true); vhost_dev_stop(&net->dev, dev); vhost_dev_disable_notifiers(&net->dev, dev); } int vhost_net_start(VirtIODevice *dev, NetClientState *ncs, int total_queues) { BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(dev))); VirtioBusState *vbus = VIRTIO_BUS(qbus); VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(vbus); int r, i = 0; if (!k->set_guest_notifiers) { error_report("binding does not support guest notifiers"); r = -ENOSYS; goto err; } for (i = 0; i < total_queues; i++) { r = vhost_net_start_one(tap_get_vhost_net(ncs[i].peer), dev, i * 2); if (r < 0) { goto err; } } r = k->set_guest_notifiers(qbus->parent, total_queues * 2, true); if (r < 0) { error_report("Error binding guest notifier: %d", -r); goto err; } return 0; err: while (--i >= 0) { vhost_net_stop_one(tap_get_vhost_net(ncs[i].peer), dev); } return r; }
/* A VirtIODevice is being plugged */ int virtio_bus_device_plugged(VirtIODevice *vdev) { DeviceState *qdev = DEVICE(vdev); BusState *qbus = BUS(qdev_get_parent_bus(qdev)); VirtioBusState *bus = VIRTIO_BUS(qbus); VirtioBusClass *klass = VIRTIO_BUS_GET_CLASS(bus); DPRINTF("%s: plug device.\n", qbus->name); if (klass->device_plugged != NULL) { klass->device_plugged(qbus->parent); } return 0; }
/* Context: QEMU global mutex held */ void virtio_blk_data_plane_stop(VirtIOBlockDataPlane *s) { BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s->vdev))); VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); VirtIOBlock *vblk = VIRTIO_BLK(s->vdev); unsigned i; unsigned nvqs = s->conf->num_queues; if (!vblk->dataplane_started || s->stopping) { return; } /* Better luck next time. */ if (vblk->dataplane_disabled) { vblk->dataplane_disabled = false; vblk->dataplane_started = false; return; } s->stopping = true; trace_virtio_blk_data_plane_stop(s); aio_context_acquire(s->ctx); /* Stop notifications for new requests from guest */ for (i = 0; i < nvqs; i++) { VirtQueue *vq = virtio_get_queue(s->vdev, i); virtio_queue_aio_set_host_notifier_handler(vq, s->ctx, NULL); } /* Drain and switch bs back to the QEMU main loop */ blk_set_aio_context(s->conf->conf.blk, qemu_get_aio_context()); aio_context_release(s->ctx); for (i = 0; i < nvqs; i++) { virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), i, false); } /* Clean up guest notifier (irq) */ k->set_guest_notifiers(qbus->parent, nvqs, false); vblk->dataplane_started = false; s->stopping = false; }
static int virtio_scsi_vring_init(VirtIOSCSI *s, VirtQueue *vq, int n, void (*fn)(VirtIODevice *vdev, VirtQueue *vq)) { BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s))); int rc; /* Set up virtqueue notify */ rc = virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), n, true); if (rc != 0) { fprintf(stderr, "virtio-scsi: Failed to set host notifier (%d)\n", rc); s->dataplane_fenced = true; return rc; } virtio_queue_aio_set_host_notifier_handler(vq, s->ctx, fn); return 0; }
/* A VirtIODevice is being plugged */ void virtio_bus_device_plugged(VirtIODevice *vdev, Error **errp) { DeviceState *qdev = DEVICE(vdev); BusState *qbus = BUS(qdev_get_parent_bus(qdev)); VirtioBusState *bus = VIRTIO_BUS(qbus); VirtioBusClass *klass = VIRTIO_BUS_GET_CLASS(bus); VirtioDeviceClass *vdc = VIRTIO_DEVICE_GET_CLASS(vdev); bool has_iommu = virtio_host_has_feature(vdev, VIRTIO_F_IOMMU_PLATFORM); Error *local_err = NULL; DPRINTF("%s: plug device.\n", qbus->name); if (klass->pre_plugged != NULL) { klass->pre_plugged(qbus->parent, &local_err); if (local_err) { error_propagate(errp, local_err); return; } } /* Get the features of the plugged device. */ assert(vdc->get_features != NULL); vdev->host_features = vdc->get_features(vdev, vdev->host_features, &local_err); if (local_err) { error_propagate(errp, local_err); return; } if (klass->device_plugged != NULL) { klass->device_plugged(qbus->parent, &local_err); } if (local_err) { error_propagate(errp, local_err); return; } if (klass->get_dma_as != NULL && has_iommu) { virtio_add_feature(&vdev->host_features, VIRTIO_F_IOMMU_PLATFORM); vdev->dma_as = klass->get_dma_as(qbus->parent); } else { vdev->dma_as = &address_space_memory; } }
void vhost_net_stop(VirtIODevice *dev, NetClientState *ncs, int total_queues) { BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(dev))); VirtioBusState *vbus = VIRTIO_BUS(qbus); VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(vbus); int i, r; for (i = 0; i < total_queues; i++) { vhost_net_stop_one(get_vhost_net(ncs[i].peer), dev); } r = k->set_guest_notifiers(qbus->parent, total_queues * 2, false); if (r < 0) { fprintf(stderr, "vhost guest notifier cleanup failed: %d\n", r); fflush(stderr); } assert(r >= 0); }
/* A VirtIODevice is being plugged */ void virtio_bus_device_plugged(VirtIODevice *vdev, Error **errp) { DeviceState *qdev = DEVICE(vdev); BusState *qbus = BUS(qdev_get_parent_bus(qdev)); VirtioBusState *bus = VIRTIO_BUS(qbus); VirtioBusClass *klass = VIRTIO_BUS_GET_CLASS(bus); VirtioDeviceClass *vdc = VIRTIO_DEVICE_GET_CLASS(vdev); DPRINTF("%s: plug device.\n", qbus->name); if (klass->device_plugged != NULL) { klass->device_plugged(qbus->parent, errp); } /* Get the features of the plugged device. */ assert(vdc->get_features != NULL); vdev->host_features = vdc->get_features(vdev, vdev->host_features, errp); if (klass->post_plugged != NULL) { klass->post_plugged(qbus->parent, errp); } }
/* Context: QEMU global mutex held */ void virtio_scsi_dataplane_stop(VirtIOSCSI *s) { BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s))); VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s); int i; if (!s->dataplane_started || s->dataplane_stopping) { return; } /* Better luck next time. */ if (s->dataplane_fenced) { s->dataplane_fenced = false; s->dataplane_started = false; return; } s->dataplane_stopping = true; assert(s->ctx == iothread_get_aio_context(vs->conf.iothread)); aio_context_acquire(s->ctx); virtio_scsi_clear_aio(s); blk_drain_all(); /* ensure there are no in-flight requests */ aio_context_release(s->ctx); for (i = 0; i < vs->conf.num_queues + 2; i++) { virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), i, false); } /* Clean up guest notifier (irq) */ k->set_guest_notifiers(qbus->parent, vs->conf.num_queues + 2, false); s->dataplane_stopping = false; s->dataplane_started = false; }
static void vhost_net_stop_one(struct vhost_net *net, VirtIODevice *dev) { struct vhost_vring_file file = { .fd = -1 }; if (net->nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP) { for (file.index = 0; file.index < net->dev.nvqs; ++file.index) { const VhostOps *vhost_ops = net->dev.vhost_ops; int r = vhost_ops->vhost_call(&net->dev, VHOST_NET_SET_BACKEND, &file); assert(r >= 0); } } if (net->nc->info->poll) { net->nc->info->poll(net->nc, true); } vhost_dev_stop(&net->dev, dev); vhost_dev_disable_notifiers(&net->dev, dev); } static bool vhost_net_device_endian_ok(VirtIODevice *vdev) { #ifdef TARGET_IS_BIENDIAN #ifdef HOST_WORDS_BIGENDIAN return virtio_is_big_endian(vdev); #else return !virtio_is_big_endian(vdev); #endif #else return true; #endif } int vhost_net_start(VirtIODevice *dev, NetClientState *ncs, int total_queues) { BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(dev))); VirtioBusState *vbus = VIRTIO_BUS(qbus); VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(vbus); int r, e, i; if (!vhost_net_device_endian_ok(dev)) { error_report("vhost-net does not support cross-endian"); r = -ENOSYS; goto err; } if (!k->set_guest_notifiers) { error_report("binding does not support guest notifiers"); r = -ENOSYS; goto err; } for (i = 0; i < total_queues; i++) { vhost_net_set_vq_index(get_vhost_net(ncs[i].peer), i * 2); } r = k->set_guest_notifiers(qbus->parent, total_queues * 2, true); if (r < 0) { error_report("Error binding guest notifier: %d", -r); goto err; } for (i = 0; i < total_queues; i++) { r = vhost_net_start_one(get_vhost_net(ncs[i].peer), dev); if (r < 0) { goto err_start; } } return 0; err_start: while (--i >= 0) { vhost_net_stop_one(get_vhost_net(ncs[i].peer), dev); } e = k->set_guest_notifiers(qbus->parent, total_queues * 2, false); if (e < 0) { fprintf(stderr, "vhost guest notifier cleanup failed: %d\n", e); fflush(stderr); } err: return r; }
static void vhost_net_stop_one(struct vhost_net *net, VirtIODevice *dev) { struct vhost_vring_file file = { .fd = -1 }; if (net->nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP) { for (file.index = 0; file.index < net->dev.nvqs; ++file.index) { const VhostOps *vhost_ops = net->dev.vhost_ops; int r = vhost_ops->vhost_call(&net->dev, VHOST_NET_SET_BACKEND, &file); assert(r >= 0); } } if (net->nc->info->poll) { net->nc->info->poll(net->nc, true); } vhost_dev_stop(&net->dev, dev); vhost_dev_disable_notifiers(&net->dev, dev); } int vhost_net_start(VirtIODevice *dev, NetClientState *ncs, int total_queues) { BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(dev))); VirtioBusState *vbus = VIRTIO_BUS(qbus); VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(vbus); int r, e, i; if (!k->set_guest_notifiers) { error_report("binding does not support guest notifiers"); r = -ENOSYS; goto err; } r = vhost_net_set_vnet_endian(dev, ncs[0].peer, true); if (r < 0) { goto err; } for (i = 0; i < total_queues; i++) { struct vhost_net *net; net = get_vhost_net(ncs[i].peer); vhost_net_set_vq_index(net, i * 2); /* Suppress the masking guest notifiers on vhost user * because vhost user doesn't interrupt masking/unmasking * properly. */ if (net->nc->info->type == NET_CLIENT_OPTIONS_KIND_VHOST_USER) { dev->use_guest_notifier_mask = false; } } r = k->set_guest_notifiers(qbus->parent, total_queues * 2, true); if (r < 0) { error_report("Error binding guest notifier: %d", -r); goto err_endian; } for (i = 0; i < total_queues; i++) { r = vhost_net_start_one(get_vhost_net(ncs[i].peer), dev); if (r < 0) { goto err_start; } if (ncs[i].peer->vring_enable) { /* restore vring enable state */ r = vhost_set_vring_enable(ncs[i].peer, ncs[i].peer->vring_enable); if (r < 0) { goto err_start; } } } return 0; err_start: while (--i >= 0) { vhost_net_stop_one(get_vhost_net(ncs[i].peer), dev); } e = k->set_guest_notifiers(qbus->parent, total_queues * 2, false); if (e < 0) { fprintf(stderr, "vhost guest notifier cleanup failed: %d\n", e); fflush(stderr); } err_endian: vhost_net_set_vnet_endian(dev, ncs[0].peer, false); err: return r; }
/* Context: QEMU global mutex held */ void virtio_blk_data_plane_start(VirtIOBlockDataPlane *s) { BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s->vdev))); VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); VirtIOBlock *vblk = VIRTIO_BLK(s->vdev); unsigned i; unsigned nvqs = s->conf->num_queues; int r; if (vblk->dataplane_started || s->starting) { return; } s->starting = true; /* Set up guest notifier (irq) */ r = k->set_guest_notifiers(qbus->parent, nvqs, true); if (r != 0) { fprintf(stderr, "virtio-blk failed to set guest notifier (%d), " "ensure -enable-kvm is set\n", r); goto fail_guest_notifiers; } /* Set up virtqueue notify */ for (i = 0; i < nvqs; i++) { r = virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), i, true); if (r != 0) { fprintf(stderr, "virtio-blk failed to set host notifier (%d)\n", r); while (i--) { virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), i, false); } goto fail_guest_notifiers; } } s->starting = false; vblk->dataplane_started = true; trace_virtio_blk_data_plane_start(s); blk_set_aio_context(s->conf->conf.blk, s->ctx); /* Kick right away to begin processing requests already in vring */ for (i = 0; i < nvqs; i++) { VirtQueue *vq = virtio_get_queue(s->vdev, i); event_notifier_set(virtio_queue_get_host_notifier(vq)); } /* Get this show started by hooking up our callbacks */ aio_context_acquire(s->ctx); for (i = 0; i < nvqs; i++) { VirtQueue *vq = virtio_get_queue(s->vdev, i); virtio_queue_aio_set_host_notifier_handler(vq, s->ctx, virtio_blk_data_plane_handle_output); } aio_context_release(s->ctx); return; fail_guest_notifiers: vblk->dataplane_disabled = true; s->starting = false; vblk->dataplane_started = true; }
static void vhost_net_stop_one(struct vhost_net *net, VirtIODevice *dev) { struct vhost_vring_file file = { .fd = -1 }; if (net->nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP) { for (file.index = 0; file.index < net->dev.nvqs; ++file.index) { const VhostOps *vhost_ops = net->dev.vhost_ops; int r = vhost_ops->vhost_net_set_backend(&net->dev, &file); assert(r >= 0); } } if (net->nc->info->poll) { net->nc->info->poll(net->nc, true); } vhost_dev_stop(&net->dev, dev); vhost_dev_disable_notifiers(&net->dev, dev); } int vhost_net_start(VirtIODevice *dev, NetClientState *ncs, int total_queues) { BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(dev))); VirtioBusState *vbus = VIRTIO_BUS(qbus); VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(vbus); int r, e, i, j; if (!k->set_guest_notifiers) { error_report("binding does not support guest notifiers"); return -ENOSYS; } for (j = 0; j < total_queues; j++) { r = vhost_net_set_vnet_endian(dev, ncs[j].peer, true); if (r < 0) { goto err_endian; } vhost_net_set_vq_index(get_vhost_net(ncs[j].peer), j * 2); } r = k->set_guest_notifiers(qbus->parent, total_queues * 2, true); if (r < 0) { error_report("Error binding guest notifier: %d", -r); goto err_endian; } for (i = 0; i < total_queues; i++) { r = vhost_net_start_one(get_vhost_net(ncs[i].peer), dev); if (r < 0) { goto err_start; } } return 0; err_start: while (--i >= 0) { vhost_net_stop_one(get_vhost_net(ncs[i].peer), dev); } e = k->set_guest_notifiers(qbus->parent, total_queues * 2, false); if (e < 0) { fprintf(stderr, "vhost guest notifier cleanup failed: %d\n", e); fflush(stderr); } err_endian: while (--j >= 0) { vhost_net_set_vnet_endian(dev, ncs[j].peer, false); } return r; }
/* Context: QEMU global mutex held */ void virtio_scsi_dataplane_start(VirtIOSCSI *s) { int i; int rc; BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s))); VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s); if (s->dataplane_started || s->dataplane_starting || s->dataplane_fenced || s->ctx != iothread_get_aio_context(vs->conf.iothread)) { return; } s->dataplane_starting = true; /* Set up guest notifier (irq) */ rc = k->set_guest_notifiers(qbus->parent, vs->conf.num_queues + 2, true); if (rc != 0) { fprintf(stderr, "virtio-scsi: Failed to set guest notifiers (%d), " "ensure -enable-kvm is set\n", rc); goto fail_guest_notifiers; } aio_context_acquire(s->ctx); rc = virtio_scsi_vring_init(s, vs->ctrl_vq, 0, virtio_scsi_data_plane_handle_ctrl); if (rc) { goto fail_vrings; } rc = virtio_scsi_vring_init(s, vs->event_vq, 1, virtio_scsi_data_plane_handle_event); if (rc) { goto fail_vrings; } for (i = 0; i < vs->conf.num_queues; i++) { rc = virtio_scsi_vring_init(s, vs->cmd_vqs[i], i + 2, virtio_scsi_data_plane_handle_cmd); if (rc) { goto fail_vrings; } } s->dataplane_starting = false; s->dataplane_started = true; aio_context_release(s->ctx); return; fail_vrings: virtio_scsi_clear_aio(s); aio_context_release(s->ctx); for (i = 0; i < vs->conf.num_queues + 2; i++) { virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), i, false); } k->set_guest_notifiers(qbus->parent, vs->conf.num_queues + 2, false); fail_guest_notifiers: s->dataplane_fenced = true; s->dataplane_starting = false; s->dataplane_started = true; }