[RFC,2/2] virtio-rng: add a control queue
diff mbox series

Message ID 20200123151700.1367857-3-lvivier@redhat.com
State New
Headers show
Series
  • virtio-rng: add a control queue
Related show

Commit Message

Laurent Vivier Jan. 23, 2020, 3:17 p.m. UTC
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 <lvivier@redhat.com>
---
 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(-)

Patch
diff mbox series

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 */