Re: [RFC ethtool 6/6] netlink: add support for standard stats

2021-04-17 Thread Jakub Kicinski
On Sat, 17 Apr 2021 22:22:57 +0300 Ido Schimmel wrote:
> > > So you will have something like:
> > > 
> > > ETHTOOL_A_STATS_GRP_HIST_BKT_UNITS_BYTES  
> > 
> > Histogram has two dimensions, what's the second dimension for bytes?
> > Time? Packet arrival?  
> 
> Not sure what you mean. Here you are counting how many Rx/Tx packets are
> between N to M bytes in length. I meant to add two attributes. One that
> tells user space that you are counting Rx/Tx packets and the second that
> N to M are in bytes.

Ah, these were not part of the same enum, I get it now.

I thought maybe bytes were trying to cater to the queue length use case
and I wasn't sure how that histogram is constructed.

> But given your comment below about this histogram probably being a one
> time thing, I think maybe staying with the current attributes is OK.
> There is no need to over-engineer it if we don't see ourselves adding
> new histograms.
> 
> Anyway, these histograms are under ETHTOOL_A_STATS_GRP that should give
> user space all the context about what is being counted.



Re: [RFC ethtool 6/6] netlink: add support for standard stats

2021-04-17 Thread Ido Schimmel
On Sat, Apr 17, 2021 at 11:47:28AM -0700, Jakub Kicinski wrote:
> On Sat, 17 Apr 2021 21:14:40 +0300 Ido Schimmel wrote:
> > > + if (!is_json_context()) {
> > > + fprintf(stdout, "rmon-%s-etherStatsPkts",
> > > + mnl_attr_get_type(hist) == ETHTOOL_A_STATS_GRP_HIST_RX ?
> > > + "rx" : "tx");
> > > +
> > > + if (low && hi) {
> > > + fprintf(stdout, "%uto%uOctets: %llu\n", low, hi, val);
> > > + } else if (hi) {
> > > + fprintf(stdout, "%uOctets: %llu\n", hi, val);
> > > + } else if (low) {
> > > + fprintf(stdout, "%utoMaxOctets: %llu\n", low, val);
> > > + } else {
> > > + fprintf(stderr, "invalid kernel response - bad 
> > > histogram entry bounds\n");
> > > + return 1;
> > > + }
> > > + } else {
> > > + open_json_object(NULL);
> > > + print_uint(PRINT_JSON, "low", NULL, low);
> > > + print_uint(PRINT_JSON, "high", NULL, hi);
> > > + print_u64(PRINT_JSON, "val", NULL, val);  
> > 
> > In the non-JSON output you distinguish between Rx/Tx, but it's missing
> > from the JSON output as can be seen in your example:
> > 
> > ```
> >"pktsNtoM": [
> >  {
> >"low": 0,
> >"high": 64,
> >"val": 1
> >  },
> >  {
> >"low": 128,
> >"high": 255,
> >"val": 1
> >  },
> >  {
> >"low": 1024,
> >"high": 0,
> >"val": 0
> >  }
> >]
> > ```
> > 
> > I see that mlxsw and mlx5 only support Rx, but it's going to be
> > confusing with bnxt that supports both Rx and Tx.
> 
> Good catch! I added Tx last minute (even though it's non standard).
> I'll split split into two arrays - "rx-pktsNtoM" and "tx-pktsNtoM",
> sounds good? Or we can add a layer: ["pktsNtoM"]["rx"] etc.

I'm fine with both, but I think the first form will be easier when
working with jq to extract Rx/Tx. It is also more inline with the
current nesting of the netlink attributes.

> 
> > Made me think about the structure of these attributes. Currently you
> > have:
> > 
> > ETHTOOL_A_STATS_GRP_HIST_RX
> > ETHTOOL_A_STATS_GRP_HIST_BKT_LOW
> > ETHTOOL_A_STATS_GRP_HIST_BKT_HI
> > ETHTOOL_A_STATS_GRP_HIST_VAL
> > 
> > ETHTOOL_A_STATS_GRP_HIST_TX
> > ETHTOOL_A_STATS_GRP_HIST_BKT_LOW
> > ETHTOOL_A_STATS_GRP_HIST_BKT_HI
> > ETHTOOL_A_STATS_GRP_HIST_VAL
> > 
> > Did you consider:
> > 
> > ETHTOOL_A_STATS_GRP_HIST
> > ETHTOOL_A_STATS_GRP_HIST_BKT_LOW
> > ETHTOOL_A_STATS_GRP_HIST_BKT_HI
> > ETHTOOL_A_STATS_GRP_HIST_VAL
> > ETHTOOL_A_STATS_GRP_HIST_BKT_UNITS
> > ETHTOOL_A_STATS_GRP_HIST_TYPE
> 
> I went back and forth on that. The reason I put the direction in the
> type is that normal statistics don't have an extra _TYPE or direction.
> 
> It will also be easier to break the stats out to arrays if they are
> typed on the outside, see below.
> 
> > So you will have something like:
> > 
> > ETHTOOL_A_STATS_GRP_HIST_BKT_UNITS_BYTES
> 
> Histogram has two dimensions, what's the second dimension for bytes?
> Time? Packet arrival?

Not sure what you mean. Here you are counting how many Rx/Tx packets are
between N to M bytes in length. I meant to add two attributes. One that
tells user space that you are counting Rx/Tx packets and the second that
N to M are in bytes.

But given your comment below about this histogram probably being a one
time thing, I think maybe staying with the current attributes is OK.
There is no need to over-engineer it if we don't see ourselves adding
new histograms.

Anyway, these histograms are under ETHTOOL_A_STATS_GRP that should give
user space all the context about what is being counted.

> 
> > ETHTOOL_A_STATS_GRP_HIST_VAL_TYPE_RX_PACKETS
> > ETHTOOL_A_STATS_GRP_HIST_VAL_TYPE_TX_PACKETS
> > 
> > And it will allow you to get rid of the special casing of the RMON stuff
> > below:
> > 
> > ```
> > if (id == ETHTOOL_STATS_RMON) {
> > open_json_array("pktsNtoM", "");
> > 
> > mnl_attr_for_each_nested(attr, grp) {
> > s = mnl_attr_get_type(attr);
> > if (s != ETHTOOL_A_STATS_GRP_HIST_RX &&
> > s != ETHTOOL_A_STATS_GRP_HIST_TX)
> > continue;
> > 
> > if (parse_rmon_hist(attr))
> > goto err_close_rmon;
> > }
> > close_json_array("");
> > }
> > ```
> 
> We can drop the if, but we still need a separate for() loop
> to be able to place those entries in a JSON array.
> 
> > I don't know how many histograms we are going to have as part of RFCs,
> > but at least mlxsw also supports histograms of the Tx queue depth and
> > latency. Not to be exposed by this interface, but shows the importance
> > of encoding the units.
> 
> TBH I hope we'll never use the hist for anything else. S

Re: [RFC ethtool 6/6] netlink: add support for standard stats

2021-04-17 Thread Jakub Kicinski
On Sat, 17 Apr 2021 21:14:40 +0300 Ido Schimmel wrote:
> > +   if (!is_json_context()) {
> > +   fprintf(stdout, "rmon-%s-etherStatsPkts",
> > +   mnl_attr_get_type(hist) == ETHTOOL_A_STATS_GRP_HIST_RX ?
> > +   "rx" : "tx");
> > +
> > +   if (low && hi) {
> > +   fprintf(stdout, "%uto%uOctets: %llu\n", low, hi, val);
> > +   } else if (hi) {
> > +   fprintf(stdout, "%uOctets: %llu\n", hi, val);
> > +   } else if (low) {
> > +   fprintf(stdout, "%utoMaxOctets: %llu\n", low, val);
> > +   } else {
> > +   fprintf(stderr, "invalid kernel response - bad 
> > histogram entry bounds\n");
> > +   return 1;
> > +   }
> > +   } else {
> > +   open_json_object(NULL);
> > +   print_uint(PRINT_JSON, "low", NULL, low);
> > +   print_uint(PRINT_JSON, "high", NULL, hi);
> > +   print_u64(PRINT_JSON, "val", NULL, val);  
> 
> In the non-JSON output you distinguish between Rx/Tx, but it's missing
> from the JSON output as can be seen in your example:
> 
> ```
>"pktsNtoM": [
>  {
>"low": 0,
>"high": 64,
>"val": 1
>  },
>  {
>"low": 128,
>"high": 255,
>"val": 1
>  },
>  {
>"low": 1024,
>"high": 0,
>"val": 0
>  }
>]
> ```
> 
> I see that mlxsw and mlx5 only support Rx, but it's going to be
> confusing with bnxt that supports both Rx and Tx.

Good catch! I added Tx last minute (even though it's non standard).
I'll split split into two arrays - "rx-pktsNtoM" and "tx-pktsNtoM",
sounds good? Or we can add a layer: ["pktsNtoM"]["rx"] etc.

> Made me think about the structure of these attributes. Currently you
> have:
> 
> ETHTOOL_A_STATS_GRP_HIST_RX
>   ETHTOOL_A_STATS_GRP_HIST_BKT_LOW
>   ETHTOOL_A_STATS_GRP_HIST_BKT_HI
>   ETHTOOL_A_STATS_GRP_HIST_VAL
> 
> ETHTOOL_A_STATS_GRP_HIST_TX
>   ETHTOOL_A_STATS_GRP_HIST_BKT_LOW
>   ETHTOOL_A_STATS_GRP_HIST_BKT_HI
>   ETHTOOL_A_STATS_GRP_HIST_VAL
> 
> Did you consider:
> 
> ETHTOOL_A_STATS_GRP_HIST
>   ETHTOOL_A_STATS_GRP_HIST_BKT_LOW
>   ETHTOOL_A_STATS_GRP_HIST_BKT_HI
>   ETHTOOL_A_STATS_GRP_HIST_VAL
>   ETHTOOL_A_STATS_GRP_HIST_BKT_UNITS
>   ETHTOOL_A_STATS_GRP_HIST_TYPE

I went back and forth on that. The reason I put the direction in the
type is that normal statistics don't have an extra _TYPE or direction.

It will also be easier to break the stats out to arrays if they are
typed on the outside, see below.

> So you will have something like:
> 
> ETHTOOL_A_STATS_GRP_HIST_BKT_UNITS_BYTES

Histogram has two dimensions, what's the second dimension for bytes?
Time? Packet arrival?

> ETHTOOL_A_STATS_GRP_HIST_VAL_TYPE_RX_PACKETS
> ETHTOOL_A_STATS_GRP_HIST_VAL_TYPE_TX_PACKETS
> 
> And it will allow you to get rid of the special casing of the RMON stuff
> below:
> 
> ```
>   if (id == ETHTOOL_STATS_RMON) {
>   open_json_array("pktsNtoM", "");
> 
>   mnl_attr_for_each_nested(attr, grp) {
>   s = mnl_attr_get_type(attr);
>   if (s != ETHTOOL_A_STATS_GRP_HIST_RX &&
>   s != ETHTOOL_A_STATS_GRP_HIST_TX)
>   continue;
> 
>   if (parse_rmon_hist(attr))
>   goto err_close_rmon;
>   }
>   close_json_array("");
>   }
> ```

We can drop the if, but we still need a separate for() loop
to be able to place those entries in a JSON array.

> I don't know how many histograms we are going to have as part of RFCs,
> but at least mlxsw also supports histograms of the Tx queue depth and
> latency. Not to be exposed by this interface, but shows the importance
> of encoding the units.

TBH I hope we'll never use the hist for anything else. Sadly the
bucketing of various drivers is really different (at least 6
variants). But the overarching goal is a common interface for common
port stats.


Re: [RFC ethtool 6/6] netlink: add support for standard stats

2021-04-17 Thread Ido Schimmel
On Fri, Apr 16, 2021 at 09:02:52AM -0700, Jakub Kicinski wrote:
>  # ethtool -S eth0 --groups eth-phy eth-mac rmon
> Stats for eth0:
> eth-phy-SymbolErrorDuringCarrier: 1
> eth-mac-FramesTransmittedOK: 1
> eth-mac-FrameTooLongErrors: 1
> rmon-etherStatsUndersizePkts: 1
> rmon-etherStatsJabbers: 1
> rmon-etherStatsPkts64Octets: 1
> rmon-etherStatsPkts128to255Octets: 1
> rmon-etherStatsPkts1024toMaxOctets: 0
> 
>  # ethtool --json -S eth0 --groups eth-phy eth-mac rmon | jq
>  [
>   {
> "ifname": "eth0",
> "eth-phy": {
>   "SymbolErrorDuringCarrier": 1
> },
> "eth-mac": {
>   "FramesTransmittedOK": 1,
>   "FrameTooLongErrors": 0
> },
> "rmon": {
>   "etherStatsUndersizePkts": 1,
>   "etherStatsJabbers": 0,
>   "pktsNtoM": [
> {
>   "low": 0,
>   "high": 64,
>   "val": 1
> },
> {
>   "low": 128,
>   "high": 255,
>   "val": 1
> },
> {
>   "low": 1024,
>   "high": 0,
>   "val": 0
> }
>   ]
> }
>   }
>  ]
> 
>  # ethtool --json -S eth0 --groups eth-phy eth-mac rmon | \
>  jq '.[].rmon.pktsNtoM | map(select(.low >= 128)) | map(.val) | add'
>  1
> 
> Signed-off-by: Jakub Kicinski 
> ---
>  Makefile.am|   1 +
>  ethtool.8.in   |  11 ++
>  ethtool.c  |   5 +-
>  netlink/desc-ethtool.c |  39 +++
>  netlink/extapi.h   |   4 +
>  netlink/stats.c| 242 +
>  6 files changed, 301 insertions(+), 1 deletion(-)
>  create mode 100644 netlink/stats.c
> 
> diff --git a/Makefile.am b/Makefile.am
> index f643a24af97a..75c245653cda 100644
> --- a/Makefile.am
> +++ b/Makefile.am
> @@ -36,6 +36,7 @@ ethtool_SOURCES += \
> netlink/features.c netlink/privflags.c netlink/rings.c \
> netlink/channels.c netlink/coalesce.c netlink/pause.c \
> netlink/eee.c netlink/tsinfo.c netlink/fec.c \
> +   netlink/stats.c \
> netlink/desc-ethtool.c netlink/desc-genlctrl.c \
> netlink/desc-rtnl.c netlink/cable_test.c netlink/tunnels.c \
> uapi/linux/ethtool_netlink.h \
> diff --git a/ethtool.8.in b/ethtool.8.in
> index ba4e245cdc61..0ca11595158a 100644
> --- a/ethtool.8.in
> +++ b/ethtool.8.in
> @@ -240,6 +240,12 @@ ethtool \- query or control network driver and hardware 
> settings
>  .HP
>  .B ethtool \-S|\-\-statistics
>  .I devname
> +.RB [\fB--groups
> +.RB [\fBeth\-phy\fP]
> +.RB [\fBeth\-mac\fP]
> +.RB [\fBeth\-ctrl\fP]
> +.RN [\fBrmon\fP]
> +.RB ]
>  .HP
>  .B ethtool \-\-phy\-statistics
>  .I devname
> @@ -653,6 +659,11 @@ auto-negotiation is enabled.
>  .B \-S \-\-statistics
>  Queries the specified network device for NIC- and driver-specific
>  statistics.
> +.RS 4
> +.TP
> +.B \fB--groups [\fBeth\-phy\fP] [\fBeth\-mac\fP] [\fBeth\-ctrl\fP] 
> [\fBrmon\fP]
> +Request standard device statistics of a specific group.
> +.RE
>  .TP
>  .B \-\-phy\-statistics
>  Queries the specified network device for PHY specific statistics.
> diff --git a/ethtool.c b/ethtool.c
> index b07fd9292d77..1b5690f424c8 100644
> --- a/ethtool.c
> +++ b/ethtool.c
> @@ -5760,7 +5760,10 @@ static const struct option args[] = {
>   {
>   .opts   = "-S|--statistics",
>   .func   = do_gnicstats,
> - .help   = "Show adapter statistics"
> + .nlchk  = nl_gstats_chk,
> + .nlfunc = nl_gstats,
> + .help   = "Show adapter statistics",
> + .xhelp  = "   [ --groups [eth-phy] [eth-mac] 
> [eth-ctrl] [rmon] ]\n"
>   },
>   {
>   .opts   = "--phy-statistics",
> diff --git a/netlink/desc-ethtool.c b/netlink/desc-ethtool.c
> index 9da052a5b8aa..63e91ee7ffcd 100644
> --- a/netlink/desc-ethtool.c
> +++ b/netlink/desc-ethtool.c
> @@ -325,6 +325,43 @@ static const struct pretty_nla_desc __fec_desc[] = {
>   NLATTR_DESC_U32(ETHTOOL_A_FEC_ACTIVE),
>  };
>  
> +static const struct pretty_nla_desc __stats_grp_stat_desc[] = {
> + NLATTR_DESC_U64(0),  NLATTR_DESC_U64(1),  NLATTR_DESC_U64(2),
> + NLATTR_DESC_U64(3),  NLATTR_DESC_U64(4),  NLATTR_DESC_U64(5),
> + NLATTR_DESC_U64(6),  NLATTR_DESC_U64(7),  NLATTR_DESC_U64(8),
> + NLATTR_DESC_U64(9),  NLATTR_DESC_U64(10), NLATTR_DESC_U64(11),
> + NLATTR_DESC_U64(12), NLATTR_DESC_U64(13), NLATTR_DESC_U64(14),
> + NLATTR_DESC_U64(15), NLATTR_DESC_U64(16), NLATTR_DESC_U64(17),
> + NLATTR_DESC_U64(18), NLATTR_DESC_U64(19), NLATTR_DESC_U64(20),
> + NLATTR_DESC_U64(21), NLATTR_DESC_U64(22), NLATTR_DESC_U64(23),
> + NLATTR_DESC_U64(24), NLATTR_DESC_U64(25), NLATTR_DESC_U64(26),
> + NLATTR_DESC_U64(27), NLATTR_DESC_U64(28), NLATTR_DESC_U64(29),
> +};
> +
> +static const struct pretty_nla_desc __stats_grp_hist_desc[] = {
> + NLATTR_DESC_U32(ETHTOOL_A_STATS_GRP_HIST_BKT_LOW),
> + NLATTR_DESC_U32(ETHTOOL_A_STATS_GRP_HIST_BKT_HI),