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

Introduce rte_flow skeleton and implement validate operation.

Parse and convert <item>, <action>, <attributes> into hardware
specification. Perform validation, including basic sanity tests
and underlying device's supported filter capability checks.

Currently add support for:
<item>: IPv4, IPv6, TCP, and UDP.
<action>: Drop, Queue, and Count.

Also add sanity checks to ensure filters are created at specified
index in LE-TCAM region. The index in LE-TCAM region indicates
the filter rule's priority with index 0 having the highest priority.
If no index is specified, filters are created at closest available
free index.

Signed-off-by: Shagun Agrawal <shag...@chelsio.com>
Signed-off-by: Kumar Sanghvi <kuma...@chelsio.com>
Signed-off-by: Rahul Lakkireddy <rahul.lakkire...@chelsio.com>
---
 doc/guides/nics/cxgbe.rst              |   1 +
 doc/guides/nics/features/cxgbe.ini     |   1 +
 doc/guides/rel_notes/release_18_08.rst |   5 +
 drivers/net/cxgbe/Makefile             |   2 +
 drivers/net/cxgbe/base/adapter.h       |  22 ++
 drivers/net/cxgbe/cxgbe_ethdev.c       |   2 +
 drivers/net/cxgbe/cxgbe_filter.c       |  77 ++++++
 drivers/net/cxgbe/cxgbe_filter.h       |  91 +++++++
 drivers/net/cxgbe/cxgbe_flow.c         | 473 +++++++++++++++++++++++++++++++++
 drivers/net/cxgbe/cxgbe_flow.h         |  38 +++
 10 files changed, 712 insertions(+)
 create mode 100644 drivers/net/cxgbe/cxgbe_filter.c
 create mode 100644 drivers/net/cxgbe/cxgbe_flow.c
 create mode 100644 drivers/net/cxgbe/cxgbe_flow.h

diff --git a/doc/guides/nics/cxgbe.rst b/doc/guides/nics/cxgbe.rst
index 78e391473..124022cfc 100644
--- a/doc/guides/nics/cxgbe.rst
+++ b/doc/guides/nics/cxgbe.rst
@@ -30,6 +30,7 @@ CXGBE and CXGBEVF PMD has support for:
 - All multicast mode
 - Port hardware statistics
 - Jumbo frames
+- Flow API
 
 Limitations
 -----------
diff --git a/doc/guides/nics/features/cxgbe.ini 
b/doc/guides/nics/features/cxgbe.ini
index 6cf5c13f5..88f2f92b7 100644
--- a/doc/guides/nics/features/cxgbe.ini
+++ b/doc/guides/nics/features/cxgbe.ini
@@ -16,6 +16,7 @@ Allmulticast mode    = Y
 RSS hash             = Y
 RSS key update       = Y
 Flow control         = Y
+Flow API             = Y
 CRC offload          = Y
 VLAN offload         = Y
 L3 checksum offload  = Y
diff --git a/doc/guides/rel_notes/release_18_08.rst 
b/doc/guides/rel_notes/release_18_08.rst
index 5bc23c537..bc0124295 100644
--- a/doc/guides/rel_notes/release_18_08.rst
+++ b/doc/guides/rel_notes/release_18_08.rst
@@ -41,6 +41,11 @@ New Features
      Also, make sure to start the actual text at the margin.
      =========================================================
 
+* **Added Flow API support for CXGBE PMD.**
+
+  Flow API support has been added to CXGBE Poll Mode Driver to offload
+  flows to Chelsio T5/T6 NICs.
+
 
 API Changes
 -----------
diff --git a/drivers/net/cxgbe/Makefile b/drivers/net/cxgbe/Makefile
index 79fdb6f06..edc5d8188 100644
--- a/drivers/net/cxgbe/Makefile
+++ b/drivers/net/cxgbe/Makefile
@@ -49,6 +49,8 @@ SRCS-$(CONFIG_RTE_LIBRTE_CXGBE_PMD) += cxgbevf_ethdev.c
 SRCS-$(CONFIG_RTE_LIBRTE_CXGBE_PMD) += cxgbe_main.c
 SRCS-$(CONFIG_RTE_LIBRTE_CXGBE_PMD) += cxgbevf_main.c
 SRCS-$(CONFIG_RTE_LIBRTE_CXGBE_PMD) += sge.c
