xref: /linux/drivers/net/ethernet/mellanox/mlx5/core/steering/hws/mlx5hws_bwc.c (revision 07d6bf634bc8f93caf8920c9d61df761645336e2)
1 // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
2 /* Copyright (c) 2024 NVIDIA Corporation & Affiliates */
3 
4 #include "mlx5hws_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_matcher_attr * attr,u32 priority,u8 size_log)49 static void hws_bwc_matcher_init_attr(struct mlx5hws_matcher_attr *attr,
50 				      u32 priority,
51 				      u8 size_log)
52 {
53 	memset(attr, 0, sizeof(*attr));
54 
55 	attr->priority = priority;
56 	attr->optimize_using_rule_idx = 0;
57 	attr->mode = MLX5HWS_MATCHER_RESOURCE_MODE_RULE;
58 	attr->optimize_flow_src = MLX5HWS_MATCHER_FLOW_SRC_ANY;
59 	attr->insert_mode = MLX5HWS_MATCHER_INSERT_BY_HASH;
60 	attr->distribute_mode = MLX5HWS_MATCHER_DISTRIBUTE_BY_HASH;
61 	attr->rule.num_log = size_log;
62 	attr->resizable = true;
63 	attr->max_num_of_at_attach = MLX5HWS_BWC_MATCHER_ATTACH_AT_NUM;
64 }
65 
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[])66 int mlx5hws_bwc_matcher_create_simple(struct mlx5hws_bwc_matcher *bwc_matcher,
67 				      struct mlx5hws_table *table,
68 				      u32 priority,
69 				      u8 match_criteria_enable,
70 				      struct mlx5hws_match_parameters *mask,
71 				      enum mlx5hws_action_type action_types[])
72 {
73 	enum mlx5hws_action_type init_action_types[1] = { MLX5HWS_ACTION_TYP_LAST };
74 	struct mlx5hws_context *ctx = table->ctx;
75 	u16 bwc_queues = mlx5hws_bwc_queues(ctx);
76 	struct mlx5hws_matcher_attr attr = {0};
77 	int i;
78 
79 	bwc_matcher->rules = kcalloc(bwc_queues, sizeof(*bwc_matcher->rules), GFP_KERNEL);
80 	if (!bwc_matcher->rules)
81 		goto err;
82 
83 	for (i = 0; i < bwc_queues; i++)
84 		INIT_LIST_HEAD(&bwc_matcher->rules[i]);
85 
86 	hws_bwc_matcher_init_attr(&attr,
87 				  priority,
88 				  MLX5HWS_BWC_MATCHER_INIT_SIZE_LOG);
89 
90 	bwc_matcher->priority = priority;
91 	bwc_matcher->size_log = MLX5HWS_BWC_MATCHER_INIT_SIZE_LOG;
92 
93 	/* create dummy action template */
94 	bwc_matcher->at[0] =
95 		mlx5hws_action_template_create(action_types ?
96 					       action_types : init_action_types);
97 	if (!bwc_matcher->at[0]) {
98 		mlx5hws_err(table->ctx, "BWC matcher: failed creating action template\n");
99 		goto free_bwc_matcher_rules;
100 	}
101 
102 	bwc_matcher->num_of_at = 1;
103 
104 	bwc_matcher->mt = mlx5hws_match_template_create(ctx,
105 							mask->match_buf,
106 							mask->match_sz,
107 							match_criteria_enable);
108 	if (!bwc_matcher->mt) {
109 		mlx5hws_err(table->ctx, "BWC matcher: failed creating match template\n");
110 		goto free_at;
111 	}
112 
113 	bwc_matcher->matcher = mlx5hws_matcher_create(table,
114 						      &bwc_matcher->mt, 1,
115 						      &bwc_matcher->at[0],
116 						      bwc_matcher->num_of_at,
117 						      &attr);
118 	if (!bwc_matcher->matcher) {
119 		mlx5hws_err(table->ctx, "BWC matcher: failed creating HWS matcher\n");
120 		goto free_mt;
121 	}
122 
123 	return 0;
124 
125 free_mt:
126 	mlx5hws_match_template_destroy(bwc_matcher->mt);
127 free_at:
128 	mlx5hws_action_template_destroy(bwc_matcher->at[0]);
129 free_bwc_matcher_rules:
130 	kfree(bwc_matcher->rules);
131 err:
132 	return -EINVAL;
133 }
134 
135 struct mlx5hws_bwc_matcher *
mlx5hws_bwc_matcher_create(struct mlx5hws_table * table,u32 priority,u8 match_criteria_enable,struct mlx5hws_match_parameters * mask)136 mlx5hws_bwc_matcher_create(struct mlx5hws_table *table,
137 			   u32 priority,
138 			   u8 match_criteria_enable,
139 			   struct mlx5hws_match_parameters *mask)
140 {
141 	struct mlx5hws_bwc_matcher *bwc_matcher;
142 	bool is_complex;
143 	int ret;
144 
145 	if (!mlx5hws_context_bwc_supported(table->ctx)) {
146 		mlx5hws_err(table->ctx,
147 			    "BWC matcher: context created w/o BWC API compatibility\n");
148 		return NULL;
149 	}
150 
151 	bwc_matcher = kzalloc(sizeof(*bwc_matcher), GFP_KERNEL);
152 	if (!bwc_matcher)
153 		return NULL;
154 
155 	/* Check if the required match params can be all matched
156 	 * in single STE, otherwise complex matcher is needed.
157 	 */
158 
159 	is_complex = mlx5hws_bwc_match_params_is_complex(table->ctx, match_criteria_enable, mask);
160 	if (is_complex)
161 		ret = mlx5hws_bwc_matcher_create_complex(bwc_matcher,
162 							 table,
163 							 priority,
164 							 match_criteria_enable,
165 							 mask);
166 	else
167 		ret = mlx5hws_bwc_matcher_create_simple(bwc_matcher,
168 							table,
169 							priority,
170 							match_criteria_enable,
171 							mask,
172 							NULL);
173 	if (ret)
174 		goto free_bwc_matcher;
175 
176 	return bwc_matcher;
177 
178 free_bwc_matcher:
179 	kfree(bwc_matcher);
180 
181 	return NULL;
182 }
183 
mlx5hws_bwc_matcher_destroy_simple(struct mlx5hws_bwc_matcher * bwc_matcher)184 int mlx5hws_bwc_matcher_destroy_simple(struct mlx5hws_bwc_matcher *bwc_matcher)
185 {
186 	int i;
187 
188 	mlx5hws_matcher_destroy(bwc_matcher->matcher);
189 	bwc_matcher->matcher = NULL;
190 
191 	for (i = 0; i < bwc_matcher->num_of_at; i++)
192 		mlx5hws_action_template_destroy(bwc_matcher->at[i]);
193 
194 	mlx5hws_match_template_destroy(bwc_matcher->mt);
195 	kfree(bwc_matcher->rules);
196 
197 	return 0;
198 }
199 
mlx5hws_bwc_matcher_destroy(struct mlx5hws_bwc_matcher * bwc_matcher)200 int mlx5hws_bwc_matcher_destroy(struct mlx5hws_bwc_matcher *bwc_matcher)
201 {
202 	if (bwc_matcher->num_of_rules)
203 		mlx5hws_err(bwc_matcher->matcher->tbl->ctx,
204 			    "BWC matcher destroy: matcher still has %d rules\n",
205 			    bwc_matcher->num_of_rules);
206 
207 	mlx5hws_bwc_matcher_destroy_simple(bwc_matcher);
208 
209 	kfree(bwc_matcher);
210 	return 0;
211 }
212 
hws_bwc_queue_poll(struct mlx5hws_context * ctx,u16 queue_id,u32 * pending_rules,bool drain)213 static int hws_bwc_queue_poll(struct mlx5hws_context *ctx,
214 			      u16 queue_id,
215 			      u32 *pending_rules,
216 			      bool drain)
217 {
218 	struct mlx5hws_flow_op_result comp[MLX5HWS_BWC_MATCHER_REHASH_BURST_TH];
219 	u16 burst_th = hws_bwc_get_burst_th(ctx, queue_id);
220 	bool got_comp = *pending_rules >= burst_th;
221 	bool queue_full;
222 	int err = 0;
223 	int ret;
224 	int i;
225 
226 	/* Check if there are any completions at all */
227 	if (!got_comp && !drain)
228 		return 0;
229 
230 	queue_full = mlx5hws_send_engine_full(&ctx->send_queue[queue_id]);
231 	while (queue_full || ((got_comp || drain) && *pending_rules)) {
232 		ret = mlx5hws_send_queue_poll(ctx, queue_id, comp, burst_th);
233 		if (unlikely(ret < 0)) {
234 			mlx5hws_err(ctx, "BWC poll error: polling queue %d returned %d\n",
235 				    queue_id, ret);
236 			return -EINVAL;
237 		}
238 
239 		if (ret) {
240 			(*pending_rules) -= ret;
241 			for (i = 0; i < ret; i++) {
242 				if (unlikely(comp[i].status != MLX5HWS_FLOW_OP_SUCCESS)) {
243 					mlx5hws_err(ctx,
244 						    "BWC poll error: polling queue %d returned completion with error\n",
245 						    queue_id);
246 					err = -EINVAL;
247 				}
248 			}
249 			queue_full = false;
250 		}
251 
252 		got_comp = !!ret;
253 	}
254 
255 	return err;
256 }
257 
258 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)259 mlx5hws_bwc_rule_fill_attr(struct mlx5hws_bwc_matcher *bwc_matcher,
260 			   u16 bwc_queue_idx,
261 			   u32 flow_source,
262 			   struct mlx5hws_rule_attr *rule_attr)
263 {
264 	struct mlx5hws_context *ctx = bwc_matcher->matcher->tbl->ctx;
265 
266 	/* no use of INSERT_BY_INDEX in bwc rule */
267 	rule_attr->rule_idx = 0;
268 
269 	/* notify HW at each rule insertion/deletion */
270 	rule_attr->burst = 0;
271 
272 	/* We don't need user data, but the API requires it to exist */
273 	rule_attr->user_data = (void *)0xFACADE;
274 
275 	rule_attr->queue_id = mlx5hws_bwc_get_queue_id(ctx, bwc_queue_idx);
276 	rule_attr->flow_source = flow_source;
277 }
278 
279 struct mlx5hws_bwc_rule *
mlx5hws_bwc_rule_alloc(struct mlx5hws_bwc_matcher * bwc_matcher)280 mlx5hws_bwc_rule_alloc(struct mlx5hws_bwc_matcher *bwc_matcher)
281 {
282 	struct mlx5hws_bwc_rule *bwc_rule;
283 
284 	bwc_rule = kzalloc(sizeof(*bwc_rule), GFP_KERNEL);
285 	if (unlikely(!bwc_rule))
286 		goto out_err;
287 
288 	bwc_rule->rule = kzalloc(sizeof(*bwc_rule->rule), GFP_KERNEL);
289 	if (unlikely(!bwc_rule->rule))
290 		goto free_rule;
291 
292 	bwc_rule->bwc_matcher = bwc_matcher;
293 	return bwc_rule;
294 
295 free_rule:
296 	kfree(bwc_rule);
297 out_err:
298 	return NULL;
299 }
300 
mlx5hws_bwc_rule_free(struct mlx5hws_bwc_rule * bwc_rule)301 void mlx5hws_bwc_rule_free(struct mlx5hws_bwc_rule *bwc_rule)
302 {
303 	if (likely(bwc_rule->rule))
304 		kfree(bwc_rule->rule);
305 	kfree(bwc_rule);
306 }
307 
hws_bwc_rule_list_add(struct mlx5hws_bwc_rule * bwc_rule,u16 idx)308 static void hws_bwc_rule_list_add(struct mlx5hws_bwc_rule *bwc_rule, u16 idx)
309 {
310 	struct mlx5hws_bwc_matcher *bwc_matcher = bwc_rule->bwc_matcher;
311 
312 	bwc_matcher->num_of_rules++;
313 	bwc_rule->bwc_queue_idx = idx;
314 	list_add(&bwc_rule->list_node, &bwc_matcher->rules[idx]);
315 }
316 
hws_bwc_rule_list_remove(struct mlx5hws_bwc_rule * bwc_rule)317 static void hws_bwc_rule_list_remove(struct mlx5hws_bwc_rule *bwc_rule)
318 {
319 	struct mlx5hws_bwc_matcher *bwc_matcher = bwc_rule->bwc_matcher;
320 
321 	bwc_matcher->num_of_rules--;
322 	list_del_init(&bwc_rule->list_node);
323 }
324 
325 static int
hws_bwc_rule_destroy_hws_async(struct mlx5hws_bwc_rule * bwc_rule,struct mlx5hws_rule_attr * attr)326 hws_bwc_rule_destroy_hws_async(struct mlx5hws_bwc_rule *bwc_rule,
327 			       struct mlx5hws_rule_attr *attr)
328 {
329 	return mlx5hws_rule_destroy(bwc_rule->rule, attr);
330 }
331 
332 static int
hws_bwc_rule_destroy_hws_sync(struct mlx5hws_bwc_rule * bwc_rule,struct mlx5hws_rule_attr * rule_attr)333 hws_bwc_rule_destroy_hws_sync(struct mlx5hws_bwc_rule *bwc_rule,
334 			      struct mlx5hws_rule_attr *rule_attr)
335 {
336 	struct mlx5hws_context *ctx = bwc_rule->bwc_matcher->matcher->tbl->ctx;
337 	struct mlx5hws_flow_op_result completion;
338 	int ret;
339 
340 	ret = hws_bwc_rule_destroy_hws_async(bwc_rule, rule_attr);
341 	if (unlikely(ret))
342 		return ret;
343 
344 	do {
345 		ret = mlx5hws_send_queue_poll(ctx, rule_attr->queue_id, &completion, 1);
346 	} while (ret != 1);
347 
348 	if (unlikely(completion.status != MLX5HWS_FLOW_OP_SUCCESS ||
349 		     (bwc_rule->rule->status != MLX5HWS_RULE_STATUS_DELETED &&
350 		      bwc_rule->rule->status != MLX5HWS_RULE_STATUS_DELETING))) {
351 		mlx5hws_err(ctx, "Failed destroying BWC rule: completion %d, rule status %d\n",
352 			    completion.status, bwc_rule->rule->status);
353 		return -EINVAL;
354 	}
355 
356 	return 0;
357 }
358 
mlx5hws_bwc_rule_destroy_simple(struct mlx5hws_bwc_rule * bwc_rule)359 int mlx5hws_bwc_rule_destroy_simple(struct mlx5hws_bwc_rule *bwc_rule)
360 {
361 	struct mlx5hws_bwc_matcher *bwc_matcher = bwc_rule->bwc_matcher;
362 	struct mlx5hws_context *ctx = bwc_matcher->matcher->tbl->ctx;
363 	u16 idx = bwc_rule->bwc_queue_idx;
364 	struct mlx5hws_rule_attr attr;
365 	struct mutex *queue_lock; /* Protect the queue */
366 	int ret;
367 
368 	mlx5hws_bwc_rule_fill_attr(bwc_matcher, idx, 0, &attr);
369 
370 	queue_lock = hws_bwc_get_queue_lock(ctx, idx);
371 
372 	mutex_lock(queue_lock);
373 
374 	ret = hws_bwc_rule_destroy_hws_sync(bwc_rule, &attr);
375 	hws_bwc_rule_list_remove(bwc_rule);
376 
377 	mutex_unlock(queue_lock);
378 
379 	return ret;
380 }
381 
mlx5hws_bwc_rule_destroy(struct mlx5hws_bwc_rule * bwc_rule)382 int mlx5hws_bwc_rule_destroy(struct mlx5hws_bwc_rule *bwc_rule)
383 {
384 	int ret;
385 
386 	ret = mlx5hws_bwc_rule_destroy_simple(bwc_rule);
387 
388 	mlx5hws_bwc_rule_free(bwc_rule);
389 	return ret;
390 }
391 
392 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)393 hws_bwc_rule_create_async(struct mlx5hws_bwc_rule *bwc_rule,
394 			  u32 *match_param,
395 			  u8 at_idx,
396 			  struct mlx5hws_rule_action rule_actions[],
397 			  struct mlx5hws_rule_attr *rule_attr)
398 {
399 	return mlx5hws_rule_create(bwc_rule->bwc_matcher->matcher,
400 				   0, /* only one match template supported */
401 				   match_param,
402 				   at_idx,
403 				   rule_actions,
404 				   rule_attr,
405 				   bwc_rule->rule);
406 }
407 
408 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)409 hws_bwc_rule_create_sync(struct mlx5hws_bwc_rule *bwc_rule,
410 			 u32 *match_param,
411 			 u8 at_idx,
412 			 struct mlx5hws_rule_action rule_actions[],
413 			 struct mlx5hws_rule_attr *rule_attr)
414 
415 {
416 	struct mlx5hws_context *ctx = bwc_rule->bwc_matcher->matcher->tbl->ctx;
417 	u32 expected_completions = 1;
418 	int ret;
419 
420 	ret = hws_bwc_rule_create_async(bwc_rule, match_param,
421 					at_idx, rule_actions,
422 					rule_attr);
423 	if (unlikely(ret))
424 		return ret;
425 
426 	ret = hws_bwc_queue_poll(ctx, rule_attr->queue_id, &expected_completions, true);
427 
428 	return ret;
429 }
430 
431 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)432 hws_bwc_rule_update_sync(struct mlx5hws_bwc_rule *bwc_rule,
433 			 u8 at_idx,
434 			 struct mlx5hws_rule_action rule_actions[],
435 			 struct mlx5hws_rule_attr *rule_attr)
436 {
437 	struct mlx5hws_bwc_matcher *bwc_matcher = bwc_rule->bwc_matcher;
438 	struct mlx5hws_context *ctx = bwc_matcher->matcher->tbl->ctx;
439 	u32 expected_completions = 1;
440 	int ret;
441 
442 	ret = mlx5hws_rule_action_update(bwc_rule->rule,
443 					 at_idx,
444 					 rule_actions,
445 					 rule_attr);
446 	if (unlikely(ret))
447 		return ret;
448 
449 	ret = hws_bwc_queue_poll(ctx, rule_attr->queue_id, &expected_completions, true);
450 	if (unlikely(ret))
451 		mlx5hws_err(ctx, "Failed updating BWC rule (%d)\n", ret);
452 
453 	return ret;
454 }
455 
456 static bool
hws_bwc_matcher_size_maxed_out(struct mlx5hws_bwc_matcher * bwc_matcher)457 hws_bwc_matcher_size_maxed_out(struct mlx5hws_bwc_matcher *bwc_matcher)
458 {
459 	struct mlx5hws_cmd_query_caps *caps = bwc_matcher->matcher->tbl->ctx->caps;
460 
461 	return bwc_matcher->size_log + MLX5HWS_MATCHER_ASSURED_MAIN_TBL_DEPTH >=
462 	       caps->ste_alloc_log_max - 1;
463 }
464 
465 static bool
hws_bwc_matcher_rehash_size_needed(struct mlx5hws_bwc_matcher * bwc_matcher,u32 num_of_rules)466 hws_bwc_matcher_rehash_size_needed(struct mlx5hws_bwc_matcher *bwc_matcher,
467 				   u32 num_of_rules)
468 {
469 	if (unlikely(hws_bwc_matcher_size_maxed_out(bwc_matcher)))
470 		return false;
471 
472 	if (unlikely((num_of_rules * 100 / MLX5HWS_BWC_MATCHER_REHASH_PERCENT_TH) >=
473 		     (1UL << bwc_matcher->size_log)))
474 		return true;
475 
476 	return false;
477 }
478 
479 static void
hws_bwc_rule_actions_to_action_types(struct mlx5hws_rule_action rule_actions[],enum mlx5hws_action_type action_types[])480 hws_bwc_rule_actions_to_action_types(struct mlx5hws_rule_action rule_actions[],
481 				     enum mlx5hws_action_type action_types[])
482 {
483 	int i = 0;
484 
485 	for (i = 0;
486 	     rule_actions[i].action && (rule_actions[i].action->type != MLX5HWS_ACTION_TYP_LAST);
487 	     i++) {
488 		action_types[i] = (enum mlx5hws_action_type)rule_actions[i].action->type;
489 	}
490 
491 	action_types[i] = MLX5HWS_ACTION_TYP_LAST;
492 }
493 
494 static int
hws_bwc_matcher_extend_at(struct mlx5hws_bwc_matcher * bwc_matcher,struct mlx5hws_rule_action rule_actions[])495 hws_bwc_matcher_extend_at(struct mlx5hws_bwc_matcher *bwc_matcher,
496 			  struct mlx5hws_rule_action rule_actions[])
497 {
498 	enum mlx5hws_action_type action_types[MLX5HWS_BWC_MAX_ACTS];
499 
500 	hws_bwc_rule_actions_to_action_types(rule_actions, action_types);
501 
502 	bwc_matcher->at[bwc_matcher->num_of_at] =
503 		mlx5hws_action_template_create(action_types);
504 
505 	if (unlikely(!bwc_matcher->at[bwc_matcher->num_of_at]))
506 		return -ENOMEM;
507 
508 	bwc_matcher->num_of_at++;
509 	return 0;
510 }
511 
512 static int
hws_bwc_matcher_extend_size(struct mlx5hws_bwc_matcher * bwc_matcher)513 hws_bwc_matcher_extend_size(struct mlx5hws_bwc_matcher *bwc_matcher)
514 {
515 	struct mlx5hws_context *ctx = bwc_matcher->matcher->tbl->ctx;
516 	struct mlx5hws_cmd_query_caps *caps = ctx->caps;
517 
518 	if (unlikely(hws_bwc_matcher_size_maxed_out(bwc_matcher))) {
519 		mlx5hws_err(ctx, "Can't resize matcher: depth exceeds limit %d\n",
520 			    caps->rtc_log_depth_max);
521 		return -ENOMEM;
522 	}
523 
524 	bwc_matcher->size_log =
525 		min(bwc_matcher->size_log + MLX5HWS_BWC_MATCHER_SIZE_LOG_STEP,
526 		    caps->ste_alloc_log_max - MLX5HWS_MATCHER_ASSURED_MAIN_TBL_DEPTH);
527 
528 	return 0;
529 }
530 
531 static int
hws_bwc_matcher_find_at(struct mlx5hws_bwc_matcher * bwc_matcher,struct mlx5hws_rule_action rule_actions[])532 hws_bwc_matcher_find_at(struct mlx5hws_bwc_matcher *bwc_matcher,
533 			struct mlx5hws_rule_action rule_actions[])
534 {
535 	enum mlx5hws_action_type *action_type_arr;
536 	int i, j;
537 
538 	/* start from index 1 - first action template is a dummy */
539 	for (i = 1; i < bwc_matcher->num_of_at; i++) {
540 		j = 0;
541 		action_type_arr = bwc_matcher->at[i]->action_type_arr;
542 
543 		while (rule_actions[j].action &&
544 		       rule_actions[j].action->type != MLX5HWS_ACTION_TYP_LAST) {
545 			if (action_type_arr[j] != rule_actions[j].action->type)
546 				break;
547 			j++;
548 		}
549 
550 		if (action_type_arr[j] == MLX5HWS_ACTION_TYP_LAST &&
551 		    (!rule_actions[j].action ||
552 		     rule_actions[j].action->type == MLX5HWS_ACTION_TYP_LAST))
553 			return i;
554 	}
555 
556 	return -1;
557 }
558 
hws_bwc_matcher_move_all_simple(struct mlx5hws_bwc_matcher * bwc_matcher)559 static int hws_bwc_matcher_move_all_simple(struct mlx5hws_bwc_matcher *bwc_matcher)
560 {
561 	struct mlx5hws_context *ctx = bwc_matcher->matcher->tbl->ctx;
562 	u16 bwc_queues = mlx5hws_bwc_queues(ctx);
563 	struct mlx5hws_bwc_rule **bwc_rules;
564 	struct mlx5hws_rule_attr rule_attr;
565 	u32 *pending_rules;
566 	int i, j, ret = 0;
567 	bool all_done;
568 	u16 burst_th;
569 
570 	mlx5hws_bwc_rule_fill_attr(bwc_matcher, 0, 0, &rule_attr);
571 
572 	pending_rules = kcalloc(bwc_queues, sizeof(*pending_rules), GFP_KERNEL);
573 	if (!pending_rules)
574 		return -ENOMEM;
575 
576 	bwc_rules = kcalloc(bwc_queues, sizeof(*bwc_rules), GFP_KERNEL);
577 	if (!bwc_rules) {
578 		ret = -ENOMEM;
579 		goto free_pending_rules;
580 	}
581 
582 	for (i = 0; i < bwc_queues; i++) {
583 		if (list_empty(&bwc_matcher->rules[i]))
584 			bwc_rules[i] = NULL;
585 		else
586 			bwc_rules[i] = list_first_entry(&bwc_matcher->rules[i],
587 							struct mlx5hws_bwc_rule,
588 							list_node);
589 	}
590 
591 	do {
592 		all_done = true;
593 
594 		for (i = 0; i < bwc_queues; i++) {
595 			rule_attr.queue_id = mlx5hws_bwc_get_queue_id(ctx, i);
596 			burst_th = hws_bwc_get_burst_th(ctx, rule_attr.queue_id);
597 
598 			for (j = 0; j < burst_th && bwc_rules[i]; j++) {
599 				rule_attr.burst = !!((j + 1) % burst_th);
600 				ret = mlx5hws_matcher_resize_rule_move(bwc_matcher->matcher,
601 								       bwc_rules[i]->rule,
602 								       &rule_attr);
603 				if (unlikely(ret)) {
604 					mlx5hws_err(ctx,
605 						    "Moving BWC rule failed during rehash (%d)\n",
606 						    ret);
607 					goto free_bwc_rules;
608 				}
609 
610 				all_done = false;
611 				pending_rules[i]++;
612 				bwc_rules[i] = list_is_last(&bwc_rules[i]->list_node,
613 							    &bwc_matcher->rules[i]) ?
614 					       NULL : list_next_entry(bwc_rules[i], list_node);
615 
616 				ret = hws_bwc_queue_poll(ctx, rule_attr.queue_id,
617 							 &pending_rules[i], false);
618 				if (unlikely(ret))
619 					goto free_bwc_rules;
620 			}
621 		}
622 	} while (!all_done);
623 
624 	/* drain all the bwc queues */
625 	for (i = 0; i < bwc_queues; i++) {
626 		if (pending_rules[i]) {
627 			u16 queue_id = mlx5hws_bwc_get_queue_id(ctx, i);
628 
629 			mlx5hws_send_engine_flush_queue(&ctx->send_queue[queue_id]);
630 			ret = hws_bwc_queue_poll(ctx, queue_id,
631 						 &pending_rules[i], true);
632 			if (unlikely(ret))
633 				goto free_bwc_rules;
634 		}
635 	}
636 
637 free_bwc_rules:
638 	kfree(bwc_rules);
639 free_pending_rules:
640 	kfree(pending_rules);
641 
642 	return ret;
643 }
644 
hws_bwc_matcher_move_all(struct mlx5hws_bwc_matcher * bwc_matcher)645 static int hws_bwc_matcher_move_all(struct mlx5hws_bwc_matcher *bwc_matcher)
646 {
647 	return hws_bwc_matcher_move_all_simple(bwc_matcher);
648 }
649 
hws_bwc_matcher_move(struct mlx5hws_bwc_matcher * bwc_matcher)650 static int hws_bwc_matcher_move(struct mlx5hws_bwc_matcher *bwc_matcher)
651 {
652 	struct mlx5hws_context *ctx = bwc_matcher->matcher->tbl->ctx;
653 	struct mlx5hws_matcher_attr matcher_attr = {0};
654 	struct mlx5hws_matcher *old_matcher;
655 	struct mlx5hws_matcher *new_matcher;
656 	int ret;
657 
658 	hws_bwc_matcher_init_attr(&matcher_attr,
659 				  bwc_matcher->priority,
660 				  bwc_matcher->size_log);
661 
662 	old_matcher = bwc_matcher->matcher;
663 	new_matcher = mlx5hws_matcher_create(old_matcher->tbl,
664 					     &bwc_matcher->mt, 1,
665 					     bwc_matcher->at,
666 					     bwc_matcher->num_of_at,
667 					     &matcher_attr);
668 	if (!new_matcher) {
669 		mlx5hws_err(ctx, "Rehash error: matcher creation failed\n");
670 		return -ENOMEM;
671 	}
672 
673 	ret = mlx5hws_matcher_resize_set_target(old_matcher, new_matcher);
674 	if (ret) {
675 		mlx5hws_err(ctx, "Rehash error: failed setting resize target\n");
676 		return ret;
677 	}
678 
679 	ret = hws_bwc_matcher_move_all(bwc_matcher);
680 	if (ret) {
681 		mlx5hws_err(ctx, "Rehash error: moving rules failed\n");
682 		return -ENOMEM;
683 	}
684 
685 	bwc_matcher->matcher = new_matcher;
686 	mlx5hws_matcher_destroy(old_matcher);
687 
688 	return 0;
689 }
690 
691 static int
hws_bwc_matcher_rehash_size(struct mlx5hws_bwc_matcher * bwc_matcher)692 hws_bwc_matcher_rehash_size(struct mlx5hws_bwc_matcher *bwc_matcher)
693 {
694 	int ret;
695 
696 	/* If the current matcher size is already at its max size, we can't
697 	 * do the rehash. Skip it and try adding the rule again - perhaps
698 	 * there was some change.
699 	 */
700 	if (hws_bwc_matcher_size_maxed_out(bwc_matcher))
701 		return 0;
702 
703 	/* It is possible that other rule has already performed rehash.
704 	 * Need to check again if we really need rehash.
705 	 * If the reason for rehash was size, but not any more - skip rehash.
706 	 */
707 	if (!hws_bwc_matcher_rehash_size_needed(bwc_matcher, bwc_matcher->num_of_rules))
708 		return 0;
709 
710 	/* Now we're done all the checking - do the rehash:
711 	 *  - extend match RTC size
712 	 *  - create new matcher
713 	 *  - move all the rules to the new matcher
714 	 *  - destroy the old matcher
715 	 */
716 
717 	ret = hws_bwc_matcher_extend_size(bwc_matcher);
718 	if (ret)
719 		return ret;
720 
721 	return hws_bwc_matcher_move(bwc_matcher);
722 }
723 
724 static int
hws_bwc_matcher_rehash_at(struct mlx5hws_bwc_matcher * bwc_matcher)725 hws_bwc_matcher_rehash_at(struct mlx5hws_bwc_matcher *bwc_matcher)
726 {
727 	/* Rehash by action template doesn't require any additional checking.
728 	 * The bwc_matcher already contains the new action template.
729 	 * Just do the usual rehash:
730 	 *  - create new matcher
731 	 *  - move all the rules to the new matcher
732 	 *  - destroy the old matcher
733 	 */
734 	return hws_bwc_matcher_move(bwc_matcher);
735 }
736 
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)737 int mlx5hws_bwc_rule_create_simple(struct mlx5hws_bwc_rule *bwc_rule,
738 				   u32 *match_param,
739 				   struct mlx5hws_rule_action rule_actions[],
740 				   u32 flow_source,
741 				   u16 bwc_queue_idx)
742 {
743 	struct mlx5hws_bwc_matcher *bwc_matcher = bwc_rule->bwc_matcher;
744 	struct mlx5hws_context *ctx = bwc_matcher->matcher->tbl->ctx;
745 	struct mlx5hws_rule_attr rule_attr;
746 	struct mutex *queue_lock; /* Protect the queue */
747 	u32 num_of_rules;
748 	int ret = 0;
749 	int at_idx;
750 
751 	mlx5hws_bwc_rule_fill_attr(bwc_matcher, bwc_queue_idx, flow_source, &rule_attr);
752 
753 	queue_lock = hws_bwc_get_queue_lock(ctx, bwc_queue_idx);
754 
755 	mutex_lock(queue_lock);
756 
757 	/* check if rehash needed due to missing action template */
758 	at_idx = hws_bwc_matcher_find_at(bwc_matcher, rule_actions);
759 	if (unlikely(at_idx < 0)) {
760 		/* we need to extend BWC matcher action templates array */
761 		mutex_unlock(queue_lock);
762 		hws_bwc_lock_all_queues(ctx);
763 
764 		ret = hws_bwc_matcher_extend_at(bwc_matcher, rule_actions);
765 		if (unlikely(ret)) {
766 			hws_bwc_unlock_all_queues(ctx);
767 			return ret;
768 		}
769 
770 		/* action templates array was extended, we need the last idx */
771 		at_idx = bwc_matcher->num_of_at - 1;
772 
773 		ret = mlx5hws_matcher_attach_at(bwc_matcher->matcher,
774 						bwc_matcher->at[at_idx]);
775 		if (unlikely(ret)) {
776 			/* Action template attach failed, possibly due to
777 			 * requiring more action STEs.
778 			 * Need to attempt creating new matcher with all
779 			 * the action templates, including the new one.
780 			 */
781 			ret = hws_bwc_matcher_rehash_at(bwc_matcher);
782 			if (unlikely(ret)) {
783 				mlx5hws_action_template_destroy(bwc_matcher->at[at_idx]);
784 				bwc_matcher->at[at_idx] = NULL;
785 				bwc_matcher->num_of_at--;
786 
787 				hws_bwc_unlock_all_queues(ctx);
788 
789 				mlx5hws_err(ctx,
790 					    "BWC rule insertion: rehash AT failed (%d)\n", ret);
791 				return ret;
792 			}
793 		}
794 
795 		hws_bwc_unlock_all_queues(ctx);
796 		mutex_lock(queue_lock);
797 	}
798 
799 	/* check if number of rules require rehash */
800 	num_of_rules = bwc_matcher->num_of_rules;
801 
802 	if (unlikely(hws_bwc_matcher_rehash_size_needed(bwc_matcher, num_of_rules))) {
803 		mutex_unlock(queue_lock);
804 
805 		hws_bwc_lock_all_queues(ctx);
806 		ret = hws_bwc_matcher_rehash_size(bwc_matcher);
807 		hws_bwc_unlock_all_queues(ctx);
808 
809 		if (ret) {
810 			mlx5hws_err(ctx, "BWC rule insertion: rehash size [%d -> %d] failed (%d)\n",
811 				    bwc_matcher->size_log - MLX5HWS_BWC_MATCHER_SIZE_LOG_STEP,
812 				    bwc_matcher->size_log,
813 				    ret);
814 			return ret;
815 		}
816 
817 		mutex_lock(queue_lock);
818 	}
819 
820 	ret = hws_bwc_rule_create_sync(bwc_rule,
821 				       match_param,
822 				       at_idx,
823 				       rule_actions,
824 				       &rule_attr);
825 	if (likely(!ret)) {
826 		hws_bwc_rule_list_add(bwc_rule, bwc_queue_idx);
827 		mutex_unlock(queue_lock);
828 		return 0; /* rule inserted successfully */
829 	}
830 
831 	/* At this point the rule wasn't added.
832 	 * It could be because there was collision, or some other problem.
833 	 * If we don't dive deeper than API, the only thing we know is that
834 	 * the status of completion is RTE_FLOW_OP_ERROR.
835 	 * Try rehash by size and insert rule again - last chance.
836 	 */
837 
838 	mutex_unlock(queue_lock);
839 
840 	hws_bwc_lock_all_queues(ctx);
841 	ret = hws_bwc_matcher_rehash_size(bwc_matcher);
842 	hws_bwc_unlock_all_queues(ctx);
843 
844 	if (ret) {
845 		mlx5hws_err(ctx, "BWC rule insertion: rehash failed (%d)\n", ret);
846 		return ret;
847 	}
848 
849 	/* Rehash done, but we still have that pesky rule to add */
850 	mutex_lock(queue_lock);
851 
852 	ret = hws_bwc_rule_create_sync(bwc_rule,
853 				       match_param,
854 				       at_idx,
855 				       rule_actions,
856 				       &rule_attr);
857 
858 	if (unlikely(ret)) {
859 		mutex_unlock(queue_lock);
860 		mlx5hws_err(ctx, "BWC rule insertion failed (%d)\n", ret);
861 		return ret;
862 	}
863 
864 	hws_bwc_rule_list_add(bwc_rule, bwc_queue_idx);
865 	mutex_unlock(queue_lock);
866 
867 	return 0;
868 }
869 
870 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[])871 mlx5hws_bwc_rule_create(struct mlx5hws_bwc_matcher *bwc_matcher,
872 			struct mlx5hws_match_parameters *params,
873 			u32 flow_source,
874 			struct mlx5hws_rule_action rule_actions[])
875 {
876 	struct mlx5hws_context *ctx = bwc_matcher->matcher->tbl->ctx;
877 	struct mlx5hws_bwc_rule *bwc_rule;
878 	u16 bwc_queue_idx;
879 	int ret;
880 
881 	if (unlikely(!mlx5hws_context_bwc_supported(ctx))) {
882 		mlx5hws_err(ctx, "BWC rule: Context created w/o BWC API compatibility\n");
883 		return NULL;
884 	}
885 
886 	bwc_rule = mlx5hws_bwc_rule_alloc(bwc_matcher);
887 	if (unlikely(!bwc_rule))
888 		return NULL;
889 
890 	bwc_queue_idx = hws_bwc_gen_queue_idx(ctx);
891 
892 	ret = mlx5hws_bwc_rule_create_simple(bwc_rule,
893 					     params->match_buf,
894 					     rule_actions,
895 					     flow_source,
896 					     bwc_queue_idx);
897 	if (unlikely(ret)) {
898 		mlx5hws_bwc_rule_free(bwc_rule);
899 		return NULL;
900 	}
901 
902 	return bwc_rule;
903 }
904 
905 static int
hws_bwc_rule_action_update(struct mlx5hws_bwc_rule * bwc_rule,struct mlx5hws_rule_action rule_actions[])906 hws_bwc_rule_action_update(struct mlx5hws_bwc_rule *bwc_rule,
907 			   struct mlx5hws_rule_action rule_actions[])
908 {
909 	struct mlx5hws_bwc_matcher *bwc_matcher = bwc_rule->bwc_matcher;
910 	struct mlx5hws_context *ctx = bwc_matcher->matcher->tbl->ctx;
911 	struct mlx5hws_rule_attr rule_attr;
912 	struct mutex *queue_lock; /* Protect the queue */
913 	int at_idx, ret;
914 	u16 idx;
915 
916 	idx = bwc_rule->bwc_queue_idx;
917 
918 	mlx5hws_bwc_rule_fill_attr(bwc_matcher, idx, 0, &rule_attr);
919 	queue_lock = hws_bwc_get_queue_lock(ctx, idx);
920 
921 	mutex_lock(queue_lock);
922 
923 	/* check if rehash needed due to missing action template */
924 	at_idx = hws_bwc_matcher_find_at(bwc_matcher, rule_actions);
925 	if (unlikely(at_idx < 0)) {
926 		/* we need to extend BWC matcher action templates array */
927 		mutex_unlock(queue_lock);
928 		hws_bwc_lock_all_queues(ctx);
929 
930 		/* check again - perhaps other thread already did extend_at */
931 		at_idx = hws_bwc_matcher_find_at(bwc_matcher, rule_actions);
932 		if (likely(at_idx < 0)) {
933 			ret = hws_bwc_matcher_extend_at(bwc_matcher, rule_actions);
934 			if (unlikely(ret)) {
935 				hws_bwc_unlock_all_queues(ctx);
936 				mlx5hws_err(ctx, "BWC rule update: failed extending AT (%d)", ret);
937 				return -EINVAL;
938 			}
939 
940 			/* action templates array was extended, we need the last idx */
941 			at_idx = bwc_matcher->num_of_at - 1;
942 
943 			ret = mlx5hws_matcher_attach_at(bwc_matcher->matcher,
944 							bwc_matcher->at[at_idx]);
945 			if (unlikely(ret)) {
946 				/* Action template attach failed, possibly due to
947 				 * requiring more action STEs.
948 				 * Need to attempt creating new matcher with all
949 				 * the action templates, including the new one.
950 				 */
951 				ret = hws_bwc_matcher_rehash_at(bwc_matcher);
952 				if (unlikely(ret)) {
953 					mlx5hws_action_template_destroy(bwc_matcher->at[at_idx]);
954 					bwc_matcher->at[at_idx] = NULL;
955 					bwc_matcher->num_of_at--;
956 
957 					hws_bwc_unlock_all_queues(ctx);
958 
959 					mlx5hws_err(ctx,
960 						    "BWC rule update: rehash AT failed (%d)\n",
961 						    ret);
962 					return ret;
963 				}
964 			}
965 		}
966 
967 		hws_bwc_unlock_all_queues(ctx);
968 		mutex_lock(queue_lock);
969 	}
970 
971 	ret = hws_bwc_rule_update_sync(bwc_rule,
972 				       at_idx,
973 				       rule_actions,
974 				       &rule_attr);
975 	mutex_unlock(queue_lock);
976 
977 	if (unlikely(ret))
978 		mlx5hws_err(ctx, "BWC rule: update failed (%d)\n", ret);
979 
980 	return ret;
981 }
982 
mlx5hws_bwc_rule_action_update(struct mlx5hws_bwc_rule * bwc_rule,struct mlx5hws_rule_action rule_actions[])983 int mlx5hws_bwc_rule_action_update(struct mlx5hws_bwc_rule *bwc_rule,
984 				   struct mlx5hws_rule_action rule_actions[])
985 {
986 	struct mlx5hws_bwc_matcher *bwc_matcher = bwc_rule->bwc_matcher;
987 	struct mlx5hws_context *ctx = bwc_matcher->matcher->tbl->ctx;
988 
989 	if (unlikely(!mlx5hws_context_bwc_supported(ctx))) {
990 		mlx5hws_err(ctx, "BWC rule: Context created w/o BWC API compatibility\n");
991 		return -EINVAL;
992 	}
993 
994 	return hws_bwc_rule_action_update(bwc_rule, rule_actions);
995 }
996