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
	
	 Jakub Kicinski
						Jakub Kicinski