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 *
mlx5e_post_meter_get_ft(struct mlx5e_post_meter_priv * post_meter)19 mlx5e_post_meter_get_ft(struct mlx5e_post_meter_priv *post_meter)
20 {
21 return post_meter->ft;
22 }
23
24 static int
mlx5e_post_meter_table_create(struct mlx5e_priv * priv,enum mlx5_flow_namespace_type ns_type,struct mlx5e_post_meter_priv * post_meter)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
mlx5e_post_meter_fg_create(struct mlx5e_priv * priv,struct mlx5e_post_meter_priv * post_meter)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
mlx5e_post_meter_rules_create(struct mlx5e_priv * priv,struct mlx5e_post_meter_priv * post_meter,struct mlx5e_post_act * post_act,struct mlx5_fc * green_counter,struct mlx5_fc * red_counter)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
mlx5e_post_meter_rules_destroy(struct mlx5e_post_meter_priv * post_meter)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
mlx5e_post_meter_fg_destroy(struct mlx5e_post_meter_priv * post_meter)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
mlx5e_post_meter_table_destroy(struct mlx5e_post_meter_priv * post_meter)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 *
mlx5e_post_meter_init(struct mlx5e_priv * priv,enum mlx5_flow_namespace_type ns_type,struct mlx5e_post_act * post_act,struct mlx5_fc * green_counter,struct mlx5_fc * red_counter)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
mlx5e_post_meter_cleanup(struct mlx5e_post_meter_priv * post_meter)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