OVS Classifier - Classifier

Keywords: C++ less REST network

Classifier purpose

classifier is very important in ovs for streaming classification and performing appropriate actions.Belongs to match in match+action.

Classifiers are used in three places in ovs:

  • miss message processing (user-state slow path)
/* The returned rule (if any) is valid at least until the next RCU quiescent
 * period.  If the rule needs to stay around longer, the caller should take
 * a reference.
 *
 * 'flow' is non-const to allow for temporary modifications during the lookup.
 * Any changes are restored before returning. 
 * Allow users to temporarily change the contents of a flow
 */
static struct rule_dpif *
rule_dpif_lookup_in_table(struct ofproto_dpif *ofproto, ovs_version_t version,
                          uint8_t table_id, struct flow *flow,
                          struct flow_wildcards *wc)
{
    struct classifier *cls = &ofproto->up.tables[table_id].cls;
    return rule_dpif_cast(rule_from_cls_rule(classifier_lookup(cls, version,
                                                               flow, wc)));
}
  • underlay routing table lookup
/* ovs Routing lookup to get the IP address of the gateway and the source IP address of the outgoing interface */
bool
ovs_router_lookup(const struct in6_addr *ip6_dst, char output_bridge[],
                  struct in6_addr *src, struct in6_addr *gw)
{
    const struct cls_rule *cr;
    struct flow flow = {.ipv6_dst = *ip6_dst};

    cr = classifier_lookup(&cls, OVS_VERSION_MAX, &flow, NULL);
    if (cr) {
        struct ovs_router_entry *p = ovs_router_entry_cast(cr);

        ovs_strlcpy(output_bridge, p->output_bridge, IFNAMSIZ);
        *gw = p->gw;
        if (src) {
            *src = p->src_addr;
        }
        return true;
    }
    return ovs_router_lookup_fallback(ip6_dst, output_bridge, src, gw);
}
  • tunnel terminate table lookup
/* 'flow' is non-const to allow for temporary modifications during the lookup.
 * Any changes are restored before returning. 
 * flow The parameter allows temporary changes to some values, but needs to be restored before returning.
 */
odp_port_t
tnl_port_map_lookup(struct flow *flow, struct flow_wildcards *wc)
{
    //Perform classifier rule lookup
    const struct cls_rule *cr = classifier_lookup(&cls, OVS_VERSION_MAX, flow,
                                                  wc);

    return (cr) ? tnl_port_cast(cr)->portno : ODPP_NONE;
}

The core function of the classifier is the classifier_lookup function, which has four parameters:

  • Classifier for cls:table
  • Version: The current version of the rule that meets the requirements, that is, the added version of the rule needs to be smaller than the version, and the deleted version needs to be larger than the version, that is, the rule is visible in that version.
  • flow: Requires the extraction value of the matching message, which can be modified by neighbors halfway through, but ultimately keeps the message as it is and will be used as the value of the megaflow in the future.
  • Wc: wildcard character, which is wildcard-owned at the beginning of message lookup. Use the same WC throughout message matching, hit a rule and unwildcard the mask corresponding to that rule, indicating that the message needs to match these domains. When the message is transferred to another table through goto and resubmit actions, the original last t will still be usedWC after able processing proceeds, and masks between multiple rules use or relationships to process wc.WC and flow together form the value and mask of a rule in a fast-turntable.WC is important to minimize mismatches.For use scenarios where megaflow does not need to be generated, this value can be NULL, so it is NULL in ovs_router_lookup.

Data Structure Diagram

data structure

