[ovs-dev,ovs,V4,11/24] dpif-netlink: Use netdev flow put api to insert a flow

Submitted by Roi Dayan on March 13, 2017, 1:37 p.m.

Details

Message ID 1489412234-30916-12-git-send-email-roid@mellanox.com
State Superseded
Headers show

Commit Message

Roi Dayan March 13, 2017, 1:37 p.m.
From: Paul Blakey <paulb@mellanox.com>

Using the new netdev flow api operate will now try and
offload flows to the relevant netdev of the input port.
Other operate methods flows will come in later patches.

Signed-off-by: Paul Blakey <paulb@mellanox.com>
Reviewed-by: Roi Dayan <roid@mellanox.com>
---
 lib/dpif-netlink.c |  213 ++++++++++++++++++++++++++++++++++++++++++++++++++--
 lib/odp-util.c     |   53 +++++++++++++
 lib/odp-util.h     |    3 +
 3 files changed, 262 insertions(+), 7 deletions(-)

Patch hide | download patch | download mbox

diff --git a/lib/dpif-netlink.c b/lib/dpif-netlink.c
index 566edff..59b87ff 100644
--- a/lib/dpif-netlink.c
+++ b/lib/dpif-netlink.c
@@ -72,6 +72,7 @@  enum { MAX_PORTS = USHRT_MAX };
 #define ETH_FLAG_LRO      (1 << 15)    /* LRO is enabled */
 
 #define FLOW_DUMP_MAX_BATCH 50
