xref: /dpdk/drivers/net/mlx5/mlx5_flow_geneve.c (revision 1caa89ec1891779c234047880a1bbc12b28569c1)
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