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>
This commit is contained in:
		
							parent
							
								
									e75279c4fb
								
							
						
					
					
						commit
						08e53fbdb8
					
				| @ -25,88 +25,108 @@ | ||||
| #include <linux/virtio_rng.h> | ||||
| #include <linux/module.h> | ||||
| 
 | ||||
| static struct virtqueue *vq; | ||||
| static unsigned int data_avail; | ||||
| static DECLARE_COMPLETION(have_data); | ||||
| static bool busy; | ||||
| 
 | ||||
| struct virtrng_info { | ||||
| 	struct virtio_device *vdev; | ||||
| 	struct hwrng hwrng; | ||||
| 	struct virtqueue *vq; | ||||
| 	unsigned int data_avail; | ||||
| 	struct completion have_data; | ||||
| 	bool busy; | ||||
| }; | ||||
| 
 | ||||
| static void random_recv_done(struct virtqueue *vq) | ||||
| { | ||||
| 	struct virtrng_info *vi = vq->vdev->priv; | ||||
| 
 | ||||
| 	/* We can get spurious callbacks, e.g. shared IRQs + virtio_pci. */ | ||||
| 	if (!virtqueue_get_buf(vq, &data_avail)) | ||||
| 	if (!virtqueue_get_buf(vi->vq, &vi->data_avail)) | ||||
| 		return; | ||||
| 
 | ||||
| 	complete(&have_data); | ||||
| 	complete(&vi->have_data); | ||||
| } | ||||
| 
 | ||||
| /* The host will fill any buffer we give it with sweet, sweet randomness. */ | ||||
| static void register_buffer(u8 *buf, size_t size) | ||||
| static void register_buffer(struct virtrng_info *vi, u8 *buf, size_t size) | ||||
| { | ||||
| 	struct scatterlist sg; | ||||
| 
 | ||||
| 	sg_init_one(&sg, buf, size); | ||||
| 
 | ||||
| 	/* There should always be room for one buffer. */ | ||||
| 	virtqueue_add_inbuf(vq, &sg, 1, buf, GFP_KERNEL); | ||||
| 	virtqueue_add_inbuf(vi->vq, &sg, 1, buf, GFP_KERNEL); | ||||
| 
 | ||||
| 	virtqueue_kick(vq); | ||||
| 	virtqueue_kick(vi->vq); | ||||
| } | ||||
| 
 | ||||
