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