I'm getting back to this, finally.
On Wed, May 08, 2013 at 08:13:18AM +0000, Rajahalme, Jarno (NSN - FI/Espoo)
wrote:
> On Mar 27, 2013, at 6:32 , ext Ben Pfaff wrote:
>
> > We have a controller that puts many rules with different metadata values
> > into the flow table, where metadata is used (by "resubmit"s) to distinguish
> > stages in a pipeline. Thus, any given flow only needs to be hashed into
> > classifier "cls_table"s that contain a match for the flow's metadata value.
> > This commit optimizes the classifier lookup by (probabilistically) skipping
> > the "cls_table"s that can't possibly match.
> >
> > (The "metadata" referred to here is the OpenFlow 1.1+ "metadata" field,
> > which is a 64-bit field similar in purpose to the "registers" defined by
> > Open vSwitch.)
> >
> > This speeds up flow setup performance with the controller in question by
> > about 19%.
> >
> > Bug #14282.
> > Signed-off-by: Ben Pfaff <[email protected]>
...
> > +static struct cls_partition *
> > +create_partition(struct classifier *cls, struct cls_table *table,
> > + ovs_be64 metadata)
> > +{
> > + uint32_t hash = hash_metadata(metadata);
> > + struct cls_partition *partition = find_partition(cls, metadata, hash);
> > + if (partition) {
> > + partition->n_refs++;
> > + partition->tags |= table->tag;
>
> In principle, partition->tags could fill up over time as the bits
> are left there as rules are removed from the classifier. However, is
> seems that the rule/table dynamics could be such that this is no
> problem in practice?
I don't know. I don't think anyone has done long-term testing as
rules are added and removed.
I have an idea for how to solve the problem with O(1) additional work
when adding or removing rules. I'll work on that and probably post a
followup patch.
> > old_rule = insert_rule(cls, table, rule);
> > if (!old_rule) {
> > + if (minimask_get_metadata_mask(&rule->match.mask) == OVS_BE64_MAX)
> > {
> > + ovs_be64 metadata = miniflow_get_metadata(&rule->match.flow);
> > + rule->partition = create_partition(cls, table, metadata);
>
> I guess that Andy's comment on masked metadata support would be a
> generalization to this, where the metadata mask would be made part
> of the cls_partition, and the hmap would be keyed by the combination
> of the (masked) metadata and the mask. It would be pretty
> straightforward, but would be a bit more work. With this
> generalization this part would look like something like this:
>
> ovs_be64 metadata_mask =
> minimask_get_metadata_mask(&rule->match.mask);
> if (metadata_mask) {
> ovs_be64 metadata = miniflow_get_metadata(&rule->match.flow);
> rule->partition = create_partition(cls, table, metadata,
> metadata_mask);
>
> Corresponding changes would be needed elsewhere.
>
> I have no idea of the performance impact, though.
I see that you posted a later followup saying that this would probably
wouldn't be a good idea, so I'll ignore this.
> > best = NULL;
> > LIST_FOR_EACH (table, list_node, &cls->tables_priority) {
> > - struct cls_rule *rule = find_match(table, flow);
> > + struct cls_rule *rule = find_match(table, flow, tags);
>
> How about keeping the tags check here instead? Like:
> LIST_FOR_EACH (table, list_node, &cls->tables_priority) {
> if (!tag_intersects(tags, table->tag))
> continue;
> struct cls_rule *rule = find_match(table, flow);
OK, I folded this in, thanks.
> > if (rule) {
> > best = rule;
> > LIST_FOR_EACH_CONTINUE (table, list_node,
> > &cls->tables_priority) {
> > @@ -270,7 +361,7 @@ classifier_lookup(const struct classifier *cls, const
> > struct flow *flow)
> > * can not find anything better. */
> > return best;
> > }
> > - rule = find_match(table, flow);
> > + rule = find_match(table, flow, tags);
>
> And here:
>
> if (!tag_intersects(tags, table->tag))
> continue;
> rule = find_match(table, flow);
Here too.
> Maybe it could be a little bit faster, in addition to keeping the
> find_match() intact.
I think you're right.
> > @@ -19,17 +19,66 @@
> >
> > /* Flow classifier.
> > *
> > - * A classifier is a "struct classifier",
> > - * a hash map from a set of wildcards to a "struct cls_table",
> > - * a hash map from fixed field values to "struct cls_rule",
> > - * which can contain a list of otherwise identical
> > rules
> > - * with lower priorities.
> > + * Basic Design
> > + * ============
> > + *
>
> Maybe start with a note that each OpenFlow flow table is represented by a
> classifier instance so the difference between OF tables and classifier tables
> would be more apparent.
I added a section:
* What?
* =====
*
* A flow classifier holds any number of "rules", each of which specifies
* values to match for some fields or subfields and a priority. The primary
* design goal for the classifier is that, given a packet, it can as quickly as
* possible find the highest-priority rule that matches the packet.
*
* Each OpenFlow table is implemented as a flow classifier.
> > + * Suppose that all the rules in a classifier had the same form. For
> > example,
> > + * suppose that they all matched on the source and destination Ethernet
> > address
> > + * and wildcarded all the other fields. Then the obvious way to implement
> > a
> > + * classifier would be a hash table on the source and destination Ethernet
> > + * addresses. If new classification rules came along with a different
> > form,
> > + * you could add a second hash table that hashed on the fields matched in
> > those
> > + * rules. With two hash tables, you look up a given flow in each hash
> > table.
> > + * If there are no matches, the classifier didn't contain a match; if you
> > find
> > + * a match in one of them, that's the result; if you find a match in both
> > of
> > + * them, then the result is the rule with the higher priority.
> > + *
> > + * This is how the classifier works. In a "struct classifier", each form
> > of
> > + * "struct cls_rule" present (based on its ->match.mask) goes into a
> > separate
> > + * "struct cls_table". A lookup does a hash lookup in every "struct
> > cls_table"
> > + * in the classifier and returns the highest-priority match that it finds.
>
> Could add:
>
> "The tables are kept in a descending priority order according to the
> highest priority rule in each table. This way the lookups following
> a found match can be limited to tables that possibly have higher
> priority rules than already found."
Thanks. I changed this paragraph to:
* This is how the classifier works. In a "struct classifier", each form of
* "struct cls_rule" present (based on its ->match.mask) goes into a separate
* "struct cls_table". A lookup does a hash lookup in every "struct cls_table"
* in the classifier and tracks the highest-priority match that it finds. The
* tables are kept in a descending priority order according to the highest
* priority rule in each table, which allows lookup to skip over tables that
* can't possibly have a higher-priority match than already found.
> > + * The classifier has a special optimization to speed up matching in this
> > + * scenario:
> > + *
> > + * - Each cls_table that matches on metadata gets a random tag (see
> > tag.h).
>
> To me the word "random" caused some confusion at first, so maybe put it like
> this instead:
>
> "Each cls_table that matches on metadata gets a tag derived from the table's
> mask, so that it is highly likely that each table has a unique tag."
>
> And maybe add that the possible non-unique table tags lead to some
> harmless checking of more tables than needed.
That is better, thanks. I changed this item to:
* - Each cls_table that matches on metadata gets a tag derived from the
* table's mask, so that it is likely that each table has a unique tag.
* (Duplicate tags have a performance cost but do not affect
* correctness.)
> > + * - For each metadata value matched by any cls_rule, the classifier
> > + * constructs a "struct cls_partition" indexed by the metadata value.
> > + * The cls_partition has a 'tags' member whose value is the
> > bitwise-OR of
> > + * the tags of each cls_table that contains any rule that matches on
> > the
> > + * cls_partition's metadata value.
>
> So in other words 'struct cls_partition' associates metadata values with
> tables that
> need to be checked with flows with that specific metadata value.
OK, I added that sentence to this item. It would be clearer without
the two uses of "with", but I didn't see a good way to do that.
Thanks,
Ben.
_______________________________________________
dev mailing list
[email protected]
http://openvswitch.org/mailman/listinfo/dev