Use Flow Manager (flowman) to support egress PORT_ID action. It can
steer egress packets from PFs and VFs to any uplink port as long as
they are all on the same VIC adapter. It can also steer packets
between ports on the same VIC adapter (i.e. loopback).

Signed-off-by: Hyong Youb Kim <hyon...@cisco.com>
Reviewed-by: John Daley <johnd...@cisco.com>
---
 doc/guides/rel_notes/release_20_11.rst |   1 +
 drivers/net/enic/enic_fm_flow.c        | 154 +++++++++++++++++++++++++
 2 files changed, 155 insertions(+)

diff --git a/doc/guides/rel_notes/release_20_11.rst 
b/doc/guides/rel_notes/release_20_11.rst
index 180ab8fa0..afe81711a 100644
--- a/doc/guides/rel_notes/release_20_11.rst
+++ b/doc/guides/rel_notes/release_20_11.rst
@@ -138,3 +138,4 @@ Tested Platforms
 * **Updated Cisco enic driver.**
 
   * Added support for VF representors with single-queue Tx/Rx and flow API
+  * Added support for egress PORT_ID action
diff --git a/drivers/net/enic/enic_fm_flow.c b/drivers/net/enic/enic_fm_flow.c
index 9255f2592..296e7e30d 100644
--- a/drivers/net/enic/enic_fm_flow.c
+++ b/drivers/net/enic/enic_fm_flow.c
@@ -97,6 +97,8 @@ struct enic_fm_flow {
        uint64_t action_handle;
        struct enic_fm_counter *counter;
        struct enic_fm_fet *fet;
+       /* Auto-added steer action for hairpin flows (e.g. vnic->vnic) */
+       struct enic_fm_flow *hairpin_steer_flow;
 };
 
 struct enic_fm_jump_flow {
@@ -166,6 +168,9 @@ struct enic_flowman {
        int action_op_count;
        /* Tags used for representor flows */
        uint8_t vf_rep_tag;
+       /* For auto-added steer action for hairpin */
+       int need_hairpin_steer;
+       uint64_t hairpin_steer_vnic_h;
 };
 
 static int enic_fm_tbl_free(struct enic_flowman *fm, uint64_t handle);
@@ -244,6 +249,7 @@ static const enum rte_flow_action_type 
enic_fm_supported_eg_actions[] = {
        RTE_FLOW_ACTION_TYPE_OF_PUSH_VLAN,
        RTE_FLOW_ACTION_TYPE_OF_SET_VLAN_PCP,
        RTE_FLOW_ACTION_TYPE_OF_SET_VLAN_VID,
+       RTE_FLOW_ACTION_TYPE_PORT_ID,
        RTE_FLOW_ACTION_TYPE_PASSTHRU,
        RTE_FLOW_ACTION_TYPE_VOID,
        RTE_FLOW_ACTION_TYPE_VXLAN_ENCAP,
@@ -1120,6 +1126,68 @@ enic_fm_find_vnic(struct enic *enic, const struct 
rte_pci_addr *addr,
        return 0;
 }
 
+/*
+ * Egress: target port should be either PF uplink or VF.
+ * Supported cases
+ * 1. VF egress -> PF uplink
+ *   PF may be this VF's PF, or another PF, as long as they are on the same 
VIC.
+ * 2. VF egress -> VF
+ *
+ * Unsupported cases
+ * 1. PF egress -> VF
+ *   App should be using representor to pass packets to VF
+ */
+static int
+vf_egress_port_id_action(struct enic_flowman *fm,
+                        struct rte_eth_dev *dst_dev,
+                        uint64_t dst_vnic_h,
+                        struct fm_action_op *fm_op,
+                        struct rte_flow_error *error)
+{
+       struct enic *src_enic, *dst_enic;
+       struct enic_vf_representor *vf;
+       uint8_t uif;
+       int ret;
+
+       ENICPMD_FUNC_TRACE();
+       src_enic = fm->user_enic;
+       dst_enic = pmd_priv(dst_dev);
+       if (!(src_enic->rte_dev->data->dev_flags & RTE_ETH_DEV_REPRESENTOR)) {
+               return rte_flow_error_set(error, EINVAL,
+                       RTE_FLOW_ERROR_TYPE_ACTION,
+                       NULL, "source port is not VF representor");
+       }
+
+       /* VF -> PF uplink. dst is not VF representor */
+       if (!(dst_dev->data->dev_flags & RTE_ETH_DEV_REPRESENTOR)) {
+               /* PF is the VF's PF? Then nothing to do */
+               vf = VF_ENIC_TO_VF_REP(src_enic);
+               if (vf->pf == dst_enic) {
+                       ENICPMD_LOG(DEBUG, "destination port is VF's PF");
+                       return 0;
+               }
+               /* If not, steer to the remote PF's uplink */
+               uif = dst_enic->fm_vnic_uif;
+               ENICPMD_LOG(DEBUG, "steer to uplink %u", uif);
+               memset(fm_op, 0, sizeof(*fm_op));
+               fm_op->fa_op = FMOP_SET_EGPORT;
+               fm_op->set_egport.egport = uif;
+               ret = enic_fm_append_action_op(fm, fm_op, error);
+               return ret;
+       }
+
+       /* VF -> VF loopback. Hairpin and steer to vnic */
+       memset(fm_op, 0, sizeof(*fm_op));
+       fm_op->fa_op = FMOP_EG_HAIRPIN;
+       ret = enic_fm_append_action_op(fm, fm_op, error);
+       if (ret)
+               return ret;
+       ENICPMD_LOG(DEBUG, "egress hairpin");
+       fm->hairpin_steer_vnic_h = dst_vnic_h;
+       fm->need_hairpin_steer = 1;
+       return 0;
+}
+
 /* Translate flow actions to flowman TCAM entry actions */
 static int
 enic_fm_copy_action(struct enic_flowman *fm,
@@ -1310,6 +1378,10 @@ enic_fm_copy_action(struct enic_flowman *fm,
                        const struct rte_flow_action_port_id *port;
                        struct rte_eth_dev *dev;
 
+                       if (!ingress && (overlap & PORT_ID)) {
+                               ENICPMD_LOG(DEBUG, "cannot have multiple egress 
PORT_ID actions");
+                               goto unsupported;
+                       }
                        port = actions->conf;
                        if (port->original) {
                                vnic_h = enic->fm_vnic_handle; /* This port */
@@ -1339,6 +1411,13 @@ enic_fm_copy_action(struct enic_flowman *fm,
                         * Ingress. Nothing more to do. We add an implicit
                         * steer at the end if needed.
                         */
+                       if (ingress)
+                               break;
+                       /* Egress */
+                       ret = vf_egress_port_id_action(fm, dev, vnic_h, &fm_op,
+                               error);
+                       if (ret)
+                               return ret;
                        break;
                }
                case RTE_FLOW_ACTION_TYPE_VXLAN_DECAP: {
@@ -1920,9 +1999,15 @@ __enic_fm_flow_free(struct enic_flowman *fm, struct 
enic_fm_flow *fm_flow)
 static void
 enic_fm_flow_free(struct enic_flowman *fm, struct rte_flow *flow)
 {
+       struct enic_fm_flow *steer = flow->fm->hairpin_steer_flow;
+
        if (flow->fm->fet && flow->fm->fet->default_key)
                remove_jump_flow(fm, flow);
        __enic_fm_flow_free(fm, flow->fm);
+       if (steer) {
+               __enic_fm_flow_free(fm, steer);
+               free(steer);
+       }
        free(flow->fm);
        free(flow);
 }
@@ -2173,11 +2258,71 @@ convert_jump_flows(struct enic_flowman *fm, struct 
enic_fm_fet *fet,
        }
 }
 
+static int
+add_hairpin_steer(struct enic_flowman *fm, struct rte_flow *flow,
+                 struct rte_flow_error *error)
+{
+       struct fm_tcam_match_entry *fm_tcam_entry;
+       struct enic_fm_flow *fm_flow;
+       struct fm_action *fm_action;
+       struct fm_action_op fm_op;
+       int ret;
+
+       ENICPMD_FUNC_TRACE();
+       fm_flow = calloc(1, sizeof(*fm_flow));
+       if (fm_flow == NULL) {
+               rte_flow_error_set(error, ENOMEM, RTE_FLOW_ERROR_TYPE_HANDLE,
+                       NULL, "enic: cannot allocate rte_flow");
+               return -ENOMEM;
+       }
+       /* Original egress hairpin flow */
+       fm_tcam_entry = &fm->tcam_entry;
+       fm_action = &fm->action;
+       /* Use the match pattern of the egress flow as is, without counters */
+       fm_tcam_entry->ftm_flags &= ~FMEF_COUNTER;
+       /* The only action is steer to vnic */
+       fm->action_op_count = 0;
+       memset(fm_action, 0, sizeof(*fm_action));
+       memset(&fm_op, 0, sizeof(fm_op));
+       /* Always to queue 0 for now */
+       fm_op.fa_op = FMOP_RQ_STEER;
+       fm_op.rq_steer.rq_index = 0;
+       fm_op.rq_steer.vnic_handle = fm->hairpin_steer_vnic_h;
+       ret = enic_fm_append_action_op(fm, &fm_op, error);
+       if (ret)
+               goto error_with_flow;
+       ENICPMD_LOG(DEBUG, "add steer op");
+       /* Add required END */
+       memset(&fm_op, 0, sizeof(fm_op));
+       fm_op.fa_op = FMOP_END;
+       ret = enic_fm_append_action_op(fm, &fm_op, error);
+       if (ret)
+               goto error_with_flow;
+       /* Add the ingress flow */
+       fm_flow->action_handle = FM_INVALID_HANDLE;
+       fm_flow->entry_handle = FM_INVALID_HANDLE;
+       ret = __enic_fm_flow_add_entry(fm, fm_flow, fm_tcam_entry, fm_action,
+                                      FM_TCAM_RTE_GROUP, 1 /* ingress */, 
error);
+       if (ret) {
+               ENICPMD_LOG(ERR, "cannot add hairpin-steer flow");
+               goto error_with_flow;
+       }
+       /* The new flow is now the egress flow's paired flow */
+       flow->fm->hairpin_steer_flow = fm_flow;
+       return 0;
+
+error_with_flow:
+       free(fm_flow);
+       return ret;
+}
+
 static void
 enic_fm_open_scratch(struct enic_flowman *fm)
 {
        fm->action_op_count = 0;
        fm->fet = NULL;
+       fm->need_hairpin_steer = 0;
+       fm->hairpin_steer_vnic_h = 0;
        memset(&fm->tcam_entry, 0, sizeof(fm->tcam_entry));
        memset(&fm->action, 0, sizeof(fm->action));
 }
@@ -2325,6 +2470,15 @@ enic_fm_flow_create(struct rte_eth_dev *dev,
        flow = enic_fm_flow_add_entry(fm, fm_tcam_entry, fm_action,
                                      attrs, error);
        if (flow) {
+               /* Add ingress rule that pairs with hairpin rule */
+               if (fm->need_hairpin_steer) {
+                       ret = add_hairpin_steer(fm, flow, error);
+                       if (ret) {
+                               enic_fm_flow_free(fm, flow);
+                               flow = NULL;
+                               goto error_with_scratch;
+                       }
+               }
                LIST_INSERT_HEAD(&enic->flows, flow, next);
                fet = flow->fm->fet;
                if (fet && fet->default_key) {
-- 
2.26.2

Reply via email to