xref: /dpdk/drivers/net/cxgbe/mps_tcam.c (revision e12a0166c80f65e35408f4715b2f3a60763c3741)
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 		rte_atomic_fetch_add_explicit(&entry->refcnt, 1, rte_memory_order_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 	rte_atomic_store_explicit(&entry->refcnt, 1, rte_memory_order_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 		rte_atomic_store_explicit(&entry->refcnt, 1, rte_memory_order_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 	rte_atomic_store_explicit(&entry->refcnt, 0, rte_memory_order_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 (rte_atomic_load_explicit(&entry->refcnt, rte_memory_order_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 = rte_atomic_fetch_sub_explicit(&entry->refcnt, 1,
199 						    rte_memory_order_relaxed) - 1;
200 
201 	if (ret == 0) {
202 		reset_mpstcam_entry(entry);
203 		t->full = false;	/* We have atleast 1 free entry */
204 		cxgbe_update_free_idx(t);
205 	}
206 
207 	t4_os_write_unlock(&t->lock);
208 	return ret;
209 }
210 
cxgbe_mpstcam_rawf_enable(struct port_info * pi)211 int cxgbe_mpstcam_rawf_enable(struct port_info *pi)
212 {
213 	struct adapter *adap = pi->adapter;
214 	struct mps_tcam_entry *entry;
215 	struct mpstcam_table *t;
216 	u16 rawf_idx;
217 	int ret = 0;
218 
219 	t = adap->mpstcam;
220 	if (adap->params.rawf_size == 0 || t == NULL)
221 		return -EOPNOTSUPP;
222 
223 	t4_os_write_lock(&t->lock);
224 	rawf_idx = adap->params.rawf_start + pi->port_id;
225 	entry = &t->entry[rawf_idx];
226 	if (rte_atomic_load_explicit(&entry->refcnt, rte_memory_order_relaxed) == 1)
227 		goto out_unlock;
228 
229 	ret = t4_alloc_raw_mac_filt(adap, pi->viid, entry->eth_addr,
230 				    entry->mask, rawf_idx, 0, pi->port_id,
231 				    false);
232 	if (ret < 0)
233 		goto out_unlock;
234 
235 	rte_atomic_store_explicit(&entry->refcnt, 1, rte_memory_order_relaxed);
236 
237 out_unlock:
238 	t4_os_write_unlock(&t->lock);
239 	return ret;
240 }
241 
cxgbe_mpstcam_rawf_disable(struct port_info * pi)242 int cxgbe_mpstcam_rawf_disable(struct port_info *pi)
243 {
244 	struct adapter *adap = pi->adapter;
245 	struct mps_tcam_entry *entry;
246 	struct mpstcam_table *t;
247 	u16 rawf_idx;
248 	int ret = 0;
249 
250 	t = adap->mpstcam;
251 	if (adap->params.rawf_size == 0 || t == NULL)
252 		return -EOPNOTSUPP;
253 
254 	t4_os_write_lock(&t->lock);
255 	rawf_idx = adap->params.rawf_start + pi->port_id;
256 	entry = &t->entry[rawf_idx];
257 	if (rte_atomic_load_explicit(&entry->refcnt, rte_memory_order_relaxed) != 1)
258 		goto out_unlock;
259 
260 	ret = t4_free_raw_mac_filt(adap, pi->viid, entry->eth_addr,
261 				   entry->mask, rawf_idx, 0, pi->port_id,
262 				   false);
263 	if (ret < 0)
264 		goto out_unlock;
265 
266 	rte_atomic_store_explicit(&entry->refcnt, 0, rte_memory_order_relaxed);
267 
268 out_unlock:
269 	t4_os_write_unlock(&t->lock);
270 	return ret;
271 }
272 
t4_init_mpstcam(struct adapter * adap)273 struct mpstcam_table *t4_init_mpstcam(struct adapter *adap)
274 {
275 	u16 size = adap->params.arch.mps_tcam_size;
276 	struct mpstcam_table *t;
277 	int i;
278 
279 	t =  t4_os_alloc(sizeof(*t) + size * sizeof(struct mps_tcam_entry));
280 	if (!t)
281 		return NULL;
282 
283 	t4_os_rwlock_init(&t->lock);
284 	t->full = false;
285 	t->size = size;
286 
287 	for (i = 0; i < size; i++) {
288 		reset_mpstcam_entry(&t->entry[i]);
289 		t->entry[i].mpstcam = t;
290 		t->entry[i].idx = i;
291 	}
292 
293 	/* RAW MAC entries are reserved for match-all wildcard to
294 	 * match all promiscuous traffic. So, mark them special.
295 	 */
296 	for (i = 0; i < adap->params.rawf_size; i++)
297 		t->entry[adap->params.rawf_start + i].state = MPS_ENTRY_RAWF;
298 
299 	/* first entry is used by chip. this is overwritten only
300 	 * in t4_cleanup_mpstcam()
301 	 */
302 	t->entry[0].state = MPS_ENTRY_USED;
303 	t->free_idx = 1;
304 
305 	return t;
306 }
307 
t4_cleanup_mpstcam(struct adapter * adap)308 void t4_cleanup_mpstcam(struct adapter *adap)
309 {
310 	if (adap->mpstcam)
311 		t4_os_free(adap->mpstcam);
312 }
313