[ovs-dev] [PATCH v2 8/9] schema: Add "fieldspec" to table "Flow_Table".

Jarno Rajahalme jrajahalme at nicira.com
Wed Nov 13 21:32:48 UTC 2013


"fieldspec" is a string map with the following keys:

"prefix"  A list of field names for which prefix lookup should be used.

The fields for which prefix lookup can be enabled are:
- tun_id, tun_src, tun_dst
- metadata
- reg0 -- reg7
- ip_src, ip_dst (or aliases nw_src and nw_dst)
- ipv6_src, ipv6_dst

There is a maximum number of fields that can be enabled for any one
flow table.  Currently this limit is 3.

Examples:

ovs-vsctl set Bridge br0 flow_tables:0=@N1 -- \
 --id=@N1 create Flow_Table name=table0
ovs-vsctl set Bridge br0 flow_tables:1=@N1 -- \
 --id=@N1 create Flow_Table name=table1

ovs-vsctl set Flow_Table table0 fieldspec:prefix=tun_id
ovs-vsctl set Flow_Table table1 fieldspec:prefix=ip_dst,ip_src

Signed-off-by: Jarno Rajahalme <jrajahalme at nicira.com>
---
 lib/classifier.c           |   89 ++++++++++++++++++++++++++++++++++++--------
 lib/classifier.h           |   17 ++++++---
 ofproto/ofproto.c          |    6 ++-
 ofproto/ofproto.h          |    8 ++++
 tests/classifier.at        |    1 +
 tests/ofproto-dpif.at      |    2 +
 tests/test-classifier.c    |   17 ++++++++-
 vswitchd/bridge.c          |   60 +++++++++++++++++++++++++++++
 vswitchd/vswitch.ovsschema |    9 +++--
 9 files changed, 182 insertions(+), 27 deletions(-)

diff --git a/lib/classifier.c b/lib/classifier.c
index 026287b..f7fd1f2 100644
--- a/lib/classifier.c
+++ b/lib/classifier.c
@@ -66,6 +66,8 @@ static struct cls_rule *next_rule_in_list(struct cls_rule *);
 static uint8_t minimask_get_prefix_len(const struct minimask *,
                                        const struct mf_field *,
                                        unsigned int *);
+static void trie_init(struct classifier *, int trie_idx,
+                      const struct mf_field *);
 static bool trie_lookup(const struct cls_trie *, const struct flow *,
                         struct trie_ctx *);
 static void trie_destroy(struct trie_node *);
@@ -169,12 +171,6 @@ cls_rule_is_catchall(const struct cls_rule *rule)
 void
 classifier_init(struct classifier *cls, const uint8_t *flow_segments)
 {
-    int i;
-
-    static enum mf_field_id trie_fields[2] = {
-        MFF_IPV4_DST, MFF_IPV4_SRC
-    };
-
     cls->n_rules = 0;
     hmap_init(&cls->subtables);
     list_init(&cls->subtables_priority);
@@ -187,12 +183,7 @@ classifier_init(struct classifier *cls, const uint8_t *flow_segments)
             cls->flow_segments[cls->n_flow_segments++] = *flow_segments++;
         }
     }
-    for (i = 0; i < sizeof trie_fields / sizeof trie_fields[0]; i++) {
-        cls->tries[i].field = mf_from_id(trie_fields[i]);
-        ovs_assert(cls->tries[i].field);
-        cls->tries[i].root = NULL;
-    }
-    cls->n_tries = i;
+    cls->n_tries = 0;
 }
 
 /* Destroys 'cls'.  Rules within 'cls', if any, are not freed; this is the
@@ -227,6 +218,71 @@ classifier_destroy(struct classifier *cls)
     }
 }
 
+void
+classifier_set_prefix_fields(struct classifier *cls,
+                             const enum mf_field_id *trie_fields,
+                             unsigned int n_fields)
+{
+    int i, trie;
+
+    for (i = 0, trie = 0; i < n_fields && trie < CLS_MAX_TRIES; i++) {
+        const struct mf_field *field = mf_from_id(trie_fields[i]);
+        if (!field || field->flow_u32ofs < 0) {
+            /* Non-existing or incompatible field. */
+            continue;
+        }
+        if (trie >= cls->n_tries || field != cls->tries[trie].field) {
+            if (trie < cls->n_tries && cls->tries[trie].root) {
+                trie_destroy(cls->tries[trie].root);
+            }
+            trie_init(cls, trie, field);
+        }
+        trie++;
+    }
+
+    /* Destroy the rest. */
+    for (i = trie; i < cls->n_tries; i++) {
+        if (cls->tries[i].root) {
+            trie_destroy(cls->tries[i].root);
+            cls->tries[i].root = NULL;
+        }
+    }
+    cls->n_tries = trie;
+}
+
+static void
+trie_init(struct classifier *cls, int trie_idx,
+          const struct mf_field *field)
+{
+    struct cls_trie *trie = &cls->tries[trie_idx];
+    struct cls_subtable *subtable;
+
+    trie->root = NULL;
+    trie->field = field;
+
+    /* Add existing rules to the trie. */
+    LIST_FOR_EACH (subtable, list_node, &cls->subtables_priority) {
+        struct cls_rule *head;
+
+        /* Initialize subtable's prefix length on this field. */
+        subtable->trie_plen[trie_idx]
+            = minimask_get_prefix_len(&subtable->mask, field, NULL);
+
+        if (!subtable->trie_plen[trie_idx]) {
+            /* Field not in this subtable. */
+            continue;
+        }
+
+        HMAP_FOR_EACH (head, hmap_node, &subtable->rules) {
+            struct cls_rule *rule;
+            FOR_EACH_RULE_IN_LIST (rule, head) {
+                trie_insert(trie, rule);
+            }
+        }
+    }
+}
+
+
 /* Returns true if 'cls' contains no classification rules, false otherwise. */
 bool
 classifier_is_empty(const struct classifier *cls)
@@ -422,7 +478,7 @@ classifier_lookup(const struct classifier *cls, const struct flow *flow,
     struct cls_subtable *subtable;
     struct cls_rule *best;
     tag_type tags;
-    struct trie_ctx trie_ctx[CLS_N_TRIES];
+    struct trie_ctx trie_ctx[CLS_MAX_TRIES];
     int i, n_tries = 0;
 
     /* Determine 'tags' such that, if 'subtable->tag' doesn't intersect them,
@@ -897,8 +953,9 @@ update_subtables_after_removal(struct classifier *cls,
 /* Return 'true' if can skip rest of the subtable based on the prefix trie
  * lookup results. */
 static inline bool
-check_tries(const struct trie_ctx trie_ctx[CLS_N_TRIES], unsigned int n_tries,
-            const uint8_t field_plen[CLS_N_TRIES], uint8_t next_u32ofs,
+check_tries(const struct trie_ctx trie_ctx[CLS_MAX_TRIES],
+            unsigned int n_tries,
+            const uint8_t field_plen[CLS_MAX_TRIES], uint8_t next_u32ofs,
             struct flow_wildcards *wc)
 {
     int j;
@@ -941,7 +998,7 @@ check_tries(const struct trie_ctx trie_ctx[CLS_N_TRIES], unsigned int n_tries,
 
 static struct cls_rule *
 find_match(const struct cls_subtable *subtable, const struct flow *flow,
-           const struct trie_ctx trie_ctx[CLS_N_TRIES], unsigned int n_tries,
+           const struct trie_ctx trie_ctx[CLS_MAX_TRIES], unsigned int n_tries,
            struct flow_wildcards *wc)
 {
     uint32_t basis = 0, hash;
diff --git a/lib/classifier.h b/lib/classifier.h
index 9c50b32..ad6030f 100644
--- a/lib/classifier.h
+++ b/lib/classifier.h
@@ -121,15 +121,15 @@ extern "C" {
 extern struct ovs_mutex ofproto_mutex;
 struct trie_node;
 
-/* Maximum number of prefix trees per classifier. */
-#define CLS_N_TRIES 3
-
 struct cls_trie {
     const struct mf_field *field; /* Trie field, or NULL. */
     struct trie_node *root;       /* NULL if none. */
 };
 
-enum { CLS_MAX_INDICES = 3 };
+enum {
+    CLS_MAX_INDICES = 3, /* Maximum number of hash indices per classifier. */
+    CLS_MAX_TRIES = 3    /* Maximum number of prefix trees per classifier. */
+};
 
 /* A flow classifier. */
 struct classifier {
@@ -142,7 +142,7 @@ struct classifier {
                                      */
     struct hmap partitions;     /* Contains "struct cls_partition"s. */
     struct ovs_rwlock rwlock OVS_ACQ_AFTER(ofproto_mutex);
-    struct cls_trie tries[CLS_N_TRIES];
+    struct cls_trie tries[CLS_MAX_TRIES];
     unsigned int n_tries;
 };
 
@@ -161,7 +161,7 @@ struct cls_subtable {
     uint8_t n_indices;           /* How many indices to use. */
     uint8_t index_ofs[CLS_MAX_INDICES]; /* u32 flow segment boundaries. */
     struct hindex indices[CLS_MAX_INDICES]; /* Search indices. */
-    uint8_t trie_plen[CLS_N_TRIES]; /* Trie prefix length of the 'mask' */
+    uint8_t trie_plen[CLS_MAX_TRIES]; /* Trie prefix length of the 'mask' */
 };
 
 /* Returns true if 'table' is a "catch-all" subtable that will match every
@@ -212,6 +212,11 @@ bool cls_rule_is_loose_match(const struct cls_rule *rule,
 
 void classifier_init(struct classifier *cls, const uint8_t *flow_segments);
 void classifier_destroy(struct classifier *);
+void classifier_set_prefix_fields(struct classifier *cls,
+                                  const enum mf_field_id *trie_fields,
+                                  unsigned int n_trie_fields)
+    OVS_REQ_WRLOCK(cls->rwlock);
+
 bool classifier_is_empty(const struct classifier *cls)
     OVS_REQ_RDLOCK(cls->rwlock);
 int classifier_count(const struct classifier *cls)
diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c
index 5cd6b1e..d5ee99c 100644
--- a/ofproto/ofproto.c
+++ b/ofproto/ofproto.c
@@ -1138,7 +1138,7 @@ ofproto_configure_table(struct ofproto *ofproto, int table_id,
     }
 
     table->max_flows = s->max_flows;
-    ovs_rwlock_rdlock(&table->cls.rwlock);
+    ovs_rwlock_wrlock(&table->cls.rwlock);
     if (classifier_count(&table->cls) > table->max_flows
         && table->eviction_fields) {
         /* 'table' contains more flows than allowed.  We might not be able to
@@ -1154,6 +1154,10 @@ ofproto_configure_table(struct ofproto *ofproto, int table_id,
             break;
         }
     }
+
+    classifier_set_prefix_fields(&table->cls,
+                                 s->prefix_fields, s->n_prefix_fields);
+
     ovs_rwlock_unlock(&table->cls.rwlock);
 }
 
diff --git a/ofproto/ofproto.h b/ofproto/ofproto.h
index 903d1f4..2a680c3 100644
--- a/ofproto/ofproto.h
+++ b/ofproto/ofproto.h
@@ -23,7 +23,9 @@
 #include <stddef.h>
 #include <stdint.h>
 #include "cfm.h"
+#include "classifier.h"
 #include "flow.h"
+#include "meta-flow.h"
 #include "netflow.h"
 #include "sset.h"
 #include "stp.h"
@@ -381,6 +383,12 @@ struct ofproto_table_settings {
      * distinguished by different values for the subfields within 'groups'. */
     struct mf_subfield *groups;
     size_t n_groups;
+
+    /*
+     * Fields for which prefix trie lookup is maintained.
+     */
+    unsigned int n_prefix_fields;
+    enum mf_field_id prefix_fields[CLS_MAX_TRIES];
 };
 
 int ofproto_get_n_tables(const struct ofproto *);
diff --git a/tests/classifier.at b/tests/classifier.at
index 0b38b2f..0aa723f 100644
--- a/tests/classifier.at
+++ b/tests/classifier.at
@@ -27,6 +27,7 @@ AT_BANNER([flow classifier lookup segmentation])
 AT_SETUP([flow classifier - lookup segmentation])
 OVS_VSWITCHD_START
 ADD_OF_PORTS([br0], [1], [2], [3])
+AT_CHECK([ovs-vsctl set Bridge br0 flow_tables:0=@N1 -- --id=@N1 create Flow_Table name=t0 fieldspec:prefix=nw_dst,nw_src], [0], [ignore], [])
 AT_DATA([flows.txt], [dnl
 table=0 in_port=1 priority=16,tcp,nw_dst=10.1.0.0/255.255.0.0,action=output(3)
 table=0 in_port=1 priority=32,tcp,nw_dst=10.1.2.15,action=output(2)
diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at
index 0c367b3..eabac98 100644
--- a/tests/ofproto-dpif.at
+++ b/tests/ofproto-dpif.at
@@ -2579,6 +2579,7 @@ AT_CLEANUP
 AT_SETUP([ofproto-dpif megaflow - L3 classification])
 OVS_VSWITCHD_START
 ADD_OF_PORTS([br0], [1], [2])
+AT_CHECK([ovs-vsctl set Bridge br0 flow_tables:0=@N1 -- --id=@N1 create Flow_Table name=t0 fieldspec:prefix=nw_dst,nw_src], [0], [ignore], [])
 AT_DATA([flows.txt], [dnl
 table=0 in_port=1,icmp,nw_src=10.0.0.4 actions=output(2)
 ])
@@ -2920,6 +2921,7 @@ AT_CLEANUP
 AT_SETUP([ofproto-dpif megaflow - dec_ttl])
 OVS_VSWITCHD_START
 ADD_OF_PORTS([br0], [1], [2])
+AT_CHECK([ovs-vsctl set Bridge br0 flow_tables:0=@N1 -- --id=@N1 create Flow_Table name=t0 fieldspec:prefix=nw_dst,nw_src], [0], [ignore], [])
 AT_DATA([flows.txt], [dnl
 table=0 in_port=1,icmp,nw_src=10.0.0.4 actions=dec_ttl,output(2)
 ])
diff --git a/tests/test-classifier.c b/tests/test-classifier.c
index cb9b3db..85c3a4b 100644
--- a/tests/test-classifier.c
+++ b/tests/test-classifier.c
@@ -605,6 +605,10 @@ shuffle_u32s(uint32_t *p, size_t n)
 
 /* Classifier tests. */
 
+static enum mf_field_id trie_fields[2] = {
+    MFF_IPV4_DST, MFF_IPV4_SRC
+};
+
 /* Tests an empty classifier. */
 static void
 test_empty(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
@@ -613,7 +617,8 @@ test_empty(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
     struct tcls tcls;
 
     classifier_init(&cls, flow_segment_u32s);
-    ovs_rwlock_rdlock(&cls.rwlock);
+    ovs_rwlock_wrlock(&cls.rwlock);
+    classifier_set_prefix_fields(&cls, trie_fields, ARRAY_SIZE(trie_fields));
     tcls_init(&tcls);
     assert(classifier_is_empty(&cls));
     assert(tcls_is_empty(&tcls));
@@ -646,6 +651,8 @@ test_single_rule(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
 
         classifier_init(&cls, flow_segment_u32s);
         ovs_rwlock_wrlock(&cls.rwlock);
+        classifier_set_prefix_fields(&cls, trie_fields,
+                                     ARRAY_SIZE(trie_fields));
         tcls_init(&tcls);
 
         tcls_rule = tcls_insert(&tcls, rule);
@@ -685,6 +692,8 @@ test_rule_replacement(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
 
         classifier_init(&cls, flow_segment_u32s);
         ovs_rwlock_wrlock(&cls.rwlock);
+        classifier_set_prefix_fields(&cls, trie_fields,
+                                     ARRAY_SIZE(trie_fields));
         tcls_init(&tcls);
         tcls_insert(&tcls, rule1);
         classifier_insert(&cls, &rule1->cls_rule);
@@ -797,6 +806,8 @@ test_many_rules_in_one_list (int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
 
             classifier_init(&cls, flow_segment_u32s);
             ovs_rwlock_wrlock(&cls.rwlock);
+            classifier_set_prefix_fields(&cls, trie_fields,
+                                         ARRAY_SIZE(trie_fields));
             tcls_init(&tcls);
 
             for (i = 0; i < ARRAY_SIZE(ops); i++) {
@@ -899,6 +910,8 @@ test_many_rules_in_one_table(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
 
         classifier_init(&cls, flow_segment_u32s);
         ovs_rwlock_wrlock(&cls.rwlock);
+        classifier_set_prefix_fields(&cls, trie_fields,
+                                     ARRAY_SIZE(trie_fields));
         tcls_init(&tcls);
 
         for (i = 0; i < N_RULES; i++) {
@@ -961,6 +974,8 @@ test_many_rules_in_n_tables(int n_tables)
 
         classifier_init(&cls, flow_segment_u32s);
         ovs_rwlock_wrlock(&cls.rwlock);
+        classifier_set_prefix_fields(&cls, trie_fields,
+                                     ARRAY_SIZE(trie_fields));
         tcls_init(&tcls);
 
         for (i = 0; i < MAX_RULES; i++) {
diff --git a/vswitchd/bridge.c b/vswitchd/bridge.c
index 4a3b849..7af4a4a 100644
--- a/vswitchd/bridge.c
+++ b/vswitchd/bridge.c
@@ -3119,6 +3119,44 @@ bridge_configure_remotes(struct bridge *br,
     }
 }
 
+static unsigned int
+parse_field_list(const char *str, enum mf_field_id fields[], unsigned int size,
+                 const char *brname)
+{
+    char *string, *name, *save_ptr;
+    unsigned int n_fields = 0;
+
+    if (!str) {
+        return 0;
+    }
+
+    string = strdup(str);
+
+    for (name = strtok_r(string, ",", &save_ptr); name;
+         name = strtok_r(NULL, ",", &save_ptr)) {
+
+        const struct mf_field *mf = mf_from_name(name);
+        if (!mf) {
+            VLOG_WARN("bridge %s: fieldspec:prefix Unknown field: %s",
+                      brname, name);
+            continue;
+        }
+        if (mf->flow_u32ofs < 0) {
+            VLOG_WARN("bridge %s: Field %s not compatible with "
+                      "prefix lookup.", brname, name);
+            continue;
+        }
+        if (n_fields >= size) {
+            VLOG_WARN("bridge %s: Too many prefix fields, field not used: %s.",
+                      brname, name);
+            continue;
+        }
+        fields[n_fields++] = mf->id;
+    }
+    free(string);
+    return n_fields;
+}
+
 static void
 bridge_configure_tables(struct bridge *br)
 {
@@ -3135,6 +3173,8 @@ bridge_configure_tables(struct bridge *br)
         s.max_flows = UINT_MAX;
         s.groups = NULL;
         s.n_groups = 0;
+        s.n_prefix_fields = 0;
+        memset(s.prefix_fields, ~0, sizeof(s.prefix_fields));
 
         if (j < br->cfg->n_flow_tables && i == br->cfg->key_flow_tables[j]) {
             struct ovsrec_flow_table *cfg = br->cfg->value_flow_tables[j++];
@@ -3166,6 +3206,26 @@ bridge_configure_tables(struct bridge *br)
                     }
                 }
             }
+            /* Parse fieldspec. */
+            s.n_prefix_fields = parse_field_list(smap_get(&cfg->fieldspec,
+                                                          "prefix"),
+                                                 s.prefix_fields,
+                                                 ARRAY_SIZE(s.prefix_fields),
+                                                 br->name);
+            if (s.n_prefix_fields > 0) {
+                int k;
+                struct ds ds = DS_EMPTY_INITIALIZER;
+                for (k = 0; k < s.n_prefix_fields; k++) {
+                    if (k) {
+                        ds_put_char(&ds, ',');
+                    }
+                    ds_put_cstr(&ds, mf_from_id(s.prefix_fields[k])->name);
+                }
+
+                VLOG_INFO("bridge %s table %d: Prefix lookup with: %s.",
+                          br->name, i, ds_cstr(&ds));
+                ds_destroy(&ds);
+            }
         }
 
         ofproto_configure_table(br->ofproto, i, &s);
diff --git a/vswitchd/vswitch.ovsschema b/vswitchd/vswitch.ovsschema
index 78ebc89..cea6a43 100644
--- a/vswitchd/vswitch.ovsschema
+++ b/vswitchd/vswitch.ovsschema
@@ -1,6 +1,6 @@
 {"name": "Open_vSwitch",
- "version": "7.3.0",
- "cksum": "2811681289 20311",
+ "version": "7.4.0",
+ "cksum": "907509119 20416",
  "tables": {
    "Open_vSwitch": {
      "columns": {
@@ -300,7 +300,10 @@
 			  "enum": ["set", ["refuse", "evict"]]},
 		  "min": 0, "max": 1}},
        "groups": {
-	 "type": {"key": "string", "min": 0, "max": "unlimited"}}}},
+	 "type": {"key": "string", "min": 0, "max": "unlimited"}},
+       "fieldspec": {
+	 "type": {"key": "string", "value": "string",
+		  "min": 0, "max": "unlimited"}}}},
    "QoS": {
      "columns": {
        "type": {
-- 
1.7.10.4




More information about the dev mailing list