+#define OPERATE_MAX_OPS 50
 
 struct dpif_netlink_dp {
     /* Generic Netlink header. */
@@ -1763,8 +1764,6 @@  static size_t
 dpif_netlink_operate__(struct dpif_netlink *dpif,
                        struct dpif_op **ops, size_t n_ops)
 {
-    enum { MAX_OPS = 50 };
-
     struct op_auxdata {
         struct nl_transaction txn;
 
@@ -1773,12 +1772,12 @@  dpif_netlink_operate__(struct dpif_netlink *dpif,
 
         struct ofpbuf reply;
         uint64_t reply_stub[1024 / 8];
-    } auxes[MAX_OPS];
+    } auxes[OPERATE_MAX_OPS];
 
-    struct nl_transaction *txnsp[MAX_OPS];
+    struct nl_transaction *txnsp[OPERATE_MAX_OPS];
     size_t i;
 
-    n_ops = MIN(n_ops, MAX_OPS);
+    n_ops = MIN(n_ops, OPERATE_MAX_OPS);
     for (i = 0; i < n_ops; i++) {
         struct op_auxdata *aux = &auxes[i];
         struct dpif_op *op = ops[i];
@@ -1921,18 +1920,218 @@  dpif_netlink_operate__(struct dpif_netlink *dpif,
     return n_ops;
 }
 
+static int
+parse_flow_put(struct dpif_netlink *dpif, struct dpif_flow_put *put)
+{
+    static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
+    struct match match;
+    odp_port_t in_port;
+    const struct nlattr *nla;
+    size_t left;
+    int outputs = 0;
+    struct netdev *dev;
+    struct offload_info info;
+    ovs_be16 dst_port = 0;
+    int err;
+
+    if (put->flags & DPIF_FP_PROBE) {
+        return EOPNOTSUPP;
+    }
+
+    err = parse_key_and_mask_to_match(put->key, put->key_len, put->mask,
+                                      put->mask_len, &match);
+    if (err) {
+        return err;
+    }
+
+    /* When we try to install a dummy flow from a probed feature. */
+    if (match.flow.dl_type == htons(0x1234)) {
+        return EOPNOTSUPP;
+    }
+
+    in_port = match.flow.in_port.odp_port;
+    dev = netdev_hmap_port_get(in_port, DPIF_HMAP_KEY(&dpif->dpif));
+    if (!dev) {
+        return EOPNOTSUPP;
+    }
+
+    /* Get tunnel dst port and count outputs */
+    NL_ATTR_FOR_EACH(nla, left, put->actions, put->actions_len) {
+        if (nl_attr_type(nla) == OVS_ACTION_ATTR_OUTPUT) {
+            const struct netdev_tunnel_config *tnl_cfg;
+            struct netdev *outdev;
+            odp_port_t out_port;
+
+            outputs++;
+            if (outputs > 1) {
+                VLOG_DBG_RL(&rl, "offloading multiple ports isn't supported");
+                err = EOPNOTSUPP;
+                goto out;
+            }
+
+            out_port = nl_attr_get_odp_port(nla);
+            outdev = netdev_hmap_port_get(out_port, DPIF_HMAP_KEY(&dpif->dpif));
+            if (!outdev) {
+                err = EOPNOTSUPP;
+                goto out;
+            }
+            tnl_cfg = netdev_get_tunnel_config(outdev);
+            if (tnl_cfg && tnl_cfg->dst_port != 0) {
+                dst_port = tnl_cfg->dst_port;
+            }
+            netdev_close(outdev);
+        }
+    }
+
+    info.port_hmap_obj = DPIF_HMAP_KEY(&dpif->dpif);
+    info.tp_dst_port = dst_port;
+    err = netdev_flow_put(dev, &match,
+                          CONST_CAST(struct nlattr *, put->actions),
+                          put->actions_len, put->stats,
+                          CONST_CAST(ovs_u128 *, put->ufid), &info);
+
+    if (!err) {
+        if (put->flags & DPIF_FP_MODIFY) {
+            struct dpif_op *opp;
+            struct dpif_op op;
+
+            op.type = DPIF_OP_FLOW_DEL;
+            op.u.flow_del.key = put->key;
+            op.u.flow_del.key_len = put->key_len;
+            op.u.flow_del.ufid = put->ufid;
+            op.u.flow_del.pmd_id = put->pmd_id;
+            op.u.flow_del.stats = NULL;
+            op.u.flow_del.terse = false;
+
+            opp = &op;
+            dpif_netlink_operate__(dpif, &opp, 1);
+        }
+
+        VLOG_DBG("added flow");
+    } else if (err != EEXIST) {
+        VLOG_ERR_RL(&rl, "failed adding flow: %s", ovs_strerror(err));
+    }
+
+out:
+    if (err && err != EEXIST && (put->flags & DPIF_FP_MODIFY)) {
+        /* Modified rule can't be offloaded, try and delete from HW */
+        int del_err = netdev_flow_del(dev, put->ufid, put->stats);
+
+        if (!del_err) {
+            /* Delete from hw success, so old flow was offloaded.
+             * Change flags to create the flow in kernel */
+            put->flags &= ~DPIF_FP_MODIFY;
+            put->flags |= DPIF_FP_CREATE;
+        } else if (del_err != ENOENT) {
+            VLOG_ERR_RL(&rl, "failed to delete offloaded flow: %s",
+                        ovs_strerror(del_err));
+            /* stop proccesing the flow in kernel */
+            err = 0;
+        }
+    }
+
+    netdev_close(dev);
+
+    return err;
+}
+
 static void
-dpif_netlink_operate(struct dpif *dpif_, struct dpif_op **ops, size_t n_ops)
+dbg_print_flow(const struct nlattr *key, size_t key_len,
+               const struct nlattr *mask, size_t mask_len,
+               const struct nlattr *actions, size_t actions_len,
+               const ovs_u128 *ufid,
+               const char *op)
+{
+        struct ds s;
+
+        ds_init(&s);
+        ds_put_cstr(&s, op);
+        ds_put_cstr(&s, " (");
+        odp_format_ufid(ufid, &s);
+        ds_put_cstr(&s, ")");
+        if (key_len) {
+            ds_put_cstr(&s, "\nflow (verbose): ");
+            odp_flow_format(key, key_len, mask, mask_len, NULL, &s, true);
+            ds_put_cstr(&s, "\nflow: ");
+            odp_flow_format(key, key_len, mask, mask_len, NULL, &s, false);
+        }
+        ds_put_cstr(&s, "\nactions: ");
+        format_odp_actions(&s, actions, actions_len);
+        VLOG_DBG("\n%s", ds_cstr(&s));
+        ds_destroy(&s);
+}
+
+static int
+try_send_to_netdev(struct dpif_netlink *dpif, struct dpif_op *op)
 {
-    struct dpif_netlink *dpif = dpif_netlink_cast(dpif_);
+    switch (op->type) {
+    case DPIF_OP_FLOW_PUT: {
+        struct dpif_flow_put *put = &op->u.flow_put;
 
+        if (!put->ufid) {
+            break;
+        }
+        dbg_print_flow(put->key, put->key_len, put->mask, put->mask_len,
+                       put->actions, put->actions_len, put->ufid,
+                       (put->flags & DPIF_FP_MODIFY ? "PUT(MODIFY)" : "PUT"));
+        return parse_flow_put(dpif, put);
+    }
+    case DPIF_OP_FLOW_DEL:
+    case DPIF_OP_FLOW_GET:
+    case DPIF_OP_EXECUTE:
+    default:
+        break;
+    }
+    return EOPNOTSUPP;
+}
+
+static void
+dpif_netlink_operate_chunks(struct dpif_netlink *dpif, struct dpif_op **ops,
+                            size_t n_ops)
+{
     while (n_ops > 0) {
         size_t chunk = dpif_netlink_operate__(dpif, ops, n_ops);
+
         ops += chunk;
         n_ops -= chunk;
     }
 }
 
+static void
+dpif_netlink_operate(struct dpif *dpif_, struct dpif_op **ops, size_t n_ops)
+{
+    struct dpif_netlink *dpif = dpif_netlink_cast(dpif_);
+    struct dpif_op *new_ops[OPERATE_MAX_OPS];
+    int count = 0;
+    int i = 0;
+    int err = 0;
+
+    if (netdev_flow_api_enabled) {
+        while (n_ops > 0) {
+            count = 0;
+
+            while (n_ops > 0 && count < OPERATE_MAX_OPS) {
+                struct dpif_op *op = ops[i++];
+
+                err = try_send_to_netdev(dpif, op);
+                if (err && err != EEXIST) {
+                    new_ops[count++] = op;
+                } else {
+                    op->error = err;
+                }
+
+                n_ops--;
+            }
+
+            dpif_netlink_operate_chunks(dpif, new_ops, count);
+        }
+
+        return;
+    }
+
+    dpif_netlink_operate_chunks(dpif, ops, n_ops);
+}
+
 #if _WIN32
 static void
 dpif_netlink_handler_uninit(struct dpif_handler *handler)
diff --git a/lib/odp-util.c b/lib/odp-util.c
index 4106738..274d2ee 100644
--- a/lib/odp-util.c
+++ b/lib/odp-util.c
@@ -41,6 +41,7 @@ 
 #include "util.h"
 #include "uuid.h"
 #include "openvswitch/vlog.h"
+#include "openvswitch/match.h"
 
 VLOG_DEFINE_THIS_MODULE(odp_util);
 
@@ -5294,6 +5295,58 @@  odp_flow_key_to_mask(const struct nlattr *mask_key, size_t mask_key_len,
     }
 }
 
+/* Converts the netlink formated key/mask to match.
+ * Fails if odp_flow_key_from_key/mask and odp_flow_key_key/mask
+ * disagree on the acceptable form of flow */
+int
+parse_key_and_mask_to_match(const struct nlattr *key, size_t key_len,
+                            const struct nlattr *mask, size_t mask_len,
+                            struct match *match)
+{
+    enum odp_key_fitness fitness;
+
+    fitness = odp_flow_key_to_flow(key, key_len, &match->flow);
+    if (fitness) {
+        /* This should not happen: it indicates that odp_flow_key_from_flow()
+         * and odp_flow_key_to_flow() disagree on the acceptable form of a
+         * flow.  Log the problem as an error, with enough details to enable
+         * debugging. */
+        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+
+        if (!VLOG_DROP_ERR(&rl)) {
+            struct ds s;
+
+            ds_init(&s);
+            odp_flow_format(key, key_len, NULL, 0, NULL, &s, true);
+            VLOG_ERR("internal error parsing flow key %s", ds_cstr(&s));
+            ds_destroy(&s);
+        }
+
+        return EINVAL;
+    }
+
+    fitness = odp_flow_key_to_mask(mask, mask_len, &match->wc, &match->flow);
+    if (fitness) {
+        /* This should not happen: it indicates that
+         * odp_flow_key_from_mask() and odp_flow_key_to_mask()
+         * disagree on the acceptable form of a mask.  Log the problem
+         * as an error, with enough details to enable debugging. */
+        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+
+        if (!VLOG_DROP_ERR(&rl)) {
+            struct ds s;
+
+            VLOG_ERR("internal error parsing flow mask %s (%s)",
+                     ds_cstr(&s), odp_key_fitness_to_string(fitness));
+            ds_destroy(&s);
+        }
+
+        return EINVAL;
+    }
+
+    return 0;
+}
+
 /* Returns 'fitness' as a string, for use in debug messages. */
 const char *
 odp_key_fitness_to_string(enum odp_key_fitness fitness)
diff --git a/lib/odp-util.h b/lib/odp-util.h
index 42011bc..3f59ab7 100644
--- a/lib/odp-util.h
+++ b/lib/odp-util.h
@@ -236,6 +236,9 @@  enum odp_key_fitness odp_flow_key_to_mask(const struct nlattr *mask_key,
                                           size_t mask_key_len,
                                           struct flow_wildcards *mask,
                                           const struct flow *flow);
+int parse_key_and_mask_to_match(const struct nlattr *key, size_t key_len,
+                                const struct nlattr *mask, size_t mask_len,
+                                struct match *match);
 
 const char *odp_key_fitness_to_string(enum odp_key_fitness);