2
0
mirror of https://github.com/openvswitch/ovs synced 2025-08-31 14:25:26 +00:00

Classifier: Staged subtable matching.

Subtable lookup is performed in ranges defined for struct flow,
starting from metadata (registers, in_port, etc.), then L2 header, L3,
and finally L4 ports.  Whenever it is found that there are no matches
in the current subtable, the rest of the subtable can be skipped.  The
rationale of this logic is that as many fields as possible can remain
wildcarded.


Signed-off-by: Jarno Rajahalme <jrajahalme@nicira.com>
This commit is contained in:
Jarno Rajahalme
2013-11-19 17:31:29 -08:00
parent 4f88b5e5cf
commit 476f36e83b
15 changed files with 394 additions and 63 deletions

View File

@@ -41,8 +41,9 @@ static void update_subtables_after_removal(struct classifier *,
struct cls_subtable *,
unsigned int del_priority);
static struct cls_rule *find_match(const struct cls_subtable *,
const struct flow *);
static struct cls_rule *find_match_wc(const struct cls_subtable *,
const struct flow *,
struct flow_wildcards *);
static struct cls_rule *find_equal(struct cls_subtable *,
const struct miniflow *, uint32_t hash);
static struct cls_rule *insert_rule(struct classifier *,
@@ -149,13 +150,20 @@ cls_rule_is_catchall(const struct cls_rule *rule)
/* Initializes 'cls' as a classifier that initially contains no classification
* rules. */
void
classifier_init(struct classifier *cls)
classifier_init(struct classifier *cls, const uint8_t *flow_segments)
{
cls->n_rules = 0;
hmap_init(&cls->subtables);
list_init(&cls->subtables_priority);
hmap_init(&cls->partitions);
ovs_rwlock_init(&cls->rwlock);
cls->n_flow_segments = 0;
if (flow_segments) {
while (cls->n_flow_segments < CLS_MAX_INDICES
&& *flow_segments < FLOW_U32S) {
cls->flow_segments[cls->n_flow_segments++] = *flow_segments++;
}
}
}
/* Destroys 'cls'. Rules within 'cls', if any, are not freed; this is the
@@ -298,8 +306,15 @@ classifier_remove(struct classifier *cls, struct cls_rule *rule)
struct cls_partition *partition;
struct cls_rule *head;
struct cls_subtable *subtable;
int i;
subtable = find_subtable(cls, &rule->match.mask);
/* Remove rule node from indices. */
for (i = 0; i < subtable->n_indices; i++) {
hindex_remove(&subtable->indices[i], &rule->index_nodes[i]);
}
head = find_equal(subtable, &rule->match.flow, rule->hmap_node.hash);
if (head != rule) {
list_remove(&rule->list);
@@ -380,10 +395,7 @@ classifier_lookup(const struct classifier *cls, const struct flow *flow,
continue;
}
rule = find_match(subtable, flow);
if (wc) {
flow_wildcards_fold_minimask(wc, &subtable->mask);
}
rule = find_match_wc(subtable, flow, wc);
if (rule) {
best = rule;
LIST_FOR_EACH_CONTINUE (subtable, list_node,
@@ -397,10 +409,7 @@ classifier_lookup(const struct classifier *cls, const struct flow *flow,
continue;
}
rule = find_match(subtable, flow);
if (wc) {
flow_wildcards_fold_minimask(wc, &subtable->mask);
}
rule = find_match_wc(subtable, flow, wc);
if (rule && rule->priority > best->priority) {
best = rule;
}
@@ -657,11 +666,43 @@ insert_subtable(struct classifier *cls, const struct minimask *mask)
{
uint32_t hash = minimask_hash(mask, 0);
struct cls_subtable *subtable;
int i, index = 0;
struct flow_wildcards old, new;
uint8_t prev;
subtable = xzalloc(sizeof *subtable);
hmap_init(&subtable->rules);
minimask_clone(&subtable->mask, mask);
hmap_insert(&cls->subtables, &subtable->hmap_node, minimask_hash(mask, 0));
/* Init indices for segmented lookup, if any. */
flow_wildcards_init_catchall(&new);
old = new;
prev = 0;
for (i = 0; i < cls->n_flow_segments; i++) {
flow_wildcards_fold_minimask_range(&new, mask, prev,
cls->flow_segments[i]);
/* Add an index if it adds mask bits. */
if (!flow_wildcards_equal(&new, &old)) {
hindex_init(&subtable->indices[index]);
subtable->index_ofs[index] = cls->flow_segments[i];
index++;
old = new;
}
prev = cls->flow_segments[i];
}
/* Check if the rest of the subtable's mask adds any bits,
* and remove the last index if it doesn't. */
if (index > 0) {
flow_wildcards_fold_minimask_range(&new, mask, prev, FLOW_U32S);
if (flow_wildcards_equal(&new, &old)) {
--index;
subtable->index_ofs[index] = 0;
hindex_destroy(&subtable->indices[index]);
}
}
subtable->n_indices = index;
hmap_insert(&cls->subtables, &subtable->hmap_node, hash);
list_push_back(&cls->subtables_priority, &subtable->list_node);
subtable->tag = (minimask_get_metadata_mask(mask) == OVS_BE64_MAX
? tag_create_deterministic(hash)
@@ -673,6 +714,11 @@ insert_subtable(struct classifier *cls, const struct minimask *mask)
static void
destroy_subtable(struct classifier *cls, struct cls_subtable *subtable)
{
int i;
for (i = 0; i < subtable->n_indices; i++) {
hindex_destroy(&subtable->indices[i]);
}
minimask_destroy(&subtable->mask);
hmap_remove(&cls->subtables, &subtable->hmap_node);
hmap_destroy(&subtable->rules);
@@ -774,10 +820,10 @@ update_subtables_after_removal(struct classifier *cls,
}
}
static struct cls_rule *
find_match(const struct cls_subtable *subtable, const struct flow *flow)
static inline struct cls_rule *
find_match(const struct cls_subtable *subtable, const struct flow *flow,
uint32_t hash)
{
uint32_t hash = flow_hash_in_minimask(flow, &subtable->mask, 0);
struct cls_rule *rule;
HMAP_FOR_EACH_WITH_HASH (rule, hmap_node, hash, &subtable->rules) {
@@ -789,6 +835,71 @@ find_match(const struct cls_subtable *subtable, const struct flow *flow)
return NULL;
}
static struct cls_rule *
find_match_wc(const struct cls_subtable *subtable, const struct flow *flow,
struct flow_wildcards * wc)
{
uint32_t basis = 0, hash;
struct cls_rule *rule = NULL;
uint8_t prev_u32ofs = 0;
int i;
if (!wc) {
return find_match(subtable, flow,
flow_hash_in_minimask(flow, &subtable->mask, 0));
}
/* Try to finish early by checking fields in segments. */
for (i = 0; i < subtable->n_indices; i++) {
struct hindex_node *inode;
hash = flow_hash_in_minimask_range(flow, &subtable->mask, prev_u32ofs,
subtable->index_ofs[i], &basis);
prev_u32ofs = subtable->index_ofs[i];
inode = hindex_node_with_hash(&subtable->indices[i], hash);
if (!inode) {
/* No match, can stop immediately, but must fold in the mask
* covered so far. */
flow_wildcards_fold_minimask_range(wc, &subtable->mask, 0,
prev_u32ofs);
return NULL;
}
/* If we have narrowed down to a single rule already, check whether
* that rule matches. If it does match, then we're done. If it does
* not match, then we know that we will never get a match, but we do
* not yet know how many wildcards we need to fold into 'wc' so we
* continue iterating through indices to find that out. (We won't
* waste time calling minimatch_matches_flow() again because we've set
* 'rule' nonnull.)
*
* This check shows a measurable benefit with non-trivial flow tables.
*
* (Rare) hash collisions may cause us to miss the opportunity for this
* optimization. */
if (!inode->s && !rule) {
ASSIGN_CONTAINER(rule, inode - i, index_nodes);
if (minimatch_matches_flow(&rule->match, flow)) {
goto out;
}
}
}
if (!rule) {
/* Multiple potential matches exist, look for one. */
hash = flow_hash_in_minimask_range(flow, &subtable->mask, prev_u32ofs,
FLOW_U32S, &basis);
rule = find_match(subtable, flow, hash);
} else {
/* We already narrowed the matching candidates down to just 'rule',
* but it didn't match. */
rule = NULL;
}
out:
flow_wildcards_fold_minimask(wc, &subtable->mask);
return rule;
}
static struct cls_rule *
find_equal(struct cls_subtable *subtable, const struct miniflow *flow,
uint32_t hash)
@@ -809,19 +920,30 @@ insert_rule(struct classifier *cls, struct cls_subtable *subtable,
{
struct cls_rule *head;
struct cls_rule *old = NULL;
int i;
uint32_t basis = 0, hash;
uint8_t prev_u32ofs = 0;
new->hmap_node.hash = miniflow_hash_in_minimask(&new->match.flow,
&new->match.mask, 0);
head = find_equal(subtable, &new->match.flow, new->hmap_node.hash);
/* Add new node to segment indices. */
for (i = 0; i < subtable->n_indices; i++) {
hash = minimatch_hash_range(&new->match, prev_u32ofs,
subtable->index_ofs[i], &basis);
hindex_insert(&subtable->indices[i], &new->index_nodes[i], hash);
prev_u32ofs = subtable->index_ofs[i];
}
hash = minimatch_hash_range(&new->match, prev_u32ofs, FLOW_U32S, &basis);
head = find_equal(subtable, &new->match.flow, hash);
if (!head) {
hmap_insert(&subtable->rules, &new->hmap_node, new->hmap_node.hash);
hmap_insert(&subtable->rules, &new->hmap_node, hash);
list_init(&new->list);
goto out;
} else {
/* Scan the list for the insertion point that will keep the list in
* order of decreasing priority. */
struct cls_rule *rule;
new->hmap_node.hash = hash; /* Otherwise done by hmap_insert. */
FOR_EACH_RULE_IN_LIST (rule, head) {
if (new->priority >= rule->priority) {
if (rule == head) {
@@ -848,6 +970,11 @@ insert_rule(struct classifier *cls, struct cls_subtable *subtable,
out:
if (!old) {
update_subtables_after_insertion(cls, subtable, new->priority);
} else {
/* Remove old node from indices. */
for (i = 0; i < subtable->n_indices; i++) {
hindex_remove(&subtable->indices[i], &old->index_nodes[i]);
}
}
return old;
}