When the linux kernel wants to switch to another device it needs to be able to flush the on-fly requests.
Signed-off-by: Laurent Vivier <[email protected]> --- hw/core/machine.c | 1 + hw/virtio/trace-events | 6 ++ hw/virtio/virtio-rng.c | 81 ++++++++++++++++++++- include/hw/virtio/virtio-rng.h | 3 +- include/standard-headers/linux/virtio_rng.h | 14 ++++ 5 files changed, 101 insertions(+), 4 deletions(-) diff --git a/hw/core/machine.c b/hw/core/machine.c index 3e288bfceb7f..6c417dbdc02a 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -34,6 +34,7 @@ GlobalProperty hw_compat_4_2[] = { { "vhost-blk-device", "seg_max_adjust", "off"}, { "usb-host", "suppress-remote-wake", "off" }, { "usb-redir", "suppress-remote-wake", "off" }, + { "virtio-rng", "ctrl-queue", "off" }, }; const size_t hw_compat_4_2_len = G_N_ELEMENTS(hw_compat_4_2); diff --git a/hw/virtio/trace-events b/hw/virtio/trace-events index e28ba48da621..95b77f3a3056 100644 --- a/hw/virtio/trace-events +++ b/hw/virtio/trace-events @@ -38,6 +38,12 @@ virtio_rng_popped(void *rng) "rng %p: elem popped" virtio_rng_pushed(void *rng, size_t len) "rng %p: %zd bytes pushed" virtio_rng_request(void *rng, size_t size, unsigned quota) "rng %p: %zd bytes requested, %u bytes quota left" virtio_rng_vm_state_change(void *rng, int running, int state) "rng %p: state change to running %d state %d" +virtio_rng_ctrl(void *rng) "rng %p" +virtio_rng_ctrl_popped(void *rng) "rng %p" +virtio_rng_ctrl_pushed(void *rng) "rng %p" +virtio_rng_flush(void *rng) "rng %p" +virtio_rng_flush_popped(void *rng) "rng %p" +virtio_rng_flush_pushed(void *rng) "rng %p" # virtio-balloon.c # diff --git a/hw/virtio/virtio-rng.c b/hw/virtio/virtio-rng.c index a0bca55bef55..389aa8997f3d 100644 --- a/hw/virtio/virtio-rng.c +++ b/hw/virtio/virtio-rng.c @@ -128,9 +128,76 @@ static void virtio_rng_handle_input(VirtIODevice *vdev, VirtQueue *vq) virtio_rng_process(vrng); } -static uint64_t get_features(VirtIODevice *vdev, uint64_t f, Error **errp) +static virtio_rng_ctrl_ack virtio_rng_flush(VirtIORNG *vrng) { - return f; + VirtQueueElement *elem; + VirtIODevice *vdev = VIRTIO_DEVICE(vrng); + + trace_virtio_rng_flush(vrng); + while (!virtio_queue_empty(vrng->input_vq)) { + elem = virtqueue_pop(vrng->input_vq, sizeof(VirtQueueElement)); + if (!elem) { + break; + } + trace_virtio_rng_flush_popped(vrng); + virtqueue_push(vrng->input_vq, elem, 0); + trace_virtio_rng_flush_pushed(vrng); + g_free(elem); + } + virtio_notify(vdev, vrng->input_vq); + + return VIRTIO_RNG_OK; +} + +static void virtio_rng_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq) +{ + VirtIORNG *vrng = VIRTIO_RNG(vdev); + VirtQueueElement *elem; + virtio_rng_ctrl_ack status = VIRTIO_RNG_ERR; + struct virtio_rng_ctrl_hdr ctrl; + size_t s; + + trace_virtio_rng_ctrl(vrng); + for (;;) { + elem = virtqueue_pop(vq, sizeof(VirtQueueElement)); + if (!elem) { + break; + } + trace_virtio_rng_ctrl_popped(vrng); + + if (iov_size(elem->in_sg, elem->in_num) < sizeof(status) || + iov_size(elem->out_sg, elem->out_num) < sizeof(ctrl)) { + virtio_error(vdev, "virtio-rng ctrl missing headers"); + virtqueue_detach_element(vq, elem, 0); + g_free(elem); + break; + } + + s = iov_to_buf(elem->out_sg, elem->out_num, 0, &ctrl, sizeof(ctrl)); + if (s != sizeof(ctrl)) { + status = VIRTIO_RNG_ERR; + } else if (ctrl.cmd == VIRTIO_RNG_CMD_FLUSH) { + status = virtio_rng_flush(vrng); + } + + s = iov_from_buf(elem->in_sg, elem->in_num, 0, &status, sizeof(status)); + assert(s == sizeof(status)); + + virtqueue_push(vq, elem, sizeof(status)); + trace_virtio_rng_ctrl_pushed(vrng); + virtio_notify(vdev, vq); + g_free(elem); + } +} + +static uint64_t virtio_rng_get_features(VirtIODevice *vdev, uint64_t features, + Error **errp) +{ + VirtIORNG *vrng = VIRTIO_RNG(vdev); + + features |= vrng->conf.host_features; + + return features; } static void virtio_rng_vm_state_change(void *opaque, int running, @@ -221,6 +288,9 @@ static void virtio_rng_device_realize(DeviceState *dev, Error **errp) virtio_init(vdev, "virtio-rng", VIRTIO_ID_RNG, 0); vrng->input_vq = virtio_add_queue(vdev, 8, virtio_rng_handle_input); + if (virtio_has_feature(vrng->conf.host_features, VIRTIO_RNG_F_CTRL_VQ)) { + vrng->ctrl_vq = virtio_add_queue(vdev, 8, virtio_rng_handle_ctrl); + } vrng->quota_remaining = vrng->conf.max_bytes; vrng->rate_limit_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, check_rate_limit, vrng); @@ -238,6 +308,9 @@ static void virtio_rng_device_unrealize(DeviceState *dev, Error **errp) qemu_del_vm_change_state_handler(vrng->vmstate); timer_del(vrng->rate_limit_timer); timer_free(vrng->rate_limit_timer); + if (virtio_has_feature(vrng->conf.host_features, VIRTIO_RNG_F_CTRL_VQ)) { + virtio_delete_queue(vrng->ctrl_vq); + } virtio_delete_queue(vrng->input_vq); virtio_cleanup(vdev); } @@ -261,6 +334,8 @@ static Property virtio_rng_properties[] = { DEFINE_PROP_UINT64("max-bytes", VirtIORNG, conf.max_bytes, INT64_MAX), DEFINE_PROP_UINT32("period", VirtIORNG, conf.period_ms, 1 << 16), DEFINE_PROP_LINK("rng", VirtIORNG, conf.rng, TYPE_RNG_BACKEND, RngBackend *), + DEFINE_PROP_BIT64("ctrl-queue", VirtIORNG, conf.host_features, + VIRTIO_RNG_F_CTRL_VQ, true), DEFINE_PROP_END_OF_LIST(), }; @@ -274,7 +349,7 @@ static void virtio_rng_class_init(ObjectClass *klass, void *data) set_bit(DEVICE_CATEGORY_MISC, dc->categories); vdc->realize = virtio_rng_device_realize; vdc->unrealize = virtio_rng_device_unrealize; - vdc->get_features = get_features; + vdc->get_features = virtio_rng_get_features; vdc->set_status = virtio_rng_set_status; } diff --git a/include/hw/virtio/virtio-rng.h b/include/hw/virtio/virtio-rng.h index d77daf126828..abecec7b244a 100644 --- a/include/hw/virtio/virtio-rng.h +++ b/include/hw/virtio/virtio-rng.h @@ -26,13 +26,14 @@ struct VirtIORNGConf { RngBackend *rng; uint64_t max_bytes; uint32_t period_ms; + uint64_t host_features; }; typedef struct VirtIORNG { VirtIODevice parent_obj; - /* Only one vq - guest puts buffer(s) on it when it needs entropy */ VirtQueue *input_vq; + VirtQueue *ctrl_vq; VirtIORNGConf conf; diff --git a/include/standard-headers/linux/virtio_rng.h b/include/standard-headers/linux/virtio_rng.h index 60fc798bde18..b80e9298817e 100644 --- a/include/standard-headers/linux/virtio_rng.h +++ b/include/standard-headers/linux/virtio_rng.h @@ -5,4 +5,18 @@ #include "standard-headers/linux/virtio_ids.h" #include "standard-headers/linux/virtio_config.h" +/* The features bitmap for virtuio rng */ +#define VIRTIO_RNG_F_CTRL_VQ 0 /* Device has control queue */ + +struct virtio_rng_ctrl_hdr { + uint8_t cmd; +} QEMU_PACKED; + +#define VIRTIO_RNG_CMD_FLUSH 0 + +typedef uint8_t virtio_rng_ctrl_ack; + +#define VIRTIO_RNG_OK 0 +#define VIRTIO_RNG_ERR 1 + #endif /* _LINUX_VIRTIO_RNG_H */ -- 2.23.0
