1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2019 Conrad Meyer <[email protected]>
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28 #include <sys/cdefs.h>
29 #include <sys/param.h>
30 #include <sys/domainset.h>
31 #include <sys/fail.h>
32 #include <sys/limits.h>
33 #include <sys/lock.h>
34 #include <sys/kernel.h>
35 #include <sys/malloc.h>
36 #include <sys/mutex.h>
37 #include <sys/queue.h>
38 #include <sys/random.h>
39 #include <sys/sdt.h>
40 #include <sys/sysctl.h>
41 #include <sys/systm.h>
42 #include <sys/taskqueue.h>
43
44 #include <machine/atomic.h>
45 #include <machine/smp.h>
46
47 #include <dev/random/randomdev.h>
48 #include <dev/random/random_harvestq.h>
49
50 #include <dev/random/fenestrasX/fx_brng.h>
51 #include <dev/random/fenestrasX/fx_hash.h>
52 #include <dev/random/fenestrasX/fx_pool.h>
53 #include <dev/random/fenestrasX/fx_priv.h>
54 #include <dev/random/fenestrasX/fx_pub.h>
55
56 /*
57 * Timer-based reseed interval growth factor and limit in seconds. (§ 3.2)
58 */
59 #define FXENT_RESSED_INTVL_GFACT 3
60 #define FXENT_RESEED_INTVL_MAX 3600
61
62 /*
63 * Pool reseed schedule. Initially, only pool 0 is active. Until the timer
64 * interval reaches INTVL_MAX, only pool 0 is used.
65 *
66 * After reaching INTVL_MAX, pool k is either activated (if inactive) or used
67 * (if active) every 3^k timer reseeds. (§ 3.3)
68 *
69 * (Entropy harvesting only round robins across active pools.)
70 */
71 #define FXENT_RESEED_BASE 3
72
73 /*
74 * Number of bytes from high quality sources to allocate to pool 0 before
75 * normal round-robin allocation after each timer reseed. (§ 3.4)
76 */
77 #define FXENT_HI_SRC_POOL0_BYTES 32
78
79 /*
80 * § 3.1
81 *
82 * Low sources provide unconditioned entropy, such as mouse movements; high
83 * sources are assumed to provide high-quality random bytes. Pull sources are
84 * those which can be polled, i.e., anything randomdev calls a "random_source."
85 *
86 * In the whitepaper, low sources are pull. For us, at least in the existing
87 * design, low-quality sources push into some global ring buffer and then get
88 * forwarded into the RNG by a thread that continually polls. Presumably their
89 * design batches low entopy signals in some way (SHA512?) and only requests
90 * them dynamically on reseed. I'm not sure what the benefit is vs feeding
91 * into the pools directly.
92 */
93 enum fxrng_ent_access_cls {
94 FXRNG_PUSH,
95 FXRNG_PULL,
96 };
97 enum fxrng_ent_source_cls {
98 FXRNG_HI,
99 FXRNG_LO,
100 FXRNG_GARBAGE,
101 };
102 struct fxrng_ent_cls {
103 enum fxrng_ent_access_cls entc_axx_cls;
104 enum fxrng_ent_source_cls entc_src_cls;
105 };
106
107 static const struct fxrng_ent_cls fxrng_hi_pull = {
108 .entc_axx_cls = FXRNG_PULL,
109 .entc_src_cls = FXRNG_HI,
110 };
111 static const struct fxrng_ent_cls fxrng_hi_push = {
112 .entc_axx_cls = FXRNG_PUSH,
113 .entc_src_cls = FXRNG_HI,
114 };
115 static const struct fxrng_ent_cls fxrng_lo_push = {
116 .entc_axx_cls = FXRNG_PUSH,
117 .entc_src_cls = FXRNG_LO,
118 };
119 static const struct fxrng_ent_cls fxrng_garbage = {
120 .entc_axx_cls = FXRNG_PUSH,
121 .entc_src_cls = FXRNG_GARBAGE,
122 };
123
124 /*
125 * This table is a mapping of randomdev's current source abstractions to the
126 * designations above; at some point, if the design seems reasonable, it would
127 * make more sense to pull this up into the abstraction layer instead.
128 */
129 static const struct fxrng_ent_char {
130 const struct fxrng_ent_cls *entc_cls;
131 } fxrng_ent_char[ENTROPYSOURCE] = {
132 [RANDOM_CACHED] = {
133 .entc_cls = &fxrng_hi_push,
134 },
135 [RANDOM_ATTACH] = {
136 .entc_cls = &fxrng_lo_push,
137 },
138 [RANDOM_KEYBOARD] = {
139 .entc_cls = &fxrng_lo_push,
140 },
141 [RANDOM_MOUSE] = {
142 .entc_cls = &fxrng_lo_push,
143 },
144 [RANDOM_NET_TUN] = {
145 .entc_cls = &fxrng_lo_push,
146 },
147 [RANDOM_NET_ETHER] = {
148 .entc_cls = &fxrng_lo_push,
149 },
150 [RANDOM_NET_NG] = {
151 .entc_cls = &fxrng_lo_push,
152 },
153 [RANDOM_INTERRUPT] = {
154 .entc_cls = &fxrng_lo_push,
155 },
156 [RANDOM_SWI] = {
157 .entc_cls = &fxrng_lo_push,
158 },
159 [RANDOM_FS_ATIME] = {
160 .entc_cls = &fxrng_lo_push,
161 },
162 [RANDOM_UMA] = {
163 .entc_cls = &fxrng_lo_push,
164 },
165 [RANDOM_CALLOUT] = {
166 .entc_cls = &fxrng_lo_push,
167 },
168 [RANDOM_PURE_OCTEON] = {
169 .entc_cls = &fxrng_hi_push, /* Could be made pull. */
170 },
171 [RANDOM_PURE_SAFE] = {
172 .entc_cls = &fxrng_hi_push,
173 },
174 [RANDOM_PURE_GLXSB] = {
175 .entc_cls = &fxrng_hi_push,
176 },
177 [RANDOM_PURE_HIFN] = {
178 .entc_cls = &fxrng_hi_push,
179 },
180 [RANDOM_PURE_RDRAND] = {
181 .entc_cls = &fxrng_hi_pull,
182 },
183 [RANDOM_PURE_NEHEMIAH] = {
184 .entc_cls = &fxrng_hi_pull,
185 },
186 [RANDOM_PURE_RNDTEST] = {
187 .entc_cls = &fxrng_garbage,
188 },
189 [RANDOM_PURE_VIRTIO] = {
190 .entc_cls = &fxrng_hi_pull,
191 },
192 [RANDOM_PURE_BROADCOM] = {
193 .entc_cls = &fxrng_hi_push,
194 },
195 [RANDOM_PURE_CCP] = {
196 .entc_cls = &fxrng_hi_pull,
197 },
198 [RANDOM_PURE_DARN] = {
199 .entc_cls = &fxrng_hi_pull,
200 },
201 [RANDOM_PURE_TPM] = {
202 .entc_cls = &fxrng_hi_push,
203 },
204 [RANDOM_PURE_VMGENID] = {
205 .entc_cls = &fxrng_hi_push,
206 },
207 };
208
209 /* Useful for single-bit-per-source state. */
210 BITSET_DEFINE(fxrng_bits, ENTROPYSOURCE);
211
212 /* XXX Borrowed from not-yet-committed D22702. */
213 #ifndef BIT_TEST_SET_ATOMIC_ACQ
214 #define BIT_TEST_SET_ATOMIC_ACQ(_s, n, p) \
215 (atomic_testandset_acq_long( \
216 &(p)->__bits[__bitset_word((_s), (n))], (n)) != 0)
217 #endif
218 #define FXENT_TEST_SET_ATOMIC_ACQ(n, p) \
219 BIT_TEST_SET_ATOMIC_ACQ(ENTROPYSOURCE, n, p)
220
221 /* For special behavior on first-time entropy sources. (§ 3.1) */
222 static struct fxrng_bits __read_mostly fxrng_seen;
223
224 /* For special behavior for high-entropy sources after a reseed. (§ 3.4) */
225 _Static_assert(FXENT_HI_SRC_POOL0_BYTES <= UINT8_MAX, "");
226 static uint8_t __read_mostly fxrng_reseed_seen[ENTROPYSOURCE];
227
228 /* Entropy pools. Lock order is ENT -> RNG(root) -> RNG(leaf). */
229 static struct mtx fxent_pool_lk;
230 MTX_SYSINIT(fx_pool, &fxent_pool_lk, "fx entropy pool lock", MTX_DEF);
231 #define FXENT_LOCK() mtx_lock(&fxent_pool_lk)
232 #define FXENT_UNLOCK() mtx_unlock(&fxent_pool_lk)
233 #define FXENT_ASSERT(rng) mtx_assert(&fxent_pool_lk, MA_OWNED)
234 #define FXENT_ASSERT_NOT(rng) mtx_assert(&fxent_pool_lk, MA_NOTOWNED)
235 static struct fxrng_hash fxent_pool[FXRNG_NPOOLS];
236 static unsigned __read_mostly fxent_nactpools = 1;
237 static struct timeout_task fxent_reseed_timer;
238 static int __read_mostly fxent_timer_ready;
239
240 /*
241 * Track number of bytes of entropy harvested from high-quality sources prior
242 * to initial keying. The idea is to collect more jitter entropy when fewer
243 * high-quality bytes were available and less if we had other good sources. We
244 * want to provide always-on availability but don't necessarily have *any*
245 * great sources on some platforms.
246 *
247 * Like fxrng_ent_char: at some point, if the design seems reasonable, it would
248 * make more sense to pull this up into the abstraction layer instead.
249 *
250 * Jitter entropy is unimplemented for now.
251 */
252 static unsigned long fxrng_preseed_ent;
253
254 void
fxrng_pools_init(void)255 fxrng_pools_init(void)
256 {
257 size_t i;
258
259 for (i = 0; i < nitems(fxent_pool); i++)
260 fxrng_hash_init(&fxent_pool[i]);
261 }
262
263 static inline bool
fxrng_hi_source(enum random_entropy_source src)264 fxrng_hi_source(enum random_entropy_source src)
265 {
266 return (fxrng_ent_char[src].entc_cls->entc_src_cls == FXRNG_HI);
267 }
268
269 /*
270 * A racy check that this high-entropy source's event should contribute to
271 * pool0 on the basis of per-source byte count. The check is racy for two
272 * reasons:
273 * - Performance: The vast majority of the time, we've already taken 32 bytes
274 * from any present high quality source and the racy check lets us avoid
275 * dirtying the cache for the global array.
276 * - Correctness: It's fine that the check is racy. The failure modes are:
277 * • False positive: We will detect when we take the lock.
278 * • False negative: We still collect the entropy; it just won't be
279 * preferentially placed in pool0 in this case.
280 */
281 static inline bool
fxrng_hi_pool0_eligible_racy(enum random_entropy_source src)282 fxrng_hi_pool0_eligible_racy(enum random_entropy_source src)
283 {
284 return (atomic_load_acq_8(&fxrng_reseed_seen[src]) <
285 FXENT_HI_SRC_POOL0_BYTES);
286 }
287
288 /*
289 * Top level entropy processing API from randomdev.
290 *
291 * Invoked by the core randomdev subsystem both for preload entropy, "push"
292 * sources (like interrupts, keyboard, etc) and pull sources (RDRAND, etc).
293 */
294 void
fxrng_event_processor(struct harvest_event * event)295 fxrng_event_processor(struct harvest_event *event)
296 {
297 enum random_entropy_source src;
298 unsigned pool;
299 bool first_time, first_32;
300
301 src = event->he_source;
302
303 ASSERT_DEBUG(event->he_size <= sizeof(event->he_entropy),
304 "%s: he_size: %u > sizeof(he_entropy): %zu", __func__,
305 (unsigned)event->he_size, sizeof(event->he_entropy));
306
307 /*
308 * Zero bytes of source entropy doesn't count as observing this source
309 * for the first time. We still harvest the counter entropy.
310 */
311 first_time = event->he_size > 0 &&
312 !FXENT_TEST_SET_ATOMIC_ACQ(src, &fxrng_seen);
313 if (__predict_false(first_time)) {
314 /*
315 * "The first time [any source] provides entropy, it is used to
316 * directly reseed the root PRNG. The entropy pools are
317 * bypassed." (§ 3.1)
318 *
319 * Unlike Windows, we cannot rely on loader(8) seed material
320 * being present, so we perform initial keying in the kernel.
321 * We use brng_generation 0 to represent an unkeyed state.
322 *
323 * Prior to initial keying, it doesn't make sense to try to mix
324 * the entropy directly with the root PRNG state, as the root
325 * PRNG is unkeyed. Instead, we collect pre-keying dynamic
326 * entropy in pool0 and do not bump the root PRNG seed version
327 * or set its key. Initial keying will incorporate pool0 and
328 * bump the brng_generation (seed version).
329 *
330 * After initial keying, we do directly mix in first-time
331 * entropy sources. We use the root BRNG to generate 32 bytes
332 * and use fxrng_hash to mix it with the new entropy source and
333 * re-key with the first 256 bits of hash output.
334 */
335 FXENT_LOCK();
336 FXRNG_BRNG_LOCK(&fxrng_root);
337 if (__predict_true(fxrng_root.brng_generation > 0)) {
338 /* Bypass the pools: */
339 FXENT_UNLOCK();
340 fxrng_brng_src_reseed(event);
341 FXRNG_BRNG_ASSERT_NOT(&fxrng_root);
342 return;
343 }
344
345 /*
346 * Keying the root PRNG requires both FXENT_LOCK and the PRNG's
347 * lock, so we only need to hold on to the pool lock to prevent
348 * initial keying without this entropy.
349 */
350 FXRNG_BRNG_UNLOCK(&fxrng_root);
351
352 /* Root PRNG hasn't been keyed yet, just accumulate event. */
353 fxrng_hash_update(&fxent_pool[0], &event->he_somecounter,
354 sizeof(event->he_somecounter));
355 fxrng_hash_update(&fxent_pool[0], event->he_entropy,
356 event->he_size);
357
358 if (fxrng_hi_source(src)) {
359 /* Prevent overflow. */
360 if (fxrng_preseed_ent <= ULONG_MAX - event->he_size)
361 fxrng_preseed_ent += event->he_size;
362 }
363 FXENT_UNLOCK();
364 return;
365 }
366 /* !first_time */
367
368 /*
369 * "The first 32 bytes produced by a high entropy source after a reseed
370 * from the pools is always put in pool 0." (§ 3.4)
371 *
372 * The first-32-byte tracking data in fxrng_reseed_seen is reset in
373 * fxent_timer_reseed_npools() below.
374 */
375 first_32 = event->he_size > 0 &&
376 fxrng_hi_source(src) &&
377 atomic_load_acq_int(&fxent_nactpools) > 1 &&
378 fxrng_hi_pool0_eligible_racy(src);
379 if (__predict_false(first_32)) {
380 unsigned rem, seen;
381
382 FXENT_LOCK();
383 seen = fxrng_reseed_seen[src];
384 if (seen == FXENT_HI_SRC_POOL0_BYTES)
385 goto round_robin;
386
387 rem = FXENT_HI_SRC_POOL0_BYTES - seen;
388 rem = MIN(rem, event->he_size);
389
390 fxrng_reseed_seen[src] = seen + rem;
391
392 /*
393 * We put 'rem' bytes in pool0, and any remaining bytes are
394 * round-robin'd across other pools.
395 */
396 fxrng_hash_update(&fxent_pool[0],
397 ((uint8_t *)event->he_entropy) + event->he_size - rem,
398 rem);
399 if (rem == event->he_size) {
400 fxrng_hash_update(&fxent_pool[0], &event->he_somecounter,
401 sizeof(event->he_somecounter));
402 FXENT_UNLOCK();
403 return;
404 }
405
406 /*
407 * If fewer bytes were needed than this even provied, We only
408 * take the last rem bytes of the entropy buffer and leave the
409 * timecounter to be round-robin'd with the remaining entropy.
410 */
411 event->he_size -= rem;
412 goto round_robin;
413 }
414 /* !first_32 */
415
416 FXENT_LOCK();
417
418 round_robin:
419 FXENT_ASSERT();
420 pool = event->he_destination % fxent_nactpools;
421 fxrng_hash_update(&fxent_pool[pool], event->he_entropy,
422 event->he_size);
423 fxrng_hash_update(&fxent_pool[pool], &event->he_somecounter,
424 sizeof(event->he_somecounter));
425
426 if (__predict_false(fxrng_hi_source(src) &&
427 atomic_load_acq_64(&fxrng_root_generation) == 0)) {
428 /* Prevent overflow. */
429 if (fxrng_preseed_ent <= ULONG_MAX - event->he_size)
430 fxrng_preseed_ent += event->he_size;
431 }
432 FXENT_UNLOCK();
433 }
434
435 /*
436 * Top level "seeded" API/signal from randomdev.
437 *
438 * This is our warning that a request is coming: we need to be seeded. In
439 * fenestrasX, a request for random bytes _never_ fails. "We (ed: ditto) have
440 * observed that there are many callers that never check for the error code,
441 * even if they are generating cryptographic key material." (§ 1.6)
442 *
443 * If we returned 'false', both read_random(9) and chacha20_randomstir()
444 * (arc4random(9)) will blindly charge on with something almost certainly worse
445 * than what we've got, or are able to get quickly enough.
446 */
447 bool
fxrng_alg_seeded(void)448 fxrng_alg_seeded(void)
449 {
450 uint8_t hash[FXRNG_HASH_SZ];
451 sbintime_t sbt;
452
453 /* The vast majority of the time, we expect to already be seeded. */
454 if (__predict_true(atomic_load_acq_64(&fxrng_root_generation) != 0))
455 return (true);
456
457 /*
458 * Take the lock and recheck; only one thread needs to do the initial
459 * seeding work.
460 */
461 FXENT_LOCK();
462 if (atomic_load_acq_64(&fxrng_root_generation) != 0) {
463 FXENT_UNLOCK();
464 return (true);
465 }
466 /* XXX Any one-off initial seeding goes here. */
467
468 fxrng_hash_finish(&fxent_pool[0], hash, sizeof(hash));
469 fxrng_hash_init(&fxent_pool[0]);
470
471 fxrng_brng_reseed(hash, sizeof(hash));
472 FXENT_UNLOCK();
473
474 randomdev_unblock();
475 explicit_bzero(hash, sizeof(hash));
476
477 /*
478 * This may be called too early for taskqueue_thread to be initialized.
479 * fxent_pool_timer_init will detect if we've already unblocked and
480 * queue the first timer reseed at that point.
481 */
482 if (atomic_load_acq_int(&fxent_timer_ready) != 0) {
483 sbt = SBT_1S;
484 taskqueue_enqueue_timeout_sbt(taskqueue_thread,
485 &fxent_reseed_timer, -sbt, (sbt / 3), C_PREL(2));
486 }
487 return (true);
488 }
489
490 /*
491 * Timer-based reseeds and pool expansion.
492 */
493 static void
fxent_timer_reseed_npools(unsigned n)494 fxent_timer_reseed_npools(unsigned n)
495 {
496 /*
497 * 64 * 8 => moderately large 512 bytes. Could be static, as we are
498 * only used in a static context. On the other hand, this is in
499 * threadqueue TASK context and we're likely nearly at top of stack
500 * already.
501 */
502 uint8_t hash[FXRNG_HASH_SZ * FXRNG_NPOOLS];
503 unsigned i;
504
505 ASSERT_DEBUG(n > 0 && n <= FXRNG_NPOOLS, "n:%u", n);
506
507 FXENT_ASSERT();
508 /*
509 * Collect entropy from pools 0..n-1 by concatenating the output hashes
510 * and then feeding them into fxrng_brng_reseed, which will hash the
511 * aggregate together with the current root PRNG keystate to produce a
512 * new key. It will also bump the global generation counter
513 * appropriately.
514 */
515 for (i = 0; i < n; i++) {
516 fxrng_hash_finish(&fxent_pool[i], hash + i * FXRNG_HASH_SZ,
517 FXRNG_HASH_SZ);
518 fxrng_hash_init(&fxent_pool[i]);
519 }
520
521 fxrng_brng_reseed(hash, n * FXRNG_HASH_SZ);
522 explicit_bzero(hash, n * FXRNG_HASH_SZ);
523
524 /*
525 * "The first 32 bytes produced by a high entropy source after a reseed
526 * from the pools is always put in pool 0." (§ 3.4)
527 *
528 * So here we reset the tracking (somewhat naively given the majority
529 * of sources on most machines are not what we consider "high", but at
530 * 32 bytes it's smaller than a cache line), so the next 32 bytes are
531 * prioritized into pool0.
532 *
533 * See corresponding use of fxrng_reseed_seen in fxrng_event_processor.
534 */
535 memset(fxrng_reseed_seen, 0, sizeof(fxrng_reseed_seen));
536 FXENT_ASSERT();
537 }
538
539 static void
fxent_timer_reseed(void * ctx __unused,int pending __unused)540 fxent_timer_reseed(void *ctx __unused, int pending __unused)
541 {
542 static unsigned reseed_intvl_sec = 1;
543 /* Only reseeds after FXENT_RESEED_INTVL_MAX is achieved. */
544 static uint64_t reseed_number = 1;
545
546 unsigned next_ival, i, k;
547 sbintime_t sbt;
548
549 if (reseed_intvl_sec < FXENT_RESEED_INTVL_MAX) {
550 next_ival = FXENT_RESSED_INTVL_GFACT * reseed_intvl_sec;
551 if (next_ival > FXENT_RESEED_INTVL_MAX)
552 next_ival = FXENT_RESEED_INTVL_MAX;
553 FXENT_LOCK();
554 fxent_timer_reseed_npools(1);
555 FXENT_UNLOCK();
556 } else {
557 /*
558 * The creation of entropy pools beyond 0 is enabled when the
559 * reseed interval hits the maximum. (§ 3.3)
560 */
561 next_ival = reseed_intvl_sec;
562
563 /*
564 * Pool 0 is used every reseed; pool 1..0 every 3rd reseed; and in
565 * general, pool n..0 every 3^n reseeds.
566 */
567 k = reseed_number;
568 reseed_number++;
569
570 /* Count how many pools, from [0, i), to use for reseed. */
571 for (i = 1; i < MIN(fxent_nactpools + 1, FXRNG_NPOOLS); i++) {
572 if ((k % FXENT_RESEED_BASE) != 0)
573 break;
574 k /= FXENT_RESEED_BASE;
575 }
576
577 /*
578 * If we haven't activated pool i yet, activate it and only
579 * reseed from [0, i-1). (§ 3.3)
580 */
581 FXENT_LOCK();
582 if (i == fxent_nactpools + 1) {
583 fxent_timer_reseed_npools(fxent_nactpools);
584 fxent_nactpools++;
585 } else {
586 /* Just reseed from [0, i). */
587 fxent_timer_reseed_npools(i);
588 }
589 FXENT_UNLOCK();
590 }
591
592 /* Schedule the next reseed. */
593 sbt = next_ival * SBT_1S;
594 taskqueue_enqueue_timeout_sbt(taskqueue_thread, &fxent_reseed_timer,
595 -sbt, (sbt / 3), C_PREL(2));
596
597 reseed_intvl_sec = next_ival;
598 }
599
600 static void
fxent_pool_timer_init(void * dummy __unused)601 fxent_pool_timer_init(void *dummy __unused)
602 {
603 sbintime_t sbt;
604
605 TIMEOUT_TASK_INIT(taskqueue_thread, &fxent_reseed_timer, 0,
606 fxent_timer_reseed, NULL);
607
608 if (atomic_load_acq_64(&fxrng_root_generation) != 0) {
609 sbt = SBT_1S;
610 taskqueue_enqueue_timeout_sbt(taskqueue_thread,
611 &fxent_reseed_timer, -sbt, (sbt / 3), C_PREL(2));
612 }
613 atomic_store_rel_int(&fxent_timer_ready, 1);
614 }
615 /* After taskqueue_thread is initialized in SI_SUB_TASKQ:SI_ORDER_SECOND. */
616 SYSINIT(fxent_pool_timer_init, SI_SUB_TASKQ, SI_ORDER_ANY,
617 fxent_pool_timer_init, NULL);
618