/* A flow classifier. */
/* flow classifier */
struct classifier {
    int n_rules;                    /* Total number of rules. Total number of rules */
    uint8_t n_flow_segments;        /* The number of segments of a segment matcher, generally four segments: metadata, l2,l3,l4, but this value can only be 3, and the last segment is handled separately */
    //64-byte offset to store the starting address of each segment
    uint8_t flow_segments[CLS_MAX_INDICES]; /* Flow segment boundaries to use
                                             * for staged lookup. The end boundary of a segment matcher, which has four segments and three boundaries 
                                             * |---Paragraph 1----|--- - Paragraph 2----|----- Paragraph 3--|- Paragraph 4-----|
                                             *           Boundary 1 Boundary 2 Boundary 3   
                                             */
    struct cmap subtables_map;      /* Contains "struct cls_subtable"s. Subtable hash table, under classifier, differs by flow mask
                                     * Divide into different subtables, each with the same rule mask, a stream table with multiple subtables
                                     */
    struct pvector subtables;       /* Array of subtables, prioritizing the installed tables so that lower-priority tables can be skipped */
    struct cmap partitions;         /* Contains "struct cls_partition"s. */
    struct cls_trie tries[CLS_MAX_TRIES]; /* Prefix tries. Prefix tree, used to filter some rules */
    unsigned int n_tries;           /* Number of prefix trees */
    bool publish;                   /* Make changes visible to lookups? Indicates whether the query is ready */
};

Segment boundary

/* U64 indices for segmented flow classification. */
/* 64bit Segment flow classifier */
const uint8_t flow_segment_u64s[4] = {
    FLOW_SEGMENT_1_ENDS_AT / sizeof(uint64_t),/* Metadata Matching End Address */
    FLOW_SEGMENT_2_ENDS_AT / sizeof(uint64_t),/* Link Layer Matching End Address */
    FLOW_SEGMENT_3_ENDS_AT / sizeof(uint64_t),/* Network Layer Matching End Address */
    FLOW_U64S                                 /* Transport Layer Matching End Address */
};

classifier process

  1. From the first subtable of cls->subtables, loop through each of the required subtables, calling the function find_match_wc to process the subtable.

The function find_match_wc does the following:

trie-filter the flow in the first three segments of the sub-table and start the next sub-table if it does not meet the hit criteria.

Calculate the hash value of flow in the first three subsections of the subtable->indices[i] to see if there is a corresponding hash value. The first three subsections are processed by hash values

Filter.

After the first two have met the requirements, trie-filter the last segment and start the next sub-table if the hit criteria are not met.

Hash the last segment and call the function find_match in the subtable with the hash value to match exactly.

  1. After all the subtables have been traversed, the rules hit in the matching are matched to find the rule with the highest priority.

correlation function

classifier_init

Initialize a classifier.

/* Initializes 'cls' as a classifier that initially contains no classification
 * rules. Initialize a classifier
 * The parameter flow_segments is an array of segment boundaries, flow_segment_u64s */
void
classifier_init(struct classifier *cls, const uint8_t *flow_segments)
{
    cls->n_rules = 0;/* Number of rules is 0 */
    cmap_init(&cls->subtables_map);/* Initialize subtable map */
    pvector_init(&cls->subtables);//Initialize Subtable Array
    cls->n_flow_segments = 0;/* Initialization Segment 0 */
    if (flow_segments) {/* If a segment matcher exists, then the end address of the initialization segment matcher, where only three segments are initialized */
        while (cls->n_flow_segments < CLS_MAX_INDICES
               && *flow_segments < FLOW_U64S) {
            cls->flow_segments[cls->n_flow_segments++] = *flow_segments++;
        }
    }
    cls->n_tries = 0;/* The number of default prefix tree matchers is 0 */
    for (int i = 0; i < CLS_MAX_TRIES; i++) {/* Initialize prefix tree */
        trie_init(cls, i, NULL);
    }
    cls->publish = true;
}

classifier_insert

Insert a rule into the classifier.

/* Inserts 'rule' into 'cls'.  Until 'rule' is removed from 'cls', the caller
 * must not modify or free it.
 *
 * 'cls' must not contain an identical rule (including wildcards, values of
 * fixed fields, and priority).  Use classifier_find_rule_exactly() to find
 * such a rule. 
 * Insert a rule into the classifier until the rule is deleted from the classifier, and the caller cannot modify and release the rule*/
