[ovs-dev] [PATCH ovs V3 12/25] dpif-netlink: Use netdev flow put api to insert a flow

Roi Dayan roid at mellanox.com
Wed Feb 8 15:29:25 UTC 2017


From: Paul Blakey <paulb at 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 at mellanox.com>
Reviewed-by: Roi Dayan <roid at mellanox.com>
---
 lib/dpif-netlink.c | 222 +++++++++++++++++++++++++++++++++++++++++++++++++++--
 lib/odp-util.c     |  53 +++++++++++++
 lib/odp-util.h     |   3 +
 3 files changed, 271 insertions(+), 7 deletions(-)

diff --git a/lib/dpif-netlink.c b/lib/dpif-netlink.c
index 5519c10..a7525de 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. */
@@ -1764,8 +1765,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;
 
@@ -1774,12 +1773,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];
@@ -1922,18 +1921,227 @@ dpif_netlink_operate__(struct dpif_netlink *dpif,
     return n_ops;
 }
 
+static int
+parse_flow_get(struct dpif_netlink *dpif, struct dpif_flow_get *get)
+{
+    struct dpif_flow *dpif_flow = get->flow;
+    struct match match;
+    struct nlattr *actions;
+    struct dpif_flow_stats stats;
+    struct ofpbuf buf;
+    uint64_t act_buf[1024 / 8];
+    struct odputil_keybuf maskbuf;
+    struct odputil_keybuf keybuf;
+    struct odputil_keybuf actbuf;
+    struct ofpbuf key, mask, act;
+    int err;
+
+    ofpbuf_use_stack(&buf, &act_buf, sizeof act_buf);
+    err = netdev_ports_flow_get(DPIF_HMAP_KEY(&dpif->dpif), &match,
+                                &actions, &stats, get->ufid, &buf);
+    if (err) {
+        return err;
+    }
+
+    VLOG_DBG("found flow from netdev, translating to dpif flow");
+
+    ofpbuf_use_stack(&key, &keybuf, sizeof keybuf);
+    ofpbuf_use_stack(&act, &actbuf, sizeof actbuf);
+    ofpbuf_use_stack(&mask, &maskbuf, sizeof maskbuf);
+    dpif_netlink_netdev_match_to_dpif_flow(&match, &key, &mask, actions,
+                                           &stats,
+                                           (ovs_u128 *) get->ufid,
+                                           dpif_flow,
+                                           false);
+    ofpbuf_put(get->buffer, nl_attr_get(actions), nl_attr_get_size(actions));
+    dpif_flow->actions = ofpbuf_at(get->buffer, 0, 0);
+    dpif_flow->actions_len = nl_attr_get_size(actions);
+
+    // XXX: missing ofpbuf_uninit?
+    return 0;
+}
+
+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 = 0;
+
+    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;
+    }
+
+    /* 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_WARN_RL(&rl, "offloading multiple ports isn't supported");
+                err = EOPNOTSUPP;
+                goto err_out;
+            }
+
+            out_port = nl_attr_get_odp_port(nla);
+            outdev = netdev_hmap_port_get(out_port, DPIF_HMAP_KEY(&dpif->dpif));
+            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;
+    in_port = match.flow.in_port.odp_port;
+    dev = netdev_hmap_port_get(in_port, DPIF_HMAP_KEY(&dpif->dpif));
+    err = netdev_flow_put(dev, &match,
+                          CONST_CAST(struct nlattr *, put->actions),
+                          put->actions_len, put->stats,
+                          CONST_CAST(ovs_u128 *, put->ufid), &info);
+    netdev_close(dev);
+
+    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));
+    }
+
+err_out:
+
+    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");
+        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);
 
-- 
2.7.4



More information about the dev mailing list