From: Shagun Agrawal <shag...@chelsio.com>

Query firmware for the new filter work request to offload flows with
actions to modify IP and TCP/UDP port addresses. When available,
translate IP and TCP/UDP port address modify actions to internal
hardware specification and offload the flow to hardware.

Signed-off-by: Shagun Agrawal <shag...@chelsio.com>
Signed-off-by: Rahul Lakkireddy <rahul.lakkire...@chelsio.com>
---
 drivers/net/cxgbe/base/common.h         |   1 +
 drivers/net/cxgbe/base/t4fw_interface.h |  50 +++++++++++++
 drivers/net/cxgbe/cxgbe_filter.c        |  23 +++++-
 drivers/net/cxgbe/cxgbe_filter.h        |  26 ++++++-
 drivers/net/cxgbe/cxgbe_flow.c          | 127 ++++++++++++++++++++++++++++++++
 drivers/net/cxgbe/cxgbe_main.c          |  10 +++
 6 files changed, 233 insertions(+), 4 deletions(-)

diff --git a/drivers/net/cxgbe/base/common.h b/drivers/net/cxgbe/base/common.h
index e524f7931..f4b2e14cd 100644
--- a/drivers/net/cxgbe/base/common.h
+++ b/drivers/net/cxgbe/base/common.h
@@ -256,6 +256,7 @@ struct adapter_params {
 
        bool ulptx_memwrite_dsgl;          /* use of T5 DSGL allowed */
        u8 fw_caps_support;               /* 32-bit Port Capabilities */
+       bool filter2_wr_support;        /* FW support for FILTER2_WR */
 };
 
 /* Firmware Port Capabilities types.
diff --git a/drivers/net/cxgbe/base/t4fw_interface.h 
b/drivers/net/cxgbe/base/t4fw_interface.h
index 842aa1263..d20d73590 100644
--- a/drivers/net/cxgbe/base/t4fw_interface.h
+++ b/drivers/net/cxgbe/base/t4fw_interface.h
@@ -59,6 +59,7 @@ enum fw_wr_opcodes {
        FW_ETH_TX_PKTS_WR       = 0x09,
        FW_ETH_TX_PKT_VM_WR     = 0x11,
        FW_ETH_TX_PKTS_VM_WR    = 0x12,
+       FW_FILTER2_WR           = 0x77,
        FW_ETH_TX_PKTS2_WR      = 0x78,
 };
 
@@ -185,6 +186,51 @@ struct fw_filter_wr {
        __u8   sma[6];
 };
 
+struct fw_filter2_wr {
+       __be32 op_pkd;
+       __be32 len16_pkd;
+       __be64 r3;
+       __be32 tid_to_iq;
+       __be32 del_filter_to_l2tix;
+       __be16 ethtype;
+       __be16 ethtypem;
+       __u8   frag_to_ovlan_vldm;
+       __u8   smac_sel;
+       __be16 rx_chan_rx_rpl_iq;
+       __be32 maci_to_matchtypem;
+       __u8   ptcl;
+       __u8   ptclm;
+       __u8   ttyp;
+       __u8   ttypm;
+       __be16 ivlan;
+       __be16 ivlanm;
+       __be16 ovlan;
+       __be16 ovlanm;
+       __u8   lip[16];
+       __u8   lipm[16];
+       __u8   fip[16];
+       __u8   fipm[16];
+       __be16 lp;
+       __be16 lpm;
+       __be16 fp;
+       __be16 fpm;
+       __be16 r7;
+       __u8   sma[6];
+       __be16 r8;
+       __u8   filter_type_swapmac;
+       __u8   natmode_to_ulp_type;
+       __be16 newlport;
+       __be16 newfport;
+       __u8   newlip[16];
+       __u8   newfip[16];
+       __be32 natseqcheck;
+       __be32 r9;
+       __be64 r10;
+       __be64 r11;
+       __be64 r12;
+       __be64 r13;
+};
+
 #define S_FW_FILTER_WR_TID     12
 #define V_FW_FILTER_WR_TID(x)  ((x) << S_FW_FILTER_WR_TID)
 
@@ -288,6 +334,9 @@ struct fw_filter_wr {
 #define S_FW_FILTER_WR_MATCHTYPEM      0
 #define V_FW_FILTER_WR_MATCHTYPEM(x)   ((x) << S_FW_FILTER_WR_MATCHTYPEM)
 
+#define S_FW_FILTER2_WR_NATMODE                5
+#define V_FW_FILTER2_WR_NATMODE(x)     ((x) << S_FW_FILTER2_WR_NATMODE)
+
 /******************************************************************************
  *  C O M M A N D s
  *********************/
