forked from mirrors/linux
		
	ethtool: add standard pause stats
Currently drivers have to report their pause frames statistics via ethtool -S, and there is a wide variety of names used for these statistics. Add the two statistics defined in IEEE 802.3x to the standard API. Create a new ethtool request header flag for including statistics in the response to GET commands. Always create the ETHTOOL_A_PAUSE_STATS nest in replies when flag is set. Testing if driver declares the op is not a reliable way of checking if any stats will actually be included and therefore we don't want to give the impression that presence of ETHTOOL_A_PAUSE_STATS indicates driver support. Note that this patch does not include PFC counters, which may fit better in dcbnl? But mostly I don't need them/have a setup to test them so I haven't looked deeply into exposing them :) v3: - add a helper for "uninitializing" stats, rather than a cryptic memset() (Andrew) Signed-off-by: Jakub Kicinski <kuba@kernel.org> Reviewed-by: Saeed Mahameed <saeedm@nvidia.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
		
							parent
							
								
									0f9ad4e759
								
							
						
					
					
						commit
						9a27a33027
					
				
					 4 changed files with 116 additions and 2 deletions
				
			
		| 
						 | 
				
			
			@ -68,6 +68,7 @@ the flags may not apply to requests. Recognized flags are:
 | 
			
		|||
  =================================  ===================================
 | 
			
		||||
  ``ETHTOOL_FLAG_COMPACT_BITSETS``   use compact format bitsets in reply
 | 
			
		||||
  ``ETHTOOL_FLAG_OMIT_REPLY``        omit optional reply (_SET and _ACT)
 | 
			
		||||
  ``ETHTOOL_FLAG_STATS``             include optional device statistics
 | 
			
		||||
  =================================  ===================================
 | 
			
		||||
 | 
			
		||||
New request flags should follow the general idea that if the flag is not set,
 | 
			
		||||
| 
						 | 
				
			
			@ -989,8 +990,18 @@ Kernel response contents:
 | 
			
		|||
  ``ETHTOOL_A_PAUSE_AUTONEG``            bool    pause autonegotiation
 | 
			
		||||
  ``ETHTOOL_A_PAUSE_RX``                 bool    receive pause frames
 | 
			
		||||
  ``ETHTOOL_A_PAUSE_TX``                 bool    transmit pause frames
 | 
			
		||||
  ``ETHTOOL_A_PAUSE_STATS``              nested  pause statistics
 | 
			
		||||
  =====================================  ======  ==========================
 | 
			
		||||
 | 
			
		||||
``ETHTOOL_A_PAUSE_STATS`` are reported if ``ETHTOOL_FLAG_STATS`` was set
 | 
			
		||||
in ``ETHTOOL_A_HEADER_FLAGS``.
 | 
			
		||||
It will be empty if driver did not report any statistics. Drivers fill in
 | 
			
		||||
the statistics in the following structure:
 | 
			
		||||
 | 
			
		||||
.. kernel-doc:: include/linux/ethtool.h
 | 
			
		||||
    :identifiers: ethtool_pause_stats
 | 
			
		||||
 | 
			
		||||
Each member has a corresponding attribute defined.
 | 
			
		||||
 | 
			
		||||
PAUSE_SET
 | 
			
		||||
============
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -241,6 +241,27 @@ bool ethtool_convert_link_mode_to_legacy_u32(u32 *legacy_u32,
 | 
			
		|||
	 ETHTOOL_COALESCE_PKT_RATE_LOW | ETHTOOL_COALESCE_PKT_RATE_HIGH | \
 | 
			
		||||
	 ETHTOOL_COALESCE_RATE_SAMPLE_INTERVAL)
 | 
			
		||||
 | 
			
		||||
#define ETHTOOL_STAT_NOT_SET	(~0ULL)
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * struct ethtool_pause_stats - statistics for IEEE 802.3x pause frames
 | 
			
		||||
 * @tx_pause_frames: transmitted pause frame count. Reported to user space
 | 
			
		||||
 *	as %ETHTOOL_A_PAUSE_STAT_TX_FRAMES.
 | 
			
		||||
 *
 | 
			
		||||
 *	Equivalent to `30.3.4.2 aPAUSEMACCtrlFramesTransmitted`
 | 
			
		||||
 *	from the standard.
 | 
			
		||||
 *
 | 
			
		||||
 * @rx_pause_frames: received pause frame count. Reported to user space
 | 
			
		||||
 *	as %ETHTOOL_A_PAUSE_STAT_RX_FRAMES. Equivalent to:
 | 
			
		||||
 *
 | 
			
		||||
 *	Equivalent to `30.3.4.3 aPAUSEMACCtrlFramesReceived`
 | 
			
		||||
 *	from the standard.
 | 
			
		||||
 */
 | 
			
		||||
struct ethtool_pause_stats {
 | 
			
		||||
	u64 tx_pause_frames;
 | 
			
		||||
	u64 rx_pause_frames;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * struct ethtool_ops - optional netdev operations
 | 
			
		||||
 * @supported_coalesce_params: supported types of interrupt coalescing.
 | 
			
		||||
| 
						 | 
				
			
			@ -282,6 +303,9 @@ bool ethtool_convert_link_mode_to_legacy_u32(u32 *legacy_u32,
 | 
			
		|||
 *	Returns a negative error code or zero.
 | 
			
		||||
 * @get_ringparam: Report ring sizes
 | 
			
		||||
 * @set_ringparam: Set ring sizes.  Returns a negative error code or zero.
 | 
			
		||||
 * @get_pause_stats: Report pause frame statistics. Drivers must not zero
 | 
			
		||||
 *	statistics which they don't report. The stats structure is initialized
 | 
			
		||||
 *	to ETHTOOL_STAT_NOT_SET indicating driver does not report statistics.
 | 
			
		||||
 * @get_pauseparam: Report pause parameters
 | 
			
		||||
 * @set_pauseparam: Set pause parameters.  Returns a negative error code
 | 
			
		||||
 *	or zero.
 | 
			
		||||
| 
						 | 
				
			
			@ -418,6 +442,8 @@ struct ethtool_ops {
 | 
			
		|||
				 struct ethtool_ringparam *);
 | 
			
		||||
	int	(*set_ringparam)(struct net_device *,
 | 
			
		||||
				 struct ethtool_ringparam *);
 | 
			
		||||
	void	(*get_pause_stats)(struct net_device *dev,
 | 
			
		||||
				   struct ethtool_pause_stats *pause_stats);
 | 
			
		||||
	void	(*get_pauseparam)(struct net_device *,
 | 
			
		||||
				  struct ethtool_pauseparam*);
 | 
			
		||||
	int	(*set_pauseparam)(struct net_device *,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -91,9 +91,12 @@ enum {
 | 
			
		|||
#define ETHTOOL_FLAG_COMPACT_BITSETS	(1 << 0)
 | 
			
		||||
/* provide optional reply for SET or ACT requests */
 | 
			
		||||
#define ETHTOOL_FLAG_OMIT_REPLY	(1 << 1)
 | 
			
		||||
/* request statistics, if supported by the driver */
 | 
			
		||||
#define ETHTOOL_FLAG_STATS		(1 << 2)
 | 
			
		||||
 | 
			
		||||
#define ETHTOOL_FLAG_ALL (ETHTOOL_FLAG_COMPACT_BITSETS | \
 | 
			
		||||
			  ETHTOOL_FLAG_OMIT_REPLY)
 | 
			
		||||
			  ETHTOOL_FLAG_OMIT_REPLY | \
 | 
			
		||||
			  ETHTOOL_FLAG_STATS)
 | 
			
		||||
 | 
			
		||||
enum {
 | 
			
		||||
	ETHTOOL_A_HEADER_UNSPEC,
 | 
			
		||||
| 
						 | 
				
			
			@ -376,12 +379,25 @@ enum {
 | 
			
		|||
	ETHTOOL_A_PAUSE_AUTONEG,			/* u8 */
 | 
			
		||||
	ETHTOOL_A_PAUSE_RX,				/* u8 */
 | 
			
		||||
	ETHTOOL_A_PAUSE_TX,				/* u8 */
 | 
			
		||||
	ETHTOOL_A_PAUSE_STATS,				/* nest - _PAUSE_STAT_* */
 | 
			
		||||
 | 
			
		||||
	/* add new constants above here */
 | 
			
		||||
	__ETHTOOL_A_PAUSE_CNT,
 | 
			
		||||
	ETHTOOL_A_PAUSE_MAX = (__ETHTOOL_A_PAUSE_CNT - 1)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum {
 | 
			
		||||
	ETHTOOL_A_PAUSE_STAT_UNSPEC,
 | 
			
		||||
	ETHTOOL_A_PAUSE_STAT_PAD,
 | 
			
		||||
 | 
			
		||||
	ETHTOOL_A_PAUSE_STAT_TX_FRAMES,
 | 
			
		||||
	ETHTOOL_A_PAUSE_STAT_RX_FRAMES,
 | 
			
		||||
 | 
			
		||||
	/* add new constants above here */
 | 
			
		||||
	__ETHTOOL_A_PAUSE_STAT_CNT,
 | 
			
		||||
	ETHTOOL_A_PAUSE_STAT_MAX = (__ETHTOOL_A_PAUSE_STAT_CNT - 1)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* EEE */
 | 
			
		||||
 | 
			
		||||
enum {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -10,6 +10,7 @@ struct pause_req_info {
 | 
			
		|||
struct pause_reply_data {
 | 
			
		||||
	struct ethnl_reply_data		base;
 | 
			
		||||
	struct ethtool_pauseparam	pauseparam;
 | 
			
		||||
	struct ethtool_pause_stats	pausestat;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define PAUSE_REPDATA(__reply_base) \
 | 
			
		||||
| 
						 | 
				
			
			@ -22,8 +23,15 @@ pause_get_policy[ETHTOOL_A_PAUSE_MAX + 1] = {
 | 
			
		|||
	[ETHTOOL_A_PAUSE_AUTONEG]		= { .type = NLA_REJECT },
 | 
			
		||||
	[ETHTOOL_A_PAUSE_RX]			= { .type = NLA_REJECT },
 | 
			
		||||
	[ETHTOOL_A_PAUSE_TX]			= { .type = NLA_REJECT },
 | 
			
		||||
	[ETHTOOL_A_PAUSE_STATS]			= { .type = NLA_REJECT },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void ethtool_stats_init(u64 *stats, unsigned int n)
 | 
			
		||||
{
 | 
			
		||||
	while (n--)
 | 
			
		||||
		stats[n] = ETHTOOL_STAT_NOT_SET;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int pause_prepare_data(const struct ethnl_req_info *req_base,
 | 
			
		||||
			      struct ethnl_reply_data *reply_base,
 | 
			
		||||
			      struct genl_info *info)
 | 
			
		||||
| 
						 | 
				
			
			@ -34,10 +42,17 @@ static int pause_prepare_data(const struct ethnl_req_info *req_base,
 | 
			
		|||
 | 
			
		||||
	if (!dev->ethtool_ops->get_pauseparam)
 | 
			
		||||
		return -EOPNOTSUPP;
 | 
			
		||||
 | 
			
		||||
	ret = ethnl_ops_begin(dev);
 | 
			
		||||
	if (ret < 0)
 | 
			
		||||
		return ret;
 | 
			
		||||
	dev->ethtool_ops->get_pauseparam(dev, &data->pauseparam);
 | 
			
		||||
	if (req_base->flags & ETHTOOL_FLAG_STATS &&
 | 
			
		||||
	    dev->ethtool_ops->get_pause_stats) {
 | 
			
		||||
		ethtool_stats_init((u64 *)&data->pausestat,
 | 
			
		||||
				   sizeof(data->pausestat) / 8);
 | 
			
		||||
		dev->ethtool_ops->get_pause_stats(dev, &data->pausestat);
 | 
			
		||||
	}
 | 
			
		||||
	ethnl_ops_complete(dev);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
| 
						 | 
				
			
			@ -46,9 +61,50 @@ static int pause_prepare_data(const struct ethnl_req_info *req_base,
 | 
			
		|||
static int pause_reply_size(const struct ethnl_req_info *req_base,
 | 
			
		||||
			    const struct ethnl_reply_data *reply_base)
 | 
			
		||||
{
 | 
			
		||||
	return nla_total_size(sizeof(u8)) +	/* _PAUSE_AUTONEG */
 | 
			
		||||
	int n = nla_total_size(sizeof(u8)) +	/* _PAUSE_AUTONEG */
 | 
			
		||||
		nla_total_size(sizeof(u8)) +	/* _PAUSE_RX */
 | 
			
		||||
		nla_total_size(sizeof(u8));	/* _PAUSE_TX */
 | 
			
		||||
 | 
			
		||||
	if (req_base->flags & ETHTOOL_FLAG_STATS)
 | 
			
		||||
		n += nla_total_size(0) +	/* _PAUSE_STATS */
 | 
			
		||||
			nla_total_size_64bit(sizeof(u64)) *
 | 
			
		||||
				(ETHTOOL_A_PAUSE_STAT_MAX - 2);
 | 
			
		||||
	return n;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int ethtool_put_stat(struct sk_buff *skb, u64 val, u16 attrtype,
 | 
			
		||||
			    u16 padtype)
 | 
			
		||||
{
 | 
			
		||||
	if (val == ETHTOOL_STAT_NOT_SET)
 | 
			
		||||
		return 0;
 | 
			
		||||
	if (nla_put_u64_64bit(skb, attrtype, val, padtype))
 | 
			
		||||
		return -EMSGSIZE;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int pause_put_stats(struct sk_buff *skb,
 | 
			
		||||
			   const struct ethtool_pause_stats *pause_stats)
 | 
			
		||||
{
 | 
			
		||||
	const u16 pad = ETHTOOL_A_PAUSE_STAT_PAD;
 | 
			
		||||
	struct nlattr *nest;
 | 
			
		||||
 | 
			
		||||
	nest = nla_nest_start(skb, ETHTOOL_A_PAUSE_STATS);
 | 
			
		||||
	if (!nest)
 | 
			
		||||
		return -EMSGSIZE;
 | 
			
		||||
 | 
			
		||||
	if (ethtool_put_stat(skb, pause_stats->tx_pause_frames,
 | 
			
		||||
			     ETHTOOL_A_PAUSE_STAT_TX_FRAMES, pad) ||
 | 
			
		||||
	    ethtool_put_stat(skb, pause_stats->rx_pause_frames,
 | 
			
		||||
			     ETHTOOL_A_PAUSE_STAT_RX_FRAMES, pad))
 | 
			
		||||
		goto err_cancel;
 | 
			
		||||
 | 
			
		||||
	nla_nest_end(skb, nest);
 | 
			
		||||
	return 0;
 | 
			
		||||
 | 
			
		||||
err_cancel:
 | 
			
		||||
	nla_nest_cancel(skb, nest);
 | 
			
		||||
	return -EMSGSIZE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int pause_fill_reply(struct sk_buff *skb,
 | 
			
		||||
| 
						 | 
				
			
			@ -63,6 +119,10 @@ static int pause_fill_reply(struct sk_buff *skb,
 | 
			
		|||
	    nla_put_u8(skb, ETHTOOL_A_PAUSE_TX, !!pauseparam->tx_pause))
 | 
			
		||||
		return -EMSGSIZE;
 | 
			
		||||
 | 
			
		||||
	if (req_base->flags & ETHTOOL_FLAG_STATS &&
 | 
			
		||||
	    pause_put_stats(skb, &data->pausestat))
 | 
			
		||||
		return -EMSGSIZE;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -89,6 +149,7 @@ pause_set_policy[ETHTOOL_A_PAUSE_MAX + 1] = {
 | 
			
		|||
	[ETHTOOL_A_PAUSE_AUTONEG]		= { .type = NLA_U8 },
 | 
			
		||||
	[ETHTOOL_A_PAUSE_RX]			= { .type = NLA_U8 },
 | 
			
		||||
	[ETHTOOL_A_PAUSE_TX]			= { .type = NLA_U8 },
 | 
			
		||||
	[ETHTOOL_A_PAUSE_STATS]			= { .type = NLA_REJECT },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
int ethnl_set_pause(struct sk_buff *skb, struct genl_info *info)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue