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

Commit 9766e97a authored by David Ahern's avatar David Ahern Committed by David S. Miller
Browse files

net: rocker: Add support for retrieving port level statistics



Add support for retrieving port level statistics from device.
Hook is added for ethtool's stats functionality. For example,

$ ethtool -S eth3
NIC statistics:
     rx_packets: 12
     rx_bytes: 2790
     rx_dropped: 0
     rx_errors: 0
     tx_packets: 8
     tx_bytes: 728
     tx_dropped: 0
     tx_errors: 0

Signed-off-by: default avatarDavid Ahern <dsahern@gmail.com>
Acked-by: default avatarScott Feldman <sfeldma@gmail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent fe3ef616
Loading
Loading
Loading
Loading
+134 −0
Original line number Diff line number Diff line
@@ -3833,11 +3833,145 @@ static void rocker_port_get_drvinfo(struct net_device *dev,
	strlcpy(drvinfo->version, UTS_RELEASE, sizeof(drvinfo->version));
}

static struct rocker_port_stats {
	char str[ETH_GSTRING_LEN];
	int type;
} rocker_port_stats[] = {
	{ "rx_packets", ROCKER_TLV_CMD_PORT_STATS_RX_PKTS,    },
	{ "rx_bytes",   ROCKER_TLV_CMD_PORT_STATS_RX_BYTES,   },
	{ "rx_dropped", ROCKER_TLV_CMD_PORT_STATS_RX_DROPPED, },
	{ "rx_errors",  ROCKER_TLV_CMD_PORT_STATS_RX_ERRORS,  },

	{ "tx_packets", ROCKER_TLV_CMD_PORT_STATS_TX_PKTS,    },
	{ "tx_bytes",   ROCKER_TLV_CMD_PORT_STATS_TX_BYTES,   },
	{ "tx_dropped", ROCKER_TLV_CMD_PORT_STATS_TX_DROPPED, },
	{ "tx_errors",  ROCKER_TLV_CMD_PORT_STATS_TX_ERRORS,  },
};

#define ROCKER_PORT_STATS_LEN  ARRAY_SIZE(rocker_port_stats)

static void rocker_port_get_strings(struct net_device *netdev, u32 stringset,
				    u8 *data)
{
	u8 *p = data;
	int i;

	switch (stringset) {
	case ETH_SS_STATS:
		for (i = 0; i < ARRAY_SIZE(rocker_port_stats); i++) {
			memcpy(p, rocker_port_stats[i].str, ETH_GSTRING_LEN);
			p += ETH_GSTRING_LEN;
		}
		break;
	}
}

static int
rocker_cmd_get_port_stats_prep(struct rocker *rocker,
			       struct rocker_port *rocker_port,
			       struct rocker_desc_info *desc_info,
			       void *priv)
{
	struct rocker_tlv *cmd_stats;

	if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_CMD_TYPE,
			       ROCKER_TLV_CMD_TYPE_GET_PORT_STATS))
		return -EMSGSIZE;

	cmd_stats = rocker_tlv_nest_start(desc_info, ROCKER_TLV_CMD_INFO);
	if (!cmd_stats)
		return -EMSGSIZE;

	if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_CMD_PORT_STATS_LPORT,
			       rocker_port->lport))
		return -EMSGSIZE;

	rocker_tlv_nest_end(desc_info, cmd_stats);

	return 0;
}

static int
rocker_cmd_get_port_stats_ethtool_proc(struct rocker *rocker,
				       struct rocker_port *rocker_port,
				       struct rocker_desc_info *desc_info,
				       void *priv)
{
	struct rocker_tlv *attrs[ROCKER_TLV_CMD_MAX + 1];
	struct rocker_tlv *stats_attrs[ROCKER_TLV_CMD_PORT_STATS_MAX + 1];
	struct rocker_tlv *pattr;
	u32 lport;
	u64 *data = priv;
	int i;

	rocker_tlv_parse_desc(attrs, ROCKER_TLV_CMD_MAX, desc_info);

	if (!attrs[ROCKER_TLV_CMD_INFO])
		return -EIO;

	rocker_tlv_parse_nested(stats_attrs, ROCKER_TLV_CMD_PORT_STATS_MAX,
				attrs[ROCKER_TLV_CMD_INFO]);

	if (!stats_attrs[ROCKER_TLV_CMD_PORT_STATS_LPORT])
		return -EIO;

	lport = rocker_tlv_get_u32(stats_attrs[ROCKER_TLV_CMD_PORT_STATS_LPORT]);
	if (lport != rocker_port->lport)
		return -EIO;

	for (i = 0; i < ARRAY_SIZE(rocker_port_stats); i++) {
		pattr = stats_attrs[rocker_port_stats[i].type];
		if (!pattr)
			continue;

		data[i] = rocker_tlv_get_u64(pattr);
	}

	return 0;
}

