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:
Родитель
0f9ad4e759
Коммит
9a27a33027
|
@ -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)
|
||||||
|
|
Загрузка…
Ссылка в новой задаче