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 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 *
25 * Portions Copyright 2008 Denis Cheng
26 */
27
28 /*
29 * The event generator in this module is the producer half of a
30 * metering system which blocks flows using consumer routines in the
31 * flowop_library.c module. Four routines in that module can limit rates
32 * by event rate (flowoplib_eventlimit), by I/O operations rate
33 * (flowoplib_iopslimit()), by operations rate (flowoplib_opslimit),
34 * or by I/O bandwidth limit (flowoplib_bwlimit). By setting appropriate
35 * event generation rates, required calls per second, I/O ops per second,
36 * file system ops per second, or I/O bandwidth per second limits can
37 * be set. Note, the generated events are shared with all consumer
38 * flowops, of which their will be one for each process / thread
39 * instance which has a consumer flowop defined in it.
40 */
41
42 #include <sys/time.h>
43
44 #include "filebench.h"
45 #include "vars.h"
46 #include "eventgen.h"
47 #include "flowop.h"
48 #include "ipc.h"
49
50 /*
51 * Prints "how to use" information for the eventgen module
52 */
53 void
eventgen_usage(void)54 eventgen_usage(void)
55 {
56 (void) fprintf(stderr, "eventgen rate=<rate>\n");
57 (void) fprintf(stderr, "\n");
58 }
59
60 /*
61 * The producer side of the event system.
62 * Once eventgen_hz has been set by eventgen_setrate(),
63 * the routine sends eventgen_hz events per second until
64 * the program terminates. Events are posted by incrementing
65 * filebench_shm->shm_eventgen_q by the number of generated
66 * events then signalling the condition variable
67 * filebench_shm->shm_eventgen_cv to indicate to event consumers
68 * that more events are available.
69 *
70 * Eventgen_thread attempts to sleep for 10 event periods,
71 * then, once awakened, determines how many periods actually
72 * passed since sleeping, and issues a set of events equal
73 * to the number of periods that it slept, thus keeping the
74 * average rate at the requested rate.
75 */
76 static void
eventgen_thread(void)77 eventgen_thread(void)
78 {
79 hrtime_t last;
80
81 last = gethrtime();
82 filebench_shm->shm_eventgen_enabled = FALSE;
83
84 /* CONSTCOND */
85 while (1) {
86 struct timespec sleeptime;
87 hrtime_t delta;
88 int count, rate;
89
90 if (filebench_shm->shm_eventgen_hz == NULL) {
91 (void) sleep(1);
92 continue;
93 } else {
94 rate = avd_get_int(filebench_shm->shm_eventgen_hz);
95 if (rate > 0) {
96 filebench_shm->shm_eventgen_enabled = TRUE;
97 } else {
98 continue;
99 }
100 }
101
102 /* Sleep for 10xperiod */
103 sleeptime.tv_sec = 0;
104 sleeptime.tv_nsec = FB_SEC2NSEC / rate;
105
106 sleeptime.tv_nsec *= 10;
107 if (sleeptime.tv_nsec < 1000UL)
108 sleeptime.tv_nsec = 1000UL;
109
110 sleeptime.tv_sec = sleeptime.tv_nsec / FB_SEC2NSEC;
111 if (sleeptime.tv_sec > 0)
112 sleeptime.tv_nsec -= (sleeptime.tv_sec * FB_SEC2NSEC);
113
114 (void) nanosleep(&sleeptime, NULL);
115 delta = gethrtime() - last;
116 last = gethrtime();
117 count = (rate * delta) / FB_SEC2NSEC;
118
119 filebench_log(LOG_DEBUG_SCRIPT,
120 "delta %llums count %d",
121 (u_longlong_t)(delta / 1000000), count);
122
123 /* Send 'count' events */
124 (void) ipc_mutex_lock(&filebench_shm->shm_eventgen_lock);
125 /* Keep the producer with a max of 5 second depth */
126 if (filebench_shm->shm_eventgen_q < (5 * rate))
127 filebench_shm->shm_eventgen_q += count;
128
129 (void) pthread_cond_signal(&filebench_shm->shm_eventgen_cv);
130
131 (void) ipc_mutex_unlock(&filebench_shm->shm_eventgen_lock);
132 }
133 }
134
135 /*
136 * Creates a thread to run the event generator eventgen_thread
137 * routine. Shuts down filebench if the eventgen thread cannot
138 * be created.
139 */
140 void
eventgen_init(void)141 eventgen_init(void)
142 {
143 /*
144 * Linux does not like it if the first
145 * argument to pthread_create is null. It actually
146 * segv's. -neel
147 */
148 pthread_t tid;
149
150 if (pthread_create(&tid, NULL,
151 (void *(*)(void*))eventgen_thread, 0) != 0) {
152 filebench_log(LOG_ERROR, "create timer thread failed: %s",
153 strerror(errno));
154 filebench_shutdown(1);
155 }
156 }
157
158 /*
159 * Puts the current event rate in the integer portion of the
160 * supplied var_t. Returns a pointer to the var_t.
161 */
162 var_t *
eventgen_ratevar(var_t * var)163 eventgen_ratevar(var_t *var)
164 {
165 VAR_SET_INT(var, avd_get_int(filebench_shm->shm_eventgen_hz));
166 return (var);
167 }
168
169 /*
170 * Sets the event generator rate to that supplied by
171 * var_t *rate.
172 */
173 void
eventgen_setrate(avd_t rate)174 eventgen_setrate(avd_t rate)
175 {
176 filebench_shm->shm_eventgen_hz = rate;
177 if (rate == NULL) {
178 filebench_log(LOG_ERROR,
179 "eventgen_setrate() called without a rate");
180 return;
181 }
182
183 if (AVD_IS_VAR(rate)) {
184 filebench_log(LOG_VERBOSE,
185 "Eventgen rate taken from variable");
186 } else {
187 filebench_log(LOG_VERBOSE, "Eventgen: %llu per second",
188 (u_longlong_t)avd_get_int(rate));
189 }
190 }
191
192 /*
193 * Clears the event queue so we have a clean start
194 */
195 void
eventgen_reset(void)196 eventgen_reset(void)
197 {
198 filebench_shm->shm_eventgen_q = 0;
199 }
200