Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
vhost-user-rng: Add vhost-user-rng implementation
Introduce a random number generator (RNG) backend that communicates with a vhost-user server to retrieve entropy. That way other VMM that comply with the vhost user protocl can use the same vhost-user daemon without having to write yet another RNG driver. Reviewed-by: Alex Bennée <alex.bennee@linaro.org> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org> Message-Id: <20211012205904.4106769-2-mathieu.poirier@linaro.org> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
- Loading branch information
1 parent
6889eb2
commit 821d28b
Showing
4 changed files
with
328 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,289 @@ | ||
/* | ||
* Vhost-user RNG virtio device | ||
* | ||
* Copyright (c) 2021 Mathieu Poirier <mathieu.poirier@linaro.org> | ||
* | ||
* Implementation seriously tailored on vhost-user-i2c.c | ||
* | ||
* SPDX-License-Identifier: GPL-2.0-or-later | ||
*/ | ||
|
||
#include "qemu/osdep.h" | ||
#include "qapi/error.h" | ||
#include "hw/qdev-properties.h" | ||
#include "hw/virtio/virtio-bus.h" | ||
#include "hw/virtio/vhost-user-rng.h" | ||
#include "qemu/error-report.h" | ||
#include "standard-headers/linux/virtio_ids.h" | ||
|
||
static void vu_rng_start(VirtIODevice *vdev) | ||
{ | ||
VHostUserRNG *rng = VHOST_USER_RNG(vdev); | ||
BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev))); | ||
VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); | ||
int ret; | ||
int i; | ||
|
||
if (!k->set_guest_notifiers) { | ||
error_report("binding does not support guest notifiers"); | ||
return; | ||
} | ||
|
||
ret = vhost_dev_enable_notifiers(&rng->vhost_dev, vdev); | ||
if (ret < 0) { | ||
error_report("Error enabling host notifiers: %d", -ret); | ||
return; | ||
} | ||
|
||
ret = k->set_guest_notifiers(qbus->parent, rng->vhost_dev.nvqs, true); | ||
if (ret < 0) { | ||
error_report("Error binding guest notifier: %d", -ret); | ||
goto err_host_notifiers; | ||
} | ||
|
||
rng->vhost_dev.acked_features = vdev->guest_features; | ||
ret = vhost_dev_start(&rng->vhost_dev, vdev); | ||
if (ret < 0) { | ||
error_report("Error starting vhost-user-rng: %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 < rng->vhost_dev.nvqs; i++) { | ||
vhost_virtqueue_mask(&rng->vhost_dev, vdev, i, false); | ||
} | ||
|
||
return; | ||
|
||
err_guest_notifiers: | ||
k->set_guest_notifiers(qbus->parent, rng->vhost_dev.nvqs, false); | ||
err_host_notifiers: | ||
vhost_dev_disable_notifiers(&rng->vhost_dev, vdev); | ||
} | ||
|
||
static void vu_rng_stop(VirtIODevice *vdev) | ||
{ | ||
VHostUserRNG *rng = VHOST_USER_RNG(vdev); | ||
BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev))); | ||
VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); | ||
int ret; | ||
|
||
if (!k->set_guest_notifiers) { | ||
return; | ||
} | ||
|
||
vhost_dev_stop(&rng->vhost_dev, vdev); | ||
|
||
ret = k->set_guest_notifiers(qbus->parent, rng->vhost_dev.nvqs, false); | ||
if (ret < 0) { | ||
error_report("vhost guest notifier cleanup failed: %d", ret); | ||
return; | ||
} | ||
|
||
vhost_dev_disable_notifiers(&rng->vhost_dev, vdev); | ||
} | ||
|
||
static void vu_rng_set_status(VirtIODevice *vdev, uint8_t status) | ||
{ | ||
VHostUserRNG *rng = VHOST_USER_RNG(vdev); | ||
bool should_start = status & VIRTIO_CONFIG_S_DRIVER_OK; | ||
|
||
if (!vdev->vm_running) { | ||
should_start = false; | ||
} | ||
|
||
if (rng->vhost_dev.started == should_start) { | ||
return; | ||
} | ||
|
||
if (should_start) { | ||
vu_rng_start(vdev); | ||
} else { | ||
vu_rng_stop(vdev); | ||
} | ||
} | ||
|
||
static uint64_t vu_rng_get_features(VirtIODevice *vdev, | ||
uint64_t requested_features, Error **errp) | ||
{ | ||
/* No feature bits used yet */ | ||
return requested_features; | ||
} | ||
|
||
static void vu_rng_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_rng_guest_notifier_mask(VirtIODevice *vdev, int idx, bool mask) | ||
{ | ||
VHostUserRNG *rng = VHOST_USER_RNG(vdev); | ||
|
||
vhost_virtqueue_mask(&rng->vhost_dev, vdev, idx, mask); | ||
} | ||
|
||
static bool vu_rng_guest_notifier_pending(VirtIODevice *vdev, int idx) | ||
{ | ||
VHostUserRNG *rng = VHOST_USER_RNG(vdev); | ||
|
||
return vhost_virtqueue_pending(&rng->vhost_dev, idx); | ||
} | ||
|
||
static void vu_rng_connect(DeviceState *dev) | ||
{ | ||
VirtIODevice *vdev = VIRTIO_DEVICE(dev); | ||
VHostUserRNG *rng = VHOST_USER_RNG(vdev); | ||
|
||
if (rng->connected) { | ||
return; | ||
} | ||
|
||
rng->connected = true; | ||
|
||
/* restore vhost state */ | ||
if (virtio_device_started(vdev, vdev->status)) { | ||
vu_rng_start(vdev); | ||
} | ||
} | ||
|
||
static void vu_rng_disconnect(DeviceState *dev) | ||
{ | ||
VirtIODevice *vdev = VIRTIO_DEVICE(dev); | ||
VHostUserRNG *rng = VHOST_USER_RNG(vdev); | ||
|
||
if (!rng->connected) { | ||
return; | ||
} | ||
|
||
rng->connected = false; | ||
|
||
if (rng->vhost_dev.started) { | ||
vu_rng_stop(vdev); | ||
} | ||
} | ||
|
||
static void vu_rng_event(void *opaque, QEMUChrEvent event) | ||
{ | ||
DeviceState *dev = opaque; | ||
|
||
switch (event) { | ||
case CHR_EVENT_OPENED: | ||
vu_rng_connect(dev); | ||
break; | ||
case CHR_EVENT_CLOSED: | ||
vu_rng_disconnect(dev); | ||
break; | ||
case CHR_EVENT_BREAK: | ||
case CHR_EVENT_MUX_IN: | ||
case CHR_EVENT_MUX_OUT: | ||
/* Ignore */ | ||
break; | ||
} | ||
} | ||
|
||
static void vu_rng_device_realize(DeviceState *dev, Error **errp) | ||
{ | ||
VirtIODevice *vdev = VIRTIO_DEVICE(dev); | ||
VHostUserRNG *rng = VHOST_USER_RNG(dev); | ||
int ret; | ||
|
||
if (!rng->chardev.chr) { | ||
error_setg(errp, "missing chardev"); | ||
return; | ||
} | ||
|
||
if (!vhost_user_init(&rng->vhost_user, &rng->chardev, errp)) { | ||
return; | ||
} | ||
|
||
virtio_init(vdev, "vhost-user-rng", VIRTIO_ID_RNG, 0); | ||
|
||
rng->req_vq = virtio_add_queue(vdev, 4, vu_rng_handle_output); | ||
if (!rng->req_vq) { | ||
error_setg_errno(errp, -1, "virtio_add_queue() failed"); | ||
goto virtio_add_queue_failed; | ||
} | ||
|
||
rng->vhost_dev.nvqs = 1; | ||
rng->vhost_dev.vqs = g_new0(struct vhost_virtqueue, rng->vhost_dev.nvqs); | ||
ret = vhost_dev_init(&rng->vhost_dev, &rng->vhost_user, | ||
VHOST_BACKEND_TYPE_USER, 0, errp); | ||
if (ret < 0) { | ||
error_setg_errno(errp, -ret, "vhost_dev_init() failed"); | ||
goto vhost_dev_init_failed; | ||
} | ||
|
||
qemu_chr_fe_set_handlers(&rng->chardev, NULL, NULL, vu_rng_event, NULL, | ||
dev, NULL, true); | ||
|
||
return; | ||
|
||
vhost_dev_init_failed: | ||
virtio_delete_queue(rng->req_vq); | ||
virtio_add_queue_failed: | ||
virtio_cleanup(vdev); | ||
vhost_user_cleanup(&rng->vhost_user); | ||
} | ||
|
||
static void vu_rng_device_unrealize(DeviceState *dev) | ||
{ | ||
VirtIODevice *vdev = VIRTIO_DEVICE(dev); | ||
VHostUserRNG *rng = VHOST_USER_RNG(dev); | ||
|
||
vu_rng_set_status(vdev, 0); | ||
|
||
vhost_dev_cleanup(&rng->vhost_dev); | ||
g_free(rng->vhost_dev.vqs); | ||
rng->vhost_dev.vqs = NULL; | ||
virtio_delete_queue(rng->req_vq); | ||
virtio_cleanup(vdev); | ||
vhost_user_cleanup(&rng->vhost_user); | ||
} | ||
|
||
static const VMStateDescription vu_rng_vmstate = { | ||
.name = "vhost-user-rng", | ||
.unmigratable = 1, | ||
}; | ||
|
||
static Property vu_rng_properties[] = { | ||
DEFINE_PROP_CHR("chardev", VHostUserRNG, chardev), | ||
DEFINE_PROP_END_OF_LIST(), | ||
}; | ||
|
||
static void vu_rng_class_init(ObjectClass *klass, void *data) | ||
{ | ||
DeviceClass *dc = DEVICE_CLASS(klass); | ||
VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); | ||
|
||
device_class_set_props(dc, vu_rng_properties); | ||
dc->vmsd = &vu_rng_vmstate; | ||
set_bit(DEVICE_CATEGORY_INPUT, dc->categories); | ||
|
||
vdc->realize = vu_rng_device_realize; | ||
vdc->unrealize = vu_rng_device_unrealize; | ||
vdc->get_features = vu_rng_get_features; | ||
vdc->set_status = vu_rng_set_status; | ||
vdc->guest_notifier_mask = vu_rng_guest_notifier_mask; | ||
vdc->guest_notifier_pending = vu_rng_guest_notifier_pending; | ||
} | ||
|
||
static const TypeInfo vu_rng_info = { | ||
.name = TYPE_VHOST_USER_RNG, | ||
.parent = TYPE_VIRTIO_DEVICE, | ||
.instance_size = sizeof(VHostUserRNG), | ||
.class_init = vu_rng_class_init, | ||
}; | ||
|
||
static void vu_rng_register_types(void) | ||
{ | ||
type_register_static(&vu_rng_info); | ||
} | ||
|
||
type_init(vu_rng_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,33 @@ | ||
/* | ||
* Vhost-user RNG virtio device | ||
* | ||
* Copyright (c) 2021 Mathieu Poirier <mathieu.poirier@linaro.org> | ||
* | ||
* SPDX-License-Identifier: GPL-2.0-or-later | ||
*/ | ||
|
||
#ifndef _QEMU_VHOST_USER_RNG_H | ||
#define _QEMU_VHOST_USER_RNG_H | ||
|
||
#include "hw/virtio/virtio.h" | ||
#include "hw/virtio/vhost.h" | ||
#include "hw/virtio/vhost-user.h" | ||
#include "chardev/char-fe.h" | ||
|
||
#define TYPE_VHOST_USER_RNG "vhost-user-rng" | ||
OBJECT_DECLARE_SIMPLE_TYPE(VHostUserRNG, VHOST_USER_RNG) | ||
|
||
struct VHostUserRNG { | ||
/*< private >*/ | ||
VirtIODevice parent; | ||
CharBackend chardev; | ||
struct vhost_virtqueue *vhost_vq; | ||
struct vhost_dev vhost_dev; | ||
VhostUserState vhost_user; | ||
VirtQueue *req_vq; | ||
bool connected; | ||
|
||
/*< public >*/ | ||
}; | ||
|
||
#endif /* _QEMU_VHOST_USER_RNG_H */ |