> On Jun 11, 2015, at 3:00 PM, Gurucharan Shetty <shet...@nicira.com> wrote:
> 
> On Tue, Jun 9, 2015 at 5:24 PM, Jarno Rajahalme <jrajaha...@nicira.com 
> <mailto:jrajaha...@nicira.com>> wrote:
>> This patch allows classifier rules to become visible and invisible in
>> specific versions.  A 'version' is defined as a positive monotonically
>> increasing integer, which never wraps around.
>> 
>> The new 'visibility' attribute replaces the prior 'to_be_removed' and
>> 'visible' attributes.
>> 
>> When versioning is not used, the 'version' parameter should be passed
>> as 'CLS_MIN_VERSION' when creating rules, and 'CLS_MAX_VERSION' when
>> looking up flows.
>> 
>> This feature enables the support for atomic OpenFlow bundles without
>> significant performance penalty on 64-bit systems. There is a
>> performance decrease in 32-bit systems due to 64-bit atomics used.
>> 
>> Signed-off-by: Jarno Rajahalme <jrajaha...@nicira.com>
> 
> Looks like this patch causes ovs-vswitchd to crash in unit tests of
> Windows with the following backtrace:
> 
>> ovs-vswitchd.exe!abort() Line 88 C
>  ovs-vswitchd.exe!ofproto_dpif_add_internal_flow(ofproto_dpif *
> ofproto, const match * match, int priority, unsigned short
> idle_timeout, const ofpbuf * ofpacts, rule * * rulep) Line 5454 C

On master line ofproto-dpif.c:5452 is the OVS_NOT_REACHED();

I’ve seen this happen earlier while coding this, but this should not happen on 
master. The cause was that the internal rule was added for a next version, but 
the lookup was done with the old version, so the new rule was invisible. But on 
master the rule is added in version CLS_MIN_VERSION (= 1) and the lookup (that 
seems to fail) is done in CLS_MAX_VERSION (LLONG_MAX), so it should work.

You could try to modify the classifier_lookup line in ofproto-dpif.c to use a 
much lower number, in case something goes wrong with the atomics used within 
the classifier:

-        cls_rule = classifier_lookup(cls, CLS_MAX_VERSION, flow, wc);
+        cls_rule = classifier_lookup(cls, 10, flow, wc);

In master the version number is not yet incremented, so this should work.

Anyway, master works on Linux, both with gcc and clang. Sounds like a weird 
compiler problem.

Hmm. was there something wrong with the 64-bit atomics on the Windows compiler?

Also, I have not tried 32-bit compilation of the master, my current setup can’t 
handle multilibs, so I can’t try that out yet.

  Jarno