@@ -642,6 +691,7 @@ enum fw_params_param_dev {
        FW_PARAMS_PARAM_DEV_FWREV       = 0x0B, /* fw version */
        FW_PARAMS_PARAM_DEV_TPREV       = 0x0C, /* tp version */
        FW_PARAMS_PARAM_DEV_ULPTX_MEMWRITE_DSGL = 0x17,
+       FW_PARAMS_PARAM_DEV_FILTER2_WR  = 0x1D,
 };
 
 /*
diff --git a/drivers/net/cxgbe/cxgbe_filter.c b/drivers/net/cxgbe/cxgbe_filter.c
index d098b9308..6744b6a60 100644
--- a/drivers/net/cxgbe/cxgbe_filter.c
+++ b/drivers/net/cxgbe/cxgbe_filter.c
@@ -30,6 +30,10 @@ int validate_filter(struct adapter *adapter, struct 
ch_filter_specification *fs)
 
 #undef S
 #undef U
+
+       if (fs->nat_mode && !adapter->params.filter2_wr_support)
+               return -EOPNOTSUPP;
+
        return 0;
 }
 
@@ -193,7 +197,7 @@ int set_filter_wr(struct rte_eth_dev *dev, unsigned int 
fidx)
        struct adapter *adapter = ethdev2adap(dev);
        struct filter_entry *f = &adapter->tids.ftid_tab[fidx];
        struct rte_mbuf *mbuf;
-       struct fw_filter_wr *fwr;
+       struct fw_filter2_wr *fwr;
        struct sge_ctrl_txq *ctrlq;
        unsigned int port_id = ethdev2pinfo(dev)->port_id;
        int ret;
@@ -208,13 +212,16 @@ int set_filter_wr(struct rte_eth_dev *dev, unsigned int 
fidx)
        mbuf->data_len = sizeof(*fwr);
        mbuf->pkt_len = mbuf->data_len;
 
-       fwr = rte_pktmbuf_mtod(mbuf, struct fw_filter_wr *);
+       fwr = rte_pktmbuf_mtod(mbuf, struct fw_filter2_wr *);
        memset(fwr, 0, sizeof(*fwr));
 
        /*
         * Construct the work request to set the filter.
         */
