net/mlx5: fix shared RSS capability check
[dpdk.git] / drivers / net / mlx5 / mlx5_flow_age.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright 2020 Mellanox Technologies, Ltd
3  */
4 #include <mlx5_prm.h>
5 #include <rte_malloc.h>
6 #include <rte_cycles.h>
7 #include <rte_eal_paging.h>
8
9 #include <mlx5_malloc.h>
10 #include <mlx5_common_os.h>
11 #include <mlx5_common_devx.h>
12
13 #include "mlx5.h"
14 #include "mlx5_flow.h"
15
16
17 /**
18  * Destroy Completion Queue used for ASO access.
19  *
20  * @param[in] cq
21  *   ASO CQ to destroy.
22  */
23 static void
24 mlx5_aso_cq_destroy(struct mlx5_aso_cq *cq)
25 {
26         if (cq->cq_obj.cq)
27                 mlx5_devx_cq_destroy(&cq->cq_obj);
28         memset(cq, 0, sizeof(*cq));
29 }
30
31 /**
32  * Create Completion Queue used for ASO access.
33  *
34  * @param[in] ctx
35  *   Context returned from mlx5 open_device() glue function.
36  * @param[in/out] cq
37  *   Pointer to CQ to create.
38  * @param[in] log_desc_n
39  *   Log of number of descriptors in queue.
40  * @param[in] socket
41  *   Socket to use for allocation.
42  * @param[in] uar_page_id
43  *   UAR page ID to use.
44  *
45  * @return
46  *   0 on success, a negative errno value otherwise and rte_errno is set.
47  */
48 static int
49 mlx5_aso_cq_create(void *ctx, struct mlx5_aso_cq *cq, uint16_t log_desc_n,
50                    int socket, int uar_page_id)
51 {
52         struct mlx5_devx_cq_attr attr = {
53                 .uar_page_id = uar_page_id,
54         };
55
56         cq->log_desc_n = log_desc_n;
57         cq->cq_ci = 0;
58         return mlx5_devx_cq_create(ctx, &cq->cq_obj, log_desc_n, &attr, socket);
59 }
60
61 /**
62  * Free MR resources.
63  *
64  * @param[in] mr
65  *   MR to free.
66  */
67 static void
68 mlx5_aso_devx_dereg_mr(struct mlx5_aso_devx_mr *mr)
69 {
70         claim_zero(mlx5_devx_cmd_destroy(mr->mkey));
71         if (!mr->is_indirect && mr->umem)
72                 claim_zero(mlx5_glue->devx_umem_dereg(mr->umem));
73         mlx5_free(mr->buf);
74         memset(mr, 0, sizeof(*mr));
75 }
76
77 /**
78  * Register Memory Region.
79  *
80  * @param[in] ctx
81  *   Context returned from mlx5 open_device() glue function.
82  * @param[in] length
83  *   Size of MR buffer.
84  * @param[in/out] mr
85  *   Pointer to MR to create.
86  * @param[in] socket
87  *   Socket to use for allocation.
88  * @param[in] pdn
89  *   Protection Domain number to use.
90  *
91  * @return
92  *   0 on success, a negative errno value otherwise and rte_errno is set.
93  */
94 static int
95 mlx5_aso_devx_reg_mr(void *ctx, size_t length, struct mlx5_aso_devx_mr *mr,
96                      int socket, int pdn)
97 {
98         struct mlx5_devx_mkey_attr mkey_attr;
99
100         mr->buf = mlx5_malloc(MLX5_MEM_RTE | MLX5_MEM_ZERO, length, 4096,
101                               socket);
102         if (!mr->buf) {
103                 DRV_LOG(ERR, "Failed to create ASO bits mem for MR by Devx.");
104                 return -1;
105         }
106         mr->umem = mlx5_os_umem_reg(ctx, mr->buf, length,
107                                                  IBV_ACCESS_LOCAL_WRITE);
108         if (!mr->umem) {
109                 DRV_LOG(ERR, "Failed to register Umem for MR by Devx.");
110                 goto error;
111         }
112         mkey_attr.addr = (uintptr_t)mr->buf;
113         mkey_attr.size = length;
114         mkey_attr.umem_id = mlx5_os_get_umem_id(mr->umem);
115         mkey_attr.pd = pdn;
116         mkey_attr.pg_access = 1;
117         mkey_attr.klm_array = NULL;
118         mkey_attr.klm_num = 0;
119         mkey_attr.relaxed_ordering_read = 0;
120         mkey_attr.relaxed_ordering_write = 0;
121         mr->mkey = mlx5_devx_cmd_mkey_create(ctx, &mkey_attr);
122         if (!mr->mkey) {
123                 DRV_LOG(ERR, "Failed to create direct Mkey.");
124                 goto error;
125         }
126         mr->length = length;
127         mr->is_indirect = false;
128         return 0;
129 error:
130         if (mr->umem)
131                 claim_zero(mlx5_glue->devx_umem_dereg(mr->umem));
132         mlx5_free(mr->buf);
133         return -1;
134 }
135
136 /**
137  * Destroy Send Queue used for ASO access.
138  *
139  * @param[in] sq
140  *   ASO SQ to destroy.
141  */
142 static void
143 mlx5_aso_destroy_sq(struct mlx5_aso_sq *sq)
144 {
145         mlx5_devx_sq_destroy(&sq->sq_obj);
146         mlx5_aso_cq_destroy(&sq->cq);
147         mlx5_aso_devx_dereg_mr(&sq->mr);
148         memset(sq, 0, sizeof(*sq));
149 }
150
151 /**
152  * Initialize Send Queue used for ASO access.
153  *
154  * @param[in] sq
155  *   ASO SQ to initialize.
156  */
157 static void
158 mlx5_aso_init_sq(struct mlx5_aso_sq *sq)
159 {
160         volatile struct mlx5_aso_wqe *restrict wqe;
161         int i;
162         int size = 1 << sq->log_desc_n;
163         uint64_t addr;
164
165         /* All the next fields state should stay constant. */
166         for (i = 0, wqe = &sq->sq_obj.aso_wqes[0]; i < size; ++i, ++wqe) {
167                 wqe->general_cseg.sq_ds = rte_cpu_to_be_32((sq->sqn << 8) |
168                                                           (sizeof(*wqe) >> 4));
169                 wqe->aso_cseg.lkey = rte_cpu_to_be_32(sq->mr.mkey->id);
170                 addr = (uint64_t)((uint64_t *)sq->mr.buf + i *
171                                             MLX5_ASO_AGE_ACTIONS_PER_POOL / 64);
172                 wqe->aso_cseg.va_h = rte_cpu_to_be_32((uint32_t)(addr >> 32));
173                 wqe->aso_cseg.va_l_r = rte_cpu_to_be_32((uint32_t)addr | 1u);
174                 wqe->aso_cseg.operand_masks = rte_cpu_to_be_32
175                         (0u |
176                          (ASO_OPER_LOGICAL_OR << ASO_CSEG_COND_OPER_OFFSET) |
177                          (ASO_OP_ALWAYS_TRUE << ASO_CSEG_COND_1_OPER_OFFSET) |
178                          (ASO_OP_ALWAYS_TRUE << ASO_CSEG_COND_0_OPER_OFFSET) |
179                          (BYTEWISE_64BYTE << ASO_CSEG_DATA_MASK_MODE_OFFSET));
180                 wqe->aso_cseg.data_mask = RTE_BE64(UINT64_MAX);
181         }
182 }
183
184 /**
185  * Create Send Queue used for ASO access.
186  *
187  * @param[in] ctx
188  *   Context returned from mlx5 open_device() glue function.
189  * @param[in/out] sq
190  *   Pointer to SQ to create.
191  * @param[in] socket
192  *   Socket to use for allocation.
193  * @param[in] uar
194  *   User Access Region object.
195  * @param[in] pdn
196  *   Protection Domain number to use.
197  * @param[in] log_desc_n
198  *   Log of number of descriptors in queue.
199  *
200  * @return
201  *   0 on success, a negative errno value otherwise and rte_errno is set.
202  */
203 static int
204 mlx5_aso_sq_create(void *ctx, struct mlx5_aso_sq *sq, int socket,
205                    void *uar, uint32_t pdn,  uint16_t log_desc_n)
206 {
207         struct mlx5_devx_create_sq_attr attr = {
208                 .user_index = 0xFFFF,
209                 .wq_attr = (struct mlx5_devx_wq_attr){
210                         .pd = pdn,
211                         .uar_page = mlx5_os_get_devx_uar_page_id(uar),
212                 },
213         };
214         struct mlx5_devx_modify_sq_attr modify_attr = {
215                 .state = MLX5_SQC_STATE_RDY,
216         };
217         uint32_t sq_desc_n = 1 << log_desc_n;
218         uint16_t log_wqbb_n;
219         int ret;
220
221         if (mlx5_aso_devx_reg_mr(ctx, (MLX5_ASO_AGE_ACTIONS_PER_POOL / 8) *
222                                  sq_desc_n, &sq->mr, socket, pdn))
223                 return -1;
224         if (mlx5_aso_cq_create(ctx, &sq->cq, log_desc_n, socket,
225                                mlx5_os_get_devx_uar_page_id(uar)))
226                 goto error;
227         sq->log_desc_n = log_desc_n;
228         attr.cqn = sq->cq.cq_obj.cq->id;
229         /* for mlx5_aso_wqe that is twice the size of mlx5_wqe */
230         log_wqbb_n = log_desc_n + 1;
231         ret = mlx5_devx_sq_create(ctx, &sq->sq_obj, log_wqbb_n, &attr, socket);
232         if (ret) {
233                 DRV_LOG(ERR, "Can't create SQ object.");
234                 rte_errno = ENOMEM;
235                 goto error;
236         }
237         ret = mlx5_devx_cmd_modify_sq(sq->sq_obj.sq, &modify_attr);
238         if (ret) {
239                 DRV_LOG(ERR, "Can't change SQ state to ready.");
240                 rte_errno = ENOMEM;
241                 goto error;
242         }
243         sq->pi = 0;
244         sq->head = 0;
245         sq->tail = 0;
246         sq->sqn = sq->sq_obj.sq->id;
247         sq->uar_addr = mlx5_os_get_devx_uar_reg_addr(uar);
248         mlx5_aso_init_sq(sq);
249         return 0;
250 error:
251         mlx5_aso_destroy_sq(sq);
252         return -1;
253 }
254
255 /**
256  * API to create and initialize Send Queue used for ASO access.
257  *
258  * @param[in] sh
259  *   Pointer to shared device context.
260  *
261  * @return
262  *   0 on success, a negative errno value otherwise and rte_errno is set.
263  */
264 int
265 mlx5_aso_queue_init(struct mlx5_dev_ctx_shared *sh)
266 {
267         return mlx5_aso_sq_create(sh->ctx, &sh->aso_age_mng->aso_sq, 0,
268                                   sh->tx_uar, sh->pdn, MLX5_ASO_QUEUE_LOG_DESC);
269 }
270
271 /**
272  * API to destroy Send Queue used for ASO access.
273  *
274  * @param[in] sh
275  *   Pointer to shared device context.
276  */
277 void
278 mlx5_aso_queue_uninit(struct mlx5_dev_ctx_shared *sh)
279 {
280         mlx5_aso_destroy_sq(&sh->aso_age_mng->aso_sq);
281 }
282
283 /**
284  * Write a burst of WQEs to ASO SQ.
285  *
286  * @param[in] mng
287  *   ASO management data, contains the SQ.
288  * @param[in] n
289  *   Index of the last valid pool.
290  *
291  * @return
292  *   Number of WQEs in burst.
293  */
294 static uint16_t
295 mlx5_aso_sq_enqueue_burst(struct mlx5_aso_age_mng *mng, uint16_t n)
296 {
297         volatile struct mlx5_aso_wqe *wqe;
298         struct mlx5_aso_sq *sq = &mng->aso_sq;
299         struct mlx5_aso_age_pool *pool;
300         uint16_t size = 1 << sq->log_desc_n;
301         uint16_t mask = size - 1;
302         uint16_t max;
303         uint16_t start_head = sq->head;
304
305         max = RTE_MIN(size - (uint16_t)(sq->head - sq->tail), n - sq->next);
306         if (unlikely(!max))
307                 return 0;
308         sq->elts[start_head & mask].burst_size = max;
309         do {
310                 wqe = &sq->sq_obj.aso_wqes[sq->head & mask];
311                 rte_prefetch0(&sq->sq_obj.aso_wqes[(sq->head + 1) & mask]);
312                 /* Fill next WQE. */
313                 rte_spinlock_lock(&mng->resize_sl);
314                 pool = mng->pools[sq->next];
315                 rte_spinlock_unlock(&mng->resize_sl);
316                 sq->elts[sq->head & mask].pool = pool;
317                 wqe->general_cseg.misc =
318                                 rte_cpu_to_be_32(((struct mlx5_devx_obj *)
319                                                  (pool->flow_hit_aso_obj))->id);
320                 wqe->general_cseg.flags = RTE_BE32(MLX5_COMP_ONLY_FIRST_ERR <<
321                                                          MLX5_COMP_MODE_OFFSET);
322                 wqe->general_cseg.opcode = rte_cpu_to_be_32
323                                                 (MLX5_OPCODE_ACCESS_ASO |
324                                                  (ASO_OPC_MOD_FLOW_HIT <<
325                                                   WQE_CSEG_OPC_MOD_OFFSET) |
326                                                  (sq->pi <<
327                                                   WQE_CSEG_WQE_INDEX_OFFSET));
328                 sq->pi += 2; /* Each WQE contains 2 WQEBB's. */
329                 sq->head++;
330                 sq->next++;
331                 max--;
332         } while (max);
333         wqe->general_cseg.flags = RTE_BE32(MLX5_COMP_ALWAYS <<
334                                                          MLX5_COMP_MODE_OFFSET);
335         rte_io_wmb();
336         sq->sq_obj.db_rec[MLX5_SND_DBR] = rte_cpu_to_be_32(sq->pi);
337         rte_wmb();
338         *sq->uar_addr = *(volatile uint64_t *)wqe; /* Assume 64 bit ARCH.*/
339         rte_wmb();
340         return sq->elts[start_head & mask].burst_size;
341 }
342
343 /**
344  * Debug utility function. Dump contents of error CQE and WQE.
345  *
346  * @param[in] cqe
347  *   Error CQE to dump.
348  * @param[in] wqe
349  *   Error WQE to dump.
350  */
351 static void
352 mlx5_aso_dump_err_objs(volatile uint32_t *cqe, volatile uint32_t *wqe)
353 {
354         int i;
355
356         DRV_LOG(ERR, "Error cqe:");
357         for (i = 0; i < 16; i += 4)
358                 DRV_LOG(ERR, "%08X %08X %08X %08X", cqe[i], cqe[i + 1],
359                         cqe[i + 2], cqe[i + 3]);
360         DRV_LOG(ERR, "\nError wqe:");
361         for (i = 0; i < (int)sizeof(struct mlx5_aso_wqe) / 4; i += 4)
362                 DRV_LOG(ERR, "%08X %08X %08X %08X", wqe[i], wqe[i + 1],
363                         wqe[i + 2], wqe[i + 3]);
364 }
365
366 /**
367  * Handle case of error CQE.
368  *
369  * @param[in] sq
370  *   ASO SQ to use.
371  */
372 static void
373 mlx5_aso_cqe_err_handle(struct mlx5_aso_sq *sq)
374 {
375         struct mlx5_aso_cq *cq = &sq->cq;
376         uint32_t idx = cq->cq_ci & ((1 << cq->log_desc_n) - 1);
377         volatile struct mlx5_err_cqe *cqe =
378                         (volatile struct mlx5_err_cqe *)&cq->cq_obj.cqes[idx];
379
380         cq->errors++;
381         idx = rte_be_to_cpu_16(cqe->wqe_counter) & (1u << sq->log_desc_n);
382         mlx5_aso_dump_err_objs((volatile uint32_t *)cqe,
383                                (volatile uint32_t *)&sq->sq_obj.aso_wqes[idx]);
384 }
385
386 /**
387  * Update ASO objects upon completion.
388  *
389  * @param[in] sh
390  *   Shared device context.
391  * @param[in] n
392  *   Number of completed ASO objects.
393  */
394 static void
395 mlx5_aso_age_action_update(struct mlx5_dev_ctx_shared *sh, uint16_t n)
396 {
397         struct mlx5_aso_age_mng *mng = sh->aso_age_mng;
398         struct mlx5_aso_sq *sq = &mng->aso_sq;
399         struct mlx5_age_info *age_info;
400         const uint16_t size = 1 << sq->log_desc_n;
401         const uint16_t mask = size - 1;
402         const uint64_t curr = MLX5_CURR_TIME_SEC;
403         uint16_t expected = AGE_CANDIDATE;
404         uint16_t i;
405
406         for (i = 0; i < n; ++i) {
407                 uint16_t idx = (sq->tail + i) & mask;
408                 struct mlx5_aso_age_pool *pool = sq->elts[idx].pool;
409                 uint64_t diff = curr - pool->time_of_last_age_check;
410                 uint64_t *addr = sq->mr.buf;
411                 int j;
412
413                 addr += idx * MLX5_ASO_AGE_ACTIONS_PER_POOL / 64;
414                 pool->time_of_last_age_check = curr;
415                 for (j = 0; j < MLX5_ASO_AGE_ACTIONS_PER_POOL; j++) {
416                         struct mlx5_aso_age_action *act = &pool->actions[j];
417                         struct mlx5_age_param *ap = &act->age_params;
418                         uint8_t byte;
419                         uint8_t offset;
420                         uint8_t *u8addr;
421                         uint8_t hit;
422
423                         if (__atomic_load_n(&ap->state, __ATOMIC_RELAXED) !=
424                                             AGE_CANDIDATE)
425                                 continue;
426                         byte = 63 - (j / 8);
427                         offset = j % 8;
428                         u8addr = (uint8_t *)addr;
429                         hit = (u8addr[byte] >> offset) & 0x1;
430                         if (hit) {
431                                 __atomic_store_n(&ap->sec_since_last_hit, 0,
432                                                  __ATOMIC_RELAXED);
433                         } else {
434                                 struct mlx5_priv *priv;
435
436                                 __atomic_fetch_add(&ap->sec_since_last_hit,
437                                                    diff, __ATOMIC_RELAXED);
438                                 /* If timeout passed add to aged-out list. */
439                                 if (ap->sec_since_last_hit <= ap->timeout)
440                                         continue;
441                                 priv =
442                                 rte_eth_devices[ap->port_id].data->dev_private;
443                                 age_info = GET_PORT_AGE_INFO(priv);
444                                 rte_spinlock_lock(&age_info->aged_sl);
445                                 if (__atomic_compare_exchange_n(&ap->state,
446                                                                 &expected,
447                                                                 AGE_TMOUT,
448                                                                 false,
449                                                                __ATOMIC_RELAXED,
450                                                             __ATOMIC_RELAXED)) {
451                                         LIST_INSERT_HEAD(&age_info->aged_aso,
452                                                          act, next);
453                                         MLX5_AGE_SET(age_info,
454                                                      MLX5_AGE_EVENT_NEW);
455                                 }
456                                 rte_spinlock_unlock(&age_info->aged_sl);
457                         }
458                 }
459         }
460         mlx5_age_event_prepare(sh);
461 }
462
463 /**
464  * Handle completions from WQEs sent to ASO SQ.
465  *
466  * @param[in] sh
467  *   Shared device context.
468  *
469  * @return
470  *   Number of CQEs handled.
471  */
472 static uint16_t
473 mlx5_aso_completion_handle(struct mlx5_dev_ctx_shared *sh)
474 {
475         struct mlx5_aso_age_mng *mng = sh->aso_age_mng;
476         struct mlx5_aso_sq *sq = &mng->aso_sq;
477         struct mlx5_aso_cq *cq = &sq->cq;
478         volatile struct mlx5_cqe *restrict cqe;
479         const unsigned int cq_size = 1 << cq->log_desc_n;
480         const unsigned int mask = cq_size - 1;
481         uint32_t idx;
482         uint32_t next_idx = cq->cq_ci & mask;
483         const uint16_t max = (uint16_t)(sq->head - sq->tail);
484         uint16_t i = 0;
485         int ret;
486         if (unlikely(!max))
487                 return 0;
488         do {
489                 idx = next_idx;
490                 next_idx = (cq->cq_ci + 1) & mask;
491                 rte_prefetch0(&cq->cq_obj.cqes[next_idx]);
492                 cqe = &cq->cq_obj.cqes[idx];
493                 ret = check_cqe(cqe, cq_size, cq->cq_ci);
494                 /*
495                  * Be sure owner read is done before any other cookie field or
496                  * opaque field.
497                  */
498                 rte_io_rmb();
499                 if (unlikely(ret != MLX5_CQE_STATUS_SW_OWN)) {
500                         if (likely(ret == MLX5_CQE_STATUS_HW_OWN))
501                                 break;
502                         mlx5_aso_cqe_err_handle(sq);
503                 } else {
504                         i += sq->elts[(sq->tail + i) & mask].burst_size;
505                 }
506                 cq->cq_ci++;
507         } while (1);
508         if (likely(i)) {
509                 mlx5_aso_age_action_update(sh, i);
510                 sq->tail += i;
511                 rte_io_wmb();
512                 cq->cq_obj.db_rec[0] = rte_cpu_to_be_32(cq->cq_ci);
513         }
514         return i;
515 }
516
517 /**
518  * Periodically read CQEs and send WQEs to ASO SQ.
519  *
520  * @param[in] arg
521  *   Shared device context containing the ASO SQ.
522  */
523 static void
524 mlx5_flow_aso_alarm(void *arg)
525 {
526         struct mlx5_dev_ctx_shared *sh = arg;
527         struct mlx5_aso_sq *sq = &sh->aso_age_mng->aso_sq;
528         uint32_t us = 100u;
529         uint16_t n;
530
531         rte_spinlock_lock(&sh->aso_age_mng->resize_sl);
532         n = sh->aso_age_mng->next;
533         rte_spinlock_unlock(&sh->aso_age_mng->resize_sl);
534         mlx5_aso_completion_handle(sh);
535         if (sq->next == n) {
536                 /* End of loop: wait 1 second. */
537                 us = US_PER_S;
538                 sq->next = 0;
539         }
540         mlx5_aso_sq_enqueue_burst(sh->aso_age_mng, n);
541         if (rte_eal_alarm_set(us, mlx5_flow_aso_alarm, sh))
542                 DRV_LOG(ERR, "Cannot reinitialize aso alarm.");
543 }
544
545 /**
546  * API to start ASO access using ASO SQ.
547  *
548  * @param[in] sh
549  *   Pointer to shared device context.
550  *
551  * @return
552  *   0 on success, a negative errno value otherwise and rte_errno is set.
553  */
554 int
555 mlx5_aso_queue_start(struct mlx5_dev_ctx_shared *sh)
556 {
557         if (rte_eal_alarm_set(US_PER_S, mlx5_flow_aso_alarm, sh)) {
558                 DRV_LOG(ERR, "Cannot reinitialize ASO age alarm.");
559                 return -rte_errno;
560         }
561         return 0;
562 }
563
564 /**
565  * API to stop ASO access using ASO SQ.
566  *
567  * @param[in] sh
568  *   Pointer to shared device context.
569  *
570  * @return
571  *   0 on success, a negative errno value otherwise and rte_errno is set.
572  */
573 int
574 mlx5_aso_queue_stop(struct mlx5_dev_ctx_shared *sh)
575 {
576         int retries = 1024;
577
578         if (!sh->aso_age_mng->aso_sq.sq_obj.sq)
579                 return -EINVAL;
580         rte_errno = 0;
581         while (--retries) {
582                 rte_eal_alarm_cancel(mlx5_flow_aso_alarm, sh);
583                 if (rte_errno != EINPROGRESS)
584                         break;
585                 rte_pause();
586         }
587         return -rte_errno;
588 }