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
  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
  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
 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 *
 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
 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
 196 eventgen_reset(void)
 197 {
 198         filebench_shm->shm_eventgen_q = 0;
 199 }