From: Laurent Vivier lvivier@redhat.com
[ Upstream commit 9a4b612d675b03f7fc9fa1957ca399c8223f3954 ]
If we ensure we have already some data available by enqueuing again the buffer once data are exhausted, we can return what we have without waiting for the device answer.
Signed-off-by: Laurent Vivier lvivier@redhat.com Link: https://lore.kernel.org/r/20211028101111.128049-5-lvivier@redhat.com Signed-off-by: Michael S. Tsirkin mst@redhat.com Stable-dep-of: ac52578d6e8d ("hwrng: virtio - Fix race on data_avail and actual data") Signed-off-by: Sasha Levin sashal@kernel.org --- drivers/char/hw_random/virtio-rng.c | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-)
diff --git a/drivers/char/hw_random/virtio-rng.c b/drivers/char/hw_random/virtio-rng.c index c88f175e60a4c..a84248c26fd7f 100644 --- a/drivers/char/hw_random/virtio-rng.c +++ b/drivers/char/hw_random/virtio-rng.c @@ -32,7 +32,6 @@ struct virtrng_info { struct virtqueue *vq; char name[25]; int index; - bool busy; bool hwrng_register_done; bool hwrng_removed; /* data transfer */ @@ -56,16 +55,18 @@ static void random_recv_done(struct virtqueue *vq) return;
vi->data_idx = 0; - vi->busy = false;
complete(&vi->have_data); }
-/* The host will fill any buffer we give it with sweet, sweet randomness. */ -static void register_buffer(struct virtrng_info *vi) +static void request_entropy(struct virtrng_info *vi) { struct scatterlist sg;
+ reinit_completion(&vi->have_data); + vi->data_avail = 0; + vi->data_idx = 0; + sg_init_one(&sg, vi->data, sizeof(vi->data));
/* There should always be room for one buffer. */ @@ -81,6 +82,8 @@ static unsigned int copy_data(struct virtrng_info *vi, void *buf, memcpy(buf, vi->data + vi->data_idx, size); vi->data_idx += size; vi->data_avail -= size; + if (vi->data_avail == 0) + request_entropy(vi); return size; }
@@ -110,13 +113,7 @@ static int virtio_read(struct hwrng *rng, void *buf, size_t size, bool wait) * so either size is 0 or data_avail is 0 */ while (size != 0) { - /* data_avail is 0 */ - if (!vi->busy) { - /* no pending request, ask for more */ - vi->busy = true; - reinit_completion(&vi->have_data); - register_buffer(vi); - } + /* data_avail is 0 but a request is pending */ ret = wait_for_completion_killable(&vi->have_data); if (ret < 0) return ret; @@ -138,8 +135,7 @@ static void virtio_cleanup(struct hwrng *rng) { struct virtrng_info *vi = (struct virtrng_info *)rng->priv;
- if (vi->busy) - complete(&vi->have_data); + complete(&vi->have_data); }
static int probe_common(struct virtio_device *vdev) @@ -175,6 +171,9 @@ static int probe_common(struct virtio_device *vdev) goto err_find; }
+ /* we always have a pending entropy request */ + request_entropy(vi); + return 0;
err_find: @@ -193,7 +192,6 @@ static void remove_common(struct virtio_device *vdev) vi->data_idx = 0; complete(&vi->have_data); vdev->config->reset(vdev); - vi->busy = false; if (vi->hwrng_register_done) hwrng_unregister(&vi->hwrng); vdev->config->del_vqs(vdev);