-       fwr->op_pkd = cpu_to_be32(V_FW_WR_OP(FW_FILTER_WR));
+       if (adapter->params.filter2_wr_support)
+               fwr->op_pkd = cpu_to_be32(V_FW_WR_OP(FW_FILTER2_WR));
+       else
+               fwr->op_pkd = cpu_to_be32(V_FW_WR_OP(FW_FILTER_WR));
        fwr->len16_pkd = cpu_to_be32(V_FW_WR_LEN16(sizeof(*fwr) / 16));
        fwr->tid_to_iq =
                cpu_to_be32(V_FW_FILTER_WR_TID(f->tid) |
@@ -224,6 +231,7 @@ int set_filter_wr(struct rte_eth_dev *dev, unsigned int 
fidx)
        fwr->del_filter_to_l2tix =
                cpu_to_be32(V_FW_FILTER_WR_DROP(f->fs.action == FILTER_DROP) |
                            V_FW_FILTER_WR_DIRSTEER(f->fs.dirsteer) |
+                           V_FW_FILTER_WR_LPBK(f->fs.action == FILTER_SWITCH) |
                            V_FW_FILTER_WR_HITCNTS(f->fs.hitcnts) |
                            V_FW_FILTER_WR_PRIO(f->fs.prio));
        fwr->ethtype = cpu_to_be16(f->fs.val.ethtype);
@@ -244,6 +252,15 @@ int set_filter_wr(struct rte_eth_dev *dev, unsigned int 
fidx)
        fwr->fp = cpu_to_be16(f->fs.val.fport);
        fwr->fpm = cpu_to_be16(f->fs.mask.fport);
 
+       if (adapter->params.filter2_wr_support && f->fs.nat_mode) {
+               fwr->natmode_to_ulp_type =
+                       V_FW_FILTER2_WR_NATMODE(f->fs.nat_mode);
+               memcpy(fwr->newlip, f->fs.nat_lip, sizeof(fwr->newlip));
+               memcpy(fwr->newfip, f->fs.nat_fip, sizeof(fwr->newfip));
+               fwr->newlport = cpu_to_be16(f->fs.nat_lport);
+               fwr->newfport = cpu_to_be16(f->fs.nat_fport);
+       }
+
        /*
         * Mark the filter as "pending" and ship off the Filter Work Request.
         * When we get the Work Request Reply we'll clear the pending status.
diff --git a/drivers/net/cxgbe/cxgbe_filter.h b/drivers/net/cxgbe/cxgbe_filter.h
index 4df37b9cd..609751474 100644
--- a/drivers/net/cxgbe/cxgbe_filter.h
+++ b/drivers/net/cxgbe/cxgbe_filter.h
@@ -97,6 +97,18 @@ struct ch_filter_specification {
        uint32_t dirsteer:1;    /* 0 => RSS, 1 => steer to iq */
        uint32_t iq:10;         /* ingress queue */
 
+       /*
+        * Switch proxy/rewrite fields.  An ingress packet which matches a
+        * filter with "switch" set will be looped back out as an egress
+        * packet -- potentially with some Ethernet header rewriting.
+        */
+       uint32_t nat_mode:3;    /* specify NAT operation mode */
+
+       uint8_t nat_lip[16];    /* local IP to use after NAT'ing */
+       uint8_t nat_fip[16];    /* foreign IP to use after NAT'ing */
+       uint16_t nat_lport;     /* local port to use after NAT'ing */
+       uint16_t nat_fport;     /* foreign port to use after NAT'ing */
+
        /* Filter rule value/mask pairs. */
        struct ch_filter_tuple val;
        struct ch_filter_tuple mask;
@@ -104,7 +116,19 @@ struct ch_filter_specification {
 
 enum {
        FILTER_PASS = 0,        /* default */
-       FILTER_DROP
+       FILTER_DROP,
+       FILTER_SWITCH,
+};
+
+enum {
+       NAT_MODE_NONE = 0,      /* No NAT performed */
+       NAT_MODE_DIP,           /* NAT on Dst IP */
+       NAT_MODE_DIP_DP,        /* NAT on Dst IP, Dst Port */
+       NAT_MODE_DIP_DP_SIP,    /* NAT on Dst IP, Dst Port and Src IP */
+       NAT_MODE_DIP_DP_SP,     /* NAT on Dst IP, Dst Port and Src Port */
+       NAT_MODE_SIP_SP,        /* NAT on Src IP and Src Port */
+       NAT_MODE_DIP_SIP_SP,    /* NAT on Dst IP, Src IP and Src Port */
+       NAT_MODE_ALL            /* NAT on entire 4-tuple */
 };
 
 enum filter_type {
diff --git a/drivers/net/cxgbe/cxgbe_flow.c b/drivers/net/cxgbe/cxgbe_flow.c
index 23b7d00b3..1ef330a0f 100644
--- a/drivers/net/cxgbe/cxgbe_flow.c
+++ b/drivers/net/cxgbe/cxgbe_flow.c
@@ -249,6 +249,97 @@ static int cxgbe_get_fidx(struct rte_flow *flow, unsigned 
int *fidx)
        return 0;
 }
 
+static int
+ch_rte_parse_nat(uint8_t nmode, struct ch_filter_specification *fs)
+{
+       /* nmode:
+        * BIT_0 = [src_ip],   BIT_1 = [dst_ip]
+        * BIT_2 = [src_port], BIT_3 = [dst_port]
+        *
+        * Only below cases are supported as per our spec.
+        */
+       switch (nmode) {
+       case 0:  /* 0000b */
+               fs->nat_mode = NAT_MODE_NONE;
+               break;
+       case 2:  /* 0010b */
+               fs->nat_mode = NAT_MODE_DIP;
+               break;
+       case 5:  /* 0101b */
+               fs->nat_mode = NAT_MODE_SIP_SP;
+               break;
+       case 7:  /* 0111b */
+               fs->nat_mode = NAT_MODE_DIP_SIP_SP;
+               break;
+       case 10: /* 1010b */
+               fs->nat_mode = NAT_MODE_DIP_DP;
+               break;
+       case 11: /* 1011b */
+               fs->nat_mode = NAT_MODE_DIP_DP_SIP;
+               break;
+       case 14: /* 1110 */
+               fs->nat_mode = NAT_MODE_DIP_DP_SP;
+               break;
+       case 15: /* 1111b */
+               fs->nat_mode = NAT_MODE_ALL;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int
+ch_rte_parse_atype_switch(const struct rte_flow_action *a, uint8_t *nmode,
+                         struct ch_filter_specification *fs,
+                         struct rte_flow_error *e)
+{
+       const struct rte_flow_action_of_set_nw_ipv4 *ipv4;
+       const struct rte_flow_action_of_set_nw_ipv6 *ipv6;
+       const struct rte_flow_action_of_set_tp *port;
+
+       switch (a->type) {
+       case RTE_FLOW_ACTION_TYPE_OF_SET_NW_IPV4_SRC:
+               ipv4 = (const struct rte_flow_action_of_set_nw_ipv4 *)a->conf;
+               memcpy(fs->nat_fip, &ipv4->ipv4_addr, sizeof(ipv4->ipv4_addr));
+               *nmode |= 1 << 0;
+               break;
+       case RTE_FLOW_ACTION_TYPE_OF_SET_NW_IPV4_DST:
+               ipv4 = (const struct rte_flow_action_of_set_nw_ipv4 *)a->conf;
+               memcpy(fs->nat_lip, &ipv4->ipv4_addr, sizeof(ipv4->ipv4_addr));
+               *nmode |= 1 << 1;
+               break;
+       case RTE_FLOW_ACTION_TYPE_OF_SET_NW_IPV6_SRC:
+               ipv6 = (const struct rte_flow_action_of_set_nw_ipv6 *)a->conf;
+               memcpy(fs->nat_fip, ipv6->ipv6_addr, sizeof(ipv6->ipv6_addr));
+               *nmode |= 1 << 0;
+               break;
+       case RTE_FLOW_ACTION_TYPE_OF_SET_NW_IPV6_DST:
+               ipv6 = (const struct rte_flow_action_of_set_nw_ipv6 *)a->conf;
+               memcpy(fs->nat_lip, ipv6->ipv6_addr, sizeof(ipv6->ipv6_addr));
+               *nmode |= 1 << 1;
+               break;
+       case RTE_FLOW_ACTION_TYPE_OF_SET_TP_SRC:
+               port = (const struct rte_flow_action_of_set_tp *)a->conf;
+               fs->nat_fport = be16_to_cpu(port->port);
+               *nmode |= 1 << 2;
+               break;
+       case RTE_FLOW_ACTION_TYPE_OF_SET_TP_DST:
+               port = (const struct rte_flow_action_of_set_tp *)a->conf;
+               fs->nat_lport = be16_to_cpu(port->port);
+               *nmode |= 1 << 3;
+               break;
+       default:
+               /* We are not supposed to come here */
+               return rte_flow_error_set(e, EINVAL,
+                                         RTE_FLOW_ERROR_TYPE_ACTION, a,
+                                         "Action not supported");
+       }
+
+       return 0;
+}
+
 static int
 cxgbe_rtef_parse_actions(struct rte_flow *flow,
                         const struct rte_flow_action action[],
@@ -257,6 +348,8 @@ cxgbe_rtef_parse_actions(struct rte_flow *flow,
        struct ch_filter_specification *fs = &flow->fs;
        const struct rte_flow_action_queue *q;
        const struct rte_flow_action *a;
+       int ret;
+       uint8_t nmode = 0, nat_ipv4 = 0, nat_ipv6 = 0;
        char abit = 0;
 
        for (a = action; a->type != RTE_FLOW_ACTION_TYPE_END; a++) {
@@ -291,6 +384,36 @@ cxgbe_rtef_parse_actions(struct rte_flow *flow,
                case RTE_FLOW_ACTION_TYPE_COUNT:
                        fs->hitcnts = 1;
                        break;
+
+               /* switch actions */
+               case RTE_FLOW_ACTION_TYPE_OF_SET_NW_IPV4_SRC:
+               case RTE_FLOW_ACTION_TYPE_OF_SET_NW_IPV4_DST:
+                       nat_ipv4++;
+                       goto action_switch;
+               case RTE_FLOW_ACTION_TYPE_OF_SET_NW_IPV6_SRC:
+               case RTE_FLOW_ACTION_TYPE_OF_SET_NW_IPV6_DST:
+                       nat_ipv6++;
+               case RTE_FLOW_ACTION_TYPE_OF_SET_TP_SRC:
+               case RTE_FLOW_ACTION_TYPE_OF_SET_TP_DST:
+action_switch:
+                       /* We allow multiple switch actions, but switch is
+                        * not compatible with either queue or drop
+                        */
+                       if (abit++ && fs->action != FILTER_SWITCH)
+                               return rte_flow_error_set(e, EINVAL,
+                                               RTE_FLOW_ERROR_TYPE_ACTION, a,
+                                               "overlapping action specified");
+                       if (nat_ipv4 && nat_ipv6)
+                               return rte_flow_error_set(e, EINVAL,
+                                       RTE_FLOW_ERROR_TYPE_ACTION, a,
+                                       "Can't have one address ipv4 and the"
+                                       " other ipv6");
+
+                       ret = ch_rte_parse_atype_switch(a, &nmode, fs, e);
+                       if (ret)
+                               return ret;
+                       fs->action = FILTER_SWITCH;
+                       break;
                default:
                        /* Not supported action : return error */
                        return rte_flow_error_set(e, ENOTSUP,
@@ -299,6 +422,10 @@ cxgbe_rtef_parse_actions(struct rte_flow *flow,
                }
        }
 
+       if (ch_rte_parse_nat(nmode, fs))
+               return rte_flow_error_set(e, EINVAL,
+                                         RTE_FLOW_ERROR_TYPE_ACTION, a,
+                                         "invalid settings for swich action");
        return 0;
 }
 
diff --git a/drivers/net/cxgbe/cxgbe_main.c b/drivers/net/cxgbe/cxgbe_main.c
index 21ad380ae..50d93e7da 100644
--- a/drivers/net/cxgbe/cxgbe_main.c
+++ b/drivers/net/cxgbe/cxgbe_main.c
@@ -990,6 +990,16 @@ static int adap_init0(struct adapter *adap)
        if (ret < 0)
                goto bye;
 
+       /* See if FW supports FW_FILTER2 work request */
+       if (is_t4(adap->params.chip)) {
+               adap->params.filter2_wr_support = 0;
+       } else {
+               params[0] = FW_PARAM_DEV(FILTER2_WR);
+               ret = t4_query_params(adap, adap->mbox, adap->pf, 0,
+                                     1, params, val);
+               adap->params.filter2_wr_support = (ret == 0 && val[0] != 0);
+       }
+
        /* query tid-related parameters */
        params[0] = FW_PARAM_DEV(NTID);
        ret = t4_query_params(adap, adap->mbox, adap->pf, 0, 1,
-- 
2.14.1

Reply via email to