1 // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
2 // Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3 
4 #include "act.h"
5 #include "en/tc_priv.h"
6 #include "fs_core.h"
7 
police_act_validate_control(enum flow_action_id act_id,struct netlink_ext_ack * extack)8 static bool police_act_validate_control(enum flow_action_id act_id,
9 					struct netlink_ext_ack *extack)
10 {
11 	if (act_id != FLOW_ACTION_PIPE &&
12 	    act_id != FLOW_ACTION_ACCEPT &&
13 	    act_id != FLOW_ACTION_JUMP &&
14 	    act_id != FLOW_ACTION_DROP) {
15 		NL_SET_ERR_MSG_MOD(extack,
16 				   "Offload not supported when conform-exceed action is not pipe, ok, jump or drop");
17 		return false;
18 	}
19 
20 	return true;
21 }
22 
police_act_validate(const struct flow_action_entry * act,struct netlink_ext_ack * extack)23 static int police_act_validate(const struct flow_action_entry *act,
24 			       struct netlink_ext_ack *extack)
25 {
26 	if (!police_act_validate_control(act->police.exceed.act_id, extack) ||
27 	    !police_act_validate_control(act->police.notexceed.act_id, extack))
28 		return -EOPNOTSUPP;
29 
30 	if (act->police.peakrate_bytes_ps ||
31 	    act->police.avrate || act->police.overhead) {
32 		NL_SET_ERR_MSG_MOD(extack,
33 				   "Offload not supported when peakrate/avrate/overhead is configured");
34 		return -EOPNOTSUPP;
35 	}
36 
37 	return 0;
38 }
39 
40 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)41 tc_act_can_offload_police(struct mlx5e_tc_act_parse_state *parse_state,
42 			  const struct flow_action_entry *act,
43 			  int act_index,
44 			  struct mlx5_flow_attr *attr)
45 {
46 	int err;
47 
48 	err = police_act_validate(act, parse_state->extack);
49 	if (err)
50 		return false;
51 
52 	return !!mlx5e_get_flow_meters(parse_state->flow->priv->mdev);
53 }
54 
55 static int
fill_meter_params_from_act(const struct flow_action_entry * act,struct mlx5e_flow_meter_params * params)56 fill_meter_params_from_act(const struct flow_action_entry *act,
57 			   struct mlx5e_flow_meter_params *params)
58 {
59 	params->index = act->hw_index;
60 	if (act->police.rate_bytes_ps) {
61 		params->mode = MLX5_RATE_LIMIT_BPS;
62 		/* change rate to bits per second */
63 		params->rate = act->police.rate_bytes_ps << 3;
64 		params->burst = act->police.burst;
65 	} else if (act->police.rate_pkt_ps) {
66 		params->mode = MLX5_RATE_LIMIT_PPS;
67 		params->rate = act->police.rate_pkt_ps;
68 		params->burst = act->police.burst_pkt;
69 	} else if (act->police.mtu) {
70 		params->mtu = act->police.mtu;
71 	} else {
72 		return -EOPNOTSUPP;
73 	}
74 
75 	return 0;
76 }
77 
78 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)79 tc_act_parse_police(struct mlx5e_tc_act_parse_state *parse_state,
80 		    const struct flow_action_entry *act,
81 		    struct mlx5e_priv *priv,
82 		    struct mlx5_flow_attr *attr)
83 {
84 	enum mlx5_flow_namespace_type ns =  mlx5e_get_flow_namespace(parse_state->flow);
85 	struct mlx5e_flow_meter_params *params = &attr->meter_attr.params;
86 	int err;
87 
88 	err = fill_meter_params_from_act(act, params);
89 	if (err)
90 		return err;
91 
92 	if (params->mtu) {
93 		if (!(mlx5_fs_get_capabilities(priv->mdev, ns) &
94 		      MLX5_FLOW_STEERING_CAP_MATCH_RANGES))
95 			return -EOPNOTSUPP;
96 
97 		attr->action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST;
98 		attr->flags |= MLX5_ATTR_FLAG_MTU;
99 	} else {
100 		attr->action |= MLX5_FLOW_CONTEXT_ACTION_EXECUTE_ASO;
101 		attr->exe_aso_type = MLX5_EXE_ASO_FLOW_METER;
102 	}
103 
104 	return 0;
105 }
106 
107 static bool
tc_act_is_multi_table_act_police(struct mlx5e_priv * priv,const struct flow_action_entry * act,struct mlx5_flow_attr * attr)108 tc_act_is_multi_table_act_police(struct mlx5e_priv *priv,
109 				 const struct flow_action_entry *act,
110 				 struct mlx5_flow_attr *attr)
111 {
112 	return true;
113 }
114 
115 static int
tc_act_police_offload(struct mlx5e_priv * priv,struct flow_offload_action * fl_act,struct flow_action_entry * act)116 tc_act_police_offload(struct mlx5e_priv *priv,
117 		      struct flow_offload_action *fl_act,
118 		      struct flow_action_entry *act)
119 {
120 	struct mlx5e_flow_meter_params params = {};
121 	struct mlx5e_flow_meter_handle *meter;
122 	int err = 0;
123 
124 	err = police_act_validate(act, fl_act->extack);
125 	if (err)
126 		return err;
127 
128 	err = fill_meter_params_from_act(act, &params);
129 	if (err)
130 		return err;
131 
132 	meter = mlx5e_tc_meter_get(priv->mdev, &params);
133 	if (IS_ERR(meter) && PTR_ERR(meter) == -ENOENT) {
134 		meter = mlx5e_tc_meter_replace(priv->mdev, &params);
135 	} else if (!IS_ERR(meter)) {
136 		err = mlx5e_tc_meter_update(meter, &params);
137 		mlx5e_tc_meter_put(meter);
138 	}
139 
140 	if (IS_ERR(meter)) {
141 		NL_SET_ERR_MSG_MOD(fl_act->extack, "Failed to get flow meter");
142 		mlx5_core_err(priv->mdev, "Failed to get flow meter %d\n", params.index);
143 		err = PTR_ERR(meter);
144 	}
145 
146 	return err;
147 }
148 
149 static int
tc_act_police_destroy(struct mlx5e_priv * priv,struct flow_offload_action * fl_act)150 tc_act_police_destroy(struct mlx5e_priv *priv,
151 		      struct flow_offload_action *fl_act)
152 {
153 	struct mlx5e_flow_meter_params params = {};
154 	struct mlx5e_flow_meter_handle *meter;
155 
156 	params.index = fl_act->index;
157 	meter = mlx5e_tc_meter_get(priv->mdev, &params);
158 	if (IS_ERR(meter)) {
159 		NL_SET_ERR_MSG_MOD(fl_act->extack, "Failed to get flow meter");
160 		mlx5_core_err(priv->mdev, "Failed to get flow meter %d\n", params.index);
161 		return PTR_ERR(meter);
162 	}
163 	/* first put for the get and second for cleanup */
164 	mlx5e_tc_meter_put(meter);
165 	mlx5e_tc_meter_put(meter);
166 	return 0;
167 }
168 
169 static int
tc_act_police_stats(struct mlx5e_priv * priv,struct flow_offload_action * fl_act)170 tc_act_police_stats(struct mlx5e_priv *priv,
171 		    struct flow_offload_action *fl_act)
172 {
173 	struct mlx5e_flow_meter_params params = {};
174 	struct mlx5e_flow_meter_handle *meter;
175 	u64 bytes, packets, drops, lastuse;
176 
177 	params.index = fl_act->index;
178 	meter = mlx5e_tc_meter_get(priv->mdev, &params);
179 	if (IS_ERR(meter)) {
180 		NL_SET_ERR_MSG_MOD(fl_act->extack, "Failed to get flow meter");
181 		return PTR_ERR(meter);
182 	}
183 
184 	mlx5e_tc_meter_get_stats(meter, &bytes, &packets, &drops, &lastuse);
185 	flow_stats_update(&fl_act->stats, bytes, packets, drops, lastuse,
186 			  FLOW_ACTION_HW_STATS_DELAYED);
187 	mlx5e_tc_meter_put(meter);
188 	return 0;
189 }
190 
191 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)192 tc_act_police_get_branch_ctrl(const struct flow_action_entry *act,
193 			      struct mlx5e_tc_act_branch_ctrl *cond_true,
194 			      struct mlx5e_tc_act_branch_ctrl *cond_false)
195 {
196 	cond_true->act_id = act->police.notexceed.act_id;
197 	cond_true->extval = act->police.notexceed.extval;
198 
199 	cond_false->act_id = act->police.exceed.act_id;
200 	cond_false->extval = act->police.exceed.extval;
201 	return true;
202 }
203 
204 struct mlx5e_tc_act mlx5e_tc_act_police = {
205 	.can_offload = tc_act_can_offload_police,
206 	.parse_action = tc_act_parse_police,
207 	.is_multi_table_act = tc_act_is_multi_table_act_police,
208 	.offload_action = tc_act_police_offload,
209 	.destroy_action = tc_act_police_destroy,
210 	.stats_action = tc_act_police_stats,
211 	.get_branch_ctrl = tc_act_police_get_branch_ctrl,
212 };
213