1 /* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright (c) 2022 NVIDIA Corporation & Affiliates
3 */
4
5 #include <rte_flow.h>
6
7 #include <mlx5_malloc.h>
8 #include <stdint.h>
9
10 #include "generic/rte_byteorder.h"
11 #include "mlx5.h"
12 #include "mlx5_flow.h"
13 #include "rte_pmd_mlx5.h"
14
15 #if defined(HAVE_IBV_FLOW_DV_SUPPORT) || !defined(HAVE_INFINIBAND_VERBS_H)
16
17 #define MAX_GENEVE_OPTION_DATA_SIZE 32
18 #define MAX_GENEVE_OPTION_TOTAL_DATA_SIZE \
19 (MAX_GENEVE_OPTION_DATA_SIZE * MAX_GENEVE_OPTIONS_RESOURCES)
20
21 #define INVALID_SAMPLE_ID (UINT8_MAX)
22
23 /**
24 * Single DW inside GENEVE TLV option.
25 */
26 struct mlx5_geneve_tlv_resource {
27 struct mlx5_devx_obj *obj; /* FW object returned in parser creation. */
28 uint32_t modify_field; /* Modify field ID for this DW. */
29 uint8_t offset; /* Offset used in obj creation, from option start. */
30 };
31
32 /**
33 * Single GENEVE TLV option context.
34 * May include some FW objects for different DWs in same option.
35 */
36 struct mlx5_geneve_tlv_option {
37 uint8_t type;
38 uint16_t class;
39 uint8_t class_mode;
40 struct mlx5_hl_data match_data[MAX_GENEVE_OPTION_DATA_SIZE];
41 uint32_t match_data_size;
42 struct mlx5_hl_data hl_ok_bit;
43 struct mlx5_geneve_tlv_resource resources[MAX_GENEVE_OPTIONS_RESOURCES];
44 RTE_ATOMIC(uint32_t) refcnt;
45 };
46
47 /**
48 * List of GENEVE TLV options.
49 */
50 struct mlx5_geneve_tlv_options {
51 /* List of configured GENEVE TLV options. */
52 struct mlx5_geneve_tlv_option options[MAX_GENEVE_OPTIONS_RESOURCES];
53 /*
54 * Copy of list given in parser creation, use to compare with new
55 * configuration.
56 */
57 struct rte_pmd_mlx5_geneve_tlv spec[MAX_GENEVE_OPTIONS_RESOURCES];
58 rte_be32_t buffer[MAX_GENEVE_OPTION_TOTAL_DATA_SIZE];
59 uint8_t nb_options; /* Number entries in above lists. */
60 RTE_ATOMIC(uint32_t) refcnt;
61 };
62
63 /**
64 * Check if type and class is matching to given GENEVE TLV option.
65 *
66 * @param type
67 * GENEVE option type.
68 * @param class
69 * GENEVE option class.
70 * @param option
71 * Pointer to GENEVE TLV option structure.
72 *
73 * @return
74 * True if this type and class match to this option, false otherwise.
75 */
76 static inline bool
option_match_type_and_class(uint8_t type,uint16_t class,struct mlx5_geneve_tlv_option * option)77 option_match_type_and_class(uint8_t type, uint16_t class,
78 struct mlx5_geneve_tlv_option *option)
79 {
80 if (type != option->type)
81 return false;
82 if (option->class_mode == 1 && option->class != class)
83 return false;
84 return true;
85 }
86
87 /**
88 * Get GENEVE TLV option matching to given type and class.
89 *
90 * @param priv
91 * Pointer to port's private data.
92 * @param type
93 * GENEVE option type.
94 * @param class
95 * GENEVE option class.
96 *
97 * @return
98 * Pointer to option structure if exist, NULL otherwise and rte_errno is set.
99 */
100 static struct mlx5_geneve_tlv_option *
mlx5_geneve_tlv_option_get(const struct mlx5_priv * priv,uint8_t type,uint16_t class)101 mlx5_geneve_tlv_option_get(const struct mlx5_priv *priv, uint8_t type,
102 uint16_t class)
103 {
104 struct mlx5_geneve_tlv_options *options;
105 uint8_t i;
106
107 if (priv->tlv_options == NULL) {
108 DRV_LOG(ERR,
109 "Port %u doesn't have configured GENEVE TLV options.",
110 priv->dev_data->port_id);
111 rte_errno = EINVAL;
112 return NULL;
113 }
114 options = priv->tlv_options;
115 MLX5_ASSERT(options != NULL);
116 for (i = 0; i < options->nb_options; ++i) {
117 struct mlx5_geneve_tlv_option *option = &options->options[i];
118
119 if (option_match_type_and_class(type, class, option))
120 return option;
121 }
122 DRV_LOG(ERR, "TLV option type %u class %u doesn't exist.", type, class);
123 rte_errno = ENOENT;
124 return NULL;
125 }
126
127 int
mlx5_get_geneve_hl_data(const void * dr_ctx,uint8_t type,uint16_t class,struct mlx5_hl_data ** const hl_ok_bit,uint8_t * num_of_dws,struct mlx5_hl_data ** const hl_dws,bool * ok_bit_on_class)128 mlx5_get_geneve_hl_data(const void *dr_ctx, uint8_t type, uint16_t class,
129 struct mlx5_hl_data ** const hl_ok_bit,
130 uint8_t *num_of_dws,
131 struct mlx5_hl_data ** const hl_dws,
132 bool *ok_bit_on_class)
133 {
134 uint16_t port_id;
135
136 MLX5_ETH_FOREACH_DEV(port_id, NULL) {
137 struct mlx5_priv *priv;
138 struct mlx5_geneve_tlv_option *option;
139
140 priv = rte_eth_devices[port_id].data->dev_private;
141 if (priv->dr_ctx != dr_ctx)
142 continue;
143 /* Find specific option inside list. */
144 option = mlx5_geneve_tlv_option_get(priv, type, class);
145 if (option == NULL)
146 return -rte_errno;
147 *hl_ok_bit = &option->hl_ok_bit;
148 *hl_dws = option->match_data;
149 *num_of_dws = option->match_data_size;
150 *ok_bit_on_class = !!(option->class_mode == 1);
151 return 0;
152 }
153 DRV_LOG(ERR, "DR CTX %p doesn't belong to any DPDK port.", dr_ctx);
154 return -EINVAL;
155 }
156
157 /**
158 * Calculate total data size.
159 *
160 * @param[in] priv
161 * Pointer to port's private data.
162 * @param[in] geneve_opt
163 * Pointer to GENEVE option item structure.
164 * @param[out] error
165 * Pointer to error structure.
166 *
167 * @return
168 * 0 on success, a negative errno value otherwise and rte_errno is set.
169 */
170 int
mlx5_flow_geneve_tlv_option_validate(struct mlx5_priv * priv,const struct rte_flow_item * geneve_opt,struct rte_flow_error * error)171 mlx5_flow_geneve_tlv_option_validate(struct mlx5_priv *priv,
172 const struct rte_flow_item *geneve_opt,
173 struct rte_flow_error *error)
174 {
175 const struct rte_flow_item_geneve_opt *spec = geneve_opt->spec;
176 const struct rte_flow_item_geneve_opt *mask = geneve_opt->mask;
177 struct mlx5_geneve_tlv_option *option;
178
179 option = mlx5_geneve_tlv_option_get(priv, spec->option_type, spec->option_class);
180 if (option == NULL)
181 return rte_flow_error_set(error, rte_errno,
182 RTE_FLOW_ERROR_TYPE_ITEM, NULL,
183 "Unregistered GENEVE option");
184 if (mask->option_type != UINT8_MAX)
185 return rte_flow_error_set(error, EINVAL,
186 RTE_FLOW_ERROR_TYPE_ITEM, NULL,
187 "GENEVE option type must be fully masked");
188 if (option->class_mode == 1 && mask->option_class != UINT16_MAX)
189 return rte_flow_error_set(error, EINVAL,
190 RTE_FLOW_ERROR_TYPE_ITEM, NULL,
191 "GENEVE option class must be fully masked");
192 return 0;
193 }
194
195 /**
196 * Register single GENEVE TLV option as used by pattern template.
197 *
198 * @param[in] priv
199 * Pointer to port's private data.
200 * @param[in] spec
201 * Pointer to GENEVE option item structure.
202 * @param[out] mng
203 * Pointer to GENEVE option manager.
204 *
205 * @return
206 * 0 on success, a negative errno value otherwise and rte_errno is set.
207 */
208 int
mlx5_geneve_tlv_option_register(struct mlx5_priv * priv,const struct rte_flow_item_geneve_opt * spec,struct mlx5_geneve_tlv_options_mng * mng)209 mlx5_geneve_tlv_option_register(struct mlx5_priv *priv,
210 const struct rte_flow_item_geneve_opt *spec,
211 struct mlx5_geneve_tlv_options_mng *mng)
212 {
213 struct mlx5_geneve_tlv_option *option;
214
215 option = mlx5_geneve_tlv_option_get(priv, spec->option_type, spec->option_class);
216 if (option == NULL)
217 return -rte_errno;
218 /* Increase the option reference counter. */
219 rte_atomic_fetch_add_explicit(&option->refcnt, 1,
220 rte_memory_order_relaxed);
221 /* Update the manager with option information. */
222 mng->options[mng->nb_options].opt_type = spec->option_type;
223 mng->options[mng->nb_options].opt_class = spec->option_class;
224 mng->nb_options++;
225 return 0;
226 }
227
228 /**
229 * Unregister all GENEVE TLV options used by pattern template.
230 *
231 * @param[in] priv
232 * Pointer to port's private data.
233 * @param[in] mng
234 * Pointer to GENEVE option manager.
235 */
236 void
mlx5_geneve_tlv_options_unregister(struct mlx5_priv * priv,struct mlx5_geneve_tlv_options_mng * mng)237 mlx5_geneve_tlv_options_unregister(struct mlx5_priv *priv,
238 struct mlx5_geneve_tlv_options_mng *mng)
239 {
240 struct mlx5_geneve_tlv_option *option;
241 uint8_t i;
242
243 for (i = 0; i < mng->nb_options; ++i) {
244 option = mlx5_geneve_tlv_option_get(priv,
245 mng->options[i].opt_type,
246 mng->options[i].opt_class);
247 MLX5_ASSERT(option != NULL);
248 /* Decrease the option reference counter. */
249 rte_atomic_fetch_sub_explicit(&option->refcnt, 1,
250 rte_memory_order_relaxed);
251 mng->options[i].opt_type = 0;
252 mng->options[i].opt_class = 0;
253 }
254 mng->nb_options = 0;
255 }
256
257 /**
258 * Get single DW resource from given option.
259 *
260 * @param option
261 * Pointer to single GENEVE TLV option.
262 * @param offset
263 * Offset of DW related to option start.
264 *
265 * @return
266 * DW resource on success, NULL otherwise and rte_errno is set.
267 */
268 static struct mlx5_geneve_tlv_resource *
mlx5_geneve_tlv_option_get_resource_by_offset(struct mlx5_geneve_tlv_option * option,uint8_t offset)269 mlx5_geneve_tlv_option_get_resource_by_offset(struct mlx5_geneve_tlv_option *option,
270 uint8_t offset)
271 {
272 uint8_t i;
273
274 for (i = 0; option->resources[i].obj != NULL; ++i) {
275 if (option->resources[i].offset < offset)
276 continue;
277 if (option->resources[i].offset == offset)
278 return &option->resources[i];
279 break;
280 }
281 DRV_LOG(ERR, "The DW in offset %u wasn't configured.", offset);
282 rte_errno = EINVAL;
283 return NULL;
284 }
285
286 int
mlx5_get_geneve_option_modify_field_id(const void * dr_ctx,uint8_t type,uint16_t class,uint8_t dw_offset)287 mlx5_get_geneve_option_modify_field_id(const void *dr_ctx, uint8_t type,
288 uint16_t class, uint8_t dw_offset)
289 {
290 uint16_t port_id;
291
292 MLX5_ETH_FOREACH_DEV(port_id, NULL) {
293 struct mlx5_priv *priv;
294 struct mlx5_geneve_tlv_option *option;
295 struct mlx5_geneve_tlv_resource *resource;
296
297 priv = rte_eth_devices[port_id].data->dev_private;
298 if (priv->dr_ctx != dr_ctx)
299 continue;
300 /* Find specific option inside list. */
301 option = mlx5_geneve_tlv_option_get(priv, type, class);
302 if (option == NULL)
303 return -rte_errno;
304 /* Find specific FW object inside option resources. */
305 resource = mlx5_geneve_tlv_option_get_resource_by_offset(option,
306 dw_offset);
307 if (resource == NULL)
308 return -rte_errno;
309 return resource->modify_field;
310 }
311 DRV_LOG(ERR, "DR CTX %p doesn't belong to any DPDK port.", dr_ctx);
312 rte_errno = EINVAL;
313 return -rte_errno;
314 }
315
316 /**
317 * Get modify field ID for single DW inside configured GENEVE TLV option.
318 *
319 * @param[in] priv
320 * Pointer to port's private data.
321 * @param[in] data
322 * Pointer to modify field data structure.
323 *
324 * @return
325 * Modify field ID on success, negative errno otherwise and rte_errno is set.
326 */
327 int
mlx5_geneve_opt_modi_field_get(struct mlx5_priv * priv,const struct rte_flow_field_data * data)328 mlx5_geneve_opt_modi_field_get(struct mlx5_priv *priv,
329 const struct rte_flow_field_data *data)
330 {
331 uint16_t class = data->class_id;
332 uint8_t type = data->type;
333 struct mlx5_geneve_tlv_option *option;
334 struct mlx5_geneve_tlv_resource *resource;
335 uint8_t offset;
336
337 option = mlx5_geneve_tlv_option_get(priv, type, class);
338 if (option == NULL)
339 return -rte_errno;
340 switch (data->field) {
341 case RTE_FLOW_FIELD_GENEVE_OPT_TYPE:
342 case RTE_FLOW_FIELD_GENEVE_OPT_CLASS:
343 if (!option->match_data[0].dw_mask) {
344 DRV_LOG(ERR, "DW0 isn't configured");
345 rte_errno = EINVAL;
346 return -rte_errno;
347 }
348 resource = &option->resources[0];
349 MLX5_ASSERT(resource->offset == 0);
350 break;
351 case RTE_FLOW_FIELD_GENEVE_OPT_DATA:
352 /*
353 * Convert offset twice:
354 * - First conversion from bit offset to DW offset.
355 * - Second conversion is to be related to data start instead
356 * of option start.
357 */
358 offset = (data->offset >> 5) + 1;
359 resource = mlx5_geneve_tlv_option_get_resource_by_offset(option,
360 offset);
361 break;
362 default:
363 DRV_LOG(ERR,
364 "Field ID %u doesn't describe GENEVE option header.",
365 data->field);
366 rte_errno = EINVAL;
367 return -rte_errno;
368 }
369 if (resource == NULL)
370 return -rte_errno;
371 return resource->modify_field;
372 }
373
374 /**
375 * Create single GENEVE TLV option sample.
376 *
377 * @param ctx
378 * Context returned from mlx5 open_device() glue function.
379 * @param attr
380 * Pointer to GENEVE TLV option attributes structure.
381 * @param query_attr
382 * Pointer to match sample info attributes structure.
383 * @param match_data
384 * Pointer to header layout structure to update.
385 * @param resource
386 * Pointer to single sample context to fill.
387 * @param sample_id
388 * The flex parser id for single DW or UINT8_MAX for multiple DWs.
389 *
390 * @return
391 * 0 on success, a negative errno otherwise and rte_errno is set.
392 */
393 static int
mlx5_geneve_tlv_option_create_sample(void * ctx,struct mlx5_devx_geneve_tlv_option_attr * attr,struct mlx5_devx_match_sample_info_query_attr * query_attr,struct mlx5_hl_data * match_data,struct mlx5_geneve_tlv_resource * resource,uint8_t sample_id)394 mlx5_geneve_tlv_option_create_sample(void *ctx,
395 struct mlx5_devx_geneve_tlv_option_attr *attr,
396 struct mlx5_devx_match_sample_info_query_attr *query_attr,
397 struct mlx5_hl_data *match_data,
398 struct mlx5_geneve_tlv_resource *resource, uint8_t sample_id)
399 {
400 struct mlx5_devx_obj *obj;
401 int ret;
402
403 obj = mlx5_devx_cmd_create_geneve_tlv_option(ctx, attr);
404 if (obj == NULL)
405 return -rte_errno;
406 if (sample_id == INVALID_SAMPLE_ID)
407 ret = mlx5_devx_cmd_query_geneve_tlv_option(ctx, obj, query_attr);
408 else
409 ret = mlx5_devx_cmd_match_sample_info_query(ctx, sample_id, query_attr);
410 if (ret) {
411 claim_zero(mlx5_devx_cmd_destroy(obj));
412 return ret;
413 }
414 resource->obj = obj;
415 resource->offset = attr->sample_offset;
416 resource->modify_field = query_attr->modify_field_id;
417 match_data->dw_offset = query_attr->sample_dw_data;
418 match_data->dw_mask = 0xffffffff;
419 return 0;
420 }
421
422 /**
423 * Destroy single GENEVE TLV option sample.
424 *
425 * @param resource
426 * Pointer to single sample context to clean.
427 */
428 static void
mlx5_geneve_tlv_option_destroy_sample(struct mlx5_geneve_tlv_resource * resource)429 mlx5_geneve_tlv_option_destroy_sample(struct mlx5_geneve_tlv_resource *resource)
430 {
431 claim_zero(mlx5_devx_cmd_destroy(resource->obj));
432 resource->obj = NULL;
433 }
434
435 /*
436 * Sample for DW0 are created when one of two conditions is met:
437 * 1. Header is matchable.
438 * 2. This option doesn't configure any data DW.
439 */
440 static bool
should_configure_sample_for_dw0(const struct rte_pmd_mlx5_geneve_tlv * spec)441 should_configure_sample_for_dw0(const struct rte_pmd_mlx5_geneve_tlv *spec)
442 {
443 uint8_t i;
444
445 if (spec->match_on_class_mode == 2)
446 return true;
447 for (i = 0; i < spec->sample_len; ++i)
448 if (spec->match_data_mask[i] != 0)
449 return false;
450 return true;
451 }
452
453 /**
454 * Create single GENEVE TLV option.
455 *
456 * @param ctx
457 * Context returned from mlx5 open_device() glue function.
458 * @param spec
459 * Pointer to user configuration.
460 * @param option
461 * Pointer to single GENEVE TLV option to fill.
462 * @param sample_id
463 * The flex parser id for single DW or UINT8_MAX for multiple DWs.
464 *
465 * @return
466 * 0 on success, a negative errno otherwise and rte_errno is set.
467 */
468 static int
mlx5_geneve_tlv_option_create(void * ctx,const struct rte_pmd_mlx5_geneve_tlv * spec,struct mlx5_geneve_tlv_option * option,uint8_t sample_id)469 mlx5_geneve_tlv_option_create(void *ctx, const struct rte_pmd_mlx5_geneve_tlv *spec,
470 struct mlx5_geneve_tlv_option *option, uint8_t sample_id)
471 {
472 struct mlx5_devx_geneve_tlv_option_attr attr = {
473 .option_class = spec->option_class,
474 .option_type = spec->option_type,
475 .option_data_len = spec->option_len,
476 .option_class_ignore = spec->match_on_class_mode == 1 ? 0 : 1,
477 .offset_valid = sample_id == INVALID_SAMPLE_ID ? 1 : 0,
478 };
479 struct mlx5_devx_match_sample_info_query_attr query_attr = {0};
480 struct mlx5_geneve_tlv_resource *resource;
481 uint8_t i, resource_id = 0;
482 int ret;
483
484 if (should_configure_sample_for_dw0(spec)) {
485 MLX5_ASSERT(sample_id == INVALID_SAMPLE_ID);
486 attr.sample_offset = 0;
487 resource = &option->resources[resource_id];
488 ret = mlx5_geneve_tlv_option_create_sample(ctx, &attr,
489 &query_attr,
490 &option->match_data[0],
491 resource,
492 INVALID_SAMPLE_ID);
493 if (ret)
494 return ret;
495 resource_id++;
496 }
497 /*
498 * Create FW object for each DW request by user.
499 * Starting from 1 since FW offset starts from header.
500 */
501 for (i = 1; i <= spec->sample_len; ++i) {
502 if (spec->match_data_mask[i - 1] == 0)
503 continue;
504 /* offset of data + offset inside data = specific DW offset. */
505 attr.sample_offset = spec->offset + i;
506 resource = &option->resources[resource_id];
507 ret = mlx5_geneve_tlv_option_create_sample(ctx, &attr,
508 &query_attr,
509 &option->match_data[i],
510 resource,
511 sample_id);
512 if (ret)
513 goto error;
514 resource_id++;
515 }
516 /*
517 * Update the OK bit information according to last query.
518 * It should be same for each query under same option.
519 */
520 option->hl_ok_bit.dw_offset = query_attr.sample_dw_ok_bit;
521 option->hl_ok_bit.dw_mask = 1 << query_attr.sample_dw_ok_bit_offset;
522 option->match_data_size = spec->sample_len + 1;
523 option->type = spec->option_type;
524 option->class = spec->option_class;
525 option->class_mode = spec->match_on_class_mode;
526 rte_atomic_store_explicit(&option->refcnt, 0, rte_memory_order_relaxed);
527 return 0;
528 error:
529 for (i = 0; i < resource_id; ++i) {
530 resource = &option->resources[i];
531 mlx5_geneve_tlv_option_destroy_sample(resource);
532 }
533 return ret;
534 }
535
536 /**
537 * Destroy single GENEVE TLV option.
538 *
539 * @param option
540 * Pointer to single GENEVE TLV option to destroy.
541 *
542 * @return
543 * 0 on success, a negative errno otherwise and rte_errno is set.
544 */
545 static int
mlx5_geneve_tlv_option_destroy(struct mlx5_geneve_tlv_option * option)546 mlx5_geneve_tlv_option_destroy(struct mlx5_geneve_tlv_option *option)
547 {
548 uint8_t i;
549
550 if (rte_atomic_load_explicit(&option->refcnt, rte_memory_order_relaxed)) {
551 DRV_LOG(ERR,
552 "Option type %u class %u is still in used by %u tables.",
553 option->type, option->class, option->refcnt);
554 rte_errno = EBUSY;
555 return -rte_errno;
556 }
557 for (i = 0; option->resources[i].obj != NULL; ++i)
558 mlx5_geneve_tlv_option_destroy_sample(&option->resources[i]);
559 return 0;
560 }
561
562 /**
563 * Copy the GENEVE TLV option user configuration for future comparing.
564 *
565 * @param dst
566 * Pointer to internal user configuration copy.
567 * @param src
568 * Pointer to user configuration.
569 * @param match_data_mask
570 * Pointer to allocated data array.
571 */
572 static void
mlx5_geneve_tlv_option_copy(struct rte_pmd_mlx5_geneve_tlv * dst,const struct rte_pmd_mlx5_geneve_tlv * src,rte_be32_t * match_data_mask)573 mlx5_geneve_tlv_option_copy(struct rte_pmd_mlx5_geneve_tlv *dst,
574 const struct rte_pmd_mlx5_geneve_tlv *src,
575 rte_be32_t *match_data_mask)
576 {
577 uint8_t i;
578
579 dst->option_type = src->option_type;
580 dst->option_class = src->option_class;
581 dst->option_len = src->option_len;
582 dst->offset = src->offset;
583 dst->match_on_class_mode = src->match_on_class_mode;
584 dst->sample_len = src->sample_len;
585 for (i = 0; i < dst->sample_len; ++i)
586 match_data_mask[i] = src->match_data_mask[i];
587 dst->match_data_mask = match_data_mask;
588 }
589
590 /**
591 * Create list of GENEVE TLV options according to user configuration list.
592 *
593 * @param sh
594 * Shared context the options are being created on.
595 * @param tlv_list
596 * A list of GENEVE TLV options to create parser for them.
597 * @param nb_options
598 * The number of options in TLV list.
599 * @param sample_id
600 * The flex parser id for single DW or UINT8_MAX for multiple DWs.
601 *
602 * @return
603 * A pointer to GENEVE TLV options parser structure on success,
604 * NULL otherwise and rte_errno is set.
605 */
606 static struct mlx5_geneve_tlv_options *
mlx5_geneve_tlv_options_create(struct mlx5_dev_ctx_shared * sh,const struct rte_pmd_mlx5_geneve_tlv tlv_list[],uint8_t nb_options,uint8_t sample_id)607 mlx5_geneve_tlv_options_create(struct mlx5_dev_ctx_shared *sh,
608 const struct rte_pmd_mlx5_geneve_tlv tlv_list[],
609 uint8_t nb_options, uint8_t sample_id)
610 {
611 struct mlx5_geneve_tlv_options *options;
612 const struct rte_pmd_mlx5_geneve_tlv *spec;
613 rte_be32_t *data_mask;
614 uint8_t i, j;
615 int ret;
616
617 options = mlx5_malloc(MLX5_MEM_ZERO | MLX5_MEM_RTE,
618 sizeof(struct mlx5_geneve_tlv_options),
619 RTE_CACHE_LINE_SIZE, SOCKET_ID_ANY);
620 if (options == NULL) {
621 DRV_LOG(ERR,
622 "Failed to allocate memory for GENEVE TLV options.");
623 rte_errno = ENOMEM;
624 return NULL;
625 }
626 for (i = 0; i < nb_options; ++i) {
627 spec = &tlv_list[i];
628 ret = mlx5_geneve_tlv_option_create(sh->cdev->ctx, spec,
629 &options->options[i], sample_id);
630 if (ret < 0)
631 goto error;
632 /* Copy the user list for comparing future configuration. */
633 data_mask = options->buffer + i * MAX_GENEVE_OPTION_DATA_SIZE;
634 mlx5_geneve_tlv_option_copy(&options->spec[i], spec, data_mask);
635 }
636 MLX5_ASSERT(sh->phdev->sh == NULL);
637 sh->phdev->sh = sh;
638 options->nb_options = nb_options;
639 options->refcnt = 1;
640 return options;
641 error:
642 for (j = 0; j < i; ++j)
643 mlx5_geneve_tlv_option_destroy(&options->options[j]);
644 mlx5_free(options);
645 return NULL;
646 }
647
648 /**
649 * Destroy GENEVE TLV options structure.
650 *
651 * @param options
652 * Pointer to GENEVE TLV options structure to destroy.
653 * @param phdev
654 * Pointer physical device options were created on.
655 *
656 * @return
657 * 0 on success, a negative errno otherwise and rte_errno is set.
658 */
659 int
mlx5_geneve_tlv_options_destroy(struct mlx5_geneve_tlv_options * options,struct mlx5_physical_device * phdev)660 mlx5_geneve_tlv_options_destroy(struct mlx5_geneve_tlv_options *options,
661 struct mlx5_physical_device *phdev)
662 {
663 uint8_t i;
664 int ret;
665
666 if (--options->refcnt)
667 return 0;
668 for (i = 0; i < options->nb_options; ++i) {
669 ret = mlx5_geneve_tlv_option_destroy(&options->options[i]);
670 if (ret < 0) {
671 DRV_LOG(ERR,
672 "Failed to destroy option %u, %u/%u is already destroyed.",
673 i, i, options->nb_options);
674 return ret;
675 }
676 }
677 mlx5_free(options);
678 phdev->tlv_options = NULL;
679 phdev->sh = NULL;
680 return 0;
681 }
682
683 /**
684 * Check if GENEVE TLV options are hosted on the current port
685 * and the port can be closed
686 *
687 * @param priv
688 * Device private data.
689 *
690 * @return
691 * 0 on success, a negative EBUSY and rte_errno is set.
692 */
693 int
mlx5_geneve_tlv_options_check_busy(struct mlx5_priv * priv)694 mlx5_geneve_tlv_options_check_busy(struct mlx5_priv *priv)
695 {
696 struct mlx5_physical_device *phdev = mlx5_get_locked_physical_device(priv);
697 struct mlx5_dev_ctx_shared *sh = priv->sh;
698
699 if (!phdev || phdev->sh != sh) {
700 mlx5_unlock_physical_device();
701 return 0;
702 }
703 if (!sh->phdev->tlv_options || sh->phdev->tlv_options->refcnt == 1) {
704 /* Mark port as being closed one */
705 sh->phdev->sh = NULL;
706 mlx5_unlock_physical_device();
707 return 0;
708 }
709 mlx5_unlock_physical_device();
710 rte_errno = EBUSY;
711 return -EBUSY;
712 }
713
714 /**
715 * Validate GENEVE TLV option user request structure.
716 *
717 * @param attr
718 * Pointer to HCA attribute structure.
719 * @param option
720 * Pointer to user configuration.
721 *
722 * @return
723 * 0 on success, a negative errno otherwise and rte_errno is set.
724 */
725 static int
mlx5_geneve_tlv_option_validate(struct mlx5_hca_attr * attr,const struct rte_pmd_mlx5_geneve_tlv * option)726 mlx5_geneve_tlv_option_validate(struct mlx5_hca_attr *attr,
727 const struct rte_pmd_mlx5_geneve_tlv *option)
728 {
729 if (option->option_len > attr->max_geneve_tlv_option_data_len) {
730 DRV_LOG(ERR,
731 "GENEVE TLV option length (%u) exceeds the limit (%u).",
732 option->option_len,
733 attr->max_geneve_tlv_option_data_len);
734 rte_errno = ENOTSUP;
735 return -rte_errno;
736 }
737 if (option->option_len < option->offset + option->sample_len) {
738 DRV_LOG(ERR,
739 "GENEVE TLV option length is smaller than (offset + sample_len).");
740 rte_errno = EINVAL;
741 return -rte_errno;
742 }
743 if (option->match_on_class_mode > 2) {
744 DRV_LOG(ERR,
745 "GENEVE TLV option match_on_class_mode is invalid.");
746 rte_errno = EINVAL;
747 return -rte_errno;
748 }
749 return 0;
750 }
751
752 /**
753 * Get the number of requested DWs in given GENEVE TLV option.
754 *
755 * @param option
756 * Pointer to user configuration.
757 *
758 * @return
759 * Number of requested DWs for given GENEVE TLV option.
760 */
761 static uint8_t
mlx5_geneve_tlv_option_get_nb_dws(const struct rte_pmd_mlx5_geneve_tlv * option)762 mlx5_geneve_tlv_option_get_nb_dws(const struct rte_pmd_mlx5_geneve_tlv *option)
763 {
764 uint8_t nb_dws = 0;
765 uint8_t i;
766
767 if (option->match_on_class_mode == 2)
768 nb_dws++;
769 for (i = 0; i < option->sample_len; ++i) {
770 if (option->match_data_mask[i] == 0xffffffff)
771 nb_dws++;
772 }
773 return nb_dws;
774 }
775
776 /**
777 * Compare GENEVE TLV option user request structure.
778 *
779 * @param option1
780 * Pointer to first user configuration.
781 * @param option2
782 * Pointer to second user configuration.
783 *
784 * @return
785 * True if the options are equal, false otherwise.
786 */
787 static bool
mlx5_geneve_tlv_option_compare(const struct rte_pmd_mlx5_geneve_tlv * option1,const struct rte_pmd_mlx5_geneve_tlv * option2)788 mlx5_geneve_tlv_option_compare(const struct rte_pmd_mlx5_geneve_tlv *option1,
789 const struct rte_pmd_mlx5_geneve_tlv *option2)
790 {
791 uint8_t i;
792
793 if (option1->option_type != option2->option_type ||
794 option1->option_class != option2->option_class ||
795 option1->option_len != option2->option_len ||
796 option1->offset != option2->offset ||
797 option1->match_on_class_mode != option2->match_on_class_mode ||
798 option1->sample_len != option2->sample_len)
799 return false;
800 for (i = 0; i < option1->sample_len; ++i) {
801 if (option1->match_data_mask[i] != option2->match_data_mask[i])
802 return false;
803 }
804 return true;
805 }
806
807 /**
808 * Check whether the given GENEVE TLV option list is equal to internal list.
809 * The lists are equal when they have same size and same options in the same
810 * order inside the list.
811 *
812 * @param options
813 * Pointer to GENEVE TLV options structure.
814 * @param tlv_list
815 * A list of GENEVE TLV options to compare.
816 * @param nb_options
817 * The number of options in TLV list.
818 *
819 * @return
820 * True if the lists are equal, false otherwise.
821 */
822 static bool
mlx5_is_same_geneve_tlv_options(const struct mlx5_geneve_tlv_options * options,const struct rte_pmd_mlx5_geneve_tlv tlv_list[],uint8_t nb_options)823 mlx5_is_same_geneve_tlv_options(const struct mlx5_geneve_tlv_options *options,
824 const struct rte_pmd_mlx5_geneve_tlv tlv_list[],
825 uint8_t nb_options)
826 {
827 const struct rte_pmd_mlx5_geneve_tlv *spec = options->spec;
828 uint8_t i;
829
830 if (options->nb_options != nb_options)
831 return false;
832 for (i = 0; i < nb_options; ++i) {
833 if (!mlx5_geneve_tlv_option_compare(&spec[i], &tlv_list[i]))
834 return false;
835 }
836 return true;
837 }
838
839 static inline bool
multiple_dws_supported(struct mlx5_hca_attr * attr)840 multiple_dws_supported(struct mlx5_hca_attr *attr)
841 {
842 return attr->geneve_tlv_option_offset && attr->geneve_tlv_sample;
843 }
844
845 void *
mlx5_geneve_tlv_parser_create(uint16_t port_id,const struct rte_pmd_mlx5_geneve_tlv tlv_list[],uint8_t nb_options)846 mlx5_geneve_tlv_parser_create(uint16_t port_id,
847 const struct rte_pmd_mlx5_geneve_tlv tlv_list[],
848 uint8_t nb_options)
849 {
850 struct mlx5_geneve_tlv_options *options = NULL;
851 struct mlx5_physical_device *phdev;
852 struct rte_eth_dev *dev;
853 struct mlx5_priv *priv;
854 struct mlx5_hca_attr *attr;
855 uint8_t sample_id;
856
857 /*
858 * Validate the input before taking a lock and before any memory
859 * allocation.
860 */
861 if (rte_eth_dev_is_valid_port(port_id) < 0) {
862 DRV_LOG(ERR, "There is no Ethernet device for port %u.",
863 port_id);
864 rte_errno = ENODEV;
865 return NULL;
866 }
867 dev = &rte_eth_devices[port_id];
868 priv = dev->data->dev_private;
869 if (priv->tlv_options) {
870 DRV_LOG(ERR, "Port %u already has GENEVE TLV parser.", port_id);
871 rte_errno = EEXIST;
872 return NULL;
873 }
874 if (priv->sh->config.dv_flow_en < 2) {
875 DRV_LOG(ERR,
876 "GENEVE TLV parser is only supported for HW steering.");
877 rte_errno = ENOTSUP;
878 return NULL;
879 }
880 attr = &priv->sh->cdev->config.hca_attr;
881 if (!attr->query_match_sample_info || !attr->geneve_tlv_opt) {
882 DRV_LOG(ERR, "Not enough capabilities to support GENEVE TLV parser, is this device eswitch manager?");
883 rte_errno = ENOTSUP;
884 return NULL;
885 }
886 DRV_LOG(DEBUG, "Max DWs supported for GENEVE TLV option is %u",
887 attr->max_geneve_tlv_options);
888 if (nb_options > attr->max_geneve_tlv_options) {
889 DRV_LOG(ERR,
890 "GENEVE TLV option number (%u) exceeds the limit (%u).",
891 nb_options, attr->max_geneve_tlv_options);
892 rte_errno = EINVAL;
893 return NULL;
894 }
895 if (multiple_dws_supported(attr)) {
896 uint8_t total_dws = 0;
897 uint8_t i;
898
899 MLX5_ASSERT(attr->max_geneve_tlv_options >= MAX_GENEVE_OPTIONS_RESOURCES);
900 for (i = 0; i < nb_options; ++i) {
901 if (mlx5_geneve_tlv_option_validate(attr, &tlv_list[i]) < 0) {
902 DRV_LOG(ERR, "GENEVE TLV option %u is invalid.", i);
903 return NULL;
904 }
905 total_dws += mlx5_geneve_tlv_option_get_nb_dws(&tlv_list[i]);
906 }
907 if (total_dws > MAX_GENEVE_OPTIONS_RESOURCES) {
908 DRV_LOG(ERR,
909 "Total requested DWs (%u) exceeds the limit (%u).",
910 total_dws, MAX_GENEVE_OPTIONS_RESOURCES);
911 rte_errno = EINVAL;
912 return NULL;
913 }
914 /* Multiple DWs is supported, each of the has sample ID given later. */
915 sample_id = INVALID_SAMPLE_ID;
916 DRV_LOG(DEBUG, "GENEVE TLV parser supports multiple DWs, FLEX_PARSER_PROFILE_ENABLE == 8");
917 } else {
918 const struct rte_pmd_mlx5_geneve_tlv *option = &tlv_list[0];
919
920 if (option->offset != 0) {
921 DRV_LOG(ERR,
922 "GENEVE TLV option offset %u is required but not supported.",
923 option->offset);
924 rte_errno = ENOTSUP;
925 return NULL;
926 }
927 if (option->sample_len != option->option_len) {
928 DRV_LOG(ERR,
929 "GENEVE TLV option length (%u) should be equal to sample length (%u).",
930 option->option_len, option->sample_len);
931 rte_errno = ENOTSUP;
932 return NULL;
933 }
934 if (option->match_on_class_mode != 1) {
935 DRV_LOG(ERR,
936 "GENEVE TLV option match_on_class_mode %u is invalid for flex parser profile 0.",
937 option->match_on_class_mode);
938 rte_errno = EINVAL;
939 return NULL;
940 }
941 if (mlx5_geneve_tlv_option_validate(attr, option) < 0)
942 return NULL;
943 /* Single DW is supported, its sample ID is given. */
944 sample_id = attr->geneve_tlv_option_sample_id;
945 DRV_LOG(DEBUG, "GENEVE TLV parser supports only single DW, FLEX_PARSER_PROFILE_ENABLE == 0");
946 }
947 /* Take lock for this physical device and manage the options. */
948 phdev = mlx5_get_locked_physical_device(priv);
949 options = priv->sh->phdev->tlv_options;
950 if (options) {
951 if (!mlx5_is_same_geneve_tlv_options(options, tlv_list,
952 nb_options)) {
953 mlx5_unlock_physical_device();
954 DRV_LOG(ERR, "Another port has already prepared different GENEVE TLV parser.");
955 rte_errno = EEXIST;
956 return NULL;
957 }
958 if (phdev->sh == NULL) {
959 mlx5_unlock_physical_device();
960 DRV_LOG(ERR, "GENEVE TLV options are hosted on port being closed.");
961 rte_errno = EBUSY;
962 return NULL;
963 }
964 /* Use existing options. */
965 options->refcnt++;
966 goto exit;
967 }
968 /* Create GENEVE TLV options for this physical device. */
969 options = mlx5_geneve_tlv_options_create(priv->sh, tlv_list, nb_options, sample_id);
970 if (!options) {
971 mlx5_unlock_physical_device();
972 return NULL;
973 }
974 phdev->tlv_options = options;
975 exit:
976 mlx5_unlock_physical_device();
977 priv->tlv_options = options;
978 return priv;
979 }
980
981 int
mlx5_geneve_tlv_parser_destroy(void * handle)982 mlx5_geneve_tlv_parser_destroy(void *handle)
983 {
984 struct mlx5_priv *priv = (struct mlx5_priv *)handle;
985 struct mlx5_physical_device *phdev;
986 int ret;
987
988 if (priv == NULL) {
989 DRV_LOG(ERR, "Handle input is invalid (NULL).");
990 rte_errno = EINVAL;
991 return -rte_errno;
992 }
993 if (priv->tlv_options == NULL) {
994 DRV_LOG(ERR, "This parser has been already released.");
995 rte_errno = ENOENT;
996 return -rte_errno;
997 }
998 /* Take lock for this physical device and manage the options. */
999 phdev = mlx5_get_locked_physical_device(priv);
1000 /* Destroy the options */
1001 ret = mlx5_geneve_tlv_options_destroy(phdev->tlv_options, phdev);
1002 if (ret < 0) {
1003 mlx5_unlock_physical_device();
1004 return ret;
1005 }
1006 priv->tlv_options = NULL;
1007 mlx5_unlock_physical_device();
1008 return 0;
1009 }
1010
1011 #endif /* defined(HAVE_IBV_FLOW_DV_SUPPORT) || !defined(HAVE_INFINIBAND_VERBS_H) */
1012