net/mlx5e: Introduce net device priv flags infrastructure
[sfrench/cifs-2.6.git] / drivers / net / ethernet / mellanox / mlx5 / core / en_ethtool.c
index 3476ab844634bd5f4b46f74a484f24c2c62d228d..f8bbc2b44fb3c08235d834f481ca6e22f4704f8e 100644 (file)
@@ -165,60 +165,128 @@ static const struct {
        },
 };
 
+static unsigned long mlx5e_query_pfc_combined(struct mlx5e_priv *priv)
+{
+       struct mlx5_core_dev *mdev = priv->mdev;
+       u8 pfc_en_tx;
+       u8 pfc_en_rx;
+       int err;
+
+       err = mlx5_query_port_pfc(mdev, &pfc_en_tx, &pfc_en_rx);
+
+       return err ? 0 : pfc_en_tx | pfc_en_rx;
+}
+
+#define MLX5E_NUM_Q_CNTRS(priv) (NUM_Q_COUNTERS * (!!priv->q_counter))
+#define MLX5E_NUM_RQ_STATS(priv) \
+       (NUM_RQ_STATS * priv->params.num_channels * \
+        test_bit(MLX5E_STATE_OPENED, &priv->state))
+#define MLX5E_NUM_SQ_STATS(priv) \
+       (NUM_SQ_STATS * priv->params.num_channels * priv->params.num_tc * \
+        test_bit(MLX5E_STATE_OPENED, &priv->state))
+#define MLX5E_NUM_PFC_COUNTERS(priv) hweight8(mlx5e_query_pfc_combined(priv))
+
 static int mlx5e_get_sset_count(struct net_device *dev, int sset)
 {
        struct mlx5e_priv *priv = netdev_priv(dev);
 
        switch (sset) {
        case ETH_SS_STATS:
-               return NUM_VPORT_COUNTERS + NUM_PPORT_COUNTERS +
-                      priv->params.num_channels * NUM_RQ_STATS +
-                      priv->params.num_channels * priv->params.num_tc *
-                                                  NUM_SQ_STATS;
+               return NUM_SW_COUNTERS +
+                      MLX5E_NUM_Q_CNTRS(priv) +
+                      NUM_VPORT_COUNTERS + NUM_PPORT_COUNTERS +
+                      MLX5E_NUM_RQ_STATS(priv) +
+                      MLX5E_NUM_SQ_STATS(priv) +
+                      MLX5E_NUM_PFC_COUNTERS(priv);
+       case ETH_SS_PRIV_FLAGS:
+               return ARRAY_SIZE(mlx5e_priv_flags);
        /* fallthrough */
        default:
                return -EOPNOTSUPP;
        }
 }
 
