]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - sys/dev/random/random_harvestq.c
Copy head (r256279) to stable/10 as part of the 10.0-RELEASE cycle.
[FreeBSD/stable/10.git] / sys / dev / random / random_harvestq.c
1 /*-
2  * Copyright (c) 2013 Arthur Mesh
3  * Copyright (c) 2000-2009 Mark R V Murray
4  * Copyright (c) 2004 Robert N. M. Watson
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer
12  *    in this position and unchanged.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  *
28  * $FreeBSD$
29  */
30
31 #include <sys/cdefs.h>
32 __FBSDID("$FreeBSD$");
33
34 #include <sys/param.h>
35 #include <sys/systm.h>
36 #include <sys/kernel.h>
37 #include <sys/kthread.h>
38 #include <sys/lock.h>
39 #include <sys/malloc.h>
40 #include <sys/mutex.h>
41 #include <sys/random.h>
42 #include <sys/sysctl.h>
43 #include <sys/unistd.h>
44
45 #include <dev/random/randomdev_soft.h>
46
47 #include "random_harvestq.h"
48
49 #define RANDOM_FIFO_MAX 256     /* How many events to queue up */
50
51 MALLOC_DEFINE(M_ENTROPY, "entropy", "Entropy harvesting buffers");
52
53 /*
54  * The harvest mutex protects the consistency of the entropy fifos and
55  * empty fifo.
56  */
57 struct mtx      harvest_mtx;
58
59 /* Lockable FIFO queue holding entropy buffers */
60 struct entropyfifo {
61         int count;
62         STAILQ_HEAD(harvestlist, harvest) head;
63 };
64
65 /* Empty entropy buffers */
66 static struct entropyfifo emptyfifo;
67
68 #define EMPTYBUFFERS    1024
69
70 /* Harvested entropy */
71 static struct entropyfifo harvestfifo[ENTROPYSOURCE];
72
73 /* <0 to end the kthread, 0 to let it run, 1 to flush the harvest queues */
74 int random_kthread_control = 0;
75
76 static struct proc *random_kthread_proc;
77
78 static void
79 random_kthread(void *arg)
80 {
81         STAILQ_HEAD(, harvest) local_queue;
82         struct harvest *event = NULL;
83         int local_count;
84         enum esource source;
85         event_proc_f func = arg;
86
87         STAILQ_INIT(&local_queue);
88         local_count = 0;
89
90         /* Process until told to stop */
91         mtx_lock_spin(&harvest_mtx);
92         for (; random_kthread_control >= 0;) {
93
94                 /* Cycle through all the entropy sources */
95                 for (source = RANDOM_START; source < ENTROPYSOURCE; source++) {
96                         /*
97                          * Drain entropy source records into a thread-local
98                          * queue for processing while not holding the mutex.
99                          */
100                         STAILQ_CONCAT(&local_queue, &harvestfifo[source].head);
101                         local_count += harvestfifo[source].count;
102                         harvestfifo[source].count = 0;
103                 }
104
105                 /*
106                  * Deal with events, if any, dropping the mutex as we process
107                  * each event.  Then push the events back into the empty
108                  * fifo.
109                  */
110                 if (!STAILQ_EMPTY(&local_queue)) {
111                         mtx_unlock_spin(&harvest_mtx);
112                         STAILQ_FOREACH(event, &local_queue, next)
113                                 func(event);
114                         mtx_lock_spin(&harvest_mtx);
115                         STAILQ_CONCAT(&emptyfifo.head, &local_queue);
116                         emptyfifo.count += local_count;
117                         local_count = 0;
118                 }
119
120                 KASSERT(local_count == 0, ("random_kthread: local_count %d",
121                     local_count));
122
123                 /*
124                  * If a queue flush was commanded, it has now happened,
125                  * and we can mark this by resetting the command.
126                  */
127                 if (random_kthread_control == 1)
128                         random_kthread_control = 0;
129
130                 /* Work done, so don't belabour the issue */
131                 msleep_spin_sbt(&random_kthread_control, &harvest_mtx,
132                     "-", SBT_1S / 10, 0, C_PREL(1));
133
134         }
135         mtx_unlock_spin(&harvest_mtx);
136
137         random_set_wakeup_exit(&random_kthread_control);
138         /* NOTREACHED */
139 }
140
141 void
142 random_harvestq_init(event_proc_f cb)
143 {
144         int error, i;
145         struct harvest *np;
146         enum esource e;
147
148         /* Initialise the harvest fifos */
149         STAILQ_INIT(&emptyfifo.head);
150         emptyfifo.count = 0;
151         for (i = 0; i < EMPTYBUFFERS; i++) {
152                 np = malloc(sizeof(struct harvest), M_ENTROPY, M_WAITOK);
153                 STAILQ_INSERT_TAIL(&emptyfifo.head, np, next);
154         }
155         for (e = RANDOM_START; e < ENTROPYSOURCE; e++) {
156                 STAILQ_INIT(&harvestfifo[e].head);
157                 harvestfifo[e].count = 0;
158         }
159
160         mtx_init(&harvest_mtx, "entropy harvest mutex", NULL, MTX_SPIN);
161
162
163         /* Start the hash/reseed thread */
164         error = kproc_create(random_kthread, cb,
165             &random_kthread_proc, RFHIGHPID, 0, "rand_harvestq"); /* RANDOM_CSPRNG_NAME */
166
167         if (error != 0)
168                 panic("Cannot create entropy maintenance thread.");
169 }
170
171 void
172 random_harvestq_deinit(void)
173 {
174         struct harvest *np;
175         enum esource e;
176
177         /* Destroy the harvest fifos */
178         while (!STAILQ_EMPTY(&emptyfifo.head)) {
179                 np = STAILQ_FIRST(&emptyfifo.head);
180                 STAILQ_REMOVE_HEAD(&emptyfifo.head, next);
181                 free(np, M_ENTROPY);
182         }
183         for (e = RANDOM_START; e < ENTROPYSOURCE; e++) {
184                 while (!STAILQ_EMPTY(&harvestfifo[e].head)) {
185                         np = STAILQ_FIRST(&harvestfifo[e].head);
186                         STAILQ_REMOVE_HEAD(&harvestfifo[e].head, next);
187                         free(np, M_ENTROPY);
188                 }
189         }
190
191         mtx_destroy(&harvest_mtx);
192 }
193
194 /*
195  * Entropy harvesting routine. This is supposed to be fast; do
196  * not do anything slow in here!
197  */
198 void
199 random_harvestq_internal(u_int64_t somecounter, const void *entropy,
200     u_int count, u_int bits, u_int frac, enum esource origin)
201 {
202         struct harvest *event;
203
204         KASSERT(origin >= RANDOM_START && origin <= RANDOM_PURE,
205             ("random_harvest_internal: origin %d invalid\n", origin));
206
207         /* Lockless read to avoid lock operations if fifo is full. */
208         if (harvestfifo[origin].count >= RANDOM_FIFO_MAX)
209                 return;
210
211         mtx_lock_spin(&harvest_mtx);
212
213         /*
214          * Don't make the harvest queues too big - help to prevent low-grade
215          * entropy swamping
216          */
217         if (harvestfifo[origin].count < RANDOM_FIFO_MAX) {
218                 event = STAILQ_FIRST(&emptyfifo.head);
219                 if (event != NULL) {
220                         /* Add the harvested data to the fifo */
221                         STAILQ_REMOVE_HEAD(&emptyfifo.head, next);
222                         harvestfifo[origin].count++;
223                         event->somecounter = somecounter;
224                         event->size = count;
225                         event->bits = bits;
226                         event->frac = frac;
227                         event->source = origin;
228
229                         /* XXXX Come back and make this dynamic! */
230                         count = MIN(count, HARVESTSIZE);
231                         memcpy(event->entropy, entropy, count);
232
233 #if 0
234                         {
235                         int i;
236                         printf("Harvest:%16jX ", event->somecounter);
237                         for (i = 0; i < event->size; i++)
238                                 printf("%02X", event->entropy[i]);
239                         for (; i < 16; i++)
240                                 printf("  ");
241                         printf(" %2d 0x%2X.%03X %02X\n", event->size, event->bits, event->frac, event->source);
242                         }
243 #endif
244
245                         STAILQ_INSERT_TAIL(&harvestfifo[origin].head,
246                             event, next);
247                 }
248         }
249         mtx_unlock_spin(&harvest_mtx);
250 }
251