btrfs-progs: utils: Introduce new pseudo random API
David has reported some quite chaos usage of pseudo random numbers. Like using static srand seed, or even calling rand() without setting seed correctly. The new pseudo random API will initialize the random seed on its first calling and use uniformly distributed pseudo random number generator as backend. Signed-off-by: Qu Wenruo <quwenruo@cn.fujitsu.com> [ renamed variables and functions, added prefixes ] Signed-off-by: David Sterba <dsterba@suse.com>
This commit is contained in:
parent
ee0908ee81
commit
17239a9c89
2 changed files with 96 additions and 0 deletions
62
utils.c
62
utils.c
|
@ -58,6 +58,9 @@ static int btrfs_scan_done = 0;
|
|||
|
||||
static char argv0_buf[ARGV0_BUF_SIZE] = "btrfs";
|
||||
|
||||
static int rand_seed_initlized = 0;
|
||||
static unsigned short rand_seed[3];
|
||||
|
||||
const char *get_argv0_buf(void)
|
||||
{
|
||||
return argv0_buf;
|
||||
|
@ -3227,3 +3230,62 @@ out:
|
|||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void init_rand_seed(u64 seed)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* only use the last 48 bits */
|
||||
for (i = 0; i < 3; i++) {
|
||||
rand_seed[i] = (unsigned short)(seed ^ (unsigned short)(-1));
|
||||
seed >>= 16;
|
||||
}
|
||||
rand_seed_initlized = 1;
|
||||
}
|
||||
|
||||
static void __init_seed(void)
|
||||
{
|
||||
struct timeval tv;
|
||||
int ret;
|
||||
int fd;
|
||||
|
||||
if(rand_seed_initlized)
|
||||
return;
|
||||
/* Use urandom as primary seed source. */
|
||||
fd = open("/dev/urandom", O_RDONLY);
|
||||
if (fd >= 0) {
|
||||
ret = read(fd, rand_seed, sizeof(rand_seed));
|
||||
close(fd);
|
||||
if (ret < sizeof(rand_seed))
|
||||
goto fallback;
|
||||
} else {
|
||||
fallback:
|
||||
/* Use time and pid as fallback seed */
|
||||
warning("failed to read /dev/urandom, use time and pid as random seed");
|
||||
gettimeofday(&tv, 0);
|
||||
rand_seed[0] = getpid() ^ (tv.tv_sec & 0xFFFF);
|
||||
rand_seed[1] = getppid() ^ (tv.tv_usec & 0xFFFF);
|
||||
rand_seed[2] = (tv.tv_sec ^ tv.tv_usec) >> 16;
|
||||
}
|
||||
rand_seed_initlized = 1;
|
||||
}
|
||||
|
||||
u32 rand_u32(void)
|
||||
{
|
||||
__init_seed();
|
||||
/*
|
||||
* Don't use nrand48, its range is [0,2^31) The highest bit will alwasy
|
||||
* be 0. Use jrand48 to include the highest bit.
|
||||
*/
|
||||
return (u32)jrand48(rand_seed);
|
||||
}
|
||||
|
||||
unsigned int rand_range(unsigned int upper)
|
||||
{
|
||||
__init_seed();
|
||||
/*
|
||||
* Use the full 48bits to mod, which would be more uniformly
|
||||
* distributed
|
||||
*/
|
||||
return (unsigned int)(jrand48(rand_seed) % upper);
|
||||
}
|
||||
|
|
34
utils.h
34
utils.h
|
@ -337,4 +337,38 @@ static inline int error_on(int condition, const char *fmt, ...)
|
|||
return 1;
|
||||
}
|
||||
|
||||
/* Pseudo random number generator wrappers */
|
||||
u32 rand_u32(void);
|
||||
|
||||
static inline int rand_int(void)
|
||||
{
|
||||
return (int)(rand_u32());
|
||||
}
|
||||
|
||||
static inline u64 rand_u64(void)
|
||||
{
|
||||
u64 ret = 0;
|
||||
|
||||
ret += rand_u32();
|
||||
ret <<= 32;
|
||||
ret += rand_u32();
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline u16 rand_u16(void)
|
||||
{
|
||||
return (u16)(rand_u32());
|
||||
}
|
||||
|
||||
static inline u8 rand_u8(void)
|
||||
{
|
||||
return (u8)(rand_u32());
|
||||
}
|
||||
|
||||
/* Return random number in range [0, limit) */
|
||||
unsigned int rand_range(unsigned int upper);
|
||||
|
||||
/* Also allow setting the seed manually */
|
||||
void init_rand_seed(u64 seed);
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in a new issue