Patchwork [7/8] virtio-rng: add rate limiting support

login
register
mail settings
Submitter Anthony Liguori
Date Oct. 30, 2012, 11:02 p.m.
Message ID <1351638178-15056-8-git-send-email-aliguori@us.ibm.com>
Download mbox | patch
Permalink /patch/195661/
State New
Headers show

Comments

Anthony Liguori - Oct. 30, 2012, 11:02 p.m.
This adds parameters to virtio-rng-pci to allow rate limiting the entropy a
guest receives.  An example command line:

$ qemu -device virtio-rng-pci,max-bytes=1024,period=1000

Would limit entropy collection to 1Kb/s.

Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
---
 hw/virtio-pci.c |  7 +++++++
 hw/virtio-rng.c | 63 +++++++++++++++++++++++++++++++++++++++++++++++++--------
 hw/virtio-rng.h |  2 ++
 3 files changed, 64 insertions(+), 8 deletions(-)

Patch

diff --git a/hw/virtio-pci.c b/hw/virtio-pci.c
index a3d4777..f90296d 100644
--- a/hw/virtio-pci.c
+++ b/hw/virtio-pci.c
@@ -1015,6 +1015,13 @@  static void virtio_rng_initfn(Object *obj)
 
 static Property virtio_rng_properties[] = {
     DEFINE_VIRTIO_COMMON_FEATURES(VirtIOPCIProxy, host_features),
+    /* Set a default rate limit of 2^47 bytes per minute or roughly 2TB/s.  If
+       you have an entropy source capable of generating more entropy than this
+       and you can pass it through via virtio-rng, then hats off to you.  Until
+       then, this is unlimited for all practical purposes.
+    */
+    DEFINE_PROP_UINT64("max-bytes", VirtIOPCIProxy, rng.max_bytes, INT64_MAX),
+    DEFINE_PROP_UINT32("period", VirtIOPCIProxy, rng.period_ms, 1 << 16),
     DEFINE_PROP_END_OF_LIST(),
 };
 
diff --git a/hw/virtio-rng.c b/hw/virtio-rng.c
index b7fb5e9..3ca96c8 100644
--- a/hw/virtio-rng.c
+++ b/hw/virtio-rng.c
@@ -31,6 +31,12 @@  typedef struct VirtIORNG {
     bool popped;
 
     RngBackend *rng;
+
+    /* We purposefully don't migrate this state.  The quota will reset on the
+     * destination as a result.  Rate limiting is host state, not guest state.
+     */
+    QEMUTimer *rate_limit_timer;
+    int64_t quota_remaining;
 } VirtIORNG;
 
 static bool is_guest_ready(VirtIORNG *vrng)
@@ -55,6 +61,8 @@  static size_t pop_an_elem(VirtIORNG *vrng)
     return size;
 }
 
+static void virtio_rng_process(VirtIORNG *vrng);
+
 /* Send data from a char device over to the guest */
 static void chr_read(void *opaque, const void *buf, size_t size)
 {
@@ -66,6 +74,8 @@  static void chr_read(void *opaque, const void *buf, size_t size)
         return;
     }
 
+    vrng->quota_remaining -= size;
+
     offset = 0;
     while (offset < size) {
         if (!pop_an_elem(vrng)) {
@@ -85,23 +95,32 @@  static void chr_read(void *opaque, const void *buf, size_t size)
      * didn't have enough data to fill them all, indicate we want more
      * data.
      */
-    len = pop_an_elem(vrng);
-    if (len) {
-        rng_backend_request_entropy(vrng->rng, size, chr_read, vrng);
-    }
+    virtio_rng_process(vrng);
 }
 
-static void handle_input(VirtIODevice *vdev, VirtQueue *vq)
+static void virtio_rng_process(VirtIORNG *vrng)
 {
-    VirtIORNG *vrng = DO_UPCAST(VirtIORNG, vdev, vdev);
-    size_t size;
+    ssize_t size;
+
+    if (!is_guest_ready(vrng)) {
+        return;
+    }
 
     size = pop_an_elem(vrng);
-    if (size) {
+    size = MIN(vrng->quota_remaining, size);
+
+    if (size > 0) {
         rng_backend_request_entropy(vrng->rng, size, chr_read, vrng);
     }
 }
 
+
+static void handle_input(VirtIODevice *vdev, VirtQueue *vq)
+{
+    VirtIORNG *vrng = DO_UPCAST(VirtIORNG, vdev, vdev);
+    virtio_rng_process(vrng);
+}
+
 static uint32_t get_features(VirtIODevice *vdev, uint32_t f)
 {
     return f;
@@ -163,9 +182,27 @@  static int virtio_rng_load(QEMUFile *f, void *opaque, int version_id)
         virtqueue_map_sg(vrng->elem.out_sg, vrng->elem.out_addr,
                          vrng->elem.out_num, 0);
     }
+
+    /* We may have an element ready but couldn't process it due to a quota
+       limit.  Make sure to try again after live migration when the quota may
+       have been reset.
+    */
+    virtio_rng_process(vrng);
+
     return 0;
 }
 
+static void check_rate_limit(void *opaque)
+{
+    VirtIORNG *s = opaque;
+
+    s->quota_remaining = s->conf->max_bytes;
+    virtio_rng_process(s);
+    qemu_mod_timer(s->rate_limit_timer,
+                   qemu_get_clock_ms(vm_clock) + s->conf->period_ms);
+}
+
+
 VirtIODevice *virtio_rng_init(DeviceState *dev, VirtIORNGConf *conf)
 {
     VirtIORNG *vrng;
@@ -196,6 +233,16 @@  VirtIODevice *virtio_rng_init(DeviceState *dev, VirtIORNGConf *conf)
     vrng->qdev = dev;
     vrng->conf = conf;
     vrng->popped = false;
+    vrng->quota_remaining = vrng->conf->max_bytes;
+
+    g_assert_cmpint(vrng->conf->max_bytes, <=, INT64_MAX);
+
+    vrng->rate_limit_timer = qemu_new_timer_ms(vm_clock,
+                                               check_rate_limit, vrng);
+
+    qemu_mod_timer(vrng->rate_limit_timer,
+                   qemu_get_clock_ms(vm_clock) + vrng->conf->period_ms);
+
     register_savevm(dev, "virtio-rng", -1, 1, virtio_rng_save,
                     virtio_rng_load, vrng);
 
diff --git a/hw/virtio-rng.h b/hw/virtio-rng.h
index fbb0104..7324d0a 100644
--- a/hw/virtio-rng.h
+++ b/hw/virtio-rng.h
@@ -19,6 +19,8 @@ 
 
 struct VirtIORNGConf {
     RngBackend *rng;
+    uint64_t max_bytes;
+    uint32_t period_ms;
 };
 
 #endif