Commit 9a27a330 authored by Jakub Kicinski's avatar Jakub Kicinski Committed by David S. Miller

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: default avatarJakub Kicinski <kuba@kernel.org>
Reviewed-by: default avatarSaeed Mahameed <saeedm@nvidia.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 0f9ad4e7
...@@ -68,6 +68,7 @@ the flags may not apply to requests. Recognized flags are: ...@@ -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: ...@@ -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, ...@@ -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, ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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] = { ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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] = { ...@@ -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)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment