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