>  ovs-vswitchd.exe!add_internal_miss_flow(ofproto_dpif * ofproto, int
> id, const ofpbuf * ofpacts, rule_dpif * * rulep) Line 1301 C
>  ovs-vswitchd.exe!add_internal_flows(ofproto_dpif * ofproto) Line 1328 C
>  ovs-vswitchd.exe!construct(ofproto * ofproto_) Line 1282 C
>  ovs-vswitchd.exe!ofproto_create(const char * datapath_name, const
> char * datapath_type, ofproto * * ofprotop) Line 549 C
>  ovs-vswitchd.exe!bridge_reconfigure(const ovsrec_open_vswitch *
> ovs_cfg) Line 618 C
>  ovs-vswitchd.exe!bridge_run() Line 2950 C
>  ovs-vswitchd.exe!main(int argc, char * * argv) Line 117 C
>  [External Code]
> 
> 
> Does anything standout?
> 
>> ---
>> lib/classifier-private.h |   52 ++++++++++-
>> lib/classifier.c         |  221 
>> +++++++++++++++++++++++++++++-----------------
>> lib/classifier.h         |  148 +++++++++++++++++++++----------
>> lib/ovs-router.c         |    7 +-
>> lib/tnl-ports.c          |    9 +-
>> ofproto/ofproto-dpif.c   |    2 +-
>> ofproto/ofproto.c        |   94 +++++++++-----------
>> tests/test-classifier.c  |    6 +-
>> utilities/ovs-ofctl.c    |    3 +-
>> 9 files changed, 347 insertions(+), 195 deletions(-)
>> 
>> diff --git a/lib/classifier-private.h b/lib/classifier-private.h
>> index a7edbe9..f7462d2 100644
>> --- a/lib/classifier-private.h
>> +++ b/lib/classifier-private.h
>> @@ -79,13 +79,63 @@ struct cls_match {
>>                                                     * 'indices'. */
>>     /* Accessed by all readers. */
>>     struct cmap_node cmap_node; /* Within struct cls_subtable 'rules'. */
>> -    bool visible;
>> +
>> +    /* Controls rule's visibility to lookups.
>> +     *
>> +     * When 'visibility' is:
>> +     *
>> +     * > 0  - rule is visible starting from version 'visibility'
>> +     * <= 0 - rule is invisible starting from version '-(visibility)'
>> +     *
>> +     * The minimum version number used in lookups is 1 (== CLS_NO_VERSION),
>> +     * which implies that when 'visibility' is:
>> +     *
>> +     * 1    - rule is visible in all lookup versions
>> +     * 0    - rule is invisible in all lookup versions. */
>> +    atomic_llong visibility;
>> +
>>     const struct cls_rule *cls_rule;
>>     OVSRCU_TYPE(struct cls_conjunction_set *) conj_set;
>>     const struct miniflow flow; /* Matching rule. Mask is in the subtable. */
>>     /* 'flow' must be the last field. */
>> };
>> 
>> +static inline void
>> +cls_match_set_visibility(struct cls_match *rule, long long version)
>> +{
>> +    atomic_store_relaxed(&rule->visibility, version);
>> +}
>> +
>> +static inline bool
>> +cls_match_visible_in_version(const struct cls_match *rule, long long 
>> version)
>> +{
>> +    long long visibility;
>> +
>> +    /* clang does not want to read from a const atomic. */
>> +    atomic_read_relaxed(&CONST_CAST(struct cls_match *, rule)->visibility,
>> +                        &visibility);
>> +
>> +    if (OVS_LIKELY(visibility > 0)) {
>> +        /* Rule is visible starting from version 'visibility'. */
>> +        return version >= visibility;
>> +    } else {
>> +        /* Rule is invisible starting from version '-visibility'. */
>> +        return version < -visibility;
>> +    }
>> +}
>> +
>> +static inline bool
>> +cls_match_is_eventually_invisible(const struct cls_match *rule)
>> +{
>> +    long long visibility;
>> +
>> +    /* clang does not want to read from a const atomic. */
>> +    atomic_read_relaxed(&CONST_CAST(struct cls_match *, rule)->visibility,
>> +                        &visibility);
>> +
>> +    return visibility <= 0;
>> +}
>> +
>> /* A longest-prefix match tree. */
>> struct trie_node {
>>     uint32_t prefix;           /* Prefix bits for this node, MSB first. */
>> diff --git a/lib/classifier.c b/lib/classifier.c
>> index 6075cf7..2b2d3f6 100644
>> --- a/lib/classifier.c
>> +++ b/lib/classifier.c
>> @@ -99,7 +99,7 @@ cls_match_alloc(const struct cls_rule *rule,
>>     rculist_init(&cls_match->list);
>>     *CONST_CAST(const struct cls_rule **, &cls_match->cls_rule) = rule;
>>     *CONST_CAST(int *, &cls_match->priority) = rule->priority;
>> -    cls_match->visible = false;
>> +    atomic_init(&cls_match->visibility, 0);   /* Initially invisible. */
>>     miniflow_clone_inline(CONST_CAST(struct miniflow *, &cls_match->flow),
>>                           &rule->match.flow, count);
>>     ovsrcu_set_hidden(&cls_match->conj_set,
>> @@ -115,6 +115,7 @@ static struct cls_subtable *insert_subtable(struct 
>> classifier *cls,
>> 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,
>>                                              const struct flow *,
>>                                              struct trie_ctx *,
>>                                              unsigned int n_tries,
>> @@ -139,12 +140,12 @@ next_rule_in_list(const struct cls_match *rule, const 
>> struct cls_match *head)
>> 
>> /* 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 
>> 'to_be_removed',
>> - * and the later rules are marked as '!visible'.  This gets a bit complex if
>> - * there are two rules of the same priority in the list, as in that case the
>> - * head and tail of the list will have the same priority. */
>> + * 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)
>> +next_visible_rule_in_list(const struct cls_match *rule, long long version)
>> {
>>     const struct cls_match *next = rule;
>> 
>> @@ -154,7 +155,7 @@ next_visible_rule_in_list(const struct cls_match *rule)
>>             /* We have reached the head of the list, stop. */
>>             return NULL;
>>         }
>> -    } while (!next->visible);
>> +    } while (!cls_match_visible_in_version(next, version));
>> 
>>     return next;
>> }
>> @@ -206,11 +207,14 @@ static bool mask_prefix_bits_set(const struct 
>> flow_wildcards *,
>> /* cls_rule. */
>> 
>> static inline void
>> -cls_rule_init__(struct cls_rule *rule, unsigned int priority)
>> +cls_rule_init__(struct cls_rule *rule, unsigned int priority,
>> +                long long version)
>> {
>> +    ovs_assert(version > 0);
>> +
>>     rculist_init(&rule->node);
>> -    rule->priority = priority;
>> -    rule->to_be_removed = false;
>> +    *CONST_CAST(int *, &rule->priority) = priority;
>> +    *CONST_CAST(long long *, &rule->version) = version;
>>     rule->cls_match = NULL;
>> }
>> 
>> @@ -223,19 +227,21 @@ cls_rule_init__(struct cls_rule *rule, unsigned int 
>> priority)
>>  * 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)
>> +cls_rule_init(struct cls_rule *rule, const struct match *match, int 
>> priority,
>> +              long long version)
>> {
>> -    cls_rule_init__(rule, priority);
>> -    minimatch_init(&rule->match, match);
>> +    cls_rule_init__(rule, priority, version);
>> +    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)
>> +                             const struct minimatch *match, int priority,
>> +                             long long version)
>> {
>> -    cls_rule_init__(rule, priority);
>> -    minimatch_clone(&rule->match, match);
>> +    cls_rule_init__(rule, priority, version);
>> +    minimatch_clone(CONST_CAST(struct minimatch *, &rule->match), match);
>> }
>> 
>> /* Initializes 'dst' as a copy of 'src'.
>> @@ -244,20 +250,21 @@ cls_rule_init_from_minimatch(struct cls_rule *rule,
>> void
>> cls_rule_clone(struct cls_rule *dst, const struct cls_rule *src)
>> {
>> -    cls_rule_init__(dst, src->priority);
>> -    minimatch_clone(&dst->match, &src->match);
>> +    cls_rule_init__(dst, src->priority, src->version);
>> +    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);
>> +    cls_rule_init__(dst, src->priority, src->version);
>> +    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
>> @@ -275,7 +282,7 @@ cls_rule_destroy(struct cls_rule *rule)
>>     ovs_assert(rculist_next_protected(&rule->node) == RCULIST_POISON
>>                || rculist_is_empty(&rule->node));
>> 
>> -    minimatch_destroy(&rule->match);
>> +    minimatch_destroy(CONST_CAST(struct minimatch *, &rule->match));
>> }
>> 
>> void
>> @@ -327,15 +334,53 @@ cls_rule_is_catchall(const struct cls_rule *rule)
>>     return minimask_is_catchall(&rule->match.mask);
>> }
>> 
>> -/* Rules inserted during classifier_defer() need to be made visible before
>> - * calling classifier_publish().
>> +/* 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().
>> + *
>> + * '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);
>> +    }
>> +}
>> +
>> +/* This undoes the change made by cls_rule_make_invisible_after_version().
>>  *
>>  * 'rule' must be in a classifier. */
>> -void cls_rule_make_visible(const struct cls_rule *rule)
>> +void
>> +cls_rule_restore_visibility(const struct cls_rule *rule)
>> {
>> -    rule->cls_match->visible = true;
>> +    atomic_store_relaxed(&rule->cls_match->visibility, rule->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)
>> +{
>> +    return cls_match_visible_in_version(rule->cls_match, version);
>> +}
>> 
>> /* Initializes 'cls' as a classifier that initially contains no 
>> classification
>>  * rules. */
>> @@ -597,7 +642,7 @@ const struct cls_rule *
>> classifier_replace(struct classifier *cls, const struct cls_rule *rule,
>>                    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;
>> @@ -607,6 +652,11 @@ classifier_replace(struct classifier *cls, const struct 
>> cls_rule *rule,
>>     uint32_t hash;
>>     int i;
>> 
>> +    ovs_assert(rule->version > 0);
>> +
>> +    /* 'new' is initially invisible to lookups. */
>> +    new = cls_match_alloc(rule, conjs, n_conjs);
>> +
>>     CONST_CAST(struct cls_rule *, rule)->cls_match = new;
>> 
>>     subtable = find_subtable(cls, &rule->match.mask);
>> @@ -673,12 +723,12 @@ classifier_replace(struct classifier *cls, const 
>> struct cls_rule *rule,
>>         struct cls_match *iter;
>> 
>>         /* Scan the list for the insertion point that will keep the list in
>> -         * order of decreasing priority.
>> -         * Insert after 'to_be_removed' rules of the same priority. */
>> +         * order of decreasing priority.  Insert after rules marked 
>> invisible
>> +         * in any version of the same priority. */
>>         FOR_EACH_RULE_IN_LIST_PROTECTED (iter, head) {
>>             if (rule->priority > iter->priority
>>                 || (rule->priority == iter->priority
>> -                    && !iter->cls_rule->to_be_removed)) {
>> +                    && !cls_match_is_eventually_invisible(iter))) {
>>                 break;
>>             }
>>         }
>> @@ -716,8 +766,8 @@ classifier_replace(struct classifier *cls, const struct 
>> cls_rule *rule,
>> 
>>                 /* No change in subtable's max priority or max count. */
>> 
>> -                /* Make rule visible to lookups? */
>> -                new->visible = cls->publish;
>> +                /* Make 'new' visible to lookups in the appropriate 
>> version. */
>> +                cls_match_set_visibility(new, rule->version);
>> 
>>                 /* Make rule visible to iterators (immediately). */
>>                 rculist_replace(CONST_CAST(struct rculist *, &rule->node),
>> @@ -732,8 +782,8 @@ classifier_replace(struct classifier *cls, const struct 
>> cls_rule *rule,
>>         }
>>     }
>> 
>> -    /* Make rule visible to lookups? */
>> -    new->visible = cls->publish;
>> +    /* Make 'new' visible to lookups in the appropriate version. */
>> +    cls_match_set_visibility(new, rule->version);
>> 
>>     /* Make rule visible to iterators (immediately). */
>>     rculist_push_back(&subtable->rules_list,
>> @@ -1026,8 +1076,9 @@ free_conjunctive_matches(struct hmap *matches,
>>  * '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, long long 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];
>> @@ -1094,7 +1145,8 @@ classifier_lookup__(const struct classifier *cls, 
>> struct flow *flow,
>> 
>>         /* 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;
>>         }
>> @@ -1218,7 +1270,7 @@ classifier_lookup__(const struct classifier *cls, 
>> struct flow *flow,
>>                 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) {
>> @@ -1246,7 +1298,7 @@ classifier_lookup__(const struct classifier *cls, 
>> struct flow *flow,
>>             }
>> 
>>             /* Find next-lower-priority flow with identical flow match. */
>> -            match = next_visible_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);
>> @@ -1271,9 +1323,10 @@ classifier_lookup__(const struct classifier *cls, 
>> struct flow *flow,
>>     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
>> @@ -1283,18 +1336,16 @@ classifier_lookup__(const struct classifier *cls, 
>> struct flow *flow,
>>  * '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, long long 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
>> - * contain an exact match.
>> - *
>> - * Returns the first matching rule that is not 'to_be_removed'.  Only one 
>> such
>> - * rule may exist. */
>> + * matching criteria as 'target', and that is visible in 'target->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)
>> @@ -1318,7 +1369,7 @@ classifier_find_rule_exactly(const struct classifier 
>> *cls,
>>             break; /* Not found. */
>>         }
>>         if (rule->priority == target->priority
>> -            && !rule->cls_rule->to_be_removed) {
>> +            && cls_match_visible_in_version(rule, target->version)) {
>>             return rule->cls_rule;
>>         }
>>     }
>> @@ -1326,16 +1377,18 @@ classifier_find_rule_exactly(const struct classifier 
>> *cls,
>> }
>> 
>> /* 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,
>> +                              long long version)
>> {
>>     const struct cls_rule *retval;
>>     struct cls_rule cr;
>> 
>> -    cls_rule_init(&cr, target, priority);
>> +    cls_rule_init(&cr, target, priority, version);
>>     retval = classifier_find_rule_exactly(cls, &cr);
>>     cls_rule_destroy(&cr);
>> 
>> @@ -1344,16 +1397,12 @@ classifier_find_match_exactly(const struct 
>> classifier *cls,
>> 
>> /* 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.
>> + * 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
>>  * on dl_type, any packet from that specific port and with that specific
>> - * dl_type could match both, if the rules also have the same priority.
>> - *
>> - * 'target' is not considered to overlap with a rule that has been marked
>> - * as 'to_be_removed'.
>> - */
>> + * 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)
>> @@ -1371,9 +1420,10 @@ classifier_rule_overlaps(const struct classifier *cls,
>> 
>>         RCULIST_FOR_EACH (rule, node, &subtable->rules_list) {
>>             if (rule->priority == target->priority
>> -                && !rule->to_be_removed
>>                 && miniflow_equal_in_minimask(&target->match.flow,
>> -                                              &rule->match.flow, &mask)) {
>> +                                              &rule->match.flow, &mask)
>> +                && cls_match_visible_in_version(rule->cls_match,
>> +                                                target->version)) {
>>                 return true;
>>             }
>>         }
>> @@ -1425,16 +1475,17 @@ cls_rule_is_loose_match(const struct cls_rule *rule,
>> 
>> /* 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)
>> {
>> -    /* Iterators never see rules that have been marked for removal.
>> -     * This allows them to be oblivious of duplicate rules. */
>> -    return (!rule->to_be_removed &&
>> -            (!target
>> -             || miniflow_equal_in_minimask(&rule->match.flow,
>> -                                           &target->match.flow,
>> -                                           &target->match.mask)));
>> +    /* 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);
>> }
>> 
>> static const struct cls_rule *
>> @@ -1457,10 +1508,13 @@ search_subtable(const struct cls_subtable *subtable,
>> /* Initializes 'cursor' for iterating through rules in 'cls', and returns the
>>  * first matching cls_rule via '*pnode', or NULL if there are no matches.
>>  *
>> - *     - If 'target' is null, the cursor will visit every rule in 'cls'.
>> + *     - 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 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 'target->version'.
>>  *
>>  * Ignores target->priority. */
>> struct cls_cursor
>> @@ -1470,7 +1524,9 @@ cls_cursor_start(const struct classifier *cls, const 
>> struct cls_rule *target)
>>     struct cls_subtable *subtable;
>> 
>>     cursor.cls = cls;
>> -    cursor.target = target && !cls_rule_is_catchall(target) ? target : NULL;
>> +    cursor.target = target && (!cls_rule_is_catchall(target)
>> +                               || target->version != CLS_MAX_VERSION)
>> +        ? target : NULL;
>>     cursor.rule = NULL;
>> 
>>     /* Find first rule. */
>> @@ -1722,8 +1778,8 @@ miniflow_and_mask_matches_flow(const struct miniflow 
>> *flow,
>> }
>> 
>> 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, long long version,
>> +           const struct flow *flow, uint32_t hash)
>> {
>>     const struct cls_match *head, *rule;
>> 
>> @@ -1733,7 +1789,7 @@ find_match(const struct cls_subtable *subtable, const 
>> struct flow *flow,
>>                                                       flow))) {
>>             /* Return highest priority rule that is visible. */
>>             FOR_EACH_RULE_IN_LIST(rule, head) {
>> -                if (OVS_LIKELY(rule->visible)) {
>> +                if (OVS_LIKELY(cls_match_visible_in_version(rule, 
>> version))) {
>>                     return rule;
>>                 }
>>             }
>> @@ -1791,9 +1847,9 @@ fill_range_wc(const struct cls_subtable *subtable, 
>> struct flow_wildcards *wc,
>> }
>> 
>> 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, long long 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;
>> @@ -1801,7 +1857,7 @@ find_match_wc(const struct cls_subtable *subtable, 
>> const struct flow *flow,
>>     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));
>>     }
>> 
>> @@ -1842,7 +1898,8 @@ find_match_wc(const struct cls_subtable *subtable, 
>> const struct flow *flow,
>>                                                   flow, wc)) {
>>                 /* Return highest priority rule that is visible. */
>>                 FOR_EACH_RULE_IN_LIST(rule, head) {
>> -                    if (OVS_LIKELY(rule->visible)) {
>> +                    if (OVS_LIKELY(cls_match_visible_in_version(rule,
>> +                                                                version))) {
>>                         return rule;
>>                     }
>>                 }
>> @@ -1859,7 +1916,7 @@ find_match_wc(const struct cls_subtable *subtable, 
>> const struct flow *flow,
>>     }
>>     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
>> diff --git a/lib/classifier.h b/lib/classifier.h
>> index d69c201..cb0030a 100644
>> --- a/lib/classifier.h
>> +++ b/lib/classifier.h
>> @@ -210,46 +210,98 @@
>>  * Each eliminated subtable lookup also reduces the amount of un-wildcarding.
>>  *
>>  *
>> - * Tentative Modifications
>> - * =======================
>> - *
>> - * When a new rule is added to a classifier, it can optionally be 
>> "invisible".
>> - * That means that lookups won't find the rule, although iterations through
>> - * the classifier will see it.
>> - *
>> - * Similarly, deletions from a classifier can be "tentative", by setting
>> - * 'to_be_removed' to true within the rule.  A rule that is tentatively 
>> deleted
>> - * will not appear in iterations, although it will still be found by 
>> lookups.
>> + * Classifier Versioning
>> + * =====================
>> + *
>> + * Classifier lookups are always done in a specific classifier version, 
>> where
>> + * a version is defined to be a natural number.
>> + *
>> + * When a new rule is added to a classifier, it is set to become visible in 
>> a
>> + * specific version.  If the version number used at insert time is larger 
>> than
>> + * any version number currently used in lookups, the new rule is said to be
>> + * invisible to lookups.  This means that lookups won't find the rule, but 
>> the
>> + * rule is immediately available to classifier iterations.
>> + *
>> + * Similarly, a rule can be marked as to be deleted in a future version, or
>> + * more precisely, to be visible upto a given version number.  To delete a 
>> rule
>> + * in a way to not remove the rule before all ongoing lookups are finished, 
>> the
>> + * rule should be marked as "to be deleted" by setting the rule's 
>> visibility to
>> + * the negation of the last version number in which it should be visible.
>> + * Then, when all the lookups use a later version number, the rule can be
>> + * actually deleted from the classifier.  A rule that is marked for deletion
>> + * after a future version will not appear in iterations, although it will 
>> still
>> + * be found by lookups using a lookup version number up to that future 
>> version
>> + * number.
>>  *
>>  * Classifiers can hold duplicate rules (rules with the same match criteria 
>> and
>> - * priority) when tentative modifications are involved: one (or more) 
>> identical
>> - * tentatively deleted rules can coexist in a classifier with at most one
>> - * identical invisible rule.
>> - *
>> - * The classifier supports tentative modifications for two reasons:
>> - *
>> - *     1. Performance: Adding (or deleting) a rule can, in pathological 
>> cases,
>> - *        have a cost proportional to the number of rules already in the
>> - *        classifier.  When multiple rules are being added (or deleted) in 
>> one
>> - *        go, though, this cost can be paid just once, not once per addition
>> - *        (or deletion), as long as it is OK for any new rules to be 
>> invisible
>> - *        until the batch change is complete.
>> - *
>> - *     2. Staging additions and deletions: Invisibility allows a rule to be
>> - *        added tentatively, to possibly be modified or removed before it
>> - *        becomes visible.  Tentatively deletion allows a rule to be 
>> scheduled
>> - *        for deletion before it is certain that the deletion is desirable.
>> + * priority) when at most one of the duplicates with the same priority is
>> + * visible in any given lookup version.  The caller responsible for 
>> classifier
>> + * modifications must maintain this invariant.
>> + *
>> + * The classifier supports versioning for two reasons:
>> + *
>> + *     1. Support for versioned modifications makes it possible to perform 
>> an
>> + *        arbitraty series of classifier changes as one atomic transaction,
>> + *        where intermediate versions of the classifier are not visible to 
>> any
>> + *        lookups.  Also, when a rule is added for a future version, or 
>> marked
>> + *        for removal after the current version, such modifications can be
>> + *        reverted without any visible effects to any of the current 
>> lookups.
>> + *
>> + *     2. Performance: Adding (or deleting) a large set of rules can, in
>> + *        pathological cases, have a cost proportional to the number of 
>> rules
>> + *        already in the classifier.  When multiple rules are being added 
>> (or
>> + *        deleted) in one go, though, this pathological case cost can be
>> + *        typically avoided, as long as it is OK for any new rules to be
>> + *        invisible until the batch change is complete.
>> + *
>> + * Note that the classifier_replace() function replaces a rule immediately, 
>> and
>> + * is therefore not safe to use with versioning.  It is still available for 
>> the
>> + * users that do not use versioning.
>> + *
>> + *
>> + * Deferred Publication
>> + * ====================
>> + *
>> + * Removing large number of rules from classifier can be costly, as the
>> + * supporting data structures are teared down, in many cases just to be
>> + * re-instantiated right after.  In the worst case, as when each rule has a
>> + * different match pattern (mask), the maintenance of the match patterns can
>> + * have cost O(N^2), where N is the number of different match patterns.  To
>> + * alleviate this, the classifier supports a "deferred mode", in which 
>> changes
>> + * in internal data structures needed for future version lookups may not be
>> + * fully computed yet.  The computation is finalized when the deferred mode 
>> is
>> + * turned off.
>> + *
>> + * This feature can be used with versioning such that all changes to future
>> + * versions are made in the deferred mode.  Then, right before making the 
>> new
>> + * version visible to lookups, the deferred mode is turned off so that all 
>> the
>> + * data structures are ready for lookups with the new version number.
>>  *
>>  * To use deferred publication, first call classifier_defer().  Then, modify
>> - * the classifier via additions and deletions.  Call 
>> cls_rule_make_visible() on
>> - * each new rule at an appropriate time.  Finally, call 
>> classifier_publish().
>> + * the classifier via additions (classifier_insert() with a specific, future
>> + * version number) and deletions (use 
>> cls_rule_make_removable_after_version()).
>> + * Then call classifier_publish(), and after that, announce the new version
>> + * number to be used in lookups.
>>  *
>>  *
>>  * Thread-safety
>>  * =============
>>  *
>> - * The classifier may safely be accessed by many reader threads 
>> concurrently or
>> - * by a single writer. */
>> + * The classifier may safely be accessed by many reader threads concurrently
>> + * and by a single writer, or by multiple writers when they guarantee 
>> mutually
>> + * exlucive access to classifier modifications.
>> + *
>> + * Since the classifier rules are RCU protected, the rule destruction after
>> + * removal from the classifier must be RCU postponed.  Also, when 
>> versioning is
>> + * used, the rule removal itself needs to be typically RCU postponed.  In 
>> this
>> + * case the rule destruction is doubly RCU postponed, i.e., the second
>> + * ovsrcu_postpone() call to destruct the rule is called from the first RCU
>> + * callback that removes the rule.
>> + *
>> + * Rules that have never been visible to lookups are an exeption to the 
>> above
>> + * rule.  Such rules can be removed immediately, but their destruction must
>> + * still be RCU postponed, as the rule's visibility attribute may be 
>> examined
>> + * parallel to the rule's removal. */
>> 
>> #include "cmap.h"
>> #include "match.h"
>> @@ -275,6 +327,8 @@ struct cls_trie {
>> };
>> 
>> enum {
>> +    CLS_MIN_VERSION = 1,   /* Default version number to use. */
>> +    CLS_MAX_VERSION = LLONG_MAX, /* Last possible version number. */
>>     CLS_MAX_INDICES = 3,   /* Maximum number of lookup indices per subtable. 
>> */
>>     CLS_MAX_TRIES = 3      /* Maximum number of prefix trees per classifier. 
>> */
>> };
>> @@ -301,22 +355,17 @@ struct cls_conjunction {
>> 
>> /* A rule to be inserted to the classifier. */
>> struct cls_rule {
>> -    struct rculist node;         /* In struct cls_subtable 'rules_list'. */
>> -    int priority;                /* Larger numbers are higher priorities. */
>> -    bool to_be_removed;          /* Rule will be deleted.
>> -                                  * This is the only field that may be
>> -                                  * modified after the rule has been added 
>> to
>> -                                  * a classifier.  Modifications are to be
>> -                                  * done only under same locking as all 
>> other
>> -                                  * classifier modifications.  This field 
>> may
>> -                                  * not be examined by lookups. */
>> -    struct cls_match *cls_match; /* NULL if not in a classifier. */
>> -    struct minimatch match;      /* Matching rule. */
>> +    struct rculist node;          /* In struct cls_subtable 'rules_list'. */
>> +    const int priority;           /* Larger numbers are higher priorities. 
>> */
>> +    const long long version;      /* Version in which the rule was added. */
>> +    struct cls_match *cls_match;  /* NULL if not in a classifier. */
>> +    const struct minimatch match; /* Matching rule. */
>> };
>> 
>> -void cls_rule_init(struct cls_rule *, const struct match *, int priority);
>> +void cls_rule_init(struct cls_rule *, const struct match *, int priority,
>> +                   long long version);
>> void cls_rule_init_from_minimatch(struct cls_rule *, const struct minimatch 
>> *,
>> -                                  int priority);
>> +                                  int priority, long long version);
>> void cls_rule_clone(struct cls_rule *, const struct cls_rule *);
>> void cls_rule_move(struct cls_rule *dst, struct cls_rule *src);
>> void cls_rule_destroy(struct cls_rule *);
>> @@ -330,7 +379,11 @@ void cls_rule_format(const struct cls_rule *, struct ds 
>> *);
>> bool cls_rule_is_catchall(const struct cls_rule *);
>> bool cls_rule_is_loose_match(const struct cls_rule *rule,
>>                              const struct minimatch *criteria);
>> -void cls_rule_make_visible(const struct cls_rule *rule);
>> +bool cls_rule_visible_in_version(const struct cls_rule *, long long 
>> version);
>> +void cls_rule_make_invisible_in_version(const struct cls_rule *,
>> +                                        long long version,
>> +                                        long long lookup_version);
>> +void cls_rule_restore_visibility(const struct cls_rule *);
>> 
>> /* Constructor/destructor.  Must run single-threaded. */
>> void classifier_init(struct classifier *, const uint8_t *flow_segments);
>> @@ -354,7 +407,7 @@ static inline void classifier_publish(struct classifier 
>> *);
>> /* Lookups.  These are RCU protected and may run concurrently with modifiers
>>  * and each other. */
>> const struct cls_rule *classifier_lookup(const struct classifier *,
>> -                                         struct flow *,
>> +                                         long long version, struct flow *,
>>                                          struct flow_wildcards *);
>> bool classifier_rule_overlaps(const struct classifier *,
>>                               const struct cls_rule *);
>> @@ -362,7 +415,8 @@ const struct cls_rule 
>> *classifier_find_rule_exactly(const struct classifier *,
>>                                                     const struct cls_rule *);
>> const struct cls_rule *classifier_find_match_exactly(const struct classifier 
>> *,
>>                                                      const struct match *,
>> -                                                     int priority);
>> +                                                     int priority,
>> +                                                     long long version);
>> bool classifier_is_empty(const struct classifier *);
>> int classifier_count(const struct classifier *);
>> 
>> diff --git a/lib/ovs-router.c b/lib/ovs-router.c
>> index bf205d6..532487e 100644
>> --- a/lib/ovs-router.c
>> +++ b/lib/ovs-router.c
>> @@ -68,7 +68,7 @@ ovs_router_lookup(ovs_be32 ip_dst, char output_bridge[], 
>> ovs_be32 *gw)
>>     const struct cls_rule *cr;
>>     struct flow flow = {.nw_dst = ip_dst};
>> 
>> -    cr = classifier_lookup(&cls, &flow, NULL);
>> +    cr = classifier_lookup(&cls, CLS_MAX_VERSION, &flow, NULL);
>>     if (cr) {
>>         struct ovs_router_entry *p = ovs_router_entry_cast(cr);
>> 
>> @@ -115,7 +115,8 @@ ovs_router_insert__(uint8_t priority, ovs_be32 ip_dst, 
>> uint8_t plen,
>>     p->nw_addr = match.flow.nw_dst;
>>     p->plen = plen;
>>     p->priority = priority;
>> -    cls_rule_init(&p->cr, &match, priority); /* Longest prefix matches 
>> first. */
>> +    /* Longest prefix matches first. */
>> +    cls_rule_init(&p->cr, &match, priority, CLS_MIN_VERSION);
>> 
>>     ovs_mutex_lock(&mutex);
>>     cr = classifier_replace(&cls, &p->cr, NULL, 0);
>> @@ -144,7 +145,7 @@ rt_entry_delete(uint8_t priority, ovs_be32 ip_dst, 
>> uint8_t plen)
>> 
>>     rt_init_match(&match, ip_dst, plen);
>> 
>> -    cls_rule_init(&rule, &match, priority);
>> +    cls_rule_init(&rule, &match, priority, CLS_MIN_VERSION);
>> 
>>     /* Find the exact rule. */
>>     cr = classifier_find_rule_exactly(&cls, &rule);
>> diff --git a/lib/tnl-ports.c b/lib/tnl-ports.c
>> index 759d6ba..2602db5 100644
>> --- a/lib/tnl-ports.c
>> +++ b/lib/tnl-ports.c
>> @@ -84,7 +84,7 @@ tnl_port_map_insert(odp_port_t port, ovs_be32 ip_dst, 
>> ovs_be16 udp_port,
>> 
>>     ovs_mutex_lock(&mutex);
>>     do {
>> -        cr = classifier_lookup(&cls, &match.flow, NULL);
>> +        cr = classifier_lookup(&cls, CLS_MAX_VERSION, &match.flow, NULL);
>>         p = tnl_port_cast(cr);
>>         /* Try again if the rule was released before we get the reference. */
>>     } while (p && !ovs_refcount_try_ref_rcu(&p->ref_cnt));
>> @@ -99,7 +99,7 @@ tnl_port_map_insert(odp_port_t port, ovs_be32 ip_dst, 
>> ovs_be16 udp_port,
>>         match.wc.masks.tp_dst = OVS_BE16_MAX;
>>         match.wc.masks.nw_src = OVS_BE32_MAX;
>> 
>> -        cls_rule_init(&p->cr, &match, 0);   /* Priority == 0. */
>> +        cls_rule_init(&p->cr, &match, 0, CLS_MIN_VERSION); /* Priority == 
>> 0. */
>>         ovs_refcount_init(&p->ref_cnt);
>>         ovs_strlcpy(p->dev_name, dev_name, sizeof p->dev_name);
>> 
>> @@ -130,7 +130,7 @@ tnl_port_map_delete(ovs_be32 ip_dst, ovs_be16 udp_port)
>> 
>>     tnl_port_init_flow(&flow, ip_dst, udp_port);
>> 
>> -    cr = classifier_lookup(&cls, &flow, NULL);
>> +    cr = classifier_lookup(&cls, CLS_MAX_VERSION, &flow, NULL);
>>     tnl_port_unref(cr);
>> }
>> 
>> @@ -139,7 +139,8 @@ tnl_port_map_delete(ovs_be32 ip_dst, ovs_be16 udp_port)
>> odp_port_t
>> tnl_port_map_lookup(struct flow *flow, struct flow_wildcards *wc)
>> {
>> -    const struct cls_rule *cr = classifier_lookup(&cls, flow, wc);
>> +    const struct cls_rule *cr = classifier_lookup(&cls, CLS_MAX_VERSION, 
>> flow,
>> +                                                  wc);
>> 
>>     return (cr) ? tnl_port_cast(cr)->portno : ODPP_NONE;
>> }
>> diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
>> index c4cafe0..81beca0 100644
>> --- a/ofproto/ofproto-dpif.c
>> +++ b/ofproto/ofproto-dpif.c
>> @@ -3725,7 +3725,7 @@ rule_dpif_lookup_in_table(struct ofproto_dpif 
>> *ofproto, uint8_t table_id,
>>     struct rule_dpif *rule;
>> 
>>     do {
>> -        cls_rule = classifier_lookup(cls, flow, wc);
>> +        cls_rule = classifier_lookup(cls, CLS_MAX_VERSION, flow, wc);
>> 
>>         rule = rule_dpif_cast(rule_from_cls_rule(cls_rule));
>> 
>> diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c
>> index f2e9557..b5424b9 100644
>> --- a/ofproto/ofproto.c
>> +++ b/ofproto/ofproto.c
>> @@ -153,6 +153,7 @@ struct rule_criteria {
>> 
>> static void rule_criteria_init(struct rule_criteria *, uint8_t table_id,
>>                                const struct match *match, int priority,
>> +                               long long version,
>>                                ovs_be64 cookie, ovs_be64 cookie_mask,
>>                                ofp_port_t out_port, uint32_t out_group);
>> static void rule_criteria_require_rw(struct rule_criteria *,
>> @@ -2043,7 +2044,8 @@ ofproto_add_flow(struct ofproto *ofproto, const struct 
>> match *match,
>>     /* First do a cheap check whether the rule we're looking for already 
>> exists
>>      * with the actions that we want.  If it does, then we're done. */
>>     rule = rule_from_cls_rule(classifier_find_match_exactly(
>> -                                  &ofproto->tables[0].cls, match, 
>> priority));
>> +                                  &ofproto->tables[0].cls, match, priority,
>> +                                  CLS_MAX_VERSION));
>>     if (rule) {
>>         const struct rule_actions *actions = rule_get_actions(rule);
>>         must_add = !ofpacts_equal(actions->ofpacts, actions->ofpacts_len,
>> @@ -2080,9 +2082,9 @@ ofproto_flow_mod(struct ofproto *ofproto, struct 
>> ofputil_flow_mod *fm)
>>         struct rule *rule;
>>         bool done = false;
>> 
>> -        rule = rule_from_cls_rule(classifier_find_match_exactly(&table->cls,
>> -                                                                &fm->match,
>> -                                                                
>> fm->priority));
>> +        rule = rule_from_cls_rule(classifier_find_match_exactly(
>> +                                      &table->cls, &fm->match,
>> +                                      fm->priority, CLS_MAX_VERSION));
>>         if (rule) {
>>             /* Reading many of the rule fields and writing on 'modified'
>>              * requires the rule->mutex.  Also, rule->actions may change
>> @@ -2129,7 +2131,8 @@ ofproto_delete_flow(struct ofproto *ofproto,
>>     /* First do a cheap check whether the rule we're looking for has already
>>      * been deleted.  If so, then we're done. */
>>     rule = rule_from_cls_rule(classifier_find_match_exactly(cls, target,
>> -                                                            priority));
>> +                                                            priority,
>> +                                                            
>> CLS_MAX_VERSION));
>>     if (!rule) {
>>         return;
>>     }
>> @@ -3038,7 +3041,7 @@ learned_cookies_flush(struct ofproto *ofproto, struct 
>> ovs_list *dead_cookies)
>>         struct match match;
>> 
>>         match_init_catchall(&match);
>> -        rule_criteria_init(&criteria, c->table_id, &match, 0,
>> +        rule_criteria_init(&criteria, c->table_id, &match, 0, 
>> CLS_MAX_VERSION,
>>                            c->cookie, OVS_BE64_MAX, OFPP_ANY, OFPG_ANY);
>>         rule_criteria_require_rw(&criteria, false);
>>         collect_rules_loose(ofproto, &criteria, &rules);
>> @@ -3676,12 +3679,12 @@ next_matching_table(const struct ofproto *ofproto,
>>  * supplied as 0. */
>> static void
>> rule_criteria_init(struct rule_criteria *criteria, uint8_t table_id,
>> -                   const struct match *match, int priority,
>> +                   const struct match *match, int priority, long long 
>> version,
>>                    ovs_be64 cookie, ovs_be64 cookie_mask,
>>                    ofp_port_t out_port, uint32_t out_group)
>> {
>>     criteria->table_id = table_id;
>> -    cls_rule_init(&criteria->cr, match, priority);
>> +    cls_rule_init(&criteria->cr, match, priority, version);
>>     criteria->cookie = cookie;
>>     criteria->cookie_mask = cookie_mask;
>>     criteria->out_port = out_port;
>> @@ -3785,7 +3788,7 @@ rule_collection_destroy(struct rule_collection *rules)
>>  * function verifies most of the criteria in 'c' itself, but the caller must
>>  * check 'c->cr' itself.
>>  *
>> - * Rules that have already been marked as 'to_be_removed' are not collected.
>> + * Rules that have already been marked for removal are not collected.
>>  *
>>  * Increments '*n_readonly' if 'rule' wasn't added because it's read-only 
>> (and
>>  * 'c' only includes modifiable rules). */
>> @@ -3799,7 +3802,7 @@ collect_rule(struct rule *rule, const struct 
>> rule_criteria *c,
>>         && ofproto_rule_has_out_group(rule, c->out_group)
>>         && !((rule->flow_cookie ^ c->cookie) & c->cookie_mask)
>>         && (!rule_is_hidden(rule) || c->include_hidden)
>> -        && !rule->cr.to_be_removed) {
>> +        && cls_rule_visible_in_version(&rule->cr, c->cr.version)) {
>>         /* Rule matches all the criteria... */
>>         if (!rule_is_readonly(rule) || c->include_readonly) {
>>             /* ...add it. */
>> @@ -3951,8 +3954,9 @@ handle_flow_stats_request(struct ofconn *ofconn,
>>         return error;
>>     }
>> 
>> -    rule_criteria_init(&criteria, fsr.table_id, &fsr.match, 0, fsr.cookie,
>> -                       fsr.cookie_mask, fsr.out_port, fsr.out_group);
>> +    rule_criteria_init(&criteria, fsr.table_id, &fsr.match, 0, 
>> CLS_MAX_VERSION,
>> +                       fsr.cookie, fsr.cookie_mask, fsr.out_port,
>> +                       fsr.out_group);
>> 
>>     ovs_mutex_lock(&ofproto_mutex);
>>     error = collect_rules_loose(ofproto, &criteria, &rules);
>> @@ -4115,7 +4119,7 @@ handle_aggregate_stats_request(struct ofconn *ofconn,
>>     }
>> 
>>     rule_criteria_init(&criteria, request.table_id, &request.match, 0,
>> -                       request.cookie, request.cookie_mask,
>> +                       CLS_MAX_VERSION, request.cookie, request.cookie_mask,
>>                        request.out_port, request.out_group);
>> 
>>     ovs_mutex_lock(&ofproto_mutex);
>> @@ -4404,10 +4408,10 @@ add_flow_start(struct ofproto *ofproto, struct 
>> ofputil_flow_mod *fm,
>>         return OFPERR_OFPBRC_EPERM;
>>     }
>> 
>> -    cls_rule_init(&cr, &fm->match, fm->priority);
>> +    cls_rule_init(&cr, &fm->match, fm->priority, CLS_MIN_VERSION);
>> 
>>     /* Check for the existence of an identical rule.
>> -     * This will not return rules earlier marked as 'to_be_removed'. */
>> +     * This will not return rules earlier marked for removal. */
>>     rule = rule_from_cls_rule(classifier_find_rule_exactly(&table->cls, 
>> &cr));
>>     if (rule) {
>>         /* Transform "add" into "modify" of an existing identical flow. */
>> @@ -4506,7 +4510,6 @@ add_flow_finish(struct ofproto *ofproto, struct 
>> ofputil_flow_mod *fm,
>>     } else {
>>         struct oftable *table = &ofproto->tables[rule->table_id];
>> 
>> -        cls_rule_make_visible(&rule->cr);
>>         classifier_publish(&table->cls);
>> 
>>         learned_cookies_inc(ofproto, rule_get_actions(rule));
>> @@ -4573,7 +4576,7 @@ modify_flows_check__(struct ofproto *ofproto, struct 
>> ofputil_flow_mod *fm,
>>     return 0;
>> }
>> 
>> -/* Modifies the 'rule', changing them to match 'fm'. */
>> +/* Modifies the 'rule', changing it to match 'fm'. */
>> static void
>> modify_flow__(struct ofproto *ofproto, struct ofputil_flow_mod *fm,
>>               const struct flow_mod_requester *req, struct rule *rule,
>> @@ -4742,7 +4745,7 @@ modify_flows_start_loose(struct ofproto *ofproto, 
>> struct ofputil_flow_mod *fm,
>>     struct rule_criteria criteria;
>>     enum ofperr error;
>> 
>> -    rule_criteria_init(&criteria, fm->table_id, &fm->match, 0,
>> +    rule_criteria_init(&criteria, fm->table_id, &fm->match, 0, 
>> CLS_MAX_VERSION,
>>                        fm->cookie, fm->cookie_mask, OFPP_ANY, OFPG11_ANY);
>>     rule_criteria_require_rw(&criteria,
>>                              (fm->flags & OFPUTIL_FF_NO_READONLY) != 0);
>> @@ -4796,7 +4799,8 @@ modify_flow_start_strict(struct ofproto *ofproto, 
>> struct ofputil_flow_mod *fm,
>>     enum ofperr error;
>> 
>>     rule_criteria_init(&criteria, fm->table_id, &fm->match, fm->priority,
>> -                       fm->cookie, fm->cookie_mask, OFPP_ANY, OFPG11_ANY);
>> +                       CLS_MAX_VERSION, fm->cookie, fm->cookie_mask, 
>> OFPP_ANY,
>> +                       OFPG11_ANY);
>>     rule_criteria_require_rw(&criteria,
>>                              (fm->flags & OFPUTIL_FF_NO_READONLY) != 0);
>>     error = collect_rules_strict(ofproto, &criteria, rules);
>> @@ -4826,11 +4830,12 @@ delete_flows__(const struct rule_collection *rules,
>>         struct ovs_list dead_cookies = OVS_LIST_INITIALIZER(&dead_cookies);
>>         struct ofproto *ofproto = rules->rules[0]->ofproto;
>>         struct rule *rule, *next;
>> +        uint8_t prev_table = UINT8_MAX;
>>         size_t i;
>> 
>>         for (i = 0, next = rules->rules[0];
>>              rule = next, next = (++i < rules->n) ? rules->rules[i] : NULL,
>> -                 rule; ) {
>> +                 rule; prev_table = rule->table_id) {
>>             struct classifier *cls = &ofproto->tables[rule->table_id].cls;
>>             uint8_t next_table = next ? next->table_id : UINT8_MAX;
>> 
>> @@ -4840,7 +4845,8 @@ delete_flows__(const struct rule_collection *rules,
>>                              req ? req->ofconn : NULL,
>>                              req ? req->request->xid : 0, NULL);
>> 
>> -            if (next_table == rule->table_id) {
>> +            /* Defer once for each new table. */
>> +            if (rule->table_id != prev_table) {
>>                 classifier_defer(cls);
>>             }
>>             if (!classifier_remove(cls, &rule->cr)) {
>> @@ -4873,7 +4879,7 @@ delete_flows_start_loose(struct ofproto *ofproto,
>>     struct rule_criteria criteria;
>>     enum ofperr error;
>> 
>> -    rule_criteria_init(&criteria, fm->table_id, &fm->match, 0,
>> +    rule_criteria_init(&criteria, fm->table_id, &fm->match, 0, 
>> CLS_MAX_VERSION,
>>                        fm->cookie, fm->cookie_mask,
>>                        fm->out_port, fm->out_group);
>>     rule_criteria_require_rw(&criteria,
>> @@ -4885,7 +4891,10 @@ delete_flows_start_loose(struct ofproto *ofproto,
>>         for (size_t i = 0; i < rules->n; i++) {
>>             struct rule *rule = rules->rules[i];
>> 
>> -            CONST_CAST(struct cls_rule *, &rule->cr)->to_be_removed = true;
>> +            cls_rule_make_invisible_in_version(CONST_CAST(struct cls_rule *,
>> +                                                          &rule->cr),
>> +                                               CLS_MIN_VERSION,
>> +                                               CLS_MIN_VERSION);
>>         }
>>     }
>> 
>> @@ -4897,9 +4906,7 @@ delete_flows_revert(struct rule_collection *rules)
>>     OVS_REQUIRES(ofproto_mutex)
>> {
>>     for (size_t i = 0; i < rules->n; i++) {
>> -        struct rule *rule = rules->rules[i];
>> -
>> -        CONST_CAST(struct cls_rule *, &rule->cr)->to_be_removed = false;
>> +        cls_rule_restore_visibility(&rules->rules[i]->cr);
>>     }
>>     rule_collection_destroy(rules);
>> }
>> @@ -4925,7 +4932,7 @@ delete_flow_start_strict(struct ofproto *ofproto,
>>     enum ofperr error;
>> 
>>     rule_criteria_init(&criteria, fm->table_id, &fm->match, fm->priority,
>> -                       fm->cookie, fm->cookie_mask,
>> +                       CLS_MAX_VERSION, fm->cookie, fm->cookie_mask,
>>                        fm->out_port, fm->out_group);
>>     rule_criteria_require_rw(&criteria,
>>                              (fm->flags & OFPUTIL_FF_NO_READONLY) != 0);
>> @@ -4936,7 +4943,10 @@ delete_flow_start_strict(struct ofproto *ofproto,
>>         for (size_t i = 0; i < rules->n; i++) {
>>             struct rule *rule = rules->rules[i];
>> 
>> -            CONST_CAST(struct cls_rule *, &rule->cr)->to_be_removed = true;
>> +            cls_rule_make_invisible_in_version(CONST_CAST(struct cls_rule *,
>> +                                                          &rule->cr),
>> +                                               CLS_MIN_VERSION,
>> +                                               CLS_MIN_VERSION);
>>         }
>>     }
>> 
>> @@ -5340,7 +5350,7 @@ ofproto_collect_ofmonitor_refresh_rules(const struct 
>> ofmonitor *m,
>>     const struct oftable *table;
>>     struct cls_rule target;
>> 
>> -    cls_rule_init_from_minimatch(&target, &m->match, 0);
>> +    cls_rule_init_from_minimatch(&target, &m->match, 0, CLS_MAX_VERSION);
>>     FOR_EACH_MATCHING_TABLE (table, m->table_id, ofproto) {
>>         struct rule *rule;
>> 
>> @@ -5877,8 +5887,8 @@ group_get_ref_count(struct ofgroup *group)
>>     uint32_t count;
>> 
>>     match_init_catchall(&match);
>> -    rule_criteria_init(&criteria, 0xff, &match, 0, htonll(0), htonll(0),
>> -                       OFPP_ANY, group->group_id);
>> +    rule_criteria_init(&criteria, 0xff, &match, 0, CLS_MAX_VERSION, 
>> htonll(0),
>> +                       htonll(0), OFPP_ANY, group->group_id);
>>     ovs_mutex_lock(&ofproto_mutex);
>>     error = collect_rules_loose(ofproto, &criteria, &rules);
>>     ovs_mutex_unlock(&ofproto_mutex);
>> @@ -6518,26 +6528,6 @@ do_bundle_flow_mod_finish(struct ofproto *ofproto, 
>> struct ofputil_flow_mod *fm,
>>     }
>> }
>> 
>> -/* Commit phases (all while locking ofproto_mutex):
>> - *
>> - * 1. Gather resources - do not send any events or notifications.
>> - *
>> - * add: Check conflicts, check for a displaced flow. If no displaced flow
>> - *      exists, add the new flow, but mark it as "invisible".
>> - * mod: Collect affected flows, Do not modify yet.
>> - * del: Collect affected flows, Do not delete yet.
>> - *
>> - * 2a. Fail if any errors are found.  After this point no errors are 
>> possible.
>> - * No visible changes were made, so rollback is minimal (remove added 
>> invisible
>> - * flows, revert 'to_be_removed' status of flows).
>> - *
>> - * 2b. Commit the changes
>> - *
>> - * add: if have displaced flow, modify it, otherwise mark the new flow as
>> - *      "visible".
>> - * mod: Modify the collected flows.
>> - * del: Delete the collected flows.
>> - */
>> static enum ofperr
>> do_bundle_commit(struct ofconn *ofconn, uint32_t id, uint16_t flags)
>> {
>> @@ -7434,7 +7424,7 @@ ofproto_get_vlan_usage(struct ofproto *ofproto, 
>> unsigned long int *vlan_bitmap)
>> 
>>     match_init_catchall(&match);
>>     match_set_vlan_vid_masked(&match, htons(VLAN_CFI), htons(VLAN_CFI));
>> -    cls_rule_init(&target, &match, 0);
>> +    cls_rule_init(&target, &match, 0, CLS_MAX_VERSION);
>> 
>>     free(ofproto->vlan_bitmap);
>>     ofproto->vlan_bitmap = bitmap_allocate(4096);
>> diff --git a/tests/test-classifier.c b/tests/test-classifier.c
>> index a615438..24fc5eb 100644
>> --- a/tests/test-classifier.c
>> +++ b/tests/test-classifier.c
>> @@ -433,7 +433,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, &flow, &wc);
>> +        cr0 = classifier_lookup(cls, CLS_MAX_VERSION, &flow, &wc);
>>         cr1 = tcls_lookup(tcls, &flow);
>>         assert((cr0 == NULL) == (cr1 == NULL));
>>         if (cr0 != NULL) {
>> @@ -443,7 +443,7 @@ compare_classifiers(struct classifier *cls, struct tcls 
>> *tcls)
>>             assert(cls_rule_equal(cr0, cr1));
>>             assert(tr0->aux == tr1->aux);
>>         }
>> -        cr2 = classifier_lookup(cls, &flow, NULL);
>> +        cr2 = classifier_lookup(cls, CLS_MAX_VERSION, &flow, NULL);
>>         assert(cr2 == cr0);
>>     }
>> }
>> @@ -635,7 +635,7 @@ 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);
>> +                  : INT_MAX, CLS_MIN_VERSION);
>>     return rule;
>> }
>> 
>> diff --git a/utilities/ovs-ofctl.c b/utilities/ovs-ofctl.c
>> index be0f149..72ec758 100644
>> --- a/utilities/ovs-ofctl.c
>> +++ b/utilities/ovs-ofctl.c
>> @@ -2473,7 +2473,7 @@ fte_insert(struct classifier *cls, const struct match 
>> *match,
>>     struct fte *old, *fte;
>> 
>>     fte = xzalloc(sizeof *fte);
>> -    cls_rule_init(&fte->rule, match, priority);
>> +    cls_rule_init(&fte->rule, match, priority, CLS_MIN_VERSION);
>>     fte->versions[index] = version;
>> 
>>     old = fte_from_cls_rule(classifier_replace(cls, &fte->rule, NULL, 0));
>> @@ -2483,7 +2483,6 @@ fte_insert(struct classifier *cls, const struct match 
>> *match,
>> 
>>         ovsrcu_postpone(fte_free, old);
>>     }
>> -    cls_rule_make_visible(&fte->rule);
>> }
>> 
>> /* Reads the flows in 'filename' as flow table entries in 'cls' for the 
>> version
>> --
>> 1.7.10.4
>> 
>> _______________________________________________
>> dev mailing list
>> dev@openvswitch.org <mailto:dev@openvswitch.org>
>> http://openvswitch.org/mailman/listinfo/dev 
>> <http://openvswitch.org/mailman/listinfo/dev>
_______________________________________________
dev mailing list
dev@openvswitch.org
http://openvswitch.org/mailman/listinfo/dev

Reply via email to