Enables flow classification on softnic rx path so that proceding
functions of the packet processing pipeline such as metering and
policing could be implemented.

Example: Create "soft" port for "hard" port "0000:02:00.1",
enable the Flow Classification (FC) feature with default
settings:
  --vdev 'net_softnic0,hard_name=0000:02:00.1,soft_fc=on'

Signed-off-by: Jasvinder Singh <jasvinder.si...@intel.com>
---
 drivers/net/softnic/Makefile                    |   2 +-
 drivers/net/softnic/rte_eth_softnic.c           | 389 +++++++++++++++++++++++-
 drivers/net/softnic/rte_eth_softnic.h           |  16 +
 drivers/net/softnic/rte_eth_softnic_fc.c        | 377 +++++++++++++++++++++++
 drivers/net/softnic/rte_eth_softnic_internals.h | 162 +++++++++-
 mk/rte.app.mk                                   |   2 +
 6 files changed, 937 insertions(+), 11 deletions(-)
 create mode 100644 drivers/net/softnic/rte_eth_softnic_fc.c

diff --git a/drivers/net/softnic/Makefile b/drivers/net/softnic/Makefile
index 09ed62e..4f7f6c9 100644
--- a/drivers/net/softnic/Makefile
+++ b/drivers/net/softnic/Makefile
@@ -51,7 +51,7 @@ LIBABIVER := 1
 #
 SRCS-$(CONFIG_RTE_LIBRTE_PMD_SOFTNIC) += rte_eth_softnic.c
 SRCS-$(CONFIG_RTE_LIBRTE_PMD_SOFTNIC) += rte_eth_softnic_tm.c
-
+SRCS-$(CONFIG_RTE_LIBRTE_PMD_SOFTNIC) += rte_eth_softnic_fc.c
 #
 # Export include files
 #
diff --git a/drivers/net/softnic/rte_eth_softnic.c 
b/drivers/net/softnic/rte_eth_softnic.c
index 3e47c2f..a5779d3 100644
--- a/drivers/net/softnic/rte_eth_softnic.c
+++ b/drivers/net/softnic/rte_eth_softnic.c
@@ -61,6 +61,12 @@
 #define PMD_PARAM_SOFT_TM_ENQ_BSZ                      "soft_tm_enq_bsz"
 #define PMD_PARAM_SOFT_TM_DEQ_BSZ                      "soft_tm_deq_bsz"
 
+#define PMD_PARAM_SOFT_FC                                      "soft_fc"
+#define PMD_PARAM_SOFT_FC_NB_RULES                     "soft_fc_nb_rules"
+#define PMD_PARAM_SOFT_FC_FLOW_KEY_SIZE                "soft_fc_flow_key_size"
+#define PMD_PARAM_SOFT_FC_FLOW_KEY_OFFSET      "soft_fc_flow_key_offset"
+#define PMD_PARAM_SOFT_FC_FLOW_KEY_MASK                "soft_fc_flow_key_mask"
+
 #define PMD_PARAM_HARD_NAME                                    "hard_name"
 #define PMD_PARAM_HARD_TX_QUEUE_ID                     "hard_tx_queue_id"
 
@@ -74,6 +80,11 @@ static const char *pmd_valid_args[] = {
        PMD_PARAM_SOFT_TM_QSIZE3,
        PMD_PARAM_SOFT_TM_ENQ_BSZ,
        PMD_PARAM_SOFT_TM_DEQ_BSZ,
+       PMD_PARAM_SOFT_FC,
+       PMD_PARAM_SOFT_FC_NB_RULES,
+       PMD_PARAM_SOFT_FC_FLOW_KEY_SIZE,
+       PMD_PARAM_SOFT_FC_FLOW_KEY_OFFSET,
+       PMD_PARAM_SOFT_FC_FLOW_KEY_MASK,
        PMD_PARAM_HARD_NAME,
        PMD_PARAM_HARD_TX_QUEUE_ID,
        NULL
@@ -96,6 +107,15 @@ static const struct rte_eth_dev_info pmd_dev_info = {
        },
 };
 
+static __rte_always_inline int
+run_tm(struct rte_eth_dev *dev);
+
+static __rte_always_inline int
+run_default(struct rte_eth_dev *dev);
+
+static __rte_always_inline int
+run_fc(struct rte_eth_dev *dev);
+
 static void
 pmd_dev_infos_get(struct rte_eth_dev *dev __rte_unused,
        struct rte_eth_dev_info *dev_info)
