}
}
-static struct test_rule *make_rule(int wc_fields, int priority, int value_pat,
- long long version);
+static struct test_rule *make_rule(int wc_fields, int priority, int value_pat);
static void free_rule(struct test_rule *);
static struct test_rule *clone_rule(const struct test_rule *);
for (i = 0; i < cls->n_rules; ) {
struct test_rule *pos = cls->rules[i];
- if (!minimask_has_extra(&pos->cls_rule.match.mask,
- &target->match.mask)) {
+ if (!minimask_has_extra(pos->cls_rule.match.mask,
+ target->match.mask)) {
struct flow flow;
- miniflow_expand(&pos->cls_rule.match.flow, &flow);
+ miniflow_expand(pos->cls_rule.match.flow, &flow);
if (match(target, &flow)) {
tcls_remove(cls, pos);
continue;
static void
compare_classifiers(struct classifier *cls, size_t n_invisible_rules,
- long long version, struct tcls *tcls)
+ cls_version_t version, struct tcls *tcls)
{
static const int confidence = 500;
unsigned int i;
static void
check_tables(const struct classifier *cls, int n_tables, int n_rules,
- int n_dups, int n_invisible, long long version)
+ int n_dups, int n_invisible, cls_version_t version)
OVS_NO_THREAD_SAFETY_ANALYSIS
{
const struct cls_subtable *table;
CMAP_FOR_EACH (head, cmap_node, &table->rules) {
int prev_priority = INT_MAX;
- long long prev_version = 0;
+ cls_version_t prev_version = 0;
const struct cls_match *rule, *prev;
bool found_visible_rules_in_list = false;
}
FOR_EACH_RULE_IN_LIST_PROTECTED(rule, prev, head) {
- long long rule_version;
+ cls_version_t rule_version;
const struct cls_rule *found_rule;
/* Priority may not increase. */
}
/* Rule must be visible in the version it was inserted. */
- rule_version = rule->cls_rule->version;
+ rule_version = rule->add_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);
+ found_rule = classifier_find_rule_exactly(cls, rule->cls_rule,
+ rule_version);
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);
+ assert(found_rule->cls_match->add_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));
+ assert(!cls_match_visible_in_version(
+ rule, found_rule->cls_match->add_version));
}
if (rule->priority == prev_priority) {
}
static struct test_rule *
-make_rule(int wc_fields, int priority, int value_pat, long long version)
+make_rule(int wc_fields, int priority, int value_pat)
{
const struct cls_field *f;
struct test_rule *rule;
cls_rule_init(&rule->cls_rule, &match, wc_fields
? (priority == INT_MIN ? priority + 1 :
priority == INT_MAX ? priority - 1 : priority)
- : 0, version);
+ : 0);
return rule;
}
struct tcls tcls;
rule = make_rule(wc_fields,
- hash_bytes(&wc_fields, sizeof wc_fields, 0), 0,
- CLS_MIN_VERSION);
+ hash_bytes(&wc_fields, sizeof wc_fields, 0), 0);
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);
+ classifier_insert(&cls, &rule->cls_rule, CLS_MIN_VERSION, NULL, 0);
compare_classifiers(&cls, 0, CLS_MIN_VERSION, &tcls);
check_tables(&cls, 1, 1, 0, 0, CLS_MIN_VERSION);
struct test_rule *rule2;
struct tcls tcls;
- 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);
+ rule1 = make_rule(wc_fields, OFP_DEFAULT_PRIORITY, UINT_MAX);
+ rule2 = make_rule(wc_fields, OFP_DEFAULT_PRIORITY, UINT_MAX);
rule2->aux += 5;
rule2->aux += 5;
set_prefix_fields(&cls);
tcls_init(&tcls);
tcls_insert(&tcls, rule1);
- classifier_insert(&cls, &rule1->cls_rule, NULL, 0);
+ classifier_insert(&cls, &rule1->cls_rule, CLS_MIN_VERSION, NULL, 0);
compare_classifiers(&cls, 0, CLS_MIN_VERSION, &tcls);
check_tables(&cls, 1, 1, 0, 0, CLS_MIN_VERSION);
tcls_destroy(&tcls);
tcls_insert(&tcls, rule2);
assert(test_rule_from_cls_rule(
- classifier_replace(&cls, &rule2->cls_rule,
+ classifier_replace(&cls, &rule2->cls_rule, CLS_MIN_VERSION,
NULL, 0)) == rule1);
ovsrcu_postpone(free_rule, rule1);
compare_classifiers(&cls, 0, CLS_MIN_VERSION, &tcls);
int pri_rules[N_RULES];
struct classifier cls;
struct tcls tcls;
- long long version = CLS_MIN_VERSION;
+ cls_version_t 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, version);
+ rules[i] = make_rule(456, pris[i], 0);
tcls_rules[i] = NULL;
pri_rules[i] = -1;
}
tcls_rules[j] = tcls_insert(&tcls, rules[j]);
if (versioned) {
/* Insert the new rule in the next version. */
- *CONST_CAST(long long *, &rules[j]->cls_rule.version)
- = ++version;
+ ++version;
displaced_rule = test_rule_from_cls_rule(
classifier_find_rule_exactly(&cls,
- &rules[j]->cls_rule));
+ &rules[j]->cls_rule,
+ version));
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);
+ &displaced_rule->cls_rule, version);
n_invisible_rules++;
removable_rule = &displaced_rule->cls_rule;
}
- classifier_insert(&cls, &rules[j]->cls_rule, NULL, 0);
+ classifier_insert(&cls, &rules[j]->cls_rule, version,
+ NULL, 0);
} else {
displaced_rule = test_rule_from_cls_rule(
classifier_replace(&cls, &rules[j]->cls_rule,
- NULL, 0));
+ version, NULL, 0));
}
if (pri_rules[pris[j]] >= 0) {
int k = pri_rules[pris[j]];
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;
+ cls_rule_make_invisible_in_version(
+ &rules[j]->cls_rule, version);
n_invisible_rules++;
removable_rule = &rules[j]->cls_rule;
} else {
struct test_rule *tcls_rules[N_RULES];
struct classifier cls;
struct tcls tcls;
- long long version = CLS_MIN_VERSION;
+ cls_version_t version = CLS_MIN_VERSION;
size_t n_invisible_rules = 0;
int value_pats[N_RULES];
int value_mask;
} while (array_contains(value_pats, i, value_pats[i]));
++version;
- rules[i] = make_rule(wcf, priority, value_pats[i], version);
+ rules[i] = make_rule(wcf, priority, value_pats[i]);
tcls_rules[i] = tcls_insert(&tcls, rules[i]);
- classifier_insert(&cls, &rules[i]->cls_rule, NULL, 0);
+ classifier_insert(&cls, &rules[i]->cls_rule, version, NULL, 0);
compare_classifiers(&cls, n_invisible_rules, version, &tcls);
check_tables(&cls, 1, i + 1, 0, n_invisible_rules, version);
tcls_remove(&tcls, tcls_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;
+ cls_rule_make_invisible_in_version(&rules[i]->cls_rule,
+ version);
n_invisible_rules++;
} else {
classifier_remove(&cls, &rules[i]->cls_rule);
int priorities[MAX_RULES];
struct classifier cls;
struct tcls tcls;
- long long version = CLS_MIN_VERSION;
+ cls_version_t version = CLS_MIN_VERSION;
size_t n_invisible_rules = 0;
struct ovs_list list = OVS_LIST_INITIALIZER(&list);
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, version);
+ rule = make_rule(wcf, priority, value_pat);
tcls_insert(&tcls, rule);
- classifier_insert(&cls, &rule->cls_rule, NULL, 0);
+ classifier_insert(&cls, &rule->cls_rule, version, NULL, 0);
compare_classifiers(&cls, n_invisible_rules, version, &tcls);
check_tables(&cls, -1, i + 1, -1, n_invisible_rules, version);
}
target = clone_rule(tcls.rules[random_range(tcls.n_rules)]);
- CLS_FOR_EACH_TARGET (rule, cls_rule, &cls, &target->cls_rule) {
+ CLS_FOR_EACH_TARGET (rule, cls_rule, &cls, &target->cls_rule,
+ version) {
if (versioned) {
/* Mark the rule for removal after the current version. */
cls_rule_make_invisible_in_version(&rule->cls_rule,
- version + 1, version);
+ version + 1);
n_removable_rules++;
compare_classifiers(&cls, n_invisible_rules, version,
&tcls);
}
}
+/* Returns a copy of 'src'. The caller must eventually free the returned
+ * miniflow with free(). */
+static struct miniflow *
+miniflow_clone__(const struct miniflow *src)
+{
+ struct miniflow *dst;
+ size_t data_size;
+
+ data_size = miniflow_alloc(&dst, 1, src);
+ miniflow_clone(dst, src, data_size / sizeof(uint64_t));
+ return dst;
+}
+
+/* Returns a hash value for 'flow', given 'basis'. */
+static inline uint32_t
+miniflow_hash__(const struct miniflow *flow, uint32_t basis)
+{
+ const uint64_t *values = miniflow_get_values(flow);
+ const uint64_t *p = values;
+ uint32_t hash = basis;
+ uint64_t hash_tnl_map = 0, hash_pkt_map = 0;
+ uint64_t map;
+
+ for (map = flow->tnl_map; map; map = zero_rightmost_1bit(map)) {
+ if (*p) {
+ hash = hash_add64(hash, *p);
+ hash_tnl_map |= rightmost_1bit(map);
+ }
+ p++;
+ }
+ for (map = flow->pkt_map; map; map = zero_rightmost_1bit(map)) {
+ if (*p) {
+ hash = hash_add64(hash, *p);
+ hash_pkt_map |= rightmost_1bit(map);
+ }
+ p++;
+ }
+ hash = hash_add64(hash, hash_tnl_map);
+ hash = hash_add64(hash, hash_pkt_map);
+
+ return hash_finish(hash, p - values);
+}
+
static void
test_miniflow(struct ovs_cmdl_context *ctx OVS_UNUSED)
{
random_set_seed(0xb3faca38);
for (idx = 0; next_random_flow(&flow, idx); idx++) {
const uint64_t *flow_u64 = (const uint64_t *) &flow;
- struct miniflow miniflow, miniflow2, miniflow3;
+ struct miniflow *miniflow, *miniflow2, *miniflow3;
struct flow flow2, flow3;
struct flow_wildcards mask;
- struct minimask minimask;
+ struct minimask *minimask;
int i;
/* Convert flow to miniflow. */
- miniflow_init(&miniflow, &flow);
+ miniflow = miniflow_create(&flow);
/* Check that the flow equals its miniflow. */
- assert(miniflow_get_vid(&miniflow) == vlan_tci_to_vid(flow.vlan_tci));
+ assert(miniflow_get_vid(miniflow) == vlan_tci_to_vid(flow.vlan_tci));
for (i = 0; i < FLOW_U64S; i++) {
- assert(miniflow_get(&miniflow, i) == flow_u64[i]);
+ assert(miniflow_get(miniflow, i) == flow_u64[i]);
}
/* Check that the miniflow equals itself. */
- assert(miniflow_equal(&miniflow, &miniflow));
+ assert(miniflow_equal(miniflow, miniflow));
/* Convert miniflow back to flow and verify that it's the same. */
- miniflow_expand(&miniflow, &flow2);
+ miniflow_expand(miniflow, &flow2);
assert(flow_equal(&flow, &flow2));
/* Check that copying a miniflow works properly. */
- miniflow_clone(&miniflow2, &miniflow);
- assert(miniflow_equal(&miniflow, &miniflow2));
- assert(miniflow_hash(&miniflow, 0) == miniflow_hash(&miniflow2, 0));
- miniflow_expand(&miniflow2, &flow3);
+ miniflow2 = miniflow_clone__(miniflow);
+ assert(miniflow_equal(miniflow, miniflow2));
+ assert(miniflow_hash__(miniflow, 0) == miniflow_hash__(miniflow2, 0));
+ miniflow_expand(miniflow2, &flow3);
assert(flow_equal(&flow, &flow3));
/* Check that masked matches work as expected for identical flows and
do {
next_random_flow(&mask.masks, 1);
} while (flow_wildcards_is_catchall(&mask));
- minimask_init(&minimask, &mask);
- assert(minimask_is_catchall(&minimask)
+ minimask = minimask_create(&mask);
+ assert(minimask_is_catchall(minimask)
== flow_wildcards_is_catchall(&mask));
- assert(miniflow_equal_in_minimask(&miniflow, &miniflow2, &minimask));
- assert(miniflow_equal_flow_in_minimask(&miniflow, &flow2, &minimask));
- assert(miniflow_hash_in_minimask(&miniflow, &minimask, 0x12345678) ==
- flow_hash_in_minimask(&flow, &minimask, 0x12345678));
+ assert(miniflow_equal_in_minimask(miniflow, miniflow2, minimask));
+ assert(miniflow_equal_flow_in_minimask(miniflow, &flow2, minimask));
+ assert(miniflow_hash_in_minimask(miniflow, minimask, 0x12345678) ==
+ flow_hash_in_minimask(&flow, minimask, 0x12345678));
+ assert(minimask_hash(minimask, 0) ==
+ miniflow_hash__(&minimask->masks, 0));
/* Check that masked matches work as expected for differing flows and
* miniflows. */
toggle_masked_flow_bits(&flow2, &mask);
- assert(!miniflow_equal_flow_in_minimask(&miniflow, &flow2, &minimask));
- miniflow_init(&miniflow3, &flow2);
- assert(!miniflow_equal_in_minimask(&miniflow, &miniflow3, &minimask));
+ assert(!miniflow_equal_flow_in_minimask(miniflow, &flow2, minimask));
+ miniflow3 = miniflow_create(&flow2);
+ assert(!miniflow_equal_in_minimask(miniflow, miniflow3, minimask));
/* Clean up. */
- miniflow_destroy(&miniflow);
- miniflow_destroy(&miniflow2);
- miniflow_destroy(&miniflow3);
- minimask_destroy(&minimask);
+ free(miniflow);
+ free(miniflow2);
+ free(miniflow3);
+ free(minimask);
}
}
test_minimask_has_extra(struct ovs_cmdl_context *ctx OVS_UNUSED)
{
struct flow_wildcards catchall;
- struct minimask minicatchall;
+ struct minimask *minicatchall;
struct flow flow;
unsigned int idx;
flow_wildcards_init_catchall(&catchall);
- minimask_init(&minicatchall, &catchall);
- assert(minimask_is_catchall(&minicatchall));
+ minicatchall = minimask_create(&catchall);
+ assert(minimask_is_catchall(minicatchall));
random_set_seed(0x2ec7905b);
for (idx = 0; next_random_flow(&flow, idx); idx++) {
struct flow_wildcards mask;
- struct minimask minimask;
+ struct minimask *minimask;
mask.masks = flow;
- minimask_init(&minimask, &mask);
- assert(!minimask_has_extra(&minimask, &minimask));
- assert(minimask_has_extra(&minicatchall, &minimask)
- == !minimask_is_catchall(&minimask));
- if (!minimask_is_catchall(&minimask)) {
- struct minimask minimask2;
+ minimask = minimask_create(&mask);
+ assert(!minimask_has_extra(minimask, minimask));
+ assert(minimask_has_extra(minicatchall, minimask)
+ == !minimask_is_catchall(minimask));
+ if (!minimask_is_catchall(minimask)) {
+ struct minimask *minimask2;
wildcard_extra_bits(&mask);
- minimask_init(&minimask2, &mask);
- assert(minimask_has_extra(&minimask2, &minimask));
- assert(!minimask_has_extra(&minimask, &minimask2));
- minimask_destroy(&minimask2);
+ minimask2 = minimask_create(&mask);
+ assert(minimask_has_extra(minimask2, minimask));
+ assert(!minimask_has_extra(minimask, minimask2));
+ free(minimask2);
}
- minimask_destroy(&minimask);
+ free(minimask);
}
- minimask_destroy(&minicatchall);
+ free(minicatchall);
}
static void
test_minimask_combine(struct ovs_cmdl_context *ctx OVS_UNUSED)
{
struct flow_wildcards catchall;
- struct minimask minicatchall;
+ struct minimask *minicatchall;
struct flow flow;
unsigned int idx;
flow_wildcards_init_catchall(&catchall);
- minimask_init(&minicatchall, &catchall);
- assert(minimask_is_catchall(&minicatchall));
+ minicatchall = minimask_create(&catchall);
+ assert(minimask_is_catchall(minicatchall));
random_set_seed(0x181bf0cd);
for (idx = 0; next_random_flow(&flow, idx); idx++) {
- struct minimask minimask, minimask2, minicombined;
+ struct minimask *minimask, *minimask2;
struct flow_wildcards mask, mask2, combined, combined2;
- uint64_t storage[FLOW_U64S];
+ struct {
+ struct minimask minicombined;
+ uint64_t storage[FLOW_U64S];
+ } m;
struct flow flow2;
mask.masks = flow;
- minimask_init(&minimask, &mask);
+ minimask = minimask_create(&mask);
- minimask_combine(&minicombined, &minimask, &minicatchall, storage);
- assert(minimask_is_catchall(&minicombined));
+ minimask_combine(&m.minicombined, minimask, minicatchall, m.storage);
+ assert(minimask_is_catchall(&m.minicombined));
any_random_flow(&flow2);
mask2.masks = flow2;
- minimask_init(&minimask2, &mask2);
+ minimask2 = minimask_create(&mask2);
- minimask_combine(&minicombined, &minimask, &minimask2, storage);
+ minimask_combine(&m.minicombined, minimask, minimask2, m.storage);
flow_wildcards_and(&combined, &mask, &mask2);
- minimask_expand(&minicombined, &combined2);
+ minimask_expand(&m.minicombined, &combined2);
assert(flow_wildcards_equal(&combined, &combined2));
- minimask_destroy(&minimask);
- minimask_destroy(&minimask2);
+ free(minimask);
+ free(minimask2);
}
- minimask_destroy(&minicatchall);
+ free(minicatchall);
}
\f
static const struct ovs_cmdl_command commands[] = {