Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

hwrng: virtio-rng - Convert to new API

This patch converts virtio-rng to the new hw_rng API.

In the process it fixes a previously untriggered buffering bug where the
buffer is not drained correctly if it has a non-multiple-of-4 length.

Performance has improved under qemu-kvm testing also.

Signed-off-by: Ian Molton <ian.molton@collabora.co.uk>
Acked-by: Matt Mackall <mpm@selenic.com>
Acked-by: Rusty Russell <rusty@rustcorp.com.au>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>

authored by

Ian Molton and committed by
Herbert Xu
bb347d98 83863243

+27 -51
+27 -51
drivers/char/hw_random/virtio-rng.c
··· 16 16 * along with this program; if not, write to the Free Software 17 17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 18 18 */ 19 + 19 20 #include <linux/err.h> 20 21 #include <linux/hw_random.h> 21 22 #include <linux/scatterlist.h> ··· 24 23 #include <linux/virtio.h> 25 24 #include <linux/virtio_rng.h> 26 25 27 - /* The host will fill any buffer we give it with sweet, sweet randomness. We 28 - * give it 64 bytes at a time, and the hwrng framework takes it 4 bytes at a 29 - * time. */ 30 - #define RANDOM_DATA_SIZE 64 31 - 32 26 static struct virtqueue *vq; 33 - static u32 *random_data; 34 - static unsigned int data_left; 27 + static unsigned int data_avail; 35 28 static DECLARE_COMPLETION(have_data); 29 + static bool busy; 36 30 37 31 static void random_recv_done(struct virtqueue *vq) 38 32 { 39 - unsigned int len; 40 - 41 33 /* We can get spurious callbacks, e.g. shared IRQs + virtio_pci. */ 42 - if (!vq->vq_ops->get_buf(vq, &len)) 34 + if (!vq->vq_ops->get_buf(vq, &data_avail)) 43 35 return; 44 36 45 - data_left += len; 46 37 complete(&have_data); 47 38 } 48 39 49 - static void register_buffer(void) 40 + /* The host will fill any buffer we give it with sweet, sweet randomness. */ 41 + static void register_buffer(u8 *buf, size_t size) 50 42 { 51 43 struct scatterlist sg; 52 44 53 - sg_init_one(&sg, random_data+data_left, RANDOM_DATA_SIZE-data_left); 45 + sg_init_one(&sg, buf, size); 46 + 54 47 /* There should always be room for one buffer. */ 55 - if (vq->vq_ops->add_buf(vq, &sg, 0, 1, random_data) < 0) 48 + if (vq->vq_ops->add_buf(vq, &sg, 0, 1, buf) < 0) 56 49 BUG(); 50 + 57 51 vq->vq_ops->kick(vq); 58 52 } 59 53 60 - /* At least we don't udelay() in a loop like some other drivers. */ 61 - static int virtio_data_present(struct hwrng *rng, int wait) 54 + static int virtio_read(struct hwrng *rng, void *buf, size_t size, bool wait) 62 55 { 63 - if (data_left >= sizeof(u32)) 64 - return 1; 65 56 66 - again: 57 + if (!busy) { 58 + busy = true; 59 + init_completion(&have_data); 60 + register_buffer(buf, size); 61 + } 62 + 67 63 if (!wait) 68 64 return 0; 69 65 70 66 wait_for_completion(&have_data); 71 67 72 - /* Not enough? Re-register. */ 73 - if (unlikely(data_left < sizeof(u32))) { 74 - register_buffer(); 75 - goto again; 76 - } 68 + busy = false; 77 69 78 - return 1; 70 + return data_avail; 79 71 } 80 72 81 - /* virtio_data_present() must have succeeded before this is called. */ 82 - static int virtio_data_read(struct hwrng *rng, u32 *data) 73 + static void virtio_cleanup(struct hwrng *rng) 83 74 { 84 - BUG_ON(data_left < sizeof(u32)); 85 - data_left -= sizeof(u32); 86 - *data = random_data[data_left / 4]; 87 - 88 - if (data_left < sizeof(u32)) { 89 - init_completion(&have_data); 90 - register_buffer(); 91 - } 92 - return sizeof(*data); 75 + if (busy) 76 + wait_for_completion(&have_data); 93 77 } 78 + 94 79 95 80 static struct hwrng virtio_hwrng = { 96 - .name = "virtio", 97 - .data_present = virtio_data_present, 98 - .data_read = virtio_data_read, 81 + .name = "virtio", 82 + .cleanup = virtio_cleanup, 83 + .read = virtio_read, 99 84 }; 100 85 101 86 static int virtrng_probe(struct virtio_device *vdev) ··· 99 112 return err; 100 113 } 101 114 102 - register_buffer(); 103 115 return 0; 104 116 } 105 117 ··· 124 138 125 139 static int __init init(void) 126 140 { 127 - int err; 128 - 129 - random_data = kmalloc(RANDOM_DATA_SIZE, GFP_KERNEL); 130 - if (!random_data) 131 - return -ENOMEM; 132 - 133 - err = register_virtio_driver(&virtio_rng); 134 - if (err) 135 - kfree(random_data); 136 - return err; 141 + return register_virtio_driver(&virtio_rng); 137 142 } 138 143 139 144 static void __exit fini(void) 140 145 { 141 - kfree(random_data); 142 146 unregister_virtio_driver(&virtio_rng); 143 147 } 144 148 module_init(init);