static int rocker_cmd_get_port_stats_ethtool(struct rocker_port *rocker_port,
					     void *priv)
{
	return rocker_cmd_exec(rocker_port->rocker, rocker_port,
			       rocker_cmd_get_port_stats_prep, NULL,
			       rocker_cmd_get_port_stats_ethtool_proc,
			       priv, false);
}

static void rocker_port_get_stats(struct net_device *dev,
				  struct ethtool_stats *stats, u64 *data)
{
	struct rocker_port *rocker_port = netdev_priv(dev);

	if (rocker_cmd_get_port_stats_ethtool(rocker_port, data) != 0) {
		int i;

		for (i = 0; i < ARRAY_SIZE(rocker_port_stats); ++i)
			data[i] = 0;
	}

	return;
}

static int rocker_port_get_sset_count(struct net_device *netdev, int sset)
{
	switch (sset) {
	case ETH_SS_STATS:
		return ROCKER_PORT_STATS_LEN;
	default:
		return -EOPNOTSUPP;
	}
}

static const struct ethtool_ops rocker_port_ethtool_ops = {
	.get_settings		= rocker_port_get_settings,
	.set_settings		= rocker_port_set_settings,
	.get_drvinfo		= rocker_port_get_drvinfo,
	.get_link		= ethtool_op_get_link,
	.get_strings		= rocker_port_get_strings,
	.get_ethtool_stats	= rocker_port_get_stats,
	.get_sset_count		= rocker_port_get_sset_count,
};

/*****************
+21 −0
Original line number Diff line number Diff line
@@ -127,6 +127,9 @@ enum {
	ROCKER_TLV_CMD_TYPE_OF_DPA_GROUP_DEL,
	ROCKER_TLV_CMD_TYPE_OF_DPA_GROUP_GET_STATS,

	ROCKER_TLV_CMD_TYPE_CLEAR_PORT_STATS,
	ROCKER_TLV_CMD_TYPE_GET_PORT_STATS,

	__ROCKER_TLV_CMD_TYPE_MAX,
	ROCKER_TLV_CMD_TYPE_MAX = __ROCKER_TLV_CMD_TYPE_MAX - 1,
};
@@ -146,6 +149,24 @@ enum {
			__ROCKER_TLV_CMD_PORT_SETTINGS_MAX - 1,
};

enum {
	ROCKER_TLV_CMD_PORT_STATS_UNSPEC,
	ROCKER_TLV_CMD_PORT_STATS_LPORT,            /* u32 */

	ROCKER_TLV_CMD_PORT_STATS_RX_PKTS,          /* u64 */
	ROCKER_TLV_CMD_PORT_STATS_RX_BYTES,         /* u64 */
	ROCKER_TLV_CMD_PORT_STATS_RX_DROPPED,       /* u64 */
	ROCKER_TLV_CMD_PORT_STATS_RX_ERRORS,        /* u64 */

	ROCKER_TLV_CMD_PORT_STATS_TX_PKTS,          /* u64 */
	ROCKER_TLV_CMD_PORT_STATS_TX_BYTES,         /* u64 */
	ROCKER_TLV_CMD_PORT_STATS_TX_DROPPED,       /* u64 */
	ROCKER_TLV_CMD_PORT_STATS_TX_ERRORS,        /* u64 */

	__ROCKER_TLV_CMD_PORT_STATS_MAX,
	ROCKER_TLV_CMD_PORT_STATS_MAX = __ROCKER_TLV_CMD_PORT_STATS_MAX - 1,
};

enum rocker_port_mode {
	ROCKER_PORT_MODE_OF_DPA,
};