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 }