AUTHORS: Add Kyle Mestery.
[sliver-openvswitch.git] / lib / stress.c
1 /*
2  * Copyright (c) 2010, 2011 Nicira, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at:
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include <config.h>
18 #include "stress.h"
19 #include <stdlib.h>
20 #include <string.h>
21 #include "unixctl.h"
22 #include "dynamic-string.h"
23 #include "random.h"
24 #include "util.h"
25 #include "vlog.h"
26
27 VLOG_DEFINE_THIS_MODULE(stress);
28
29 /* The stress options. */
30 #if USE_LINKER_SECTIONS
31 extern struct stress_option *__start_stress_options[];
32 extern struct stress_option *__stop_stress_options[];
33 #define stress_options __start_stress_options
34 #define n_stress_options (__stop_stress_options - __start_stress_options)
35 #else  /* !USE_LINKER_SECTIONS */
36 #undef STRESS_OPTION
37 #define STRESS_OPTION(NAME, DESCRIPTION, RECOMMENDED, MIN, MAX, DEFAULT) \
38         STRESS_OPTION__(NAME, DESCRIPTION, RECOMMENDED, MIN, MAX, DEFAULT);
39 #include "stress.def"
40 #undef STRESS_OPTION
41
42 struct stress_option *stress_options[] = {
43 #define STRESS_OPTION(NAME, DESCRIPTION, RECOMMENDED, MIN, MAX, DEFAULT) \
44         &stress_##NAME,
45 #include "stress.def"
46 #undef STRESS_OPTION
47 };
48 #define n_stress_options ARRAY_SIZE(stress_options)
49 #endif  /* !USE_LINKER_SECTIONS */
50
51 /* Enable stress options? */
52 static bool stress_enabled;
53 \f
54 static void
55 stress_reset(struct stress_option *option)
56 {
57     if (!option->period || !stress_enabled) {
58         option->counter = UINT_MAX;
59     } else if (!option->random) {
60         option->counter = option->period;
61     } else if (option->period < UINT32_MAX / 2) {
62         /* Random distribution with mean of option->period. */
63         option->counter = random_uint32() % ((2 * option->period) - 1) + 1;
64     } else {
65         option->counter = random_uint32();
66     }
67 }
68
69 static void
70 stress_enable(bool enable)
71 {
72     if (stress_enabled != enable) {
73         int i;
74
75         stress_enabled = enable;
76         for (i = 0; i < n_stress_options; i++) {
77             stress_reset(stress_options[i]);
78         }
79     }
80 }
81
82 bool
83 stress_sample_slowpath__(struct stress_option *option)
84 {
85     stress_reset(option);
86     if (option->period && stress_enabled) {
87         static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
88
89         option->hits++;
90         VLOG_DBG_RL(&rl, "%s hit (%llu total)", option->name, option->hits);
91
92         return true;
93     } else {
94         return false;
95     }
96 }
97
98 static void
99 stress_set(struct stress_option *option, unsigned int period, bool random)
100 {
101     if (period > option->max) {
102         period = option->max;
103     }
104     if (period < option->min) {
105         period = option->min;
106     }
107     if (period != option->period || random != option->random) {
108         option->random = random;
109         option->period = period;
110         stress_reset(option);
111     }
112 }
113 \f
114 static void
115 stress_unixctl_list(struct unixctl_conn *conn, int argc OVS_UNUSED,
116                     const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED)
117 {
118     int i, found = 0;
119     struct ds results;
120
121     ds_init(&results);
122     ds_put_cstr(&results, "NAME (DESCRIPTION)\n");
123     ds_put_format(&results, "%11s %10s %10s %10s\n",
124                   "PERIOD", "MODE", "COUNTER", "HITS");
125     ds_put_format(&results, "%11s %10s %10s %10s\n",
126                   "RECOMMENDED", "MINIMUM", "MAXIMUM", "DEFAULT");
127     for (i = 0; i < n_stress_options; i++) {
128         struct stress_option *option = stress_options[i];
129         if (!argv[1] || strstr(option->name, argv[1])) {
130             ds_put_format(&results, "\n%s (%s)\n",
131                           option->name, option->description);
132             if (option->period) {
133                 ds_put_format(&results, "%11u %10s ", option->period,
134                               option->random ? "random" : "periodic");
135                 if (stress_enabled) {
136                     ds_put_format(&results, "%10u", option->counter);
137                 } else {
138                     ds_put_cstr(&results, "     n/a");
139                 }
140             } else {
141                 ds_put_format(&results, "%11s %10s %10s",
142                               "disabled", "n/a", "n/a");
143             }
144             ds_put_format(&results, " %10llu\n", option->hits);
145             ds_put_format(&results, "%11u %10u %10u ",
146                           option->recommended, option->min, option->max);
147             if (!option->def) {
148                 ds_put_format(&results, "%10s", "disabled");
149             } else {
150                 ds_put_format(&results, "%10u", option->def);
151             }
152             ds_put_char(&results, '\n');
153             found++;
154         }
155     }
156     if (found) {
157         unixctl_command_reply(conn, ds_cstr(&results));
158     } else {
159         unixctl_command_reply_error(conn, NULL);
160     }
161     ds_destroy(&results);
162 }
163
164 static void
165 stress_unixctl_enable(struct unixctl_conn *conn, int argc OVS_UNUSED,
166                       const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED)
167 {
168     stress_enable(true);
169     unixctl_command_reply(conn, NULL);
170 }
171
172 static void
173 stress_unixctl_disable(struct unixctl_conn *conn, int argc OVS_UNUSED,
174                        const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED)
175 {
176     stress_enable(false);
177     unixctl_command_reply(conn, NULL);
178 }
179
180 static void
181 stress_unixctl_set(struct unixctl_conn *conn, int argc OVS_UNUSED,
182                    const char *argv[], void *aux OVS_UNUSED)
183 {
184     const char *option_name = argv[1];
185     const char *option_val = argv[2];
186     int i;
187
188     for (i = 0; i < n_stress_options; i++) {
189         struct stress_option *option = stress_options[i];
190         if (!strcmp(option_name, option->name)) {
191             unsigned int period = strtoul(option_val, NULL, 0);
192             bool random = !strcmp(argv[3], "random");
193
194             stress_set(option, period, random);
195             unixctl_command_reply(conn, NULL);
196             return;
197         }
198     }
199
200     unixctl_command_reply_error(conn, NULL);
201 }
202
203 /* Exposes ovs-appctl access to the stress options.
204  *
205  * This function is not required to simply reference stress options and have
206  * them fire at their default periods.
207  */
208 void
209 stress_init_command(void)
210 {
211     unixctl_command_register("stress/list", "", 0, 1,
212                              stress_unixctl_list, NULL);
213     unixctl_command_register("stress/set", "option period [random | periodic]",
214                              2, 3, stress_unixctl_set, NULL);
215     unixctl_command_register("stress/enable", "", 0, 0,
216                              stress_unixctl_enable, NULL);
217     unixctl_command_register("stress/disable", "", 0, 0,
218                              stress_unixctl_disable, NULL);
219 }