}
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;
- atomic_init(&cls_match->visibility, 0); /* Initially invisible. */
- 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 *,
- long long version,
+ 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 *head)
-{
- const struct cls_match *next = next_rule_in_list__(rule);
- return next != head ? next : NULL;
-}
-
-/* Return the next lower-priority rule in the list that is visible. Multiple
- * identical rules with the same priority may exist transitionally. In that
- * case the first rule of a given priority has been marked as visible in one
- * version and the later rules are marked as visible on the other version.
- * This makes it possible to for the head and tail of the list have the same
- * priority. */
-static inline const struct cls_match *
-next_visible_rule_in_list(const struct cls_match *rule, long long version)
-{
- const struct cls_match *next = rule;
-
do {
- next = next_rule_in_list__(next);
- if (next->priority > rule->priority || next == rule) {
- /* We have reached the head of the list, stop. */
- return NULL;
- }
- } while (!cls_match_visible_in_version(next, version));
-
- return next;
-}
+ 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 = NULL;
- next = OBJECT_CONTAINING(rculist_next_protected(&rule->list), next, list);
- return next;
-}
-
-static inline struct cls_match *
-next_rule_in_list_protected(struct cls_match *rule, struct cls_match *head)
-{
- struct cls_match *next = next_rule_in_list_protected__(rule);
- return next != head ? 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, HEAD))
-#define FOR_EACH_RULE_IN_LIST_PROTECTED(RULE, HEAD) \
- for ((RULE) = (HEAD); (RULE) != NULL; \
- (RULE) = next_rule_in_list_protected(RULE, HEAD))
-
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. */
static inline void
-cls_rule_init__(struct cls_rule *rule, unsigned int priority,
- long long version)
+cls_rule_init__(struct cls_rule *rule, unsigned int priority)
{
- ovs_assert(version > 0);
-
rculist_init(&rule->node);
*CONST_CAST(int *, &rule->priority) = priority;
- *CONST_CAST(long long *, &rule->version) = version;
rule->cls_match = NULL;
}
* Clients should not use priority INT_MIN. (OpenFlow uses priorities between
* 0 and UINT16_MAX, inclusive.) */
void
-cls_rule_init(struct cls_rule *rule, const struct match *match, int priority,
- long long version)
+cls_rule_init(struct cls_rule *rule, const struct match *match, int priority)
{
- cls_rule_init__(rule, priority, version);
+ cls_rule_init__(rule, priority);
minimatch_init(CONST_CAST(struct minimatch *, &rule->match), match);
}
/* Same as cls_rule_init() for initialization from a "struct minimatch". */
void
cls_rule_init_from_minimatch(struct cls_rule *rule,
- const struct minimatch *match, int priority,
- long long version)
+ const struct minimatch *match, int priority)
{
- cls_rule_init__(rule, priority, version);
+ cls_rule_init__(rule, priority);
minimatch_clone(CONST_CAST(struct minimatch *, &rule->match), match);
}
void
cls_rule_clone(struct cls_rule *dst, const struct cls_rule *src)
{
- cls_rule_init__(dst, src->priority, src->version);
+ cls_rule_init__(dst, src->priority);
minimatch_clone(CONST_CAST(struct minimatch *, &dst->match), &src->match);
}
void
cls_rule_move(struct cls_rule *dst, struct cls_rule *src)
{
- cls_rule_init__(dst, src->priority, src->version);
+ cls_rule_init__(dst, src->priority);
minimatch_move(CONST_CAST(struct minimatch *, &dst->match),
CONST_CAST(struct minimatch *, &src->match));
}
* ('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(CONST_CAST(struct minimatch *, &rule->match));
}
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 after 'version'. Once that version is made invisible
- * (by changing the version parameter used in lookups), the rule should be
- * actually removed via ovsrcu_postpone().
+/* 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. */
+ * 'rule' must be in a classifier. */
void
-cls_rule_make_invisible_in_version(const struct cls_rule *rule_,
- long long version, long long lookup_version)
-{
- struct cls_match *rule = rule_->cls_match;
-
- /* XXX: Adjust when versioning is actually used. */
- ovs_assert(version >= rule_->version && version >= lookup_version);
-
- /* Normally, we call this when deleting a rule that is already visible to
- * lookups. However, sometimes a bundle transaction will add a rule and
- * then delete it before the rule has ever become visible. If we set such
- * a rule to become invisible in a future 'version', it would become
- * visible to all prior versions. So, in this case we must set the rule
- * visibility to 0 (== never visible). */
- if (cls_match_visible_in_version(rule, lookup_version)) {
- /* Make invisible starting at 'version'. */
- atomic_store_relaxed(&rule->visibility, -version);
- } else {
- /* Rule has not yet been visible to lookups, make invisible in all
- * version. */
- atomic_store_relaxed(&rule->visibility, 0);
- }
+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_after_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)
{
- atomic_store_relaxed(&rule->cls_match->visibility, rule->version);
+ 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, long long version)
+cls_rule_visible_in_version(const struct cls_rule *rule, cls_version_t version)
{
return cls_match_visible_in_version(rule->cls_match, version);
}
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;
uint32_t hash;
int i;
- ovs_assert(rule->version > 0);
-
/* 'new' is initially invisible to lookups. */
- new = cls_match_alloc(rule, conjs, n_conjs);
+ 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);
}
}
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. Insert after rules marked invisible
* in any version of the same priority. */
- FOR_EACH_RULE_IN_LIST_PROTECTED (iter, head) {
+ FOR_EACH_RULE_IN_LIST_PROTECTED (iter, prev, head) {
if (rule->priority > iter->priority
|| (rule->priority == iter->priority
&& !cls_match_is_eventually_invisible(iter))) {
}
}
- /* 'iter' now at the insertion point or NULL if 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 'new' visible to lookups in the appropriate version. */
- cls_match_set_visibility(new, rule->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),
return old;
}
} else {
- rculist_push_back(&head->list, &new->list);
+ /* 'new' is new node after 'prev' */
+ cls_match_insert(prev, iter, new);
}
}
/* Make 'new' visible to lookups in the appropriate version. */
- cls_match_set_visibility(new, rule->version);
+ cls_match_set_remove_version(new, CLS_NOT_REMOVED_VERSION);
/* Make rule visible to iterators (immediately). */
rculist_push_back(&subtable->rules_list,
* 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);
}
const struct cls_rule *
classifier_remove(struct classifier *cls, const struct cls_rule *cls_rule)
{
- struct cls_match *rule, *prev, *next;
+ struct cls_match *rule, *prev, *next, *head;
struct cls_partition *partition;
struct cls_conjunction_set *conj_set;
struct cls_subtable *subtable;
/* Remove 'cls_rule' from the subtable's rules list. */
rculist_remove(CONST_CAST(struct rculist *, &cls_rule->node));
- INIT_CONTAINER(prev, rculist_back_protected(&rule->list), list);
- INIT_CONTAINER(next, rculist_next(&rule->list), list);
-
- /* Remove from the list of equal rules. */
- rculist_remove(&rule->list);
-
- /* Cheap check for a non-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, &cls_rule->match.mask);
+ subtable = find_subtable(cls, cls_rule->match.mask);
ovs_assert(subtable);
for (i = 0; i < subtable->n_indices; i++) {
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 != prev &&
- rule != find_equal(subtable, &cls_rule->match.flow, hash)) {
+ 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;
}
/* 'rule' is the head rule. Check if there is another rule to
* replace 'rule' in the data structures. */
- if (next != rule) {
+ next = cls_match_next_protected(rule);
+ if (next) {
subtable_replace_head_rule(cls, subtable, rule, next, hash, ihash);
goto check_priority;
}
pvector_publish(&cls->subtables);
}
-free:
+ /* free the rule. */
conj_set = ovsrcu_get_protected(struct cls_conjunction_set *,
&rule->conj_set);
if (conj_set) {
ovsrcu_postpone(free, conj_set);
}
- ovsrcu_postpone(free, rule);
+ ovsrcu_postpone(cls_match_free_cb, rule);
cls->n_rules--;
return cls_rule;
* '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, long long version,
+classifier_lookup__(const struct classifier *cls, cls_version_t version,
struct flow *flow, struct flow_wildcards *wc,
bool allow_conjunctive_matches)
{
* '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, long long version,
+classifier_lookup(const struct classifier *cls, cls_version_t version,
struct flow *flow, struct flow_wildcards *wc)
{
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', and that is visible in 'target->version.
+ * 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) {
+ 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, target->version)) {
+ && cls_match_visible_in_version(rule, version)) {
return rule->cls_rule;
}
}
const struct cls_rule *
classifier_find_match_exactly(const struct classifier *cls,
const struct match *target, int priority,
- long long version)
+ cls_version_t version)
{
const struct cls_rule *retval;
struct cls_rule cr;
- cls_rule_init(&cr, target, priority, version);
- retval = classifier_find_rule_exactly(cls, &cr);
+ cls_rule_init(&cr, target, priority);
+ 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, and if both rules are visible in the same version.
+/* 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)
- && cls_match_visible_in_version(rule->cls_match,
- target->version)) {
+ && 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. */
-/* Rule may only match a target if it is visible in target's version. For NULL
- * target we only return rules that are not invisible in any version. */
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)
{
- /* Iterators never see duplicate rules with the same priority. */
- return target
- ? (miniflow_equal_in_minimask(&rule->match.flow, &target->match.flow,
- &target->match.mask)
- && cls_match_visible_in_version(rule->cls_match, target->version))
- : !cls_match_is_eventually_invisible(rule->cls_match);
+ /* 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, or if the 'target' is a catchall target and the
- * target's version is CLS_NO_VERSION, the cursor will visit every rule
- * in 'cls' that is not invisible in any version.
+ * - 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 and that
- * the rule is visible in 'target->version'.
+ * the rule is visible in 'version'.
*
* Ignores target->priority. */
struct cls_cursor
-cls_cursor_start(const struct classifier *cls, const struct cls_rule *target)
+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->version != CLS_MAX_VERSION)
- ? target : NULL;
+ 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;
+ const uint64_t *target_u64 = (const uint64_t *)target;
+ size_t idx;
- MAP_FOR_EACH_INDEX(idx, mask->masks.map) {
- uint64_t diff = (*flowp++ ^ flow_u64_value(target, idx)) & *maskp++;
-
- 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, long long version,
+find_match(const struct cls_subtable *subtable, cls_version_t version,
const struct flow *flow, uint32_t hash)
{
const struct cls_match *head, *rule;
&subtable->mask,
flow))) {
/* Return highest priority rule that is visible. */
- FOR_EACH_RULE_IN_LIST(rule, head) {
+ 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, long long version,
+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)
{
if (miniflow_and_mask_matches_flow_wc(&head->flow, &subtable->mask,
flow, wc)) {
/* Return highest priority rule that is visible. */
- FOR_EACH_RULE_IN_LIST(rule, head) {
+ CLS_MATCH_FOR_EACH (rule, head) {
if (OVS_LIKELY(cls_match_visible_in_version(rule,
version))) {
return rule;
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);
+}