File Index
Files and directories
Sign in
FILE

bwc.c

File preview with syntax highlighting.

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