Merge branches 'arm/rockchip', 'arm/exynos', 'arm/smmu', 'x86/vt-d', 'x86/amd', ...
[sfrench/cifs-2.6.git] / drivers / net / ethernet / mellanox / mlx4 / cmd.c
index 546ca4226916dbf2261525c12dc087ba35c7f4d7..529ef0594b902ebaf2838cf478ef914a0b69d5b7 100644 (file)
@@ -48,6 +48,7 @@
 
 #include "mlx4.h"
 #include "fw.h"
+#include "fw_qos.h"
 
 #define CMD_POLL_TOKEN 0xffff
 #define INBOX_MASK     0xffffffffffffff00ULL
@@ -713,8 +714,13 @@ static int mlx4_cmd_wait(struct mlx4_dev *dev, u64 in_param, u64 *out_param,
                                         msecs_to_jiffies(timeout))) {
                mlx4_warn(dev, "command 0x%x timed out (go bit not cleared)\n",
                          op);
-               err = -EIO;
-               goto out_reset;
+               if (op == MLX4_CMD_NOP) {
+                       err = -EBUSY;
+                       goto out;
+               } else {
+                       err = -EIO;
+                       goto out_reset;
+               }
        }
 
        err = context->result;
@@ -726,7 +732,8 @@ static int mlx4_cmd_wait(struct mlx4_dev *dev, u64 in_param, u64 *out_param,
                 */
                if (op == MLX4_CMD_SET_PORT &&
                    (in_modifier == 1 || in_modifier == 2) &&
-                   op_modifier == 0 && context->fw_status == CMD_STAT_BAD_SIZE)
+                   op_modifier == MLX4_SET_PORT_IB_OPCODE &&
+                   context->fw_status == CMD_STAT_BAD_SIZE)
                        mlx4_dbg(dev, "command 0x%x failed: fw status = 0x%x\n",
                                 op, context->fw_status);
                else
