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 /* this definition prevents warning about
29    using undefined round() function */
30 #include <math.h>
31 #include "filebench.h"
32 #include "ipc.h"
33 #include "gamma_dist.h"
34 #include "cvars/mtwist/mtwist.h"
35 
36 /*
37  * Generates a 64-bit random number using mtwist or from a provided random
38  * variable "avd".
39  *
40  * Returned random number "randp" is clipped by the "max" value and rounded off
41  * by the "round" value.  Returns 0 on success, shuts down Filebench on
42  * failure.
43  */
44 void
fb_random64(uint64_t * randp,uint64_t max,uint64_t round,avd_t avd)45 fb_random64(uint64_t *randp, uint64_t max, uint64_t round, avd_t avd)
46 {
47 	double random_normalized;
48 	uint64_t random = 0;
49 
50 	if (avd) {
51 		/* get it from the random variable */
52 		if (!AVD_IS_RANDOM(avd)) {
53 			/* trying to get random value not from
54 				random variable. That's a clear error. */
55 			filebench_log(LOG_ERROR, "filebench_randomno64: trying"
56 			" to get a random value from not a random variable");
57 			filebench_shutdown(1);
58 			/* NOT REACHABLE */
59 		} else {
60 			random = avd_get_int(avd);
61 		}
62 	} else {
63 		random = mt_llrand();
64 	}
65 
66 	/*
67 	 * If round is not zero, then caller will get a random number in the
68 	 * range [0; max - round].  This allows the caller to use this
69 	 * function, for example, to obtain a pointer in an allocated memory
70 	 * region of [0; max] and read/write round bytes safely starting
71 	 * at this pointer.
72 	 */
73 	max = max - round;
74 
75 	random_normalized = (double)random / UINT64_MAX;
76 	random = random_normalized * max;
77 
78 	if (round) {
79 		random = random / round;
80 		random = random * round;
81 	}
82 
83 	*randp = random;
84 }
85 
86 /*
87  * Same as filebench_randomno64, but for 32 bit integers.
88  */
89 
90 void
fb_random32(uint32_t * randp,uint32_t max,uint32_t round,avd_t avd)91 fb_random32(uint32_t *randp,
92 		uint32_t max, uint32_t round, avd_t avd)
93 {
94 	uint64_t rand64;
95 
96 	fb_random64(&rand64, max, round, avd);
97 
98 	/* rand64 always fits uint32, since "max" above was 32 bit */
99 	*randp = (uint32_t)rand64;
100 }
101 
102 /*
103  * Same as filebench_randomno64, but for probability [0-1].
104  */
105 static double
fb_random_probability()106 fb_random_probability()
107 {
108 	uint64_t randnum;
109 
110 	fb_random64(&randnum, UINT64_MAX, 0, NULL);
111 
112 	/* convert to 0-1 probability */
113 	return (double)randnum / (double)(UINT64_MAX);
114 }
115 
116 /****************************************
117  *					*
118  * randist related functions		*
119  *					*
120  ****************************************/
121 
122 static double
fb_rand_src_rand48(unsigned short * xi)123 fb_rand_src_rand48(unsigned short *xi)
124 {
125 	return (erand48(xi));
126 }
127 
128 static double
fb_rand_src_random(unsigned short * xi)129 fb_rand_src_random(unsigned short *xi)
130 {
131 	return fb_random_probability();
132 }
133 
134 /*
135  * fetch a uniformly distributed random number from the supplied
136  * random object.
137  */
138 static double
rand_uniform_get(randdist_t * rndp)139 rand_uniform_get(randdist_t *rndp)
140 {
141 	double		dprob, dmin, dres, dround;
142 
143 	dmin = (double)rndp->rnd_vint_min;
144 	dround = (double)rndp->rnd_vint_round;
145 
146 	dprob = (*rndp->rnd_src)(rndp->rnd_xi);
147 
148 	dres = (dprob * (2.0 * (rndp->rnd_dbl_mean - dmin))) + dmin;
149 
150 	if (dround == 0.0)
151 		return (dres);
152 	else
153 		return (round(dres / dround) * dround);
154 }
155 
156 /*
157  * fetch a gamma distributed random number from the supplied
158  * random object.
159  */
160 static double
rand_gamma_get(randdist_t * rndp)161 rand_gamma_get(randdist_t *rndp)
162 {
163 	double		dmult, dres, dmin, dround;
164 
165 	dmin = (double)rndp->rnd_vint_min;
166 	dround = (double)rndp->rnd_vint_round;
167 
168 	dmult = (rndp->rnd_dbl_mean - dmin) / rndp->rnd_dbl_gamma;
169 
170 	dres = gamma_dist_knuth_src(rndp->rnd_dbl_gamma,
171 	    dmult, rndp->rnd_src, rndp->rnd_xi) + dmin;
172 
173 	if (dround == 0.0)
174 		return (dres);
175 	else
176 		return (round(dres / dround) * dround);
177 }
178 
179 /*
180  * fetch a table driven random number from the supplied
181  * random object.
182  */
183 static double
rand_table_get(randdist_t * rndp)184 rand_table_get(randdist_t *rndp)
185 {
186 	double		dprob, dprcnt, dtabres, dsclres, dmin, dround;
187 	int		idx;
188 
189 	dmin = (double)rndp->rnd_vint_min;
190 	dround = (double)rndp->rnd_vint_round;
191 
192 	dprob = (*rndp->rnd_src)(rndp->rnd_xi);
193 
194 	dprcnt = (dprob * (double)(PF_TAB_SIZE));
195 	idx = (int)dprcnt;
196 
197 	dtabres = (rndp->rnd_rft[idx].rf_base +
198 	    (rndp->rnd_rft[idx].rf_range * (dprcnt - (double)idx)));
199 
200 	dsclres = (dtabres * (rndp->rnd_dbl_mean - dmin)) + dmin;
201 
202 	if (dround == 0.0)
203 		return (dsclres);
204 	else
205 		return (round(dsclres / dround) * dround);
206 }
207 
208 /*
209  * Set the random seed in the supplied random object.
210  */
211 static void
rand_seed_set(randdist_t * rndp)212 rand_seed_set(randdist_t *rndp)
213 {
214 	union {
215 		uint64_t  ll;
216 		uint16_t  w[4];
217 	} temp1;
218 	int  idx;
219 
220 	temp1.ll = (uint64_t)avd_get_int(rndp->rnd_seed);
221 
222 	for (idx = 0; idx < 3; idx++) {
223 
224 #ifdef _BIG_ENDIAN
225 		rndp->rnd_xi[idx] = temp1.w[3-idx];
226 #else
227 		rndp->rnd_xi[idx] = temp1.w[idx];
228 #endif
229 	}
230 }
231 
232 /*
233  * Define a random entity which will contain the parameters of a random
234  * distribution.
235  */
236 randdist_t *
randdist_alloc(void)237 randdist_alloc(void)
238 {
239 	randdist_t *rndp;
240 
241 	if ((rndp = (randdist_t *)ipc_malloc(FILEBENCH_RANDDIST)) == NULL) {
242 		filebench_log(LOG_ERROR, "Out of memory for random dist");
243 		return (NULL);
244 	}
245 
246 	/* place on global list */
247 	rndp->rnd_next = filebench_shm->shm_rand_list;
248 	filebench_shm->shm_rand_list = rndp;
249 
250 	return (rndp);
251 }
252 
253 /*
254  * Initializes a random distribution entity, converting avd_t parameters to
255  * doubles, and converting the list of probability density function table
256  * entries, if supplied, into a probablilty function table.
257  */
258 void
randdist_init(randdist_t * rndp)259 randdist_init(randdist_t *rndp)
260 {
261 	probtabent_t	*rdte_hdp, *ptep;
262 	double		tablemean, tablemin = 0;
263 	int		pteidx;
264 
265 	/* convert parameters to doubles */
266 	rndp->rnd_dbl_gamma = (double)avd_get_int(rndp->rnd_gamma) / 1000.0;
267 	if (rndp->rnd_mean != NULL)
268 		rndp->rnd_dbl_mean  = (double)avd_get_int(rndp->rnd_mean);
269 	else
270 		rndp->rnd_dbl_mean = rndp->rnd_dbl_gamma;
271 
272 	/* de-reference min and round amounts for later use */
273 	rndp->rnd_vint_min  = avd_get_int(rndp->rnd_min);
274 	rndp->rnd_vint_round  = avd_get_int(rndp->rnd_round);
275 
276 	filebench_log(LOG_DEBUG_IMPL,
277 	    "init random var: Mean = %6.0llf, Gamma = %6.3llf, Min = %llu",
278 	    rndp->rnd_dbl_mean, rndp->rnd_dbl_gamma,
279 	    (u_longlong_t)rndp->rnd_vint_min);
280 
281 	/* initialize distribution to apply */
282 	switch (rndp->rnd_type & RAND_TYPE_MASK) {
283 	case RAND_TYPE_UNIFORM:
284 		rndp->rnd_get = rand_uniform_get;
285 		break;
286 
287 	case RAND_TYPE_GAMMA:
288 		rndp->rnd_get = rand_gamma_get;
289 		break;
290 
291 	case RAND_TYPE_TABLE:
292 		rndp->rnd_get = rand_table_get;
293 		break;
294 
295 	default:
296 		filebench_log(LOG_DEBUG_IMPL, "Random Type not Specified");
297 		filebench_shutdown(1);
298 		return;
299 	}
300 
301 	/* initialize source of random numbers */
302 	if (rndp->rnd_type & RAND_SRC_GENERATOR) {
303 		rndp->rnd_src = fb_rand_src_rand48;
304 		rand_seed_set(rndp);
305 	} else {
306 		rndp->rnd_src = fb_rand_src_random;
307 	}
308 
309 	/* any random distribution table to convert? */
310 	if ((rdte_hdp = rndp->rnd_probtabs) == NULL)
311 		return;
312 
313 	/* determine random distribution max and mins and initialize table */
314 	pteidx = 0;
315 	tablemean = 0.0;
316 	for (ptep = rdte_hdp; ptep; ptep = ptep->pte_next) {
317 		double	dmin, dmax;
318 		int	entcnt;
319 
320 		dmax = (double)avd_get_int(ptep->pte_segmax);
321 		dmin = (double)avd_get_int(ptep->pte_segmin);
322 
323 		/* initialize table minimum on first pass */
324 		if (pteidx == 0)
325 			tablemin = dmin;
326 
327 		/* update table minimum */
328 		if (tablemin > dmin)
329 			tablemin = dmin;
330 
331 		entcnt = (int)avd_get_int(ptep->pte_percent);
332 		tablemean += (((dmin + dmax)/2.0) * (double)entcnt);
333 
334 		/* populate the lookup table */
335 
336 		for (; entcnt > 0; entcnt--) {
337 			rndp->rnd_rft[pteidx].rf_base = dmin;
338 			rndp->rnd_rft[pteidx].rf_range = dmax - dmin;
339 			pteidx++;
340 		}
341 	}
342 
343 	/* check to see if probability equals 100% */
344 	if (pteidx != PF_TAB_SIZE)
345 		filebench_log(LOG_ERROR,
346 		    "Prob table only totals %d%%", pteidx);
347 
348 	/* If table is not supplied with a mean value, set it to table mean */
349 	if (rndp->rnd_dbl_mean == 0.0)
350 		rndp->rnd_dbl_mean = (double)tablemean / (double)PF_TAB_SIZE;
351 
352 	/* now normalize the entries for a min value of 0, mean of 1 */
353 	tablemean = (tablemean / 100.0) - tablemin;
354 
355 	/* special case if really a constant value */
356 	if (tablemean == 0.0) {
357 		for (pteidx = 0; pteidx < PF_TAB_SIZE; pteidx++) {
358 			rndp->rnd_rft[pteidx].rf_base = 0.0;
359 			rndp->rnd_rft[pteidx].rf_range = 0.0;
360 		}
361 		return;
362 	}
363 
364 	for (pteidx = 0; pteidx < PF_TAB_SIZE; pteidx++) {
365 
366 		rndp->rnd_rft[pteidx].rf_base =
367 		    ((rndp->rnd_rft[pteidx].rf_base - tablemin) / tablemean);
368 		rndp->rnd_rft[pteidx].rf_range =
369 		    (rndp->rnd_rft[pteidx].rf_range / tablemean);
370 	}
371 }
372