diff mbox

[ovs-dev,ovs,RFC,8/9] dpif-hw-netlink: support for flow dump from tc

Message ID 1474980364-9291-9-git-send-email-paulb@mellanox.com
State RFC
Headers show

Commit Message

Paul Blakey Sept. 27, 2016, 12:46 p.m. UTC
added support dump flows from tc.

Signed-off-by: Paul Blakey <paulb@mellanox.com>
Signed-off-by: Shahar Klein <shahark@mellanox.com>
---
 lib/dpif-hw-netlink.c | 215 ++++++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 199 insertions(+), 16 deletions(-)
diff mbox

Patch

diff --git a/lib/dpif-hw-netlink.c b/lib/dpif-hw-netlink.c
index 9473832..663b15b 100644
--- a/lib/dpif-hw-netlink.c
+++ b/lib/dpif-hw-netlink.c
@@ -852,63 +852,246 @@  static int
 dpif_hw_netlink_flow_flush(struct dpif *dpif_)
 {
     struct dpif_hw_netlink *dpif = dpif_hw_netlink_cast(dpif_);
+    struct port_netdev_hash_data *data;
 
+    VLOG_DBG("%s %d %s, (%p) flush start\n", __FILE__, __LINE__, __func__,
+             dpif);
+    HMAP_FOR_EACH(data, node, &dpif->port_to_netdev) {
+        if (data->netdev) {
+            VLOG_DBG("%s %d %s, (%p) flusing port: %d, netdev: %p\n", __FILE__,
+                     __LINE__, __func__, dpif, data->port, data->netdev);
+            tc_flush_flower(netdev_get_ifindex(data->netdev));
+        }
+    }
+
+    VLOG_DBG("%s %d %s, (%p) flush end\n", __FILE__, __LINE__, __func__, dpif);
     return dpif->lp_dpif_netlink->dpif_class->
         flow_flush(dpif->lp_dpif_netlink);
 }
 
