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 
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 
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 	if (act->police.rate_pkt_ps) {
38 		NL_SET_ERR_MSG_MOD(extack,
39 				   "QoS offload not support packets per second");
40 		return -EOPNOTSUPP;
41 	}
42 
43 	return 0;
44 }
45 
46 static bool
47 tc_act_can_offload_police(struct mlx5e_tc_act_parse_state *parse_state,
48 			  const struct flow_action_entry *act,
49 			  int act_index,
50 			  struct mlx5_flow_attr *attr)
51 {
52 	int err;
53 
54 	err = police_act_validate(act, parse_state->extack);
55 	if (err)
56 		return false;
57 
58 	return !!mlx5e_get_flow_meters(parse_state->flow->priv->mdev);
59 }
60 
61 static int
62 fill_meter_params_from_act(const struct flow_action_entry *act,
63 			   struct mlx5e_flow_meter_params *params)
64 {
65 	params->index = act->hw_index;
66 	if (act->police.rate_bytes_ps) {
67 		params->mode = MLX5_RATE_LIMIT_BPS;
68 		/* change rate to bits per second */
69 		params->rate = act->police.rate_bytes_ps << 3;
70 		params->burst = act->police.burst;
71 	} else if (act->police.rate_pkt_ps) {
72 		params->mode = MLX5_RATE_LIMIT_PPS;
73 		params->rate = act->police.rate_pkt_ps;
74 		params->burst = act->police.burst_pkt;
75 	} else if (act->police.mtu) {
76 		params->mtu = act->police.mtu;
77 	} else {
78 		return -EOPNOTSUPP;
79 	}
80 
81 	return 0;
82 }
83 
84 static int
85 tc_act_parse_police(struct mlx5e_tc_act_parse_state *parse_state,
86 		    const struct flow_action_entry *act,
87 		    struct mlx5e_priv *priv,
88 		    struct mlx5_flow_attr *attr)
89 {
90 	enum mlx5_flow_namespace_type ns =  mlx5e_get_flow_namespace(parse_state->flow);
91 	struct mlx5e_flow_meter_params *params = &attr->meter_attr.params;
92 	int err;
93 
94 	err = fill_meter_params_from_act(act, params);
95 	if (err)
96 		return err;
97 
98 	if (params->mtu) {
99 		if (!(mlx5_fs_get_capabilities(priv->mdev, ns) &
100 		      MLX5_FLOW_STEERING_CAP_MATCH_RANGES))
101 			return -EOPNOTSUPP;
102 
103 		attr->action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST;
104 		attr->flags |= MLX5_ATTR_FLAG_MTU;
105 	} else {
106 		attr->action |= MLX5_FLOW_CONTEXT_ACTION_EXECUTE_ASO;
107 		attr->exe_aso_type = MLX5_EXE_ASO_FLOW_METER;
108 	}
109 
110 	return 0;
111 }
112 
113 static bool
114 tc_act_is_multi_table_act_police(struct mlx5e_priv *priv,
115 				 const struct flow_action_entry *act,
116 				 struct mlx5_flow_attr *attr)
117 {
118 	return true;
119 }
120 
121 static int
122 tc_act_police_offload(struct mlx5e_priv *priv,
123 		      struct flow_offload_action *fl_act,
124 		      struct flow_action_entry *act)
125 {
126 	struct mlx5e_flow_meter_params params = {};
127 	struct mlx5e_flow_meter_handle *meter;
128 	int err = 0;
129 
130 	err = police_act_validate(act, fl_act->extack);
131 	if (err)
132 		return err;
133 
134 	err = fill_meter_params_from_act(act, &params);
135 	if (err)
136 		return err;
137 
138 	meter = mlx5e_tc_meter_get(priv->mdev, &params);
139 	if (IS_ERR(meter) && PTR_ERR(meter) == -ENOENT) {
140 		meter = mlx5e_tc_meter_replace(priv->mdev, &params);
141 	} else if (!IS_ERR(meter)) {
142 		err = mlx5e_tc_meter_update(meter, &params);
143 		mlx5e_tc_meter_put(meter);
144 	}
145 
146 	if (IS_ERR(meter)) {
147 		NL_SET_ERR_MSG_MOD(fl_act->extack, "Failed to get flow meter");
148 		mlx5_core_err(priv->mdev, "Failed to get flow meter %d\n", params.index);
149 		err = PTR_ERR(meter);
150 	}
151 
152 	return err;
153 }
154 
155 static int
156 tc_act_police_destroy(struct mlx5e_priv *priv,
157 		      struct flow_offload_action *fl_act)
158 {
159 	struct mlx5e_flow_meter_params params = {};
160 	struct mlx5e_flow_meter_handle *meter;
161 
162 	params.index = fl_act->index;
163 	meter = mlx5e_tc_meter_get(priv->mdev, &params);
164 	if (IS_ERR(meter)) {
165 		NL_SET_ERR_MSG_MOD(fl_act->extack, "Failed to get flow meter");
166 		mlx5_core_err(priv->mdev, "Failed to get flow meter %d\n", params.index);
167 		return PTR_ERR(meter);
168 	}
169 	/* first put for the get and second for cleanup */
170 	mlx5e_tc_meter_put(meter);
171 	mlx5e_tc_meter_put(meter);
172 	return 0;
173 }
174 
175 static int
176 tc_act_police_stats(struct mlx5e_priv *priv,
177 		    struct flow_offload_action *fl_act)
178 {
179 	struct mlx5e_flow_meter_params params = {};
180 	struct mlx5e_flow_meter_handle *meter;
181 	u64 bytes, packets, drops, lastuse;
182 
183 	params.index = fl_act->index;
184 	meter = mlx5e_tc_meter_get(priv->mdev, &params);
185 	if (IS_ERR(meter)) {
186 		NL_SET_ERR_MSG_MOD(fl_act->extack, "Failed to get flow meter");
187 		mlx5_core_err(priv->mdev, "Failed to get flow meter %d\n", params.index);
188 		return PTR_ERR(meter);
189 	}
190 
191 	mlx5e_tc_meter_get_stats(meter, &bytes, &packets, &drops, &lastuse);
192 	flow_stats_update(&fl_act->stats, bytes, packets, drops, lastuse,
193 			  FLOW_ACTION_HW_STATS_DELAYED);
194 	mlx5e_tc_meter_put(meter);
195 	return 0;
196 }
197 
198 static bool
199 tc_act_police_get_branch_ctrl(const struct flow_action_entry *act,
200 			      struct mlx5e_tc_act_branch_ctrl *cond_true,
201 			      struct mlx5e_tc_act_branch_ctrl *cond_false)
202 {
203 	cond_true->act_id = act->police.notexceed.act_id;
204 	cond_true->extval = act->police.notexceed.extval;
205 
206 	cond_false->act_id = act->police.exceed.act_id;
207 	cond_false->extval = act->police.exceed.extval;
208 	return true;
209 }
210 
211 struct mlx5e_tc_act mlx5e_tc_act_police = {
212 	.can_offload = tc_act_can_offload_police,
213 	.parse_action = tc_act_parse_police,
214 	.is_multi_table_act = tc_act_is_multi_table_act_police,
215 	.offload_action = tc_act_police_offload,
216 	.destroy_action = tc_act_police_destroy,
217 	.stats_action = tc_act_police_stats,
218 	.get_branch_ctrl = tc_act_police_get_branch_ctrl,
219 };
220