@@ -121,14 +141,28 @@ pmd_dev_configure(struct rte_eth_dev *dev)
 static int
 pmd_rx_queue_setup(struct rte_eth_dev *dev,
        uint16_t rx_queue_id,
-       uint16_t nb_rx_desc __rte_unused,
+       uint16_t nb_rx_desc,
        unsigned int socket_id,
        const struct rte_eth_rxconf *rx_conf __rte_unused,
        struct rte_mempool *mb_pool __rte_unused)
 {
        struct pmd_internals *p = dev->data->dev_private;
 
-       if (p->params.soft.intrusive == 0) {
+       if (p->params.soft.intrusive == 0 && fc_enabled(dev)) {
+               uint32_t size = RTE_ETH_NAME_MAX_LEN + strlen("_rxq") + 4;
+               char name[size];
+               struct rte_ring *r;
+
+               snprintf(name, sizeof(name), "%s_rxq%04x",
+                       dev->data->name, rx_queue_id);
+               r = rte_ring_create(name, nb_rx_desc, socket_id,
+                       RING_F_SP_ENQ | RING_F_SC_DEQ);
+               if (r == NULL)
+                       return -1;
+
+               dev->data->rx_queues[rx_queue_id] = r;
+
+       } else if (p->params.soft.intrusive == 0) {
                struct pmd_rx_queue *rxq;
 
                rxq = rte_zmalloc_socket(p->params.soft.name,
@@ -177,12 +211,34 @@ static int
 pmd_dev_start(struct rte_eth_dev *dev)
 {
        struct pmd_internals *p = dev->data->dev_private;
+       uint32_t tx_ops_flag = p->params.tx_ops_data.ops_flag;
+       uint32_t rx_ops_flag = p->params.rx_ops_data.ops_flag;
+       uint32_t nb_tx_ops = 0, nb_rx_ops = 0;
+
+       memset(&p->params.tx_ops_data.ops, 0,
+               MAX_NB_OPS * sizeof(*p->params.tx_ops_data.ops));
+       memset(&p->params.rx_ops_data.ops, 0,
+               MAX_NB_OPS * sizeof(*p->params.rx_ops_data.ops));
 
        if (tm_used(dev)) {
                int status = tm_start(p);
 
                if (status)
                        return status;
+
+               /** Set run time tx ops data*/
+               if (!(tx_ops_flag & PMD_FEATURE_TM)) {
+                       p->params.tx_ops_data.ops_flag |= PMD_FEATURE_TM;
+                       p->params.tx_ops_data.ops[nb_tx_ops++] = run_tm;
+               }
+       }
+
+       if (fc_enabled(dev)) {
+               /** Set run time rx ops data */
+               if (!(rx_ops_flag & PMD_FEATURE_FC)) {
+                       p->params.rx_ops_data.ops_flag |= PMD_FEATURE_FC;
+                       p->params.rx_ops_data.ops[nb_rx_ops++] = run_fc;
+               }
        }
 
        dev->data->dev_link.link_status = ETH_LINK_UP;
@@ -194,6 +250,12 @@ pmd_dev_start(struct rte_eth_dev *dev)
                dev->rx_pkt_burst = hard_dev->rx_pkt_burst;
        }
 
+       /* Set default if no tx ops */
+       if (nb_tx_ops == 0)
+               p->params.tx_ops_data.ops[nb_tx_ops++] = run_default;
+
+       p->params.rx_ops_data.nb_ops = nb_rx_ops;
+       p->params.tx_ops_data.nb_ops = nb_tx_ops;
        return 0;
 }
 
@@ -234,6 +296,18 @@ pmd_tm_ops_get(struct rte_eth_dev *dev, void *arg)
        return 0;
 }
 
+static int
+pmd_flow_ops_get(struct rte_eth_dev *dev,
+       __rte_unused enum rte_filter_type filter_type,
+       __rte_unused enum rte_filter_op filter_op,
+       void *arg)
+{
+       *(const struct rte_flow_ops **)arg =
+               (fc_enabled(dev)) ? &pmd_flow_ops : NULL;
+
+       return 0;
+}
+
 static const struct eth_dev_ops pmd_ops = {
        .dev_configure = pmd_dev_configure,
        .dev_start = pmd_dev_start,
@@ -244,10 +318,11 @@ static const struct eth_dev_ops pmd_ops = {
        .rx_queue_setup = pmd_rx_queue_setup,
        .tx_queue_setup = pmd_tx_queue_setup,
        .tm_ops_get = pmd_tm_ops_get,
+       .filter_ctrl = pmd_flow_ops_get,
 };
 
 static uint16_t
-pmd_rx_pkt_burst(void *rxq,
+pmd_rx_pkt_burst_default(void *rxq,
        struct rte_mbuf **rx_pkts,
        uint16_t nb_pkts)
 {
@@ -260,6 +335,19 @@ pmd_rx_pkt_burst(void *rxq,
 }
 
 static uint16_t
+pmd_rx_pkt_burst(void *rxq,
+       struct rte_mbuf **rx_pkts,
+       uint16_t nb_pkts)
+{
+       struct rte_ring *rx_queue = rxq;
+
+       return (uint16_t)rte_ring_sc_dequeue_burst(rx_queue,
+                       (void **)rx_pkts,
+                       nb_pkts,
+                       NULL);
+}
+
+static uint16_t
 pmd_tx_pkt_burst(void *txq,
        struct rte_mbuf **tx_pkts,
        uint16_t nb_pkts)
@@ -406,16 +494,138 @@ run_tm(struct rte_eth_dev *dev)
        return 0;
 }
 
-int
-rte_pmd_softnic_run(uint16_t port_id)
+static __rte_always_inline int
+fc_flow_classifier_run(struct pmd_internals *p,
+       struct rte_mbuf **pkts,
+       const uint16_t nb_pkts,
+       uint32_t pkt_offset)
+{
+       struct rte_flow_action_queue *queue;
+       struct fc_table_entry *entry;
+       uint64_t pkts_mask = RTE_LEN2MASK(nb_pkts, uint64_t);
+       uint32_t nb_tables = p->soft.fc.nb_tables;
+       uint32_t table_mask = p->soft.fc.table_mask;
+       uint64_t action_mask;
+       uint32_t *ptr, i, j;
+       int ret = -EINVAL;
+
+       for (i = 0; i < nb_tables; i++) {
+               if (table_mask & (1LU << i)) {
+                       struct fc_table *table = &p->soft.fc.tables[i];
+                       uint64_t lookup_hit_mask;
+
+                       ret = table->ops.f_lookup(table->h_table,
+                               pkts, pkts_mask, &lookup_hit_mask,
+                               (void **)p->soft.fc.entries);
+                       if (ret)
+                               return ret;
+
+                       if (lookup_hit_mask) {
+                               for (j = 0; j < nb_pkts; j++) {
+                                       uint64_t pkt_mask = 1LLU << j;
+
+                                       if ((lookup_hit_mask & pkt_mask) == 0)
+                                               continue;
+
+                                       /* Meta-data */
+                                       enum rte_flow_action_type act_type =
+                                               RTE_FLOW_ACTION_TYPE_QUEUE;
+                                       entry = p->soft.fc.entries[j];
+                                       action_mask = entry->action_mask;
+
+                                       if (action_mask & (1LLU << act_type)) {
+                                               queue = &entry->act.queue;
+                                               ptr = RTE_PKT_METADATA_PTR(
+                                                       pkts[j], pkt_offset);
+                                               *ptr = queue->index;
+                                       }
+                               }
+                       }
+               }
+       }
+
+       return 0;
+}
+
+static __rte_always_inline int
+run_fc(struct rte_eth_dev *dev)
+{
+       struct pmd_internals *p = dev->data->dev_private;
+
+       /* Persistent Context: Read Only (update not required) */
+       struct rte_eth_dev *hard_dev = DEV_HARD(p);
+       struct rte_mbuf **pkts = p->soft.fc.pkts;
+       uint16_t nb_rx_queues = hard_dev->data->nb_rx_queues;
+
+       /* Not part of the persistent context */
+       uint16_t nb_pkts, i;
+
+       /* Hard device RXQ read, Soft Device RXQ write */
+       for (i = 0; i < nb_rx_queues; i++) {
+               struct rte_ring *rxq = dev->data->rx_queues[i];
+
+               /* Hard device RXQ read */
+               nb_pkts = rte_eth_rx_burst(p->hard.port_id,
+                                        i, pkts, DEFAULT_BURST_SIZE);
+
+               /* Classify packets when complete burst is available */
+               if (nb_pkts) {
+                       fc_flow_classifier_run(p, pkts, nb_pkts,
+                               FC_ACTION_METADATA_OFFSET);
+
+                       /* Soft device RXQ write */
+                       rte_ring_enqueue_burst(rxq, (void **)pkts,
+                               nb_pkts, NULL);
+               }
+       }
+       return 0;
+}
+
+static int
+rte_pmd_softnic_rx_run(uint16_t port_id)
 {
        struct rte_eth_dev *dev = &rte_eth_devices[port_id];
+       struct pmd_internals *p = dev->data->dev_private;
+
+       uint32_t nb_rx_ops = p->params.rx_ops_data.nb_ops;
+       uint32_t i;
+
+#ifdef RTE_LIBRTE_ETHDEV_DEBUG
+       RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, 0);
+#endif
+
+       for (i = 0; i < nb_rx_ops; i++)
+               p->params.rx_ops_data.ops[i](dev);
+
+       return 0;
+}
+
+static int
+rte_pmd_softnic_tx_run(uint16_t port_id)
+{
+       struct rte_eth_dev *dev = &rte_eth_devices[port_id];
+       struct pmd_internals *p = dev->data->dev_private;
+
+       uint32_t nb_tx_ops = p->params.tx_ops_data.nb_ops;
+       uint32_t i;
 
 #ifdef RTE_LIBRTE_ETHDEV_DEBUG
        RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, 0);
 #endif
 
-       return (tm_used(dev)) ? run_tm(dev) : run_default(dev);
+       for (i = 0; i < nb_tx_ops; i++)
+               p->params.tx_ops_data.ops[i](dev);
+
+       return 0;
+}
+
+int
+rte_pmd_softnic_run(uint16_t port_id)
+{
+       rte_pmd_softnic_rx_run(port_id);
+       rte_pmd_softnic_tx_run(port_id);
+
+       return 0;
 }
 
 static struct ether_addr eth_addr = { .addr_bytes = {0} };
@@ -502,6 +712,20 @@ pmd_init(struct pmd_params *params, int numa_node)
                }
        }
 
