net/mlx5e: Rearrange tc tunnel code in a modular way

Rearrange tc tunnel code so that it would be easy to add future tunnels:
 - Define tc tunnel object with the fields and callbacks that any
   tunnel must implement.
 - Define tc UDP tunnel object for UDP tunnels, such as VXLAN
 - Move each tunnel code (GRE, VXLAN) to its own separate file
 - Rewrite tc tunnel implementation in a general way - using only
   the objects and their callbacks.

Reviewed-by: Oz Shlomo <ozsh@mellanox.com>
Signed-off-by: Yevgeny Kliteynik <kliteyn@mellanox.com>
Signed-off-by: Saeed Mahameed <saeedm@mellanox.com>
This commit is contained in:
Yevgeny Kliteynik 2019-04-14 17:50:01 +03:00 committed by Saeed Mahameed
parent 1f6da30697
commit d386939a32
7 changed files with 389 additions and 224 deletions

View file

@ -32,7 +32,7 @@ mlx5_core-$(CONFIG_MLX5_EN_ARFS) += en_arfs.o
mlx5_core-$(CONFIG_MLX5_EN_RXNFC) += en_fs_ethtool.o
mlx5_core-$(CONFIG_MLX5_CORE_EN_DCB) += en_dcbnl.o en/port_buffer.o
mlx5_core-$(CONFIG_MLX5_ESWITCH) += en_rep.o en_tc.o en/tc_tun.o lib/port_tun.o lag_mp.o \
lib/geneve.o
lib/geneve.o en/tc_tun_vxlan.o en/tc_tun_gre.o
#
# Core extra

View file

