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

Generated by: LCOV version 1.14