aboutsummaryrefslogtreecommitdiff
path: root/drivers/char
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/char')
-rw-r--r--drivers/char/hw_random/core.c47
-rw-r--r--drivers/char/hw_random/virtio-rng.c115
-rw-r--r--drivers/char/i8k.c4
-rw-r--r--drivers/char/random.c30
-rw-r--r--drivers/char/raw.c8
5 files changed, 144 insertions, 60 deletions
diff --git a/drivers/char/hw_random/core.c b/drivers/char/hw_random/core.c
index 334601cc81c..c4419ea1ab0 100644
--- a/drivers/char/hw_random/core.c
+++ b/drivers/char/hw_random/core.c
@@ -55,16 +55,41 @@ static DEFINE_MUTEX(rng_mutex);
static int data_avail;
static u8 *rng_buffer;
+static inline int rng_get_data(struct hwrng *rng, u8 *buffer, size_t size,
+ int wait);
+
static size_t rng_buffer_size(void)
{
return SMP_CACHE_BYTES < 32 ? 32 : SMP_CACHE_BYTES;
}
+static void add_early_randomness(struct hwrng *rng)
+{
+ unsigned char bytes[16];
+ int bytes_read;
+
+ /*
+ * Currently only virtio-rng cannot return data during device
+ * probe, and that's handled in virtio-rng.c itself. If there
+ * are more such devices, this call to rng_get_data can be
+ * made conditional here instead of doing it per-device.
+ */
+ bytes_read = rng_get_data(rng, bytes, sizeof(bytes), 1);
+ if (bytes_read > 0)
+ add_device_randomness(bytes, bytes_read);
+}
+
static inline int hwrng_init(struct hwrng *rng)
{
- if (!rng->init)
- return 0;
- return rng->init(rng);
+ if (rng->init) {
+ int ret;
+
+ ret = rng->init(rng);
+ if (ret)
+ return ret;
+ }
+ add_early_randomness(rng);
+ return 0;
}
static inline void hwrng_cleanup(struct hwrng *rng)
@@ -304,8 +329,6 @@ int hwrng_register(struct hwrng *rng)
{
int err = -EINVAL;
struct hwrng *old_rng, *tmp;
- unsigned char bytes[16];
- int bytes_read;
if (rng->name == NULL ||
(rng->data_read == NULL && rng->read == NULL))
@@ -347,9 +370,17 @@ int hwrng_register(struct hwrng *rng)
INIT_LIST_HEAD(&rng->list);
list_add_tail(&rng->list, &rng_list);
- bytes_read = rng_get_data(rng, bytes, sizeof(bytes), 1);
- if (bytes_read > 0)
- add_device_randomness(bytes, bytes_read);
+ if (old_rng && !rng->init) {
+ /*
+ * Use a new device's input to add some randomness to
+ * the system. If this rng device isn't going to be
+ * used right away, its init function hasn't been
+ * called yet; so only use the randomness from devices
+ * that don't need an init callback.
+ */
+ add_early_randomness(rng);
+ }
+
out_unlock:
mutex_unlock(&rng_mutex);
out:
diff --git a/drivers/char/hw_random/virtio-rng.c b/drivers/char/hw_random/virtio-rng.c
index 2ce0e225e58..e9b15bc18b4 100644
--- a/drivers/char/hw_random/virtio-rng.c
+++ b/drivers/char/hw_random/virtio-rng.c
@@ -25,101 +25,140 @@
#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;
+static DEFINE_IDA(rng_index_ida);
+
+struct virtrng_info {
+ struct virtio_device *vdev;
+ struct hwrng hwrng;
+ struct virtqueue *vq;
+ unsigned int data_avail;
+ struct completion have_data;
+ bool busy;
+ char name[25];
+ int index;
+};
+
+static bool probe_done;
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);
+ /*
+ * Don't ask host for data till we're setup. This call can
+ * happen during hwrng_register(), after commit d9e7972619.
+ */
+ if (unlikely(!probe_done))
+ return 0;
+
+ 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;
-
-static struct hwrng virtio_hwrng = {
- .name = "virtio",
- .cleanup = virtio_cleanup,
- .read = virtio_read,
-};
+ if (vi->busy)
+ wait_for_completion(&vi->have_data);
+}
static int probe_common(struct virtio_device *vdev)
{
- int err;
+ int err, index;
+ struct virtrng_info *vi = NULL;
- if (vq) {
- /* We only support one device for now */
- return -EBUSY;
+ vi = kzalloc(sizeof(struct virtrng_info), GFP_KERNEL);
+ if (!vi)
+ return -ENOMEM;
+
+ vi->index = index = ida_simple_get(&rng_index_ida, 0, 0, GFP_KERNEL);
+ if (index < 0) {
+ kfree(vi);
+ return index;
}
+ sprintf(vi->name, "virtio_rng.%d", index);
+ init_completion(&vi->have_data);
+
+ vi->hwrng = (struct hwrng) {
+ .read = virtio_read,
+ .cleanup = virtio_cleanup,
+ .priv = (unsigned long)vi,
+ .name = vi->name,
+ };
+ vdev->priv = vi;
+
/* 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);
+ vi->vq = NULL;
+ kfree(vi);
+ ida_simple_remove(&rng_index_ida, index);
return err;
}
- err = hwrng_register(&virtio_hwrng);
+ err = hwrng_register(&vi->hwrng);
if (err) {
vdev->config->del_vqs(vdev);
- vq = NULL;
+ vi->vq = NULL;
+ kfree(vi);
+ ida_simple_remove(&rng_index_ida, index);
return err;
}
+ probe_done = true;
return 0;
}
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;
+ ida_simple_remove(&rng_index_ida, vi->index);
+ kfree(vi);
}
static int virtrng_probe(struct virtio_device *vdev)
diff --git a/drivers/char/i8k.c b/drivers/char/i8k.c
index d915707d2ba..93dcad0c1cb 100644
--- a/drivers/char/i8k.c
+++ b/drivers/char/i8k.c
@@ -138,7 +138,9 @@ static int i8k_smm(struct smm_regs *regs)
if (!alloc_cpumask_var(&old_mask, GFP_KERNEL))
return -ENOMEM;
cpumask_copy(old_mask, &current->cpus_allowed);
- set_cpus_allowed_ptr(current, cpumask_of(0));
+ rc = set_cpus_allowed_ptr(current, cpumask_of(0));
+ if (rc)
+ goto out;
if (smp_processor_id() != 0) {
rc = -EBUSY;
goto out;
diff --git a/drivers/char/random.c b/drivers/char/random.c
index 4ad71ef2cd5..71529e196b8 100644
--- a/drivers/char/random.c
+++ b/drivers/char/random.c
@@ -641,7 +641,7 @@ retry:
} while (unlikely(entropy_count < pool_size-2 && pnfrac));
}
- if (entropy_count < 0) {
+ if (unlikely(entropy_count < 0)) {
pr_warn("random: negative entropy/overflow: pool %s count %d\n",
r->name, entropy_count);
WARN_ON(1);
@@ -980,26 +980,37 @@ static void push_to_pool(struct work_struct *work)
static size_t account(struct entropy_store *r, size_t nbytes, int min,
int reserved)
{
- int have_bytes;
int entropy_count, orig;
- size_t ibytes;
+ size_t ibytes, nfrac;
BUG_ON(r->entropy_count > r->poolinfo->poolfracbits);
/* Can we pull enough? */
retry:
entropy_count = orig = ACCESS_ONCE(r->entropy_count);
- have_bytes = entropy_count >> (ENTROPY_SHIFT + 3);
ibytes = nbytes;
/* If limited, never pull more than available */
- if (r->limit)
- ibytes = min_t(size_t, ibytes, have_bytes - reserved);
+ if (r->limit) {
+ int have_bytes = entropy_count >> (ENTROPY_SHIFT + 3);
+
+ if ((have_bytes -= reserved) < 0)
+ have_bytes = 0;
+ ibytes = min_t(size_t, ibytes, have_bytes);
+ }
if (ibytes < min)
ibytes = 0;
- if (have_bytes >= ibytes + reserved)
- entropy_count -= ibytes << (ENTROPY_SHIFT + 3);
+
+ if (unlikely(entropy_count < 0)) {
+ pr_warn("random: negative entropy count: pool %s count %d\n",
+ r->name, entropy_count);
+ WARN_ON(1);
+ entropy_count = 0;
+ }
+ nfrac = ibytes << (ENTROPY_SHIFT + 3);
+ if ((size_t) entropy_count > nfrac)
+ entropy_count -= nfrac;
else
- entropy_count = reserved << (ENTROPY_SHIFT + 3);
+ entropy_count = 0;
if (cmpxchg(&r->entropy_count, orig, entropy_count) != orig)
goto retry;
@@ -1375,6 +1386,7 @@ urandom_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos)
"with %d bits of entropy available\n",
current->comm, nonblocking_pool.entropy_total);
+ nbytes = min_t(size_t, nbytes, INT_MAX >> (ENTROPY_SHIFT + 3));
ret = extract_entropy_user(&nonblocking_pool, buf, nbytes);
trace_urandom_read(8 * nbytes, ENTROPY_BITS(&nonblocking_pool),
diff --git a/drivers/char/raw.c b/drivers/char/raw.c
index 6e8d65e9b1d..0102dc78860 100644
--- a/drivers/char/raw.c
+++ b/drivers/char/raw.c
@@ -284,10 +284,10 @@ static long raw_ctl_compat_ioctl(struct file *file, unsigned int cmd,
#endif
static const struct file_operations raw_fops = {
- .read = do_sync_read,
- .aio_read = generic_file_aio_read,
- .write = do_sync_write,
- .aio_write = blkdev_aio_write,
+ .read = new_sync_read,
+ .read_iter = generic_file_read_iter,
+ .write = new_sync_write,
+ .write_iter = blkdev_write_iter,
.fsync = blkdev_fsync,
.open = raw_open,
.release = raw_release,