2 * Copyright (c) 2016, Mellanox Technologies. All rights reserved.
4 * This software is available to you under a choice of one of two
5 * licenses. You may choose to be licensed under the terms of the GNU
6 * General Public License (GPL) Version 2, available from the file
7 * COPYING in the main directory of this source tree, or the
8 * OpenIB.org BSD license below:
10 * Redistribution and use in source and binary forms, with or
11 * without modification, are permitted provided that the following
14 * - Redistributions of source code must retain the above
15 * copyright notice, this list of conditions and the following
18 * - Redistributions in binary form must reproduce the above
19 * copyright notice, this list of conditions and the following
20 * disclaimer in the documentation and/or other materials
21 * provided with the distribution.
23 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
27 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
28 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
29 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
33 #include <net/flow_dissector.h>
34 #include <net/pkt_cls.h>
35 #include <net/tc_act/tc_gact.h>
36 #include <net/tc_act/tc_skbedit.h>
37 #include <linux/mlx5/fs.h>
38 #include <linux/mlx5/device.h>
39 #include <linux/rhashtable.h>
43 struct mlx5e_tc_flow
{
44 struct rhash_head node
;
46 struct mlx5_flow_rule
*rule
;
49 #define MLX5E_TC_FLOW_TABLE_NUM_ENTRIES 1024
50 #define MLX5E_TC_FLOW_TABLE_NUM_GROUPS 4
52 static struct mlx5_flow_rule
*mlx5e_tc_add_flow(struct mlx5e_priv
*priv
,
53 u32
*match_c
, u32
*match_v
,
54 u32 action
, u32 flow_tag
)
56 struct mlx5_flow_destination dest
= {
57 .type
= MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE
,
58 {.ft
= priv
->fts
.vlan
.t
},
60 struct mlx5_flow_rule
*rule
;
61 bool table_created
= false;
63 if (IS_ERR_OR_NULL(priv
->fts
.tc
.t
)) {
65 mlx5_create_auto_grouped_flow_table(priv
->fts
.ns
, 0,
66 MLX5E_TC_FLOW_TABLE_NUM_ENTRIES
,
67 MLX5E_TC_FLOW_TABLE_NUM_GROUPS
);
68 if (IS_ERR(priv
->fts
.tc
.t
)) {
69 netdev_err(priv
->netdev
,
70 "Failed to create tc offload table\n");
71 return ERR_CAST(priv
->fts
.tc
.t
);
77 rule
= mlx5_add_flow_rule(priv
->fts
.tc
.t
, MLX5_MATCH_OUTER_HEADERS
,
80 action
& MLX5_FLOW_CONTEXT_ACTION_FWD_DEST
? &dest
: NULL
);
82 if (IS_ERR(rule
) && table_created
) {
83 mlx5_destroy_flow_table(priv
->fts
.tc
.t
);
84 priv
->fts
.tc
.t
= NULL
;
90 static void mlx5e_tc_del_flow(struct mlx5e_priv
*priv
,
91 struct mlx5_flow_rule
*rule
)
93 mlx5_del_flow_rule(rule
);
95 if (!mlx5e_tc_num_filters(priv
)) {
96 mlx5_destroy_flow_table(priv
->fts
.tc
.t
);
97 priv
->fts
.tc
.t
= NULL
;
101 static int parse_cls_flower(struct mlx5e_priv
*priv
,
102 u32
*match_c
, u32
*match_v
,
103 struct tc_cls_flower_offload
*f
)
105 void *headers_c
= MLX5_ADDR_OF(fte_match_param
, match_c
, outer_headers
);
106 void *headers_v
= MLX5_ADDR_OF(fte_match_param
, match_v
, outer_headers
);
110 if (f
->dissector
->used_keys
&
111 ~(BIT(FLOW_DISSECTOR_KEY_CONTROL
) |
112 BIT(FLOW_DISSECTOR_KEY_BASIC
) |
113 BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS
) |
114 BIT(FLOW_DISSECTOR_KEY_IPV4_ADDRS
) |
115 BIT(FLOW_DISSECTOR_KEY_IPV6_ADDRS
) |
116 BIT(FLOW_DISSECTOR_KEY_PORTS
))) {
117 netdev_warn(priv
->netdev
, "Unsupported key used: 0x%x\n",
118 f
->dissector
->used_keys
);
122 if (dissector_uses_key(f
->dissector
, FLOW_DISSECTOR_KEY_CONTROL
)) {
123 struct flow_dissector_key_control
*key
=
124 skb_flow_dissector_target(f
->dissector
,
125 FLOW_DISSECTOR_KEY_BASIC
,
127 addr_type
= key
->addr_type
;
130 if (dissector_uses_key(f
->dissector
, FLOW_DISSECTOR_KEY_BASIC
)) {
131 struct flow_dissector_key_basic
*key
=
132 skb_flow_dissector_target(f
->dissector
,
133 FLOW_DISSECTOR_KEY_BASIC
,
135 struct flow_dissector_key_basic
*mask
=
136 skb_flow_dissector_target(f
->dissector
,
137 FLOW_DISSECTOR_KEY_BASIC
,
139 ip_proto
= key
->ip_proto
;
141 MLX5_SET(fte_match_set_lyr_2_4
, headers_c
, ethertype
,
142 ntohs(mask
->n_proto
));
143 MLX5_SET(fte_match_set_lyr_2_4
, headers_v
, ethertype
,
144 ntohs(key
->n_proto
));
146 MLX5_SET(fte_match_set_lyr_2_4
, headers_c
, ip_protocol
,
148 MLX5_SET(fte_match_set_lyr_2_4
, headers_v
, ip_protocol
,
152 if (dissector_uses_key(f
->dissector
, FLOW_DISSECTOR_KEY_ETH_ADDRS
)) {
153 struct flow_dissector_key_eth_addrs
*key
=
154 skb_flow_dissector_target(f
->dissector
,
155 FLOW_DISSECTOR_KEY_ETH_ADDRS
,
157 struct flow_dissector_key_eth_addrs
*mask
=
158 skb_flow_dissector_target(f
->dissector
,
159 FLOW_DISSECTOR_KEY_ETH_ADDRS
,
162 ether_addr_copy(MLX5_ADDR_OF(fte_match_set_lyr_2_4
, headers_c
,
165 ether_addr_copy(MLX5_ADDR_OF(fte_match_set_lyr_2_4
, headers_v
,
169 ether_addr_copy(MLX5_ADDR_OF(fte_match_set_lyr_2_4
, headers_c
,
172 ether_addr_copy(MLX5_ADDR_OF(fte_match_set_lyr_2_4
, headers_v
,
177 if (addr_type
== FLOW_DISSECTOR_KEY_IPV4_ADDRS
) {
178 struct flow_dissector_key_ipv4_addrs
*key
=
179 skb_flow_dissector_target(f
->dissector
,
180 FLOW_DISSECTOR_KEY_IPV4_ADDRS
,
182 struct flow_dissector_key_ipv4_addrs
*mask
=
183 skb_flow_dissector_target(f
->dissector
,
184 FLOW_DISSECTOR_KEY_IPV4_ADDRS
,
187 memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4
, headers_c
,
188 src_ipv4_src_ipv6
.ipv4_layout
.ipv4
),
189 &mask
->src
, sizeof(mask
->src
));
190 memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4
, headers_v
,
191 src_ipv4_src_ipv6
.ipv4_layout
.ipv4
),
192 &key
->src
, sizeof(key
->src
));
193 memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4
, headers_c
,
194 dst_ipv4_dst_ipv6
.ipv4_layout
.ipv4
),
195 &mask
->dst
, sizeof(mask
->dst
));
196 memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4
, headers_v
,
197 dst_ipv4_dst_ipv6
.ipv4_layout
.ipv4
),
198 &key
->dst
, sizeof(key
->dst
));
201 if (addr_type
== FLOW_DISSECTOR_KEY_IPV6_ADDRS
) {
202 struct flow_dissector_key_ipv6_addrs
*key
=
203 skb_flow_dissector_target(f
->dissector
,
204 FLOW_DISSECTOR_KEY_IPV6_ADDRS
,
206 struct flow_dissector_key_ipv6_addrs
*mask
=
207 skb_flow_dissector_target(f
->dissector
,
208 FLOW_DISSECTOR_KEY_IPV6_ADDRS
,
211 memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4
, headers_c
,
212 src_ipv4_src_ipv6
.ipv6_layout
.ipv6
),
213 &mask
->src
, sizeof(mask
->src
));
214 memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4
, headers_v
,
215 src_ipv4_src_ipv6
.ipv6_layout
.ipv6
),
216 &key
->src
, sizeof(key
->src
));
218 memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4
, headers_c
,
219 dst_ipv4_dst_ipv6
.ipv6_layout
.ipv6
),
220 &mask
->dst
, sizeof(mask
->dst
));
221 memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4
, headers_v
,
222 dst_ipv4_dst_ipv6
.ipv6_layout
.ipv6
),
223 &key
->dst
, sizeof(key
->dst
));
226 if (dissector_uses_key(f
->dissector
, FLOW_DISSECTOR_KEY_PORTS
)) {
227 struct flow_dissector_key_ports
*key
=
228 skb_flow_dissector_target(f
->dissector
,
229 FLOW_DISSECTOR_KEY_PORTS
,
231 struct flow_dissector_key_ports
*mask
=
232 skb_flow_dissector_target(f
->dissector
,
233 FLOW_DISSECTOR_KEY_PORTS
,
237 MLX5_SET(fte_match_set_lyr_2_4
, headers_c
,
238 tcp_sport
, ntohs(mask
->src
));
239 MLX5_SET(fte_match_set_lyr_2_4
, headers_v
,
240 tcp_sport
, ntohs(key
->src
));
242 MLX5_SET(fte_match_set_lyr_2_4
, headers_c
,
243 tcp_dport
, ntohs(mask
->dst
));
244 MLX5_SET(fte_match_set_lyr_2_4
, headers_v
,
245 tcp_dport
, ntohs(key
->dst
));
249 MLX5_SET(fte_match_set_lyr_2_4
, headers_c
,
250 udp_sport
, ntohs(mask
->src
));
251 MLX5_SET(fte_match_set_lyr_2_4
, headers_v
,
252 udp_sport
, ntohs(key
->src
));
254 MLX5_SET(fte_match_set_lyr_2_4
, headers_c
,
255 udp_dport
, ntohs(mask
->dst
));
256 MLX5_SET(fte_match_set_lyr_2_4
, headers_v
,
257 udp_dport
, ntohs(key
->dst
));
260 netdev_err(priv
->netdev
,
261 "Only UDP and TCP transport are supported\n");
269 static int parse_tc_actions(struct mlx5e_priv
*priv
, struct tcf_exts
*exts
,
270 u32
*action
, u32
*flow_tag
)
272 const struct tc_action
*a
;
274 if (tc_no_actions(exts
))
277 *flow_tag
= MLX5_FS_DEFAULT_FLOW_TAG
;
280 tc_for_each_action(a
, exts
) {
281 /* Only support a single action per rule */
285 if (is_tcf_gact_shot(a
)) {
286 *action
|= MLX5_FLOW_CONTEXT_ACTION_DROP
;
290 if (is_tcf_skbedit_mark(a
)) {
291 u32 mark
= tcf_skbedit_mark(a
);
293 if (mark
& ~MLX5E_TC_FLOW_ID_MASK
) {
294 netdev_warn(priv
->netdev
, "Bad flow mark - only 16 bit is supported: 0x%x\n",
300 *action
|= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST
;
310 int mlx5e_configure_flower(struct mlx5e_priv
*priv
, __be16 protocol
,
311 struct tc_cls_flower_offload
*f
)
313 struct mlx5e_tc_flow_table
*tc
= &priv
->fts
.tc
;
319 struct mlx5e_tc_flow
*flow
;
320 struct mlx5_flow_rule
*old
= NULL
;
322 flow
= rhashtable_lookup_fast(&tc
->ht
, &f
->cookie
,
327 flow
= kzalloc(sizeof(*flow
), GFP_KERNEL
);
329 match_c
= kzalloc(MLX5_ST_SZ_BYTES(fte_match_param
), GFP_KERNEL
);
330 match_v
= kzalloc(MLX5_ST_SZ_BYTES(fte_match_param
), GFP_KERNEL
);
331 if (!match_c
|| !match_v
|| !flow
) {
336 flow
->cookie
= f
->cookie
;
338 err
= parse_cls_flower(priv
, match_c
, match_v
, f
);
342 err
= parse_tc_actions(priv
, f
->exts
, &action
, &flow_tag
);
346 err
= rhashtable_insert_fast(&tc
->ht
, &flow
->node
,
351 flow
->rule
= mlx5e_tc_add_flow(priv
, match_c
, match_v
, action
,
353 if (IS_ERR(flow
->rule
)) {
354 err
= PTR_ERR(flow
->rule
);
359 mlx5e_tc_del_flow(priv
, old
);
364 rhashtable_remove_fast(&tc
->ht
, &flow
->node
, tc
->ht_params
);
375 int mlx5e_delete_flower(struct mlx5e_priv
*priv
,
376 struct tc_cls_flower_offload
*f
)
378 struct mlx5e_tc_flow
*flow
;
379 struct mlx5e_tc_flow_table
*tc
= &priv
->fts
.tc
;
381 flow
= rhashtable_lookup_fast(&tc
->ht
, &f
->cookie
,
386 rhashtable_remove_fast(&tc
->ht
, &flow
->node
, tc
->ht_params
);
388 mlx5e_tc_del_flow(priv
, flow
->rule
);
395 static const struct rhashtable_params mlx5e_tc_flow_ht_params
= {
396 .head_offset
= offsetof(struct mlx5e_tc_flow
, node
),
397 .key_offset
= offsetof(struct mlx5e_tc_flow
, cookie
),
398 .key_len
= sizeof(((struct mlx5e_tc_flow
*)0)->cookie
),
399 .automatic_shrinking
= true,
402 int mlx5e_tc_init(struct mlx5e_priv
*priv
)
404 struct mlx5e_tc_flow_table
*tc
= &priv
->fts
.tc
;
406 tc
->ht_params
= mlx5e_tc_flow_ht_params
;
407 return rhashtable_init(&tc
->ht
, &tc
->ht_params
);
410 static void _mlx5e_tc_del_flow(void *ptr
, void *arg
)
412 struct mlx5e_tc_flow
*flow
= ptr
;
413 struct mlx5e_priv
*priv
= arg
;
415 mlx5e_tc_del_flow(priv
, flow
->rule
);
419 void mlx5e_tc_cleanup(struct mlx5e_priv
*priv
)
421 struct mlx5e_tc_flow_table
*tc
= &priv
->fts
.tc
;
423 rhashtable_free_and_destroy(&tc
->ht
, _mlx5e_tc_del_flow
, priv
);
425 if (!IS_ERR_OR_NULL(priv
->fts
.tc
.t
)) {
426 mlx5_destroy_flow_table(priv
->fts
.tc
.t
);
427 priv
->fts
.tc
.t
= NULL
;