Skip to content

Commit

Permalink
net/mlx5e: TC preparation refactoring for routing update event
Browse files Browse the repository at this point in the history
Following patch in series implement routing update event which requires
ability to modify rule match_to_reg modify header actions dynamically
during rule lifetime. In order to accommodate such behavior, refactor and
extend TC infrastructure in following ways:

- Modify mod_hdr infrastructure to preserve its parse attribute for whole
rule lifetime, instead of deallocating it after rule creation.

- Extend match_to_reg infrastructure with new function
mlx5e_tc_match_to_reg_set_and_get_id() that returns mod_hdr action id that
can be used afterwards to update the action, and
mlx5e_tc_match_to_reg_mod_hdr_change() that can modify existing actions by
its id.

- Extend tun API with new functions mlx5e_tc_tun_update_header_ipv{4|6}()
that are used to updated existing encap entry tunnel header.

Signed-off-by: Vlad Buslov <vladbu@nvidia.com>
Signed-off-by: Dmytro Linkin <dlinkin@nvidia.com>
Reviewed-by: Roi Dayan <roid@nvidia.com>
Signed-off-by: Saeed Mahameed <saeedm@nvidia.com>
  • Loading branch information
Vlad Buslov authored and Saeed Mahameed committed Feb 6, 2021
1 parent 2221d95 commit c7b9038
Show file tree
Hide file tree
Showing 5 changed files with 288 additions and 9 deletions.
1 change: 0 additions & 1 deletion drivers/net/ethernet/mellanox/mlx5/core/en/tc_ct.c
Original file line number Diff line number Diff line change
Expand Up @@ -1763,7 +1763,6 @@ __mlx5_tc_ct_flow_offload_clear(struct mlx5_tc_ct_priv *ct_priv,
goto err_set_registers;
}

dealloc_mod_hdr_actions(mod_acts);
pre_ct_attr->modify_hdr = mod_hdr;
pre_ct_attr->action |= MLX5_FLOW_CONTEXT_ACTION_MOD_HDR;

Expand Down
198 changes: 198 additions & 0 deletions drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun.c
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,105 @@ int mlx5e_tc_tun_create_header_ipv4(struct mlx5e_priv *priv,
return err;
}

int mlx5e_tc_tun_update_header_ipv4(struct mlx5e_priv *priv,
struct net_device *mirred_dev,
struct mlx5e_encap_entry *e)
{
int max_encap_size = MLX5_CAP_ESW(priv->mdev, max_encap_header_size);
const struct ip_tunnel_key *tun_key = &e->tun_info->key;
TC_TUN_ROUTE_ATTR_INIT(attr);
int ipv4_encap_size;
char *encap_header;
struct iphdr *ip;
u8 nud_state;
int err;

/* add the IP fields */
attr.fl.fl4.flowi4_tos = tun_key->tos;
attr.fl.fl4.daddr = tun_key->u.ipv4.dst;
attr.fl.fl4.saddr = tun_key->u.ipv4.src;
attr.ttl = tun_key->ttl;

err = mlx5e_route_lookup_ipv4_get(priv, mirred_dev, &attr);
if (err)
return err;

ipv4_encap_size =
(is_vlan_dev(attr.route_dev) ? VLAN_ETH_HLEN : ETH_HLEN) +
sizeof(struct iphdr) +
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",
ipv4_encap_size, max_encap_size);
err = -EOPNOTSUPP;
goto release_neigh;
}

encap_header = kzalloc(ipv4_encap_size, GFP_KERNEL);
if (!encap_header) {
err = -ENOMEM;
goto release_neigh;
}

e->route_dev_ifindex = attr.route_dev->ifindex;

read_lock_bh(&attr.n->lock);
nud_state = attr.n->nud_state;
ether_addr_copy(e->h_dest, attr.n->ha);
WRITE_ONCE(e->nhe->neigh_dev, attr.n->dev);
read_unlock_bh(&attr.n->lock);

/* add ethernet header */
ip = (struct iphdr *)gen_eth_tnl_hdr(encap_header, attr.route_dev, e,
ETH_P_IP);

/* add ip header */
ip->tos = tun_key->tos;
ip->version = 0x4;
ip->ihl = 0x5;
ip->ttl = attr.ttl;
ip->daddr = attr.fl.fl4.daddr;
ip->saddr = attr.fl.fl4.saddr;

/* add tunneling protocol header */
err = mlx5e_gen_ip_tunnel_header((char *)ip + sizeof(struct iphdr),
&ip->protocol, e);
if (err)
goto free_encap;