@ -3,9 +3,19 @@
#include <net/vxlan.h>
#include <net/gre.h>
#include "lib/vxlan.h"
#include "en/tc_tun.h"
struct mlx5e_tc_tunnel *mlx5e_get_tc_tun(struct net_device *tunnel_dev)
{
if (netif_is_vxlan(tunnel_dev))
return &vxlan_tunnel;
else if (netif_is_gretap(tunnel_dev) ||
netif_is_ip6gretap(tunnel_dev))
return &gre_tunnel;
else
return NULL;
}
static int get_route_and_out_devs(struct mlx5e_priv *priv,
struct net_device *dev,
struct net_device **route_dev,
@ -141,64 +151,15 @@ static int mlx5e_route_lookup_ipv6(struct mlx5e_priv *priv,
return 0;
}
static int mlx5e_gen_vxlan_header(char buf[],
const struct ip_tunnel_key *tun_key)
{
__be32 tun_id = tunnel_id_to_key32(tun_key->tun_id);
struct udphdr *udp = (struct udphdr *)(buf);
struct vxlanhdr *vxh = (struct vxlanhdr *)
((char *)udp + sizeof(struct udphdr));
udp->dest = tun_key->tp_dst;
vxh->vx_flags = VXLAN_HF_VNI;
vxh->vx_vni = vxlan_vni_field(tun_id);
return 0;
}
static int mlx5e_gen_gre_header(char buf[], const struct ip_tunnel_key *tun_key)
{
__be32 tun_id = tunnel_id_to_key32(tun_key->tun_id);
int hdr_len;
struct gre_base_hdr *greh = (struct gre_base_hdr *)(buf);
/* the HW does not calculate GRE csum or sequences */
if (tun_key->tun_flags & (TUNNEL_CSUM | TUNNEL_SEQ))
return -EOPNOTSUPP;
greh->protocol = htons(ETH_P_TEB);
/* GRE key */
hdr_len = gre_calc_hlen(tun_key->tun_flags);
greh->flags = gre_tnl_flags_to_gre_flags(tun_key->tun_flags);
if (tun_key->tun_flags & TUNNEL_KEY) {
__be32 *ptr = (__be32 *)(((u8 *)greh) + hdr_len - 4);
*ptr = tun_id;
}
return 0;
}
static int mlx5e_gen_ip_tunnel_header(char buf[], __u8 *ip_proto,
struct mlx5e_encap_entry *e)
{
int err = 0;
const struct ip_tunnel_key *key = &e->tun_info->key;
if (e->tunnel_type == MLX5E_TC_TUNNEL_TYPE_VXLAN) {
*ip_proto = IPPROTO_UDP;
err = mlx5e_gen_vxlan_header(buf, key);
} else if (e->tunnel_type == MLX5E_TC_TUNNEL_TYPE_GRETAP) {
*ip_proto = IPPROTO_GRE;
err = mlx5e_gen_gre_header(buf, key);
} else {
pr_warn("mlx5: Cannot generate tunnel header for tunnel type (%d)\n"
, e->tunnel_type);
err = -EOPNOTSUPP;
if (!e->tunnel) {
pr_warn("mlx5: Cannot generate tunnel header for this tunnel\n");
return -EOPNOTSUPP;
}
return err;
return e->tunnel->generate_ip_tun_hdr(buf, ip_proto, e);
}
static char *gen_eth_tnl_hdr(char *buf, struct net_device *dev,
@ -254,7 +215,7 @@ int mlx5e_tc_tun_create_header_ipv4(struct mlx5e_priv *priv,
ipv4_encap_size =
(is_vlan_dev(route_dev) ? VLAN_ETH_HLEN : ETH_HLEN) +
sizeof(struct iphdr) +
e->tunnel_hlen;
e->tunnel->calc_hlen(e);
if (max_encap_size < ipv4_encap_size) {
mlx5_core_warn(priv->mdev, "encap size %d too big, max supported is %d\n",
@ -370,7 +331,7 @@ int mlx5e_tc_tun_create_header_ipv6(struct mlx5e_priv *priv,
ipv6_encap_size =
(is_vlan_dev(route_dev) ? VLAN_ETH_HLEN : ETH_HLEN) +
sizeof(struct ipv6hdr) +
e->tunnel_hlen;
e->tunnel->calc_hlen(e);
if (max_encap_size < ipv6_encap_size) {
mlx5_core_warn(priv->mdev, "encap size %d too big, max supported is %d\n",
@ -456,27 +417,12 @@ out:
return err;
}
int mlx5e_tc_tun_get_type(struct net_device *tunnel_dev)
{
if (netif_is_vxlan(tunnel_dev))
return MLX5E_TC_TUNNEL_TYPE_VXLAN;
else if (netif_is_gretap(tunnel_dev) ||
netif_is_ip6gretap(tunnel_dev))
return MLX5E_TC_TUNNEL_TYPE_GRETAP;
else
return MLX5E_TC_TUNNEL_TYPE_UNKNOWN;
}
bool mlx5e_tc_tun_device_to_offload(struct mlx5e_priv *priv,
struct net_device *netdev)
{
int tunnel_type = mlx5e_tc_tun_get_type(netdev);
struct mlx5e_tc_tunnel *tunnel = mlx5e_get_tc_tun(netdev);
if (tunnel_type == MLX5E_TC_TUNNEL_TYPE_VXLAN &&
MLX5_CAP_ESW(priv->mdev, vxlan_encap_decap))
return true;
else if (tunnel_type == MLX5E_TC_TUNNEL_TYPE_GRETAP &&
MLX5_CAP_ESW(priv->mdev, nvgre_encap_decap))
if (tunnel && tunnel->can_offload(priv))
return true;
else
return false;
@ -487,137 +433,14 @@ int mlx5e_tc_tun_init_encap_attr(struct net_device *tunnel_dev,
struct mlx5e_encap_entry *e,
struct netlink_ext_ack *extack)
{
e->tunnel_type = mlx5e_tc_tun_get_type(tunnel_dev);
struct mlx5e_tc_tunnel *tunnel = mlx5e_get_tc_tun(tunnel_dev);
if (e->tunnel_type == MLX5E_TC_TUNNEL_TYPE_VXLAN) {
int dst_port = be16_to_cpu(e->tun_info->key.tp_dst);
if (!mlx5_vxlan_lookup_port(priv->mdev->vxlan, dst_port)) {
NL_SET_ERR_MSG_MOD(extack,
"vxlan udp dport was not registered with the HW");
netdev_warn(priv->netdev,
"%d isn't an offloaded vxlan udp dport\n",
dst_port);
return -EOPNOTSUPP;
}
e->reformat_type = MLX5_REFORMAT_TYPE_L2_TO_VXLAN;
e->tunnel_hlen = VXLAN_HLEN;
} else if (e->tunnel_type == MLX5E_TC_TUNNEL_TYPE_GRETAP) {
e->reformat_type = MLX5_REFORMAT_TYPE_L2_TO_NVGRE;
e->tunnel_hlen = gre_calc_hlen(e->tun_info->key.tun_flags);
} else {
if (!tunnel) {
e->reformat_type = -1;
e->tunnel_hlen = -1;
return -EOPNOTSUPP;
}
return 0;
}
static int mlx5e_tc_tun_parse_vxlan(struct mlx5e_priv *priv,
struct mlx5_flow_spec *spec,
struct tc_cls_flower_offload *f,
void *headers_c,
void *headers_v)
{
struct flow_rule *rule = tc_cls_flower_offload_flow_rule(f);
struct netlink_ext_ack *extack = f->common.extack;
void *misc_c = MLX5_ADDR_OF(fte_match_param,
spec->match_criteria,
misc_parameters);
void *misc_v = MLX5_ADDR_OF(fte_match_param,
spec->match_value,
misc_parameters);
struct flow_match_ports enc_ports;
flow_rule_match_enc_ports(rule, &enc_ports);
/* Full udp dst port must be given */
if (!flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ENC_PORTS) ||
memchr_inv(&enc_ports.mask->dst, 0xff, sizeof(enc_ports.mask->dst))) {
NL_SET_ERR_MSG_MOD(extack,
"VXLAN decap filter must include enc_dst_port condition");
netdev_warn(priv->netdev,
"VXLAN decap filter must include enc_dst_port condition\n");
return -EOPNOTSUPP;
}
/* udp dst port must be knonwn as a VXLAN port */
if (!mlx5_vxlan_lookup_port(priv->mdev->vxlan, be16_to_cpu(enc_ports.key->dst))) {
NL_SET_ERR_MSG_MOD(extack,
"Matched UDP port is not registered as a VXLAN port");
netdev_warn(priv->netdev,
"UDP port %d is not registered as a VXLAN port\n",
be16_to_cpu(enc_ports.key->dst));
return -EOPNOTSUPP;
}
/* dst UDP port is valid here */
MLX5_SET_TO_ONES(fte_match_set_lyr_2_4, headers_c, ip_protocol);
MLX5_SET(fte_match_set_lyr_2_4, headers_v, ip_protocol, IPPROTO_UDP);
MLX5_SET(fte_match_set_lyr_2_4, headers_c, udp_dport,
ntohs(enc_ports.mask->dst));
MLX5_SET(fte_match_set_lyr_2_4, headers_v, udp_dport,
ntohs(enc_ports.key->dst));
MLX5_SET(fte_match_set_lyr_2_4, headers_c, udp_sport,
ntohs(enc_ports.mask->src));
MLX5_SET(fte_match_set_lyr_2_4, headers_v, udp_sport,
ntohs(enc_ports.key->src));
/* match on VNI */
if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ENC_KEYID)) {
struct flow_match_enc_keyid enc_keyid;
flow_rule_match_enc_keyid(rule, &enc_keyid);
MLX5_SET(fte_match_set_misc, misc_c, vxlan_vni,
be32_to_cpu(enc_keyid.mask->keyid));
MLX5_SET(fte_match_set_misc, misc_v, vxlan_vni,
be32_to_cpu(enc_keyid.key->keyid));
}
return 0;
}
static int mlx5e_tc_tun_parse_gretap(struct mlx5e_priv *priv,
struct mlx5_flow_spec *spec,
struct tc_cls_flower_offload *f,
void *outer_headers_c,
void *outer_headers_v)
{
void *misc_c = MLX5_ADDR_OF(fte_match_param, spec->match_criteria,
misc_parameters);
void *misc_v = MLX5_ADDR_OF(fte_match_param, spec->match_value,
misc_parameters);
struct flow_rule *rule = tc_cls_flower_offload_flow_rule(f);
if (!MLX5_CAP_ESW(priv->mdev, nvgre_encap_decap)) {
NL_SET_ERR_MSG_MOD(f->common.extack,
"GRE HW offloading is not supported");
netdev_warn(priv->netdev, "GRE HW offloading is not supported\n");
return -EOPNOTSUPP;
}
MLX5_SET_TO_ONES(fte_match_set_lyr_2_4, outer_headers_c, ip_protocol);
MLX5_SET(fte_match_set_lyr_2_4, outer_headers_v,
ip_protocol, IPPROTO_GRE);
/* gre protocol*/
MLX5_SET_TO_ONES(fte_match_set_misc, misc_c, gre_protocol);
MLX5_SET(fte_match_set_misc, misc_v, gre_protocol, ETH_P_TEB);
/* gre key */
if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ENC_KEYID)) {
struct flow_match_enc_keyid enc_keyid;
flow_rule_match_enc_keyid(rule, &enc_keyid);
MLX5_SET(fte_match_set_misc, misc_c,
gre_key.key, be32_to_cpu(enc_keyid.mask->keyid));
MLX5_SET(fte_match_set_misc, misc_v,
gre_key.key, be32_to_cpu(enc_keyid.key->keyid));
}
return 0;
return tunnel->init_encap_attr(tunnel_dev, priv, e, extack);
}
int mlx5e_tc_tun_parse(struct net_device *filter_dev,
@ -627,25 +450,87 @@ int mlx5e_tc_tun_parse(struct net_device *filter_dev,
void *headers_c,
void *headers_v, u8 *match_level)
{
int tunnel_type;
struct mlx5e_tc_tunnel *tunnel = mlx5e_get_tc_tun(filter_dev);
int err = 0;
tunnel_type = mlx5e_tc_tun_get_type(filter_dev);
if (tunnel_type == MLX5E_TC_TUNNEL_TYPE_VXLAN) {
*match_level = MLX5_MATCH_L4;
err = mlx5e_tc_tun_parse_vxlan(priv, spec, f,
headers_c, headers_v);
} else if (tunnel_type == MLX5E_TC_TUNNEL_TYPE_GRETAP) {
*match_level = MLX5_MATCH_L3;
err = mlx5e_tc_tun_parse_gretap(priv, spec, f,
headers_c, headers_v);
} else {
if (!tunnel) {
netdev_warn(priv->netdev,
"decapsulation offload is not supported for %s (kind: \"%s\")\n",
netdev_name(filter_dev),
"decapsulation offload is not supported for %s net device\n",
mlx5e_netdev_kind(filter_dev));
return -EOPNOTSUPP;
err = -EOPNOTSUPP;
goto out;
}
*match_level = tunnel->match_level;
if (tunnel->parse_udp_ports) {
err = tunnel->parse_udp_ports(priv, spec, f,
headers_c, headers_v);
if (err)
goto out;
}
if (tunnel->parse_tunnel) {
err = tunnel->parse_tunnel(priv, spec, f,
headers_c, headers_v);
if (err)
goto out;
}
out:
return err;
}
int mlx5e_tc_tun_parse_udp_ports(struct mlx5e_priv *priv,
struct mlx5_flow_spec *spec,
struct tc_cls_flower_offload *f,
void *headers_c,
void *headers_v)
{
struct flow_rule *rule = tc_cls_flower_offload_flow_rule(f);
struct netlink_ext_ack *extack = f->common.extack;
struct flow_match_ports enc_ports;
/* Full udp dst port must be given */
if (!flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ENC_PORTS)) {
NL_SET_ERR_MSG_MOD(extack,
"UDP tunnel decap filter must include enc_dst_port condition");
netdev_warn(priv->netdev,
"UDP tunnel decap filter must include enc_dst_port condition\n");
return -EOPNOTSUPP;
}
flow_rule_match_enc_ports(rule, &enc_ports);
if (memchr_inv(&enc_ports.mask->dst, 0xff,
sizeof(enc_ports.mask->dst))) {
NL_SET_ERR_MSG_MOD(extack,
"UDP tunnel decap filter must match enc_dst_port fully");
netdev_warn(priv->netdev,
"UDP tunnel decap filter must match enc_dst_port fully\n");
return -EOPNOTSUPP;
}
/* match on UDP protocol and dst port number */
MLX5_SET_TO_ONES(fte_match_set_lyr_2_4, headers_c, ip_protocol);
MLX5_SET(fte_match_set_lyr_2_4, headers_v, ip_protocol, IPPROTO_UDP);
MLX5_SET(fte_match_set_lyr_2_4, headers_c, udp_dport,
ntohs(enc_ports.mask->dst));
MLX5_SET(fte_match_set_lyr_2_4, headers_v, udp_dport,
ntohs(enc_ports.key->dst));
/* UDP src port on outer header is generated by HW,
* so it is probably a bad idea to request matching it.
* Nonetheless, it is allowed.
*/
MLX5_SET(fte_match_set_lyr_2_4, headers_c, udp_sport,
ntohs(enc_ports.mask->src));
MLX5_SET(fte_match_set_lyr_2_4, headers_v, udp_sport,
ntohs(enc_ports.key->src));
return 0;
}

View file

@ -14,9 +14,39 @@
enum {
MLX5E_TC_TUNNEL_TYPE_UNKNOWN,
MLX5E_TC_TUNNEL_TYPE_VXLAN,
MLX5E_TC_TUNNEL_TYPE_GRETAP
MLX5E_TC_TUNNEL_TYPE_GRETAP,
};
struct mlx5e_tc_tunnel {
int tunnel_type;
enum mlx5_flow_match_level match_level;
bool (*can_offload)(struct mlx5e_priv *priv);
int (*calc_hlen)(struct mlx5e_encap_entry *e);
int (*init_encap_attr)(struct net_device *tunnel_dev,
struct mlx5e_priv *priv,
struct mlx5e_encap_entry *e,
struct netlink_ext_ack *extack);
int (*generate_ip_tun_hdr)(char buf[],
__u8 *ip_proto,
struct mlx5e_encap_entry *e);
int (*parse_udp_ports)(struct mlx5e_priv *priv,
struct mlx5_flow_spec *spec,
struct tc_cls_flower_offload *f,
void *headers_c,
void *headers_v);
int (*parse_tunnel)(struct mlx5e_priv *priv,
struct mlx5_flow_spec *spec,
struct tc_cls_flower_offload *f,
void *headers_c,
void *headers_v);
};
extern struct mlx5e_tc_tunnel vxlan_tunnel;
extern struct mlx5e_tc_tunnel gre_tunnel;
struct mlx5e_tc_tunnel *mlx5e_get_tc_tun(struct net_device *tunnel_dev);
int mlx5e_tc_tun_init_encap_attr(struct net_device *tunnel_dev,
struct mlx5e_priv *priv,
struct mlx5e_encap_entry *e,
@ -30,7 +60,6 @@ int mlx5e_tc_tun_create_header_ipv6(struct mlx5e_priv *priv,
struct net_device *mirred_dev,
struct mlx5e_encap_entry *e);
int mlx5e_tc_tun_get_type(struct net_device *tunnel_dev);
bool mlx5e_tc_tun_device_to_offload(struct mlx5e_priv *priv,
struct net_device *netdev);
@ -41,4 +70,10 @@ int mlx5e_tc_tun_parse(struct net_device *filter_dev,
void *headers_c,
void *headers_v, u8 *match_level);
int mlx5e_tc_tun_parse_udp_ports(struct mlx5e_priv *priv,
struct mlx5_flow_spec *spec,
struct tc_cls_flower_offload *f,
void *headers_c,
void *headers_v);
#endif //__MLX5_EN_TC_TUNNEL_H__

View file

@ -0,0 +1,95 @@
// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
/* Copyright (c) 2018 Mellanox Technologies. */
#include <net/gre.h>
#include "en/tc_tun.h"
static bool mlx5e_tc_tun_can_offload_gretap(struct mlx5e_priv *priv)
{
return !!MLX5_CAP_ESW(priv->mdev, nvgre_encap_decap);
}
static int mlx5e_tc_tun_calc_hlen_gretap(struct mlx5e_encap_entry *e)
{
return gre_calc_hlen(e->tun_info->key.tun_flags);
}
static int mlx5e_tc_tun_init_encap_attr_gretap(struct net_device *tunnel_dev,
struct mlx5e_priv *priv,
struct mlx5e_encap_entry *e,
struct netlink_ext_ack *extack)
{
e->tunnel = &gre_tunnel;
e->reformat_type = MLX5_REFORMAT_TYPE_L2_TO_NVGRE;
return 0;
}
static int mlx5e_gen_ip_tunnel_header_gretap(char buf[],
__u8 *ip_proto,
struct mlx5e_encap_entry *e)
{
const struct ip_tunnel_key *tun_key = &e->tun_info->key;
struct gre_base_hdr *greh = (struct gre_base_hdr *)(buf);
__be32 tun_id = tunnel_id_to_key32(tun_key->tun_id);
int hdr_len;
*ip_proto = IPPROTO_GRE;
/* the HW does not calculate GRE csum or sequences */
if (tun_key->tun_flags & (TUNNEL_CSUM | TUNNEL_SEQ))
return -EOPNOTSUPP;
greh->protocol = htons(ETH_P_TEB);
/* GRE key */
hdr_len = mlx5e_tc_tun_calc_hlen_gretap(e);
greh->flags = gre_tnl_flags_to_gre_flags(tun_key->tun_flags);
if (tun_key->tun_flags & TUNNEL_KEY) {
__be32 *ptr = (__be32 *)(((u8 *)greh) + hdr_len - 4);
*ptr = tun_id;
}
return 0;
}
static int mlx5e_tc_tun_parse_gretap(struct mlx5e_priv *priv,
struct mlx5_flow_spec *spec,
struct tc_cls_flower_offload *f,
void *headers_c,
void *headers_v)
{
void *misc_c = MLX5_ADDR_OF(fte_match_param, spec->match_criteria, misc_parameters);
void *misc_v = MLX5_ADDR_OF(fte_match_param, spec->match_value, misc_parameters);
struct flow_rule *rule = tc_cls_flower_offload_flow_rule(f);
MLX5_SET_TO_ONES(fte_match_set_lyr_2_4, headers_c, ip_protocol);
MLX5_SET(fte_match_set_lyr_2_4, headers_v, ip_protocol, IPPROTO_GRE);
/* gre protocol */
MLX5_SET_TO_ONES(fte_match_set_misc, misc_c, gre_protocol);
MLX5_SET(fte_match_set_misc, misc_v, gre_protocol, ETH_P_TEB);
/* gre key */
if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ENC_KEYID)) {
struct flow_match_enc_keyid enc_keyid;
flow_rule_match_enc_keyid(rule, &enc_keyid);
MLX5_SET(fte_match_set_misc, misc_c,
gre_key.key, be32_to_cpu(enc_keyid.mask->keyid));
MLX5_SET(fte_match_set_misc, misc_v,
gre_key.key, be32_to_cpu(enc_keyid.key->keyid));
}
return 0;
}
struct mlx5e_tc_tunnel gre_tunnel = {
.tunnel_type = MLX5E_TC_TUNNEL_TYPE_GRETAP,
.match_level = MLX5_MATCH_L3,
.can_offload = mlx5e_tc_tun_can_offload_gretap,
.calc_hlen = mlx5e_tc_tun_calc_hlen_gretap,
.init_encap_attr = mlx5e_tc_tun_init_encap_attr_gretap,
.generate_ip_tun_hdr = mlx5e_gen_ip_tunnel_header_gretap,
.parse_udp_ports = NULL,
.parse_tunnel = mlx5e_tc_tun_parse_gretap,
};

View file

@ -0,0 +1,151 @@
// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
/* Copyright (c) 2018 Mellanox Technologies. */
#include <net/vxlan.h>
#include "lib/vxlan.h"
#include "en/tc_tun.h"
static bool mlx5e_tc_tun_can_offload_vxlan(struct mlx5e_priv *priv)
{
return !!MLX5_CAP_ESW(priv->mdev, vxlan_encap_decap);
}
static int mlx5e_tc_tun_calc_hlen_vxlan(struct mlx5e_encap_entry *e)
{
return VXLAN_HLEN;
}
static int mlx5e_tc_tun_check_udp_dport_vxlan(struct mlx5e_priv *priv,
struct tc_cls_flower_offload *f)
{
struct flow_rule *rule = tc_cls_flower_offload_flow_rule(f);
struct netlink_ext_ack *extack = f->common.extack;
struct flow_match_ports enc_ports;
if (!flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ENC_PORTS))
return -EOPNOTSUPP;
flow_rule_match_enc_ports(rule, &enc_ports);
/* check the UDP destination port validity */
if (!mlx5_vxlan_lookup_port(priv->mdev->vxlan,
be16_to_cpu(enc_ports.key->dst))) {
NL_SET_ERR_MSG_MOD(extack,
"Matched UDP dst port is not registered as a VXLAN port");
netdev_warn(priv->netdev,
"UDP port %d is not registered as a VXLAN port\n",
be16_to_cpu(enc_ports.key->dst));
return -EOPNOTSUPP;
}
return 0;
}
static int mlx5e_tc_tun_parse_udp_ports_vxlan(struct mlx5e_priv *priv,
struct mlx5_flow_spec *spec,
struct tc_cls_flower_offload *f,
void *headers_c,
void *headers_v)
{
int err = 0;
err = mlx5e_tc_tun_parse_udp_ports(priv, spec, f, headers_c, headers_v);
if (err)
return err;
return mlx5e_tc_tun_check_udp_dport_vxlan(priv, f);
}
static int mlx5e_tc_tun_init_encap_attr_vxlan(struct net_device *tunnel_dev,
struct mlx5e_priv *priv,
struct mlx5e_encap_entry *e,
struct netlink_ext_ack *extack)
{
int dst_port = be16_to_cpu(e->tun_info->key.tp_dst);
e->tunnel = &vxlan_tunnel;
if (!mlx5_vxlan_lookup_port(priv->mdev->vxlan, dst_port)) {
NL_SET_ERR_MSG_MOD(extack,
"vxlan udp dport was not registered with the HW");
netdev_warn(priv->netdev,
"%d isn't an offloaded vxlan udp dport\n",
dst_port);
return -EOPNOTSUPP;
}
e->reformat_type = MLX5_REFORMAT_TYPE_L2_TO_VXLAN;
return 0;
}
static int mlx5e_gen_ip_tunnel_header_vxlan(char buf[],
__u8 *ip_proto,
struct mlx5e_encap_entry *e)
{
const struct ip_tunnel_key *tun_key = &e->tun_info->key;
__be32 tun_id = tunnel_id_to_key32(tun_key->tun_id);
struct udphdr *udp = (struct udphdr *)(buf);
struct vxlanhdr *vxh;
vxh = (struct vxlanhdr *)((char *)udp + sizeof(struct udphdr));
*ip_proto = IPPROTO_UDP;
udp->dest = tun_key->tp_dst;
vxh->vx_flags = VXLAN_HF_VNI;
vxh->vx_vni = vxlan_vni_field(tun_id);
return 0;
}
static int mlx5e_tc_tun_parse_vxlan(struct mlx5e_priv *priv,
struct mlx5_flow_spec *spec,
struct tc_cls_flower_offload *f,
void *headers_c,
void *headers_v)
{
struct flow_rule *rule = tc_cls_flower_offload_flow_rule(f);
struct netlink_ext_ack *extack = f->common.extack;
struct flow_match_enc_keyid enc_keyid;
void *misc_c, *misc_v;
misc_c = MLX5_ADDR_OF(fte_match_param, spec->match_criteria, misc_parameters);
misc_v = MLX5_ADDR_OF(fte_match_param, spec->match_value, misc_parameters);
if (!flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ENC_KEYID))
return 0;
flow_rule_match_enc_keyid(rule, &enc_keyid);
if (!enc_keyid.mask->keyid)
return 0;
/* match on VNI is required */
if (!MLX5_CAP_ESW_FLOWTABLE_FDB(priv->mdev,
ft_field_support.outer_vxlan_vni)) {
NL_SET_ERR_MSG_MOD(extack,
"Matching on VXLAN VNI is not supported");
netdev_warn(priv->netdev,
"Matching on VXLAN VNI is not supported\n");
return -EOPNOTSUPP;
}
MLX5_SET(fte_match_set_misc, misc_c, vxlan_vni,
be32_to_cpu(enc_keyid.mask->keyid));
MLX5_SET(fte_match_set_misc, misc_v, vxlan_vni,
be32_to_cpu(enc_keyid.key->keyid));
return 0;
}
struct mlx5e_tc_tunnel vxlan_tunnel = {
.tunnel_type = MLX5E_TC_TUNNEL_TYPE_VXLAN,
.match_level = MLX5_MATCH_L4,
.can_offload = mlx5e_tc_tun_can_offload_vxlan,
.calc_hlen = mlx5e_tc_tun_calc_hlen_vxlan,
.init_encap_attr = mlx5e_tc_tun_init_encap_attr_vxlan,
.generate_ip_tun_hdr = mlx5e_gen_ip_tunnel_header_vxlan,
.parse_udp_ports = mlx5e_tc_tun_parse_udp_ports_vxlan,
.parse_tunnel = mlx5e_tc_tun_parse_vxlan,
};

View file

@ -155,8 +155,7 @@ struct mlx5e_encap_entry {
struct net_device *out_dev;
struct net_device *route_dev;
int tunnel_type;
int tunnel_hlen;
struct mlx5e_tc_tunnel *tunnel;
int reformat_type;
u8 flags;
char *encap_header;

View file

@ -2569,20 +2569,20 @@ static int parse_tc_nic_actions(struct mlx5e_priv *priv,
struct encap_key {
const struct ip_tunnel_key *ip_tun_key;
int tunnel_type;
struct mlx5e_tc_tunnel *tc_tunnel;
};
static inline int cmp_encap_info(struct encap_key *a,
struct encap_key *b)
{
return memcmp(a->ip_tun_key, b->ip_tun_key, sizeof(*a->ip_tun_key)) ||
a->tunnel_type != b->tunnel_type;
a->tc_tunnel->tunnel_type != b->tc_tunnel->tunnel_type;
}
static inline int hash_encap_info(struct encap_key *key)
{
return jhash(key->ip_tun_key, sizeof(*key->ip_tun_key),
key->tunnel_type);
key->tc_tunnel->tunnel_type);
}
@ -2624,14 +2624,14 @@ static int mlx5e_attach_encap(struct mlx5e_priv *priv,
tun_info = parse_attr->tun_info[out_index];
family = ip_tunnel_info_af(tun_info);
key.ip_tun_key = &tun_info->key;
key.tunnel_type = mlx5e_tc_tun_get_type(mirred_dev);
key.tc_tunnel = mlx5e_get_tc_tun(mirred_dev);
hash_key = hash_encap_info(&key);
hash_for_each_possible_rcu(esw->offloads.encap_tbl, e,
encap_hlist, hash_key) {
e_key.ip_tun_key = &e->tun_info->key;
e_key.tunnel_type = e->tunnel_type;
e_key.tc_tunnel = e->tunnel;
if (!cmp_encap_info(&e_key, &key)) {
found = true;
break;