Skip to content

Commit 9a27a33

Browse files
kuba-moodavem330
authored andcommitted
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 <[email protected]> Reviewed-by: Saeed Mahameed <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 0f9ad4e commit 9a27a33

File tree

4 files changed

+116
-2
lines changed

4 files changed

+116
-2
lines changed

Documentation/networking/ethtool-netlink.rst

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ the flags may not apply to requests. Recognized flags are:
6868
================================= ===================================
6969
``ETHTOOL_FLAG_COMPACT_BITSETS`` use compact format bitsets in reply
7070
``ETHTOOL_FLAG_OMIT_REPLY`` omit optional reply (_SET and _ACT)
71+
``ETHTOOL_FLAG_STATS`` include optional device statistics
7172
================================= ===================================
7273

7374
New request flags should follow the general idea that if the flag is not set,
@@ -989,8 +990,18 @@ Kernel response contents:
989990
``ETHTOOL_A_PAUSE_AUTONEG`` bool pause autonegotiation
990991
``ETHTOOL_A_PAUSE_RX`` bool receive pause frames
991992
``ETHTOOL_A_PAUSE_TX`` bool transmit pause frames
993+
``ETHTOOL_A_PAUSE_STATS`` nested pause statistics
992994
===================================== ====== ==========================
993995

996+
``ETHTOOL_A_PAUSE_STATS`` are reported if ``ETHTOOL_FLAG_STATS`` was set
997+
in ``ETHTOOL_A_HEADER_FLAGS``.
998+
It will be empty if driver did not report any statistics. Drivers fill in
999+
the statistics in the following structure:
1000+
1001+
.. kernel-doc:: include/linux/ethtool.h
1002+
:identifiers: ethtool_pause_stats
1003+
1004+
Each member has a corresponding attribute defined.
9941005

9951006
PAUSE_SET
9961007
============

include/linux/ethtool.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,27 @@ bool ethtool_convert_link_mode_to_legacy_u32(u32 *legacy_u32,
241241
ETHTOOL_COALESCE_PKT_RATE_LOW | ETHTOOL_COALESCE_PKT_RATE_HIGH | \
242242
ETHTOOL_COALESCE_RATE_SAMPLE_INTERVAL)
243243

244+
#define ETHTOOL_STAT_NOT_SET (~0ULL)
245+
246+
/**
247+
* struct ethtool_pause_stats - statistics for IEEE 802.3x pause frames
248+
* @tx_pause_frames: transmitted pause frame count. Reported to user space
249+
* as %ETHTOOL_A_PAUSE_STAT_TX_FRAMES.
250+
*
251+
* Equivalent to `30.3.4.2 aPAUSEMACCtrlFramesTransmitted`
252+
* from the standard.
253+
*
254+
* @rx_pause_frames: received pause frame count. Reported to user space
255+
* as %ETHTOOL_A_PAUSE_STAT_RX_FRAMES. Equivalent to:
256+
*
257+
* Equivalent to `30.3.4.3 aPAUSEMACCtrlFramesReceived`
258+
* from the standard.
259+
*/
260+
struct ethtool_pause_stats {
261+
u64 tx_pause_frames;
262+
u64 rx_pause_frames;
263+
};
264+
244265
/**
245266
* struct ethtool_ops - optional netdev operations
246267
* @supported_coalesce_params: supported types of interrupt coalescing.
@@ -282,6 +303,9 @@ bool ethtool_convert_link_mode_to_legacy_u32(u32 *legacy_u32,
282303
* Returns a negative error code or zero.
283304
* @get_ringparam: Report ring sizes
284305
* @set_ringparam: Set ring sizes. Returns a negative error code or zero.
306+
* @get_pause_stats: Report pause frame statistics. Drivers must not zero
307+
* statistics which they don't report. The stats structure is initialized
308+
* to ETHTOOL_STAT_NOT_SET indicating driver does not report statistics.
285309
* @get_pauseparam: Report pause parameters
286310
* @set_pauseparam: Set pause parameters. Returns a negative error code
287311
* or zero.
@@ -418,6 +442,8 @@ struct ethtool_ops {
418442
struct ethtool_ringparam *);
419443
int (*set_ringparam)(struct net_device *,
420444
struct ethtool_ringparam *);
445+
void (*get_pause_stats)(struct net_device *dev,
446+
struct ethtool_pause_stats *pause_stats);
421447
void (*get_pauseparam)(struct net_device *,
422448
struct ethtool_pauseparam*);
423449
int (*set_pauseparam)(struct net_device *,

include/uapi/linux/ethtool_netlink.h

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,9 +91,12 @@ enum {
9191
#define ETHTOOL_FLAG_COMPACT_BITSETS (1 << 0)
9292
/* provide optional reply for SET or ACT requests */
9393
#define ETHTOOL_FLAG_OMIT_REPLY (1 << 1)
94+
/* request statistics, if supported by the driver */
95+
#define ETHTOOL_FLAG_STATS (1 << 2)
9496

