xref: /linux/drivers/net/ethernet/mellanox/mlx5/core/steering/hws/bwc.c (revision 87320be9f0d24fce67631b7eef919f0b79c3e45c)
1 // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
2 /* Copyright (c) 2024 NVIDIA Corporation & Affiliates */
3 
4 #include "internal.h"
5 
hws_bwc_gen_queue_idx(struct mlx5hws_context * ctx)6 static u16 hws_bwc_gen_queue_idx(struct mlx5hws_context *ctx)
7 {
8 	/* assign random queue */
9 	return get_random_u8() % mlx5hws_bwc_queues(ctx);
10 }
11 
12 static u16
hws_bwc_get_burst_th(struct mlx5hws_context * ctx,u16 queue_id)13 hws_bwc_get_burst_th(struct mlx5hws_context *ctx, u16 queue_id)
14 {
15 	return min(ctx->send_queue[queue_id].num_entries / 2,
16 		   MLX5HWS_BWC_MATCHER_REHASH_BURST_TH);
17 }
18 
19 static struct mutex *
hws_bwc_get_queue_lock(struct mlx5hws_context * ctx,u16 idx)20 hws_bwc_get_queue_lock(struct mlx5hws_context *ctx, u16 idx)
21 {
22 	return &ctx->bwc_send_queue_locks[idx];
23 }
24 
hws_bwc_lock_all_queues(struct mlx5hws_context * ctx)25 static void hws_bwc_lock_all_queues(struct mlx5hws_context *ctx)
26 {
27 	u16 bwc_queues = mlx5hws_bwc_queues(ctx);
28 	struct mutex *queue_lock; /* Protect the queue */
29 	int i;
30 
31 	for (i = 0; i < bwc_queues; i++) {
32 		queue_lock = hws_bwc_get_queue_lock(ctx, i);
33 		mutex_lock(queue_lock);
34 	}
35 }
36 
hws_bwc_unlock_all_queues(struct mlx5hws_context * ctx)37 static void hws_bwc_unlock_all_queues(struct mlx5hws_context *ctx)
38 {
39 	u16 bwc_queues = mlx5hws_bwc_queues(ctx);
40 	struct mutex *queue_lock; /* Protect the queue */
41 	int i = bwc_queues;
42 
43 	while (i--) {
44 		queue_lock = hws_bwc_get_queue_lock(ctx, i);
45 		mutex_unlock(queue_lock);
46 	}
47 }
48 
hws_bwc_matcher_init_attr(struct mlx5hws_bwc_matcher * bwc_matcher,u32 priority,u8 size_log_rx,u8 size_log_tx,struct mlx5hws_matcher_attr * attr)49 static void hws_bwc_matcher_init_attr(struct mlx5hws_bwc_matcher *bwc_matcher,
50 				      u32 priority,
51 				      u8 size_log_rx, u8 size_log_tx,
52 				      struct mlx5hws_matcher_attr *attr)
53 {
54 	memset(attr, 0, sizeof(*attr));
55 
56 	attr->priority = priority;
57 	attr->optimize_using_rule_idx = 0;
58 	attr->mode = MLX5HWS_MATCHER_RESOURCE_MODE_RULE;
59 	attr->optimize_flow_src = MLX5HWS_MATCHER_FLOW_SRC_ANY;
60 	attr->insert_mode = MLX5HWS_MATCHER_INSERT_BY_HASH;
61 	attr->distribute_mode = MLX5HWS_MATCHER_DISTRIBUTE_BY_HASH;
62 	attr->size[MLX5HWS_MATCHER_SIZE_TYPE_RX].rule.num_log = size_log_rx;
63 	attr->size[MLX5HWS_MATCHER_SIZE_TYPE_TX].rule.num_log = size_log_tx;
64 	attr->resizable = true;
65 	attr->max_num_of_at_attach = MLX5HWS_BWC_MATCHER_ATTACH_AT_NUM;
66 }
67 
68 static int
hws_bwc_matcher_move_all_simple(struct mlx5hws_bwc_matcher * bwc_matcher)69 hws_bwc_matcher_move_all_simple(struct mlx5hws_bwc_matcher *bwc_matcher)
70 {
71 	struct mlx5hws_context *ctx = bwc_matcher->matcher->tbl->ctx;
72 	struct mlx5hws_matcher *matcher = bwc_matcher->matcher;
73 	int drain_error = 0, move_error = 0, poll_error = 0;
74 	u16 bwc_queues = mlx5hws_bwc_queues(ctx);
75 	struct mlx5hws_rule_attr rule_attr;
76 	struct mlx5hws_bwc_rule *bwc_rule;
77 	struct mlx5hws_send_engine *queue;
78 	struct list_head *rules_list;
79 	u32 pending_rules;
80 	int i, ret = 0;
81 	bool drain;
82 
83 	mlx5hws_bwc_rule_fill_attr(bwc_matcher, 0, 0, &rule_attr);
84 
85 	for (i = 0; i < bwc_queues; i++) {
86 		if (list_empty(&bwc_matcher->rules[i]))
87 			continue;
88 
89 		pending_rules = 0;
90 		rule_attr.queue_id = mlx5hws_bwc_get_queue_id(ctx, i);
91 		rules_list = &bwc_matcher->rules[i];
92 
93 		list_for_each_entry(bwc_rule, rules_list, list_node) {
94 			ret = mlx5hws_matcher_resize_rule_move(matcher,
95 							       bwc_rule->rule,
96 							       &rule_attr);
97 			if (unlikely(ret)) {
98 				if (!move_error) {
99 					mlx5hws_err(ctx,
100 						    "Moving BWC rule: move failed (%d), attempting to move rest of the rules\n",
101 						    ret);
102 					move_error = ret;
103 				}
104 				/* Rule wasn't queued, no need to poll */
105 				continue;
106 			}
107 
108 			pending_rules++;
109 			drain = pending_rules >=
110 				hws_bwc_get_burst_th(ctx, rule_attr.queue_id);
111 			ret = mlx5hws_bwc_queue_poll(ctx,
112 						     rule_attr.queue_id,
113 						     &pending_rules,
114 						     drain);
115 			if (unlikely(ret)) {
116 				if (ret == -ETIMEDOUT) {
117 					mlx5hws_err(ctx,
118 						    "Moving BWC rule: timeout polling for completions (%d), aborting rehash\n",
119 						    ret);
120 					return ret;
121 				}
122 				if (!poll_error) {
123 					mlx5hws_err(ctx,
124 						    "Moving BWC rule: polling for completions failed (%d), attempting to move rest of the rules\n",
125 						    ret);
126 					poll_error = ret;
127 				}
128 			}
129 		}
130 
131 		if (pending_rules) {
132 			queue = &ctx->send_queue[rule_attr.queue_id];
133 			mlx5hws_send_engine_flush_queue(queue);
134 			ret = mlx5hws_bwc_queue_poll(ctx,
135 						     rule_attr.queue_id,
136 						     &pending_rules,
137 						     true);
138 			if (unlikely(ret)) {
139 				if (ret == -ETIMEDOUT) {
140 					mlx5hws_err(ctx,
141 						    "Moving bwc rule: timeout draining completions (%d), aborting rehash\n",
142 						    ret);
143 					return ret;
144 				}
145 				if (!drain_error) {
146 					mlx5hws_err(ctx,
147 						    "Moving bwc rule: drain failed (%d), attempting to move rest of the rules\n",
148 						    ret);
149 					drain_error = ret;
150 				}
151 			}
152 		}
153 	}
154 
155 	/* Return the first error that happened */
156 	if (unlikely(move_error))
157 		return move_error;
158 	if (unlikely(poll_error))
159 		return poll_error;
160 	if (unlikely(drain_error))
161 		return drain_error;
162 
163 	return ret;
164 }
165 
hws_bwc_matcher_move_all(struct mlx5hws_bwc_matcher * bwc_matcher)166 static int hws_bwc_matcher_move_all(struct mlx5hws_bwc_matcher *bwc_matcher)
167 {
168 	switch (bwc_matcher->matcher_type) {
169 	case MLX5HWS_BWC_MATCHER_SIMPLE:
170 		return hws_bwc_matcher_move_all_simple(bwc_matcher);
171 	case MLX5HWS_BWC_MATCHER_COMPLEX_FIRST:
172 		return mlx5hws_bwc_matcher_complex_move_first(bwc_matcher);
173 	case MLX5HWS_BWC_MATCHER_COMPLEX_SUBMATCHER:
174 		return mlx5hws_bwc_matcher_complex_move(bwc_matcher);
175 	default:
176 		return -EINVAL;
177 	}
178 }
179 
hws_bwc_matcher_move(struct mlx5hws_bwc_matcher * bwc_matcher)180 static int hws_bwc_matcher_move(struct mlx5hws_bwc_matcher *bwc_matcher)
181 {
182 	struct mlx5hws_context *ctx = bwc_matcher->matcher->tbl->ctx;
183 	struct mlx5hws_matcher_attr matcher_attr = {0};
184 	struct mlx5hws_matcher *old_matcher;
185 	struct mlx5hws_matcher *new_matcher;
186 	int ret;
187 
188 	hws_bwc_matcher_init_attr(bwc_matcher,
189 				  bwc_matcher->priority,
190 				  bwc_matcher->rx_size.size_log,
191 				  bwc_matcher->tx_size.size_log,
192 				  &matcher_attr);
193 
194 	old_matcher = bwc_matcher->matcher;
195 	new_matcher = mlx5hws_matcher_create(old_matcher->tbl,
196 					     &bwc_matcher->mt, 1,
197 					     bwc_matcher->at,
198 					     bwc_matcher->num_of_at,
199 					     &matcher_attr);
200 	if (!new_matcher) {
201 		mlx5hws_err(ctx, "Rehash error: matcher creation failed\n");
202 		return -ENOMEM;
203 	}
204 
205 	ret = mlx5hws_matcher_resize_set_target(old_matcher, new_matcher);
206 	if (ret) {
207 		mlx5hws_err(ctx, "Rehash error: failed setting resize target\n");
208 		mlx5hws_matcher_destroy(new_matcher);
209 		return ret;
210 	}
211 
212 	ret = hws_bwc_matcher_move_all(bwc_matcher);
213 	if (ret)
214 		mlx5hws_err(ctx, "Rehash error: moving rules failed, attempting to remove the old matcher\n");
215 
216 	/* Error during rehash can't be rolled back.
217 	 * The best option here is to allow the rehash to complete and remove
218 	 * the old matcher - can't leave the matcher in the 'in_resize' state.
219 	 */
220 
221 	bwc_matcher->matcher = new_matcher;
222 	mlx5hws_matcher_destroy(old_matcher);
223 
224 	return ret;
225 }
226 
mlx5hws_bwc_matcher_create_simple(struct mlx5hws_bwc_matcher * bwc_matcher,struct mlx5hws_table * table,u32 priority,u8 match_criteria_enable,struct mlx5hws_match_parameters * mask,enum mlx5hws_action_type action_types[])227 int mlx5hws_bwc_matcher_create_simple(struct mlx5hws_bwc_matcher *bwc_matcher,
228 				      struct mlx5hws_table *table,
229 				      u32 priority,
230 				      u8 match_criteria_enable,
231 				      struct mlx5hws_match_parameters *mask,
232 				      enum mlx5hws_action_type action_types[])
233 {
234 	enum mlx5hws_action_type init_action_types[1] = { MLX5HWS_ACTION_TYP_LAST };
235 	struct mlx5hws_context *ctx = table->ctx;
236 	u16 bwc_queues = mlx5hws_bwc_queues(ctx);
237 	struct mlx5hws_matcher_attr attr = {0};
238 	int i;
239 
240 	bwc_matcher->rules = kzalloc_objs(*bwc_matcher->rules, bwc_queues);
241 	if (!bwc_matcher->rules)
242 		goto err;
243 
244 	for (i = 0; i < bwc_queues; i++)
245 		INIT_LIST_HEAD(&bwc_matcher->rules[i]);
246 
247 	hws_bwc_matcher_init_attr(bwc_matcher,
248 				  priority,
249 				  bwc_matcher->rx_size.size_log,
250 				  bwc_matcher->tx_size.size_log,
251 				  &attr);
252 
253 	bwc_matcher->matcher_type = MLX5HWS_BWC_MATCHER_SIMPLE;
254 	bwc_matcher->priority = priority;
255 
256 	bwc_matcher->size_of_at_array = MLX5HWS_BWC_MATCHER_ATTACH_AT_NUM;
257 	bwc_matcher->at = kzalloc_objs(*bwc_matcher->at,
258 				       bwc_matcher->size_of_at_array);
259 	if (!bwc_matcher->at)
260 		goto free_bwc_matcher_rules;
261 
262 	/* create dummy action template */
263 	bwc_matcher->at[0] =
264 		mlx5hws_action_template_create(action_types ?
265 					       action_types : init_action_types);
266 	if (!bwc_matcher->at[0]) {
267 		mlx5hws_err(table->ctx, "BWC matcher: failed creating action template\n");
268 		goto free_bwc_matcher_at_array;
269 	}
270 
271 	bwc_matcher->num_of_at = 1;
272 
273 	bwc_matcher->mt = mlx5hws_match_template_create(ctx,
274 							mask->match_buf,
275 							mask->match_sz,
276 							match_criteria_enable);
277 	if (!bwc_matcher->mt) {
278 		mlx5hws_err(table->ctx, "BWC matcher: failed creating match template\n");
279 		goto free_at;
280 	}
281 
282 	bwc_matcher->matcher = mlx5hws_matcher_create(table,
283 						      &bwc_matcher->mt, 1,
284 						      &bwc_matcher->at[0],
285 						      bwc_matcher->num_of_at,
286 						      &attr);
287 	if (!bwc_matcher->matcher) {
288 		mlx5hws_err(table->ctx, "BWC matcher: failed creating HWS matcher\n");
289 		goto free_mt;
290 	}
291 
292 	return 0;
293 
294 free_mt:
295 	mlx5hws_match_template_destroy(bwc_matcher->mt);
296 free_at:
297 	mlx5hws_action_template_destroy(bwc_matcher->at[0]);
298 free_bwc_matcher_at_array:
299 	kfree(bwc_matcher->at);
300 free_bwc_matcher_rules:
301 	kfree(bwc_matcher->rules);
302 err:
303 	return -EINVAL;
304 }
305 
306 static void
hws_bwc_matcher_init_size_rxtx(struct mlx5hws_bwc_matcher_size * size)307 hws_bwc_matcher_init_size_rxtx(struct mlx5hws_bwc_matcher_size *size)
308 {
309 	size->size_log = MLX5HWS_BWC_MATCHER_INIT_SIZE_LOG;
310 	atomic_set(&size->num_of_rules, 0);
311 	atomic_set(&size->rehash_required, false);
312 }
313 
hws_bwc_matcher_init_size(struct mlx5hws_bwc_matcher * bwc_matcher)314 static void hws_bwc_matcher_init_size(struct mlx5hws_bwc_matcher *bwc_matcher)
315 {
316 	hws_bwc_matcher_init_size_rxtx(&bwc_matcher->rx_size);
317 	hws_bwc_matcher_init_size_rxtx(&bwc_matcher->tx_size);
318 }
319 
320 struct mlx5hws_bwc_matcher *
mlx5hws_bwc_matcher_create(struct mlx5hws_table * table,u32 priority,u8 match_criteria_enable,struct mlx5hws_match_parameters * mask)321 mlx5hws_bwc_matcher_create(struct mlx5hws_table *table,
322 			   u32 priority,
323 			   u8 match_criteria_enable,
324 			   struct mlx5hws_match_parameters *mask)
325 {
326 	struct mlx5hws_bwc_matcher *bwc_matcher;
327 	bool is_complex;
328 	int ret;
329 
330 	if (!mlx5hws_context_bwc_supported(table->ctx)) {
331 		mlx5hws_err(table->ctx,
332 			    "BWC matcher: context created w/o BWC API compatibility\n");
333 		return NULL;
334 	}
335 
336 	bwc_matcher = kzalloc_obj(*bwc_matcher);
337 	if (!bwc_matcher)
338 		return NULL;
339 
340 	hws_bwc_matcher_init_size(bwc_matcher);
341 
342 	/* Check if the required match params can be all matched
343 	 * in single STE, otherwise complex matcher is needed.
344 	 */
345 
346 	is_complex = mlx5hws_bwc_match_params_is_complex(table->ctx, match_criteria_enable, mask);
347 	if (is_complex)
348 		ret = mlx5hws_bwc_matcher_create_complex(bwc_matcher,
349 							 table,
350 							 priority,
351 							 match_criteria_enable,
352 							 mask);
353 	else
354 		ret = mlx5hws_bwc_matcher_create_simple(bwc_matcher,
355 							table,
356 							priority,
357 							match_criteria_enable,
358 							mask,
359 							NULL);
360 	if (ret)
361 		goto free_bwc_matcher;
362 
363 	return bwc_matcher;
364 
365 free_bwc_matcher:
366 	kfree(bwc_matcher);
367 
368 	return NULL;
369 }
370 
mlx5hws_bwc_matcher_destroy_simple(struct mlx5hws_bwc_matcher * bwc_matcher)371 int mlx5hws_bwc_matcher_destroy_simple(struct mlx5hws_bwc_matcher *bwc_matcher)
372 {
373 	int i;
374 
375 	mlx5hws_matcher_destroy(bwc_matcher->matcher);
376 	bwc_matcher->matcher = NULL;
377 
378 	for (i = 0; i < bwc_matcher->num_of_at; i++)
379 		mlx5hws_action_template_destroy(bwc_matcher->at[i]);
380 	kfree(bwc_matcher->at);
381 
382 	mlx5hws_match_template_destroy(bwc_matcher->mt);
383 	kfree(bwc_matcher->rules);
384 
385 	return 0;
386 }
387 
mlx5hws_bwc_matcher_destroy(struct mlx5hws_bwc_matcher * bwc_matcher)388 int mlx5hws_bwc_matcher_destroy(struct mlx5hws_bwc_matcher *bwc_matcher)
389 {
390 	u32 rx_rules = atomic_read(&bwc_matcher->rx_size.num_of_rules);
391 	u32 tx_rules = atomic_read(&bwc_matcher->tx_size.num_of_rules);
392 
393 	if (rx_rules || tx_rules)
394 		mlx5hws_err(bwc_matcher->matcher->tbl->ctx,
395 			    "BWC matcher destroy: matcher still has %u RX and %u TX rules\n",
396 			    rx_rules, tx_rules);
397 
398 	if (bwc_matcher->matcher_type == MLX5HWS_BWC_MATCHER_COMPLEX_FIRST)
399 		mlx5hws_bwc_matcher_destroy_complex(bwc_matcher);
400 	else
401 		mlx5hws_bwc_matcher_destroy_simple(bwc_matcher);
402 
403 	kfree(bwc_matcher);
404 	return 0;
405 }
406 
mlx5hws_bwc_queue_poll(struct mlx5hws_context * ctx,u16 queue_id,u32 * pending_rules,bool drain)407 int mlx5hws_bwc_queue_poll(struct mlx5hws_context *ctx,
408 			   u16 queue_id,
409 			   u32 *pending_rules,
410 			   bool drain)
411 {
412 	unsigned long timeout = jiffies +
413 				secs_to_jiffies(MLX5HWS_BWC_POLLING_TIMEOUT);
414 	struct mlx5hws_flow_op_result comp[MLX5HWS_BWC_MATCHER_REHASH_BURST_TH];
415 	u16 burst_th = hws_bwc_get_burst_th(ctx, queue_id);
416 	bool got_comp = *pending_rules >= burst_th;
417 	bool queue_full;
418 	int err = 0;
419 	int ret;
420 	int i;
421 
422 	/* Check if there are any completions at all */
423 	if (!got_comp && !drain)
424 		return 0;
425 
426 	if (unlikely(ctx->mdev->state == MLX5_DEVICE_STATE_INTERNAL_ERROR)) {
427 		/* If the device is down for any reason (e.g. FLR), the HW will
428 		 * no longer generate completions.
429 		 * Note that ETIMEDOUT is returned here because the BWC layer
430 		 * already has a special handling for timeouts - it breaks the
431 		 * rehash / resize / shrink loops to avoid chain of timeouts.
432 		 */
433 		mlx5_core_warn_once(ctx->mdev,
434 				    "BWC poll: device is down, polling for completion aborted\n");
435 		return -ETIMEDOUT;
436 	}
437 
438 	queue_full = mlx5hws_send_engine_full(&ctx->send_queue[queue_id]);
439 	while (queue_full || ((got_comp || drain) && *pending_rules)) {
440 		ret = mlx5hws_send_queue_poll(ctx, queue_id, comp, burst_th);
441 		if (unlikely(ret < 0)) {
442 			mlx5hws_err(ctx, "BWC poll error: polling queue %d returned %d\n",
443 				    queue_id, ret);
444 			return -EINVAL;
445 		}
446 
447 		if (ret) {
448 			(*pending_rules) -= ret;
449 			for (i = 0; i < ret; i++) {
450 				if (unlikely(comp[i].status != MLX5HWS_FLOW_OP_SUCCESS)) {
451 					mlx5hws_err(ctx,
452 						    "BWC poll error: polling queue %d returned completion with error\n",
453 						    queue_id);
454 					err = -EINVAL;
455 				}
456 			}
457 			queue_full = false;
458 		}
459 
460 		got_comp = !!ret;
461 
462 		if (unlikely(!got_comp && time_after(jiffies, timeout))) {
463 			mlx5hws_err(ctx, "BWC poll error: polling queue %d - TIMEOUT\n", queue_id);
464 			return -ETIMEDOUT;
465 		}
466 	}
467 
468 	return err;
469 }
470 
471 void
mlx5hws_bwc_rule_fill_attr(struct mlx5hws_bwc_matcher * bwc_matcher,u16 bwc_queue_idx,u32 flow_source,struct mlx5hws_rule_attr * rule_attr)472 mlx5hws_bwc_rule_fill_attr(struct mlx5hws_bwc_matcher *bwc_matcher,
473 			   u16 bwc_queue_idx,
474 			   u32 flow_source,
475 			   struct mlx5hws_rule_attr *rule_attr)
476 {
477 	struct mlx5hws_context *ctx = bwc_matcher->matcher->tbl->ctx;
478 
479 	/* no use of INSERT_BY_INDEX in bwc rule */
480 	rule_attr->rule_idx = 0;
481 
482 	/* notify HW at each rule insertion/deletion */
483 	rule_attr->burst = 0;
484 
485 	/* We don't need user data, but the API requires it to exist */
486 	rule_attr->user_data = (void *)0xFACADE;
487 
488 	rule_attr->queue_id = mlx5hws_bwc_get_queue_id(ctx, bwc_queue_idx);
489 	rule_attr->flow_source = flow_source;
490 }
491 
492 struct mlx5hws_bwc_rule *
mlx5hws_bwc_rule_alloc(struct mlx5hws_bwc_matcher * bwc_matcher)493 mlx5hws_bwc_rule_alloc(struct mlx5hws_bwc_matcher *bwc_matcher)
494 {
495 	struct mlx5hws_bwc_rule *bwc_rule;
496 
497 	bwc_rule = kzalloc_obj(*bwc_rule);
498 	if (unlikely(!bwc_rule))
499 		goto out_err;
500 
501 	bwc_rule->rule = kzalloc_obj(*bwc_rule->rule);
502 	if (unlikely(!bwc_rule->rule))
503 		goto free_rule;
504 
505 	bwc_rule->bwc_matcher = bwc_matcher;
506 	return bwc_rule;
507 
508 free_rule:
509 	kfree(bwc_rule);
510 out_err:
511 	return NULL;
512 }
513 
mlx5hws_bwc_rule_free(struct mlx5hws_bwc_rule * bwc_rule)514 void mlx5hws_bwc_rule_free(struct mlx5hws_bwc_rule *bwc_rule)
515 {
516 	if (likely(bwc_rule->rule))
517 		kfree(bwc_rule->rule);
518 	kfree(bwc_rule);
519 }
520 
hws_bwc_rule_list_add(struct mlx5hws_bwc_rule * bwc_rule,u16 idx)521 static void hws_bwc_rule_list_add(struct mlx5hws_bwc_rule *bwc_rule, u16 idx)
522 {
523 	struct mlx5hws_bwc_matcher *bwc_matcher = bwc_rule->bwc_matcher;
524 
525 	bwc_rule->bwc_queue_idx = idx;
526 	list_add(&bwc_rule->list_node, &bwc_matcher->rules[idx]);
527 }
528 
hws_bwc_rule_list_remove(struct mlx5hws_bwc_rule * bwc_rule)529 static void hws_bwc_rule_list_remove(struct mlx5hws_bwc_rule *bwc_rule)
530 {
531 	list_del_init(&bwc_rule->list_node);
532 }
533 
534 static int
hws_bwc_rule_destroy_hws_async(struct mlx5hws_bwc_rule * bwc_rule,struct mlx5hws_rule_attr * attr)535 hws_bwc_rule_destroy_hws_async(struct mlx5hws_bwc_rule *bwc_rule,
536 			       struct mlx5hws_rule_attr *attr)
537 {
538 	return mlx5hws_rule_destroy(bwc_rule->rule, attr);
539 }
540 
541 static int
hws_bwc_rule_destroy_hws_sync(struct mlx5hws_bwc_rule * bwc_rule,struct mlx5hws_rule_attr * rule_attr)542 hws_bwc_rule_destroy_hws_sync(struct mlx5hws_bwc_rule *bwc_rule,
543 			      struct mlx5hws_rule_attr *rule_attr)
544 {
545 	struct mlx5hws_context *ctx = bwc_rule->bwc_matcher->matcher->tbl->ctx;
546 	u32 expected_completions = 1;
547 	int ret;
548 
549 	ret = hws_bwc_rule_destroy_hws_async(bwc_rule, rule_attr);
550 	if (unlikely(ret))
551 		return ret;
552 
553 	ret = mlx5hws_bwc_queue_poll(ctx, rule_attr->queue_id,
554 				     &expected_completions, true);
555 	if (unlikely(ret))
556 		return ret;
557 
558 	if (unlikely(bwc_rule->rule->status != MLX5HWS_RULE_STATUS_DELETED &&
559 		     bwc_rule->rule->status != MLX5HWS_RULE_STATUS_DELETING)) {
560 		mlx5hws_err(ctx, "Failed destroying BWC rule: rule status %d\n",
561 			    bwc_rule->rule->status);
562 		return -EINVAL;
563 	}
564 
565 	return 0;
566 }
567 
hws_bwc_rule_cnt_dec(struct mlx5hws_bwc_rule * bwc_rule)568 static void hws_bwc_rule_cnt_dec(struct mlx5hws_bwc_rule *bwc_rule)
569 {
570 	struct mlx5hws_bwc_matcher *bwc_matcher = bwc_rule->bwc_matcher;
571 
572 	if (!bwc_rule->skip_rx)
573 		atomic_dec(&bwc_matcher->rx_size.num_of_rules);
574 	if (!bwc_rule->skip_tx)
575 		atomic_dec(&bwc_matcher->tx_size.num_of_rules);
576 }
577 
578 static int
hws_bwc_matcher_rehash_shrink(struct mlx5hws_bwc_matcher * bwc_matcher)579 hws_bwc_matcher_rehash_shrink(struct mlx5hws_bwc_matcher *bwc_matcher)
580 {
581 	struct mlx5hws_bwc_matcher_size *rx_size = &bwc_matcher->rx_size;
582 	struct mlx5hws_bwc_matcher_size *tx_size = &bwc_matcher->tx_size;
583 
584 	/* It is possible that another thread has added a rule.
585 	 * Need to check again if we really need rehash/shrink.
586 	 */
587 	if (atomic_read(&rx_size->num_of_rules) ||
588 	    atomic_read(&tx_size->num_of_rules))
589 		return 0;
590 
591 	/* If the current matcher RX/TX size is already at its initial size. */
592 	if (rx_size->size_log == MLX5HWS_BWC_MATCHER_INIT_SIZE_LOG &&
593 	    tx_size->size_log == MLX5HWS_BWC_MATCHER_INIT_SIZE_LOG)
594 		return 0;
595 
596 	/* Now we've done all the checking - do the shrinking:
597 	 *  - reset match RTC size to the initial size
598 	 *  - create new matcher
599 	 *  - move the rules, which will not do anything as the matcher is empty
600 	 *  - destroy the old matcher
601 	 */
602 
603 	rx_size->size_log = MLX5HWS_BWC_MATCHER_INIT_SIZE_LOG;
604 	tx_size->size_log = MLX5HWS_BWC_MATCHER_INIT_SIZE_LOG;
605 
606 	return hws_bwc_matcher_move(bwc_matcher);
607 }
608 
hws_bwc_rule_cnt_dec_with_shrink(struct mlx5hws_bwc_rule * bwc_rule,u16 bwc_queue_idx)609 static int hws_bwc_rule_cnt_dec_with_shrink(struct mlx5hws_bwc_rule *bwc_rule,
610 					    u16 bwc_queue_idx)
611 {
612 	struct mlx5hws_bwc_matcher *bwc_matcher = bwc_rule->bwc_matcher;
613 	struct mlx5hws_context *ctx = bwc_matcher->matcher->tbl->ctx;
614 	struct mutex *queue_lock; /* Protect the queue */
615 	int ret;
616 
617 	hws_bwc_rule_cnt_dec(bwc_rule);
618 
619 	if (atomic_read(&bwc_matcher->rx_size.num_of_rules) ||
620 	    atomic_read(&bwc_matcher->tx_size.num_of_rules))
621 		return 0;
622 
623 	/* Matcher has no more rules - shrink it to save ICM. */
624 
625 	queue_lock = hws_bwc_get_queue_lock(ctx, bwc_queue_idx);
626 	mutex_unlock(queue_lock);
627 
628 	hws_bwc_lock_all_queues(ctx);
629 	ret = hws_bwc_matcher_rehash_shrink(bwc_matcher);
630 	hws_bwc_unlock_all_queues(ctx);
631 
632 	mutex_lock(queue_lock);
633 
634 	if (unlikely(ret))
635 		mlx5hws_err(ctx,
636 			    "BWC rule deletion: shrinking empty matcher failed (%d)\n",
637 			    ret);
638 
639 	return ret;
640 }
641 
mlx5hws_bwc_rule_destroy_simple(struct mlx5hws_bwc_rule * bwc_rule)642 int mlx5hws_bwc_rule_destroy_simple(struct mlx5hws_bwc_rule *bwc_rule)
643 {
644 	struct mlx5hws_bwc_matcher *bwc_matcher = bwc_rule->bwc_matcher;
645 	struct mlx5hws_context *ctx = bwc_matcher->matcher->tbl->ctx;
646 	u16 idx = bwc_rule->bwc_queue_idx;
647 	struct mlx5hws_rule_attr attr;
648 	struct mutex *queue_lock; /* Protect the queue */
649 	int ret;
650 
651 	mlx5hws_bwc_rule_fill_attr(bwc_matcher, idx, 0, &attr);
652 
653 	queue_lock = hws_bwc_get_queue_lock(ctx, idx);
654 
655 	mutex_lock(queue_lock);
656 
657 	ret = hws_bwc_rule_destroy_hws_sync(bwc_rule, &attr);
658 	hws_bwc_rule_list_remove(bwc_rule);
659 	hws_bwc_rule_cnt_dec_with_shrink(bwc_rule, idx);
660 
661 	mutex_unlock(queue_lock);
662 
663 	return ret;
664 }
665 
mlx5hws_bwc_rule_destroy(struct mlx5hws_bwc_rule * bwc_rule)666 int mlx5hws_bwc_rule_destroy(struct mlx5hws_bwc_rule *bwc_rule)
667 {
668 	bool is_complex = bwc_rule->bwc_matcher->matcher_type ==
669 			  MLX5HWS_BWC_MATCHER_COMPLEX_FIRST;
670 	int ret = 0;
671 
672 	if (is_complex)
673 		ret = mlx5hws_bwc_rule_destroy_complex(bwc_rule);
674 	else
675 		ret = mlx5hws_bwc_rule_destroy_simple(bwc_rule);
676 
677 	mlx5hws_bwc_rule_free(bwc_rule);
678 	return ret;
679 }
680 
681 static int
hws_bwc_rule_create_async(struct mlx5hws_bwc_rule * bwc_rule,u32 * match_param,u8 at_idx,struct mlx5hws_rule_action rule_actions[],struct mlx5hws_rule_attr * rule_attr)682 hws_bwc_rule_create_async(struct mlx5hws_bwc_rule *bwc_rule,
683 			  u32 *match_param,
684 			  u8 at_idx,
685 			  struct mlx5hws_rule_action rule_actions[],
686 			  struct mlx5hws_rule_attr *rule_attr)
687 {
688 	return mlx5hws_rule_create(bwc_rule->bwc_matcher->matcher,
689 				   0, /* only one match template supported */
690 				   match_param,
691 				   at_idx,
692 				   rule_actions,
693 				   rule_attr,
694 				   bwc_rule->rule);
695 }
696 
697 static int
hws_bwc_rule_create_sync(struct mlx5hws_bwc_rule * bwc_rule,u32 * match_param,u8 at_idx,struct mlx5hws_rule_action rule_actions[],struct mlx5hws_rule_attr * rule_attr)698 hws_bwc_rule_create_sync(struct mlx5hws_bwc_rule *bwc_rule,
699 			 u32 *match_param,
700 			 u8 at_idx,
701 			 struct mlx5hws_rule_action rule_actions[],
702 			 struct mlx5hws_rule_attr *rule_attr)
703 
704 {
705 	struct mlx5hws_context *ctx = bwc_rule->bwc_matcher->matcher->tbl->ctx;
706 	u32 expected_completions = 1;
707 	int ret;
708 
709 	ret = hws_bwc_rule_create_async(bwc_rule, match_param,
710 					at_idx, rule_actions,
711 					rule_attr);
712 	if (unlikely(ret))
713 		return ret;
714 
715 	return mlx5hws_bwc_queue_poll(ctx, rule_attr->queue_id,
716 				      &expected_completions, true);
717 }
718 
719 static int
hws_bwc_rule_update_sync(struct mlx5hws_bwc_rule * bwc_rule,u8 at_idx,struct mlx5hws_rule_action rule_actions[],struct mlx5hws_rule_attr * rule_attr)720 hws_bwc_rule_update_sync(struct mlx5hws_bwc_rule *bwc_rule,
721 			 u8 at_idx,
722 			 struct mlx5hws_rule_action rule_actions[],
723 			 struct mlx5hws_rule_attr *rule_attr)
724 {
725 	struct mlx5hws_bwc_matcher *bwc_matcher = bwc_rule->bwc_matcher;
726 	struct mlx5hws_context *ctx = bwc_matcher->matcher->tbl->ctx;
727 	u32 expected_completions = 1;
728 	int ret;
729 
730 	ret = mlx5hws_rule_action_update(bwc_rule->rule,
731 					 at_idx,
732 					 rule_actions,
733 					 rule_attr);
734 	if (unlikely(ret))
735 		return ret;
736 
737 	ret = mlx5hws_bwc_queue_poll(ctx, rule_attr->queue_id,
738 				     &expected_completions, true);
739 	if (unlikely(ret))
740 		mlx5hws_err(ctx, "Failed updating BWC rule (%d)\n", ret);
741 
742 	return ret;
743 }
744 
745 static bool
hws_bwc_matcher_size_maxed_out(struct mlx5hws_bwc_matcher * bwc_matcher,struct mlx5hws_bwc_matcher_size * size)746 hws_bwc_matcher_size_maxed_out(struct mlx5hws_bwc_matcher *bwc_matcher,
747 			       struct mlx5hws_bwc_matcher_size *size)
748 {
749 	struct mlx5hws_cmd_query_caps *caps = bwc_matcher->matcher->tbl->ctx->caps;
750 
751 	/* check the match RTC size */
752 	return (size->size_log + MLX5HWS_MATCHER_ASSURED_MAIN_TBL_DEPTH +
753 		MLX5HWS_BWC_MATCHER_SIZE_LOG_STEP) >
754 	       (caps->ste_alloc_log_max - 1);
755 }
756 
757 static bool
hws_bwc_matcher_rehash_size_needed(struct mlx5hws_bwc_matcher * bwc_matcher,struct mlx5hws_bwc_matcher_size * size,u32 num_of_rules)758 hws_bwc_matcher_rehash_size_needed(struct mlx5hws_bwc_matcher *bwc_matcher,
759 				   struct mlx5hws_bwc_matcher_size *size,
760 				   u32 num_of_rules)
761 {
762 	if (unlikely(hws_bwc_matcher_size_maxed_out(bwc_matcher, size)))
763 		return false;
764 
765 	if (unlikely((num_of_rules * 100 / MLX5HWS_BWC_MATCHER_REHASH_PERCENT_TH) >=
766 		     (1UL << size->size_log)))
767 		return true;
768 
769 	return false;
770 }
771 
772 static void
hws_bwc_rule_actions_to_action_types(struct mlx5hws_rule_action rule_actions[],enum mlx5hws_action_type action_types[])773 hws_bwc_rule_actions_to_action_types(struct mlx5hws_rule_action rule_actions[],
774 				     enum mlx5hws_action_type action_types[])
775 {
776 	int i = 0;
777 
778 	for (i = 0;
779 	     rule_actions[i].action && (rule_actions[i].action->type != MLX5HWS_ACTION_TYP_LAST);
780 	     i++) {
781 		action_types[i] = (enum mlx5hws_action_type)rule_actions[i].action->type;
782 	}
783 
784 	action_types[i] = MLX5HWS_ACTION_TYP_LAST;
785 }
786 
787 static int
hws_bwc_matcher_extend_at(struct mlx5hws_bwc_matcher * bwc_matcher,struct mlx5hws_rule_action rule_actions[])788 hws_bwc_matcher_extend_at(struct mlx5hws_bwc_matcher *bwc_matcher,
789 			  struct mlx5hws_rule_action rule_actions[])
790 {
791 	enum mlx5hws_action_type action_types[MLX5HWS_BWC_MAX_ACTS];
792 	void *p;
793 
794 	if (unlikely(bwc_matcher->num_of_at >= bwc_matcher->size_of_at_array)) {
795 		if (bwc_matcher->size_of_at_array >= MLX5HWS_MATCHER_MAX_AT)
796 			return -ENOMEM;
797 		bwc_matcher->size_of_at_array *= 2;
798 		p = krealloc(bwc_matcher->at,
799 			     bwc_matcher->size_of_at_array *
800 				     sizeof(*bwc_matcher->at),
801 			     __GFP_ZERO | GFP_KERNEL);
802 		if (!p) {
803 			bwc_matcher->size_of_at_array /= 2;
804 			return -ENOMEM;
805 		}
806 
807 		bwc_matcher->at = p;
808 	}
809 
810 	hws_bwc_rule_actions_to_action_types(rule_actions, action_types);
811 
812 	bwc_matcher->at[bwc_matcher->num_of_at] =
813 		mlx5hws_action_template_create(action_types);
814 
815 	if (unlikely(!bwc_matcher->at[bwc_matcher->num_of_at]))
816 		return -ENOMEM;
817 
818 	bwc_matcher->num_of_at++;
819 	return 0;
820 }
821 
822 static int
hws_bwc_matcher_extend_size(struct mlx5hws_bwc_matcher * bwc_matcher,struct mlx5hws_bwc_matcher_size * size)823 hws_bwc_matcher_extend_size(struct mlx5hws_bwc_matcher *bwc_matcher,
824 			    struct mlx5hws_bwc_matcher_size *size)
825 {
826 	struct mlx5hws_context *ctx = bwc_matcher->matcher->tbl->ctx;
827 	struct mlx5hws_cmd_query_caps *caps = ctx->caps;
828 
829 	if (unlikely(hws_bwc_matcher_size_maxed_out(bwc_matcher, size))) {
830 		mlx5hws_err(ctx, "Can't resize matcher: depth exceeds limit %d\n",
831 			    caps->rtc_log_depth_max);
832 		return -ENOMEM;
833 	}
834 
835 	size->size_log = min(size->size_log + MLX5HWS_BWC_MATCHER_SIZE_LOG_STEP,
836 			     caps->ste_alloc_log_max -
837 				     MLX5HWS_MATCHER_ASSURED_MAIN_TBL_DEPTH);
838 
839 	return 0;
840 }
841 
842 static int
hws_bwc_matcher_find_at(struct mlx5hws_bwc_matcher * bwc_matcher,struct mlx5hws_rule_action rule_actions[])843 hws_bwc_matcher_find_at(struct mlx5hws_bwc_matcher *bwc_matcher,
844 			struct mlx5hws_rule_action rule_actions[])
845 {
846 	enum mlx5hws_action_type *action_type_arr;
847 	int i, j;
848 
849 	/* start from index 1 - first action template is a dummy */
850 	for (i = 1; i < bwc_matcher->num_of_at; i++) {
851 		j = 0;
852 		action_type_arr = bwc_matcher->at[i]->action_type_arr;
853 
854 		while (rule_actions[j].action &&
855 		       rule_actions[j].action->type != MLX5HWS_ACTION_TYP_LAST) {
856 			if (action_type_arr[j] != rule_actions[j].action->type)
857 				break;
858 			j++;
859 		}
860 
861 		if (action_type_arr[j] == MLX5HWS_ACTION_TYP_LAST &&
862 		    (!rule_actions[j].action ||
863 		     rule_actions[j].action->type == MLX5HWS_ACTION_TYP_LAST))
864 			return i;
865 	}
866 
867 	return -1;
868 }
869 
870 static int
hws_bwc_matcher_rehash_size(struct mlx5hws_bwc_matcher * bwc_matcher)871 hws_bwc_matcher_rehash_size(struct mlx5hws_bwc_matcher *bwc_matcher)
872 {
873 	bool need_rx_rehash, need_tx_rehash;
874 	int ret;
875 
876 	need_rx_rehash = atomic_read(&bwc_matcher->rx_size.rehash_required);
877 	need_tx_rehash = atomic_read(&bwc_matcher->tx_size.rehash_required);
878 
879 	/* It is possible that another rule has already performed rehash.
880 	 * Need to check again if we really need rehash.
881 	 */
882 	if (!need_rx_rehash && !need_tx_rehash)
883 		return 0;
884 
885 	/* If the current matcher RX/TX size is already at its max size,
886 	 * it can't be rehashed.
887 	 */
888 	if (need_rx_rehash &&
889 	    hws_bwc_matcher_size_maxed_out(bwc_matcher,
890 					   &bwc_matcher->rx_size)) {
891 		atomic_set(&bwc_matcher->rx_size.rehash_required, false);
892 		need_rx_rehash = false;
893 	}
894 	if (need_tx_rehash &&
895 	    hws_bwc_matcher_size_maxed_out(bwc_matcher,
896 					   &bwc_matcher->tx_size)) {
897 		atomic_set(&bwc_matcher->tx_size.rehash_required, false);
898 		need_tx_rehash = false;
899 	}
900 
901 	/* If both RX and TX rehash flags are now off, it means that whatever
902 	 * we wanted to rehash is now at its max size - no rehash can be done.
903 	 * Return and try adding the rule again - perhaps there was some change.
904 	 */
905 	if (!need_rx_rehash && !need_tx_rehash)
906 		return 0;
907 
908 	/* Now we're done all the checking - do the rehash:
909 	 *  - extend match RTC size
910 	 *  - create new matcher
911 	 *  - move all the rules to the new matcher
912 	 *  - destroy the old matcher
913 	 */
914 	atomic_set(&bwc_matcher->rx_size.rehash_required, false);
915 	atomic_set(&bwc_matcher->tx_size.rehash_required, false);
916 
917 	if (need_rx_rehash) {
918 		ret = hws_bwc_matcher_extend_size(bwc_matcher,
919 						  &bwc_matcher->rx_size);
920 		if (ret)
921 			return ret;
922 	}
923 
924 	if (need_tx_rehash) {
925 		ret = hws_bwc_matcher_extend_size(bwc_matcher,
926 						  &bwc_matcher->tx_size);
927 		if (ret)
928 			return ret;
929 	}
930 
931 	return hws_bwc_matcher_move(bwc_matcher);
932 }
933 
hws_bwc_rule_get_at_idx(struct mlx5hws_bwc_rule * bwc_rule,struct mlx5hws_rule_action rule_actions[],u16 bwc_queue_idx)934 static int hws_bwc_rule_get_at_idx(struct mlx5hws_bwc_rule *bwc_rule,
935 				   struct mlx5hws_rule_action rule_actions[],
936 				   u16 bwc_queue_idx)
937 {
938 	struct mlx5hws_bwc_matcher *bwc_matcher = bwc_rule->bwc_matcher;
939 	struct mlx5hws_context *ctx = bwc_matcher->matcher->tbl->ctx;
940 	struct mutex *queue_lock; /* Protect the queue */
941 	int at_idx, ret;
942 
943 	/* check if rehash needed due to missing action template */
944 	at_idx = hws_bwc_matcher_find_at(bwc_matcher, rule_actions);
945 	if (likely(at_idx >= 0))
946 		return at_idx;
947 
948 	/* we need to extend BWC matcher action templates array */
949 	queue_lock = hws_bwc_get_queue_lock(ctx, bwc_queue_idx);
950 	mutex_unlock(queue_lock);
951 	hws_bwc_lock_all_queues(ctx);
952 
953 	/* check again - perhaps other thread already did extend_at */
954 	at_idx = hws_bwc_matcher_find_at(bwc_matcher, rule_actions);
955 	if (at_idx >= 0)
956 		goto out;
957 
958 	ret = hws_bwc_matcher_extend_at(bwc_matcher, rule_actions);
959 	if (unlikely(ret)) {
960 		mlx5hws_err(ctx, "BWC rule: failed extending AT (%d)", ret);
961 		at_idx = -EINVAL;
962 		goto out;
963 	}
964 
965 	/* action templates array was extended, we need the last idx */
966 	at_idx = bwc_matcher->num_of_at - 1;
967 	ret = mlx5hws_matcher_attach_at(bwc_matcher->matcher,
968 					bwc_matcher->at[at_idx]);
969 	if (unlikely(ret)) {
970 		mlx5hws_err(ctx, "BWC rule: failed attaching new AT (%d)", ret);
971 		at_idx = -EINVAL;
972 		goto out;
973 	}
974 
975 out:
976 	hws_bwc_unlock_all_queues(ctx);
977 	mutex_lock(queue_lock);
978 	return at_idx;
979 }
980 
hws_bwc_rule_cnt_inc_rxtx(struct mlx5hws_bwc_rule * bwc_rule,struct mlx5hws_bwc_matcher_size * size)981 static void hws_bwc_rule_cnt_inc_rxtx(struct mlx5hws_bwc_rule *bwc_rule,
982 				      struct mlx5hws_bwc_matcher_size *size)
983 {
984 	u32 num_of_rules = atomic_inc_return(&size->num_of_rules);
985 
986 	if (unlikely(hws_bwc_matcher_rehash_size_needed(bwc_rule->bwc_matcher,
987 							size, num_of_rules)))
988 		atomic_set(&size->rehash_required, true);
989 }
990 
hws_bwc_rule_cnt_inc(struct mlx5hws_bwc_rule * bwc_rule)991 static void hws_bwc_rule_cnt_inc(struct mlx5hws_bwc_rule *bwc_rule)
992 {
993 	struct mlx5hws_bwc_matcher *bwc_matcher = bwc_rule->bwc_matcher;
994 
995 	if (!bwc_rule->skip_rx)
996 		hws_bwc_rule_cnt_inc_rxtx(bwc_rule, &bwc_matcher->rx_size);
997 	if (!bwc_rule->skip_tx)
998 		hws_bwc_rule_cnt_inc_rxtx(bwc_rule, &bwc_matcher->tx_size);
999 }
1000 
hws_bwc_rule_cnt_inc_with_rehash(struct mlx5hws_bwc_rule * bwc_rule,u16 bwc_queue_idx)1001 static int hws_bwc_rule_cnt_inc_with_rehash(struct mlx5hws_bwc_rule *bwc_rule,
1002 					    u16 bwc_queue_idx)
1003 {
1004 	struct mlx5hws_bwc_matcher *bwc_matcher = bwc_rule->bwc_matcher;
1005 	struct mlx5hws_context *ctx = bwc_matcher->matcher->tbl->ctx;
1006 	struct mutex *queue_lock; /* Protect the queue */
1007 	int ret;
1008 
1009 	hws_bwc_rule_cnt_inc(bwc_rule);
1010 
1011 	if (!atomic_read(&bwc_matcher->rx_size.rehash_required) &&
1012 	    !atomic_read(&bwc_matcher->tx_size.rehash_required))
1013 		return 0;
1014 
1015 	queue_lock = hws_bwc_get_queue_lock(ctx, bwc_queue_idx);
1016 	mutex_unlock(queue_lock);
1017 
1018 	hws_bwc_lock_all_queues(ctx);
1019 	ret = hws_bwc_matcher_rehash_size(bwc_matcher);
1020 	hws_bwc_unlock_all_queues(ctx);
1021 
1022 	mutex_lock(queue_lock);
1023 
1024 	if (likely(!ret))
1025 		return 0;
1026 
1027 	/* Failed to rehash. Print a diagnostic and rollback the counters. */
1028 	mlx5hws_err(ctx,
1029 		    "BWC rule insertion: rehash to sizes [%d, %d] failed (%d)\n",
1030 		    bwc_matcher->rx_size.size_log,
1031 		    bwc_matcher->tx_size.size_log, ret);
1032 	hws_bwc_rule_cnt_dec(bwc_rule);
1033 
1034 	return ret;
1035 }
1036 
mlx5hws_bwc_rule_create_simple(struct mlx5hws_bwc_rule * bwc_rule,u32 * match_param,struct mlx5hws_rule_action rule_actions[],u32 flow_source,u16 bwc_queue_idx)1037 int mlx5hws_bwc_rule_create_simple(struct mlx5hws_bwc_rule *bwc_rule,
1038 				   u32 *match_param,
1039 				   struct mlx5hws_rule_action rule_actions[],
1040 				   u32 flow_source,
1041 				   u16 bwc_queue_idx)
1042 {
1043 	struct mlx5hws_bwc_matcher *bwc_matcher = bwc_rule->bwc_matcher;
1044 	struct mlx5hws_context *ctx = bwc_matcher->matcher->tbl->ctx;
1045 	struct mlx5hws_rule_attr rule_attr;
1046 	struct mutex *queue_lock; /* Protect the queue */
1047 	int ret = 0;
1048 	int at_idx;
1049 
1050 	mlx5hws_bwc_rule_fill_attr(bwc_matcher, bwc_queue_idx, flow_source, &rule_attr);
1051 
1052 	queue_lock = hws_bwc_get_queue_lock(ctx, bwc_queue_idx);
1053 
1054 	mutex_lock(queue_lock);
1055 
1056 	at_idx = hws_bwc_rule_get_at_idx(bwc_rule, rule_actions, bwc_queue_idx);
1057 	if (unlikely(at_idx < 0)) {
1058 		mutex_unlock(queue_lock);
1059 		mlx5hws_err(ctx, "BWC rule create: failed getting AT (%d)",
1060 			    ret);
1061 		return -EINVAL;
1062 	}
1063 
1064 	ret = hws_bwc_rule_cnt_inc_with_rehash(bwc_rule, bwc_queue_idx);
1065 	if (unlikely(ret)) {
1066 		mutex_unlock(queue_lock);
1067 		return ret;
1068 	}
1069 
1070 	ret = hws_bwc_rule_create_sync(bwc_rule,
1071 				       match_param,
1072 				       at_idx,
1073 				       rule_actions,
1074 				       &rule_attr);
1075 	if (likely(!ret)) {
1076 		hws_bwc_rule_list_add(bwc_rule, bwc_queue_idx);
1077 		mutex_unlock(queue_lock);
1078 		return 0; /* rule inserted successfully */
1079 	}
1080 
1081 	/* Rule insertion could fail due to queue being full, timeout, or
1082 	 * matcher in resize. In such cases, no point in trying to rehash.
1083 	 */
1084 	if (ret == -EBUSY || ret == -ETIMEDOUT || ret == -EAGAIN) {
1085 		mutex_unlock(queue_lock);
1086 		mlx5hws_err(ctx,
1087 			    "BWC rule insertion failed - %s (%d)\n",
1088 			    ret == -EBUSY ? "queue is full" :
1089 			    ret == -ETIMEDOUT ? "timeout" :
1090 			    ret == -EAGAIN ? "matcher in resize" : "N/A",
1091 			    ret);
1092 		hws_bwc_rule_cnt_dec(bwc_rule);
1093 		return ret;
1094 	}
1095 
1096 	/* At this point the rule wasn't added.
1097 	 * It could be because there was collision, or some other problem.
1098 	 * Try rehash by size and insert rule again - last chance.
1099 	 */
1100 	if (!bwc_rule->skip_rx)
1101 		atomic_set(&bwc_matcher->rx_size.rehash_required, true);
1102 	if (!bwc_rule->skip_tx)
1103 		atomic_set(&bwc_matcher->tx_size.rehash_required, true);
1104 
1105 	mutex_unlock(queue_lock);
1106 
1107 	hws_bwc_lock_all_queues(ctx);
1108 	ret = hws_bwc_matcher_rehash_size(bwc_matcher);
1109 	hws_bwc_unlock_all_queues(ctx);
1110 
1111 	if (ret) {
1112 		mlx5hws_err(ctx, "BWC rule insertion: rehash failed (%d)\n", ret);
1113 		hws_bwc_rule_cnt_dec(bwc_rule);
1114 		return ret;
1115 	}
1116 
1117 	/* Rehash done, but we still have that pesky rule to add */
1118 	mutex_lock(queue_lock);
1119 
1120 	ret = hws_bwc_rule_create_sync(bwc_rule,
1121 				       match_param,
1122 				       at_idx,
1123 				       rule_actions,
1124 				       &rule_attr);
1125 
1126 	if (unlikely(ret)) {
1127 		mutex_unlock(queue_lock);
1128 		mlx5hws_err(ctx, "BWC rule insertion failed (%d)\n", ret);
1129 		hws_bwc_rule_cnt_dec(bwc_rule);
1130 		return ret;
1131 	}
1132 
1133 	hws_bwc_rule_list_add(bwc_rule, bwc_queue_idx);
1134 	mutex_unlock(queue_lock);
1135 
1136 	return 0;
1137 }
1138 
1139 struct mlx5hws_bwc_rule *
mlx5hws_bwc_rule_create(struct mlx5hws_bwc_matcher * bwc_matcher,struct mlx5hws_match_parameters * params,u32 flow_source,struct mlx5hws_rule_action rule_actions[])1140 mlx5hws_bwc_rule_create(struct mlx5hws_bwc_matcher *bwc_matcher,
1141 			struct mlx5hws_match_parameters *params,
1142 			u32 flow_source,
1143 			struct mlx5hws_rule_action rule_actions[])
1144 {
1145 	struct mlx5hws_context *ctx = bwc_matcher->matcher->tbl->ctx;
1146 	struct mlx5hws_bwc_rule *bwc_rule;
1147 	u16 bwc_queue_idx;
1148 	int ret;
1149 
1150 	if (unlikely(!mlx5hws_context_bwc_supported(ctx))) {
1151 		mlx5hws_err(ctx, "BWC rule: Context created w/o BWC API compatibility\n");
1152 		return NULL;
1153 	}
1154 
1155 	bwc_rule = mlx5hws_bwc_rule_alloc(bwc_matcher);
1156 	if (unlikely(!bwc_rule))
1157 		return NULL;
1158 
1159 	bwc_rule->flow_source = flow_source;
1160 	mlx5hws_rule_skip(bwc_matcher->matcher, flow_source,
1161 			  &bwc_rule->skip_rx, &bwc_rule->skip_tx);
1162 
1163 	bwc_queue_idx = hws_bwc_gen_queue_idx(ctx);
1164 
1165 	if (bwc_matcher->matcher_type == MLX5HWS_BWC_MATCHER_COMPLEX_FIRST)
1166 		ret = mlx5hws_bwc_rule_create_complex(bwc_rule,
1167 						      params,
1168 						      flow_source,
1169 						      rule_actions,
1170 						      bwc_queue_idx);
1171 	else
1172 		ret = mlx5hws_bwc_rule_create_simple(bwc_rule,
1173 						     params->match_buf,
1174 						     rule_actions,
1175 						     flow_source,
1176 						     bwc_queue_idx);
1177 	if (unlikely(ret)) {
1178 		mlx5hws_bwc_rule_free(bwc_rule);
1179 		return NULL;
1180 	}
1181 
1182 	return bwc_rule;
1183 }
1184 
1185 static int
hws_bwc_rule_action_update(struct mlx5hws_bwc_rule * bwc_rule,struct mlx5hws_rule_action rule_actions[])1186 hws_bwc_rule_action_update(struct mlx5hws_bwc_rule *bwc_rule,
1187 			   struct mlx5hws_rule_action rule_actions[])
1188 {
1189 	struct mlx5hws_bwc_matcher *bwc_matcher = bwc_rule->bwc_matcher;
1190 	struct mlx5hws_context *ctx = bwc_matcher->matcher->tbl->ctx;
1191 	struct mlx5hws_rule_attr rule_attr;
1192 	struct mutex *queue_lock; /* Protect the queue */
1193 	int at_idx, ret;
1194 	u16 idx;
1195 
1196 	idx = bwc_rule->bwc_queue_idx;
1197 
1198 	mlx5hws_bwc_rule_fill_attr(bwc_matcher, idx, bwc_rule->flow_source,
1199 				   &rule_attr);
1200 	queue_lock = hws_bwc_get_queue_lock(ctx, idx);
1201 
1202 	mutex_lock(queue_lock);
1203 
1204 	at_idx = hws_bwc_rule_get_at_idx(bwc_rule, rule_actions, idx);
1205 	if (unlikely(at_idx < 0)) {
1206 		mutex_unlock(queue_lock);
1207 		mlx5hws_err(ctx, "BWC rule update: failed getting AT\n");
1208 		return -EINVAL;
1209 	}
1210 
1211 	ret = hws_bwc_rule_update_sync(bwc_rule,
1212 				       at_idx,
1213 				       rule_actions,
1214 				       &rule_attr);
1215 	mutex_unlock(queue_lock);
1216 
1217 	if (unlikely(ret))
1218 		mlx5hws_err(ctx, "BWC rule: update failed (%d)\n", ret);
1219 
1220 	return ret;
1221 }
1222 
mlx5hws_bwc_rule_action_update(struct mlx5hws_bwc_rule * bwc_rule,struct mlx5hws_rule_action rule_actions[])1223 int mlx5hws_bwc_rule_action_update(struct mlx5hws_bwc_rule *bwc_rule,
1224 				   struct mlx5hws_rule_action rule_actions[])
1225 {
1226 	struct mlx5hws_bwc_matcher *bwc_matcher = bwc_rule->bwc_matcher;
1227 	struct mlx5hws_context *ctx = bwc_matcher->matcher->tbl->ctx;
1228 
1229 	if (unlikely(!mlx5hws_context_bwc_supported(ctx))) {
1230 		mlx5hws_err(ctx, "BWC rule: Context created w/o BWC API compatibility\n");
1231 		return -EINVAL;
1232 	}
1233 
1234 	/* For complex rules, the update should happen on the last subrule. */
1235 	while (bwc_rule->next_subrule)
1236 		bwc_rule = bwc_rule->next_subrule;
1237 
1238 	return hws_bwc_rule_action_update(bwc_rule, rule_actions);
1239 }
1240