diff options
Diffstat (limited to 'drivers/char/hw_random/core.c')
| -rw-r--r-- | drivers/char/hw_random/core.c | 107 | 
1 files changed, 64 insertions, 43 deletions
diff --git a/drivers/char/hw_random/core.c b/drivers/char/hw_random/core.c index fc93e2fc7c7..82367262f3a 100644 --- a/drivers/char/hw_random/core.c +++ b/drivers/char/hw_random/core.c @@ -52,7 +52,8 @@  static struct hwrng *current_rng;  static LIST_HEAD(rng_list);  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)  { @@ -67,19 +68,6 @@ static inline void hwrng_cleanup(struct hwrng *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)  {  	/* enforce read-only access to this chrdev */ @@ -91,54 +79,87 @@ static int rng_dev_open(struct inode *inode, struct file *filp)  	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,  			    size_t size, loff_t *offp)  { -	u32 data;  	ssize_t ret = 0;  	int err = 0; -	int bytes_read; +	int bytes_read, len;  	while (size) { -		err = -ERESTARTSYS; -		if (mutex_lock_interruptible(&rng_mutex)) +		if (mutex_lock_interruptible(&rng_mutex)) { +			err = -ERESTARTSYS;  			goto out; +		} +  		if (!current_rng) { -			mutex_unlock(&rng_mutex);  			err = -ENODEV; -			goto out; +			goto out_unlock;  		} -		bytes_read = 0; -		if (hwrng_data_present(current_rng, -				       !(filp->f_flags & O_NONBLOCK))) -			bytes_read = hwrng_data_read(current_rng, &data); -		mutex_unlock(&rng_mutex); - -		err = -EAGAIN; -		if (!bytes_read && (filp->f_flags & O_NONBLOCK)) -			goto out; -		if (bytes_read < 0) { -			err = bytes_read; -			goto out; +		if (!data_avail) { +			bytes_read = rng_get_data(current_rng, rng_buffer, +				sizeof(rng_buffer), +				!(filp->f_flags & O_NONBLOCK)); +			if (bytes_read < 0) { +				err = bytes_read; +				goto out_unlock; +			} +			data_avail = bytes_read;  		} -		err = -EFAULT; -		while (bytes_read && size) { -			if (put_user((u8)data, buf++)) -				goto out; -			size--; -			ret++; -			bytes_read--; -			data >>= 8; +		if (!data_avail) { +			if (filp->f_flags & O_NONBLOCK) { +				err = -EAGAIN; +				goto out_unlock; +			} +		} else { +			len = data_avail; +			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())  			schedule_timeout_interruptible(1); -		err = -ERESTARTSYS; -		if (signal_pending(current)) + +		if (signal_pending(current)) { +			err = -ERESTARTSYS;  			goto out; +		}  	} +out_unlock: +	mutex_unlock(&rng_mutex);  out:  	return ret ? : err;  } @@ -280,7 +301,7 @@ int hwrng_register(struct hwrng *rng)  	struct hwrng *old_rng, *tmp;  	if (rng->name == NULL || -	    rng->data_read == NULL) +	    (rng->data_read == NULL && rng->read == NULL))  		goto out;  	mutex_lock(&rng_mutex);  |