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 <b...@nicira.com>

...

> > +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
dev@openvswitch.org
http://openvswitch.org/mailman/listinfo/dev

Reply via email to