diff options
Diffstat (limited to 'drivers/char')
-rw-r--r-- | drivers/char/hw_random/virtio-rng.c | 78 |
1 files changed, 27 insertions, 51 deletions
diff --git a/drivers/char/hw_random/virtio-rng.c b/drivers/char/hw_random/virtio-rng.c index 915157fcff98..bdaef8e94021 100644 --- a/drivers/char/hw_random/virtio-rng.c +++ b/drivers/char/hw_random/virtio-rng.c @@ -16,6 +16,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + #include <linux/err.h> #include <linux/hw_random.h> #include <linux/scatterlist.h> @@ -23,78 +24,64 @@ #include <linux/virtio.h> #include <linux/virtio_rng.h> -/* The host will fill any buffer we give it with sweet, sweet randomness. We - * give it 64 bytes at a time, and the hwrng framework takes it 4 bytes at a - * time. */ -#define RANDOM_DATA_SIZE 64 - static struct virtqueue *vq; -static u32 *random_data; -static unsigned int data_left; +static unsigned int data_avail; static DECLARE_COMPLETION(have_data); +static bool busy; static void random_recv_done(struct virtqueue *vq) { - unsigned int len; - /* We can get spurious callbacks, e.g. shared IRQs + virtio_pci. */ - if (!vq->vq_ops->get_buf(vq, &len)) + if (!vq->vq_ops->get_buf(vq, &data_avail)) return; - data_left += len; complete(&have_data); } -static void register_buffer(void) +/* The host will fill any buffer we give it with sweet, sweet randomness. */ +static void register_buffer(u8 *buf, size_t size) { struct scatterlist sg; - sg_init_one(&sg, random_data+data_left, RANDOM_DATA_SIZE-data_left); + sg_init_one(&sg, buf, size); + /* There should always be room for one buffer. */ - if (vq->vq_ops->add_buf(vq, &sg, 0, 1, random_data) < 0) + if (vq->vq_ops->add_buf(vq, &sg, 0, 1, buf) < 0) BUG(); + vq->vq_ops->kick(vq); } -/* At least we don't udelay() in a loop like some other drivers. */ -static int virtio_data_present(struct hwrng *rng, int wait) +static int virtio_read(struct hwrng *rng, void *buf, size_t size, bool wait) { - if (data_left >= sizeof(u32)) - return 1; -again: + if (!busy) { + busy = true; + init_completion(&have_data); + register_buffer(buf, size); + } + if (!wait) return 0; wait_for_completion(&have_data); - /* Not enough? Re-register. */ - if (unlikely(data_left < sizeof(u32))) { - register_buffer(); - goto again; - } + busy = false; - return 1; + return data_avail; } -/* virtio_data_present() must have succeeded before this is called. */ -static int virtio_data_read(struct hwrng *rng, u32 *data) +static void virtio_cleanup(struct hwrng *rng) { - BUG_ON(data_left < sizeof(u32)); - data_left -= sizeof(u32); - *data = random_data[data_left / 4]; - - if (data_left < sizeof(u32)) { - init_completion(&have_data); - register_buffer(); - } - return sizeof(*data); + if (busy) + wait_for_completion(&have_data); } + static struct hwrng virtio_hwrng = { - .name = "virtio", - .data_present = virtio_data_present, - .data_read = virtio_data_read, + .name = "virtio", + .cleanup = virtio_cleanup, + .read = virtio_read, }; static int virtrng_probe(struct virtio_device *vdev) @@ -112,7 +99,6 @@ static int virtrng_probe(struct virtio_device *vdev) return err; } - register_buffer(); return 0; } @@ -138,21 +124,11 @@ static struct virtio_driver virtio_rng = { static int __init init(void) { - int err; - - random_data = kmalloc(RANDOM_DATA_SIZE, GFP_KERNEL); - if (!random_data) - return -ENOMEM; - - err = register_virtio_driver(&virtio_rng); - if (err) - kfree(random_data); - return err; + return register_virtio_driver(&virtio_rng); } static void __exit fini(void) { - kfree(random_data); unregister_virtio_driver(&virtio_rng); } module_init(init); |