+       /* Flow Classification */
+       if (params->soft.flags & PMD_FEATURE_FC) {
+               status = fc_init(p, params, numa_node);
+               if (status) {
+                       if (p->params.soft.flags & PMD_FEATURE_TM)
+                               tm_free(p);
+
+                       default_free(p);
+                       free(p->params.hard.name);
+                       rte_free(p);
+                       return NULL;
+               }
+       }
+
        return p;
 }
 
@@ -511,6 +735,9 @@ pmd_free(struct pmd_internals *p)
        if (p->params.soft.flags & PMD_FEATURE_TM)
                tm_free(p);
 
+       if (p->params.soft.flags & PMD_FEATURE_FC)
+               fc_free(p);
+
        default_free(p);
 
        free(p->params.hard.name);
@@ -539,9 +766,14 @@ pmd_ethdev_register(struct rte_vdev_device *vdev,
                return -ENOMEM;
 
        /* dev */
-       soft_dev->rx_pkt_burst = (params->soft.intrusive) ?
-               NULL : /* set up later */
-               pmd_rx_pkt_burst;
+       if (params->soft.intrusive == 0 &&
+               (params->soft.flags & PMD_FEATURE_FC))
+               soft_dev->rx_pkt_burst = pmd_rx_pkt_burst;
+       else if (params->soft.intrusive == 0)
+               soft_dev->rx_pkt_burst = pmd_rx_pkt_burst_default;
+       else
+               soft_dev->rx_pkt_burst = NULL; /* Set up later */
+
        soft_dev->tx_pkt_burst = pmd_tx_pkt_burst;
        soft_dev->tx_pkt_prepare = NULL;
        soft_dev->dev_ops = &pmd_ops;
@@ -586,10 +818,63 @@ get_uint32(const char *key __rte_unused, const char 
*value, void *extra_args)
        return 0;
 }
 
