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

Generated by: LCOV version 1.14