net/cxgbe: implement flow create operation
[dpdk.git] / drivers / net / cxgbe / cxgbe_filter.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2018 Chelsio Communications.
3  * All rights reserved.
4  */
5
6 #include "common.h"
7 #include "t4_regs.h"
8 #include "cxgbe_filter.h"
9
10 /**
11  * Validate if the requested filter specification can be set by checking
12  * if the requested features have been enabled
13  */
14 int validate_filter(struct adapter *adapter, struct ch_filter_specification *fs)
15 {
16         u32 fconf;
17
18         /*
19          * Check for unconfigured fields being used.
20          */
21         fconf = adapter->params.tp.vlan_pri_map;
22
23 #define S(_field) \
24         (fs->val._field || fs->mask._field)
25 #define U(_mask, _field) \
26         (!(fconf & (_mask)) && S(_field))
27
28         if (U(F_ETHERTYPE, ethtype) || U(F_PROTOCOL, proto))
29                 return -EOPNOTSUPP;
30
31 #undef S
32 #undef U
33         return 0;
34 }
35
36 /**
37  * Get the queue to which the traffic must be steered to.
38  */
39 static unsigned int get_filter_steerq(struct rte_eth_dev *dev,
40                                       struct ch_filter_specification *fs)
41 {
42         struct port_info *pi = ethdev2pinfo(dev);
43         struct adapter *adapter = pi->adapter;
44         unsigned int iq;
45
46         /*
47          * If the user has requested steering matching Ingress Packets
48          * to a specific Queue Set, we need to make sure it's in range
49          * for the port and map that into the Absolute Queue ID of the
50          * Queue Set's Response Queue.
51          */
52         if (!fs->dirsteer) {
53                 iq = 0;
54         } else {
55                 /*
56                  * If the iq id is greater than the number of qsets,
57                  * then assume it is an absolute qid.
58                  */
59                 if (fs->iq < pi->n_rx_qsets)
60                         iq = adapter->sge.ethrxq[pi->first_qset +
61                                                  fs->iq].rspq.abs_id;
62                 else
63                         iq = fs->iq;
64         }
65
66         return iq;
67 }
68
69 /* Return an error number if the indicated filter isn't writable ... */
70 int writable_filter(struct filter_entry *f)
71 {
72         if (f->locked)
73                 return -EPERM;
74         if (f->pending)
75                 return -EBUSY;
76
77         return 0;
78 }
79
80 /**
81  * Check if entry already filled.
82  */
83 bool is_filter_set(struct tid_info *t, int fidx, int family)
84 {
85         bool result = FALSE;
86         int i, max;
87
88         /* IPv6 requires four slots and IPv4 requires only 1 slot.
89          * Ensure, there's enough slots available.
90          */
91         max = family == FILTER_TYPE_IPV6 ? fidx + 3 : fidx;
92
93         t4_os_lock(&t->ftid_lock);
94         for (i = fidx; i <= max; i++) {
95                 if (rte_bitmap_get(t->ftid_bmap, i)) {
96                         result = TRUE;
97                         break;
98                 }
99         }
100         t4_os_unlock(&t->ftid_lock);
101         return result;
102 }
103
104 /**
105  * Allocate a available free entry
106  */
107 int cxgbe_alloc_ftid(struct adapter *adap, unsigned int family)
108 {
109         struct tid_info *t = &adap->tids;
110         int pos;
111         int size = t->nftids;
112
113         t4_os_lock(&t->ftid_lock);
114         if (family == FILTER_TYPE_IPV6)
115                 pos = cxgbe_bitmap_find_free_region(t->ftid_bmap, size, 4);
116         else
117                 pos = cxgbe_find_first_zero_bit(t->ftid_bmap, size);
118         t4_os_unlock(&t->ftid_lock);
119
120         return pos < size ? pos : -1;
121 }
122
123 /**
124  * Clear a filter and release any of its resources that we own.  This also
125  * clears the filter's "pending" status.
126  */
127 void clear_filter(struct filter_entry *f)
128 {
129         /*
130          * The zeroing of the filter rule below clears the filter valid,
131          * pending, locked flags etc. so it's all we need for
132          * this operation.
133          */
134         memset(f, 0, sizeof(*f));
135 }
136
137 int set_filter_wr(struct rte_eth_dev *dev, unsigned int fidx)
138 {
139         struct adapter *adapter = ethdev2adap(dev);
140         struct filter_entry *f = &adapter->tids.ftid_tab[fidx];
141         struct rte_mbuf *mbuf;
142         struct fw_filter_wr *fwr;
143         struct sge_ctrl_txq *ctrlq;
144         unsigned int port_id = ethdev2pinfo(dev)->port_id;
145         int ret;
146
147         ctrlq = &adapter->sge.ctrlq[port_id];
148         mbuf = rte_pktmbuf_alloc(ctrlq->mb_pool);
149         if (!mbuf) {
150                 ret = -ENOMEM;
151                 goto out;
152         }
153
154         mbuf->data_len = sizeof(*fwr);
155         mbuf->pkt_len = mbuf->data_len;
156
157         fwr = rte_pktmbuf_mtod(mbuf, struct fw_filter_wr *);
158         memset(fwr, 0, sizeof(*fwr));
159
160         /*
161          * Construct the work request to set the filter.
162          */
163         fwr->op_pkd = cpu_to_be32(V_FW_WR_OP(FW_FILTER_WR));
164         fwr->len16_pkd = cpu_to_be32(V_FW_WR_LEN16(sizeof(*fwr) / 16));
165         fwr->tid_to_iq =
166                 cpu_to_be32(V_FW_FILTER_WR_TID(f->tid) |
167                             V_FW_FILTER_WR_RQTYPE(f->fs.type) |
168                             V_FW_FILTER_WR_NOREPLY(0) |
169                             V_FW_FILTER_WR_IQ(f->fs.iq));
170         fwr->del_filter_to_l2tix =
171                 cpu_to_be32(V_FW_FILTER_WR_DROP(f->fs.action == FILTER_DROP) |
172                             V_FW_FILTER_WR_DIRSTEER(f->fs.dirsteer) |
173                             V_FW_FILTER_WR_HITCNTS(f->fs.hitcnts) |
174                             V_FW_FILTER_WR_PRIO(f->fs.prio));
175         fwr->ethtype = cpu_to_be16(f->fs.val.ethtype);
176         fwr->ethtypem = cpu_to_be16(f->fs.mask.ethtype);
177         fwr->smac_sel = 0;
178         fwr->rx_chan_rx_rpl_iq =
179                 cpu_to_be16(V_FW_FILTER_WR_RX_CHAN(0) |
180                             V_FW_FILTER_WR_RX_RPL_IQ(adapter->sge.fw_evtq.abs_id
181                                                      ));
182         fwr->ptcl = f->fs.val.proto;
183         fwr->ptclm = f->fs.mask.proto;
184         rte_memcpy(fwr->lip, f->fs.val.lip, sizeof(fwr->lip));
185         rte_memcpy(fwr->lipm, f->fs.mask.lip, sizeof(fwr->lipm));
186         rte_memcpy(fwr->fip, f->fs.val.fip, sizeof(fwr->fip));
187         rte_memcpy(fwr->fipm, f->fs.mask.fip, sizeof(fwr->fipm));
188         fwr->lp = cpu_to_be16(f->fs.val.lport);
189         fwr->lpm = cpu_to_be16(f->fs.mask.lport);
190         fwr->fp = cpu_to_be16(f->fs.val.fport);
191         fwr->fpm = cpu_to_be16(f->fs.mask.fport);
192
193         /*
194          * Mark the filter as "pending" and ship off the Filter Work Request.
195          * When we get the Work Request Reply we'll clear the pending status.
196          */
197         f->pending = 1;
198         t4_mgmt_tx(ctrlq, mbuf);
199         return 0;
200
201 out:
202         return ret;
203 }
204
205 /**
206  * Set the corresponding entry in the bitmap. 4 slots are
207  * marked for IPv6, whereas only 1 slot is marked for IPv4.
208  */
209 static int cxgbe_set_ftid(struct tid_info *t, int fidx, int family)
210 {
211         t4_os_lock(&t->ftid_lock);
212         if (rte_bitmap_get(t->ftid_bmap, fidx)) {
213                 t4_os_unlock(&t->ftid_lock);
214                 return -EBUSY;
215         }
216
217         if (family == FILTER_TYPE_IPV4) {
218                 rte_bitmap_set(t->ftid_bmap, fidx);
219         } else {
220                 rte_bitmap_set(t->ftid_bmap, fidx);
221                 rte_bitmap_set(t->ftid_bmap, fidx + 1);
222                 rte_bitmap_set(t->ftid_bmap, fidx + 2);
223                 rte_bitmap_set(t->ftid_bmap, fidx + 3);
224         }
225         t4_os_unlock(&t->ftid_lock);
226         return 0;
227 }
228
229 /**
230  * Clear the corresponding entry in the bitmap. 4 slots are
231  * cleared for IPv6, whereas only 1 slot is cleared for IPv4.
232  */
233 static void cxgbe_clear_ftid(struct tid_info *t, int fidx, int family)
234 {
235         t4_os_lock(&t->ftid_lock);
236         if (family == FILTER_TYPE_IPV4) {
237                 rte_bitmap_clear(t->ftid_bmap, fidx);
238         } else {
239                 rte_bitmap_clear(t->ftid_bmap, fidx);
240                 rte_bitmap_clear(t->ftid_bmap, fidx + 1);
241                 rte_bitmap_clear(t->ftid_bmap, fidx + 2);
242                 rte_bitmap_clear(t->ftid_bmap, fidx + 3);
243         }
244         t4_os_unlock(&t->ftid_lock);
245 }
246
247 /**
248  * Check a Chelsio Filter Request for validity, convert it into our internal
249  * format and send it to the hardware.  Return 0 on success, an error number
250  * otherwise.  We attach any provided filter operation context to the internal
251  * filter specification in order to facilitate signaling completion of the
252  * operation.
253  */
254 int cxgbe_set_filter(struct rte_eth_dev *dev, unsigned int filter_id,
255                      struct ch_filter_specification *fs,
256                      struct filter_ctx *ctx)
257 {
258         struct port_info *pi = ethdev2pinfo(dev);
259         struct adapter *adapter = pi->adapter;
260         unsigned int fidx, iq, fid_bit = 0;
261         struct filter_entry *f;
262         int ret;
263
264         if (filter_id >= adapter->tids.nftids)
265                 return -ERANGE;
266
267         ret = validate_filter(adapter, fs);
268         if (ret)
269                 return ret;
270
271         /*
272          * Ensure filter id is aligned on the 4 slot boundary for IPv6
273          * maskfull filters.
274          */
275         if (fs->type)
276                 filter_id &= ~(0x3);
277
278         ret = is_filter_set(&adapter->tids, filter_id, fs->type);
279         if (ret)
280                 return -EBUSY;
281
282         iq = get_filter_steerq(dev, fs);
283
284         /*
285          * IPv6 filters occupy four slots and must be aligned on
286          * four-slot boundaries.  IPv4 filters only occupy a single
287          * slot and have no alignment requirements but writing a new
288          * IPv4 filter into the middle of an existing IPv6 filter
289          * requires clearing the old IPv6 filter.
290          */
291         if (fs->type == FILTER_TYPE_IPV4) { /* IPv4 */
292                 /*
293                  * If our IPv4 filter isn't being written to a
294                  * multiple of four filter index and there's an IPv6
295                  * filter at the multiple of 4 base slot, then we need
296                  * to delete that IPv6 filter ...
297                  */
298                 fidx = filter_id & ~0x3;
299                 if (fidx != filter_id && adapter->tids.ftid_tab[fidx].fs.type) {
300                         f = &adapter->tids.ftid_tab[fidx];
301                         if (f->valid)
302                                 return -EBUSY;
303                 }
304         } else { /* IPv6 */
305                 /*
306                  * Ensure that the IPv6 filter is aligned on a
307                  * multiple of 4 boundary.
308                  */
309                 if (filter_id & 0x3)
310                         return -EINVAL;
311
312                 /*
313                  * Check all except the base overlapping IPv4 filter
314                  * slots.
315                  */
316                 for (fidx = filter_id + 1; fidx < filter_id + 4; fidx++) {
317                         f = &adapter->tids.ftid_tab[fidx];
318                         if (f->valid)
319                                 return -EBUSY;
320                 }
321         }
322
323         /*
324          * Check to make sure that provided filter index is not
325          * already in use by someone else
326          */
327         f = &adapter->tids.ftid_tab[filter_id];
328         if (f->valid)
329                 return -EBUSY;
330
331         fidx = adapter->tids.ftid_base + filter_id;
332         fid_bit = filter_id;
333         ret = cxgbe_set_ftid(&adapter->tids, fid_bit,
334                              fs->type ? FILTER_TYPE_IPV6 : FILTER_TYPE_IPV4);
335         if (ret)
336                 return ret;
337
338         /*
339          * Check to make sure the filter requested is writable ...
340          */
341         ret = writable_filter(f);
342         if (ret) {
343                 /* Clear the bits we have set above */
344                 cxgbe_clear_ftid(&adapter->tids, fid_bit,
345                                  fs->type ? FILTER_TYPE_IPV6 :
346                                             FILTER_TYPE_IPV4);
347                 return ret;
348         }
349
350         /*
351          * Convert the filter specification into our internal format.
352          * We copy the PF/VF specification into the Outer VLAN field
353          * here so the rest of the code -- including the interface to
354          * the firmware -- doesn't have to constantly do these checks.
355          */
356         f->fs = *fs;
357         f->fs.iq = iq;
358         f->dev = dev;
359
360         /*
361          * Attempt to set the filter.  If we don't succeed, we clear
362          * it and return the failure.
363          */
364         f->ctx = ctx;
365         f->tid = fidx; /* Save the actual tid */
366         ret = set_filter_wr(dev, filter_id);
367         if (ret) {
368                 fid_bit = f->tid - adapter->tids.ftid_base;
369                 cxgbe_clear_ftid(&adapter->tids, fid_bit,
370                                  fs->type ? FILTER_TYPE_IPV6 :
371                                             FILTER_TYPE_IPV4);
372                 clear_filter(f);
373         }
374
375         return ret;
376 }
377
378 /**
379  * Handle a LE-TCAM filter write/deletion reply.
380  */
381 void filter_rpl(struct adapter *adap, const struct cpl_set_tcb_rpl *rpl)
382 {
383         struct filter_entry *f = NULL;
384         unsigned int tid = GET_TID(rpl);
385         int idx, max_fidx = adap->tids.nftids;
386
387         /* Get the corresponding filter entry for this tid */
388         if (adap->tids.ftid_tab) {
389                 /* Check this in normal filter region */
390                 idx = tid - adap->tids.ftid_base;
391                 if (idx >= max_fidx)
392                         return;
393
394                 f = &adap->tids.ftid_tab[idx];
395                 if (f->tid != tid)
396                         return;
397         }
398
399         /* We found the filter entry for this tid */
400         if (f) {
401                 unsigned int ret = G_COOKIE(rpl->cookie);
402                 struct filter_ctx *ctx;
403
404                 /*
405                  * Pull off any filter operation context attached to the
406                  * filter.
407                  */
408                 ctx = f->ctx;
409                 f->ctx = NULL;
410
411                 if (ret == FW_FILTER_WR_FLT_ADDED) {
412                         f->pending = 0;  /* asynchronous setup completed */
413                         f->valid = 1;
414                         if (ctx) {
415                                 ctx->tid = f->tid;
416                                 ctx->result = 0;
417                         }
418                 } else {
419                         /*
420                          * Something went wrong.  Issue a warning about the
421                          * problem and clear everything out.
422                          */
423                         dev_warn(adap, "filter %u setup failed with error %u\n",
424                                  idx, ret);
425                         clear_filter(f);
426                         if (ctx)
427                                 ctx->result = -EINVAL;
428                 }
429
430                 if (ctx)
431                         t4_complete(&ctx->completion);
432         }
433 }