net/cxgbe: add API to program hardware MPS table
authorShagun Agrawal <shaguna@chelsio.com>
Mon, 27 Aug 2018 12:52:31 +0000 (18:22 +0530)
committerFerruh Yigit <ferruh.yigit@intel.com>
Fri, 14 Sep 2018 18:08:41 +0000 (20:08 +0200)
Add API to program and manage hardware Multi Port Switch table. MPS
holds destination MAC addresses to be matched against incoming packets
for further rule processing. Packets not matching any entry in MPS table
will be dropped by default, unless the underlying port is in promiscuous
mode.

Signed-off-by: Shagun Agrawal <shaguna@chelsio.com>
Signed-off-by: Rahul Lakkireddy <rahul.lakkireddy@chelsio.com>
drivers/net/cxgbe/Makefile
drivers/net/cxgbe/base/adapter.h
drivers/net/cxgbe/base/common.h
drivers/net/cxgbe/base/t4_hw.c
drivers/net/cxgbe/base/t4_regs.h
drivers/net/cxgbe/base/t4fw_interface.h
drivers/net/cxgbe/cxgbe_main.c
drivers/net/cxgbe/meson.build
drivers/net/cxgbe/mps_tcam.c [new file with mode: 0644]
drivers/net/cxgbe/mps_tcam.h [new file with mode: 0644]

index d75b070..68466f1 100644 (file)
@@ -53,6 +53,7 @@ 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) += clip_tbl.c
+SRCS-$(CONFIG_RTE_LIBRTE_CXGBE_PMD) += mps_tcam.c
 SRCS-$(CONFIG_RTE_LIBRTE_CXGBE_PMD) += l2t.c
 SRCS-$(CONFIG_RTE_LIBRTE_CXGBE_PMD) += t4vf_hw.c
 
index 9f4a965..47cfc5f 100644 (file)
@@ -328,6 +328,7 @@ struct adapter {
        unsigned int l2t_end;     /* Layer 2 table end */
        struct clip_tbl *clipt;   /* CLIP table */
        struct l2t_data *l2t;     /* Layer 2 table */
+       struct mpstcam_table *mpstcam;
 
        struct tid_info tids;     /* Info used to access TID related tables */
 };
index 157201d..9f57568 100644 (file)
@@ -388,6 +388,12 @@ int t4_free_vi(struct adapter *adap, unsigned int mbox,
 int t4_set_rxmode(struct adapter *adap, unsigned int mbox, unsigned int viid,
                  int mtu, int promisc, int all_multi, int bcast, int vlanex,
                  bool sleep_ok);
+int t4_free_raw_mac_filt(struct adapter *adap, unsigned int viid,
+                        const u8 *addr, const u8 *mask, unsigned int idx,
+                        u8 lookup_type, u8 port_id, bool sleep_ok);
+int t4_alloc_raw_mac_filt(struct adapter *adap, unsigned int viid,
+                         const u8 *addr, const u8 *mask, unsigned int idx,
+                         u8 lookup_type, u8 port_id, bool sleep_ok);
 int t4_change_mac(struct adapter *adap, unsigned int mbox, unsigned int viid,
                  int idx, const u8 *addr, bool persist, bool add_smt);
 int t4_enable_vi_params(struct adapter *adap, unsigned int mbox,
index 31762c9..d608941 100644 (file)
@@ -4161,6 +4161,112 @@ int t4_set_rxmode(struct adapter *adap, unsigned int mbox, unsigned int viid,
                return t4vf_wr_mbox(adap, &c, sizeof(c), NULL);
 }
 
+/**
+ *     t4_alloc_raw_mac_filt - Adds a raw mac entry in mps tcam
+ *     @adap: the adapter
+ *     @viid: the VI id
+ *     @mac: the MAC address
+ *     @mask: the mask
+ *     @idx: index at which to add this entry
+ *     @port_id: the port index
+ *     @lookup_type: MAC address for inner (1) or outer (0) header
+ *     @sleep_ok: call is allowed to sleep
+ *
+ *     Adds the mac entry at the specified index using raw mac interface.
+ *
+ *     Returns a negative error number or the allocated index for this mac.
+ */
+int t4_alloc_raw_mac_filt(struct adapter *adap, unsigned int viid,
+                         const u8 *addr, const u8 *mask, unsigned int idx,
+                         u8 lookup_type, u8 port_id, bool sleep_ok)
+{
+       int ret = 0;
+       struct fw_vi_mac_cmd c;
+       struct fw_vi_mac_raw *p = &c.u.raw;
+       u32 val;
+
+       memset(&c, 0, sizeof(c));
+       c.op_to_viid = cpu_to_be32(V_FW_CMD_OP(FW_VI_MAC_CMD) |
+                                  F_FW_CMD_REQUEST | F_FW_CMD_WRITE |
+                                  V_FW_VI_MAC_CMD_VIID(viid));
+       val = V_FW_CMD_LEN16(1) |
+             V_FW_VI_MAC_CMD_ENTRY_TYPE(FW_VI_MAC_TYPE_RAW);
+       c.freemacs_to_len16 = cpu_to_be32(val);
+
+       /* Specify that this is an inner mac address */
+       p->raw_idx_pkd = cpu_to_be32(V_FW_VI_MAC_CMD_RAW_IDX(idx));
+
+       /* Lookup Type. Outer header: 0, Inner header: 1 */
+       p->data0_pkd = cpu_to_be32(V_DATALKPTYPE(lookup_type) |
+                                  V_DATAPORTNUM(port_id));
+       /* Lookup mask and port mask */
+       p->data0m_pkd = cpu_to_be64(V_DATALKPTYPE(M_DATALKPTYPE) |
+                                   V_DATAPORTNUM(M_DATAPORTNUM));
+
+       /* Copy the address and the mask */
+       memcpy((u8 *)&p->data1[0] + 2, addr, ETHER_ADDR_LEN);
+       memcpy((u8 *)&p->data1m[0] + 2, mask, ETHER_ADDR_LEN);
+
+       ret = t4_wr_mbox_meat(adap, adap->mbox, &c, sizeof(c), &c, sleep_ok);
+       if (ret == 0) {
+               ret = G_FW_VI_MAC_CMD_RAW_IDX(be32_to_cpu(p->raw_idx_pkd));
+               if (ret != (int)idx)
+                       ret = -ENOMEM;
+       }
+
+       return ret;
+}
+
+/**
+ *     t4_free_raw_mac_filt - Frees a raw mac entry in mps tcam
+ *     @adap: the adapter
+ *     @viid: the VI id
+ *     @addr: the MAC address
+ *     @mask: the mask
+ *     @idx: index of the entry in mps tcam
+ *     @lookup_type: MAC address for inner (1) or outer (0) header
+ *     @port_id: the port index
+ *     @sleep_ok: call is allowed to sleep
+ *
+ *     Removes the mac entry at the specified index using raw mac interface.
+ *
+ *     Returns a negative error number on failure.
+ */
+int t4_free_raw_mac_filt(struct adapter *adap, unsigned int viid,
+                        const u8 *addr, const u8 *mask, unsigned int idx,
+                        u8 lookup_type, u8 port_id, bool sleep_ok)
+{
+       struct fw_vi_mac_cmd c;
+       struct fw_vi_mac_raw *p = &c.u.raw;
+       u32 raw;
+
+       memset(&c, 0, sizeof(c));
+       c.op_to_viid = cpu_to_be32(V_FW_CMD_OP(FW_VI_MAC_CMD) |
+                                  F_FW_CMD_REQUEST | F_FW_CMD_WRITE |
+                                  V_FW_CMD_EXEC(0) |
+                                  V_FW_VI_MAC_CMD_VIID(viid));
+       raw = V_FW_VI_MAC_CMD_ENTRY_TYPE(FW_VI_MAC_TYPE_RAW);
+       c.freemacs_to_len16 = cpu_to_be32(V_FW_VI_MAC_CMD_FREEMACS(0) |
+                                         raw |
+                                         V_FW_CMD_LEN16(1));
+
+       p->raw_idx_pkd = cpu_to_be32(V_FW_VI_MAC_CMD_RAW_IDX(idx) |
+                                    FW_VI_MAC_ID_BASED_FREE);
+
+       /* Lookup Type. Outer header: 0, Inner header: 1 */
+       p->data0_pkd = cpu_to_be32(V_DATALKPTYPE(lookup_type) |
+                                  V_DATAPORTNUM(port_id));
+       /* Lookup mask and port mask */
+       p->data0m_pkd = cpu_to_be64(V_DATALKPTYPE(M_DATALKPTYPE) |
+                                   V_DATAPORTNUM(M_DATAPORTNUM));
+
+       /* Copy the address and the mask */
+       memcpy((u8 *)&p->data1[0] + 2, addr, ETHER_ADDR_LEN);
+       memcpy((u8 *)&p->data1m[0] + 2, mask, ETHER_ADDR_LEN);
+
+       return t4_wr_mbox_meat(adap, adap->mbox, &c, sizeof(c), &c, sleep_ok);
+}
+
 /**
  * t4_change_mac - modifies the exact-match filter for a MAC address
  * @adap: the adapter
index 6f872ed..af8c741 100644 (file)
 #define MPS_T5_CLS_SRAM_H(idx) (A_MPS_T5_CLS_SRAM_H + (idx) * 8)
 #define NUM_MPS_T5_CLS_SRAM_H_INSTANCES 512
 
+#define S_DATAPORTNUM    12
+#define M_DATAPORTNUM    0xfU
+#define V_DATAPORTNUM(x) ((x) << S_DATAPORTNUM)
+
+#define S_DATALKPTYPE    10
+#define M_DATALKPTYPE    0x3U
+#define V_DATALKPTYPE(x) ((x) << S_DATALKPTYPE)
+
 /* registers for module SGE */
 #define SGE_BASE_ADDR 0x1000
 
index 1c08637..e2d2ee8 100644 (file)
@@ -1282,12 +1282,17 @@ struct fw_vi_cmd {
 /* Special VI_MAC command index ids */
 #define FW_VI_MAC_ADD_MAC              0x3FF
 #define FW_VI_MAC_ADD_PERSIST_MAC      0x3FE
+#define FW_VI_MAC_ID_BASED_FREE         0x3FC
 
 enum fw_vi_mac_smac {
        FW_VI_MAC_MPS_TCAM_ENTRY,
        FW_VI_MAC_SMT_AND_MPSTCAM
 };
 
+enum fw_vi_mac_entry_types {
+       FW_VI_MAC_TYPE_RAW = 0x2,
+};
+
 struct fw_vi_mac_cmd {
        __be32 op_to_viid;
        __be32 freemacs_to_len16;
@@ -1299,6 +1304,13 @@ struct fw_vi_mac_cmd {
                struct fw_vi_mac_hash {
                        __be64 hashvec;
                } hash;
+               struct fw_vi_mac_raw {
+                       __be32 raw_idx_pkd;
+                       __be32 data0_pkd;
+                       __be32 data1[2];
+                       __be64 data0m_pkd;
+                       __be32 data1m[2];
+               } raw;
        } u;
 };
 
@@ -1308,6 +1320,12 @@ struct fw_vi_mac_cmd {
 #define G_FW_VI_MAC_CMD_VIID(x)        \
        (((x) >> S_FW_VI_MAC_CMD_VIID) & M_FW_VI_MAC_CMD_VIID)
 
+#define S_FW_VI_MAC_CMD_FREEMACS       31
+#define V_FW_VI_MAC_CMD_FREEMACS(x)    ((x) << S_FW_VI_MAC_CMD_FREEMACS)
+
+#define S_FW_VI_MAC_CMD_ENTRY_TYPE      23
+#define V_FW_VI_MAC_CMD_ENTRY_TYPE(x)   ((x) << S_FW_VI_MAC_CMD_ENTRY_TYPE)
+
 #define S_FW_VI_MAC_CMD_VALID          15
 #define M_FW_VI_MAC_CMD_VALID          0x1
 #define V_FW_VI_MAC_CMD_VALID(x)       ((x) << S_FW_VI_MAC_CMD_VALID)
@@ -1327,6 +1345,12 @@ struct fw_vi_mac_cmd {
 #define G_FW_VI_MAC_CMD_IDX(x) \
        (((x) >> S_FW_VI_MAC_CMD_IDX) & M_FW_VI_MAC_CMD_IDX)
 
+#define S_FW_VI_MAC_CMD_RAW_IDX         16
+#define M_FW_VI_MAC_CMD_RAW_IDX         0xffff
+#define V_FW_VI_MAC_CMD_RAW_IDX(x)      ((x) << S_FW_VI_MAC_CMD_RAW_IDX)
+#define G_FW_VI_MAC_CMD_RAW_IDX(x)      \
+       (((x) >> S_FW_VI_MAC_CMD_RAW_IDX) & M_FW_VI_MAC_CMD_RAW_IDX)
+
 struct fw_vi_rxmode_cmd {
        __be32 op_to_viid;
        __be32 retval_len16;
index be2bc42..20d2de4 100644 (file)
@@ -39,6 +39,7 @@
 #include "cxgbe.h"
 #include "clip_tbl.h"
 #include "l2t.h"
+#include "mps_tcam.h"
 
 /**
  * Allocate a chunk of memory. The allocated memory is cleared.
@@ -1689,6 +1690,7 @@ void cxgbe_close(struct adapter *adapter)
 
        if (adapter->flags & FULL_INIT_DONE) {
                tid_free(&adapter->tids);
+               t4_cleanup_mpstcam(adapter);
                t4_cleanup_clip_tbl(adapter);
                t4_cleanup_l2t(adapter);
                if (is_pf4(adapter))
@@ -1877,6 +1879,11 @@ allocate_mac:
                         "filter support disabled. Continuing\n");
        }
 
+       adapter->mpstcam = t4_init_mpstcam(adapter);
+       if (!adapter->mpstcam)
+               dev_warn(adapter, "could not allocate mps tcam table."
+                        " Continuing\n");
+
        if (is_hashfilter(adapter)) {
                if (t4_read_reg(adapter, A_LE_DB_CONFIG) & F_HASHEN) {
                        u32 hash_base, hash_reg;
index f6a442c..c51af26 100644 (file)
@@ -9,6 +9,7 @@ sources = files('cxgbe_ethdev.c',
        'cxgbe_filter.c',
        'cxgbe_flow.c',
        'clip_tbl.c',
+       'mps_tcam.c',
        'l2t.c',
        'base/t4_hw.c',
        'base/t4vf_hw.c')
diff --git a/drivers/net/cxgbe/mps_tcam.c b/drivers/net/cxgbe/mps_tcam.c
new file mode 100644 (file)
index 0000000..02ec69a
--- /dev/null
@@ -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);
+       }
+}
diff --git a/drivers/net/cxgbe/mps_tcam.h b/drivers/net/cxgbe/mps_tcam.h
new file mode 100644 (file)
index 0000000..c3d6fe0
--- /dev/null
@@ -0,0 +1,52 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Chelsio Communications.
+ * All rights reserved.
+ */
+
+#ifndef _CXGBE_MPSTCAM_H_
+#define _CXGBE_MPSTCAM_H_
+
+#include "common.h"
+
+enum {
+       MPS_ENTRY_UNUSED,       /* Keep this first so memset 0 renders
+                                * the correct state. Other states can
+                                * be added in future like MPS_ENTRY_BUSY
+                                * to reduce contention while mboxing
+                                * the request to f/w or to denote attributes
+                                * for a specific entry
+                                */
+       MPS_ENTRY_USED,
+};
+
+struct mps_tcam_entry {
+       u8 state;
+       u16 idx;
+
+       /* add data here which uniquely defines an entry */
+       u8 eth_addr[ETHER_ADDR_LEN];
+       u8 mask[ETHER_ADDR_LEN];
+
+       struct mpstcam_table *mpstcam; /* backptr */
+       rte_atomic32_t refcnt;
+};
+
+struct mpstcam_table {
+       u16 size;
+       rte_rwlock_t lock;
+       u16 free_idx;   /* next free index */
+       bool full;      /* since free index can be present
+                        * anywhere in the table, size and
+                        * free_idx cannot alone determine
+                        * if the table is full
+                        */
+       struct mps_tcam_entry entry[0];
+};
+
+struct mpstcam_table *t4_init_mpstcam(struct adapter *adap);
+void t4_cleanup_mpstcam(struct adapter *adap);
+int cxgbe_mpstcam_alloc(struct port_info *pi, const u8 *mac, const u8 *mask);
+int cxgbe_mpstcam_remove(struct port_info *pi, u16 idx);
+int cxgbe_mpstcam_modify(struct port_info *pi, int idx, const u8 *addr);
+
+#endif /* _CXGBE_MPSTCAM_H_ */