New upstream version 18.08
[deb_dpdk.git] / drivers / net / cxgbe / clip_tbl.c
diff --git a/drivers/net/cxgbe/clip_tbl.c b/drivers/net/cxgbe/clip_tbl.c
new file mode 100644 (file)
index 0000000..5e4dc52
--- /dev/null
@@ -0,0 +1,193 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Chelsio Communications.
+ * All rights reserved.
+ */
+
+#include "common.h"
+#include "clip_tbl.h"
+
+/**
+ * Allocate clip entry in HW with associated IPV4/IPv6 address
+ */
+static int clip6_get_mbox(const struct rte_eth_dev *dev, const u32 *lip)
+{
+       struct adapter *adap = ethdev2adap(dev);
+       struct fw_clip_cmd c;
+       u64 hi = ((u64)lip[1]) << 32 | lip[0];
+       u64 lo = ((u64)lip[3]) << 32 | lip[2];
+
+       memset(&c, 0, sizeof(c));
+       c.op_to_write = cpu_to_be32(V_FW_CMD_OP(FW_CLIP_CMD) |
+                                   F_FW_CMD_REQUEST | F_FW_CMD_WRITE);
+       c.alloc_to_len16 = cpu_to_be32(F_FW_CLIP_CMD_ALLOC | FW_LEN16(c));
+       c.ip_hi = hi;
+       c.ip_lo = lo;
+       return t4_wr_mbox_meat(adap, adap->mbox, &c, sizeof(c), &c, false);
+}
+
+/**
+ * Delete clip entry in HW having the associated IPV4/IPV6 address
+ */
+static int clip6_release_mbox(const struct rte_eth_dev *dev, const u32 *lip)
+{
+       struct adapter *adap = ethdev2adap(dev);
+       struct fw_clip_cmd c;
+       u64 hi = ((u64)lip[1]) << 32 | lip[0];
+       u64 lo = ((u64)lip[3]) << 32 | lip[2];
+
+       memset(&c, 0, sizeof(c));
+       c.op_to_write = cpu_to_be32(V_FW_CMD_OP(FW_CLIP_CMD) |
+                                   F_FW_CMD_REQUEST | F_FW_CMD_READ);
+       c.alloc_to_len16 = cpu_to_be32(F_FW_CLIP_CMD_FREE | FW_LEN16(c));
+       c.ip_hi = hi;
+       c.ip_lo = lo;
+       return t4_wr_mbox_meat(adap, adap->mbox, &c, sizeof(c), &c, false);
+}
+
+/**
+ * cxgbe_clip_release - Release associated CLIP entry
+ * @ce: clip entry to release
+ *
+ * Releases ref count and frees up a clip entry from CLIP table
+ */
+void cxgbe_clip_release(struct rte_eth_dev *dev, struct clip_entry *ce)
+{
+       int ret;
+
+       t4_os_lock(&ce->lock);
+       if (rte_atomic32_dec_and_test(&ce->refcnt)) {
+               ret = clip6_release_mbox(dev, ce->addr);
+               if (ret)
+                       dev_debug(adap, "CLIP FW DEL CMD failed: %d", ret);
+       }
+       t4_os_unlock(&ce->lock);
+}
+
+/**
+ * find_or_alloc_clipe - Find/Allocate a free CLIP entry
+ * @c: CLIP table
+ * @lip: IPV4/IPV6 address to compare/add
+ * Returns pointer to the IPV4/IPV6 entry found/created
+ *
+ * Finds/Allocates an CLIP entry to be used for a filter rule.
+ */
+static struct clip_entry *find_or_alloc_clipe(struct clip_tbl *c,
+                                             const u32 *lip)
+{
+       struct clip_entry *end, *e;
+       struct clip_entry *first_free = NULL;
+       unsigned int clipt_size = c->clipt_size;
+
+       for (e = &c->cl_list[0], end = &c->cl_list[clipt_size]; e != end; ++e) {
+               if (rte_atomic32_read(&e->refcnt) == 0) {
+                       if (!first_free)
+                               first_free = e;
+               } else {
+                       if (memcmp(lip, e->addr, sizeof(e->addr)) == 0)
+                               goto exists;
+               }
+       }
+
+       if (first_free) {
+               e = first_free;
+               goto exists;
+       }
+
+       return NULL;
+
+exists:
+       return e;
+}
+
+static struct clip_entry *t4_clip_alloc(struct rte_eth_dev *dev,
+                                       u32 *lip, u8 v6)
+{
+       struct adapter *adap = ethdev2adap(dev);
+       struct clip_tbl *ctbl = adap->clipt;
+       struct clip_entry *ce;
+       int ret = 0;
+
+       if (!ctbl)
+               return NULL;
+
+       t4_os_write_lock(&ctbl->lock);
+       ce = find_or_alloc_clipe(ctbl, lip);
+       if (ce) {
+               t4_os_lock(&ce->lock);
+               if (!rte_atomic32_read(&ce->refcnt)) {
+                       rte_memcpy(ce->addr, lip, sizeof(ce->addr));
+                       if (v6) {
+                               ce->type = FILTER_TYPE_IPV6;
+                               rte_atomic32_set(&ce->refcnt, 1);
+                               ret = clip6_get_mbox(dev, lip);
+                               if (ret)
+                                       dev_debug(adap,
+                                                 "CLIP FW ADD CMD failed: %d",
+                                                 ret);
+                       } else {
+                               ce->type = FILTER_TYPE_IPV4;
+                       }
+               } else {
+                       rte_atomic32_inc(&ce->refcnt);
+               }
+               t4_os_unlock(&ce->lock);
+       }
+       t4_os_write_unlock(&ctbl->lock);
+
+       return ret ? NULL : ce;
+}
+
+/**
+ * cxgbe_clip_alloc - Allocate a IPV6 CLIP entry
+ * @dev: rte_eth_dev pointer
+ * @lip: IPV6 address to add
+ * Returns pointer to the CLIP entry created
+ *
+ * Allocates a IPV6 CLIP entry to be used for a filter rule.
+ */
+struct clip_entry *cxgbe_clip_alloc(struct rte_eth_dev *dev, u32 *lip)
+{
+       return t4_clip_alloc(dev, lip, FILTER_TYPE_IPV6);
+}
+
+/**
+ * Initialize CLIP Table
+ */
+struct clip_tbl *t4_init_clip_tbl(unsigned int clipt_start,
+                                 unsigned int clipt_end)
+{
+       unsigned int clipt_size;
+       struct clip_tbl *ctbl;
+       unsigned int i;
+
+       if (clipt_start >= clipt_end)
+               return NULL;
+
+       clipt_size = clipt_end - clipt_start + 1;
+
+       ctbl = t4_os_alloc(sizeof(*ctbl) +
+                          clipt_size * sizeof(struct clip_entry));
+       if (!ctbl)
+               return NULL;
+
+       ctbl->clipt_start = clipt_start;
+       ctbl->clipt_size = clipt_size;
+
+       t4_os_rwlock_init(&ctbl->lock);
+
+       for (i = 0; i < ctbl->clipt_size; i++) {
+               t4_os_lock_init(&ctbl->cl_list[i].lock);
+               rte_atomic32_set(&ctbl->cl_list[i].refcnt, 0);
+       }
+
+       return ctbl;
+}
+
+/**
+ * Cleanup CLIP Table
+ */
+void t4_cleanup_clip_tbl(struct adapter *adap)
+{
+       if (adap->clipt)
+               t4_os_free(adap->clipt);
+}