diff options
author | Amos Kong <akong@redhat.com> | 2014-05-13 21:03:46 -0400 |
---|---|---|
committer | Rusty Russell <rusty@rustcorp.com.au> | 2014-05-13 21:20:34 -0400 |
commit | 08e53fbdb85c0f6f45c0f7c1ea3defc1752a95ce (patch) | |
tree | aff27f2f0cc164159387e4566d9aaf182048cc95 /drivers/char | |
parent | e75279c4fb853f42004cbabb6dbf6b23188dc163 (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.c | 102 |
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 | ||
28 | static struct virtqueue *vq; | 28 | |
29 | static unsigned int data_avail; | 29 | struct virtrng_info { |
30 | static DECLARE_COMPLETION(have_data); | 30 | struct virtio_device *vdev; |
31 | static 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 | ||
33 | static void random_recv_done(struct virtqueue *vq) | 38 | static 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. */ |
43 | static void register_buffer(u8 *buf, size_t size) | 50 | static 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 | ||
55 | static int virtio_read(struct hwrng *rng, void *buf, size_t size, bool wait) | 62 | static 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 | ||
77 | static void virtio_cleanup(struct hwrng *rng) | 85 | static 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 | ||
84 | static 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 | ||
90 | static int probe_common(struct virtio_device *vdev) | 93 | static 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 | ||
116 | static void remove_common(struct virtio_device *vdev) | 136 | static 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 | ||
125 | static int virtrng_probe(struct virtio_device *vdev) | 149 | static int virtrng_probe(struct virtio_device *vdev) |