@@ -17,6 +17,11 @@ m4_foreach(
AT_CHECK([ovstest test-classifier m4_bpatsubst(testname, [versioned], [--versioned])], [0], [], [])
AT_CLEANUP])])
+AT_BANNER([flow classifier stress tests])
+AT_SETUP([flow classifier - prefixes reconfiguration stress test])
+AT_CHECK([ovstest test-classifier stress-prefixes], [0], [stdout])
+AT_CLEANUP
+
AT_BANNER([miniflow unit tests])
m4_foreach(
[testname],
@@ -37,6 +37,7 @@
#include "command-line.h"
#include "fatal-signal.h"
#include "flow.h"
+#include "openvswitch/vlog.h"
#include "ovstest.h"
#include "ovs-atomic.h"
#include "ovs-thread.h"
@@ -757,11 +758,22 @@ shuffle_u32s(uint32_t *p, size_t n)
*q = tmp;
}
}
+
+static void
+shuffle_fields(enum mf_field_id *p, size_t n)
+{
+ for (; n > 1; n--, p++) {
+ enum mf_field_id *q = &p[random_range(n)];
+ enum mf_field_id tmp = *p;
+ *p = *q;
+ *q = tmp;
+ }
+}
/* Classifier tests. */
-static enum mf_field_id trie_fields[2] = {
- MFF_IPV4_DST, MFF_IPV4_SRC
+static enum mf_field_id trie_fields[4] = {
+ MFF_IPV4_DST, MFF_IPV4_SRC, MFF_IPV6_DST, MFF_IPV6_SRC,
};
static void
@@ -1286,7 +1298,7 @@ static int n_tables; /* Number of subtables. */
static int n_threads; /* Number of threads to search and mutate. */
static int n_lookups; /* Number of lookups each thread performs. */
-static void benchmark(bool use_wc);
+static void benchmark(bool use_wc, bool stress_prefixes);
static int
elapsed(const struct timeval *start)
@@ -1337,9 +1349,29 @@ run_benchmarks(struct ovs_cmdl_context *ctx)
n_rules, n_priorities, n_tables, n_threads, n_lookups);
puts("\nWithout wildcards: \n");
- benchmark(false);
+ benchmark(false, false);
puts("\nWith wildcards: \n");
- benchmark(true);
+ benchmark(true, false);
+}
+
+static void
+run_prefix_stress(struct ovs_cmdl_context *ctx OVS_UNUSED)
+{
+ vlog_set_levels(NULL, VLF_ANY_DESTINATION, VLL_OFF);
+ vlog_set_levels(NULL, VLF_CONSOLE, VLL_WARN);
+
+ n_rules = 10000;
+ n_priorities = 2;
+ n_tables = 30;
+ n_threads = 2;
+ n_lookups = 2000000;
+
+ printf("\nStress testing prefixes with:\n"
+ "%d rules with %d priorities in %d tables, "
+ "%d threads doing %d lookups each\n",
+ n_rules, n_priorities, n_tables, n_threads, n_lookups);
+
+ benchmark(true, true);
}
struct cls_aux {
@@ -1347,6 +1379,7 @@ struct cls_aux {
size_t n_lookup_flows;
struct flow *lookup_flows;
bool use_wc;
+ bool quiesce;
atomic_int hits;
atomic_int misses;
};
@@ -1382,15 +1415,47 @@ lookup_classifier(void *aux_)
} else {
misses++;
}
+ if (aux->quiesce) {
+ ovsrcu_quiesce();
+ }
}
atomic_add(&aux->hits, hits, &old_hits);
atomic_add(&aux->misses, misses, &old_misses);
return NULL;
}
+struct prefix_aux {
+ struct classifier *cls;
+ atomic_bool running;
+ size_t n_updates;
+};
+
+static void *
+update_prefixes(void *aux_)
+{
+ struct prefix_aux *aux = aux_;
+ size_t n, n_updates = 0;
+ bool running = true;
+
+ random_set_seed(1);
+
+ while (running) {
+ n_updates++;
+
+ shuffle_fields(trie_fields, ARRAY_SIZE(trie_fields));
+ n = random_range(ARRAY_SIZE(trie_fields) + 1);
+ classifier_set_prefix_fields(aux->cls, trie_fields, n);
+ verify_tries(aux->cls);
+
+ atomic_read_relaxed(&aux->running, &running);
+ }
+ aux->n_updates = n_updates;
+ return NULL;
+}
+
/* Benchmark classification. */
static void
-benchmark(bool use_wc)
+benchmark(bool use_wc, bool stress_prefixes)
{
struct classifier cls;
ovs_version_t version = OVS_VERSION_MIN;
@@ -1421,6 +1486,7 @@ benchmark(bool use_wc)
/* Create lookup flows. */
aux.use_wc = use_wc;
+ aux.quiesce = stress_prefixes;
aux.cls = &cls;
aux.n_lookup_flows = 2 * N_FLOW_VALUES;
aux.lookup_flows = xzalloc(aux.n_lookup_flows * sizeof *aux.lookup_flows);
@@ -1465,6 +1531,18 @@ benchmark(bool use_wc)
}
}
+ pthread_t prefix_thread;
+ struct prefix_aux paux;
+
+ if (stress_prefixes) {
+ paux.cls = &cls;
+ paux.n_updates = 0;
+ atomic_init(&paux.running, true);
+
+ prefix_thread = ovs_thread_create("prefixes", update_prefixes, &paux);
+ ovsrcu_quiesce_start();
+ }
+
/* Lookup. */
xgettimeofday(&start);
threads = xmalloc(n_threads * sizeof *threads);
@@ -1479,6 +1557,13 @@ benchmark(bool use_wc)
free(threads);
+ if (stress_prefixes) {
+ atomic_store_relaxed(&paux.running, false);
+ xpthread_join(prefix_thread, NULL);
+ printf("Prefixes updated %"PRIuSIZE" times.\n", paux.n_updates);
+ ovsrcu_quiesce_end();
+ }
+
int hits, misses;
atomic_read(&aux.hits, &hits);
atomic_read(&aux.misses, &misses);
@@ -1852,6 +1937,7 @@ static const struct ovs_cmdl_command commands[] = {
{"many-rules-in-two-tables", NULL, 0, 0, test_many_rules_in_two_tables, OVS_RO },
{"many-rules-in-five-tables", NULL, 0, 0, test_many_rules_in_five_tables, OVS_RO },
{"benchmark", NULL, 0, 5, run_benchmarks, OVS_RO },
+ {"stress-prefixes", NULL, 0, 0, run_prefix_stress, OVS_RO },
/* Miniflow and minimask tests. */
{"miniflow", NULL, 0, 0, test_miniflow, OVS_RO },
This test is reusing the benchmark infrastructure, but it has some pre-defined parameters, so it's easier to run in the test suite. The benchmark code is adjusted to start another thread that does prefix updates continuously in a loop and the lookup threads are updated to be able to enter quiescent state periodically, so the reconfiguration can proceed. This test is a reproducer for the crashes fixed in the previous commit. Signed-off-by: Ilya Maximets <i.maximets@ovn.org> --- tests/classifier.at | 5 +++ tests/test-classifier.c | 98 ++++++++++++++++++++++++++++++++++++++--- 2 files changed, 97 insertions(+), 6 deletions(-)