|
@@ -48,6 +48,7 @@
|
|
|
|
|
|
#include "mlx4.h"
|
|
|
#include "fw.h"
|
|
|
+#include "fw_qos.h"
|
|
|
|
|
|
#define CMD_POLL_TOKEN 0xffff
|
|
|
#define INBOX_MASK 0xffffffffffffff00ULL
|
|
@@ -725,7 +726,8 @@ static int mlx4_cmd_wait(struct mlx4_dev *dev, u64 in_param, u64 *out_param,
|
|
|
* specific command/input_mod/opcode_mod/fw-status to be debug.
|
|
|
*/
|
|
|
if (op == MLX4_CMD_SET_PORT && in_modifier == 1 &&
|
|
|
- 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
|
|
@@ -1454,6 +1456,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,
|
|
@@ -1790,7 +1810,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 &&
|
|
@@ -1848,6 +1869,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;
|
|
@@ -1856,6 +1878,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;
|
|
@@ -1865,6 +1888,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)
|
|
|
{
|
|
@@ -2212,6 +2292,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);
|
|
@@ -2231,6 +2314,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]);
|
|
@@ -2242,6 +2327,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);
|
|
@@ -2250,15 +2338,30 @@ 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;
|
|
|
}
|
|
|
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,
|
|
@@ -2679,6 +2782,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);
|
|
@@ -2722,12 +2922,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",
|
|
@@ -2736,6 +2946,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)
|
|
@@ -2809,7 +3082,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;
|