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
match_entry(struct mps_tcam_entry * entry,const u8 * eth_addr,const u8 * mask)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
cxgbe_update_free_idx(struct mpstcam_table * t)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 *
cxgbe_mpstcam_lookup(struct mpstcam_table * t,const u8 * eth_addr,const u8 * mask)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 entry[i].state == MPS_ENTRY_RAWF)
54 continue; /* entry is not being used */
55 if (match_entry(&entry[i], eth_addr, mask))
56 return &entry[i];
57 }
58
59 return NULL;
60 }
61
cxgbe_mpstcam_alloc(struct port_info * pi,const u8 * eth_addr,const u8 * mask)62 int cxgbe_mpstcam_alloc(struct port_info *pi, const u8 *eth_addr,
63 const u8 *mask)
64 {
65 struct adapter *adap = pi->adapter;
66 struct mpstcam_table *mpstcam = adap->mpstcam;
67 struct mps_tcam_entry *entry;
68 int ret;
69
70 if (!adap->mpstcam) {
71 dev_err(adap, "mpstcam table is not available\n");
72 return -EOPNOTSUPP;
73 }
74
75 /* If entry already present, return it. */
76 t4_os_write_lock(&mpstcam->lock);
77 entry = cxgbe_mpstcam_lookup(adap->mpstcam, eth_addr, mask);
78 if (entry) {
79 __atomic_add_fetch(&entry->refcnt, 1, __ATOMIC_RELAXED);
80 t4_os_write_unlock(&mpstcam->lock);
81 return entry->idx;
82 }
83
84 if (mpstcam->full) {
85 t4_os_write_unlock(&mpstcam->lock);
86 dev_err(adap, "mps-tcam table is full\n");
87 return -ENOMEM;
88 }
89
90 ret = t4_alloc_raw_mac_filt(adap, pi->viid, eth_addr, mask,
91 mpstcam->free_idx, 0, pi->port_id, false);
92 if (ret <= 0) {
93 t4_os_write_unlock(&mpstcam->lock);
94 return ret;
95 }
96
97 /* Fill in the new values */
98 entry = &mpstcam->entry[ret];
99 memcpy(entry->eth_addr, eth_addr, RTE_ETHER_ADDR_LEN);
100 memcpy(entry->mask, mask, RTE_ETHER_ADDR_LEN);
101 __atomic_store_n(&entry->refcnt, 1, __ATOMIC_RELAXED);
102 entry->state = MPS_ENTRY_USED;
103
104 if (cxgbe_update_free_idx(mpstcam))
105 mpstcam->full = true;
106
107 t4_os_write_unlock(&mpstcam->lock);
108 return ret;
109 }
110
cxgbe_mpstcam_modify(struct port_info * pi,int idx,const u8 * addr)111 int cxgbe_mpstcam_modify(struct port_info *pi, int idx, const u8 *addr)
112 {
113 struct adapter *adap = pi->adapter;
114 struct mpstcam_table *mpstcam = adap->mpstcam;
115 struct mps_tcam_entry *entry;
116
117 if (!mpstcam)
118 return -EOPNOTSUPP;
119 t4_os_write_lock(&mpstcam->lock);
120 if (idx != -1 && idx >= mpstcam->size) {
121 t4_os_write_unlock(&mpstcam->lock);
122 return -EINVAL;
123 }
124 if (idx >= 0) {
125 entry = &mpstcam->entry[idx];
126 /* user wants to modify an existing entry.
127 * verify if entry exists
128 */
129 if (entry->state != MPS_ENTRY_USED) {
130 t4_os_write_unlock(&mpstcam->lock);
131 return -EINVAL;
132 }
133 }
134
135 idx = t4_change_mac(adap, adap->mbox, pi->viid, idx, addr, true, true);
136 if (idx < 0) {
137 t4_os_write_unlock(&mpstcam->lock);
138 return idx;
139 }
140
141 /* idx can now be different from what user provided */
142 entry = &mpstcam->entry[idx];
143 memcpy(entry->eth_addr, addr, RTE_ETHER_ADDR_LEN);
144 memset(entry->mask, ~0, RTE_ETHER_ADDR_LEN);
145 /* NOTE: we have considered the case that idx returned by t4_change_mac
146 * will be different from the user provided value only if user
147 * provided value is -1
148 */
149 if (entry->state == MPS_ENTRY_UNUSED) {
150 __atomic_store_n(&entry->refcnt, 1, __ATOMIC_RELAXED);
151 entry->state = MPS_ENTRY_USED;
152 }
153
154 if (cxgbe_update_free_idx(mpstcam))
155 mpstcam->full = true;
156
157 t4_os_write_unlock(&mpstcam->lock);
158 return idx;
159 }
160
161 /**
162 * hold appropriate locks while calling this.
163 */
reset_mpstcam_entry(struct mps_tcam_entry * entry)164 static inline void reset_mpstcam_entry(struct mps_tcam_entry *entry)
165 {
166 memset(entry->eth_addr, 0, RTE_ETHER_ADDR_LEN);
167 memset(entry->mask, 0, RTE_ETHER_ADDR_LEN);
168 __atomic_store_n(&entry->refcnt, 0, __ATOMIC_RELAXED);
169 entry->state = MPS_ENTRY_UNUSED;
170 }
171
172 /**
173 * ret < 0: fatal error
174 * ret = 0: entry removed in h/w
175 * ret > 0: updated refcount.
176 */
cxgbe_mpstcam_remove(struct port_info * pi,u16 idx)177 int cxgbe_mpstcam_remove(struct port_info *pi, u16 idx)
178 {
179 struct adapter *adap = pi->adapter;
180 struct mpstcam_table *t = adap->mpstcam;
181 struct mps_tcam_entry *entry;
182 int ret;
183
184 if (!t)
185 return -EOPNOTSUPP;
186 t4_os_write_lock(&t->lock);
187 entry = &t->entry[idx];
188 if (entry->state != MPS_ENTRY_USED) {
189 t4_os_write_unlock(&t->lock);
190 return -EINVAL;
191 }
192
193 if (__atomic_load_n(&entry->refcnt, __ATOMIC_RELAXED) == 1)
194 ret = t4_free_raw_mac_filt(adap, pi->viid, entry->eth_addr,
195 entry->mask, idx, 1, pi->port_id,
196 false);
197 else
198 ret = __atomic_sub_fetch(&entry->refcnt, 1, __ATOMIC_RELAXED);
199
200 if (ret == 0) {
201 reset_mpstcam_entry(entry);
202 t->full = false; /* We have atleast 1 free entry */
203 cxgbe_update_free_idx(t);
204 }
205
206 t4_os_write_unlock(&t->lock);
207 return ret;
208 }
209
cxgbe_mpstcam_rawf_enable(struct port_info * pi)210 int cxgbe_mpstcam_rawf_enable(struct port_info *pi)
211 {
212 struct adapter *adap = pi->adapter;
213 struct mps_tcam_entry *entry;
214 struct mpstcam_table *t;
215 u16 rawf_idx;
216 int ret = 0;
217
218 t = adap->mpstcam;
219 if (adap->params.rawf_size == 0 || t == NULL)
220 return -EOPNOTSUPP;
221
222 t4_os_write_lock(&t->lock);
223 rawf_idx = adap->params.rawf_start + pi->port_id;
224 entry = &t->entry[rawf_idx];
225 if (__atomic_load_n(&entry->refcnt, __ATOMIC_RELAXED) == 1)
226 goto out_unlock;
227
228 ret = t4_alloc_raw_mac_filt(adap, pi->viid, entry->eth_addr,
229 entry->mask, rawf_idx, 0, pi->port_id,
230 false);
231 if (ret < 0)
232 goto out_unlock;
233
234 __atomic_store_n(&entry->refcnt, 1, __ATOMIC_RELAXED);
235
236 out_unlock:
237 t4_os_write_unlock(&t->lock);
238 return ret;
239 }
240
cxgbe_mpstcam_rawf_disable(struct port_info * pi)241 int cxgbe_mpstcam_rawf_disable(struct port_info *pi)
242 {
243 struct adapter *adap = pi->adapter;
244 struct mps_tcam_entry *entry;
245 struct mpstcam_table *t;
246 u16 rawf_idx;
247 int ret = 0;
248
249 t = adap->mpstcam;
250 if (adap->params.rawf_size == 0 || t == NULL)
251 return -EOPNOTSUPP;
252
253 t4_os_write_lock(&t->lock);
254 rawf_idx = adap->params.rawf_start + pi->port_id;
255 entry = &t->entry[rawf_idx];
256 if (__atomic_load_n(&entry->refcnt, __ATOMIC_RELAXED) != 1)
257 goto out_unlock;
258
259 ret = t4_free_raw_mac_filt(adap, pi->viid, entry->eth_addr,
260 entry->mask, rawf_idx, 0, pi->port_id,
261 false);
262 if (ret < 0)
263 goto out_unlock;
264
265 __atomic_store_n(&entry->refcnt, 0, __ATOMIC_RELAXED);
266
267 out_unlock:
268 t4_os_write_unlock(&t->lock);
269 return ret;
270 }
271
t4_init_mpstcam(struct adapter * adap)272 struct mpstcam_table *t4_init_mpstcam(struct adapter *adap)
273 {
274 u16 size = adap->params.arch.mps_tcam_size;
275 struct mpstcam_table *t;
276 int i;
277
278 t = t4_os_alloc(sizeof(*t) + size * sizeof(struct mps_tcam_entry));
279 if (!t)
280 return NULL;
281
282 t4_os_rwlock_init(&t->lock);
283 t->full = false;
284 t->size = size;
285
286 for (i = 0; i < size; i++) {
287 reset_mpstcam_entry(&t->entry[i]);
288 t->entry[i].mpstcam = t;
289 t->entry[i].idx = i;
290 }
291
292 /* RAW MAC entries are reserved for match-all wildcard to
293 * match all promiscuous traffic. So, mark them special.
294 */
295 for (i = 0; i < adap->params.rawf_size; i++)
296 t->entry[adap->params.rawf_start + i].state = MPS_ENTRY_RAWF;
297
298 /* first entry is used by chip. this is overwritten only
299 * in t4_cleanup_mpstcam()
300 */
301 t->entry[0].state = MPS_ENTRY_USED;
302 t->free_idx = 1;
303
304 return t;
305 }
306
t4_cleanup_mpstcam(struct adapter * adap)307 void t4_cleanup_mpstcam(struct adapter *adap)
308 {
309 if (adap->mpstcam)
310 t4_os_free(adap->mpstcam);
311 }
312