Signed-off-by: Jarno Rajahalme <jrajaha...@nicira.com> --- tests/classifier.at | 8 +- tests/test-classifier.c | 301 +++++++++++++++++++++++++++++++++++++---------- 2 files changed, 245 insertions(+), 64 deletions(-)
diff --git a/tests/classifier.at b/tests/classifier.at index cfa1bc7..1e75123 100644 --- a/tests/classifier.at +++ b/tests/classifier.at @@ -6,11 +6,15 @@ m4_foreach( [single-rule], [rule-replacement], [many-rules-in-one-list], + [versioned many-rules-in-one-list], [many-rules-in-one-table], + [versioned many-rules-in-one-table], [many-rules-in-two-tables], - [many-rules-in-five-tables]], + [versioned many-rules-in-two-tables], + [many-rules-in-five-tables], + [versioned many-rules-in-five-tables]], [AT_SETUP([flow classifier - m4_bpatsubst(testname, [-], [ ])]) - AT_CHECK([ovstest test-classifier testname], [0], [], []) + AT_CHECK([ovstest test-classifier m4_bpatsubst(testname, [versioned], [--versioned])], [0], [], []) AT_CLEANUP])]) AT_BANNER([miniflow unit tests]) diff --git a/tests/test-classifier.c b/tests/test-classifier.c index cb65533..9e837c3 100644 --- a/tests/test-classifier.c +++ b/tests/test-classifier.c @@ -42,6 +42,8 @@ #include "unaligned.h" #include "util.h" +static bool versioned = false; + /* Fields in a rule. */ #define CLS_FIELDS \ /* struct flow all-caps */ \ @@ -88,6 +90,7 @@ static const struct cls_field cls_fields[CLS_N_FIELDS] = { }; struct test_rule { + struct ovs_list list_node; int aux; /* Auxiliary data. */ struct cls_rule cls_rule; /* Classifier rule data. */ }; @@ -107,7 +110,8 @@ test_rule_destroy(struct test_rule *rule) } } -static struct test_rule *make_rule(int wc_fields, int priority, int value_pat); +static struct test_rule *make_rule(int wc_fields, int priority, int value_pat, + long long version); static void free_rule(struct test_rule *); static struct test_rule *clone_rule(const struct test_rule *); @@ -398,12 +402,13 @@ get_value(unsigned int *x, unsigned n_values) } static void -compare_classifiers(struct classifier *cls, struct tcls *tcls) +compare_classifiers(struct classifier *cls, size_t n_invisible_rules, + long long version, struct tcls *tcls) { static const int confidence = 500; unsigned int i; - assert(classifier_count(cls) == tcls->n_rules); + assert(classifier_count(cls) == tcls->n_rules + n_invisible_rules); for (i = 0; i < confidence; i++) { const struct cls_rule *cr0, *cr1, *cr2; struct flow flow; @@ -433,7 +438,7 @@ compare_classifiers(struct classifier *cls, struct tcls *tcls) /* This assertion is here to suppress a GCC 4.9 array-bounds warning */ ovs_assert(cls->n_tries <= CLS_MAX_TRIES); - cr0 = classifier_lookup(cls, CLS_MAX_VERSION, &flow, &wc); + cr0 = classifier_lookup(cls, version, &flow, &wc); cr1 = tcls_lookup(tcls, &flow); assert((cr0 == NULL) == (cr1 == NULL)); if (cr0 != NULL) { @@ -442,8 +447,12 @@ compare_classifiers(struct classifier *cls, struct tcls *tcls) assert(cls_rule_equal(cr0, cr1)); assert(tr0->aux == tr1->aux); + + /* Make sure the rule should have been visible. */ + assert(cr0->cls_match); + assert(cls_match_visible_in_version(cr0->cls_match, version)); } - cr2 = classifier_lookup(cls, CLS_MAX_VERSION, &flow, NULL); + cr2 = classifier_lookup(cls, version, &flow, NULL); assert(cr2 == cr0); } } @@ -511,14 +520,17 @@ verify_tries(struct classifier *cls) static void check_tables(const struct classifier *cls, int n_tables, int n_rules, - int n_dups) + int n_dups, int n_invisible, long long version) OVS_NO_THREAD_SAFETY_ANALYSIS { const struct cls_subtable *table; struct test_rule *test_rule; int found_tables = 0; + int found_tables_with_visible_rules = 0; int found_rules = 0; int found_dups = 0; + int found_invisible = 0; + int found_visible_but_removable = 0; int found_rules2 = 0; pvector_verify(&cls->subtables); @@ -527,6 +539,7 @@ check_tables(const struct classifier *cls, int n_tables, int n_rules, int max_priority = INT_MIN; unsigned int max_count = 0; bool found = false; + bool found_visible_rules = false; const struct cls_subtable *iter; /* Locate the subtable from 'subtables'. */ @@ -548,47 +561,105 @@ check_tables(const struct classifier *cls, int n_tables, int n_rules, == (table->ports_mask_len ? cmap_count(&table->rules) : 0)); found_tables++; + CMAP_FOR_EACH (head, cmap_node, &table->rules) { int prev_priority = INT_MAX; - const struct cls_match *rule; + long long prev_version = 0; + const struct cls_match *rule, *prev; + bool found_visible_rules_in_list = false; + + assert(head->priority <= table->max_priority); if (head->priority > max_priority) { max_priority = head->priority; - max_count = 1; - } else if (head->priority == max_priority) { - ++max_count; + max_count = 0; } - found_rules++; - CLS_MATCH_FOR_EACH_AFTER_HEAD (rule, head) { - assert(rule->priority < prev_priority); - assert(rule->priority <= table->max_priority); + FOR_EACH_RULE_IN_LIST_PROTECTED(rule, prev, head) { + long long rule_version; + const struct cls_rule *found_rule; + + /* Priority may not increase. */ + assert(rule->priority <= prev_priority); + + if (rule->priority == max_priority) { + ++max_count; + } + + /* Count invisible rules and visible duplicates. */ + if (!cls_match_visible_in_version(rule, version)) { + found_invisible++; + } else { + if (cls_match_is_eventually_invisible(rule)) { + found_visible_but_removable++; + } + if (found_visible_rules_in_list) { + found_dups++; + } + found_visible_rules_in_list = true; + found_visible_rules = true; + } + + /* Rule must be visible in the version it was inserted. */ + rule_version = rule->cls_rule->version; + assert(cls_match_visible_in_version(rule, rule_version)); + + /* We should always find the latest version of the rule, + * unless all rules have been marked for removal. + * Later versions must always be later in the list. */ + found_rule = classifier_find_rule_exactly(cls, rule->cls_rule); + if (found_rule && found_rule != rule->cls_rule) { + + assert(found_rule->priority == rule->priority); + + /* Found rule may not have a lower version. */ + assert(found_rule->version >= rule_version); + + /* This rule must not be visible in the found rule's + * version. */ + assert(!cls_match_visible_in_version(rule, + found_rule->version)); + } + + if (rule->priority == prev_priority) { + /* Exact duplicate rule may not have a lower version. */ + assert(rule_version >= prev_version); + + /* Previous rule must not be visible in rule's version. */ + assert(!cls_match_visible_in_version(prev, rule_version)); + } prev_priority = rule->priority; + prev_version = rule_version; found_rules++; - found_dups++; - assert(classifier_find_rule_exactly(cls, rule->cls_rule) - == rule->cls_rule); } } + + if (found_visible_rules) { + found_tables_with_visible_rules++; + } + assert(table->max_priority == max_priority); assert(table->max_count == max_count); } assert(found_tables == cmap_count(&cls->subtables_map)); assert(found_tables == pvector_count(&cls->subtables)); - assert(n_tables == -1 || n_tables == cmap_count(&cls->subtables_map)); - assert(n_rules == -1 || found_rules == n_rules); + assert(n_tables == -1 || n_tables == found_tables_with_visible_rules); + assert(n_rules == -1 || found_rules == n_rules + found_invisible); assert(n_dups == -1 || found_dups == n_dups); + assert(found_invisible == n_invisible); CLS_FOR_EACH (test_rule, cls_rule, cls) { found_rules2++; } - assert(found_rules == found_rules2); + /* Iteration does not see removable rules. */ + assert(found_rules + == found_rules2 + found_visible_but_removable + found_invisible); } static struct test_rule * -make_rule(int wc_fields, int priority, int value_pat) +make_rule(int wc_fields, int priority, int value_pat, long long version) { const struct cls_field *f; struct test_rule *rule; @@ -634,8 +705,9 @@ make_rule(int wc_fields, int priority, int value_pat) rule = xzalloc(sizeof *rule); cls_rule_init(&rule->cls_rule, &match, wc_fields - ? (priority == INT_MIN ? priority + 1 : priority) - : INT_MAX, CLS_MIN_VERSION); + ? (priority == INT_MIN ? priority + 1 : + priority == INT_MAX ? priority - 1 : priority) + : 0, version); return rule; } @@ -705,7 +777,7 @@ test_empty(struct ovs_cmdl_context *ctx OVS_UNUSED) tcls_init(&tcls); assert(classifier_is_empty(&cls)); assert(tcls_is_empty(&tcls)); - compare_classifiers(&cls, &tcls); + compare_classifiers(&cls, 0, CLS_MIN_VERSION, &tcls); classifier_destroy(&cls); tcls_destroy(&tcls); } @@ -729,22 +801,22 @@ test_single_rule(struct ovs_cmdl_context *ctx OVS_UNUSED) struct tcls tcls; rule = make_rule(wc_fields, - hash_bytes(&wc_fields, sizeof wc_fields, 0), 0); - + hash_bytes(&wc_fields, sizeof wc_fields, 0), 0, + CLS_MIN_VERSION); classifier_init(&cls, flow_segment_u64s); set_prefix_fields(&cls); tcls_init(&tcls); - tcls_rule = tcls_insert(&tcls, rule); + classifier_insert(&cls, &rule->cls_rule, NULL, 0); - compare_classifiers(&cls, &tcls); - check_tables(&cls, 1, 1, 0); + compare_classifiers(&cls, 0, CLS_MIN_VERSION, &tcls); + check_tables(&cls, 1, 1, 0, 0, CLS_MIN_VERSION); classifier_remove(&cls, &rule->cls_rule); tcls_remove(&tcls, tcls_rule); assert(classifier_is_empty(&cls)); assert(tcls_is_empty(&tcls)); - compare_classifiers(&cls, &tcls); + compare_classifiers(&cls, 0, CLS_MIN_VERSION, &tcls); ovsrcu_postpone(free_rule, rule); classifier_destroy(&cls); @@ -764,8 +836,10 @@ test_rule_replacement(struct ovs_cmdl_context *ctx OVS_UNUSED) struct test_rule *rule2; struct tcls tcls; - rule1 = make_rule(wc_fields, OFP_DEFAULT_PRIORITY, UINT_MAX); - rule2 = make_rule(wc_fields, OFP_DEFAULT_PRIORITY, UINT_MAX); + rule1 = make_rule(wc_fields, OFP_DEFAULT_PRIORITY, UINT_MAX, + CLS_MIN_VERSION); + rule2 = make_rule(wc_fields, OFP_DEFAULT_PRIORITY, UINT_MAX, + CLS_MIN_VERSION); rule2->aux += 5; rule2->aux += 5; @@ -774,8 +848,8 @@ test_rule_replacement(struct ovs_cmdl_context *ctx OVS_UNUSED) tcls_init(&tcls); tcls_insert(&tcls, rule1); classifier_insert(&cls, &rule1->cls_rule, NULL, 0); - compare_classifiers(&cls, &tcls); - check_tables(&cls, 1, 1, 0); + compare_classifiers(&cls, 0, CLS_MIN_VERSION, &tcls); + check_tables(&cls, 1, 1, 0, 0, CLS_MIN_VERSION); tcls_destroy(&tcls); tcls_init(&tcls); @@ -785,8 +859,8 @@ test_rule_replacement(struct ovs_cmdl_context *ctx OVS_UNUSED) classifier_replace(&cls, &rule2->cls_rule, NULL, 0)) == rule1); ovsrcu_postpone(free_rule, rule1); - compare_classifiers(&cls, &tcls); - check_tables(&cls, 1, 1, 0); + compare_classifiers(&cls, 0, CLS_MIN_VERSION, &tcls); + check_tables(&cls, 1, 1, 0, 0, CLS_MIN_VERSION); classifier_defer(&cls); classifier_remove(&cls, &rule2->cls_rule); @@ -876,11 +950,13 @@ test_many_rules_in_one_list (struct ovs_cmdl_context *ctx OVS_UNUSED) int pri_rules[N_RULES]; struct classifier cls; struct tcls tcls; + long long version = CLS_MIN_VERSION; + size_t n_invisible_rules = 0; n_permutations++; for (i = 0; i < N_RULES; i++) { - rules[i] = make_rule(456, pris[i], 0); + rules[i] = make_rule(456, pris[i], 0, version); tcls_rules[i] = NULL; pri_rules[i] = -1; } @@ -890,16 +966,36 @@ test_many_rules_in_one_list (struct ovs_cmdl_context *ctx OVS_UNUSED) tcls_init(&tcls); for (i = 0; i < ARRAY_SIZE(ops); i++) { + struct test_rule *displaced_rule = NULL; + struct cls_rule *removable_rule = NULL; int j = ops[i]; int m, n; if (!tcls_rules[j]) { - struct test_rule *displaced_rule; - tcls_rules[j] = tcls_insert(&tcls, rules[j]); - displaced_rule = test_rule_from_cls_rule( - classifier_replace(&cls, &rules[j]->cls_rule, - NULL, 0)); + if (versioned) { + /* Insert the new rule in the next version. */ + *CONST_CAST(long long *, &rules[j]->cls_rule.version) + = ++version; + + displaced_rule = test_rule_from_cls_rule( + classifier_find_rule_exactly(&cls, + &rules[j]->cls_rule)); + if (displaced_rule) { + /* Mark the old rule for removal after the current + * version. */ + cls_rule_make_invisible_in_version( + &displaced_rule->cls_rule, version, + version - 1); + n_invisible_rules++; + removable_rule = &displaced_rule->cls_rule; + } + classifier_insert(&cls, &rules[j]->cls_rule, NULL, 0); + } else { + displaced_rule = test_rule_from_cls_rule( + classifier_replace(&cls, &rules[j]->cls_rule, + NULL, 0)); + } if (pri_rules[pris[j]] >= 0) { int k = pri_rules[pris[j]]; assert(displaced_rule != NULL); @@ -911,18 +1007,37 @@ test_many_rules_in_one_list (struct ovs_cmdl_context *ctx OVS_UNUSED) } pri_rules[pris[j]] = j; } else { - classifier_remove(&cls, &rules[j]->cls_rule); + if (versioned) { + /* Mark the rule for removal after the current + * version. */ + cls_rule_make_invisible_in_version( + &rules[j]->cls_rule, version + 1, version); + ++version; + n_invisible_rules++; + removable_rule = &rules[j]->cls_rule; + } else { + classifier_remove(&cls, &rules[j]->cls_rule); + } tcls_remove(&tcls, tcls_rules[j]); tcls_rules[j] = NULL; pri_rules[pris[j]] = -1; } - compare_classifiers(&cls, &tcls); - + compare_classifiers(&cls, n_invisible_rules, version, &tcls); n = 0; for (m = 0; m < N_RULES; m++) { n += tcls_rules[m] != NULL; } - check_tables(&cls, n > 0, n, n - 1); + check_tables(&cls, n > 0, n, n - 1, n_invisible_rules, + version); + + if (versioned && removable_rule) { + /* Removable rule is no longer visible. */ + assert(removable_rule->cls_match); + assert(!cls_match_visible_in_version( + removable_rule->cls_match, version)); + classifier_remove(&cls, removable_rule); + n_invisible_rules--; + } } classifier_defer(&cls); @@ -978,6 +1093,8 @@ test_many_rules_in_one_table(struct ovs_cmdl_context *ctx OVS_UNUSED) struct test_rule *tcls_rules[N_RULES]; struct classifier cls; struct tcls tcls; + long long version = CLS_MIN_VERSION; + size_t n_invisible_rules = 0; int value_pats[N_RULES]; int value_mask; int wcf; @@ -999,22 +1116,44 @@ test_many_rules_in_one_table(struct ovs_cmdl_context *ctx OVS_UNUSED) value_pats[i] = random_uint32() & value_mask; } while (array_contains(value_pats, i, value_pats[i])); - rules[i] = make_rule(wcf, priority, value_pats[i]); + ++version; + rules[i] = make_rule(wcf, priority, value_pats[i], version); tcls_rules[i] = tcls_insert(&tcls, rules[i]); classifier_insert(&cls, &rules[i]->cls_rule, NULL, 0); - compare_classifiers(&cls, &tcls); + compare_classifiers(&cls, n_invisible_rules, version, &tcls); - check_tables(&cls, 1, i + 1, 0); + check_tables(&cls, 1, i + 1, 0, n_invisible_rules, version); } for (i = 0; i < N_RULES; i++) { tcls_remove(&tcls, tcls_rules[i]); - classifier_remove(&cls, &rules[i]->cls_rule); - compare_classifiers(&cls, &tcls); - ovsrcu_postpone(free_rule, rules[i]); + if (versioned) { + /* Mark the rule for removal after the current version. */ + cls_rule_make_invisible_in_version(&rules[i]->cls_rule, + version + 1, version); + ++version; + n_invisible_rules++; + } else { + classifier_remove(&cls, &rules[i]->cls_rule); + } + compare_classifiers(&cls, n_invisible_rules, version, &tcls); + check_tables(&cls, i < N_RULES - 1, N_RULES - (i + 1), 0, + n_invisible_rules, version); + if (!versioned) { + ovsrcu_postpone(free_rule, rules[i]); + } + } - check_tables(&cls, i < N_RULES - 1, N_RULES - (i + 1), 0); + if (versioned) { + for (i = 0; i < N_RULES; i++) { + classifier_remove(&cls, &rules[i]->cls_rule); + n_invisible_rules--; + + compare_classifiers(&cls, n_invisible_rules, version, &tcls); + check_tables(&cls, 0, 0, 0, n_invisible_rules, version); + ovsrcu_postpone(free_rule, rules[i]); + } } classifier_destroy(&cls); @@ -1043,6 +1182,9 @@ test_many_rules_in_n_tables(int n_tables) int priorities[MAX_RULES]; struct classifier cls; struct tcls tcls; + long long version = CLS_MIN_VERSION; + size_t n_invisible_rules = 0; + struct ovs_list list = OVS_LIST_INITIALIZER(&list); random_set_seed(iteration + 1); for (i = 0; i < MAX_RULES; i++) { @@ -1059,29 +1201,57 @@ test_many_rules_in_n_tables(int n_tables) int priority = priorities[i]; int wcf = wcfs[random_range(n_tables)]; int value_pat = random_uint32() & ((1u << CLS_N_FIELDS) - 1); - rule = make_rule(wcf, priority, value_pat); + rule = make_rule(wcf, priority, value_pat, version); tcls_insert(&tcls, rule); classifier_insert(&cls, &rule->cls_rule, NULL, 0); - compare_classifiers(&cls, &tcls); - check_tables(&cls, -1, i + 1, -1); + compare_classifiers(&cls, n_invisible_rules, version, &tcls); + check_tables(&cls, -1, i + 1, -1, n_invisible_rules, version); } - while (!classifier_is_empty(&cls)) { + while (classifier_count(&cls) - n_invisible_rules > 0) { struct test_rule *target; struct test_rule *rule; + size_t n_removable_rules = 0; target = clone_rule(tcls.rules[random_range(tcls.n_rules)]); CLS_FOR_EACH_TARGET (rule, cls_rule, &cls, &target->cls_rule) { - if (classifier_remove(&cls, &rule->cls_rule)) { + if (versioned) { + /* Mark the rule for removal after the current version. */ + cls_rule_make_invisible_in_version(&rule->cls_rule, + version + 1, version); + n_removable_rules++; + compare_classifiers(&cls, n_invisible_rules, version, + &tcls); + check_tables(&cls, -1, -1, -1, n_invisible_rules, version); + + list_push_back(&list, &rule->list_node); + } else if (classifier_remove(&cls, &rule->cls_rule)) { ovsrcu_postpone(free_rule, rule); } } + ++version; + n_invisible_rules += n_removable_rules; + tcls_delete_matches(&tcls, &target->cls_rule); - compare_classifiers(&cls, &tcls); - check_tables(&cls, -1, -1, -1); free_rule(target); + + compare_classifiers(&cls, n_invisible_rules, version, &tcls); + check_tables(&cls, -1, -1, -1, n_invisible_rules, version); + } + if (versioned) { + struct test_rule *rule; + + /* Remove rules that are no longer visible. */ + LIST_FOR_EACH_POP (rule, list_node, &list) { + classifier_remove(&cls, &rule->cls_rule); + n_invisible_rules--; + + compare_classifiers(&cls, n_invisible_rules, version, + &tcls); + check_tables(&cls, -1, -1, -1, n_invisible_rules, version); + } } destroy_classifier(&cls); @@ -1406,8 +1576,8 @@ static const struct ovs_cmdl_command commands[] = { {"destroy-null", NULL, 0, 0, test_destroy_null}, {"single-rule", NULL, 0, 0, test_single_rule}, {"rule-replacement", NULL, 0, 0, test_rule_replacement}, - {"many-rules-in-one-list", NULL, 0, 0, test_many_rules_in_one_list}, - {"many-rules-in-one-table", NULL, 0, 0, test_many_rules_in_one_table}, + {"many-rules-in-one-list", NULL, 0, 1, test_many_rules_in_one_list}, + {"many-rules-in-one-table", NULL, 0, 1, test_many_rules_in_one_table}, {"many-rules-in-two-tables", NULL, 0, 0, test_many_rules_in_two_tables}, {"many-rules-in-five-tables", NULL, 0, 0, test_many_rules_in_five_tables}, @@ -1427,6 +1597,13 @@ test_classifier_main(int argc, char *argv[]) .argv = argv + 1, }; set_program_name(argv[0]); + + if (argc > 1 && !strcmp(argv[1], "--versioned")) { + versioned = true; + ctx.argc--; + ctx.argv++; + } + init_values(); ovs_cmdl_run_command(&ctx, commands); } -- 1.7.10.4 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev