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