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