+static void mlx5e_fill_stats_strings(struct mlx5e_priv *priv, uint8_t *data)
+{
+       int i, j, tc, prio, idx = 0;
+       unsigned long pfc_combined;
+
+       /* SW counters */
+       for (i = 0; i < NUM_SW_COUNTERS; i++)
+               strcpy(data + (idx++) * ETH_GSTRING_LEN, sw_stats_desc[i].name);
+
+       /* Q counters */
+       for (i = 0; i < MLX5E_NUM_Q_CNTRS(priv); i++)
+               strcpy(data + (idx++) * ETH_GSTRING_LEN, q_stats_desc[i].name);
+
+       /* VPORT counters */
+       for (i = 0; i < NUM_VPORT_COUNTERS; i++)
+               strcpy(data + (idx++) * ETH_GSTRING_LEN,
+                      vport_stats_desc[i].name);
+
+       /* PPORT counters */
+       for (i = 0; i < NUM_PPORT_802_3_COUNTERS; i++)
+               strcpy(data + (idx++) * ETH_GSTRING_LEN,
+                      pport_802_3_stats_desc[i].name);
+
+       for (i = 0; i < NUM_PPORT_2863_COUNTERS; i++)
+               strcpy(data + (idx++) * ETH_GSTRING_LEN,
+                      pport_2863_stats_desc[i].name);
+
+       for (i = 0; i < NUM_PPORT_2819_COUNTERS; i++)
+               strcpy(data + (idx++) * ETH_GSTRING_LEN,
+                      pport_2819_stats_desc[i].name);
+
+       for (prio = 0; prio < NUM_PPORT_PRIO; prio++) {
+               for (i = 0; i < NUM_PPORT_PER_PRIO_TRAFFIC_COUNTERS; i++)
+                       sprintf(data + (idx++) * ETH_GSTRING_LEN, "prio%d_%s",
+                               prio,
+                               pport_per_prio_traffic_stats_desc[i].name);
+       }
+
+       pfc_combined = mlx5e_query_pfc_combined(priv);
+       for_each_set_bit(prio, &pfc_combined, NUM_PPORT_PRIO) {
+               for (i = 0; i < NUM_PPORT_PER_PRIO_PFC_COUNTERS; i++) {
+                       sprintf(data + (idx++) * ETH_GSTRING_LEN, "prio%d_%s",
+                               prio, pport_per_prio_pfc_stats_desc[i].name);
+               }
+       }
+
+       if (!test_bit(MLX5E_STATE_OPENED, &priv->state))
+               return;
+
+       /* per channel counters */
+       for (i = 0; i < priv->params.num_channels; i++)
+               for (j = 0; j < NUM_RQ_STATS; j++)
+                       sprintf(data + (idx++) * ETH_GSTRING_LEN, "rx%d_%s", i,
+                               rq_stats_desc[j].name);
+
+       for (tc = 0; tc < priv->params.num_tc; tc++)
+               for (i = 0; i < priv->params.num_channels; i++)
+                       for (j = 0; j < NUM_SQ_STATS; j++)
+                               sprintf(data + (idx++) * ETH_GSTRING_LEN,
+                                       "tx%d_%s",
+                                       priv->channeltc_to_txq_map[i][tc],
+                                       sq_stats_desc[j].name);
+}
+
 static void mlx5e_get_strings(struct net_device *dev,
                              uint32_t stringset, uint8_t *data)
 {
-       int i, j, tc, idx = 0;
        struct mlx5e_priv *priv = netdev_priv(dev);
+       int i;
 
        switch (stringset) {
        case ETH_SS_PRIV_FLAGS:
+               for (i = 0; i < ARRAY_SIZE(mlx5e_priv_flags); i++)
+                       strcpy(data + i * ETH_GSTRING_LEN, mlx5e_priv_flags[i]);
                break;
 
        case ETH_SS_TEST:
                break;
 
        case ETH_SS_STATS:
-               /* VPORT counters */
-               for (i = 0; i < NUM_VPORT_COUNTERS; i++)
-                       strcpy(data + (idx++) * ETH_GSTRING_LEN,
-                              vport_strings[i]);
-
-               /* PPORT counters */
-               for (i = 0; i < NUM_PPORT_COUNTERS; i++)
-                       strcpy(data + (idx++) * ETH_GSTRING_LEN,
-                              pport_strings[i]);
-
-               /* per channel counters */
-               for (i = 0; i < priv->params.num_channels; i++)
-                       for (j = 0; j < NUM_RQ_STATS; j++)
-                               sprintf(data + (idx++) * ETH_GSTRING_LEN,
-                                       "rx%d_%s", i, rq_stats_strings[j]);
-
-               for (tc = 0; tc < priv->params.num_tc; tc++)
-                       for (i = 0; i < priv->params.num_channels; i++)
-                               for (j = 0; j < NUM_SQ_STATS; j++)
-                                       sprintf(data +
-                                             (idx++) * ETH_GSTRING_LEN,
-                                             "tx%d_%s",
-                                             priv->channeltc_to_txq_map[i][tc],
-                                             sq_stats_strings[j]);
+               mlx5e_fill_stats_strings(priv, data);
                break;
        }
 }
