Signed-off-by: Ethan Jackson <et...@nicira.com>
---
 lib/classifier.c        |    3 +++
 lib/classifier.h        |   55 +++++++++++++++++++++++++++++++----------------
 ofproto/ofproto-dpif.c  |   18 +++++++++++++++-
 ofproto/ofproto.c       |   43 +++++++++++++++++++++++++++++++-----
 tests/test-classifier.c |   19 ++++++++++++++--
 utilities/ovs-ofctl.c   |    4 ++++
 6 files changed, 116 insertions(+), 26 deletions(-)

diff --git a/lib/classifier.c b/lib/classifier.c
index a717bd3..556278f 100644
--- a/lib/classifier.c
+++ b/lib/classifier.c
@@ -25,6 +25,7 @@
 #include "odp-util.h"
 #include "ofp-util.h"
 #include "packets.h"
+#include "ovs-thread.h"
 
 static struct cls_table *find_table(const struct classifier *,
                                     const struct minimask *);
@@ -143,6 +144,7 @@ classifier_init(struct classifier *cls)
     cls->n_rules = 0;
     hmap_init(&cls->tables);
     list_init(&cls->tables_priority);
+    ovs_rwlock_init(&cls->rwlock);
 }
 
 /* Destroys 'cls'.  Rules within 'cls', if any, are not freed; this is the
@@ -157,6 +159,7 @@ classifier_destroy(struct classifier *cls)
             destroy_table(cls, table);
         }
         hmap_destroy(&cls->tables);
+        ovs_rwlock_destroy(&cls->rwlock);
     }
 }
 
diff --git a/lib/classifier.h b/lib/classifier.h
index a14a24d..51d7c32 100644
--- a/lib/classifier.h
+++ b/lib/classifier.h
@@ -24,7 +24,14 @@
  *              a hash map from fixed field values to "struct cls_rule",
  *                      which can contain a list of otherwise identical rules
  *                      with lower priorities.
- */
+ *
+ * Thread-safety
+ * =============
+ *
+ * When locked properly, the classifier is thread safe as long as the following
+ * conditions are satisfied.
+ * - Only the main thread calls functions requiring a write lock.
+ * - Only the main thread is allowed to iterate over rules. */
 
 #include "flow.h"
 #include "hmap.h"
@@ -32,6 +39,8 @@
 #include "match.h"
 #include "openflow/nicira-ext.h"
 #include "openflow/openflow.h"
