net/mlx5e: Add TX max rate support for MQPRIO channel mode
authorTariq Toukan <tariqt@nvidia.com>
Wed, 29 Sep 2021 13:30:38 +0000 (16:30 +0300)
committerSaeed Mahameed <saeedm@nvidia.com>
Tue, 5 Oct 2021 01:10:52 +0000 (18:10 -0700)
Add driver max_rate support for the MQPRIO bw_rlimit shaper
in channel mode.

Signed-off-by: Tariq Toukan <tariqt@nvidia.com>
Reviewed-by: Maxim Mikityanskiy <maximmi@nvidia.com>
Signed-off-by: Saeed Mahameed <saeedm@nvidia.com>
drivers/net/ethernet/mellanox/mlx5/core/en.h
drivers/net/ethernet/mellanox/mlx5/core/en/qos.c
drivers/net/ethernet/mellanox/mlx5/core/en/qos.h
drivers/net/ethernet/mellanox/mlx5/core/en_main.c

index e06c8b816a326cdec98f9e6f48b26189b1561278..a3a4fece0cac31754d23dc56f58550d59bfd3858 100644 (file)
@@ -251,6 +251,9 @@ struct mlx5e_params {
                u16 mode;
                u8 num_tc;
                struct netdev_tc_txq tc_to_txq[TC_MAX_QUEUE];
+               struct {
+                       struct mlx5e_mqprio_rl *rl;
+               } channel;
        } mqprio;
        bool rx_cqe_compress_def;
        bool tunneled_offload_en;
@@ -877,6 +880,7 @@ struct mlx5e_priv {
 #endif
        struct mlx5e_scratchpad    scratchpad;
        struct mlx5e_htb           htb;
+       struct mlx5e_mqprio_rl    *mqprio_rl;
 };
 
 struct mlx5e_rx_handlers {
index 17a607541af6d7008a5da68661fe61e24d0a3561..50977f01a0503d23a061239f4185f910528c6b2b 100644 (file)
@@ -7,6 +7,21 @@
 
 #define BYTES_IN_MBIT 125000
 
+int mlx5e_qos_bytes_rate_check(struct mlx5_core_dev *mdev, u64 nbytes)
+{
+       if (nbytes < BYTES_IN_MBIT) {
+               qos_warn(mdev, "Input rate (%llu Bytes/sec) below minimum supported (%u Bytes/sec)\n",
+                        nbytes, BYTES_IN_MBIT);
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static u32 mlx5e_qos_bytes2mbits(struct mlx5_core_dev *mdev, u64 nbytes)
+{
+       return div_u64(nbytes, BYTES_IN_MBIT);
+}
+
 int mlx5e_qos_max_leaf_nodes(struct mlx5_core_dev *mdev)
 {
        return min(MLX5E_QOS_MAX_LEAF_NODES, mlx5_qos_max_leaf_nodes(mdev));
@@ -980,3 +995,87 @@ int mlx5e_htb_node_modify(struct mlx5e_priv *priv, u16 classid, u64 rate, u64 ce
 
        return err;
 }
+
+struct mlx5e_mqprio_rl {
+       struct mlx5_core_dev *mdev;
+       u32 root_id;
+       u32 *leaves_id;
+       u8 num_tc;
+};
+
+struct mlx5e_mqprio_rl *mlx5e_mqprio_rl_alloc(void)
+{
+       return kvzalloc(sizeof(struct mlx5e_mqprio_rl), GFP_KERNEL);
+}
+
+void mlx5e_mqprio_rl_free(struct mlx5e_mqprio_rl *rl)
+{
+       kvfree(rl);
+}
+
+int mlx5e_mqprio_rl_init(struct mlx5e_mqprio_rl *rl, struct mlx5_core_dev *mdev, u8 num_tc,
+                        u64 max_rate[])
+{
+       int err;
+       int tc;
+
+       if (!mlx5_qos_is_supported(mdev)) {
+               qos_warn(mdev, "Missing QoS capabilities. Try disabling SRIOV or use a supported device.");
+               return -EOPNOTSUPP;
+       }
+       if (num_tc > mlx5e_qos_max_leaf_nodes(mdev))
+               return -EINVAL;
+
+       rl->mdev = mdev;
+       rl->num_tc = num_tc;
+       rl->leaves_id = kvcalloc(num_tc, sizeof(*rl->leaves_id), GFP_KERNEL);
+       if (!rl->leaves_id)
+               return -ENOMEM;
+
+       err = mlx5_qos_create_root_node(mdev, &rl->root_id);
+       if (err)
+               goto err_free_leaves;
+
+       qos_dbg(mdev, "Root created, id %#x\n", rl->root_id);
+
+       for (tc = 0; tc < num_tc; tc++) {
+               u32 max_average_bw;
+
+               max_average_bw = mlx5e_qos_bytes2mbits(mdev, max_rate[tc]);
+               err = mlx5_qos_create_leaf_node(mdev, rl->root_id, 0, max_average_bw,
+                                               &rl->leaves_id[tc]);
+               if (err)
+                       goto err_destroy_leaves;
+
+               qos_dbg(mdev, "Leaf[%d] created, id %#x, max average bw %u Mbits/sec\n",
+                       tc, rl->leaves_id[tc], max_average_bw);
+       }
+       return 0;
+
+err_destroy_leaves:
+       while (--tc >= 0)
+               mlx5_qos_destroy_node(mdev, rl->leaves_id[tc]);
+       mlx5_qos_destroy_node(mdev, rl->root_id);
+err_free_leaves:
+       kvfree(rl->leaves_id);
+       return err;
+}
+
+void mlx5e_mqprio_rl_cleanup(struct mlx5e_mqprio_rl *rl)
+{
+       int tc;
+
+       for (tc = 0; tc < rl->num_tc; tc++)
+               mlx5_qos_destroy_node(rl->mdev, rl->leaves_id[tc]);
+       mlx5_qos_destroy_node(rl->mdev, rl->root_id);
+       kvfree(rl->leaves_id);
+}
+
+int mlx5e_mqprio_rl_get_node_hw_id(struct mlx5e_mqprio_rl *rl, int tc, u32 *hw_id)
+{
+       if (tc >= rl->num_tc)
+               return -EINVAL;
+
+       *hw_id = rl->leaves_id[tc];
+       return 0;
+}
index 757682b7c0e04233656c13de58402a97323d08d9..b7558907ba20865159d9bea21432edb3320924bc 100644 (file)
@@ -12,6 +12,7 @@ struct mlx5e_priv;
 struct mlx5e_channels;
 struct mlx5e_channel;
 
+int mlx5e_qos_bytes_rate_check(struct mlx5_core_dev *mdev, u64 nbytes);
 int mlx5e_qos_max_leaf_nodes(struct mlx5_core_dev *mdev);
 int mlx5e_qos_cur_leaf_nodes(struct mlx5e_priv *priv);
 
@@ -41,4 +42,12 @@ int mlx5e_htb_leaf_del_last(struct mlx5e_priv *priv, u16 classid, bool force,
 int mlx5e_htb_node_modify(struct mlx5e_priv *priv, u16 classid, u64 rate, u64 ceil,
                          struct netlink_ext_ack *extack);
 
+/* MQPRIO TX rate limit */
+struct mlx5e_mqprio_rl;
+struct mlx5e_mqprio_rl *mlx5e_mqprio_rl_alloc(void);
+void mlx5e_mqprio_rl_free(struct mlx5e_mqprio_rl *rl);
+int mlx5e_mqprio_rl_init(struct mlx5e_mqprio_rl *rl, struct mlx5_core_dev *mdev, u8 num_tc,
+                        u64 max_rate[]);
+void mlx5e_mqprio_rl_cleanup(struct mlx5e_mqprio_rl *rl);
+int mlx5e_mqprio_rl_get_node_hw_id(struct mlx5e_mqprio_rl *rl, int tc, u32 *hw_id);
 #endif
index 9f6685d3b17b4e872cc9f063e399fda927c2f0cb..e81e5505207cdbf9529c89774b8e00c793f4faba 100644 (file)
@@ -1705,6 +1705,36 @@ static void mlx5e_close_tx_cqs(struct mlx5e_channel *c)
                mlx5e_close_cq(&c->sq[tc].cq);
 }
 
+static int mlx5e_mqprio_txq_to_tc(struct netdev_tc_txq *tc_to_txq, unsigned int txq)
+{
+       int tc;
+
+       for (tc = 0; tc < TC_MAX_QUEUE; tc++)
+               if (txq - tc_to_txq[tc].offset < tc_to_txq[tc].count)
+                       return tc;
+
+       WARN(1, "Unexpected TCs configuration. No match found for txq %u", txq);
+       return -ENOENT;
+}
+
+static int mlx5e_txq_get_qos_node_hw_id(struct mlx5e_params *params, int txq_ix,
+                                       u32 *hw_id)
+{
+       int tc;
+
+       if (params->mqprio.mode != TC_MQPRIO_MODE_CHANNEL ||
+           !params->mqprio.channel.rl) {
+               *hw_id = 0;
+               return 0;
+       }
+
+       tc = mlx5e_mqprio_txq_to_tc(params->mqprio.tc_to_txq, txq_ix);
+       if (tc < 0)
+               return tc;
+
+       return mlx5e_mqprio_rl_get_node_hw_id(params->mqprio.channel.rl, tc, hw_id);
+}
+
 static int mlx5e_open_sqs(struct mlx5e_channel *c,
                          struct mlx5e_params *params,
                          struct mlx5e_channel_param *cparam)
@@ -1713,9 +1743,15 @@ static int mlx5e_open_sqs(struct mlx5e_channel *c,
 
        for (tc = 0; tc < mlx5e_get_dcb_num_tc(params); tc++) {
                int txq_ix = c->ix + tc * params->num_channels;
+               u32 qos_queue_group_id;
+
+               err = mlx5e_txq_get_qos_node_hw_id(params, txq_ix, &qos_queue_group_id);
+               if (err)
+                       goto err_close_sqs;
 
                err = mlx5e_open_txqsq(c, c->priv->tisn[c->lag_port][tc], txq_ix,
-                                      params, &cparam->txq_sq, &c->sq[tc], tc, 0,
+                                      params, &cparam->txq_sq, &c->sq[tc], tc,
+                                      qos_queue_group_id,
                                       &c->priv->channel_stats[c->ix].sq[tc]);
                if (err)
                        goto err_close_sqs;
@@ -2341,6 +2377,13 @@ static int mlx5e_update_netdev_queues(struct mlx5e_priv *priv)
                netdev_warn(netdev, "netif_set_real_num_rx_queues failed, %d\n", err);
                goto err_txqs;
        }
+       if (priv->mqprio_rl != priv->channels.params.mqprio.channel.rl) {
+               if (priv->mqprio_rl) {
+                       mlx5e_mqprio_rl_cleanup(priv->mqprio_rl);
+                       mlx5e_mqprio_rl_free(priv->mqprio_rl);
+               }
+               priv->mqprio_rl = priv->channels.params.mqprio.channel.rl;
+       }
 
        return 0;
 
@@ -2902,15 +2945,18 @@ static void mlx5e_params_mqprio_dcb_set(struct mlx5e_params *params, u8 num_tc)
 {
        params->mqprio.mode = TC_MQPRIO_MODE_DCB;
        params->mqprio.num_tc = num_tc;
+       params->mqprio.channel.rl = NULL;
        mlx5e_mqprio_build_default_tc_to_txq(params->mqprio.tc_to_txq, num_tc,
                                             params->num_channels);
 }
 
 static void mlx5e_params_mqprio_channel_set(struct mlx5e_params *params,
-                                           struct tc_mqprio_qopt *qopt)
+                                           struct tc_mqprio_qopt *qopt,
+                                           struct mlx5e_mqprio_rl *rl)
 {
        params->mqprio.mode = TC_MQPRIO_MODE_CHANNEL;
        params->mqprio.num_tc = qopt->num_tc;
+       params->mqprio.channel.rl = rl;
        mlx5e_mqprio_build_tc_to_txq(params->mqprio.tc_to_txq, qopt);
 }
 
@@ -2970,9 +3016,13 @@ static int mlx5e_mqprio_channel_validate(struct mlx5e_priv *priv,
                        netdev_err(netdev, "Min tx rate is not supported\n");
                        return -EINVAL;
                }
+
                if (mqprio->max_rate[i]) {
-                       netdev_err(netdev, "Max tx rate is not supported\n");
-                       return -EINVAL;
+                       int err;
+
+                       err = mlx5e_qos_bytes_rate_check(priv->mdev, mqprio->max_rate[i]);
+                       if (err)
+                               return err;
                }
 
                if (mqprio->qopt.offset[i] != agg_count) {
@@ -2991,11 +3041,22 @@ static int mlx5e_mqprio_channel_validate(struct mlx5e_priv *priv,
        return 0;
 }
 
+static bool mlx5e_mqprio_rate_limit(struct tc_mqprio_qopt_offload *mqprio)
+{
+       int tc;
+
+       for (tc = 0; tc < mqprio->qopt.num_tc; tc++)
+               if (mqprio->max_rate[tc])
+                       return true;
+       return false;
+}
+
 static int mlx5e_setup_tc_mqprio_channel(struct mlx5e_priv *priv,
                                         struct tc_mqprio_qopt_offload *mqprio)
 {
        mlx5e_fp_preactivate preactivate;
        struct mlx5e_params new_params;
+       struct mlx5e_mqprio_rl *rl;
        bool nch_changed;
        int err;
 
@@ -3003,13 +3064,32 @@ static int mlx5e_setup_tc_mqprio_channel(struct mlx5e_priv *priv,
        if (err)
                return err;
 
+       rl = NULL;
+       if (mlx5e_mqprio_rate_limit(mqprio)) {
+               rl = mlx5e_mqprio_rl_alloc();
+               if (!rl)
+                       return -ENOMEM;
+               err = mlx5e_mqprio_rl_init(rl, priv->mdev, mqprio->qopt.num_tc,
+                                          mqprio->max_rate);
+               if (err) {
+                       mlx5e_mqprio_rl_free(rl);
+                       return err;
+               }
+       }
+
        new_params = priv->channels.params;
-       mlx5e_params_mqprio_channel_set(&new_params, &mqprio->qopt);
+       mlx5e_params_mqprio_channel_set(&new_params, &mqprio->qopt, rl);
 
        nch_changed = mlx5e_get_dcb_num_tc(&priv->channels.params) > 1;
        preactivate = nch_changed ? mlx5e_num_channels_changed_ctx :
                mlx5e_update_netdev_queues_ctx;
-       return mlx5e_safe_switch_params(priv, &new_params, preactivate, NULL, true);
+       err = mlx5e_safe_switch_params(priv, &new_params, preactivate, NULL, true);
+       if (err && rl) {
+               mlx5e_mqprio_rl_cleanup(rl);
+               mlx5e_mqprio_rl_free(rl);
+       }
+
+       return err;
 }
 
 static int mlx5e_setup_tc_mqprio(struct mlx5e_priv *priv,
@@ -4809,6 +4889,11 @@ void mlx5e_priv_cleanup(struct mlx5e_priv *priv)
                kfree(priv->htb.qos_sq_stats[i]);
        kvfree(priv->htb.qos_sq_stats);
 
+       if (priv->mqprio_rl) {
+               mlx5e_mqprio_rl_cleanup(priv->mqprio_rl);
+               mlx5e_mqprio_rl_free(priv->mqprio_rl);
+       }
+
        memset(priv, 0, sizeof(*priv));
 }