diff options
Diffstat (limited to 'drivers/net/ethernet/netronome/nfp/flower/action.c')
| -rw-r--r-- | drivers/net/ethernet/netronome/nfp/flower/action.c | 427 |
1 files changed, 338 insertions, 89 deletions
diff --git a/drivers/net/ethernet/netronome/nfp/flower/action.c b/drivers/net/ethernet/netronome/nfp/flower/action.c index 80df9a5d4217..244dc261006e 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/action.c +++ b/drivers/net/ethernet/netronome/nfp/flower/action.c | |||
| @@ -1,39 +1,11 @@ | |||
| 1 | /* | 1 | // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) |
| 2 | * Copyright (C) 2017 Netronome Systems, Inc. | 2 | /* Copyright (C) 2017-2018 Netronome Systems, Inc. */ |
| 3 | * | ||
| 4 | * This software is dual licensed under the GNU General License Version 2, | ||
| 5 | * June 1991 as shown in the file COPYING in the top-level directory of this | ||
| 6 | * source tree or the BSD 2-Clause License provided below. You have the | ||
| 7 | * option to license this software under the complete terms of either license. | ||
| 8 | * | ||
| 9 | * The BSD 2-Clause License: | ||
| 10 | * | ||
| 11 | * Redistribution and use in source and binary forms, with or | ||
| 12 | * without modification, are permitted provided that the following | ||
| 13 | * conditions are met: | ||
| 14 | * | ||
| 15 | * 1. Redistributions of source code must retain the above | ||
| 16 | * copyright notice, this list of conditions and the following | ||
| 17 | * disclaimer. | ||
| 18 | * | ||
| 19 | * 2. Redistributions in binary form must reproduce the above | ||
| 20 | * copyright notice, this list of conditions and the following | ||
| 21 | * disclaimer in the documentation and/or other materials | ||
| 22 | * provided with the distribution. | ||
| 23 | * | ||
| 24 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
| 25 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
| 26 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | ||
| 27 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS | ||
| 28 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | ||
| 29 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | ||
| 30 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
| 31 | * SOFTWARE. | ||
| 32 | */ | ||
| 33 | 3 | ||
| 34 | #include <linux/bitfield.h> | 4 | #include <linux/bitfield.h> |
| 5 | #include <net/geneve.h> | ||
| 35 | #include <net/pkt_cls.h> | 6 | #include <net/pkt_cls.h> |
| 36 | #include <net/switchdev.h> | 7 | #include <net/switchdev.h> |
| 8 | #include <net/tc_act/tc_csum.h> | ||
| 37 | #include <net/tc_act/tc_gact.h> | 9 | #include <net/tc_act/tc_gact.h> |
| 38 | #include <net/tc_act/tc_mirred.h> | 10 | #include <net/tc_act/tc_mirred.h> |
| 39 | #include <net/tc_act/tc_pedit.h> | 11 | #include <net/tc_act/tc_pedit.h> |
| @@ -44,6 +16,17 @@ | |||
| 44 | #include "main.h" | 16 | #include "main.h" |
| 45 | #include "../nfp_net_repr.h" | 17 | #include "../nfp_net_repr.h" |
| 46 | 18 | ||
| 19 | /* The kernel versions of TUNNEL_* are not ABI and therefore vulnerable | ||
| 20 | * to change. Such changes will break our FW ABI. | ||
| 21 | */ | ||
| 22 | #define NFP_FL_TUNNEL_CSUM cpu_to_be16(0x01) | ||
| 23 | #define NFP_FL_TUNNEL_KEY cpu_to_be16(0x04) | ||
| 24 | #define NFP_FL_TUNNEL_GENEVE_OPT cpu_to_be16(0x0800) | ||
| 25 | #define NFP_FL_SUPPORTED_TUNNEL_INFO_FLAGS IP_TUNNEL_INFO_TX | ||
| 26 | #define NFP_FL_SUPPORTED_IPV4_UDP_TUN_FLAGS (NFP_FL_TUNNEL_CSUM | \ | ||
| 27 | NFP_FL_TUNNEL_KEY | \ | ||
| 28 | NFP_FL_TUNNEL_GENEVE_OPT) | ||
| 29 | |||
| 47 | static void nfp_fl_pop_vlan(struct nfp_fl_pop_vlan *pop_vlan) | 30 | static void nfp_fl_pop_vlan(struct nfp_fl_pop_vlan *pop_vlan) |
| 48 | { | 31 | { |
| 49 | size_t act_size = sizeof(struct nfp_fl_pop_vlan); | 32 | size_t act_size = sizeof(struct nfp_fl_pop_vlan); |
| @@ -72,6 +55,42 @@ nfp_fl_push_vlan(struct nfp_fl_push_vlan *push_vlan, | |||
| 72 | push_vlan->vlan_tci = cpu_to_be16(tmp_push_vlan_tci); | 55 | push_vlan->vlan_tci = cpu_to_be16(tmp_push_vlan_tci); |
| 73 | } | 56 | } |
| 74 | 57 | ||
| 58 | static int | ||
| 59 | nfp_fl_pre_lag(struct nfp_app *app, const struct tc_action *action, | ||
| 60 | struct nfp_fl_payload *nfp_flow, int act_len) | ||
| 61 | { | ||
| 62 | size_t act_size = sizeof(struct nfp_fl_pre_lag); | ||
| 63 | struct nfp_fl_pre_lag *pre_lag; | ||
| 64 | struct net_device *out_dev; | ||
| 65 | int err; | ||
| 66 | |||
| 67 | out_dev = tcf_mirred_dev(action); | ||
| 68 | if (!out_dev || !netif_is_lag_master(out_dev)) | ||
| 69 | return 0; | ||
| 70 | |||
| 71 | if (act_len + act_size > NFP_FL_MAX_A_SIZ) | ||
| 72 | return -EOPNOTSUPP; | ||
| 73 | |||
| 74 | /* Pre_lag action must be first on action list. | ||
| 75 | * If other actions already exist they need pushed forward. | ||
| 76 | */ | ||
| 77 | if (act_len) | ||
| 78 | memmove(nfp_flow->action_data + act_size, | ||
| 79 | nfp_flow->action_data, act_len); | ||
| 80 | |||
| 81 | pre_lag = (struct nfp_fl_pre_lag *)nfp_flow->action_data; | ||
| 82 | err = nfp_flower_lag_populate_pre_action(app, out_dev, pre_lag); | ||
| 83 | if (err) | ||
| 84 | return err; | ||
| 85 | |||
| 86 | pre_lag->head.jump_id = NFP_FL_ACTION_OPCODE_PRE_LAG; | ||
| 87 | pre_lag->head.len_lw = act_size >> NFP_FL_LW_SIZ; | ||
| 88 | |||
| 89 | nfp_flow->meta.shortcut = cpu_to_be32(NFP_FL_SC_ACT_NULL); | ||
| 90 | |||
| 91 | return act_size; | ||
| 92 | } | ||
| 93 | |||
| 75 | static bool nfp_fl_netdev_is_tunnel_type(struct net_device *out_dev, | 94 | static bool nfp_fl_netdev_is_tunnel_type(struct net_device *out_dev, |
| 76 | enum nfp_flower_tun_type tun_type) | 95 | enum nfp_flower_tun_type tun_type) |
| 77 | { | 96 | { |
| @@ -88,12 +107,13 @@ static bool nfp_fl_netdev_is_tunnel_type(struct net_device *out_dev, | |||
| 88 | } | 107 | } |
| 89 | 108 | ||
| 90 | static int | 109 | static int |
| 91 | nfp_fl_output(struct nfp_fl_output *output, const struct tc_action *action, | 110 | nfp_fl_output(struct nfp_app *app, struct nfp_fl_output *output, |
| 92 | struct nfp_fl_payload *nfp_flow, bool last, | 111 | const struct tc_action *action, struct nfp_fl_payload *nfp_flow, |
| 93 | struct net_device *in_dev, enum nfp_flower_tun_type tun_type, | 112 | bool last, struct net_device *in_dev, |
| 94 | int *tun_out_cnt) | 113 | enum nfp_flower_tun_type tun_type, int *tun_out_cnt) |
| 95 | { | 114 | { |
| 96 | size_t act_size = sizeof(struct nfp_fl_output); | 115 | size_t act_size = sizeof(struct nfp_fl_output); |
| 116 | struct nfp_flower_priv *priv = app->priv; | ||
| 97 | struct net_device *out_dev; | 117 | struct net_device *out_dev; |
| 98 | u16 tmp_flags; | 118 | u16 tmp_flags; |
| 99 | 119 | ||
| @@ -118,6 +138,15 @@ nfp_fl_output(struct nfp_fl_output *output, const struct tc_action *action, | |||
| 118 | output->flags = cpu_to_be16(tmp_flags | | 138 | output->flags = cpu_to_be16(tmp_flags | |
| 119 | NFP_FL_OUT_FLAGS_USE_TUN); | 139 | NFP_FL_OUT_FLAGS_USE_TUN); |
| 120 | output->port = cpu_to_be32(NFP_FL_PORT_TYPE_TUN | tun_type); | 140 | output->port = cpu_to_be32(NFP_FL_PORT_TYPE_TUN | tun_type); |
| 141 | } else if (netif_is_lag_master(out_dev) && | ||
| 142 | priv->flower_ext_feats & NFP_FL_FEATS_LAG) { | ||
| 143 | int gid; | ||
| 144 | |||
| 145 | output->flags = cpu_to_be16(tmp_flags); | ||
| 146 | gid = nfp_flower_lag_get_output_id(app, out_dev); | ||
| 147 | if (gid < 0) | ||
| 148 | return gid; | ||
| 149 | output->port = cpu_to_be32(NFP_FL_LAG_OUT | gid); | ||
| 121 | } else { | 150 | } else { |
| 122 | /* Set action output parameters. */ | 151 | /* Set action output parameters. */ |
| 123 | output->flags = cpu_to_be16(tmp_flags); | 152 | output->flags = cpu_to_be16(tmp_flags); |
| @@ -164,7 +193,7 @@ static struct nfp_fl_pre_tunnel *nfp_fl_pre_tunnel(char *act_data, int act_len) | |||
| 164 | struct nfp_fl_pre_tunnel *pre_tun_act; | 193 | struct nfp_fl_pre_tunnel *pre_tun_act; |
| 165 | 194 | ||
| 166 | /* Pre_tunnel action must be first on action list. | 195 | /* Pre_tunnel action must be first on action list. |
| 167 | * If other actions already exist they need pushed forward. | 196 | * If other actions already exist they need to be pushed forward. |
| 168 | */ | 197 | */ |
| 169 | if (act_len) | 198 | if (act_len) |
| 170 | memmove(act_data + act_size, act_data, act_len); | 199 | memmove(act_data + act_size, act_data, act_len); |
| @@ -180,7 +209,71 @@ static struct nfp_fl_pre_tunnel *nfp_fl_pre_tunnel(char *act_data, int act_len) | |||
| 180 | } | 209 | } |
| 181 | 210 | ||
| 182 | static int | 211 | static int |
| 183 | nfp_fl_set_ipv4_udp_tun(struct nfp_fl_set_ipv4_udp_tun *set_tun, | 212 | nfp_fl_push_geneve_options(struct nfp_fl_payload *nfp_fl, int *list_len, |
| 213 | const struct tc_action *action) | ||
| 214 | { | ||
| 215 | struct ip_tunnel_info *ip_tun = tcf_tunnel_info(action); | ||
| 216 | int opt_len, opt_cnt, act_start, tot_push_len; | ||
| 217 | u8 *src = ip_tunnel_info_opts(ip_tun); | ||
| 218 | |||
| 219 | /* We need to populate the options in reverse order for HW. | ||
| 220 | * Therefore we go through the options, calculating the | ||
| 221 | * number of options and the total size, then we populate | ||
| 222 | * them in reverse order in the action list. | ||
| 223 | */ | ||
| 224 | opt_cnt = 0; | ||
| 225 | tot_push_len = 0; | ||
| 226 | opt_len = ip_tun->options_len; | ||
| 227 | while (opt_len > 0) { | ||
| 228 | struct geneve_opt *opt = (struct geneve_opt *)src; | ||
| 229 | |||
| 230 | opt_cnt++; | ||
| 231 | if (opt_cnt > NFP_FL_MAX_GENEVE_OPT_CNT) | ||
| 232 | return -EOPNOTSUPP; | ||
| 233 | |||
| 234 | tot_push_len += sizeof(struct nfp_fl_push_geneve) + | ||
| 235 | opt->length * 4; | ||
| 236 | if (tot_push_len > NFP_FL_MAX_GENEVE_OPT_ACT) | ||
| 237 | return -EOPNOTSUPP; | ||
| 238 | |||
| 239 | opt_len -= sizeof(struct geneve_opt) + opt->length * 4; | ||
| 240 | src += sizeof(struct geneve_opt) + opt->length * 4; | ||
| 241 | } | ||
| 242 | |||
| 243 | if (*list_len + tot_push_len > NFP_FL_MAX_A_SIZ) | ||
| 244 | return -EOPNOTSUPP; | ||
| 245 | |||
| 246 | act_start = *list_len; | ||
| 247 | *list_len += tot_push_len; | ||
| 248 | src = ip_tunnel_info_opts(ip_tun); | ||
| 249 | while (opt_cnt) { | ||
| 250 | struct geneve_opt *opt = (struct geneve_opt *)src; | ||
| 251 | struct nfp_fl_push_geneve *push; | ||
| 252 | size_t act_size, len; | ||
| 253 | |||
| 254 | opt_cnt--; | ||
| 255 | act_size = sizeof(struct nfp_fl_push_geneve) + opt->length * 4; | ||
| 256 | tot_push_len -= act_size; | ||
| 257 | len = act_start + tot_push_len; | ||
| 258 | |||
| 259 | push = (struct nfp_fl_push_geneve *)&nfp_fl->action_data[len]; | ||
| 260 | push->head.jump_id = NFP_FL_ACTION_OPCODE_PUSH_GENEVE; | ||
| 261 | push->head.len_lw = act_size >> NFP_FL_LW_SIZ; | ||
| 262 | push->reserved = 0; | ||
| 263 | push->class = opt->opt_class; | ||
| 264 | push->type = opt->type; | ||
| 265 | push->length = opt->length; | ||
| 266 | memcpy(&push->opt_data, opt->opt_data, opt->length * 4); | ||
| 267 | |||
| 268 | src += sizeof(struct geneve_opt) + opt->length * 4; | ||
| 269 | } | ||
| 270 | |||
| 271 | return 0; | ||
| 272 | } | ||
| 273 | |||
| 274 | static int | ||
| 275 | nfp_fl_set_ipv4_udp_tun(struct nfp_app *app, | ||
| 276 | struct nfp_fl_set_ipv4_udp_tun *set_tun, | ||
| 184 | const struct tc_action *action, | 277 | const struct tc_action *action, |
| 185 | struct nfp_fl_pre_tunnel *pre_tun, | 278 | struct nfp_fl_pre_tunnel *pre_tun, |
| 186 | enum nfp_flower_tun_type tun_type, | 279 | enum nfp_flower_tun_type tun_type, |
| @@ -188,16 +281,19 @@ nfp_fl_set_ipv4_udp_tun(struct nfp_fl_set_ipv4_udp_tun *set_tun, | |||
| 188 | { | 281 | { |
| 189 | size_t act_size = sizeof(struct nfp_fl_set_ipv4_udp_tun); | 282 | size_t act_size = sizeof(struct nfp_fl_set_ipv4_udp_tun); |
| 190 | struct ip_tunnel_info *ip_tun = tcf_tunnel_info(action); | 283 | struct ip_tunnel_info *ip_tun = tcf_tunnel_info(action); |
| 284 | struct nfp_flower_priv *priv = app->priv; | ||
| 191 | u32 tmp_set_ip_tun_type_index = 0; | 285 | u32 tmp_set_ip_tun_type_index = 0; |
| 192 | /* Currently support one pre-tunnel so index is always 0. */ | 286 | /* Currently support one pre-tunnel so index is always 0. */ |
| 193 | int pretun_idx = 0; | 287 | int pretun_idx = 0; |
| 194 | struct net *net; | ||
| 195 | 288 | ||
| 196 | if (ip_tun->options_len) | 289 | BUILD_BUG_ON(NFP_FL_TUNNEL_CSUM != TUNNEL_CSUM || |
| 290 | NFP_FL_TUNNEL_KEY != TUNNEL_KEY || | ||
| 291 | NFP_FL_TUNNEL_GENEVE_OPT != TUNNEL_GENEVE_OPT); | ||
| 292 | if (ip_tun->options_len && | ||
| 293 | (tun_type != NFP_FL_TUNNEL_GENEVE || | ||
| 294 | !(priv->flower_ext_feats & NFP_FL_FEATS_GENEVE_OPT))) | ||
| 197 | return -EOPNOTSUPP; | 295 | return -EOPNOTSUPP; |
| 198 | 296 | ||
| 199 | net = dev_net(netdev); | ||
| 200 | |||
| 201 | set_tun->head.jump_id = NFP_FL_ACTION_OPCODE_SET_IPV4_TUNNEL; | 297 | set_tun->head.jump_id = NFP_FL_ACTION_OPCODE_SET_IPV4_TUNNEL; |
| 202 | set_tun->head.len_lw = act_size >> NFP_FL_LW_SIZ; | 298 | set_tun->head.len_lw = act_size >> NFP_FL_LW_SIZ; |
| 203 | 299 | ||
| @@ -208,7 +304,42 @@ nfp_fl_set_ipv4_udp_tun(struct nfp_fl_set_ipv4_udp_tun *set_tun, | |||
| 208 | 304 | ||
| 209 | set_tun->tun_type_index = cpu_to_be32(tmp_set_ip_tun_type_index); | 305 | set_tun->tun_type_index = cpu_to_be32(tmp_set_ip_tun_type_index); |
| 210 | set_tun->tun_id = ip_tun->key.tun_id; | 306 | set_tun->tun_id = ip_tun->key.tun_id; |
| 211 | set_tun->ttl = net->ipv4.sysctl_ip_default_ttl; | 307 | |
| 308 | if (ip_tun->key.ttl) { | ||
| 309 | set_tun->ttl = ip_tun->key.ttl; | ||
| 310 | } else { | ||
| 311 | struct net *net = dev_net(netdev); | ||
| 312 | struct flowi4 flow = {}; | ||
| 313 | struct rtable *rt; | ||
| 314 | int err; | ||
| 315 | |||
| 316 | /* Do a route lookup to determine ttl - if fails then use | ||
| 317 | * default. Note that CONFIG_INET is a requirement of | ||
| 318 | * CONFIG_NET_SWITCHDEV so must be defined here. | ||
| 319 | */ | ||
| 320 | flow.daddr = ip_tun->key.u.ipv4.dst; | ||
| 321 | flow.flowi4_proto = IPPROTO_UDP; | ||
| 322 | rt = ip_route_output_key(net, &flow); | ||
| 323 | err = PTR_ERR_OR_ZERO(rt); | ||
| 324 | if (!err) { | ||
| 325 | set_tun->ttl = ip4_dst_hoplimit(&rt->dst); | ||
| 326 | ip_rt_put(rt); | ||
| 327 | } else { | ||
| 328 | set_tun->ttl = net->ipv4.sysctl_ip_default_ttl; | ||
| 329 | } | ||
| 330 | } | ||
| 331 | |||
| 332 | set_tun->tos = ip_tun->key.tos; | ||
| 333 | |||
| 334 | if (!(ip_tun->key.tun_flags & NFP_FL_TUNNEL_KEY) || | ||
| 335 | ip_tun->key.tun_flags & ~NFP_FL_SUPPORTED_IPV4_UDP_TUN_FLAGS) | ||
| 336 | return -EOPNOTSUPP; | ||
| 337 | set_tun->tun_flags = ip_tun->key.tun_flags; | ||
| 338 | |||
| 339 | if (tun_type == NFP_FL_TUNNEL_GENEVE) { | ||
| 340 | set_tun->tun_proto = htons(ETH_P_TEB); | ||
| 341 | set_tun->tun_len = ip_tun->options_len / 4; | ||
| 342 | } | ||
| 212 | 343 | ||
| 213 | /* Complete pre_tunnel action. */ | 344 | /* Complete pre_tunnel action. */ |
| 214 | pre_tun->ipv4_dst = ip_tun->key.u.ipv4.dst; | 345 | pre_tun->ipv4_dst = ip_tun->key.u.ipv4.dst; |
| @@ -268,12 +399,14 @@ nfp_fl_set_ip4(const struct tc_action *action, int idx, u32 off, | |||
| 268 | 399 | ||
| 269 | switch (off) { | 400 | switch (off) { |
| 270 | case offsetof(struct iphdr, daddr): | 401 | case offsetof(struct iphdr, daddr): |
| 271 | set_ip_addr->ipv4_dst_mask = mask; | 402 | set_ip_addr->ipv4_dst_mask |= mask; |
| 272 | set_ip_addr->ipv4_dst = exact; | 403 | set_ip_addr->ipv4_dst &= ~mask; |
| 404 | set_ip_addr->ipv4_dst |= exact & mask; | ||
| 273 | break; | 405 | break; |
| 274 | case offsetof(struct iphdr, saddr): | 406 | case offsetof(struct iphdr, saddr): |
| 275 | set_ip_addr->ipv4_src_mask = mask; | 407 | set_ip_addr->ipv4_src_mask |= mask; |
| 276 | set_ip_addr->ipv4_src = exact; | 408 | set_ip_addr->ipv4_src &= ~mask; |
| 409 | set_ip_addr->ipv4_src |= exact & mask; | ||
| 277 | break; | 410 | break; |
| 278 | default: | 411 | default: |
| 279 | return -EOPNOTSUPP; | 412 | return -EOPNOTSUPP; |
| @@ -287,11 +420,12 @@ nfp_fl_set_ip4(const struct tc_action *action, int idx, u32 off, | |||
| 287 | } | 420 | } |
| 288 | 421 | ||
| 289 | static void | 422 | static void |
| 290 | nfp_fl_set_ip6_helper(int opcode_tag, int idx, __be32 exact, __be32 mask, | 423 | nfp_fl_set_ip6_helper(int opcode_tag, u8 word, __be32 exact, __be32 mask, |
| 291 | struct nfp_fl_set_ipv6_addr *ip6) | 424 | struct nfp_fl_set_ipv6_addr *ip6) |
| 292 | { | 425 | { |
| 293 | ip6->ipv6[idx % 4].mask = mask; | 426 | ip6->ipv6[word].mask |= mask; |
| 294 | ip6->ipv6[idx % 4].exact = exact; | 427 | ip6->ipv6[word].exact &= ~mask; |
| 428 | ip6->ipv6[word].exact |= exact & mask; | ||
| 295 | 429 | ||
| 296 | ip6->reserved = cpu_to_be16(0); | 430 | ip6->reserved = cpu_to_be16(0); |
| 297 | ip6->head.jump_id = opcode_tag; | 431 | ip6->head.jump_id = opcode_tag; |
| @@ -304,6 +438,7 @@ nfp_fl_set_ip6(const struct tc_action *action, int idx, u32 off, | |||
| 304 | struct nfp_fl_set_ipv6_addr *ip_src) | 438 | struct nfp_fl_set_ipv6_addr *ip_src) |
| 305 | { | 439 | { |
| 306 | __be32 exact, mask; | 440 | __be32 exact, mask; |
| 441 | u8 word; | ||
| 307 | 442 | ||
| 308 | /* We are expecting tcf_pedit to return a big endian value */ | 443 | /* We are expecting tcf_pedit to return a big endian value */ |
| 309 | mask = (__force __be32)~tcf_pedit_mask(action, idx); | 444 | mask = (__force __be32)~tcf_pedit_mask(action, idx); |
| @@ -312,17 +447,20 @@ nfp_fl_set_ip6(const struct tc_action *action, int idx, u32 off, | |||
| 312 | if (exact & ~mask) | 447 | if (exact & ~mask) |
| 313 | return -EOPNOTSUPP; | 448 | return -EOPNOTSUPP; |
| 314 | 449 | ||
| 315 | if (off < offsetof(struct ipv6hdr, saddr)) | 450 | if (off < offsetof(struct ipv6hdr, saddr)) { |
| 316 | return -EOPNOTSUPP; | 451 | return -EOPNOTSUPP; |
| 317 | else if (off < offsetof(struct ipv6hdr, daddr)) | 452 | } else if (off < offsetof(struct ipv6hdr, daddr)) { |
| 318 | nfp_fl_set_ip6_helper(NFP_FL_ACTION_OPCODE_SET_IPV6_SRC, idx, | 453 | word = (off - offsetof(struct ipv6hdr, saddr)) / sizeof(exact); |
| 454 | nfp_fl_set_ip6_helper(NFP_FL_ACTION_OPCODE_SET_IPV6_SRC, word, | ||
| 319 | exact, mask, ip_src); | 455 | exact, mask, ip_src); |
| 320 | else if (off < offsetof(struct ipv6hdr, daddr) + | 456 | } else if (off < offsetof(struct ipv6hdr, daddr) + |
| 321 | sizeof(struct in6_addr)) | 457 | sizeof(struct in6_addr)) { |
| 322 | nfp_fl_set_ip6_helper(NFP_FL_ACTION_OPCODE_SET_IPV6_DST, idx, | 458 | word = (off - offsetof(struct ipv6hdr, daddr)) / sizeof(exact); |
| 459 | nfp_fl_set_ip6_helper(NFP_FL_ACTION_OPCODE_SET_IPV6_DST, word, | ||
| 323 | exact, mask, ip_dst); | 460 | exact, mask, ip_dst); |
| 324 | else | 461 | } else { |
| 325 | return -EOPNOTSUPP; | 462 | return -EOPNOTSUPP; |
| 463 | } | ||
| 326 | 464 | ||
| 327 | return 0; | 465 | return 0; |
| 328 | } | 466 | } |
| @@ -352,8 +490,27 @@ nfp_fl_set_tport(const struct tc_action *action, int idx, u32 off, | |||
| 352 | return 0; | 490 | return 0; |
| 353 | } | 491 | } |
| 354 | 492 | ||
| 493 | static u32 nfp_fl_csum_l4_to_flag(u8 ip_proto) | ||
| 494 | { | ||
| 495 | switch (ip_proto) { | ||
| 496 | case 0: | ||
| 497 | /* Filter doesn't force proto match, | ||
| 498 | * both TCP and UDP will be updated if encountered | ||
| 499 | */ | ||
| 500 | return TCA_CSUM_UPDATE_FLAG_TCP | TCA_CSUM_UPDATE_FLAG_UDP; | ||
| 501 | case IPPROTO_TCP: | ||
| 502 | return TCA_CSUM_UPDATE_FLAG_TCP; | ||
| 503 | case IPPROTO_UDP: | ||
| 504 | return TCA_CSUM_UPDATE_FLAG_UDP; | ||
| 505 | default: | ||
| 506 | /* All other protocols will be ignored by FW */ | ||
| 507 | return 0; | ||
| 508 | } | ||
| 509 | } | ||
| 510 | |||
| 355 | static int | 511 | static int |
| 356 | nfp_fl_pedit(const struct tc_action *action, char *nfp_action, int *a_len) | 512 | nfp_fl_pedit(const struct tc_action *action, struct tc_cls_flower_offload *flow, |
| 513 | char *nfp_action, int *a_len, u32 *csum_updated) | ||
| 357 | { | 514 | { |
| 358 | struct nfp_fl_set_ipv6_addr set_ip6_dst, set_ip6_src; | 515 | struct nfp_fl_set_ipv6_addr set_ip6_dst, set_ip6_src; |
| 359 | struct nfp_fl_set_ip4_addrs set_ip_addr; | 516 | struct nfp_fl_set_ip4_addrs set_ip_addr; |
| @@ -361,8 +518,9 @@ nfp_fl_pedit(const struct tc_action *action, char *nfp_action, int *a_len) | |||
| 361 | struct nfp_fl_set_eth set_eth; | 518 | struct nfp_fl_set_eth set_eth; |
| 362 | enum pedit_header_type htype; | 519 | enum pedit_header_type htype; |
| 363 | int idx, nkeys, err; | 520 | int idx, nkeys, err; |
| 364 | size_t act_size; | 521 | size_t act_size = 0; |
| 365 | u32 offset, cmd; | 522 | u32 offset, cmd; |
| 523 | u8 ip_proto = 0; | ||
| 366 | 524 | ||
| 367 | memset(&set_ip6_dst, 0, sizeof(set_ip6_dst)); | 525 | memset(&set_ip6_dst, 0, sizeof(set_ip6_dst)); |
| 368 | memset(&set_ip6_src, 0, sizeof(set_ip6_src)); | 526 | memset(&set_ip6_src, 0, sizeof(set_ip6_src)); |
| @@ -405,18 +563,35 @@ nfp_fl_pedit(const struct tc_action *action, char *nfp_action, int *a_len) | |||
| 405 | return err; | 563 | return err; |
| 406 | } | 564 | } |
| 407 | 565 | ||
| 566 | if (dissector_uses_key(flow->dissector, FLOW_DISSECTOR_KEY_BASIC)) { | ||
| 567 | struct flow_dissector_key_basic *basic; | ||
| 568 | |||
| 569 | basic = skb_flow_dissector_target(flow->dissector, | ||
| 570 | FLOW_DISSECTOR_KEY_BASIC, | ||
| 571 | flow->key); | ||
| 572 | ip_proto = basic->ip_proto; | ||
| 573 | } | ||
| 574 | |||
| 408 | if (set_eth.head.len_lw) { | 575 | if (set_eth.head.len_lw) { |
| 409 | act_size = sizeof(set_eth); | 576 | act_size = sizeof(set_eth); |
| 410 | memcpy(nfp_action, &set_eth, act_size); | 577 | memcpy(nfp_action, &set_eth, act_size); |
| 411 | *a_len += act_size; | 578 | *a_len += act_size; |
| 412 | } else if (set_ip_addr.head.len_lw) { | 579 | } |
| 580 | if (set_ip_addr.head.len_lw) { | ||
| 581 | nfp_action += act_size; | ||
| 413 | act_size = sizeof(set_ip_addr); | 582 | act_size = sizeof(set_ip_addr); |
| 414 | memcpy(nfp_action, &set_ip_addr, act_size); | 583 | memcpy(nfp_action, &set_ip_addr, act_size); |
| 415 | *a_len += act_size; | 584 | *a_len += act_size; |
| 416 | } else if (set_ip6_dst.head.len_lw && set_ip6_src.head.len_lw) { | 585 | |
| 586 | /* Hardware will automatically fix IPv4 and TCP/UDP checksum. */ | ||
| 587 | *csum_updated |= TCA_CSUM_UPDATE_FLAG_IPV4HDR | | ||
| 588 | nfp_fl_csum_l4_to_flag(ip_proto); | ||
| 589 | } | ||
| 590 | if (set_ip6_dst.head.len_lw && set_ip6_src.head.len_lw) { | ||
| 417 | /* TC compiles set src and dst IPv6 address as a single action, | 591 | /* TC compiles set src and dst IPv6 address as a single action, |
| 418 | * the hardware requires this to be 2 separate actions. | 592 | * the hardware requires this to be 2 separate actions. |
| 419 | */ | 593 | */ |
| 594 | nfp_action += act_size; | ||
| 420 | act_size = sizeof(set_ip6_src); | 595 | act_size = sizeof(set_ip6_src); |
| 421 | memcpy(nfp_action, &set_ip6_src, act_size); | 596 | memcpy(nfp_action, &set_ip6_src, act_size); |
| 422 | *a_len += act_size; | 597 | *a_len += act_size; |
| @@ -425,60 +600,114 @@ nfp_fl_pedit(const struct tc_action *action, char *nfp_action, int *a_len) | |||
| 425 | memcpy(&nfp_action[sizeof(set_ip6_src)], &set_ip6_dst, | 600 | memcpy(&nfp_action[sizeof(set_ip6_src)], &set_ip6_dst, |
| 426 | act_size); | 601 | act_size); |
| 427 | *a_len += act_size; | 602 | *a_len += act_size; |
| 603 | |||
| 604 | /* Hardware will automatically fix TCP/UDP checksum. */ | ||
| 605 | *csum_updated |= nfp_fl_csum_l4_to_flag(ip_proto); | ||
| 428 | } else if (set_ip6_dst.head.len_lw) { | 606 | } else if (set_ip6_dst.head.len_lw) { |
| 607 | nfp_action += act_size; | ||
| 429 | act_size = sizeof(set_ip6_dst); | 608 | act_size = sizeof(set_ip6_dst); |
| 430 | memcpy(nfp_action, &set_ip6_dst, act_size); | 609 | memcpy(nfp_action, &set_ip6_dst, act_size); |
| 431 | *a_len += act_size; | 610 | *a_len += act_size; |
| 611 | |||
| 612 | /* Hardware will automatically fix TCP/UDP checksum. */ | ||
| 613 | *csum_updated |= nfp_fl_csum_l4_to_flag(ip_proto); | ||
| 432 | } else if (set_ip6_src.head.len_lw) { | 614 | } else if (set_ip6_src.head.len_lw) { |
| 615 | nfp_action += act_size; | ||
| 433 | act_size = sizeof(set_ip6_src); | 616 | act_size = sizeof(set_ip6_src); |
| 434 | memcpy(nfp_action, &set_ip6_src, act_size); | 617 | memcpy(nfp_action, &set_ip6_src, act_size); |
| 435 | *a_len += act_size; | 618 | *a_len += act_size; |
| 436 | } else if (set_tport.head.len_lw) { | 619 | |
| 620 | /* Hardware will automatically fix TCP/UDP checksum. */ | ||
| 621 | *csum_updated |= nfp_fl_csum_l4_to_flag(ip_proto); | ||
| 622 | } | ||
| 623 | if (set_tport.head.len_lw) { | ||
| 624 | nfp_action += act_size; | ||
| 437 | act_size = sizeof(set_tport); | 625 | act_size = sizeof(set_tport); |
| 438 | memcpy(nfp_action, &set_tport, act_size); | 626 | memcpy(nfp_action, &set_tport, act_size); |
| 439 | *a_len += act_size; | 627 | *a_len += act_size; |
| 628 | |||
| 629 | /* Hardware will automatically fix TCP/UDP checksum. */ | ||
| 630 | *csum_updated |= nfp_fl_csum_l4_to_flag(ip_proto); | ||
| 631 | } | ||
| 632 | |||
| 633 | return 0; | ||
| 634 | } | ||
| 635 | |||
| 636 | static int | ||
| 637 | nfp_flower_output_action(struct nfp_app *app, const struct tc_action *a, | ||
| 638 | struct nfp_fl_payload *nfp_fl, int *a_len, | ||
| 639 | struct net_device *netdev, bool last, | ||
| 640 | enum nfp_flower_tun_type *tun_type, int *tun_out_cnt, | ||
| 641 | int *out_cnt, u32 *csum_updated) | ||
| 642 | { | ||
| 643 | struct nfp_flower_priv *priv = app->priv; | ||
| 644 | struct nfp_fl_output *output; | ||
| 645 | int err, prelag_size; | ||
| 646 | |||
| 647 | /* If csum_updated has not been reset by now, it means HW will | ||
| 648 | * incorrectly update csums when they are not requested. | ||
| 649 | */ | ||
| 650 | if (*csum_updated) | ||
| 651 | return -EOPNOTSUPP; | ||
| 652 | |||
| 653 | if (*a_len + sizeof(struct nfp_fl_output) > NFP_FL_MAX_A_SIZ) | ||
| 654 | return -EOPNOTSUPP; | ||
| 655 | |||
| 656 | output = (struct nfp_fl_output *)&nfp_fl->action_data[*a_len]; | ||
| 657 | err = nfp_fl_output(app, output, a, nfp_fl, last, netdev, *tun_type, | ||
| 658 | tun_out_cnt); | ||
| 659 | if (err) | ||
| 660 | return err; | ||
| 661 | |||
| 662 | *a_len += sizeof(struct nfp_fl_output); | ||
| 663 | |||
| 664 | if (priv->flower_ext_feats & NFP_FL_FEATS_LAG) { | ||
| 665 | /* nfp_fl_pre_lag returns -err or size of prelag action added. | ||
| 666 | * This will be 0 if it is not egressing to a lag dev. | ||
| 667 | */ | ||
| 668 | prelag_size = nfp_fl_pre_lag(app, a, nfp_fl, *a_len); | ||
| 669 | if (prelag_size < 0) | ||
| 670 | return prelag_size; | ||
| 671 | else if (prelag_size > 0 && (!last || *out_cnt)) | ||
| 672 | return -EOPNOTSUPP; | ||
| 673 | |||
| 674 | *a_len += prelag_size; | ||
| 440 | } | 675 | } |
| 676 | (*out_cnt)++; | ||
| 441 | 677 | ||
| 442 | return 0; | 678 | return 0; |
| 443 | } | 679 | } |
| 444 | 680 | ||
| 445 | static int | 681 | static int |
| 446 | nfp_flower_loop_action(const struct tc_action *a, | 682 | nfp_flower_loop_action(struct nfp_app *app, const struct tc_action *a, |
| 683 | struct tc_cls_flower_offload *flow, | ||
| 447 | struct nfp_fl_payload *nfp_fl, int *a_len, | 684 | struct nfp_fl_payload *nfp_fl, int *a_len, |
| 448 | struct net_device *netdev, | 685 | struct net_device *netdev, |
| 449 | enum nfp_flower_tun_type *tun_type, int *tun_out_cnt) | 686 | enum nfp_flower_tun_type *tun_type, int *tun_out_cnt, |
| 687 | int *out_cnt, u32 *csum_updated) | ||
| 450 | { | 688 | { |
| 451 | struct nfp_fl_set_ipv4_udp_tun *set_tun; | 689 | struct nfp_fl_set_ipv4_udp_tun *set_tun; |
| 452 | struct nfp_fl_pre_tunnel *pre_tun; | 690 | struct nfp_fl_pre_tunnel *pre_tun; |
| 453 | struct nfp_fl_push_vlan *psh_v; | 691 | struct nfp_fl_push_vlan *psh_v; |
| 454 | struct nfp_fl_pop_vlan *pop_v; | 692 | struct nfp_fl_pop_vlan *pop_v; |
| 455 | struct nfp_fl_output *output; | ||
| 456 | int err; | 693 | int err; |
| 457 | 694 | ||
| 458 | if (is_tcf_gact_shot(a)) { | 695 | if (is_tcf_gact_shot(a)) { |
| 459 | nfp_fl->meta.shortcut = cpu_to_be32(NFP_FL_SC_ACT_DROP); | 696 | nfp_fl->meta.shortcut = cpu_to_be32(NFP_FL_SC_ACT_DROP); |
| 460 | } else if (is_tcf_mirred_egress_redirect(a)) { | 697 | } else if (is_tcf_mirred_egress_redirect(a)) { |
| 461 | if (*a_len + sizeof(struct nfp_fl_output) > NFP_FL_MAX_A_SIZ) | 698 | err = nfp_flower_output_action(app, a, nfp_fl, a_len, netdev, |
| 462 | return -EOPNOTSUPP; | 699 | true, tun_type, tun_out_cnt, |
| 463 | 700 | out_cnt, csum_updated); | |
| 464 | output = (struct nfp_fl_output *)&nfp_fl->action_data[*a_len]; | ||
| 465 | err = nfp_fl_output(output, a, nfp_fl, true, netdev, *tun_type, | ||
| 466 | tun_out_cnt); | ||
| 467 | if (err) | 701 | if (err) |
| 468 | return err; | 702 | return err; |
| 469 | 703 | ||
| 470 | *a_len += sizeof(struct nfp_fl_output); | ||
| 471 | } else if (is_tcf_mirred_egress_mirror(a)) { | 704 | } else if (is_tcf_mirred_egress_mirror(a)) { |
| 472 | if (*a_len + sizeof(struct nfp_fl_output) > NFP_FL_MAX_A_SIZ) | 705 | err = nfp_flower_output_action(app, a, nfp_fl, a_len, netdev, |
| 473 | return -EOPNOTSUPP; | 706 | false, tun_type, tun_out_cnt, |
| 474 | 707 | out_cnt, csum_updated); | |
| 475 | output = (struct nfp_fl_output *)&nfp_fl->action_data[*a_len]; | ||
| 476 | err = nfp_fl_output(output, a, nfp_fl, false, netdev, *tun_type, | ||
| 477 | tun_out_cnt); | ||
| 478 | if (err) | 708 | if (err) |
| 479 | return err; | 709 | return err; |
| 480 | 710 | ||
| 481 | *a_len += sizeof(struct nfp_fl_output); | ||
| 482 | } else if (is_tcf_vlan(a) && tcf_vlan_action(a) == TCA_VLAN_ACT_POP) { | 711 | } else if (is_tcf_vlan(a) && tcf_vlan_action(a) == TCA_VLAN_ACT_POP) { |
| 483 | if (*a_len + sizeof(struct nfp_fl_pop_vlan) > NFP_FL_MAX_A_SIZ) | 712 | if (*a_len + sizeof(struct nfp_fl_pop_vlan) > NFP_FL_MAX_A_SIZ) |
| 484 | return -EOPNOTSUPP; | 713 | return -EOPNOTSUPP; |
| @@ -498,11 +727,16 @@ nfp_flower_loop_action(const struct tc_action *a, | |||
| 498 | nfp_fl_push_vlan(psh_v, a); | 727 | nfp_fl_push_vlan(psh_v, a); |
| 499 | *a_len += sizeof(struct nfp_fl_push_vlan); | 728 | *a_len += sizeof(struct nfp_fl_push_vlan); |
| 500 | } else if (is_tcf_tunnel_set(a)) { | 729 | } else if (is_tcf_tunnel_set(a)) { |
| 730 | struct ip_tunnel_info *ip_tun = tcf_tunnel_info(a); | ||
| 501 | struct nfp_repr *repr = netdev_priv(netdev); | 731 | struct nfp_repr *repr = netdev_priv(netdev); |
| 732 | |||
| 502 | *tun_type = nfp_fl_get_tun_from_act_l4_port(repr->app, a); | 733 | *tun_type = nfp_fl_get_tun_from_act_l4_port(repr->app, a); |
| 503 | if (*tun_type == NFP_FL_TUNNEL_NONE) | 734 | if (*tun_type == NFP_FL_TUNNEL_NONE) |
| 504 | return -EOPNOTSUPP; | 735 | return -EOPNOTSUPP; |
| 505 | 736 | ||
| 737 | if (ip_tun->mode & ~NFP_FL_SUPPORTED_TUNNEL_INFO_FLAGS) | ||
| 738 | return -EOPNOTSUPP; | ||
| 739 | |||
| 506 | /* Pre-tunnel action is required for tunnel encap. | 740 | /* Pre-tunnel action is required for tunnel encap. |
| 507 | * This checks for next hop entries on NFP. | 741 | * This checks for next hop entries on NFP. |
| 508 | * If none, the packet falls back before applying other actions. | 742 | * If none, the packet falls back before applying other actions. |
| @@ -515,9 +749,13 @@ nfp_flower_loop_action(const struct tc_action *a, | |||
| 515 | nfp_fl->meta.shortcut = cpu_to_be32(NFP_FL_SC_ACT_NULL); | 749 | nfp_fl->meta.shortcut = cpu_to_be32(NFP_FL_SC_ACT_NULL); |
| 516 | *a_len += sizeof(struct nfp_fl_pre_tunnel); | 750 | *a_len += sizeof(struct nfp_fl_pre_tunnel); |
| 517 | 751 | ||
| 752 | err = nfp_fl_push_geneve_options(nfp_fl, a_len, a); | ||
| 753 | if (err) | ||
| 754 | return err; | ||
| 755 | |||
| 518 | set_tun = (void *)&nfp_fl->action_data[*a_len]; | 756 | set_tun = (void *)&nfp_fl->action_data[*a_len]; |
| 519 | err = nfp_fl_set_ipv4_udp_tun(set_tun, a, pre_tun, *tun_type, | 757 | err = nfp_fl_set_ipv4_udp_tun(app, set_tun, a, pre_tun, |
| 520 | netdev); | 758 | *tun_type, netdev); |
| 521 | if (err) | 759 | if (err) |
| 522 | return err; | 760 | return err; |
| 523 | *a_len += sizeof(struct nfp_fl_set_ipv4_udp_tun); | 761 | *a_len += sizeof(struct nfp_fl_set_ipv4_udp_tun); |
| @@ -525,8 +763,17 @@ nfp_flower_loop_action(const struct tc_action *a, | |||
| 525 | /* Tunnel decap is handled by default so accept action. */ | 763 | /* Tunnel decap is handled by default so accept action. */ |
| 526 | return 0; | 764 | return 0; |
| 527 | } else if (is_tcf_pedit(a)) { | 765 | } else if (is_tcf_pedit(a)) { |
| 528 | if (nfp_fl_pedit(a, &nfp_fl->action_data[*a_len], a_len)) | 766 | if (nfp_fl_pedit(a, flow, &nfp_fl->action_data[*a_len], |
| 767 | a_len, csum_updated)) | ||
| 768 | return -EOPNOTSUPP; | ||
| 769 | } else if (is_tcf_csum(a)) { | ||
| 770 | /* csum action requests recalc of something we have not fixed */ | ||
| 771 | if (tcf_csum_update_flags(a) & ~*csum_updated) | ||
| 529 | return -EOPNOTSUPP; | 772 | return -EOPNOTSUPP; |
| 773 | /* If we will correctly fix the csum we can remove it from the | ||
| 774 | * csum update list. Which will later be used to check support. | ||
| 775 | */ | ||
| 776 | *csum_updated &= ~tcf_csum_update_flags(a); | ||
| 530 | } else { | 777 | } else { |
| 531 | /* Currently we do not handle any other actions. */ | 778 | /* Currently we do not handle any other actions. */ |
| 532 | return -EOPNOTSUPP; | 779 | return -EOPNOTSUPP; |
| @@ -535,14 +782,15 @@ nfp_flower_loop_action(const struct tc_action *a, | |||
| 535 | return 0; | 782 | return 0; |
| 536 | } | 783 | } |
| 537 | 784 | ||
| 538 | int nfp_flower_compile_action(struct tc_cls_flower_offload *flow, | 785 | int nfp_flower_compile_action(struct nfp_app *app, |
| 786 | struct tc_cls_flower_offload *flow, | ||
| 539 | struct net_device *netdev, | 787 | struct net_device *netdev, |
| 540 | struct nfp_fl_payload *nfp_flow) | 788 | struct nfp_fl_payload *nfp_flow) |
| 541 | { | 789 | { |
| 542 | int act_len, act_cnt, err, tun_out_cnt; | 790 | int act_len, act_cnt, err, tun_out_cnt, out_cnt, i; |
| 543 | enum nfp_flower_tun_type tun_type; | 791 | enum nfp_flower_tun_type tun_type; |
| 544 | const struct tc_action *a; | 792 | const struct tc_action *a; |
| 545 | LIST_HEAD(actions); | 793 | u32 csum_updated = 0; |
| 546 | 794 | ||
| 547 | memset(nfp_flow->action_data, 0, NFP_FL_MAX_A_SIZ); | 795 | memset(nfp_flow->action_data, 0, NFP_FL_MAX_A_SIZ); |
| 548 | nfp_flow->meta.act_len = 0; | 796 | nfp_flow->meta.act_len = 0; |
| @@ -550,11 +798,12 @@ int nfp_flower_compile_action(struct tc_cls_flower_offload *flow, | |||
| 550 | act_len = 0; | 798 | act_len = 0; |
| 551 | act_cnt = 0; | 799 | act_cnt = 0; |
| 552 | tun_out_cnt = 0; | 800 | tun_out_cnt = 0; |
| 801 | out_cnt = 0; | ||
| 553 | 802 | ||
| 554 | tcf_exts_to_list(flow->exts, &actions); | 803 | tcf_exts_for_each_action(i, a, flow->exts) { |
| 555 | list_for_each_entry(a, &actions, list) { | 804 | err = nfp_flower_loop_action(app, a, flow, nfp_flow, &act_len, |
| 556 | err = nfp_flower_loop_action(a, nfp_flow, &act_len, netdev, | 805 | netdev, &tun_type, &tun_out_cnt, |
| 557 | &tun_type, &tun_out_cnt); | 806 | &out_cnt, &csum_updated); |
| 558 | if (err) | 807 | if (err) |
| 559 | return err; | 808 | return err; |
| 560 | act_cnt++; | 809 | act_cnt++; |