void
classifier_insert(struct classifier *cls,/* classifier */ 
                        const struct cls_rule *rule,/* rule */
                        ovs_version_t version, /* Rule Version */
                        const struct cls_conjunction conj[],
                        size_t n_conj)
{
    const struct cls_rule *displaced_rule
        = classifier_replace(cls, rule, version, conj, n_conj);
    ovs_assert(!displaced_rule);
}

/* 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) 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
 * rule, even rules that cannot have any effect because the new rule matches a
 * superset of their flows and has higher priority.
 *
 * If it does not contain the same rule, it returns null.
 */
const struct cls_rule *
classifier_replace(struct classifier *cls, const struct cls_rule *rule,
                   ovs_version_t version,
                   const struct cls_conjunction *conjs, size_t n_conjs)
{
    struct cls_match *new;
    struct cls_subtable *subtable;
    uint32_t ihash[CLS_MAX_INDICES];
    struct cls_match *head;
    unsigned int mask_offset;
    size_t n_rules = 0;
    uint32_t basis;
    uint32_t hash;
    unsigned int i;

    /* 'new' is initially invisible to lookups. */
    new = cls_match_alloc(rule, version, conjs, n_conjs);/* Assign a new matcher */
    ovsrcu_set(&CONST_CAST(struct cls_rule *, rule)->cls_match, new);/* Set Rule Matcher */

    /* Find Subtables Based on Rule Mask */
    subtable = find_subtable(cls, rule->match.mask);/* Find Subtable from Mask */
    if (!subtable) {/* Subtable not found, create a new subtable, insert rules into the new subtable  */
        subtable = insert_subtable(cls, rule->match.mask);
    }

    /* Compute hashes in segments. */
    /* Calculate hash values within segments */
    basis = 0;
    mask_offset = 0;
    for (i = 0; i < subtable->n_indices; i++) {/*Traverse through each segment, calculating hash values for each segment, which can be used to filter */
        /* hash each segment of a rule */
        ihash[i] = minimatch_hash_range(&rule->match, subtable->index_maps[i],
                                        &mask_offset, &basis);
    }
    /* hash the last paragraph */
    hash = minimatch_hash_range(&rule->match, subtable->index_maps[i],
                                &mask_offset, &basis);

    /* Finds rules in a subtable based on hash values, with the hash value of the last segment as the key of the hash bucket */
    head = find_equal(subtable, rule->match.flow, hash);
    if (!head) {/* Not found, add rule */
        /* Add rule to tries.
         *
         * Concurrent readers might miss seeing the rule until this update,
         * which might require being fixed up by revalidation later. */
        for (i = 0; i < cls->n_tries; i++) {/* Traverse each prefix tree of the classifier */
            if (subtable->trie_plen[i]) {/* If the corresponding mask length of the subtable exists, insert the prefix tree */
                trie_insert(&cls->tries[i], rule, subtable->trie_plen[i]);/* This is the prefix length */
            }
        }

        /* Add rule to ports trie. */
        /* Add Rule to Port Tree, tree insert only transport layer source ports */
        if (subtable->ports_mask_len) {
            /* We mask the value to be inserted to always have the wildcarded
             * bits in known (zero) state, so we can include them in comparison
             * and they will always match (== their original value does not
             * matter). */
            ovs_be32 masked_ports = minimatch_get_ports(&rule->match);

            trie_insert_prefix(&subtable->ports_trie, &masked_ports,
                               subtable->ports_mask_len);
        }

        /* Add new node to segment indices. Insert hash value into subtable segment hash filter table */
        for (i = 0; i < subtable->n_indices; i++) {
            ccmap_inc(&subtable->indices[i], ihash[i]);
        }
        n_rules = cmap_insert(&subtable->rules, &new->cmap_node, hash);
    } else {   /* Equal rules exist in the classifier already. */
        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, prev, head) {
            if (rule->priority > iter->priority
                || (rule->priority == iter->priority
                    && !cls_match_is_eventually_invisible(iter))) {
                break;
            }
        }

        /* Replace 'iter' with 'new' or insert 'new' between 'prev' and
         * 'iter'. */
        if (iter) {
            struct cls_rule *old;

            if (rule->priority == iter->priority) {
                cls_match_replace(prev, iter, new);
                old = CONST_CAST(struct cls_rule *, iter->cls_rule);
            } else {
                cls_match_insert(prev, iter, new);
                old = NULL;
            }

            /* Replace the existing head in data structures, if rule is the new
             * head. */
            if (iter == head) {
                cmap_replace(&subtable->rules, &head->cmap_node,
                             &new->cmap_node, hash);
            }

            if (old) {
                struct cls_conjunction_set *conj_set;

                conj_set = ovsrcu_get_protected(struct cls_conjunction_set *,
                                                &iter->conj_set);
                if (conj_set) {
                    ovsrcu_postpone(free, conj_set);
                }

                ovsrcu_set(&old->cls_match, NULL); /* Marks old rule as removed
                                                    * from the classifier. */
                ovsrcu_postpone(cls_match_free_cb, iter);

                /* No change in subtable's max priority or max count. */

                /* Make 'new' visible to lookups in the appropriate version. */
                cls_match_set_remove_version(new, OVS_VERSION_NOT_REMOVED);

                /* Make rule visible to iterators (immediately). */
                rculist_replace(CONST_CAST(struct rculist *, &rule->node),
                                &old->node);

                /* Return displaced rule.  Caller is responsible for keeping it
                 * around until all threads quiesce. */
                return old;
            }
        } else {
            /* 'new' is new node after 'prev' */
            cls_match_insert(prev, iter, new);
        }
    }

    /* Make 'new' visible to lookups in the appropriate version. */
    cls_match_set_remove_version(new, OVS_VERSION_NOT_REMOVED);

    /* Make rule visible to iterators (immediately). */
    rculist_push_back(&subtable->rules_list,
                      CONST_CAST(struct rculist *, &rule->node));

    /* Rule was added, not replaced.  Update 'subtable's 'max_priority' and
     * 'max_count', if necessary.
     *
     * The rule was already inserted, but concurrent readers may not see the
     * rule yet as the subtables vector is not updated yet.  This will have to
     * be fixed by revalidation later. */
    if (n_rules == 1) {
        subtable->max_priority = rule->priority;
        subtable->max_count = 1;
        pvector_insert(&cls->subtables, subtable, rule->priority);
    } else if (rule->priority == subtable->max_priority) {
        ++subtable->max_count;
    } else if (rule->priority > subtable->max_priority) {
        subtable->max_priority = rule->priority;
        subtable->max_count = 1;
        pvector_change_priority(&cls->subtables, subtable, rule->priority);
    }

    /* Nothing was replaced. */
    cls->n_rules++;

    if (cls->publish) {/* Update Subtable */
        pvector_publish(&cls->subtables);
    }

    return NULL;
}

