/* * 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 #include "stress.h" #include #include #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; 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); } } 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); }