Call the netdev-offload flow put/del/get APIs through the
dpif-offload-tc layer, and remove them from the netdev-offload layer.

Note that we still need to remove the dependency on the
netdev-offload infrastructure for initializing and locating netdevs.
This will be addressed in a separate patch.

Signed-off-by: Eelco Chaudron <[email protected]>
---
 lib/dpif-netlink.c      |   2 +-
 lib/dpif-offload-tc.c   | 241 ++++++++++++++++++++++++++++++++++++++++
 lib/netdev-offload-tc.c |  37 +++---
 lib/netdev-offload-tc.h |   9 ++
 lib/netdev-offload.h    |   1 +
 5 files changed, 269 insertions(+), 21 deletions(-)

diff --git a/lib/dpif-netlink.c b/lib/dpif-netlink.c
index 362272da3..e61fbc0c5 100644
--- a/lib/dpif-netlink.c
+++ b/lib/dpif-netlink.c
@@ -2213,7 +2213,7 @@ out:
              * Change flags to create the flow in kernel */
             put->flags &= ~DPIF_FP_MODIFY;
             put->flags |= DPIF_FP_CREATE;
-        } else if (del_err != ENOENT) {
+        } else if (del_err != ENOENT && del_err != EOPNOTSUPP) {
             VLOG_ERR_RL(&rl, "failed to delete offloaded flow: %s",
                         ovs_strerror(del_err));
             /* stop proccesing the flow in kernel */
diff --git a/lib/dpif-offload-tc.c b/lib/dpif-offload-tc.c
index c343cd9c1..d8fae7ed6 100644
--- a/lib/dpif-offload-tc.c
+++ b/lib/dpif-offload-tc.c
@@ -40,6 +40,7 @@ struct dpif_offload_tc {
 
     /* Configuration specific variables. */
     struct ovsthread_once once_enable; /* Track first-time enablement. */
+    bool recirc_id_shared;
 };
 
 /* tc's flow dump specific data structures. */
@@ -184,6 +185,11 @@ dpif_offload_tc_open(const struct dpif_offload_class 
*offload_class,
     offload_tc->port_mgr = dpif_offload_port_mgr_init();
     offload_tc->once_enable = (struct ovsthread_once) \
                               OVSTHREAD_ONCE_INITIALIZER;
+    offload_tc->recirc_id_shared = !!(dpif_get_features(dpif)
+                                      & OVS_DP_F_TC_RECIRC_SHARING);
+
+    VLOG_DBG("Datapath %s recirculation id sharing ",
+             offload_tc->recirc_id_shared ? "supports" : "does not support");
 
     dpif_offload_tc_meter_init();
 
@@ -587,6 +593,240 @@ dpif_offload_tc_flow_dump_thread_destroy(
     free(thread);
 }
 
+static int
+dpif_offload_tc_parse_flow_put(struct dpif_offload_tc *offload_tc,
+                               struct dpif *dpif, struct dpif_flow_put *put)
+{
+    static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
+    struct dpif_offload_port_mgr_port *port;
+    const struct nlattr *nla;
+    struct offload_info info;
+    struct match match;
+    odp_port_t in_port;
+    size_t left;
+    int err;
+
+    info.tc_modify_flow_deleted = false;
+    info.tc_modify_flow = false;
+
+    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;
+    }
+
+    in_port = match.flow.in_port.odp_port;
+    port = dpif_offload_port_mgr_find_by_odp_port(offload_tc->port_mgr,
+                                                  in_port);
+    if (!port) {
+        return EOPNOTSUPP;
+    }
+
+    /* Check the output port for a tunnel. */
+    NL_ATTR_FOR_EACH (nla, left, put->actions, put->actions_len) {
+        if (nl_attr_type(nla) == OVS_ACTION_ATTR_OUTPUT) {
+            struct dpif_offload_port_mgr_port *mgr_port;
+            odp_port_t out_port;
+
+            out_port = nl_attr_get_odp_port(nla);
+            mgr_port = dpif_offload_port_mgr_find_by_odp_port(
+                offload_tc->port_mgr, out_port);
+
+            if (!mgr_port) {
+                err = EOPNOTSUPP;
+                goto out;
+            }
+        }
+    }
+
+    info.recirc_id_shared_with_tc = offload_tc->recirc_id_shared;
+
+    err = netdev_offload_tc_flow_put(port->netdev, &match,
+                                     CONST_CAST(struct nlattr *, put->actions),
+                                     put->actions_len,
+                                     CONST_CAST(ovs_u128 *, put->ufid),
+                                     &info, put->stats);
+
+    if (!err) {
+        if (put->flags & DPIF_FP_MODIFY && !info.tc_modify_flow) {
+            struct dpif_op *opp;
+            struct dpif_op op;
+
+            op.type = DPIF_OP_FLOW_DEL;
+            op.flow_del.key = put->key;
+            op.flow_del.key_len = put->key_len;
+            op.flow_del.ufid = put->ufid;
+            op.flow_del.pmd_id = put->pmd_id;
+            op.flow_del.stats = NULL;
+            op.flow_del.terse = false;
+
+            opp = &op;
+            dpif_operate(dpif, &opp, 1, DPIF_OFFLOAD_NEVER);
+        }
+
+        VLOG_DBG("added flow");
+    } else if (err != EEXIST) {
+        struct netdev *oor_netdev = NULL;
+        enum vlog_level level;
+
+        if (err == ENOSPC
+            && dpif_offload_is_offload_rebalance_policy_enabled()) {
+            /*
+             * We need to set OOR on the input netdev (i.e, 'dev') for the
+             * flow.  But if the flow has a tunnel attribute (i.e, decap
+             * action, with a virtual device like a VxLAN interface as its
+             * in-port), then lookup and set OOR on the underlying tunnel
+             * (real) netdev. */
+            oor_netdev = flow_get_tunnel_netdev(&match.flow.tunnel);
+            if (!oor_netdev) {
+                /* Not a 'tunnel' flow. */
+                oor_netdev = port->netdev;
+            }
+            netdev_set_hw_info(oor_netdev, HW_INFO_TYPE_OOR, true);
+        }
+        level = (err == ENOSPC || err == EOPNOTSUPP) ? VLL_DBG : VLL_ERR;
+        VLOG_RL(&rl, level, "failed to offload flow: %s: %s",
+                ovs_strerror(err),
+                (oor_netdev ? netdev_get_name(oor_netdev) :
+                              netdev_get_name(port->netdev)));
+    }
+
+out:
+    if (err && err != EEXIST && (put->flags & DPIF_FP_MODIFY)) {
+        /* Modified rule can't be offloaded, try and delete from HW. */
+        int del_err = 0;
+
+        if (!info.tc_modify_flow_deleted) {
+            del_err = netdev_offload_tc_flow_del(put->ufid, put->stats);
+        }
+
+        if (!del_err) {
+            /* Delete from hw success, so old flow was offloaded.
+             * Change flags to create the flow at the dpif level. */
+            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 processing the flow in kernel. */
+            err = 0;
+        }
+    }
+
+    return err;
+}
+
+static int
+dpif_offload_tc_parse_flow_get(struct dpif_offload_tc *offload_tc,
+                               struct dpif_flow_get *get)
+{
+    struct dpif_offload_port_mgr_port *port;
+    struct dpif_flow *dpif_flow = get->flow;
+    struct odputil_keybuf maskbuf;
+    struct odputil_keybuf keybuf;
+    struct odputil_keybuf actbuf;
+    struct ofpbuf key, mask, act;
+    struct dpif_flow_stats stats;
+    struct dpif_flow_attrs attrs;
+    uint64_t act_buf[1024 / 8];
+    struct nlattr *actions;
+    struct match match;
+    struct ofpbuf buf;
+    int err = ENOENT;
+
+    ofpbuf_use_stack(&buf, &act_buf, sizeof act_buf);
+
+     DPIF_OFFLOAD_PORT_MGR_PORT_FOR_EACH (port, offload_tc->port_mgr) {
+         if (!netdev_offload_tc_flow_get(port->netdev, &match, &actions,
+                                         get->ufid, &stats, &attrs, &buf)) {
+             err = 0;
+             break;
+         }
+     }
+
+     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_offload_tc_netdev_match_to_dpif_flow(&match, &key, &mask, actions,
+                                              &stats, &attrs,
+                                              (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);
+
+    return 0;
+}
+
+static void
+dpif_offload_tc_operate(struct dpif *dpif, const struct dpif_offload *offload,
+                        struct dpif_op **ops, size_t n_ops)
+{
+    struct dpif_offload_tc *offload_tc = dpif_offload_tc_cast(offload);
+
+    for (size_t i = 0; i < n_ops; i++) {
+        struct dpif_op *op = ops[i];
+        int error = EOPNOTSUPP;
+
+        if (op->error >= 0) {
+            continue;
+        }
+
+        switch (op->type) {
+        case DPIF_OP_FLOW_PUT: {
+            struct dpif_flow_put *put = &op->flow_put;
+
+            if (!put->ufid) {
+                break;
+            }
+
+            error = dpif_offload_tc_parse_flow_put(offload_tc, dpif, put);
+            break;
+        }
+        case DPIF_OP_FLOW_DEL: {
+            struct dpif_flow_del *del = &op->flow_del;
+
+            if (!del->ufid) {
+                break;
+            }
+
+            error = netdev_offload_tc_flow_del(del->ufid, del->stats);
+            break;
+        }
+        case DPIF_OP_FLOW_GET: {
+            struct dpif_flow_get *get = &op->flow_get;
+
+            if (!get->ufid) {
+                break;
+            }
+
+            error = dpif_offload_tc_parse_flow_get(offload_tc, get);
+            break;
+        }
+        case DPIF_OP_EXECUTE:
+            break;
+        } /* End of 'switch (op->type)'. */
+
+        if (error != EOPNOTSUPP && error != ENOENT) {
+            /* If the operation is unsupported or the entry was not found,
+             * we are skipping this flow operation.  Otherwise, it was
+             * processed and we should report the result. */
+            op->error = error;
+        }
+    }
+}
+
 struct dpif_offload_class dpif_offload_tc_class = {
     .type = "tc",
     .impl_type = DPIF_OFFLOAD_IMPL_HW_ONLY,
@@ -609,6 +849,7 @@ struct dpif_offload_class dpif_offload_tc_class = {
     .flow_dump_destroy = dpif_offload_tc_flow_dump_destroy,
     .flow_dump_thread_create = dpif_offload_tc_flow_dump_thread_create,
     .flow_dump_thread_destroy = dpif_offload_tc_flow_dump_thread_destroy,
+    .operate = dpif_offload_tc_operate,
     .flow_get_n_offloaded = dpif_offload_tc_flow_get_n_offloaded,
     .meter_set = dpif_offload_tc_meter_set,
     .meter_get = dpif_offload_tc_meter_get,
diff --git a/lib/netdev-offload-tc.c b/lib/netdev-offload-tc.c
index 164d8c65b..7e8d3b645 100644
--- a/lib/netdev-offload-tc.c
+++ b/lib/netdev-offload-tc.c
@@ -2300,11 +2300,11 @@ netdev_tc_parse_nl_actions(struct netdev *netdev, 
struct tc_flower *flower,
     return 0;
 }
 
-static int
-netdev_tc_flow_put(struct netdev *netdev, struct match *match,
-                   struct nlattr *actions, size_t actions_len,
-                   const ovs_u128 *ufid, struct offload_info *info,
-                   struct dpif_flow_stats *stats)
+int
+netdev_offload_tc_flow_put(struct netdev *netdev, struct match *match,
+                           struct nlattr *actions, size_t actions_len,
+                           const ovs_u128 *ufid, struct offload_info *info,
+                           struct dpif_flow_stats *stats)
 {
     static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
     enum tc_qdisc_hook hook = get_tc_qdisc_hook(netdev);
@@ -2672,6 +2672,7 @@ netdev_tc_flow_put(struct netdev *netdev, struct match 
*match,
     if (get_ufid_tc_mapping(ufid, &id) == 0) {
         VLOG_DBG_RL(&rl, "updating old handle: %d prio: %d",
                     id.handle, id.prio);
+        info->tc_modify_flow = true;
         info->tc_modify_flow_deleted = !del_filter_and_ufid_mapping(
             &id, ufid, &adjust_stats);
     }
@@ -2720,14 +2721,14 @@ netdev_tc_flow_put(struct netdev *netdev, struct match 
*match,
     return err;
 }
 
-static int
-netdev_tc_flow_get(struct netdev *netdev,
-                   struct match *match,
-                   struct nlattr **actions,
-                   const ovs_u128 *ufid,
-                   struct dpif_flow_stats *stats,
-                   struct dpif_flow_attrs *attrs,
-                   struct ofpbuf *buf)
+int
+netdev_offload_tc_flow_get(struct netdev *netdev,
+                           struct match *match,
+                           struct nlattr **actions,
+                           const ovs_u128 *ufid,
+                           struct dpif_flow_stats *stats,
+                           struct dpif_flow_attrs *attrs,
+                           struct ofpbuf *buf)
 {
     static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
     struct tc_flower flower;
@@ -2777,10 +2778,9 @@ netdev_tc_flow_get(struct netdev *netdev,
     return 0;
 }
 
-static int
-netdev_tc_flow_del(struct netdev *netdev OVS_UNUSED,
-                   const ovs_u128 *ufid,
-                   struct dpif_flow_stats *stats)
+int
+netdev_offload_tc_flow_del(const ovs_u128 *ufid,
+                           struct dpif_flow_stats *stats)
 {
     struct tcf_id id;
     int error;
@@ -3464,8 +3464,5 @@ dpif_offload_tc_meter_del(const struct dpif_offload 
*offload OVS_UNUSED,
 
 const struct netdev_flow_api netdev_offload_tc = {
    .type = "linux_tc",
-   .flow_put = netdev_tc_flow_put,
-   .flow_get = netdev_tc_flow_get,
-   .flow_del = netdev_tc_flow_del,
    .init_flow_api = netdev_tc_init_flow_api,
 };
diff --git a/lib/netdev-offload-tc.h b/lib/netdev-offload-tc.h
index e4740c799..1b5f33524 100644
--- a/lib/netdev-offload-tc.h
+++ b/lib/netdev-offload-tc.h
@@ -42,6 +42,15 @@ bool netdev_offload_tc_flow_dump_next(struct 
netdev_tc_flow_dump *,
                                       struct dpif_flow_attrs *, ovs_u128 *ufid,
                                       struct ofpbuf *rbuffer,
                                       struct ofpbuf *wbuffer);
+int netdev_offload_tc_flow_put(struct netdev *, struct match *,
+                               struct nlattr *actions, size_t actions_len,
+                               const ovs_u128 *ufid, struct offload_info *,
+                               struct dpif_flow_stats *);
+int netdev_offload_tc_flow_del(const ovs_u128 *ufid, struct dpif_flow_stats *);
+int netdev_offload_tc_flow_get(struct netdev *, struct match *,
+                               struct nlattr **actions, const ovs_u128 *ufid,
+                               struct dpif_flow_stats *,
+                               struct dpif_flow_attrs *, struct ofpbuf *);
 void dpif_offload_tc_meter_init(void);
 int dpif_offload_tc_meter_set(const struct dpif_offload *, ofproto_meter_id,
                               struct ofputil_meter_config *);
diff --git a/lib/netdev-offload.h b/lib/netdev-offload.h
index 0223b9686..cb8f4dc8e 100644
--- a/lib/netdev-offload.h
+++ b/lib/netdev-offload.h
@@ -70,6 +70,7 @@ struct offload_info {
      */
     uint32_t flow_mark;
 
+    bool tc_modify_flow; /* Indicates tc modified the flow. */
     bool tc_modify_flow_deleted; /* Indicate the tc modify flow put success
                                   * to delete the original flow. */
     odp_port_t orig_in_port; /* Originating in_port for tnl flows. */
-- 
2.50.1

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

Reply via email to