Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
hw/virtio: Add boilerplate for vhost-user-scmi device
This creates the QEMU side of the vhost-user-scmi device which connects to the remote daemon. It is based on code of similar vhost-user devices. Signed-off-by: Milan Zamazal <mzamazal@redhat.com> Message-Id: <20230628100524.342666-2-mzamazal@redhat.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
- Loading branch information
Showing
5 changed files
with
348 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,306 @@ | ||
| /* | ||
| * Vhost-user SCMI virtio device | ||
| * | ||
| * SPDX-FileCopyrightText: Red Hat, Inc. | ||
| * SPDX-License-Identifier: GPL-2.0-or-later | ||
| * | ||
| * Implementation based on other vhost-user devices in QEMU. | ||
| */ | ||
|
|
||
| #include "qemu/osdep.h" | ||
| #include "qapi/error.h" | ||
| #include "qemu/error-report.h" | ||
| #include "hw/virtio/virtio-bus.h" | ||
| #include "hw/virtio/vhost-user-scmi.h" | ||
| #include "standard-headers/linux/virtio_ids.h" | ||
| #include "standard-headers/linux/virtio_scmi.h" | ||
| #include "trace.h" | ||
|
|
||
| /* | ||
| * In this version, we don't support VIRTIO_SCMI_F_SHARED_MEMORY. | ||
| * Note that VIRTIO_SCMI_F_SHARED_MEMORY is currently not supported in | ||
| * Linux VirtIO SCMI guest driver. | ||
| */ | ||
| static const int feature_bits[] = { | ||
| VIRTIO_F_VERSION_1, | ||
| VIRTIO_F_NOTIFY_ON_EMPTY, | ||
| VIRTIO_RING_F_INDIRECT_DESC, | ||
| VIRTIO_RING_F_EVENT_IDX, | ||
| VIRTIO_F_RING_RESET, | ||
| VIRTIO_SCMI_F_P2A_CHANNELS, | ||
| VHOST_INVALID_FEATURE_BIT | ||
| }; | ||
|
|
||
| static int vu_scmi_start(VirtIODevice *vdev) | ||
| { | ||
| VHostUserSCMI *scmi = VHOST_USER_SCMI(vdev); | ||
| BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev))); | ||
| VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); | ||
| struct vhost_dev *vhost_dev = &scmi->vhost_dev; | ||
| int ret, i; | ||
|
|
||
| if (!k->set_guest_notifiers) { | ||
| error_report("binding does not support guest notifiers"); | ||
| return -ENOSYS; | ||
| } | ||
|
|
||
| ret = vhost_dev_enable_notifiers(vhost_dev, vdev); | ||
| if (ret < 0) { | ||
| error_report("Error enabling host notifiers: %d", ret); | ||
| return ret; | ||
| } | ||
|
|
||
| ret = k->set_guest_notifiers(qbus->parent, vhost_dev->nvqs, true); | ||
| if (ret < 0) { | ||
| error_report("Error binding guest notifier: %d", ret); | ||
| goto err_host_notifiers; | ||
| } | ||
|
|
||
| vhost_ack_features(&scmi->vhost_dev, feature_bits, vdev->guest_features); | ||
|
|
||
| ret = vhost_dev_start(&scmi->vhost_dev, vdev, true); | ||
| if (ret < 0) { | ||
| error_report("Error starting vhost-user-scmi: %d", ret); | ||
| goto err_guest_notifiers; | ||
| } | ||
|
|
||
| /* | ||
| * guest_notifier_mask/pending not used yet, so just unmask | ||
| * everything here. virtio-pci will do the right thing by | ||
| * enabling/disabling irqfd. | ||
| */ | ||
| for (i = 0; i < scmi->vhost_dev.nvqs; i++) { | ||
| vhost_virtqueue_mask(&scmi->vhost_dev, vdev, i, false); | ||
| } | ||
| return 0; | ||
|
|
||
| err_guest_notifiers: | ||
| k->set_guest_notifiers(qbus->parent, vhost_dev->nvqs, false); | ||
| err_host_notifiers: | ||
| vhost_dev_disable_notifiers(vhost_dev, vdev); | ||
|
|
||
| return ret; | ||
| } | ||
|
|
||
| static void vu_scmi_stop(VirtIODevice *vdev) | ||
| { | ||
| VHostUserSCMI *scmi = VHOST_USER_SCMI(vdev); | ||
| BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev))); | ||
| VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); | ||
| struct vhost_dev *vhost_dev = &scmi->vhost_dev; | ||
| int ret; | ||
|
|
||
| if (!k->set_guest_notifiers) { | ||
| return; | ||
| } | ||
|
|
||
| vhost_dev_stop(vhost_dev, vdev, true); | ||
|
|
||
| ret = k->set_guest_notifiers(qbus->parent, vhost_dev->nvqs, false); | ||
| if (ret < 0) { | ||
| error_report("vhost guest notifier cleanup failed: %d", ret); | ||
| return; | ||
| } | ||
| vhost_dev_disable_notifiers(vhost_dev, vdev); | ||
| } | ||
|
|
||
| static void vu_scmi_set_status(VirtIODevice *vdev, uint8_t status) | ||
| { | ||
| VHostUserSCMI *scmi = VHOST_USER_SCMI(vdev); | ||
| bool should_start = virtio_device_should_start(vdev, status); | ||
|
|
||
| if (!scmi->connected) { | ||
| return; | ||
| } | ||
| if (vhost_dev_is_started(&scmi->vhost_dev) == should_start) { | ||
| return; | ||
| } | ||
|
|
||
| if (should_start) { | ||
| vu_scmi_start(vdev); | ||
| } else { | ||
| vu_scmi_stop(vdev); | ||
| } | ||
| } | ||
|
|
||
| static uint64_t vu_scmi_get_features(VirtIODevice *vdev, uint64_t features, | ||
| Error **errp) | ||
| { | ||
| VHostUserSCMI *scmi = VHOST_USER_SCMI(vdev); | ||
|
|
||
| return vhost_get_features(&scmi->vhost_dev, feature_bits, features); | ||
| } | ||
|
|
||
| static void vu_scmi_handle_output(VirtIODevice *vdev, VirtQueue *vq) | ||
| { | ||
| /* | ||
| * Not normally called; it's the daemon that handles the queue; | ||
| * however virtio's cleanup path can call this. | ||
| */ | ||
| } | ||
|
|
||
| static void vu_scmi_guest_notifier_mask(VirtIODevice *vdev, int idx, bool mask) | ||
| { | ||
| VHostUserSCMI *scmi = VHOST_USER_SCMI(vdev); | ||
|
|
||
| if (idx == VIRTIO_CONFIG_IRQ_IDX) { | ||
| return; | ||
| } | ||
|
|
||
| vhost_virtqueue_mask(&scmi->vhost_dev, vdev, idx, mask); | ||
| } | ||
|
|
||
| static bool vu_scmi_guest_notifier_pending(VirtIODevice *vdev, int idx) | ||
| { | ||
| VHostUserSCMI *scmi = VHOST_USER_SCMI(vdev); | ||
|
|
||
| return vhost_virtqueue_pending(&scmi->vhost_dev, idx); | ||
| } | ||
|
|
||
| static void vu_scmi_connect(DeviceState *dev) | ||
| { | ||
| VirtIODevice *vdev = VIRTIO_DEVICE(dev); | ||
| VHostUserSCMI *scmi = VHOST_USER_SCMI(vdev); | ||
|
|
||
| if (scmi->connected) { | ||
| return; | ||
| } | ||
| scmi->connected = true; | ||
|
|
||
| /* restore vhost state */ | ||
| if (virtio_device_started(vdev, vdev->status)) { | ||
| vu_scmi_start(vdev); | ||
| } | ||
| } | ||
|
|
||
| static void vu_scmi_disconnect(DeviceState *dev) | ||
| { | ||
| VirtIODevice *vdev = VIRTIO_DEVICE(dev); | ||
| VHostUserSCMI *scmi = VHOST_USER_SCMI(vdev); | ||
|
|
||
| if (!scmi->connected) { | ||
| return; | ||
| } | ||
| scmi->connected = false; | ||
|
|
||
| if (vhost_dev_is_started(&scmi->vhost_dev)) { | ||
| vu_scmi_stop(vdev); | ||
| } | ||
| } | ||
|
|
||
| static void vu_scmi_event(void *opaque, QEMUChrEvent event) | ||
| { | ||
| DeviceState *dev = opaque; | ||
|
|
||
| switch (event) { | ||
| case CHR_EVENT_OPENED: | ||
| vu_scmi_connect(dev); | ||
| break; | ||
| case CHR_EVENT_CLOSED: | ||
| vu_scmi_disconnect(dev); | ||
| break; | ||
| case CHR_EVENT_BREAK: | ||
| case CHR_EVENT_MUX_IN: | ||
| case CHR_EVENT_MUX_OUT: | ||
| /* Ignore */ | ||
| break; | ||
| } | ||
| } | ||
|
|
||
| static void do_vhost_user_cleanup(VirtIODevice *vdev, VHostUserSCMI *scmi) | ||
| { | ||
| virtio_delete_queue(scmi->cmd_vq); | ||
| virtio_delete_queue(scmi->event_vq); | ||
| g_free(scmi->vhost_dev.vqs); | ||
| virtio_cleanup(vdev); | ||
| vhost_user_cleanup(&scmi->vhost_user); | ||
| } | ||
|
|
||
| static void vu_scmi_device_realize(DeviceState *dev, Error **errp) | ||
| { | ||
| VirtIODevice *vdev = VIRTIO_DEVICE(dev); | ||
| VHostUserSCMI *scmi = VHOST_USER_SCMI(dev); | ||
| int ret; | ||
|
|
||
| if (!scmi->chardev.chr) { | ||
| error_setg(errp, "vhost-user-scmi: chardev is mandatory"); | ||
| return; | ||
| } | ||
|
|
||
| vdev->host_features |= (1ULL << VIRTIO_SCMI_F_P2A_CHANNELS); | ||
|
|
||
| if (!vhost_user_init(&scmi->vhost_user, &scmi->chardev, errp)) { | ||
| return; | ||
| } | ||
|
|
||
| virtio_init(vdev, VIRTIO_ID_SCMI, 0); | ||
|
|
||
| scmi->cmd_vq = virtio_add_queue(vdev, 256, vu_scmi_handle_output); | ||
| scmi->event_vq = virtio_add_queue(vdev, 256, vu_scmi_handle_output); | ||
| scmi->vhost_dev.nvqs = 2; | ||
| scmi->vhost_dev.vqs = g_new0(struct vhost_virtqueue, scmi->vhost_dev.nvqs); | ||
|
|
||
| ret = vhost_dev_init(&scmi->vhost_dev, &scmi->vhost_user, | ||
| VHOST_BACKEND_TYPE_USER, 0, errp); | ||
| if (ret < 0) { | ||
| error_setg_errno(errp, -ret, | ||
| "vhost-user-scmi: vhost_dev_init() failed"); | ||
| do_vhost_user_cleanup(vdev, scmi); | ||
| return; | ||
| } | ||
|
|
||
| qemu_chr_fe_set_handlers(&scmi->chardev, NULL, NULL, vu_scmi_event, NULL, | ||
| dev, NULL, true); | ||
|
|
||
| return; | ||
| } | ||
|
|
||
| static void vu_scmi_device_unrealize(DeviceState *dev) | ||
| { | ||
| VirtIODevice *vdev = VIRTIO_DEVICE(dev); | ||
| VHostUserSCMI *scmi = VHOST_USER_SCMI(dev); | ||
|
|
||
| vu_scmi_set_status(vdev, 0); | ||
| vhost_dev_cleanup(&scmi->vhost_dev); | ||
| do_vhost_user_cleanup(vdev, scmi); | ||
| } | ||
|
|
||
| static const VMStateDescription vu_scmi_vmstate = { | ||
| .name = "vhost-user-scmi", | ||
| .unmigratable = 1, | ||
| }; | ||
|
|
||
| static Property vu_scmi_properties[] = { | ||
| DEFINE_PROP_CHR("chardev", VHostUserSCMI, chardev), | ||
| DEFINE_PROP_END_OF_LIST(), | ||
| }; | ||
|
|
||
| static void vu_scmi_class_init(ObjectClass *klass, void *data) | ||
| { | ||
| DeviceClass *dc = DEVICE_CLASS(klass); | ||
| VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); | ||
|
|
||
| device_class_set_props(dc, vu_scmi_properties); | ||
| dc->vmsd = &vu_scmi_vmstate; | ||
| set_bit(DEVICE_CATEGORY_INPUT, dc->categories); | ||
| vdc->realize = vu_scmi_device_realize; | ||
| vdc->unrealize = vu_scmi_device_unrealize; | ||
| vdc->get_features = vu_scmi_get_features; | ||
| vdc->set_status = vu_scmi_set_status; | ||
| vdc->guest_notifier_mask = vu_scmi_guest_notifier_mask; | ||
| vdc->guest_notifier_pending = vu_scmi_guest_notifier_pending; | ||
| } | ||
|
|
||
| static const TypeInfo vu_scmi_info = { | ||
| .name = TYPE_VHOST_USER_SCMI, | ||
| .parent = TYPE_VIRTIO_DEVICE, | ||
| .instance_size = sizeof(VHostUserSCMI), | ||
| .class_init = vu_scmi_class_init, | ||
| }; | ||
|
|
||
| static void vu_scmi_register_types(void) | ||
| { | ||
| type_register_static(&vu_scmi_info); | ||
| } | ||
|
|
||
| type_init(vu_scmi_register_types) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| /* | ||
| * Vhost-user SCMI virtio device | ||
| * | ||
| * Copyright (c) 2023 Red Hat, Inc. | ||
| * | ||
| * SPDX-License-Identifier: GPL-2.0-or-later | ||
| */ | ||
|
|
||
| #ifndef _QEMU_VHOST_USER_SCMI_H | ||
| #define _QEMU_VHOST_USER_SCMI_H | ||
|
|
||
| #include "hw/virtio/virtio.h" | ||
| #include "hw/virtio/vhost.h" | ||
| #include "hw/virtio/vhost-user.h" | ||
|
|
||
| #define TYPE_VHOST_USER_SCMI "vhost-user-scmi" | ||
| OBJECT_DECLARE_SIMPLE_TYPE(VHostUserSCMI, VHOST_USER_SCMI); | ||
|
|
||
| struct VHostUserSCMI { | ||
| VirtIODevice parent; | ||
| CharBackend chardev; | ||
| struct vhost_virtqueue *vhost_vqs; | ||
| struct vhost_dev vhost_dev; | ||
| VhostUserState vhost_user; | ||
| VirtQueue *cmd_vq; | ||
| VirtQueue *event_vq; | ||
| bool connected; | ||
| }; | ||
|
|
||
| #endif /* _QEMU_VHOST_USER_SCMI_H */ |