+struct dpif_hw_netlink_flow_dump {
+    struct dpif_flow_dump up;
+    struct dpif_flow_dump *netlink_dump;
+    struct nl_dump *flow_dumps;
+    int num_dumps;
+    int given;
+    struct ovs_mutex lock;
+    odp_port_t ports[10];
+    struct netdev *netdevs[10];
+};
+
+static struct dpif_hw_netlink_flow_dump *
+dpif_hw_netlink_flow_dump_cast(struct dpif_flow_dump *dump)
+{
+    return CONTAINER_OF(dump, struct dpif_hw_netlink_flow_dump, up);
+}
+
 static struct dpif_flow_dump *
 dpif_hw_netlink_flow_dump_create(const struct dpif *dpif_, bool terse)
 {
-    struct dpif_flow_dump *dump;
+    struct dpif_hw_netlink_flow_dump *dump;
     struct dpif_hw_netlink *dpif = dpif_hw_netlink_cast(dpif_);
+    struct nl_dump *flow_dumps = 0;
+    int count = 0;
 
-    dump =
-        dpif->lp_dpif_netlink->dpif_class->
-        flow_dump_create(dpif->lp_dpif_netlink, terse);
-    dump->dpif = CONST_CAST(struct dpif *, dpif_);
+    int num_ports = hmap_count(&dpif->port_to_netdev);
+
+    dump = xmalloc(sizeof *dump);
+    dpif_flow_dump_init(&dump->up, dpif_);
+    dump->up.terse = terse;
 
-    return dump;
+    if (num_ports) {
+        flow_dumps = xmalloc(sizeof (struct nl_dump) * num_ports);
 
+        struct port_netdev_hash_data *data;
+
+        HMAP_FOR_EACH(data, node, &dpif->port_to_netdev) {
+            if (data->netdev) {
+                dump->ports[count] = data->port;
+                dump->netdevs[count] = data->netdev;
+
+                tc_dump_flower_start(netdev_get_ifindex(data->netdev),
+                                     &flow_dumps[count]);
+                count++;
+            }
+        }
+    }
+
+    dump->netlink_dump =
+        dpif->lp_dpif_netlink->dpif_class->
+        flow_dump_create(dpif->lp_dpif_netlink, terse);
+    dump->flow_dumps = flow_dumps;
+    dump->num_dumps = count;
+    dump->given = 0;
+    ovs_mutex_init(&dump->lock);
+    return &dump->up;
 }
 
 static int
 dpif_hw_netlink_flow_dump_destroy(struct dpif_flow_dump *dump_)
 {
+    int error;
+    struct dpif_hw_netlink_flow_dump *dump =
+        dpif_hw_netlink_flow_dump_cast(dump_);
     struct dpif_hw_netlink *dpif = dpif_hw_netlink_cast(dump_->dpif);
 
-    dump_->dpif = dpif->lp_dpif_netlink;
-    return dpif->lp_dpif_netlink->dpif_class->flow_dump_destroy(dump_);
+    int cur = 0;
+
+    for (cur = 0; cur < dump->num_dumps; cur++) {
+        struct nl_dump *nl_dump = &dump->flow_dumps[cur];
+
+        int ret = nl_dump_done(nl_dump);
+
+        if (ret != 0)
+            VLOG_ERR("nl_dump_done error  ret[%d]: %d\n", cur, ret);
+    }
+
+    error =
+        dpif->lp_dpif_netlink->dpif_class->
+        flow_dump_destroy(dump->netlink_dump);
+
+    if (dump->flow_dumps)
+        free(dump->flow_dumps);
+    free(dump);
+
+    return error;
+}
+
+struct dpif_hw_netlink_flow_dump_thread {
+    struct dpif_flow_dump_thread up;
+    struct dpif_flow_dump_thread *netlink_thread;
+    struct dpif_hw_netlink_flow_dump *dump;
+    struct ofpbuf nl_flows;
+    struct ofpbuf temp_buf;
+    int current_dump;
+    int flower_done;
+};
+
+static void
+dpif_hw_netlink_get_next_dump(struct dpif_hw_netlink_flow_dump_thread *thread)
+{
+    /* TODO:Consider changing to a atomc dump->given... */
+
+    struct dpif_hw_netlink_flow_dump *dump = thread->dump;
+
+    ovs_mutex_lock(&dump->lock);
+    /* if we haven't finished (dumped everything) */
+    if (dump->given < dump->num_dumps) {
+        /* if we are the first to find that given dump is finished (for race
+         * condition, e.g 3 finish dump 0 at the same time) */
+        if (thread->current_dump == dump->given) {
+            thread->current_dump = ++dump->given;
+            /* did we just finish the last dump? done. */
+            if (dump->given == dump->num_dumps) {
+                thread->flower_done = 1;
+            }
+        } else
+            /* otherwise, we are behind, catch up */
+            thread->current_dump = dump->given;
+    } else {
+        /* some other thread finished */
+        thread->flower_done = 1;
+    }
+    ovs_mutex_unlock(&dump->lock);
 }
 
 static struct dpif_flow_dump_thread *
 dpif_hw_netlink_flow_dump_thread_create(struct dpif_flow_dump *dump_)
 {
+    struct dpif_hw_netlink_flow_dump *dump =
+        dpif_hw_netlink_flow_dump_cast(dump_);
     struct dpif_hw_netlink *dpif = dpif_hw_netlink_cast(dump_->dpif);
+    struct dpif_hw_netlink_flow_dump_thread *thread;
 
-    return dpif->lp_dpif_netlink->dpif_class->flow_dump_thread_create(dump_);
+    thread = xmalloc(sizeof *thread);
+    dpif_flow_dump_thread_init(&thread->up, &dump->up);
+    thread->netlink_thread =
+        dpif->lp_dpif_netlink->dpif_class->
+        flow_dump_thread_create(dump->netlink_dump);
+    thread->dump = dump;
+
+    /* 
+     * A thread can be created at any time, 
+     * so another thread might finish the dump already (and advance dump->given), 
+     * so we might be done before we even started. 
+     */
+
+    ovs_mutex_lock(&dump->lock);
+    thread->current_dump = dump->given;
+    thread->flower_done = dump->given < dump->num_dumps ? 0 : 1;
+    ovs_mutex_unlock(&dump->lock);
+
+    if (!thread->flower_done) {
+        ofpbuf_init(&thread->nl_flows, NL_DUMP_BUFSIZE);        /* TODO:
+                                                                 * uninit
+                                                                 * where? */
+        ofpbuf_init(&thread->temp_buf, NL_DUMP_BUFSIZE);
+    }
+    /* another option is setting current to -1, and calling get_next_dump, but 
+     * its kinda ugly */
+    return &thread->up;
 
 }
 