9597
#define ETHTOOL_FLAG_ALL (ETHTOOL_FLAG_COMPACT_BITSETS | \
96-
ETHTOOL_FLAG_OMIT_REPLY)
98+
ETHTOOL_FLAG_OMIT_REPLY | \
99+
ETHTOOL_FLAG_STATS)
97100

98101
enum {
99102
ETHTOOL_A_HEADER_UNSPEC,
@@ -376,12 +379,25 @@ enum {
376379
ETHTOOL_A_PAUSE_AUTONEG, /* u8 */
377380
ETHTOOL_A_PAUSE_RX, /* u8 */
378381
ETHTOOL_A_PAUSE_TX, /* u8 */
382+
ETHTOOL_A_PAUSE_STATS, /* nest - _PAUSE_STAT_* */
379383

380384
/* add new constants above here */
381385
__ETHTOOL_A_PAUSE_CNT,
382386
ETHTOOL_A_PAUSE_MAX = (__ETHTOOL_A_PAUSE_CNT - 1)
383387
};
384388

389+
enum {
390+
ETHTOOL_A_PAUSE_STAT_UNSPEC,
391+
ETHTOOL_A_PAUSE_STAT_PAD,
392+
393+
ETHTOOL_A_PAUSE_STAT_TX_FRAMES,
394+
ETHTOOL_A_PAUSE_STAT_RX_FRAMES,
395+
396+
/* add new constants above here */
397+
__ETHTOOL_A_PAUSE_STAT_CNT,
398+
ETHTOOL_A_PAUSE_STAT_MAX = (__ETHTOOL_A_PAUSE_STAT_CNT - 1)
399+
};
400+
385401
/* EEE */
386402

387403
enum {

net/ethtool/pause.c

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ struct pause_req_info {
1010
struct pause_reply_data {
1111
struct ethnl_reply_data base;
1212
struct ethtool_pauseparam pauseparam;
13+
struct ethtool_pause_stats pausestat;
1314
};
1415

1516
#define PAUSE_REPDATA(__reply_base) \
@@ -22,8 +23,15 @@ pause_get_policy[ETHTOOL_A_PAUSE_MAX + 1] = {
2223
[ETHTOOL_A_PAUSE_AUTONEG] = { .type = NLA_REJECT },
2324
[ETHTOOL_A_PAUSE_RX] = { .type = NLA_REJECT },
2425
[ETHTOOL_A_PAUSE_TX] = { .type = NLA_REJECT },
26+
[ETHTOOL_A_PAUSE_STATS] = { .type = NLA_REJECT },
2527
};
2628

29+
static void ethtool_stats_init(u64 *stats, unsigned int n)
30+
{
31+
while (n--)
32+
stats[n] = ETHTOOL_STAT_NOT_SET;
33+
}
34+
2735
static int pause_prepare_data(const struct ethnl_req_info *req_base,
2836
struct ethnl_reply_data *reply_base,
2937
struct genl_info *info)
@@ -34,10 +42,17 @@ static int pause_prepare_data(const struct ethnl_req_info *req_base,
3442

3543
if (!dev->ethtool_ops->get_pauseparam)
3644
return -EOPNOTSUPP;
45+
3746
ret = ethnl_ops_begin(dev);
3847
if (ret < 0)
3948
return ret;
4049
dev->ethtool_ops->get_pauseparam(dev, &data->pauseparam);
50+
if (req_base->flags & ETHTOOL_FLAG_STATS &&
51+
dev->ethtool_ops->get_pause_stats) {
52+
ethtool_stats_init((u64 *)&data->pausestat,
53+
sizeof(data->pausestat) / 8);
54+
dev->ethtool_ops->get_pause_stats(dev, &data->pausestat);
55+
}
4156
ethnl_ops_complete(dev);
4257

4358
return 0;
@@ -46,9 +61,50 @@ static int pause_prepare_data(const struct ethnl_req_info *req_base,
4661
static int pause_reply_size(const struct ethnl_req_info *req_base,
4762
const struct ethnl_reply_data *reply_base)
4863
{
49-
return nla_total_size(sizeof(u8)) + /* _PAUSE_AUTONEG */
64+
int n = nla_total_size(sizeof(u8)) + /* _PAUSE_AUTONEG */
5065
nla_total_size(sizeof(u8)) + /* _PAUSE_RX */
5166
nla_total_size(sizeof(u8)); /* _PAUSE_TX */
67+
68+
if (req_base->flags & ETHTOOL_FLAG_STATS)
69+
n += nla_total_size(0) + /* _PAUSE_STATS */
70+
nla_total_size_64bit(sizeof(u64)) *
71+
(ETHTOOL_A_PAUSE_STAT_MAX - 2);
72+
return n;
73+
}
74+
75+
static int ethtool_put_stat(struct sk_buff *skb, u64 val, u16 attrtype,
76+
u16 padtype)
77+
{
78+
if (val == ETHTOOL_STAT_NOT_SET)
79+
return 0;
80+
if (nla_put_u64_64bit(skb, attrtype, val, padtype))
81+
return -EMSGSIZE;
82+
83+
return 0;
84+
}
85+
86+
static int pause_put_stats(struct sk_buff *skb,
87+
const struct ethtool_pause_stats *pause_stats)
88+
{
89+
const u16 pad = ETHTOOL_A_PAUSE_STAT_PAD;
90+
struct nlattr *nest;
91+
92+
nest = nla_nest_start(skb, ETHTOOL_A_PAUSE_STATS);
93+
if (!nest)
94+
return -EMSGSIZE;
95+
96+
if (ethtool_put_stat(skb, pause_stats->tx_pause_frames,
97+
ETHTOOL_A_PAUSE_STAT_TX_FRAMES, pad) ||
98+
ethtool_put_stat(skb, pause_stats->rx_pause_frames,
99+
ETHTOOL_A_PAUSE_STAT_RX_FRAMES, pad))
100+
goto err_cancel;
101+
102+
nla_nest_end(skb, nest);
103+
return 0;
104+
105+
err_cancel:
106+
nla_nest_cancel(skb, nest);
107+
return -EMSGSIZE;
52108
}
53109

54110
static int pause_fill_reply(struct sk_buff *skb,
@@ -63,6 +119,10 @@ static int pause_fill_reply(struct sk_buff *skb,
63119
nla_put_u8(skb, ETHTOOL_A_PAUSE_TX, !!pauseparam->tx_pause))
64120
return -EMSGSIZE;
65121

122+
if (req_base->flags & ETHTOOL_FLAG_STATS &&
123+
pause_put_stats(skb, &data->pausestat))
124+
return -EMSGSIZE;
125+
66126
return 0;
67127
}
68128

@@ -89,6 +149,7 @@ pause_set_policy[ETHTOOL_A_PAUSE_MAX + 1] = {
89149
[ETHTOOL_A_PAUSE_AUTONEG] = { .type = NLA_U8 },
90150
[ETHTOOL_A_PAUSE_RX] = { .type = NLA_U8 },
91151
[ETHTOOL_A_PAUSE_TX] = { .type = NLA_U8 },
152+
[ETHTOOL_A_PAUSE_STATS] = { .type = NLA_REJECT },
92153
};
93154

94155
int ethnl_set_pause(struct sk_buff *skb, struct genl_info *info)

0 commit comments

Comments
 (0)