net/mlx5e: Add indirect tc offload of ovs internal port
Register callbacks for tc blocks of ovs internal port devices. This allows an indirect offloading rules that apply on such devices as the filter device. In case a rule is added to a tc block of an internal port, the mlx5 driver will implicitly add a matching on the internal port's unique vport metadata value to the rule's matching list. Therefore, only packets that previously hit a rule that redirects to an internal port and got the vport metadata overwritten to the internal port's unique metadata, can match on such indirect rule. Offloading of both ingress and egress tc blocks of internal ports is supported as opposed to other devices where only ingress block offloading is supported. Signed-off-by: Ariel Levkovich <lariel@nvidia.com> Reviewed-by: Paul Blakey <paulb@nvidia.com> Reviewed-by: Vlad Buslov <vladbu@nvidia.com> Reviewed-by: Roi Dayan <roid@nvidia.com> Signed-off-by: Saeed Mahameed <saeedm@nvidia.com>
This commit is contained in:
Родитель
100ad4e2d7
Коммит
166f431ec6
|
@ -25,6 +25,7 @@
|
|||
struct mlx5e_rep_indr_block_priv {
|
||||
struct net_device *netdev;
|
||||
struct mlx5e_rep_priv *rpriv;
|
||||
enum flow_block_binder_type binder_type;
|
||||
|
||||
struct list_head list;
|
||||
};
|
||||
|
@ -299,14 +300,16 @@ int mlx5e_rep_tc_event_port_affinity(struct mlx5e_priv *priv)
|
|||
|
||||
static struct mlx5e_rep_indr_block_priv *
|
||||
mlx5e_rep_indr_block_priv_lookup(struct mlx5e_rep_priv *rpriv,
|
||||
struct net_device *netdev)
|
||||
struct net_device *netdev,
|
||||
enum flow_block_binder_type binder_type)
|
||||
{
|
||||
struct mlx5e_rep_indr_block_priv *cb_priv;
|
||||
|
||||
list_for_each_entry(cb_priv,
|
||||
&rpriv->uplink_priv.tc_indr_block_priv_list,
|
||||
list)
|
||||
if (cb_priv->netdev == netdev)
|
||||
if (cb_priv->netdev == netdev &&
|
||||
cb_priv->binder_type == binder_type)
|
||||
return cb_priv;
|
||||
|
||||
return NULL;
|
||||
|
@ -344,9 +347,13 @@ mlx5e_rep_indr_offload(struct net_device *netdev,
|
|||
static int mlx5e_rep_indr_setup_tc_cb(enum tc_setup_type type,
|
||||
void *type_data, void *indr_priv)
|
||||
{
|
||||
unsigned long flags = MLX5_TC_FLAG(EGRESS) | MLX5_TC_FLAG(ESW_OFFLOAD);
|
||||
unsigned long flags = MLX5_TC_FLAG(ESW_OFFLOAD);
|
||||
struct mlx5e_rep_indr_block_priv *priv = indr_priv;
|
||||
|
||||
flags |= (priv->binder_type == FLOW_BLOCK_BINDER_TYPE_CLSACT_EGRESS) ?
|
||||
MLX5_TC_FLAG(EGRESS) :
|
||||
MLX5_TC_FLAG(INGRESS);
|
||||
|
||||
switch (type) {
|
||||
case TC_SETUP_CLSFLOWER:
|
||||
return mlx5e_rep_indr_offload(priv->netdev, type_data, priv,
|
||||
|
@ -428,11 +435,14 @@ mlx5e_rep_indr_setup_block(struct net_device *netdev, struct Qdisc *sch,
|
|||
void (*cleanup)(struct flow_block_cb *block_cb))
|
||||
{
|
||||
struct mlx5e_priv *priv = netdev_priv(rpriv->netdev);
|
||||
struct mlx5_eswitch *esw = priv->mdev->priv.eswitch;
|
||||
bool is_ovs_int_port = netif_is_ovs_master(netdev);
|
||||
struct mlx5e_rep_indr_block_priv *indr_priv;
|
||||
struct flow_block_cb *block_cb;
|
||||
|
||||
if (!mlx5e_tc_tun_device_to_offload(priv, netdev) &&
|
||||
!(is_vlan_dev(netdev) && vlan_dev_real_dev(netdev) == rpriv->netdev)) {
|
||||
!(is_vlan_dev(netdev) && vlan_dev_real_dev(netdev) == rpriv->netdev) &&
|
||||
!is_ovs_int_port) {
|
||||
if (!(netif_is_macvlan(netdev) && macvlan_dev_real_dev(netdev) == rpriv->netdev))
|
||||
return -EOPNOTSUPP;
|
||||
if (!mlx5e_rep_macvlan_mode_supported(netdev)) {
|
||||
|
@ -441,7 +451,14 @@ mlx5e_rep_indr_setup_block(struct net_device *netdev, struct Qdisc *sch,
|
|||
}
|
||||
}
|
||||
|
||||
if (f->binder_type != FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS)
|
||||
if (f->binder_type != FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS &&
|
||||
f->binder_type != FLOW_BLOCK_BINDER_TYPE_CLSACT_EGRESS)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (f->binder_type == FLOW_BLOCK_BINDER_TYPE_CLSACT_EGRESS && !is_ovs_int_port)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (is_ovs_int_port && !mlx5e_tc_int_port_supported(esw))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
f->unlocked_driver_cb = true;
|
||||
|
@ -449,7 +466,7 @@ mlx5e_rep_indr_setup_block(struct net_device *netdev, struct Qdisc *sch,
|
|||
|
||||
switch (f->command) {
|
||||
case FLOW_BLOCK_BIND:
|
||||
indr_priv = mlx5e_rep_indr_block_priv_lookup(rpriv, netdev);
|
||||
indr_priv = mlx5e_rep_indr_block_priv_lookup(rpriv, netdev, f->binder_type);
|
||||
if (indr_priv)
|
||||
return -EEXIST;
|
||||
|
||||
|
@ -459,6 +476,7 @@ mlx5e_rep_indr_setup_block(struct net_device *netdev, struct Qdisc *sch,
|
|||
|
||||
indr_priv->netdev = netdev;
|
||||
indr_priv->rpriv = rpriv;
|
||||
indr_priv->binder_type = f->binder_type;
|
||||
list_add(&indr_priv->list,
|
||||
&rpriv->uplink_priv.tc_indr_block_priv_list);
|
||||
|
||||
|
@ -476,7 +494,7 @@ mlx5e_rep_indr_setup_block(struct net_device *netdev, struct Qdisc *sch,
|
|||
|
||||
return 0;
|
||||
case FLOW_BLOCK_UNBIND:
|
||||
indr_priv = mlx5e_rep_indr_block_priv_lookup(rpriv, netdev);
|
||||
indr_priv = mlx5e_rep_indr_block_priv_lookup(rpriv, netdev, f->binder_type);
|
||||
if (!indr_priv)
|
||||
return -ENOENT;
|
||||
|
||||
|
|
|
@ -1437,6 +1437,32 @@ mlx5e_tc_add_fdb_flow(struct mlx5e_priv *priv,
|
|||
parse_attr = attr->parse_attr;
|
||||
esw_attr = attr->esw_attr;
|
||||
|
||||
if (netif_is_ovs_master(parse_attr->filter_dev)) {
|
||||
struct mlx5e_tc_int_port *int_port;
|
||||
|
||||
if (attr->chain) {
|
||||
NL_SET_ERR_MSG_MOD(extack,
|
||||
"Internal port rule is only supported on chain 0");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
if (attr->dest_chain) {
|
||||
NL_SET_ERR_MSG_MOD(extack,
|
||||
"Internal port rule offload doesn't support goto action");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
int_port = mlx5e_tc_int_port_get(mlx5e_get_int_port_priv(priv),
|
||||
parse_attr->filter_dev->ifindex,
|
||||
flow_flag_test(flow, EGRESS) ?
|
||||
MLX5E_TC_INT_PORT_EGRESS :
|
||||
MLX5E_TC_INT_PORT_INGRESS);
|
||||
if (IS_ERR(int_port))
|
||||
return PTR_ERR(int_port);
|
||||
|
||||
esw_attr->int_port = int_port;
|
||||
}
|
||||
|
||||
for (out_index = 0; out_index < MLX5_MAX_FLOW_FWD_VPORTS; out_index++) {
|
||||
struct net_device *out_dev;
|
||||
int mirred_ifindex;
|
||||
|
@ -1592,6 +1618,9 @@ static void mlx5e_tc_del_fdb_flow(struct mlx5e_priv *priv,
|
|||
if (attr->action & MLX5_FLOW_CONTEXT_ACTION_COUNT)
|
||||
mlx5_fc_destroy(esw_attr->counter_dev, attr->counter);
|
||||
|
||||
if (esw_attr->int_port)
|
||||
mlx5e_tc_int_port_put(mlx5e_get_int_port_priv(priv), esw_attr->int_port);
|
||||
|
||||
if (esw_attr->dest_int_port)
|
||||
mlx5e_tc_int_port_put(mlx5e_get_int_port_priv(priv), esw_attr->dest_int_port);
|
||||
|
||||
|
@ -4254,10 +4283,11 @@ static int parse_tc_fdb_actions(struct mlx5e_priv *priv,
|
|||
}
|
||||
}
|
||||
|
||||
/* If we forward to internal port we can only have 1 dest */
|
||||
if (esw_attr->dest_int_port && esw_attr->out_count > 1) {
|
||||
/* Forward to/from internal port can only have 1 dest */
|
||||
if ((netif_is_ovs_master(parse_attr->filter_dev) || esw_attr->dest_int_port) &&
|
||||
esw_attr->out_count > 1) {
|
||||
NL_SET_ERR_MSG_MOD(extack,
|
||||
"Redirect to internal port should be the only destination");
|
||||
"Rules with internal port can have only one destination");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
|
|
|
@ -468,6 +468,7 @@ struct mlx5_esw_flow_attr {
|
|||
struct mlx5_core_dev *in_mdev;
|
||||
struct mlx5_core_dev *counter_dev;
|
||||
struct mlx5e_tc_int_port *dest_int_port;
|
||||
struct mlx5e_tc_int_port *int_port;
|
||||
|
||||
int split_count;
|
||||
int out_count;
|
||||
|
|
|
@ -86,12 +86,18 @@ mlx5_eswitch_set_rule_flow_source(struct mlx5_eswitch *esw,
|
|||
struct mlx5_flow_spec *spec,
|
||||
struct mlx5_esw_flow_attr *attr)
|
||||
{
|
||||
if (MLX5_CAP_ESW_FLOWTABLE(esw->dev, flow_source) &&
|
||||
attr && attr->in_rep)
|
||||
spec->flow_context.flow_source =
|
||||
attr->in_rep->vport == MLX5_VPORT_UPLINK ?
|
||||
MLX5_FLOW_CONTEXT_FLOW_SOURCE_UPLINK :
|
||||
MLX5_FLOW_CONTEXT_FLOW_SOURCE_LOCAL_VPORT;
|
||||
if (!MLX5_CAP_ESW_FLOWTABLE(esw->dev, flow_source) || !attr || !attr->in_rep)
|
||||
return;
|
||||
|
||||
if (attr->int_port) {
|
||||
spec->flow_context.flow_source = mlx5e_tc_int_port_get_flow_source(attr->int_port);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
spec->flow_context.flow_source = (attr->in_rep->vport == MLX5_VPORT_UPLINK) ?
|
||||
MLX5_FLOW_CONTEXT_FLOW_SOURCE_UPLINK :
|
||||
MLX5_FLOW_CONTEXT_FLOW_SOURCE_LOCAL_VPORT;
|
||||
}
|
||||
|
||||
/* Actually only the upper 16 bits of reg c0 need to be cleared, but the lower 16 bits
|
||||
|
@ -121,6 +127,8 @@ mlx5_eswitch_set_rule_source_port(struct mlx5_eswitch *esw,
|
|||
struct mlx5_eswitch *src_esw,
|
||||
u16 vport)
|
||||
{
|
||||
struct mlx5_esw_flow_attr *esw_attr = attr->esw_attr;
|
||||
u32 metadata;
|
||||
void *misc2;
|
||||
void *misc;
|
||||
|
||||
|
@ -130,10 +138,16 @@ mlx5_eswitch_set_rule_source_port(struct mlx5_eswitch *esw,
|
|||
if (mlx5_eswitch_vport_match_metadata_enabled(esw)) {
|
||||
if (mlx5_esw_indir_table_decap_vport(attr))
|
||||
vport = mlx5_esw_indir_table_decap_vport(attr);
|
||||
|
||||
if (esw_attr->int_port)
|
||||
metadata =
|
||||
mlx5e_tc_int_port_get_metadata_for_match(esw_attr->int_port);
|
||||
else
|
||||
metadata =
|
||||
mlx5_eswitch_get_vport_metadata_for_match(src_esw, vport);
|
||||
|
||||
misc2 = MLX5_ADDR_OF(fte_match_param, spec->match_value, misc_parameters_2);
|
||||
MLX5_SET(fte_match_set_misc2, misc2, metadata_reg_c_0,
|
||||
mlx5_eswitch_get_vport_metadata_for_match(src_esw,
|
||||
vport));
|
||||
MLX5_SET(fte_match_set_misc2, misc2, metadata_reg_c_0, metadata);
|
||||
|
||||
misc2 = MLX5_ADDR_OF(fte_match_param, spec->match_criteria, misc_parameters_2);
|
||||
MLX5_SET(fte_match_set_misc2, misc2, metadata_reg_c_0,
|
||||
|
|
Загрузка…
Ссылка в новой задаче