+
+ /* Egress partial-offload needs an output action at the end. */
+ out_port = nl_attr_get_odp_port(attrs[2].action);
+ if (out_port == ODPP_NONE) {
+ return false;
+ }
+
+ /* Support egress partial-offload only when out-port is offload capable. */
+ out_netdev = netdev_ports_get(out_port, dpif_type_str);
+ if (!out_netdev || !netdev_dpdk_flow_api_supported(out_netdev)) {
+ return false;
+ }
+
+ /* Flow can be egress partial-offloaded. */
+ *egress_netdev = out_netdev;
+ offload->flow->egress_offload_port = out_port;
+ return true;
}
static int
@@ -2552,7 +2716,9 @@ dp_netdev_flow_offload_put(struct dp_flow_offload_item
*offload)
bool modification = offload->op == DP_NETDEV_FLOW_OFFLOAD_OP_MOD;
struct offload_info info;
struct netdev *port;
- uint32_t mark;
+ struct netdev *egress_port = NULL;
+ bool alloc_mark = true;
+ uint32_t mark = INVALID_FLOW_MARK;
int ret;
if (flow->dead) {
@@ -2564,11 +2730,25 @@ dp_netdev_flow_offload_put(struct dp_flow_offload_item
*offload)
return -1;
}
- if (dp_netdev_alloc_flow_mark(flow, modification, &mark)) {
- /* flow already offloaded */
+ info.attr_egress = 0;
+ info.partial_actions = 0;
+
+ if (unlikely(should_partial_offload_egress(port, offload, &egress_port))) {
+ if (egress_port) {
netdev_close(port);
- return 0;
+ port = egress_port;
+ info.attr_egress = 1;
+ alloc_mark = false;
+ }
+ info.partial_actions = 1;
+ }
+
+ if (alloc_mark && dp_netdev_alloc_flow_mark(flow, modification, &mark)) {
+ /* flow already offloaded */
+ netdev_close(port);
+ return 0;
}
+
info.flow_mark = mark;
/* Taking a global 'port_mutex' to fulfill thread safety restrictions for
@@ -2585,17 +2765,24 @@ dp_netdev_flow_offload_put(struct dp_flow_offload_item
*offload)
goto err_free;
}
- if (!modification) {
+ if (unlikely(info.partial_actions && egress_port)) {
+ 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) {
megaflow_to_mark_associate(&flow->mega_ufid, mark);
mark_to_flow_associate(mark, flow);
}
return 0;
err_free:
- if (!modification) {
- flow_mark_free(mark);
- } else {
- mark_to_flow_disassociate(pmd, flow);
+ if (mark != INVALID_FLOW_MARK) {
+ if (!modification) {
+ flow_mark_free(mark);
+ } else {
+ mark_to_flow_disassociate(pmd, flow);
+ }
}
return -1;
}
@@ -2711,7 +2898,8 @@ dp_netdev_pmd_remove_flow(struct dp_netdev_pmd_thread
*pmd,
ovs_assert(cls != NULL);
dpcls_remove(cls, &flow->cr);
cmap_remove(&pmd->flow_table, node, dp_netdev_flow_hash(&flow->ufid));
- if (flow->mark != INVALID_FLOW_MARK) {
+ if (flow->mark != INVALID_FLOW_MARK || (flow->partial_actions_offloaded
+ && flow->egress_offload_port != ODPP_NONE)) {
queue_netdev_flow_del(pmd, flow);
}
flow->dead = true;
@@ -3469,6 +3657,8 @@ dp_netdev_flow_add(struct dp_netdev_pmd_thread *pmd,
flow->dead = false;
flow->batch = NULL;
flow->mark = INVALID_FLOW_MARK;
+ flow->partial_actions_offloaded = false;
+ flow->egress_offload_port = ODPP_NONE;
*CONST_CAST(unsigned *, &flow->pmd_id) = pmd->core_id;
*CONST_CAST(struct flow *, &flow->flow) = match->flow;
*CONST_CAST(ovs_u128 *, &flow->ufid) = *ufid;
diff --git a/lib/netdev-offload-dpdk.c b/lib/netdev-offload-dpdk.c
index 2ed3cb125..ad384e101 100644
--- a/lib/netdev-offload-dpdk.c
+++ b/lib/netdev-offload-dpdk.c
@@ -57,6 +57,7 @@ static struct cmap ufid_to_rte_flow = CMAP_INITIALIZER;
struct ufid_to_rte_flow_data {
struct cmap_node node;
ovs_u128 ufid;
+ uint32_t refcnt;
struct rte_flow *rte_flow;
bool actions_offloaded;
struct dpif_flow_stats stats;
@@ -97,6 +98,7 @@ ufid_to_rte_flow_associate(const ovs_u128 *ufid,
ovs_assert(data_prev->rte_flow == NULL);
}
+ data->refcnt = 1;
data->ufid = *ufid;
data->rte_flow = rte_flow;
data->actions_offloaded = actions_offloaded;
@@ -1494,7 +1496,8 @@ static int
parse_clone_actions(struct netdev *netdev,
struct flow_actions *actions,
const struct nlattr *clone_actions,
- const size_t clone_actions_len)
+ const size_t clone_actions_len,
+ struct offload_info *info)
{
const struct nlattr *ca;
unsigned int cleft;
@@ -1519,8 +1522,11 @@ parse_clone_actions(struct netdev *netdev,
add_flow_action(actions, RTE_FLOW_ACTION_TYPE_RAW_ENCAP,
raw_encap);
} else if (clone_type == OVS_ACTION_ATTR_OUTPUT) {
- if (add_output_action(netdev, actions, ca)) {
- return -1;
+ /* add output action only if full-offload */
+ if (!info->partial_actions) {
+ if (add_output_action(netdev, actions, ca)) {
+ return -1;
+ }
}
} else {
VLOG_DBG_RL(&rl,
@@ -1537,12 +1543,15 @@ static int
parse_flow_actions(struct netdev *netdev,
struct flow_actions *actions,
struct nlattr *nl_actions,
- size_t nl_actions_len)
+ size_t nl_actions_len,
+ struct offload_info *info)
{
struct nlattr *nla;
size_t left;
- add_count_action(actions);
+ if (!info->partial_actions) {
+ add_count_action(actions);
+ }
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)) {
@@ -1573,7 +1582,7 @@ parse_flow_actions(struct netdev *netdev,
size_t clone_actions_len = nl_attr_get_size(nla);
if (parse_clone_actions(netdev, actions, clone_actions,
- clone_actions_len)) {
+ clone_actions_len, info)) {
return -1;
}
} else {
@@ -1595,15 +1604,22 @@ static struct rte_flow *
netdev_offload_dpdk_actions(struct netdev *netdev,
struct flow_patterns *patterns,
struct nlattr *nl_actions,
- size_t actions_len)
+ size_t actions_len,
+ struct offload_info *info)
{
- const struct rte_flow_attr flow_attr = { .ingress = 1, .transfer = 1 };
+ struct rte_flow_attr flow_attr = { .ingress = 1, .transfer = 1 };
struct flow_actions actions = { .actions = NULL, .cnt = 0 };
struct rte_flow *flow = NULL;
struct rte_flow_error error;
int ret;
- ret = parse_flow_actions(netdev, &actions, nl_actions, actions_len);
+ if (info->attr_egress) {
+ flow_attr.ingress = 0;
+ flow_attr.egress = 1;
+ flow_attr.transfer = 0;
+ }
+
+ ret = parse_flow_actions(netdev, &actions, nl_actions, actions_len,info);
if (ret) {
goto out;
}
@@ -1635,8 +1651,15 @@ netdev_offload_dpdk_add_flow(struct netdev *netdev,
}
flow = netdev_offload_dpdk_actions(netdev, &patterns, nl_actions,
- actions_len);
- if (!flow) {
+ actions_len, info);
+ if (flow) {
+ if (info->partial_actions && info->attr_egress) {
+ /* 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;
+ }
+ } else if (!(info->partial_actions && info->attr_egress)) {
/* If we failed to offload the rule actions fallback to MARK+RSS
* actions.
*/
@@ -1686,18 +1709,29 @@ netdev_offload_dpdk_flow_put(struct netdev *netdev,
struct match *match,
struct dpif_flow_stats *stats)
{
struct ufid_to_rte_flow_data *rte_flow_data;
- int ret;
+ int ret = 0;
- /*
- * If an old rte_flow exists, it means it's a flow modification.
- * Here destroy the old rte flow first before adding a new one.
- */
rte_flow_data = ufid_to_rte_flow_data_find(ufid);
if (rte_flow_data && rte_flow_data->rte_flow) {
- ret = netdev_offload_dpdk_destroy_flow(netdev, ufid,
- rte_flow_data->rte_flow);
- if (ret < 0) {
+ if (unlikely(info->partial_actions && info->attr_egress)) {
+ /* In the case of partial action offload, the same mega-flow
+ * could be offloaded by multiple PMD threads. Avoid creating
+ * multiple rte_flows and just update the refcnt.
+ */
+ 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++;
return ret;
+ } else {
+ /*
+ * If an old rte_flow exists, it means it's a flow modification.
+ * Here destroy the old rte flow first before adding a new one.
+ */
+ ret = netdev_offload_dpdk_destroy_flow(netdev, ufid,
+ rte_flow_data->rte_flow);
+ if (ret < 0) {
+ return ret;
+ }
}
}
@@ -1719,6 +1753,12 @@ netdev_offload_dpdk_flow_del(struct netdev *netdev, const ovs_u128 *ufid,
return -1;
}
+ VLOG_DBG_RL("%s: mega_ufid: "UUID_FMT" refcnt: %d\n", __func__,
+ UUID_ARGS((struct uuid *)ufid), rte_flow_data->refcnt);
+ if (rte_flow_data->refcnt-- > 1) {
+ return 0;
+ }
+
if (stats) {
memset(stats, 0, sizeof *stats);
}
diff --git a/lib/netdev-offload.h b/lib/netdev-offload.h
index 4c0ed2ae8..55fcc711c 100644
--- a/lib/netdev-offload.h
+++ b/lib/netdev-offload.h
@@ -67,6 +67,8 @@ struct offload_info {
bool recirc_id_shared_with_tc; /* Indicates whever tc chains will be in
* sync with datapath recirc ids. */
+ uint8_t attr_egress; /* Egress direction offload */
+ uint8_t partial_actions; /* Partial action offload; no forward action */
/*
* The flow mark id assigened to the flow. If any pkts hit the flow,