]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c
Merge branches 'pm-core', 'pm-qos', 'pm-domains' and 'pm-opp'
[linux.git] / drivers / net / ethernet / mellanox / mlx5 / core / eswitch_offloads.c
index d239f5d0ea3683d886a7e21dc9d7d6274e15a65e..595f7c7383b399440aedec593ae0fb0c37bb6748 100644 (file)
@@ -43,33 +43,36 @@ enum {
        FDB_SLOW_PATH
 };
 
-struct mlx5_flow_rule *
+struct mlx5_flow_handle *
 mlx5_eswitch_add_offloaded_rule(struct mlx5_eswitch *esw,
                                struct mlx5_flow_spec *spec,
                                struct mlx5_esw_flow_attr *attr)
 {
-       struct mlx5_flow_destination dest = { 0 };
+       struct mlx5_flow_destination dest[2] = {};
+       struct mlx5_flow_act flow_act = {0};
        struct mlx5_fc *counter = NULL;
-       struct mlx5_flow_rule *rule;
+       struct mlx5_flow_handle *rule;
        void *misc;
-       int action;
+       int i = 0;
 
        if (esw->mode != SRIOV_OFFLOADS)
                return ERR_PTR(-EOPNOTSUPP);
 
        /* per flow vlan pop/push is emulated, don't set that into the firmware */
-       action = attr->action & ~(MLX5_FLOW_CONTEXT_ACTION_VLAN_PUSH | MLX5_FLOW_CONTEXT_ACTION_VLAN_POP);
+       flow_act.action = attr->action & ~(MLX5_FLOW_CONTEXT_ACTION_VLAN_PUSH | MLX5_FLOW_CONTEXT_ACTION_VLAN_POP);
 
-       if (action & MLX5_FLOW_CONTEXT_ACTION_FWD_DEST) {
-               dest.type = MLX5_FLOW_DESTINATION_TYPE_VPORT;
-               dest.vport_num = attr->out_rep->vport;
-               action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST;
-       } else if (action & MLX5_FLOW_CONTEXT_ACTION_COUNT) {
+       if (flow_act.action & MLX5_FLOW_CONTEXT_ACTION_FWD_DEST) {
+               dest[i].type = MLX5_FLOW_DESTINATION_TYPE_VPORT;
+               dest[i].vport_num = attr->out_rep->vport;
+               i++;
+       }
+       if (flow_act.action & MLX5_FLOW_CONTEXT_ACTION_COUNT) {
                counter = mlx5_fc_create(esw->dev, true);
                if (IS_ERR(counter))
                        return ERR_CAST(counter);
-               dest.type = MLX5_FLOW_DESTINATION_TYPE_COUNTER;
-               dest.counter = counter;
+               dest[i].type = MLX5_FLOW_DESTINATION_TYPE_COUNTER;
+               dest[i].counter = counter;
+               i++;
        }
 
        misc = MLX5_ADDR_OF(fte_match_param, spec->match_value, misc_parameters);
@@ -80,10 +83,14 @@ mlx5_eswitch_add_offloaded_rule(struct mlx5_eswitch *esw,
 
        spec->match_criteria_enable = MLX5_MATCH_OUTER_HEADERS |
                                      MLX5_MATCH_MISC_PARAMETERS;
+       if (flow_act.action & MLX5_FLOW_CONTEXT_ACTION_DECAP)
+               spec->match_criteria_enable |= MLX5_MATCH_INNER_HEADERS;
 
-       rule = mlx5_add_flow_rule((struct mlx5_flow_table *)esw->fdb_table.fdb,
-                                 spec, action, 0, &dest);
+       if (attr->encap)
+               flow_act.encap_id = attr->encap->encap_id;
 
+       rule = mlx5_add_flow_rules((struct mlx5_flow_table *)esw->fdb_table.fdb,
+                                  spec, &flow_act, dest, i);
        if (IS_ERR(rule))
                mlx5_fc_destroy(esw->dev, counter);
 
@@ -159,7 +166,7 @@ static int esw_add_vlan_action_check(struct mlx5_esw_flow_attr *attr,
        return 0;
 
 out_notsupp:
-       return -ENOTSUPP;
+       return -EOPNOTSUPP;
 }
 
 int mlx5_eswitch_add_vlan_action(struct mlx5_eswitch *esw,
@@ -270,11 +277,12 @@ int mlx5_eswitch_del_vlan_action(struct mlx5_eswitch *esw,
        return err;
 }
 
-static struct mlx5_flow_rule *
+static struct mlx5_flow_handle *
 mlx5_eswitch_add_send_to_vport_rule(struct mlx5_eswitch *esw, int vport, u32 sqn)
 {
+       struct mlx5_flow_act flow_act = {0};
        struct mlx5_flow_destination dest;
-       struct mlx5_flow_rule *flow_rule;
+       struct mlx5_flow_handle *flow_rule;
        struct mlx5_flow_spec *spec;
        void *misc;
 
@@ -296,10 +304,10 @@ mlx5_eswitch_add_send_to_vport_rule(struct mlx5_eswitch *esw, int vport, u32 sqn
        spec->match_criteria_enable = MLX5_MATCH_MISC_PARAMETERS;
        dest.type = MLX5_FLOW_DESTINATION_TYPE_VPORT;
        dest.vport_num = vport;
+       flow_act.action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST;
 
-       flow_rule = mlx5_add_flow_rule(esw->fdb_table.offloads.fdb, spec,
-                                      MLX5_FLOW_CONTEXT_ACTION_FWD_DEST,
-                                      0, &dest);
+       flow_rule = mlx5_add_flow_rules(esw->fdb_table.offloads.fdb, spec,
+                                       &flow_act, &dest, 1);
        if (IS_ERR(flow_rule))
                esw_warn(esw->dev, "FDB: Failed to add send to vport rule err %ld\n", PTR_ERR(flow_rule));
 out:
@@ -316,7 +324,7 @@ void mlx5_eswitch_sqs2vport_stop(struct mlx5_eswitch *esw,
                return;
 
        list_for_each_entry_safe(esw_sq, tmp, &rep->vport_sqs_list, list) {
-               mlx5_del_flow_rule(esw_sq->send_to_vport_rule);
+               mlx5_del_flow_rules(esw_sq->send_to_vport_rule);
                list_del(&esw_sq->list);
                kfree(esw_sq);
        }
@@ -326,7 +334,7 @@ int mlx5_eswitch_sqs2vport_start(struct mlx5_eswitch *esw,
                                 struct mlx5_eswitch_rep *rep,
                                 u16 *sqns_array, int sqns_num)
 {
-       struct mlx5_flow_rule *flow_rule;
+       struct mlx5_flow_handle *flow_rule;
        struct mlx5_esw_sq *esw_sq;
        int err;
        int i;
@@ -362,8 +370,9 @@ int mlx5_eswitch_sqs2vport_start(struct mlx5_eswitch *esw,
 
 static int esw_add_fdb_miss_rule(struct mlx5_eswitch *esw)
 {
+       struct mlx5_flow_act flow_act = {0};
        struct mlx5_flow_destination dest;
-       struct mlx5_flow_rule *flow_rule = NULL;
+       struct mlx5_flow_handle *flow_rule = NULL;
        struct mlx5_flow_spec *spec;
        int err = 0;
 
@@ -376,10 +385,10 @@ static int esw_add_fdb_miss_rule(struct mlx5_eswitch *esw)
 
        dest.type = MLX5_FLOW_DESTINATION_TYPE_VPORT;
        dest.vport_num = 0;
+       flow_act.action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST;
 
-       flow_rule = mlx5_add_flow_rule(esw->fdb_table.offloads.fdb, spec,
-                                      MLX5_FLOW_CONTEXT_ACTION_FWD_DEST,
-                                      0, &dest);
+       flow_rule = mlx5_add_flow_rules(esw->fdb_table.offloads.fdb, spec,
+                                       &flow_act, &dest, 1);
        if (IS_ERR(flow_rule)) {
                err = PTR_ERR(flow_rule);
                esw_warn(esw->dev,  "FDB: Failed to add miss flow rule err %d\n", err);
@@ -406,6 +415,7 @@ static int esw_create_offloads_fdb_table(struct mlx5_eswitch *esw, int nvports)
        u32 *flow_group_in;
        void *match_criteria;
        int table_size, ix, err = 0;
+       u32 flags = 0;
 
        flow_group_in = mlx5_vzalloc(inlen);
        if (!flow_group_in)
@@ -414,15 +424,21 @@ static int esw_create_offloads_fdb_table(struct mlx5_eswitch *esw, int nvports)
        root_ns = mlx5_get_flow_namespace(dev, MLX5_FLOW_NAMESPACE_FDB);
        if (!root_ns) {
                esw_warn(dev, "Failed to get FDB flow namespace\n");
+               err = -EOPNOTSUPP;
                goto ns_err;
        }
 
        esw_debug(dev, "Create offloads FDB table, log_max_size(%d)\n",
                  MLX5_CAP_ESW_FLOWTABLE_FDB(dev, log_max_ft_size));
 
+       if (MLX5_CAP_ESW_FLOWTABLE_FDB(dev, encap) &&
+           MLX5_CAP_ESW_FLOWTABLE_FDB(dev, decap))
+               flags |= MLX5_FLOW_TABLE_TUNNEL_EN;
+
        fdb = mlx5_create_auto_grouped_flow_table(root_ns, FDB_FAST_PATH,
                                                  ESW_OFFLOADS_NUM_ENTRIES,
-                                                 ESW_OFFLOADS_NUM_GROUPS, 0);
+                                                 ESW_OFFLOADS_NUM_GROUPS, 0,
+                                                 flags);
        if (IS_ERR(fdb)) {
                err = PTR_ERR(fdb);
                esw_warn(dev, "Failed to create Fast path FDB Table err %d\n", err);
@@ -431,7 +447,7 @@ static int esw_create_offloads_fdb_table(struct mlx5_eswitch *esw, int nvports)
        esw->fdb_table.fdb = fdb;
 
        table_size = nvports + MAX_PF_SQ + 1;
-       fdb = mlx5_create_flow_table(root_ns, FDB_SLOW_PATH, table_size, 0);
+       fdb = mlx5_create_flow_table(root_ns, FDB_SLOW_PATH, table_size, 0, 0);
        if (IS_ERR(fdb)) {
                err = PTR_ERR(fdb);
                esw_warn(dev, "Failed to create slow path FDB Table err %d\n", err);
@@ -502,7 +518,7 @@ static void esw_destroy_offloads_fdb_table(struct mlx5_eswitch *esw)
                return;
 
        esw_debug(esw->dev, "Destroy offloads FDB Table\n");
-       mlx5_del_flow_rule(esw->fdb_table.offloads.miss_rule);
+       mlx5_del_flow_rules(esw->fdb_table.offloads.miss_rule);
        mlx5_destroy_flow_group(esw->fdb_table.offloads.send_to_vport_grp);
        mlx5_destroy_flow_group(esw->fdb_table.offloads.miss_grp);
 
@@ -520,10 +536,10 @@ static int esw_create_offloads_table(struct mlx5_eswitch *esw)
        ns = mlx5_get_flow_namespace(dev, MLX5_FLOW_NAMESPACE_OFFLOADS);
        if (!ns) {
                esw_warn(esw->dev, "Failed to get offloads flow namespace\n");
-               return -ENOMEM;
+               return -EOPNOTSUPP;
        }
 
-       ft_offloads = mlx5_create_flow_table(ns, 0, dev->priv.sriov.num_vfs + 2, 0);
+       ft_offloads = mlx5_create_flow_table(ns, 0, dev->priv.sriov.num_vfs + 2, 0, 0);
        if (IS_ERR(ft_offloads)) {
                err = PTR_ERR(ft_offloads);
                esw_warn(esw->dev, "Failed to create offloads table, err %d\n", err);
@@ -586,11 +602,12 @@ static void esw_destroy_vport_rx_group(struct mlx5_eswitch *esw)
        mlx5_destroy_flow_group(esw->offloads.vport_rx_group);
 }
 
-struct mlx5_flow_rule *
+struct mlx5_flow_handle *
 mlx5_eswitch_create_vport_rx_rule(struct mlx5_eswitch *esw, int vport, u32 tirn)
 {
+       struct mlx5_flow_act flow_act = {0};
        struct mlx5_flow_destination dest;
-       struct mlx5_flow_rule *flow_rule;
+       struct mlx5_flow_handle *flow_rule;
        struct mlx5_flow_spec *spec;
        void *misc;
 
@@ -611,9 +628,9 @@ mlx5_eswitch_create_vport_rx_rule(struct mlx5_eswitch *esw, int vport, u32 tirn)
        dest.type = MLX5_FLOW_DESTINATION_TYPE_TIR;
        dest.tir_num = tirn;
 
-       flow_rule = mlx5_add_flow_rule(esw->offloads.ft_offloads, spec,
-                                      MLX5_FLOW_CONTEXT_ACTION_FWD_DEST,
-                                      0, &dest);
+       flow_act.action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST;
+       flow_rule = mlx5_add_flow_rules(esw->offloads.ft_offloads, spec,
+                                      &flow_act, &dest, 1);
        if (IS_ERR(flow_rule)) {
                esw_warn(esw->dev, "fs offloads: Failed to add vport rx rule err %ld\n", PTR_ERR(flow_rule));
                goto out;
@@ -639,7 +656,15 @@ static int esw_offloads_start(struct mlx5_eswitch *esw)
                esw_warn(esw->dev, "Failed setting eswitch to offloads, err %d\n", err);
                err1 = mlx5_eswitch_enable_sriov(esw, num_vfs, SRIOV_LEGACY);
                if (err1)
-                       esw_warn(esw->dev, "Failed setting eswitch back to legacy, err %d\n", err);
+                       esw_warn(esw->dev, "Failed setting eswitch back to legacy, err %d\n", err1);
+       }
+       if (esw->offloads.inline_mode == MLX5_INLINE_MODE_NONE) {
+               if (mlx5_eswitch_inline_mode_get(esw,
+                                                num_vfs,
+                                                &esw->offloads.inline_mode)) {
+                       esw->offloads.inline_mode = MLX5_INLINE_MODE_L2;
+                       esw_warn(esw->dev, "Inline mode is different between vports\n");
+               }
        }
        return err;
 }
@@ -650,9 +675,14 @@ int esw_offloads_init(struct mlx5_eswitch *esw, int nvports)
        int vport;
        int err;
 
+       /* disable PF RoCE so missed packets don't go through RoCE steering */
+       mlx5_dev_list_lock();
+       mlx5_remove_dev_by_protocol(esw->dev, MLX5_INTERFACE_PROTOCOL_IB);
+       mlx5_dev_list_unlock();
+
        err = esw_create_offloads_fdb_table(esw, nvports);
        if (err)
-               return err;
+               goto create_fdb_err;
 
        err = esw_create_offloads_table(esw);
        if (err)
@@ -671,6 +701,7 @@ int esw_offloads_init(struct mlx5_eswitch *esw, int nvports)
                if (err)
                        goto err_reps;
        }
+
        return 0;
 
 err_reps:
@@ -687,6 +718,13 @@ int esw_offloads_init(struct mlx5_eswitch *esw, int nvports)
 
 create_ft_err:
        esw_destroy_offloads_fdb_table(esw);
+
+create_fdb_err:
+       /* enable back PF RoCE */
+       mlx5_dev_list_lock();
+       mlx5_add_dev_by_protocol(esw->dev, MLX5_INTERFACE_PROTOCOL_IB);
+       mlx5_dev_list_unlock();
+
        return err;
 }
 
@@ -703,6 +741,11 @@ static int esw_offloads_stop(struct mlx5_eswitch *esw)
                        esw_warn(esw->dev, "Failed setting eswitch back to offloads, err %d\n", err);
        }
 
+       /* enable back PF RoCE */
+       mlx5_dev_list_lock();
+       mlx5_add_dev_by_protocol(esw->dev, MLX5_INTERFACE_PROTOCOL_IB);
+       mlx5_dev_list_unlock();
+
        return err;
 }
 
@@ -755,6 +798,50 @@ static int esw_mode_to_devlink(u16 mlx5_mode, u16 *mode)
        return 0;
 }
 
+static int esw_inline_mode_from_devlink(u8 mode, u8 *mlx5_mode)
+{
+       switch (mode) {
+       case DEVLINK_ESWITCH_INLINE_MODE_NONE:
+               *mlx5_mode = MLX5_INLINE_MODE_NONE;
+               break;
+       case DEVLINK_ESWITCH_INLINE_MODE_LINK:
+               *mlx5_mode = MLX5_INLINE_MODE_L2;
+               break;
+       case DEVLINK_ESWITCH_INLINE_MODE_NETWORK:
+               *mlx5_mode = MLX5_INLINE_MODE_IP;
+               break;
+       case DEVLINK_ESWITCH_INLINE_MODE_TRANSPORT:
+               *mlx5_mode = MLX5_INLINE_MODE_TCP_UDP;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int esw_inline_mode_to_devlink(u8 mlx5_mode, u8 *mode)
+{
+       switch (mlx5_mode) {
+       case MLX5_INLINE_MODE_NONE:
+               *mode = DEVLINK_ESWITCH_INLINE_MODE_NONE;
+               break;
+       case MLX5_INLINE_MODE_L2:
+               *mode = DEVLINK_ESWITCH_INLINE_MODE_LINK;
+               break;
+       case MLX5_INLINE_MODE_IP:
+               *mode = DEVLINK_ESWITCH_INLINE_MODE_NETWORK;
+               break;
+       case MLX5_INLINE_MODE_TCP_UDP:
+               *mode = DEVLINK_ESWITCH_INLINE_MODE_TRANSPORT;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
 int mlx5_devlink_eswitch_mode_set(struct devlink *devlink, u16 mode)
 {
        struct mlx5_core_dev *dev;
@@ -799,6 +886,95 @@ int mlx5_devlink_eswitch_mode_get(struct devlink *devlink, u16 *mode)
        return esw_mode_to_devlink(dev->priv.eswitch->mode, mode);
 }
 
+int mlx5_devlink_eswitch_inline_mode_set(struct devlink *devlink, u8 mode)
+{
+       struct mlx5_core_dev *dev = devlink_priv(devlink);
+       struct mlx5_eswitch *esw = dev->priv.eswitch;
+       int num_vports = esw->enabled_vports;
+       int err;
+       int vport;
+       u8 mlx5_mode;
+
+       if (!MLX5_CAP_GEN(dev, vport_group_manager))
+               return -EOPNOTSUPP;
+
+       if (esw->mode == SRIOV_NONE)
+               return -EOPNOTSUPP;
+
+       if (MLX5_CAP_ETH(dev, wqe_inline_mode) !=
+           MLX5_CAP_INLINE_MODE_VPORT_CONTEXT)
+               return -EOPNOTSUPP;
+
+       err = esw_inline_mode_from_devlink(mode, &mlx5_mode);
+       if (err)
+               goto out;
+
+       for (vport = 1; vport < num_vports; vport++) {
+               err = mlx5_modify_nic_vport_min_inline(dev, vport, mlx5_mode);
+               if (err) {
+                       esw_warn(dev, "Failed to set min inline on vport %d\n",
+                                vport);
+                       goto revert_inline_mode;
+               }
+       }
+
+       esw->offloads.inline_mode = mlx5_mode;
+       return 0;
+
+revert_inline_mode:
+       while (--vport > 0)
+               mlx5_modify_nic_vport_min_inline(dev,
+                                                vport,
+                                                esw->offloads.inline_mode);
+out:
+       return err;
+}
+
+int mlx5_devlink_eswitch_inline_mode_get(struct devlink *devlink, u8 *mode)
+{
+       struct mlx5_core_dev *dev = devlink_priv(devlink);
+       struct mlx5_eswitch *esw = dev->priv.eswitch;
+
+       if (!MLX5_CAP_GEN(dev, vport_group_manager))
+               return -EOPNOTSUPP;
+
+       if (esw->mode == SRIOV_NONE)
+               return -EOPNOTSUPP;
+
+       if (MLX5_CAP_ETH(dev, wqe_inline_mode) !=
+           MLX5_CAP_INLINE_MODE_VPORT_CONTEXT)
+               return -EOPNOTSUPP;
+
+       return esw_inline_mode_to_devlink(esw->offloads.inline_mode, mode);
+}
+
+int mlx5_eswitch_inline_mode_get(struct mlx5_eswitch *esw, int nvfs, u8 *mode)
+{
+       struct mlx5_core_dev *dev = esw->dev;
+       int vport;
+       u8 prev_mlx5_mode, mlx5_mode = MLX5_INLINE_MODE_L2;
+
+       if (!MLX5_CAP_GEN(dev, vport_group_manager))
+               return -EOPNOTSUPP;
+
+       if (esw->mode == SRIOV_NONE)
+               return -EOPNOTSUPP;
+
+       if (MLX5_CAP_ETH(dev, wqe_inline_mode) !=
+           MLX5_CAP_INLINE_MODE_VPORT_CONTEXT)
+               return -EOPNOTSUPP;
+
+       for (vport = 1; vport <= nvfs; vport++) {
+               mlx5_query_nic_vport_min_inline(dev, vport, &mlx5_mode);
+               if (vport > 1 && prev_mlx5_mode != mlx5_mode)
+                       return -EINVAL;
+               prev_mlx5_mode = mlx5_mode;
+       }
+
+       *mode = mlx5_mode;
+       return 0;
+}
+
 void mlx5_eswitch_register_vport_rep(struct mlx5_eswitch *esw,
                                     int vport_index,
                                     struct mlx5_eswitch_rep *__rep)
@@ -813,7 +989,7 @@ void mlx5_eswitch_register_vport_rep(struct mlx5_eswitch *esw,
        rep->load   = __rep->load;
        rep->unload = __rep->unload;
        rep->vport  = __rep->vport;
-       rep->priv_data = __rep->priv_data;
+       rep->netdev = __rep->netdev;
        ether_addr_copy(rep->hw_id, __rep->hw_id);
 
        INIT_LIST_HEAD(&rep->vport_sqs_list);
@@ -833,3 +1009,13 @@ void mlx5_eswitch_unregister_vport_rep(struct mlx5_eswitch *esw,
 
        rep->valid = false;
 }
+
+struct net_device *mlx5_eswitch_get_uplink_netdev(struct mlx5_eswitch *esw)
+{
+#define UPLINK_REP_INDEX 0
+       struct mlx5_esw_offload *offloads = &esw->offloads;
+       struct mlx5_eswitch_rep *rep;
+
+       rep = &offloads->vport_reps[UPLINK_REP_INDEX];
+       return rep->netdev;
+}