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