xref: /dpdk/drivers/net/cxgbe/mps_tcam.c (revision 68a03efeed657e6e05f281479b33b51102797e15)
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