Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7ed674bc authored by Ido Schimmel's avatar Ido Schimmel Committed by David S. Miller
Browse files

mlxsw: spectrum: Expose per-priority counters via ethtool



Expose per-priority bytes / packets / PFC packets counters via ethtool.

These counters are very useful when debugging QoS functionality and
provide a better insight into the device's forwarding plane.

Signed-off-by: default avatarIdo Schimmel <idosch@mellanox.com>
Signed-off-by: default avatarJiri Pirko <jiri@mellanox.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 09714275
Loading
Loading
Loading
Loading
+107 −9
Original line number Original line Diff line number Diff line
@@ -899,7 +899,7 @@ struct mlxsw_sp_port_hw_stats {
	u64 (*getter)(char *payload);
	u64 (*getter)(char *payload);
};
};


static const struct mlxsw_sp_port_hw_stats mlxsw_sp_port_hw_stats[] = {
static struct mlxsw_sp_port_hw_stats mlxsw_sp_port_hw_stats[] = {
	{
	{
		.str = "a_frames_transmitted_ok",
		.str = "a_frames_transmitted_ok",
		.getter = mlxsw_reg_ppcnt_a_frames_transmitted_ok_get,
		.getter = mlxsw_reg_ppcnt_a_frames_transmitted_ok_get,
@@ -980,6 +980,58 @@ static const struct mlxsw_sp_port_hw_stats mlxsw_sp_port_hw_stats[] = {


#define MLXSW_SP_PORT_HW_STATS_LEN ARRAY_SIZE(mlxsw_sp_port_hw_stats)
#define MLXSW_SP_PORT_HW_STATS_LEN ARRAY_SIZE(mlxsw_sp_port_hw_stats)


static struct mlxsw_sp_port_hw_stats mlxsw_sp_port_hw_prio_stats[] = {
	{
		.str = "rx_octets_prio",
		.getter = mlxsw_reg_ppcnt_rx_octets_get,
	},
	{
		.str = "rx_frames_prio",
		.getter = mlxsw_reg_ppcnt_rx_frames_get,
	},
	{
		.str = "tx_octets_prio",
		.getter = mlxsw_reg_ppcnt_tx_octets_get,
	},
	{
		.str = "tx_frames_prio",
		.getter = mlxsw_reg_ppcnt_tx_frames_get,
	},
	{
		.str = "rx_pause_prio",
		.getter = mlxsw_reg_ppcnt_rx_pause_get,
	},
	{
		.str = "rx_pause_duration_prio",
		.getter = mlxsw_reg_ppcnt_rx_pause_duration_get,
	},
	{
		.str = "tx_pause_prio",
		.getter = mlxsw_reg_ppcnt_tx_pause_get,
	},
	{
		.str = "tx_pause_duration_prio",
		.getter = mlxsw_reg_ppcnt_tx_pause_duration_get,
	},
};

#define MLXSW_SP_PORT_HW_PRIO_STATS_LEN ARRAY_SIZE(mlxsw_sp_port_hw_prio_stats)

#define MLXSW_SP_PORT_ETHTOOL_STATS_LEN (MLXSW_SP_PORT_HW_STATS_LEN + \
					 MLXSW_SP_PORT_HW_PRIO_STATS_LEN * \
					 IEEE_8021QAZ_MAX_TCS)

static void mlxsw_sp_port_get_prio_strings(u8 **p, int prio)
{
	int i;

	for (i = 0; i < MLXSW_SP_PORT_HW_PRIO_STATS_LEN; i++) {
		snprintf(*p, ETH_GSTRING_LEN, "%s_%d",
			 mlxsw_sp_port_hw_prio_stats[i].str, prio);
		*p += ETH_GSTRING_LEN;
	}
}

static void mlxsw_sp_port_get_strings(struct net_device *dev,
static void mlxsw_sp_port_get_strings(struct net_device *dev,
				      u32 stringset, u8 *data)
				      u32 stringset, u8 *data)
{
{
@@ -993,6 +1045,10 @@ static void mlxsw_sp_port_get_strings(struct net_device *dev,
			       ETH_GSTRING_LEN);
			       ETH_GSTRING_LEN);
			p += ETH_GSTRING_LEN;
			p += ETH_GSTRING_LEN;
		}
		}

		for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++)
			mlxsw_sp_port_get_prio_strings(&p, i);

		break;
		break;
	}
	}
}
}
@@ -1020,27 +1076,69 @@ static int mlxsw_sp_port_set_phys_id(struct net_device *dev,
	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mlcr), mlcr_pl);
	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mlcr), mlcr_pl);
}
}


