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