This allows the driver to move bigger data block when the backend allows
it.

Add a backend buffer_size for this purpose. For the moment only
virtio-rng defines it.

Using bigger buffer with virtio-rng improves performance from:

  # dd if=/dev/hwrng of=/dev/null bs=1024 count=1024000
  1048576000 bytes (1.0 GB, 1000 MiB) copied, 674.303 s, 1.6 MB/s
  # dd if=/dev/hwrng of=/dev/null bs=4096 count=256000
  1048576000 bytes (1.0 GB, 1000 MiB) copied, 622.394 s, 1.7 MB/s

to

  # dd if=/dev/hwrng of=/dev/null bs=1024 count=1024000
  1048576000 bytes (1.0 GB, 1000 MiB) copied, 41.0579 s, 25.5 MB/s
  # dd if=/dev/hwrng of=/dev/null bs=4096 count=256000
  1048576000 bytes (1.0 GB, 1000 MiB) copied, 14.394 s, 72.8 MB/s

Signed-off-by: Laurent Vivier <lviv...@redhat.com>
---
 drivers/char/hw_random/core.c       | 37 ++++++++++++++++++++---------
 drivers/char/hw_random/virtio-rng.c |  1 +
 include/linux/hw_random.h           |  2 ++
 3 files changed, 29 insertions(+), 11 deletions(-)

diff --git a/drivers/char/hw_random/core.c b/drivers/char/hw_random/core.c
index 8c1c47dd9f46..3d8ce3c4d79c 100644
--- a/drivers/char/hw_random/core.c
+++ b/drivers/char/hw_random/core.c
@@ -56,15 +56,25 @@ static void start_khwrngd(void);
 static inline int rng_get_data(struct hwrng *rng, u8 *buffer, size_t size,
                               int wait);
 
-static size_t rng_buffer_size(void)
+static size_t rng_min_buffer_size(void)
 {
-       return SMP_CACHE_BYTES < 32 ? 32 : SMP_CACHE_BYTES;
+       return max_t(size_t, 32, SMP_CACHE_BYTES);
+}
+
+static size_t rng_max_buffer_size(struct hwrng *rng)
+{
+       size_t size;
+
+       size = max_t(size_t, rng->buffer_size, SMP_CACHE_BYTES);
+
+       /* rng_buffer can store up to PAGE_SIZE */
+       return min(PAGE_SIZE, size);
 }
 
 static void add_early_randomness(struct hwrng *rng)
 {
        int bytes_read;
-       size_t size = min_t(size_t, 16, rng_buffer_size());
+       size_t size = min_t(size_t, 16, rng_min_buffer_size());
 
        mutex_lock(&reading_mutex);
        bytes_read = rng_get_data(rng, rng_buffer, size, 0);
@@ -226,9 +236,14 @@ static ssize_t rng_dev_read(struct file *filp, char __user 
*buf,
                        goto out_put;
                }
                if (!data_avail) {
-                       bytes_read = rng_get_data(rng, rng_buffer,
-                               rng_buffer_size(),
-                               !(filp->f_flags & O_NONBLOCK));
+                       size_t to_read;
+                       /* read at least 32 bytes, up to rng_max_buffer_size()
+                        * but no more than size
+                        */
+                       to_read = max_t(size_t, 32,
+                                       min(size, rng_max_buffer_size(rng)));
+                       bytes_read = rng_get_data(rng, rng_buffer, to_read,
+                                                 !(filp->f_flags & 
O_NONBLOCK));
                        if (bytes_read < 0) {
                                err = bytes_read;
                                goto out_unlock_reading;
@@ -440,7 +455,7 @@ static int hwrng_fillfn(void *unused)
                        break;
                mutex_lock(&reading_mutex);
                rc = rng_get_data(rng, rng_fillbuf,
-                                 rng_buffer_size(), 1);
+                                 rng_min_buffer_size(), 1);
                mutex_unlock(&reading_mutex);
                put_rng(rng);
                if (rc <= 0) {
@@ -614,11 +629,11 @@ static int __init hwrng_modinit(void)
        int ret;
 
        /* kmalloc makes this safe for virt_to_page() in virtio_rng.c */
-       rng_buffer = kmalloc(rng_buffer_size(), GFP_KERNEL);
+       rng_buffer = (u8 *)get_zeroed_page(GFP_KERNEL);
        if (!rng_buffer)
                return -ENOMEM;
 
-       rng_fillbuf = kmalloc(rng_buffer_size(), GFP_KERNEL);
+       rng_fillbuf = kmalloc(rng_min_buffer_size(), GFP_KERNEL);
        if (!rng_fillbuf) {
                kfree(rng_buffer);
                return -ENOMEM;
@@ -627,7 +642,7 @@ static int __init hwrng_modinit(void)
        ret = register_miscdev();
        if (ret) {
                kfree(rng_fillbuf);
-               kfree(rng_buffer);
+               free_page((unsigned long)rng_buffer);
        }
 
        return ret;
@@ -637,7 +652,7 @@ static void __exit hwrng_modexit(void)
 {
        mutex_lock(&rng_mutex);
        BUG_ON(current_rng);
-       kfree(rng_buffer);
+       free_page((unsigned long)rng_buffer);
        kfree(rng_fillbuf);
        mutex_unlock(&rng_mutex);
 
diff --git a/drivers/char/hw_random/virtio-rng.c 
b/drivers/char/hw_random/virtio-rng.c
index a90001e02bf7..b71c137fcd05 100644
--- a/drivers/char/hw_random/virtio-rng.c
+++ b/drivers/char/hw_random/virtio-rng.c
@@ -104,6 +104,7 @@ static int probe_common(struct virtio_device *vdev)
 
        vi->hwrng = (struct hwrng) {
                .read = virtio_read,
+               .buffer_size = PAGE_SIZE,
                .cleanup = virtio_cleanup,
                .priv = (unsigned long)vi,
                .name = vi->name,
diff --git a/include/linux/hw_random.h b/include/linux/hw_random.h
index 8e6dd908da21..582c8787f808 100644
--- a/include/linux/hw_random.h
+++ b/include/linux/hw_random.h
@@ -31,6 +31,7 @@
  * @read:              New API. drivers can fill up to max bytes of data
  *                     into the buffer. The buffer is aligned for any type
  *                     and max is a multiple of 4 and >= 32 bytes.
+ * @buffer_size:       Optional. if not 0, optimal buffer size.
  * @priv:              Private data, for use by the RNG driver.
  * @quality:           Estimation of true entropy in RNG's bitstream
  *                     (in bits of entropy per 1024 bits of input;
@@ -43,6 +44,7 @@ struct hwrng {
        int (*data_present)(struct hwrng *rng, int wait);
        int (*data_read)(struct hwrng *rng, u32 *data);
        int (*read)(struct hwrng *rng, void *data, size_t max, bool wait);
+       size_t buffer_size;
        unsigned long priv;
        unsigned short quality;
 
-- 
2.26.2

Reply via email to