text: // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
  // Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
  
  #include "act.h"
  #include "en/tc_priv.h"
  #include "en/tc_ct.h"
  
  static bool
  tc_act_can_offload_ct(struct mlx5e_tc_act_parse_state *parse_state,
  		      const struct flow_action_entry *act,
  		      int act_index,
  		      struct mlx5_flow_attr *attr)
  {
  	bool clear_action = act->ct.action & TCA_CT_ACT_CLEAR;
  	struct netlink_ext_ack *extack = parse_state->extack;
  
  	if (parse_state->ct && !clear_action) {
  		NL_SET_ERR_MSG_MOD(extack, "Multiple CT actions are not supported");
  		return false;
  	}
  
  	return true;
  }
  
  static int
  tc_act_parse_ct(struct mlx5e_tc_act_parse_state *parse_state,
  		const struct flow_action_entry *act,
  		struct mlx5e_priv *priv,
  		struct mlx5_flow_attr *attr)
  {
  	bool clear_action = act->ct.action & TCA_CT_ACT_CLEAR;
  	int err;
  
  	/* It's redundant to do ct clear more than once. */
  	if (clear_action && parse_state->ct_clear)
  		return 0;
  
  	err = mlx5_tc_ct_parse_action(parse_state->ct_priv, attr,
  				      &attr->parse_attr->mod_hdr_acts,
  				      act, parse_state->extack);
  	if (err)
  		return err;
  
  
  	if (mlx5e_is_eswitch_flow(parse_state->flow))
  		attr->esw_attr->split_count = attr->esw_attr->out_count;
  
  	if (clear_action) {
  		parse_state->ct_clear = true;
  	} else {
  		attr->flags |= MLX5_ATTR_FLAG_CT;
  		flow_flag_set(parse_state->flow, CT);
  		parse_state->ct = true;
  	}
  
  	return 0;
  }
  
  static int
  tc_act_post_parse_ct(struct mlx5e_tc_act_parse_state *parse_state,
  		     struct mlx5e_priv *priv,
  		     struct mlx5_flow_attr *attr)
  {
  	struct mlx5e_tc_mod_hdr_acts *mod_acts = &attr->parse_attr->mod_hdr_acts;
  	int err;
  
  	/* If ct action exist, we can ignore previous ct_clear actions */
  	if (parse_state->ct)
  		return 0;
  
  	if (parse_state->ct_clear) {
  		err = mlx5_tc_ct_set_ct_clear_regs(parse_state->ct_priv, mod_acts);
  		if (err) {
  			NL_SET_ERR_MSG_MOD(parse_state->extack,
  					   "Failed to set registers for ct clear");
  			return err;
  		}
  		attr->action |= MLX5_FLOW_CONTEXT_ACTION_MOD_HDR;
  
  		/* Prevent handling of additional, redundant clear actions */
  		parse_state->ct_clear = false;
  	}
  
  	return 0;
  }
  
  static bool
  tc_act_is_multi_table_act_ct(struct mlx5e_priv *priv,
  			     const struct flow_action_entry *act,
  			     struct mlx5_flow_attr *attr)
  {
  	if (act->ct.action & TCA_CT_ACT_CLEAR)
  		return false;
  
  	return true;
  }
  
  struct mlx5e_tc_act mlx5e_tc_act_ct = {
  	.can_offload = tc_act_can_offload_ct,
  	.parse_action = tc_act_parse_ct,
  	.is_multi_table_act = tc_act_is_multi_table_act_ct,
  	.post_parse = tc_act_post_parse_ct,
  };
  
  