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 <[email protected]> --- lib/dpif-netdev.c | 61 ++++++++++++++++++---- lib/netdev-offload-dpdk.c | 97 ++++++++++++++++++++++++++++++++--- lib/netdev-offload-provider.h | 6 +++ lib/netdev-offload.c | 35 +++++++++++++ lib/netdev-offload.h | 4 ++ 5 files changed, 186 insertions(+), 17 deletions(-) diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c index 53f07fc44..60de686bd 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; @@ -2551,6 +2552,28 @@ dp_netdev_flow_offload_del(struct dp_flow_offload_item *offload) } } +/* 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, const char *dpif_type, + struct match *match, struct nlattr *actions, + size_t act_len, struct netdev **egress_netdev, + odp_port_t *egress_port) +{ + if (netdev_partial_offload_ingress(in_netdev, dpif_type, match, actions, + act_len)) { + return true; + } else if (netdev_partial_offload_egress(in_netdev, dpif_type, match, + actions, act_len, egress_netdev, + egress_port)) { + return true; + } else { + return false; + } +} + static int dp_netdev_alloc_flow_mark(struct dp_netdev_flow *flow, bool modification, uint32_t *markp) @@ -2626,14 +2649,14 @@ dp_netdev_flow_offload_put(struct dp_flow_offload_item *offload) info.attr_egress = 0; info.partial_actions = 0; - - if (unlikely(netdev_partial_offload_egress(netdev, dpif_type_str, - &offload->match, - CONST_CAST(struct nlattr *, - offload->actions), - offload->actions_len, - &egress_netdev, - &egress_port))) { + info.partial_actions_offloaded = 0; + + if (unlikely(should_partial_offload(netdev, dpif_type_str, &offload->match, + CONST_CAST(struct nlattr *, + offload->actions), + offload->actions_len, + &egress_netdev, + &egress_port))) { if (egress_netdev) { netdev_close(netdev); netdev = egress_netdev; @@ -2666,12 +2689,14 @@ dp_netdev_flow_offload_put(struct dp_flow_offload_item *offload) goto err_free; } - if (unlikely(info.partial_actions && egress_netdev)) { + 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); } @@ -7529,6 +7554,7 @@ dp_netdev_assist_cb(void *dp OVS_UNUSED, const struct nlattr *a) switch (type) { case OVS_ACTION_ATTR_PUSH_VLAN: + case OVS_ACTION_ATTR_POP_VLAN: return true; default: return false; @@ -7903,7 +7929,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 449333248..a4da03e62 100644 --- a/lib/netdev-offload-dpdk.c +++ b/lib/netdev-offload-dpdk.c @@ -1347,8 +1347,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); @@ -1390,6 +1393,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; } @@ -1407,9 +1417,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; } @@ -1447,11 +1459,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 @@ -1518,6 +1531,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 { /* @@ -1635,6 +1649,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". @@ -1682,6 +1708,64 @@ parse_nlattr_actions(struct nlattr *actions, size_t actions_len, return 0; } +/* 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 +netdev_offload_dpdk_ingress_partial(struct netdev *netdev, + struct match *match, + struct nlattr *actions, + size_t actions_len) +{ + 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(netdev)) { + return false; + } + + rc = parse_nlattr_actions(actions, 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, netdev->dpif_type); + if (out_netdev && is_dpdk_vhost_netdev(out_netdev)) { + return true; + } + + return false; +} + /* This function determines if the given flow should be partially offloaded * on the egress device, when the in-port is not offload-capable like a * vhost-user port. The function currently supports offloading of only @@ -1754,4 +1838,5 @@ const struct netdev_flow_api netdev_offload_dpdk = { .init_flow_api = netdev_offload_dpdk_init_flow_api, .flow_get = netdev_offload_dpdk_flow_get, .flow_offload_egress_partial = netdev_offload_dpdk_egress_partial, + .flow_offload_ingress_partial = netdev_offload_dpdk_ingress_partial, }; diff --git a/lib/netdev-offload-provider.h b/lib/netdev-offload-provider.h index 94973b24d..f0e944281 100644 --- a/lib/netdev-offload-provider.h +++ b/lib/netdev-offload-provider.h @@ -93,6 +93,12 @@ struct netdev_flow_api { bool (*flow_offload_egress_partial)(struct netdev *, struct match *, struct nlattr *, size_t, struct netdev **, odp_port_t *); + + /* Determine if the flow should be partial offloaded to the ingress + * device and if yes return true; otherwise return false. + */ + bool (*flow_offload_ingress_partial)(struct netdev *, struct match *, + struct nlattr *, size_t); }; int netdev_register_flow_api_provider(const struct netdev_flow_api *); diff --git a/lib/netdev-offload.c b/lib/netdev-offload.c index 4fee717d9..9ce9bfb8b 100644 --- a/lib/netdev-offload.c +++ b/lib/netdev-offload.c @@ -733,3 +733,38 @@ netdev_partial_offload_egress(struct netdev *netdev, const char *dpif_type, netdev_close(flow_api_netdev); return true; } + +bool +netdev_partial_offload_ingress(struct netdev *netdev, const char *dpif_type, + struct match *match, struct nlattr *actions, + size_t act_len) +{ + struct netdev *flow_api_netdev; + struct port_to_netdev_data *data; + struct netdev_flow_api *flow_api = + ovsrcu_get(const struct netdev_flow_api *, &netdev->flow_api); + + /* Ingress netdev is not offload capable */ + if (!flow_api) { + return false; + } + + /* Ingress netdev must belong to the datapath specified */ + if (netdev_get_dpif_type(netdev) != dpif_type) { + return false; + } + + /* flow_api does not support ingress partial offload */ + if (!flow_api->flow_offload_ingress_partial) { + return false; + } + + /* Can the flow be partial offloaded to the ingress dev ? */ + if (!flow_api->flow_offload_ingress_partial(netdev, match, actions, + act_len)) { + return false; + } + + /* Success */ + return true; +} diff --git a/lib/netdev-offload.h b/lib/netdev-offload.h index 66b31af59..bf782fa29 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, @@ -131,6 +132,9 @@ bool netdev_partial_offload_egress(struct netdev *netdev, struct nlattr *actions, size_t act_len, struct netdev **egress_netdev, odp_port_t *egress_port); +bool netdev_partial_offload_ingress(struct netdev *netdev, + const char *dpif_type, struct match *match, + struct nlattr *actions, size_t act_len); #ifdef __cplusplus } #endif -- 2.25.0.rc2 _______________________________________________ dev mailing list [email protected] https://mail.openvswitch.org/mailman/listinfo/ovs-dev
