diff mbox series

[ovs-dev,v5,7/7] dpif-netdev: Support partial offload of POP_VLAN action

Message ID 20200709064732.31387-8-sriharsha.basavapatna@broadcom.com
State Superseded
Headers show
Series netdev datapath: Partial action offload | expand

Commit Message

Sriharsha Basavapatna July 9, 2020, 6:47 a.m. UTC
If the output-port is a vhost-user port and the action is POP_VLAN,
offload the action on the ingress device if it is offload capable.

Note:
- With ingress partial action offload, the flow must be added to the
  mark-to-flow table. Otherwise, since the action (e.g, POP_VLAN) is
  already performed in the HW, the flow can't be located in the datapath
  flow tables and caches.
- The mark action is offloaded implicitly to facilitate this mark-to-flow
  lookup.
- Add a new member 'partial_actions_offloaded' to the info structure passed
  to the offload layer. When the offload layer successfully offloads the
  partial action, it indicates this to the dpif-netdev layer through this
  flag. This is needed by the dpif-netdev layer to distinguish partial
  offload (i.e, classification offload or mark,rss actions) from partial
  actions offload (classification + some actions, e.g vlan-pop,mark actions)
  in ingress direction.

Signed-off-by: Sriharsha Basavapatna <sriharsha.basavapatna@broadcom.com>
---
 lib/dpif-netdev.c         | 114 ++++++++++++++++++++++++++++++++++++--
 lib/netdev-offload-dpdk.c |  26 +++++++--
 lib/netdev-offload.h      |   1 +
 lib/odp-execute.c         |   8 +--
 4 files changed, 132 insertions(+), 17 deletions(-)
diff mbox series

Patch

diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
index bfb016059..3f295ca33 100644
--- a/lib/dpif-netdev.c
+++ b/lib/dpif-netdev.c
@@ -115,6 +115,7 @@  COVERAGE_DEFINE(datapath_drop_invalid_tnl_port);
 COVERAGE_DEFINE(datapath_drop_rx_invalid_packet);
 COVERAGE_DEFINE(datapath_skip_tunnel_push);
 COVERAGE_DEFINE(datapath_skip_vlan_push);
+COVERAGE_DEFINE(datapath_skip_vlan_pop);
 
 /* Protects against changes to 'dp_netdevs'. */
 static struct ovs_mutex dp_netdev_mutex = OVS_MUTEX_INITIALIZER;
@@ -2565,6 +2566,18 @@  enum num_action_attr_egress {
     MAX_ACTION_ATTRS_EGRESS = TUNNEL_PUSH_ATTRS
 };
 
+/*
+ * Maxium number of actions to be parsed while selecting a flow for ingress
+ * partial action offload. This number is currently based on the minimum
+ * number of attributes seen with the vlan pop action (vlan_pop, output).
+ * This number includes output action to a single vhost device (uplink) and
+ * does not support multiple output actions.
+ */
+enum num_action_attr_ingress {
+    VLAN_POP_ATTRS = 2,        /* vlan_pop, output */
+    MAX_ACTION_ATTRS_INGRESS = VLAN_POP_ATTRS
+};
+
 /*
  * This function parses the list of OVS "actions" of length "actions_len",
  * and returns them in an array of action "attrs", of size "max_attrs".
@@ -2678,6 +2691,83 @@  should_partial_offload_egress(struct netdev *in_netdev,
     return true;
 }
 
+/* This function determines if the given flow should be partially offloaded
+ * on the ingress device, when the out-port is not offload-capable like a
+ * vhost-user port. The function currently supports offloading of only
+ * vlan-pop action.
+ */
+static bool
+should_partial_offload_ingress(struct netdev *in_netdev,
+                               struct dp_flow_offload_item *offload)
+{
+    const char *dpif_type_str =
+        dpif_normalize_type(offload->pmd->dp->class->type);
+    struct action_attr attrs[MAX_ACTION_ATTRS_INGRESS];
+    odp_port_t out_port = ODPP_NONE;
+    struct netdev *out_netdev;
+    int num_attrs = 0;
+    int type;
+    int rc;
+
+    /* Support ingress partial-offload only
+     * when the in-port supports offloads.
+     */
+    if (!netdev_dpdk_flow_api_supported(in_netdev)) {
+         return false;
+    }
+
+    rc = parse_nlattr_actions(offload->actions, offload->actions_len, attrs,
+                              MAX_ACTION_ATTRS_INGRESS, &num_attrs);
+    if (rc == E2BIG) {
+        /* Action list too big; decline partial offload */
+        return false;
+    }
+
+    /* Minimum number of attrs expected (pop_vlan) */
+    if (num_attrs < VLAN_POP_ATTRS) {
+        return false;
+    }
+
+    if (num_attrs == VLAN_POP_ATTRS &&
+        (attrs[0].type != OVS_ACTION_ATTR_POP_VLAN ||
+         attrs[1].type != OVS_ACTION_ATTR_OUTPUT)) {
+        return false;
+    }
+
+    /* Ingress partial-offload needs an output action at the end. */
+    out_port = nl_attr_get_odp_port(attrs[num_attrs - 1].action);
+    if (out_port == ODPP_NONE) {
+        return false;
+    }
+
+    /* Support ingress partial-offload only if out-port is vhost-user. */
+    out_netdev = netdev_ports_get(out_port, dpif_type_str);
+    if (out_netdev && is_dpdk_vhost_netdev(out_netdev)) {
+        return true;
+    }
+
+    return false;
+}
+
+/* This function determines if the given flow actions can be partially
+ * offloaded. Partial action offload is attempted when either the in-port
+ * or the out-port for the flow is a vhost-user port.
+ */
+static bool
+should_partial_offload(struct netdev *in_netdev,
+                       struct dp_flow_offload_item *offload,
+                       struct netdev **egress_netdev)
+{
+    if (should_partial_offload_ingress(in_netdev, offload)) {
+        return true;
+    } else if (should_partial_offload_egress(in_netdev, offload,
+               egress_netdev)) {
+        return true;
+    } else {
+        return false;
+    }
+}
+
 static int
 dp_netdev_alloc_flow_mark(struct dp_netdev_flow *flow, bool modification,
                           uint32_t *markp)
