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
filebench_randomno64(uint64_t * randp,uint64_t max,uint64_t round,avd_t avd)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
filebench_randomno32(uint32_t * randp,uint32_t max,uint32_t round,avd_t avd)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
rand_src_rand48(unsigned short * xi)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
rand_src_urandom(unsigned short * xi)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
rand_uniform_get(randdist_t * rndp)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
rand_gamma_get(randdist_t * rndp)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
rand_table_get(randdist_t * rndp)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
rand_seed_set(randdist_t * rndp)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 *
randdist_alloc(void)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
randdist_init_one(randdist_t * rndp)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
randdist_init(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
fb_random_init(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