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 33static 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 */ 41int 42filebench_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 */ 95int 96filebench_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 */ 146static double 147rand_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 */ 157static double 158rand_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 */ 178static double 179rand_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 */ 200static double 201rand_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 */ 223static double 224rand_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 */ 251static void 252rand_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 */ 276randdist_t * 277randdist_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 */ 298static void 299randdist_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 */ 416void 417randdist_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 */ 428void 429fb_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