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 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
cxgbe_mpstcam_alloc(struct port_info * pi,const u8 * eth_addr,const u8 * mask)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
cxgbe_mpstcam_modify(struct port_info * pi,int idx,const u8 * addr)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 */
reset_mpstcam_entry(struct mps_tcam_entry * entry)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 */
cxgbe_mpstcam_remove(struct port_info * pi,u16 idx)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
t4_init_mpstcam(struct adapter * adap)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
t4_cleanup_mpstcam(struct adapter * adap)238 void t4_cleanup_mpstcam(struct adapter *adap)
239 {
240 if (adap->mpstcam)
241 t4_os_free(adap->mpstcam);
242 }
243