net/i40e: fix Rx packet statistics
[dpdk.git] / drivers / net / cxgbe / mps_tcam.c
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                     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
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
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  */
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  */
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
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
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
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
307 void t4_cleanup_mpstcam(struct adapter *adap)
308 {
309         if (adap->mpstcam)
310                 t4_os_free(adap->mpstcam);
311 }