diff --git a/utils.c b/utils.c index 641eac43..eeff9108 100644 --- a/utils.c +++ b/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); +} diff --git a/utils.h b/utils.h index 7a392c4c..f48c43e3 100644 --- a/utils.h +++ b/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