static void mlxsw_sp_port_get_stats(struct net_device *dev,
static int
				    struct ethtool_stats *stats, u64 *data)
mlxsw_sp_get_hw_stats_by_group(struct mlxsw_sp_port_hw_stats **p_hw_stats,
			       int *p_len, enum mlxsw_reg_ppcnt_grp grp)
{
	switch (grp) {
	case  MLXSW_REG_PPCNT_IEEE_8023_CNT:
		*p_hw_stats = mlxsw_sp_port_hw_stats;
		*p_len = MLXSW_SP_PORT_HW_STATS_LEN;
		break;
	case MLXSW_REG_PPCNT_PRIO_CNT:
		*p_hw_stats = mlxsw_sp_port_hw_prio_stats;
		*p_len = MLXSW_SP_PORT_HW_PRIO_STATS_LEN;
		break;
	default:
		WARN_ON(1);
		return -ENOTSUPP;
	}
	return 0;
}

static void __mlxsw_sp_port_get_stats(struct net_device *dev,
				      enum mlxsw_reg_ppcnt_grp grp, int prio,
				      u64 *data, int data_index)
{
{
	struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
	struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
	struct mlxsw_sp_port_hw_stats *hw_stats;
	char ppcnt_pl[MLXSW_REG_PPCNT_LEN];
	char ppcnt_pl[MLXSW_REG_PPCNT_LEN];
	int i;
	int i, len;
	int err;
	int err;


	mlxsw_reg_ppcnt_pack(ppcnt_pl, mlxsw_sp_port->local_port,
	err = mlxsw_sp_get_hw_stats_by_group(&hw_stats, &len, grp);
			     MLXSW_REG_PPCNT_IEEE_8023_CNT, 0);
	if (err)
		return;
	mlxsw_reg_ppcnt_pack(ppcnt_pl, mlxsw_sp_port->local_port, grp, prio);
	err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ppcnt), ppcnt_pl);
	err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ppcnt), ppcnt_pl);
	for (i = 0; i < MLXSW_SP_PORT_HW_STATS_LEN; i++)
	for (i = 0; i < len; i++)
		data[i] = !err ? mlxsw_sp_port_hw_stats[i].getter(ppcnt_pl) : 0;
		data[data_index + i] = !err ? hw_stats[i].getter(ppcnt_pl) : 0;
}

static void mlxsw_sp_port_get_stats(struct net_device *dev,
				    struct ethtool_stats *stats, u64 *data)
{
	int i, data_index = 0;

	/* IEEE 802.3 Counters */
	__mlxsw_sp_port_get_stats(dev, MLXSW_REG_PPCNT_IEEE_8023_CNT, 0,
				  data, data_index);
	data_index = MLXSW_SP_PORT_HW_STATS_LEN;

	/* Per-Priority Counters */
	for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
		__mlxsw_sp_port_get_stats(dev, MLXSW_REG_PPCNT_PRIO_CNT, i,
					  data, data_index);
		data_index += MLXSW_SP_PORT_HW_PRIO_STATS_LEN;
	}
}
}


static int mlxsw_sp_port_get_sset_count(struct net_device *dev, int sset)
static int mlxsw_sp_port_get_sset_count(struct net_device *dev, int sset)
{
{
	switch (sset) {
	switch (sset) {
	case ETH_SS_STATS:
	case ETH_SS_STATS:
		return MLXSW_SP_PORT_HW_STATS_LEN;
		return MLXSW_SP_PORT_ETHTOOL_STATS_LEN;
	default:
	default:
		return -EOPNOTSUPP;
		return -EOPNOTSUPP;
	}
	}