net/ixgbe: store L2 tunnel filter
[dpdk.git] / drivers / net / ixgbe / ixgbe_ethdev.c
index a24561b..f3dc3ed 100644 (file)
@@ -60,6 +60,7 @@
 #include <rte_malloc.h>
 #include <rte_random.h>
 #include <rte_dev.h>
+#include <rte_hash_crc.h>
 
 #include "ixgbe_logs.h"
 #include "base/ixgbe_api.h"
@@ -165,6 +166,10 @@ enum ixgbevf_xcast_modes {
 
 static int eth_ixgbe_dev_init(struct rte_eth_dev *eth_dev);
 static int eth_ixgbe_dev_uninit(struct rte_eth_dev *eth_dev);
+static int ixgbe_fdir_filter_init(struct rte_eth_dev *eth_dev);
+static int ixgbe_fdir_filter_uninit(struct rte_eth_dev *eth_dev);
+static int ixgbe_l2_tn_filter_init(struct rte_eth_dev *eth_dev);
+static int ixgbe_l2_tn_filter_uninit(struct rte_eth_dev *eth_dev);
 static int  ixgbe_dev_configure(struct rte_eth_dev *dev);
 static int  ixgbe_dev_start(struct rte_eth_dev *dev);
 static void ixgbe_dev_stop(struct rte_eth_dev *dev);
@@ -1323,11 +1328,18 @@ eth_ixgbe_dev_init(struct rte_eth_dev *eth_dev)
        /* enable support intr */
        ixgbe_enable_intr(eth_dev);
 
+       /* initialize filter info */
+       memset(filter_info, 0,
+              sizeof(struct ixgbe_filter_info));
+
        /* initialize 5tuple filter list */
        TAILQ_INIT(&filter_info->fivetuple_list);
-       memset(filter_info->fivetuple_mask, 0,
-              sizeof(uint32_t) * IXGBE_5TUPLE_ARRAY_SIZE);
 
+       /* initialize flow director filter list & hash */
+       ixgbe_fdir_filter_init(eth_dev);
+
+       /* initialize l2 tunnel filter list & hash */
+       ixgbe_l2_tn_filter_init(eth_dev);
        return 0;
 }
 
@@ -1369,9 +1381,125 @@ eth_ixgbe_dev_uninit(struct rte_eth_dev *eth_dev)
        rte_free(eth_dev->data->hash_mac_addrs);
        eth_dev->data->hash_mac_addrs = NULL;
 
+       /* remove all the fdir filters & hash */
+       ixgbe_fdir_filter_uninit(eth_dev);
+
+       /* remove all the L2 tunnel filters & hash */
+       ixgbe_l2_tn_filter_uninit(eth_dev);
+
+       return 0;
+}
+
+static int ixgbe_fdir_filter_uninit(struct rte_eth_dev *eth_dev)
+{
+       struct ixgbe_hw_fdir_info *fdir_info =
+               IXGBE_DEV_PRIVATE_TO_FDIR_INFO(eth_dev->data->dev_private);
+       struct ixgbe_fdir_filter *fdir_filter;
+
+               if (fdir_info->hash_map)
+               rte_free(fdir_info->hash_map);
+       if (fdir_info->hash_handle)
+               rte_hash_free(fdir_info->hash_handle);
+
+       while ((fdir_filter = TAILQ_FIRST(&fdir_info->fdir_list))) {
+               TAILQ_REMOVE(&fdir_info->fdir_list,
+                            fdir_filter,
+                            entries);
+               rte_free(fdir_filter);
+       }
+
+       return 0;
+}
+
+static int ixgbe_l2_tn_filter_uninit(struct rte_eth_dev *eth_dev)
+{
+       struct ixgbe_l2_tn_info *l2_tn_info =
+               IXGBE_DEV_PRIVATE_TO_L2_TN_INFO(eth_dev->data->dev_private);
+       struct ixgbe_l2_tn_filter *l2_tn_filter;
+
+       if (l2_tn_info->hash_map)
+               rte_free(l2_tn_info->hash_map);
+       if (l2_tn_info->hash_handle)
+               rte_hash_free(l2_tn_info->hash_handle);
+
+       while ((l2_tn_filter = TAILQ_FIRST(&l2_tn_info->l2_tn_list))) {
+               TAILQ_REMOVE(&l2_tn_info->l2_tn_list,
+                            l2_tn_filter,
+                            entries);
+               rte_free(l2_tn_filter);
+       }
+
+       return 0;
+}
+
+static int ixgbe_fdir_filter_init(struct rte_eth_dev *eth_dev)
+{
+       struct ixgbe_hw_fdir_info *fdir_info =
+               IXGBE_DEV_PRIVATE_TO_FDIR_INFO(eth_dev->data->dev_private);
+       char fdir_hash_name[RTE_HASH_NAMESIZE];
+       struct rte_hash_parameters fdir_hash_params = {
+               .name = fdir_hash_name,
+               .entries = IXGBE_MAX_FDIR_FILTER_NUM,
+               .key_len = sizeof(union ixgbe_atr_input),
+               .hash_func = rte_hash_crc,
+               .hash_func_init_val = 0,
+               .socket_id = rte_socket_id(),
+       };
+
+       TAILQ_INIT(&fdir_info->fdir_list);
+       snprintf(fdir_hash_name, RTE_HASH_NAMESIZE,
+                "fdir_%s", eth_dev->data->name);
+       fdir_info->hash_handle = rte_hash_create(&fdir_hash_params);
+       if (!fdir_info->hash_handle) {
+               PMD_INIT_LOG(ERR, "Failed to create fdir hash table!");
+               return -EINVAL;
+       }
+       fdir_info->hash_map = rte_zmalloc("ixgbe",
+                                         sizeof(struct ixgbe_fdir_filter *) *
+                                         IXGBE_MAX_FDIR_FILTER_NUM,
+                                         0);
+       if (!fdir_info->hash_map) {
+               PMD_INIT_LOG(ERR,
+                            "Failed to allocate memory for fdir hash map!");
+               return -ENOMEM;
+       }
        return 0;
 }
 
