diff options
author | Maor Gottlieb <maorg@mellanox.com> | 2016-07-04 10:23:08 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2016-07-05 03:06:02 -0400 |
commit | 6dc6071cfcde6cf687f8d288c9cef9ee6ee24dc7 (patch) | |
tree | e9a4b2d401edb2d4f857f7f85825002965087bee | |
parent | 0da2d66666d32769fa0aebb5f1d2d0a86be6c5d2 (diff) |
net/mlx5e: Add ethtool flow steering support
Implement etrhtool set_rxnfc callback to support ethtool flow spec
direct steering. This patch adds only the support of ether flow type
spec. L3/L4 flow specs support will be added in downstream patches.
Signed-off-by: Maor Gottlieb <maorg@mellanox.com>
Signed-off-by: Saeed Mahameed <saeedm@mellanox.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | drivers/net/ethernet/mellanox/mlx5/core/Makefile | 2 | ||||
-rw-r--r-- | drivers/net/ethernet/mellanox/mlx5/core/en.h | 20 | ||||
-rw-r--r-- | drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c | 21 | ||||
-rw-r--r-- | drivers/net/ethernet/mellanox/mlx5/core/en_fs.c | 3 | ||||
-rw-r--r-- | drivers/net/ethernet/mellanox/mlx5/core/en_fs_ethtool.c | 393 | ||||
-rw-r--r-- | drivers/net/ethernet/mellanox/mlx5/core/fs_core.c | 28 | ||||
-rw-r--r-- | include/linux/mlx5/fs.h | 1 |
7 files changed, 456 insertions, 12 deletions
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/Makefile b/drivers/net/ethernet/mellanox/mlx5/core/Makefile index a574deabdda8..05cc1effc13c 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/Makefile +++ b/drivers/net/ethernet/mellanox/mlx5/core/Makefile | |||
@@ -8,6 +8,6 @@ mlx5_core-y := main.o cmd.o debugfs.o fw.o eq.o uar.o pagealloc.o \ | |||
8 | mlx5_core-$(CONFIG_MLX5_CORE_EN) += wq.o eswitch.o eswitch_offloads.o \ | 8 | mlx5_core-$(CONFIG_MLX5_CORE_EN) += wq.o eswitch.o eswitch_offloads.o \ |
9 | en_main.o en_common.o en_fs.o en_ethtool.o en_tx.o \ | 9 | en_main.o en_common.o en_fs.o en_ethtool.o en_tx.o \ |
10 | en_rx.o en_rx_am.o en_txrx.o en_clock.o vxlan.o \ | 10 | en_rx.o en_rx_am.o en_txrx.o en_clock.o vxlan.o \ |
11 | en_tc.o en_arfs.o en_rep.o | 11 | en_tc.o en_arfs.o en_rep.o en_fs_ethtool.o |
12 | 12 | ||
13 | mlx5_core-$(CONFIG_MLX5_CORE_EN_DCB) += en_dcbnl.o | 13 | mlx5_core-$(CONFIG_MLX5_CORE_EN_DCB) += en_dcbnl.o |
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index 00643a116492..357320e79de2 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h | |||
@@ -544,8 +544,22 @@ enum { | |||
544 | MLX5E_ARFS_FT_LEVEL | 544 | MLX5E_ARFS_FT_LEVEL |
545 | }; | 545 | }; |
546 | 546 | ||
547 | struct mlx5e_ethtool_table { | ||
548 | struct mlx5_flow_table *ft; | ||
549 | int num_rules; | ||
550 | }; | ||
551 | |||
552 | #define ETHTOOL_NUM_L2_FTS 4 | ||
553 | |||
554 | struct mlx5e_ethtool_steering { | ||
555 | struct mlx5e_ethtool_table l2_ft[ETHTOOL_NUM_L2_FTS]; | ||
556 | struct list_head rules; | ||
557 | int tot_num_rules; | ||
558 | }; | ||
559 | |||
547 | struct mlx5e_flow_steering { | 560 | struct mlx5e_flow_steering { |
548 | struct mlx5_flow_namespace *ns; | 561 | struct mlx5_flow_namespace *ns; |
562 | struct mlx5e_ethtool_steering ethtool; | ||
549 | struct mlx5e_tc_table tc; | 563 | struct mlx5e_tc_table tc; |
550 | struct mlx5e_vlan_table vlan; | 564 | struct mlx5e_vlan_table vlan; |
551 | struct mlx5e_l2_table l2; | 565 | struct mlx5e_l2_table l2; |
@@ -701,6 +715,12 @@ int mlx5e_create_flow_steering(struct mlx5e_priv *priv); | |||
701 | void mlx5e_destroy_flow_steering(struct mlx5e_priv *priv); | 715 | void mlx5e_destroy_flow_steering(struct mlx5e_priv *priv); |
702 | void mlx5e_init_l2_addr(struct mlx5e_priv *priv); | 716 | void mlx5e_init_l2_addr(struct mlx5e_priv *priv); |
703 | void mlx5e_destroy_flow_table(struct mlx5e_flow_table *ft); | 717 | void mlx5e_destroy_flow_table(struct mlx5e_flow_table *ft); |
718 | int mlx5e_ethtool_flow_replace(struct mlx5e_priv *priv, | ||
719 | struct ethtool_rx_flow_spec *fs); | ||
720 | int mlx5e_ethtool_flow_remove(struct mlx5e_priv *priv, | ||
721 | int location); | ||
722 | void mlx5e_ethtool_init_steering(struct mlx5e_priv *priv); | ||
723 | void mlx5e_ethtool_cleanup_steering(struct mlx5e_priv *priv); | ||
704 | void mlx5e_set_rx_mode_work(struct work_struct *work); | 724 | void mlx5e_set_rx_mode_work(struct work_struct *work); |
705 | 725 | ||
706 | void mlx5e_fill_hwstamp(struct mlx5e_tstamp *clock, u64 timestamp, | 726 | void mlx5e_fill_hwstamp(struct mlx5e_tstamp *clock, u64 timestamp, |
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c index 7e61ffa96732..edbb665f4d22 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c | |||
@@ -1368,6 +1368,26 @@ static u32 mlx5e_get_priv_flags(struct net_device *netdev) | |||
1368 | return priv->pflags; | 1368 | return priv->pflags; |
1369 | } | 1369 | } |
1370 | 1370 | ||
1371 | static int mlx5e_set_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd) | ||
1372 | { | ||
1373 | int err = 0; | ||
1374 | struct mlx5e_priv *priv = netdev_priv(dev); | ||
1375 | |||
1376 | switch (cmd->cmd) { | ||
1377 | case ETHTOOL_SRXCLSRLINS: | ||
1378 | err = mlx5e_ethtool_flow_replace(priv, &cmd->fs); | ||
1379 | break; | ||
1380 | case ETHTOOL_SRXCLSRLDEL: | ||
1381 | err = mlx5e_ethtool_flow_remove(priv, cmd->fs.location); | ||
1382 | break; | ||
1383 | default: | ||
1384 | err = -EOPNOTSUPP; | ||
1385 | break; | ||
1386 | } | ||
1387 | |||
1388 | return err; | ||
1389 | } | ||
1390 | |||
1371 | const struct ethtool_ops mlx5e_ethtool_ops = { | 1391 | const struct ethtool_ops mlx5e_ethtool_ops = { |
1372 | .get_drvinfo = mlx5e_get_drvinfo, | 1392 | .get_drvinfo = mlx5e_get_drvinfo, |
1373 | .get_link = ethtool_op_get_link, | 1393 | .get_link = ethtool_op_get_link, |
@@ -1387,6 +1407,7 @@ const struct ethtool_ops mlx5e_ethtool_ops = { | |||
1387 | .get_rxfh = mlx5e_get_rxfh, | 1407 | .get_rxfh = mlx5e_get_rxfh, |
1388 | .set_rxfh = mlx5e_set_rxfh, | 1408 | .set_rxfh = mlx5e_set_rxfh, |
1389 | .get_rxnfc = mlx5e_get_rxnfc, | 1409 | .get_rxnfc = mlx5e_get_rxnfc, |
1410 | .set_rxnfc = mlx5e_set_rxnfc, | ||
1390 | .get_tunable = mlx5e_get_tunable, | 1411 | .get_tunable = mlx5e_get_tunable, |
1391 | .set_tunable = mlx5e_set_tunable, | 1412 | .set_tunable = mlx5e_set_tunable, |
1392 | .get_pauseparam = mlx5e_get_pauseparam, | 1413 | .get_pauseparam = mlx5e_get_pauseparam, |
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c b/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c index 2e1e86316fe7..1587a9fd5724 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c | |||
@@ -1084,6 +1084,8 @@ int mlx5e_create_flow_steering(struct mlx5e_priv *priv) | |||
1084 | goto err_destroy_l2_table; | 1084 | goto err_destroy_l2_table; |
1085 | } | 1085 | } |
1086 | 1086 | ||
1087 | mlx5e_ethtool_init_steering(priv); | ||
1088 | |||
1087 | return 0; | 1089 | return 0; |
1088 | 1090 | ||
1089 | err_destroy_l2_table: | 1091 | err_destroy_l2_table: |
@@ -1103,4 +1105,5 @@ void mlx5e_destroy_flow_steering(struct mlx5e_priv *priv) | |||
1103 | mlx5e_destroy_l2_table(priv); | 1105 | mlx5e_destroy_l2_table(priv); |
1104 | mlx5e_destroy_ttc_table(priv); | 1106 | mlx5e_destroy_ttc_table(priv); |
1105 | mlx5e_arfs_destroy_tables(priv); | 1107 | mlx5e_arfs_destroy_tables(priv); |
1108 | mlx5e_ethtool_cleanup_steering(priv); | ||
1106 | } | 1109 | } |
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_fs_ethtool.c b/drivers/net/ethernet/mellanox/mlx5/core/en_fs_ethtool.c new file mode 100644 index 000000000000..ee28a9fc0b9d --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_fs_ethtool.c | |||
@@ -0,0 +1,393 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2016, Mellanox Technologies. All rights reserved. | ||
3 | * | ||
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: | ||
9 | * | ||
10 | * Redistribution and use in source and binary forms, with or | ||
11 | * without modification, are permitted provided that the following | ||
12 | * conditions are met: | ||
13 | * | ||
14 | * - Redistributions of source code must retain the above | ||
15 | * copyright notice, this list of conditions and the following | ||
16 | * disclaimer. | ||
17 | * | ||
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. | ||
22 | * | ||
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 | ||
30 | * SOFTWARE. | ||
31 | */ | ||
32 | |||
33 | #include <linux/mlx5/fs.h> | ||
34 | #include "en.h" | ||
35 | |||
36 | struct mlx5e_ethtool_rule { | ||
37 | struct list_head list; | ||
38 | struct ethtool_rx_flow_spec flow_spec; | ||
39 | struct mlx5_flow_rule *rule; | ||
40 | struct mlx5e_ethtool_table *eth_ft; | ||
41 | }; | ||
42 | |||
43 | static void put_flow_table(struct mlx5e_ethtool_table *eth_ft) | ||
44 | { | ||
45 | if (!--eth_ft->num_rules) { | ||
46 | mlx5_destroy_flow_table(eth_ft->ft); | ||
47 | eth_ft->ft = NULL; | ||
48 | } | ||
49 | } | ||
50 | |||
51 | #define MLX5E_ETHTOOL_L2_PRIO 0 | ||
52 | #define MLX5E_ETHTOOL_NUM_ENTRIES 64000 | ||
53 | #define MLX5E_ETHTOOL_NUM_GROUPS 10 | ||
54 | static struct mlx5e_ethtool_table *get_flow_table(struct mlx5e_priv *priv, | ||
55 | struct ethtool_rx_flow_spec *fs, | ||
56 | int num_tuples) | ||
57 | { | ||
58 | struct mlx5e_ethtool_table *eth_ft; | ||
59 | struct mlx5_flow_namespace *ns; | ||
60 | struct mlx5_flow_table *ft; | ||
61 | int max_tuples; | ||
62 | int table_size; | ||
63 | int prio; | ||
64 | |||
65 | switch (fs->flow_type & ~(FLOW_EXT | FLOW_MAC_EXT)) { | ||
66 | case ETHER_FLOW: | ||
67 | max_tuples = ETHTOOL_NUM_L2_FTS; | ||
68 | prio = max_tuples - num_tuples; | ||
69 | eth_ft = &priv->fs.ethtool.l2_ft[prio]; | ||
70 | prio += MLX5E_ETHTOOL_L2_PRIO; | ||
71 | break; | ||
72 | default: | ||
73 | return ERR_PTR(-EINVAL); | ||
74 | } | ||
75 | |||
76 | eth_ft->num_rules++; | ||
77 | if (eth_ft->ft) | ||
78 | return eth_ft; | ||
79 | |||
80 | ns = mlx5_get_flow_namespace(priv->mdev, | ||
81 | MLX5_FLOW_NAMESPACE_ETHTOOL); | ||
82 | if (!ns) | ||
83 | return ERR_PTR(-ENOTSUPP); | ||
84 | |||
85 | table_size = min_t(u32, BIT(MLX5_CAP_FLOWTABLE(priv->mdev, | ||
86 | flow_table_properties_nic_receive.log_max_ft_size)), | ||
87 | MLX5E_ETHTOOL_NUM_ENTRIES); | ||
88 | ft = mlx5_create_auto_grouped_flow_table(ns, prio, | ||
89 | table_size, | ||
90 | MLX5E_ETHTOOL_NUM_GROUPS, 0); | ||
91 | if (IS_ERR(ft)) | ||
92 | return (void *)ft; | ||
93 | |||
94 | eth_ft->ft = ft; | ||
95 | return eth_ft; | ||
96 | } | ||
97 | |||
98 | static void mask_spec(u8 *mask, u8 *val, size_t size) | ||
99 | { | ||
100 | unsigned int i; | ||
101 | |||
102 | for (i = 0; i < size; i++, mask++, val++) | ||
103 | *((u8 *)val) = *((u8 *)mask) & *((u8 *)val); | ||
104 | } | ||
105 | |||
106 | static int set_flow_attrs(u32 *match_c, u32 *match_v, | ||
107 | struct ethtool_rx_flow_spec *fs) | ||
108 | { | ||
109 | void *outer_headers_c = MLX5_ADDR_OF(fte_match_param, match_c, | ||
110 | outer_headers); | ||
111 | void *outer_headers_v = MLX5_ADDR_OF(fte_match_param, match_v, | ||
112 | outer_headers); | ||
113 | u32 flow_type = fs->flow_type & ~(FLOW_EXT | FLOW_MAC_EXT); | ||
114 | struct ethhdr *eth_val; | ||
115 | struct ethhdr *eth_mask; | ||
116 | |||
117 | switch (flow_type) { | ||
118 | case ETHER_FLOW: | ||
119 | eth_mask = &fs->m_u.ether_spec; | ||
120 | eth_val = &fs->h_u.ether_spec; | ||
121 | |||
122 | mask_spec((u8 *)eth_mask, (u8 *)eth_val, sizeof(*eth_mask)); | ||
123 | ether_addr_copy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, | ||
124 | outer_headers_c, smac_47_16), | ||
125 | eth_mask->h_source); | ||
126 | ether_addr_copy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, | ||
127 | outer_headers_v, smac_47_16), | ||
128 | eth_val->h_source); | ||
129 | ether_addr_copy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, | ||
130 | outer_headers_c, dmac_47_16), | ||
131 | eth_mask->h_dest); | ||
132 | ether_addr_copy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, | ||
133 | outer_headers_v, dmac_47_16), | ||
134 | eth_val->h_dest); | ||
135 | MLX5_SET(fte_match_set_lyr_2_4, outer_headers_c, ethertype, | ||
136 | ntohs(eth_mask->h_proto)); | ||
137 | MLX5_SET(fte_match_set_lyr_2_4, outer_headers_v, ethertype, | ||
138 | ntohs(eth_val->h_proto)); | ||
139 | break; | ||
140 | default: | ||
141 | return -EINVAL; | ||
142 | } | ||
143 | |||
144 | if ((fs->flow_type & FLOW_EXT) && | ||
145 | (fs->m_ext.vlan_tci & cpu_to_be16(VLAN_VID_MASK))) { | ||
146 | MLX5_SET(fte_match_set_lyr_2_4, outer_headers_c, | ||
147 | vlan_tag, 1); | ||
148 | MLX5_SET(fte_match_set_lyr_2_4, outer_headers_v, | ||
149 | vlan_tag, 1); | ||
150 | MLX5_SET(fte_match_set_lyr_2_4, outer_headers_c, | ||
151 | first_vid, 0xfff); | ||
152 | MLX5_SET(fte_match_set_lyr_2_4, outer_headers_v, | ||
153 | first_vid, ntohs(fs->h_ext.vlan_tci)); | ||
154 | } | ||
155 | |||
156 | return 0; | ||
157 | } | ||
158 | |||
159 | static void add_rule_to_list(struct mlx5e_priv *priv, | ||
160 | struct mlx5e_ethtool_rule *rule) | ||
161 | { | ||
162 | struct mlx5e_ethtool_rule *iter; | ||
163 | struct list_head *head = &priv->fs.ethtool.rules; | ||
164 | |||
165 | list_for_each_entry(iter, &priv->fs.ethtool.rules, list) { | ||
166 | if (iter->flow_spec.location > rule->flow_spec.location) | ||
167 | break; | ||
168 | head = &iter->list; | ||
169 | } | ||
170 | priv->fs.ethtool.tot_num_rules++; | ||
171 | list_add(&rule->list, head); | ||
172 | } | ||
173 | |||
174 | static bool outer_header_zero(u32 *match_criteria) | ||
175 | { | ||
176 | int size = MLX5_ST_SZ_BYTES(fte_match_param); | ||
177 | char *outer_headers_c = MLX5_ADDR_OF(fte_match_param, match_criteria, | ||
178 | outer_headers); | ||
179 | |||
180 | return outer_headers_c[0] == 0 && !memcmp(outer_headers_c, | ||
181 | outer_headers_c + 1, | ||
182 | size - 1); | ||
183 | } | ||
184 | |||
185 | static struct mlx5_flow_rule *add_ethtool_flow_rule(struct mlx5e_priv *priv, | ||
186 | struct mlx5_flow_table *ft, | ||
187 | struct ethtool_rx_flow_spec *fs) | ||
188 | { | ||
189 | struct mlx5_flow_destination *dst = NULL; | ||
190 | struct mlx5_flow_spec *spec; | ||
191 | struct mlx5_flow_rule *rule; | ||
192 | int err = 0; | ||
193 | u32 action; | ||
194 | |||
195 | spec = mlx5_vzalloc(sizeof(*spec)); | ||
196 | if (!spec) | ||
197 | return ERR_PTR(-ENOMEM); | ||
198 | err = set_flow_attrs(spec->match_criteria, spec->match_value, | ||
199 | fs); | ||
200 | if (err) | ||
201 | goto free; | ||
202 | |||
203 | if (fs->ring_cookie == RX_CLS_FLOW_DISC) { | ||
204 | action = MLX5_FLOW_CONTEXT_ACTION_DROP; | ||
205 | } else { | ||
206 | dst = kzalloc(sizeof(*dst), GFP_KERNEL); | ||
207 | if (!dst) { | ||
208 | err = -ENOMEM; | ||
209 | goto free; | ||
210 | } | ||
211 | |||
212 | dst->type = MLX5_FLOW_DESTINATION_TYPE_TIR; | ||
213 | dst->tir_num = priv->direct_tir[fs->ring_cookie].tirn; | ||
214 | action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST; | ||
215 | } | ||
216 | |||
217 | spec->match_criteria_enable = (!outer_header_zero(spec->match_criteria)); | ||
218 | rule = mlx5_add_flow_rule(ft, spec, action, | ||
219 | MLX5_FS_DEFAULT_FLOW_TAG, dst); | ||
220 | if (IS_ERR(rule)) { | ||
221 | err = PTR_ERR(rule); | ||
222 | netdev_err(priv->netdev, "%s: failed to add ethtool steering rule: %d\n", | ||
223 | __func__, err); | ||
224 | goto free; | ||
225 | } | ||
226 | free: | ||
227 | kvfree(spec); | ||
228 | kfree(dst); | ||
229 | return err ? ERR_PTR(err) : rule; | ||
230 | } | ||
231 | |||
232 | static void del_ethtool_rule(struct mlx5e_priv *priv, | ||
233 | struct mlx5e_ethtool_rule *eth_rule) | ||
234 | { | ||
235 | if (eth_rule->rule) | ||
236 | mlx5_del_flow_rule(eth_rule->rule); | ||
237 | list_del(ð_rule->list); | ||
238 | priv->fs.ethtool.tot_num_rules--; | ||
239 | put_flow_table(eth_rule->eth_ft); | ||
240 | kfree(eth_rule); | ||
241 | } | ||
242 | |||
243 | static struct mlx5e_ethtool_rule *find_ethtool_rule(struct mlx5e_priv *priv, | ||
244 | int location) | ||
245 | { | ||
246 | struct mlx5e_ethtool_rule *iter; | ||
247 | |||
248 | list_for_each_entry(iter, &priv->fs.ethtool.rules, list) { | ||
249 | if (iter->flow_spec.location == location) | ||
250 | return iter; | ||
251 | } | ||
252 | return NULL; | ||
253 | } | ||
254 | |||
255 | static struct mlx5e_ethtool_rule *get_ethtool_rule(struct mlx5e_priv *priv, | ||
256 | int location) | ||
257 | { | ||
258 | struct mlx5e_ethtool_rule *eth_rule; | ||
259 | |||
260 | eth_rule = find_ethtool_rule(priv, location); | ||
261 | if (eth_rule) | ||
262 | del_ethtool_rule(priv, eth_rule); | ||
263 | |||
264 | eth_rule = kzalloc(sizeof(*eth_rule), GFP_KERNEL); | ||
265 | if (!eth_rule) | ||
266 | return ERR_PTR(-ENOMEM); | ||
267 | |||
268 | add_rule_to_list(priv, eth_rule); | ||
269 | return eth_rule; | ||
270 | } | ||
271 | |||
272 | #define MAX_NUM_OF_ETHTOOL_RULES BIT(10) | ||
273 | static int validate_flow(struct mlx5e_priv *priv, | ||
274 | struct ethtool_rx_flow_spec *fs) | ||
275 | { | ||
276 | struct ethhdr *eth_mask; | ||
277 | int num_tuples = 0; | ||
278 | |||
279 | if (fs->location >= MAX_NUM_OF_ETHTOOL_RULES) | ||
280 | return -EINVAL; | ||
281 | |||
282 | if (fs->ring_cookie >= priv->params.num_channels && | ||
283 | fs->ring_cookie != RX_CLS_FLOW_DISC) | ||
284 | return -EINVAL; | ||
285 | |||
286 | switch (fs->flow_type & ~(FLOW_EXT | FLOW_MAC_EXT)) { | ||
287 | case ETHER_FLOW: | ||
288 | eth_mask = &fs->m_u.ether_spec; | ||
289 | if (!is_zero_ether_addr(eth_mask->h_dest)) | ||
290 | num_tuples++; | ||
291 | if (!is_zero_ether_addr(eth_mask->h_source)) | ||
292 | num_tuples++; | ||
293 | if (eth_mask->h_proto) | ||
294 | num_tuples++; | ||
295 | break; | ||
296 | default: | ||
297 | return -EINVAL; | ||
298 | } | ||
299 | if ((fs->flow_type & FLOW_EXT)) { | ||
300 | if (fs->m_ext.vlan_etype || | ||
301 | (fs->m_ext.vlan_tci != cpu_to_be16(VLAN_VID_MASK))) | ||
302 | return -EINVAL; | ||
303 | |||
304 | if (fs->m_ext.vlan_tci) { | ||
305 | if (be16_to_cpu(fs->h_ext.vlan_tci) >= VLAN_N_VID) | ||
306 | return -EINVAL; | ||
307 | } | ||
308 | num_tuples++; | ||
309 | } | ||
310 | |||
311 | return num_tuples; | ||
312 | } | ||
313 | |||
314 | int mlx5e_ethtool_flow_replace(struct mlx5e_priv *priv, | ||
315 | struct ethtool_rx_flow_spec *fs) | ||
316 | { | ||
317 | struct mlx5e_ethtool_table *eth_ft; | ||
318 | struct mlx5e_ethtool_rule *eth_rule; | ||
319 | struct mlx5_flow_rule *rule; | ||
320 | int num_tuples; | ||
321 | int err; | ||
322 | |||
323 | num_tuples = validate_flow(priv, fs); | ||
324 | if (num_tuples <= 0) { | ||
325 | netdev_warn(priv->netdev, "%s: flow is not valid\n", __func__); | ||
326 | return -EINVAL; | ||
327 | } | ||
328 | |||
329 | eth_ft = get_flow_table(priv, fs, num_tuples); | ||
330 | if (IS_ERR(eth_ft)) | ||
331 | return PTR_ERR(eth_ft); | ||
332 | |||
333 | eth_rule = get_ethtool_rule(priv, fs->location); | ||
334 | if (IS_ERR(eth_rule)) { | ||
335 | put_flow_table(eth_ft); | ||
336 | return PTR_ERR(eth_rule); | ||
337 | } | ||
338 | |||
339 | eth_rule->flow_spec = *fs; | ||
340 | eth_rule->eth_ft = eth_ft; | ||
341 | if (!eth_ft->ft) { | ||
342 | err = -EINVAL; | ||
343 | goto del_ethtool_rule; | ||
344 | } | ||
345 | rule = add_ethtool_flow_rule(priv, eth_ft->ft, fs); | ||
346 | if (IS_ERR(rule)) { | ||
347 | err = PTR_ERR(rule); | ||
348 | goto del_ethtool_rule; | ||
349 | } | ||
350 | |||
351 | eth_rule->rule = rule; | ||
352 | |||
353 | return 0; | ||
354 | |||
355 | del_ethtool_rule: | ||
356 | del_ethtool_rule(priv, eth_rule); | ||
357 | |||
358 | return err; | ||
359 | } | ||
360 | |||
361 | int mlx5e_ethtool_flow_remove(struct mlx5e_priv *priv, | ||
362 | int location) | ||
363 | { | ||
364 | struct mlx5e_ethtool_rule *eth_rule; | ||
365 | int err = 0; | ||
366 | |||
367 | if (location >= MAX_NUM_OF_ETHTOOL_RULES) | ||
368 | return -ENOSPC; | ||
369 | |||
370 | eth_rule = find_ethtool_rule(priv, location); | ||
371 | if (!eth_rule) { | ||
372 | err = -ENOENT; | ||
373 | goto out; | ||
374 | } | ||
375 | |||
376 | del_ethtool_rule(priv, eth_rule); | ||
377 | out: | ||
378 | return err; | ||
379 | } | ||
380 | |||
381 | void mlx5e_ethtool_cleanup_steering(struct mlx5e_priv *priv) | ||
382 | { | ||
383 | struct mlx5e_ethtool_rule *iter; | ||
384 | struct mlx5e_ethtool_rule *temp; | ||
385 | |||
386 | list_for_each_entry_safe(iter, temp, &priv->fs.ethtool.rules, list) | ||
387 | del_ethtool_rule(priv, iter); | ||
388 | } | ||
389 | |||
390 | void mlx5e_ethtool_init_steering(struct mlx5e_priv *priv) | ||
391 | { | ||
392 | INIT_LIST_HEAD(&priv->fs.ethtool.rules); | ||
393 | } | ||
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c index 3e9577537b52..83fa98fde795 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c | |||
@@ -67,13 +67,21 @@ | |||
67 | #define FS_REQUIRED_CAPS(...) {.arr_sz = INIT_CAPS_ARRAY_SIZE(__VA_ARGS__), \ | 67 | #define FS_REQUIRED_CAPS(...) {.arr_sz = INIT_CAPS_ARRAY_SIZE(__VA_ARGS__), \ |
68 | .caps = (long[]) {__VA_ARGS__} } | 68 | .caps = (long[]) {__VA_ARGS__} } |
69 | 69 | ||
70 | #define FS_CHAINING_CAPS FS_REQUIRED_CAPS(FS_CAP(flow_table_properties_nic_receive.flow_modify_en), \ | ||
71 | FS_CAP(flow_table_properties_nic_receive.modify_root), \ | ||
72 | FS_CAP(flow_table_properties_nic_receive.identified_miss_table_mode), \ | ||
73 | FS_CAP(flow_table_properties_nic_receive.flow_table_modify)) | ||
74 | |||
70 | #define LEFTOVERS_NUM_LEVELS 1 | 75 | #define LEFTOVERS_NUM_LEVELS 1 |
71 | #define LEFTOVERS_NUM_PRIOS 1 | 76 | #define LEFTOVERS_NUM_PRIOS 1 |
72 | 77 | ||
73 | #define BY_PASS_PRIO_NUM_LEVELS 1 | 78 | #define BY_PASS_PRIO_NUM_LEVELS 1 |
74 | #define BY_PASS_MIN_LEVEL (KERNEL_MIN_LEVEL + MLX5_BY_PASS_NUM_PRIOS +\ | 79 | #define BY_PASS_MIN_LEVEL (ETHTOOL_MIN_LEVEL + MLX5_BY_PASS_NUM_PRIOS +\ |
75 | LEFTOVERS_NUM_PRIOS) | 80 | LEFTOVERS_NUM_PRIOS) |
76 | 81 | ||
82 | #define ETHTOOL_PRIO_NUM_LEVELS 1 | ||
83 | #define ETHTOOL_NUM_PRIOS 4 | ||
84 | #define ETHTOOL_MIN_LEVEL (KERNEL_MIN_LEVEL + ETHTOOL_NUM_PRIOS) | ||
77 | /* Vlan, mac, ttc, aRFS */ | 85 | /* Vlan, mac, ttc, aRFS */ |
78 | #define KERNEL_NIC_PRIO_NUM_LEVELS 4 | 86 | #define KERNEL_NIC_PRIO_NUM_LEVELS 4 |
79 | #define KERNEL_NIC_NUM_PRIOS 1 | 87 | #define KERNEL_NIC_NUM_PRIOS 1 |
@@ -103,27 +111,24 @@ static struct init_tree_node { | |||
103 | int num_levels; | 111 | int num_levels; |
104 | } root_fs = { | 112 | } root_fs = { |
105 | .type = FS_TYPE_NAMESPACE, | 113 | .type = FS_TYPE_NAMESPACE, |
106 | .ar_size = 5, | 114 | .ar_size = 6, |
107 | .children = (struct init_tree_node[]) { | 115 | .children = (struct init_tree_node[]) { |
108 | ADD_PRIO(0, BY_PASS_MIN_LEVEL, 0, | 116 | ADD_PRIO(0, BY_PASS_MIN_LEVEL, 0, |
109 | FS_REQUIRED_CAPS(FS_CAP(flow_table_properties_nic_receive.flow_modify_en), | 117 | FS_CHAINING_CAPS, |
110 | FS_CAP(flow_table_properties_nic_receive.modify_root), | ||
111 | FS_CAP(flow_table_properties_nic_receive.identified_miss_table_mode), | ||
112 | FS_CAP(flow_table_properties_nic_receive.flow_table_modify)), | ||
113 | ADD_NS(ADD_MULTIPLE_PRIO(MLX5_BY_PASS_NUM_PRIOS, | 118 | ADD_NS(ADD_MULTIPLE_PRIO(MLX5_BY_PASS_NUM_PRIOS, |
114 | BY_PASS_PRIO_NUM_LEVELS))), | 119 | BY_PASS_PRIO_NUM_LEVELS))), |
115 | ADD_PRIO(0, OFFLOADS_MIN_LEVEL, 0, {}, | 120 | ADD_PRIO(0, OFFLOADS_MIN_LEVEL, 0, {}, |
116 | ADD_NS(ADD_MULTIPLE_PRIO(OFFLOADS_NUM_PRIOS, OFFLOADS_MAX_FT))), | 121 | ADD_NS(ADD_MULTIPLE_PRIO(OFFLOADS_NUM_PRIOS, OFFLOADS_MAX_FT))), |
117 | 122 | ADD_PRIO(0, ETHTOOL_MIN_LEVEL, 0, | |
123 | FS_CHAINING_CAPS, | ||
124 | ADD_NS(ADD_MULTIPLE_PRIO(ETHTOOL_NUM_PRIOS, | ||
125 | ETHTOOL_PRIO_NUM_LEVELS))), | ||
118 | ADD_PRIO(0, KERNEL_MIN_LEVEL, 0, {}, | 126 | ADD_PRIO(0, KERNEL_MIN_LEVEL, 0, {}, |
119 | ADD_NS(ADD_MULTIPLE_PRIO(1, 1), | 127 | ADD_NS(ADD_MULTIPLE_PRIO(1, 1), |
120 | ADD_MULTIPLE_PRIO(KERNEL_NIC_NUM_PRIOS, | 128 | ADD_MULTIPLE_PRIO(KERNEL_NIC_NUM_PRIOS, |
121 | KERNEL_NIC_PRIO_NUM_LEVELS))), | 129 | KERNEL_NIC_PRIO_NUM_LEVELS))), |
122 | ADD_PRIO(0, BY_PASS_MIN_LEVEL, 0, | 130 | ADD_PRIO(0, BY_PASS_MIN_LEVEL, 0, |
123 | FS_REQUIRED_CAPS(FS_CAP(flow_table_properties_nic_receive.flow_modify_en), | 131 | FS_CHAINING_CAPS, |
124 | FS_CAP(flow_table_properties_nic_receive.modify_root), | ||
125 | FS_CAP(flow_table_properties_nic_receive.identified_miss_table_mode), | ||
126 | FS_CAP(flow_table_properties_nic_receive.flow_table_modify)), | ||
127 | ADD_NS(ADD_MULTIPLE_PRIO(LEFTOVERS_NUM_PRIOS, LEFTOVERS_NUM_LEVELS))), | 132 | ADD_NS(ADD_MULTIPLE_PRIO(LEFTOVERS_NUM_PRIOS, LEFTOVERS_NUM_LEVELS))), |
128 | ADD_PRIO(0, ANCHOR_MIN_LEVEL, 0, {}, | 133 | ADD_PRIO(0, ANCHOR_MIN_LEVEL, 0, {}, |
129 | ADD_NS(ADD_MULTIPLE_PRIO(ANCHOR_NUM_PRIOS, ANCHOR_NUM_LEVELS))), | 134 | ADD_NS(ADD_MULTIPLE_PRIO(ANCHOR_NUM_PRIOS, ANCHOR_NUM_LEVELS))), |
@@ -1375,6 +1380,7 @@ struct mlx5_flow_namespace *mlx5_get_flow_namespace(struct mlx5_core_dev *dev, | |||
1375 | switch (type) { | 1380 | switch (type) { |
1376 | case MLX5_FLOW_NAMESPACE_BYPASS: | 1381 | case MLX5_FLOW_NAMESPACE_BYPASS: |
1377 | case MLX5_FLOW_NAMESPACE_OFFLOADS: | 1382 | case MLX5_FLOW_NAMESPACE_OFFLOADS: |
1383 | case MLX5_FLOW_NAMESPACE_ETHTOOL: | ||
1378 | case MLX5_FLOW_NAMESPACE_KERNEL: | 1384 | case MLX5_FLOW_NAMESPACE_KERNEL: |
1379 | case MLX5_FLOW_NAMESPACE_LEFTOVERS: | 1385 | case MLX5_FLOW_NAMESPACE_LEFTOVERS: |
1380 | case MLX5_FLOW_NAMESPACE_ANCHOR: | 1386 | case MLX5_FLOW_NAMESPACE_ANCHOR: |
diff --git a/include/linux/mlx5/fs.h b/include/linux/mlx5/fs.h index d22fe7e5a39a..e036d6030867 100644 --- a/include/linux/mlx5/fs.h +++ b/include/linux/mlx5/fs.h | |||
@@ -55,6 +55,7 @@ static inline void build_leftovers_ft_param(int *priority, | |||
55 | enum mlx5_flow_namespace_type { | 55 | enum mlx5_flow_namespace_type { |
56 | MLX5_FLOW_NAMESPACE_BYPASS, | 56 | MLX5_FLOW_NAMESPACE_BYPASS, |
57 | MLX5_FLOW_NAMESPACE_OFFLOADS, | 57 | MLX5_FLOW_NAMESPACE_OFFLOADS, |
58 | MLX5_FLOW_NAMESPACE_ETHTOOL, | ||
58 | MLX5_FLOW_NAMESPACE_KERNEL, | 59 | MLX5_FLOW_NAMESPACE_KERNEL, |
59 | MLX5_FLOW_NAMESPACE_LEFTOVERS, | 60 | MLX5_FLOW_NAMESPACE_LEFTOVERS, |
60 | MLX5_FLOW_NAMESPACE_ANCHOR, | 61 | MLX5_FLOW_NAMESPACE_ANCHOR, |