classifier_lookup

/* 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
 * earlier, 'wc' should have been initialized (e.g., by
 * flow_wildcards_init_catchall()).
 *
 * 'flow' is non-const to allow for temporary modifications during the lookup.
 * Any changes are restored before returning.
 * Find a rule from the cls classifier that matches the highest priority of the flow.
 * If there is no rule matching the flow in the classifier cls, it returns null, and if more than one rule matches the flow, it returns a maximum
 * Priority.
 * flow can be modified temporarily
 */
const struct cls_rule *
classifier_lookup(const struct classifier *cls, /* classifier */ovs_version_t version,/* Edition */
                  struct flow *flow,/* flow to find */ struct flow_wildcards *wc/* wildcard */)
{
    return classifier_lookup__(cls, version, flow, wc, true);
}

/* Like classifier_lookup(), except that support for conjunctive matches can be
 * configured with 'allow_conjunctive_matches'.  That feature is not exposed
 * externally because turning off conjunctive matches is only useful to avoid
 * recursion within this function itself.
 *
 * '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, ovs_version_t version,/* Classifier and its version number */
                    struct flow *flow, struct flow_wildcards *wc,/* Matching Stream and Wildcard */
                    bool allow_conjunctive_matches)/* Allow conjunctive matching */
{
    struct trie_ctx trie_ctx[CLS_MAX_TRIES];
    const struct cls_match *match;
    /* Highest-priority flow in 'cls' that certainly matches 'flow'. */
    /* flow pointing to highest priority in cls */
    const struct cls_match *hard = NULL;
    int hard_pri = INT_MIN;     /* hard ? hard->priority : INT_MIN. */

    /* Highest-priority conjunctive flows in 'cls' matching 'flow'.  Since
     * these are (components of) conjunctive flows, we can only know whether
     * the full conjunctive flow matches after seeing multiple of them.  Thus,
     * we refer to these as "soft matches". We only have to look at all the association matches
     * We know it's a perfect match, so we call it a soft match
     * The highest priority Association matching, in the classifier, matches the flow with the highest priority Association flows
     * */
    struct cls_conjunction_set *soft_stub[64];/* Associate matching sets, our default is 64 sets */
    struct cls_conjunction_set **soft = soft_stub;/* Soft Match Set */
    size_t n_soft = 0, allocated_soft = ARRAY_SIZE(soft_stub);/* 64 Elements */
    int soft_pri = INT_MIN;    /* n_soft ? MAX(soft[*]->priority) : INT_MIN. */

    /* Synchronize for cls->n_tries and subtable->trie_plen.  They can change
     * when table configuration changes, which happens typically only on
     * startup. */
    atomic_thread_fence(memory_order_acquire);

    /* Initialize trie contexts for find_match_wc(). */
    /* Initialize trie context for wc lookup */
    for (int i = 0; i < cls->n_tries; i++) {
        trie_ctx_init(&trie_ctx[i], &cls->tries[i]);
    }

    /* Main loop. */
    /* The main loop, which traverses all the subtables, shows that each loop processes one subtable and returns one time because the subtable masks are the same
    ** A common match for multiple rules that are the same as the mask area, which is a single-chain list sorted by priority.
    */
    struct cls_subtable *subtable;
    PVECTOR_FOR_EACH_PRIORITY (subtable, hard_pri + 1, 2, sizeof *subtable,
                               &cls->subtables) {
        struct cls_conjunction_set *conj_set;/* Associator Collection */

        /* Skip subtables with no match, or where the match is lower-priority
         * than some certain match we've already found.
         * Skip subtables that do not have the same match, or match a lower priority than previous matches
         */
        match = find_match_wc(subtable, version, flow, trie_ctx, cls->n_tries,
                              wc);
        if (!match || match->priority <= hard_pri) {/* Filter out rules with lower or equal precedence than those already matched */
            continue;
        }

        /* Get Association Matching of Rules */
        conj_set = ovsrcu_get(struct cls_conjunction_set *, &match->conj_set);
        if (!conj_set) {/* The match has no associated match and is directly a hard match */
            /* 'match' isn't part of a conjunctive match.  It's the best
             * certain match we've got so far, since we know that it's
             * higher-priority than hard_pri.
             *
             * (There might be a higher-priority conjunctive match.  We can't
             * tell yet.) */
            hard = match;
            hard_pri = hard->priority;/* Hard match with highest priority currently hit */
        } else if (allow_conjunctive_matches) {/* There are association matches and association matches are allowed */
            /* 'match' is part of a conjunctive match.  Add it to the list. */
            /* The match is part of the association match and is added to the list of chains */
            if (OVS_UNLIKELY(n_soft >= allocated_soft)) {/* More associations than we can accommodate and need to be reassigned */
                struct cls_conjunction_set **old_soft = soft;/* Save old matches first */

                allocated_soft *= 2;/* Expand exponentially */
                soft = xmalloc(allocated_soft * sizeof *soft);/* Assign Soft Match Array */
                memcpy(soft, old_soft, n_soft * sizeof *soft);/* Copy in the original content */
                if (old_soft != soft_stub) {/* Release previous content if soft match is replaced */
                    free(old_soft);
                }
            }
            /* Add new soft matches */
            soft[n_soft++] = conj_set;

            /* Keep track of the highest-priority soft match. */
            /* If the old precedence is less than the current rule, update the priority */
            if (soft_pri < match->priority) {
                soft_pri = match->priority;
            }
        }
    }

    /* In the common case, at this point we have no soft matches and we can
     * return immediately.  (We do the same thing if we have potential soft
     * matches but none of them are higher-priority than our hard match.) 
     * Normally: by the time we get here, we don't have a soft match and we'll be back right away.
     * If we have a potential soft match, we will do the same thing*/
    if (hard_pri >= soft_pri) {/* If the priority of hard matching is greater than or equal to soft matching, we will choose the hard matching rule and soft matching needs to be freed if memory is allocated */
        if (soft != soft_stub) {
            free(soft);/* Release our allocated memory */
        }
        return hard ? hard->cls_rule : NULL;/* Return hit rule */
    }

    /* At this point, we have some soft matches.  We might also have a hard
     * match; if so, its priority is lower than the highest-priority soft
     * match. 
     * To get here, we have multiple soft matches, and we need to turn each soft matching Association match into a new one
     * Full Association Matching */

    /* Soft match loop.
     * The outer loop iterates through each priority and selects the highest priority for this loop through the first for statement.
     * Check whether soft matches are real matches. Check if the soft match is a true match, that is, if every dimension of the association rule is hit, the soft match is upgraded to the hard match */
    for (;;) {
        /* Delete soft matches that are null.  This only happens in second and
         * subsequent iterations of the soft match loop, when we drop back from
         * a high-priority soft match to a lower-priority one.
         *
         * Also, delete soft matches whose priority is less than or equal to
         * the hard match's priority.  In the first iteration of the soft
         * match, these can be in 'soft' because the earlier main loop found
         * the soft match before the hard match.  In second and later iteration
         * of the soft match loop, these can be in 'soft' because we dropped
         * back from a high-priority soft match to a lower-priority soft match.
         *
         * It is tempting to delete soft matches that cannot be satisfied
         * because there are fewer soft matches than required to satisfy any of
         * their conjunctions, but we cannot do that because there might be
         * lower priority soft or hard matches with otherwise identical
         * matches.  (We could special case those here, but there's no
         * need--we'll do so at the bottom of the soft match loop anyway and
         * this duplicates less code.)
         *
         * It's also tempting to break out of the soft match loop if 'n_soft ==
         * 1' but that would also miss lower-priority hard matches.  We could
         * special case that also but again there's no need. */
        /* Filter out all soft matches that have lower priority than hard matches and are empty. */
        for (int i = 0; i < n_soft; ) {
            if (!soft[i] || soft[i]->priority <= hard_pri) {
                soft[i] = soft[--n_soft];
            } else {
                i++;
            }
        }
        /* Jump out if all soft matches fail */
        if (!n_soft) {
            break;
        }

        /* Find the highest priority among the soft matches.  (We know this
         * must be higher than the hard match's priority; otherwise we would
         * have deleted all of the soft matches in the previous loop.)  Count
         * the number of soft matches that have that priority. 
         * Find the highest priority soft match from all soft matches, which must be higher than hard matches.Also calculate the number of soft matches for this priority
         */
        soft_pri = INT_MIN;
        int n_soft_pri = 0;/* Get the highest soft match priority and its number */
        for (int i = 0; i < n_soft; i++) {/* Traverse through remaining compliant soft matches */
            if (soft[i]->priority > soft_pri) {
                soft_pri = soft[i]->priority;/* Update Soft Matching Has Priority */
                n_soft_pri = 1;
            } else if (soft[i]->priority == soft_pri) {
                n_soft_pri++;/* Calculate the number of soft matches with the same highest priority */
            }
        }
        ovs_assert(soft_pri > hard_pri);

        /* Look for a real match among the highest-priority soft matches.
         *
         * It's unusual to have many conjunctive matches, so we use stubs to
         * avoid calling malloc() in the common case.  An hmap has a built-in
         * stub for up to 2 hmap_nodes; possibly, we would benefit a variant
         * with a bigger stub. */
        struct conjunctive_match cm_stubs[16];
        struct hmap matches;

        hmap_init(&matches);/* Initial staging hash, which is used by all soft matches of the same priority, is cleared in each loop.This allows you to combine
                             * Soft matches in multiple subtables form hard matches.
                             */
        for (int i = 0; i < n_soft; i++) {/* Walk through each soft match and only process the priority we need, the highest priority selected this time 
                                           * A soft match of this priority is processed in a loop at a time.Set a dimension bit.
                                           */
            uint32_t id;

            if (soft[i]->priority == soft_pri/* If this is the priority we need */
                && find_conjunctive_match(soft[i], n_soft_pri, &matches,/* Find Association matches based on Association id */
                                          cm_stubs, ARRAY_SIZE(cm_stubs),
                                          &id)) {/* Filter each association match of this soft match into the matches hash table */
                uint32_t saved_conj_id = flow->conj_id;/* Associated action id of record stream */
                const struct cls_rule *rule;

                flow->conj_id = id; /* Final Association id, second step search based on Association ID */
                rule = classifier_lookup__(cls, version, flow, wc, false);/* Flow table lookup does not allow processing of association actions, as previously handled */
                flow->conj_id = saved_conj_id;

                if (rule) {/* Find Rules */
                    free_conjunctive_matches(&matches,
                                             cm_stubs, ARRAY_SIZE(cm_stubs));
                    if (soft != soft_stub) {
                        free(soft);
                    }
                    return rule;
                }
            }
        }
        free_conjunctive_matches(&matches, cm_stubs, ARRAY_SIZE(cm_stubs));

        /* There's no real match among the highest-priority soft matches.
         * However, if any of those soft matches has a lower-priority but
         * otherwise identical flow match, then we need to consider those for
         * soft or hard matches.
         *
         * The next iteration of the soft match loop will delete any null
         * pointers we put into 'soft' (and some others too). 
         * Go here and say that this priority does not form a hard match
         */
        for (int i = 0; i < n_soft; i++) {
            if (soft[i]->priority != soft_pri) {
                continue;
            }

            /* Find next-lower-priority flow with identical flow match. */
            /* Find a match for the next priority from the same matching criteria, and if the match is an association match,
            ** Prepare for the next round.Otherwise, as a hard match.
            */
            match = next_visible_rule_in_list(soft[i]->match, version);
            if (match) {
                soft[i] = ovsrcu_get(struct cls_conjunction_set *,
                                     &match->conj_set);
                if (!soft[i]) {//This rule is not an association match, and as a hard match it is preferred to the original hard match.
                    /* The flow is a hard match; don't treat as a soft
                     * match. */
                    if (match->priority > hard_pri) {
                        hard = match;
                        hard_pri = hard->priority;
                    }
                }
            } else {
                /* No such lower-priority flow (probably the common case). */
                /* Is empty, needs to be cleared, will make count modification in next cycle */
                soft[i] = NULL;
            }
        }
    }

    /* Release if a newly allocated soft match saves a control block */
    if (soft != soft_stub) {
        free(soft);
    }
    return hard ? hard->cls_rule : NULL;
}

