Commit 8c4dc42b authored by Eli Britstein's avatar Eli Britstein Committed by Saeed Mahameed

net/mlx5e: Support multiple encapsulations for a TC flow

Currently a flow is associated with a single encap structure. The FW
extended destination features enables the driver to associate a flow
with multiple encap instances.

Change the encap id field from a flow scope to a per destination value
in the flow attributes struct. Use the encaps array to associate a flow
table entry with multiple encap entries.

Update the neigh logic to offload only if all encapsulations used in a
flow are connected, and un-offload upon the first one disconnected.

Note that the driver can now support up to two encap destinations.
Signed-off-by: default avatarEli Britstein <elibr@mellanox.com>
Reviewed-by: default avatarOz Shlomo <ozsh@mellanox.com>
Signed-off-by: default avatarSaeed Mahameed <saeedm@mellanox.com>
parent 79baaec7
...@@ -843,14 +843,15 @@ static void mlx5e_tc_del_nic_flow(struct mlx5e_priv *priv, ...@@ -843,14 +843,15 @@ static void mlx5e_tc_del_nic_flow(struct mlx5e_priv *priv,
} }
static void mlx5e_detach_encap(struct mlx5e_priv *priv, static void mlx5e_detach_encap(struct mlx5e_priv *priv,
struct mlx5e_tc_flow *flow); struct mlx5e_tc_flow *flow, int out_index);
static int mlx5e_attach_encap(struct mlx5e_priv *priv, static int mlx5e_attach_encap(struct mlx5e_priv *priv,
struct ip_tunnel_info *tun_info, struct ip_tunnel_info *tun_info,
struct net_device *mirred_dev, struct net_device *mirred_dev,
struct net_device **encap_dev, struct net_device **encap_dev,
struct mlx5e_tc_flow *flow, struct mlx5e_tc_flow *flow,
struct netlink_ext_ack *extack); struct netlink_ext_ack *extack,
int out_index);
static struct mlx5_flow_handle * static struct mlx5_flow_handle *
mlx5e_tc_offload_fdb_rules(struct mlx5_eswitch *esw, mlx5e_tc_offload_fdb_rules(struct mlx5_eswitch *esw,
...@@ -955,18 +956,22 @@ mlx5e_tc_add_fdb_flow(struct mlx5e_priv *priv, ...@@ -955,18 +956,22 @@ mlx5e_tc_add_fdb_flow(struct mlx5e_priv *priv,
} }
for (out_index = 0; out_index < MLX5_MAX_FLOW_FWD_VPORTS; out_index++) { for (out_index = 0; out_index < MLX5_MAX_FLOW_FWD_VPORTS; out_index++) {
int mirred_ifindex;
if (!(attr->dests[out_index].flags & MLX5_ESW_DEST_ENCAP)) if (!(attr->dests[out_index].flags & MLX5_ESW_DEST_ENCAP))
continue; continue;
mirred_ifindex = attr->parse_attr->mirred_ifindex[out_index];
out_dev = __dev_get_by_index(dev_net(priv->netdev), out_dev = __dev_get_by_index(dev_net(priv->netdev),
attr->parse_attr->mirred_ifindex[0]); mirred_ifindex);
encap_err = mlx5e_attach_encap(priv, &parse_attr->tun_info[0], err = mlx5e_attach_encap(priv,
out_dev, &encap_dev, flow, &parse_attr->tun_info[out_index],
extack); out_dev, &encap_dev, flow,
if (encap_err && encap_err != -EAGAIN) { extack, out_index);
err = encap_err; if (err && err != -EAGAIN)
goto err_attach_encap; goto err_attach_encap;
} if (err == -EAGAIN)
encap_err = err;
out_priv = netdev_priv(encap_dev); out_priv = netdev_priv(encap_dev);
rpriv = out_priv->ppriv; rpriv = out_priv->ppriv;
attr->dests[out_index].rep = rpriv->rep; attr->dests[out_index].rep = rpriv->rep;
...@@ -1022,10 +1027,8 @@ mlx5e_tc_add_fdb_flow(struct mlx5e_priv *priv, ...@@ -1022,10 +1027,8 @@ mlx5e_tc_add_fdb_flow(struct mlx5e_priv *priv,
mlx5_eswitch_del_vlan_action(esw, attr); mlx5_eswitch_del_vlan_action(esw, attr);
err_add_vlan: err_add_vlan:
for (out_index = 0; out_index < MLX5_MAX_FLOW_FWD_VPORTS; out_index++) for (out_index = 0; out_index < MLX5_MAX_FLOW_FWD_VPORTS; out_index++)
if (attr->dests[out_index].flags & MLX5_ESW_DEST_ENCAP) { if (attr->dests[out_index].flags & MLX5_ESW_DEST_ENCAP)
mlx5e_detach_encap(priv, flow); mlx5e_detach_encap(priv, flow, out_index);
break;
}
err_attach_encap: err_attach_encap:
err_max_prio_chain: err_max_prio_chain:
return err; return err;
...@@ -1049,10 +1052,8 @@ static void mlx5e_tc_del_fdb_flow(struct mlx5e_priv *priv, ...@@ -1049,10 +1052,8 @@ static void mlx5e_tc_del_fdb_flow(struct mlx5e_priv *priv,
mlx5_eswitch_del_vlan_action(esw, attr); mlx5_eswitch_del_vlan_action(esw, attr);
for (out_index = 0; out_index < MLX5_MAX_FLOW_FWD_VPORTS; out_index++) for (out_index = 0; out_index < MLX5_MAX_FLOW_FWD_VPORTS; out_index++)
if (attr->dests[out_index].flags & MLX5_ESW_DEST_ENCAP) { if (attr->dests[out_index].flags & MLX5_ESW_DEST_ENCAP)
mlx5e_detach_encap(priv, flow); mlx5e_detach_encap(priv, flow, out_index);
break;
}
kvfree(attr->parse_attr); kvfree(attr->parse_attr);
if (attr->action & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR) if (attr->action & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR)
...@@ -1087,11 +1088,30 @@ void mlx5e_tc_encap_flows_add(struct mlx5e_priv *priv, ...@@ -1087,11 +1088,30 @@ void mlx5e_tc_encap_flows_add(struct mlx5e_priv *priv,
mlx5e_rep_queue_neigh_stats_work(priv); mlx5e_rep_queue_neigh_stats_work(priv);
list_for_each_entry(efi, &e->flows, list) { list_for_each_entry(efi, &e->flows, list) {
bool all_flow_encaps_valid = true;
int i;
flow = container_of(efi, struct mlx5e_tc_flow, encaps[efi->index]); flow = container_of(efi, struct mlx5e_tc_flow, encaps[efi->index]);
esw_attr = flow->esw_attr; esw_attr = flow->esw_attr;
esw_attr->encap_id = e->encap_id;
spec = &esw_attr->parse_attr->spec; spec = &esw_attr->parse_attr->spec;
esw_attr->dests[efi->index].encap_id = e->encap_id;
esw_attr->dests[efi->index].flags |= MLX5_ESW_DEST_ENCAP_VALID;
/* Flow can be associated with multiple encap entries.
* Before offloading the flow verify that all of them have
* a valid neighbour.
*/
for (i = 0; i < MLX5_MAX_FLOW_FWD_VPORTS; i++) {
if (!(esw_attr->dests[i].flags & MLX5_ESW_DEST_ENCAP))
continue;
if (!(esw_attr->dests[i].flags & MLX5_ESW_DEST_ENCAP_VALID)) {
all_flow_encaps_valid = false;
break;
}
}
/* Do not offload flows with unresolved neighbors */
if (!all_flow_encaps_valid)
continue;
/* update from slow path rule to encap rule */ /* update from slow path rule to encap rule */
rule = mlx5e_tc_offload_fdb_rules(esw, flow, spec, esw_attr); rule = mlx5e_tc_offload_fdb_rules(esw, flow, spec, esw_attr);
if (IS_ERR(rule)) { if (IS_ERR(rule)) {
...@@ -1124,6 +1144,8 @@ void mlx5e_tc_encap_flows_del(struct mlx5e_priv *priv, ...@@ -1124,6 +1144,8 @@ void mlx5e_tc_encap_flows_del(struct mlx5e_priv *priv,
/* update from encap rule to slow path rule */ /* update from encap rule to slow path rule */
rule = mlx5e_tc_offload_to_slow_path(esw, flow, spec, &slow_attr); rule = mlx5e_tc_offload_to_slow_path(esw, flow, spec, &slow_attr);
/* mark the flow's encap dest as non-valid */
flow->esw_attr->dests[efi->index].flags &= ~MLX5_ESW_DEST_ENCAP_VALID;
if (IS_ERR(rule)) { if (IS_ERR(rule)) {
err = PTR_ERR(rule); err = PTR_ERR(rule);
...@@ -1207,11 +1229,11 @@ void mlx5e_tc_update_neigh_used_value(struct mlx5e_neigh_hash_entry *nhe) ...@@ -1207,11 +1229,11 @@ void mlx5e_tc_update_neigh_used_value(struct mlx5e_neigh_hash_entry *nhe)
} }
static void mlx5e_detach_encap(struct mlx5e_priv *priv, static void mlx5e_detach_encap(struct mlx5e_priv *priv,
struct mlx5e_tc_flow *flow) struct mlx5e_tc_flow *flow, int out_index)
{ {
struct list_head *next = flow->encaps[0].list.next; struct list_head *next = flow->encaps[out_index].list.next;
list_del(&flow->encaps[0].list); list_del(&flow->encaps[out_index].list);
if (list_empty(next)) { if (list_empty(next)) {
struct mlx5e_encap_entry *e; struct mlx5e_encap_entry *e;
...@@ -2324,7 +2346,8 @@ static int mlx5e_attach_encap(struct mlx5e_priv *priv, ...@@ -2324,7 +2346,8 @@ static int mlx5e_attach_encap(struct mlx5e_priv *priv,
struct net_device *mirred_dev, struct net_device *mirred_dev,
struct net_device **encap_dev, struct net_device **encap_dev,
struct mlx5e_tc_flow *flow, struct mlx5e_tc_flow *flow,
struct netlink_ext_ack *extack) struct netlink_ext_ack *extack,
int out_index)
{ {
struct mlx5_eswitch *esw = priv->mdev->priv.eswitch; struct mlx5_eswitch *esw = priv->mdev->priv.eswitch;
unsigned short family = ip_tunnel_info_af(tun_info); unsigned short family = ip_tunnel_info_af(tun_info);
...@@ -2371,13 +2394,15 @@ static int mlx5e_attach_encap(struct mlx5e_priv *priv, ...@@ -2371,13 +2394,15 @@ static int mlx5e_attach_encap(struct mlx5e_priv *priv,
hash_add_rcu(esw->offloads.encap_tbl, &e->encap_hlist, hash_key); hash_add_rcu(esw->offloads.encap_tbl, &e->encap_hlist, hash_key);
attach_flow: attach_flow:
list_add(&flow->encaps[0].list, &e->flows); list_add(&flow->encaps[out_index].list, &e->flows);
flow->encaps[0].index = 0; flow->encaps[out_index].index = out_index;
*encap_dev = e->out_dev; *encap_dev = e->out_dev;
if (e->flags & MLX5_ENCAP_ENTRY_VALID) if (e->flags & MLX5_ENCAP_ENTRY_VALID) {
attr->encap_id = e->encap_id; attr->dests[out_index].encap_id = e->encap_id;
else attr->dests[out_index].flags |= MLX5_ESW_DEST_ENCAP_VALID;
} else {
err = -EAGAIN; err = -EAGAIN;
}
return err; return err;
...@@ -2516,8 +2541,10 @@ static int parse_tc_fdb_actions(struct mlx5e_priv *priv, struct tcf_exts *exts, ...@@ -2516,8 +2541,10 @@ static int parse_tc_fdb_actions(struct mlx5e_priv *priv, struct tcf_exts *exts,
attr->dests[attr->out_count].mdev = out_priv->mdev; attr->dests[attr->out_count].mdev = out_priv->mdev;
attr->out_count++; attr->out_count++;
} else if (encap) { } else if (encap) {
parse_attr->mirred_ifindex[0] = out_dev->ifindex; parse_attr->mirred_ifindex[attr->out_count] =
parse_attr->tun_info[0] = *info; out_dev->ifindex;
parse_attr->tun_info[attr->out_count] = *info;
encap = false;
attr->parse_attr = parse_attr; attr->parse_attr = parse_attr;
attr->dests[attr->out_count].flags |= attr->dests[attr->out_count].flags |=
MLX5_ESW_DEST_ENCAP; MLX5_ESW_DEST_ENCAP;
......
...@@ -283,6 +283,7 @@ enum mlx5_flow_match_level { ...@@ -283,6 +283,7 @@ enum mlx5_flow_match_level {
enum { enum {
MLX5_ESW_DEST_ENCAP = BIT(0), MLX5_ESW_DEST_ENCAP = BIT(0),
MLX5_ESW_DEST_ENCAP_VALID = BIT(1),
}; };
struct mlx5_esw_flow_attr { struct mlx5_esw_flow_attr {
...@@ -298,11 +299,11 @@ struct mlx5_esw_flow_attr { ...@@ -298,11 +299,11 @@ struct mlx5_esw_flow_attr {
u8 vlan_prio[MLX5_FS_VLAN_DEPTH]; u8 vlan_prio[MLX5_FS_VLAN_DEPTH];
u8 total_vlan; u8 total_vlan;
bool vlan_handled; bool vlan_handled;
u32 encap_id;
struct { struct {
u32 flags; u32 flags;
struct mlx5_eswitch_rep *rep; struct mlx5_eswitch_rep *rep;
struct mlx5_core_dev *mdev; struct mlx5_core_dev *mdev;
u32 encap_id;
} dests[MLX5_MAX_FLOW_FWD_VPORTS]; } dests[MLX5_MAX_FLOW_FWD_VPORTS];
u32 mod_hdr_id; u32 mod_hdr_id;
u8 match_level; u8 match_level;
......
...@@ -130,9 +130,10 @@ mlx5_eswitch_add_offloaded_rule(struct mlx5_eswitch *esw, ...@@ -130,9 +130,10 @@ mlx5_eswitch_add_offloaded_rule(struct mlx5_eswitch *esw,
MLX5_FLOW_DEST_VPORT_VHCA_ID; MLX5_FLOW_DEST_VPORT_VHCA_ID;
if (attr->dests[j].flags & MLX5_ESW_DEST_ENCAP) { if (attr->dests[j].flags & MLX5_ESW_DEST_ENCAP) {
flow_act.action |= MLX5_FLOW_CONTEXT_ACTION_PACKET_REFORMAT; flow_act.action |= MLX5_FLOW_CONTEXT_ACTION_PACKET_REFORMAT;
flow_act.reformat_id = attr->encap_id; flow_act.reformat_id = attr->dests[j].encap_id;
dest[i].vport.flags |= MLX5_FLOW_DEST_VPORT_REFORMAT_ID; dest[i].vport.flags |= MLX5_FLOW_DEST_VPORT_REFORMAT_ID;
dest[i].vport.reformat_id = attr->encap_id; dest[i].vport.reformat_id =
attr->dests[j].encap_id;
} }
i++; i++;
} }
...@@ -228,7 +229,7 @@ mlx5_eswitch_add_fwd_rule(struct mlx5_eswitch *esw, ...@@ -228,7 +229,7 @@ mlx5_eswitch_add_fwd_rule(struct mlx5_eswitch *esw,
dest[i].vport.flags |= MLX5_FLOW_DEST_VPORT_VHCA_ID; dest[i].vport.flags |= MLX5_FLOW_DEST_VPORT_VHCA_ID;
if (attr->dests[i].flags & MLX5_ESW_DEST_ENCAP) { if (attr->dests[i].flags & MLX5_ESW_DEST_ENCAP) {
dest[i].vport.flags |= MLX5_FLOW_DEST_VPORT_REFORMAT_ID; dest[i].vport.flags |= MLX5_FLOW_DEST_VPORT_REFORMAT_ID;
dest[i].vport.reformat_id = attr->encap_id; dest[i].vport.reformat_id = attr->dests[i].encap_id;
} }
} }
dest[i].type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE; dest[i].type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment