aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/char/hw_random/virtio-rng.c
diff options
context:
space:
mode:
authorRusty Russell <rusty@rustcorp.com.au>2009-06-13 00:16:39 -0400
committerRusty Russell <rusty@rustcorp.com.au>2009-06-12 08:46:40 -0400
commit594de1dd6449f79c99e1ba4577ea0e4e06e2b405 (patch)
treeeeaa70736d1aa2b40433d5dc5c98195406591d85 /drivers/char/hw_random/virtio-rng.c
parent98e94444748e9af93423d1fab90543e75569a58c (diff)
virtio: handle short buffers in virtio_rng.
If the device fills less than 4 bytes of our random buffer, we'll BUG_ON. It's nicer to handle the case where it partially fills the buffer (the protocol doesn't explicitly bad that). Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Diffstat (limited to 'drivers/char/hw_random/virtio-rng.c')
-rw-r--r--drivers/char/hw_random/virtio-rng.c24
1 files changed, 16 insertions, 8 deletions
diff --git a/drivers/char/hw_random/virtio-rng.c b/drivers/char/hw_random/virtio-rng.c
index f2041fede822..32216b623248 100644
--- a/drivers/char/hw_random/virtio-rng.c
+++ b/drivers/char/hw_random/virtio-rng.c
@@ -35,13 +35,13 @@ static DECLARE_COMPLETION(have_data);
35 35
36static void random_recv_done(struct virtqueue *vq) 36static void random_recv_done(struct virtqueue *vq)
37{ 37{
38 int len; 38 unsigned int len;
39 39
40 /* We can get spurious callbacks, e.g. shared IRQs + virtio_pci. */ 40 /* We can get spurious callbacks, e.g. shared IRQs + virtio_pci. */
41 if (!vq->vq_ops->get_buf(vq, &len)) 41 if (!vq->vq_ops->get_buf(vq, &len))
42 return; 42 return;
43 43
44 data_left = len / sizeof(random_data[0]); 44 data_left += len;
45 complete(&have_data); 45 complete(&have_data);
46} 46}
47 47
@@ -49,7 +49,7 @@ static void register_buffer(void)
49{ 49{
50 struct scatterlist sg; 50 struct scatterlist sg;
51 51
52 sg_init_one(&sg, random_data, RANDOM_DATA_SIZE); 52 sg_init_one(&sg, random_data+data_left, RANDOM_DATA_SIZE-data_left);
53 /* There should always be room for one buffer. */ 53 /* There should always be room for one buffer. */
54 if (vq->vq_ops->add_buf(vq, &sg, 0, 1, random_data) != 0) 54 if (vq->vq_ops->add_buf(vq, &sg, 0, 1, random_data) != 0)
55 BUG(); 55 BUG();
@@ -59,24 +59,32 @@ static void register_buffer(void)
59/* At least we don't udelay() in a loop like some other drivers. */ 59/* At least we don't udelay() in a loop like some other drivers. */
60static int virtio_data_present(struct hwrng *rng, int wait) 60static int virtio_data_present(struct hwrng *rng, int wait)
61{ 61{
62 if (data_left) 62 if (data_left >= sizeof(u32))
63 return 1; 63 return 1;
64 64
65again:
65 if (!wait) 66 if (!wait)
66 return 0; 67 return 0;
67 68
68 wait_for_completion(&have_data); 69 wait_for_completion(&have_data);
70
71 /* Not enough? Re-register. */
72 if (unlikely(data_left < sizeof(u32))) {
73 register_buffer();
74 goto again;
75 }
76
69 return 1; 77 return 1;
70} 78}
71 79
72/* virtio_data_present() must have succeeded before this is called. */ 80/* virtio_data_present() must have succeeded before this is called. */
73static int virtio_data_read(struct hwrng *rng, u32 *data) 81static int virtio_data_read(struct hwrng *rng, u32 *data)
74{ 82{
75 BUG_ON(!data_left); 83 BUG_ON(data_left < sizeof(u32));
76 84 data_left -= sizeof(u32);
77 *data = random_data[--data_left]; 85 *data = random_data[data_left / 4];
78 86
79 if (!data_left) { 87 if (data_left < sizeof(u32)) {
80 init_completion(&have_data); 88 init_completion(&have_data);
81 register_buffer(); 89 register_buffer();
82 } 90 }