@@ -227,7 +295,8 @@ static void mlx5e_get_ethtool_stats(struct net_device *dev,
                                    struct ethtool_stats *stats, u64 *data)
 {
        struct mlx5e_priv *priv = netdev_priv(dev);
-       int i, j, tc, idx = 0;
+       int i, j, tc, prio, idx = 0;
+       unsigned long pfc_combined;
 
        if (!data)
                return;
@@ -237,33 +306,68 @@ static void mlx5e_get_ethtool_stats(struct net_device *dev,
                mlx5e_update_stats(priv);
        mutex_unlock(&priv->state_lock);
 
+       for (i = 0; i < NUM_SW_COUNTERS; i++)
+               data[idx++] = MLX5E_READ_CTR64_CPU(&priv->stats.sw,
+                                                  sw_stats_desc, i);
+
+       for (i = 0; i < MLX5E_NUM_Q_CNTRS(priv); i++)
+               data[idx++] = MLX5E_READ_CTR32_CPU(&priv->stats.qcnt,
+                                                  q_stats_desc, i);
+
        for (i = 0; i < NUM_VPORT_COUNTERS; i++)
-               data[idx++] = ((u64 *)&priv->stats.vport)[i];
+               data[idx++] = MLX5E_READ_CTR64_BE(priv->stats.vport.query_vport_out,
+                                                 vport_stats_desc, i);
+
+       for (i = 0; i < NUM_PPORT_802_3_COUNTERS; i++)
+               data[idx++] = MLX5E_READ_CTR64_BE(&priv->stats.pport.IEEE_802_3_counters,
+                                                 pport_802_3_stats_desc, i);
+
+       for (i = 0; i < NUM_PPORT_2863_COUNTERS; i++)
+               data[idx++] = MLX5E_READ_CTR64_BE(&priv->stats.pport.RFC_2863_counters,
+                                                 pport_2863_stats_desc, i);
+
+       for (i = 0; i < NUM_PPORT_2819_COUNTERS; i++)
+               data[idx++] = MLX5E_READ_CTR64_BE(&priv->stats.pport.RFC_2819_counters,
+                                                 pport_2819_stats_desc, i);
+
+       for (prio = 0; prio < NUM_PPORT_PRIO; prio++) {
+               for (i = 0; i < NUM_PPORT_PER_PRIO_TRAFFIC_COUNTERS; i++)
+                       data[idx++] = MLX5E_READ_CTR64_BE(&priv->stats.pport.per_prio_counters[prio],
+                                                pport_per_prio_traffic_stats_desc, i);
+       }
+
+       pfc_combined = mlx5e_query_pfc_combined(priv);
+       for_each_set_bit(prio, &pfc_combined, NUM_PPORT_PRIO) {
+               for (i = 0; i < NUM_PPORT_PER_PRIO_PFC_COUNTERS; i++) {
+                       data[idx++] = MLX5E_READ_CTR64_BE(&priv->stats.pport.per_prio_counters[prio],
+                                                         pport_per_prio_pfc_stats_desc, i);
+               }
+       }
 
-       for (i = 0; i < NUM_PPORT_COUNTERS; i++)
-               data[idx++] = be64_to_cpu(((__be64 *)&priv->stats.pport)[i]);
+       if (!test_bit(MLX5E_STATE_OPENED, &priv->state))
+               return;
 
        /* per channel counters */
        for (i = 0; i < priv->params.num_channels; i++)
                for (j = 0; j < NUM_RQ_STATS; j++)
-                       data[idx++] = !test_bit(MLX5E_STATE_OPENED,
-                                               &priv->state) ? 0 :
-                                      ((u64 *)&priv->channel[i]->rq.stats)[j];
+                       data[idx++] =
+                              MLX5E_READ_CTR64_CPU(&priv->channel[i]->rq.stats,
+                                                   rq_stats_desc, j);
 
        for (tc = 0; tc < priv->params.num_tc; tc++)
                for (i = 0; i < priv->params.num_channels; i++)
                        for (j = 0; j < NUM_SQ_STATS; j++)
-                               data[idx++] = !test_bit(MLX5E_STATE_OPENED,
-                                                       &priv->state) ? 0 :
-                               ((u64 *)&priv->channel[i]->sq[tc].stats)[j];
+                               data[idx++] = MLX5E_READ_CTR64_CPU(&priv->channel[i]->sq[tc].stats,
+                                                                  sq_stats_desc, j);
 }
 
 static void mlx5e_get_ringparam(struct net_device *dev,
                                struct ethtool_ringparam *param)
 {
        struct mlx5e_priv *priv = netdev_priv(dev);
+       int rq_wq_type = priv->params.rq_wq_type;
 
-       param->rx_max_pending = 1 << MLX5E_PARAMS_MAXIMUM_LOG_RQ_SIZE;
+       param->rx_max_pending = 1 << mlx5_max_log_rq_size(rq_wq_type);
        param->tx_max_pending = 1 << MLX5E_PARAMS_MAXIMUM_LOG_SQ_SIZE;
        param->rx_pending     = 1 << priv->params.log_rq_size;
        param->tx_pending     = 1 << priv->params.log_sq_size;
@@ -274,6 +378,7 @@ static int mlx5e_set_ringparam(struct net_device *dev,
 {
        struct mlx5e_priv *priv = netdev_priv(dev);
        bool was_opened;
+       int rq_wq_type = priv->params.rq_wq_type;
        u16 min_rx_wqes;
        u8 log_rq_size;
        u8 log_sq_size;
@@ -289,16 +394,16 @@ static int mlx5e_set_ringparam(struct net_device *dev,
                            __func__);
                return -EINVAL;
        }
-       if (param->rx_pending < (1 << MLX5E_PARAMS_MINIMUM_LOG_RQ_SIZE)) {
+       if (param->rx_pending < (1 << mlx5_min_log_rq_size(rq_wq_type))) {
                netdev_info(dev, "%s: rx_pending (%d) < min (%d)\n",
                            __func__, param->rx_pending,
-                           1 << MLX5E_PARAMS_MINIMUM_LOG_RQ_SIZE);
+                           1 << mlx5_min_log_rq_size(rq_wq_type));
                return -EINVAL;
        }
-       if (param->rx_pending > (1 << MLX5E_PARAMS_MAXIMUM_LOG_RQ_SIZE)) {
+       if (param->rx_pending > (1 << mlx5_max_log_rq_size(rq_wq_type))) {
                netdev_info(dev, "%s: rx_pending (%d) > max (%d)\n",
                            __func__, param->rx_pending,
-                           1 << MLX5E_PARAMS_MAXIMUM_LOG_RQ_SIZE);
+                           1 << mlx5_max_log_rq_size(rq_wq_type));
                return -EINVAL;
        }
        if (param->tx_pending < (1 << MLX5E_PARAMS_MINIMUM_LOG_SQ_SIZE)) {
@@ -316,8 +421,7 @@ static int mlx5e_set_ringparam(struct net_device *dev,
 
        log_rq_size = order_base_2(param->rx_pending);
        log_sq_size = order_base_2(param->tx_pending);
-       min_rx_wqes = min_t(u16, param->rx_pending - 1,
-                           MLX5E_PARAMS_DEFAULT_MIN_RX_WQES);
+       min_rx_wqes = mlx5_min_rx_wqes(rq_wq_type, param->rx_pending);
 
        if (log_rq_size == priv->params.log_rq_size &&
            log_sq_size == priv->params.log_sq_size &&
@@ -357,6 +461,7 @@ static int mlx5e_set_channels(struct net_device *dev,
        struct mlx5e_priv *priv = netdev_priv(dev);
        int ncv = mlx5e_get_max_num_channels(priv->mdev);
        unsigned int count = ch->combined_count;
+       bool arfs_enabled;
        bool was_opened;
        int err = 0;
 
@@ -385,13 +490,27 @@ static int mlx5e_set_channels(struct net_device *dev,
        if (was_opened)
                mlx5e_close_locked(dev);
 
+       arfs_enabled = dev->features & NETIF_F_NTUPLE;
+       if (arfs_enabled)
+               mlx5e_arfs_disable(priv);
+
        priv->params.num_channels = count;
-       mlx5e_build_default_indir_rqt(priv->params.indirection_rqt,
+       mlx5e_build_default_indir_rqt(priv->mdev, priv->params.indirection_rqt,
                                      MLX5E_INDIR_RQT_SIZE, count);
 
        if (was_opened)
                err = mlx5e_open_locked(dev);
+       if (err)
+               goto out;
+
+       if (arfs_enabled) {
+               err = mlx5e_arfs_enable(priv);
+               if (err)
+                       netdev_err(dev, "%s: mlx5e_arfs_enable failed: %d\n",
+                                  __func__, err);
+       }
 
+out:
        mutex_unlock(&priv->state_lock);
 
        return err;
@@ -499,6 +618,25 @@ static u32 ptys2ethtool_supported_port(u32 eth_proto_cap)
        return 0;
 }
 
+int mlx5e_get_max_linkspeed(struct mlx5_core_dev *mdev, u32 *speed)
+{
+       u32 max_speed = 0;
+       u32 proto_cap;
+       int err;
+       int i;
+
+       err = mlx5_query_port_proto_cap(mdev, &proto_cap, MLX5_PTYS_EN);
+       if (err)
+               return err;
+
+       for (i = 0; i < MLX5E_LINK_MODES_NUMBER; ++i)
+               if (proto_cap & MLX5E_PROT_MASK(i))
+                       max_speed = max(max_speed, ptys2ethtool_table[i].speed);
+
+       *speed = max_speed;
+       return 0;
+}
+
 static void get_speed_duplex(struct net_device *netdev,
                             u32 eth_proto_oper,
                             struct ethtool_cmd *cmd)
@@ -727,9 +865,8 @@ static void mlx5e_modify_tirs_hash(struct mlx5e_priv *priv, void *in, int inlen)
        MLX5_SET(modify_tir_in, in, bitmask.hash, 1);
        mlx5e_build_tir_ctx_hash(tirc, priv);
 
-       for (i = 0; i < MLX5E_NUM_TT; i++)
-               if (IS_HASHING_TT(i))
-                       mlx5_core_modify_tir(mdev, priv->tirn[i], in, inlen);
+       for (i = 0; i < MLX5E_NUM_INDIR_TIRS; i++)
+               mlx5_core_modify_tir(mdev, priv->indir_tirn[i], in, inlen);
 }
 
 static int mlx5e_set_rxfh(struct net_device *dev, const u32 *indir,
@@ -751,9 +888,11 @@ static int mlx5e_set_rxfh(struct net_device *dev, const u32 *indir,
        mutex_lock(&priv->state_lock);
 
        if (indir) {
+               u32 rqtn = priv->indir_rqtn;
+
                memcpy(priv->params.indirection_rqt, indir,
                       sizeof(priv->params.indirection_rqt));
-               mlx5e_redirect_rqt(priv, MLX5E_INDIRECTION_RQT);
+               mlx5e_redirect_rqt(priv, rqtn, MLX5E_INDIR_RQT_SIZE, 0);
        }
 
        if (key)
@@ -1036,6 +1175,160 @@ static int mlx5e_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol)
        return mlx5_set_port_wol(mdev, mlx5_wol_mode);
 }
 
+static int mlx5e_set_phys_id(struct net_device *dev,
+                            enum ethtool_phys_id_state state)
+{
+       struct mlx5e_priv *priv = netdev_priv(dev);
+       struct mlx5_core_dev *mdev = priv->mdev;
+       u16 beacon_duration;
+
+       if (!MLX5_CAP_GEN(mdev, beacon_led))
+               return -EOPNOTSUPP;
+
+       switch (state) {
+       case ETHTOOL_ID_ACTIVE:
+               beacon_duration = MLX5_BEACON_DURATION_INF;
+               break;
+       case ETHTOOL_ID_INACTIVE:
+               beacon_duration = MLX5_BEACON_DURATION_OFF;
+               break;
+       default:
+               return -EOPNOTSUPP;
+       }
+
+       return mlx5_set_port_beacon(mdev, beacon_duration);
+}
+
+static int mlx5e_get_module_info(struct net_device *netdev,
+                                struct ethtool_modinfo *modinfo)
+{
+       struct mlx5e_priv *priv = netdev_priv(netdev);
+       struct mlx5_core_dev *dev = priv->mdev;
+       int size_read = 0;
+       u8 data[4];
+
+       size_read = mlx5_query_module_eeprom(dev, 0, 2, data);
+       if (size_read < 2)
+               return -EIO;
+
+       /* data[0] = identifier byte */
+       switch (data[0]) {
+       case MLX5_MODULE_ID_QSFP:
+               modinfo->type       = ETH_MODULE_SFF_8436;
+               modinfo->eeprom_len = ETH_MODULE_SFF_8436_LEN;
+               break;
+       case MLX5_MODULE_ID_QSFP_PLUS:
+       case MLX5_MODULE_ID_QSFP28:
+               /* data[1] = revision id */
+               if (data[0] == MLX5_MODULE_ID_QSFP28 || data[1] >= 0x3) {
+                       modinfo->type       = ETH_MODULE_SFF_8636;
+                       modinfo->eeprom_len = ETH_MODULE_SFF_8636_LEN;
+               } else {
+                       modinfo->type       = ETH_MODULE_SFF_8436;
+                       modinfo->eeprom_len = ETH_MODULE_SFF_8436_LEN;
+               }
+               break;
+       case MLX5_MODULE_ID_SFP:
+               modinfo->type       = ETH_MODULE_SFF_8472;
+               modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN;
+               break;
+       default:
+               netdev_err(priv->netdev, "%s: cable type not recognized:0x%x\n",
+                          __func__, data[0]);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int mlx5e_get_module_eeprom(struct net_device *netdev,
+                                  struct ethtool_eeprom *ee,
+                                  u8 *data)
+{
+       struct mlx5e_priv *priv = netdev_priv(netdev);
+       struct mlx5_core_dev *mdev = priv->mdev;
+       int offset = ee->offset;
+       int size_read;
+       int i = 0;
+
+       if (!ee->len)
+               return -EINVAL;
+
+       memset(data, 0, ee->len);
+
+       while (i < ee->len) {
+               size_read = mlx5_query_module_eeprom(mdev, offset, ee->len - i,
+                                                    data + i);
+
+               if (!size_read)
+                       /* Done reading */
+                       return 0;
+
+               if (size_read < 0) {
+                       netdev_err(priv->netdev, "%s: mlx5_query_eeprom failed:0x%x\n",
+                                  __func__, size_read);
+                       return 0;
+               }
+
+               i += size_read;
+               offset += size_read;
+       }
+
+       return 0;
+}
+
+typedef int (*mlx5e_pflag_handler)(struct net_device *netdev, bool enable);
+
+static int set_pflag_nop(struct net_device *netdev, bool enable)
+{
+       return 0;
+}
+
+static int mlx5e_handle_pflag(struct net_device *netdev,
+                             u32 wanted_flags,
+                             enum mlx5e_priv_flag flag,
+                             mlx5e_pflag_handler pflag_handler)
+{
+       struct mlx5e_priv *priv = netdev_priv(netdev);
+       bool enable = !!(wanted_flags & flag);
+       u32 changes = wanted_flags ^ priv->pflags;
+       int err;
+
+       if (!(changes & flag))
+               return 0;
+
+       err = pflag_handler(netdev, enable);
+       if (err) {
+               netdev_err(netdev, "%s private flag 0x%x failed err %d\n",
+                          enable ? "Enable" : "Disable", flag, err);
+               return err;
+       }
+
+       MLX5E_SET_PRIV_FLAG(priv, flag, enable);
+       return 0;
+}
+
+static int mlx5e_set_priv_flags(struct net_device *netdev, u32 pflags)
+{
+       struct mlx5e_priv *priv = netdev_priv(netdev);
+       int err;
+
+       mutex_lock(&priv->state_lock);
+
+       err = mlx5e_handle_pflag(netdev, pflags, MLX5E_PFLAG_NOP,
+                                set_pflag_nop);
+
+       mutex_unlock(&priv->state_lock);
+       return err ? -EINVAL : 0;
+}
+
+static u32 mlx5e_get_priv_flags(struct net_device *netdev)
+{
+       struct mlx5e_priv *priv = netdev_priv(netdev);
+
+       return priv->pflags;
+}
+
 const struct ethtool_ops mlx5e_ethtool_ops = {
        .get_drvinfo       = mlx5e_get_drvinfo,
        .get_link          = ethtool_op_get_link,
@@ -1060,6 +1353,11 @@ const struct ethtool_ops mlx5e_ethtool_ops = {
        .get_pauseparam    = mlx5e_get_pauseparam,
        .set_pauseparam    = mlx5e_set_pauseparam,
        .get_ts_info       = mlx5e_get_ts_info,
+       .set_phys_id       = mlx5e_set_phys_id,
        .get_wol           = mlx5e_get_wol,
        .set_wol           = mlx5e_set_wol,
+       .get_module_info   = mlx5e_get_module_info,
+       .get_module_eeprom = mlx5e_get_module_eeprom,
+       .get_priv_flags    = mlx5e_get_priv_flags,
+       .set_priv_flags    = mlx5e_set_priv_flags
 };