@@ -937,21 +944,34 @@ static int mlx4_MAD_IFC_wrapper(struct mlx4_dev *dev, int slave,
                                return err;
                        }
                        if (smp->attr_id == IB_SMP_ATTR_GUID_INFO) {
-                               /* compute slave's gid block */
-                               smp->attr_mod = cpu_to_be32(slave / 8);
-                               /* execute cmd */
-                               err = mlx4_cmd_box(dev, inbox->dma, outbox->dma,
-                                            vhcr->in_modifier, opcode_modifier,
-                                            vhcr->op, MLX4_CMD_TIME_CLASS_C, MLX4_CMD_NATIVE);
-                               if (!err) {
-                                       /* if needed, move slave gid to index 0 */
-                                       if (slave % 8)
-                                               memcpy(outsmp->data,
-                                                      outsmp->data + (slave % 8) * 8, 8);
-                                       /* delete all other gids */
-                                       memset(outsmp->data + 8, 0, 56);
+                               __be64 guid = mlx4_get_admin_guid(dev, slave,
+                                                                 port);
+
+                               /* set the PF admin guid to the FW/HW burned
+                                * GUID, if it wasn't yet set
+                                */
+                               if (slave == 0 && guid == 0) {
+                                       smp->attr_mod = 0;
+                                       err = mlx4_cmd_box(dev,
+                                                          inbox->dma,
+                                                          outbox->dma,
+                                                          vhcr->in_modifier,
+                                                          opcode_modifier,
+                                                          vhcr->op,
+                                                          MLX4_CMD_TIME_CLASS_C,
+                                                          MLX4_CMD_NATIVE);
+                                       if (err)
+                                               return err;
+                                       mlx4_set_admin_guid(dev,
+                                                           *(__be64 *)outsmp->
+                                                           data, slave, port);
+                               } else {
+                                       memcpy(outsmp->data, &guid, 8);
                                }
-                               return err;
+
+                               /* clean all other gids */
+                               memset(outsmp->data + 8, 0, 56);
+                               return 0;
                        }
                        if (smp->attr_id == IB_SMP_ATTR_NODE_INFO) {
                                err = mlx4_cmd_box(dev, inbox->dma, outbox->dma,
@@ -1455,6 +1475,24 @@ static struct mlx4_cmd_info cmd_info[] = {
                .verify = NULL,
                .wrapper = mlx4_CMD_EPERM_wrapper,
        },
+       {
+               .opcode = MLX4_CMD_ALLOCATE_VPP,
+               .has_inbox = false,
+               .has_outbox = true,
+               .out_is_imm = false,
+               .encode_slave_id = false,
+               .verify = NULL,
+               .wrapper = mlx4_CMD_EPERM_wrapper,
+       },
+       {
+               .opcode = MLX4_CMD_SET_VPORT_QOS,
+               .has_inbox = false,
+               .has_outbox = true,
+               .out_is_imm = false,
+               .encode_slave_id = false,
+               .verify = NULL,
+               .wrapper = mlx4_CMD_EPERM_wrapper,
+       },
        {
                .opcode = MLX4_CMD_CONF_SPECIAL_QP,
                .has_inbox = false,
@@ -1500,6 +1538,15 @@ static struct mlx4_cmd_info cmd_info[] = {
                .verify = NULL,
                .wrapper = mlx4_ACCESS_REG_wrapper,
        },
+       {
+               .opcode = MLX4_CMD_CONGESTION_CTRL_OPCODE,
+               .has_inbox = false,
+               .has_outbox = false,
+               .out_is_imm = false,
+               .encode_slave_id = false,
+               .verify = NULL,
+               .wrapper = mlx4_CMD_EPERM_wrapper,
+       },
        /* Native multicast commands are not available for guests */
        {
                .opcode = MLX4_CMD_QP_ATTACH,
@@ -1782,7 +1829,8 @@ static int mlx4_master_immediate_activate_vlan_qos(struct mlx4_priv *priv,
 
        if (vp_oper->state.default_vlan == vp_admin->default_vlan &&
            vp_oper->state.default_qos == vp_admin->default_qos &&
-           vp_oper->state.link_state == vp_admin->link_state)
+           vp_oper->state.link_state == vp_admin->link_state &&
+           vp_oper->state.qos_vport == vp_admin->qos_vport)
                return 0;
 
        if (!(priv->mfunc.master.slave_state[slave].active &&
@@ -1840,6 +1888,7 @@ static int mlx4_master_immediate_activate_vlan_qos(struct mlx4_priv *priv,
        vp_oper->state.default_vlan = vp_admin->default_vlan;
        vp_oper->state.default_qos = vp_admin->default_qos;
        vp_oper->state.link_state = vp_admin->link_state;
+       vp_oper->state.qos_vport = vp_admin->qos_vport;
 
        if (vp_admin->link_state == IFLA_VF_LINK_STATE_DISABLE)
                work->flags |= MLX4_VF_IMMED_VLAN_FLAG_LINK_DISABLE;
@@ -1848,6 +1897,7 @@ static int mlx4_master_immediate_activate_vlan_qos(struct mlx4_priv *priv,
        work->port = port;
        work->slave = slave;
        work->qos = vp_oper->state.default_qos;
+       work->qos_vport = vp_oper->state.qos_vport;
        work->vlan_id = vp_oper->state.default_vlan;
        work->vlan_ix = vp_oper->vlan_idx;
        work->priv = priv;
@@ -1857,6 +1907,63 @@ static int mlx4_master_immediate_activate_vlan_qos(struct mlx4_priv *priv,
        return 0;
 }
 
+static void mlx4_set_default_port_qos(struct mlx4_dev *dev, int port)
+{
+       struct mlx4_qos_manager *port_qos_ctl;
+       struct mlx4_priv *priv = mlx4_priv(dev);
+
+       port_qos_ctl = &priv->mfunc.master.qos_ctl[port];
+       bitmap_zero(port_qos_ctl->priority_bm, MLX4_NUM_UP);
+
+       /* Enable only default prio at PF init routine */
+       set_bit(MLX4_DEFAULT_QOS_PRIO, port_qos_ctl->priority_bm);
+}
+
+static void mlx4_allocate_port_vpps(struct mlx4_dev *dev, int port)
+{
+       int i;
+       int err;
+       int num_vfs;
+       u16 availible_vpp;
+       u8 vpp_param[MLX4_NUM_UP];
+       struct mlx4_qos_manager *port_qos;
+       struct mlx4_priv *priv = mlx4_priv(dev);
+
+       err = mlx4_ALLOCATE_VPP_get(dev, port, &availible_vpp, vpp_param);
+       if (err) {
+               mlx4_info(dev, "Failed query availible VPPs\n");
+               return;
+       }
+
+       port_qos = &priv->mfunc.master.qos_ctl[port];
+       num_vfs = (availible_vpp /
+                  bitmap_weight(port_qos->priority_bm, MLX4_NUM_UP));
+
+       for (i = 0; i < MLX4_NUM_UP; i++) {
+               if (test_bit(i, port_qos->priority_bm))
+                       vpp_param[i] = num_vfs;
+       }
+
+       err = mlx4_ALLOCATE_VPP_set(dev, port, vpp_param);
+       if (err) {
+               mlx4_info(dev, "Failed allocating VPPs\n");
+               return;
+       }
+
+       /* Query actual allocated VPP, just to make sure */
+       err = mlx4_ALLOCATE_VPP_get(dev, port, &availible_vpp, vpp_param);
+       if (err) {
+               mlx4_info(dev, "Failed query availible VPPs\n");
+               return;
+       }
+
+       port_qos->num_of_qos_vfs = num_vfs;
+       mlx4_dbg(dev, "Port %d Availible VPPs %d\n", port, availible_vpp);
+
+       for (i = 0; i < MLX4_NUM_UP; i++)
+               mlx4_dbg(dev, "Port %d UP %d Allocated %d VPPs\n", port, i,
+                        vpp_param[i]);
+}
 
 static int mlx4_master_activate_admin_state(struct mlx4_priv *priv, int slave)
 {
@@ -2204,6 +2311,9 @@ int mlx4_multi_func_init(struct mlx4_dev *dev)
        }
 
        if (mlx4_is_master(dev)) {
+               struct mlx4_vf_oper_state *vf_oper;
+               struct mlx4_vf_admin_state *vf_admin;
+
                priv->mfunc.master.slave_state =
                        kzalloc(dev->num_slaves *
                                sizeof(struct mlx4_slave_state), GFP_KERNEL);
@@ -2223,6 +2333,8 @@ int mlx4_multi_func_init(struct mlx4_dev *dev)
                        goto err_comm_oper;
 
                for (i = 0; i < dev->num_slaves; ++i) {
+                       vf_admin = &priv->mfunc.master.vf_admin[i];
+                       vf_oper = &priv->mfunc.master.vf_oper[i];
                        s_state = &priv->mfunc.master.slave_state[i];
                        s_state->last_cmd = MLX4_COMM_CMD_RESET;
                        mutex_init(&priv->mfunc.master.gen_eqe_mutex[i]);
@@ -2234,6 +2346,9 @@ int mlx4_multi_func_init(struct mlx4_dev *dev)
                                     &priv->mfunc.comm[i].slave_read);
                        mmiowb();
                        for (port = 1; port <= MLX4_MAX_PORTS; port++) {
+                               struct mlx4_vport_state *admin_vport;
+                               struct mlx4_vport_state *oper_vport;
+
                                s_state->vlan_filter[port] =
                                        kzalloc(sizeof(struct mlx4_vlan_fltr),
                                                GFP_KERNEL);
@@ -2242,15 +2357,31 @@ int mlx4_multi_func_init(struct mlx4_dev *dev)
                                                kfree(s_state->vlan_filter[port]);
                                        goto err_slaves;
                                }
+
+                               admin_vport = &vf_admin->vport[port];
+                               oper_vport = &vf_oper->vport[port].state;
                                INIT_LIST_HEAD(&s_state->mcast_filters[port]);
-                               priv->mfunc.master.vf_admin[i].vport[port].default_vlan = MLX4_VGT;
-                               priv->mfunc.master.vf_oper[i].vport[port].state.default_vlan = MLX4_VGT;
-                               priv->mfunc.master.vf_oper[i].vport[port].vlan_idx = NO_INDX;
-                               priv->mfunc.master.vf_oper[i].vport[port].mac_idx = NO_INDX;
+                               admin_vport->default_vlan = MLX4_VGT;
+                               oper_vport->default_vlan = MLX4_VGT;
+                               admin_vport->qos_vport =
+                                               MLX4_VPP_DEFAULT_VPORT;
+                               oper_vport->qos_vport = MLX4_VPP_DEFAULT_VPORT;
+                               vf_oper->vport[port].vlan_idx = NO_INDX;
+                               vf_oper->vport[port].mac_idx = NO_INDX;
+                               mlx4_set_random_admin_guid(dev, i, port);
                        }
                        spin_lock_init(&s_state->lock);
                }
 
+               if (dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_QOS_VPP) {
+                       for (port = 1; port <= dev->caps.num_ports; port++) {
+                               if (mlx4_is_eth(dev, port)) {
+                                       mlx4_set_default_port_qos(dev, port);
+                                       mlx4_allocate_port_vpps(dev, port);
+                               }
+                       }
+               }
+
                memset(&priv->mfunc.master.cmd_eqe, 0, dev->caps.eqe_size);
                priv->mfunc.master.cmd_eqe.type = MLX4_EVENT_TYPE_CMD;
                INIT_WORK(&priv->mfunc.master.comm_work,
@@ -2671,6 +2802,103 @@ static int mlx4_slaves_closest_port(struct mlx4_dev *dev, int slave, int port)
        return port;
 }
 
+static int mlx4_set_vport_qos(struct mlx4_priv *priv, int slave, int port,
+                             int max_tx_rate)
+{
+       int i;
+       int err;
+       struct mlx4_qos_manager *port_qos;
+       struct mlx4_dev *dev = &priv->dev;
+       struct mlx4_vport_qos_param vpp_qos[MLX4_NUM_UP];
+
+       port_qos = &priv->mfunc.master.qos_ctl[port];
+       memset(vpp_qos, 0, sizeof(struct mlx4_vport_qos_param) * MLX4_NUM_UP);
+
+       if (slave > port_qos->num_of_qos_vfs) {
+               mlx4_info(dev, "No availible VPP resources for this VF\n");
+               return -EINVAL;
+       }
+
+       /* Query for default QoS values from Vport 0 is needed */
+       err = mlx4_SET_VPORT_QOS_get(dev, port, 0, vpp_qos);
+       if (err) {
+               mlx4_info(dev, "Failed to query Vport 0 QoS values\n");
+               return err;
+       }
+
+       for (i = 0; i < MLX4_NUM_UP; i++) {
+               if (test_bit(i, port_qos->priority_bm) && max_tx_rate) {
+                       vpp_qos[i].max_avg_bw = max_tx_rate;
+                       vpp_qos[i].enable = 1;
+               } else {
+                       /* if user supplied tx_rate == 0, meaning no rate limit
+                        * configuration is required. so we are leaving the
+                        * value of max_avg_bw as queried from Vport 0.
+                        */
+                       vpp_qos[i].enable = 0;
+               }
+       }
+
+       err = mlx4_SET_VPORT_QOS_set(dev, port, slave, vpp_qos);
+       if (err) {
+               mlx4_info(dev, "Failed to set Vport %d QoS values\n", slave);
+               return err;
+       }
+
+       return 0;
+}
+
+static bool mlx4_is_vf_vst_and_prio_qos(struct mlx4_dev *dev, int port,
+                                       struct mlx4_vport_state *vf_admin)
+{
+       struct mlx4_qos_manager *info;
+       struct mlx4_priv *priv = mlx4_priv(dev);
+
+       if (!mlx4_is_master(dev) ||
+           !(dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_QOS_VPP))
+               return false;
+
+       info = &priv->mfunc.master.qos_ctl[port];
+
+       if (vf_admin->default_vlan != MLX4_VGT &&
+           test_bit(vf_admin->default_qos, info->priority_bm))
+               return true;
+
+       return false;
+}
+
+static bool mlx4_valid_vf_state_change(struct mlx4_dev *dev, int port,
+                                      struct mlx4_vport_state *vf_admin,
+                                      int vlan, int qos)
+{
+       struct mlx4_vport_state dummy_admin = {0};
+
+       if (!mlx4_is_vf_vst_and_prio_qos(dev, port, vf_admin) ||
+           !vf_admin->tx_rate)
+               return true;
+
+       dummy_admin.default_qos = qos;
+       dummy_admin.default_vlan = vlan;
+
+       /* VF wants to move to other VST state which is valid with current
+        * rate limit. Either differnt default vlan in VST or other
+        * supported QoS priority. Otherwise we don't allow this change when
+        * the TX rate is still configured.
+        */
+       if (mlx4_is_vf_vst_and_prio_qos(dev, port, &dummy_admin))
+               return true;
+
+       mlx4_info(dev, "Cannot change VF state to %s while rate is set\n",
+                 (vlan == MLX4_VGT) ? "VGT" : "VST");
+
+       if (vlan != MLX4_VGT)
+               mlx4_info(dev, "VST priority %d not supported for QoS\n", qos);
+
+       mlx4_info(dev, "Please set rate to 0 prior to this VF state change\n");
+
+       return false;
+}
+
 int mlx4_set_vf_mac(struct mlx4_dev *dev, int port, int vf, u64 mac)
 {
        struct mlx4_priv *priv = mlx4_priv(dev);
@@ -2714,12 +2942,22 @@ int mlx4_set_vf_vlan(struct mlx4_dev *dev, int port, int vf, u16 vlan, u8 qos)
        port = mlx4_slaves_closest_port(dev, slave, port);
        vf_admin = &priv->mfunc.master.vf_admin[slave].vport[port];
 
+       if (!mlx4_valid_vf_state_change(dev, port, vf_admin, vlan, qos))
+               return -EPERM;
+
        if ((0 == vlan) && (0 == qos))
                vf_admin->default_vlan = MLX4_VGT;
        else
                vf_admin->default_vlan = vlan;
        vf_admin->default_qos = qos;
 
+       /* If rate was configured prior to VST, we saved the configured rate
+        * in vf_admin->rate and now, if priority supported we enforce the QoS
+        */
+       if (mlx4_is_vf_vst_and_prio_qos(dev, port, vf_admin) &&
+           vf_admin->tx_rate)
+               vf_admin->qos_vport = slave;
+
        if (mlx4_master_immediate_activate_vlan_qos(priv, slave, port))
                mlx4_info(dev,
                          "updating vf %d port %d config will take effect on next VF restart\n",
@@ -2728,6 +2966,69 @@ int mlx4_set_vf_vlan(struct mlx4_dev *dev, int port, int vf, u16 vlan, u8 qos)
 }
 EXPORT_SYMBOL_GPL(mlx4_set_vf_vlan);
 
+int mlx4_set_vf_rate(struct mlx4_dev *dev, int port, int vf, int min_tx_rate,
+                    int max_tx_rate)
+{
+       int err;
+       int slave;
+       struct mlx4_vport_state *vf_admin;
+       struct mlx4_priv *priv = mlx4_priv(dev);
+
+       if (!mlx4_is_master(dev) ||
+           !(dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_QOS_VPP))
+               return -EPROTONOSUPPORT;
+
+       if (min_tx_rate) {
+               mlx4_info(dev, "Minimum BW share not supported\n");
+               return -EPROTONOSUPPORT;
+       }
+
+       slave = mlx4_get_slave_indx(dev, vf);
+       if (slave < 0)
+               return -EINVAL;
+
+       port = mlx4_slaves_closest_port(dev, slave, port);
+       vf_admin = &priv->mfunc.master.vf_admin[slave].vport[port];
+
+       err = mlx4_set_vport_qos(priv, slave, port, max_tx_rate);
+       if (err) {
+               mlx4_info(dev, "vf %d failed to set rate %d\n", vf,
+                         max_tx_rate);
+               return err;
+       }
+
+       vf_admin->tx_rate = max_tx_rate;
+       /* if VF is not in supported mode (VST with supported prio),
+        * we do not change vport configuration for its QPs, but save
+        * the rate, so it will be enforced when it moves to supported
+        * mode next time.
+        */
+       if (!mlx4_is_vf_vst_and_prio_qos(dev, port, vf_admin)) {
+               mlx4_info(dev,
+                         "rate set for VF %d when not in valid state\n", vf);
+
+               if (vf_admin->default_vlan != MLX4_VGT)
+                       mlx4_info(dev, "VST priority not supported by QoS\n");
+               else
+                       mlx4_info(dev, "VF in VGT mode (needed VST)\n");
+
+               mlx4_info(dev,
+                         "rate %d take affect when VF moves to valid state\n",
+                         max_tx_rate);
+               return 0;
+       }
+
+       /* If user sets rate 0 assigning default vport for its QPs */
+       vf_admin->qos_vport = max_tx_rate ? slave : MLX4_VPP_DEFAULT_VPORT;
+
+       if (priv->mfunc.master.slave_state[slave].active &&
+           dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_UPDATE_QP)
+               mlx4_master_immediate_activate_vlan_qos(priv, slave, port);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(mlx4_set_vf_rate);
+
  /* mlx4_get_slave_default_vlan -
  * return true if VST ( default vlan)
  * if VST, will return vlan & qos (if not NULL)
@@ -2801,7 +3102,12 @@ int mlx4_get_vf_config(struct mlx4_dev *dev, int port, int vf, struct ifla_vf_in
 
        ivf->vlan               = s_info->default_vlan;
        ivf->qos                = s_info->default_qos;
-       ivf->max_tx_rate        = s_info->tx_rate;
+
+       if (mlx4_is_vf_vst_and_prio_qos(dev, port, s_info))
+               ivf->max_tx_rate = s_info->tx_rate;
+       else
+               ivf->max_tx_rate = 0;
+
        ivf->min_tx_rate        = 0;
        ivf->spoofchk           = s_info->spoofchk;
        ivf->linkstate          = s_info->link_state;