Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v5.11 219 lines 4.5 kB view raw
1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * Randomness driver for virtio 4 * Copyright (C) 2007, 2008 Rusty Russell IBM Corporation 5 */ 6 7#include <linux/err.h> 8#include <linux/hw_random.h> 9#include <linux/scatterlist.h> 10#include <linux/spinlock.h> 11#include <linux/virtio.h> 12#include <linux/virtio_rng.h> 13#include <linux/module.h> 14#include <linux/slab.h> 15 16static DEFINE_IDA(rng_index_ida); 17 18struct virtrng_info { 19 struct hwrng hwrng; 20 struct virtqueue *vq; 21 struct completion have_data; 22 char name[25]; 23 unsigned int data_avail; 24 int index; 25 bool busy; 26 bool hwrng_register_done; 27 bool hwrng_removed; 28}; 29 30static void random_recv_done(struct virtqueue *vq) 31{ 32 struct virtrng_info *vi = vq->vdev->priv; 33 34 /* We can get spurious callbacks, e.g. shared IRQs + virtio_pci. */ 35 if (!virtqueue_get_buf(vi->vq, &vi->data_avail)) 36 return; 37 38 complete(&vi->have_data); 39} 40 41/* The host will fill any buffer we give it with sweet, sweet randomness. */ 42static void register_buffer(struct virtrng_info *vi, u8 *buf, size_t size) 43{ 44 struct scatterlist sg; 45 46 sg_init_one(&sg, buf, size); 47 48 /* There should always be room for one buffer. */ 49 virtqueue_add_inbuf(vi->vq, &sg, 1, buf, GFP_KERNEL); 50 51 virtqueue_kick(vi->vq); 52} 53 54static int virtio_read(struct hwrng *rng, void *buf, size_t size, bool wait) 55{ 56 int ret; 57 struct virtrng_info *vi = (struct virtrng_info *)rng->priv; 58 59 if (vi->hwrng_removed) 60 return -ENODEV; 61 62 if (!vi->busy) { 63 vi->busy = true; 64 reinit_completion(&vi->have_data); 65 register_buffer(vi, buf, size); 66 } 67 68 if (!wait) 69 return 0; 70 71 ret = wait_for_completion_killable(&vi->have_data); 72 if (ret < 0) 73 return ret; 74 75 vi->busy = false; 76 77 return vi->data_avail; 78} 79 80static void virtio_cleanup(struct hwrng *rng) 81{ 82 struct virtrng_info *vi = (struct virtrng_info *)rng->priv; 83 84 if (vi->busy) 85 wait_for_completion(&vi->have_data); 86} 87 88static int probe_common(struct virtio_device *vdev) 89{ 90 int err, index; 91 struct virtrng_info *vi = NULL; 92 93 vi = kzalloc(sizeof(struct virtrng_info), GFP_KERNEL); 94 if (!vi) 95 return -ENOMEM; 96 97 vi->index = index = ida_simple_get(&rng_index_ida, 0, 0, GFP_KERNEL); 98 if (index < 0) { 99 err = index; 100 goto err_ida; 101 } 102 sprintf(vi->name, "virtio_rng.%d", index); 103 init_completion(&vi->have_data); 104 105 vi->hwrng = (struct hwrng) { 106 .read = virtio_read, 107 .cleanup = virtio_cleanup, 108 .priv = (unsigned long)vi, 109 .name = vi->name, 110 .quality = 1000, 111 }; 112 vdev->priv = vi; 113 114 /* We expect a single virtqueue. */ 115 vi->vq = virtio_find_single_vq(vdev, random_recv_done, "input"); 116 if (IS_ERR(vi->vq)) { 117 err = PTR_ERR(vi->vq); 118 goto err_find; 119 } 120 121 return 0; 122 123err_find: 124 ida_simple_remove(&rng_index_ida, index); 125err_ida: 126 kfree(vi); 127 return err; 128} 129 130static void remove_common(struct virtio_device *vdev) 131{ 132 struct virtrng_info *vi = vdev->priv; 133 134 vi->hwrng_removed = true; 135 vi->data_avail = 0; 136 complete(&vi->have_data); 137 vdev->config->reset(vdev); 138 vi->busy = false; 139 if (vi->hwrng_register_done) 140 hwrng_unregister(&vi->hwrng); 141 vdev->config->del_vqs(vdev); 142 ida_simple_remove(&rng_index_ida, vi->index); 143 kfree(vi); 144} 145 146static int virtrng_probe(struct virtio_device *vdev) 147{ 148 return probe_common(vdev); 149} 150 151static void virtrng_remove(struct virtio_device *vdev) 152{ 153 remove_common(vdev); 154} 155 156static void virtrng_scan(struct virtio_device *vdev) 157{ 158 struct virtrng_info *vi = vdev->priv; 159 int err; 160 161 err = hwrng_register(&vi->hwrng); 162 if (!err) 163 vi->hwrng_register_done = true; 164} 165 166#ifdef CONFIG_PM_SLEEP 167static int virtrng_freeze(struct virtio_device *vdev) 168{ 169 remove_common(vdev); 170 return 0; 171} 172 173static int virtrng_restore(struct virtio_device *vdev) 174{ 175 int err; 176 177 err = probe_common(vdev); 178 if (!err) { 179 struct virtrng_info *vi = vdev->priv; 180 181 /* 182 * Set hwrng_removed to ensure that virtio_read() 183 * does not block waiting for data before the 184 * registration is complete. 185 */ 186 vi->hwrng_removed = true; 187 err = hwrng_register(&vi->hwrng); 188 if (!err) { 189 vi->hwrng_register_done = true; 190 vi->hwrng_removed = false; 191 } 192 } 193 194 return err; 195} 196#endif 197 198static const struct virtio_device_id id_table[] = { 199 { VIRTIO_ID_RNG, VIRTIO_DEV_ANY_ID }, 200 { 0 }, 201}; 202 203static struct virtio_driver virtio_rng_driver = { 204 .driver.name = KBUILD_MODNAME, 205 .driver.owner = THIS_MODULE, 206 .id_table = id_table, 207 .probe = virtrng_probe, 208 .remove = virtrng_remove, 209 .scan = virtrng_scan, 210#ifdef CONFIG_PM_SLEEP 211 .freeze = virtrng_freeze, 212 .restore = virtrng_restore, 213#endif 214}; 215 216module_virtio_driver(virtio_rng_driver); 217MODULE_DEVICE_TABLE(virtio, id_table); 218MODULE_DESCRIPTION("Virtio random number driver"); 219MODULE_LICENSE("GPL");