aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/char
diff options
context:
space:
mode:
authorAmos Kong <akong@redhat.com>2014-05-13 21:03:46 -0400
committerRusty Russell <rusty@rustcorp.com.au>2014-05-13 21:20:34 -0400
commit08e53fbdb85c0f6f45c0f7c1ea3defc1752a95ce (patch)
treeaff27f2f0cc164159387e4566d9aaf182048cc95 /drivers/char
parente75279c4fb853f42004cbabb6dbf6b23188dc163 (diff)
virtio-rng: support multiple virtio-rng devices
Current hwrng core supports to register multiple hwrng devices, and there is only one device really works in the same time. QEMU alsu supports to have multiple virtio-rng backends. This patch changes virtio-rng driver to support multiple virtio-rng devices. ]# cat /sys/class/misc/hw_random/rng_available virtio_rng.0 virtio_rng.1 ]# cat /sys/class/misc/hw_random/rng_current virtio_rng.0 ]# echo -n virtio_rng.1 > /sys/class/misc/hw_random/rng_current ]# dd if=/dev/hwrng of=/dev/null Signed-off-by: Amos Kong <akong@redhat.com> Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Diffstat (limited to 'drivers/char')
-rw-r--r--drivers/char/hw_random/virtio-rng.c102
1 files changed, 63 insertions, 39 deletions
diff --git a/drivers/char/hw_random/virtio-rng.c b/drivers/char/hw_random/virtio-rng.c
index 2ce0e225e58c..12e242bbb0f5 100644
--- a/drivers/char/hw_random/virtio-rng.c
+++ b/drivers/char/hw_random/virtio-rng.c
@@ -25,88 +25,108 @@
25#include <linux/virtio_rng.h> 25#include <linux/virtio_rng.h>
26#include <linux/module.h> 26#include <linux/module.h>
27 27
28static struct virtqueue *vq; 28
29static unsigned int data_avail; 29struct virtrng_info {
30static DECLARE_COMPLETION(have_data); 30 struct virtio_device *vdev;
31static bool busy; 31 struct hwrng hwrng;
32 struct virtqueue *vq;
33 unsigned int data_avail;
34 struct completion have_data;
35 bool busy;
36};
32 37
33static void random_recv_done(struct virtqueue *vq) 38static void random_recv_done(struct virtqueue *vq)
34{ 39{
40 struct virtrng_info *vi = vq->vdev->priv;
41
35 /* We can get spurious callbacks, e.g. shared IRQs + virtio_pci. */ 42 /* We can get spurious callbacks, e.g. shared IRQs + virtio_pci. */
36 if (!virtqueue_get_buf(vq, &data_avail)) 43 if (!virtqueue_get_buf(vi->vq, &vi->data_avail))
37 return; 44 return;
38 45
39 complete(&have_data); 46 complete(&vi->have_data);
40} 47}
41 48
42/* The host will fill any buffer we give it with sweet, sweet randomness. */ 49/* The host will fill any buffer we give it with sweet, sweet randomness. */
43static void register_buffer(u8 *buf, size_t size) 50static void register_buffer(struct virtrng_info *vi, u8 *buf, size_t size)
44{ 51{
45 struct scatterlist sg; 52 struct scatterlist sg;
46 53
47 sg_init_one(&sg, buf, size); 54 sg_init_one(&sg, buf, size);
48 55
49 /* There should always be room for one buffer. */ 56 /* There should always be room for one buffer. */
50 virtqueue_add_inbuf(vq, &sg, 1, buf, GFP_KERNEL); 57 virtqueue_add_inbuf(vi->vq, &sg, 1, buf, GFP_KERNEL);
51 58
52 virtqueue_kick(vq); 59 virtqueue_kick(vi->vq);
53} 60}
54 61
55static int virtio_read(struct hwrng *rng, void *buf, size_t size, bool wait) 62static int virtio_read(struct hwrng *rng, void *buf, size_t size, bool wait)
56{ 63{
57 int ret; 64 int ret;
65 struct virtrng_info *vi = (struct virtrng_info *)rng->priv;
58 66
59 if (!busy) { 67 if (!vi->busy) {
60 busy = true; 68 vi->busy = true;
61 init_completion(&have_data); 69 init_completion(&vi->have_data);
62 register_buffer(buf, size); 70 register_buffer(vi, buf, size);
63 } 71 }
64 72
65 if (!wait) 73 if (!wait)
66 return 0; 74 return 0;
67 75
68 ret = wait_for_completion_killable(&have_data); 76 ret = wait_for_completion_killable(&vi->have_data);
69 if (ret < 0) 77 if (ret < 0)
70 return ret; 78 return ret;
71 79
72 busy = false; 80 vi->busy = false;
73 81
74 return data_avail; 82 return vi->data_avail;
75} 83}
76 84
77static void virtio_cleanup(struct hwrng *rng) 85static void virtio_cleanup(struct hwrng *rng)
78{ 86{
79 if (busy) 87 struct virtrng_info *vi = (struct virtrng_info *)rng->priv;
80 wait_for_completion(&have_data);
81}
82
83 88
84static struct hwrng virtio_hwrng = { 89 if (vi->busy)
85 .name = "virtio", 90 wait_for_completion(&vi->have_data);
86 .cleanup = virtio_cleanup, 91}
87 .read = virtio_read,
88};
89 92
90static int probe_common(struct virtio_device *vdev) 93static int probe_common(struct virtio_device *vdev)
91{ 94{
92 int err; 95 int err, i;
96 struct virtrng_info *vi = NULL;
97
98 vi = kmalloc(sizeof(struct virtrng_info), GFP_KERNEL);
99 vi->hwrng.name = kmalloc(40, GFP_KERNEL);
100 init_completion(&vi->have_data);
101
102 vi->hwrng.read = virtio_read;
103 vi->hwrng.cleanup = virtio_cleanup;
104 vi->hwrng.priv = (unsigned long)vi;
105 vdev->priv = vi;
93 106
94 if (vq) {
95 /* We only support one device for now */
96 return -EBUSY;
97 }
98 /* We expect a single virtqueue. */ 107 /* We expect a single virtqueue. */
99 vq = virtio_find_single_vq(vdev, random_recv_done, "input"); 108 vi->vq = virtio_find_single_vq(vdev, random_recv_done, "input");
100 if (IS_ERR(vq)) { 109 if (IS_ERR(vi->vq)) {
101 err = PTR_ERR(vq); 110 err = PTR_ERR(vi->vq);
102 vq = NULL; 111 kfree(vi->hwrng.name);
112 vi->vq = NULL;
113 kfree(vi);
114 vi = NULL;
103 return err; 115 return err;
104 } 116 }
105 117
106 err = hwrng_register(&virtio_hwrng); 118 i = 0;
119 do {
120 sprintf(vi->hwrng.name, "virtio_rng.%d", i++);
121 err = hwrng_register(&vi->hwrng);
122 } while (err == -EEXIST);
123
107 if (err) { 124 if (err) {
108 vdev->config->del_vqs(vdev); 125 vdev->config->del_vqs(vdev);
109 vq = NULL; 126 kfree(vi->hwrng.name);
127 vi->vq = NULL;
128 kfree(vi);
129 vi = NULL;
110 return err; 130 return err;
111 } 131 }
112 132
@@ -115,11 +135,15 @@ static int probe_common(struct virtio_device *vdev)
115 135
116static void remove_common(struct virtio_device *vdev) 136static void remove_common(struct virtio_device *vdev)
117{ 137{
138 struct virtrng_info *vi = vdev->priv;
118 vdev->config->reset(vdev); 139 vdev->config->reset(vdev);
119 busy = false; 140 vi->busy = false;
120 hwrng_unregister(&virtio_hwrng); 141 hwrng_unregister(&vi->hwrng);
121 vdev->config->del_vqs(vdev); 142 vdev->config->del_vqs(vdev);
122 vq = NULL; 143 kfree(vi->hwrng.name);
144 vi->vq = NULL;
145 kfree(vi);
146 vi = NULL;
123} 147}
124 148
125static int virtrng_probe(struct virtio_device *vdev) 149static int virtrng_probe(struct virtio_device *vdev)