LCOV - code coverage report
Current view: top level - lib/eal/common - rte_random.c (source / functions) Hit Total Coverage
Test: Code coverage Lines: 44 50 88.0 %
Date: 2025-04-03 19:37:06 Functions: 5 6 83.3 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 18 28 64.3 %

           Branch data     Line data    Source code
       1                 :            : /* SPDX-License-Identifier: BSD-3-Clause
       2                 :            :  * Copyright(c) 2019 Ericsson AB
       3                 :            :  */
       4                 :            : 
       5                 :            : #ifdef __RDSEED__
       6                 :            : #ifndef RTE_TOOLCHAIN_MSVC
       7                 :            : #include <x86intrin.h>
       8                 :            : #endif
       9                 :            : #endif
      10                 :            : #include <unistd.h>
      11                 :            : 
      12                 :            : #include <rte_bitops.h>
      13                 :            : #include <rte_branch_prediction.h>
      14                 :            : #include <rte_cycles.h>
      15                 :            : #include <rte_lcore.h>
      16                 :            : #include <rte_lcore_var.h>
      17                 :            : #include <rte_random.h>
      18                 :            : 
      19                 :            : #include "eal_private.h"
      20                 :            : 
      21                 :            : struct __rte_cache_aligned rte_rand_state {
      22                 :            :         uint64_t z1;
      23                 :            :         uint64_t z2;
      24                 :            :         uint64_t z3;
      25                 :            :         uint64_t z4;
      26                 :            :         uint64_t z5;
      27                 :            : };
      28                 :            : 
      29                 :            : static RTE_LCORE_VAR_HANDLE(struct rte_rand_state, rand_state);
      30                 :            : 
      31                 :            : /* instance to be shared by all unregistered non-EAL threads */
      32                 :            : static struct rte_rand_state unregistered_rand_state;
      33                 :            : 
      34                 :            : static uint32_t
      35                 :            : __rte_rand_lcg32(uint32_t *seed)
      36                 :            : {
      37                 :     116100 :         *seed = 1103515245U * *seed + 12345U;
      38                 :            : 
      39                 :            :         return *seed;
      40                 :            : }
      41                 :            : 
      42                 :            : static uint64_t
      43                 :            : __rte_rand_lcg64(uint32_t *seed)
      44                 :            : {
      45                 :            :         uint64_t low;
      46                 :            :         uint64_t high;
      47                 :            : 
      48                 :            :         /* A 64-bit LCG would have been much cleaner, but good
      49                 :            :          * multiplier/increments for such seem hard to come by.
      50                 :            :          */
      51                 :            : 
      52                 :     116100 :         low = __rte_rand_lcg32(seed);
      53                 :     116100 :         high = __rte_rand_lcg32(seed);
      54                 :            : 
      55                 :     116100 :         return low | (high << 32);
      56                 :            : }
      57                 :            : 
      58                 :            : static uint64_t
      59                 :            : __rte_rand_lfsr258_gen_seed(uint32_t *seed, uint64_t min_value)
      60                 :            : {
      61                 :            :         uint64_t res;
      62                 :            : 
      63                 :            :         res = __rte_rand_lcg64(seed);
      64                 :            : 
      65                 :     116100 :         if (res < min_value)
      66                 :          0 :                 res += min_value;
      67                 :            : 
      68                 :            :         return res;
      69                 :            : }
      70                 :            : 
      71                 :            : static void
      72                 :      23220 : __rte_srand_lfsr258(uint64_t seed, struct rte_rand_state *state)
      73                 :            : {
      74                 :            :         uint32_t lcg_seed;
      75                 :            : 
      76         [ -  + ]:      23220 :         lcg_seed = (uint32_t)(seed ^ (seed >> 32));
      77                 :            : 
      78         [ -  + ]:      23220 :         state->z1 = __rte_rand_lfsr258_gen_seed(&lcg_seed, 2UL);
      79         [ -  + ]:      23220 :         state->z2 = __rte_rand_lfsr258_gen_seed(&lcg_seed, 512UL);
      80         [ -  + ]:      23220 :         state->z3 = __rte_rand_lfsr258_gen_seed(&lcg_seed, 4096UL);
      81         [ -  + ]:      23220 :         state->z4 = __rte_rand_lfsr258_gen_seed(&lcg_seed, 131072UL);
      82                 :      23220 :         state->z5 = __rte_rand_lfsr258_gen_seed(&lcg_seed, 8388608UL);
      83                 :      23220 : }
      84                 :            : 
      85                 :            : void
      86                 :        180 : rte_srand(uint64_t seed)
      87                 :            : {
      88                 :            :         unsigned int lcore_id;
      89                 :            : 
      90                 :            :         /* add lcore_id to seed to avoid having the same sequence */
      91         [ +  + ]:      23220 :         for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
      92                 :            :                 struct rte_rand_state *lcore_state =
      93                 :      23040 :                         RTE_LCORE_VAR_LCORE(lcore_id, rand_state);
      94                 :            : 
      95                 :      23040 :                 __rte_srand_lfsr258(seed + lcore_id, lcore_state);
      96                 :            :         }
      97                 :            : 
      98                 :        180 :         __rte_srand_lfsr258(seed + lcore_id, &unregistered_rand_state);
      99                 :        180 : }
     100                 :            : 
     101                 :            : static __rte_always_inline uint64_t
     102                 :            : __rte_rand_lfsr258_comp(uint64_t z, uint64_t a, uint64_t b, uint64_t c,
     103                 :            :                         uint64_t d)
     104                 :            : {
     105                 :  429661288 :         return ((z & c) << d) ^ (((z << a) ^ z) >> b);
     106                 :            : }
     107                 :            : 
     108                 :            : /* Based on L’Ecuyer, P.: Tables of maximally equidistributed combined
     109                 :            :  * LFSR generators.
     110                 :            :  */
     111                 :            : 
     112                 :            : static __rte_always_inline uint64_t
     113                 :            : __rte_rand_lfsr258(struct rte_rand_state *state)
     114                 :            : {
     115                 :  429661288 :         state->z1 = __rte_rand_lfsr258_comp(state->z1, 1UL, 53UL,
     116                 :            :                                             18446744073709551614UL, 10UL);
     117                 :  429661288 :         state->z2 = __rte_rand_lfsr258_comp(state->z2, 24UL, 50UL,
     118                 :            :                                             18446744073709551104UL, 5UL);
     119                 :  429661288 :         state->z3 = __rte_rand_lfsr258_comp(state->z3, 3UL, 23UL,
     120                 :            :                                             18446744073709547520UL, 29UL);
     121                 :  429661288 :         state->z4 = __rte_rand_lfsr258_comp(state->z4, 5UL, 24UL,
     122                 :            :                                             18446744073709420544UL, 23UL);
     123                 :  429661288 :         state->z5 = __rte_rand_lfsr258_comp(state->z5, 3UL, 33UL,
     124                 :            :                                             18446744073701163008UL, 8UL);
     125                 :            : 
     126                 :  429661288 :         return state->z1 ^ state->z2 ^ state->z3 ^ state->z4 ^ state->z5;
     127                 :            : }
     128                 :            : 
     129                 :            : static __rte_always_inline
     130                 :            : struct rte_rand_state *__rte_rand_get_state(void)
     131                 :            : {
     132                 :            :         unsigned int idx;
     133                 :            : 
     134                 :            :         idx = rte_lcore_id();
     135                 :            : 
     136   [ +  -  +  - ]:  428035675 :         if (unlikely(idx == LCORE_ID_ANY)) {
     137                 :            :                 /* Make sure rte_*rand() was called after rte_eal_init(). */
     138                 :            :                 RTE_ASSERT(rand_state != NULL);
     139                 :            :                 return &unregistered_rand_state;
     140                 :            :         }
     141                 :            : 
     142                 :  428035675 :         return RTE_LCORE_VAR(rand_state);
     143                 :            : }
     144                 :            : 
     145                 :            : uint64_t
     146         [ +  - ]:  360330126 : rte_rand(void)
     147                 :            : {
     148                 :            :         struct rte_rand_state *state;
     149                 :            : 
     150                 :            :         state = __rte_rand_get_state();
     151                 :            : 
     152                 :  360330126 :         return __rte_rand_lfsr258(state);
     153                 :            : }
     154                 :            : 
     155                 :            : uint64_t
     156                 :   67705637 : rte_rand_max(uint64_t upper_bound)
     157                 :            : {
     158                 :            :         struct rte_rand_state *state;
     159                 :            :         uint8_t ones;
     160                 :            :         uint8_t leading_zeros;
     161                 :            :         uint64_t mask = ~((uint64_t)0);
     162                 :            :         uint64_t res;
     163                 :            : 
     164         [ +  + ]:   67705637 :         if (unlikely(upper_bound < 2))
     165                 :            :                 return 0;
     166                 :            : 
     167                 :            :         state = __rte_rand_get_state();
     168                 :            : 
     169                 :   67705549 :         ones = rte_popcount64(upper_bound);
     170                 :            : 
     171                 :            :         /* Handle power-of-2 upper_bound as a special case, since it
     172                 :            :          * has no bias issues.
     173                 :            :          */
     174         [ +  + ]:   67705549 :         if (unlikely(ones == 1))
     175                 :   61422187 :                 return __rte_rand_lfsr258(state) & (upper_bound - 1);
     176                 :            : 
     177                 :            :         /* The approach to avoiding bias is to create a mask that
     178                 :            :          * stretches beyond the request value range, and up to the
     179                 :            :          * next power-of-2. In case the masked generated random value
     180                 :            :          * is equal to or greater than the upper bound, just discard
     181                 :            :          * the value and generate a new one.
     182                 :            :          */
     183                 :            : 
     184                 :            :         leading_zeros = rte_clz64(upper_bound);
     185                 :    6283362 :         mask >>= leading_zeros;
     186                 :            : 
     187                 :            :         do {
     188                 :    7908975 :                 res = __rte_rand_lfsr258(state) & mask;
     189         [ +  + ]:    7908975 :         } while (unlikely(res >= upper_bound));
     190                 :            : 
     191                 :            :         return res;
     192                 :            : }
     193                 :            : 
     194                 :            : double
     195                 :          0 : rte_drand(void)
     196                 :            : {
     197                 :            :         static const uint64_t denom = (uint64_t)1 << 53;
     198                 :          0 :         uint64_t rand64 = rte_rand();
     199                 :            : 
     200                 :            :         /*
     201                 :            :          * The double mantissa only has 53 bits, so we uniformly mask off the
     202                 :            :          * high 11 bits and then floating-point divide by 2^53 to achieve a
     203                 :            :          * result in [0, 1).
     204                 :            :          *
     205                 :            :          * We are not allowed to emit 1.0, so denom must be one greater than
     206                 :            :          * the possible range of the preceding step.
     207                 :            :          */
     208                 :            : 
     209                 :          0 :         rand64 &= denom - 1;
     210                 :          0 :         return (double)rand64 / denom;
     211                 :            : }
     212                 :            : 
     213                 :            : static uint64_t
     214                 :            : __rte_random_initial_seed(void)
     215                 :            : {
     216                 :            : #ifdef RTE_LIBEAL_USE_GETENTROPY
     217                 :            :         int ge_rc;
     218                 :            :         uint64_t ge_seed;
     219                 :            : 
     220                 :            :         ge_rc = getentropy(&ge_seed, sizeof(ge_seed));
     221                 :            : 
     222                 :            :         if (ge_rc == 0)
     223                 :            :                 return ge_seed;
     224                 :            : #endif
     225                 :            : #ifdef __RDSEED__
     226                 :            :         unsigned int rdseed_low;
     227                 :            :         unsigned int rdseed_high;
     228                 :            : 
     229                 :            :         /* first fallback: rdseed instruction, if available */
     230   [ +  -  +  - ]:        360 :         if (_rdseed32_step(&rdseed_low) == 1 &&
     231                 :            :             _rdseed32_step(&rdseed_high) == 1)
     232                 :        180 :                 return (uint64_t)rdseed_low | ((uint64_t)rdseed_high << 32);
     233                 :            : #endif
     234                 :            :         /* second fallback: seed using rdtsc */
     235                 :          0 :         return rte_get_tsc_cycles();
     236                 :            : }
     237                 :            : 
     238                 :            : void
     239                 :        180 : eal_rand_init(void)
     240                 :            : {
     241                 :            :         uint64_t seed;
     242                 :            : 
     243                 :        180 :         RTE_LCORE_VAR_ALLOC(rand_state);
     244                 :            : 
     245                 :            :         seed = __rte_random_initial_seed();
     246                 :            : 
     247                 :        180 :         rte_srand(seed);
     248                 :        180 : }

Generated by: LCOV version 1.14