Random device in 1.3.30 - Oops and some ideas

Marek Michalkiewicz (marekm@i17linuxb.ists.pwr.wroc.pl)
Wed, 4 Oct 1995 19:48:52 +0100 (MET)


When testing the random device by reading it in several processes
at the same time, I got an Oops message, here is the output from
ksymoops:

EIP: 17cc6b T _read_random+17b/1b0
Trace: 17ce75 T _read_random_wait+45/100
Trace: 120d3c T _sys_read+8c/b0
Trace: 10a779 T _system_call+59/a0
Trace: 19002b T _math_emulate+453/c10

(This is probably somewhere in extract_entropy which is inline -
I'm not sure why: it is quite big and called twice, does inline
make any noticeable speed difference here?)

I got this with slightly modified code, so it could be my fault,
but I can't find what I screwed up, my patch (see below) is so
simple... What I did is "dd if=/dev/random of=somefile bs=1 &"
started 5 times (writing to different files) - one of them died
after a minute or so. If you think this is a bug introduced by
me, _please_ tell me (I am still learning kernel hacking).

Two suggestions:

- add blocking mode for /dev/random where it blocks until enough
random bytes are read or the read is interrupted by a signal (this
is what my patch below does), to get the old behaviour, open with
the O_NONBLOCK flag. I hope it is not too late to make this change
since the random device just appeared in 1.3.30 and nobody except
those on the bleeding edge should be using it yet... We usually
need certain amount of random data so I think blocking mode would
be useful.

- make it possible to add randomness by writing to /dev/random.
Suppose you have a noise generator connected to a D/A converter
(like a zener diode, coupled to the microphone input of a sound
card) - a daemon could read noise samples from /dev/audio, and
write them to /dev/random, and programs using /dev/random don't
need to know about any other devices, and automatically get more
randomness more quickly.

Thanks, and please keep up the good work.

Marek

--- linux/include/linux/random.h.orig Mon Sep 25 07:19:07 1995
+++ linux/include/linux/random.h Sun Oct 1 18:38:04 1995
@@ -30,6 +30,8 @@
char * buf, int nbytes);
int read_random_unlimited(struct inode * inode, struct file * file,
char * buf, int nbytes);
+int read_random_wait(struct inode * inode, struct file * file,
+ char * buf, int nbytes);
#else
#define add_keyboard_randomness(x)
#define add_interrupt_randomness(x)
--- linux/drivers/char/random.c.orig Mon Sep 25 07:19:07 1995
+++ linux/drivers/char/random.c Sun Oct 1 18:14:50 1995
@@ -164,6 +164,7 @@
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/string.h>
+#include <linux/fcntl.h>
#include <linux/random.h>

#include <asm/segment.h>
@@ -197,6 +198,10 @@
static struct timer_rand_state keyboard_timer_state;
static struct timer_rand_state irq_timer_state[NR_IRQS];

+#ifdef linux
+static struct wait_queue *random_wait;
+#endif
+
#ifndef MIN
#define MIN(a,b) (((a) < (b)) ? (a) : (b))
#endif
@@ -214,6 +219,9 @@
random_state.length = RANDPOOL;
random_state.pool = random_pool;
flush_random(&random_state);
+#ifdef linux
+ random_wait = NULL;
+#endif
}

/*
@@ -393,6 +401,9 @@
r->entropy_count += entropy_level;
if (r->entropy_count > r->length*8)
r->entropy_count = r->length * 8;
+#ifdef linux
+ wake_up_interruptible(&random_wait);
+#endif
}

/*
@@ -530,6 +541,32 @@
char * buf,int nbytes)
{
return extract_entropy(&random_state, buf, nbytes, 1);
+}
+
+int read_random_wait(struct inode * inode, struct file * file,
+ char * buf, int nbytes)
+{
+ unsigned long flags;
+ int bytesleft, n;
+
+ if (file->f_flags & O_NONBLOCK)
+ return read_random(inode, file, buf, nbytes);
+
+ bytesleft = nbytes;
+ for (;;) {
+ n = read_random(inode, file, buf, bytesleft);
+ bytesleft -= n;
+ if (!bytesleft)
+ break;
+ save_flags(flags);
+ cli();
+ interruptible_sleep_on(&random_wait);
+ restore_flags(flags);
+ if (current->signal & ~current->blocked)
+ break;
+ buf += n;
+ }
+ return nbytes - bytesleft;
}
#endif

--- linux/drivers/char/mem.c.orig Mon Sep 25 07:19:08 1995
+++ linux/drivers/char/mem.c Sun Oct 1 18:38:04 1995
@@ -321,7 +321,7 @@
#ifdef CONFIG_RANDOM
static struct file_operations random_fops = {
memory_lseek,
- read_random,
+ read_random_wait,
write_random,
NULL, /* full_readdir */
NULL, /* full_select */