"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 <jrajaha...@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 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev