1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright(c) 2018 Chelsio Communications. 3 * All rights reserved. 4 */ 5 6 #include "mps_tcam.h" 7 8 static inline bool 9 match_entry(struct mps_tcam_entry *entry, const u8 *eth_addr, const u8 *mask) 10 { 11 if (!memcmp(eth_addr, entry->eth_addr, RTE_ETHER_ADDR_LEN) && 12 !memcmp(mask, entry->mask, RTE_ETHER_ADDR_LEN)) 13 return true; 14 return false; 15 } 16 17 static int cxgbe_update_free_idx(struct mpstcam_table *t) 18 { 19 struct mps_tcam_entry *entry = t->entry; 20 u16 i, next = t->free_idx + 1; 21 22 if (entry[t->free_idx].state == MPS_ENTRY_UNUSED) 23 /* You are already pointing to a free entry !! */ 24 return 0; 25 26 /* loop, till we don't rollback to same index where we started */ 27 for (i = next; i != t->free_idx; i++) { 28 if (i == t->size) 29 /* rollback and search free entry from start */ 30 i = 0; 31 32 if (entry[i].state == MPS_ENTRY_UNUSED) { 33 t->free_idx = i; 34 return 0; 35 } 36 } 37 38 return -1; /* table is full */ 39 } 40 41 static struct mps_tcam_entry * 42 cxgbe_mpstcam_lookup(struct mpstcam_table *t, const u8 *eth_addr, 43 const u8 *mask) 44 { 45 struct mps_tcam_entry *entry = t->entry; 46 int i; 47 48 if (!entry) 49 return NULL; 50 51 for (i = 0; i < t->size; i++) { 52 if (entry[i].state == MPS_ENTRY_UNUSED) 53 continue; /* entry is not being used */ 54 if (match_entry(&entry[i], eth_addr, mask)) 55 return &entry[i]; 56 } 57 58 return NULL; 59 } 60 61 int cxgbe_mpstcam_alloc(struct port_info *pi, const u8 *eth_addr, 62 const u8 *mask) 63 { 64 struct adapter *adap = pi->adapter; 65 struct mpstcam_table *mpstcam = adap->mpstcam; 66 struct mps_tcam_entry *entry; 67 int ret; 68 69 if (!adap->mpstcam) { 70 dev_err(adap, "mpstcam table is not available\n"); 71 return -EOPNOTSUPP; 72 } 73 74 /* If entry already present, return it. */ 75 t4_os_write_lock(&mpstcam->lock); 76 entry = cxgbe_mpstcam_lookup(adap->mpstcam, eth_addr, mask); 77 if (entry) { 78 rte_atomic32_add(&entry->refcnt, 1); 79 t4_os_write_unlock(&mpstcam->lock); 80 return entry->idx; 81 } 82 83 if (mpstcam->full) { 84 t4_os_write_unlock(&mpstcam->lock); 85 dev_err(adap, "mps-tcam table is full\n"); 86 return -ENOMEM; 87 } 88 89 ret = t4_alloc_raw_mac_filt(adap, pi->viid, eth_addr, mask, 90 mpstcam->free_idx, 0, pi->port_id, false); 91 if (ret <= 0) { 92 t4_os_write_unlock(&mpstcam->lock); 93 return ret; 94 } 95 96 /* Fill in the new values */ 97 entry = &mpstcam->entry[ret]; 98 memcpy(entry->eth_addr, eth_addr, RTE_ETHER_ADDR_LEN); 99 memcpy(entry->mask, mask, RTE_ETHER_ADDR_LEN); 100 rte_atomic32_set(&entry->refcnt, 1); 101 entry->state = MPS_ENTRY_USED; 102 103 if (cxgbe_update_free_idx(mpstcam)) 104 mpstcam->full = true; 105 106 t4_os_write_unlock(&mpstcam->lock); 107 return ret; 108 } 109 110 int cxgbe_mpstcam_modify(struct port_info *pi, int idx, const u8 *addr) 111 { 112 struct adapter *adap = pi->adapter; 113 struct mpstcam_table *mpstcam = adap->mpstcam; 114 struct mps_tcam_entry *entry; 115 116 if (!mpstcam) 117 return -EOPNOTSUPP; 118 t4_os_write_lock(&mpstcam->lock); 119 if (idx != -1 && idx >= mpstcam->size) { 120 t4_os_write_unlock(&mpstcam->lock); 121 return -EINVAL; 122 } 123 if (idx >= 0) { 124 entry = &mpstcam->entry[idx]; 125 /* user wants to modify an existing entry. 126 * verify if entry exists 127 */ 128 if (entry->state != MPS_ENTRY_USED) { 129 t4_os_write_unlock(&mpstcam->lock); 130 return -EINVAL; 131 } 132 } 133 134 idx = t4_change_mac(adap, adap->mbox, pi->viid, idx, addr, true, true); 135 if (idx < 0) { 136 t4_os_write_unlock(&mpstcam->lock); 137 return idx; 138 } 139 140 /* idx can now be different from what user provided */ 141 entry = &mpstcam->entry[idx]; 142 memcpy(entry->eth_addr, addr, RTE_ETHER_ADDR_LEN); 143 memset(entry->mask, ~0, RTE_ETHER_ADDR_LEN); 144 /* NOTE: we have considered the case that idx returned by t4_change_mac 145 * will be different from the user provided value only if user 146 * provided value is -1 147 */ 148 if (entry->state == MPS_ENTRY_UNUSED) { 149 rte_atomic32_set(&entry->refcnt, 1); 150 entry->state = MPS_ENTRY_USED; 151 } 152 153 if (cxgbe_update_free_idx(mpstcam)) 154 mpstcam->full = true; 155 156 t4_os_write_unlock(&mpstcam->lock); 157 return idx; 158 } 159 160 /** 161 * hold appropriate locks while calling this. 162 */ 163 static inline void reset_mpstcam_entry(struct mps_tcam_entry *entry) 164 { 165 memset(entry->eth_addr, 0, RTE_ETHER_ADDR_LEN); 166 memset(entry->mask, 0, RTE_ETHER_ADDR_LEN); 167 rte_atomic32_clear(&entry->refcnt); 168 entry->state = MPS_ENTRY_UNUSED; 169 } 170 171 /** 172 * ret < 0: fatal error 173 * ret = 0: entry removed in h/w 174 * ret > 0: updated refcount. 175 */ 176 int cxgbe_mpstcam_remove(struct port_info *pi, u16 idx) 177 { 178 struct adapter *adap = pi->adapter; 179 struct mpstcam_table *t = adap->mpstcam; 180 struct mps_tcam_entry *entry; 181 int ret; 182 183 if (!t) 184 return -EOPNOTSUPP; 185 t4_os_write_lock(&t->lock); 186 entry = &t->entry[idx]; 187 if (entry->state == MPS_ENTRY_UNUSED) { 188 t4_os_write_unlock(&t->lock); 189 return -EINVAL; 190 } 191 192 if (rte_atomic32_read(&entry->refcnt) == 1) 193 ret = t4_free_raw_mac_filt(adap, pi->viid, entry->eth_addr, 194 entry->mask, idx, 1, pi->port_id, 195 false); 196 else 197 ret = rte_atomic32_sub_return(&entry->refcnt, 1); 198 199 if (ret == 0) { 200 reset_mpstcam_entry(entry); 201 t->full = false; /* We have atleast 1 free entry */ 202 cxgbe_update_free_idx(t); 203 } 204 205 t4_os_write_unlock(&t->lock); 206 return ret; 207 } 208 209 struct mpstcam_table *t4_init_mpstcam(struct adapter *adap) 210 { 211 struct mpstcam_table *t; 212 int i; 213 u16 size = adap->params.arch.mps_tcam_size; 214 215 t = t4_os_alloc(sizeof(*t) + size * sizeof(struct mps_tcam_entry)); 216 if (!t) 217 return NULL; 218 219 t4_os_rwlock_init(&t->lock); 220 t->full = false; 221 t->size = size; 222 223 for (i = 0; i < size; i++) { 224 reset_mpstcam_entry(&t->entry[i]); 225 t->entry[i].mpstcam = t; 226 t->entry[i].idx = i; 227 } 228 229 /* first entry is used by chip. this is overwritten only 230 * in t4_cleanup_mpstcam() 231 */ 232 t->entry[0].state = MPS_ENTRY_USED; 233 t->free_idx = 1; 234 235 return t; 236 } 237 238 void t4_cleanup_mpstcam(struct adapter *adap) 239 { 240 if (adap->mpstcam) 241 t4_os_free(adap->mpstcam); 242 } 243