| static int virtio_read(struct hwrng *rng, void *buf, size_t size, bool wait) | ||||
| { | ||||
| 	int ret; | ||||
| 	struct virtrng_info *vi = (struct virtrng_info *)rng->priv; | ||||
| 
 | ||||
| 	if (!busy) { | ||||
| 		busy = true; | ||||
| 		init_completion(&have_data); | ||||
| 		register_buffer(buf, size); | ||||
| 	if (!vi->busy) { | ||||
| 		vi->busy = true; | ||||
| 		init_completion(&vi->have_data); | ||||
| 		register_buffer(vi, buf, size); | ||||
| 	} | ||||
| 
 | ||||
| 	if (!wait) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	ret = wait_for_completion_killable(&have_data); | ||||
| 	ret = wait_for_completion_killable(&vi->have_data); | ||||
| 	if (ret < 0) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	busy = false; | ||||
| 	vi->busy = false; | ||||
| 
 | ||||
| 	return data_avail; | ||||
| 	return vi->data_avail; | ||||
| } | ||||
| 
 | ||||
| static void virtio_cleanup(struct hwrng *rng) | ||||
| { | ||||
| 	if (busy) | ||||
| 		wait_for_completion(&have_data); | ||||
| 	struct virtrng_info *vi = (struct virtrng_info *)rng->priv; | ||||
| 
 | ||||
| 	if (vi->busy) | ||||
| 		wait_for_completion(&vi->have_data); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static struct hwrng virtio_hwrng = { | ||||
| 	.name		= "virtio", | ||||
| 	.cleanup	= virtio_cleanup, | ||||
| 	.read		= virtio_read, | ||||
| }; | ||||
| 
 | ||||
| static int probe_common(struct virtio_device *vdev) | ||||
| { | ||||
| 	int err; | ||||
| 	int err, i; | ||||
| 	struct virtrng_info *vi = NULL; | ||||
| 
 | ||||
| 	vi = kmalloc(sizeof(struct virtrng_info), GFP_KERNEL); | ||||
| 	vi->hwrng.name = kmalloc(40, GFP_KERNEL); | ||||
| 	init_completion(&vi->have_data); | ||||
| 
 | ||||
| 	vi->hwrng.read = virtio_read; | ||||
| 	vi->hwrng.cleanup = virtio_cleanup; | ||||
| 	vi->hwrng.priv = (unsigned long)vi; | ||||
| 	vdev->priv = vi; | ||||
| 
 | ||||
| 	if (vq) { | ||||
| 		/* We only support one device for now */ | ||||
| 		return -EBUSY; | ||||
| 	} | ||||
| 	/* We expect a single virtqueue. */ | ||||
| 	vq = virtio_find_single_vq(vdev, random_recv_done, "input"); | ||||
| 	if (IS_ERR(vq)) { | ||||
| 		err = PTR_ERR(vq); | ||||
| 		vq = NULL; | ||||
| 	vi->vq = virtio_find_single_vq(vdev, random_recv_done, "input"); | ||||
| 	if (IS_ERR(vi->vq)) { | ||||
| 		err = PTR_ERR(vi->vq); | ||||
| 		kfree(vi->hwrng.name); | ||||
| 		vi->vq = NULL; | ||||
| 		kfree(vi); | ||||
| 		vi = NULL; | ||||
| 		return err; | ||||
| 	} | ||||
| 
 | ||||
| 	err = hwrng_register(&virtio_hwrng); | ||||
| 	i = 0; | ||||
| 	do { | ||||
| 		sprintf(vi->hwrng.name, "virtio_rng.%d", i++); | ||||
| 		err = hwrng_register(&vi->hwrng); | ||||
| 	} while (err == -EEXIST); | ||||
| 
 | ||||
| 	if (err) { | ||||
| 		vdev->config->del_vqs(vdev); | ||||
| 		vq = NULL; | ||||
| 		kfree(vi->hwrng.name); | ||||
| 		vi->vq = NULL; | ||||
| 		kfree(vi); | ||||
| 		vi = NULL; | ||||
| 		return err; | ||||
| 	} | ||||
| 
 | ||||
| @ -115,11 +135,15 @@ static int probe_common(struct virtio_device *vdev) | ||||
| 
 | ||||
| static void remove_common(struct virtio_device *vdev) | ||||
| { | ||||
| 	struct virtrng_info *vi = vdev->priv; | ||||
| 	vdev->config->reset(vdev); | ||||
| 	busy = false; | ||||
| 	hwrng_unregister(&virtio_hwrng); | ||||
| 	vi->busy = false; | ||||
| 	hwrng_unregister(&vi->hwrng); | ||||
| 	vdev->config->del_vqs(vdev); | ||||
| 	vq = NULL; | ||||
| 	kfree(vi->hwrng.name); | ||||
| 	vi->vq = NULL; | ||||
| 	kfree(vi); | ||||
| 	vi = NULL; | ||||
| } | ||||
| 
 | ||||
| static int virtrng_probe(struct virtio_device *vdev) | ||||
|  | ||||
| @ -31,7 +31,7 @@ | ||||
|  * @priv:		Private data, for use by the RNG driver. | ||||
|  */ | ||||
| struct hwrng { | ||||
| 	const char *name; | ||||
| 	char *name; | ||||
| 	int (*init)(struct hwrng *rng); | ||||
| 	void (*cleanup)(struct hwrng *rng); | ||||
| 	int (*data_present)(struct hwrng *rng, int wait); | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user