[ovs-dev] [PATCH v4 12/14] ovn-controller: Add data structure for indexing lports, multicast groups.

Ben Pfaff blp at ovn.org
Fri Feb 19 08:34:22 UTC 2016


This was more or less implemented inside lflow.c until now, but some
upcoming code that shouldn't be in that file needs to use it too.

This also adds a second index on lports, so that lports can be looked up
based on the logical datapath tunnel key and the logical port tunnel key.
An upcoming commit will add a user for this new index.

Signed-off-by: Ben Pfaff <blp at ovn.org>
---
 ovn/controller/automake.mk      |   2 +
 ovn/controller/lflow.c          | 181 ++++++++++------------------------------
 ovn/controller/lflow.h          |  10 ++-
 ovn/controller/lport.c          | 157 ++++++++++++++++++++++++++++++++++
 ovn/controller/lport.h          |  67 +++++++++++++++
 ovn/controller/ovn-controller.c |  36 ++++----
 6 files changed, 299 insertions(+), 154 deletions(-)
 create mode 100644 ovn/controller/lport.c
 create mode 100644 ovn/controller/lport.h

diff --git a/ovn/controller/automake.mk b/ovn/controller/automake.mk
index cadfa9c..cf57bbd 100644
--- a/ovn/controller/automake.mk
+++ b/ovn/controller/automake.mk
@@ -8,6 +8,8 @@ ovn_controller_ovn_controller_SOURCES = \
 	ovn/controller/encaps.h \
 	ovn/controller/lflow.c \
 	ovn/controller/lflow.h \
+	ovn/controller/lport.c \
+	ovn/controller/lport.h \
 	ovn/controller/ofctrl.c \
 	ovn/controller/ofctrl.h \
 	ovn/controller/pinctrl.c \