summary

After a long analysis, we have roughly cleared up the implementation mechanism of ovs classifier, but there are some more important details that we do not want to understand:

  1. Generally, caches are matched precisely, and mask matching can easily lead to mismatches.If ovs choose megaflow as the mask matcher, is there any possibility of mismatching?
  2. The wc parameter of the classifier_lookup function holds the masks or results of all hit rules.The mask contains not only the rules of the ultimate highest priority in each table, but also the masks of other priorities hit in each of the subtables in each table.That is, wc is not only a mask or a mask for the rules that ultimately execute the action, but also contains some masks for rules that hit but do not return the action to be executed at low priority.What does this mean?
  3. What does the following code snippet do in function check_tries?
 /* Possible to skip the rest of the subtable if subtable's
             * prefix on the field is not included in the lookup result. 
             * Check to see if this sub-table hits, and if hits match the next filter tree.otherwise
             */
            if (!be_get_bit_at(&ctx->match_plens.be32, field_plen[j] - 1)) {
                /* We want the trie lookup to never result in unwildcarding
                 * any bits that would not be unwildcarded otherwise.
                 * Since the trie is shared by the whole classifier, it is
                 * possible that the 'maskbits' contain bits that are
                 * irrelevant for the partition relevant for the current
                 * packet.  Hence the checks below. 
                 * Trie Is shared by the entire classifier.It is possible that maskbits may contain parts that are not relevant to this message.
                 * Therefore, the following checks are required.
                 */

                /* Check that the trie result will not unwildcard more bits
                 * than this subtable would otherwise. 
                 * This tree is the longest*/
                if (ctx->maskbits <= field_plen[j]) {//No Full Match
                    /* Unwildcard the bits and skip the rest. */
                    /* Set non-wildcard bit s to skip other sub-tables */
                    mask_set_prefix_bits(wc, ctx->be32ofs, ctx->maskbits);
                    /* Note: Prerequisite already unwildcarded, as the only
                     * prerequisite of the supported trie lookup fields is
                     * the ethertype, which is always unwildcarded. */
                    return true;
                }
                /* Can skip if the field is already unwildcarded. */
                if (mask_prefix_bits_set(wc, ctx->be32ofs, ctx->maskbits)) {
                    return true;
                }
            }
  1. The following code snippet in the function find_match_wc failed to understand what it does?
