Branch data Line data Source code
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
77 : : option_match_type_and_class(uint8_t type, uint16_t class,
78 : : struct mlx5_geneve_tlv_option *option)
79 : : {
80 : 0 : if (type != option->type)
81 : : return false;
82 [ # # # # ]: 0 : 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 *
101 : 0 : 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 [ # # ]: 0 : if (priv->tlv_options == NULL) {
108 : 0 : DRV_LOG(ERR,
109 : : "Port %u doesn't have configured GENEVE TLV options.",
110 : : priv->dev_data->port_id);
111 : 0 : rte_errno = EINVAL;
112 : 0 : return NULL;
113 : : }
114 : : options = priv->tlv_options;
115 : : MLX5_ASSERT(options != NULL);
116 [ # # ]: 0 : for (i = 0; i < options->nb_options; ++i) {
117 [ # # ]: 0 : struct mlx5_geneve_tlv_option *option = &options->options[i];
118 : :
119 : : if (option_match_type_and_class(type, class, option))
120 : 0 : return option;
121 : : }
122 : 0 : DRV_LOG(ERR, "TLV option type %u class %u doesn't exist.", type, class);
123 : 0 : rte_errno = ENOENT;
124 : 0 : return NULL;
125 : : }
126 : :
127 : : int
128 : 0 : 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 [ # # ]: 0 : MLX5_ETH_FOREACH_DEV(port_id, NULL) {
137 : : struct mlx5_priv *priv;
138 : : struct mlx5_geneve_tlv_option *option;
139 : :
140 : 0 : priv = rte_eth_devices[port_id].data->dev_private;
141 [ # # ]: 0 : if (priv->dr_ctx != dr_ctx)
142 : : continue;
143 : : /* Find specific option inside list. */
144 : 0 : option = mlx5_geneve_tlv_option_get(priv, type, class);
145 [ # # ]: 0 : if (option == NULL)
146 : 0 : return -rte_errno;
147 : 0 : *hl_ok_bit = &option->hl_ok_bit;
148 : 0 : *hl_dws = option->match_data;
149 : 0 : *num_of_dws = option->match_data_size;
150 : 0 : *ok_bit_on_class = !!(option->class_mode == 1);
151 : 0 : return 0;
152 : : }
153 : 0 : DRV_LOG(ERR, "DR CTX %p doesn't belong to any DPDK port.", dr_ctx);
154 : 0 : 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
171 : 0 : 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 : 0 : const struct rte_flow_item_geneve_opt *spec = geneve_opt->spec;
176 : 0 : const struct rte_flow_item_geneve_opt *mask = geneve_opt->mask;
177 : : struct mlx5_geneve_tlv_option *option;
178 : :
179 : 0 : option = mlx5_geneve_tlv_option_get(priv, spec->option_type, spec->option_class);
180 [ # # ]: 0 : if (option == NULL)
181 : 0 : return rte_flow_error_set(error, rte_errno,
182 : : RTE_FLOW_ERROR_TYPE_ITEM, NULL,
183 : : "Unregistered GENEVE option");
184 [ # # ]: 0 : if (mask->option_type != UINT8_MAX)
185 : 0 : return rte_flow_error_set(error, EINVAL,
186 : : RTE_FLOW_ERROR_TYPE_ITEM, NULL,
187 : : "GENEVE option type must be fully masked");
188 [ # # # # ]: 0 : if (option->class_mode == 1 && mask->option_class != UINT16_MAX)
189 : 0 : 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
209 : 0 : 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 : 0 : option = mlx5_geneve_tlv_option_get(priv, spec->option_type, spec->option_class);
216 [ # # ]: 0 : if (option == NULL)
217 : 0 : return -rte_errno;
218 : : /* Increase the option reference counter. */
219 : 0 : rte_atomic_fetch_add_explicit(&option->refcnt, 1,
220 : : rte_memory_order_relaxed);
221 : : /* Update the manager with option information. */
222 : 0 : mng->options[mng->nb_options].opt_type = spec->option_type;
223 : 0 : mng->options[mng->nb_options].opt_class = spec->option_class;
224 : 0 : mng->nb_options++;
225 : 0 : 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
237 : 0 : 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 [ # # ]: 0 : for (i = 0; i < mng->nb_options; ++i) {
244 : 0 : option = mlx5_geneve_tlv_option_get(priv,
245 : 0 : mng->options[i].opt_type,
246 : 0 : mng->options[i].opt_class);
247 : : MLX5_ASSERT(option != NULL);
248 : : /* Decrease the option reference counter. */
249 : 0 : rte_atomic_fetch_sub_explicit(&option->refcnt, 1,
250 : : rte_memory_order_relaxed);
251 : 0 : mng->options[i].opt_type = 0;
252 : 0 : mng->options[i].opt_class = 0;
253 : : }
254 : 0 : mng->nb_options = 0;
255 : 0 : }
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 *
269 : 0 : 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 [ # # ]: 0 : for (i = 0; option->resources[i].obj != NULL; ++i) {
275 [ # # ]: 0 : if (option->resources[i].offset < offset)
276 : : continue;
277 [ # # ]: 0 : if (option->resources[i].offset == offset)
278 : 0 : return &option->resources[i];
279 : : break;
280 : : }
281 : 0 : DRV_LOG(ERR, "The DW in offset %u wasn't configured.", offset);
282 : 0 : rte_errno = EINVAL;
283 : 0 : return NULL;
284 : : }
285 : :
286 : : int
287 : 0 : 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 [ # # ]: 0 : 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 : 0 : priv = rte_eth_devices[port_id].data->dev_private;
298 [ # # ]: 0 : if (priv->dr_ctx != dr_ctx)
299 : : continue;
300 : : /* Find specific option inside list. */
301 : 0 : option = mlx5_geneve_tlv_option_get(priv, type, class);
302 [ # # ]: 0 : if (option == NULL)
303 : 0 : return -rte_errno;
304 : : /* Find specific FW object inside option resources. */
305 : 0 : resource = mlx5_geneve_tlv_option_get_resource_by_offset(option,
306 : : dw_offset);
307 [ # # ]: 0 : if (resource == NULL)
308 : 0 : return -rte_errno;
309 : 0 : return resource->modify_field;
310 : : }
311 : 0 : DRV_LOG(ERR, "DR CTX %p doesn't belong to any DPDK port.", dr_ctx);
312 : 0 : rte_errno = EINVAL;
313 : 0 : 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
328 : 0 : mlx5_geneve_opt_modi_field_get(struct mlx5_priv *priv,
329 : : const struct rte_flow_field_data *data)
330 : : {
331 : 0 : uint16_t class = data->class_id;
332 : 0 : 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 : 0 : option = mlx5_geneve_tlv_option_get(priv, type, class);
338 [ # # ]: 0 : if (option == NULL)
339 : 0 : return -rte_errno;
340 [ # # # ]: 0 : switch (data->field) {
341 : 0 : case RTE_FLOW_FIELD_GENEVE_OPT_TYPE:
342 : : case RTE_FLOW_FIELD_GENEVE_OPT_CLASS:
343 [ # # ]: 0 : if (!option->match_data[0].dw_mask) {
344 : 0 : DRV_LOG(ERR, "DW0 isn't configured");
345 : 0 : rte_errno = EINVAL;
346 : 0 : return -rte_errno;
347 : : }
348 : 0 : resource = &option->resources[0];
349 : : MLX5_ASSERT(resource->offset == 0);
350 : 0 : break;
351 : 0 : 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 : 0 : offset = (data->offset >> 5) + 1;
359 : 0 : resource = mlx5_geneve_tlv_option_get_resource_by_offset(option,
360 : : offset);
361 : 0 : break;
362 : 0 : default:
363 : 0 : DRV_LOG(ERR,
364 : : "Field ID %u doesn't describe GENEVE option header.",
365 : : data->field);
366 : 0 : rte_errno = EINVAL;
367 : 0 : return -rte_errno;
368 : : }
369 [ # # ]: 0 : if (resource == NULL)
370 : 0 : return -rte_errno;
371 : 0 : 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
394 : 0 : 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 : 0 : obj = mlx5_devx_cmd_create_geneve_tlv_option(ctx, attr);
404 [ # # ]: 0 : if (obj == NULL)
405 : 0 : return -rte_errno;
406 [ # # ]: 0 : if (sample_id == INVALID_SAMPLE_ID)
407 : 0 : ret = mlx5_devx_cmd_query_geneve_tlv_option(ctx, obj, query_attr);
408 : : else
409 : 0 : ret = mlx5_devx_cmd_match_sample_info_query(ctx, sample_id, query_attr);
410 [ # # ]: 0 : if (ret) {
411 : 0 : claim_zero(mlx5_devx_cmd_destroy(obj));
412 : 0 : return ret;
413 : : }
414 : 0 : resource->obj = obj;
415 : 0 : resource->offset = attr->sample_offset;
416 : 0 : resource->modify_field = query_attr->modify_field_id;
417 : 0 : match_data->dw_offset = query_attr->sample_dw_data;
418 : 0 : match_data->dw_mask = 0xffffffff;
419 : 0 : 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
429 : : mlx5_geneve_tlv_option_destroy_sample(struct mlx5_geneve_tlv_resource *resource)
430 : : {
431 : 0 : claim_zero(mlx5_devx_cmd_destroy(resource->obj));
432 : 0 : 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
441 : : should_configure_sample_for_dw0(const struct rte_pmd_mlx5_geneve_tlv *spec)
442 : : {
443 : : uint8_t i;
444 : :
445 : 0 : if (spec->match_on_class_mode == 2)
446 : : return true;
447 [ # # ]: 0 : for (i = 0; i < spec->sample_len; ++i)
448 [ # # ]: 0 : 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
469 : 0 : 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 : 0 : struct mlx5_devx_geneve_tlv_option_attr attr = {
473 : 0 : .option_class = spec->option_class,
474 : 0 : .option_type = spec->option_type,
475 : 0 : .option_data_len = spec->option_len,
476 : 0 : .option_class_ignore = spec->match_on_class_mode == 1 ? 0 : 1,
477 : 0 : .offset_valid = sample_id == INVALID_SAMPLE_ID ? 1 : 0,
478 : : };
479 [ # # ]: 0 : 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 [ # # ]: 0 : if (should_configure_sample_for_dw0(spec)) {
485 : : MLX5_ASSERT(sample_id == INVALID_SAMPLE_ID);
486 : : attr.sample_offset = 0;
487 : 0 : resource = &option->resources[resource_id];
488 : 0 : ret = mlx5_geneve_tlv_option_create_sample(ctx, &attr,
489 : : &query_attr,
490 : : &option->match_data[0],
491 : : resource,
492 : : INVALID_SAMPLE_ID);
493 [ # # ]: 0 : 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 [ # # ]: 0 : for (i = 1; i <= spec->sample_len; ++i) {
502 [ # # ]: 0 : if (spec->match_data_mask[i - 1] == 0)
503 : 0 : continue;
504 : : /* offset of data + offset inside data = specific DW offset. */
505 : 0 : attr.sample_offset = spec->offset + i;
506 : 0 : resource = &option->resources[resource_id];
507 : 0 : ret = mlx5_geneve_tlv_option_create_sample(ctx, &attr,
508 : : &query_attr,
509 : 0 : &option->match_data[i],
510 : : resource,
511 : : sample_id);
512 [ # # ]: 0 : if (ret)
513 : 0 : goto error;
514 : 0 : 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 : 0 : option->hl_ok_bit.dw_offset = query_attr.sample_dw_ok_bit;
521 : 0 : option->hl_ok_bit.dw_mask = 1 << query_attr.sample_dw_ok_bit_offset;
522 : 0 : option->match_data_size = spec->sample_len + 1;
523 : 0 : option->type = spec->option_type;
524 : 0 : option->class = spec->option_class;
525 : 0 : option->class_mode = spec->match_on_class_mode;
526 : 0 : rte_atomic_store_explicit(&option->refcnt, 0, rte_memory_order_relaxed);
527 : 0 : return 0;
528 : : error:
529 [ # # ]: 0 : for (i = 0; i < resource_id; ++i) {
530 : 0 : 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
546 : 0 : mlx5_geneve_tlv_option_destroy(struct mlx5_geneve_tlv_option *option)
547 : : {
548 : : uint8_t i;
549 : :
550 [ # # ]: 0 : if (rte_atomic_load_explicit(&option->refcnt, rte_memory_order_relaxed)) {
551 : 0 : DRV_LOG(ERR,
552 : : "Option type %u class %u is still in used by %u tables.",
553 : : option->type, option->class, option->refcnt);
554 : 0 : rte_errno = EBUSY;
555 : 0 : return -rte_errno;
556 : : }
557 [ # # ]: 0 : 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
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 : 0 : dst->option_type = src->option_type;
580 : 0 : dst->option_class = src->option_class;
581 : 0 : dst->option_len = src->option_len;
582 : 0 : dst->offset = src->offset;
583 : 0 : dst->match_on_class_mode = src->match_on_class_mode;
584 : 0 : dst->sample_len = src->sample_len;
585 [ # # ]: 0 : for (i = 0; i < dst->sample_len; ++i)
586 : 0 : match_data_mask[i] = src->match_data_mask[i];
587 : 0 : 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 *
607 : 0 : 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 : 0 : 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 [ # # ]: 0 : if (options == NULL) {
621 : 0 : DRV_LOG(ERR,
622 : : "Failed to allocate memory for GENEVE TLV options.");
623 : 0 : rte_errno = ENOMEM;
624 : 0 : return NULL;
625 : : }
626 [ # # ]: 0 : for (i = 0; i < nb_options; ++i) {
627 : 0 : spec = &tlv_list[i];
628 : 0 : ret = mlx5_geneve_tlv_option_create(sh->cdev->ctx, spec,
629 : 0 : &options->options[i], sample_id);
630 [ # # ]: 0 : if (ret < 0)
631 : 0 : goto error;
632 : : /* Copy the user list for comparing future configuration. */
633 : 0 : 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 : 0 : sh->phdev->sh = sh;
638 : 0 : options->nb_options = nb_options;
639 : 0 : options->refcnt = 1;
640 : 0 : return options;
641 : : error:
642 [ # # ]: 0 : for (j = 0; j < i; ++j)
643 : 0 : mlx5_geneve_tlv_option_destroy(&options->options[j]);
644 : 0 : mlx5_free(options);
645 : 0 : 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
660 : 0 : 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 [ # # ]: 0 : if (--options->refcnt)
667 : : return 0;
668 [ # # ]: 0 : for (i = 0; i < options->nb_options; ++i) {
669 : 0 : ret = mlx5_geneve_tlv_option_destroy(&options->options[i]);
670 [ # # ]: 0 : if (ret < 0) {
671 : 0 : DRV_LOG(ERR,
672 : : "Failed to destroy option %u, %u/%u is already destroyed.",
673 : : i, i, options->nb_options);
674 : 0 : return ret;
675 : : }
676 : : }
677 : 0 : mlx5_free(options);
678 : 0 : phdev->tlv_options = NULL;
679 : 0 : phdev->sh = NULL;
680 : 0 : 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
694 : 0 : mlx5_geneve_tlv_options_check_busy(struct mlx5_priv *priv)
695 : : {
696 : 0 : struct mlx5_physical_device *phdev = mlx5_get_locked_physical_device(priv);
697 : 0 : struct mlx5_dev_ctx_shared *sh = priv->sh;
698 : :
699 [ # # # # ]: 0 : if (!phdev || phdev->sh != sh) {
700 : 0 : mlx5_unlock_physical_device();
701 : 0 : return 0;
702 : : }
703 [ # # # # ]: 0 : if (!sh->phdev->tlv_options || sh->phdev->tlv_options->refcnt == 1) {
704 : : /* Mark port as being closed one */
705 : 0 : sh->phdev->sh = NULL;
706 : 0 : mlx5_unlock_physical_device();
707 : 0 : return 0;
708 : : }
709 : 0 : mlx5_unlock_physical_device();
710 : 0 : rte_errno = EBUSY;
711 : 0 : 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
726 : 0 : mlx5_geneve_tlv_option_validate(struct mlx5_hca_attr *attr,
727 : : const struct rte_pmd_mlx5_geneve_tlv *option)
728 : : {
729 [ # # ]: 0 : if (option->option_len > attr->max_geneve_tlv_option_data_len) {
730 : 0 : 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 : 0 : rte_errno = ENOTSUP;
735 : 0 : return -rte_errno;
736 : : }
737 [ # # ]: 0 : if (option->option_len < option->offset + option->sample_len) {
738 : 0 : DRV_LOG(ERR,
739 : : "GENEVE TLV option length is smaller than (offset + sample_len).");
740 : 0 : rte_errno = EINVAL;
741 : 0 : return -rte_errno;
742 : : }
743 [ # # ]: 0 : if (option->match_on_class_mode > 2) {
744 : 0 : DRV_LOG(ERR,
745 : : "GENEVE TLV option match_on_class_mode is invalid.");
746 : 0 : rte_errno = EINVAL;
747 : 0 : 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
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 [ # # ]: 0 : if (option->match_on_class_mode == 2)
768 : : nb_dws++;
769 [ # # ]: 0 : for (i = 0; i < option->sample_len; ++i) {
770 [ # # ]: 0 : if (option->match_data_mask[i] == 0xffffffff)
771 : 0 : 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
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 : 0 : 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 : 0 : option1->match_on_class_mode != option2->match_on_class_mode ||
798 : : option1->sample_len != option2->sample_len)
799 : : return false;
800 [ # # ]: 0 : for (i = 0; i < option1->sample_len; ++i) {
801 [ # # ]: 0 : 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
823 : 0 : 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 : 0 : const struct rte_pmd_mlx5_geneve_tlv *spec = options->spec;
828 : : uint8_t i;
829 : :
830 [ # # ]: 0 : if (options->nb_options != nb_options)
831 : : return false;
832 [ # # ]: 0 : for (i = 0; i < nb_options; ++i) {
833 [ # # # # ]: 0 : if (!mlx5_geneve_tlv_option_compare(&spec[i], &tlv_list[i]))
834 : : return false;
835 : : }
836 : : return true;
837 : : }
838 : :
839 : : static inline bool
840 : : multiple_dws_supported(struct mlx5_hca_attr *attr)
841 : : {
842 : 0 : return attr->geneve_tlv_option_offset && attr->geneve_tlv_sample;
843 : : }
844 : :
845 : : void *
846 : 0 : 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 [ # # ]: 0 : if (rte_eth_dev_is_valid_port(port_id) < 0) {
862 : 0 : DRV_LOG(ERR, "There is no Ethernet device for port %u.",
863 : : port_id);
864 : 0 : rte_errno = ENODEV;
865 : 0 : return NULL;
866 : : }
867 : : dev = &rte_eth_devices[port_id];
868 : 0 : priv = dev->data->dev_private;
869 [ # # ]: 0 : if (priv->tlv_options) {
870 : 0 : DRV_LOG(ERR, "Port %u already has GENEVE TLV parser.", port_id);
871 : 0 : rte_errno = EEXIST;
872 : 0 : return NULL;
873 : : }
874 [ # # ]: 0 : if (priv->sh->config.dv_flow_en < 2) {
875 : 0 : DRV_LOG(ERR,
876 : : "GENEVE TLV parser is only supported for HW steering.");
877 : 0 : rte_errno = ENOTSUP;
878 : 0 : return NULL;
879 : : }
880 : 0 : attr = &priv->sh->cdev->config.hca_attr;
881 [ # # # # ]: 0 : if (!attr->query_match_sample_info || !attr->geneve_tlv_opt) {
882 : 0 : DRV_LOG(ERR, "Not enough capabilities to support GENEVE TLV parser, is this device eswitch manager?");
883 : 0 : rte_errno = ENOTSUP;
884 : 0 : return NULL;
885 : : }
886 : 0 : DRV_LOG(DEBUG, "Max DWs supported for GENEVE TLV option is %u",
887 : : attr->max_geneve_tlv_options);
888 [ # # ]: 0 : if (nb_options > attr->max_geneve_tlv_options) {
889 : 0 : DRV_LOG(ERR,
890 : : "GENEVE TLV option number (%u) exceeds the limit (%u).",
891 : : nb_options, attr->max_geneve_tlv_options);
892 : 0 : rte_errno = EINVAL;
893 : 0 : return NULL;
894 : : }
895 [ # # ]: 0 : 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 [ # # ]: 0 : for (i = 0; i < nb_options; ++i) {
901 [ # # ]: 0 : if (mlx5_geneve_tlv_option_validate(attr, &tlv_list[i]) < 0) {
902 : 0 : DRV_LOG(ERR, "GENEVE TLV option %u is invalid.", i);
903 : 0 : return NULL;
904 : : }
905 : 0 : total_dws += mlx5_geneve_tlv_option_get_nb_dws(&tlv_list[i]);
906 : : }
907 [ # # ]: 0 : if (total_dws > MAX_GENEVE_OPTIONS_RESOURCES) {
908 : 0 : DRV_LOG(ERR,
909 : : "Total requested DWs (%u) exceeds the limit (%u).",
910 : : total_dws, MAX_GENEVE_OPTIONS_RESOURCES);
911 : 0 : rte_errno = EINVAL;
912 : 0 : return NULL;
913 : : }
914 : : /* Multiple DWs is supported, each of the has sample ID given later. */
915 : : sample_id = INVALID_SAMPLE_ID;
916 : 0 : 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 [ # # ]: 0 : if (option->offset != 0) {
921 : 0 : DRV_LOG(ERR,
922 : : "GENEVE TLV option offset %u is required but not supported.",
923 : : option->offset);
924 : 0 : rte_errno = ENOTSUP;
925 : 0 : return NULL;
926 : : }
927 [ # # ]: 0 : if (option->sample_len != option->option_len) {
928 : 0 : DRV_LOG(ERR,
929 : : "GENEVE TLV option length (%u) should be equal to sample length (%u).",
930 : : option->option_len, option->sample_len);
931 : 0 : rte_errno = ENOTSUP;
932 : 0 : return NULL;
933 : : }
934 [ # # ]: 0 : if (option->match_on_class_mode != 1) {
935 : 0 : 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 : 0 : rte_errno = EINVAL;
939 : 0 : return NULL;
940 : : }
941 [ # # ]: 0 : if (mlx5_geneve_tlv_option_validate(attr, option) < 0)
942 : : return NULL;
943 : : /* Single DW is supported, its sample ID is given. */
944 : 0 : sample_id = attr->geneve_tlv_option_sample_id;
945 : 0 : 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 : 0 : phdev = mlx5_get_locked_physical_device(priv);
949 : 0 : options = priv->sh->phdev->tlv_options;
950 [ # # ]: 0 : if (options) {
951 [ # # ]: 0 : if (!mlx5_is_same_geneve_tlv_options(options, tlv_list,
952 : : nb_options)) {
953 : 0 : mlx5_unlock_physical_device();
954 : 0 : DRV_LOG(ERR, "Another port has already prepared different GENEVE TLV parser.");
955 : 0 : rte_errno = EEXIST;
956 : 0 : return NULL;
957 : : }
958 [ # # ]: 0 : if (phdev->sh == NULL) {
959 : 0 : mlx5_unlock_physical_device();
960 : 0 : DRV_LOG(ERR, "GENEVE TLV options are hosted on port being closed.");
961 : 0 : rte_errno = EBUSY;
962 : 0 : return NULL;
963 : : }
964 : : /* Use existing options. */
965 : 0 : options->refcnt++;
966 : 0 : goto exit;
967 : : }
968 : : /* Create GENEVE TLV options for this physical device. */
969 : 0 : options = mlx5_geneve_tlv_options_create(priv->sh, tlv_list, nb_options, sample_id);
970 [ # # ]: 0 : if (!options) {
971 : 0 : mlx5_unlock_physical_device();
972 : 0 : return NULL;
973 : : }
974 : 0 : phdev->tlv_options = options;
975 : 0 : exit:
976 : 0 : mlx5_unlock_physical_device();
977 : 0 : priv->tlv_options = options;
978 : 0 : return priv;
979 : : }
980 : :
981 : : int
982 : 0 : 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 [ # # ]: 0 : if (priv == NULL) {
989 : 0 : DRV_LOG(ERR, "Handle input is invalid (NULL).");
990 : 0 : rte_errno = EINVAL;
991 : 0 : return -rte_errno;
992 : : }
993 [ # # ]: 0 : if (priv->tlv_options == NULL) {
994 : 0 : DRV_LOG(ERR, "This parser has been already released.");
995 : 0 : rte_errno = ENOENT;
996 : 0 : return -rte_errno;
997 : : }
998 : : /* Take lock for this physical device and manage the options. */
999 : 0 : phdev = mlx5_get_locked_physical_device(priv);
1000 : : /* Destroy the options */
1001 : 0 : ret = mlx5_geneve_tlv_options_destroy(phdev->tlv_options, phdev);
1002 [ # # ]: 0 : if (ret < 0) {
1003 : 0 : mlx5_unlock_physical_device();
1004 : 0 : return ret;
1005 : : }
1006 : 0 : priv->tlv_options = NULL;
1007 : 0 : mlx5_unlock_physical_device();
1008 : 0 : return 0;
1009 : : }
1010 : :
1011 : : #endif /* defined(HAVE_IBV_FLOW_DV_SUPPORT) || !defined(HAVE_INFINIBAND_VERBS_H) */
|