diff --git a/ovn/controller/lflow.c b/ovn/controller/lflow.c
index 912c609..bd08ad6 100644
--- a/ovn/controller/lflow.c
+++ b/ovn/controller/lflow.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015 Nicira, Inc.
+/* Copyright (c) 2015, 2016 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -15,12 +15,13 @@
 
 #include <config.h>
 #include "lflow.h"
+#include "lport.h"
 #include "dynamic-string.h"
 #include "ofctrl.h"
 #include "ofp-actions.h"
 #include "ofpbuf.h"
 #include "openvswitch/vlog.h"
-#include "ovn/controller/ovn-controller.h"
+#include "ovn-controller.h"
 #include "ovn/lib/actions.h"
 #include "ovn/lib/expr.h"
 #include "ovn/lib/ovn-sb-idl.h"
@@ -43,8 +44,8 @@ add_logical_register(struct shash *symtab, enum mf_field_id id)
     expr_symtab_add_field(symtab, name, id, NULL, false);
 }
 
-static void
-symtab_init(void)
+void
+lflow_init(void)
 {
     shash_init(&symtab);
 
@@ -155,161 +156,62 @@ symtab_init(void)
     expr_symtab_add_field(&symtab, "sctp.dst", MFF_SCTP_DST, "sctp", false);
 }
 
-/* Logical datapaths and logical port numbers. */
-
-enum ldp_type {
-    LDP_TYPE_ROUTER,
-    LDP_TYPE_SWITCH,
-};
-
-/* A logical datapath.
- *
- * 'ports' maps 'logical_port' names to 'tunnel_key' values in the OVN_SB
- * Port_Binding table within the logical datapath. */
-struct logical_datapath {
-    struct hmap_node hmap_node; /* Indexed on 'uuid'. */
-    struct uuid uuid;           /* UUID from Datapath_Binding row. */
-    uint32_t tunnel_key;        /* 'tunnel_key' from Datapath_Binding row. */
-    struct simap ports;         /* Logical port name to port number. */
-    enum ldp_type type;         /* Type of logical datapath */
+struct lookup_port_aux {
+    const struct lport_index *lports;
+    const struct mcgroup_index *mcgroups;
+    const struct sbrec_datapath_binding *dp;
 };
 
-/* Contains "struct logical_datapath"s. */
-static struct hmap logical_datapaths = HMAP_INITIALIZER(&logical_datapaths);
-
-/* Finds and returns the logical_datapath for 'binding', or NULL if no such
- * logical_datapath exists. */
-static struct logical_datapath *
-ldp_lookup(const struct sbrec_datapath_binding *binding)
-{
-    struct logical_datapath *ldp;
-    HMAP_FOR_EACH_IN_BUCKET (ldp, hmap_node, uuid_hash(&binding->header_.uuid),
-                             &logical_datapaths) {
-        if (uuid_equals(&ldp->uuid, &binding->header_.uuid)) {
-            return ldp;
-        }
-    }
-    return NULL;
-}
-
-/* Creates a new logical_datapath for the given 'binding'. */
-static struct logical_datapath *
-ldp_create(const struct sbrec_datapath_binding *binding)
-{
-    struct logical_datapath *ldp;
-
-    ldp = xmalloc(sizeof *ldp);
-    hmap_insert(&logical_datapaths, &ldp->hmap_node,
-                uuid_hash(&binding->header_.uuid));
-    ldp->uuid = binding->header_.uuid;
-    ldp->tunnel_key = binding->tunnel_key;
-    const char *ls = smap_get(&binding->external_ids, "logical-switch");
-    ldp->type = ls ? LDP_TYPE_SWITCH : LDP_TYPE_ROUTER;
-    simap_init(&ldp->ports);
-    return ldp;
-}
-
-static struct logical_datapath *
-ldp_lookup_or_create(const struct sbrec_datapath_binding *binding)
-{
-    struct logical_datapath *ldp = ldp_lookup(binding);
-    return ldp ? ldp : ldp_create(binding);
-}
-
-static void
-ldp_free(struct logical_datapath *ldp)
+static bool
+lookup_port_cb(const void *aux_, const char *port_name, unsigned int *portp)
 {
-    simap_destroy(&ldp->ports);
-    hmap_remove(&logical_datapaths, &ldp->hmap_node);
-    free(ldp);
-}
+    const struct lookup_port_aux *aux = aux_;
 
-/* Iterates through all of the records in the Port_Binding table, updating the
- * table of logical_datapaths to match the values found in active
- * Port_Bindings. */
-static void
-ldp_run(struct controller_ctx *ctx)
-{
-    struct logical_datapath *ldp;
-    HMAP_FOR_EACH (ldp, hmap_node, &logical_datapaths) {
-        simap_clear(&ldp->ports);
+    const struct sbrec_port_binding *pb
+        = lport_lookup_by_name(aux->lports, port_name);
+    if (pb && pb->datapath == aux->dp) {
+        *portp = pb->tunnel_key;
+        return true;
     }
 
-    const struct sbrec_port_binding *binding;
-    SBREC_PORT_BINDING_FOR_EACH (binding, ctx->ovnsb_idl) {
-        struct logical_datapath *ldp = ldp_lookup_or_create(binding->datapath);
-
-        simap_put(&ldp->ports, binding->logical_port, binding->tunnel_key);
+    const struct sbrec_multicast_group *mg
+        = mcgroup_lookup_by_dp_name(aux->mcgroups, aux->dp, port_name);
+    if (mg) {
+        *portp = mg->tunnel_key;
+        return true;
     }
 
-    const struct sbrec_multicast_group *mc;
-    SBREC_MULTICAST_GROUP_FOR_EACH (mc, ctx->ovnsb_idl) {
-        struct logical_datapath *ldp = ldp_lookup_or_create(mc->datapath);
-        simap_put(&ldp->ports, mc->name, mc->tunnel_key);
-    }
-
-    struct logical_datapath *next_ldp;
-    HMAP_FOR_EACH_SAFE (ldp, next_ldp, hmap_node, &logical_datapaths) {
-        if (simap_is_empty(&ldp->ports)) {
-            ldp_free(ldp);
-        }
-    }
-}
-
-static void
-ldp_destroy(void)
-{
-    struct logical_datapath *ldp, *next_ldp;
-    HMAP_FOR_EACH_SAFE (ldp, next_ldp, hmap_node, &logical_datapaths) {
-        ldp_free(ldp);
-    }
-}
-
-void
-lflow_init(void)
-{
-    symtab_init();
+    return false;
 }
 
 static bool
-lookup_port_cb(const void *ldp_, const char *port_name, unsigned int *portp)
+is_switch(const struct sbrec_datapath_binding *ldp)
 {
-    const struct logical_datapath *ldp = ldp_;
-    const struct simap_node *node = simap_find(&ldp->ports, port_name);
-    if (!node) {
-        return false;
-    }
-    *portp = node->data;
-    return true;
+    return smap_get(&ldp->external_ids, "logical-switch") != NULL;
+
 }
 
 /* Translates logical flows in the Logical_Flow table in the OVN_SB database
  * into OpenFlow flows.  See ovn-architecture(7) for more information. */
 void
-lflow_run(struct controller_ctx *ctx, struct hmap *flow_table,
-          const struct simap *ct_zones,
-          struct hmap *local_datapaths)
+lflow_run(struct controller_ctx *ctx, const struct lport_index *lports,
+          const struct mcgroup_index *mcgroups,
+          const struct hmap *local_datapaths,
+          const struct simap *ct_zones, struct hmap *flow_table)
 {
     struct hmap flows = HMAP_INITIALIZER(&flows);
     uint32_t conj_id_ofs = 1;
 
-    ldp_run(ctx);
-
     const struct sbrec_logical_flow *lflow;
     SBREC_LOGICAL_FLOW_FOR_EACH (lflow, ctx->ovnsb_idl) {
-        /* Find the "struct logical_datapath" associated with this
-         * Logical_Flow row.  If there's no such struct, that must be because
-         * no logical ports are bound to that logical datapath, so there's no
-         * point in maintaining any flows for it anyway, so skip it. */
-        const struct logical_datapath *ldp;
-        ldp = ldp_lookup(lflow->logical_datapath);
+        /* Determine translation of logical table IDs to physical table IDs. */
+        bool ingress = !strcmp(lflow->pipeline, "ingress");
+
+        const struct sbrec_datapath_binding *ldp = lflow->logical_datapath;
         if (!ldp) {
             continue;
         }
-
-        bool ingress = !strcmp(lflow->pipeline, "ingress");
-
-        if (ldp->type == LDP_TYPE_SWITCH && !ingress) {
+        if (!ingress && is_switch(ldp)) {
             /* For a logical switch datapath, local_datapaths tells us if there
              * are any local ports for this datapath.  If not, processing
              * logical flows for the egress pipeline of this datapath is
@@ -356,10 +258,15 @@ lflow_run(struct controller_ctx *ctx, struct hmap *flow_table,
         char *error;
 
         ofpbuf_use_stub(&ofpacts, ofpacts_stub, sizeof ofpacts_stub);
+        struct lookup_port_aux aux = {
+            .lports = lports,
+            .mcgroups = mcgroups,
+            .dp = lflow->logical_datapath
+        };
         struct action_params ap = {
             .symtab = &symtab,
             .lookup_port = lookup_port_cb,
-            .aux = ldp,
+            .aux = &aux,
             .ct_zones = ct_zones,
 
             .n_tables = LOG_PIPELINE_LEN,
@@ -400,14 +307,15 @@ lflow_run(struct controller_ctx *ctx, struct hmap *flow_table,
 
         expr = expr_simplify(expr);
         expr = expr_normalize(expr);
-        uint32_t n_conjs = expr_to_matches(expr, lookup_port_cb, ldp,
+        uint32_t n_conjs = expr_to_matches(expr, lookup_port_cb, &aux,
                                            &matches);
         expr_destroy(expr);
 
         /* Prepare the OpenFlow matches for adding to the flow table. */
         struct expr_match *m;
         HMAP_FOR_EACH (m, hmap_node, &matches) {
-            match_set_metadata(&m->match, htonll(ldp->tunnel_key));
+            match_set_metadata(&m->match,
+                               htonll(lflow->logical_datapath->tunnel_key));
             if (m->match.wc.masks.conj_id) {
                 m->match.flow.conj_id += conj_id_ofs;
             }
@@ -445,5 +353,4 @@ void
 lflow_destroy(void)
 {
     expr_symtab_destroy(&symtab);
-    ldp_destroy();
 }
diff --git a/ovn/controller/lflow.h b/ovn/controller/lflow.h
index ccbad30..603edfd 100644
--- a/ovn/controller/lflow.h
+++ b/ovn/controller/lflow.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015 Nicira, Inc.
+/* Copyright (c) 2015, 2016 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -37,6 +37,8 @@
 
 struct controller_ctx;
 struct hmap;
+struct lport_index;
+struct mcgroup_index;
 struct simap;
 struct uuid;
 
@@ -56,9 +58,11 @@ struct uuid;
 #define LOG_PIPELINE_LEN 16
 
 void lflow_init(void);
-void lflow_run(struct controller_ctx *, struct hmap *flow_table,
+void lflow_run(struct controller_ctx *, const struct lport_index *,
+               const struct mcgroup_index *,
+               const struct hmap *local_datapaths, 
                const struct simap *ct_zones,
-               struct hmap *local_datapaths);
+               struct hmap *flow_table);
 void lflow_destroy(void);
 
 #endif /* ovn/lflow.h */
diff --git a/ovn/controller/lport.c b/ovn/controller/lport.c
new file mode 100644
index 0000000..e4c6f8c
--- /dev/null
+++ b/ovn/controller/lport.c
@@ -0,0 +1,157 @@
+/* Copyright (c) 2015, 2016 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 "lport.h"
+#include "hash.h"
+#include "openvswitch/vlog.h"
+#include "ovn/lib/ovn-sb-idl.h"
+
+VLOG_DEFINE_THIS_MODULE(lport);
+
+/* A logical port. */
+struct lport {
+    struct hmap_node name_node; /* Index by name. */
+    struct hmap_node key_node;  /* Index by (dp_key, port_key). */
+    const struct sbrec_port_binding *pb;
+};
+
+void
+lport_index_init(struct lport_index *lports, struct ovsdb_idl *ovnsb_idl)
+{
+    hmap_init(&lports->by_name);
+    hmap_init(&lports->by_key);
+
+    const struct sbrec_port_binding *pb;
+    SBREC_PORT_BINDING_FOR_EACH (pb, ovnsb_idl) {
+        if (lport_lookup_by_name(lports, pb->logical_port)) {
+            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
+            VLOG_WARN_RL(&rl, "duplicate logical port name '%s'",
+                         pb->logical_port);
+            continue;
+        }
+
+        struct lport *p = xmalloc(sizeof *p);
+        hmap_insert(&lports->by_name, &p->name_node,
+                    hash_string(pb->logical_port, 0));
+        hmap_insert(&lports->by_key, &p->key_node,
+                    hash_int(pb->tunnel_key, pb->datapath->tunnel_key));
+        p->pb = pb;
+    }
+}
+
+void
+lport_index_destroy(struct lport_index *lports)
+{
+    /* Destroy all of the "struct lport"s.
+     *
+     * We don't have to remove the node from both indexes. */
+    struct lport *port, *next;
+    HMAP_FOR_EACH_SAFE (port, next, name_node, &lports->by_name) {
+        hmap_remove(&lports->by_name, &port->name_node);
+        free(port);
+    }
+
+    hmap_destroy(&lports->by_name);
+    hmap_destroy(&lports->by_key);
+}
+
+/* Finds and returns the lport with the given 'name', or NULL if no such lport
+ * exists. */
+const struct sbrec_port_binding *
+lport_lookup_by_name(const struct lport_index *lports, const char *name)
+{
+    const struct lport *lport;
+    HMAP_FOR_EACH_WITH_HASH (lport, name_node, hash_string(name, 0),
+                             &lports->by_name) {
+        if (!strcmp(lport->pb->logical_port, name)) {
+            return lport->pb;
+        }
+    }
+    return NULL;
+}
+
+const struct sbrec_port_binding *
+lport_lookup_by_key(const struct lport_index *lports,
+                    uint32_t dp_key, uint16_t port_key)
+{
+    const struct lport *lport;
+    HMAP_FOR_EACH_WITH_HASH (lport, key_node, hash_int(port_key, dp_key),
+                             &lports->by_key) {
+        if (port_key == lport->pb->tunnel_key
+            && dp_key == lport->pb->datapath->tunnel_key) {
+            return lport->pb;
+        }
+    }
+    return NULL;
+}
+
+struct mcgroup {
+    struct hmap_node dp_name_node; /* Index by (logical datapath, name). */
+    const struct sbrec_multicast_group *mg;
+};
+
+void
+mcgroup_index_init(struct mcgroup_index *mcgroups, struct ovsdb_idl *ovnsb_idl)
+{
+    hmap_init(&mcgroups->by_dp_name);
+
+    const struct sbrec_multicast_group *sb;
+    SBREC_MULTICAST_GROUP_FOR_EACH (sb, ovnsb_idl) {
+        const struct uuid *dp_uuid = &sb->datapath->header_.uuid;
+        if (mcgroup_lookup_by_dp_name(mcgroups, sb->datapath, sb->name)) {
+            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
+            VLOG_WARN_RL(&rl, "datapath "UUID_FMT" contains duplicate "
+                         "multicast group '%s'", UUID_ARGS(dp_uuid), sb->name);
+            continue;
+        }
+
+        struct mcgroup *m = xmalloc(sizeof *m);
+        hmap_insert(&mcgroups->by_dp_name, &m->dp_name_node,
+                    hash_string(sb->name, uuid_hash(dp_uuid)));
+        m->mg = sb;
+    }
+}
+
+void
+mcgroup_index_destroy(struct mcgroup_index *mcgroups)
+{
+    struct mcgroup *mcgroup, *next;
+    HMAP_FOR_EACH_SAFE (mcgroup, next, dp_name_node, &mcgroups->by_dp_name) {
+        hmap_remove(&mcgroups->by_dp_name, &mcgroup->dp_name_node);
+        free(mcgroup);
+    }
+
+    hmap_destroy(&mcgroups->by_dp_name);
+}
+
+const struct sbrec_multicast_group *
+mcgroup_lookup_by_dp_name(const struct mcgroup_index *mcgroups,
+                          const struct sbrec_datapath_binding *dp,
+                          const char *name)
+{
+    const struct uuid *dp_uuid = &dp->header_.uuid;
+    const struct mcgroup *mcgroup;
+    HMAP_FOR_EACH_WITH_HASH (mcgroup, dp_name_node,
+                             hash_string(name, uuid_hash(dp_uuid)),
+                             &mcgroups->by_dp_name) {
+        if (uuid_equals(&mcgroup->mg->datapath->header_.uuid, dp_uuid)
+            && !strcmp(mcgroup->mg->name, name)) {
+            return mcgroup->mg;
+        }
+    }
+    return NULL;
+}
diff --git a/ovn/controller/lport.h b/ovn/controller/lport.h
new file mode 100644
index 0000000..f09e2eb
--- /dev/null
+++ b/ovn/controller/lport.h
@@ -0,0 +1,67 @@
+/* Copyright (c) 2015, 2016 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 OVN_LPORT_H
+#define OVN_LPORT_H 1
+
+#include <stdint.h>
+#include "hmap.h"
+
+struct ovsdb_idl;
+struct sbrec_datapath_binding;
+
+/* Logical port and multicast group indexes
+ * ========================================
+ *
+ * This data structure holds multiple indexes over logical ports, to allow for
+ * efficient searching for logical ports by name or number.
+ */
+
+struct lport_index {
+    struct hmap by_name;
+    struct hmap by_key;
+};
+
+void lport_index_init(struct lport_index *, struct ovsdb_idl *);
+void lport_index_destroy(struct lport_index *);
+
+const struct sbrec_port_binding *lport_lookup_by_name(
+    const struct lport_index *, const char *name);
+const struct sbrec_port_binding *lport_lookup_by_key(
+    const struct lport_index *, uint32_t dp_key, uint16_t port_key);
+
+/* Multicast group index
+ * =====================
+ *
+ * This is separate from the logical port index because of namespace issues:
+ * logical port names are globally unique, but multicast group names are only
+ * unique within the scope of a logical datapath.
+ *
+ * Multicast groups could be indexed by number also, but so far the clients do
+ * not need this index. */
+
+struct mcgroup_index {
+    struct hmap by_dp_name;
+};
+
+void mcgroup_index_init(struct mcgroup_index *, struct ovsdb_idl *);
+void mcgroup_index_destroy(struct mcgroup_index *);
+
+const struct sbrec_multicast_group *mcgroup_lookup_by_dp_name(
+    const struct mcgroup_index *,
+    const struct sbrec_datapath_binding *,
+    const char *name);
+
+#endif /* ovn/lport.h */
diff --git a/ovn/controller/ovn-controller.c b/ovn/controller/ovn-controller.c
index 9799a19..517ca2a 100644
--- a/ovn/controller/ovn-controller.c
+++ b/ovn/controller/ovn-controller.c
@@ -23,33 +23,33 @@
 #include <stdlib.h>
 #include <string.h>
 
+#include "binding.h"
+#include "chassis.h"
 #include "command-line.h"
 #include "compiler.h"
 #include "daemon.h"
 #include "dirs.h"
 #include "dynamic-string.h"
+#include "encaps.h"
+#include "fatal-signal.h"
+#include "hmap.h"
+#include "lflow.h"
+#include "lib/vswitch-idl.h"
+#include "lport.h"
+#include "ofctrl.h"
 #include "openvswitch/vconn.h"
 #include "openvswitch/vlog.h"
 #include "ovn/lib/ovn-sb-idl.h"
+#include "patch.h"
+#include "physical.h"
+#include "pinctrl.h"
 #include "poll-loop.h"
-#include "fatal-signal.h"
-#include "lib/hmap.h"
-#include "lib/vswitch-idl.h"
 #include "smap.h"
-#include "stream.h"
 #include "stream-ssl.h"
+#include "stream.h"
 #include "unixctl.h"
 #include "util.h"
 
-#include "ofctrl.h"
-#include "pinctrl.h"
-#include "binding.h"
-#include "chassis.h"
-#include "encaps.h"
-#include "patch.h"
-#include "physical.h"
-#include "lflow.h"
-
 VLOG_DEFINE_THIS_MODULE(main);
 
 static unixctl_cb_func ovn_controller_exit;
@@ -295,18 +295,26 @@ main(int argc, char *argv[])
         if (br_int) {
             patch_run(&ctx, br_int, &local_datapaths);
 
+            struct lport_index lports;
+            struct mcgroup_index mcgroups;
+            lport_index_init(&lports, ctx.ovnsb_idl);
+            mcgroup_index_init(&mcgroups, ctx.ovnsb_idl);
+
             enum mf_field_id mff_ovn_geneve = ofctrl_run(br_int);
 
             pinctrl_run(br_int);
 
             struct hmap flow_table = HMAP_INITIALIZER(&flow_table);
-            lflow_run(&ctx, &flow_table, &ct_zones, &local_datapaths);
+            lflow_run(&ctx, &lports, &mcgroups, &local_datapaths,
+                      &ct_zones, &flow_table);
             if (chassis_id) {
                 physical_run(&ctx, mff_ovn_geneve,
                              br_int, chassis_id, &ct_zones, &flow_table);
             }
             ofctrl_put(&flow_table);
             hmap_destroy(&flow_table);
+            mcgroup_index_destroy(&mcgroups);
+            lport_index_destroy(&lports);
         }
 
         /* local_datapaths contains bare hmap_node instances.
-- 
2.1.3




More information about the dev mailing list