xref: /onnv-gate/usr/src/cmd/filebench/common/fb_random.c (revision 8404:b96b8ad1c3e9)
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