+SRCS-$(CONFIG_RTE_LIBRTE_CXGBE_PMD) += cxgbe_filter.c
+SRCS-$(CONFIG_RTE_LIBRTE_CXGBE_PMD) += cxgbe_flow.c
 SRCS-$(CONFIG_RTE_LIBRTE_CXGBE_PMD) += t4_hw.c
 SRCS-$(CONFIG_RTE_LIBRTE_CXGBE_PMD) += t4vf_hw.c
 
diff --git a/drivers/net/cxgbe/base/adapter.h b/drivers/net/cxgbe/base/adapter.h
index 1a0f96e40..f3434d28a 100644
--- a/drivers/net/cxgbe/base/adapter.h
+++ b/drivers/net/cxgbe/base/adapter.h
@@ -312,6 +312,17 @@ struct adapter {
        struct tid_info tids;     /* Info used to access TID related tables */
 };
 
+/**
+ * ethdev2pinfo - return the port_info structure associated with a rte_eth_dev
+ * @dev: the rte_eth_dev
+ *
+ * Return the struct port_info associated with a rte_eth_dev
+ */
+static inline struct port_info *ethdev2pinfo(const struct rte_eth_dev *dev)
+{
+       return (struct port_info *)dev->data->dev_private;
+}
+
 /**
  * adap2pinfo - return the port_info of a port
  * @adap: the adapter
@@ -324,6 +335,17 @@ static inline struct port_info *adap2pinfo(const struct 
adapter *adap, int idx)
        return adap->port[idx];
 }
 
+/**
+ * ethdev2adap - return the adapter structure associated with a rte_eth_dev
+ * @dev: the rte_eth_dev
+ *
+ * Return the struct adapter associated with a rte_eth_dev
+ */
+static inline struct adapter *ethdev2adap(const struct rte_eth_dev *dev)
+{
+       return ethdev2pinfo(dev)->adapter;
+}
+
 #define CXGBE_PCI_REG(reg) rte_read32(reg)
 
 static inline uint64_t cxgbe_read_addr64(volatile void *addr)
diff --git a/drivers/net/cxgbe/cxgbe_ethdev.c b/drivers/net/cxgbe/cxgbe_ethdev.c
index 32450915c..1adb8e41f 100644
--- a/drivers/net/cxgbe/cxgbe_ethdev.c
+++ b/drivers/net/cxgbe/cxgbe_ethdev.c
@@ -36,6 +36,7 @@
 
 #include "cxgbe.h"
 #include "cxgbe_pfvf.h"
+#include "cxgbe_flow.h"
 
 /*
  * Macros needed to support the PCI Device ID Table ...
@@ -1036,6 +1037,7 @@ static const struct eth_dev_ops cxgbe_eth_dev_ops = {
        .rx_queue_start         = cxgbe_dev_rx_queue_start,
        .rx_queue_stop          = cxgbe_dev_rx_queue_stop,
        .rx_queue_release       = cxgbe_dev_rx_queue_release,
+       .filter_ctrl            = cxgbe_dev_filter_ctrl,
        .stats_get              = cxgbe_dev_stats_get,
        .stats_reset            = cxgbe_dev_stats_reset,
        .flow_ctrl_get          = cxgbe_flow_ctrl_get,
diff --git a/drivers/net/cxgbe/cxgbe_filter.c b/drivers/net/cxgbe/cxgbe_filter.c
new file mode 100644
index 000000000..6b10a8be1
--- /dev/null
+++ b/drivers/net/cxgbe/cxgbe_filter.c
@@ -0,0 +1,77 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2014-2018 Chelsio Communications.
+ * All rights reserved.
+ */
+
+#include "common.h"
+#include "t4_regs.h"
+#include "cxgbe_filter.h"
+
+/**
+ * Validate if the requested filter specification can be set by checking
+ * if the requested features have been enabled
+ */
+int validate_filter(struct adapter *adapter, struct ch_filter_specification 
*fs)
+{
+       u32 fconf;
+
+       /*
+        * Check for unconfigured fields being used.
+        */
+       fconf = adapter->params.tp.vlan_pri_map;
+
+#define S(_field) \
+       (fs->val._field || fs->mask._field)
+#define U(_mask, _field) \
+       (!(fconf & (_mask)) && S(_field))
+
+       if (U(F_ETHERTYPE, ethtype) || U(F_PROTOCOL, proto))
+               return -EOPNOTSUPP;
+
+#undef S
+#undef U
+       return 0;
+}
+
+/**
+ * Check if entry already filled.
+ */
+bool is_filter_set(struct tid_info *t, int fidx, int family)
+{
+       bool result = FALSE;
+       int i, max;
+
+       /* IPv6 requires four slots and IPv4 requires only 1 slot.
+        * Ensure, there's enough slots available.
+        */
+       max = family == FILTER_TYPE_IPV6 ? fidx + 3 : fidx;
+
+       t4_os_lock(&t->ftid_lock);
+       for (i = fidx; i <= max; i++) {
+               if (rte_bitmap_get(t->ftid_bmap, i)) {
+                       result = TRUE;
+                       break;
+               }
+       }
+       t4_os_unlock(&t->ftid_lock);
+       return result;
+}
+
+/**
+ * Allocate a available free entry
+ */
+int cxgbe_alloc_ftid(struct adapter *adap, unsigned int family)
+{
+       struct tid_info *t = &adap->tids;
+       int pos;
+       int size = t->nftids;
+
+       t4_os_lock(&t->ftid_lock);
+       if (family == FILTER_TYPE_IPV6)
+               pos = cxgbe_bitmap_find_free_region(t->ftid_bmap, size, 4);
+       else
+               pos = cxgbe_find_first_zero_bit(t->ftid_bmap, size);
+       t4_os_unlock(&t->ftid_lock);
+
+       return pos < size ? pos : -1;
+}
diff --git a/drivers/net/cxgbe/cxgbe_filter.h b/drivers/net/cxgbe/cxgbe_filter.h
index d69c79e80..a9d2d3d39 100644
--- a/drivers/net/cxgbe/cxgbe_filter.h
+++ b/drivers/net/cxgbe/cxgbe_filter.h
@@ -77,21 +77,112 @@ struct ch_filter_tuple {
  * Filter specification
  */
 struct ch_filter_specification {
+       /* Administrative fields for filter. */
+       uint32_t hitcnts:1;     /* count filter hits in TCB */
+       uint32_t prio:1;        /* filter has priority over active/server */
+
+       /*
+        * Fundamental filter typing.  This is the one element of filter
+        * matching that doesn't exist as a (value, mask) tuple.
+        */
+       uint32_t type:1;        /* 0 => IPv4, 1 => IPv6 */
+
+       /*
+        * Packet dispatch information.  Ingress packets which match the
+        * filter rules will be dropped, passed to the host or switched back
+        * out as egress packets.
+        */
+       uint32_t action:2;      /* drop, pass, switch */
+
+       uint32_t dirsteer:1;    /* 0 => RSS, 1 => steer to iq */
+       uint32_t iq:10;         /* ingress queue */
+
        /* Filter rule value/mask pairs. */
        struct ch_filter_tuple val;
        struct ch_filter_tuple mask;
 };
 
+enum {
+       FILTER_PASS = 0,        /* default */
+       FILTER_DROP
+};
+
+enum filter_type {
+       FILTER_TYPE_IPV4 = 0,
+       FILTER_TYPE_IPV6,
+};
+
 /*
  * Host shadow copy of ingress filter entry.  This is in host native format
  * and doesn't match the ordering or bit order, etc. of the hardware or the
  * firmware command.
  */
 struct filter_entry {
+       struct rte_eth_dev *dev;    /* Port's rte eth device */
+
        /*
         * The filter itself.
         */
        struct ch_filter_specification fs;
 };
 
+#define FILTER_ID_MAX   (~0U)
+
+struct tid_info;
+struct adapter;
+
+/**
+ * Find first clear bit in the bitmap.
+ */
+static inline unsigned int cxgbe_find_first_zero_bit(struct rte_bitmap *bmap,
+                                                    unsigned int size)
+{
+       unsigned int idx;
+
+       for (idx = 0; idx < size; idx++)
+               if (!rte_bitmap_get(bmap, idx))
+                       break;
+
+       return idx;
+}
+
+/**
+ * Find a free region of 'num' consecutive entries.
+ */
+static inline unsigned int
+cxgbe_bitmap_find_free_region(struct rte_bitmap *bmap, unsigned int size,
+                             unsigned int num)
+{
+       unsigned int idx, j, free = 0;
+
+       if (num > size)
+               return size;
+
+       for (idx = 0; idx < size; idx += num) {
+               for (j = 0; j < num; j++) {
+                       if (!rte_bitmap_get(bmap, idx + j)) {
+                               free++;
+                       } else {
+                               free = 0;
+                               break;
+                       }
+               }
+
+               /* Found the Region */
+               if (free == num)
+                       break;
+
+               /* Reached the end and still no region found */
+               if ((idx + num) > size) {
+                       idx = size;
+                       break;
+               }
+       }
+
+       return idx;
+}
+
+bool is_filter_set(struct tid_info *, int fidx, int family);
+int cxgbe_alloc_ftid(struct adapter *adap, unsigned int family);
+int validate_filter(struct adapter *adap, struct ch_filter_specification *fs);
 #endif /* _CXGBE_FILTER_H_ */
diff --git a/drivers/net/cxgbe/cxgbe_flow.c b/drivers/net/cxgbe/cxgbe_flow.c
new file mode 100644
index 000000000..a01708e70
--- /dev/null
+++ b/drivers/net/cxgbe/cxgbe_flow.c
@@ -0,0 +1,473 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2014-2018 Chelsio Communications.
+ * All rights reserved.
+ */
+#include "common.h"
+#include "cxgbe_flow.h"
+
+#define __CXGBE_FILL_FS(__v, __m, fs, elem, e) \
+do { \
+       if (!((fs)->val.elem || (fs)->mask.elem)) { \
+               (fs)->val.elem = (__v); \
+               (fs)->mask.elem = (__m); \
+       } else { \
+               return rte_flow_error_set(e, EINVAL, RTE_FLOW_ERROR_TYPE_ITEM, \
+                                         NULL, "a filter can be specified" \
+                                         " only once"); \
+       } \
+} while (0)
+
+#define __CXGBE_FILL_FS_MEMCPY(__v, __m, fs, elem) \
+do { \
+       memcpy(&(fs)->val.elem, &(__v), sizeof(__v)); \
+       memcpy(&(fs)->mask.elem, &(__m), sizeof(__m)); \
+} while (0)
+
+#define CXGBE_FILL_FS(v, m, elem) \
+       __CXGBE_FILL_FS(v, m, fs, elem, e)
+
+#define CXGBE_FILL_FS_MEMCPY(v, m, elem) \
+       __CXGBE_FILL_FS_MEMCPY(v, m, fs, elem)
+
+static int
+cxgbe_validate_item(const struct rte_flow_item *i, struct rte_flow_error *e)
+{
+       /* rte_flow specification does not allow it. */
+       if (!i->spec && (i->mask ||  i->last))
+               return rte_flow_error_set(e, EINVAL, RTE_FLOW_ERROR_TYPE_ITEM,
+                                  i, "last or mask given without spec");
+       /*
+        * We don't support it.
+        * Although, we can support values in last as 0's or last == spec.
+        * But this will not provide user with any additional functionality
+        * and will only increase the complexity for us.
+        */
+       if (i->last)
+               return rte_flow_error_set(e, ENOTSUP, RTE_FLOW_ERROR_TYPE_ITEM,
+                                  i, "last is not supported by chelsio pmd");
+       return 0;
+}
+
+static int
+ch_rte_parsetype_udp(const void *dmask, const struct rte_flow_item *item,
+                    struct ch_filter_specification *fs,
+                    struct rte_flow_error *e)
+{
+       const struct rte_flow_item_udp *val = item->spec;
+       const struct rte_flow_item_udp *umask = item->mask;
+       const struct rte_flow_item_udp *mask;
+
+       mask = umask ? umask : (const struct rte_flow_item_udp *)dmask;
+
+       if (mask->hdr.dgram_len || mask->hdr.dgram_cksum)
+               return rte_flow_error_set(e, ENOTSUP, RTE_FLOW_ERROR_TYPE_ITEM,
+                                         item,
+                                         "udp: only src/dst port supported");
+
+       CXGBE_FILL_FS(IPPROTO_UDP, 0xff, proto);
+       if (!val)
+               return 0;
+       CXGBE_FILL_FS(be16_to_cpu(val->hdr.src_port),
+                     be16_to_cpu(mask->hdr.src_port), fport);
+       CXGBE_FILL_FS(be16_to_cpu(val->hdr.dst_port),
+                     be16_to_cpu(mask->hdr.dst_port), lport);
+       return 0;
+}
+
+static int
+ch_rte_parsetype_tcp(const void *dmask, const struct rte_flow_item *item,
+                    struct ch_filter_specification *fs,
+                    struct rte_flow_error *e)
+{
+       const struct rte_flow_item_tcp *val = item->spec;
+       const struct rte_flow_item_tcp *umask = item->mask;
+       const struct rte_flow_item_tcp *mask;
+
+       mask = umask ? umask : (const struct rte_flow_item_tcp *)dmask;
+
+       if (mask->hdr.sent_seq || mask->hdr.recv_ack || mask->hdr.data_off ||
+           mask->hdr.tcp_flags || mask->hdr.rx_win || mask->hdr.cksum ||
+           mask->hdr.tcp_urp)
+               return rte_flow_error_set(e, ENOTSUP, RTE_FLOW_ERROR_TYPE_ITEM,
+                                         item,
+                                         "tcp: only src/dst port supported");
+
+       CXGBE_FILL_FS(IPPROTO_TCP, 0xff, proto);
+       if (!val)
+               return 0;
+       CXGBE_FILL_FS(be16_to_cpu(val->hdr.src_port),
+                     be16_to_cpu(mask->hdr.src_port), fport);
+       CXGBE_FILL_FS(be16_to_cpu(val->hdr.dst_port),
+                     be16_to_cpu(mask->hdr.dst_port), lport);
+       return 0;
+}
+
+static int
+ch_rte_parsetype_ipv4(const void *dmask, const struct rte_flow_item *item,
+                     struct ch_filter_specification *fs,
+                     struct rte_flow_error *e)
+{
+       const struct rte_flow_item_ipv4 *val = item->spec;
+       const struct rte_flow_item_ipv4 *umask = item->mask;
+       const struct rte_flow_item_ipv4 *mask;
+
+       mask = umask ? umask : (const struct rte_flow_item_ipv4 *)dmask;
+
+       if (mask->hdr.time_to_live || mask->hdr.type_of_service)
+               return rte_flow_error_set(e, ENOTSUP, RTE_FLOW_ERROR_TYPE_ITEM,
+                                         item, "ttl/tos are not supported");
+
+       fs->type = FILTER_TYPE_IPV4;
+       CXGBE_FILL_FS(ETHER_TYPE_IPv4, 0xffff, ethtype);
+       if (!val)
+               return 0; /* ipv4 wild card */
+
+       CXGBE_FILL_FS(val->hdr.next_proto_id, mask->hdr.next_proto_id, proto);
+       CXGBE_FILL_FS_MEMCPY(val->hdr.dst_addr, mask->hdr.dst_addr, lip);
+       CXGBE_FILL_FS_MEMCPY(val->hdr.src_addr, mask->hdr.src_addr, fip);
+
+       return 0;
+}
+
+static int
+ch_rte_parsetype_ipv6(const void *dmask, const struct rte_flow_item *item,
+                     struct ch_filter_specification *fs,
+                     struct rte_flow_error *e)
+{
+       const struct rte_flow_item_ipv6 *val = item->spec;
+       const struct rte_flow_item_ipv6 *umask = item->mask;
+       const struct rte_flow_item_ipv6 *mask;
+
+       mask = umask ? umask : (const struct rte_flow_item_ipv6 *)dmask;
+
+       if (mask->hdr.vtc_flow ||
+           mask->hdr.payload_len || mask->hdr.hop_limits)
+               return rte_flow_error_set(e, ENOTSUP, RTE_FLOW_ERROR_TYPE_ITEM,
+                                         item,
+                                         "tc/flow/hop are not supported");
+
+       fs->type = FILTER_TYPE_IPV6;
+       CXGBE_FILL_FS(ETHER_TYPE_IPv6, 0xffff, ethtype);
+       if (!val)
+               return 0; /* ipv6 wild card */
+
+       CXGBE_FILL_FS(val->hdr.proto, mask->hdr.proto, proto);
+       CXGBE_FILL_FS_MEMCPY(val->hdr.dst_addr, mask->hdr.dst_addr, lip);
+       CXGBE_FILL_FS_MEMCPY(val->hdr.src_addr, mask->hdr.src_addr, fip);
+
+       return 0;
+}
+
+static int
+cxgbe_rtef_parse_attr(struct rte_flow *flow, const struct rte_flow_attr *attr,
+                     struct rte_flow_error *e)
+{
+       if (attr->egress)
+               return rte_flow_error_set(e, ENOTSUP, RTE_FLOW_ERROR_TYPE_ATTR,
+                                         attr, "attribute:<egress> is"
+                                         " not supported !");
+       if (attr->group > 0)
+               return rte_flow_error_set(e, ENOTSUP, RTE_FLOW_ERROR_TYPE_ATTR,
+                                         attr, "group parameter is"
+                                         " not supported.");
+
+       flow->fidx = attr->priority ? attr->priority - 1 : FILTER_ID_MAX;
+
+       return 0;
+}
+
+static inline int check_rxq(struct rte_eth_dev *dev, uint16_t rxq)
+{
+       struct port_info *pi = ethdev2pinfo(dev);
+
+       if (rxq > pi->n_rx_qsets)
+               return -EINVAL;
+       return 0;
+}
+
+static int cxgbe_validate_fidxondel(struct filter_entry *f, unsigned int fidx)
+{
+       struct adapter *adap = ethdev2adap(f->dev);
+       struct ch_filter_specification fs = f->fs;
+
+       if (fidx >= adap->tids.nftids) {
+               dev_err(adap, "invalid flow index %d.\n", fidx);
+               return -EINVAL;
+       }
+       if (!is_filter_set(&adap->tids, fidx, fs.type)) {
+               dev_err(adap, "Already free fidx:%d f:%p\n", fidx, f);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int
+cxgbe_validate_fidxonadd(struct ch_filter_specification *fs,
+                        struct adapter *adap, unsigned int fidx)
+{
+       if (is_filter_set(&adap->tids, fidx, fs->type)) {
+               dev_err(adap, "filter index: %d is busy.\n", fidx);
+               return -EBUSY;
+       }
+       if (fidx >= adap->tids.nftids) {
+               dev_err(adap, "filter index (%u) >= max(%u)\n",
+                       fidx, adap->tids.nftids);
+               return -ERANGE;
+       }
+
+       return 0;
+}
+
+static int
+cxgbe_verify_fidx(struct rte_flow *flow, unsigned int fidx, uint8_t del)
+{
+       return del ? cxgbe_validate_fidxondel(flow->f, fidx) :
+               cxgbe_validate_fidxonadd(&flow->fs,
+                                        ethdev2adap(flow->dev), fidx);
+}
+
+static int cxgbe_get_fidx(struct rte_flow *flow, unsigned int *fidx)
+{
+       struct ch_filter_specification *fs = &flow->fs;
+       struct adapter *adap = ethdev2adap(flow->dev);
+
+       /* For tcam get the next available slot, if default value specified */
+       if (flow->fidx == FILTER_ID_MAX) {
+               int idx;
+
+               idx = cxgbe_alloc_ftid(adap, fs->type);
+               if (idx < 0) {
+                       dev_err(adap, "unable to get a filter index in tcam\n");
+                       return -ENOMEM;
+               }
+               *fidx = (unsigned int)idx;
+       } else {
+               *fidx = flow->fidx;
+       }
+
+       return 0;
+}
+
+static int
+cxgbe_rtef_parse_actions(struct rte_flow *flow,
+                        const struct rte_flow_action action[],
+                        struct rte_flow_error *e)
+{
+       struct ch_filter_specification *fs = &flow->fs;
+       const struct rte_flow_action_queue *q;
+       const struct rte_flow_action *a;
+       char abit = 0;
+
+       for (a = action; a->type != RTE_FLOW_ACTION_TYPE_END; a++) {
+               switch (a->type) {
+               case RTE_FLOW_ACTION_TYPE_VOID:
+                       continue;
+               case RTE_FLOW_ACTION_TYPE_DROP:
+                       if (abit++)
+                               return rte_flow_error_set(e, EINVAL,
+                                               RTE_FLOW_ERROR_TYPE_ACTION, a,
+                                               "specify only 1 pass/drop");
+                       fs->action = FILTER_DROP;
+                       break;
+               case RTE_FLOW_ACTION_TYPE_QUEUE:
+                       q = (const struct rte_flow_action_queue *)a->conf;
+                       if (!q)
+                               return rte_flow_error_set(e, EINVAL,
+                                               RTE_FLOW_ERROR_TYPE_ACTION, q,
+                                               "specify rx queue index");
+                       if (check_rxq(flow->dev, q->index))
+                               return rte_flow_error_set(e, EINVAL,
+                                               RTE_FLOW_ERROR_TYPE_ACTION, q,
+                                               "Invalid rx queue");
+                       if (abit++)
+                               return rte_flow_error_set(e, EINVAL,
+                                               RTE_FLOW_ERROR_TYPE_ACTION, a,
+                                               "specify only 1 pass/drop");
+                       fs->action = FILTER_PASS;
+                       fs->dirsteer = 1;
+                       fs->iq = q->index;
+                       break;
+               case RTE_FLOW_ACTION_TYPE_COUNT:
+                       fs->hitcnts = 1;
+                       break;
+               default:
+                       /* Not supported action : return error */
+                       return rte_flow_error_set(e, ENOTSUP,
+                                                 RTE_FLOW_ERROR_TYPE_ACTION,
+                                                 a, "Action not supported");
+               }
+       }
+
+       return 0;
+}
+
+struct chrte_fparse parseitem[] = {
+       [RTE_FLOW_ITEM_TYPE_IPV4] = {
+               .fptr  = ch_rte_parsetype_ipv4,
+               .dmask = &rte_flow_item_ipv4_mask,
+       },
+
+       [RTE_FLOW_ITEM_TYPE_IPV6] = {
+               .fptr  = ch_rte_parsetype_ipv6,
+               .dmask = &rte_flow_item_ipv6_mask,
+       },
+
+       [RTE_FLOW_ITEM_TYPE_UDP] = {
+               .fptr  = ch_rte_parsetype_udp,
+               .dmask = &rte_flow_item_udp_mask,
+       },
+
+       [RTE_FLOW_ITEM_TYPE_TCP] = {
+               .fptr  = ch_rte_parsetype_tcp,
+               .dmask = &rte_flow_item_tcp_mask,
+       },
+};
+
+static int
+cxgbe_rtef_parse_items(struct rte_flow *flow,
+                      const struct rte_flow_item items[],
+                      struct rte_flow_error *e)
+{
+       const struct rte_flow_item *i;
+       char repeat[ARRAY_SIZE(parseitem)] = {0};
+
+       for (i = items; i->type != RTE_FLOW_ITEM_TYPE_END; i++) {
+               struct chrte_fparse *idx = &flow->item_parser[i->type];
+               int ret;
+
+               if (i->type > ARRAY_SIZE(parseitem))
+                       return rte_flow_error_set(e, ENOTSUP,
+                                                 RTE_FLOW_ERROR_TYPE_ITEM,
+                                                 i, "Item not supported");
+
+               switch (i->type) {
+               case RTE_FLOW_ITEM_TYPE_VOID:
+                       continue;
+               default:
+                       /* check if item is repeated */
+                       if (repeat[i->type])
+                               return rte_flow_error_set(e, ENOTSUP,
+                                               RTE_FLOW_ERROR_TYPE_ITEM, i,
+                                               "parse items cannot be repeated 
(except void)");
+                       repeat[i->type] = 1;
+
+                       /* validate the item */
+                       ret = cxgbe_validate_item(i, e);
+                       if (ret)
+                               return ret;
+
+                       if (!idx || !idx->fptr) {
+                               return rte_flow_error_set(e, ENOTSUP,
+                                               RTE_FLOW_ERROR_TYPE_ITEM, i,
+                                               "Item not supported");
+                       } else {
+                               ret = idx->fptr(idx->dmask, i, &flow->fs, e);
+                               if (ret)
+                                       return ret;
+                       }
+               }
+       }
+
+       return 0;
+}
+
+static int
+cxgbe_flow_parse(struct rte_flow *flow,
+                const struct rte_flow_attr *attr,
+                const struct rte_flow_item item[],
+                const struct rte_flow_action action[],
+                struct rte_flow_error *e)
+{
+       int ret;
+
+       /* parse user request into ch_filter_specification */
+       ret = cxgbe_rtef_parse_attr(flow, attr, e);
+       if (ret)
+               return ret;
+       ret = cxgbe_rtef_parse_items(flow, item, e);
+       if (ret)
+               return ret;
+       return cxgbe_rtef_parse_actions(flow, action, e);
+}
+
+static int
+cxgbe_flow_validate(struct rte_eth_dev *dev,
+                   const struct rte_flow_attr *attr,
+                   const struct rte_flow_item item[],
+                   const struct rte_flow_action action[],
+                   struct rte_flow_error *e)
+{
+       struct adapter *adap = ethdev2adap(dev);
+       struct rte_flow *flow;
+       unsigned int fidx;
+       int ret;
+
+       flow = t4_os_alloc(sizeof(struct rte_flow));
+       if (!flow)
+               return rte_flow_error_set(e, ENOMEM, RTE_FLOW_ERROR_TYPE_HANDLE,
+                               NULL,
+                               "Unable to allocate memory for filter_entry");
+
+       flow->item_parser = parseitem;
+       flow->dev = dev;
+
+       ret = cxgbe_flow_parse(flow, attr, item, action, e);
+       if (ret) {
+               t4_os_free(flow);
+               return ret;
+       }
+
+       if (validate_filter(adap, &flow->fs)) {
+               t4_os_free(flow);
+               return rte_flow_error_set(e, EINVAL, RTE_FLOW_ERROR_TYPE_HANDLE,
+                               NULL,
+                               "validation failed. Check f/w config file.");
+       }
+
+       if (cxgbe_get_fidx(flow, &fidx)) {
+               t4_os_free(flow);
+               return rte_flow_error_set(e, ENOMEM, RTE_FLOW_ERROR_TYPE_HANDLE,
+                                         NULL, "no memory in tcam.");
+       }
+
+       if (cxgbe_verify_fidx(flow, fidx, 0)) {
+               t4_os_free(flow);
+               return rte_flow_error_set(e, EINVAL, RTE_FLOW_ERROR_TYPE_HANDLE,
+                                         NULL, "validation failed");
+       }
+
+       t4_os_free(flow);
+       return 0;
+}
+
+static const struct rte_flow_ops cxgbe_flow_ops = {
+       .validate       = cxgbe_flow_validate,
+       .create         = NULL,
+       .destroy        = NULL,
+       .flush          = NULL,
+       .query          = NULL,
+       .isolate        = NULL,
+};
+
+int
+cxgbe_dev_filter_ctrl(struct rte_eth_dev *dev,
+                     enum rte_filter_type filter_type,
+                     enum rte_filter_op filter_op,
+                     void *arg)
+{
+       int ret = 0;
+
+       RTE_SET_USED(dev);
+       switch (filter_type) {
+       case RTE_ETH_FILTER_GENERIC:
+               if (filter_op != RTE_ETH_FILTER_GET)
+                       return -EINVAL;
+               *(const void **)arg = &cxgbe_flow_ops;
+               break;
+       default:
+               ret = -ENOTSUP;
+               break;
+       }
+       return ret;
+}
diff --git a/drivers/net/cxgbe/cxgbe_flow.h b/drivers/net/cxgbe/cxgbe_flow.h
new file mode 100644
index 000000000..45bc37082
--- /dev/null
+++ b/drivers/net/cxgbe/cxgbe_flow.h
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2014-2018 Chelsio Communications.
+ * All rights reserved.
+ */
+#ifndef _CXGBE_FLOW_H_
+#define _CXGBE_FLOW_H_
+
+#include <rte_flow_driver.h>
+#include "cxgbe_filter.h"
+
+struct chrte_fparse {
+       int (*fptr)(const void *mask, /* currently supported mask */
+                   const struct rte_flow_item *item, /* user input */
+                   struct ch_filter_specification *fs, /* where to parse */
+                   struct rte_flow_error *e);
+       const void *dmask; /* Specify what is supported by chelsio by default*/
+};
+
+struct rte_flow {
+       struct filter_entry *f;
+       struct ch_filter_specification fs; /* temp, to create filter */
+       struct chrte_fparse *item_parser;
+       /*
+        * filter_entry doesn't store user priority.
+        * Post creation of filter this will indicate the
+        * flow index (fidx) for both hash and tcam filters
+        */
+       unsigned int fidx;
+       struct rte_eth_dev *dev;
+};
+
+int
+cxgbe_dev_filter_ctrl(struct rte_eth_dev *dev,
+                     enum rte_filter_type filter_type,
+                     enum rte_filter_op filter_op,
+                     void *arg);
+
+#endif /* _CXGBE_FLOW_H_ */
-- 
2.14.1

Reply via email to