if (!rule && subtable->ports_mask_len) {/* Not found, and there is a tdports Engine tree */
        /* The final stage had ports, but there was no match.  Instead of
         * unwildcarding all the ports bits, use the ports trie to figure out a
         * smaller set of bits to unwildcard. 
         * The last stage is the transport layer information, but this sub-table has no rule hits.Replacement rules need to match the whole
         * Port domain, a minimum mask domain calculated by trie, can minimize mismatches.
         */
        unsigned int mbits;
        ovs_be32 value, plens, mask;
        /* Get the mask for the four-tier port */
        mask = MINIFLOW_GET_BE32(&subtable->mask.masks, tp_src);/* Get four-layer source port mask */
        /* And */
        value = ((OVS_FORCE ovs_be32 *)flow)[TP_PORTS_OFS32] & mask;/* Mask and manipulate with streams to get the values we need to find */
        /* Looking for the port tree returns mbits indicating the longest location value can hit in a trie, and if it does not, participates in a match
        ** The number of bits, that is, the value under the mbits mask, must not be hit.Used to reduce mismatches.
        */
        mbits = trie_lookup_value(&subtable->ports_trie, &value, &plens, 32);

        ((OVS_FORCE ovs_be32 *)&wc->masks)[TP_PORTS_OFS32] |=
            mask & be32_prefix_mask(mbits);

        goto no_match;
    }

Limited ability, I have not yet understood the above issues, I hope that friends who are familiar with this can correct them.

Posted by Namadoor on Sun, 22 Sep 2019 01:09:08 -0700