@@ -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:
@@ -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 {
/*
@@ -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,
@@ -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);
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(-)