Commit c779c57a authored by siddhu-swarup's avatar siddhu-swarup Committed by Tomasz Zawadzki
Browse files

util: add spdk_rand_xorshift64() and spdk_rand_xorshift64_seed()



Introduced a lightweight 64-bit xorshift pseudo-random number generator (PRNG)
to improve randomness distribution and reproducibility in SPDK tools.

This PRNG resolves the limitations of rand_r(), which only provides 32-bit
entropy and results in LBA repetition on large block devices (e.g., >32 GiB).
Using xorshift64 ensures better coverage across the address space for tools
like bdevperf and nvme_perf.

- Added inline spdk_rand_xorshift64() to include/spdk/util.h
- Added spdk_rand_xorshift64_seed() for safe seed initialization
- Replaced all rand_r() or two-call RNG usages in bdevperf, perf, and zipf
- Improved Doxygen documentation formatting in util.h

Fixes issue #3548.

Change-Id: Ib6a88906efdeffd8d56b68e991da0e17257fdf90
Signed-off-by: default avatarsiddhu-swarup <ialluraiah@msystechnologies.com>
Reviewed-on: https://review.spdk.io/c/spdk/spdk/+/25801


Community-CI: Broadcom CI <spdk-ci.pdl@broadcom.com>
Reviewed-by: default avatarChangpeng Liu <changpeliu@tencent.com>
Tested-by: default avatarSPDK Automated Test System <spdkbot@gmail.com>
Community-CI: Mellanox Build Bot
Reviewed-by: default avatarJim Harris <jim.harris@nvidia.com>
parent 3b5315e8
Loading
Loading
Loading
Loading
+5 −10
Original line number Diff line number Diff line
@@ -86,7 +86,7 @@ struct ns_entry {
	uint32_t		block_size;
	uint32_t		md_size;
	bool			md_interleave;
	unsigned int		seed;
	uint64_t		seed;
	struct spdk_zipf	*zipf;
	bool			pi_loc;
	enum spdk_nvme_pi_type	pi_type;
@@ -1277,7 +1277,7 @@ register_ns(struct spdk_nvme_ctrlr *ctrlr, struct spdk_nvme_ns *ns)
	entry->io_size_blocks = g_io_size_bytes / sector_size;

	if (g_is_random) {
		entry->seed = rand();
		entry->seed = spdk_rand_xorshift64_seed();
		if (g_zipf_theta > 0) {
			entry->zipf = spdk_zipf_create(entry->size_in_ios, g_zipf_theta, 0);
		}
@@ -1445,13 +1445,7 @@ submit_single_io(struct perf_task *task)
	if (entry->zipf) {
		offset_in_ios = spdk_zipf_generate(entry->zipf);
	} else if (g_is_random) {
		/* rand_r() returns int, so we need to use two calls to ensure
		 * we get a large enough value to cover a very large block
		 * device.
		 */
		rand_value = (uint64_t)rand_r(&entry->seed) *
			     ((uint64_t)RAND_MAX + 1) +
			     rand_r(&entry->seed);
		rand_value = spdk_rand_xorshift64(&entry->seed);
		offset_in_ios = rand_value % entry->size_in_ios;
	} else {
		offset_in_ios = ns_ctx->offset_in_ios++;
@@ -1463,7 +1457,8 @@ submit_single_io(struct perf_task *task)
	task->submit_tsc = spdk_get_ticks();

	if ((g_rw_percentage == 100) ||
	    (g_rw_percentage != 0 && ((rand_r(&entry->seed) % 100) < g_rw_percentage))) {
	    (g_rw_percentage != 0 &&
	     ((spdk_rand_xorshift64(&entry->seed) % 100) < (uint64_t)g_rw_percentage))) {
		task->is_read = true;
	} else {
		task->is_read = false;
+7 −10
Original line number Diff line number Diff line
@@ -154,7 +154,7 @@ struct bdevperf_job {
	bool				flush;
	bool				abort;
	int				queue_depth;
	unsigned int			seed;
	uint64_t			seed;

	uint64_t			io_completed;
	uint64_t			io_failed;
@@ -1328,11 +1328,7 @@ bdevperf_submit_single(struct bdevperf_job *job, struct bdevperf_task *task)
	if (job->zipf) {
		offset_in_ios = spdk_zipf_generate(job->zipf);
	} else if (job->is_random) {
		/* RAND_MAX is only INT32_MAX, so use 2 calls to rand_r to
		 * get a large enough value to ensure we are issuing I/O
		 * uniformly across the whole bdev.
		 */
		rand_value = (uint64_t)rand_r(&job->seed) * RAND_MAX + rand_r(&job->seed);
		rand_value = spdk_rand_xorshift64(&job->seed);
		offset_in_ios = rand_value % job->size_in_ios;

		if (g_random_map) {
@@ -1395,7 +1391,8 @@ bdevperf_submit_single(struct bdevperf_job *job, struct bdevperf_task *task)
	} else if (job->write_zeroes) {
		task->io_type = SPDK_BDEV_IO_TYPE_WRITE_ZEROES;
	} else if ((job->rw_percentage == 100) ||
		   (job->rw_percentage != 0 && ((rand_r(&job->seed) % 100) < job->rw_percentage))) {
		   (job->rw_percentage != 0 &&
		    ((spdk_rand_xorshift64(&job->seed) % 100) < (uint64_t)job->rw_percentage))) {
		assert(!job->verify);
		task->io_type = SPDK_BDEV_IO_TYPE_READ;
		if (!g_zcopy) {
@@ -1823,19 +1820,19 @@ job_init_rw(struct bdevperf_job *job, enum job_config_rw rw)
	case JOB_CONFIG_RW_RANDREAD:
		job->is_random = true;
		job->rw_percentage = 100;
		job->seed = rand();
		job->seed = spdk_rand_xorshift64_seed();
		break;
	case JOB_CONFIG_RW_RANDWRITE:
		job->is_random = true;
		job->rw_percentage = 0;
		job->seed = rand();
		job->seed = spdk_rand_xorshift64_seed();
		break;
	case JOB_CONFIG_RW_RW:
		job->is_random = false;
		break;
	case JOB_CONFIG_RW_RANDRW:
		job->is_random = true;
		job->seed = rand();
		job->seed =  spdk_rand_xorshift64_seed();
		break;
	case JOB_CONFIG_RW_RESET:
		/* Reset shares the flow with verify. */
+37 −0
Original line number Diff line number Diff line
@@ -93,6 +93,43 @@ extern "C" {

uint32_t spdk_u32log2(uint32_t x);

/**
 * Generate a 64-bit pseudo-random number using xorshift algorithm.
 *
 * \param state the current seed value.
 * \return a new pseudo-random 64-bit number.
 */
static inline uint64_t
spdk_rand_xorshift64(uint64_t *state)
{
	uint64_t x = *state;

	x ^= x << 13;
	x ^= x >> 7;
	x ^= x << 17;

	*state = x;
	return x;
}

/**
 * Generate a non-zero initial seed for xorshift64.
 *
 * \return a random 64-bit seed value(non-zero).
 */
static inline uint64_t
spdk_rand_xorshift64_seed(void)
{
	uint64_t seed = ((uint64_t)rand() << 32) | rand();

	/* Avoid zero seed */
	if (seed == 0) {
		seed = 1;
	}

	return seed;
}

static inline uint32_t
spdk_align32pow2(uint32_t x)
{
+3 −3
Original line number Diff line number Diff line
@@ -14,7 +14,7 @@ struct spdk_zipf {
	double		theta;
	double		zetan;
	double		val1_limit;
	uint32_t	seed;
	uint64_t	seed;
};

static double
@@ -71,7 +71,7 @@ spdk_zipf_create(uint64_t range, double theta, uint32_t seed)
	}

	zipf->range = range;
	zipf->seed = seed;
	zipf->seed = spdk_rand_xorshift64_seed();

	zipf->theta = theta;
	zipf->alpha = 1.0 / (1.0 - zipf->theta);
@@ -97,7 +97,7 @@ spdk_zipf_generate(struct spdk_zipf *zipf)
	double randu, randz;
	uint64_t val;

	randu = (double)rand_r(&zipf->seed) / RAND_MAX;
	randu = (double)spdk_rand_xorshift64(&zipf->seed) / (double)UINT64_MAX;
	randz = randu * zipf->zetan;

	if (randz < 1.0) {