+#include "ovs-thread.h"
+#include "util.h"
 
 #ifdef __cplusplus
 extern "C" {
@@ -42,6 +51,7 @@ struct classifier {
     int n_rules;                /* Total number of rules. */
     struct hmap tables;         /* Contains "struct cls_table"s.  */
     struct list tables_priority; /* Tables in descending priority order */
+    struct ovs_rwlock rwlock;
 };
 
 /* A set of rules that all have the same fields wildcarded. */
@@ -88,26 +98,35 @@ bool cls_rule_is_catchall(const struct cls_rule *);
 bool cls_rule_is_loose_match(const struct cls_rule *rule,
                              const struct minimatch *criteria);
 
-void classifier_init(struct classifier *);
+void classifier_init(struct classifier *cls);
 void classifier_destroy(struct classifier *);
-bool classifier_is_empty(const struct classifier *);
-int classifier_count(const struct classifier *);
-void classifier_insert(struct classifier *, struct cls_rule *);
-struct cls_rule *classifier_replace(struct classifier *, struct cls_rule *);
-void classifier_remove(struct classifier *, struct cls_rule *);
-struct cls_rule *classifier_lookup(const struct classifier *,
+bool classifier_is_empty(const struct classifier *cls)
+    OVS_REQ_RDLOCK(cls->rwlock);
+int classifier_count(const struct classifier *cls)
+    OVS_REQ_RDLOCK(cls->rwlock);
+void classifier_insert(struct classifier *cls, struct cls_rule *)
+    OVS_REQ_WRLOCK(cls->rwlock);
+struct cls_rule *classifier_replace(struct classifier *cls, struct cls_rule *)
+    OVS_REQ_WRLOCK(cls->rwlock);
+void classifier_remove(struct classifier *cls, struct cls_rule *)
+    OVS_REQ_WRLOCK(cls->rwlock);
+struct cls_rule *classifier_lookup(const struct classifier *cls,
                                    const struct flow *,
-                                   struct flow_wildcards *);
-bool classifier_rule_overlaps(const struct classifier *,
-                              const struct cls_rule *);
+                                   struct flow_wildcards *)
+    OVS_REQ_RDLOCK(cls->rwlock);
+bool classifier_rule_overlaps(const struct classifier *cls,
+                              const struct cls_rule *)
+    OVS_REQ_RDLOCK(cls->rwlock);
 
 typedef void cls_cb_func(struct cls_rule *, void *aux);
 
-struct cls_rule *classifier_find_rule_exactly(const struct classifier *,
-                                              const struct cls_rule *);
-struct cls_rule *classifier_find_match_exactly(const struct classifier *,
+struct cls_rule *classifier_find_rule_exactly(const struct classifier *cls,
+                                              const struct cls_rule *)
+    OVS_REQ_RDLOCK(cls->rwlock);
+struct cls_rule *classifier_find_match_exactly(const struct classifier *cls,
                                                const struct match *,
-                                               unsigned int priority);
+                                               unsigned int priority)
+    OVS_REQ_RDLOCK(cls->rwlock);
 
 /* Iteration. */
 
@@ -117,10 +136,10 @@ struct cls_cursor {
     const struct cls_rule *target;
 };
 
-void cls_cursor_init(struct cls_cursor *, const struct classifier *,
+void cls_cursor_init(struct cls_cursor *cursor, const struct classifier *cls,
                      const struct cls_rule *match);
-struct cls_rule *cls_cursor_first(struct cls_cursor *);
-struct cls_rule *cls_cursor_next(struct cls_cursor *, struct cls_rule *);
+struct cls_rule *cls_cursor_first(struct cls_cursor *cursor);
+struct cls_rule *cls_cursor_next(struct cls_cursor *cursor, struct cls_rule *);
 
 #define CLS_CURSOR_FOR_EACH(RULE, MEMBER, CURSOR)                       \
     for (ASSIGN_CONTAINER(RULE, cls_cursor_first(CURSOR), MEMBER);      \
diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
index b484a07..a466bc9 100644
--- a/ofproto/ofproto-dpif.c
+++ b/ofproto/ofproto-dpif.c
@@ -1619,6 +1619,7 @@ run(struct ofproto *ofproto_)
     ovs_rwlock_unlock(&ofproto->ml->rwlock);
 
     /* Check the consistency of a random facet, to aid debugging. */
+    ovs_rwlock_rdlock(&ofproto->facets.rwlock);
     if (time_msec() >= ofproto->consistency_rl
         && !classifier_is_empty(&ofproto->facets)
         && !ofproto->backer->need_revalidate) {
@@ -1638,6 +1639,7 @@ run(struct ofproto *ofproto_)
             ofproto->backer->need_revalidate = REV_INCONSISTENCY;
         }
     }
+    ovs_rwlock_unlock(&ofproto->facets.rwlock);
 
     return 0;
 }
@@ -1690,7 +1692,9 @@ get_memory_usage(const struct ofproto *ofproto_, struct 
simap *usage)
     size_t n_subfacets = 0;
     struct facet *facet;
 
+    ovs_rwlock_rdlock(&ofproto->facets.rwlock);
     simap_increase(usage, "facets", classifier_count(&ofproto->facets));
+    ovs_rwlock_unlock(&ofproto->facets.rwlock);
 
     cls_cursor_init(&cursor, &ofproto->facets, NULL);
     CLS_CURSOR_FOR_EACH (facet, cr, &cursor) {
@@ -4360,7 +4364,9 @@ facet_create(const struct flow_miss *miss, struct 
rule_dpif *rule,
 
     match_init(&match, &facet->flow, &facet->xout.wc);
     cls_rule_init(&facet->cr, &match, OFP_DEFAULT_PRIORITY);
+    ovs_rwlock_wrlock(&ofproto->facets.rwlock);
     classifier_insert(&ofproto->facets, &facet->cr);
+    ovs_rwlock_unlock(&ofproto->facets.rwlock);
 
     facet->nf_flow.output_iface = facet->xout.nf_output_iface;
     facet->fail_open = rule->up.cr.priority == FAIL_OPEN_PRIORITY;
@@ -4428,7 +4434,9 @@ facet_remove(struct facet *facet)
                         &facet->subfacets) {
         subfacet_destroy__(subfacet);
     }
+    ovs_rwlock_wrlock(&facet->ofproto->facets.rwlock);
     classifier_remove(&facet->ofproto->facets, &facet->cr);
+    ovs_rwlock_unlock(&facet->ofproto->facets.rwlock);
     cls_rule_destroy(&facet->cr);
     facet_free(facet);
 }
@@ -4572,7 +4580,11 @@ facet_flush_stats(struct facet *facet)
 static struct facet *
 facet_find(struct ofproto_dpif *ofproto, const struct flow *flow)
 {
-    struct cls_rule *cr = classifier_lookup(&ofproto->facets, flow, NULL);
+    struct cls_rule *cr;
+
+    ovs_rwlock_rdlock(&ofproto->facets.rwlock);
+    cr = classifier_lookup(&ofproto->facets, flow, NULL);
+    ovs_rwlock_unlock(&ofproto->facets.rwlock);
     return cr ? CONTAINER_OF(cr, struct facet, cr) : NULL;
 }
 
@@ -5137,14 +5149,18 @@ rule_dpif_lookup_in_table(struct ofproto_dpif *ofproto,
         struct flow ofpc_normal_flow = *flow;
         ofpc_normal_flow.tp_src = htons(0);
         ofpc_normal_flow.tp_dst = htons(0);
+        ovs_rwlock_rdlock(&cls->rwlock);
         cls_rule = classifier_lookup(cls, &ofpc_normal_flow, wc);
+        ovs_rwlock_unlock(&cls->rwlock);
     } else if (frag && ofproto->up.frag_handling == OFPC_FRAG_DROP) {
         cls_rule = &ofproto->drop_frags_rule->up.cr;
         if (wc) {
             flow_wildcards_init_exact(wc);
         }
     } else {
+        ovs_rwlock_rdlock(&cls->rwlock);
         cls_rule = classifier_lookup(cls, flow, wc);
+        ovs_rwlock_unlock(&cls->rwlock);
     }
     return rule_dpif_cast(rule_from_cls_rule(cls_rule));
 }
diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c
index 0625ccf..49fcaa9 100644
--- a/ofproto/ofproto.c
+++ b/ofproto/ofproto.c
@@ -1017,6 +1017,7 @@ ofproto_configure_table(struct ofproto *ofproto, int 
table_id,
     }
 
     table->max_flows = s->max_flows;
+    ovs_rwlock_rdlock(&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
@@ -1032,6 +1033,7 @@ ofproto_configure_table(struct ofproto *ofproto, int 
table_id,
             break;
         }
     }
+    ovs_rwlock_unlock(&table->cls.rwlock);
 }
 
 bool
@@ -1385,7 +1387,9 @@ ofproto_get_memory_usage(const struct ofproto *ofproto, 
struct simap *usage)
 
     n_rules = 0;
     OFPROTO_FOR_EACH_TABLE (table, ofproto) {
+        ovs_rwlock_rdlock(&table->cls.rwlock);
         n_rules += classifier_count(&table->cls);
+        ovs_rwlock_unlock(&table->cls.rwlock);
     }
     simap_increase(usage, "rules", n_rules);
 
@@ -1612,8 +1616,10 @@ ofproto_add_flow(struct ofproto *ofproto, const struct 
match *match,
 {
     const struct rule *rule;
 
+    ovs_rwlock_rdlock(&ofproto->tables[0].cls.rwlock);
     rule = rule_from_cls_rule(classifier_find_match_exactly(
                                   &ofproto->tables[0].cls, match, priority));
+    ovs_rwlock_unlock(&ofproto->tables[0].cls.rwlock);
     if (!rule || !ofpacts_equal(rule->ofpacts, rule->ofpacts_len,
                                 ofpacts, ofpacts_len)) {
         struct ofputil_flow_mod fm;
@@ -1650,8 +1656,10 @@ ofproto_delete_flow(struct ofproto *ofproto,
 {
     struct rule *rule;
 
+    ovs_rwlock_rdlock(&ofproto->tables[0].cls.rwlock);
     rule = rule_from_cls_rule(classifier_find_match_exactly(
                                   &ofproto->tables[0].cls, target, priority));
+    ovs_rwlock_unlock(&ofproto->tables[0].cls.rwlock);
     if (!rule) {
         /* No such rule -> success. */
         return true;
@@ -2613,7 +2621,9 @@ handle_table_stats_request(struct ofconn *ofconn,
         ots[i].instructions = htonl(OFPIT11_ALL);
         ots[i].config = htonl(OFPTC11_TABLE_MISS_MASK);
         ots[i].max_entries = htonl(1000000); /* An arbitrary big number. */
+        ovs_rwlock_rdlock(&p->tables[i].cls.rwlock);
         ots[i].active_count = htonl(classifier_count(&p->tables[i].cls));
+        ovs_rwlock_unlock(&p->tables[i].cls.rwlock);
     }
 
     p->ofproto_class->get_tables(p, ots);
@@ -2956,8 +2966,10 @@ collect_rules_strict(struct ofproto *ofproto, uint8_t 
table_id,
     FOR_EACH_MATCHING_TABLE (table, table_id, ofproto) {
         struct rule *rule;
 
+        ovs_rwlock_rdlock(&table->cls.rwlock);
         rule = rule_from_cls_rule(classifier_find_rule_exactly(&table->cls,
                                                                &cr));
+        ovs_rwlock_unlock(&table->cls.rwlock);
         if (rule) {
             if (rule->pending) {
                 error = OFPROTO_POSTPONE;
@@ -3306,6 +3318,7 @@ add_flow(struct ofproto *ofproto, struct ofconn *ofconn,
     struct rule *victim;
     struct rule *rule;
     uint8_t table_id;
+    bool overlaps;
     int error;
 
     error = check_table_id(ofproto, fm->table_id);
@@ -3362,8 +3375,10 @@ add_flow(struct ofproto *ofproto, struct ofconn *ofconn,
     }
 
     /* Check for overlap, if requested. */
-    if (fm->flags & OFPFF_CHECK_OVERLAP
-        && classifier_rule_overlaps(&table->cls, &rule->cr)) {
+    ovs_rwlock_rdlock(&table->cls.rwlock);
+    overlaps = classifier_rule_overlaps(&table->cls, &rule->cr);
+    ovs_rwlock_unlock(&table->cls.rwlock);
+    if (fm->flags & OFPFF_CHECK_OVERLAP && overlaps) {
         cls_rule_destroy(&rule->cr);
         ofproto->ofproto_class->rule_dealloc(rule);
         return OFPERR_OFPFMFC_OVERLAP;
@@ -3401,8 +3416,12 @@ add_flow(struct ofproto *ofproto, struct ofconn *ofconn,
     } else {
         struct ofoperation *op;
         struct rule *evict;
+        size_t n_rules;
 
-        if (classifier_count(&table->cls) > table->max_flows) {
+        ovs_rwlock_rdlock(&table->cls.rwlock);
+        n_rules = classifier_count(&table->cls);
+        ovs_rwlock_unlock(&table->cls.rwlock);
+        if (n_rules > table->max_flows) {
             bool was_evictable;
 
             was_evictable = rule->evictable;
@@ -5069,9 +5088,17 @@ ofproto_evict(struct ofproto *ofproto)
 
     group = ofopgroup_create_unattached(ofproto);
     OFPROTO_FOR_EACH_TABLE (table, ofproto) {
-        while (classifier_count(&table->cls) > table->max_flows
-               && table->eviction_fields) {
+        while (table->eviction_fields) {
             struct rule *rule;
+            size_t n_rules;
+
+            ovs_rwlock_rdlock(&table->cls.rwlock);
+            n_rules = classifier_count(&table->cls);
+            ovs_rwlock_unlock(&table->cls.rwlock);
+
+            if (n_rules <= table->max_flows) {
+                break;
+            }
 
             rule = choose_rule_to_evict(table);
             if (!rule || rule->pending) {
@@ -5282,7 +5309,9 @@ oftable_init(struct oftable *table)
 static void
 oftable_destroy(struct oftable *table)
 {
+    ovs_rwlock_rdlock(&table->cls.rwlock);
     ovs_assert(classifier_is_empty(&table->cls));
+    ovs_rwlock_unlock(&table->cls.rwlock);
     oftable_disable_eviction(table);
     classifier_destroy(&table->cls);
     free(table->name);
@@ -5375,7 +5404,9 @@ oftable_remove_rule(struct rule *rule)
     struct ofproto *ofproto = rule->ofproto;
     struct oftable *table = &ofproto->tables[rule->table_id];
 
+    ovs_rwlock_wrlock(&table->cls.rwlock);
     classifier_remove(&table->cls, &rule->cr);
+    ovs_rwlock_unlock(&table->cls.rwlock);
     if (rule->meter_id) {
         list_remove(&rule->meter_list_node);
     }
@@ -5408,7 +5439,9 @@ oftable_replace_rule(struct rule *rule)
         struct meter *meter = ofproto->meters[rule->meter_id];
         list_insert(&meter->rules, &rule->meter_list_node);
     }
+    ovs_rwlock_wrlock(&table->cls.rwlock);
     victim = rule_from_cls_rule(classifier_replace(&table->cls, &rule->cr));
+    ovs_rwlock_unlock(&table->cls.rwlock);
     if (victim) {
         if (victim->meter_id) {
             list_remove(&victim->meter_list_node);
diff --git a/tests/test-classifier.c b/tests/test-classifier.c
index 7fb1447..1d3d2d4 100644
--- a/tests/test-classifier.c
+++ b/tests/test-classifier.c
@@ -396,6 +396,7 @@ get_value(unsigned int *x, unsigned n_values)
 
 static void
 compare_classifiers(struct classifier *cls, struct tcls *tcls)
+    OVS_REQ_RDLOCK(cls->rwlock)
 {
     static const int confidence = 500;
     unsigned int i;
@@ -444,17 +445,19 @@ destroy_classifier(struct classifier *cls)
     struct test_rule *rule, *next_rule;
     struct cls_cursor cursor;
 
+    ovs_rwlock_wrlock(&cls->rwlock);
     cls_cursor_init(&cursor, cls, NULL);
     CLS_CURSOR_FOR_EACH_SAFE (rule, next_rule, cls_rule, &cursor) {
         classifier_remove(cls, &rule->cls_rule);
         free_rule(rule);
     }
+    ovs_rwlock_unlock(&cls->rwlock);
     classifier_destroy(cls);
 }
 
 static void
-check_tables(const struct classifier *cls,
-             int n_tables, int n_rules, int n_dups)
+check_tables(const struct classifier *cls, int n_tables, int n_rules,
+             int n_dups) OVS_REQ_RDLOCK(cls->rwlock)
 {
     const struct cls_table *table;
     struct test_rule *test_rule;
@@ -610,10 +613,12 @@ test_empty(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
     struct tcls tcls;
 
     classifier_init(&cls);
+    ovs_rwlock_rdlock(&cls.rwlock);
     tcls_init(&tcls);
     assert(classifier_is_empty(&cls));
     assert(tcls_is_empty(&tcls));
     compare_classifiers(&cls, &tcls);
+    ovs_rwlock_unlock(&cls.rwlock);
     classifier_destroy(&cls);
     tcls_destroy(&tcls);
 }
@@ -640,6 +645,7 @@ test_single_rule(int argc OVS_UNUSED, char *argv[] 
OVS_UNUSED)
                          hash_bytes(&wc_fields, sizeof wc_fields, 0), 0);
 
         classifier_init(&cls);
+        ovs_rwlock_wrlock(&cls.rwlock);
         tcls_init(&tcls);
 
         tcls_rule = tcls_insert(&tcls, rule);
@@ -654,6 +660,7 @@ test_single_rule(int argc OVS_UNUSED, char *argv[] 
OVS_UNUSED)
         compare_classifiers(&cls, &tcls);
 
         free_rule(rule);
+        ovs_rwlock_unlock(&cls.rwlock);
         classifier_destroy(&cls);
         tcls_destroy(&tcls);
     }
@@ -677,6 +684,7 @@ test_rule_replacement(int argc OVS_UNUSED, char *argv[] 
OVS_UNUSED)
         rule2->aux += 5;
 
         classifier_init(&cls);
+        ovs_rwlock_wrlock(&cls.rwlock);
         tcls_init(&tcls);
         tcls_insert(&tcls, rule1);
         classifier_insert(&cls, &rule1->cls_rule);
@@ -692,6 +700,7 @@ test_rule_replacement(int argc OVS_UNUSED, char *argv[] 
OVS_UNUSED)
         check_tables(&cls, 1, 1, 0);
         compare_classifiers(&cls, &tcls);
         tcls_destroy(&tcls);
+        ovs_rwlock_unlock(&cls.rwlock);
         destroy_classifier(&cls);
     }
 }
@@ -787,6 +796,7 @@ test_many_rules_in_one_list (int argc OVS_UNUSED, char 
*argv[] OVS_UNUSED)
             }
 
             classifier_init(&cls);
+            ovs_rwlock_wrlock(&cls.rwlock);
             tcls_init(&tcls);
 
             for (i = 0; i < ARRAY_SIZE(ops); i++) {
@@ -825,6 +835,7 @@ test_many_rules_in_one_list (int argc OVS_UNUSED, char 
*argv[] OVS_UNUSED)
                 compare_classifiers(&cls, &tcls);
             }
 
+            ovs_rwlock_unlock(&cls.rwlock);
             classifier_destroy(&cls);
             tcls_destroy(&tcls);
 
@@ -887,6 +898,7 @@ test_many_rules_in_one_table(int argc OVS_UNUSED, char 
*argv[] OVS_UNUSED)
         } while ((1 << count_ones(value_mask)) < N_RULES);
 
         classifier_init(&cls);
+        ovs_rwlock_wrlock(&cls.rwlock);
         tcls_init(&tcls);
 
         for (i = 0; i < N_RULES; i++) {
@@ -913,6 +925,7 @@ test_many_rules_in_one_table(int argc OVS_UNUSED, char 
*argv[] OVS_UNUSED)
             compare_classifiers(&cls, &tcls);
         }
 
+        ovs_rwlock_unlock(&cls.rwlock);
         classifier_destroy(&cls);
         tcls_destroy(&tcls);
     }
@@ -947,6 +960,7 @@ test_many_rules_in_n_tables(int n_tables)
         shuffle(priorities, ARRAY_SIZE(priorities));
 
         classifier_init(&cls);
+        ovs_rwlock_wrlock(&cls.rwlock);
         tcls_init(&tcls);
 
         for (i = 0; i < MAX_RULES; i++) {
@@ -979,6 +993,7 @@ test_many_rules_in_n_tables(int n_tables)
             free_rule(target);
         }
 
+        ovs_rwlock_unlock(&cls.rwlock);
         destroy_classifier(&cls);
         tcls_destroy(&tcls);
     }
diff --git a/utilities/ovs-ofctl.c b/utilities/ovs-ofctl.c
index 68b73bf..5ee7ed1 100644
--- a/utilities/ovs-ofctl.c
+++ b/utilities/ovs-ofctl.c
@@ -1891,7 +1891,9 @@ fte_free_all(struct classifier *cls)
 
     cls_cursor_init(&cursor, cls, NULL);
     CLS_CURSOR_FOR_EACH_SAFE (fte, next, rule, &cursor) {
+        ovs_rwlock_wrlock(&cls->rwlock);
         classifier_remove(cls, &fte->rule);
+        ovs_rwlock_unlock(&cls->rwlock);
         fte_free(fte);
     }
     classifier_destroy(cls);
@@ -1912,7 +1914,9 @@ fte_insert(struct classifier *cls, const struct match 
*match,
     cls_rule_init(&fte->rule, match, priority);
     fte->versions[index] = version;
 
+    ovs_rwlock_wrlock(&cls->rwlock);
     old = fte_from_cls_rule(classifier_replace(cls, &fte->rule));
+    ovs_rwlock_unlock(&cls->rwlock);
     if (old) {
         fte_version_free(old->versions[index]);
         fte->versions[!index] = old->versions[!index];
-- 
1.7.9.5

_______________________________________________
dev mailing list
dev@openvswitch.org
http://openvswitch.org/mailman/listinfo/dev

Reply via email to