Fixes random reads
[akaros.git] / kern / src / ns / random.c
1 // INFERNO
2 #include <vfs.h>
3 #include <kfs.h>
4 #include <slab.h>
5 #include <kmalloc.h>
6 #include <kref.h>
7 #include <string.h>
8 #include <stdio.h>
9 #include <assert.h>
10 #include <error.h>
11 #include <cpio.h>
12 #include <pmap.h>
13 #include <smp.h>
14 #include <ip.h>
15 #include <alarm.h>
16
17 static struct
18 {
19         qlock_t qlock;
20         struct rendez   producer;
21         struct rendez consumer;
22         uint32_t        randomcount;
23         uint8_t buf[1024];
24         uint8_t *ep;
25         uint8_t *rp;
26         uint8_t *wp;
27         uint8_t next;
28         uint8_t bits;
29         uint8_t wakeme;
30         uint8_t filled;
31         int     target;
32         uint32_t        randn;
33 } rb;
34
35 static int
36 rbnotfull(void*unused)
37 {
38         int i;
39
40         i = rb.wp - rb.rp;
41         if(i < 0)
42                 i += sizeof(rb.buf);
43         return i < rb.target;
44 }
45
46 static int
47 rbnotempty(void*unused)
48 {
49         return rb.wp != rb.rp;
50 }
51
52 static void
53 genrandom(void*unused)
54 {
55         /* TODO: set this to low priority, if we ever have something like that */
56         //setpri(PriBackground);
57
58         for(;;) {
59                 for(;;)
60                         if(++rb.randomcount > 100000)
61                                 break;
62                 //if(anyhigher())
63                         kthread_yield();
64                 if(rb.filled || !rbnotfull(0))
65                         rendez_sleep(&rb.producer, rbnotfull, 0);
66         }
67 }
68
69 /*
70  *  produce random bits in a circular buffer
71  */
72 static void
73 randomclock(void)
74 {
75         uint8_t *p;
76
77         if(rb.randomcount == 0)
78                 return;
79
80         if(!rbnotfull(0)) {
81                 rb.filled = 1;
82                 return;
83         }
84
85         rb.bits = (rb.bits<<2) ^ (rb.randomcount&3);
86         rb.randomcount = 0;
87
88         rb.next += 2;
89         if(rb.next != 8)
90                 return;
91
92         rb.next = 0;
93         *rb.wp ^= rb.bits ^ *rb.rp;
94         p = rb.wp+1;
95         if(p == rb.ep)
96                 p = rb.buf;
97         rb.wp = p;
98
99         if(rb.wakeme)
100                 rendez_wakeup(&rb.consumer);
101 }
102
103 struct alarm_waiter random_waiter;
104 static void __random_alarm(struct alarm_waiter *waiter)
105 {
106         randomclock();
107         /* Set our alarm to go off, incrementing from our last tick (instead of
108          * setting it relative to now, since some time has passed since the alarm
109          * first went off.  Note, this may be now or in the past! */
110         set_awaiter_inc(&random_waiter, 13000);
111         __set_alarm(&per_cpu_info[core_id()].tchain, &random_waiter);
112 }
113
114 void
115 randominit(void)
116 {
117         qlock_init(&rb.qlock);
118         rendez_init(&rb.producer);
119         rendez_init(&rb.consumer);
120         rb.target = 16;
121         rb.ep = rb.buf + sizeof(rb.buf);
122         rb.rp = rb.wp = rb.buf;
123         /* Run randomclock every 13 ms */
124         /* Frequency close but not equal to HZ */
125         init_awaiter(&random_waiter, __random_alarm);
126         set_awaiter_rel(&random_waiter, 13000);
127         set_alarm(&per_cpu_info[core_id()].tchain, &random_waiter);
128         /* instead of waiting for randomread to kick it off */
129         ktask("genrand", genrandom, NULL);
130 }
131
132 /*
133  *  consume random bytes from a circular buffer
134  */
135 uint32_t
136 randomread(void *xp, uint32_t n)
137 {
138         ERRSTACK(1);
139         int i, sofar;
140         uint8_t *e, *p;
141
142         p = xp;
143
144         qlock(&(&rb)->qlock);
145         if(waserror()){
146                 qunlock(&(&rb)->qlock);
147                 nexterror();
148         }
149
150         for(sofar = 0; sofar < n; sofar += i){
151                 i = rb.wp - rb.rp;
152                 if(i == 0){
153                         rb.wakeme = 1;
154                         rendez_wakeup(&rb.producer);
155                         rendez_sleep(&rb.consumer, rbnotempty, 0);
156                         rb.wakeme = 0;
157                         continue;
158                 }
159                 if(i < 0)
160                         i = rb.ep - rb.rp;
161                 if((i+sofar) > n)
162                         i = n - sofar;
163                 memmove(p + sofar, rb.rp, i);
164                 e = rb.rp + i;
165                 if(e == rb.ep)
166                         e = rb.buf;
167                 rb.rp = e;
168         }
169         if(rb.filled && rb.wp == rb.rp){
170                 i = 2*rb.target;
171                 if(i > sizeof(rb.buf) - 1)
172                         i = sizeof(rb.buf) - 1;
173                 rb.target = i;
174                 rb.filled = 0;
175         }
176         poperror();
177         qunlock(&(&rb)->qlock);
178
179         rendez_wakeup(&rb.producer);
180
181         return n;
182 }