+static int ixgbe_l2_tn_filter_init(struct rte_eth_dev *eth_dev)
+{
+       struct ixgbe_l2_tn_info *l2_tn_info =
+               IXGBE_DEV_PRIVATE_TO_L2_TN_INFO(eth_dev->data->dev_private);
+       char l2_tn_hash_name[RTE_HASH_NAMESIZE];
+       struct rte_hash_parameters l2_tn_hash_params = {
+               .name = l2_tn_hash_name,
+               .entries = IXGBE_MAX_L2_TN_FILTER_NUM,
+               .key_len = sizeof(struct ixgbe_l2_tn_key),
+               .hash_func = rte_hash_crc,
+               .hash_func_init_val = 0,
+               .socket_id = rte_socket_id(),
+       };
+
+       TAILQ_INIT(&l2_tn_info->l2_tn_list);
+       snprintf(l2_tn_hash_name, RTE_HASH_NAMESIZE,
+                "l2_tn_%s", eth_dev->data->name);
+       l2_tn_info->hash_handle = rte_hash_create(&l2_tn_hash_params);
+       if (!l2_tn_info->hash_handle) {
+               PMD_INIT_LOG(ERR, "Failed to create L2 TN hash table!");
+               return -EINVAL;
+       }
+       l2_tn_info->hash_map = rte_zmalloc("ixgbe",
+                                  sizeof(struct ixgbe_l2_tn_filter *) *
+                                  IXGBE_MAX_L2_TN_FILTER_NUM,
+                                  0);
+       if (!l2_tn_info->hash_map) {
+               PMD_INIT_LOG(ERR,
+                       "Failed to allocate memory for L2 TN hash map!");
+               return -ENOMEM;
+       }
+
+       return 0;
+}
 /*
  * Negotiate mailbox API version with the PF.
  * After reset API version is always set to the basic one (ixgbe_mbox_api_10).
@@ -5817,15 +5945,18 @@ ixgbe_syn_filter_set(struct rte_eth_dev *dev,
                        bool add)
 {
        struct ixgbe_hw *hw = IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+       struct ixgbe_filter_info *filter_info =
+               IXGBE_DEV_PRIVATE_TO_FILTER_INFO(dev->data->dev_private);
+       uint32_t syn_info;
        uint32_t synqf;
 
        if (filter->queue >= IXGBE_MAX_RX_QUEUE_NUM)
                return -EINVAL;
 
-       synqf = IXGBE_READ_REG(hw, IXGBE_SYNQF);
+       syn_info = filter_info->syn_info;
 
        if (add) {
-               if (synqf & IXGBE_SYN_FILTER_ENABLE)
+               if (syn_info & IXGBE_SYN_FILTER_ENABLE)
                        return -EINVAL;
                synqf = (uint32_t)(((filter->queue << IXGBE_SYN_FILTER_QUEUE_SHIFT) &
                        IXGBE_SYN_FILTER_QUEUE) | IXGBE_SYN_FILTER_ENABLE);
@@ -5835,10 +5966,13 @@ ixgbe_syn_filter_set(struct rte_eth_dev *dev,
                else
                        synqf &= ~IXGBE_SYN_FILTER_SYNQFP;
        } else {
-               if (!(synqf & IXGBE_SYN_FILTER_ENABLE))
+               synqf = IXGBE_READ_REG(hw, IXGBE_SYNQF);
+               if (!(syn_info & IXGBE_SYN_FILTER_ENABLE))
                        return -ENOENT;
                synqf &= ~(IXGBE_SYN_FILTER_QUEUE | IXGBE_SYN_FILTER_ENABLE);
        }
+
+       filter_info->syn_info = synqf;
        IXGBE_WRITE_REG(hw, IXGBE_SYNQF, synqf);
        IXGBE_WRITE_FLUSH(hw);
        return 0;
@@ -7352,12 +7486,104 @@ ixgbe_e_tag_filter_add(struct rte_eth_dev *dev,
        return -EINVAL;
 }
 
+static inline struct ixgbe_l2_tn_filter *
+ixgbe_l2_tn_filter_lookup(struct ixgbe_l2_tn_info *l2_tn_info,
+                         struct ixgbe_l2_tn_key *key)
+{
+       int ret;
+
+       ret = rte_hash_lookup(l2_tn_info->hash_handle, (const void *)key);
+       if (ret < 0)
+               return NULL;
+
+       return l2_tn_info->hash_map[ret];
+}
+
+static inline int
+ixgbe_insert_l2_tn_filter(struct ixgbe_l2_tn_info *l2_tn_info,
+                         struct ixgbe_l2_tn_filter *l2_tn_filter)
+{
+       int ret;
+
+       ret = rte_hash_add_key(l2_tn_info->hash_handle,
+                              &l2_tn_filter->key);
+
+       if (ret < 0) {
+               PMD_DRV_LOG(ERR,
+                           "Failed to insert L2 tunnel filter"
+                           " to hash table %d!",
+                           ret);
+               return ret;
+       }
+
+       l2_tn_info->hash_map[ret] = l2_tn_filter;
+
+       TAILQ_INSERT_TAIL(&l2_tn_info->l2_tn_list, l2_tn_filter, entries);
+
+       return 0;
+}
+
+static inline int
+ixgbe_remove_l2_tn_filter(struct ixgbe_l2_tn_info *l2_tn_info,
+                         struct ixgbe_l2_tn_key *key)
+{
+       int ret;
+       struct ixgbe_l2_tn_filter *l2_tn_filter;
+
+       ret = rte_hash_del_key(l2_tn_info->hash_handle, key);
+
+       if (ret < 0) {
+               PMD_DRV_LOG(ERR,
+                           "No such L2 tunnel filter to delete %d!",
+                           ret);
+               return ret;
+       }
+
+       l2_tn_filter = l2_tn_info->hash_map[ret];
+       l2_tn_info->hash_map[ret] = NULL;
+
+       TAILQ_REMOVE(&l2_tn_info->l2_tn_list, l2_tn_filter, entries);
+       rte_free(l2_tn_filter);
+
+       return 0;
+}
+
 /* Add l2 tunnel filter */
 static int
 ixgbe_dev_l2_tunnel_filter_add(struct rte_eth_dev *dev,
                               struct rte_eth_l2_tunnel_conf *l2_tunnel)
 {
-       int ret = 0;
+       int ret;
+       struct ixgbe_l2_tn_info *l2_tn_info =
+               IXGBE_DEV_PRIVATE_TO_L2_TN_INFO(dev->data->dev_private);
+       struct ixgbe_l2_tn_key key;
+       struct ixgbe_l2_tn_filter *node;
+
+       key.l2_tn_type = l2_tunnel->l2_tunnel_type;
+       key.tn_id = l2_tunnel->tunnel_id;
+
+       node = ixgbe_l2_tn_filter_lookup(l2_tn_info, &key);
+
+       if (node) {
+               PMD_DRV_LOG(ERR, "The L2 tunnel filter already exists!");
+               return -EINVAL;
+       }
+
+       node = rte_zmalloc("ixgbe_l2_tn",
+                          sizeof(struct ixgbe_l2_tn_filter),
+                          0);
+       if (!node)
+               return -ENOMEM;
+
+       (void)rte_memcpy(&node->key,
+                        &key,
+                        sizeof(struct ixgbe_l2_tn_key));
+       node->pool = l2_tunnel->pool;
+       ret = ixgbe_insert_l2_tn_filter(l2_tn_info, node);
+       if (ret < 0) {
+               rte_free(node);
+               return ret;
+       }
 
        switch (l2_tunnel->l2_tunnel_type) {
        case RTE_L2_TUNNEL_TYPE_E_TAG:
@@ -7369,6 +7595,9 @@ ixgbe_dev_l2_tunnel_filter_add(struct rte_eth_dev *dev,
                break;
        }
 
+       if (ret < 0)
+               (void)ixgbe_remove_l2_tn_filter(l2_tn_info, &key);
+
        return ret;
 }
 
@@ -7377,7 +7606,16 @@ static int
 ixgbe_dev_l2_tunnel_filter_del(struct rte_eth_dev *dev,
                               struct rte_eth_l2_tunnel_conf *l2_tunnel)
 {
-       int ret = 0;
+       int ret;
+       struct ixgbe_l2_tn_info *l2_tn_info =
+               IXGBE_DEV_PRIVATE_TO_L2_TN_INFO(dev->data->dev_private);
+       struct ixgbe_l2_tn_key key;
+
+       key.l2_tn_type = l2_tunnel->l2_tunnel_type;
+       key.tn_id = l2_tunnel->tunnel_id;
+       ret = ixgbe_remove_l2_tn_filter(l2_tn_info, &key);
+       if (ret < 0)
+               return ret;
 
        switch (l2_tunnel->l2_tunnel_type) {
        case RTE_L2_TUNNEL_TYPE_E_TAG:
@@ -7403,7 +7641,7 @@ ixgbe_dev_l2_tunnel_filter_handle(struct rte_eth_dev *dev,
                                  enum rte_filter_op filter_op,
                                  void *arg)
 {
-       int ret = 0;
+       int ret;
 
        if (filter_op == RTE_ETH_FILTER_NOP)
                return 0;