e->encap_size = ipv4_encap_size;
kfree(e->encap_header);
e->encap_header = encap_header;

if (!(nud_state & NUD_VALID)) {
neigh_event_send(attr.n, NULL);
/* the encap entry will be made valid on neigh update event
* and not used before that.
*/
goto release_neigh;
}
e->pkt_reformat = mlx5_packet_reformat_alloc(priv->mdev,
e->reformat_type,
ipv4_encap_size, encap_header,
MLX5_FLOW_NAMESPACE_FDB);
if (IS_ERR(e->pkt_reformat)) {
err = PTR_ERR(e->pkt_reformat);
goto free_encap;
}

e->flags |= MLX5_ENCAP_ENTRY_VALID;
mlx5e_rep_queue_neigh_stats_work(netdev_priv(attr.out_dev));
mlx5e_route_lookup_ipv4_put(&attr);
return err;

free_encap:
kfree(encap_header);
release_neigh:
mlx5e_route_lookup_ipv4_put(&attr);
return err;
}

#if IS_ENABLED(CONFIG_INET) && IS_ENABLED(CONFIG_IPV6)
static int mlx5e_route_lookup_ipv6_get(struct mlx5e_priv *priv,
struct net_device *mirred_dev,
Expand Down Expand Up @@ -476,6 +575,105 @@ int mlx5e_tc_tun_create_header_ipv6(struct mlx5e_priv *priv,
mlx5e_route_lookup_ipv6_put(&attr);
return err;
}

int mlx5e_tc_tun_update_header_ipv6(struct mlx5e_priv *priv,
struct net_device *mirred_dev,
struct mlx5e_encap_entry *e)
{
int max_encap_size = MLX5_CAP_ESW(priv->mdev, max_encap_header_size);
const struct ip_tunnel_key *tun_key = &e->tun_info->key;
TC_TUN_ROUTE_ATTR_INIT(attr);
struct ipv6hdr *ip6h;
int ipv6_encap_size;
char *encap_header;
u8 nud_state;
int err;

attr.ttl = tun_key->ttl;

attr.fl.fl6.flowlabel = ip6_make_flowinfo(RT_TOS(tun_key->tos), tun_key->label);
attr.fl.fl6.daddr = tun_key->u.ipv6.dst;
attr.fl.fl6.saddr = tun_key->u.ipv6.src;

err = mlx5e_route_lookup_ipv6_get(priv, mirred_dev, &attr);
if (err)
return err;

ipv6_encap_size =
(is_vlan_dev(attr.route_dev) ? VLAN_ETH_HLEN : ETH_HLEN) +
sizeof(struct ipv6hdr) +
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",
ipv6_encap_size, max_encap_size);
err = -EOPNOTSUPP;
goto release_neigh;
}

encap_header = kzalloc(ipv6_encap_size, GFP_KERNEL);
if (!encap_header) {
err = -ENOMEM;
goto release_neigh;
}

e->route_dev_ifindex = attr.route_dev->ifindex;

read_lock_bh(&attr.n->lock);
nud_state = attr.n->nud_state;
ether_addr_copy(e->h_dest, attr.n->ha);
WRITE_ONCE(e->nhe->neigh_dev, attr.n->dev);
read_unlock_bh(&attr.n->lock);

/* add ethernet header */
ip6h = (struct ipv6hdr *)gen_eth_tnl_hdr(encap_header, attr.route_dev, e,
ETH_P_IPV6);

/* add ip header */
ip6_flow_hdr(ip6h, tun_key->tos, 0);
/* the HW fills up ipv6 payload len */
ip6h->hop_limit = attr.ttl;
ip6h->daddr = attr.fl.fl6.daddr;
ip6h->saddr = attr.fl.fl6.saddr;

/* add tunneling protocol header */
err = mlx5e_gen_ip_tunnel_header((char *)ip6h + sizeof(struct ipv6hdr),
&ip6h->nexthdr, e);
if (err)
goto free_encap;

e->encap_size = ipv6_encap_size;
kfree(e->encap_header);
e->encap_header = encap_header;

if (!(nud_state & NUD_VALID)) {
neigh_event_send(attr.n, NULL);
/* the encap entry will be made valid on neigh update event
* and not used before that.
*/
goto release_neigh;
}

e->pkt_reformat = mlx5_packet_reformat_alloc(priv->mdev,
e->reformat_type,
ipv6_encap_size, encap_header,
MLX5_FLOW_NAMESPACE_FDB);
if (IS_ERR(e->pkt_reformat)) {
err = PTR_ERR(e->pkt_reformat);
goto free_encap;
}