+static uint32_t
+get_hex_val(char c)
+{
+       switch (c) {
+       case '0': case '1': case '2': case '3': case '4': case '5':
+       case '6': case '7': case '8': case '9':
+               return c - '0';
+       case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
+               return c - 'A' + 10;
+       case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+               return c - 'a' + 10;
+       default:
+               return 0;
+       }
+}
+
+static int
+get_hex_string(char *src, uint8_t *dst, uint32_t *size)
+{
+       char *c;
+       uint32_t len, i;
+
+       /* Check input parameters */
+       if ((src == NULL) ||
+               (dst == NULL) ||
+               (size == NULL) ||
+               (*size == 0))
+               return -1;
+
+       len = strlen(src);
+       if (((len & 3) != 0) ||
+               (len > (*size) * 2))
+               return -1;
+       *size = len / 2;
+
+       for (c = src; *c != 0; c++) {
+               if ((((*c) >= '0') && ((*c) <= '9')) ||
+                       (((*c) >= 'A') && ((*c) <= 'F')) ||
+                       (((*c) >= 'a') && ((*c) <= 'f')))
+                       continue;
+
+               return -1;
+       }
+
+       /* Convert chars to bytes */
+       for (i = 0; i < *size; i++)
+               dst[i] = get_hex_val(src[2 * i]) * 16 +
+                       get_hex_val(src[2 * i + 1]);
+
+       return 0;
+}
+
 static int
 pmd_parse_args(struct pmd_params *p, const char *name, const char *params)
 {
        struct rte_kvargs *kvlist;
+       char key_mask_str[SOFTNIC_SOFT_FC_FLOW_RULE_KEY_MAX_SIZE * 2 + 1];
        int i, ret;
 
        kvlist = rte_kvargs_parse(params, pmd_valid_args);
@@ -606,6 +891,11 @@ pmd_parse_args(struct pmd_params *p, const char *name, 
const char *params)
                p->soft.tm.qsize[i] = SOFTNIC_SOFT_TM_QUEUE_SIZE;
        p->soft.tm.enq_bsz = SOFTNIC_SOFT_TM_ENQ_BSZ;
        p->soft.tm.deq_bsz = SOFTNIC_SOFT_TM_DEQ_BSZ;
+       p->soft.fc.nb_rules = SOFTNIC_SOFT_FC_NB_RULES;
+       p->soft.fc.key_size = SOFTNIC_SOFT_FC_FLOW_RULE_KEY_SIZE;
+       p->soft.fc.key_offset = SOFTNIC_SOFT_FC_FLOW_RULE_KEY_OFFSET;
+       snprintf(key_mask_str, sizeof(key_mask_str),
+               "%s", "00FF0000FFFFFFFFFFFFFFFFFFFFFFFF");
        p->hard.tx_queue_id = SOFTNIC_HARD_TX_QUEUE_ID;
 
        /* SOFT: TM (optional) */
@@ -718,6 +1008,75 @@ pmd_parse_args(struct pmd_params *p, const char *name, 
const char *params)
                p->soft.flags |= PMD_FEATURE_TM;
        }
 
