| // 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 "fs_core.h" |
| |
| static bool police_act_validate_control(enum flow_action_id act_id, |
| struct netlink_ext_ack *extack) |
| { |
| if (act_id != FLOW_ACTION_PIPE && |
| act_id != FLOW_ACTION_ACCEPT && |
| act_id != FLOW_ACTION_JUMP && |
| act_id != FLOW_ACTION_DROP) { |
| NL_SET_ERR_MSG_MOD(extack, |
| "Offload not supported when conform-exceed action is not pipe, ok, jump or drop"); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| static int police_act_validate(const struct flow_action_entry *act, |
| struct netlink_ext_ack *extack) |
| { |
| if (!police_act_validate_control(act->police.exceed.act_id, extack) || |
| !police_act_validate_control(act->police.notexceed.act_id, extack)) |
| return -EOPNOTSUPP; |
| |
| if (act->police.peakrate_bytes_ps || |
| act->police.avrate || act->police.overhead) { |
| NL_SET_ERR_MSG_MOD(extack, |
| "Offload not supported when peakrate/avrate/overhead is configured"); |
| return -EOPNOTSUPP; |
| } |
| |
| return 0; |
| } |
| |
| static bool |
| tc_act_can_offload_police(struct mlx5e_tc_act_parse_state *parse_state, |
| const struct flow_action_entry *act, |
| int act_index, |
| struct mlx5_flow_attr *attr) |
| { |
| int err; |
| |
| err = police_act_validate(act, parse_state->extack); |
| if (err) |
| return false; |
| |
| return !!mlx5e_get_flow_meters(parse_state->flow->priv->mdev); |
| } |
| |
| static int |
| fill_meter_params_from_act(const struct flow_action_entry *act, |
| struct mlx5e_flow_meter_params *params) |
| { |
| params->index = act->hw_index; |
| if (act->police.rate_bytes_ps) { |
| params->mode = MLX5_RATE_LIMIT_BPS; |
| /* change rate to bits per second */ |
| params->rate = act->police.rate_bytes_ps << 3; |
| params->burst = act->police.burst; |
| } else if (act->police.rate_pkt_ps) { |
| params->mode = MLX5_RATE_LIMIT_PPS; |
| params->rate = act->police.rate_pkt_ps; |
| params->burst = act->police.burst_pkt; |
| } else if (act->police.mtu) { |
| params->mtu = act->police.mtu; |
| } else { |
| return -EOPNOTSUPP; |
| } |
| |
| return 0; |
| } |
| |
| static int |
| tc_act_parse_police(struct mlx5e_tc_act_parse_state *parse_state, |
| const struct flow_action_entry *act, |
| struct mlx5e_priv *priv, |
| struct mlx5_flow_attr *attr) |
| { |
| enum mlx5_flow_namespace_type ns = mlx5e_get_flow_namespace(parse_state->flow); |
| struct mlx5e_flow_meter_params *params = &attr->meter_attr.params; |
| int err; |
| |
| err = fill_meter_params_from_act(act, params); |
| if (err) |
| return err; |
| |
| if (params->mtu) { |
| if (!(mlx5_fs_get_capabilities(priv->mdev, ns) & |
| MLX5_FLOW_STEERING_CAP_MATCH_RANGES)) |
| return -EOPNOTSUPP; |
| |
| attr->action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST; |
| attr->flags |= MLX5_ATTR_FLAG_MTU; |
| } else { |
| attr->action |= MLX5_FLOW_CONTEXT_ACTION_EXECUTE_ASO; |
| attr->exe_aso_type = MLX5_EXE_ASO_FLOW_METER; |
| } |
| |
| return 0; |
| } |
| |
| static bool |
| tc_act_is_multi_table_act_police(struct mlx5e_priv *priv, |
| const struct flow_action_entry *act, |
| struct mlx5_flow_attr *attr) |
| { |
| return true; |
| } |
| |
| static int |
| tc_act_police_offload(struct mlx5e_priv *priv, |
| struct flow_offload_action *fl_act, |
| struct flow_action_entry *act) |
| { |
| struct mlx5e_flow_meter_params params = {}; |
| struct mlx5e_flow_meter_handle *meter; |
| int err = 0; |
| |
| err = police_act_validate(act, fl_act->extack); |
| if (err) |
| return err; |
| |
| err = fill_meter_params_from_act(act, ¶ms); |
| if (err) |
| return err; |
| |
| meter = mlx5e_tc_meter_get(priv->mdev, ¶ms); |
| if (IS_ERR(meter) && PTR_ERR(meter) == -ENOENT) { |
| meter = mlx5e_tc_meter_replace(priv->mdev, ¶ms); |
| } else if (!IS_ERR(meter)) { |
| err = mlx5e_tc_meter_update(meter, ¶ms); |
| mlx5e_tc_meter_put(meter); |
| } |
| |
| if (IS_ERR(meter)) { |
| NL_SET_ERR_MSG_MOD(fl_act->extack, "Failed to get flow meter"); |
| mlx5_core_err(priv->mdev, "Failed to get flow meter %d\n", params.index); |
| err = PTR_ERR(meter); |
| } |
| |
| return err; |
| } |
| |
| static int |
| tc_act_police_destroy(struct mlx5e_priv *priv, |
| struct flow_offload_action *fl_act) |
| { |
| struct mlx5e_flow_meter_params params = {}; |
| struct mlx5e_flow_meter_handle *meter; |
| |
| params.index = fl_act->index; |
| meter = mlx5e_tc_meter_get(priv->mdev, ¶ms); |
| if (IS_ERR(meter)) { |
| NL_SET_ERR_MSG_MOD(fl_act->extack, "Failed to get flow meter"); |
| mlx5_core_err(priv->mdev, "Failed to get flow meter %d\n", params.index); |
| return PTR_ERR(meter); |
| } |
| /* first put for the get and second for cleanup */ |
| mlx5e_tc_meter_put(meter); |
| mlx5e_tc_meter_put(meter); |
| return 0; |
| } |
| |
| static int |
| tc_act_police_stats(struct mlx5e_priv *priv, |
| struct flow_offload_action *fl_act) |
| { |
| struct mlx5e_flow_meter_params params = {}; |
| struct mlx5e_flow_meter_handle *meter; |
| u64 bytes, packets, drops, lastuse; |
| |
| params.index = fl_act->index; |
| meter = mlx5e_tc_meter_get(priv->mdev, ¶ms); |
| if (IS_ERR(meter)) { |
| NL_SET_ERR_MSG_MOD(fl_act->extack, "Failed to get flow meter"); |
| return PTR_ERR(meter); |
| } |
| |
| mlx5e_tc_meter_get_stats(meter, &bytes, &packets, &drops, &lastuse); |
| flow_stats_update(&fl_act->stats, bytes, packets, drops, lastuse, |
| FLOW_ACTION_HW_STATS_DELAYED); |
| mlx5e_tc_meter_put(meter); |
| return 0; |
| } |
| |
| static bool |
| tc_act_police_get_branch_ctrl(const struct flow_action_entry *act, |
| struct mlx5e_tc_act_branch_ctrl *cond_true, |
| struct mlx5e_tc_act_branch_ctrl *cond_false) |
| { |
| cond_true->act_id = act->police.notexceed.act_id; |
| cond_true->extval = act->police.notexceed.extval; |
| |
| cond_false->act_id = act->police.exceed.act_id; |
| cond_false->extval = act->police.exceed.extval; |
| return true; |
| } |
| |
| struct mlx5e_tc_act mlx5e_tc_act_police = { |
| .can_offload = tc_act_can_offload_police, |
| .parse_action = tc_act_parse_police, |
| .is_multi_table_act = tc_act_is_multi_table_act_police, |
| .offload_action = tc_act_police_offload, |
| .destroy_action = tc_act_police_destroy, |
| .stats_action = tc_act_police_stats, |
| .get_branch_ctrl = tc_act_police_get_branch_ctrl, |
| }; |