xref: /plan9-contrib/sys/src/9k/port/random.c (revision 9ef1f84b659abcb917c5c090acbce0772e494f21)
1*9ef1f84bSDavid du Colombier #include	"u.h"
2*9ef1f84bSDavid du Colombier #include	"../port/lib.h"
3*9ef1f84bSDavid du Colombier #include	"mem.h"
4*9ef1f84bSDavid du Colombier #include	"dat.h"
5*9ef1f84bSDavid du Colombier #include	"fns.h"
6*9ef1f84bSDavid du Colombier 
7*9ef1f84bSDavid du Colombier struct Rb
8*9ef1f84bSDavid du Colombier {
9*9ef1f84bSDavid du Colombier 	QLock;
10*9ef1f84bSDavid du Colombier 	Rendez	producer;
11*9ef1f84bSDavid du Colombier 	Rendez	consumer;
12*9ef1f84bSDavid du Colombier 	ulong	randomcount;
13*9ef1f84bSDavid du Colombier 	uchar	buf[1024];
14*9ef1f84bSDavid du Colombier 	uchar	*ep;
15*9ef1f84bSDavid du Colombier 	uchar	*rp;
16*9ef1f84bSDavid du Colombier 	uchar	*wp;
17*9ef1f84bSDavid du Colombier 	uchar	next;
18*9ef1f84bSDavid du Colombier 	uchar	wakeme;
19*9ef1f84bSDavid du Colombier 	ushort	bits;
20*9ef1f84bSDavid du Colombier 	ulong	randn;
21*9ef1f84bSDavid du Colombier } rb;
22*9ef1f84bSDavid du Colombier 
23*9ef1f84bSDavid du Colombier static int
rbnotfull(void *)24*9ef1f84bSDavid du Colombier rbnotfull(void*)
25*9ef1f84bSDavid du Colombier {
26*9ef1f84bSDavid du Colombier 	int i;
27*9ef1f84bSDavid du Colombier 
28*9ef1f84bSDavid du Colombier 	i = rb.rp - rb.wp;
29*9ef1f84bSDavid du Colombier 	return i != 1 && i != (1 - sizeof(rb.buf));
30*9ef1f84bSDavid du Colombier }
31*9ef1f84bSDavid du Colombier 
32*9ef1f84bSDavid du Colombier static int
rbnotempty(void *)33*9ef1f84bSDavid du Colombier rbnotempty(void*)
34*9ef1f84bSDavid du Colombier {
35*9ef1f84bSDavid du Colombier 	return rb.wp != rb.rp;
36*9ef1f84bSDavid du Colombier }
37*9ef1f84bSDavid du Colombier 
38*9ef1f84bSDavid du Colombier static void
genrandom(void *)39*9ef1f84bSDavid du Colombier genrandom(void*)
40*9ef1f84bSDavid du Colombier {
41*9ef1f84bSDavid du Colombier 	up->basepri = PriNormal;
42*9ef1f84bSDavid du Colombier 	up->priority = up->basepri;
43*9ef1f84bSDavid du Colombier 
44*9ef1f84bSDavid du Colombier 	for(;;){
45*9ef1f84bSDavid du Colombier 		for(;;)
46*9ef1f84bSDavid du Colombier 			if(++rb.randomcount > 100000)
47*9ef1f84bSDavid du Colombier 				break;
48*9ef1f84bSDavid du Colombier 		if(anyhigher())
49*9ef1f84bSDavid du Colombier 			sched();
50*9ef1f84bSDavid du Colombier 		if(!rbnotfull(0))
51*9ef1f84bSDavid du Colombier 			sleep(&rb.producer, rbnotfull, 0);
52*9ef1f84bSDavid du Colombier 	}
53*9ef1f84bSDavid du Colombier }
54*9ef1f84bSDavid du Colombier 
55*9ef1f84bSDavid du Colombier /*
56*9ef1f84bSDavid du Colombier  *  produce random bits in a circular buffer
57*9ef1f84bSDavid du Colombier  */
58*9ef1f84bSDavid du Colombier static void
randomclock(void)59*9ef1f84bSDavid du Colombier randomclock(void)
60*9ef1f84bSDavid du Colombier {
61*9ef1f84bSDavid du Colombier 	if(rb.randomcount == 0 || !rbnotfull(0))
62*9ef1f84bSDavid du Colombier 		return;
63*9ef1f84bSDavid du Colombier 
64*9ef1f84bSDavid du Colombier 	rb.bits = (rb.bits<<2) ^ rb.randomcount;
65*9ef1f84bSDavid du Colombier 	rb.randomcount = 0;
66*9ef1f84bSDavid du Colombier 
67*9ef1f84bSDavid du Colombier 	rb.next++;
68*9ef1f84bSDavid du Colombier 	if(rb.next != 8/2)
69*9ef1f84bSDavid du Colombier 		return;
70*9ef1f84bSDavid du Colombier 	rb.next = 0;
71*9ef1f84bSDavid du Colombier 
72*9ef1f84bSDavid du Colombier 	*rb.wp ^= rb.bits;
73*9ef1f84bSDavid du Colombier 	if(rb.wp+1 == rb.ep)
74*9ef1f84bSDavid du Colombier 		rb.wp = rb.buf;
75*9ef1f84bSDavid du Colombier 	else
76*9ef1f84bSDavid du Colombier 		rb.wp = rb.wp+1;
77*9ef1f84bSDavid du Colombier 
78*9ef1f84bSDavid du Colombier 	if(rb.wakeme)
79*9ef1f84bSDavid du Colombier 		wakeup(&rb.consumer);
80*9ef1f84bSDavid du Colombier }
81*9ef1f84bSDavid du Colombier 
82*9ef1f84bSDavid du Colombier void
randominit(void)83*9ef1f84bSDavid du Colombier randominit(void)
84*9ef1f84bSDavid du Colombier {
85*9ef1f84bSDavid du Colombier 	/* Frequency close but not equal to HZ */
86*9ef1f84bSDavid du Colombier 	addclock0link(randomclock, 13);
87*9ef1f84bSDavid du Colombier 	rb.ep = rb.buf + sizeof(rb.buf);
88*9ef1f84bSDavid du Colombier 	rb.rp = rb.wp = rb.buf;
89*9ef1f84bSDavid du Colombier 	kproc("genrandom", genrandom, 0);
90*9ef1f84bSDavid du Colombier }
91*9ef1f84bSDavid du Colombier 
92*9ef1f84bSDavid du Colombier /*
93*9ef1f84bSDavid du Colombier  *  consume random bytes from a circular buffer
94*9ef1f84bSDavid du Colombier  */
95*9ef1f84bSDavid du Colombier ulong
randomread(void * xp,ulong n)96*9ef1f84bSDavid du Colombier randomread(void *xp, ulong n)
97*9ef1f84bSDavid du Colombier {
98*9ef1f84bSDavid du Colombier 	uchar *e, *p;
99*9ef1f84bSDavid du Colombier 	ulong x;
100*9ef1f84bSDavid du Colombier 
101*9ef1f84bSDavid du Colombier 	p = xp;
102*9ef1f84bSDavid du Colombier 
103*9ef1f84bSDavid du Colombier 	if(waserror()){
104*9ef1f84bSDavid du Colombier 		qunlock(&rb);
105*9ef1f84bSDavid du Colombier 		nexterror();
106*9ef1f84bSDavid du Colombier 	}
107*9ef1f84bSDavid du Colombier 
108*9ef1f84bSDavid du Colombier 	qlock(&rb);
109*9ef1f84bSDavid du Colombier 	for(e = p + n; p < e; ){
110*9ef1f84bSDavid du Colombier 		if(rb.wp == rb.rp){
111*9ef1f84bSDavid du Colombier 			rb.wakeme = 1;
112*9ef1f84bSDavid du Colombier 			wakeup(&rb.producer);
113*9ef1f84bSDavid du Colombier 			sleep(&rb.consumer, rbnotempty, 0);
114*9ef1f84bSDavid du Colombier 			rb.wakeme = 0;
115*9ef1f84bSDavid du Colombier 			continue;
116*9ef1f84bSDavid du Colombier 		}
117*9ef1f84bSDavid du Colombier 
118*9ef1f84bSDavid du Colombier 		/*
119*9ef1f84bSDavid du Colombier 		 *  beating clocks will be predictable if
120*9ef1f84bSDavid du Colombier 		 *  they are synchronized.  Use a cheap pseudo-
121*9ef1f84bSDavid du Colombier 		 *  random number generator to obscure any cycles.
122*9ef1f84bSDavid du Colombier 		 */
123*9ef1f84bSDavid du Colombier 		x = rb.randn*1103515245 ^ *rb.rp;
124*9ef1f84bSDavid du Colombier 		*p++ = rb.randn = x;
125*9ef1f84bSDavid du Colombier 
126*9ef1f84bSDavid du Colombier 		if(rb.rp+1 == rb.ep)
127*9ef1f84bSDavid du Colombier 			rb.rp = rb.buf;
128*9ef1f84bSDavid du Colombier 		else
129*9ef1f84bSDavid du Colombier 			rb.rp = rb.rp+1;
130*9ef1f84bSDavid du Colombier 	}
131*9ef1f84bSDavid du Colombier 	qunlock(&rb);
132*9ef1f84bSDavid du Colombier 	poperror();
133*9ef1f84bSDavid du Colombier 
134*9ef1f84bSDavid du Colombier 	wakeup(&rb.producer);
135*9ef1f84bSDavid du Colombier 
136*9ef1f84bSDavid du Colombier 	return n;
137*9ef1f84bSDavid du Colombier }
138