e->flags |= MLX5_ENCAP_ENTRY_VALID;
mlx5e_rep_queue_neigh_stats_work(netdev_priv(attr.out_dev));
mlx5e_route_lookup_ipv6_put(&attr);
return err;

free_encap:
kfree(encap_header);
release_neigh:
mlx5e_route_lookup_ipv6_put(&attr);
return err;
}
#endif

int mlx5e_tc_tun_route_lookup(struct mlx5e_priv *priv,
Expand Down
10 changes: 10 additions & 0 deletions drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,16 +59,26 @@ int mlx5e_tc_tun_init_encap_attr(struct net_device *tunnel_dev,
int mlx5e_tc_tun_create_header_ipv4(struct mlx5e_priv *priv,
struct net_device *mirred_dev,
struct mlx5e_encap_entry *e);
int mlx5e_tc_tun_update_header_ipv4(struct mlx5e_priv *priv,
struct net_device *mirred_dev,
struct mlx5e_encap_entry *e);

#if IS_ENABLED(CONFIG_INET) && IS_ENABLED(CONFIG_IPV6)
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_update_header_ipv6(struct mlx5e_priv *priv,
struct net_device *mirred_dev,
struct mlx5e_encap_entry *e);
#else
static inline int
mlx5e_tc_tun_create_header_ipv6(struct mlx5e_priv *priv,
struct net_device *mirred_dev,
struct mlx5e_encap_entry *e) { return -EOPNOTSUPP; }
int mlx5e_tc_tun_update_header_ipv6(struct mlx5e_priv *priv,
struct net_device *mirred_dev,
struct mlx5e_encap_entry *e)
{ return -EOPNOTSUPP; }
#endif
int mlx5e_tc_tun_route_lookup(struct mlx5e_priv *priv,
struct mlx5_flow_spec *spec,
Expand Down
73 changes: 65 additions & 8 deletions drivers/net/ethernet/mellanox/mlx5/core/en_tc.c
Original file line number Diff line number Diff line change
Expand Up @@ -170,11 +170,11 @@ mlx5e_tc_match_to_reg_get_match(struct mlx5_flow_spec *spec,
}

int
mlx5e_tc_match_to_reg_set(struct mlx5_core_dev *mdev,
struct mlx5e_tc_mod_hdr_acts *mod_hdr_acts,
enum mlx5_flow_namespace_type ns,
enum mlx5e_tc_attr_to_reg type,
u32 data)
mlx5e_tc_match_to_reg_set_and_get_id(struct mlx5_core_dev *mdev,
struct mlx5e_tc_mod_hdr_acts *mod_hdr_acts,
enum mlx5_flow_namespace_type ns,
enum mlx5e_tc_attr_to_reg type,
u32 data)
{
int moffset = mlx5e_tc_attr_to_reg_mappings[type].moffset;
int mfield = mlx5e_tc_attr_to_reg_mappings[type].mfield;
Expand All @@ -198,9 +198,10 @@ mlx5e_tc_match_to_reg_set(struct mlx5_core_dev *mdev,
MLX5_SET(set_action_in, modact, offset, moffset * 8);
MLX5_SET(set_action_in, modact, length, mlen * 8);
MLX5_SET(set_action_in, modact, data, data);
err = mod_hdr_acts->num_actions;
mod_hdr_acts->num_actions++;

return 0;
return err;
}

static struct mlx5_tc_ct_priv *
Expand Down Expand Up @@ -249,6 +250,41 @@ mlx5_tc_rule_delete(struct mlx5e_priv *priv,
mlx5e_del_offloaded_nic_rule(priv, rule, attr);
}

int
mlx5e_tc_match_to_reg_set(struct mlx5_core_dev *mdev,
struct mlx5e_tc_mod_hdr_acts *mod_hdr_acts,
enum mlx5_flow_namespace_type ns,
enum mlx5e_tc_attr_to_reg type,
u32 data)
{
int ret = mlx5e_tc_match_to_reg_set_and_get_id(mdev, mod_hdr_acts, ns, type, data);

return ret < 0 ? ret : 0;
}

void mlx5e_tc_match_to_reg_mod_hdr_change(struct mlx5_core_dev *mdev,
struct mlx5e_tc_mod_hdr_acts *mod_hdr_acts,
enum mlx5e_tc_attr_to_reg type,
int act_id, u32 data)
{
int moffset = mlx5e_tc_attr_to_reg_mappings[type].moffset;
int mfield = mlx5e_tc_attr_to_reg_mappings[type].mfield;
int mlen = mlx5e_tc_attr_to_reg_mappings[type].mlen;
char *modact;

modact = mod_hdr_acts->actions + (act_id * MLX5_MH_ACT_SZ);

/* Firmware has 5bit length field and 0 means 32bits */
if (mlen == 4)
mlen = 0;

MLX5_SET(set_action_in, modact, action_type, MLX5_ACTION_TYPE_SET);
MLX5_SET(set_action_in, modact, field, mfield);
MLX5_SET(set_action_in, modact, offset, moffset * 8);
MLX5_SET(set_action_in, modact, length, mlen * 8);
MLX5_SET(set_action_in, modact, data, data);
}

struct mlx5e_hairpin {
struct mlx5_hairpin *pair;

Expand Down Expand Up @@ -1214,6 +1250,26 @@ int mlx5e_tc_query_route_vport(struct net_device *out_dev, struct net_device *ro
return err;
}

int mlx5e_tc_add_flow_mod_hdr(struct mlx5e_priv *priv,
struct mlx5e_tc_flow_parse_attr *parse_attr,
struct mlx5e_tc_flow *flow)
{
struct mlx5e_tc_mod_hdr_acts *mod_hdr_acts = &parse_attr->mod_hdr_acts;
struct mlx5_modify_hdr *mod_hdr;

mod_hdr = mlx5_modify_header_alloc(priv->mdev,
get_flow_name_space(flow),
mod_hdr_acts->num_actions,
mod_hdr_acts->actions);
if (IS_ERR(mod_hdr))
return PTR_ERR(mod_hdr);

WARN_ON(flow->attr->modify_hdr);
flow->attr->modify_hdr = mod_hdr;

return 0;
}

static int
mlx5e_tc_add_fdb_flow(struct mlx5e_priv *priv,
struct mlx5e_tc_flow *flow,
Expand Down Expand Up @@ -1293,7 +1349,6 @@ mlx5e_tc_add_fdb_flow(struct mlx5e_priv *priv,
if (attr->action & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR &&
!(attr->ct_attr.ct_action & TCA_CT_ACT_CLEAR)) {
err = mlx5e_attach_mod_hdr(priv, flow, parse_attr);
dealloc_mod_hdr_actions(&parse_attr->mod_hdr_acts);
if (err)
return err;
}
Expand Down Expand Up @@ -1376,8 +1431,10 @@ static void mlx5e_tc_del_fdb_flow(struct mlx5e_priv *priv,

mlx5_tc_ct_match_del(get_ct_priv(priv), &flow->attr->ct_attr);

if (attr->action & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR)
if (attr->action & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR) {
dealloc_mod_hdr_actions(&attr->parse_attr->mod_hdr_acts);
mlx5e_detach_mod_hdr(priv, flow);
}

if (attr->action & MLX5_FLOW_CONTEXT_ACTION_COUNT)
mlx5_fc_destroy(esw_attr->counter_dev, attr->counter);
Expand Down
15 changes: 15 additions & 0 deletions drivers/net/ethernet/mellanox/mlx5/core/en_tc.h
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,11 @@ int mlx5e_tc_match_to_reg_set(struct mlx5_core_dev *mdev,
enum mlx5e_tc_attr_to_reg type,
u32 data);

void mlx5e_tc_match_to_reg_mod_hdr_change(struct mlx5_core_dev *mdev,
struct mlx5e_tc_mod_hdr_acts *mod_hdr_acts,
enum mlx5e_tc_attr_to_reg type,
int act_id, u32 data);

void mlx5e_tc_match_to_reg_match(struct mlx5_flow_spec *spec,
enum mlx5e_tc_attr_to_reg type,
u32 data,
Expand All @@ -224,6 +229,16 @@ void mlx5e_tc_match_to_reg_get_match(struct mlx5_flow_spec *spec,
u32 *data,
u32 *mask);

int mlx5e_tc_match_to_reg_set_and_get_id(struct mlx5_core_dev *mdev,
struct mlx5e_tc_mod_hdr_acts *mod_hdr_acts,
enum mlx5_flow_namespace_type ns,
enum mlx5e_tc_attr_to_reg type,
u32 data);

int mlx5e_tc_add_flow_mod_hdr(struct mlx5e_priv *priv,
struct mlx5e_tc_flow_parse_attr *parse_attr,
struct mlx5e_tc_flow *flow);

int alloc_mod_hdr_actions(struct mlx5_core_dev *mdev,
int namespace,
struct mlx5e_tc_mod_hdr_acts *mod_hdr_acts);
Expand Down

0 comments on commit c7b9038

Please sign in to comment.