1 /* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright(c) 2019 Ericsson AB
11 #include <rte_branch_prediction.h>
12 #include <rte_cycles.h>
14 #include <rte_lcore.h>
15 #include <rte_memory.h>
16 #include <rte_random.h>
18 struct rte_rand_state {
24 } __rte_cache_aligned;
26 static struct rte_rand_state rand_states[RTE_MAX_LCORE];
29 __rte_rand_lcg32(uint32_t *seed)
31 *seed = 1103515245U * *seed + 12345U;
37 __rte_rand_lcg64(uint32_t *seed)
42 /* A 64-bit LCG would have been much cleaner, but good
43 * multiplier/increments for such seem hard to come by.
46 low = __rte_rand_lcg32(seed);
47 high = __rte_rand_lcg32(seed);
49 return low | (high << 32);
53 __rte_rand_lfsr258_gen_seed(uint32_t *seed, uint64_t min_value)
57 res = __rte_rand_lcg64(seed);
66 __rte_srand_lfsr258(uint64_t seed, struct rte_rand_state *state)
70 lcg_seed = (uint32_t)(seed ^ (seed >> 32));
72 state->z1 = __rte_rand_lfsr258_gen_seed(&lcg_seed, 2UL);
73 state->z2 = __rte_rand_lfsr258_gen_seed(&lcg_seed, 512UL);
74 state->z3 = __rte_rand_lfsr258_gen_seed(&lcg_seed, 4096UL);
75 state->z4 = __rte_rand_lfsr258_gen_seed(&lcg_seed, 131072UL);
76 state->z5 = __rte_rand_lfsr258_gen_seed(&lcg_seed, 8388608UL);
80 rte_srand(uint64_t seed)
82 unsigned int lcore_id;
84 /* add lcore_id to seed to avoid having the same sequence */
85 for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++)
86 __rte_srand_lfsr258(seed + lcore_id, &rand_states[lcore_id]);
89 static __rte_always_inline uint64_t
90 __rte_rand_lfsr258_comp(uint64_t z, uint64_t a, uint64_t b, uint64_t c,
93 return ((z & c) << d) ^ (((z << a) ^ z) >> b);
96 /* Based on L’Ecuyer, P.: Tables of maximally equidistributed combined
100 static __rte_always_inline uint64_t
101 __rte_rand_lfsr258(struct rte_rand_state *state)
103 state->z1 = __rte_rand_lfsr258_comp(state->z1, 1UL, 53UL,
104 18446744073709551614UL, 10UL);
105 state->z2 = __rte_rand_lfsr258_comp(state->z2, 24UL, 50UL,
106 18446744073709551104UL, 5UL);
107 state->z3 = __rte_rand_lfsr258_comp(state->z3, 3UL, 23UL,
108 18446744073709547520UL, 29UL);
109 state->z4 = __rte_rand_lfsr258_comp(state->z4, 5UL, 24UL,
110 18446744073709420544UL, 23UL);
111 state->z5 = __rte_rand_lfsr258_comp(state->z5, 3UL, 33UL,
112 18446744073701163008UL, 8UL);
114 return state->z1 ^ state->z2 ^ state->z3 ^ state->z4 ^ state->z5;
117 static __rte_always_inline
118 struct rte_rand_state *__rte_rand_get_state(void)
120 unsigned int lcore_id;
122 lcore_id = rte_lcore_id();
124 if (unlikely(lcore_id == LCORE_ID_ANY))
125 lcore_id = rte_get_master_lcore();
127 return &rand_states[lcore_id];
133 struct rte_rand_state *state;
135 state = __rte_rand_get_state();
137 return __rte_rand_lfsr258(state);
141 rte_rand_max(uint64_t upper_bound)
143 struct rte_rand_state *state;
145 uint8_t leading_zeros;
146 uint64_t mask = ~((uint64_t)0);
149 if (unlikely(upper_bound < 2))
152 state = __rte_rand_get_state();
154 ones = __builtin_popcountll(upper_bound);
156 /* Handle power-of-2 upper_bound as a special case, since it
157 * has no bias issues.
159 if (unlikely(ones == 1))
160 return __rte_rand_lfsr258(state) & (upper_bound - 1);
162 /* The approach to avoiding bias is to create a mask that
163 * stretches beyond the request value range, and up to the
164 * next power-of-2. In case the masked generated random value
165 * is equal to or greater than the upper bound, just discard
166 * the value and generate a new one.
169 leading_zeros = __builtin_clzll(upper_bound);
170 mask >>= leading_zeros;
173 res = __rte_rand_lfsr258(state) & mask;
174 } while (unlikely(res >= upper_bound));
180 __rte_random_initial_seed(void)
182 #ifdef RTE_LIBEAL_USE_GETENTROPY
186 ge_rc = getentropy(&ge_seed, sizeof(ge_seed));
192 unsigned int rdseed_low;
193 unsigned int rdseed_high;
195 /* first fallback: rdseed instruction, if available */
196 if (_rdseed32_step(&rdseed_low) == 1 &&
197 _rdseed32_step(&rdseed_high) == 1)
198 return (uint64_t)rdseed_low | ((uint64_t)rdseed_high << 32);
200 /* second fallback: seed using rdtsc */
201 return rte_get_tsc_cycles();
204 RTE_INIT(rte_rand_init)
208 seed = __rte_random_initial_seed();