18a0edc91SConrad Meyer /*- 2*4d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 38a0edc91SConrad Meyer * 48a0edc91SConrad Meyer * Copyright 2020 Conrad Meyer <cem@FreeBSD.org>. All rights reserved. 58a0edc91SConrad Meyer * 68a0edc91SConrad Meyer * Redistribution and use in source and binary forms, with or without 78a0edc91SConrad Meyer * modification, are permitted provided that the following conditions 88a0edc91SConrad Meyer * are met: 98a0edc91SConrad Meyer * 1. Redistributions of source code must retain the above copyright 108a0edc91SConrad Meyer * notice, this list of conditions and the following disclaimer. 118a0edc91SConrad Meyer * 2. Redistributions in binary form must reproduce the above copyright 128a0edc91SConrad Meyer * notice, this list of conditions and the following disclaimer in the 138a0edc91SConrad Meyer * documentation and/or other materials provided with the distribution. 148a0edc91SConrad Meyer * 158a0edc91SConrad Meyer * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 168a0edc91SConrad Meyer * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 178a0edc91SConrad Meyer * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 188a0edc91SConrad Meyer * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 198a0edc91SConrad Meyer * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 208a0edc91SConrad Meyer * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 218a0edc91SConrad Meyer * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 228a0edc91SConrad Meyer * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 238a0edc91SConrad Meyer * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 248a0edc91SConrad Meyer * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 258a0edc91SConrad Meyer * SUCH DAMAGE. 268a0edc91SConrad Meyer */ 278a0edc91SConrad Meyer 288a0edc91SConrad Meyer #include <sys/cdefs.h> 298a0edc91SConrad Meyer __FBSDID("$FreeBSD$"); 308a0edc91SConrad Meyer 318a0edc91SConrad Meyer #include <sys/param.h> 328a0edc91SConrad Meyer #include <sys/kernel.h> 338a0edc91SConrad Meyer #include <sys/pcpu.h> 348a0edc91SConrad Meyer #include <sys/prng.h> 358a0edc91SConrad Meyer #include <sys/smp.h> 368a0edc91SConrad Meyer #include <sys/systm.h> 378a0edc91SConrad Meyer 388a0edc91SConrad Meyer #if !PCG_HAS_128BIT_OPS 398a0edc91SConrad Meyer /* On 32-bit platforms, gang together two 32-bit generators. */ 408a0edc91SConrad Meyer typedef struct { 418a0edc91SConrad Meyer pcg32u_random_t states[2]; 428a0edc91SConrad Meyer } pcg64u_random_t; 438a0edc91SConrad Meyer 448a0edc91SConrad Meyer static inline void 458a0edc91SConrad Meyer pcg64u_srandom_r(pcg64u_random_t *state64, uint64_t seed) 468a0edc91SConrad Meyer { 478a0edc91SConrad Meyer pcg32u_srandom_r(&state64->states[0], seed); 488a0edc91SConrad Meyer pcg32u_srandom_r(&state64->states[1], seed); 498a0edc91SConrad Meyer } 508a0edc91SConrad Meyer 518a0edc91SConrad Meyer static inline uint64_t 528a0edc91SConrad Meyer pcg64u_random_r(pcg64u_random_t *state64) 538a0edc91SConrad Meyer { 548a0edc91SConrad Meyer return ((((uint64_t)pcg32u_random_r(&state64->states[0])) << 32) | 558a0edc91SConrad Meyer pcg32u_random_r(&state64->states[1])); 568a0edc91SConrad Meyer } 578a0edc91SConrad Meyer 588a0edc91SConrad Meyer static inline uint64_t 598a0edc91SConrad Meyer pcg64u_boundedrand_r(pcg64u_random_t *state64, uint64_t bound) 608a0edc91SConrad Meyer { 618a0edc91SConrad Meyer uint64_t threshold = -bound % bound; 628a0edc91SConrad Meyer for (;;) { 638a0edc91SConrad Meyer uint64_t r = pcg64u_random_r(state64); 648a0edc91SConrad Meyer if (r >= threshold) 658a0edc91SConrad Meyer return (r % bound); 668a0edc91SConrad Meyer } 678a0edc91SConrad Meyer } 688a0edc91SConrad Meyer #endif 698a0edc91SConrad Meyer 708a0edc91SConrad Meyer DPCPU_DEFINE_STATIC(pcg32u_random_t, pcpu_prng32_state); 718a0edc91SConrad Meyer DPCPU_DEFINE_STATIC(pcg64u_random_t, pcpu_prng64_state); 728a0edc91SConrad Meyer 738a0edc91SConrad Meyer static void 748a0edc91SConrad Meyer prng_init(void *dummy __unused) 758a0edc91SConrad Meyer { 768a0edc91SConrad Meyer pcg32u_random_t *state; 778a0edc91SConrad Meyer pcg64u_random_t *state64; 788a0edc91SConrad Meyer int i; 798a0edc91SConrad Meyer 808a0edc91SConrad Meyer CPU_FOREACH(i) { 818a0edc91SConrad Meyer state = DPCPU_ID_PTR(i, pcpu_prng32_state); 828a0edc91SConrad Meyer pcg32u_srandom_r(state, 1); 838a0edc91SConrad Meyer state64 = DPCPU_ID_PTR(i, pcpu_prng64_state); 848a0edc91SConrad Meyer pcg64u_srandom_r(state64, 1); 858a0edc91SConrad Meyer } 868a0edc91SConrad Meyer } 878a0edc91SConrad Meyer SYSINIT(prng_init, SI_SUB_CPU, SI_ORDER_ANY, prng_init, NULL); 888a0edc91SConrad Meyer 898a0edc91SConrad Meyer uint32_t 908a0edc91SConrad Meyer prng32(void) 918a0edc91SConrad Meyer { 928a0edc91SConrad Meyer uint32_t r; 938a0edc91SConrad Meyer 948a0edc91SConrad Meyer critical_enter(); 958a0edc91SConrad Meyer r = pcg32u_random_r(DPCPU_PTR(pcpu_prng32_state)); 968a0edc91SConrad Meyer critical_exit(); 978a0edc91SConrad Meyer return (r); 988a0edc91SConrad Meyer } 998a0edc91SConrad Meyer 1008a0edc91SConrad Meyer uint32_t 1018a0edc91SConrad Meyer prng32_bounded(uint32_t bound) 1028a0edc91SConrad Meyer { 1038a0edc91SConrad Meyer uint32_t r; 1048a0edc91SConrad Meyer 1058a0edc91SConrad Meyer critical_enter(); 1068a0edc91SConrad Meyer r = pcg32u_boundedrand_r(DPCPU_PTR(pcpu_prng32_state), bound); 1078a0edc91SConrad Meyer critical_exit(); 1088a0edc91SConrad Meyer return (r); 1098a0edc91SConrad Meyer } 1108a0edc91SConrad Meyer 1118a0edc91SConrad Meyer uint64_t 1128a0edc91SConrad Meyer prng64(void) 1138a0edc91SConrad Meyer { 1148a0edc91SConrad Meyer uint64_t r; 1158a0edc91SConrad Meyer 1168a0edc91SConrad Meyer critical_enter(); 1178a0edc91SConrad Meyer r = pcg64u_random_r(DPCPU_PTR(pcpu_prng64_state)); 1188a0edc91SConrad Meyer critical_exit(); 1198a0edc91SConrad Meyer return (r); 1208a0edc91SConrad Meyer } 1218a0edc91SConrad Meyer 1228a0edc91SConrad Meyer uint64_t 1238a0edc91SConrad Meyer prng64_bounded(uint64_t bound) 1248a0edc91SConrad Meyer { 1258a0edc91SConrad Meyer uint64_t r; 1268a0edc91SConrad Meyer 1278a0edc91SConrad Meyer critical_enter(); 1288a0edc91SConrad Meyer r = pcg64u_boundedrand_r(DPCPU_PTR(pcpu_prng64_state), bound); 1298a0edc91SConrad Meyer critical_exit(); 1308a0edc91SConrad Meyer return (r); 1318a0edc91SConrad Meyer } 132