Merge branch 'mlxsw-per-prio-tc-counters'

Jiri Pirko says:

====================
mlxsw: Add per-{Prio,TC} counters

Ido says:

Add per-priority and per-tc counters, which are very useful for debugging
purposes and fine-tuning.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2016-07-20 14:53:57 -07:00
commit c0d661ca37
2 changed files with 171 additions and 9 deletions

View File

@ -2503,6 +2503,7 @@ MLXSW_ITEM32(reg, ppcnt, pnat, 0x00, 14, 2);
enum mlxsw_reg_ppcnt_grp {
MLXSW_REG_PPCNT_IEEE_8023_CNT = 0x0,
MLXSW_REG_PPCNT_PRIO_CNT = 0x10,
MLXSW_REG_PPCNT_TC_CNT = 0x11,
};
/* reg_ppcnt_grp
@ -2703,6 +2704,23 @@ MLXSW_ITEM64(reg, ppcnt, tx_pause_duration, 0x08 + 0x68, 0, 64);
*/
MLXSW_ITEM64(reg, ppcnt, tx_pause_transition, 0x08 + 0x70, 0, 64);
/* Ethernet Per Traffic Group Counters */
/* reg_ppcnt_tc_transmit_queue
* Contains the transmit queue depth in cells of traffic class
* selected by prio_tc and the port selected by local_port.
* The field cannot be cleared.
* Access: RO
*/
MLXSW_ITEM64(reg, ppcnt, tc_transmit_queue, 0x08 + 0x00, 0, 64);
/* reg_ppcnt_tc_no_buffer_discard_uc
* The number of unicast packets dropped due to lack of shared
* buffer resources.
* Access: RO
*/
MLXSW_ITEM64(reg, ppcnt, tc_no_buffer_discard_uc, 0x08 + 0x08, 0, 64);
static inline void mlxsw_reg_ppcnt_pack(char *payload, u8 local_port,
enum mlxsw_reg_ppcnt_grp grp,
u8 prio_tc)

View File

@ -899,7 +899,7 @@ struct mlxsw_sp_port_hw_stats {
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",
.getter = mlxsw_reg_ppcnt_a_frames_transmitted_ok_get,
@ -980,6 +980,90 @@ 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)
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)
static u64 mlxsw_reg_ppcnt_tc_transmit_queue_bytes_get(char *ppcnt_pl)
{
u64 transmit_queue = mlxsw_reg_ppcnt_tc_transmit_queue_get(ppcnt_pl);
return MLXSW_SP_CELLS_TO_BYTES(transmit_queue);
}
static struct mlxsw_sp_port_hw_stats mlxsw_sp_port_hw_tc_stats[] = {
{
.str = "tc_transmit_queue_tc",
.getter = mlxsw_reg_ppcnt_tc_transmit_queue_bytes_get,
},
{
.str = "tc_no_buffer_discard_uc_tc",
.getter = mlxsw_reg_ppcnt_tc_no_buffer_discard_uc_get,
},
};
#define MLXSW_SP_PORT_HW_TC_STATS_LEN ARRAY_SIZE(mlxsw_sp_port_hw_tc_stats)
#define MLXSW_SP_PORT_ETHTOOL_STATS_LEN (MLXSW_SP_PORT_HW_STATS_LEN + \
(MLXSW_SP_PORT_HW_PRIO_STATS_LEN + \
MLXSW_SP_PORT_HW_TC_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_tc_strings(u8 **p, int tc)
{
int i;
for (i = 0; i < MLXSW_SP_PORT_HW_TC_STATS_LEN; i++) {
snprintf(*p, ETH_GSTRING_LEN, "%s_%d",
mlxsw_sp_port_hw_tc_stats[i].str, tc);
*p += ETH_GSTRING_LEN;
}
}
static void mlxsw_sp_port_get_strings(struct net_device *dev,
u32 stringset, u8 *data)
{
@ -993,6 +1077,13 @@ static void mlxsw_sp_port_get_strings(struct net_device *dev,
ETH_GSTRING_LEN);
p += ETH_GSTRING_LEN;
}
for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++)
mlxsw_sp_port_get_prio_strings(&p, i);
for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++)
mlxsw_sp_port_get_tc_strings(&p, i);
break;
}
}
@ -1020,27 +1111,80 @@ static int mlxsw_sp_port_set_phys_id(struct net_device *dev,
return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mlcr), mlcr_pl);
}
static void mlxsw_sp_port_get_stats(struct net_device *dev,
struct ethtool_stats *stats, u64 *data)
static int
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;
case MLXSW_REG_PPCNT_TC_CNT:
*p_hw_stats = mlxsw_sp_port_hw_tc_stats;
*p_len = MLXSW_SP_PORT_HW_TC_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 *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
struct mlxsw_sp_port_hw_stats *hw_stats;
char ppcnt_pl[MLXSW_REG_PPCNT_LEN];
int i;
int i, len;
int err;
mlxsw_reg_ppcnt_pack(ppcnt_pl, mlxsw_sp_port->local_port,
MLXSW_REG_PPCNT_IEEE_8023_CNT, 0);
err = mlxsw_sp_get_hw_stats_by_group(&hw_stats, &len, grp);
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);
for (i = 0; i < MLXSW_SP_PORT_HW_STATS_LEN; i++)
data[i] = !err ? mlxsw_sp_port_hw_stats[i].getter(ppcnt_pl) : 0;
for (i = 0; i < len; i++)
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;
}
/* Per-TC Counters */
for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
__mlxsw_sp_port_get_stats(dev, MLXSW_REG_PPCNT_TC_CNT, i,
data, data_index);
data_index += MLXSW_SP_PORT_HW_TC_STATS_LEN;
}
}
static int mlxsw_sp_port_get_sset_count(struct net_device *dev, int sset)
{
switch (sset) {
case ETH_SS_STATS:
return MLXSW_SP_PORT_HW_STATS_LEN;
return MLXSW_SP_PORT_ETHTOOL_STATS_LEN;
default:
return -EOPNOTSUPP;
}