+static struct dpif_hw_netlink_flow_dump_thread *
+dpif_hw_netlink_flow_dump_thread_cast(struct dpif_flow_dump_thread *thread)
+{
+    return CONTAINER_OF(thread, struct dpif_hw_netlink_flow_dump_thread, up);
+}
+
 static void
 dpif_hw_netlink_flow_dump_thread_destroy(struct dpif_flow_dump_thread *thread_)
 {
     struct dpif_hw_netlink *dpif = dpif_hw_netlink_cast(thread_->dpif);
+    struct dpif_hw_netlink_flow_dump_thread *thread
+        = dpif_hw_netlink_flow_dump_thread_cast(thread_);
+
+    dpif->lp_dpif_netlink->dpif_class->
+        flow_dump_thread_destroy(thread->netlink_thread);
 
-    thread_->dpif = dpif->lp_dpif_netlink;
-    return dpif->lp_dpif_netlink->
-        dpif_class->flow_dump_thread_destroy(thread_);
+    free(thread);
 }
 
 static int
 dpif_hw_netlink_flow_dump_next(struct dpif_flow_dump_thread *thread_,
                                struct dpif_flow *flows, int max_flows)
 {
-    struct dpif_hw_netlink *dpif = dpif_hw_netlink_cast(thread_->dpif);
+    struct dpif_hw_netlink_flow_dump_thread *thread
+        = dpif_hw_netlink_flow_dump_thread_cast(thread_);
+    struct dpif_hw_netlink_flow_dump *dump = thread->dump;
+    struct dpif_hw_netlink *dpif = dpif_hw_netlink_cast(thread->up.dpif);
+    int n_flows = 0;
+
+    while (!thread->flower_done && n_flows < max_flows) {
+        int cur = thread->current_dump;
+        odp_port_t inport = dump->ports[cur];
+        struct netdev *indev = dump->netdevs[cur];
+        struct ofpbuf nl_flow;
+        struct nl_dump *nl_dump = &dump->flow_dumps[cur];
+
+        if (nl_dump_next(nl_dump, &nl_flow, &thread->nl_flows)) {
+            struct tc_flow tc_flow;
+
+            if (parse_tc_flow(&nl_flow, &tc_flow))
+                continue;
+
+            /* if we got handle, convert netlink flow to dpif_flow */
+            if (tc_flow.handle)
+                dpif_hw_tc_flow_to_dpif_flow(dpif, &tc_flow, &flows[n_flows++],
+                                             inport, &thread->temp_buf, indev);
+        } else
+            dpif_hw_netlink_get_next_dump(thread);
+    }
 
-    thread_->dpif = dpif->lp_dpif_netlink;
-    return dpif->lp_dpif_netlink->dpif_class->flow_dump_next(thread_, flows,
-                                                             max_flows);
+    /* if we got here, flower done or got to max flows if flow done and not
+     * got got max, call kernel datapath to dump remaining flows */
+    if (thread->flower_done && n_flows < max_flows) {
+        return n_flows +
+            dpif->lp_dpif_netlink->dpif_class->
+            flow_dump_next(thread->netlink_thread, flows + n_flows,
+                           max_flows - n_flows);
+    }
+    return n_flows;
 }
 
 static bool