1 // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
2 // Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3 
4 #include "en/tc_priv.h"
5 #include "post_meter.h"
6 #include "en/tc/post_act.h"
7 
8 #define MLX5_PACKET_COLOR_BITS MLX5_REG_MAPPING_MBITS(PACKET_COLOR_TO_REG)
9 #define MLX5_PACKET_COLOR_MASK MLX5_REG_MAPPING_MASK(PACKET_COLOR_TO_REG)
10 
11 struct mlx5e_post_meter_priv {
12 	struct mlx5_flow_table *ft;
13 	struct mlx5_flow_group *fg;
14 	struct mlx5_flow_handle *fwd_green_rule;
15 	struct mlx5_flow_handle *drop_red_rule;
16 };
17 
18 struct mlx5_flow_table *
19 mlx5e_post_meter_get_ft(struct mlx5e_post_meter_priv *post_meter)
20 {
21 	return post_meter->ft;
22 }
23 
24 static int
25 mlx5e_post_meter_table_create(struct mlx5e_priv *priv,
26 			      enum mlx5_flow_namespace_type ns_type,
27 			      struct mlx5e_post_meter_priv *post_meter)
28 {
29 	struct mlx5_flow_table_attr ft_attr = {};
30 	struct mlx5_flow_namespace *root_ns;
31 
32 	root_ns = mlx5_get_flow_namespace(priv->mdev, ns_type);
33 	if (!root_ns) {
34 		mlx5_core_warn(priv->mdev, "Failed to get namespace for flow meter\n");
35 		return -EOPNOTSUPP;
36 	}
37 
38 	ft_attr.flags = MLX5_FLOW_TABLE_UNMANAGED;
39 	ft_attr.prio = FDB_SLOW_PATH;
40 	ft_attr.max_fte = 2;
41 	ft_attr.level = 1;
42 
43 	post_meter->ft = mlx5_create_flow_table(root_ns, &ft_attr);
44 	if (IS_ERR(post_meter->ft)) {
45 		mlx5_core_warn(priv->mdev, "Failed to create post_meter table\n");
46 		return PTR_ERR(post_meter->ft);
47 	}
48 
49 	return 0;
50 }
51 
52 static int
53 mlx5e_post_meter_fg_create(struct mlx5e_priv *priv,
54 			   struct mlx5e_post_meter_priv *post_meter)
55 {
56 	int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in);
57 	void *misc2, *match_criteria;
58 	u32 *flow_group_in;
59 	int err = 0;
60 
61 	flow_group_in = kvzalloc(inlen, GFP_KERNEL);
62 	if (!flow_group_in)
63 		return -ENOMEM;
64 
65 	MLX5_SET(create_flow_group_in, flow_group_in, match_criteria_enable,
66 		 MLX5_MATCH_MISC_PARAMETERS_2);
67 	match_criteria = MLX5_ADDR_OF(create_flow_group_in, flow_group_in,
68 				      match_criteria);
69 	misc2 = MLX5_ADDR_OF(fte_match_param, match_criteria, misc_parameters_2);
70 	MLX5_SET(fte_match_set_misc2, misc2, metadata_reg_c_5, MLX5_PACKET_COLOR_MASK);
71 	MLX5_SET(create_flow_group_in, flow_group_in, start_flow_index, 0);
72 	MLX5_SET(create_flow_group_in, flow_group_in, end_flow_index, 1);
73 
74 	post_meter->fg = mlx5_create_flow_group(post_meter->ft, flow_group_in);
75 	if (IS_ERR(post_meter->fg)) {
76 		mlx5_core_warn(priv->mdev, "Failed to create post_meter flow group\n");
77 		err = PTR_ERR(post_meter->fg);
78 	}
79 
80 	kvfree(flow_group_in);
81 	return err;
82 }
83 
84 static int
85 mlx5e_post_meter_rules_create(struct mlx5e_priv *priv,
86 			      struct mlx5e_post_meter_priv *post_meter,
87 			      struct mlx5e_post_act *post_act,
88 			      struct mlx5_fc *green_counter,
89 			      struct mlx5_fc *red_counter)
90 {
91 	struct mlx5_flow_destination dest[2] = {};
92 	struct mlx5_flow_act flow_act = {};
93 	struct mlx5_flow_handle *rule;
94 	struct mlx5_flow_spec *spec;
95 	int err;
96 
97 	spec = kvzalloc(sizeof(*spec), GFP_KERNEL);
98 	if (!spec)
99 		return -ENOMEM;
100 
101 	mlx5e_tc_match_to_reg_match(spec, PACKET_COLOR_TO_REG,
102 				    MLX5_FLOW_METER_COLOR_RED, MLX5_PACKET_COLOR_MASK);
103 	flow_act.action = MLX5_FLOW_CONTEXT_ACTION_DROP |
104 			  MLX5_FLOW_CONTEXT_ACTION_COUNT;
105 	flow_act.flags |= FLOW_ACT_IGNORE_FLOW_LEVEL;
106 	dest[0].type = MLX5_FLOW_DESTINATION_TYPE_COUNTER;
107 	dest[0].counter_id = mlx5_fc_id(red_counter);
108 
109 	rule = mlx5_add_flow_rules(post_meter->ft, spec, &flow_act, dest, 1);
110 	if (IS_ERR(rule)) {
111 		mlx5_core_warn(priv->mdev, "Failed to create post_meter flow drop rule\n");
112 		err = PTR_ERR(rule);
113 		goto err_red;
114 	}
115 	post_meter->drop_red_rule = rule;
116 
117 	mlx5e_tc_match_to_reg_match(spec, PACKET_COLOR_TO_REG,
118 				    MLX5_FLOW_METER_COLOR_GREEN, MLX5_PACKET_COLOR_MASK);
119 	flow_act.action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST |
120 			  MLX5_FLOW_CONTEXT_ACTION_COUNT;
121 	dest[0].type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE;
122 	dest[0].ft = mlx5e_tc_post_act_get_ft(post_act);
123 	dest[1].type = MLX5_FLOW_DESTINATION_TYPE_COUNTER;
124 	dest[1].counter_id = mlx5_fc_id(green_counter);
125 
126 	rule = mlx5_add_flow_rules(post_meter->ft, spec, &flow_act, dest, 2);
127 	if (IS_ERR(rule)) {
128 		mlx5_core_warn(priv->mdev, "Failed to create post_meter flow fwd rule\n");
129 		err = PTR_ERR(rule);
130 		goto err_green;
131 	}
132 	post_meter->fwd_green_rule = rule;
133 
134 	kvfree(spec);
135 	return 0;
136 
137 err_green:
138 	mlx5_del_flow_rules(post_meter->drop_red_rule);
139 err_red:
140 	kvfree(spec);
141 	return err;
142 }
143 
144 static void
145 mlx5e_post_meter_rules_destroy(struct mlx5e_post_meter_priv *post_meter)
146 {
147 	mlx5_del_flow_rules(post_meter->drop_red_rule);
148 	mlx5_del_flow_rules(post_meter->fwd_green_rule);
149 }
150 
151 static void
152 mlx5e_post_meter_fg_destroy(struct mlx5e_post_meter_priv *post_meter)
153 {
154 	mlx5_destroy_flow_group(post_meter->fg);
155 }
156 
157 static void
158 mlx5e_post_meter_table_destroy(struct mlx5e_post_meter_priv *post_meter)
159 {
160 	mlx5_destroy_flow_table(post_meter->ft);
161 }
162 
163 struct mlx5e_post_meter_priv *
164 mlx5e_post_meter_init(struct mlx5e_priv *priv,
165 		      enum mlx5_flow_namespace_type ns_type,
166 		      struct mlx5e_post_act *post_act,
167 		      struct mlx5_fc *green_counter,
168 		      struct mlx5_fc *red_counter)
169 {
170 	struct mlx5e_post_meter_priv *post_meter;
171 	int err;
172 
173 	post_meter = kzalloc(sizeof(*post_meter), GFP_KERNEL);
174 	if (!post_meter)
175 		return ERR_PTR(-ENOMEM);
176 
177 	err = mlx5e_post_meter_table_create(priv, ns_type, post_meter);
178 	if (err)
179 		goto err_ft;
180 
181 	err = mlx5e_post_meter_fg_create(priv, post_meter);
182 	if (err)
183 		goto err_fg;
184 
185 	err = mlx5e_post_meter_rules_create(priv, post_meter, post_act, green_counter,
186 					    red_counter);
187 	if (err)
188 		goto err_rules;
189 
190 	return post_meter;
191 
192 err_rules:
193 	mlx5e_post_meter_fg_destroy(post_meter);
194 err_fg:
195 	mlx5e_post_meter_table_destroy(post_meter);
196 err_ft:
197 	kfree(post_meter);
198 	return ERR_PTR(err);
199 }
200 
201 void
202 mlx5e_post_meter_cleanup(struct mlx5e_post_meter_priv *post_meter)
203 {
204 	mlx5e_post_meter_rules_destroy(post_meter);
205 	mlx5e_post_meter_fg_destroy(post_meter);
206 	mlx5e_post_meter_table_destroy(post_meter);
207 	kfree(post_meter);
208 }
209 
210