+       /* SOFT: FC (optional) */
+       if (rte_kvargs_count(kvlist, PMD_PARAM_SOFT_FC) == 1) {
+               char *s;
+
+               ret = rte_kvargs_process(kvlist, PMD_PARAM_SOFT_FC,
+                       &get_string, &s);
+               if (ret < 0)
+                       goto out_free;
+
+               if (strcmp(s, "on") == 0)
+                       p->soft.flags |= PMD_FEATURE_FC;
+               else if (strcmp(s, "off") == 0)
+                       p->soft.flags &= ~PMD_FEATURE_FC;
+               else
+                       ret = -EINVAL;
+
+               free(s);
+               if (ret)
+                       goto out_free;
+       }
+
+       /* SOFT: FC number of flow rules (optional) */
+       if (rte_kvargs_count(kvlist, PMD_PARAM_SOFT_FC_NB_RULES) == 1) {
+               ret = rte_kvargs_process(kvlist, PMD_PARAM_SOFT_FC_NB_RULES,
+                       &get_uint32, &p->soft.fc.nb_rules);
+               if (ret < 0)
+                       goto out_free;
+
+               p->soft.flags |= PMD_FEATURE_FC;
+       }
+
+       /* SOFT: FC flow rule key size (optional) */
+       if (rte_kvargs_count(kvlist, PMD_PARAM_SOFT_FC_FLOW_KEY_SIZE) == 1) {
+               ret = rte_kvargs_process(kvlist,
+                               PMD_PARAM_SOFT_FC_FLOW_KEY_SIZE,
+                               &get_uint32, &p->soft.fc.key_size);
+               if (ret < 0)
+                       goto out_free;
+
+               p->soft.flags |= PMD_FEATURE_FC;
+       }
+
+       /* SOFT: FC flow rule key offset (optional) */
+       if (rte_kvargs_count(kvlist, PMD_PARAM_SOFT_FC_FLOW_KEY_OFFSET) == 1) {
+               ret = rte_kvargs_process(kvlist,
+                               PMD_PARAM_SOFT_FC_FLOW_KEY_OFFSET,
+                               &get_uint32, &p->soft.fc.key_offset);
+               if (ret < 0)
+                       goto out_free;
+
+               p->soft.flags |= PMD_FEATURE_FC;
+       }
+
+       /* SOFT: FC flow rule key mask (optional) */
+       if (rte_kvargs_count(kvlist, PMD_PARAM_SOFT_FC_FLOW_KEY_MASK) == 1) {
+               ret = rte_kvargs_process(kvlist,
+                               PMD_PARAM_SOFT_FC_FLOW_KEY_MASK,
+                               &get_string, key_mask_str);
+               if (ret < 0)
+                       goto out_free;
+
+               ret = get_hex_string(key_mask_str, p->soft.fc.key_mask,
+                       &p->soft.fc.key_size);
+               if (ret < 0)
+                       goto out_free;
+
+               p->soft.flags |= PMD_FEATURE_FC;
+       }
+
        /* HARD: name (mandatory) */
        if (rte_kvargs_count(kvlist, PMD_PARAM_HARD_NAME) == 1) {
                ret = rte_kvargs_process(kvlist, PMD_PARAM_HARD_NAME,
@@ -786,6 +1145,13 @@ pmd_probe(struct rte_vdev_device *vdev)
                        return status;
        }
 
+       if (p.soft.flags & PMD_FEATURE_FC) {
+               status = fc_params_check(&p);
+
+               if (status)
+                       return status;
+       }
+
        /* Allocate and initialize soft ethdev private data */
        dev_private = pmd_init(&p, numa_node);
        if (dev_private == NULL)
@@ -847,5 +1213,10 @@ RTE_PMD_REGISTER_PARAM_STRING(net_softnic,
        PMD_PARAM_SOFT_TM_QSIZE3 "=<int> "
        PMD_PARAM_SOFT_TM_ENQ_BSZ "=<int> "
        PMD_PARAM_SOFT_TM_DEQ_BSZ "=<int> "
+       PMD_PARAM_SOFT_FC        "=on|off "
+       PMD_PARAM_SOFT_FC_NB_RULES "=<int> "
+       PMD_PARAM_SOFT_FC_FLOW_KEY_SIZE "=<int> "
+       PMD_PARAM_SOFT_FC_FLOW_KEY_OFFSET "=<int> "
+       PMD_PARAM_SOFT_FC_FLOW_KEY_MASK "=<string> "
        PMD_PARAM_HARD_NAME "=<string> "
        PMD_PARAM_HARD_TX_QUEUE_ID "=<int>");
diff --git a/drivers/net/softnic/rte_eth_softnic.h 
b/drivers/net/softnic/rte_eth_softnic.h
index b49e582..1dd17b1 100644
--- a/drivers/net/softnic/rte_eth_softnic.h
+++ b/drivers/net/softnic/rte_eth_softnic.h
@@ -56,6 +56,22 @@ extern "C" {
 #define SOFTNIC_SOFT_TM_DEQ_BSZ                                24
 #endif
 
+#ifndef SOFTNIC_SOFT_FC_NB_RULES
+#define SOFTNIC_SOFT_FC_NB_RULES                       4096
+#endif
+
+#ifndef SOFTNIC_SOFT_FC_FLOW_RULE_KEY_SIZE
+#define SOFTNIC_SOFT_FC_FLOW_RULE_KEY_SIZE             16
+#endif
+
+#ifndef SOFTNIC_SOFT_FC_FLOW_RULE_KEY_OFFSET
+#define SOFTNIC_SOFT_FC_FLOW_RULE_KEY_OFFSET           278
+#endif
+
+#ifndef SOFTNIC_SOFT_FC_FLOW_RULE_KEY_MAX_SIZE
+#define SOFTNIC_SOFT_FC_FLOW_RULE_KEY_MAX_SIZE         64
+#endif
+
 #ifndef SOFTNIC_HARD_TX_QUEUE_ID
 #define SOFTNIC_HARD_TX_QUEUE_ID                       0
 #endif
diff --git a/drivers/net/softnic/rte_eth_softnic_fc.c 
b/drivers/net/softnic/rte_eth_softnic_fc.c
new file mode 100644
index 0000000..83f7fd3
--- /dev/null
+++ b/drivers/net/softnic/rte_eth_softnic_fc.c
@@ -0,0 +1,377 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2017 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <rte_malloc.h>
+
+#include "rte_eth_softnic_internals.h"
+
+static inline uint64_t
+hash_xor_key8(void *key, void *mask, __rte_unused uint32_t key_size,
+       uint64_t seed)
+{
+       uint64_t *k = key;
+       uint64_t *m = mask;
+       uint64_t xor0;
+
+       xor0 = seed ^ (k[0] & m[0]);
+
+       return (xor0 >> 32) ^ xor0;
+}
+
+static inline uint64_t
+hash_xor_key16(void *key, void *mask, __rte_unused uint32_t key_size,
+       uint64_t seed)
+{
+       uint64_t *k = key;
+       uint64_t *m = mask;
+       uint64_t xor0;
+
+       xor0 = ((k[0] & m[0]) ^ seed) ^ (k[1] & m[1]);
+
+       return (xor0 >> 32) ^ xor0;
+}
+
+static inline uint64_t
+hash_xor_key24(void *key, void *mask, __rte_unused uint32_t key_size,
+       uint64_t seed)
+{
+       uint64_t *k = key;
+       uint64_t *m = mask;
+       uint64_t xor0;
+
+       xor0 = ((k[0] & m[0]) ^ seed) ^ (k[1] & m[1]);
+
+       xor0 ^= k[2] & m[2];
+
+       return (xor0 >> 32) ^ xor0;
+}
+
+static inline uint64_t
+hash_xor_key32(void *key, void *mask, __rte_unused uint32_t key_size,
+       uint64_t seed)
+{
+       uint64_t *k = key;
+       uint64_t *m = mask;
+       uint64_t xor0, xor1;
+
+       xor0 = ((k[0] & m[0]) ^ seed) ^ (k[1] & m[1]);
+       xor1 = (k[2] & m[2]) ^ (k[3] & m[3]);
+
+       xor0 ^= xor1;
+
+       return (xor0 >> 32) ^ xor0;
+}
+
+static inline uint64_t
+hash_xor_key40(void *key, void *mask, __rte_unused uint32_t key_size,
+       uint64_t seed)
+{
+       uint64_t *k = key;
+       uint64_t *m = mask;
+       uint64_t xor0, xor1;
+
+       xor0 = ((k[0] & m[0]) ^ seed) ^ (k[1] & m[1]);
+       xor1 = (k[2] & m[2]) ^ (k[3] & m[3]);
+
+       xor0 ^= xor1;
+
+       xor0 ^= k[4] & m[4];
+
+       return (xor0 >> 32) ^ xor0;
+}
+
+static inline uint64_t
+hash_xor_key48(void *key, void *mask, __rte_unused uint32_t key_size,
+       uint64_t seed)
+{
+       uint64_t *k = key;
+       uint64_t *m = mask;
+       uint64_t xor0, xor1, xor2;
+
+       xor0 = ((k[0] & m[0]) ^ seed) ^ (k[1] & m[1]);
+       xor1 = (k[2] & m[2]) ^ (k[3] & m[3]);
+       xor2 = (k[4] & m[4]) ^ (k[5] & m[5]);
+
+       xor0 ^= xor1;
+
+       xor0 ^= xor2;
+
+       return (xor0 >> 32) ^ xor0;
+}
+
+static inline uint64_t
+hash_xor_key56(void *key, void *mask, __rte_unused uint32_t key_size,
+       uint64_t seed)
+{
+       uint64_t *k = key;
+       uint64_t *m = mask;
+       uint64_t xor0, xor1, xor2;
+
+       xor0 = ((k[0] & m[0]) ^ seed) ^ (k[1] & m[1]);
+       xor1 = (k[2] & m[2]) ^ (k[3] & m[3]);
+       xor2 = (k[4] & m[4]) ^ (k[5] & m[5]);
+
+       xor0 ^= xor1;
+       xor2 ^= k[6] & m[6];
+
+       xor0 ^= xor2;
+
+       return (xor0 >> 32) ^ xor0;
+}
+
+static inline uint64_t
+hash_xor_key64(void *key, void *mask, __rte_unused uint32_t key_size,
+       uint64_t seed)
+{
+       uint64_t *k = key;
+       uint64_t *m = mask;
+       uint64_t xor0, xor1, xor2, xor3;
+
+       xor0 = ((k[0] & m[0]) ^ seed) ^ (k[1] & m[1]);
+       xor1 = (k[2] & m[2]) ^ (k[3] & m[3]);
+       xor2 = (k[4] & m[4]) ^ (k[5] & m[5]);
+       xor3 = (k[6] & m[6]) ^ (k[7] & m[7]);
+
+       xor0 ^= xor1;
+       xor2 ^= xor3;
+
+       xor0 ^= xor2;
+
+       return (xor0 >> 32) ^ xor0;
+}
+
+rte_table_hash_op_hash hash_func[] = {
+       hash_xor_key8,
+       hash_xor_key16,
+       hash_xor_key24,
+       hash_xor_key32,
+       hash_xor_key40,
+       hash_xor_key48,
+       hash_xor_key56,
+       hash_xor_key64
+};
+
+int
+fc_params_check(struct pmd_params *params)
+{
+       /* nb_max_rules */
+       if (params->soft.fc.nb_rules == 0)
+               return -EINVAL;
+
+       return 0;
+}
+
+static int
+fc_table_check_params(struct pmd_internals *p,
+       struct fc_table_params *params)
+{
+       /* Number of tables */
+       if (p->soft.fc.nb_tables == FC_TABLE_HASH_KEY_MAX)
+               return -EINVAL;
+
+       /* Parameter */
+       if (params == NULL)
+               return -EINVAL;
+
+       /* Ops */
+       if (params->ops == NULL)
+               return -EINVAL;
+
+       if (params->ops->f_create == NULL)
+               return -EINVAL;
+
+       if (params->ops->f_lookup == NULL)
+               return -EINVAL;
+
+       return 0;
+}
+
+static int
+rte_flow_classify_table_create(struct pmd_internals *p,
+       struct fc_table *table,
+       struct fc_table_params *params,
+       int numa_node)
+{
+       void *h_table;
+       uint32_t entry_size;
+       int ret;
+
+       /* Check input arguments */
+       ret = fc_table_check_params(p, params);
+       if (ret != 0)
+               return ret;
+
+       /* calculate table entry size */
+       entry_size = sizeof(struct fc_table_entry);
+
+       /* Create the table */
+       h_table = params->ops->f_create(params->arg_create, numa_node,
+               entry_size);
+       if (h_table == NULL)
+               return -EINVAL;
+
+       table->type = params->type;
+       p->soft.fc.table_mask |= table->type;
+       p->soft.fc.nb_tables++;
+
+       /* Save input parameters */
+       memcpy(&table->ops, params->ops, sizeof(struct rte_table_ops));
+
+       /* Initialize table internal data structure */
+       table->entry_size = entry_size;
+       table->h_table = h_table;
+
+       return 0;
+}
+
+static int
+fc_tables_create(struct pmd_internals *p,
+       struct pmd_params *params, int numa_node)
+{
+       int ret;
+       struct fc_table *table;
+
+       /** Flow Classification: Hash table (Key size = 8 bytes) */
+       struct rte_table_hash_params table_hash_params = {
+               .name = params->soft.name,
+               .key_size = params->soft.fc.key_size,
+               .key_offset = params->soft.fc.key_offset,
+               .key_mask = params->soft.fc.key_mask,
+               .n_keys = params->soft.fc.nb_rules,
+               .n_buckets = rte_align32pow2(params->soft.fc.nb_rules / 4),
+               .f_hash = hash_func[(params->soft.fc.key_size / 8) - 1],
+               .seed = 0,
+       };
+
+       struct fc_table_params table_params = {
+               .ops = NULL,
+               .arg_create = &table_hash_params,
+               .type = FC_TABLE_HASH_KEY_MAX,
+       };
+
+       switch (params->soft.fc.key_size) {
+       case 8:
+               table_params.ops = &rte_table_hash_key8_ext_ops;
+               table_params.type = FC_TABLE_HASH_KEY8;
+               break;
+
+       case 16:
+               table_params.ops = &rte_table_hash_key16_ext_ops;
+               table_params.type = FC_TABLE_HASH_KEY16;
+               break;
+
+       default:
+               table_params.ops = &rte_table_hash_ext_ops;
+               table_params.type = FC_TABLE_HASH_KEY_EXT;
+       }
+
+       table = &p->soft.fc.tables[table_params.type];
+
+       /** Create hash table */
+       ret = rte_flow_classify_table_create(p, table,
+                       &table_params,
+                       numa_node);
+       if (ret)
+               return -EINVAL;
+
+       return 0;
+}
+
+static void
+fc_flow_init(struct pmd_internals *p)
+{
+       /* Initialize classifier flow list */
+       TAILQ_INIT(&p->soft.fc.flow_list);
+}
+
+static void
+fc_flow_uninit(struct pmd_internals *p)
+{
+       /** Remove all rules from the list */
+       for ( ; ; ) {
+               struct rte_flow *flow;
+
+               flow = TAILQ_FIRST(&p->soft.fc.flow_list);
+               if (flow == NULL)
+                       break;
+
+               TAILQ_REMOVE(&p->soft.fc.flow_list, flow, node);
+               free(flow);
+       }
+}
+
+int
+fc_init(struct pmd_internals *p,
+       struct pmd_params *params,
+       int numa_node)
+{
+       int ret;
+
+       memset(&p->soft.fc, 0, sizeof(p->soft.fc));
+
+       p->soft.fc.pkts = rte_zmalloc_socket(params->soft.name,
+               2 * DEFAULT_BURST_SIZE * sizeof(struct rte_mbuf *),
+               0,
+               numa_node);
+       if (p->soft.fc.pkts == NULL)
+               return -ENOMEM;
+
+       /** Create all tables/filters for classifiers */
+       ret = fc_tables_create(p, params, numa_node);
+       if (ret) {
+               rte_free(p->soft.fc.pkts);
+               return ret;
+       }
+
+       fc_flow_init(p);
+
+       return 0;
+}
+
+void
+fc_free(struct pmd_internals *p)
+{
+       fc_flow_uninit(p);
+       rte_free(p->soft.fc.pkts);
+}
+
+const struct rte_flow_ops pmd_flow_ops = {
+       .validate = NULL,
+       .create = NULL,
+       .destroy = NULL,
+       .flush = NULL,
+};
diff --git a/drivers/net/softnic/rte_eth_softnic_internals.h 
b/drivers/net/softnic/rte_eth_softnic_internals.h
index 1f75806..9e59d62 100644
--- a/drivers/net/softnic/rte_eth_softnic_internals.h
+++ b/drivers/net/softnic/rte_eth_softnic_internals.h
@@ -39,7 +39,10 @@
 #include <rte_mbuf.h>
 #include <rte_sched.h>
 #include <rte_ethdev.h>
+#include <rte_table.h>
+#include <rte_table_hash.h>
 #include <rte_tm_driver.h>
+#include <rte_flow_driver.h>
 
 #include "rte_eth_softnic.h"
 
@@ -48,13 +51,30 @@
  */
 
 enum pmd_feature {
-       PMD_FEATURE_TM = 1, /**< Traffic Management (TM) */
+       /** Traffic Management (TM) */
+       PMD_FEATURE_TM = 1 << 0,
+
+       /** Flow Clasification (FC) */
+       PMD_FEATURE_FC = 1 << 1,
 };
 
 #ifndef INTRUSIVE
 #define INTRUSIVE                                      0
 #endif
 
+#ifndef MAX_NB_OPS
+#define MAX_NB_OPS                                     32
+#endif
+
+#ifndef FC_FLOW_KEY_MAX_SIZE
+#define FC_FLOW_KEY_MAX_SIZE           64
+#endif
+
+/**
+ * Softnic operations
+ */
+typedef int (*soft_ops)(struct rte_eth_dev *dev);
+
 struct pmd_params {
        /** Parameters for the soft device (to be created) */
        struct {
@@ -77,6 +97,14 @@ struct pmd_params {
                        uint32_t enq_bsz; /**< Enqueue burst size */
                        uint32_t deq_bsz; /**< Dequeue burst size */
                } tm;
+
+               /** Flow Classification (FC) */
+               struct {
+                       uint32_t nb_rules; /**< Max number of flow rules */
+                       uint32_t key_size; /**< Flow rule key */
+                       uint8_t key_mask[FC_FLOW_KEY_MAX_SIZE]; /**< Key mask */
+                       uint32_t key_offset; /**< Key offset */
+               } fc;
        } soft;
 
        /** Parameters for the hard device (existing) */
@@ -84,6 +112,19 @@ struct pmd_params {
                char *name; /**< Name */
                uint16_t tx_queue_id; /**< TX queue ID */
        } hard;
+
+       /** Parameters for the softnic operations (rx & tx) */
+       struct {
+               uint32_t nb_ops;
+               uint32_t ops_flag;
+               soft_ops ops[MAX_NB_OPS];
+       } rx_ops_data;
+
+       struct {
+               uint32_t nb_ops;
+               uint32_t ops_flag;
+               soft_ops ops[MAX_NB_OPS];
+       } tx_ops_data;
 };
 
 /**
@@ -225,6 +266,102 @@ struct tm_internals {
 };
 
 /**
+ * Flow Classification (FC) Internals
+ */
+#ifndef FC_ACTION_METADATA_OFFSET
+#define FC_ACTION_METADATA_OFFSET                      128
+#endif
+
+#ifndef FC_FLOW_PACKET_BURST_SIZE_MAX
+#define FC_FLOW_PACKET_BURST_SIZE_MAX          64
+#endif
+
+#define RTE_PKT_METADATA_PTR(pkt, offset)         \
+               (&((uint32_t *)(pkt))[offset])
+
+/**
+ * FC flow (PMD-specific definition of struct rte_flow)
+ */
+struct rte_flow {
+       TAILQ_ENTRY(rte_flow) node;
+       struct rte_flow_attr attr; /**< Attributes. */
+       struct rte_flow_item *pattern; /**< Items. */
+       struct rte_flow_action *actions; /**< Actions. */
+};
+
+TAILQ_HEAD(fc_flow_list, rte_flow);
+
+/* FC tables type */
+enum fc_table_hash_type {
+       FC_TABLE_HASH_KEY8 = 0,
+       FC_TABLE_HASH_KEY16,
+       FC_TABLE_HASH_KEY_EXT,
+       FC_TABLE_HASH_KEY_MAX,
+};
+
+/**
+ * FC Flow classification table parameters
+ */
+struct fc_table_params {
+       /** Table operations (specific to each table type) */
+       struct rte_table_ops *ops;
+       /** Opaque param to be passed to the table create operation when
+        * invoked
+        */
+       void *arg_create;
+
+       /** Classifier table type */
+       enum fc_table_hash_type type;
+};
+
+/**
+ * Head format for the table entry of table. For any given
+ * FC table, all table entries should have the same size and format. For
+ * any given table, the table entry has to start with a head of this
+ * structure, which contains actions mask and their associated
+ * meta-data.
+ */
+struct fc_table_entry {
+       /** Flow action mask */
+       uint64_t action_mask;
+
+       struct action {
+               /** Assign packets to a given queue index */
+               struct rte_flow_action_queue queue;
+
+               /** Traffic metering and policing (MTR) */
+               struct rte_flow_action_meter meter;
+
+       } act;
+};
+
+/** FC Flow table */
+struct fc_table {
+       /* Input parameters */
+       struct rte_table_ops ops;
+       uint32_t entry_size;
+       enum fc_table_hash_type type;
+
+       /* Handle to the low-level table object */
+       void *h_table;
+};
+
+struct fc_internals {
+       /** Flow rule list */
+       struct fc_flow_list flow_list;
+       uint32_t n_flow_rules;
+
+       /** Flow classification table */
+       struct fc_table tables[FC_TABLE_HASH_KEY_MAX];
+       uint32_t nb_tables;
+       uint32_t table_mask;
+
+       /** Run-time */
+       struct rte_mbuf **pkts;
+       struct fc_table_entry *entries[FC_FLOW_PACKET_BURST_SIZE_MAX];
+};
+
+/**
  * PMD Internals
  */
 struct pmd_internals {
@@ -235,6 +372,7 @@ struct pmd_internals {
        struct {
                struct default_internals def; /**< Default */
                struct tm_internals tm; /**< Traffic Management */
+               struct fc_internals fc; /**< flow classification */
        } soft;
 
        /** Hard device */
@@ -256,6 +394,11 @@ struct pmd_rx_queue {
  */
 extern const struct rte_tm_ops pmd_tm_ops;
 
+/**
+ * Flow API operation
+ */
+extern const struct rte_flow_ops pmd_flow_ops;
+
 int
 tm_params_check(struct pmd_params *params, uint32_t hard_rate);
 
@@ -288,4 +431,21 @@ tm_used(struct rte_eth_dev *dev)
                p->soft.tm.h.n_tm_nodes[TM_NODE_LEVEL_PORT];
 }
 
+int
+fc_params_check(struct pmd_params *params);
+
+int
+fc_init(struct pmd_internals *p, struct pmd_params *params, int numa_node);
+
+void
+fc_free(struct pmd_internals *p);
+
+static inline int
+fc_enabled(struct rte_eth_dev *dev)
+{
+       struct pmd_internals *p = dev->data->dev_private;
+
+       return (p->params.soft.flags & PMD_FEATURE_FC);
+}
+
 #endif /* __INCLUDE_RTE_ETH_SOFTNIC_INTERNALS_H__ */
diff --git a/mk/rte.app.mk b/mk/rte.app.mk
index 6a6a745..72d684c 100644
--- a/mk/rte.app.mk
+++ b/mk/rte.app.mk
@@ -60,7 +60,9 @@ _LDLIBS-y += -L$(RTE_SDK_BIN)/lib
 #
 _LDLIBS-$(CONFIG_RTE_LIBRTE_FLOW_CLASSIFY)  += -lrte_flow_classify
 _LDLIBS-$(CONFIG_RTE_LIBRTE_PIPELINE)       += -lrte_pipeline
+_LDLIBS-$(CONFIG_RTE_LIBRTE_TABLE)            += --whole-archive
 _LDLIBS-$(CONFIG_RTE_LIBRTE_TABLE)          += -lrte_table
+_LDLIBS-$(CONFIG_RTE_LIBRTE_TABLE)            += --no-whole-archive
 _LDLIBS-$(CONFIG_RTE_LIBRTE_PORT)           += -lrte_port
 
 _LDLIBS-$(CONFIG_RTE_LIBRTE_PDUMP)          += -lrte_pdump
-- 
2.9.3

Reply via email to