aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/cxgbe/mps_tcam.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/cxgbe/mps_tcam.c')
-rw-r--r--drivers/net/cxgbe/mps_tcam.c243
1 files changed, 243 insertions, 0 deletions
diff --git a/drivers/net/cxgbe/mps_tcam.c b/drivers/net/cxgbe/mps_tcam.c
new file mode 100644
index 00000000..02ec69a9
--- /dev/null
+++ b/drivers/net/cxgbe/mps_tcam.c
@@ -0,0 +1,243 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Chelsio Communications.
+ * All rights reserved.
+ */
+
+#include "mps_tcam.h"
+
+static inline bool
+match_entry(struct mps_tcam_entry *entry, const u8 *eth_addr, const u8 *mask)
+{
+ if (!memcmp(eth_addr, entry->eth_addr, ETHER_ADDR_LEN) &&
+ !memcmp(mask, entry->mask, ETHER_ADDR_LEN))
+ return true;
+ return false;
+}
+
+static int cxgbe_update_free_idx(struct mpstcam_table *t)
+{
+ struct mps_tcam_entry *entry = t->entry;
+ u16 i, next = t->free_idx + 1;
+
+ if (entry[t->free_idx].state == MPS_ENTRY_UNUSED)
+ /* You are already pointing to a free entry !! */
+ return 0;
+
+ /* loop, till we don't rollback to same index where we started */
+ for (i = next; i != t->free_idx; i++) {
+ if (i == t->size)
+ /* rollback and search free entry from start */
+ i = 0;
+
+ if (entry[i].state == MPS_ENTRY_UNUSED) {
+ t->free_idx = i;
+ return 0;
+ }
+ }
+
+ return -1; /* table is full */
+}
+
+static struct mps_tcam_entry *
+cxgbe_mpstcam_lookup(struct mpstcam_table *t, const u8 *eth_addr,
+ const u8 *mask)
+{
+ struct mps_tcam_entry *entry = t->entry;
+ int i;
+
+ if (!entry)
+ return NULL;
+
+ for (i = 0; i < t->size; i++) {
+ if (entry[i].state == MPS_ENTRY_UNUSED)
+ continue; /* entry is not being used */
+ if (match_entry(&entry[i], eth_addr, mask))
+ return &entry[i];
+ }
+
+ return NULL;
+}
+
+int cxgbe_mpstcam_alloc(struct port_info *pi, const u8 *eth_addr,
+ const u8 *mask)
+{
+ struct adapter *adap = pi->adapter;
+ struct mpstcam_table *mpstcam = adap->mpstcam;
+ struct mps_tcam_entry *entry;
+ int ret;
+
+ if (!adap->mpstcam) {
+ dev_err(adap, "mpstcam table is not available\n");
+ return -EOPNOTSUPP;
+ }
+
+ /* If entry already present, return it. */
+ t4_os_write_lock(&mpstcam->lock);
+ entry = cxgbe_mpstcam_lookup(adap->mpstcam, eth_addr, mask);
+ if (entry) {
+ rte_atomic32_add(&entry->refcnt, 1);
+ t4_os_write_unlock(&mpstcam->lock);
+ return entry->idx;
+ }
+
+ if (mpstcam->full) {
+ t4_os_write_unlock(&mpstcam->lock);
+ dev_err(adap, "mps-tcam table is full\n");
+ return -ENOMEM;
+ }
+
+ ret = t4_alloc_raw_mac_filt(adap, pi->viid, eth_addr, mask,
+ mpstcam->free_idx, 0, pi->port_id, false);
+ if (ret <= 0) {
+ t4_os_write_unlock(&mpstcam->lock);
+ return ret;
+ }
+
+ /* Fill in the new values */
+ entry = &mpstcam->entry[ret];
+ memcpy(entry->eth_addr, eth_addr, ETHER_ADDR_LEN);
+ memcpy(entry->mask, mask, ETHER_ADDR_LEN);
+ rte_atomic32_set(&entry->refcnt, 1);
+ entry->state = MPS_ENTRY_USED;
+
+ if (cxgbe_update_free_idx(mpstcam))
+ mpstcam->full = true;
+
+ t4_os_write_unlock(&mpstcam->lock);
+ return ret;
+}
+
+int cxgbe_mpstcam_modify(struct port_info *pi, int idx, const u8 *addr)
+{
+ struct adapter *adap = pi->adapter;
+ struct mpstcam_table *mpstcam = adap->mpstcam;
+ struct mps_tcam_entry *entry;
+
+ if (!mpstcam)
+ return -EOPNOTSUPP;
+ t4_os_write_lock(&mpstcam->lock);
+ if (idx != -1 && idx >= mpstcam->size) {
+ t4_os_write_unlock(&mpstcam->lock);
+ return -EINVAL;
+ }
+ if (idx >= 0) {
+ entry = &mpstcam->entry[idx];
+ /* user wants to modify an existing entry.
+ * verify if entry exists
+ */
+ if (entry->state != MPS_ENTRY_USED) {
+ t4_os_write_unlock(&mpstcam->lock);
+ return -EINVAL;
+ }
+ }
+
+ idx = t4_change_mac(adap, adap->mbox, pi->viid, idx, addr, true, true);
+ if (idx < 0) {
+ t4_os_write_unlock(&mpstcam->lock);
+ return idx;
+ }
+
+ /* idx can now be different from what user provided */
+ entry = &mpstcam->entry[idx];
+ memcpy(entry->eth_addr, addr, ETHER_ADDR_LEN);
+ /* NOTE: we have considered the case that idx returned by t4_change_mac
+ * will be different from the user provided value only if user
+ * provided value is -1
+ */
+ if (entry->state == MPS_ENTRY_UNUSED) {
+ rte_atomic32_set(&entry->refcnt, 1);
+ entry->state = MPS_ENTRY_USED;
+ }
+
+ if (cxgbe_update_free_idx(mpstcam))
+ mpstcam->full = true;
+
+ t4_os_write_unlock(&mpstcam->lock);
+ return idx;
+}
+
+/**
+ * hold appropriate locks while calling this.
+ */
+static inline void reset_mpstcam_entry(struct mps_tcam_entry *entry)
+{
+ memset(entry->eth_addr, 0, ETHER_ADDR_LEN);
+ memset(entry->mask, 0, ETHER_ADDR_LEN);
+ rte_atomic32_clear(&entry->refcnt);
+ entry->state = MPS_ENTRY_UNUSED;
+}
+
+/**
+ * ret < 0: fatal error
+ * ret = 0: entry removed in h/w
+ * ret > 0: updated refcount.
+ */
+int cxgbe_mpstcam_remove(struct port_info *pi, u16 idx)
+{
+ struct adapter *adap = pi->adapter;
+ struct mpstcam_table *t = adap->mpstcam;
+ struct mps_tcam_entry *entry;
+ int ret;
+
+ if (!t)
+ return -EOPNOTSUPP;
+ t4_os_write_lock(&t->lock);
+ entry = &t->entry[idx];
+ if (entry->state == MPS_ENTRY_UNUSED) {
+ t4_os_write_unlock(&t->lock);
+ return -EINVAL;
+ }
+
+ if (rte_atomic32_read(&entry->refcnt) == 1)
+ ret = t4_free_raw_mac_filt(adap, pi->viid, entry->eth_addr,
+ entry->mask, idx, 1, pi->port_id,
+ false);
+ else
+ ret = rte_atomic32_sub_return(&entry->refcnt, 1);
+
+ if (ret == 0) {
+ reset_mpstcam_entry(entry);
+ t->full = false; /* We have atleast 1 free entry */
+ cxgbe_update_free_idx(t);
+ }
+
+ t4_os_write_unlock(&t->lock);
+ return ret;
+}
+
+struct mpstcam_table *t4_init_mpstcam(struct adapter *adap)
+{
+ struct mpstcam_table *t;
+ int i;
+ u16 size = adap->params.arch.mps_tcam_size;
+
+ t = t4_os_alloc(sizeof(*t) + size * sizeof(struct mps_tcam_entry));
+ if (!t)
+ return NULL;
+
+ t4_os_rwlock_init(&t->lock);
+ t->full = false;
+ t->size = size;
+
+ for (i = 0; i < size; i++) {
+ reset_mpstcam_entry(&t->entry[i]);
+ t->entry[i].mpstcam = t;
+ t->entry[i].idx = i;
+ }
+
+ /* first entry is used by chip. this is overwritten only
+ * in t4_cleanup_mpstcam()
+ */
+ t->entry[0].state = MPS_ENTRY_USED;
+ t->free_idx = 1;
+
+ return t;
+}
+
+void t4_cleanup_mpstcam(struct adapter *adap)
+{
+ if (adap->mpstcam) {
+ t4_os_free(adap->mpstcam->entry);
+ t4_os_free(adap->mpstcam);
+ }
+}