[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