Commit 9996508b authored by Ian Molton's avatar Ian Molton Committed by Herbert Xu

hwrng: core - Replace u32 in driver API with byte array

This patch implements a new method by which hw_random hardware drivers
can pass data to the core more efficiently, using a shared buffer.

The old methods have been retained as a compatability layer until all the
drivers have been updated.
Signed-off-by: default avatarIan Molton <ian.molton@collabora.co.uk>
Acked-by: default avatarMatt Mackall <mpm@selenic.com>
Acked-by: default avatarRusty Russell <rusty@rustcorp.com.au>
Signed-off-by: default avatarHerbert Xu <herbert@gondor.apana.org.au>
parent 2f32bfd8
...@@ -52,7 +52,8 @@ ...@@ -52,7 +52,8 @@
static struct hwrng *current_rng; static struct hwrng *current_rng;
static LIST_HEAD(rng_list); static LIST_HEAD(rng_list);
static DEFINE_MUTEX(rng_mutex); static DEFINE_MUTEX(rng_mutex);
static int data_avail;
static u8 rng_buffer[SMP_CACHE_BYTES] __cacheline_aligned;
static inline int hwrng_init(struct hwrng *rng) static inline int hwrng_init(struct hwrng *rng)
{ {
...@@ -67,19 +68,6 @@ static inline void hwrng_cleanup(struct hwrng *rng) ...@@ -67,19 +68,6 @@ static inline void hwrng_cleanup(struct hwrng *rng)
rng->cleanup(rng); rng->cleanup(rng);
} }
static inline int hwrng_data_present(struct hwrng *rng, int wait)
{
if (!rng->data_present)
return 1;
return rng->data_present(rng, wait);
}
static inline int hwrng_data_read(struct hwrng *rng, u32 *data)
{
return rng->data_read(rng, data);
}
static int rng_dev_open(struct inode *inode, struct file *filp) static int rng_dev_open(struct inode *inode, struct file *filp)
{ {
/* enforce read-only access to this chrdev */ /* enforce read-only access to this chrdev */
...@@ -91,54 +79,87 @@ static int rng_dev_open(struct inode *inode, struct file *filp) ...@@ -91,54 +79,87 @@ static int rng_dev_open(struct inode *inode, struct file *filp)
return 0; return 0;
} }
static inline int rng_get_data(struct hwrng *rng, u8 *buffer, size_t size,
int wait) {
int present;
if (rng->read)
return rng->read(rng, (void *)buffer, size, wait);
if (rng->data_present)
present = rng->data_present(rng, wait);
else
present = 1;
if (present)
return rng->data_read(rng, (u32 *)buffer);
return 0;
}
static ssize_t rng_dev_read(struct file *filp, char __user *buf, static ssize_t rng_dev_read(struct file *filp, char __user *buf,
size_t size, loff_t *offp) size_t size, loff_t *offp)
{ {
u32 data;
ssize_t ret = 0; ssize_t ret = 0;
int err = 0; int err = 0;
int bytes_read; int bytes_read, len;
while (size) { while (size) {
err = -ERESTARTSYS; if (mutex_lock_interruptible(&rng_mutex)) {
if (mutex_lock_interruptible(&rng_mutex)) err = -ERESTARTSYS;
goto out; goto out;
}
if (!current_rng) { if (!current_rng) {
mutex_unlock(&rng_mutex);
err = -ENODEV; err = -ENODEV;
goto out; goto out_unlock;
} }
bytes_read = 0; if (!data_avail) {
if (hwrng_data_present(current_rng, bytes_read = rng_get_data(current_rng, rng_buffer,
!(filp->f_flags & O_NONBLOCK))) sizeof(rng_buffer),
bytes_read = hwrng_data_read(current_rng, &data); !(filp->f_flags & O_NONBLOCK));
mutex_unlock(&rng_mutex); if (bytes_read < 0) {
err = bytes_read;
err = -EAGAIN; goto out_unlock;
if (!bytes_read && (filp->f_flags & O_NONBLOCK)) }
goto out; data_avail = bytes_read;
if (bytes_read < 0) {
err = bytes_read;
goto out;
} }
err = -EFAULT; if (!data_avail) {
while (bytes_read && size) { if (filp->f_flags & O_NONBLOCK) {
if (put_user((u8)data, buf++)) err = -EAGAIN;
goto out; goto out_unlock;
size--; }
ret++; } else {
bytes_read--; len = data_avail;
data >>= 8; if (len > size)
len = size;
data_avail -= len;
if (copy_to_user(buf + ret, rng_buffer + data_avail,
len)) {
err = -EFAULT;
goto out_unlock;
}
size -= len;
ret += len;
} }
mutex_unlock(&rng_mutex);
if (need_resched()) if (need_resched())
schedule_timeout_interruptible(1); schedule_timeout_interruptible(1);
err = -ERESTARTSYS;
if (signal_pending(current)) if (signal_pending(current)) {
err = -ERESTARTSYS;
goto out; goto out;
}
} }
out_unlock:
mutex_unlock(&rng_mutex);
out: out:
return ret ? : err; return ret ? : err;
} }
...@@ -280,7 +301,7 @@ int hwrng_register(struct hwrng *rng) ...@@ -280,7 +301,7 @@ int hwrng_register(struct hwrng *rng)
struct hwrng *old_rng, *tmp; struct hwrng *old_rng, *tmp;
if (rng->name == NULL || if (rng->name == NULL ||
rng->data_read == NULL) (rng->data_read == NULL && rng->read == NULL))
goto out; goto out;
mutex_lock(&rng_mutex); mutex_lock(&rng_mutex);
......
...@@ -22,10 +22,12 @@ ...@@ -22,10 +22,12 @@
* @cleanup: Cleanup callback (can be NULL). * @cleanup: Cleanup callback (can be NULL).
* @data_present: Callback to determine if data is available * @data_present: Callback to determine if data is available
* on the RNG. If NULL, it is assumed that * on the RNG. If NULL, it is assumed that
* there is always data available. * there is always data available. *OBSOLETE*
* @data_read: Read data from the RNG device. * @data_read: Read data from the RNG device.
* Returns the number of lower random bytes in "data". * Returns the number of lower random bytes in "data".
* Must not be NULL. * Must not be NULL. *OSOLETE*
* @read: New API. drivers can fill up to max bytes of data
* into the buffer. The buffer is aligned for any type.
* @priv: Private data, for use by the RNG driver. * @priv: Private data, for use by the RNG driver.
*/ */
struct hwrng { struct hwrng {
...@@ -34,6 +36,7 @@ struct hwrng { ...@@ -34,6 +36,7 @@ struct hwrng {
void (*cleanup)(struct hwrng *rng); void (*cleanup)(struct hwrng *rng);
int (*data_present)(struct hwrng *rng, int wait); int (*data_present)(struct hwrng *rng, int wait);
int (*data_read)(struct hwrng *rng, u32 *data); int (*data_read)(struct hwrng *rng, u32 *data);
int (*read)(struct hwrng *rng, void *data, size_t max, bool wait);
unsigned long priv; unsigned long priv;
/* internal. */ /* internal. */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment