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 <lvivier@xxxxxxxxxx> --- 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