random: allow fractional bits to be tracked
Allow fractional bits of entropy to be tracked by scaling the entropy counter (fixed point). This will be used in a subsequent patch that accounts for entropy lost due to overwrites. [ Modified by tytso to fix up a few missing places where the entropy_count wasn't properly converted from fractional bits to bits. ] Signed-off-by: H. Peter Anvin <hpa@linux.intel.com> Signed-off-by: Theodore Ts'o <tytso@mit.edu>
This commit is contained in:
parent
9ed17b70b4
commit
a283b5c459
|
@ -279,6 +279,15 @@
|
|||
|
||||
#define LONGS(x) (((x) + sizeof(unsigned long) - 1)/sizeof(unsigned long))
|
||||
|
||||
/*
|
||||
* To allow fractional bits to be tracked, the following fields contain
|
||||
* this many fractional bits:
|
||||
*
|
||||
* entropy_count, trickle_thresh
|
||||
*/
|
||||
#define ENTROPY_SHIFT 3
|
||||
#define ENTROPY_BITS(r) ((r)->entropy_count >> ENTROPY_SHIFT)
|
||||
|
||||
/*
|
||||
* The minimum number of bits of entropy before we wake up a read on
|
||||
* /dev/random. Should be enough to do a significant reseed.
|
||||
|
@ -296,8 +305,7 @@ static int random_write_wakeup_thresh = 128;
|
|||
* When the input pool goes over trickle_thresh, start dropping most
|
||||
* samples to avoid wasting CPU time and reduce lock contention.
|
||||
*/
|
||||
|
||||
static int trickle_thresh __read_mostly = INPUT_POOL_WORDS * 28;
|
||||
static const int trickle_thresh = (INPUT_POOL_WORDS * 28) << ENTROPY_SHIFT;
|
||||
|
||||
static DEFINE_PER_CPU(int, trickle_count);
|
||||
|
||||
|
@ -311,8 +319,8 @@ static DEFINE_PER_CPU(int, trickle_count);
|
|||
*/
|
||||
|
||||
static struct poolinfo {
|
||||
int poolbitshift, poolwords, poolbytes, poolbits;
|
||||
#define S(x) ilog2(x)+5, (x), (x)*4, (x)*32
|
||||
int poolbitshift, poolwords, poolbytes, poolbits, poolfracbits;
|
||||
#define S(x) ilog2(x)+5, (x), (x)*4, (x)*32, (x) << (ENTROPY_SHIFT+5)
|
||||
int tap1, tap2, tap3, tap4, tap5;
|
||||
} poolinfo_table[] = {
|
||||
/* x^128 + x^103 + x^76 + x^51 +x^25 + x + 1 -- 105 */
|
||||
|
@ -581,7 +589,9 @@ static void fast_mix(struct fast_pool *f, const void *in, int nbytes)
|
|||
}
|
||||
|
||||
/*
|
||||
* Credit (or debit) the entropy store with n bits of entropy
|
||||
* Credit (or debit) the entropy store with n bits of entropy.
|
||||
* Use credit_entropy_bits_safe() if the value comes from userspace
|
||||
* or otherwise should be checked for extreme values.
|
||||
*/
|
||||
static void credit_entropy_bits(struct entropy_store *r, int nbits)
|
||||
{
|
||||
|
@ -593,13 +603,13 @@ static void credit_entropy_bits(struct entropy_store *r, int nbits)
|
|||
DEBUG_ENT("added %d entropy credits to %s\n", nbits, r->name);
|
||||
retry:
|
||||
entropy_count = orig = ACCESS_ONCE(r->entropy_count);
|
||||
entropy_count += nbits;
|
||||
entropy_count += nbits << ENTROPY_SHIFT;
|
||||
|
||||
if (entropy_count < 0) {
|
||||
DEBUG_ENT("negative entropy/overflow\n");
|
||||
entropy_count = 0;
|
||||
} else if (entropy_count > r->poolinfo->poolbits)
|
||||
entropy_count = r->poolinfo->poolbits;
|
||||
} else if (entropy_count > r->poolinfo->poolfracbits)
|
||||
entropy_count = r->poolinfo->poolfracbits;
|
||||
if (cmpxchg(&r->entropy_count, orig, entropy_count) != orig)
|
||||
goto retry;
|
||||
|
||||
|
@ -609,16 +619,29 @@ retry:
|
|||
r->initialized = 1;
|
||||
}
|
||||
|
||||
trace_credit_entropy_bits(r->name, nbits, entropy_count,
|
||||
trace_credit_entropy_bits(r->name, nbits,
|
||||
entropy_count >> ENTROPY_SHIFT,
|
||||
r->entropy_total, _RET_IP_);
|
||||
|
||||
/* should we wake readers? */
|
||||
if (r == &input_pool && entropy_count >= random_read_wakeup_thresh) {
|
||||
if (r == &input_pool &&
|
||||
(entropy_count >> ENTROPY_SHIFT) >= random_read_wakeup_thresh) {
|
||||
wake_up_interruptible(&random_read_wait);
|
||||
kill_fasync(&fasync, SIGIO, POLL_IN);
|
||||
}
|
||||
}
|
||||
|
||||
static void credit_entropy_bits_safe(struct entropy_store *r, int nbits)
|
||||
{
|
||||
const int nbits_max = (int)(~0U >> (ENTROPY_SHIFT + 1));
|
||||
|
||||
/* Cap the value to avoid overflows */
|
||||
nbits = min(nbits, nbits_max);
|
||||
nbits = max(nbits, -nbits_max);
|
||||
|
||||
credit_entropy_bits(r, nbits);
|
||||
}
|
||||
|
||||
/*********************************************************************
|
||||
*
|
||||
* Entropy input management
|
||||
|
@ -674,7 +697,7 @@ static void add_timer_randomness(struct timer_rand_state *state, unsigned num)
|
|||
|
||||
preempt_disable();
|
||||
/* if over the trickle threshold, use only 1 in 4096 samples */
|
||||
if (input_pool.entropy_count > trickle_thresh &&
|
||||
if (ENTROPY_BITS(&input_pool) > trickle_thresh &&
|
||||
((__this_cpu_inc_return(trickle_count) - 1) & 0xfff))
|
||||
goto out;
|
||||
|
||||
|
@ -813,8 +836,9 @@ static void xfer_secondary_pool(struct entropy_store *r, size_t nbytes)
|
|||
{
|
||||
__u32 tmp[OUTPUT_POOL_WORDS];
|
||||
|
||||
if (r->pull && r->entropy_count < nbytes * 8 &&
|
||||
r->entropy_count < r->poolinfo->poolbits) {
|
||||
if (r->pull &&
|
||||
r->entropy_count < (nbytes << (ENTROPY_SHIFT + 3)) &&
|
||||
r->entropy_count < r->poolinfo->poolfracbits) {
|
||||
/* If we're limited, always leave two wakeup worth's BITS */
|
||||
int rsvd = r->limit ? 0 : random_read_wakeup_thresh/4;
|
||||
int bytes = nbytes;
|
||||
|
@ -826,7 +850,8 @@ static void xfer_secondary_pool(struct entropy_store *r, size_t nbytes)
|
|||
|
||||
DEBUG_ENT("going to reseed %s with %d bits "
|
||||
"(%zu of %d requested)\n",
|
||||
r->name, bytes * 8, nbytes * 8, r->entropy_count);
|
||||
r->name, bytes * 8, nbytes * 8,
|
||||
r->entropy_count >> ENTROPY_SHIFT);
|
||||
|
||||
bytes = extract_entropy(r->pull, tmp, bytes,
|
||||
random_read_wakeup_thresh / 8, rsvd);
|
||||
|
@ -852,41 +877,44 @@ static size_t account(struct entropy_store *r, size_t nbytes, int min,
|
|||
{
|
||||
unsigned long flags;
|
||||
int wakeup_write = 0;
|
||||
int have_bytes;
|
||||
int entropy_count, orig;
|
||||
size_t ibytes;
|
||||
|
||||
/* Hold lock while accounting */
|
||||
spin_lock_irqsave(&r->lock, flags);
|
||||
|
||||
BUG_ON(r->entropy_count > r->poolinfo->poolbits);
|
||||
BUG_ON(r->entropy_count > r->poolinfo->poolfracbits);
|
||||
DEBUG_ENT("trying to extract %zu bits from %s\n",
|
||||
nbytes * 8, r->name);
|
||||
|
||||
/* Can we pull enough? */
|
||||
if (r->entropy_count / 8 < min + reserved) {
|
||||
nbytes = 0;
|
||||
} else {
|
||||
int entropy_count, orig;
|
||||
retry:
|
||||
entropy_count = orig = ACCESS_ONCE(r->entropy_count);
|
||||
entropy_count = orig = ACCESS_ONCE(r->entropy_count);
|
||||
have_bytes = entropy_count >> (ENTROPY_SHIFT + 3);
|
||||
ibytes = nbytes;
|
||||
if (have_bytes < min + reserved) {
|
||||
ibytes = 0;
|
||||
} else {
|
||||
/* If limited, never pull more than available */
|
||||
if (r->limit && nbytes + reserved >= entropy_count / 8)
|
||||
nbytes = entropy_count/8 - reserved;
|
||||
if (r->limit && ibytes + reserved >= have_bytes)
|
||||
ibytes = have_bytes - reserved;
|
||||
|
||||
if (entropy_count / 8 >= nbytes + reserved) {
|
||||
entropy_count -= nbytes*8;
|
||||
if (cmpxchg(&r->entropy_count, orig, entropy_count) != orig)
|
||||
goto retry;
|
||||
} else {
|
||||
entropy_count = reserved;
|
||||
if (cmpxchg(&r->entropy_count, orig, entropy_count) != orig)
|
||||
goto retry;
|
||||
}
|
||||
if (have_bytes >= ibytes + reserved)
|
||||
entropy_count -= ibytes << (ENTROPY_SHIFT + 3);
|
||||
else
|
||||
entropy_count = reserved << (ENTROPY_SHIFT + 3);
|
||||
|
||||
if (entropy_count < random_write_wakeup_thresh)
|
||||
if (cmpxchg(&r->entropy_count, orig, entropy_count) != orig)
|
||||
goto retry;
|
||||
|
||||
if ((r->entropy_count >> ENTROPY_SHIFT)
|
||||
< random_write_wakeup_thresh)
|
||||
wakeup_write = 1;
|
||||
}
|
||||
|
||||
DEBUG_ENT("debiting %zu entropy credits from %s%s\n",
|
||||
nbytes * 8, r->name, r->limit ? "" : " (unlimited)");
|
||||
ibytes * 8, r->name, r->limit ? "" : " (unlimited)");
|
||||
|
||||
spin_unlock_irqrestore(&r->lock, flags);
|
||||
|
||||
|
@ -895,7 +923,7 @@ retry:
|
|||
kill_fasync(&fasync, SIGIO, POLL_OUT);
|
||||
}
|
||||
|
||||
return nbytes;
|
||||
return ibytes;
|
||||
}
|
||||
|
||||
static void extract_buf(struct entropy_store *r, __u8 *out)
|
||||
|
@ -973,7 +1001,7 @@ static ssize_t extract_entropy(struct entropy_store *r, void *buf,
|
|||
r->last_data_init = true;
|
||||
spin_unlock_irqrestore(&r->lock, flags);
|
||||
trace_extract_entropy(r->name, EXTRACT_SIZE,
|
||||
r->entropy_count, _RET_IP_);
|
||||
ENTROPY_BITS(r), _RET_IP_);
|
||||
xfer_secondary_pool(r, EXTRACT_SIZE);
|
||||
extract_buf(r, tmp);
|
||||
spin_lock_irqsave(&r->lock, flags);
|
||||
|
@ -982,7 +1010,7 @@ static ssize_t extract_entropy(struct entropy_store *r, void *buf,
|
|||
spin_unlock_irqrestore(&r->lock, flags);
|
||||
}
|
||||
|
||||
trace_extract_entropy(r->name, nbytes, r->entropy_count, _RET_IP_);
|
||||
trace_extract_entropy(r->name, nbytes, ENTROPY_BITS(r), _RET_IP_);
|
||||
xfer_secondary_pool(r, nbytes);
|
||||
nbytes = account(r, nbytes, min, reserved);
|
||||
|
||||
|
@ -1015,7 +1043,7 @@ static ssize_t extract_entropy_user(struct entropy_store *r, void __user *buf,
|
|||
ssize_t ret = 0, i;
|
||||
__u8 tmp[EXTRACT_SIZE];
|
||||
|
||||
trace_extract_entropy_user(r->name, nbytes, r->entropy_count, _RET_IP_);
|
||||
trace_extract_entropy_user(r->name, nbytes, ENTROPY_BITS(r), _RET_IP_);
|
||||
xfer_secondary_pool(r, nbytes);
|
||||
nbytes = account(r, nbytes, 0, 0);
|
||||
|
||||
|
@ -1187,8 +1215,8 @@ random_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos)
|
|||
DEBUG_ENT("sleeping?\n");
|
||||
|
||||
wait_event_interruptible(random_read_wait,
|
||||
input_pool.entropy_count >=
|
||||
random_read_wakeup_thresh);
|
||||
ENTROPY_BITS(&input_pool) >=
|
||||
random_read_wakeup_thresh);
|
||||
|
||||
DEBUG_ENT("awake\n");
|
||||
|
||||
|
@ -1224,9 +1252,9 @@ random_poll(struct file *file, poll_table * wait)
|
|||
poll_wait(file, &random_read_wait, wait);
|
||||
poll_wait(file, &random_write_wait, wait);
|
||||
mask = 0;
|
||||
if (input_pool.entropy_count >= random_read_wakeup_thresh)
|
||||
if (ENTROPY_BITS(&input_pool) >= random_read_wakeup_thresh)
|
||||
mask |= POLLIN | POLLRDNORM;
|
||||
if (input_pool.entropy_count < random_write_wakeup_thresh)
|
||||
if (ENTROPY_BITS(&input_pool) < random_write_wakeup_thresh)
|
||||
mask |= POLLOUT | POLLWRNORM;
|
||||
return mask;
|
||||
}
|
||||
|
@ -1277,7 +1305,8 @@ static long random_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
|
|||
switch (cmd) {
|
||||
case RNDGETENTCNT:
|
||||
/* inherently racy, no point locking */
|
||||
if (put_user(input_pool.entropy_count, p))
|
||||
ent_count = ENTROPY_BITS(&input_pool);
|
||||
if (put_user(ent_count, p))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
case RNDADDTOENTCNT:
|
||||
|
@ -1285,7 +1314,7 @@ static long random_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
|
|||
return -EPERM;
|
||||
if (get_user(ent_count, p))
|
||||
return -EFAULT;
|
||||
credit_entropy_bits(&input_pool, ent_count);
|
||||
credit_entropy_bits_safe(&input_pool, ent_count);
|
||||
return 0;
|
||||
case RNDADDENTROPY:
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
|
@ -1300,7 +1329,7 @@ static long random_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
|
|||
size);
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
credit_entropy_bits(&input_pool, ent_count);
|
||||
credit_entropy_bits_safe(&input_pool, ent_count);
|
||||
return 0;
|
||||
case RNDZAPENTCNT:
|
||||
case RNDCLEARPOOL:
|
||||
|
@ -1407,6 +1436,23 @@ static int proc_do_uuid(struct ctl_table *table, int write,
|
|||
return proc_dostring(&fake_table, write, buffer, lenp, ppos);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return entropy available scaled to integral bits
|
||||
*/
|
||||
static int proc_do_entropy(ctl_table *table, int write,
|
||||
void __user *buffer, size_t *lenp, loff_t *ppos)
|
||||
{
|
||||
ctl_table fake_table;
|
||||
int entropy_count;
|
||||
|
||||
entropy_count = *(int *)table->data >> ENTROPY_SHIFT;
|
||||
|
||||
fake_table.data = &entropy_count;
|
||||
fake_table.maxlen = sizeof(entropy_count);
|
||||
|
||||
return proc_dointvec(&fake_table, write, buffer, lenp, ppos);
|
||||
}
|
||||
|
||||
static int sysctl_poolsize = INPUT_POOL_WORDS * 32;
|
||||
extern struct ctl_table random_table[];
|
||||
struct ctl_table random_table[] = {
|
||||
|
@ -1421,7 +1467,7 @@ struct ctl_table random_table[] = {
|
|||
.procname = "entropy_avail",
|
||||
.maxlen = sizeof(int),
|
||||
.mode = 0444,
|
||||
.proc_handler = proc_dointvec,
|
||||
.proc_handler = proc_do_entropy,
|
||||
.data = &input_pool.entropy_count,
|
||||
},
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue