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_COMPACT_BITSETS``   use compact format bitsets in reply
 | 
				
			||||||
  ``ETHTOOL_FLAG_OMIT_REPLY``        omit optional reply (_SET and _ACT)
 | 
					  ``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,
 | 
					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_AUTONEG``            bool    pause autonegotiation
 | 
				
			||||||
  ``ETHTOOL_A_PAUSE_RX``                 bool    receive pause frames
 | 
					  ``ETHTOOL_A_PAUSE_RX``                 bool    receive pause frames
 | 
				
			||||||
  ``ETHTOOL_A_PAUSE_TX``                 bool    transmit 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
 | 
					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_PKT_RATE_LOW | ETHTOOL_COALESCE_PKT_RATE_HIGH | \
 | 
				
			||||||
	 ETHTOOL_COALESCE_RATE_SAMPLE_INTERVAL)
 | 
						 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
 | 
					 * struct ethtool_ops - optional netdev operations
 | 
				
			||||||
 * @supported_coalesce_params: supported types of interrupt coalescing.
 | 
					 * @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.
 | 
					 *	Returns a negative error code or zero.
 | 
				
			||||||
 * @get_ringparam: Report ring sizes
 | 
					 * @get_ringparam: Report ring sizes
 | 
				
			||||||
 * @set_ringparam: Set ring sizes.  Returns a negative error code or zero.
 | 
					 * @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
 | 
					 * @get_pauseparam: Report pause parameters
 | 
				
			||||||
 * @set_pauseparam: Set pause parameters.  Returns a negative error code
 | 
					 * @set_pauseparam: Set pause parameters.  Returns a negative error code
 | 
				
			||||||
 *	or zero.
 | 
					 *	or zero.
 | 
				
			||||||
| 
						 | 
					@ -418,6 +442,8 @@ struct ethtool_ops {
 | 
				
			||||||
				 struct ethtool_ringparam *);
 | 
									 struct ethtool_ringparam *);
 | 
				
			||||||
	int	(*set_ringparam)(struct net_device *,
 | 
						int	(*set_ringparam)(struct net_device *,
 | 
				
			||||||
				 struct ethtool_ringparam *);
 | 
									 struct ethtool_ringparam *);
 | 
				
			||||||
 | 
						void	(*get_pause_stats)(struct net_device *dev,
 | 
				
			||||||
 | 
									   struct ethtool_pause_stats *pause_stats);
 | 
				
			||||||
	void	(*get_pauseparam)(struct net_device *,
 | 
						void	(*get_pauseparam)(struct net_device *,
 | 
				
			||||||
				  struct ethtool_pauseparam*);
 | 
									  struct ethtool_pauseparam*);
 | 
				
			||||||
	int	(*set_pauseparam)(struct net_device *,
 | 
						int	(*set_pauseparam)(struct net_device *,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -91,9 +91,12 @@ enum {
 | 
				
			||||||
#define ETHTOOL_FLAG_COMPACT_BITSETS	(1 << 0)
 | 
					#define ETHTOOL_FLAG_COMPACT_BITSETS	(1 << 0)
 | 
				
			||||||
/* provide optional reply for SET or ACT requests */
 | 
					/* provide optional reply for SET or ACT requests */
 | 
				
			||||||
#define ETHTOOL_FLAG_OMIT_REPLY	(1 << 1)
 | 
					#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 | \
 | 
					#define ETHTOOL_FLAG_ALL (ETHTOOL_FLAG_COMPACT_BITSETS | \
 | 
				
			||||||
			  ETHTOOL_FLAG_OMIT_REPLY)
 | 
								  ETHTOOL_FLAG_OMIT_REPLY | \
 | 
				
			||||||
 | 
								  ETHTOOL_FLAG_STATS)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
enum {
 | 
					enum {
 | 
				
			||||||
	ETHTOOL_A_HEADER_UNSPEC,
 | 
						ETHTOOL_A_HEADER_UNSPEC,
 | 
				
			||||||
| 
						 | 
					@ -376,12 +379,25 @@ enum {
 | 
				
			||||||
	ETHTOOL_A_PAUSE_AUTONEG,			/* u8 */
 | 
						ETHTOOL_A_PAUSE_AUTONEG,			/* u8 */
 | 
				
			||||||
	ETHTOOL_A_PAUSE_RX,				/* u8 */
 | 
						ETHTOOL_A_PAUSE_RX,				/* u8 */
 | 
				
			||||||
	ETHTOOL_A_PAUSE_TX,				/* u8 */
 | 
						ETHTOOL_A_PAUSE_TX,				/* u8 */
 | 
				
			||||||
 | 
						ETHTOOL_A_PAUSE_STATS,				/* nest - _PAUSE_STAT_* */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* add new constants above here */
 | 
						/* add new constants above here */
 | 
				
			||||||
	__ETHTOOL_A_PAUSE_CNT,
 | 
						__ETHTOOL_A_PAUSE_CNT,
 | 
				
			||||||
	ETHTOOL_A_PAUSE_MAX = (__ETHTOOL_A_PAUSE_CNT - 1)
 | 
						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 */
 | 
					/* EEE */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
enum {
 | 
					enum {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -10,6 +10,7 @@ struct pause_req_info {
 | 
				
			||||||
struct pause_reply_data {
 | 
					struct pause_reply_data {
 | 
				
			||||||
	struct ethnl_reply_data		base;
 | 
						struct ethnl_reply_data		base;
 | 
				
			||||||
	struct ethtool_pauseparam	pauseparam;
 | 
						struct ethtool_pauseparam	pauseparam;
 | 
				
			||||||
 | 
						struct ethtool_pause_stats	pausestat;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define PAUSE_REPDATA(__reply_base) \
 | 
					#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_AUTONEG]		= { .type = NLA_REJECT },
 | 
				
			||||||
	[ETHTOOL_A_PAUSE_RX]			= { .type = NLA_REJECT },
 | 
						[ETHTOOL_A_PAUSE_RX]			= { .type = NLA_REJECT },
 | 
				
			||||||
	[ETHTOOL_A_PAUSE_TX]			= { .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,
 | 
					static int pause_prepare_data(const struct ethnl_req_info *req_base,
 | 
				
			||||||
			      struct ethnl_reply_data *reply_base,
 | 
								      struct ethnl_reply_data *reply_base,
 | 
				
			||||||
			      struct genl_info *info)
 | 
								      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)
 | 
						if (!dev->ethtool_ops->get_pauseparam)
 | 
				
			||||||
		return -EOPNOTSUPP;
 | 
							return -EOPNOTSUPP;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ret = ethnl_ops_begin(dev);
 | 
						ret = ethnl_ops_begin(dev);
 | 
				
			||||||
	if (ret < 0)
 | 
						if (ret < 0)
 | 
				
			||||||
		return ret;
 | 
							return ret;
 | 
				
			||||||
	dev->ethtool_ops->get_pauseparam(dev, &data->pauseparam);
 | 
						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);
 | 
						ethnl_ops_complete(dev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return 0;
 | 
						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,
 | 
					static int pause_reply_size(const struct ethnl_req_info *req_base,
 | 
				
			||||||
			    const struct ethnl_reply_data *reply_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_RX */
 | 
				
			||||||
		nla_total_size(sizeof(u8));	/* _PAUSE_TX */
 | 
							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,
 | 
					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))
 | 
						    nla_put_u8(skb, ETHTOOL_A_PAUSE_TX, !!pauseparam->tx_pause))
 | 
				
			||||||
		return -EMSGSIZE;
 | 
							return -EMSGSIZE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (req_base->flags & ETHTOOL_FLAG_STATS &&
 | 
				
			||||||
 | 
						    pause_put_stats(skb, &data->pausestat))
 | 
				
			||||||
 | 
							return -EMSGSIZE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -89,6 +149,7 @@ pause_set_policy[ETHTOOL_A_PAUSE_MAX + 1] = {
 | 
				
			||||||
	[ETHTOOL_A_PAUSE_AUTONEG]		= { .type = NLA_U8 },
 | 
						[ETHTOOL_A_PAUSE_AUTONEG]		= { .type = NLA_U8 },
 | 
				
			||||||
	[ETHTOOL_A_PAUSE_RX]			= { .type = NLA_U8 },
 | 
						[ETHTOOL_A_PAUSE_RX]			= { .type = NLA_U8 },
 | 
				
			||||||
	[ETHTOOL_A_PAUSE_TX]			= { .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)
 | 
					int ethnl_set_pause(struct sk_buff *skb, struct genl_info *info)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue