xref: /onnv-gate/usr/src/cmd/filebench/common/fb_random.c (revision 8404:b96b8ad1c3e9)
16212Saw148015 /*
26212Saw148015  * CDDL HEADER START
36212Saw148015  *
46212Saw148015  * The contents of this file are subject to the terms of the
56212Saw148015  * Common Development and Distribution License (the "License").
66212Saw148015  * You may not use this file except in compliance with the License.
76212Saw148015  *
86212Saw148015  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
96212Saw148015  * or http://www.opensolaris.org/os/licensing.
106212Saw148015  * See the License for the specific language governing permissions
116212Saw148015  * and limitations under the License.
126212Saw148015  *
136212Saw148015  * When distributing Covered Code, include this CDDL HEADER in each
146212Saw148015  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
156212Saw148015  * If applicable, add the following below this CDDL HEADER, with the
166212Saw148015  * fields enclosed by brackets "[]" replaced with your own identifying
176212Saw148015  * information: Portions Copyright [yyyy] [name of copyright owner]
186212Saw148015  *
196212Saw148015  * CDDL HEADER END
206212Saw148015  */
216212Saw148015 /*
226212Saw148015  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
236212Saw148015  * Use is subject to license terms.
246212Saw148015  */
256212Saw148015 
266212Saw148015 #include <stdio.h>
276212Saw148015 #include <fcntl.h>
286212Saw148015 #include <math.h>
296212Saw148015 #include "filebench.h"
306212Saw148015 #include "ipc.h"
316212Saw148015 #include "gamma_dist.h"
326212Saw148015 
336212Saw148015 static int urandomfd;
346212Saw148015 
356212Saw148015 /*
366212Saw148015  * Reads a 64 bit random number from the urandom "file".
376212Saw148015  * Shuts down the run if the read fails. Otherwise returns
386212Saw148015  * the random number after rounding it off by "round".
396212Saw148015  * Returns 0 on success, -1 on failure.
406212Saw148015  */
416212Saw148015 int
filebench_randomno64(uint64_t * randp,uint64_t max,uint64_t round,avd_t avd)426212Saw148015 filebench_randomno64(uint64_t *randp, uint64_t max,
436212Saw148015     uint64_t round, avd_t avd)
446212Saw148015 {
456212Saw148015 	uint64_t random;
466212Saw148015 
476212Saw148015 	/* check for round value too large */
486212Saw148015 	if (max <= round) {
496212Saw148015 		*randp = 0;
506212Saw148015 
516212Saw148015 		/* if it just fits, its ok, otherwise error */
526212Saw148015 		if (max == round)
536212Saw148015 			return (0);
546212Saw148015 		else
556212Saw148015 			return (-1);
566212Saw148015 	}
576212Saw148015 
586212Saw148015 	if (avd) {
596212Saw148015 
606212Saw148015 		/* get it from the variable */
616212Saw148015 		random = avd_get_int(avd);
626212Saw148015 
636212Saw148015 	} else {
646212Saw148015 
656212Saw148015 		/* get it from urandom */
666212Saw148015 		if (read(urandomfd, &random,
676212Saw148015 		    sizeof (uint64_t)) != sizeof (uint64_t)) {
686212Saw148015 			filebench_log(LOG_ERROR,
696212Saw148015 			    "read /dev/urandom failed: %s", strerror(errno));
706212Saw148015 			filebench_shutdown(1);
716212Saw148015 		}
726212Saw148015 	}
736212Saw148015 
746212Saw148015 	/* clip with max and optionally round */
756212Saw148015 	max -= round;
766212Saw148015 	random = random / (FILEBENCH_RANDMAX64 / max);
776212Saw148015 	if (round) {
786212Saw148015 		random = random / round;
796212Saw148015 		random *= round;
806212Saw148015 	}
816212Saw148015 	if (random > max)
826212Saw148015 		random = max;
836212Saw148015 
846212Saw148015 	*randp = random;
856212Saw148015 	return (0);
866212Saw148015 }
876212Saw148015 
886212Saw148015 
896212Saw148015 /*
906212Saw148015  * Reads a 32 bit random number from the urandom "file".
916212Saw148015  * Shuts down the run if the read fails. Otherwise returns
926212Saw148015  * the random number after rounding it off by "round".
936212Saw148015  * Returns 0 on success, -1 on failure.
946212Saw148015  */
956212Saw148015 int
filebench_randomno32(uint32_t * randp,uint32_t max,uint32_t round,avd_t avd)966212Saw148015 filebench_randomno32(uint32_t *randp, uint32_t max,
976212Saw148015     uint32_t round, avd_t avd)
986212Saw148015 {
996212Saw148015 	uint32_t random;
1006212Saw148015 
1016212Saw148015 	/* check for round value too large */
1026212Saw148015 	if (max <= round) {
1036212Saw148015 		*randp = 0;
1046212Saw148015 
1056212Saw148015 		/* if it just fits, its ok, otherwise error */
1066212Saw148015 		if (max == round)
1076212Saw148015 			return (0);
1086212Saw148015 		else
1096212Saw148015 			return (-1);
1106212Saw148015 	}
1116212Saw148015 
1126212Saw148015 	if (avd) {
1136212Saw148015 
1146212Saw148015 		/* get it from the variable */
1156212Saw148015 		random = (uint32_t)avd_get_int(avd);
1166212Saw148015 
1176212Saw148015 	} else {
1186212Saw148015 
1196212Saw148015 		/* get it from urandom */
1206212Saw148015 		if (read(urandomfd, &random,
1216212Saw148015 		    sizeof (uint32_t)) != sizeof (uint32_t)) {
1226212Saw148015 			filebench_log(LOG_ERROR,
1236212Saw148015 			    "read /dev/urandom failed: %s", strerror(errno));
1246212Saw148015 			filebench_shutdown(1);
1256212Saw148015 		}
1266212Saw148015 	}
1276212Saw148015 
1286212Saw148015 	/* clip with max and optionally round */
1296212Saw148015 	max -= round;
1306212Saw148015 	random = random / (FILEBENCH_RANDMAX32 / max);
1316212Saw148015 	if (round) {
1326212Saw148015 		random = random / round;
1336212Saw148015 		random *= round;
1346212Saw148015 	}
1356212Saw148015 	if (random > max)
1366212Saw148015 		random = max;
1376212Saw148015 
1386212Saw148015 	*randp = random;
1396212Saw148015 	return (0);
1406212Saw148015 }
1416212Saw148015 
1426212Saw148015 /*
1436212Saw148015  * fetch a source random number from the pseudo random number generator:
1446212Saw148015  * erand48()
1456212Saw148015  */
1466212Saw148015 static double
rand_src_rand48(unsigned short * xi)1476212Saw148015 rand_src_rand48(unsigned short *xi)
1486212Saw148015 {
1496212Saw148015 	return (erand48(xi));
1506212Saw148015 }
1516212Saw148015 
1526212Saw148015 /*
1536212Saw148015  * fetch a source random number from the hardware random number device:
1546212Saw148015  * urandomfd. Convert it to a floating point probability.
1556212Saw148015  */
1566212Saw148015 /* ARGSUSED */
1576212Saw148015 static double
rand_src_urandom(unsigned short * xi)1586212Saw148015 rand_src_urandom(unsigned short *xi)
1596212Saw148015 {
1606212Saw148015 	fbint_t randnum;
1616212Saw148015 
1626212Saw148015 	if (read(urandomfd, &randnum,
1636212Saw148015 	    sizeof (fbint_t)) != sizeof (fbint_t)) {
1646212Saw148015 		filebench_log(LOG_ERROR,
1656212Saw148015 		    "read /dev/urandom failed: %s", strerror(errno));
1666212Saw148015 		filebench_shutdown(1);
1676212Saw148015 		return (0.0);
1686212Saw148015 	}
1696212Saw148015 
1706212Saw148015 	/* convert to 0-1 probability */
1716212Saw148015 	return ((double)randnum / (double)(FILEBENCH_RANDMAX64));
1726212Saw148015 }
1736212Saw148015 
1746212Saw148015 /*
1756212Saw148015  * fetch a uniformly distributed random number from the supplied
1766212Saw148015  * random object.
1776212Saw148015  */
1786212Saw148015 static double
rand_uniform_get(randdist_t * rndp)1796212Saw148015 rand_uniform_get(randdist_t *rndp)
1806212Saw148015 {
1816212Saw148015 	double		dprob, dmin, dres, dround;
1826212Saw148015 
1836212Saw148015 	dmin = (double)rndp->rnd_vint_min;
1846212Saw148015 	dround = (double)rndp->rnd_vint_round;
1856212Saw148015 
1866212Saw148015 	dprob = (*rndp->rnd_src)(rndp->rnd_xi);
1876212Saw148015 
1886212Saw148015 	dres = (dprob * (2.0 * (rndp->rnd_dbl_mean - dmin))) + dmin;
1896212Saw148015 
1906212Saw148015 	if (dround == 0.0)
1916212Saw148015 		return (dres);
1926212Saw148015 	else
1936212Saw148015 		return (round(dres / dround) * dround);
1946212Saw148015 }
1956212Saw148015 
1966212Saw148015 /*
1976212Saw148015  * fetch a gamma distributed random number from the supplied
1986212Saw148015  * random object.
1996212Saw148015  */
2006212Saw148015 static double
rand_gamma_get(randdist_t * rndp)2016212Saw148015 rand_gamma_get(randdist_t *rndp)
2026212Saw148015 {
2036212Saw148015 	double		dmult, dres, dmin, dround;
2046212Saw148015 
2056212Saw148015 	dmin = (double)rndp->rnd_vint_min;
2066212Saw148015 	dround = (double)rndp->rnd_vint_round;
2076212Saw148015 
2086212Saw148015 	dmult = (rndp->rnd_dbl_mean - dmin) / rndp->rnd_dbl_gamma;
2096212Saw148015 
2106212Saw148015 	dres = gamma_dist_knuth_src(rndp->rnd_dbl_gamma,
2116212Saw148015 	    dmult, rndp->rnd_src, rndp->rnd_xi) + dmin;
2126212Saw148015 
2136212Saw148015 	if (dround == 0.0)
2146212Saw148015 		return (dres);
2156212Saw148015 	else
2166212Saw148015 		return (round(dres / dround) * dround);
2176212Saw148015 }
2186212Saw148015 
2196212Saw148015 /*
2206212Saw148015  * fetch a table driven random number from the supplied
2216212Saw148015  * random object.
2226212Saw148015  */
2236212Saw148015 static double
rand_table_get(randdist_t * rndp)2246212Saw148015 rand_table_get(randdist_t *rndp)
2256212Saw148015 {
2266212Saw148015 	double		dprob, dprcnt, dtabres, dsclres, dmin, dround;
2276212Saw148015 	int		idx;
2286212Saw148015 
2296212Saw148015 	dmin = (double)rndp->rnd_vint_min;
2306212Saw148015 	dround = (double)rndp->rnd_vint_round;
2316212Saw148015 
2326212Saw148015 	dprob = (*rndp->rnd_src)(rndp->rnd_xi);
2336212Saw148015 
2346212Saw148015 	dprcnt = (dprob * (double)(PF_TAB_SIZE));
2356212Saw148015 	idx = (int)dprcnt;
2366212Saw148015 
2376212Saw148015 	dtabres = (rndp->rnd_rft[idx].rf_base +
2386212Saw148015 	    (rndp->rnd_rft[idx].rf_range * (dprcnt - (double)idx)));
2396212Saw148015 
2406212Saw148015 	dsclres = (dtabres * (rndp->rnd_dbl_mean - dmin)) + dmin;
2416212Saw148015 
2426212Saw148015 	if (dround == 0.0)
2436212Saw148015 		return (dsclres);
2446212Saw148015 	else
2456212Saw148015 		return (round(dsclres / dround) * dround);
2466212Saw148015 }
2476212Saw148015 
2486212Saw148015 /*
2496212Saw148015  * Set the random seed in the supplied random object.
2506212Saw148015  */
2516212Saw148015 static void
rand_seed_set(randdist_t * rndp)2526212Saw148015 rand_seed_set(randdist_t *rndp)
2536212Saw148015 {
2546212Saw148015 	union {
2556212Saw148015 		uint64_t  ll;
2566212Saw148015 		uint16_t  w[4];
2576212Saw148015 	} temp1;
2586212Saw148015 	int  idx;
2596212Saw148015 
2606212Saw148015 	temp1.ll = (uint64_t)avd_get_int(rndp->rnd_seed);
2616212Saw148015 
2626212Saw148015 	for (idx = 0; idx < 3; idx++) {
2636212Saw148015 
2646212Saw148015 #ifdef _BIG_ENDIAN
2656212Saw148015 		rndp->rnd_xi[idx] = temp1.w[3-idx];
2666212Saw148015 #else
2676212Saw148015 		rndp->rnd_xi[idx] = temp1.w[idx];
2686212Saw148015 #endif
2696212Saw148015 	}
2706212Saw148015 }
2716212Saw148015 
2726212Saw148015 /*
2736212Saw148015  * Define a random entity which will contain the parameters of a random
2746212Saw148015  * distribution.
2756212Saw148015  */
2766212Saw148015 randdist_t *
randdist_alloc(void)2776212Saw148015 randdist_alloc(void)
2786212Saw148015 {
2796212Saw148015 	randdist_t *rndp;
2806212Saw148015 
2816212Saw148015 	if ((rndp = (randdist_t *)ipc_malloc(FILEBENCH_RANDDIST)) == NULL) {
2826212Saw148015 		filebench_log(LOG_ERROR, "Out of memory for random dist");
2836212Saw148015 		return (NULL);
2846212Saw148015 	}
2856212Saw148015 
2866212Saw148015 	/* place on global list */
2876212Saw148015 	rndp->rnd_next = filebench_shm->shm_rand_list;
2886212Saw148015 	filebench_shm->shm_rand_list = rndp;
2896212Saw148015 
2906212Saw148015 	return (rndp);
2916212Saw148015 }
2926212Saw148015 
2936212Saw148015 /*
2946212Saw148015  * Initializes a random distribution entity, converting avd_t
2956212Saw148015  * parameters to doubles, and converting the list of probability density
2966212Saw148015  * function table entries, if supplied, into a probablilty function table
2976212Saw148015  */
2986212Saw148015 static void
randdist_init_one(randdist_t * rndp)2996212Saw148015 randdist_init_one(randdist_t *rndp)
3006212Saw148015 {
3016212Saw148015 	probtabent_t	*rdte_hdp, *ptep;
3026212Saw148015 	double		tablemean, tablemin;
3036212Saw148015 	int		pteidx;
3046212Saw148015 
3056212Saw148015 	/* convert parameters to doubles */
3066212Saw148015 	rndp->rnd_dbl_gamma = (double)avd_get_int(rndp->rnd_gamma) / 1000.0;
307*8404SAndrew.W.Wilson@sun.com 	if (rndp->rnd_mean != NULL)
308*8404SAndrew.W.Wilson@sun.com 		rndp->rnd_dbl_mean  = (double)avd_get_int(rndp->rnd_mean);
309*8404SAndrew.W.Wilson@sun.com 	else
310*8404SAndrew.W.Wilson@sun.com 		rndp->rnd_dbl_mean = rndp->rnd_dbl_gamma;
3116212Saw148015 
312*8404SAndrew.W.Wilson@sun.com 	/* de-reference min and round amounts for later use */
3136212Saw148015 	rndp->rnd_vint_min  = avd_get_int(rndp->rnd_min);
3146212Saw148015 	rndp->rnd_vint_round  = avd_get_int(rndp->rnd_round);
3156212Saw148015 
3166212Saw148015 	filebench_log(LOG_DEBUG_IMPL,
3176286Saw148015 	    "init random var %s: Mean = %6.0llf, Gamma = %6.3llf, Min = %llu",
3186212Saw148015 	    rndp->rnd_var->var_name, rndp->rnd_dbl_mean, rndp->rnd_dbl_gamma,
3196286Saw148015 	    (u_longlong_t)rndp->rnd_vint_min);
3206212Saw148015 
3216212Saw148015 	/* initialize distribution to apply */
3226212Saw148015 	switch (rndp->rnd_type & RAND_TYPE_MASK) {
3236212Saw148015 	case RAND_TYPE_UNIFORM:
3246212Saw148015 		rndp->rnd_get = rand_uniform_get;
3256212Saw148015 		break;
3266212Saw148015 
3276212Saw148015 	case RAND_TYPE_GAMMA:
3286212Saw148015 		rndp->rnd_get = rand_gamma_get;
3296212Saw148015 		break;
3306212Saw148015 
3316212Saw148015 	case RAND_TYPE_TABLE:
3326212Saw148015 		rndp->rnd_get = rand_table_get;
3336212Saw148015 		break;
3346212Saw148015 
3356212Saw148015 	default:
3366212Saw148015 		filebench_log(LOG_DEBUG_IMPL, "Random Type not Specified");
3376212Saw148015 		filebench_shutdown(1);
3386212Saw148015 		return;
3396212Saw148015 	}
3406212Saw148015 
3416212Saw148015 	/* initialize source of random numbers */
3426212Saw148015 	if (rndp->rnd_type & RAND_SRC_GENERATOR) {
3436212Saw148015 		rndp->rnd_src = rand_src_rand48;
3446212Saw148015 		rand_seed_set(rndp);
3456212Saw148015 	} else {
3466212Saw148015 		rndp->rnd_src = rand_src_urandom;
3476212Saw148015 	}
3486212Saw148015 
3496212Saw148015 	/* any random distribution table to convert? */
3506212Saw148015 	if ((rdte_hdp = rndp->rnd_probtabs) == NULL)
3516212Saw148015 		return;
3526212Saw148015 
3536212Saw148015 	/* determine random distribution max and mins and initialize table */
3546212Saw148015 	pteidx = 0;
3556212Saw148015 	tablemean = 0.0;
3566212Saw148015 	for (ptep = rdte_hdp; ptep; ptep = ptep->pte_next) {
3576212Saw148015 		double	dmin, dmax;
3586212Saw148015 		int	entcnt;
3596212Saw148015 
3606212Saw148015 		dmax = (double)avd_get_int(ptep->pte_segmax);
3616212Saw148015 		dmin = (double)avd_get_int(ptep->pte_segmin);
3626212Saw148015 
3636212Saw148015 		/* initialize table minimum on first pass */
3646212Saw148015 		if (pteidx == 0)
3656212Saw148015 			tablemin = dmin;
3666212Saw148015 
3676212Saw148015 		/* update table minimum */
3686212Saw148015 		if (tablemin > dmin)
3696212Saw148015 			tablemin = dmin;
3706212Saw148015 
3716212Saw148015 		entcnt = (int)avd_get_int(ptep->pte_percent);
3726212Saw148015 		tablemean += (((dmin + dmax)/2.0) * (double)entcnt);
3736212Saw148015 
3746212Saw148015 		/* populate the lookup table */
3756212Saw148015 
3766212Saw148015 		for (; entcnt > 0; entcnt--) {
3776212Saw148015 			rndp->rnd_rft[pteidx].rf_base = dmin;
3786212Saw148015 			rndp->rnd_rft[pteidx].rf_range = dmax - dmin;
3796212Saw148015 			pteidx++;
3806212Saw148015 		}
3816212Saw148015 	}
3826212Saw148015 
3836212Saw148015 	/* check to see if probability equals 100% */
3846212Saw148015 	if (pteidx != PF_TAB_SIZE)
3856212Saw148015 		filebench_log(LOG_ERROR,
3866212Saw148015 		    "Prob table only totals %d%%", pteidx);
3876212Saw148015 
3886212Saw148015 	/* If table is not supplied with a mean value, set it to table mean */
3896212Saw148015 	if (rndp->rnd_dbl_mean == 0.0)
3906212Saw148015 		rndp->rnd_dbl_mean = (double)tablemean / (double)PF_TAB_SIZE;
3916212Saw148015 
3926212Saw148015 	/* now normalize the entries for a min value of 0, mean of 1 */
3936212Saw148015 	tablemean = (tablemean / 100.0) - tablemin;
3946212Saw148015 
3956212Saw148015 	/* special case if really a constant value */
3966212Saw148015 	if (tablemean == 0.0) {
3976212Saw148015 		for (pteidx = 0; pteidx < PF_TAB_SIZE; pteidx++) {
3986212Saw148015 			rndp->rnd_rft[pteidx].rf_base = 0.0;
3996212Saw148015 			rndp->rnd_rft[pteidx].rf_range = 0.0;
4006212Saw148015 		}
4016212Saw148015 		return;
4026212Saw148015 	}
4036212Saw148015 
4046212Saw148015 	for (pteidx = 0; pteidx < PF_TAB_SIZE; pteidx++) {
4056212Saw148015 
4066212Saw148015 		rndp->rnd_rft[pteidx].rf_base =
4076212Saw148015 		    ((rndp->rnd_rft[pteidx].rf_base - tablemin) / tablemean);
4086212Saw148015 		rndp->rnd_rft[pteidx].rf_range =
4096212Saw148015 		    (rndp->rnd_rft[pteidx].rf_range / tablemean);
4106212Saw148015 	}
4116212Saw148015 }
4126212Saw148015 
4136212Saw148015 /*
4146212Saw148015  * initialize all the random distribution entities
4156212Saw148015  */
4166212Saw148015 void
randdist_init(void)4176212Saw148015 randdist_init(void)
4186212Saw148015 {
4196212Saw148015 	randdist_t *rndp;
4206212Saw148015 
4216212Saw148015 	for (rndp = filebench_shm->shm_rand_list; rndp; rndp = rndp->rnd_next)
4226212Saw148015 		randdist_init_one(rndp);
4236212Saw148015 }
4246212Saw148015 
4256212Saw148015 /*
4266212Saw148015  * Initialize the urandom random number source
4276212Saw148015  */
4286212Saw148015 void
fb_random_init(void)4296212Saw148015 fb_random_init(void)
4306212Saw148015 {
4316212Saw148015 	/* open the "urandom" random number device file */
4326212Saw148015 	if ((urandomfd = open("/dev/urandom", O_RDONLY)) < 0) {
4336212Saw148015 		filebench_log(LOG_ERROR, "open /dev/urandom failed: %s",
4346212Saw148015 		    strerror(errno));
4356212Saw148015 		filebench_shutdown(1);
4366212Saw148015 	}
4376212Saw148015 }
438