Home | History | Annotate | Download | only in common
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 /*
     22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 #include <stdio.h>
     27 #include <fcntl.h>
     28 #include <math.h>
     29 #include "filebench.h"
     30 #include "ipc.h"
     31 #include "gamma_dist.h"
     32 
     33 static int urandomfd;
     34 
     35 /*
     36  * Reads a 64 bit random number from the urandom "file".
     37  * Shuts down the run if the read fails. Otherwise returns
     38  * the random number after rounding it off by "round".
     39  * Returns 0 on success, -1 on failure.
     40  */
     41 int
     42 filebench_randomno64(uint64_t *randp, uint64_t max,
     43     uint64_t round, avd_t avd)
     44 {
     45 	uint64_t random;
     46 
     47 	/* check for round value too large */
     48 	if (max <= round) {
     49 		*randp = 0;
     50 
     51 		/* if it just fits, its ok, otherwise error */
     52 		if (max == round)
     53 			return (0);
     54 		else
     55 			return (-1);
     56 	}
     57 
     58 	if (avd) {
     59 
     60 		/* get it from the variable */
     61 		random = avd_get_int(avd);
     62 
     63 	} else {
     64 
     65 		/* get it from urandom */
     66 		if (read(urandomfd, &random,
     67 		    sizeof (uint64_t)) != sizeof (uint64_t)) {
     68 			filebench_log(LOG_ERROR,
     69 			    "read /dev/urandom failed: %s", strerror(errno));
     70 			filebench_shutdown(1);
     71 		}
     72 	}
     73 
     74 	/* clip with max and optionally round */
     75 	max -= round;
     76 	random = random / (FILEBENCH_RANDMAX64 / max);
     77 	if (round) {
     78 		random = random / round;
     79 		random *= round;
     80 	}
     81 	if (random > max)
     82 		random = max;
     83 
     84 	*randp = random;
     85 	return (0);
     86 }
     87 
     88 
     89 /*
     90  * Reads a 32 bit random number from the urandom "file".
     91  * Shuts down the run if the read fails. Otherwise returns
     92  * the random number after rounding it off by "round".
     93  * Returns 0 on success, -1 on failure.
     94  */
     95 int
     96 filebench_randomno32(uint32_t *randp, uint32_t max,
     97     uint32_t round, avd_t avd)
     98 {
     99 	uint32_t random;
    100 
    101 	/* check for round value too large */
    102 	if (max <= round) {
    103 		*randp = 0;
    104 
    105 		/* if it just fits, its ok, otherwise error */
    106 		if (max == round)
    107 			return (0);
    108 		else
    109 			return (-1);
    110 	}
    111 
    112 	if (avd) {
    113 
    114 		/* get it from the variable */
    115 		random = (uint32_t)avd_get_int(avd);
    116 
    117 	} else {
    118 
    119 		/* get it from urandom */
    120 		if (read(urandomfd, &random,
    121 		    sizeof (uint32_t)) != sizeof (uint32_t)) {
    122 			filebench_log(LOG_ERROR,
    123 			    "read /dev/urandom failed: %s", strerror(errno));
    124 			filebench_shutdown(1);
    125 		}
    126 	}
    127 
    128 	/* clip with max and optionally round */
    129 	max -= round;
    130 	random = random / (FILEBENCH_RANDMAX32 / max);
    131 	if (round) {
    132 		random = random / round;
    133 		random *= round;
    134 	}
    135 	if (random > max)
    136 		random = max;
    137 
    138 	*randp = random;
    139 	return (0);
    140 }
    141 
    142 /*
    143  * fetch a source random number from the pseudo random number generator:
    144  * erand48()
    145  */
    146 static double
    147 rand_src_rand48(unsigned short *xi)
    148 {
    149 	return (erand48(xi));
    150 }
    151 
    152 /*
    153  * fetch a source random number from the hardware random number device:
    154  * urandomfd. Convert it to a floating point probability.
    155  */
    156 /* ARGSUSED */
    157 static double
    158 rand_src_urandom(unsigned short *xi)
    159 {
    160 	fbint_t randnum;
    161 
    162 	if (read(urandomfd, &randnum,
    163 	    sizeof (fbint_t)) != sizeof (fbint_t)) {
    164 		filebench_log(LOG_ERROR,
    165 		    "read /dev/urandom failed: %s", strerror(errno));
    166 		filebench_shutdown(1);
    167 		return (0.0);
    168 	}
    169 
    170 	/* convert to 0-1 probability */
    171 	return ((double)randnum / (double)(FILEBENCH_RANDMAX64));
    172 }
    173 
    174 /*
    175  * fetch a uniformly distributed random number from the supplied
    176  * random object.
    177  */
    178 static double
    179 rand_uniform_get(randdist_t *rndp)
    180 {
    181 	double		dprob, dmin, dres, dround;
    182 
    183 	dmin = (double)rndp->rnd_vint_min;
    184 	dround = (double)rndp->rnd_vint_round;
    185 
    186 	dprob = (*rndp->rnd_src)(rndp->rnd_xi);
    187 
    188 	dres = (dprob * (2.0 * (rndp->rnd_dbl_mean - dmin))) + dmin;
    189 
    190 	if (dround == 0.0)
    191 		return (dres);
    192 	else
    193 		return (round(dres / dround) * dround);
    194 }
    195 
    196 /*
    197  * fetch a gamma distributed random number from the supplied
    198  * random object.
    199  */
    200 static double
    201 rand_gamma_get(randdist_t *rndp)
    202 {
    203 	double		dmult, dres, dmin, dround;
    204 
    205 	dmin = (double)rndp->rnd_vint_min;
    206 	dround = (double)rndp->rnd_vint_round;
    207 
    208 	dmult = (rndp->rnd_dbl_mean - dmin) / rndp->rnd_dbl_gamma;
    209 
    210 	dres = gamma_dist_knuth_src(rndp->rnd_dbl_gamma,
    211 	    dmult, rndp->rnd_src, rndp->rnd_xi) + dmin;
    212 
    213 	if (dround == 0.0)
    214 		return (dres);
    215 	else
    216 		return (round(dres / dround) * dround);
    217 }
    218 
    219 /*
    220  * fetch a table driven random number from the supplied
    221  * random object.
    222  */
    223 static double
    224 rand_table_get(randdist_t *rndp)
    225 {
    226 	double		dprob, dprcnt, dtabres, dsclres, dmin, dround;
    227 	int		idx;
    228 
    229 	dmin = (double)rndp->rnd_vint_min;
    230 	dround = (double)rndp->rnd_vint_round;
    231 
    232 	dprob = (*rndp->rnd_src)(rndp->rnd_xi);
    233 
    234 	dprcnt = (dprob * (double)(PF_TAB_SIZE));
    235 	idx = (int)dprcnt;
    236 
    237 	dtabres = (rndp->rnd_rft[idx].rf_base +
    238 	    (rndp->rnd_rft[idx].rf_range * (dprcnt - (double)idx)));
    239 
    240 	dsclres = (dtabres * (rndp->rnd_dbl_mean - dmin)) + dmin;
    241 
    242 	if (dround == 0.0)
    243 		return (dsclres);
    244 	else
    245 		return (round(dsclres / dround) * dround);
    246 }
    247 
    248 /*
    249  * Set the random seed in the supplied random object.
    250  */
    251 static void
    252 rand_seed_set(randdist_t *rndp)
    253 {
    254 	union {
    255 		uint64_t  ll;
    256 		uint16_t  w[4];
    257 	} temp1;
    258 	int  idx;
    259 
    260 	temp1.ll = (uint64_t)avd_get_int(rndp->rnd_seed);
    261 
    262 	for (idx = 0; idx < 3; idx++) {
    263 
    264 #ifdef _BIG_ENDIAN
    265 		rndp->rnd_xi[idx] = temp1.w[3-idx];
    266 #else
    267 		rndp->rnd_xi[idx] = temp1.w[idx];
    268 #endif
    269 	}
    270 }
    271 
    272 /*
    273  * Define a random entity which will contain the parameters of a random
    274  * distribution.
    275  */
    276 randdist_t *
    277 randdist_alloc(void)
    278 {
    279 	randdist_t *rndp;
    280 
    281 	if ((rndp = (randdist_t *)ipc_malloc(FILEBENCH_RANDDIST)) == NULL) {
    282 		filebench_log(LOG_ERROR, "Out of memory for random dist");
    283 		return (NULL);
    284 	}
    285 
    286 	/* place on global list */
    287 	rndp->rnd_next = filebench_shm->shm_rand_list;
    288 	filebench_shm->shm_rand_list = rndp;
    289 
    290 	return (rndp);
    291 }
    292 
    293 /*
    294  * Initializes a random distribution entity, converting avd_t
    295  * parameters to doubles, and converting the list of probability density
    296  * function table entries, if supplied, into a probablilty function table
    297  */
    298 static void
    299 randdist_init_one(randdist_t *rndp)
    300 {
    301 	probtabent_t	*rdte_hdp, *ptep;
    302 	double		tablemean, tablemin;
    303 	int		pteidx;
    304 
    305 	/* convert parameters to doubles */
    306 	rndp->rnd_dbl_gamma = (double)avd_get_int(rndp->rnd_gamma) / 1000.0;
    307 	if (rndp->rnd_mean != NULL)
    308 		rndp->rnd_dbl_mean  = (double)avd_get_int(rndp->rnd_mean);
    309 	else
    310 		rndp->rnd_dbl_mean = rndp->rnd_dbl_gamma;
    311 
    312 	/* de-reference min and round amounts for later use */
    313 	rndp->rnd_vint_min  = avd_get_int(rndp->rnd_min);
    314 	rndp->rnd_vint_round  = avd_get_int(rndp->rnd_round);
    315 
    316 	filebench_log(LOG_DEBUG_IMPL,
    317 	    "init random var %s: Mean = %6.0llf, Gamma = %6.3llf, Min = %llu",
    318 	    rndp->rnd_var->var_name, rndp->rnd_dbl_mean, rndp->rnd_dbl_gamma,
    319 	    (u_longlong_t)rndp->rnd_vint_min);
    320 
    321 	/* initialize distribution to apply */
    322 	switch (rndp->rnd_type & RAND_TYPE_MASK) {
    323 	case RAND_TYPE_UNIFORM:
    324 		rndp->rnd_get = rand_uniform_get;
    325 		break;
    326 
    327 	case RAND_TYPE_GAMMA:
    328 		rndp->rnd_get = rand_gamma_get;
    329 		break;
    330 
    331 	case RAND_TYPE_TABLE:
    332 		rndp->rnd_get = rand_table_get;
    333 		break;
    334 
    335 	default:
    336 		filebench_log(LOG_DEBUG_IMPL, "Random Type not Specified");
    337 		filebench_shutdown(1);
    338 		return;
    339 	}
    340 
    341 	/* initialize source of random numbers */
    342 	if (rndp->rnd_type & RAND_SRC_GENERATOR) {
    343 		rndp->rnd_src = rand_src_rand48;
    344 		rand_seed_set(rndp);
    345 	} else {
    346 		rndp->rnd_src = rand_src_urandom;
    347 	}
    348 
    349 	/* any random distribution table to convert? */
    350 	if ((rdte_hdp = rndp->rnd_probtabs) == NULL)
    351 		return;
    352 
    353 	/* determine random distribution max and mins and initialize table */
    354 	pteidx = 0;
    355 	tablemean = 0.0;
    356 	for (ptep = rdte_hdp; ptep; ptep = ptep->pte_next) {
    357 		double	dmin, dmax;
    358 		int	entcnt;
    359 
    360 		dmax = (double)avd_get_int(ptep->pte_segmax);
    361 		dmin = (double)avd_get_int(ptep->pte_segmin);
    362 
    363 		/* initialize table minimum on first pass */
    364 		if (pteidx == 0)
    365 			tablemin = dmin;
    366 
    367 		/* update table minimum */
    368 		if (tablemin > dmin)
    369 			tablemin = dmin;
    370 
    371 		entcnt = (int)avd_get_int(ptep->pte_percent);
    372 		tablemean += (((dmin + dmax)/2.0) * (double)entcnt);
    373 
    374 		/* populate the lookup table */
    375 
    376 		for (; entcnt > 0; entcnt--) {
    377 			rndp->rnd_rft[pteidx].rf_base = dmin;
    378 			rndp->rnd_rft[pteidx].rf_range = dmax - dmin;
    379 			pteidx++;
    380 		}
    381 	}
    382 
    383 	/* check to see if probability equals 100% */
    384 	if (pteidx != PF_TAB_SIZE)
    385 		filebench_log(LOG_ERROR,
    386 		    "Prob table only totals %d%%", pteidx);
    387 
    388 	/* If table is not supplied with a mean value, set it to table mean */
    389 	if (rndp->rnd_dbl_mean == 0.0)
    390 		rndp->rnd_dbl_mean = (double)tablemean / (double)PF_TAB_SIZE;
    391 
    392 	/* now normalize the entries for a min value of 0, mean of 1 */
    393 	tablemean = (tablemean / 100.0) - tablemin;
    394 
    395 	/* special case if really a constant value */
    396 	if (tablemean == 0.0) {
    397 		for (pteidx = 0; pteidx < PF_TAB_SIZE; pteidx++) {
    398 			rndp->rnd_rft[pteidx].rf_base = 0.0;
    399 			rndp->rnd_rft[pteidx].rf_range = 0.0;
    400 		}
    401 		return;
    402 	}
    403 
    404 	for (pteidx = 0; pteidx < PF_TAB_SIZE; pteidx++) {
    405 
    406 		rndp->rnd_rft[pteidx].rf_base =
    407 		    ((rndp->rnd_rft[pteidx].rf_base - tablemin) / tablemean);
    408 		rndp->rnd_rft[pteidx].rf_range =
    409 		    (rndp->rnd_rft[pteidx].rf_range / tablemean);
    410 	}
    411 }
    412 
    413 /*
    414  * initialize all the random distribution entities
    415  */
    416 void
    417 randdist_init(void)
    418 {
    419 	randdist_t *rndp;
    420 
    421 	for (rndp = filebench_shm->shm_rand_list; rndp; rndp = rndp->rnd_next)
    422 		randdist_init_one(rndp);
    423 }
    424 
    425 /*
    426  * Initialize the urandom random number source
    427  */
    428 void
    429 fb_random_init(void)
    430 {
    431 	/* open the "urandom" random number device file */
    432 	if ((urandomfd = open("/dev/urandom", O_RDONLY)) < 0) {
    433 		filebench_log(LOG_ERROR, "open /dev/urandom failed: %s",
    434 		    strerror(errno));
    435 		filebench_shutdown(1);
    436 	}
    437 }
    438