@@ -2752,8 +2842,9 @@  dp_netdev_flow_offload_put(struct dp_flow_offload_item *offload)
 
     info.attr_egress = 0;
     info.partial_actions = 0;
+    info.partial_actions_offloaded = 0;
 
-    if (unlikely(should_partial_offload_egress(port, offload, &egress_port))) {
+    if (unlikely(should_partial_offload(port, offload, &egress_port))) {
         if (egress_port) {
             netdev_close(port);
             port = egress_port;
@@ -2785,12 +2876,14 @@  dp_netdev_flow_offload_put(struct dp_flow_offload_item *offload)
         goto err_free;
     }
 
-    if (unlikely(info.partial_actions && egress_port)) {
+    if (unlikely(info.partial_actions && info.partial_actions_offloaded)) {
         VLOG_DBG_RL("%s: flow: %p mega_ufid: "UUID_FMT" pmd_id: %d\n",
                     __func__, flow, UUID_ARGS((struct uuid *)&flow->mega_ufid),
                     flow->pmd_id);
         flow->partial_actions_offloaded = true;
-    } else if (!modification) {
+    }
+
+    if (!modification && alloc_mark) {
         megaflow_to_mark_associate(&flow->mega_ufid, mark);
         mark_to_flow_associate(mark, flow);
     }
@@ -8009,7 +8102,20 @@  dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
         break;
     }
 
-    case OVS_ACTION_ATTR_POP_VLAN:
+    case OVS_ACTION_ATTR_POP_VLAN: {
+        struct dp_packet *packet;
+
+        if (!dp_flow || !dp_flow->partial_actions_offloaded) {
+            DP_PACKET_BATCH_FOR_EACH (i, packet, packets_) {
+                eth_pop_vlan(packet);
+            }
+        } else {
+            packet_count = dp_packet_batch_size(packets_);
+            COVERAGE_ADD(datapath_skip_vlan_pop, packet_count);
+        }
+        break;
+    }
+
     case OVS_ACTION_ATTR_PUSH_MPLS:
     case OVS_ACTION_ATTR_POP_MPLS:
     case OVS_ACTION_ATTR_SET:
diff --git a/lib/netdev-offload-dpdk.c b/lib/netdev-offload-dpdk.c
index 6434b926d..020f9cb4c 100644
--- a/lib/netdev-offload-dpdk.c
+++ b/lib/netdev-offload-dpdk.c
@@ -1346,8 +1346,11 @@  parse_flow_actions(struct netdev *netdev,
     }
     NL_ATTR_FOR_EACH_UNSAFE (nla, left, nl_actions, nl_actions_len) {
         if (nl_attr_type(nla) == OVS_ACTION_ATTR_OUTPUT) {
-            if (add_output_action(netdev, actions, nla)) {
-                return -1;
+            /* add output action only if full-offload */
+            if (!info->partial_actions) {
+                if (add_output_action(netdev, actions, nla)) {
+                    return -1;
+                }
             }
         } else if (nl_attr_type(nla) == OVS_ACTION_ATTR_DROP) {
             add_flow_action(actions, RTE_FLOW_ACTION_TYPE_DROP, NULL);
@@ -1389,6 +1392,13 @@  parse_flow_actions(struct netdev *netdev,
         return -1;
     }
 
+    if (info->partial_actions && !info->attr_egress) {
+        struct rte_flow_action_mark *mark = xzalloc(sizeof *mark);
+
+        mark->id = info->flow_mark;
+        add_flow_action(actions, RTE_FLOW_ACTION_TYPE_MARK, mark);
+    }
+
     add_flow_action(actions, RTE_FLOW_ACTION_TYPE_END, NULL);
     return 0;
 }
@@ -1406,9 +1416,11 @@  netdev_offload_dpdk_actions(struct netdev *netdev,
     struct rte_flow_error error;
     int ret;
 
-    if (info->attr_egress) {
-        flow_attr.ingress = 0;
-        flow_attr.egress = 1;
+    if (info->partial_actions) {
+        if (info->attr_egress) {
+            flow_attr.ingress = 0;
+            flow_attr.egress = 1;
+        }
         flow_attr.transfer = 0;
     }
 
@@ -1446,11 +1458,12 @@  netdev_offload_dpdk_add_flow(struct netdev *netdev,
     flow = netdev_offload_dpdk_actions(netdev, &patterns, nl_actions,
                                        actions_len, info);
     if (flow) {
-        if (info->partial_actions && info->attr_egress) {
+        if (info->partial_actions) {
             /* actions_offloaded should be set to false with partial actions,
              * since it is still considered as partial-offload and not
              * full-offload. */
             actions_offloaded = false;
+            info->partial_actions_offloaded = 1;
         }
     } else if (!(info->partial_actions && info->attr_egress)) {
         /* If we failed to offload the rule actions fallback to MARK+RSS
@@ -1517,6 +1530,7 @@  netdev_offload_dpdk_flow_put(struct netdev *netdev, struct match *match,
             VLOG_DBG_RL("%s: mega_ufid: "UUID_FMT" refcnt: %d\n", __func__,
                         UUID_ARGS((struct uuid *)ufid), rte_flow_data->refcnt);
             rte_flow_data->refcnt++;
+            info->partial_actions_offloaded = 1;
             return ret;
         } else {
             /*
diff --git a/lib/netdev-offload.h b/lib/netdev-offload.h
index 55fcc711c..999cacbbc 100644
--- a/lib/netdev-offload.h
+++ b/lib/netdev-offload.h
@@ -69,6 +69,7 @@  struct offload_info {
                                      * sync with datapath recirc ids. */
     uint8_t attr_egress;      /* Egress direction offload */
     uint8_t partial_actions;  /* Partial action offload; no forward action */
+    uint8_t partial_actions_offloaded; /* Success flag */
 
     /*
      * The flow mark id assigened to the flow. If any pkts hit the flow,
diff --git a/lib/odp-execute.c b/lib/odp-execute.c
index 412e96b82..b44b4f29f 100644
--- a/lib/odp-execute.c
+++ b/lib/odp-execute.c
@@ -802,11 +802,11 @@  requires_datapath_assistance(const struct nlattr *a)
     case OVS_ACTION_ATTR_CT:
     case OVS_ACTION_ATTR_METER:
     case OVS_ACTION_ATTR_PUSH_VLAN:
+    case OVS_ACTION_ATTR_POP_VLAN:
         return true;
 
     case OVS_ACTION_ATTR_SET:
     case OVS_ACTION_ATTR_SET_MASKED:
-    case OVS_ACTION_ATTR_POP_VLAN:
     case OVS_ACTION_ATTR_SAMPLE:
     case OVS_ACTION_ATTR_HASH:
     case OVS_ACTION_ATTR_PUSH_MPLS:
@@ -924,12 +924,6 @@  odp_execute_actions(void *dp, struct dp_packet_batch *batch, bool steal,
             break;
         }
 
-        case OVS_ACTION_ATTR_POP_VLAN:
-            DP_PACKET_BATCH_FOR_EACH (i, packet, batch) {
-                eth_pop_vlan(packet);
-            }
-            break;
-
         case OVS_ACTION_ATTR_PUSH_MPLS: {
             const struct ovs_action_push_mpls *mpls = nl_attr_get(a);