Implement stress option framework.
[sliver-openvswitch.git] / lib / stress.c
diff --git a/lib/stress.c b/lib/stress.c
new file mode 100644 (file)
index 0000000..53f6fc0
--- /dev/null
@@ -0,0 +1,222 @@
+/*
+ * Copyright (c) 2010 Nicira Networks.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <config.h>
+#include "stress.h"
+#include <stdlib.h>
+#include <string.h>
+#include "unixctl.h"
+#include "dynamic-string.h"
+#include "random.h"
+#include "util.h"
+#include "vlog.h"
+
+VLOG_DEFINE_THIS_MODULE(stress);
+
+/* The stress options. */
+#if USE_LINKER_SECTIONS
+extern struct stress_option *__start_stress_options[];
+extern struct stress_option *__stop_stress_options[];
+#define stress_options __start_stress_options
+#define n_stress_options (__stop_stress_options - __start_stress_options)
+#else  /* !USE_LINKER_SECTIONS */
+#undef STRESS_OPTION
+#define STRESS_OPTION(NAME, DESCRIPTION, RECOMMENDED, MIN, MAX, DEFAULT) \
+        STRESS_OPTION__(NAME, DESCRIPTION, RECOMMENDED, MIN, MAX, DEFAULT);
+#include "stress.def"
+#undef STRESS_OPTION
+
+struct stress_option *stress_options[] = {
+#define STRESS_OPTION(NAME, DESCRIPTION, RECOMMENDED, MIN, MAX, DEFAULT) \
+        &stress_##NAME,
+#include "stress.def"
+#undef STRESS_OPTION
+};
+#define n_stress_options ARRAY_SIZE(stress_options)
+#endif  /* !USE_LINKER_SECTIONS */
+
+/* Enable stress options? */
+static bool stress_enabled;
+\f
+static void
+stress_reset(struct stress_option *option)
+{
+    if (!option->period || !stress_enabled) {
+        option->counter = UINT_MAX;
+    } else if (!option->random) {
+        option->counter = option->period;
+    } else if (option->period < UINT32_MAX / 2) {
+        /* Random distribution with mean of option->period. */
+        option->counter = random_uint32() % ((2 * option->period) - 1) + 1;
+    } else {
+        option->counter = random_uint32();
+    }
+}
+
+static void
+stress_enable(bool enable)
+{
+    if (stress_enabled != enable) {
+        int i;
+
+        stress_enabled = enable;
+        for (i = 0; i < n_stress_options; i++) {
+            stress_reset(stress_options[i]);
+        }
+    }
+}
+
+bool
+stress_sample_slowpath__(struct stress_option *option)
+{
+    stress_reset(option);
+    if (option->period && stress_enabled) {
+        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
+
+        option->hits++;
+        VLOG_DBG_RL(&rl, "%s hit (%llu total)", option->name, option->hits);
+
+        return true;
+    } else {
+        return false;
+    }
+}
+
+static void
+stress_set(struct stress_option *option, unsigned int period, bool random)
+{
+    if (period > option->max) {
+        period = option->max;
+    }
+    if (period < option->min) {
+        period = option->min;
+    }
+    if (period != option->period || random != option->random) {
+        option->random = random;
+        option->period = period;
+        stress_reset(option);
+    }
+}
+\f
+static void
+stress_unixctl_list(struct unixctl_conn *conn, const char *args,
+                    void *aux OVS_UNUSED)
+{
+    int i, found = 0;
+    struct ds results;
+
+    ds_init(&results);
+    ds_put_cstr(&results, "NAME (DESCRIPTION)\n");
+    ds_put_format(&results, "%11s %10s %10s %10s\n",
+                  "PERIOD", "MODE", "COUNTER", "HITS");
+    ds_put_format(&results, "%11s %10s %10s %10s\n",
+                  "RECOMMENDED", "MINIMUM", "MAXIMUM", "DEFAULT");
+    for (i = 0; i < n_stress_options; i++) {
+        struct stress_option *option = stress_options[i];
+        if (!*args || strstr(option->name, args)) {
+            ds_put_format(&results, "\n%s (%s)\n",
+                          option->name, option->description);
+            if (option->period) {
+                ds_put_format(&results, "%11u %10s ", option->period,
+                              option->random ? "random" : "periodic");
+                if (stress_enabled) {
+                    ds_put_format(&results, "%10u", option->counter);
+                } else {
+                    ds_put_cstr(&results, "     n/a");
+                }
+            } else {
+                ds_put_format(&results, "%11s %10s %10s",
+                              "disabled", "n/a", "n/a");
+            }
+            ds_put_format(&results, " %10llu\n", option->hits);
+            ds_put_format(&results, "%11u %10u %10u ",
+                          option->recommended, option->min, option->max);
+            if (!option->def) {
+                ds_put_format(&results, "%10s", "disabled");
+            } else {
+                ds_put_format(&results, "%10u", option->def);
+            }
+            ds_put_char(&results, '\n');
+            found++;
+        }
+    }
+    if (found) {
+        unixctl_command_reply(conn, 200, ds_cstr(&results));
+    } else {
+        unixctl_command_reply(conn, 404, "");
+    }
+    ds_destroy(&results);
+}
+
+static void
+stress_unixctl_enable(struct unixctl_conn *conn, const char *args OVS_UNUSED,
+                      void *aux OVS_UNUSED)
+{
+    stress_enable(true);
+    unixctl_command_reply(conn, 200, "");
+}
+
+static void
+stress_unixctl_disable(struct unixctl_conn *conn, const char *args OVS_UNUSED,
+                       void *aux OVS_UNUSED)
+{
+    stress_enable(false);
+    unixctl_command_reply(conn, 200, "");
+}
+
+static void
+stress_unixctl_set(struct unixctl_conn *conn, const char *args_,
+                   void *aux OVS_UNUSED)
+{
+    int code = 404;
+    char *args = xstrdup(args_);
+    char *save_ptr = NULL;
+    char *option_name;
+    char *option_val;
+
+    option_name = strtok_r(args, " ", &save_ptr);
+    option_val = strtok_r(NULL, " ", &save_ptr);
+    if (option_val) {
+        int i;
+        for (i = 0; i < n_stress_options; i++) {
+            struct stress_option *option = stress_options[i];
+            if (!strcmp(option_name, option->name)) {
+                unsigned int period = strtoul(option_val, NULL, 0);
+                bool random = strstr(args_, "random");
+
+                stress_set(option, period, random);
+                code = 200;
+                break;
+            }
+        }
+    }
+    unixctl_command_reply(conn, code, "");
+    free(args);
+}
+
+/* Exposes ovs-appctl access to the stress options.
+ *
+ * This function is not required to simply reference stress options and have
+ * them fire at their default periods.
+ */
+void
+stress_init_command(void)
+{
+    unixctl_command_register("stress/list", stress_unixctl_list, NULL);
+    unixctl_command_register("stress/set", stress_unixctl_set, NULL);
+    unixctl_command_register("stress/enable", stress_unixctl_enable, NULL);
+    unixctl_command_register("stress/disable", stress_unixctl_disable, NULL);
+}