}
static struct cls_match *
-cls_match_alloc(const struct cls_rule *rule,
+cls_match_alloc(const struct cls_rule *rule, cls_version_t version,
const struct cls_conjunction conj[], size_t n)
{
- int count = count_1bits(rule->match.flow.map);
+ size_t count = miniflow_n_values(rule->match.flow);
struct cls_match *cls_match
- = xmalloc(sizeof *cls_match - sizeof cls_match->flow.inline_values
- + MINIFLOW_VALUES_SIZE(count));
+ = xmalloc(sizeof *cls_match + MINIFLOW_VALUES_SIZE(count));
- rculist_init(&cls_match->list);
+ ovsrcu_init(&cls_match->next, NULL);
*CONST_CAST(const struct cls_rule **, &cls_match->cls_rule) = rule;
*CONST_CAST(int *, &cls_match->priority) = rule->priority;
- miniflow_clone_inline(CONST_CAST(struct miniflow *, &cls_match->flow),
- &rule->match.flow, count);
+ *CONST_CAST(cls_version_t *, &cls_match->add_version) = version;
+ atomic_init(&cls_match->remove_version, version); /* Initially
+ * invisible. */
+ miniflow_clone(CONST_CAST(struct miniflow *, &cls_match->flow),
+ rule->match.flow, count);
ovsrcu_set_hidden(&cls_match->conj_set,
cls_conjunction_set_alloc(cls_match, conj, n));
static void destroy_subtable(struct classifier *cls, struct cls_subtable *);
static const struct cls_match *find_match_wc(const struct cls_subtable *,
+ cls_version_t version,
const struct flow *,
struct trie_ctx *,
unsigned int n_tries,
static struct cls_match *find_equal(const struct cls_subtable *,
const struct miniflow *, uint32_t hash);
+/* Return the next visible (lower-priority) rule in the list. Multiple
+ * identical rules with the same priority may exist transitionally, but when
+ * versioning is used at most one of them is ever visible for lookups on any
+ * given 'version'. */
static inline const struct cls_match *
-next_rule_in_list__(const struct cls_match *rule)
+next_visible_rule_in_list(const struct cls_match *rule, cls_version_t version)
{
- const struct cls_match *next = NULL;
- next = OBJECT_CONTAINING(rculist_next(&rule->list), next, list);
- return next;
-}
-
-static inline const struct cls_match *
-next_rule_in_list(const struct cls_match *rule)
-{
- const struct cls_match *next = next_rule_in_list__(rule);
- return next->priority < rule->priority ? next : NULL;
-}
-
-static inline struct cls_match *
-next_rule_in_list_protected__(struct cls_match *rule)
-{
- struct cls_match *next = NULL;
- next = OBJECT_CONTAINING(rculist_next_protected(&rule->list), next, list);
- return next;
-}
+ do {
+ rule = cls_match_next(rule);
+ } while (rule && !cls_match_visible_in_version(rule, version));
-static inline struct cls_match *
-next_rule_in_list_protected(struct cls_match *rule)
-{
- struct cls_match *next = next_rule_in_list_protected__(rule);
- return next->priority < rule->priority ? next : NULL;
+ return rule;
}
-/* Iterates RULE over HEAD and all of the cls_rules on HEAD->list. */
-#define FOR_EACH_RULE_IN_LIST(RULE, HEAD) \
- for ((RULE) = (HEAD); (RULE) != NULL; (RULE) = next_rule_in_list(RULE))
-#define FOR_EACH_RULE_IN_LIST_PROTECTED(RULE, HEAD) \
- for ((RULE) = (HEAD); (RULE) != NULL; \
- (RULE) = next_rule_in_list_protected(RULE))
-
static unsigned int minimask_get_prefix_len(const struct minimask *,
const struct mf_field *);
static void trie_init(struct classifier *cls, int trie_idx,
cls_rule_init__(struct cls_rule *rule, unsigned int priority)
{
rculist_init(&rule->node);
- rule->priority = priority;
+ *CONST_CAST(int *, &rule->priority) = priority;
rule->cls_match = NULL;
}
cls_rule_init(struct cls_rule *rule, const struct match *match, int priority)
{
cls_rule_init__(rule, priority);
- minimatch_init(&rule->match, match);
+ minimatch_init(CONST_CAST(struct minimatch *, &rule->match), match);
}
/* Same as cls_rule_init() for initialization from a "struct minimatch". */
const struct minimatch *match, int priority)
{
cls_rule_init__(rule, priority);
- minimatch_clone(&rule->match, match);
+ minimatch_clone(CONST_CAST(struct minimatch *, &rule->match), match);
}
/* Initializes 'dst' as a copy of 'src'.
cls_rule_clone(struct cls_rule *dst, const struct cls_rule *src)
{
cls_rule_init__(dst, src->priority);
- minimatch_clone(&dst->match, &src->match);
+ minimatch_clone(CONST_CAST(struct minimatch *, &dst->match), &src->match);
}
/* Initializes 'dst' with the data in 'src', destroying 'src'.
+ *
* 'src' must be a cls_rule NOT in a classifier.
*
* The caller must eventually destroy 'dst' with cls_rule_destroy(). */
void
cls_rule_move(struct cls_rule *dst, struct cls_rule *src)
{
- ovs_assert(!src->cls_match); /* Must not be in a classifier. */
cls_rule_init__(dst, src->priority);
- minimatch_move(&dst->match, &src->match);
+ minimatch_move(CONST_CAST(struct minimatch *, &dst->match),
+ CONST_CAST(struct minimatch *, &src->match));
}
/* Frees memory referenced by 'rule'. Doesn't free 'rule' itself (it's
* ('rule' must not currently be in a classifier.) */
void
cls_rule_destroy(struct cls_rule *rule)
+ OVS_NO_THREAD_SAFETY_ANALYSIS
{
ovs_assert(!rule->cls_match); /* Must not be in a classifier. */
- /* Check that the rule has been properly removed from the classifier and
- * that the destruction only happens after the RCU grace period, or that
- * the rule was never inserted to the classifier in the first place. */
- ovs_assert(rculist_next_protected(&rule->node) == RCULIST_POISON
+ /* Check that the rule has been properly removed from the classifier. */
+ ovs_assert(rule->node.prev == RCULIST_POISON
|| rculist_is_empty(&rule->node));
+ rculist_poison__(&rule->node); /* Poisons also the next pointer. */
- minimatch_destroy(&rule->match);
+ minimatch_destroy(CONST_CAST(struct minimatch *, &rule->match));
}
void
bool
cls_rule_is_catchall(const struct cls_rule *rule)
{
- return minimask_is_catchall(&rule->match.mask);
+ return minimask_is_catchall(rule->match.mask);
+}
+
+/* Makes 'rule' invisible in 'remove_version'. Once that version is used in
+ * lookups, the caller should remove 'rule' via ovsrcu_postpone().
+ *
+ * 'rule' must be in a classifier. */
+void
+cls_rule_make_invisible_in_version(const struct cls_rule *rule,
+ cls_version_t remove_version)
+{
+ ovs_assert(remove_version >= rule->cls_match->add_version);
+
+ cls_match_set_remove_version(rule->cls_match, remove_version);
+}
+
+/* This undoes the change made by cls_rule_make_invisible_in_version().
+ *
+ * 'rule' must be in a classifier. */
+void
+cls_rule_restore_visibility(const struct cls_rule *rule)
+{
+ cls_match_set_remove_version(rule->cls_match, CLS_NOT_REMOVED_VERSION);
+}
+
+/* Return true if 'rule' is visible in 'version'.
+ *
+ * 'rule' must be in a classifier. */
+bool
+cls_rule_visible_in_version(const struct cls_rule *rule, cls_version_t version)
+{
+ return cls_match_visible_in_version(rule->cls_match, version);
}
\f
/* Initializes 'cls' as a classifier that initially contains no classification
static inline ovs_be32 minimatch_get_ports(const struct minimatch *match)
{
/* Could optimize to use the same map if needed for fast path. */
- return MINIFLOW_GET_BE32(&match->flow, tp_src)
- & MINIFLOW_GET_BE32(&match->mask.masks, tp_src);
+ return MINIFLOW_GET_BE32(match->flow, tp_src)
+ & MINIFLOW_GET_BE32(&match->mask->masks, tp_src);
}
static void
cmap_replace(&subtable->rules, &head->cmap_node, &new->cmap_node, hash);
}
-/* Inserts 'rule' into 'cls'. Until 'rule' is removed from 'cls', the caller
- * must not modify or free it.
+/* Inserts 'rule' into 'cls' in 'version'. Until 'rule' is removed from 'cls',
+ * the caller must not modify or free it.
*
* If 'cls' already contains an identical rule (including wildcards, values of
- * fixed fields, and priority), replaces the old rule by 'rule' and returns the
- * rule that was replaced. The caller takes ownership of the returned rule and
- * is thus responsible for destroying it with cls_rule_destroy(), after RCU
- * grace period has passed (see ovsrcu_postpone()).
+ * fixed fields, and priority) that is visible in 'version', replaces the old
+ * rule by 'rule' and returns the rule that was replaced. The caller takes
+ * ownership of the returned rule and is thus responsible for destroying it
+ * with cls_rule_destroy(), after RCU grace period has passed (see
+ * ovsrcu_postpone()).
*
* Returns NULL if 'cls' does not contain a rule with an identical key, after
* inserting the new rule. In this case, no rules are displaced by the new
*/
const struct cls_rule *
classifier_replace(struct classifier *cls, const struct cls_rule *rule,
+ cls_version_t version,
const struct cls_conjunction *conjs, size_t n_conjs)
{
- struct cls_match *new = cls_match_alloc(rule, conjs, n_conjs);
+ struct cls_match *new;
struct cls_subtable *subtable;
uint32_t ihash[CLS_MAX_INDICES];
uint8_t prev_be64ofs = 0;
uint32_t hash;
int i;
+ /* 'new' is initially invisible to lookups. */
+ new = cls_match_alloc(rule, version, conjs, n_conjs);
+
CONST_CAST(struct cls_rule *, rule)->cls_match = new;
- subtable = find_subtable(cls, &rule->match.mask);
+ subtable = find_subtable(cls, rule->match.mask);
if (!subtable) {
- subtable = insert_subtable(cls, &rule->match.mask);
+ subtable = insert_subtable(cls, rule->match.mask);
}
/* Compute hashes in segments. */
}
hash = minimatch_hash_range(&rule->match, prev_be64ofs, FLOW_U64S, &basis);
- head = find_equal(subtable, &rule->match.flow, hash);
+ head = find_equal(subtable, rule->match.flow, hash);
if (!head) {
/* Add rule to tries.
*
* Concurrent readers might miss seeing the rule until this update,
* which might require being fixed up by revalidation later. */
new->partition = NULL;
- if (minimask_get_metadata_mask(&rule->match.mask) == OVS_BE64_MAX) {
- ovs_be64 metadata = miniflow_get_metadata(&rule->match.flow);
+ if (minimask_get_metadata_mask(rule->match.mask) == OVS_BE64_MAX) {
+ ovs_be64 metadata = miniflow_get_metadata(rule->match.flow);
new->partition = create_partition(cls, subtable, metadata);
}
- /* Make rule visible to lookups. */
-
/* Add new node to segment indices.
*
* Readers may find the rule in the indices before the rule is visible
}
n_rules = cmap_insert(&subtable->rules, &new->cmap_node, hash);
} else { /* Equal rules exist in the classifier already. */
- struct cls_match *iter;
+ struct cls_match *prev, *iter;
/* Scan the list for the insertion point that will keep the list in
- * order of decreasing priority. */
- FOR_EACH_RULE_IN_LIST_PROTECTED (iter, head) {
- if (rule->priority >= iter->priority) {
+ * order of decreasing priority. Insert after rules marked invisible
+ * in any version of the same priority. */
+ FOR_EACH_RULE_IN_LIST_PROTECTED (iter, prev, head) {
+ if (rule->priority > iter->priority
+ || (rule->priority == iter->priority
+ && !cls_match_is_eventually_invisible(iter))) {
break;
}
}
- /* 'iter' now at the insertion point or NULL it at end. */
+ /* Replace 'iter' with 'new' or insert 'new' between 'prev' and
+ * 'iter'. */
if (iter) {
struct cls_rule *old;
if (rule->priority == iter->priority) {
- rculist_replace(&new->list, &iter->list);
+ cls_match_replace(prev, iter, new);
old = CONST_CAST(struct cls_rule *, iter->cls_rule);
} else {
- rculist_insert(&iter->list, &new->list);
+ cls_match_insert(prev, iter, new);
old = NULL;
}
ovsrcu_postpone(free, conj_set);
}
- ovsrcu_postpone(free, iter);
+ ovsrcu_postpone(cls_match_free_cb, iter);
old->cls_match = NULL;
/* No change in subtable's max priority or max count. */
- /* Make rule visible to iterators. */
+ /* Make 'new' visible to lookups in the appropriate version. */
+ cls_match_set_remove_version(new, CLS_NOT_REMOVED_VERSION);
+
+ /* Make rule visible to iterators (immediately). */
rculist_replace(CONST_CAST(struct rculist *, &rule->node),
&old->node);
return old;
}
} else {
- rculist_push_back(&head->list, &new->list);
+ /* 'new' is new node after 'prev' */
+ cls_match_insert(prev, iter, new);
}
}
- /* Make rule visible to iterators. */
+ /* Make 'new' visible to lookups in the appropriate version. */
+ cls_match_set_remove_version(new, CLS_NOT_REMOVED_VERSION);
+
+ /* Make rule visible to iterators (immediately). */
rculist_push_back(&subtable->rules_list,
CONST_CAST(struct rculist *, &rule->node));
* such a rule. */
void
classifier_insert(struct classifier *cls, const struct cls_rule *rule,
- const struct cls_conjunction conj[], size_t n_conj)
+ cls_version_t version, const struct cls_conjunction conj[],
+ size_t n_conj)
{
const struct cls_rule *displaced_rule
- = classifier_replace(cls, rule, conj, n_conj);
+ = classifier_replace(cls, rule, version, conj, n_conj);
ovs_assert(!displaced_rule);
}
* Returns the removed rule, or NULL, if it was already removed.
*/
const struct cls_rule *
-classifier_remove(struct classifier *cls, const struct cls_rule *rule)
+classifier_remove(struct classifier *cls, const struct cls_rule *cls_rule)
{
+ struct cls_match *rule, *prev, *next, *head;
struct cls_partition *partition;
- struct cls_match *cls_match;
struct cls_conjunction_set *conj_set;
struct cls_subtable *subtable;
- struct cls_match *prev;
- struct cls_match *next;
int i;
uint32_t basis = 0, hash, ihash[CLS_MAX_INDICES];
uint8_t prev_be64ofs = 0;
size_t n_rules;
- cls_match = rule->cls_match;
- if (!cls_match) {
+ rule = cls_rule->cls_match;
+ if (!rule) {
return NULL;
}
/* Mark as removed. */
- CONST_CAST(struct cls_rule *, rule)->cls_match = NULL;
+ CONST_CAST(struct cls_rule *, cls_rule)->cls_match = NULL;
- /* Remove 'rule' from the subtable's rules list. */
- rculist_remove(CONST_CAST(struct rculist *, &rule->node));
+ /* Remove 'cls_rule' from the subtable's rules list. */
+ rculist_remove(CONST_CAST(struct rculist *, &cls_rule->node));
- INIT_CONTAINER(prev, rculist_back_protected(&cls_match->list), list);
- INIT_CONTAINER(next, rculist_next(&cls_match->list), list);
-
- /* Remove from the list of equal rules. */
- rculist_remove(&cls_match->list);
-
- /* Check if this is NOT a head rule. */
- if (prev->priority > rule->priority) {
- /* Not the highest priority rule, no need to check subtable's
- * 'max_priority'. */
- goto free;
- }
-
- subtable = find_subtable(cls, &rule->match.mask);
+ subtable = find_subtable(cls, cls_rule->match.mask);
ovs_assert(subtable);
for (i = 0; i < subtable->n_indices; i++) {
- ihash[i] = minimatch_hash_range(&rule->match, prev_be64ofs,
+ ihash[i] = minimatch_hash_range(&cls_rule->match, prev_be64ofs,
subtable->index_ofs[i], &basis);
prev_be64ofs = subtable->index_ofs[i];
}
- hash = minimatch_hash_range(&rule->match, prev_be64ofs, FLOW_U64S, &basis);
+ hash = minimatch_hash_range(&cls_rule->match, prev_be64ofs, FLOW_U64S,
+ &basis);
+
+ head = find_equal(subtable, cls_rule->match.flow, hash);
+
+ /* Check if the rule is not the head rule. */
+ if (rule != head) {
+ struct cls_match *iter;
+
+ /* Not the head rule, but potentially one with the same priority. */
+ /* Remove from the list of equal rules. */
+ FOR_EACH_RULE_IN_LIST_PROTECTED (iter, prev, head) {
+ if (rule == iter) {
+ break;
+ }
+ }
+ ovs_assert(iter == rule);
+
+ cls_match_remove(prev, rule);
+
+ goto check_priority;
+ }
- /* Head rule. Check if 'next' is an identical, lower-priority rule that
- * will replace 'rule' in the data structures. */
- if (next->priority < rule->priority) {
- subtable_replace_head_rule(cls, subtable, cls_match, next, hash,
- ihash);
+ /* 'rule' is the head rule. Check if there is another rule to
+ * replace 'rule' in the data structures. */
+ next = cls_match_next_protected(rule);
+ if (next) {
+ subtable_replace_head_rule(cls, subtable, rule, next, hash, ihash);
goto check_priority;
}
* data structures. */
if (subtable->ports_mask_len) {
- ovs_be32 masked_ports = minimatch_get_ports(&rule->match);
+ ovs_be32 masked_ports = minimatch_get_ports(&cls_rule->match);
trie_remove_prefix(&subtable->ports_trie,
&masked_ports, subtable->ports_mask_len);
}
for (i = 0; i < cls->n_tries; i++) {
if (subtable->trie_plen[i]) {
- trie_remove(&cls->tries[i], rule, subtable->trie_plen[i]);
+ trie_remove(&cls->tries[i], cls_rule, subtable->trie_plen[i]);
}
}
/* Remove rule node from indices. */
for (i = 0; i < subtable->n_indices; i++) {
- cmap_remove(&subtable->indices[i], &cls_match->index_nodes[i],
- ihash[i]);
+ cmap_remove(&subtable->indices[i], &rule->index_nodes[i], ihash[i]);
}
- n_rules = cmap_remove(&subtable->rules, &cls_match->cmap_node, hash);
+ n_rules = cmap_remove(&subtable->rules, &rule->cmap_node, hash);
- partition = cls_match->partition;
+ partition = rule->partition;
if (partition) {
tag_tracker_subtract(&partition->tracker, &partition->tags,
subtable->tag);
if (subtable->max_priority == rule->priority
&& --subtable->max_count == 0) {
/* Find the new 'max_priority' and 'max_count'. */
- struct cls_match *head;
int max_priority = INT_MIN;
+ struct cls_match *head;
CMAP_FOR_EACH (head, cmap_node, &subtable->rules) {
if (head->priority > max_priority) {
pvector_publish(&cls->subtables);
}
-free:
+ /* free the rule. */
conj_set = ovsrcu_get_protected(struct cls_conjunction_set *,
- &cls_match->conj_set);
+ &rule->conj_set);
if (conj_set) {
ovsrcu_postpone(free, conj_set);
}
- ovsrcu_postpone(free, cls_match);
+ ovsrcu_postpone(cls_match_free_cb, rule);
cls->n_rules--;
- return rule;
+ return cls_rule;
}
/* Prefix tree context. Valid when 'lookup_done' is true. Can skip all
* 'flow' is non-const to allow for temporary modifications during the lookup.
* Any changes are restored before returning. */
static const struct cls_rule *
-classifier_lookup__(const struct classifier *cls, struct flow *flow,
- struct flow_wildcards *wc, bool allow_conjunctive_matches)
+classifier_lookup__(const struct classifier *cls, cls_version_t version,
+ struct flow *flow, struct flow_wildcards *wc,
+ bool allow_conjunctive_matches)
{
const struct cls_partition *partition;
struct trie_ctx trie_ctx[CLS_MAX_TRIES];
/* Skip subtables with no match, or where the match is lower-priority
* than some certain match we've already found. */
- match = find_match_wc(subtable, flow, trie_ctx, cls->n_tries, wc);
+ match = find_match_wc(subtable, version, flow, trie_ctx, cls->n_tries,
+ wc);
if (!match || match->priority <= hard_pri) {
continue;
}
const struct cls_rule *rule;
flow->conj_id = id;
- rule = classifier_lookup__(cls, flow, wc, false);
+ rule = classifier_lookup__(cls, version, flow, wc, false);
flow->conj_id = saved_conj_id;
if (rule) {
}
/* Find next-lower-priority flow with identical flow match. */
- match = next_rule_in_list(soft[i]->match);
+ match = next_visible_rule_in_list(soft[i]->match, version);
if (match) {
soft[i] = ovsrcu_get(struct cls_conjunction_set *,
&match->conj_set);
return hard ? hard->cls_rule : NULL;
}
-/* Finds and returns the highest-priority rule in 'cls' that matches 'flow'.
- * Returns a null pointer if no rules in 'cls' match 'flow'. If multiple rules
- * of equal priority match 'flow', returns one arbitrarily.
+/* Finds and returns the highest-priority rule in 'cls' that matches 'flow' and
+ * that is visible in 'version'. Returns a null pointer if no rules in 'cls'
+ * match 'flow'. If multiple rules of equal priority match 'flow', returns one
+ * arbitrarily.
*
* If a rule is found and 'wc' is non-null, bitwise-OR's 'wc' with the
* set of bits that were significant in the lookup. At some point
* 'flow' is non-const to allow for temporary modifications during the lookup.
* Any changes are restored before returning. */
const struct cls_rule *
-classifier_lookup(const struct classifier *cls, struct flow *flow,
- struct flow_wildcards *wc)
+classifier_lookup(const struct classifier *cls, cls_version_t version,
+ struct flow *flow, struct flow_wildcards *wc)
{
- return classifier_lookup__(cls, flow, wc, true);
+ return classifier_lookup__(cls, version, flow, wc, true);
}
/* Finds and returns a rule in 'cls' with exactly the same priority and
- * matching criteria as 'target'. Returns a null pointer if 'cls' doesn't
+ * matching criteria as 'target', and that is visible in 'version'.
+ * Only one such rule may ever exist. Returns a null pointer if 'cls' doesn't
* contain an exact match. */
const struct cls_rule *
classifier_find_rule_exactly(const struct classifier *cls,
- const struct cls_rule *target)
+ const struct cls_rule *target,
+ cls_version_t version)
{
const struct cls_match *head, *rule;
const struct cls_subtable *subtable;
- subtable = find_subtable(cls, &target->match.mask);
+ subtable = find_subtable(cls, target->match.mask);
if (!subtable) {
return NULL;
}
- head = find_equal(subtable, &target->match.flow,
- miniflow_hash_in_minimask(&target->match.flow,
- &target->match.mask, 0));
+ head = find_equal(subtable, target->match.flow,
+ miniflow_hash_in_minimask(target->match.flow,
+ target->match.mask, 0));
if (!head) {
return NULL;
}
- FOR_EACH_RULE_IN_LIST (rule, head) {
- if (target->priority >= rule->priority) {
- return target->priority == rule->priority ? rule->cls_rule : NULL;
+ CLS_MATCH_FOR_EACH (rule, head) {
+ if (rule->priority < target->priority) {
+ break; /* Not found. */
+ }
+ if (rule->priority == target->priority
+ && cls_match_visible_in_version(rule, version)) {
+ return rule->cls_rule;
}
}
return NULL;
}
/* Finds and returns a rule in 'cls' with priority 'priority' and exactly the
- * same matching criteria as 'target'. Returns a null pointer if 'cls' doesn't
- * contain an exact match. */
+ * same matching criteria as 'target', and that is visible in 'version'.
+ * Returns a null pointer if 'cls' doesn't contain an exact match visible in
+ * 'version'. */
const struct cls_rule *
classifier_find_match_exactly(const struct classifier *cls,
- const struct match *target, int priority)
+ const struct match *target, int priority,
+ cls_version_t version)
{
const struct cls_rule *retval;
struct cls_rule cr;
cls_rule_init(&cr, target, priority);
- retval = classifier_find_rule_exactly(cls, &cr);
+ retval = classifier_find_rule_exactly(cls, &cr, version);
cls_rule_destroy(&cr);
return retval;
}
-/* Checks if 'target' would overlap any other rule in 'cls'. Two rules are
- * considered to overlap if both rules have the same priority and a packet
- * could match both.
+/* Checks if 'target' would overlap any other rule in 'cls' in 'version'. Two
+ * rules are considered to overlap if both rules have the same priority and a
+ * packet could match both, and if both rules are visible in the same version.
*
* A trivial example of overlapping rules is two rules matching disjoint sets
* of fields. E.g., if one rule matches only on port number, while another only
* dl_type could match both, if the rules also have the same priority. */
bool
classifier_rule_overlaps(const struct classifier *cls,
- const struct cls_rule *target)
+ const struct cls_rule *target, cls_version_t version)
{
struct cls_subtable *subtable;
/* Iterate subtables in the descending max priority order. */
PVECTOR_FOR_EACH_PRIORITY (subtable, target->priority - 1, 2,
sizeof(struct cls_subtable), &cls->subtables) {
- uint64_t storage[FLOW_U64S];
- struct minimask mask;
+ struct {
+ struct minimask mask;
+ uint64_t storage[FLOW_U64S];
+ } m;
const struct cls_rule *rule;
- minimask_combine(&mask, &target->match.mask, &subtable->mask, storage);
+ minimask_combine(&m.mask, target->match.mask, &subtable->mask,
+ m.storage);
RCULIST_FOR_EACH (rule, node, &subtable->rules_list) {
if (rule->priority == target->priority
- && miniflow_equal_in_minimask(&target->match.flow,
- &rule->match.flow, &mask)) {
+ && miniflow_equal_in_minimask(target->match.flow,
+ rule->match.flow, &m.mask)
+ && cls_match_visible_in_version(rule->cls_match, version)) {
return true;
}
}
cls_rule_is_loose_match(const struct cls_rule *rule,
const struct minimatch *criteria)
{
- return (!minimask_has_extra(&rule->match.mask, &criteria->mask)
- && miniflow_equal_in_minimask(&rule->match.flow, &criteria->flow,
- &criteria->mask));
+ return (!minimask_has_extra(rule->match.mask, criteria->mask)
+ && miniflow_equal_in_minimask(rule->match.flow, criteria->flow,
+ criteria->mask));
}
\f
/* Iteration. */
static bool
-rule_matches(const struct cls_rule *rule, const struct cls_rule *target)
+rule_matches(const struct cls_rule *rule, const struct cls_rule *target,
+ cls_version_t version)
{
- return (!target
- || miniflow_equal_in_minimask(&rule->match.flow,
- &target->match.flow,
- &target->match.mask));
+ /* Rule may only match a target if it is visible in target's version. */
+ return cls_match_visible_in_version(rule->cls_match, version)
+ && (!target || miniflow_equal_in_minimask(rule->match.flow,
+ target->match.flow,
+ target->match.mask));
}
static const struct cls_rule *
struct cls_cursor *cursor)
{
if (!cursor->target
- || !minimask_has_extra(&subtable->mask, &cursor->target->match.mask)) {
+ || !minimask_has_extra(&subtable->mask, cursor->target->match.mask)) {
const struct cls_rule *rule;
RCULIST_FOR_EACH (rule, node, &subtable->rules_list) {
- if (rule_matches(rule, cursor->target)) {
+ if (rule_matches(rule, cursor->target, cursor->version)) {
return rule;
}
}
}
/* Initializes 'cursor' for iterating through rules in 'cls', and returns the
- * first matching cls_rule via '*pnode', or NULL if there are no matches.
+ * cursor.
*
- * - If 'target' is null, the cursor will visit every rule in 'cls'.
+ * - If 'target' is null, or if the 'target' is a catchall target, the
+ * cursor will visit every rule in 'cls' that is visible in 'version'.
*
* - If 'target' is nonnull, the cursor will visit each 'rule' in 'cls'
- * such that cls_rule_is_loose_match(rule, target) returns true.
+ * such that cls_rule_is_loose_match(rule, target) returns true and that
+ * the rule is visible in 'version'.
*
* Ignores target->priority. */
-struct cls_cursor cls_cursor_start(const struct classifier *cls,
- const struct cls_rule *target)
+struct cls_cursor
+cls_cursor_start(const struct classifier *cls, const struct cls_rule *target,
+ cls_version_t version)
{
struct cls_cursor cursor;
struct cls_subtable *subtable;
cursor.cls = cls;
cursor.target = target && !cls_rule_is_catchall(target) ? target : NULL;
+ cursor.version = version;
cursor.rule = NULL;
/* Find first rule. */
rule = cursor->rule;
subtable = cursor->subtable;
RCULIST_FOR_EACH_CONTINUE (rule, node, &subtable->rules_list) {
- if (rule_matches(rule, cursor->target)) {
+ if (rule_matches(rule, cursor->target, cursor->version)) {
return rule;
}
}
int i, index = 0;
struct flow_wildcards old, new;
uint8_t prev;
- int count = count_1bits(mask->masks.map);
+ size_t count = miniflow_n_values(&mask->masks);
- subtable = xzalloc(sizeof *subtable - sizeof mask->masks.inline_values
- + MINIFLOW_VALUES_SIZE(count));
+ subtable = xzalloc(sizeof *subtable + MINIFLOW_VALUES_SIZE(count));
cmap_init(&subtable->rules);
- miniflow_clone_inline(CONST_CAST(struct miniflow *, &subtable->mask.masks),
- &mask->masks, count);
+ miniflow_clone(CONST_CAST(struct miniflow *, &subtable->mask.masks),
+ &mask->masks, count);
/* Init indices for segmented lookup, if any. */
flow_wildcards_init_catchall(&new);
{
const uint64_t *flowp = miniflow_get_values(flow);
const uint64_t *maskp = miniflow_get_values(&mask->masks);
- int idx;
-
- MAP_FOR_EACH_INDEX(idx, mask->masks.map) {
- uint64_t diff = (*flowp++ ^ flow_u64_value(target, idx)) & *maskp++;
+ const uint64_t *target_u64 = (const uint64_t *)target;
+ size_t idx;
- if (diff) {
+ MAP_FOR_EACH_INDEX(idx, mask->masks.tnl_map) {
+ if ((*flowp++ ^ target_u64[idx]) & *maskp++) {
+ return false;
+ }
+ }
+ target_u64 += FLOW_TNL_U64S;
+ MAP_FOR_EACH_INDEX(idx, mask->masks.pkt_map) {
+ if ((*flowp++ ^ target_u64[idx]) & *maskp++) {
return false;
}
}
}
static inline const struct cls_match *
-find_match(const struct cls_subtable *subtable, const struct flow *flow,
- uint32_t hash)
+find_match(const struct cls_subtable *subtable, cls_version_t version,
+ const struct flow *flow, uint32_t hash)
{
- const struct cls_match *rule;
+ const struct cls_match *head, *rule;
- CMAP_FOR_EACH_WITH_HASH (rule, cmap_node, hash, &subtable->rules) {
- if (miniflow_and_mask_matches_flow(&rule->flow, &subtable->mask,
- flow)) {
- return rule;
+ CMAP_FOR_EACH_WITH_HASH (head, cmap_node, hash, &subtable->rules) {
+ if (OVS_LIKELY(miniflow_and_mask_matches_flow(&head->flow,
+ &subtable->mask,
+ flow))) {
+ /* Return highest priority rule that is visible. */
+ CLS_MATCH_FOR_EACH (rule, head) {
+ if (OVS_LIKELY(cls_match_visible_in_version(rule, version))) {
+ return rule;
+ }
+ }
}
}
{
const uint64_t *flowp = miniflow_get_values(flow);
const uint64_t *maskp = miniflow_get_values(&mask->masks);
- int idx;
+ const uint64_t *target_u64 = (const uint64_t *)target;
+ uint64_t *wc_u64 = (uint64_t *)&wc->masks;
+ uint64_t diff;
+ size_t idx;
- MAP_FOR_EACH_INDEX(idx, mask->masks.map) {
- uint64_t mask = *maskp++;
- uint64_t diff = (*flowp++ ^ flow_u64_value(target, idx)) & mask;
+ MAP_FOR_EACH_INDEX(idx, mask->masks.tnl_map) {
+ uint64_t msk = *maskp++;
+ diff = (*flowp++ ^ target_u64[idx]) & msk;
if (diff) {
- /* Only unwildcard if none of the differing bits is already
- * exact-matched. */
- if (!(flow_u64_value(&wc->masks, idx) & diff)) {
- /* Keep one bit of the difference. The selected bit may be
- * different in big-endian v.s. little-endian systems. */
- *flow_u64_lvalue(&wc->masks, idx) |= rightmost_1bit(diff);
- }
- return false;
+ goto out;
+ }
+
+ /* Fill in the bits that were looked at. */
+ wc_u64[idx] |= msk;
+ }
+ target_u64 += FLOW_TNL_U64S;
+ wc_u64 += FLOW_TNL_U64S;
+ MAP_FOR_EACH_INDEX(idx, mask->masks.pkt_map) {
+ uint64_t msk = *maskp++;
+
+ diff = (*flowp++ ^ target_u64[idx]) & msk;
+ if (diff) {
+ goto out;
}
+
/* Fill in the bits that were looked at. */
- *flow_u64_lvalue(&wc->masks, idx) |= mask;
+ wc_u64[idx] |= msk;
}
return true;
+
+out:
+ /* Only unwildcard if none of the differing bits is already
+ * exact-matched. */
+ if (!(wc_u64[idx] & diff)) {
+ /* Keep one bit of the difference. The selected bit may be
+ * different in big-endian v.s. little-endian systems. */
+ wc_u64[idx] |= rightmost_1bit(diff);
+ }
+ return false;
}
/* Unwildcard the fields looked up so far, if any. */
}
static const struct cls_match *
-find_match_wc(const struct cls_subtable *subtable, const struct flow *flow,
- struct trie_ctx trie_ctx[CLS_MAX_TRIES], unsigned int n_tries,
- struct flow_wildcards *wc)
+find_match_wc(const struct cls_subtable *subtable, cls_version_t version,
+ const struct flow *flow, struct trie_ctx trie_ctx[CLS_MAX_TRIES],
+ unsigned int n_tries, struct flow_wildcards *wc)
{
uint32_t basis = 0, hash;
const struct cls_match *rule = NULL;
struct range ofs;
if (OVS_UNLIKELY(!wc)) {
- return find_match(subtable, flow,
+ return find_match(subtable, version, flow,
flow_hash_in_minimask(flow, &subtable->mask, 0));
}
* (Rare) hash collisions may cause us to miss the opportunity for this
* optimization. */
if (!cmap_node_next(inode)) {
- ASSIGN_CONTAINER(rule, inode - i, index_nodes);
- if (miniflow_and_mask_matches_flow_wc(&rule->flow, &subtable->mask,
+ const struct cls_match *head;
+
+ ASSIGN_CONTAINER(head, inode - i, index_nodes);
+ if (miniflow_and_mask_matches_flow_wc(&head->flow, &subtable->mask,
flow, wc)) {
- return rule;
+ /* Return highest priority rule that is visible. */
+ CLS_MATCH_FOR_EACH (rule, head) {
+ if (OVS_LIKELY(cls_match_visible_in_version(rule,
+ version))) {
+ return rule;
+ }
+ }
}
return NULL;
}
}
hash = flow_hash_in_minimask_range(flow, &subtable->mask, ofs.start,
ofs.end, &basis);
- rule = find_match(subtable, flow, hash);
+ rule = find_match(subtable, version, flow, hash);
if (!rule && subtable->ports_mask_len) {
/* Ports are always part of the final range, if any.
* No match was found for the ports. Use the ports trie to figure out
static const ovs_be32 *
minimatch_get_prefix(const struct minimatch *match, const struct mf_field *mf)
{
- return (OVS_FORCE const ovs_be32 *)
- (miniflow_get_values(&match->flow)
- + count_1bits(match->flow.map &
- ((UINT64_C(1) << mf->flow_be32ofs / 2) - 1)))
+ size_t u64_ofs = mf->flow_be32ofs / 2;
+
+ return (OVS_FORCE const ovs_be32 *)miniflow_get__(match->flow, u64_ofs)
+ (mf->flow_be32ofs & 1);
}
* that actually exist in the classifier are ever removed. */
VLOG_WARN("Trying to remove non-existing rule from a prefix trie.");
}
+\f
+
+#define CLS_MATCH_POISON (struct cls_match *)(UINTPTR_MAX / 0xf * 0xb)
+
+void
+cls_match_free_cb(struct cls_match *rule)
+{
+ ovsrcu_set_hidden(&rule->next, CLS_MATCH_POISON);
+ free(rule);
+}