The new module will be used by ofproto to keep track of the number of
learned flows with the same cookie in the same table.

The counters are used to implement limits for the learn action.

The module implements its own internal locking, because the counters can
be increased/decreased from handlers and revalidators.

Signed-off-by: Daniele Di Proietto <[email protected]>
---
 ofproto/automake.mk       |   2 +
 ofproto/cookie-counters.c | 216 ++++++++++++++++++++++++++++++++++++++++++++++
 ofproto/cookie-counters.h |  85 ++++++++++++++++++
 3 files changed, 303 insertions(+)
 create mode 100644 ofproto/cookie-counters.c
 create mode 100644 ofproto/cookie-counters.h

diff --git a/ofproto/automake.mk b/ofproto/automake.mk
index 7c08b563b..3cc3c239c 100644
--- a/ofproto/automake.mk
+++ b/ofproto/automake.mk
@@ -17,6 +17,8 @@ ofproto_libofproto_la_SOURCES = \
        ofproto/collectors.h \
        ofproto/connmgr.c \
        ofproto/connmgr.h \
+       ofproto/cookie-counters.h \
+       ofproto/cookie-counters.c \
        ofproto/fail-open.c \
        ofproto/fail-open.h \
        ofproto/in-band.c \
diff --git a/ofproto/cookie-counters.c b/ofproto/cookie-counters.c
new file mode 100644
index 000000000..f72e8651e
--- /dev/null
+++ b/ofproto/cookie-counters.c
@@ -0,0 +1,216 @@
+/*
+ * Copyright (c) 2017 Nicira, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <config.h>
+
+#include "cookie-counters.h"
+
+#include <stdbool.h>
+
+#include "hash.h"
+#include "openvswitch/thread.h"
+
+/* Cookie counters
+ * ===============
+ *
+ * Counters for limiting the number of flows with a specific cookie in a
+ * specific table.
+ *
+ * The 'cookie_counter_map' contains 'cookie_counter's.  Each cookie counter
+ * keeps a 'count' of the number of flows with 'cookie_id' in 'table_id'.
+ *
+ * The user can increment the cookie counter by taking a reference with
+ * cookie_counter_new_ref().
+ *
+ * The reference can be inserted in 'cookie_counter_refs' to prevent the
+ * counter from decreasing until all the pointers to the reference are
+ * expired.
+ */
+
+static struct ovs_mutex mutex = OVS_MUTEX_INITIALIZER;
+
+struct cookie_counter {
+    struct hmap_node node;
+
+    const ovs_be64 cookie_id;
+    const uint8_t table_id;
+
+    struct cookie_counter_map *map;
+
+    unsigned count;
+};
+
+struct cookie_counter_ref {
+    struct cookie_counter *ref OVS_GUARDED_BY(mutex);
+    int refcnt OVS_GUARDED_BY(mutex);
+};
+
+/* Initializes the cookie counters map */
+void
+cookie_counter_table_init(struct cookie_counter_map *t)
+{
+    ovs_mutex_lock(&mutex);
+    hmap_init(&t->map);
+    ovs_mutex_unlock(&mutex);
+}
+
+/* Destroys the cookie counters map.  All references must be released at this
+ * point.  */
+void
+cookie_counter_table_destroy(struct cookie_counter_map *t)
+{
+    ovs_mutex_lock(&mutex);
+    hmap_destroy(&t->map);
+    ovs_mutex_unlock(&mutex);
+}
+
+static uint32_t
+hash_cookie_table(ovs_be64 cookie_, uint8_t table_id)
+{
+    uint64_t cookie = (OVS_FORCE uint64_t) cookie_;
+    return hash_3words(cookie, cookie >> 32, table_id);
+}
+
+static struct cookie_counter *
+cookie_counter_lookup(struct cookie_counter_map *t, ovs_be64 cookie_id,
+                      uint8_t table_id)
+    OVS_REQUIRES(mutex)
+{
+    struct cookie_counter *c;
+    uint32_t hash = hash_cookie_table(cookie_id, table_id);
+    HMAP_FOR_EACH_WITH_HASH(c, node, hash, &t->map) {
+        if (c->cookie_id == cookie_id && c->table_id == table_id) {
+            return c;
+        }
+    }
+    return NULL;
+}
+
+static void
+cookie_counter_decrement(struct cookie_counter *c)
+    OVS_REQUIRES(mutex)
+{
+    ovs_assert(c->count > 0);
+    c->count--;
+    if (c->count == 0) {
+        hmap_remove(&c->map->map, &c->node);
+        free(c);
+    }
+}
+
+/* If the counter for cookie 'cookie_id' in table 't' is less than 'limit',
+ * it will be increased by 1.  A reference is returned.  As long as the
+ * reference lives the counter will not be decreased.
+ *
+ * The reference can be copied (to prevent the counter from decreasing) into
+ * 'cookie_counter_refs' objects. */
+struct cookie_counter_ref *
+cookie_counter_new_ref(struct cookie_counter_map *t, ovs_be64 cookie_id,
+                       uint8_t table_id, unsigned limit)
+{
+    struct cookie_counter_ref *ref = NULL;
+    struct cookie_counter *c;
+
+    ovs_mutex_lock(&mutex);
+    c = cookie_counter_lookup(t, cookie_id, table_id);
+    if (c == NULL) {
+        uint32_t hash = hash_cookie_table(cookie_id, table_id);
+
+        c = xzalloc(sizeof *c);
+        *CONST_CAST(ovs_be64 *, &(c->cookie_id)) = cookie_id;
+        *CONST_CAST(uint8_t *, &(c->table_id)) = table_id;
+        c->count = 0;
+        c->map = t;
+        hmap_insert(&t->map, &c->node, hash);
+    }
+
+    ovs_assert(limit);
+
+    if (c->count < limit) {
+        c->count++;
+        ref = xzalloc(sizeof *ref);
+        ref->refcnt = 1;
+        ref->ref = c;
+    }
+
+    ovs_mutex_unlock(&mutex);
+
+    return ref;
+}
+
+static struct cookie_counter_ref *
+cookie_counter_copy_ref(struct cookie_counter_ref *ref)
+    OVS_REQUIRES(mutex)
+{
+    ovs_assert(ref->refcnt > 0);
+    ref->refcnt++;
+    return ref;
+}
+
+void
+cookie_counter_destroy_ref(struct cookie_counter_ref *ref)
+{
+    if (!ref) {
+        return;
+    }
+    ovs_mutex_lock(&mutex);
+    ovs_assert(ref->refcnt > 0);
+    ref->refcnt--;
+    if (ref->refcnt == 0) {
+        cookie_counter_decrement(ref->ref);
+        free(ref);
+    }
+    ovs_mutex_unlock(&mutex);
+}
+
+void
+cookie_counter_refs_init(struct cookie_counter_refs *refs)
+{
+    *refs = COOKIE_COUNTER_REFS_INIT;
+}
+
+void
+cookie_counter_refs_swap(struct cookie_counter_refs *a,
+                         struct cookie_counter_refs *b)
+{
+    struct cookie_counter_refs tmp;
+
+    tmp = *a;
+    *a = *b;
+    *b = tmp;
+}
+
+void
+cookie_counter_refs_add(struct cookie_counter_refs *refs,
+                        struct cookie_counter_ref *ref)
+{
+    refs->n_refs++;
+    refs->refs = xrealloc(refs->refs, refs->n_refs * sizeof *refs->refs);
+    ovs_mutex_lock(&mutex);
+    refs->refs[refs->n_refs - 1] = cookie_counter_copy_ref(ref);
+    ovs_mutex_unlock(&mutex);
+}
+
+void
+cookie_counter_refs_unref(struct cookie_counter_refs *refs)
+{
+    for (int i = 0; i < refs->n_refs; i++) {
+        cookie_counter_destroy_ref(refs->refs[i]);
+    }
+    free(refs->refs);
+    refs->refs = NULL;
+    refs->n_refs = 0;
+}
diff --git a/ofproto/cookie-counters.h b/ofproto/cookie-counters.h
new file mode 100644
index 000000000..da9be1b84
--- /dev/null
+++ b/ofproto/cookie-counters.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2017 Nicira, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef COOKIE_COUNTERS_H
+#define COOKIE_COUNTERS_H 1
+
+#include "openvswitch/hmap.h"
+#include "openvswitch/types.h"
+
+/* Cookie counters
+ * ===============
+ *
+ * Counters for limiting the number of flows with a specific cookie in a
+ * specific table.
+ *
+ *
+ * Interface
+ * =========
+ *
+ * struct cookie_counter_map keeps the counter for all the cookies in all the
+ * tables.
+ *
+ * To create a new counter or to increment an existing counter,
+ * cookie_counter_new_ref() can be used.  The function will return NULL if the
+ * counter would exceed 'limit'.  Otherwise, it will return a pointer to
+ * a reference (struct cookie_counter_ref), which must be released with
+ * cookie_counter_destroy_ref() to decrease the count.
+ *
+ * References to counters can be kept in struct cookie_counter_refs.
+ *
+ * Thread-safety
+ * =============
+ *
+ * All the functions below are thread safe.  There's a lock that protects
+ * the counter inside the module.
+ *
+ * We cannot reuse ofproto_mutex, because the references need to be released
+ * from revalidators.
+ */
+
+struct cookie_counter_map {
+    struct hmap map OVS_GUARDED; /* Protected by 'mutex' in .c */
+};
+
+struct cookie_counter_ref;
+
+struct cookie_counter_refs {
+    size_t n_refs;
+    struct cookie_counter_ref **refs;
+};
+
+#define COOKIE_COUNTER_REFS_INIT ((struct cookie_counter_refs) \
+                                  { 0, NULL})
+
+void cookie_counter_table_init(struct cookie_counter_map *);
+void cookie_counter_table_destroy(struct cookie_counter_map *);
+
+struct cookie_counter_ref *cookie_counter_new_ref(
+        struct cookie_counter_map *, ovs_be64 cookie_id,
+        uint8_t table_id, unsigned limit);
+void cookie_counter_destroy_ref(struct cookie_counter_ref *ref);
+
+void cookie_counter_refs_init(struct cookie_counter_refs *refs);
+void cookie_counter_refs_add(struct cookie_counter_refs *refs,
+                             struct cookie_counter_ref *ref);
+
+void cookie_counter_refs_swap(struct cookie_counter_refs *a,
+                              struct cookie_counter_refs *b);
+
+void cookie_counter_refs_unref(struct cookie_counter_refs *refs);
+
+#endif /* cookie-counters.h */
-- 
2.11.0

_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to