If the guest adds a buffer to the virtio queue while another buffer
is still pending and hasn't been filled and returned by the rng
device, rng-random internally discards the pending request, which
leads to the second buffer getting stuck in the queue. For the guest
this manifests as delayed completion of reads from virtio-rng, i.e.
a read is completed only after another read is issued.

This patch adds an internal queue of requests, analogous to what
rng-egd uses, to make sure that requests and responses are balanced
and correctly ordered.

Signed-off-by: Ladi Prosek <lpro...@redhat.com>
---
 backends/rng-random.c | 71 +++++++++++++++++++++++++++++++++++++++------------
 1 file changed, 54 insertions(+), 17 deletions(-)

diff --git a/backends/rng-random.c b/backends/rng-random.c
index 4e51f46..3f1fd21 100644
--- a/backends/rng-random.c
+++ b/backends/rng-random.c
@@ -22,10 +22,16 @@ struct RndRandom
     int fd;
     char *filename;
 
+    GSList *requests;
+};
+
+typedef struct RngRequest
+{
     EntropyReceiveFunc *receive_func;
     void *opaque;
     size_t size;
-};
+} RngRequest;
+
 
 /**
  * A simple and incomplete backend to request entropy from /dev/random.
@@ -37,19 +43,27 @@ struct RndRandom
 static void entropy_available(void *opaque)
 {
     RndRandom *s = RNG_RANDOM(opaque);
-    uint8_t buffer[s->size];
-    ssize_t len;
 
-    len = read(s->fd, buffer, s->size);
-    if (len < 0 && errno == EAGAIN) {
-        return;
-    }
-    g_assert(len != -1);
+    while (s->requests != NULL) {
+        RngRequest *req = s->requests->data;
+        uint8_t buffer[req->size];
+        ssize_t len;
+
+        len = read(s->fd, buffer, req->size);
+        if (len < 0 && errno == EAGAIN) {
+            return;
+        }
+        g_assert(len != -1);
 
-    s->receive_func(s->opaque, buffer, len);
-    s->receive_func = NULL;
+        req->receive_func(req->opaque, buffer, len);
 
-    qemu_set_fd_handler(s->fd, NULL, NULL, NULL);
+        s->requests = g_slist_remove_link(s->requests, s->requests);
+        g_free(req);
+    }
+
+    if (s->requests == NULL) {
+        qemu_set_fd_handler(s->fd, NULL, NULL, NULL);
+    }
 }
 
 static void rng_random_request_entropy(RngBackend *b, size_t size,
@@ -57,16 +71,36 @@ static void rng_random_request_entropy(RngBackend *b, 
size_t size,
                                         void *opaque)
 {
     RndRandom *s = RNG_RANDOM(b);
+    RngRequest *req;
 
-    if (s->receive_func) {
-        s->receive_func(s->opaque, NULL, 0);
+    req = g_malloc(sizeof(*req));
+
+    req->size = size;
+    req->receive_func = receive_entropy;
+    req->opaque = opaque;
+
+    s->requests = g_slist_append(s->requests, req);
+    if (s->requests->data == req) {
+        qemu_set_fd_handler(s->fd, entropy_available, NULL, s);
+    }
+}
+
+static void rng_random_free_requests(RndRandom *s)
+{
+    GSList *i;
+
+    for (i = s->requests; i; i = i->next) {
+        g_free(i->data);
     }
 
-    s->receive_func = receive_entropy;
-    s->opaque = opaque;
-    s->size = size;
+    g_slist_free(s->requests);
+    s->requests = NULL;
+}
 
-    qemu_set_fd_handler(s->fd, entropy_available, NULL, s);
+static void rng_random_cancel_requests(RngBackend *b)
+{
+    RndRandom *s = RNG_RANDOM(b);
+    rng_random_free_requests(s);
 }
 
 static void rng_random_opened(RngBackend *b, Error **errp)
@@ -129,6 +163,8 @@ static void rng_random_finalize(Object *obj)
     }
 
     g_free(s->filename);
+
+    rng_random_free_requests(s);
 }
 
 static void rng_random_class_init(ObjectClass *klass, void *data)
@@ -136,6 +172,7 @@ static void rng_random_class_init(ObjectClass *klass, void 
*data)
     RngBackendClass *rbc = RNG_BACKEND_CLASS(klass);
 
     rbc->request_entropy = rng_random_request_entropy;
+    rbc->cancel_requests = rng_random_cancel_requests;
     rbc->opened = rng_random_opened;
 }
 
-- 
2.5.0


Reply via email to