f80c58b4ddb9b58e03a5b180904f781104699cae
[deb_dpdk.git] / drivers / net / mlx5 / mlx5_fdir.c
1 /*-
2  *   BSD LICENSE
3  *
4  *   Copyright 2015 6WIND S.A.
5  *   Copyright 2015 Mellanox.
6  *
7  *   Redistribution and use in source and binary forms, with or without
8  *   modification, are permitted provided that the following conditions
9  *   are met:
10  *
11  *     * Redistributions of source code must retain the above copyright
12  *       notice, this list of conditions and the following disclaimer.
13  *     * Redistributions in binary form must reproduce the above copyright
14  *       notice, this list of conditions and the following disclaimer in
15  *       the documentation and/or other materials provided with the
16  *       distribution.
17  *     * Neither the name of 6WIND S.A. nor the names of its
18  *       contributors may be used to endorse or promote products derived
19  *       from this software without specific prior written permission.
20  *
21  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27  *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33
34 #include <stddef.h>
35 #include <assert.h>
36 #include <stdint.h>
37 #include <string.h>
38 #include <errno.h>
39
40 /* Verbs header. */
41 /* ISO C doesn't support unnamed structs/unions, disabling -pedantic. */
42 #ifdef PEDANTIC
43 #pragma GCC diagnostic ignored "-Wpedantic"
44 #endif
45 #include <infiniband/verbs.h>
46 #ifdef PEDANTIC
47 #pragma GCC diagnostic error "-Wpedantic"
48 #endif
49
50 /* DPDK headers don't like -pedantic. */
51 #ifdef PEDANTIC
52 #pragma GCC diagnostic ignored "-Wpedantic"
53 #endif
54 #include <rte_ether.h>
55 #include <rte_malloc.h>
56 #include <rte_ethdev.h>
57 #include <rte_common.h>
58 #include <rte_flow.h>
59 #include <rte_flow_driver.h>
60 #ifdef PEDANTIC
61 #pragma GCC diagnostic error "-Wpedantic"
62 #endif
63
64 #include "mlx5.h"
65 #include "mlx5_rxtx.h"
66
67 struct fdir_flow_desc {
68         uint16_t dst_port;
69         uint16_t src_port;
70         uint32_t src_ip[4];
71         uint32_t dst_ip[4];
72         uint8_t mac[6];
73         uint16_t vlan_tag;
74         enum hash_rxq_type type;
75 };
76
77 struct mlx5_fdir_filter {
78         LIST_ENTRY(mlx5_fdir_filter) next;
79         uint16_t queue; /* Queue assigned to if FDIR match. */
80         enum rte_eth_fdir_behavior behavior;
81         struct fdir_flow_desc desc;
82         struct ibv_exp_flow *flow;
83 };
84
85 LIST_HEAD(fdir_filter_list, mlx5_fdir_filter);
86
87 /**
88  * Convert struct rte_eth_fdir_filter to mlx5 filter descriptor.
89  *
90  * @param[in] fdir_filter
91  *   DPDK filter structure to convert.
92  * @param[out] desc
93  *   Resulting mlx5 filter descriptor.
94  * @param mode
95  *   Flow director mode.
96  */
97 static void
98 fdir_filter_to_flow_desc(const struct rte_eth_fdir_filter *fdir_filter,
99                          struct fdir_flow_desc *desc, enum rte_fdir_mode mode)
100 {
101         /* Initialize descriptor. */
102         memset(desc, 0, sizeof(*desc));
103
104         /* Set VLAN ID. */
105         desc->vlan_tag = fdir_filter->input.flow_ext.vlan_tci;
106
107         /* Set MAC address. */
108         if (mode == RTE_FDIR_MODE_PERFECT_MAC_VLAN) {
109                 rte_memcpy(desc->mac,
110                            fdir_filter->input.flow.mac_vlan_flow.mac_addr.
111                                 addr_bytes,
112                            sizeof(desc->mac));
113                 desc->type = HASH_RXQ_ETH;
114                 return;
115         }
116
117         /* Set mode */
118         switch (fdir_filter->input.flow_type) {
119         case RTE_ETH_FLOW_NONFRAG_IPV4_UDP:
120                 desc->type = HASH_RXQ_UDPV4;
121                 break;
122         case RTE_ETH_FLOW_NONFRAG_IPV4_TCP:
123                 desc->type = HASH_RXQ_TCPV4;
124                 break;
125         case RTE_ETH_FLOW_NONFRAG_IPV4_OTHER:
126                 desc->type = HASH_RXQ_IPV4;
127                 break;
128         case RTE_ETH_FLOW_NONFRAG_IPV6_UDP:
129                 desc->type = HASH_RXQ_UDPV6;
130                 break;
131         case RTE_ETH_FLOW_NONFRAG_IPV6_TCP:
132                 desc->type = HASH_RXQ_TCPV6;
133                 break;
134         case RTE_ETH_FLOW_NONFRAG_IPV6_OTHER:
135                 desc->type = HASH_RXQ_IPV6;
136                 break;
137         default:
138                 break;
139         }
140
141         /* Set flow values */
142         switch (fdir_filter->input.flow_type) {
143         case RTE_ETH_FLOW_NONFRAG_IPV4_UDP:
144         case RTE_ETH_FLOW_NONFRAG_IPV4_TCP:
145                 desc->src_port = fdir_filter->input.flow.udp4_flow.src_port;
146                 desc->dst_port = fdir_filter->input.flow.udp4_flow.dst_port;
147         case RTE_ETH_FLOW_NONFRAG_IPV4_OTHER:
148                 desc->src_ip[0] = fdir_filter->input.flow.ip4_flow.src_ip;
149                 desc->dst_ip[0] = fdir_filter->input.flow.ip4_flow.dst_ip;
150                 break;
151         case RTE_ETH_FLOW_NONFRAG_IPV6_UDP:
152         case RTE_ETH_FLOW_NONFRAG_IPV6_TCP:
153                 desc->src_port = fdir_filter->input.flow.udp6_flow.src_port;
154                 desc->dst_port = fdir_filter->input.flow.udp6_flow.dst_port;
155                 /* Fall through. */
156         case RTE_ETH_FLOW_NONFRAG_IPV6_OTHER:
157                 rte_memcpy(desc->src_ip,
158                            fdir_filter->input.flow.ipv6_flow.src_ip,
159                            sizeof(desc->src_ip));
160                 rte_memcpy(desc->dst_ip,
161                            fdir_filter->input.flow.ipv6_flow.dst_ip,
162                            sizeof(desc->dst_ip));
163                 break;
164         default:
165                 break;
166         }
167 }
168
169 /**
170  * Check if two flow descriptors overlap according to configured mask.
171  *
172  * @param priv
173  *   Private structure that provides flow director mask.
174  * @param desc1
175  *   First flow descriptor to compare.
176  * @param desc2
177  *   Second flow descriptor to compare.
178  *
179  * @return
180  *   Nonzero if descriptors overlap.
181  */
182 static int
183 priv_fdir_overlap(const struct priv *priv,
184                   const struct fdir_flow_desc *desc1,
185                   const struct fdir_flow_desc *desc2)
186 {
187         const struct rte_eth_fdir_masks *mask =
188                 &priv->dev->data->dev_conf.fdir_conf.mask;
189         unsigned int i;
190
191         if (desc1->type != desc2->type)
192                 return 0;
193         /* Ignore non masked bits. */
194         for (i = 0; i != RTE_DIM(desc1->mac); ++i)
195                 if ((desc1->mac[i] & mask->mac_addr_byte_mask) !=
196                     (desc2->mac[i] & mask->mac_addr_byte_mask))
197                         return 0;
198         if (((desc1->src_port & mask->src_port_mask) !=
199              (desc2->src_port & mask->src_port_mask)) ||
200             ((desc1->dst_port & mask->dst_port_mask) !=
201              (desc2->dst_port & mask->dst_port_mask)))
202                 return 0;
203         switch (desc1->type) {
204         case HASH_RXQ_IPV4:
205         case HASH_RXQ_UDPV4:
206         case HASH_RXQ_TCPV4:
207                 if (((desc1->src_ip[0] & mask->ipv4_mask.src_ip) !=
208                      (desc2->src_ip[0] & mask->ipv4_mask.src_ip)) ||
209                     ((desc1->dst_ip[0] & mask->ipv4_mask.dst_ip) !=
210                      (desc2->dst_ip[0] & mask->ipv4_mask.dst_ip)))
211                         return 0;
212                 break;
213         case HASH_RXQ_IPV6:
214         case HASH_RXQ_UDPV6:
215         case HASH_RXQ_TCPV6:
216                 for (i = 0; i != RTE_DIM(desc1->src_ip); ++i)
217                         if (((desc1->src_ip[i] & mask->ipv6_mask.src_ip[i]) !=
218                              (desc2->src_ip[i] & mask->ipv6_mask.src_ip[i])) ||
219                             ((desc1->dst_ip[i] & mask->ipv6_mask.dst_ip[i]) !=
220                              (desc2->dst_ip[i] & mask->ipv6_mask.dst_ip[i])))
221                                 return 0;
222                 break;
223         default:
224                 break;
225         }
226         return 1;
227 }
228
229 /**
230  * Create flow director steering rule for a specific filter.
231  *
232  * @param priv
233  *   Private structure.
234  * @param mlx5_fdir_filter
235  *   Filter to create a steering rule for.
236  * @param fdir_queue
237  *   Flow director queue for matching packets.
238  *
239  * @return
240  *   0 on success, errno value on failure.
241  */
242 static int
243 priv_fdir_flow_add(struct priv *priv,
244                    struct mlx5_fdir_filter *mlx5_fdir_filter,
245                    struct fdir_queue *fdir_queue)
246 {
247         struct ibv_exp_flow *flow;
248         struct fdir_flow_desc *desc = &mlx5_fdir_filter->desc;
249         enum rte_fdir_mode fdir_mode =
250                 priv->dev->data->dev_conf.fdir_conf.mode;
251         struct rte_eth_fdir_masks *mask =
252                 &priv->dev->data->dev_conf.fdir_conf.mask;
253         FLOW_ATTR_SPEC_ETH(data, priv_flow_attr(priv, NULL, 0, desc->type));
254         struct ibv_exp_flow_attr *attr = &data->attr;
255         uintptr_t spec_offset = (uintptr_t)&data->spec;
256         struct ibv_exp_flow_spec_eth *spec_eth;
257         struct ibv_exp_flow_spec_ipv4 *spec_ipv4;
258         struct ibv_exp_flow_spec_ipv6 *spec_ipv6;
259         struct ibv_exp_flow_spec_tcp_udp *spec_tcp_udp;
260         struct mlx5_fdir_filter *iter_fdir_filter;
261         unsigned int i;
262
263         /* Abort if an existing flow overlaps this one to avoid packet
264          * duplication, even if it targets another queue. */
265         LIST_FOREACH(iter_fdir_filter, priv->fdir_filter_list, next)
266                 if ((iter_fdir_filter != mlx5_fdir_filter) &&
267                     (iter_fdir_filter->flow != NULL) &&
268                     (priv_fdir_overlap(priv,
269                                        &mlx5_fdir_filter->desc,
270                                        &iter_fdir_filter->desc)))
271                         return EEXIST;
272
273         /*
274          * No padding must be inserted by the compiler between attr and spec.
275          * This layout is expected by libibverbs.
276          */
277         assert(((uint8_t *)attr + sizeof(*attr)) == (uint8_t *)spec_offset);
278         priv_flow_attr(priv, attr, sizeof(data), desc->type);
279
280         /* Set Ethernet spec */
281         spec_eth = (struct ibv_exp_flow_spec_eth *)spec_offset;
282
283         /* The first specification must be Ethernet. */
284         assert(spec_eth->type == IBV_EXP_FLOW_SPEC_ETH);
285         assert(spec_eth->size == sizeof(*spec_eth));
286
287         /* VLAN ID */
288         spec_eth->val.vlan_tag = desc->vlan_tag & mask->vlan_tci_mask;
289         spec_eth->mask.vlan_tag = mask->vlan_tci_mask;
290
291         /* Update priority */
292         attr->priority = 2;
293
294         if (fdir_mode == RTE_FDIR_MODE_PERFECT_MAC_VLAN) {
295                 /* MAC Address */
296                 for (i = 0; i != RTE_DIM(spec_eth->mask.dst_mac); ++i) {
297                         spec_eth->val.dst_mac[i] =
298                                 desc->mac[i] & mask->mac_addr_byte_mask;
299                         spec_eth->mask.dst_mac[i] = mask->mac_addr_byte_mask;
300                 }
301                 goto create_flow;
302         }
303
304         switch (desc->type) {
305         case HASH_RXQ_IPV4:
306         case HASH_RXQ_UDPV4:
307         case HASH_RXQ_TCPV4:
308                 spec_offset += spec_eth->size;
309
310                 /* Set IP spec */
311                 spec_ipv4 = (struct ibv_exp_flow_spec_ipv4 *)spec_offset;
312
313                 /* The second specification must be IP. */
314                 assert(spec_ipv4->type == IBV_EXP_FLOW_SPEC_IPV4);
315                 assert(spec_ipv4->size == sizeof(*spec_ipv4));
316
317                 spec_ipv4->val.src_ip =
318                         desc->src_ip[0] & mask->ipv4_mask.src_ip;
319                 spec_ipv4->val.dst_ip =
320                         desc->dst_ip[0] & mask->ipv4_mask.dst_ip;
321                 spec_ipv4->mask.src_ip = mask->ipv4_mask.src_ip;
322                 spec_ipv4->mask.dst_ip = mask->ipv4_mask.dst_ip;
323
324                 /* Update priority */
325                 attr->priority = 1;
326
327                 if (desc->type == HASH_RXQ_IPV4)
328                         goto create_flow;
329
330                 spec_offset += spec_ipv4->size;
331                 break;
332         case HASH_RXQ_IPV6:
333         case HASH_RXQ_UDPV6:
334         case HASH_RXQ_TCPV6:
335                 spec_offset += spec_eth->size;
336
337                 /* Set IP spec */
338                 spec_ipv6 = (struct ibv_exp_flow_spec_ipv6 *)spec_offset;
339
340                 /* The second specification must be IP. */
341                 assert(spec_ipv6->type == IBV_EXP_FLOW_SPEC_IPV6);
342                 assert(spec_ipv6->size == sizeof(*spec_ipv6));
343
344                 for (i = 0; i != RTE_DIM(desc->src_ip); ++i) {
345                         ((uint32_t *)spec_ipv6->val.src_ip)[i] =
346                                 desc->src_ip[i] & mask->ipv6_mask.src_ip[i];
347                         ((uint32_t *)spec_ipv6->val.dst_ip)[i] =
348                                 desc->dst_ip[i] & mask->ipv6_mask.dst_ip[i];
349                 }
350                 rte_memcpy(spec_ipv6->mask.src_ip,
351                            mask->ipv6_mask.src_ip,
352                            sizeof(spec_ipv6->mask.src_ip));
353                 rte_memcpy(spec_ipv6->mask.dst_ip,
354                            mask->ipv6_mask.dst_ip,
355                            sizeof(spec_ipv6->mask.dst_ip));
356
357                 /* Update priority */
358                 attr->priority = 1;
359
360                 if (desc->type == HASH_RXQ_IPV6)
361                         goto create_flow;
362
363                 spec_offset += spec_ipv6->size;
364                 break;
365         default:
366                 ERROR("invalid flow attribute type");
367                 return EINVAL;
368         }
369
370         /* Set TCP/UDP flow specification. */
371         spec_tcp_udp = (struct ibv_exp_flow_spec_tcp_udp *)spec_offset;
372
373         /* The third specification must be TCP/UDP. */
374         assert(spec_tcp_udp->type == IBV_EXP_FLOW_SPEC_TCP ||
375                spec_tcp_udp->type == IBV_EXP_FLOW_SPEC_UDP);
376         assert(spec_tcp_udp->size == sizeof(*spec_tcp_udp));
377
378         spec_tcp_udp->val.src_port = desc->src_port & mask->src_port_mask;
379         spec_tcp_udp->val.dst_port = desc->dst_port & mask->dst_port_mask;
380         spec_tcp_udp->mask.src_port = mask->src_port_mask;
381         spec_tcp_udp->mask.dst_port = mask->dst_port_mask;
382
383         /* Update priority */
384         attr->priority = 0;
385
386 create_flow:
387
388         errno = 0;
389         flow = ibv_exp_create_flow(fdir_queue->qp, attr);
390         if (flow == NULL) {
391                 /* It's not clear whether errno is always set in this case. */
392                 ERROR("%p: flow director configuration failed, errno=%d: %s",
393                       (void *)priv, errno,
394                       (errno ? strerror(errno) : "Unknown error"));
395                 if (errno)
396                         return errno;
397                 return EINVAL;
398         }
399
400         DEBUG("%p: added flow director rule (%p)", (void *)priv, (void *)flow);
401         mlx5_fdir_filter->flow = flow;
402         return 0;
403 }
404
405 /**
406  * Destroy a flow director queue.
407  *
408  * @param fdir_queue
409  *   Flow director queue to be destroyed.
410  */
411 void
412 priv_fdir_queue_destroy(struct priv *priv, struct fdir_queue *fdir_queue)
413 {
414         struct mlx5_fdir_filter *fdir_filter;
415
416         /* Disable filter flows still applying to this queue. */
417         LIST_FOREACH(fdir_filter, priv->fdir_filter_list, next) {
418                 unsigned int idx = fdir_filter->queue;
419                 struct rxq_ctrl *rxq_ctrl =
420                         container_of((*priv->rxqs)[idx], struct rxq_ctrl, rxq);
421
422                 assert(idx < priv->rxqs_n);
423                 if (fdir_queue == rxq_ctrl->fdir_queue &&
424                     fdir_filter->flow != NULL) {
425                         claim_zero(ibv_exp_destroy_flow(fdir_filter->flow));
426                         fdir_filter->flow = NULL;
427                 }
428         }
429         assert(fdir_queue->qp);
430         claim_zero(ibv_destroy_qp(fdir_queue->qp));
431         assert(fdir_queue->ind_table);
432         claim_zero(ibv_exp_destroy_rwq_ind_table(fdir_queue->ind_table));
433         if (fdir_queue->wq)
434                 claim_zero(ibv_exp_destroy_wq(fdir_queue->wq));
435         if (fdir_queue->cq)
436                 claim_zero(ibv_destroy_cq(fdir_queue->cq));
437 #ifndef NDEBUG
438         memset(fdir_queue, 0x2a, sizeof(*fdir_queue));
439 #endif
440         rte_free(fdir_queue);
441 }
442
443 /**
444  * Create a flow director queue.
445  *
446  * @param priv
447  *   Private structure.
448  * @param wq
449  *   Work queue to route matched packets to, NULL if one needs to
450  *   be created.
451  *
452  * @return
453  *   Related flow director queue on success, NULL otherwise.
454  */
455 static struct fdir_queue *
456 priv_fdir_queue_create(struct priv *priv, struct ibv_exp_wq *wq,
457                        unsigned int socket)
458 {
459         struct fdir_queue *fdir_queue;
460
461         fdir_queue = rte_calloc_socket(__func__, 1, sizeof(*fdir_queue),
462                                        0, socket);
463         if (!fdir_queue) {
464                 ERROR("cannot allocate flow director queue");
465                 return NULL;
466         }
467         assert(priv->pd);
468         assert(priv->ctx);
469         if (!wq) {
470                 fdir_queue->cq = ibv_exp_create_cq(
471                         priv->ctx, 1, NULL, NULL, 0,
472                         &(struct ibv_exp_cq_init_attr){
473                                 .comp_mask = 0,
474                         });
475                 if (!fdir_queue->cq) {
476                         ERROR("cannot create flow director CQ");
477                         goto error;
478                 }
479                 fdir_queue->wq = ibv_exp_create_wq(
480                         priv->ctx,
481                         &(struct ibv_exp_wq_init_attr){
482                                 .wq_type = IBV_EXP_WQT_RQ,
483                                 .max_recv_wr = 1,
484                                 .max_recv_sge = 1,
485                                 .pd = priv->pd,
486                                 .cq = fdir_queue->cq,
487                         });
488                 if (!fdir_queue->wq) {
489                         ERROR("cannot create flow director WQ");
490                         goto error;
491                 }
492                 wq = fdir_queue->wq;
493         }
494         fdir_queue->ind_table = ibv_exp_create_rwq_ind_table(
495                 priv->ctx,
496                 &(struct ibv_exp_rwq_ind_table_init_attr){
497                         .pd = priv->pd,
498                         .log_ind_tbl_size = 0,
499                         .ind_tbl = &wq,
500                         .comp_mask = 0,
501                 });
502         if (!fdir_queue->ind_table) {
503                 ERROR("cannot create flow director indirection table");
504                 goto error;
505         }
506         fdir_queue->qp = ibv_exp_create_qp(
507                 priv->ctx,
508                 &(struct ibv_exp_qp_init_attr){
509                         .qp_type = IBV_QPT_RAW_PACKET,
510                         .comp_mask =
511                                 IBV_EXP_QP_INIT_ATTR_PD |
512                                 IBV_EXP_QP_INIT_ATTR_PORT |
513                                 IBV_EXP_QP_INIT_ATTR_RX_HASH,
514                         .pd = priv->pd,
515                         .rx_hash_conf = &(struct ibv_exp_rx_hash_conf){
516                                 .rx_hash_function =
517                                         IBV_EXP_RX_HASH_FUNC_TOEPLITZ,
518                                 .rx_hash_key_len = rss_hash_default_key_len,
519                                 .rx_hash_key = rss_hash_default_key,
520                                 .rx_hash_fields_mask = 0,
521                                 .rwq_ind_tbl = fdir_queue->ind_table,
522                         },
523                         .port_num = priv->port,
524                 });
525         if (!fdir_queue->qp) {
526                 ERROR("cannot create flow director hash RX QP");
527                 goto error;
528         }
529         return fdir_queue;
530 error:
531         assert(fdir_queue);
532         assert(!fdir_queue->qp);
533         if (fdir_queue->ind_table)
534                 claim_zero(ibv_exp_destroy_rwq_ind_table
535                            (fdir_queue->ind_table));
536         if (fdir_queue->wq)
537                 claim_zero(ibv_exp_destroy_wq(fdir_queue->wq));
538         if (fdir_queue->cq)
539                 claim_zero(ibv_destroy_cq(fdir_queue->cq));
540         rte_free(fdir_queue);
541         return NULL;
542 }
543
544 /**
545  * Get flow director queue for a specific RX queue, create it in case
546  * it does not exist.
547  *
548  * @param priv
549  *   Private structure.
550  * @param idx
551  *   RX queue index.
552  *
553  * @return
554  *   Related flow director queue on success, NULL otherwise.
555  */
556 static struct fdir_queue *
557 priv_get_fdir_queue(struct priv *priv, uint16_t idx)
558 {
559         struct rxq_ctrl *rxq_ctrl =
560                 container_of((*priv->rxqs)[idx], struct rxq_ctrl, rxq);
561         struct fdir_queue *fdir_queue = rxq_ctrl->fdir_queue;
562
563         assert(rxq_ctrl->wq);
564         if (fdir_queue == NULL) {
565                 fdir_queue = priv_fdir_queue_create(priv, rxq_ctrl->wq,
566                                                     rxq_ctrl->socket);
567                 rxq_ctrl->fdir_queue = fdir_queue;
568         }
569         return fdir_queue;
570 }
571
572 /**
573  * Get or flow director drop queue. Create it if it does not exist.
574  *
575  * @param priv
576  *   Private structure.
577  *
578  * @return
579  *   Flow director drop queue on success, NULL otherwise.
580  */
581 static struct fdir_queue *
582 priv_get_fdir_drop_queue(struct priv *priv)
583 {
584         struct fdir_queue *fdir_queue = priv->fdir_drop_queue;
585
586         if (fdir_queue == NULL) {
587                 unsigned int socket = SOCKET_ID_ANY;
588
589                 /* Select a known NUMA socket if possible. */
590                 if (priv->rxqs_n && (*priv->rxqs)[0])
591                         socket = container_of((*priv->rxqs)[0],
592                                               struct rxq_ctrl, rxq)->socket;
593                 fdir_queue = priv_fdir_queue_create(priv, NULL, socket);
594                 priv->fdir_drop_queue = fdir_queue;
595         }
596         return fdir_queue;
597 }
598
599 /**
600  * Enable flow director filter and create steering rules.
601  *
602  * @param priv
603  *   Private structure.
604  * @param mlx5_fdir_filter
605  *   Filter to create steering rule for.
606  *
607  * @return
608  *   0 on success, errno value on failure.
609  */
610 static int
611 priv_fdir_filter_enable(struct priv *priv,
612                         struct mlx5_fdir_filter *mlx5_fdir_filter)
613 {
614         struct fdir_queue *fdir_queue;
615
616         /* Check if flow already exists. */
617         if (mlx5_fdir_filter->flow != NULL)
618                 return 0;
619
620         /* Get fdir_queue for specific queue. */
621         if (mlx5_fdir_filter->behavior == RTE_ETH_FDIR_REJECT)
622                 fdir_queue = priv_get_fdir_drop_queue(priv);
623         else
624                 fdir_queue = priv_get_fdir_queue(priv,
625                                                  mlx5_fdir_filter->queue);
626
627         if (fdir_queue == NULL) {
628                 ERROR("failed to create flow director rxq for queue %d",
629                       mlx5_fdir_filter->queue);
630                 return EINVAL;
631         }
632
633         /* Create flow */
634         return priv_fdir_flow_add(priv, mlx5_fdir_filter, fdir_queue);
635 }
636
637 /**
638  * Initialize flow director filters list.
639  *
640  * @param priv
641  *   Private structure.
642  *
643  * @return
644  *   0 on success, errno value on failure.
645  */
646 int
647 fdir_init_filters_list(struct priv *priv)
648 {
649         /* Filter list initialization should be done only once. */
650         if (priv->fdir_filter_list)
651                 return 0;
652
653         /* Create filters list. */
654         priv->fdir_filter_list =
655                 rte_calloc(__func__, 1, sizeof(*priv->fdir_filter_list), 0);
656
657         if (priv->fdir_filter_list == NULL) {
658                 int err = ENOMEM;
659
660                 ERROR("cannot allocate flow director filter list: %s",
661                       strerror(err));
662                 return err;
663         }
664
665         LIST_INIT(priv->fdir_filter_list);
666
667         return 0;
668 }
669
670 /**
671  * Flush all filters.
672  *
673  * @param priv
674  *   Private structure.
675  */
676 static void
677 priv_fdir_filter_flush(struct priv *priv)
678 {
679         struct mlx5_fdir_filter *mlx5_fdir_filter;
680
681         while ((mlx5_fdir_filter = LIST_FIRST(priv->fdir_filter_list))) {
682                 struct ibv_exp_flow *flow = mlx5_fdir_filter->flow;
683
684                 DEBUG("%p: flushing flow director filter %p",
685                       (void *)priv, (void *)mlx5_fdir_filter);
686                 LIST_REMOVE(mlx5_fdir_filter, next);
687                 if (flow != NULL)
688                         claim_zero(ibv_exp_destroy_flow(flow));
689                 rte_free(mlx5_fdir_filter);
690         }
691 }
692
693 /**
694  * Remove all flow director filters and delete list.
695  *
696  * @param priv
697  *   Private structure.
698  */
699 void
700 priv_fdir_delete_filters_list(struct priv *priv)
701 {
702         priv_fdir_filter_flush(priv);
703         rte_free(priv->fdir_filter_list);
704         priv->fdir_filter_list = NULL;
705 }
706
707 /**
708  * Disable flow director, remove all steering rules.
709  *
710  * @param priv
711  *   Private structure.
712  */
713 void
714 priv_fdir_disable(struct priv *priv)
715 {
716         unsigned int i;
717         struct mlx5_fdir_filter *mlx5_fdir_filter;
718
719         /* Run on every flow director filter and destroy flow handle. */
720         LIST_FOREACH(mlx5_fdir_filter, priv->fdir_filter_list, next) {
721                 struct ibv_exp_flow *flow;
722
723                 /* Only valid elements should be in the list */
724                 assert(mlx5_fdir_filter != NULL);
725                 flow = mlx5_fdir_filter->flow;
726
727                 /* Destroy flow handle */
728                 if (flow != NULL) {
729                         claim_zero(ibv_exp_destroy_flow(flow));
730                         mlx5_fdir_filter->flow = NULL;
731                 }
732         }
733
734         /* Destroy flow director context in each RX queue. */
735         for (i = 0; (i != priv->rxqs_n); i++) {
736                 struct rxq_ctrl *rxq_ctrl =
737                         container_of((*priv->rxqs)[i], struct rxq_ctrl, rxq);
738
739                 if (!rxq_ctrl->fdir_queue)
740                         continue;
741                 priv_fdir_queue_destroy(priv, rxq_ctrl->fdir_queue);
742                 rxq_ctrl->fdir_queue = NULL;
743         }
744         if (priv->fdir_drop_queue) {
745                 priv_fdir_queue_destroy(priv, priv->fdir_drop_queue);
746                 priv->fdir_drop_queue = NULL;
747         }
748 }
749
750 /**
751  * Enable flow director, create steering rules.
752  *
753  * @param priv
754  *   Private structure.
755  */
756 void
757 priv_fdir_enable(struct priv *priv)
758 {
759         struct mlx5_fdir_filter *mlx5_fdir_filter;
760
761         /* Run on every fdir filter and create flow handle */
762         LIST_FOREACH(mlx5_fdir_filter, priv->fdir_filter_list, next) {
763                 /* Only valid elements should be in the list */
764                 assert(mlx5_fdir_filter != NULL);
765
766                 priv_fdir_filter_enable(priv, mlx5_fdir_filter);
767         }
768 }
769
770 /**
771  * Find specific filter in list.
772  *
773  * @param priv
774  *   Private structure.
775  * @param fdir_filter
776  *   Flow director filter to find.
777  *
778  * @return
779  *   Filter element if found, otherwise NULL.
780  */
781 static struct mlx5_fdir_filter *
782 priv_find_filter_in_list(struct priv *priv,
783                          const struct rte_eth_fdir_filter *fdir_filter)
784 {
785         struct fdir_flow_desc desc;
786         struct mlx5_fdir_filter *mlx5_fdir_filter;
787         enum rte_fdir_mode fdir_mode = priv->dev->data->dev_conf.fdir_conf.mode;
788
789         /* Get flow director filter to look for. */
790         fdir_filter_to_flow_desc(fdir_filter, &desc, fdir_mode);
791
792         /* Look for the requested element. */
793         LIST_FOREACH(mlx5_fdir_filter, priv->fdir_filter_list, next) {
794                 /* Only valid elements should be in the list. */
795                 assert(mlx5_fdir_filter != NULL);
796
797                 /* Return matching filter. */
798                 if (!memcmp(&desc, &mlx5_fdir_filter->desc, sizeof(desc)))
799                         return mlx5_fdir_filter;
800         }
801
802         /* Filter not found */
803         return NULL;
804 }
805
806 /**
807  * Add new flow director filter and store it in list.
808  *
809  * @param priv
810  *   Private structure.
811  * @param fdir_filter
812  *   Flow director filter to add.
813  *
814  * @return
815  *   0 on success, errno value on failure.
816  */
817 static int
818 priv_fdir_filter_add(struct priv *priv,
819                      const struct rte_eth_fdir_filter *fdir_filter)
820 {
821         struct mlx5_fdir_filter *mlx5_fdir_filter;
822         enum rte_fdir_mode fdir_mode = priv->dev->data->dev_conf.fdir_conf.mode;
823         int err = 0;
824
825         /* Validate queue number. */
826         if (fdir_filter->action.rx_queue >= priv->rxqs_n) {
827                 ERROR("invalid queue number %d", fdir_filter->action.rx_queue);
828                 return EINVAL;
829         }
830
831         /* Duplicate filters are currently unsupported. */
832         mlx5_fdir_filter = priv_find_filter_in_list(priv, fdir_filter);
833         if (mlx5_fdir_filter != NULL) {
834                 ERROR("filter already exists");
835                 return EINVAL;
836         }
837
838         /* Create new flow director filter. */
839         mlx5_fdir_filter =
840                 rte_calloc(__func__, 1, sizeof(*mlx5_fdir_filter), 0);
841         if (mlx5_fdir_filter == NULL) {
842                 err = ENOMEM;
843                 ERROR("cannot allocate flow director filter: %s",
844                       strerror(err));
845                 return err;
846         }
847
848         /* Set action parameters. */
849         mlx5_fdir_filter->queue = fdir_filter->action.rx_queue;
850         mlx5_fdir_filter->behavior = fdir_filter->action.behavior;
851
852         /* Convert to mlx5 filter descriptor. */
853         fdir_filter_to_flow_desc(fdir_filter,
854                                  &mlx5_fdir_filter->desc, fdir_mode);
855
856         /* Insert new filter into list. */
857         LIST_INSERT_HEAD(priv->fdir_filter_list, mlx5_fdir_filter, next);
858
859         DEBUG("%p: flow director filter %p added",
860               (void *)priv, (void *)mlx5_fdir_filter);
861
862         /* Enable filter immediately if device is started. */
863         if (priv->started)
864                 err = priv_fdir_filter_enable(priv, mlx5_fdir_filter);
865
866         return err;
867 }
868
869 /**
870  * Update queue for specific filter.
871  *
872  * @param priv
873  *   Private structure.
874  * @param fdir_filter
875  *   Filter to be updated.
876  *
877  * @return
878  *   0 on success, errno value on failure.
879  */
880 static int
881 priv_fdir_filter_update(struct priv *priv,
882                         const struct rte_eth_fdir_filter *fdir_filter)
883 {
884         struct mlx5_fdir_filter *mlx5_fdir_filter;
885
886         /* Validate queue number. */
887         if (fdir_filter->action.rx_queue >= priv->rxqs_n) {
888                 ERROR("invalid queue number %d", fdir_filter->action.rx_queue);
889                 return EINVAL;
890         }
891
892         mlx5_fdir_filter = priv_find_filter_in_list(priv, fdir_filter);
893         if (mlx5_fdir_filter != NULL) {
894                 struct ibv_exp_flow *flow = mlx5_fdir_filter->flow;
895                 int err = 0;
896
897                 /* Update queue number. */
898                 mlx5_fdir_filter->queue = fdir_filter->action.rx_queue;
899
900                 /* Destroy flow handle. */
901                 if (flow != NULL) {
902                         claim_zero(ibv_exp_destroy_flow(flow));
903                         mlx5_fdir_filter->flow = NULL;
904                 }
905                 DEBUG("%p: flow director filter %p updated",
906                       (void *)priv, (void *)mlx5_fdir_filter);
907
908                 /* Enable filter if device is started. */
909                 if (priv->started)
910                         err = priv_fdir_filter_enable(priv, mlx5_fdir_filter);
911
912                 return err;
913         }
914
915         /* Filter not found, create it. */
916         DEBUG("%p: filter not found for update, creating new filter",
917               (void *)priv);
918         return priv_fdir_filter_add(priv, fdir_filter);
919 }
920
921 /**
922  * Delete specific filter.
923  *
924  * @param priv
925  *   Private structure.
926  * @param fdir_filter
927  *   Filter to be deleted.
928  *
929  * @return
930  *   0 on success, errno value on failure.
931  */
932 static int
933 priv_fdir_filter_delete(struct priv *priv,
934                         const struct rte_eth_fdir_filter *fdir_filter)
935 {
936         struct mlx5_fdir_filter *mlx5_fdir_filter;
937
938         mlx5_fdir_filter = priv_find_filter_in_list(priv, fdir_filter);
939         if (mlx5_fdir_filter != NULL) {
940                 struct ibv_exp_flow *flow = mlx5_fdir_filter->flow;
941
942                 /* Remove element from list. */
943                 LIST_REMOVE(mlx5_fdir_filter, next);
944
945                 /* Destroy flow handle. */
946                 if (flow != NULL) {
947                         claim_zero(ibv_exp_destroy_flow(flow));
948                         mlx5_fdir_filter->flow = NULL;
949                 }
950
951                 DEBUG("%p: flow director filter %p deleted",
952                       (void *)priv, (void *)mlx5_fdir_filter);
953
954                 /* Delete filter. */
955                 rte_free(mlx5_fdir_filter);
956
957                 return 0;
958         }
959
960         ERROR("%p: flow director delete failed, cannot find filter",
961               (void *)priv);
962         return EINVAL;
963 }
964
965 /**
966  * Get flow director information.
967  *
968  * @param priv
969  *   Private structure.
970  * @param[out] fdir_info
971  *   Resulting flow director information.
972  */
973 static void
974 priv_fdir_info_get(struct priv *priv, struct rte_eth_fdir_info *fdir_info)
975 {
976         struct rte_eth_fdir_masks *mask =
977                 &priv->dev->data->dev_conf.fdir_conf.mask;
978
979         fdir_info->mode = priv->dev->data->dev_conf.fdir_conf.mode;
980         fdir_info->guarant_spc = 0;
981
982         rte_memcpy(&fdir_info->mask, mask, sizeof(fdir_info->mask));
983
984         fdir_info->max_flexpayload = 0;
985         fdir_info->flow_types_mask[0] = 0;
986
987         fdir_info->flex_payload_unit = 0;
988         fdir_info->max_flex_payload_segment_num = 0;
989         fdir_info->flex_payload_limit = 0;
990         memset(&fdir_info->flex_conf, 0, sizeof(fdir_info->flex_conf));
991 }
992
993 /**
994  * Deal with flow director operations.
995  *
996  * @param priv
997  *   Pointer to private structure.
998  * @param filter_op
999  *   Operation to perform.
1000  * @param arg
1001  *   Pointer to operation-specific structure.
1002  *
1003  * @return
1004  *   0 on success, errno value on failure.
1005  */
1006 static int
1007 priv_fdir_ctrl_func(struct priv *priv, enum rte_filter_op filter_op, void *arg)
1008 {
1009         enum rte_fdir_mode fdir_mode =
1010                 priv->dev->data->dev_conf.fdir_conf.mode;
1011         int ret = 0;
1012
1013         if (filter_op == RTE_ETH_FILTER_NOP)
1014                 return 0;
1015
1016         if (fdir_mode != RTE_FDIR_MODE_PERFECT &&
1017             fdir_mode != RTE_FDIR_MODE_PERFECT_MAC_VLAN) {
1018                 ERROR("%p: flow director mode %d not supported",
1019                       (void *)priv, fdir_mode);
1020                 return EINVAL;
1021         }
1022
1023         switch (filter_op) {
1024         case RTE_ETH_FILTER_ADD:
1025                 ret = priv_fdir_filter_add(priv, arg);
1026                 break;
1027         case RTE_ETH_FILTER_UPDATE:
1028                 ret = priv_fdir_filter_update(priv, arg);
1029                 break;
1030         case RTE_ETH_FILTER_DELETE:
1031                 ret = priv_fdir_filter_delete(priv, arg);
1032                 break;
1033         case RTE_ETH_FILTER_FLUSH:
1034                 priv_fdir_filter_flush(priv);
1035                 break;
1036         case RTE_ETH_FILTER_INFO:
1037                 priv_fdir_info_get(priv, arg);
1038                 break;
1039         default:
1040                 DEBUG("%p: unknown operation %u", (void *)priv, filter_op);
1041                 ret = EINVAL;
1042                 break;
1043         }
1044         return ret;
1045 }
1046
1047 static const struct rte_flow_ops mlx5_flow_ops = {
1048         .validate = mlx5_flow_validate,
1049         .create = mlx5_flow_create,
1050         .destroy = mlx5_flow_destroy,
1051         .flush = mlx5_flow_flush,
1052         .query = NULL,
1053 };
1054
1055 /**
1056  * Manage filter operations.
1057  *
1058  * @param dev
1059  *   Pointer to Ethernet device structure.
1060  * @param filter_type
1061  *   Filter type.
1062  * @param filter_op
1063  *   Operation to perform.
1064  * @param arg
1065  *   Pointer to operation-specific structure.
1066  *
1067  * @return
1068  *   0 on success, negative errno value on failure.
1069  */
1070 int
1071 mlx5_dev_filter_ctrl(struct rte_eth_dev *dev,
1072                      enum rte_filter_type filter_type,
1073                      enum rte_filter_op filter_op,
1074                      void *arg)
1075 {
1076         int ret = EINVAL;
1077         struct priv *priv = dev->data->dev_private;
1078
1079         switch (filter_type) {
1080         case RTE_ETH_FILTER_GENERIC:
1081                 if (filter_op != RTE_ETH_FILTER_GET)
1082                         return -EINVAL;
1083                 *(const void **)arg = &mlx5_flow_ops;
1084                 return 0;
1085         case RTE_ETH_FILTER_FDIR:
1086                 priv_lock(priv);
1087                 ret = priv_fdir_ctrl_func(priv, filter_op, arg);
1088                 priv_unlock(priv);
1089                 break;
1090         default:
1091                 ERROR("%p: filter type (%d) not supported",
1092                       (void *)dev, filter_type);
1093                 break;
1094         }
1095
1096         return -ret;
1097 }