summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLaurent Vivier <lvivier@redhat.com>2021-10-28 12:11:10 +0200
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2023-07-27 08:37:17 +0200
commit102a354d52ca46cbe1497ff8349752ca59e3458c (patch)
tree449363ad2e3fdee46b0d250bf2640787697952fe
parentf2a7dfd35f0c925f6b3963d7823369e4122855f3 (diff)
hwrng: virtio - don't waste entropy
[ Upstream commit 5c8e933050044d6dd2a000f9a5756ae73cbe7c44 ] if we don't use all the entropy available in the buffer, keep it and use it later. Signed-off-by: Laurent Vivier <lvivier@redhat.com> Link: https://lore.kernel.org/r/20211028101111.128049-4-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>
-rw-r--r--drivers/char/hw_random/virtio-rng.c52
1 files changed, 35 insertions, 17 deletions
diff --git a/drivers/char/hw_random/virtio-rng.c b/drivers/char/hw_random/virtio-rng.c
index c8f5a3392e48..207a5f3b335c 100644
--- a/drivers/char/hw_random/virtio-rng.c
+++ b/drivers/char/hw_random/virtio-rng.c
@@ -25,6 +25,7 @@ struct virtrng_info {
/* data transfer */
struct completion have_data;
unsigned int data_avail;
+ unsigned int data_idx;
/* minimal size returned by rng_buffer_size() */
#if SMP_CACHE_BYTES < 32
u8 data[32];
@@ -41,6 +42,9 @@ static void random_recv_done(struct virtqueue *vq)
if (!virtqueue_get_buf(vi->vq, &vi->data_avail))
return;
+ vi->data_idx = 0;
+ vi->busy = false;
+
complete(&vi->have_data);
}
@@ -57,6 +61,16 @@ static void register_buffer(struct virtrng_info *vi)
virtqueue_kick(vi->vq);
}
+static unsigned int copy_data(struct virtrng_info *vi, void *buf,
+ unsigned int size)
+{
+ size = min_t(unsigned int, size, vi->data_avail);
+ memcpy(buf, vi->data + vi->data_idx, size);
+ vi->data_idx += size;
+ vi->data_avail -= size;
+ return size;
+}
+
static int virtio_read(struct hwrng *rng, void *buf, size_t size, bool wait)
{
int ret;
@@ -67,17 +81,29 @@ static int virtio_read(struct hwrng *rng, void *buf, size_t size, bool wait)
if (vi->hwrng_removed)
return -ENODEV;
- if (!vi->busy) {
- vi->busy = true;
- reinit_completion(&vi->have_data);
- register_buffer(vi);
+ read = 0;
+
+ /* copy available data */
+ if (vi->data_avail) {
+ chunk = copy_data(vi, buf, size);
+ size -= chunk;
+ read += chunk;
}
if (!wait)
- return 0;
+ return read;
- read = 0;
+ /* We have already copied available entropy,
+ * 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);
+ }
ret = wait_for_completion_killable(&vi->have_data);
if (ret < 0)
return ret;
@@ -87,20 +113,11 @@ static int virtio_read(struct hwrng *rng, void *buf, size_t size, bool wait)
if (vi->data_avail == 0)
return read;
- chunk = min_t(unsigned int, size, vi->data_avail);
- memcpy(buf + read, vi->data, chunk);
- read += chunk;
+ chunk = copy_data(vi, buf + read, size);
size -= chunk;
- vi->data_avail = 0;
-
- if (size != 0) {
- reinit_completion(&vi->have_data);
- register_buffer(vi);
- }
+ read += chunk;
}
- vi->busy = false;
-
return read;
}
@@ -160,6 +177,7 @@ static void remove_common(struct virtio_device *vdev)
vi->hwrng_removed = true;
vi->data_avail = 0;
+ vi->data_idx = 0;
complete(&vi->have_data);
vdev->config->reset(vdev);
vi->busy = false;