Make a big improvement to entropy-harvesting speed by not having any
locks (only atomic assigns) in the harvest ringbuffer.
This commit is contained in:
parent
af989ec5ea
commit
d70736850e
@ -50,7 +50,6 @@
|
|||||||
#include <dev/random/yarrow.h>
|
#include <dev/random/yarrow.h>
|
||||||
|
|
||||||
/* #define DEBUG */
|
/* #define DEBUG */
|
||||||
/* #define DEBUG1 */ /* Very noisy - prints plenty harvesting stats */
|
|
||||||
|
|
||||||
static void generator_gate(void);
|
static void generator_gate(void);
|
||||||
static void reseed(int);
|
static void reseed(int);
|
||||||
@ -69,14 +68,12 @@ struct harvest {
|
|||||||
u_char entropy[HARVESTSIZE]; /* the harvested entropy */
|
u_char entropy[HARVESTSIZE]; /* the harvested entropy */
|
||||||
u_int size, bits, frac; /* stats about the entropy */
|
u_int size, bits, frac; /* stats about the entropy */
|
||||||
enum esource source; /* stats about the entropy */
|
enum esource source; /* stats about the entropy */
|
||||||
u_int pool; /* which pool this goes into */
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Ring buffer holding harvested entropy */
|
/* Ring buffer holding harvested entropy */
|
||||||
static struct harvestring {
|
static struct harvestring {
|
||||||
struct mtx lockout_mtx;
|
volatile int head;
|
||||||
int head;
|
volatile int tail;
|
||||||
int tail;
|
|
||||||
struct harvest data[HARVEST_RING_SIZE];
|
struct harvest data[HARVEST_RING_SIZE];
|
||||||
} harvestring;
|
} harvestring;
|
||||||
|
|
||||||
@ -91,7 +88,7 @@ static struct proc *random_kthread_proc;
|
|||||||
static void
|
static void
|
||||||
random_kthread(void *arg /* NOTUSED */)
|
random_kthread(void *arg /* NOTUSED */)
|
||||||
{
|
{
|
||||||
int pl, src, overthreshhold[2], head, newtail;
|
int pl, src, overthreshhold[2], newtail;
|
||||||
struct harvest *event;
|
struct harvest *event;
|
||||||
struct source *source;
|
struct source *source;
|
||||||
|
|
||||||
@ -107,31 +104,29 @@ random_kthread(void *arg /* NOTUSED */)
|
|||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
|
|
||||||
head = atomic_load_acq_int(&harvestring.head);
|
if (harvestring.tail == harvestring.head)
|
||||||
newtail = (harvestring.tail + 1) % HARVEST_RING_SIZE;
|
tsleep(&harvestring, PUSER, "rndslp", hz/10);
|
||||||
if (harvestring.tail == head)
|
|
||||||
tsleep(&harvestring.head, PUSER, "rndslp", hz/10);
|
|
||||||
|
|
||||||
else {
|
else {
|
||||||
#ifdef DEBUG1
|
|
||||||
mtx_lock(&Giant);
|
|
||||||
printf("HARVEST src=%d bits=%d/%d pool=%d count=%lld\n",
|
|
||||||
event->source, event->bits, event->frac,
|
|
||||||
event->pool, event->somecounter);
|
|
||||||
mtx_unlock(&Giant);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Suck the harvested entropy out of the queue and hash
|
/* Suck the harvested entropy out of the queue and hash
|
||||||
* it into the appropriate pool.
|
* it into the appropriate pool.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
newtail = (harvestring.tail + 1) & HARVEST_RING_MASK;
|
||||||
event = &harvestring.data[harvestring.tail];
|
event = &harvestring.data[harvestring.tail];
|
||||||
|
|
||||||
|
/* Bump the ring counter. This action is assumed
|
||||||
|
* to be atomic.
|
||||||
|
*/
|
||||||
harvestring.tail = newtail;
|
harvestring.tail = newtail;
|
||||||
|
|
||||||
source = &random_state.pool[event->pool].source[event->source];
|
pl = random_state.which = !random_state.which;
|
||||||
yarrow_hash_iterate(&random_state.pool[event->pool].hash,
|
|
||||||
|
source = &random_state.pool[pl].source[event->source];
|
||||||
|
yarrow_hash_iterate(&random_state.pool[pl].hash,
|
||||||
event->entropy, sizeof(event->entropy));
|
event->entropy, sizeof(event->entropy));
|
||||||
yarrow_hash_iterate(&random_state.pool[event->pool].hash,
|
yarrow_hash_iterate(&random_state.pool[pl].hash,
|
||||||
&event->somecounter, sizeof(event->somecounter));
|
&event->somecounter, sizeof(event->somecounter));
|
||||||
source->frac += event->frac;
|
source->frac += event->frac;
|
||||||
source->bits += event->bits + source->frac/1024;
|
source->bits += event->bits + source->frac/1024;
|
||||||
@ -189,6 +184,9 @@ random_init(void)
|
|||||||
*/
|
*/
|
||||||
random_state.seeded = 1;
|
random_state.seeded = 1;
|
||||||
|
|
||||||
|
/* Yarrow parameters. Do not adjust these unless you have
|
||||||
|
* have a very good clue about what they do!
|
||||||
|
*/
|
||||||
random_state.gengateinterval = 10;
|
random_state.gengateinterval = 10;
|
||||||
random_state.bins = 10;
|
random_state.bins = 10;
|
||||||
random_state.pool[0].thresh = 100;
|
random_state.pool[0].thresh = 100;
|
||||||
@ -196,9 +194,7 @@ random_init(void)
|
|||||||
random_state.slowoverthresh = 2;
|
random_state.slowoverthresh = 2;
|
||||||
random_state.which = FAST;
|
random_state.which = FAST;
|
||||||
|
|
||||||
/* Initialise the mutexes */
|
|
||||||
mtx_init(&random_reseed_mtx, "random reseed", MTX_DEF);
|
mtx_init(&random_reseed_mtx, "random reseed", MTX_DEF);
|
||||||
mtx_init(&harvestring.lockout_mtx, "random harvest", MTX_DEF);
|
|
||||||
|
|
||||||
harvestring.head = 0;
|
harvestring.head = 0;
|
||||||
harvestring.tail = 0;
|
harvestring.tail = 0;
|
||||||
@ -240,11 +236,8 @@ random_deinit(void)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Command the hash/reseed thread to end and wait for it to finish */
|
/* Command the hash/reseed thread to end and wait for it to finish */
|
||||||
mtx_lock(&harvestring.lockout_mtx);
|
|
||||||
random_kthread_control = -1;
|
random_kthread_control = -1;
|
||||||
msleep((void *)&random_kthread_control, &harvestring.lockout_mtx, PUSER,
|
tsleep((void *)&random_kthread_control, PUSER, "rndend", 0);
|
||||||
"rndend", 0);
|
|
||||||
mtx_unlock(&harvestring.lockout_mtx);
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
mtx_lock(&Giant);
|
mtx_lock(&Giant);
|
||||||
@ -252,9 +245,7 @@ random_deinit(void)
|
|||||||
mtx_unlock(&Giant);
|
mtx_unlock(&Giant);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Remove the mutexes */
|
|
||||||
mtx_destroy(&random_reseed_mtx);
|
mtx_destroy(&random_reseed_mtx);
|
||||||
mtx_destroy(&harvestring.lockout_mtx);
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
mtx_lock(&Giant);
|
mtx_lock(&Giant);
|
||||||
@ -492,52 +483,32 @@ random_harvest_internal(u_int64_t somecounter, void *entropy, u_int count,
|
|||||||
u_int bits, u_int frac, enum esource origin)
|
u_int bits, u_int frac, enum esource origin)
|
||||||
{
|
{
|
||||||
struct harvest *harvest;
|
struct harvest *harvest;
|
||||||
int newhead, tail;
|
int newhead;
|
||||||
|
|
||||||
#ifdef DEBUG1
|
newhead = (harvestring.head + 1) & HARVEST_RING_MASK;
|
||||||
mtx_lock(&Giant);
|
|
||||||
printf("Random harvest\n");
|
|
||||||
mtx_unlock(&Giant);
|
|
||||||
#endif
|
|
||||||
if (origin < ENTROPYSOURCE) {
|
|
||||||
|
|
||||||
/* Add the harvested data to the ring buffer, but
|
if (newhead != harvestring.tail) {
|
||||||
* do not block.
|
|
||||||
|
/* Add the harvested data to the ring buffer */
|
||||||
|
|
||||||
|
harvest = &harvestring.data[harvestring.head];
|
||||||
|
|
||||||
|
/* Stuff the harvested data into the ring */
|
||||||
|
harvest->somecounter = somecounter;
|
||||||
|
count = count > HARVESTSIZE ? HARVESTSIZE : count;
|
||||||
|
memcpy(harvest->entropy, entropy, count);
|
||||||
|
harvest->size = count;
|
||||||
|
harvest->bits = bits;
|
||||||
|
harvest->frac = frac;
|
||||||
|
harvest->source = origin < ENTROPYSOURCE ? origin : 0;
|
||||||
|
|
||||||
|
/* Bump the ring counter. This action is assumed
|
||||||
|
* to be atomic.
|
||||||
*/
|
*/
|
||||||
if (mtx_trylock(&harvestring.lockout_mtx)) {
|
harvestring.head = newhead;
|
||||||
|
|
||||||
tail = atomic_load_acq_int(&harvestring.tail);
|
|
||||||
newhead = (harvestring.head + 1) % HARVEST_RING_SIZE;
|
|
||||||
|
|
||||||
if (newhead != tail) {
|
|
||||||
|
|
||||||
harvest = &harvestring.data[harvestring.head];
|
|
||||||
|
|
||||||
/* toggle the pool for next insertion */
|
|
||||||
harvest->pool = random_state.which;
|
|
||||||
random_state.which = !random_state.which;
|
|
||||||
|
|
||||||
/* Stuff the harvested data into the ring */
|
|
||||||
harvest->somecounter = somecounter;
|
|
||||||
count = count > HARVESTSIZE ? HARVESTSIZE : count;
|
|
||||||
memcpy(harvest->entropy, entropy, count);
|
|
||||||
harvest->size = count;
|
|
||||||
harvest->bits = bits;
|
|
||||||
harvest->frac = frac;
|
|
||||||
harvest->source = origin;
|
|
||||||
|
|
||||||
/* Bump the ring counter and shake the reseed
|
|
||||||
* process
|
|
||||||
*/
|
|
||||||
harvestring.head = newhead;
|
|
||||||
wakeup(&harvestring.head);
|
|
||||||
|
|
||||||
}
|
|
||||||
mtx_unlock(&harvestring.lockout_mtx);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Helper routine to perform explicit reseeds */
|
/* Helper routine to perform explicit reseeds */
|
||||||
|
@ -31,7 +31,10 @@
|
|||||||
* an enum in sys/random.h
|
* an enum in sys/random.h
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/* The ring size _MUST_ be a power of 2 */
|
||||||
#define HARVEST_RING_SIZE 1024 /* harvest ring buffer size */
|
#define HARVEST_RING_SIZE 1024 /* harvest ring buffer size */
|
||||||
|
#define HARVEST_RING_MASK (HARVEST_RING_SIZE - 1)
|
||||||
|
|
||||||
#define TIMEBIN 16 /* max value for Pt/t */
|
#define TIMEBIN 16 /* max value for Pt/t */
|
||||||
|
|
||||||
#define HARVESTSIZE 16 /* max size of each harvested entropy unit */
|
#define HARVESTSIZE 16 /* max size of each harvested entropy unit */
|
||||||
@ -72,6 +75,7 @@ struct random_state {
|
|||||||
int which; /* toggle - shows the current insertion pool */
|
int which; /* toggle - shows the current insertion pool */
|
||||||
int seeded; /* 0 causes blocking 1 allows normal output */
|
int seeded; /* 0 causes blocking 1 allows normal output */
|
||||||
struct selinfo rsel; /* For poll(2) */
|
struct selinfo rsel; /* For poll(2) */
|
||||||
|
u_char raw[HARVESTSIZE];/* Raw buffer for checking */
|
||||||
};
|
};
|
||||||
|
|
||||||
extern struct random_state random_state;
|
extern struct random_state random_state;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user