diff options
author | Maciej Żenczykowski <maze@google.com> | 2023-10-25 16:03:11 -0700 |
---|---|---|
committer | Maciej Żenczykowski <maze@google.com> | 2023-10-25 16:03:41 -0700 |
commit | 9f265c9270d3b4edf7c24169cec9c9cde846d2ad (patch) | |
tree | f6601a3df2c2b32e80e6dabcb128c869f50e5a2b | |
parent | 63205d83b85d394a45812e83653bf7ec2b16cef6 (diff) | |
parent | 89e6096b8b0bb505f91200cd923da042cd21983e (diff) | |
download | ethtool-9f265c9270d3b4edf7c24169cec9c9cde846d2ad.tar.gz |
Merge upstream ethtool v5.10
* commit '89e6096b8b0bb505f91200cd923da042cd21983e':
Release version 5.10.
Improve error message when SFP module is missing
ethtool: Improve compatibility between netlink and ioctl interfaces
netlink: do not send messages and process replies in nl_parser()
netlink: fix leaked instances of struct nl_socket
netlink: fix use after free in netlink_run_handler()
netlink: add message descriptions for pause stats
netlink: add descriptions for genetlink policy dumps
netlink: support 64-bit attribute types in pretty printed messages
netlink: support u32 enumerated types in pretty printing
pause: add support for dumping statistics
netlink: use policy dumping to check if stats flag is supported
netlink: prepare for more per-op info
add support for stats in subcommands
separate FLAGS out in -h
pause: add --json support
update UAPI header copies
Release version 5.9.
netlink: fix allocation failure handling in dump_features()
netlink: add tunnel offload format descriptions
add 100baseFX modes to link mode tables
update UAPI header copies
add missing link modes to mode_defs[] array
netlink: fix copy-paste error in rtm_link_summary()
fix memory leaks in do_sfeatures()
netlink: fix memory leak
bnxt: Add Broadcom driver support.
update link mode tables
netlink: mark unused function parameters of non-netlink stubs
tunnels: implement new --show-tunnels command
update UAPI header copies
ioctl: only memset non-NULL link settings
build: add -Wextra to default CFLAGS
ioctl: convert cmdline_info arrays to named initializers
settings: simplify link_mode_info[] initializers
get rid of signed/unsigned comparison warnings in register dump parsers
ioctl: get rid of signed/unsigned comparison warnings
ioctl: make argc counters unsigned
ioctl: prevent argc underflow in do_perqueue()
ioctl: check presence of eeprom length argument properly
netlink: get rid of signed/unsigned comparison warnings
netlink: Print and return an error when features weren't changed
cable-test: TDR Amplitude is signed
netlink: Fix the condition for displaying actual changes
Add QSFP-DD support
settings: clean up unused function parameters
Release version 5.8.
ioctl: avoid zero length array warning in get_stringset()
netlink: mark unused parameters of parser callbacks
netlink: mark unused parameters of bitset walker callbacks
netlink: mark unused callback parameter
igc: mark unused callback parameter
cable_test: clean up unused parameters
rename maybe_unused macro to __maybe_unused
ethtool.spec: Add bash completion script
ioctl: do not pass transceiver value back to kernel
ethtool: use "Not reported" when no FEC modes are provided
ethtool: fix netlink bitmasks when sent as NOMASK
igc: Fix output values case
ethtool: dsa: mv88e6xxx: add pretty dump for 88E6352 SERDES
man: add man page for ETHTOOL_GTUNABLE and ETHTOOL_STUNABLE
ethtool: add support for get/set ethtool_tunable
Fix segfault with cable test and ./configure --disable-netlink
igc: Parse ETQF registers
igc: Parse VLANPQF register fields
igc: Parse RCTL register fields
Add IGC driver support
netlink: settings: expand linkstate_reply_cb() to support link extended state
netlink: desc-ethtool.c: Add descriptions of extended state attributes
uapi: linux: update kernel UAPI header files
netlink: add cable test message format description
ethtool.8.in: Add --json option
ethtool.8.in: Document the cable test commands
Add --json command line argument parsing
json_writer/json_print: Import the iproute2 helper code for JSON output
Add cable test TDR support
Add cable test support
netlink: add LINKSTATE SQI support
netlink: add master/slave configuration support
update UAPI header copies
netlink: fix error message suppression
netlink: fix unwanted switch fall through in family_info_cb()
netlink: add netlink handler for tsinfo (-T)
netlink: add netlink handler for seee (--set-eee)
netlink: add netlink handler for geee (--show-eee)
netlink: add netlink handler for spause (-A)
netlink: add netlink handler for gpause (-a)
netlink: add netlink handler for scoalesce (-C)
netlink: add netlink handler for gcoalesce (-c)
netlink: add netlink handler for schannels (-L)
netlink: add netlink handler for gchannels (-l)
netlink: add netlink handler for sring (-G)
netlink: add netlink handler for gring (-g)
netlink: add netlink handler for sprivflags (--set-priv-flags)
netlink: add netlink handler for gprivflags (--show-priv-flags)
netlink: add netlink handler for sfeatures (-K)
netlink: add netlink handler for gfeatures (-k)
selftest: omit test-features if netlink is enabled
netlink: add more ethtool netlink message format descriptions
update UAPI header copies
netlink: fix msgbuff_append() helper
netlink: fix nest type grouping in parser
netlink: fix build warnings
Release version 5.7.
netlink: show netlink error even without extack
ethtool: add support for newer SFF-8024 compliance codes
netlink: use genetlink ops information to decide about fallback
refactor interface between ioctl and netlink code
features: accept long legacy flag names when setting features
ethtool.c: Report transceiver correctly
ethtool: Add support for Low Latency Reed Solomon
Rewrite printf() due to -Werror=format-security
Generated via:
git merge --log=999 "$(cd /git/ETHTOOL2 && git rev-parse remotes/tools/ethtool/v5.10^{commit}; )" -m "Merge upstream ethtool v5.10"
Test: N/A, see followup
Signed-off-by: Maciej Żenczykowski <maze@google.com>
Change-Id: I3c5f299f64c0b8eb8367fe64eaf360a7679a21ee
-rw-r--r-- | Makefile.am | 21 | ||||
-rw-r--r-- | NEWS | 69 | ||||
-rw-r--r-- | amd8111e.c | 2 | ||||
-rw-r--r-- | at76c50x-usb.c | 2 | ||||
-rw-r--r-- | bnxt.c | 97 | ||||
-rw-r--r-- | common.c | 30 | ||||
-rw-r--r-- | common.h | 19 | ||||
-rw-r--r-- | configure.ac | 2 | ||||
-rw-r--r-- | de2104x.c | 4 | ||||
-rw-r--r-- | dsa.c | 204 | ||||
-rw-r--r-- | e100.c | 2 | ||||
-rw-r--r-- | e1000.c | 2 | ||||
-rw-r--r-- | et131x.c | 2 | ||||
-rw-r--r-- | ethtool.8.in | 169 | ||||
-rw-r--r-- | ethtool.c | 971 | ||||
-rw-r--r-- | ethtool.spec.in | 1 | ||||
-rw-r--r-- | fec.c | 4 | ||||
-rw-r--r-- | fec_8xx.c | 2 | ||||
-rw-r--r-- | fjes.c | 2 | ||||
-rw-r--r-- | ibm_emac.c | 4 | ||||
-rw-r--r-- | igb.c | 2 | ||||
-rw-r--r-- | igc.c | 284 | ||||
-rw-r--r-- | internal.h | 15 | ||||
-rw-r--r-- | ixgb.c | 2 | ||||
-rw-r--r-- | ixgbe.c | 2 | ||||
-rw-r--r-- | ixgbevf.c | 2 | ||||
-rw-r--r-- | json_print.c | 228 | ||||
-rw-r--r-- | json_print.h | 67 | ||||
-rw-r--r-- | json_writer.c | 391 | ||||
-rw-r--r-- | json_writer.h | 76 | ||||
-rw-r--r-- | lan78xx.c | 2 | ||||
-rw-r--r-- | marvell.c | 6 | ||||
-rw-r--r-- | natsemi.c | 6 | ||||
-rw-r--r-- | netlink/bitset.c | 43 | ||||
-rw-r--r-- | netlink/bitset.h | 2 | ||||
-rw-r--r-- | netlink/cable_test.c | 593 | ||||
-rw-r--r-- | netlink/channels.c | 141 | ||||
-rw-r--r-- | netlink/coalesce.c | 269 | ||||
-rw-r--r-- | netlink/desc-ethtool.c | 250 | ||||
-rw-r--r-- | netlink/desc-genlctrl.c | 57 | ||||
-rw-r--r-- | netlink/eee.c | 189 | ||||
-rw-r--r-- | netlink/extapi.h | 57 | ||||
-rw-r--r-- | netlink/features.c | 532 | ||||
-rw-r--r-- | netlink/monitor.c | 87 | ||||
-rw-r--r-- | netlink/msgbuff.c | 1 | ||||
-rw-r--r-- | netlink/msgbuff.h | 6 | ||||
-rw-r--r-- | netlink/netlink.c | 368 | ||||
-rw-r--r-- | netlink/netlink.h | 77 | ||||
-rw-r--r-- | netlink/nlsock.c | 46 | ||||
-rw-r--r-- | netlink/parser.c | 130 | ||||
-rw-r--r-- | netlink/parser.h | 7 | ||||
-rw-r--r-- | netlink/pause.c | 308 | ||||
-rw-r--r-- | netlink/prettymsg.c | 21 | ||||
-rw-r--r-- | netlink/prettymsg.h | 24 | ||||
-rw-r--r-- | netlink/privflags.c | 158 | ||||
-rw-r--r-- | netlink/rings.c | 141 | ||||
-rw-r--r-- | netlink/settings.c | 648 | ||||
-rw-r--r-- | netlink/tsinfo.c | 124 | ||||
-rw-r--r-- | netlink/tunnels.c | 236 | ||||
-rw-r--r-- | qsfp-dd.c | 333 | ||||
-rw-r--r-- | qsfp-dd.h | 125 | ||||
-rw-r--r-- | qsfp.c | 148 | ||||
-rw-r--r-- | qsfp.h | 44 | ||||
-rw-r--r-- | realtek.c | 2 | ||||
-rw-r--r-- | rxclass.c | 8 | ||||
-rw-r--r-- | sfc.c | 3 | ||||
-rw-r--r-- | sff-common.c | 51 | ||||
-rw-r--r-- | sff-common.h | 26 | ||||
-rw-r--r-- | sfpdiag.c | 2 | ||||
-rw-r--r-- | sfpid.c | 68 | ||||
-rw-r--r-- | smsc911x.c | 2 | ||||
-rw-r--r-- | stmmac.c | 4 | ||||
-rw-r--r-- | tg3.c | 8 | ||||
-rw-r--r-- | tse.c | 2 | ||||
-rw-r--r-- | uapi/linux/ethtool.h | 115 | ||||
-rw-r--r-- | uapi/linux/ethtool_netlink.h | 404 | ||||
-rw-r--r-- | uapi/linux/genetlink.h | 13 | ||||
-rw-r--r-- | uapi/linux/if_link.h | 265 | ||||
-rw-r--r-- | uapi/linux/net_tstamp.h | 6 | ||||
-rw-r--r-- | uapi/linux/netlink.h | 107 | ||||
-rw-r--r-- | uapi/linux/rtnetlink.h | 52 | ||||
-rw-r--r-- | vioc.c | 2 | ||||
-rw-r--r-- | vmxnet3.c | 2 |
83 files changed, 8314 insertions, 685 deletions
diff --git a/Makefile.am b/Makefile.am index 0f8465f..e3e311d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,4 +1,4 @@ -AM_CFLAGS = -Wall +AM_CFLAGS = -Wall -Wextra AM_CPPFLAGS = -I$(top_srcdir)/uapi LDADD = -lm @@ -7,7 +7,8 @@ EXTRA_DIST = LICENSE ethtool.8 ethtool.spec.in aclocal.m4 ChangeLog autogen.sh sbin_PROGRAMS = ethtool ethtool_SOURCES = ethtool.c uapi/linux/ethtool.h internal.h \ - uapi/linux/net_tstamp.h rxclass.c common.c common.h + uapi/linux/net_tstamp.h rxclass.c common.c common.h \ + json_writer.c json_writer.h json_print.c json_print.h if ETHTOOL_ENABLE_PRETTY_DUMP ethtool_SOURCES += \ amd8111e.c de2104x.c dsa.c e100.c e1000.c et131x.c igb.c \ @@ -15,7 +16,8 @@ ethtool_SOURCES += \ pcnet32.c realtek.c tg3.c marvell.c vioc.c \ smsc911x.c at76c50x-usb.c sfc.c stmmac.c \ sff-common.c sff-common.h sfpid.c sfpdiag.c \ - ixgbevf.c tse.c vmxnet3.c qsfp.c qsfp.h fjes.c lan78xx.c + ixgbevf.c tse.c vmxnet3.c qsfp.c qsfp.h fjes.c lan78xx.c \ + igc.c qsfp-dd.c qsfp-dd.h bnxt.c endif if ENABLE_BASH_COMPLETION @@ -31,8 +33,11 @@ ethtool_SOURCES += \ netlink/monitor.c netlink/bitset.c netlink/bitset.h \ netlink/settings.c netlink/parser.c netlink/parser.h \ netlink/permaddr.c netlink/prettymsg.c netlink/prettymsg.h \ + 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/desc-ethtool.c netlink/desc-genlctrl.c \ - netlink/desc-rtnl.c \ + netlink/desc-rtnl.c netlink/cable_test.c netlink/tunnels.c \ uapi/linux/ethtool_netlink.h \ uapi/linux/netlink.h uapi/linux/genetlink.h \ uapi/linux/rtnetlink.h uapi/linux/if_link.h @@ -40,12 +45,16 @@ AM_CPPFLAGS += @MNL_CFLAGS@ LDADD += @MNL_LIBS@ endif -TESTS = test-cmdline test-features -check_PROGRAMS = test-cmdline test-features +TESTS = test-cmdline +check_PROGRAMS = test-cmdline test_cmdline_SOURCES = test-cmdline.c test-common.c $(ethtool_SOURCES) test_cmdline_CFLAGS = -DTEST_ETHTOOL +if !ETHTOOL_ENABLE_NETLINK +TESTS += test-features +check_PROGRAMS += test-features test_features_SOURCES = test-features.c test-common.c $(ethtool_SOURCES) test_features_CFLAGS = -DTEST_ETHTOOL +endif dist-hook: cp $(top_srcdir)/ethtool.spec $(distdir) @@ -1,3 +1,72 @@ +Version 5.10 - Dec 16, 2020 + * Feature: infrastructure for JSON output + * Feature: separate FLAGS in -h output + * Feature: use policy dumps to check flags support + * Feature: show pause stats (-a) + * Feature: pretty printing of policy dumps + * Feature: improve error message when SFP module is missing + * Fix: use after free in netlink_run_handler() + * Fix: leaked instances of struct nl_socket + * Fix: improve compatibility between netlink and ioctl (-s) + +Version 5.9 - Oct 15, 2020 + * Feature: extended link state + * Feature: QSFP-DD support + * Feature: tunnel information (--show-tunnels) + * Feature: Broadcom bnxt support + * Fix: improve compatibility between ioctl and netlink output + * Fix: cable test TDR amplitude output + * Fix: get rid of build warnings + * Fix: null pointer dereference running against old kernel (no arg) + * Fix: update link mode tables + * Fix: fix memory leaks and error handling found by static analysis + +Version 5.8 - Aug 4, 2020 + * Feature: more ethtool netlink message format descriptions + * Feature: omit test-features if netlink is enabled + * Feature: netlink handler for gfeatures (-k) + * Feature: netlink handler for sfeatures (-K) + * Feature: netlink handler for gprivflags (--show-priv-flags) + * Feature: netlink handler for sprivflags (--set-priv-flags) + * Feature: netlink handler for gring (-g) + * Feature: netlink handler for sring (-G) + * Feature: netlink handler for gchannels (-l) + * Feature: netlink handler for schannels (-L) + * Feature: netlink handler for gcoalesce (-c) + * Feature: netlink handler for scoalesce (-C) + * Feature: netlink handler for gpause (-a) + * Feature: netlink handler for spause (-A) + * Feature: netlink handler for geee (--show-eee) + * Feature: netlink handler for seee (--set-eee) + * Feature: netlink handler for tsinfo (-T) + * Feature: master/slave configuration support + * Feature: LINKSTATE SQI support + * Feature: cable test support + * Feature: cable test TDR support + * Feature: JSON output for cable test commands + * Feature: igc driver support + * Feature: support for get/set ethtool_tunable + * Feature: dsa: mv88e6xxx: add pretty dump for 88E6352 SERDES + * Fix: fix build warnings + * Fix: fix nest type grouping in parser + * Fix: fix msgbuff_append() helper + * Fix: fix unwanted switch fall through in family_info_cb() + * Fix: fix netlink error message suppression + * Fix: fix netlink bitmasks when sent as NOMASK + * Fix: use "Not reported" when no FEC modes are provided + * Fix: ioctl: do not pass transceiver value back to kernel + * Fix: ethtool.spec: Add bash completion script + +Version 5.7 - Jun 4, 2020 + * Feature: ethtool: Add support for Low Latency Reed Solomon + * Fix: ethtool.c: Report transceiver correctly + * Feature: features: accept long legacy flag names when setting features + * Feature: refactor interface between ioctl and netlink code + * Feature: netlink: use genetlink ops information to decide about fallback + * Feature: netlink: show netlink error even without extack + * Feature: ethtool: add support for newer SFF-8024 compliance codes + * Feature: Rewrite printf() due to -Werror=format-security + Version 5.6 - May 12, 2020 * Feature: add --debug option to control debugging messages * Feature: use named initializers in command line option list @@ -152,7 +152,7 @@ typedef enum { #define PHY_SPEED_100 0x3 -int amd8111e_dump_regs(struct ethtool_drvinfo *info maybe_unused, +int amd8111e_dump_regs(struct ethtool_drvinfo *info __maybe_unused, struct ethtool_regs *regs) { diff --git a/at76c50x-usb.c b/at76c50x-usb.c index 0121e98..fad41bf 100644 --- a/at76c50x-usb.c +++ b/at76c50x-usb.c @@ -12,7 +12,7 @@ static char *hw_versions[] = { " 505AMX", }; -int at76c50x_usb_dump_regs(struct ethtool_drvinfo *info maybe_unused, +int at76c50x_usb_dump_regs(struct ethtool_drvinfo *info __maybe_unused, struct ethtool_regs *regs) { u8 version = (u8)(regs->version >> 24); @@ -0,0 +1,97 @@ +/* Code to dump registers for NetXtreme-E/NetXtreme-C Broadcom devices. + * + * Copyright (c) 2020 Broadcom Inc. + */ +#include <stdio.h> +#include "internal.h" + +#define BNXT_PXP_REG_LEN 0x3110 +#define BNXT_PCIE_STATS_LEN (12 * sizeof(u64)) + +struct bnxt_pcie_stat { + const char *name; + u16 offset; + u8 size; + const char *format; +}; + +static const struct bnxt_pcie_stat bnxt_pcie_stats[] = { + { .name = "PL Signal integrity errors", .offset = 0, .size = 4, .format = "%llu" }, + { .name = "DL Signal integrity errors", .offset = 4, .size = 4, .format = "%llu" }, + { .name = "TLP Signal integrity errors", .offset = 8, .size = 4, .format = "%llu" }, + { .name = "Link integrity", .offset = 12, .size = 4, .format = "%llu" }, + { .name = "TX TLP traffic rate", .offset = 16, .size = 4, .format = "%llu" }, + { .name = "RX TLP traffic rate", .offset = 20, .size = 4, .format = "%llu" }, + { .name = "TX DLLP traffic rate", .offset = 24, .size = 4, .format = "%llu" }, + { .name = "RX DLLP traffic rate", .offset = 28, .size = 4, .format = "%llu" }, + { .name = "Equalization Phase 0 time(ms)", .offset = 33, .size = 1, .format = "0x%x" }, + { .name = "Equalization Phase 1 time(ms)", .offset = 32, .size = 1, .format = "0x%x" }, + { .name = "Equalization Phase 2 time(ms)", .offset = 35, .size = 1, .format = "0x%x" }, + { .name = "Equalization Phase 3 time(ms)", .offset = 34, .size = 1, .format = "0x%x" }, + { .name = "PHY LTSSM Histogram 0", .offset = 36, .size = 2, .format = "0x%x"}, + { .name = "PHY LTSSM Histogram 1", .offset = 38, .size = 2, .format = "0x%x"}, + { .name = "PHY LTSSM Histogram 2", .offset = 40, .size = 2, .format = "0x%x"}, + { .name = "PHY LTSSM Histogram 3", .offset = 42, .size = 2, .format = "0x%x"}, + { .name = "Recovery Histogram 0", .offset = 44, .size = 2, .format = "0x%x"}, + { .name = "Recovery Histogram 1", .offset = 46, .size = 2, .format = "0x%x"}, +}; + +int bnxt_dump_regs(struct ethtool_drvinfo *info __maybe_unused, struct ethtool_regs *regs) +{ + const struct bnxt_pcie_stat *stats = bnxt_pcie_stats; + u16 *pcie_stats, pcie_stat16; + u32 reg, i, pcie_stat32; + u64 pcie_stat64; + + if (regs->len < BNXT_PXP_REG_LEN) { + fprintf(stdout, "Length too short, expected at least 0x%x\n", + BNXT_PXP_REG_LEN); + return -1; + } + + fprintf(stdout, "PXP Registers\n"); + fprintf(stdout, "Offset\tValue\n"); + fprintf(stdout, "------\t-------\n"); + for (i = 0; i < BNXT_PXP_REG_LEN; i += sizeof(reg)) { + memcpy(®, ®s->data[i], sizeof(reg)); + if (reg) + fprintf(stdout, "0x%04x\t0x%08x\n", i, reg); + } + fprintf(stdout, "\n"); + + if (!regs->version) + return 0; + + if (regs->len < (BNXT_PXP_REG_LEN + BNXT_PCIE_STATS_LEN)) { + fprintf(stdout, "Length is too short, expected 0x%lx\n", + BNXT_PXP_REG_LEN + BNXT_PCIE_STATS_LEN); + return -1; + } + + pcie_stats = (u16 *)(regs->data + BNXT_PXP_REG_LEN); + fprintf(stdout, "PCIe statistics:\n"); + fprintf(stdout, "----------------\n"); + for (i = 0; i < ARRAY_SIZE(bnxt_pcie_stats); i++) { + fprintf(stdout, "%-30s : ", stats[i].name); + switch (stats[i].size) { + case 1: + pcie_stat16 = 0; + memcpy(&pcie_stat16, &pcie_stats[stats[i].offset], sizeof(u16)); + fprintf(stdout, stats[i].format, pcie_stat16); + break; + case 2: + pcie_stat32 = 0; + memcpy(&pcie_stat32, &pcie_stats[stats[i].offset], sizeof(u32)); + fprintf(stdout, stats[i].format, pcie_stat32); + break; + case 4: + pcie_stat64 = 0; + memcpy(&pcie_stat64, &pcie_stats[stats[i].offset], sizeof(u64)); + fprintf(stdout, stats[i].format, pcie_stat64); + break; + } + fprintf(stdout, "\n"); + } + + return 0; +} @@ -47,6 +47,36 @@ const struct flag_info flags_msglvl[] = { }; const unsigned int n_flags_msglvl = ARRAY_SIZE(flags_msglvl) - 1; +const struct off_flag_def off_flag_def[] = { + { "rx", "rx-checksumming", "rx-checksum", + ETHTOOL_GRXCSUM, ETHTOOL_SRXCSUM, ETH_FLAG_RXCSUM, 0 }, + { "tx", "tx-checksumming", "tx-checksum-*", + ETHTOOL_GTXCSUM, ETHTOOL_STXCSUM, ETH_FLAG_TXCSUM, 0 }, + { "sg", "scatter-gather", "tx-scatter-gather*", + ETHTOOL_GSG, ETHTOOL_SSG, ETH_FLAG_SG, 0 }, + { "tso", "tcp-segmentation-offload", "tx-tcp*-segmentation", + ETHTOOL_GTSO, ETHTOOL_STSO, ETH_FLAG_TSO, 0 }, + { "ufo", "udp-fragmentation-offload", "tx-udp-fragmentation", + ETHTOOL_GUFO, ETHTOOL_SUFO, ETH_FLAG_UFO, 0 }, + { "gso", "generic-segmentation-offload", "tx-generic-segmentation", + ETHTOOL_GGSO, ETHTOOL_SGSO, ETH_FLAG_GSO, 0 }, + { "gro", "generic-receive-offload", "rx-gro", + ETHTOOL_GGRO, ETHTOOL_SGRO, ETH_FLAG_GRO, 0 }, + { "lro", "large-receive-offload", "rx-lro", + 0, 0, ETH_FLAG_LRO, + KERNEL_VERSION(2,6,24) }, + { "rxvlan", "rx-vlan-offload", "rx-vlan-hw-parse", + 0, 0, ETH_FLAG_RXVLAN, + KERNEL_VERSION(2,6,37) }, + { "txvlan", "tx-vlan-offload", "tx-vlan-hw-insert", + 0, 0, ETH_FLAG_TXVLAN, + KERNEL_VERSION(2,6,37) }, + { "ntuple", "ntuple-filters", "rx-ntuple-filter", + 0, 0, ETH_FLAG_NTUPLE, 0 }, + { "rxhash", "receive-hashing", "rx-hashing", + 0, 0, ETH_FLAG_RXHASH, 0 }, +}; + void print_flags(const struct flag_info *info, unsigned int n_info, u32 value) { const char *sep = ""; @@ -19,6 +19,25 @@ struct flag_info { extern const struct flag_info flags_msglvl[]; extern const unsigned int n_flags_msglvl; +struct off_flag_def { + const char *short_name; + const char *long_name; + const char *kernel_name; + u32 get_cmd, set_cmd; + u32 value; + /* For features exposed through ETHTOOL_GFLAGS, the oldest + * kernel version for which we can trust the result. Where + * the flag was added at the same time the kernel started + * supporting the feature, this is 0 (to allow for backports). + * Where the feature was supported before the flag was added, + * it is the version that introduced the flag. + */ + u32 min_kernel_ver; +}; + +#define OFF_FLAG_DEF_SIZE 12 +extern const struct off_flag_def off_flag_def[OFF_FLAG_DEF_SIZE]; + void print_flags(const struct flag_info *info, unsigned int n_info, u32 value); int dump_wol(struct ethtool_wolinfo *wol); void dump_mdix(u8 mdix, u8 mdix_ctrl); diff --git a/configure.ac b/configure.ac index 1169b7f..13c2bc0 100644 --- a/configure.ac +++ b/configure.ac @@ -1,5 +1,5 @@ dnl Process this file with autoconf to produce a configure script. -AC_INIT(ethtool, 5.6, netdev@vger.kernel.org) +AC_INIT(ethtool, 5.10, netdev@vger.kernel.org) AC_PREREQ(2.52) AC_CONFIG_SRCDIR([ethtool.c]) AM_INIT_AUTOMAKE([gnu subdir-objects]) @@ -111,7 +111,7 @@ print_rx_missed(u32 csr8) } } -static void de21040_dump_regs(struct ethtool_drvinfo *info maybe_unused, +static void de21040_dump_regs(struct ethtool_drvinfo *info __maybe_unused, struct ethtool_regs *regs) { u32 tmp, v, *data = (u32 *)regs->data; @@ -417,7 +417,7 @@ static void de21040_dump_regs(struct ethtool_drvinfo *info maybe_unused, v & (1<<0) ? " Jabber disable\n" : ""); } -static void de21041_dump_regs(struct ethtool_drvinfo *info maybe_unused, +static void de21041_dump_regs(struct ethtool_drvinfo *info __maybe_unused, struct ethtool_regs *regs) { u32 tmp, v, *data = (u32 *)regs->data; @@ -3,6 +3,8 @@ #include "internal.h" +#define SERDES_OFFSET 32 + /* Macros and dump functions for the 16-bit mv88e6xxx per-port registers */ #define REG(_reg, _name, _val) \ @@ -405,21 +407,204 @@ static void dsa_mv88e6352(int reg, u16 val) case 19: REG(reg, "Rx Frame Counter", val); break; + case 20 ... 21: + REG(reg, "Reserved", val); + break; case 22: REG(reg, "LED Control", val); break; + case 23: + REG(reg, "Reserved", val); + break; case 24: REG(reg, "Tag Remap 0-3", val); break; case 25: REG(reg, "Tag Remap 4-7", val); break; + case 26: + REG(reg, "Reserved", val); + break; case 27: REG(reg, "Queue Counters", val); break; - default: + case 28 ... 31: REG(reg, "Reserved", val); break; + case SERDES_OFFSET + 0: + REG(reg - SERDES_OFFSET, "Fiber Control", val); + FIELD("Fiber Reset", "%u", !!(val & 0x8000)); + FIELD("Loopback", "%u", !!(val & 0x4000)); + FIELD("Speed", "%s", + (val & (0x2000 | 0x0040)) == 0x0000 ? "10 Mbps" : + (val & (0x2000 | 0x0040)) == 0x2000 ? "100 Mbps" : + (val & (0x2000 | 0x0040)) == 0x0040 ? "1000 Mbps" : + (val & (0x2000 | 0x0040)) == (0x2000 | 0x0040) ? + "Reserved" : "?"); + FIELD("Autoneg Enable", "%u", !!(val & 0x1000)); + FIELD("Power down", "%u", !!(val & 0x0800)); + FIELD("Isolate", "%u", !!(val & 0x0400)); + FIELD("Restart Autoneg", "%u", !!(val & 0x0200)); + FIELD("Duplex", "%s", val & 0x0100 ? "Full" : "Half"); + break; + case SERDES_OFFSET + 1: + REG(reg - SERDES_OFFSET, "Fiber Status", val); + FIELD("100Base-X FD", "%u", !!(val & 0x4000)); + FIELD("100Base-X HD", "%u", !!(val & 0x2000)); + FIELD("Autoneg Complete", "%u", !!(val & 0x0020)); + FIELD("Remote Fault", "%u", !!(val & 0x0010)); + FIELD("Autoneg Ability", "%u", !!(val & 0x0008)); + FIELD("Link Status", "%s", val & 0x0004 ? "Up" : "Down"); + break; + case SERDES_OFFSET + 2: + REG(reg - SERDES_OFFSET, "PHY ID 1", val); + break; + case SERDES_OFFSET + 3: + REG(reg - SERDES_OFFSET, "PHY ID 2", val); + break; + case SERDES_OFFSET + 4: + REG(reg - SERDES_OFFSET, "Fiber Autoneg Advertisement", val); + FIELD("Remote Fault", "%s", + (val & 0x3000) == 0x0000 ? "No error, link OK" : + (val & 0x3000) == 0x1000 ? "Link failure" : + (val & 0x3000) == 0x2000 ? "Offline" : + (val & 0x3000) == 0x3000 ? "Autoneg Error" : "?"); + FIELD("Pause", "%s", + (val & 0x0180) == 0x0000 ? "No Pause" : + (val & 0x0180) == 0x0080 ? "Symmetric Pause" : + (val & 0x0180) == 0x0100 ? "Asymmetric Pause" : + (val & 0x0180) == 0x0180 ? "Symmetric & Asymmetric Pause" : + "?"); + FIELD("1000BaseX HD", "%u", !!(val & 0x0040)); + FIELD("1000BaseX FD", "%u", !!(val & 0x0020)); + break; + case SERDES_OFFSET + 5: + REG(reg - SERDES_OFFSET, "Fiber Link Autoneg Ability", val); + FIELD("Acknowledge", "%u", !!(val & 0x4000)); + FIELD("Remote Fault", "%s", + (val & 0x3000) == 0x0000 ? "No error, link OK" : + (val & 0x3000) == 0x1000 ? "Link failure" : + (val & 0x3000) == 0x2000 ? "Offline" : + (val & 0x3000) == 0x3000 ? "Autoneg Error" : "?"); + FIELD("Pause", "%s", + (val & 0x0180) == 0x0000 ? "No Pause" : + (val & 0x0180) == 0x0080 ? "Symmetric Pause" : + (val & 0x0180) == 0x0100 ? "Asymmetric Pause" : + (val & 0x0180) == 0x0180 ? "Symmetric & Asymmetric Pause" : + "?"); + FIELD("1000BaseX HD", "%u", !!(val & 0x0040)); + FIELD("1000BaseX FD", "%u", !!(val & 0x0020)); + break; + case SERDES_OFFSET + 6: + REG(reg - SERDES_OFFSET, "Fiber Autoneg Expansion", val); + FIELD("Link Partner Next Page Ability", "%u", !!(val & 0x0008)); + FIELD("Page Received", "%u", !!(val & 0x0002)); + FIELD("Link Partner Autoneg Ability", "%u", !!(val & 0x0001)); + break; + case SERDES_OFFSET + 7: + REG(reg - SERDES_OFFSET, "Fiber Next Page Transmit", val); + break; + case SERDES_OFFSET + 8: + REG(reg - SERDES_OFFSET, "Fiber Link Partner Next Page", val); + break; + case SERDES_OFFSET + 9 ... SERDES_OFFSET + 14: + REG(reg - SERDES_OFFSET, "Reserved", val); + break; + case SERDES_OFFSET + 15: + REG(reg - SERDES_OFFSET, "Extended Status", val); + break; + case SERDES_OFFSET + 16: + REG(reg - SERDES_OFFSET, "Fiber Specific Control", val); + FIELD("Fiber Transmit FIFO Depth", "%s", + (val & 0xc000) == 0x0000 ? "16 Bits" : + (val & 0xc000) == 0x4000 ? "24 Bits" : + (val & 0xc000) == 0x8000 ? "32 Bits" : + (val & 0xc000) == 0xc000 ? "40 Bits" : "?"); + FIELD("SERDES Loopback", "%u", !!(val & 0x1000)); + FIELD("Force Link Good", "%u", !!(val & 0x0400)); + FIELD("MAC Interface Power Down", "%u", !!(val & 0x0008)); + FIELD("Mode", "%s", + (val & 0x0003) == 0x0000 ? "100BaseFX" : + (val & 0x0003) == 0x0001 ? "1000BaseX" : + (val & 0x0003) == 0x0002 ? "SGMII System" : + (val & 0x0003) == 0x0003 ? "SGMII Media" : "?"); + break; + case SERDES_OFFSET + 17: + REG(reg - SERDES_OFFSET, "Fiber Specific Status", val); + FIELD("Speed", "%s", + (val & 0xc000) == 0x0000 ? "10 Mbps" : + (val & 0xc000) == 0x4000 ? "100 Mbps" : + (val & 0xc000) == 0x8000 ? "1000 Mbps" : + (val & 0xc000) == 0xc000 ? "Reserved" : "?"); + FIELD("Duplex", "%s", val & 0x2000 ? "Full" : "Half"); + FIELD("Page Received", "%u", !!(val & 0x1000)); + FIELD("Speed/Duplex Resolved", "%u", !!(val & 0x0800)); + FIELD("Link", "%s", val & 0x0400 ? "Up" : "Down"); + FIELD("Sync", "%u", !!(val & 0x0020)); + FIELD("Energy Detect", "%s", val & 0x010 ? "False" : "True"); + FIELD("Transmit Pause", "%u", !!(val & 0x0008)); + FIELD("Receive Pause", "%u", !!(val & 0x00004)); + break; + case SERDES_OFFSET + 18: + REG(reg - SERDES_OFFSET, "Fiber Interrupt Enable", val); + FIELD("Speed Changed", "%u", !!(val & 0x4000)); + FIELD("Duplex Changed", "%u", !!(val & 0x2000)); + FIELD("Page Received", "%u", !!(val & 0x1000)); + FIELD("Autoneg Complete", "%u", !!(val & 0x0800)); + FIELD("Link Status Change", "%u", !!(val & 0x0400)); + FIELD("Symbol Error", "%u", !!(val & 0x0200)); + FIELD("False Carrier", "%u", !!(val & 0x0100)); + FIELD("Energy Detect", "%u", !!(val & 0x0010)); + break; + case SERDES_OFFSET + 19: + REG(reg - SERDES_OFFSET, "Fiber Interrupt Status", val); + FIELD("Speed Changed", "%u", !!(val & 0x4000)); + FIELD("Duplex Changed", "%u", !!(val & 0x2000)); + FIELD("Page Received", "%u", !!(val & 0x1000)); + FIELD("Autoneg Complete", "%u", !!(val & 0x0800)); + FIELD("Link Status Change", "%u", !!(val & 0x0400)); + FIELD("Symbol Error", "%u", !!(val & 0x0200)); + FIELD("False Carrier", "%u", !!(val & 0x0100)); + FIELD("Energy Detect", "%u", !!(val & 0x0010)); + break; + case SERDES_OFFSET + 20: + REG(reg - SERDES_OFFSET, "Reserved", val); + break; + case SERDES_OFFSET + 21: + REG(reg - SERDES_OFFSET, "Fiber Receive Error Counter", val); + break; + case SERDES_OFFSET + 22: + REG(reg - SERDES_OFFSET, "Reserved", val); + break; + case SERDES_OFFSET + 23: + REG(reg - SERDES_OFFSET, "PRBS Control", val); + break; + case SERDES_OFFSET + 24: + REG(reg - SERDES_OFFSET, "PRBS Error Counter LSB", val); + break; + case SERDES_OFFSET + 25: + REG(reg - SERDES_OFFSET, "PRBS Error Counter MSB", val); + break; + case SERDES_OFFSET + 26: + REG(reg - SERDES_OFFSET, "Fiber Specific Control 2", val); + FIELD("1000BaseX Noise Filtering", "%u", !!(val & 0x4000)); + FIELD("1000BaseFX Noise Filtering", "%u", !!(val & 0x2000)); + FIELD("SERDES Autoneg Bypass Enable", "%u", !!(val & 0x0040)); + FIELD("SERDES Autoneg Bypass Status", "%u", !!(val & 0x0020)); + FIELD("Fiber Transmitter Disable", "%u", !!(val & 0x0008)); + FIELD("SGMII/Fiber Output Amplitude", "%s", + (val & 0x0007) == 0x0000 ? "14mV" : + (val & 0x0007) == 0x0001 ? "112mV" : + (val & 0x0007) == 0x0002 ? "210mV" : + (val & 0x0007) == 0x0003 ? "308mV" : + (val & 0x0007) == 0x0004 ? "406mV" : + (val & 0x0007) == 0x0005 ? "504mV" : + (val & 0x0007) == 0x0006 ? "602mV" : + (val & 0x0007) == 0x0007 ? "700mV" : "?"); + break; + default: + REG(reg - SERDES_OFFSET, "Reserved", val); + break; } }; @@ -639,11 +824,11 @@ static int dsa_mv88e6xxx_dump_regs(struct ethtool_regs *regs) { const struct dsa_mv88e6xxx_switch *sw = NULL; const u16 *data = (u16 *)regs->data; + unsigned int i; u16 id; - int i; /* Marvell chips have 32 per-port 16-bit registers */ - if (regs->len < 32 * 2) + if (regs->len < 32 * sizeof(u16)) return 1; id = regs->version & 0xfff0; @@ -667,6 +852,17 @@ static int dsa_mv88e6xxx_dump_regs(struct ethtool_regs *regs) else REG(i, "", data[i]); + /* Dump the SERDES registers, if provided */ + if (regs->len > SERDES_OFFSET * sizeof(u16)) { + printf("\n%s Switch Port SERDES Registers\n", sw->name); + printf("-------------------------------------\n"); + for (i = SERDES_OFFSET; i < regs->len / 2; i++) + if (sw->dump) + sw->dump(i, data[i]); + else + REG(i - SERDES_OFFSET, "", data[i]); + } + return 0; } @@ -674,7 +870,7 @@ static int dsa_mv88e6xxx_dump_regs(struct ethtool_regs *regs) #undef FIELD #undef REG -int dsa_dump_regs(struct ethtool_drvinfo *info maybe_unused, +int dsa_dump_regs(struct ethtool_drvinfo *info __maybe_unused, struct ethtool_regs *regs) { /* DSA per-driver register dump */ @@ -36,7 +36,7 @@ #define CU_CMD 0x00F0 #define RU_CMD 0x0007 -int e100_dump_regs(struct ethtool_drvinfo *info maybe_unused, +int e100_dump_regs(struct ethtool_drvinfo *info __maybe_unused, struct ethtool_regs *regs) { u32 *regs_buff = (u32 *)regs->data; @@ -363,7 +363,7 @@ static enum e1000_mac_type e1000_get_mac_type(u16 device_id) return mac_type; } -int e1000_dump_regs(struct ethtool_drvinfo *info maybe_unused, +int e1000_dump_regs(struct ethtool_drvinfo *info __maybe_unused, struct ethtool_regs *regs) { u32 *regs_buff = (u32 *)regs->data; @@ -2,7 +2,7 @@ #include <string.h> #include "internal.h" -int et131x_dump_regs(struct ethtool_drvinfo *info maybe_unused, +int et131x_dump_regs(struct ethtool_drvinfo *info __maybe_unused, struct ethtool_regs *regs) { u8 version = (u8)(regs->version >> 24); diff --git a/ethtool.8.in b/ethtool.8.in index acc8d84..ba4e245 100644 --- a/ethtool.8.in +++ b/ethtool.8.in @@ -52,6 +52,10 @@ .\" .ds MA \fIxx\fP\fB:\fP\fIyy\fP\fB:\fP\fIzz\fP\fB:\fP\fIaa\fP\fB:\fP\fIbb\fP\fB:\fP\fIcc\fP .\" +.\" \(*MS - master-slave property +.\" +.ds MS \fBpreferred-master\fP|\fBpreferred-slave\fP|\fBforced-master\fP|\fBforced-slave\fP +.\" .\" \(*PA - IP address .\" .ds PA \fIip-address\fP @@ -113,7 +117,7 @@ . hy \\n(HY .. . -.TH ETHTOOL 8 "May 2020" "Ethtool version @VERSION@" +.TH ETHTOOL 8 "Dec 2020" "Ethtool version @VERSION@" .SH NAME ethtool \- query or control network driver and hardware settings . @@ -133,6 +137,12 @@ ethtool \- query or control network driver and hardware settings .BN --debug .I args .HP +.B ethtool [--json] +.I args +.HP +.B ethtool [-I | --include-statistics] +.I args +.HP .B ethtool \-\-monitor [ .I command @@ -255,6 +265,7 @@ ethtool \- query or control network driver and hardware settings .RB [ wol \ \fIN\fP[\fB/\fP\fIM\fP] .RB | \ wol \ \*(WO] .RB [ sopass \ \*(MA] +.RB [ master-slave \ \*(MS] .RB [ msglvl .IR N\fP[/\fIM\fP] \ | .BI msglvl \ type @@ -390,6 +401,18 @@ ethtool \- query or control network driver and hardware settings .RB [ fast-link-down ] .RB [ energy-detect-power-down ] .HP +.B ethtool \-\-get\-tunable +.I devname +.RB [ rx-copybreak ] +.RB [ tx-copybreak ] +.RB [ pfc-prevention-tout ] +.HP +.B ethtool \-\-set\-tunable +.I devname +.BN rx\-copybreak +.BN tx\-copybreak +.BN pfc\-prevention\-tout +.HP .B ethtool \-\-reset .I devname .BN flags @@ -420,7 +443,7 @@ ethtool \- query or control network driver and hardware settings .B ethtool \-\-set\-fec .I devname .B encoding -.BR auto | off | rs | baser \ [...] +.BR auto | off | rs | baser | llrs \ [...] .HP .B ethtool \-Q|\-\-per\-queue .I devname @@ -429,6 +452,19 @@ ethtool \- query or control network driver and hardware settings .I sub_command .RB ... . +.HP +.B ethtool \-\-cable\-test +.I devname +.HP +.B ethtool \-\-cable\-test\-tdr +.I devname +.BN first N +.BN last N +.BN step N +.BN pair N +.HP +.B ethtool \-\-show\-tunnels +.I devname . .\" Adjust lines (i.e. full justification) and hyphenate. .ad @@ -461,6 +497,15 @@ lB l. 0x01 Parser information .TE .TP +.BI \-\-json +Output results in JavaScript Object Notation (JSON). Only a subset of +options support this. Those which do not will continue to output +plain text in the presence of this option. +.TP +.B \-I \-\-include\-statistics +Include command-related statistics in the output. This option allows +displaying relevant device statistics for selected get commands. +.TP .B \-a \-\-show\-pause Queries the specified Ethernet device for pause parameter information. .TP @@ -646,6 +691,20 @@ Sets full or half duplex mode. .A4 port tp aui bnc mii fibre da Selects device port. .TP +.BR master-slave \ \*(MS +Configure MASTER/SLAVE role of the PHY. When the PHY is configured as MASTER, +the PMA Transmit function shall source TX_TCLK from a local clock source. When +configured as SLAVE, the PMA Transmit function shall source TX_TCLK from the +clock recovered from data stream provided by MASTER. Not all devices support this. +.TS +nokeep; +lB l. +preferred-master Prefer MASTER role on autonegotiation +preferred-slave Prefer SLAVE role on autonegotiation +forced-master Force the PHY in MASTER role. Can be used without autonegotiation +forced-slave Force the PHY in SLAVE role. Can be used without autonegotiation +.TE +.TP .A3 mdix auto on off Selects MDI-X mode for port. May be used to override the automatic detection feature of most adapters. An argument of \fBauto\fR means @@ -670,23 +729,25 @@ lB l lB. 0x004 100baseT Half 0x008 100baseT Full 0x80000000000000000 100baseT1 Full +0x40000000000000000000000 100baseFX Half +0x80000000000000000000000 100baseFX Full 0x010 1000baseT Half (not supported by IEEE standards) 0x020 1000baseT Full -0x100000000000000000 1000baseT1 Full 0x20000 1000baseKX Full 0x20000000000 1000baseX Full -0x800000000000 2500baseT Full +0x100000000000000000 1000baseT1 Full 0x8000 2500baseX Full (not supported by IEEE standards) +0x800000000000 2500baseT Full 0x1000000000000 5000baseT Full 0x1000 10000baseT Full 0x40000 10000baseKX4 Full 0x80000 10000baseKR Full 0x100000 10000baseR_FEC -0x40000000000 10000baseCR Full -0x80000000000 10000baseSR Full -0x100000000000 10000baseLR Full +0x40000000000 10000baseCR Full +0x80000000000 10000baseSR Full +0x100000000000 10000baseLR Full 0x200000000000 10000baseLRM Full -0x400000000000 10000baseER Full +0x400000000000 10000baseER Full 0x200000 20000baseMLD2 Full (not supported by IEEE standards) 0x400000 20000baseKR2 Full (not supported by IEEE standards) 0x80000000 25000baseCR Full @@ -717,11 +778,31 @@ lB l lB. 0x800000000000000 100000baseCR2 Full 0x1000000000000000 100000baseLR2_ER2_FR2 Full 0x2000000000000000 100000baseDR2 Full +0x8000000000000000000 100000baseKR Full +0x10000000000000000000 100000baseSR Full +0x20000000000000000000 100000baseLR_ER_FR Full +0x40000000000000000000 100000baseCR Full +0x80000000000000000000 100000baseDR Full 0x4000000000000000 200000baseKR4 Full 0x8000000000000000 200000baseSR4 Full 0x10000000000000000 200000baseLR4_ER4_FR4 Full 0x20000000000000000 200000baseDR4 Full 0x40000000000000000 200000baseCR4 Full +0x100000000000000000000 200000baseKR2 Full +0x200000000000000000000 200000baseSR2 Full +0x400000000000000000000 200000baseLR2_ER2_FR2 Full +0x800000000000000000000 200000baseDR2 Full +0x1000000000000000000000 200000baseCR2 Full +0x200000000000000000 400000baseKR8 Full +0x400000000000000000 400000baseSR8 Full +0x800000000000000000 400000baseLR8_ER8_FR8 Full +0x1000000000000000000 400000baseDR8 Full +0x2000000000000000000 400000baseCR8 Full +0x2000000000000000000000 400000baseKR4 Full +0x4000000000000000000000 400000baseSR4 Full +0x8000000000000000000000 400000baseLR4_ER4_FR4 Full +0x10000000000000000000000 400000baseDR4 Full +0x20000000000000000000000 400000baseCR4 Full .TE .TP .BI phyad \ N @@ -1176,6 +1257,34 @@ Gets the current configured setting for Energy Detect Power Down (if supported). .RE .TP +.B \-\-get\-tunable +Get the tunable parameters. +.RS 4 +.TP +.B rx\-copybreak +Get the current rx copybreak value in bytes. +.TP +.B tx\-copybreak +Get the current tx copybreak value in bytes. +.TP +.B pfc\-prevention\-tout +Get the current pfc prevention timeout value in msecs. +.RE +.TP +.B \-\-set\-tunable +Set driver's tunable parameters. +.RS 4 +.TP +.BI rx\-copybreak \ N +Set the rx copybreak value in bytes. +.TP +.BI tx\-copybreak \ N +Set the tx copybreak value in bytes. +.TP +.BI pfc\-prevention\-tout \ N +Set pfc prevention timeout in msecs. Value of 0 means disable and 65535 means auto. +.RE +.TP .B \-\-reset Reset hardware components specified by flags and components listed below .RS 4 @@ -1228,7 +1337,7 @@ current FEC mode, the driver or firmware must take the link down administratively and report the problem in the system logs for users to correct. .RS 4 .TP -.BR encoding\ auto | off | rs | baser \ [...] +.BR encoding\ auto | off | rs | baser | llrs \ [...] Sets the FEC encoding for the device. Combinations of options are specified as e.g. @@ -1241,6 +1350,7 @@ auto Use the driver's default encoding off Turn off FEC RS Force RS-FEC encoding BaseR Force BaseR encoding +LLRS Force LLRS-FEC encoding .TE .RE .TP @@ -1257,6 +1367,41 @@ Sub command to apply. The supported sub commands include --show-coalesce and --coalesce. .RE .TP +q.B \-\-cable\-test +Perform a cable test and report the results. What results are returned depends +on the capabilities of the network interface. Typically open pairs and shorted +pairs can be reported, along with pairs being O.K. When a fault is detected +the approximate distance to the fault may be reported. +.TP +.B \-\-cable\-test\-tdr +Perform a cable test and report the raw Time Domain Reflectometer +data. A pulse is sent down a cable pair and the amplitude of the +reflection, for a given distance, is reported. A break in the cable +returns a big reflection. Minor damage to the cable returns a small +reflection. If the cable is shorted, the amplitude of the reflection +can be negative. By default, data is returned for lengths between 0 +and 150m at 1m steps, for all pairs. However parameters can be passed +to restrict the collection of data. It should be noted, that the +interface will round the distances to whatever granularity is actually +implemented. This is often 0.8 of a meter. The results should include +the actual rounded first and last distance and step size. +.RS 4 +.TP +.B first \ N +Distance along the cable, in meters, where the first measurement +should be made. +.TP +.B last \ N +Distance along the cable, in meters, where the last measurement should +be made. +.TP +.B step \ N +Distance, in meters, between each measurement. +.TP +.B pair \ N +Which pair should be measured. Typically a cable has 4 pairs. 0 = Pair A, 1 = Pair B, ... +.RE +.TP .B \-\-monitor Listens to netlink notification and displays them. .RS 4 @@ -1270,6 +1415,12 @@ shown. If a device name is used as argument, only notification for this device are shown. Default is to show notifications for all devices. .RE +.TP +.B \-\-show\-tunnels +Show tunnel-related device capabilities and state. +List UDP ports kernel has programmed the device to parse as VxLAN, +or GENEVE tunnels. +.RE .SH BUGS Not supported (in part or whole) on all network drivers. .SH AUTHOR @@ -40,6 +40,7 @@ #include <sys/utsname.h> #include <limits.h> #include <ctype.h> +#include <inttypes.h> #include <sys/socket.h> #include <netinet/in.h> @@ -102,51 +103,6 @@ struct cmdline_info { void *seen_val; }; -struct off_flag_def { - const char *short_name; - const char *long_name; - const char *kernel_name; - u32 get_cmd, set_cmd; - u32 value; - /* For features exposed through ETHTOOL_GFLAGS, the oldest - * kernel version for which we can trust the result. Where - * the flag was added at the same time the kernel started - * supporting the feature, this is 0 (to allow for backports). - * Where the feature was supported before the flag was added, - * it is the version that introduced the flag. - */ - u32 min_kernel_ver; -}; -static const struct off_flag_def off_flag_def[] = { - { "rx", "rx-checksumming", "rx-checksum", - ETHTOOL_GRXCSUM, ETHTOOL_SRXCSUM, ETH_FLAG_RXCSUM, 0 }, - { "tx", "tx-checksumming", "tx-checksum-*", - ETHTOOL_GTXCSUM, ETHTOOL_STXCSUM, ETH_FLAG_TXCSUM, 0 }, - { "sg", "scatter-gather", "tx-scatter-gather*", - ETHTOOL_GSG, ETHTOOL_SSG, ETH_FLAG_SG, 0 }, - { "tso", "tcp-segmentation-offload", "tx-tcp*-segmentation", - ETHTOOL_GTSO, ETHTOOL_STSO, ETH_FLAG_TSO, 0 }, - { "ufo", "udp-fragmentation-offload", "tx-udp-fragmentation", - ETHTOOL_GUFO, ETHTOOL_SUFO, ETH_FLAG_UFO, 0 }, - { "gso", "generic-segmentation-offload", "tx-generic-segmentation", - ETHTOOL_GGSO, ETHTOOL_SGSO, ETH_FLAG_GSO, 0 }, - { "gro", "generic-receive-offload", "rx-gro", - ETHTOOL_GGRO, ETHTOOL_SGRO, ETH_FLAG_GRO, 0 }, - { "lro", "large-receive-offload", "rx-lro", - 0, 0, ETH_FLAG_LRO, - KERNEL_VERSION(2,6,24) }, - { "rxvlan", "rx-vlan-offload", "rx-vlan-hw-parse", - 0, 0, ETH_FLAG_RXVLAN, - KERNEL_VERSION(2,6,37) }, - { "txvlan", "tx-vlan-offload", "tx-vlan-hw-insert", - 0, 0, ETH_FLAG_TXVLAN, - KERNEL_VERSION(2,6,37) }, - { "ntuple", "ntuple-filters", "rx-ntuple-filter", - 0, 0, ETH_FLAG_NTUPLE, 0 }, - { "rxhash", "receive-hashing", "rx-hashing", - 0, 0, ETH_FLAG_RXHASH, 0 }, -}; - struct feature_def { char name[ETH_GSTRING_LEN]; int off_flag_index; /* index in off_flag_def; negative if none match */ @@ -155,7 +111,7 @@ struct feature_def { struct feature_defs { size_t n_features; /* Number of features each offload flag is associated with */ - unsigned int off_flag_matched[ARRAY_SIZE(off_flag_def)]; + unsigned int off_flag_matched[OFF_FLAG_DEF_SIZE]; /* Name and offload flag index for each feature */ struct feature_def def[0]; }; @@ -267,9 +223,9 @@ static void parse_generic_cmdline(struct cmd_context *ctx, struct cmdline_info *info, unsigned int n_info) { - int argc = ctx->argc; + unsigned int argc = ctx->argc; char **argp = ctx->argp; - int i, idx; + unsigned int i, idx; int found; for (i = 0; i < argc; i++) { @@ -410,7 +366,7 @@ static int rxflow_str_to_type(const char *str) return flow_type; } -static int do_version(struct cmd_context *ctx maybe_unused) +static int do_version(struct cmd_context *ctx __maybe_unused) { fprintf(stdout, PACKAGE " version " VERSION @@ -436,9 +392,9 @@ static void init_global_link_mode_masks(void) ETHTOOL_LINK_MODE_100baseT_Full_BIT, ETHTOOL_LINK_MODE_1000baseT_Half_BIT, ETHTOOL_LINK_MODE_1000baseT_Full_BIT, - ETHTOOL_LINK_MODE_1000baseKX_Full_BIT, - ETHTOOL_LINK_MODE_2500baseX_Full_BIT, ETHTOOL_LINK_MODE_10000baseT_Full_BIT, + ETHTOOL_LINK_MODE_2500baseX_Full_BIT, + ETHTOOL_LINK_MODE_1000baseKX_Full_BIT, ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT, ETHTOOL_LINK_MODE_10000baseKR_Full_BIT, ETHTOOL_LINK_MODE_10000baseR_FEC_BIT, @@ -487,6 +443,28 @@ static void init_global_link_mode_masks(void) ETHTOOL_LINK_MODE_200000baseCR4_Full_BIT, ETHTOOL_LINK_MODE_100baseT1_Full_BIT, ETHTOOL_LINK_MODE_1000baseT1_Full_BIT, + ETHTOOL_LINK_MODE_400000baseKR8_Full_BIT, + ETHTOOL_LINK_MODE_400000baseSR8_Full_BIT, + ETHTOOL_LINK_MODE_400000baseLR8_ER8_FR8_Full_BIT, + ETHTOOL_LINK_MODE_400000baseDR8_Full_BIT, + ETHTOOL_LINK_MODE_400000baseCR8_Full_BIT, + ETHTOOL_LINK_MODE_100000baseKR_Full_BIT, + ETHTOOL_LINK_MODE_100000baseSR_Full_BIT, + ETHTOOL_LINK_MODE_100000baseLR_ER_FR_Full_BIT, + ETHTOOL_LINK_MODE_100000baseCR_Full_BIT, + ETHTOOL_LINK_MODE_100000baseDR_Full_BIT, + ETHTOOL_LINK_MODE_200000baseKR2_Full_BIT, + ETHTOOL_LINK_MODE_200000baseSR2_Full_BIT, + ETHTOOL_LINK_MODE_200000baseLR2_ER2_FR2_Full_BIT, + ETHTOOL_LINK_MODE_200000baseDR2_Full_BIT, + ETHTOOL_LINK_MODE_200000baseCR2_Full_BIT, + ETHTOOL_LINK_MODE_400000baseKR4_Full_BIT, + ETHTOOL_LINK_MODE_400000baseSR4_Full_BIT, + ETHTOOL_LINK_MODE_400000baseLR4_ER4_FR4_Full_BIT, + ETHTOOL_LINK_MODE_400000baseDR4_Full_BIT, + ETHTOOL_LINK_MODE_400000baseCR4_Full_BIT, + ETHTOOL_LINK_MODE_100baseFX_Half_BIT, + ETHTOOL_LINK_MODE_100baseFX_Full_BIT, }; static const enum ethtool_link_mode_bit_indices additional_advertised_flags_bits[] = { @@ -502,6 +480,7 @@ static void init_global_link_mode_masks(void) ETHTOOL_LINK_MODE_FEC_NONE_BIT, ETHTOOL_LINK_MODE_FEC_RS_BIT, ETHTOOL_LINK_MODE_FEC_BASER_BIT, + ETHTOOL_LINK_MODE_FEC_LLRS_BIT, }; unsigned int i; @@ -576,20 +555,16 @@ static void dump_link_caps(const char *prefix, const char *an_prefix, "100baseT/Half" }, { 1, ETHTOOL_LINK_MODE_100baseT_Full_BIT, "100baseT/Full" }, - { 0, ETHTOOL_LINK_MODE_100baseT1_Full_BIT, - "100baseT1/Full" }, { 0, ETHTOOL_LINK_MODE_1000baseT_Half_BIT, "1000baseT/Half" }, { 1, ETHTOOL_LINK_MODE_1000baseT_Full_BIT, "1000baseT/Full" }, - { 0, ETHTOOL_LINK_MODE_1000baseT1_Full_BIT, - "1000baseT1/Full" }, - { 0, ETHTOOL_LINK_MODE_1000baseKX_Full_BIT, - "1000baseKX/Full" }, - { 0, ETHTOOL_LINK_MODE_2500baseX_Full_BIT, - "2500baseX/Full" }, { 0, ETHTOOL_LINK_MODE_10000baseT_Full_BIT, "10000baseT/Full" }, + { 0, ETHTOOL_LINK_MODE_2500baseX_Full_BIT, + "2500baseX/Full" }, + { 0, ETHTOOL_LINK_MODE_1000baseKX_Full_BIT, + "1000baseKX/Full" }, { 0, ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT, "10000baseKX4/Full" }, { 0, ETHTOOL_LINK_MODE_10000baseKR_Full_BIT, @@ -682,10 +657,59 @@ static void dump_link_caps(const char *prefix, const char *an_prefix, "200000baseDR4/Full" }, { 0, ETHTOOL_LINK_MODE_200000baseCR4_Full_BIT, "200000baseCR4/Full" }, + { 0, ETHTOOL_LINK_MODE_100baseT1_Full_BIT, + "100baseT1/Full" }, + { 0, ETHTOOL_LINK_MODE_1000baseT1_Full_BIT, + "1000baseT1/Full" }, + { 0, ETHTOOL_LINK_MODE_400000baseKR8_Full_BIT, + "400000baseKR8/Full" }, + { 0, ETHTOOL_LINK_MODE_400000baseSR8_Full_BIT, + "400000baseSR8/Full" }, + { 0, ETHTOOL_LINK_MODE_400000baseLR8_ER8_FR8_Full_BIT, + "400000baseLR8_ER8_FR8/Full" }, + { 0, ETHTOOL_LINK_MODE_400000baseDR8_Full_BIT, + "400000baseDR8/Full" }, + { 0, ETHTOOL_LINK_MODE_400000baseCR8_Full_BIT, + "400000baseCR8/Full" }, + { 0, ETHTOOL_LINK_MODE_100000baseKR_Full_BIT, + "100000baseKR/Full" }, + { 0, ETHTOOL_LINK_MODE_100000baseSR_Full_BIT, + "100000baseSR/Full" }, + { 0, ETHTOOL_LINK_MODE_100000baseLR_ER_FR_Full_BIT, + "100000baseLR_ER_FR/Full" }, + { 0, ETHTOOL_LINK_MODE_100000baseDR_Full_BIT, + "100000baseDR/Full" }, + { 0, ETHTOOL_LINK_MODE_100000baseCR_Full_BIT, + "100000baseCR/Full" }, + { 0, ETHTOOL_LINK_MODE_200000baseKR2_Full_BIT, + "200000baseKR2/Full" }, + { 0, ETHTOOL_LINK_MODE_200000baseSR2_Full_BIT, + "200000baseSR2/Full" }, + { 0, ETHTOOL_LINK_MODE_200000baseLR2_ER2_FR2_Full_BIT, + "200000baseLR2_ER2_FR2/Full" }, + { 0, ETHTOOL_LINK_MODE_200000baseDR2_Full_BIT, + "200000baseDR2/Full" }, + { 0, ETHTOOL_LINK_MODE_200000baseCR2_Full_BIT, + "200000baseCR2/Full" }, + { 0, ETHTOOL_LINK_MODE_400000baseKR4_Full_BIT, + "400000baseKR4/Full" }, + { 0, ETHTOOL_LINK_MODE_400000baseSR4_Full_BIT, + "400000baseSR4/Full" }, + { 0, ETHTOOL_LINK_MODE_400000baseLR4_ER4_FR4_Full_BIT, + "400000baseLR4_ER4_FR4/Full" }, + { 0, ETHTOOL_LINK_MODE_400000baseDR4_Full_BIT, + "400000baseDR4/Full" }, + { 0, ETHTOOL_LINK_MODE_400000baseCR4_Full_BIT, + "400000baseCR4/Full" }, + { 0, ETHTOOL_LINK_MODE_100baseFX_Half_BIT, + "100baseFX/Half" }, + { 1, ETHTOOL_LINK_MODE_100baseFX_Full_BIT, + "100baseFX/Full" }, }; int indent; - int did1, new_line_pend, i; + int did1, new_line_pend; int fecreported = 0; + unsigned int i; /* Indent just like the separate functions used to */ indent = strlen(prefix) + 14; @@ -754,6 +778,12 @@ static void dump_link_caps(const char *prefix, const char *an_prefix, fprintf(stdout, " RS"); fecreported = 1; } + if (ethtool_link_mode_test_bit(ETHTOOL_LINK_MODE_FEC_LLRS_BIT, + mask)) { + fprintf(stdout, " LLRS"); + fecreported = 1; + } + if (!fecreported) fprintf(stdout, " Not reported"); fprintf(stdout, "\n"); @@ -1087,6 +1117,8 @@ static const struct { { "lan78xx", lan78xx_dump_regs }, { "dsa", dsa_dump_regs }, { "fec", fec_dump_regs }, + { "igc", igc_dump_regs }, + { "bnxt_en", bnxt_dump_regs }, #endif }; @@ -1107,7 +1139,7 @@ void dump_hex(FILE *file, const u8 *data, int len, int offset) static int dump_regs(int gregs_dump_raw, int gregs_dump_hex, struct ethtool_drvinfo *info, struct ethtool_regs *regs) { - int i; + unsigned int i; if (gregs_dump_raw) { fwrite(regs->data, regs->len, 1, stdout); @@ -1142,7 +1174,7 @@ nested: } static int dump_eeprom(int geeprom_dump_raw, - struct ethtool_drvinfo *info maybe_unused, + struct ethtool_drvinfo *info __maybe_unused, struct ethtool_eeprom *ee) { if (geeprom_dump_raw) { @@ -1164,7 +1196,8 @@ static int dump_eeprom(int geeprom_dump_raw, static int dump_test(struct ethtool_test *test, struct ethtool_gstrings *strings) { - int i, rc; + unsigned int i; + int rc; rc = test->flags & ETH_TEST_FL_FAILED; fprintf(stdout, "The test result is %s\n", rc ? "FAIL" : "PASS"); @@ -1395,7 +1428,7 @@ static void dump_one_feature(const char *indent, const char *name, : ""); } -static int linux_version_code(void) +static unsigned int linux_version_code(void) { struct utsname utsname; unsigned version, patchlevel, sublevel = 0; @@ -1411,12 +1444,12 @@ static void dump_features(const struct feature_defs *defs, const struct feature_state *state, const struct feature_state *ref_state) { - int kernel_ver = linux_version_code(); - u32 value; + unsigned int kernel_ver = linux_version_code(); + unsigned int i, j; int indent; - int i, j; + u32 value; - for (i = 0; i < ARRAY_SIZE(off_flag_def); i++) { + for (i = 0; i < OFF_FLAG_DEF_SIZE; i++) { /* Don't show features whose state is unknown on this * kernel version */ @@ -1447,7 +1480,7 @@ static void dump_features(const struct feature_defs *defs, /* Show matching features */ for (j = 0; j < defs->n_features; j++) { - if (defs->def[j].off_flag_index != i) + if (defs->def[j].off_flag_index != (int)i) continue; if (defs->off_flag_matched[i] != 1) /* Show all matching feature states */ @@ -1562,6 +1595,8 @@ static void dump_fec(u32 fec) fprintf(stdout, " BaseR"); if (fec & ETHTOOL_FEC_RS) fprintf(stdout, " RS"); + if (fec & ETHTOOL_FEC_LLRS) + fprintf(stdout, " LLRS"); } #define N_SOTS 7 @@ -1666,7 +1701,9 @@ get_stringset(struct cmd_context *ctx, enum ethtool_stringset set_id, sset_info.hdr.reserved = 0; sset_info.hdr.sset_mask = 1ULL << set_id; if (send_ioctl(ctx, &sset_info) == 0) { - len = sset_info.hdr.sset_mask ? sset_info.hdr.data[0] : 0; + const u32 *sset_lengths = sset_info.hdr.data; + + len = sset_info.hdr.sset_mask ? sset_lengths[0] : 0; } else if (errno == EOPNOTSUPP && drvinfo_offset != 0) { /* Fallback for old kernel versions */ drvinfo.cmd = ETHTOOL_GDRVINFO; @@ -1700,8 +1737,8 @@ static struct feature_defs *get_feature_defs(struct cmd_context *ctx) { struct ethtool_gstrings *names; struct feature_defs *defs; + unsigned int i, j; u32 n_features; - int i, j; names = get_stringset(ctx, ETH_SS_FEATURES, 0, 1); if (names) { @@ -1733,7 +1770,7 @@ static struct feature_defs *get_feature_defs(struct cmd_context *ctx) defs->def[i].off_flag_index = -1; for (j = 0; - j < ARRAY_SIZE(off_flag_def) && + j < OFF_FLAG_DEF_SIZE && defs->def[i].off_flag_index < 0; j++) { const char *pattern = @@ -1855,10 +1892,24 @@ static int do_spause(struct cmd_context *ctx) int pause_rx_wanted = -1; int pause_tx_wanted = -1; struct cmdline_info cmdline_pause[] = { - { "autoneg", CMDL_BOOL, &pause_autoneg_wanted, - &epause.autoneg }, - { "rx", CMDL_BOOL, &pause_rx_wanted, &epause.rx_pause }, - { "tx", CMDL_BOOL, &pause_tx_wanted, &epause.tx_pause }, + { + .name = "autoneg", + .type = CMDL_BOOL, + .wanted_val = &pause_autoneg_wanted, + .ioctl_val = &epause.autoneg, + }, + { + .name = "rx", + .type = CMDL_BOOL, + .wanted_val = &pause_rx_wanted, + .ioctl_val = &epause.rx_pause, + }, + { + .name = "tx", + .type = CMDL_BOOL, + .wanted_val = &pause_tx_wanted, + .ioctl_val = &epause.tx_pause, + }, }; int err, changed = 0; @@ -1898,12 +1949,30 @@ static int do_sring(struct cmd_context *ctx) s32 ring_rx_jumbo_wanted = -1; s32 ring_tx_wanted = -1; struct cmdline_info cmdline_ring[] = { - { "rx", CMDL_S32, &ring_rx_wanted, &ering.rx_pending }, - { "rx-mini", CMDL_S32, &ring_rx_mini_wanted, - &ering.rx_mini_pending }, - { "rx-jumbo", CMDL_S32, &ring_rx_jumbo_wanted, - &ering.rx_jumbo_pending }, - { "tx", CMDL_S32, &ring_tx_wanted, &ering.tx_pending }, + { + .name = "rx", + .type = CMDL_S32, + .wanted_val = &ring_rx_wanted, + .ioctl_val = &ering.rx_pending, + }, + { + .name = "rx-mini", + .type = CMDL_S32, + .wanted_val = &ring_rx_mini_wanted, + .ioctl_val = &ering.rx_mini_pending, + }, + { + .name = "rx-jumbo", + .type = CMDL_S32, + .wanted_val = &ring_rx_jumbo_wanted, + .ioctl_val = &ering.rx_jumbo_pending, + }, + { + .name = "tx", + .type = CMDL_S32, + .wanted_val = &ring_tx_wanted, + .ioctl_val = &ering.tx_pending, + }, }; int err, changed = 0; @@ -1967,12 +2036,30 @@ static int do_schannels(struct cmd_context *ctx) s32 channels_other_wanted = -1; s32 channels_combined_wanted = -1; struct cmdline_info cmdline_channels[] = { - { "rx", CMDL_S32, &channels_rx_wanted, &echannels.rx_count }, - { "tx", CMDL_S32, &channels_tx_wanted, &echannels.tx_count }, - { "other", CMDL_S32, &channels_other_wanted, - &echannels.other_count }, - { "combined", CMDL_S32, &channels_combined_wanted, - &echannels.combined_count }, + { + .name = "rx", + .type = CMDL_S32, + .wanted_val = &channels_rx_wanted, + .ioctl_val = &echannels.rx_count, + }, + { + .name = "tx", + .type = CMDL_S32, + .wanted_val = &channels_tx_wanted, + .ioctl_val = &echannels.tx_count, + }, + { + .name = "other", + .type = CMDL_S32, + .wanted_val = &channels_other_wanted, + .ioctl_val = &echannels.other_count, + }, + { + .name = "combined", + .type = CMDL_S32, + .wanted_val = &channels_combined_wanted, + .ioctl_val = &echannels.combined_count, + }, }; int err, changed = 0; @@ -2082,50 +2169,138 @@ static int do_gcoalesce(struct cmd_context *ctx) #define COALESCE_CMDLINE_INFO(__ecoal) \ { \ - { "adaptive-rx", CMDL_BOOL, &coal_adaptive_rx_wanted, \ - &__ecoal.use_adaptive_rx_coalesce }, \ - { "adaptive-tx", CMDL_BOOL, &coal_adaptive_tx_wanted, \ - &__ecoal.use_adaptive_tx_coalesce }, \ - { "sample-interval", CMDL_S32, &coal_sample_rate_wanted, \ - &__ecoal.rate_sample_interval }, \ - { "stats-block-usecs", CMDL_S32, &coal_stats_wanted, \ - &__ecoal.stats_block_coalesce_usecs }, \ - { "pkt-rate-low", CMDL_S32, &coal_pkt_rate_low_wanted, \ - &__ecoal.pkt_rate_low }, \ - { "pkt-rate-high", CMDL_S32, &coal_pkt_rate_high_wanted, \ - &__ecoal.pkt_rate_high }, \ - { "rx-usecs", CMDL_S32, &coal_rx_usec_wanted, \ - &__ecoal.rx_coalesce_usecs }, \ - { "rx-frames", CMDL_S32, &coal_rx_frames_wanted, \ - &__ecoal.rx_max_coalesced_frames }, \ - { "rx-usecs-irq", CMDL_S32, &coal_rx_usec_irq_wanted, \ - &__ecoal.rx_coalesce_usecs_irq }, \ - { "rx-frames-irq", CMDL_S32, &coal_rx_frames_irq_wanted, \ - &__ecoal.rx_max_coalesced_frames_irq }, \ - { "tx-usecs", CMDL_S32, &coal_tx_usec_wanted, \ - &__ecoal.tx_coalesce_usecs }, \ - { "tx-frames", CMDL_S32, &coal_tx_frames_wanted, \ - &__ecoal.tx_max_coalesced_frames }, \ - { "tx-usecs-irq", CMDL_S32, &coal_tx_usec_irq_wanted, \ - &__ecoal.tx_coalesce_usecs_irq }, \ - { "tx-frames-irq", CMDL_S32, &coal_tx_frames_irq_wanted, \ - &__ecoal.tx_max_coalesced_frames_irq }, \ - { "rx-usecs-low", CMDL_S32, &coal_rx_usec_low_wanted, \ - &__ecoal.rx_coalesce_usecs_low }, \ - { "rx-frames-low", CMDL_S32, &coal_rx_frames_low_wanted, \ - &__ecoal.rx_max_coalesced_frames_low }, \ - { "tx-usecs-low", CMDL_S32, &coal_tx_usec_low_wanted, \ - &__ecoal.tx_coalesce_usecs_low }, \ - { "tx-frames-low", CMDL_S32, &coal_tx_frames_low_wanted, \ - &__ecoal.tx_max_coalesced_frames_low }, \ - { "rx-usecs-high", CMDL_S32, &coal_rx_usec_high_wanted, \ - &__ecoal.rx_coalesce_usecs_high }, \ - { "rx-frames-high", CMDL_S32, &coal_rx_frames_high_wanted, \ - &__ecoal.rx_max_coalesced_frames_high }, \ - { "tx-usecs-high", CMDL_S32, &coal_tx_usec_high_wanted, \ - &__ecoal.tx_coalesce_usecs_high }, \ - { "tx-frames-high", CMDL_S32, &coal_tx_frames_high_wanted, \ - &__ecoal.tx_max_coalesced_frames_high }, \ + { \ + .name = "adaptive-rx", \ + .type = CMDL_BOOL, \ + .wanted_val = &coal_adaptive_rx_wanted, \ + .ioctl_val = &__ecoal.use_adaptive_rx_coalesce, \ + }, \ + { \ + .name = "adaptive-tx", \ + .type = CMDL_BOOL, \ + .wanted_val = &coal_adaptive_tx_wanted, \ + .ioctl_val = &__ecoal.use_adaptive_tx_coalesce, \ + }, \ + { \ + .name = "sample-interval", \ + .type = CMDL_S32, \ + .wanted_val = &coal_sample_rate_wanted, \ + .ioctl_val = &__ecoal.rate_sample_interval, \ + }, \ + { \ + .name = "stats-block-usecs", \ + .type = CMDL_S32, \ + .wanted_val = &coal_stats_wanted, \ + .ioctl_val = &__ecoal.stats_block_coalesce_usecs, \ + }, \ + { \ + .name = "pkt-rate-low", \ + .type = CMDL_S32, \ + .wanted_val = &coal_pkt_rate_low_wanted, \ + .ioctl_val = &__ecoal.pkt_rate_low, \ + }, \ + { \ + .name = "pkt-rate-high", \ + .type = CMDL_S32, \ + .wanted_val = &coal_pkt_rate_high_wanted, \ + .ioctl_val = &__ecoal.pkt_rate_high, \ + }, \ + { \ + .name = "rx-usecs", \ + .type = CMDL_S32, \ + .wanted_val = &coal_rx_usec_wanted, \ + .ioctl_val = &__ecoal.rx_coalesce_usecs, \ + }, \ + { \ + .name = "rx-frames", \ + .type = CMDL_S32, \ + .wanted_val = &coal_rx_frames_wanted, \ + .ioctl_val = &__ecoal.rx_max_coalesced_frames, \ + }, \ + { \ + .name = "rx-usecs-irq", \ + .type = CMDL_S32, \ + .wanted_val = &coal_rx_usec_irq_wanted, \ + .ioctl_val = &__ecoal.rx_coalesce_usecs_irq, \ + }, \ + { \ + .name = "rx-frames-irq", \ + .type = CMDL_S32, \ + .wanted_val = &coal_rx_frames_irq_wanted, \ + .ioctl_val = &__ecoal.rx_max_coalesced_frames_irq, \ + }, \ + { \ + .name = "tx-usecs", \ + .type = CMDL_S32, \ + .wanted_val = &coal_tx_usec_wanted, \ + .ioctl_val = &__ecoal.tx_coalesce_usecs, \ + }, \ + { \ + .name = "tx-frames", \ + .type = CMDL_S32, \ + .wanted_val = &coal_tx_frames_wanted, \ + .ioctl_val = &__ecoal.tx_max_coalesced_frames, \ + }, \ + { \ + .name = "tx-usecs-irq", \ + .type = CMDL_S32, \ + .wanted_val = &coal_tx_usec_irq_wanted, \ + .ioctl_val = &__ecoal.tx_coalesce_usecs_irq, \ + }, \ + { \ + .name = "tx-frames-irq", \ + .type = CMDL_S32, \ + .wanted_val = &coal_tx_frames_irq_wanted, \ + .ioctl_val = &__ecoal.tx_max_coalesced_frames_irq, \ + }, \ + { \ + .name = "rx-usecs-low", \ + .type = CMDL_S32, \ + .wanted_val = &coal_rx_usec_low_wanted, \ + .ioctl_val = &__ecoal.rx_coalesce_usecs_low, \ + }, \ + { \ + .name = "rx-frames-low", \ + .type = CMDL_S32, \ + .wanted_val = &coal_rx_frames_low_wanted, \ + .ioctl_val = &__ecoal.rx_max_coalesced_frames_low, \ + }, \ + { \ + .name = "tx-usecs-low", \ + .type = CMDL_S32, \ + .wanted_val = &coal_tx_usec_low_wanted, \ + .ioctl_val = &__ecoal.tx_coalesce_usecs_low, \ + }, \ + { \ + .name = "tx-frames-low", \ + .type = CMDL_S32, \ + .wanted_val = &coal_tx_frames_low_wanted, \ + .ioctl_val = &__ecoal.tx_max_coalesced_frames_low, \ + }, \ + { \ + .name = "rx-usecs-high", \ + .type = CMDL_S32, \ + .wanted_val = &coal_rx_usec_high_wanted, \ + .ioctl_val = &__ecoal.rx_coalesce_usecs_high, \ + }, \ + { \ + .name = "rx-frames-high", \ + .type = CMDL_S32, \ + .wanted_val = &coal_rx_frames_high_wanted, \ + .ioctl_val = &__ecoal.rx_max_coalesced_frames_high,\ + }, \ + { \ + .name = "tx-usecs-high", \ + .type = CMDL_S32, \ + .wanted_val = &coal_tx_usec_high_wanted, \ + .ioctl_val = &__ecoal.tx_coalesce_usecs_high, \ + }, \ + { \ + .name = "tx-frames-high", \ + .type = CMDL_S32, \ + .wanted_val = &coal_tx_frames_high_wanted, \ + .ioctl_val = &__ecoal.tx_max_coalesced_frames_high,\ + }, \ } static int do_scoalesce(struct cmd_context *ctx) @@ -2181,7 +2356,7 @@ get_features(struct cmd_context *ctx, const struct feature_defs *defs) state->off_flags = 0; - for (i = 0; i < ARRAY_SIZE(off_flag_def); i++) { + for (i = 0; i < OFF_FLAG_DEF_SIZE; i++) { value = off_flag_def[i].value; if (!off_flag_def[i].get_cmd) continue; @@ -2264,12 +2439,13 @@ static int do_sfeatures(struct cmd_context *ctx) int any_changed = 0, any_mismatch = 0; u32 off_flags_wanted = 0; u32 off_flags_mask = 0; - struct ethtool_sfeatures *efeatures; + struct ethtool_sfeatures *efeatures = NULL; + struct feature_state *old_state = NULL; + struct feature_state *new_state = NULL; struct cmdline_info *cmdline_features; - struct feature_state *old_state, *new_state; struct ethtool_value eval; + unsigned int i, j; int err, rc; - int i, j; defs = get_feature_defs(ctx); if (!defs) { @@ -2290,33 +2466,37 @@ static int do_sfeatures(struct cmd_context *ctx) memset(efeatures->features, 0, FEATURE_BITS_TO_BLOCKS(defs->n_features) * sizeof(efeatures->features[0])); - } else { - efeatures = NULL; } /* Generate cmdline_info for legacy flags and kernel-named * features, and parse our arguments. */ - cmdline_features = calloc(ARRAY_SIZE(off_flag_def) + defs->n_features, + cmdline_features = calloc(2 * OFF_FLAG_DEF_SIZE + defs->n_features, sizeof(cmdline_features[0])); if (!cmdline_features) { perror("Cannot parse arguments"); rc = 1; goto err; } - for (i = 0; i < ARRAY_SIZE(off_flag_def); i++) + j = 0; + for (i = 0; i < OFF_FLAG_DEF_SIZE; i++) { flag_to_cmdline_info(off_flag_def[i].short_name, off_flag_def[i].value, &off_flags_wanted, &off_flags_mask, - &cmdline_features[i]); + &cmdline_features[j++]); + flag_to_cmdline_info(off_flag_def[i].long_name, + off_flag_def[i].value, + &off_flags_wanted, &off_flags_mask, + &cmdline_features[j++]); + } for (i = 0; i < defs->n_features; i++) flag_to_cmdline_info( defs->def[i].name, FEATURE_FIELD_FLAG(i), &FEATURE_WORD(efeatures->features, i, requested), &FEATURE_WORD(efeatures->features, i, valid), - &cmdline_features[ARRAY_SIZE(off_flag_def) + i]); + &cmdline_features[j++]); parse_generic_cmdline(ctx, &any_changed, cmdline_features, - ARRAY_SIZE(off_flag_def) + defs->n_features); + 2 * OFF_FLAG_DEF_SIZE + defs->n_features); free(cmdline_features); if (!any_changed) { @@ -2336,14 +2516,14 @@ static int do_sfeatures(struct cmd_context *ctx) * related features that the user did not specify and that * are not fixed. Warn if all related features are fixed. */ - for (i = 0; i < ARRAY_SIZE(off_flag_def); i++) { + for (i = 0; i < OFF_FLAG_DEF_SIZE; i++) { int fixed = 1; if (!(off_flags_mask & off_flag_def[i].value)) continue; for (j = 0; j < defs->n_features; j++) { - if (defs->def[j].off_flag_index != i || + if (defs->def[j].off_flag_index != (int)i || !FEATURE_BIT_IS_SET( old_state->features.features, j, available) || @@ -2377,7 +2557,7 @@ static int do_sfeatures(struct cmd_context *ctx) goto err; } } else { - for (i = 0; i < ARRAY_SIZE(off_flag_def); i++) { + for (i = 0; i < OFF_FLAG_DEF_SIZE; i++) { if (!off_flag_def[i].set_cmd) continue; if (off_flags_mask & off_flag_def[i].value) { @@ -2444,9 +2624,11 @@ static int do_sfeatures(struct cmd_context *ctx) rc = 0; err: + free(new_state); + free(old_state); free(defs); - if (efeatures) - free(efeatures); + free(efeatures); + return rc; } @@ -2497,8 +2679,8 @@ do_ioctl_glinksettings(struct cmd_context *ctx) if (link_usettings == NULL) return NULL; - /* keep transceiver 0 */ memcpy(&link_usettings->base, &ecmd.req, sizeof(link_usettings->base)); + link_usettings->deprecated.transceiver = ecmd.req.transceiver; /* copy link mode bitmaps */ u32_offs = 0; @@ -2750,9 +2932,9 @@ static int do_sset(struct cmd_context *ctx) u32 msglvl_wanted = 0; u32 msglvl_mask = 0; struct cmdline_info cmdline_msglvl[n_flags_msglvl]; - int argc = ctx->argc; + unsigned int argc = ctx->argc; char **argp = ctx->argp; - int i; + unsigned int i; int err = 0; for (i = 0; i < n_flags_msglvl; i++) @@ -2936,6 +3118,9 @@ static int do_sset(struct cmd_context *ctx) link_usettings = do_ioctl_glinksettings(ctx); if (link_usettings == NULL) link_usettings = do_ioctl_gset(ctx); + else + memset(&link_usettings->deprecated, 0, + sizeof(link_usettings->deprecated)); if (link_usettings == NULL) { perror("Cannot get current device settings"); err = -1; @@ -3112,9 +3297,21 @@ static int do_gregs(struct cmd_context *ctx) int gregs_dump_hex = 0; char *gregs_dump_file = NULL; struct cmdline_info cmdline_gregs[] = { - { "raw", CMDL_BOOL, &gregs_dump_raw, NULL }, - { "hex", CMDL_BOOL, &gregs_dump_hex, NULL }, - { "file", CMDL_STR, &gregs_dump_file, NULL }, + { + .name = "raw", + .type = CMDL_BOOL, + .wanted_val = &gregs_dump_raw, + }, + { + .name = "hex", + .type = CMDL_BOOL, + .wanted_val = &gregs_dump_hex, + }, + { + .name = "file", + .type = CMDL_STR, + .wanted_val = &gregs_dump_file, + }, }; int err; struct ethtool_drvinfo drvinfo; @@ -3208,11 +3405,25 @@ static int do_geeprom(struct cmd_context *ctx) int geeprom_changed = 0; int geeprom_dump_raw = 0; u32 geeprom_offset = 0; - u32 geeprom_length = -1; + u32 geeprom_length = 0; + int geeprom_length_seen = 0; struct cmdline_info cmdline_geeprom[] = { - { "offset", CMDL_U32, &geeprom_offset, NULL }, - { "length", CMDL_U32, &geeprom_length, NULL }, - { "raw", CMDL_BOOL, &geeprom_dump_raw, NULL }, + { + .name = "offset", + .type = CMDL_U32, + .wanted_val = &geeprom_offset, + }, + { + .name = "length", + .type = CMDL_U32, + .wanted_val = &geeprom_length, + .seen_val = &geeprom_length_seen, + }, + { + .name = "raw", + .type = CMDL_BOOL, + .wanted_val = &geeprom_dump_raw, + }, }; int err; struct ethtool_drvinfo drvinfo; @@ -3228,7 +3439,7 @@ static int do_geeprom(struct cmd_context *ctx) return 74; } - if (geeprom_length == -1) + if (!geeprom_length_seen) geeprom_length = drvinfo.eedump_len; if (drvinfo.eedump_len < geeprom_offset + geeprom_length) @@ -3258,16 +3469,34 @@ static int do_seeprom(struct cmd_context *ctx) { int seeprom_changed = 0; u32 seeprom_magic = 0; - u32 seeprom_length = -1; + u32 seeprom_length = 0; u32 seeprom_offset = 0; u8 seeprom_value = 0; + int seeprom_length_seen = 0; int seeprom_value_seen = 0; struct cmdline_info cmdline_seeprom[] = { - { "magic", CMDL_U32, &seeprom_magic, NULL }, - { "offset", CMDL_U32, &seeprom_offset, NULL }, - { "length", CMDL_U32, &seeprom_length, NULL }, - { "value", CMDL_U8, &seeprom_value, NULL, - 0, &seeprom_value_seen }, + { + .name = "magic", + .type = CMDL_U32, + .wanted_val = &seeprom_magic, + }, + { + .name = "offset", + .type = CMDL_U32, + .wanted_val = &seeprom_offset, + }, + { + .name = "length", + .type = CMDL_U32, + .wanted_val = &seeprom_length, + .seen_val = &seeprom_length_seen, + }, + { + .name = "value", + .type = CMDL_U8, + .wanted_val = &seeprom_value, + .seen_val = &seeprom_value_seen, + }, }; int err; struct ethtool_drvinfo drvinfo; @@ -3286,7 +3515,7 @@ static int do_seeprom(struct cmd_context *ctx) if (seeprom_value_seen) seeprom_length = 1; - if (seeprom_length == -1) + if (!seeprom_length_seen) seeprom_length = drvinfo.eedump_len; if (drvinfo.eedump_len < seeprom_offset + seeprom_length) { @@ -3691,7 +3920,7 @@ static int do_grxfh(struct cmd_context *ctx) struct ethtool_rxfh *rss; u32 rss_context = 0; u32 i, indir_bytes; - int arg_num = 0; + unsigned int arg_num = 0; char *hkey; int err; @@ -3889,7 +4118,7 @@ static int do_srxfh(struct cmd_context *ctx) char *hfunc_name = NULL; char *hkey = NULL; int err = 0; - int i; + unsigned int i; u32 arg_num = 0, indir_bytes = 0; u32 req_hfunc = 0; u32 entry_size = sizeof(rss_head.rss_config[0]); @@ -4155,7 +4384,8 @@ static int do_flash(struct cmd_context *ctx) static int do_permaddr(struct cmd_context *ctx) { - int i, err; + unsigned int i; + int err; struct ethtool_perm_addr *epaddr; epaddr = malloc(sizeof(struct ethtool_perm_addr) + MAX_ADDR_LEN); @@ -4562,17 +4792,35 @@ static int do_getmodule(struct cmd_context *ctx) struct ethtool_modinfo modinfo; struct ethtool_eeprom *eeprom; u32 geeprom_offset = 0; - u32 geeprom_length = -1; + u32 geeprom_length = 0; int geeprom_changed = 0; int geeprom_dump_raw = 0; int geeprom_dump_hex = 0; + int geeprom_length_seen = 0; int err; struct cmdline_info cmdline_geeprom[] = { - { "offset", CMDL_U32, &geeprom_offset, NULL }, - { "length", CMDL_U32, &geeprom_length, NULL }, - { "raw", CMDL_BOOL, &geeprom_dump_raw, NULL }, - { "hex", CMDL_BOOL, &geeprom_dump_hex, NULL }, + { + .name = "offset", + .type = CMDL_U32, + .wanted_val = &geeprom_offset, + }, + { + .name = "length", + .type = CMDL_U32, + .wanted_val = &geeprom_length, + .seen_val = &geeprom_length_seen, + }, + { + .name = "raw", + .type = CMDL_BOOL, + .wanted_val = &geeprom_dump_raw, + }, + { + .name = "hex", + .type = CMDL_BOOL, + .wanted_val = &geeprom_dump_hex, + }, }; parse_generic_cmdline(ctx, &geeprom_changed, @@ -4590,7 +4838,7 @@ static int do_getmodule(struct cmd_context *ctx) return 1; } - if (geeprom_length == -1) + if (!geeprom_length_seen) geeprom_length = modinfo.eeprom_len; if (modinfo.eeprom_len < geeprom_offset + geeprom_length) @@ -4607,7 +4855,12 @@ static int do_getmodule(struct cmd_context *ctx) eeprom->offset = geeprom_offset; err = send_ioctl(ctx, eeprom); if (err < 0) { + int saved_errno = errno; + perror("Cannot get Module EEPROM data"); + if (saved_errno == ENODEV || saved_errno == EIO || + saved_errno == ENXIO) + fprintf(stderr, "SFP module not in cage?\n"); free(eeprom); return 1; } @@ -4684,10 +4937,30 @@ static int do_seee(struct cmd_context *ctx) int change = -1, change2 = 0; struct ethtool_eee eeecmd; struct cmdline_info cmdline_eee[] = { - { "advertise", CMDL_U32, &adv_c, &eeecmd.advertised }, - { "tx-lpi", CMDL_BOOL, &lpi_c, &eeecmd.tx_lpi_enabled }, - { "tx-timer", CMDL_U32, &lpi_time_c, &eeecmd.tx_lpi_timer}, - { "eee", CMDL_BOOL, &eee_c, &eeecmd.eee_enabled}, + { + .name = "advertise", + .type = CMDL_U32, + .wanted_val = &adv_c, + .ioctl_val = &eeecmd.advertised, + }, + { + .name = "tx-lpi", + .type = CMDL_BOOL, + .wanted_val = &lpi_c, + .ioctl_val = &eeecmd.tx_lpi_enabled, + }, + { + .name = "tx-timer", + .type = CMDL_U32, + .wanted_val = &lpi_time_c, + .ioctl_val = &eeecmd.tx_lpi_timer, + }, + { + .name = "eee", + .type = CMDL_BOOL, + .wanted_val = &eee_c, + .ioctl_val = &eeecmd.eee_enabled, + }, }; if (ctx->argc == 0) @@ -4715,9 +4988,185 @@ static int do_seee(struct cmd_context *ctx) return 0; } +/* copy of net/ethtool/common.c */ +char +tunable_strings[__ETHTOOL_TUNABLE_COUNT][ETH_GSTRING_LEN] = { + [ETHTOOL_ID_UNSPEC] = "Unspec", + [ETHTOOL_RX_COPYBREAK] = "rx-copybreak", + [ETHTOOL_TX_COPYBREAK] = "tx-copybreak", + [ETHTOOL_PFC_PREVENTION_TOUT] = "pfc-prevention-tout", +}; + +union ethtool_tunable_info_val { + uint8_t u8; + uint16_t u16; + uint32_t u32; + uint64_t u64; + int8_t s8; + int16_t s16; + int32_t s32; + int64_t s64; +}; + +struct ethtool_tunable_info { + enum tunable_id t_id; + enum tunable_type_id t_type_id; + size_t size; + cmdline_type_t type; + union ethtool_tunable_info_val wanted; + int seen; +}; + +static struct ethtool_tunable_info tunables_info[] = { + { .t_id = ETHTOOL_RX_COPYBREAK, + .t_type_id = ETHTOOL_TUNABLE_U32, + .size = sizeof(u32), + .type = CMDL_U32, + }, + { .t_id = ETHTOOL_TX_COPYBREAK, + .t_type_id = ETHTOOL_TUNABLE_U32, + .size = sizeof(u32), + .type = CMDL_U32, + }, + { .t_id = ETHTOOL_PFC_PREVENTION_TOUT, + .t_type_id = ETHTOOL_TUNABLE_U16, + .size = sizeof(u16), + .type = CMDL_U16, + }, +}; +#define TUNABLES_INFO_SIZE ARRAY_SIZE(tunables_info) + +static int do_stunable(struct cmd_context *ctx) +{ + struct cmdline_info cmdline_tunable[TUNABLES_INFO_SIZE]; + struct ethtool_tunable_info *tinfo = tunables_info; + int changed = 0; + unsigned int i; + + for (i = 0; i < TUNABLES_INFO_SIZE; i++) { + cmdline_tunable[i].name = tunable_strings[tinfo[i].t_id]; + cmdline_tunable[i].type = tinfo[i].type; + cmdline_tunable[i].wanted_val = &tinfo[i].wanted; + cmdline_tunable[i].seen_val = &tinfo[i].seen; + } + + parse_generic_cmdline(ctx, &changed, cmdline_tunable, TUNABLES_INFO_SIZE); + if (!changed) + exit_bad_args(); + + for (i = 0; i < TUNABLES_INFO_SIZE; i++) { + struct ethtool_tunable *tuna; + size_t size; + int ret; + + if (!tinfo[i].seen) + continue; + + size = sizeof(*tuna) + tinfo[i].size; + tuna = calloc(1, size); + if (!tuna) { + perror(tunable_strings[tinfo[i].t_id]); + return 1; + } + tuna->cmd = ETHTOOL_STUNABLE; + tuna->id = tinfo[i].t_id; + tuna->type_id = tinfo[i].t_type_id; + tuna->len = tinfo[i].size; + memcpy(tuna->data, &tinfo[i].wanted, tuna->len); + ret = send_ioctl(ctx, tuna); + if (ret) { + perror(tunable_strings[tuna->id]); + return ret; + } + free(tuna); + } + return 0; +} + +static void print_tunable(struct ethtool_tunable *tuna) +{ + char *name = tunable_strings[tuna->id]; + union ethtool_tunable_info_val *val; + + val = (union ethtool_tunable_info_val *)tuna->data; + switch (tuna->type_id) { + case ETHTOOL_TUNABLE_U8: + fprintf(stdout, "%s: %" PRIu8 "\n", name, val->u8); + break; + case ETHTOOL_TUNABLE_U16: + fprintf(stdout, "%s: %" PRIu16 "\n", name, val->u16); + break; + case ETHTOOL_TUNABLE_U32: + fprintf(stdout, "%s: %" PRIu32 "\n", name, val->u32); + break; + case ETHTOOL_TUNABLE_U64: + fprintf(stdout, "%s: %" PRIu64 "\n", name, val->u64); + break; + case ETHTOOL_TUNABLE_S8: + fprintf(stdout, "%s: %" PRId8 "\n", name, val->s8); + break; + case ETHTOOL_TUNABLE_S16: + fprintf(stdout, "%s: %" PRId16 "\n", name, val->s16); + break; + case ETHTOOL_TUNABLE_S32: + fprintf(stdout, "%s: %" PRId32 "\n", name, val->s32); + break; + case ETHTOOL_TUNABLE_S64: + fprintf(stdout, "%s: %" PRId64 "\n", name, val->s64); + break; + default: + fprintf(stdout, "%s: Unknown format\n", name); + } +} + +static int do_gtunable(struct cmd_context *ctx) +{ + struct ethtool_tunable_info *tinfo = tunables_info; + char **argp = ctx->argp; + unsigned int argc = ctx->argc; + unsigned int i, j; + + if (argc < 1) + exit_bad_args(); + + for (i = 0; i < argc; i++) { + int valid = 0; + + for (j = 0; j < TUNABLES_INFO_SIZE; j++) { + char *ts = tunable_strings[tinfo[j].t_id]; + struct ethtool_tunable *tuna; + int ret; + + if (strcmp(argp[i], ts)) + continue; + valid = 1; + + tuna = calloc(1, sizeof(*tuna) + tinfo[j].size); + if (!tuna) { + perror(ts); + return 1; + } + tuna->cmd = ETHTOOL_GTUNABLE; + tuna->id = tinfo[j].t_id; + tuna->type_id = tinfo[j].t_type_id; + tuna->len = tinfo[j].size; + ret = send_ioctl(ctx, tuna); + if (ret) { + fprintf(stderr, "%s: Cannot get tunable\n", ts); + return ret; + } + print_tunable(tuna); + free(tuna); + } + if (!valid) + exit_bad_args(); + } + return 0; +} + static int do_get_phy_tunable(struct cmd_context *ctx) { - int argc = ctx->argc; + unsigned int argc = ctx->argc; char **argp = ctx->argp; if (argc < 1) @@ -4821,9 +5270,9 @@ static int do_reset(struct cmd_context *ctx) { struct ethtool_value resetinfo; __u32 data; - int argc = ctx->argc; + unsigned int argc = ctx->argc; char **argp = ctx->argp; - int i; + unsigned int i; if (argc == 0) exit_bad_args(); @@ -5074,7 +5523,8 @@ static int fecmode_str_to_type(const char *str) return ETHTOOL_FEC_RS; if (!strcasecmp(str, "baser")) return ETHTOOL_FEC_BASER; - + if (!strcasecmp(str, "llrs")) + return ETHTOOL_FEC_LLRS; return 0; } @@ -5110,7 +5560,8 @@ static int do_sfec(struct cmd_context *ctx) enum { ARG_NONE, ARG_ENCODING } state = ARG_NONE; struct ethtool_fecparam feccmd; int fecmode = 0, newmode; - int rv, i; + unsigned int i; + int rv; for (i = 0; i < ctx->argc; i++) { if (!strcmp(ctx->argp[i], "encoding")) { @@ -5157,7 +5608,7 @@ struct option { const char *opts; bool no_dev; int (*func)(struct cmd_context *); - int (*nlfunc)(struct cmd_context *); + nl_func_t nlfunc; const char *help; const char *xhelp; }; @@ -5179,15 +5630,18 @@ static const struct option args[] = { " [ wol %d[/%d] | p|u|m|b|a|g|s|f|d... ]\n" " [ sopass %x:%x:%x:%x:%x:%x ]\n" " [ msglvl %d[/%d] | type on|off ... [--] ]\n" + " [ master-slave master-preferred|slave-preferred|master-force|slave-force ]\n" }, { .opts = "-a|--show-pause", .func = do_gpause, + .nlfunc = nl_gpause, .help = "Show pause options" }, { .opts = "-A|--pause", .func = do_spause, + .nlfunc = nl_spause, .help = "Set pause options", .xhelp = " [ autoneg on|off ]\n" " [ rx on|off ]\n" @@ -5196,11 +5650,13 @@ static const struct option args[] = { { .opts = "-c|--show-coalesce", .func = do_gcoalesce, + .nlfunc = nl_gcoalesce, .help = "Show coalesce options" }, { .opts = "-C|--coalesce", .func = do_scoalesce, + .nlfunc = nl_scoalesce, .help = "Set coalesce options", .xhelp = " [adaptive-rx on|off]\n" " [adaptive-tx on|off]\n" @@ -5228,11 +5684,13 @@ static const struct option args[] = { { .opts = "-g|--show-ring", .func = do_gring, + .nlfunc = nl_gring, .help = "Query RX/TX ring parameters" }, { .opts = "-G|--set-ring", .func = do_sring, + .nlfunc = nl_sring, .help = "Set RX/TX ring parameters", .xhelp = " [ rx N ]\n" " [ rx-mini N ]\n" @@ -5242,11 +5700,13 @@ static const struct option args[] = { { .opts = "-k|--show-features|--show-offload", .func = do_gfeatures, + .nlfunc = nl_gfeatures, .help = "Get state of protocol offload and other features" }, { .opts = "-K|--features|--offload", .func = do_sfeatures, + .nlfunc = nl_sfeatures, .help = "Set protocol offload and other features", .xhelp = " FEATURE on|off ...\n" }, @@ -5345,6 +5805,7 @@ static const struct option args[] = { { .opts = "-T|--show-time-stamping", .func = do_tsinfo, + .nlfunc = nl_tsinfo, .help = "Show time stamping capabilities" }, { @@ -5390,11 +5851,13 @@ static const struct option args[] = { { .opts = "-l|--show-channels", .func = do_gchannels, + .nlfunc = nl_gchannels, .help = "Query Channels" }, { .opts = "-L|--set-channels", .func = do_schannels, + .nlfunc = nl_schannels, .help = "Set Channels", .xhelp = " [ rx N ]\n" " [ tx N ]\n" @@ -5404,11 +5867,13 @@ static const struct option args[] = { { .opts = "--show-priv-flags", .func = do_gprivflags, + .nlfunc = nl_gprivflags, .help = "Query private flags" }, { .opts = "--set-priv-flags", .func = do_sprivflags, + .nlfunc = nl_sprivflags, .help = "Set private flags", .xhelp = " FLAG on|off ...\n" }, @@ -5424,11 +5889,13 @@ static const struct option args[] = { { .opts = "--show-eee", .func = do_geee, + .nlfunc = nl_geee, .help = "Show EEE settings", }, { .opts = "--set-eee", .func = do_seee, + .nlfunc = nl_seee, .help = "Set EEE settings", .xhelp = " [ eee on|off ]\n" " [ advertise %x ]\n" @@ -5452,6 +5919,22 @@ static const struct option args[] = { " [ energy-detect-power-down ]\n" }, { + .opts = "--get-tunable", + .func = do_gtunable, + .help = "Get tunable", + .xhelp = " [ rx-copybreak ]\n" + " [ tx-copybreak ]\n" + " [ pfc-precention-tout ]\n" + }, + { + .opts = "--set-tunable", + .func = do_stunable, + .help = "Set tunable", + .xhelp = " [ rx-copybreak N]\n" + " [ tx-copybreak N]\n" + " [ pfc-precention-tout N]\n" + }, + { .opts = "--reset", .func = do_reset, .help = "Reset components", @@ -5486,7 +5969,7 @@ static const struct option args[] = { .opts = "--set-fec", .func = do_sfec, .help = "Set FEC settings", - .xhelp = " [ encoding auto|off|rs|baser [...]]\n" + .xhelp = " [ encoding auto|off|rs|baser|llrs [...]]\n" }, { .opts = "-Q|--per-queue", @@ -5496,6 +5979,25 @@ static const struct option args[] = { " [queue_mask %x] SUB_COMMAND\n", }, { + .opts = "--cable-test", + .nlfunc = nl_cable_test, + .help = "Perform a cable test", + }, + { + .opts = "--cable-test-tdr", + .nlfunc = nl_cable_test_tdr, + .help = "Print cable test time domain reflectrometery data", + .xhelp = " [ first N ]\n" + " [ last N ]\n" + " [ step N ]\n" + " [ pair N ]\n" + }, + { + .opts = "--show-tunnels", + .nlfunc = nl_gtunnels, + .help = "Show NIC tunnel offload information", + }, + { .opts = "-h|--help", .no_dev = true, .func = show_usage, @@ -5510,7 +6012,7 @@ static const struct option args[] = { {} }; -static int show_usage(struct cmd_context *ctx maybe_unused) +static int show_usage(struct cmd_context *ctx __maybe_unused) { int i; @@ -5518,10 +6020,10 @@ static int show_usage(struct cmd_context *ctx maybe_unused) fprintf(stdout, PACKAGE " version " VERSION "\n"); fprintf(stdout, "Usage:\n" - " ethtool [ --debug MASK ] DEVNAME\t" + " ethtool [ FLAGS ] DEVNAME\t" "Display standard information about device\n"); for (i = 0; args[i].opts; i++) { - fputs(" ethtool [ --debug MASK ] ", stdout); + fputs(" ethtool [ FLAGS ] ", stdout); fprintf(stdout, "%s %s\t%s\n", args[i].opts, args[i].no_dev ? "\t" : "DEVNAME", @@ -5530,6 +6032,11 @@ static int show_usage(struct cmd_context *ctx maybe_unused) fputs(args[i].xhelp, stdout); } nl_monitor_usage(); + fprintf(stdout, "\n"); + fprintf(stdout, "FLAGS:\n"); + fprintf(stdout, " --debug MASK turn on debugging messages\n"); + fprintf(stdout, " --json enable JSON output format (not supported by all commands)\n"); + fprintf(stdout, " -I|--include-statistics request device statistics related to the command (not supported by all commands)\n"); return 0; } @@ -5673,6 +6180,8 @@ static int do_perqueue(struct cmd_context *ctx) "The sub commands will be applied to all %d queues\n", n_queues); } else { + if (ctx->argc <= 2) + exit_bad_args(); ctx->argc--; ctx->argp++; if (parse_hex_u32_bitmap(*ctx->argp, MAX_NUM_QUEUE, @@ -5731,6 +6240,11 @@ static int ioctl_init(struct cmd_context *ctx, bool no_dev) ctx->fd = -1; return 0; } + if (strlen(ctx->devname) >= IFNAMSIZ) { + fprintf(stderr, "Device name longer than %u characters\n", + IFNAMSIZ - 1); + exit_bad_args(); + } /* Setup our control structures. */ memset(&ctx->ifr, 0, sizeof(ctx->ifr)); @@ -5750,9 +6264,9 @@ static int ioctl_init(struct cmd_context *ctx, bool no_dev) int main(int argc, char **argp) { - int (*nlfunc)(struct cmd_context *) = NULL; int (*func)(struct cmd_context *); struct cmd_context ctx = {}; + nl_func_t nlfunc = NULL; bool no_dev; int ret; int k; @@ -5763,31 +6277,41 @@ int main(int argc, char **argp) argp++; argc--; - if (*argp && !strcmp(*argp, "--debug")) { - char *eptr; + while (true) { + if (*argp && !strcmp(*argp, "--debug")) { + char *eptr; - if (argc < 2) - exit_bad_args(); - ctx.debug = strtoul(argp[1], &eptr, 0); - if (!argp[1][0] || *eptr) - exit_bad_args(); + if (argc < 2) + exit_bad_args(); + ctx.debug = strtoul(argp[1], &eptr, 0); + if (!argp[1][0] || *eptr) + exit_bad_args(); - argp += 2; - argc -= 2; + argp += 2; + argc -= 2; + continue; + } + if (*argp && !strcmp(*argp, "--json")) { + ctx.json = true; + argp += 1; + argc -= 1; + continue; + } + if (*argp && (!strcmp(*argp, "--include-statistics") || + !strcmp(*argp, "-I"))) { + ctx.show_stats = true; + argp += 1; + argc -= 1; + continue; + } + break; } -#ifdef ETHTOOL_ENABLE_NETLINK if (*argp && !strcmp(*argp, "--monitor")) { - if (netlink_init(&ctx)) { - fprintf(stderr, - "Option --monitor is only available with netlink.\n"); - return 1; - } else { - ctx.argp = ++argp; - ctx.argc = --argc; - return nl_monitor(&ctx); - } + ctx.argp = ++argp; + ctx.argc = --argc; + ret = nl_monitor(&ctx); + return ret ? 1 : 0; } -#endif /* First argument must be either a valid option or a device * name to get settings for (which we don't expect to begin @@ -5812,40 +6336,17 @@ int main(int argc, char **argp) no_dev = false; opt_found: - if (nlfunc) { - if (netlink_init(&ctx)) - nlfunc = NULL; /* fallback to ioctl() */ - } - if (!no_dev) { ctx.devname = *argp++; argc--; - /* netlink supports altnames, we will have to recheck against - * IFNAMSIZ later in case of fallback to ioctl - */ - if (!ctx.devname || strlen(ctx.devname) >= ALTIFNAMSIZ) { - netlink_done(&ctx); + if (!ctx.devname) exit_bad_args(); - } } - ctx.argc = argc; ctx.argp = argp; + netlink_run_handler(&ctx, nlfunc, !func); - if (nlfunc) { - ret = nlfunc(&ctx); - netlink_done(&ctx); - if ((ret != -EOPNOTSUPP) || !func) - return (ret >= 0) ? ret : 1; - } - - if (ctx.devname && strlen(ctx.devname) >= IFNAMSIZ) { - fprintf(stderr, - "ethtool: device names longer than %u characters are only allowed with netlink\n", - IFNAMSIZ - 1); - exit_bad_args(); - } ret = ioctl_init(&ctx, no_dev); if (ret) return ret; diff --git a/ethtool.spec.in b/ethtool.spec.in index 9c01b07..75f9be6 100644 --- a/ethtool.spec.in +++ b/ethtool.spec.in @@ -34,6 +34,7 @@ make install DESTDIR=${RPM_BUILD_ROOT} %defattr(-,root,root) %{_sbindir}/ethtool %{_mandir}/man8/ethtool.8* +%{_datadir}/bash-completion/completions/ethtool %doc AUTHORS COPYING NEWS README @@ -194,11 +194,11 @@ static void fec_dump_reg_v2(int reg, u32 val) #undef FIELD #undef REG -int fec_dump_regs(struct ethtool_drvinfo *info maybe_unused, +int fec_dump_regs(struct ethtool_drvinfo *info __maybe_unused, struct ethtool_regs *regs) { const u32 *data = (u32 *)regs->data; - int offset; + unsigned int offset; u32 val; for (offset = 0; offset < regs->len; offset += 4) { @@ -47,7 +47,7 @@ struct fec { (unsigned long)(offsetof(struct fec, x)), \ #x, f->x) -int fec_8xx_dump_regs(struct ethtool_drvinfo *info maybe_unused, +int fec_8xx_dump_regs(struct ethtool_drvinfo *info __maybe_unused, struct ethtool_regs *regs) { struct fec *f = (struct fec *)regs->data; @@ -2,7 +2,7 @@ #include <stdio.h> #include "internal.h" -int fjes_dump_regs(struct ethtool_drvinfo *info maybe_unused, +int fjes_dump_regs(struct ethtool_drvinfo *info __maybe_unused, struct ethtool_regs *regs) { u32 *regs_buff = (u32 *)regs->data; @@ -238,7 +238,7 @@ static void *print_mal_regs(void *buf) { struct emac_ethtool_regs_subhdr *hdr = buf; struct mal_regs *p = (struct mal_regs *)(hdr + 1); - int i; + unsigned int i; printf("MAL%d Registers\n", hdr->index); printf("-----------------\n"); @@ -314,7 +314,7 @@ static void *print_tah_regs(void *buf) return p + 1; } -int ibm_emac_dump_regs(struct ethtool_drvinfo *info maybe_unused, +int ibm_emac_dump_regs(struct ethtool_drvinfo *info __maybe_unused, struct ethtool_regs *regs) { struct emac_ethtool_regs_hdr *hdr = @@ -88,7 +88,7 @@ #define E1000_TCTL_RTLC 0x01000000 /* Re-transmit on late collision */ #define E1000_TCTL_NRTU 0x02000000 /* No Re-transmit on underrun */ -int igb_dump_regs(struct ethtool_drvinfo *info maybe_unused, +int igb_dump_regs(struct ethtool_drvinfo *info __maybe_unused, struct ethtool_regs *regs) { u32 *regs_buff = (u32 *)regs->data; @@ -0,0 +1,284 @@ +/* Copyright (c) 2020 Intel Corporation */ +#include <stdio.h> +#include "internal.h" + +#define RAH_RAH 0x0000FFFF +#define RAH_ASEL 0x00010000 +#define RAH_QSEL 0x000C0000 +#define RAH_QSEL_EN 0x10000000 +#define RAH_AV 0x80000000 +#define RCTL_RXEN 0x00000002 +#define RCTL_SBP 0x00000004 +#define RCTL_UPE 0x00000008 +#define RCTL_MPE 0x00000010 +#define RCTL_LPE 0x00000020 +#define RCTL_LBM 0x000000C0 +#define RCTL_LBM_PHY 0x00000000 +#define RCTL_LBM_MAC 0x00000040 +#define RCTL_HSEL 0x00000300 +#define RCTL_HSEL_MULTICAST 0x00000000 +#define RCTL_HSEL_UNICAST 0x00000100 +#define RCTL_HSEL_BOTH 0x00000200 +#define RCTL_MO 0x00003000 +#define RCTL_MO_47_36 0x00000000 +#define RCTL_MO_43_32 0x00001000 +#define RCTL_MO_39_28 0x00002000 +#define RCTL_BAM 0x00008000 +#define RCTL_BSIZE 0x00030000 +#define RCTL_BSIZE_2048 0x00000000 +#define RCTL_BSIZE_1024 0x00010000 +#define RCTL_BSIZE_512 0x00020000 +#define RCTL_VFE 0x00040000 +#define RCTL_CFIEN 0x00080000 +#define RCTL_CFI 0x00100000 +#define RCTL_PSP 0x00200000 +#define RCTL_DPF 0x00400000 +#define RCTL_PMCF 0x00800000 +#define RCTL_SECRC 0x04000000 +#define VLANPQF_VP0QSEL 0x00000003 +#define VLANPQF_VP0PBSEL 0x00000004 +#define VLANPQF_VLANP0V 0x00000008 +#define VLANPQF_VP1QSEL 0x00000030 +#define VLANPQF_VP1PBSEL 0x00000040 +#define VLANPQF_VLANP1V 0x00000080 +#define VLANPQF_VP2QSEL 0x00000300 +#define VLANPQF_VP2PBSEL 0x00000400 +#define VLANPQF_VLANP2V 0x00000800 +#define VLANPQF_VP3QSEL 0x00003000 +#define VLANPQF_VP3PBSEL 0x00004000 +#define VLANPQF_VLANP3V 0x00008000 +#define VLANPQF_VP4QSEL 0x00030000 +#define VLANPQF_VP4PBSEL 0x00040000 +#define VLANPQF_VLANP4V 0x00080000 +#define VLANPQF_VP5QSEL 0x00300000 +#define VLANPQF_VP5PBSEL 0x00400000 +#define VLANPQF_VLANP5V 0x00800000 +#define VLANPQF_VP6QSEL 0x03000000 +#define VLANPQF_VP6PBSEL 0x04000000 +#define VLANPQF_VLANP6V 0x08000000 +#define VLANPQF_VP7QSEL 0x30000000 +#define VLANPQF_VP7PBSEL 0x40000000 +#define VLANPQF_VLANP7V 0x80000000 +#define ETQF_ETYPE 0x0000FFFF +#define ETQF_QUEUE 0x00070000 +#define ETQF_ETYPE_LEN 0x01F00000 +#define ETQF_ETYPE_LEN_EN 0x02000000 +#define ETQF_FILTER_EN 0x04000000 +#define ETQF_IMMEDIATE_INTR 0x20000000 +#define ETQF_1588_TIMESTAMP 0x40000000 +#define ETQF_QUEUE_EN 0x80000000 + +#define RAH_QSEL_SHIFT 18 +#define VLANPQF_VP1QSEL_SHIFT 4 +#define VLANPQF_VP2QSEL_SHIFT 8 +#define VLANPQF_VP3QSEL_SHIFT 12 +#define VLANPQF_VP4QSEL_SHIFT 16 +#define VLANPQF_VP5QSEL_SHIFT 20 +#define VLANPQF_VP6QSEL_SHIFT 24 +#define VLANPQF_VP7QSEL_SHIFT 28 +#define ETQF_QUEUE_SHIFT 16 +#define ETQF_ETYPE_LEN_SHIFT 20 + +static const char *bit_to_boolean(u32 val) +{ + return val ? "yes" : "no"; +} + +static const char *bit_to_enable(u32 val) +{ + return val ? "enabled" : "disabled"; +} + +static const char *bit_to_prio(u32 val) +{ + return val ? "low" : "high"; +} + +int igc_dump_regs(struct ethtool_drvinfo *info __maybe_unused, + struct ethtool_regs *regs) +{ + u32 reg; + int offset, i; + u32 *regs_buff = (u32 *)regs->data; + u8 version = (u8)(regs->version >> 24); + + if (version != 2) + return -1; + + for (offset = 0; offset < 24; offset++) { + reg = regs_buff[offset]; + printf("%04d: 0x%08X\n", offset, reg); + } + + offset = 24; + + reg = regs_buff[offset]; + printf("%04d: RCTL (Receive Control Register) \n" + " Receiver: %s\n" + " Stop Bad Packets: %s\n" + " Unicast Promiscuous: %s\n" + " Multicast Promiscuous: %s\n" + " Long Packet Reception: %s\n" + " Loopback Model: %s\n" + " Hash Select for MTA: %s\n" + " Multicast/Unicast Table Offset: %s\n" + " Broadcast Accept Mode: %s\n" + " Receive Buffer Size: %s\n" + " VLAN Filter: %s\n" + " Canonical Form Indicator: %s\n" + " Canonical Form Indicator Bit: %s\n" + " Pad Small Receive Packets: %s\n" + " Discard Pause Frames: %s\n" + " Pass MAC Control Frames: %s\n" + " Strip Ethernet CRC: %s\n", + offset, + bit_to_enable(reg & RCTL_RXEN), + bit_to_enable(reg & RCTL_SBP), + bit_to_enable(reg & RCTL_UPE), + bit_to_enable(reg & RCTL_MPE), + bit_to_enable(reg & RCTL_LPE), + (reg & RCTL_LBM) == RCTL_LBM_PHY ? "PHY" : + (reg & RCTL_LBM) == RCTL_LBM_MAC ? "MAC" : + "undefined", + (reg & RCTL_HSEL) == RCTL_HSEL_MULTICAST ? "multicast only" : + (reg & RCTL_HSEL) == RCTL_HSEL_UNICAST ? "unicast only" : + (reg & RCTL_HSEL) == RCTL_HSEL_BOTH ? "multicast and unicast" : + "reserved", + (reg & RCTL_MO) == RCTL_MO_47_36 ? "bits [47:36]" : + (reg & RCTL_MO) == RCTL_MO_43_32 ? "bits [43:32]" : + (reg & RCTL_MO) == RCTL_MO_39_28 ? "bits [39:28]" : + "bits [35:24]", + bit_to_enable(reg & RCTL_BAM), + (reg & RCTL_BSIZE) == RCTL_BSIZE_2048 ? "2048 bytes" : + (reg & RCTL_BSIZE) == RCTL_BSIZE_1024 ? "1024 bytes" : + (reg & RCTL_BSIZE) == RCTL_BSIZE_512 ? "512 bytes" : + "256 bytes", + bit_to_enable(reg & RCTL_VFE), + bit_to_enable(reg & RCTL_CFIEN), + reg & RCTL_CFI ? "discarded" : "accepted", + bit_to_enable(reg & RCTL_PSP), + bit_to_enable(reg & RCTL_DPF), + bit_to_enable(reg & RCTL_PMCF), + bit_to_enable(reg & RCTL_SECRC)); + + for (offset = 25; offset < 172; offset++) { + reg = regs_buff[offset]; + printf("%04d: 0x%08X\n", offset, reg); + } + + offset = 172; + + for (i = 0; i < 16; i++) { + reg = regs_buff[offset + i]; + printf("%04d: RAL (Receive Address Low %02d) \n" + " Receive Address Low: %08X\n", + offset + i, i, + reg); + } + + offset = 188; + + for (i = 0; i < 16; i++) { + reg = regs_buff[offset + i]; + printf("%04d: RAH (Receive Address High %02d) \n" + " Receive Address High: %04X\n" + " Address Select: %s\n" + " Queue Select: %d\n" + " Queue Select Enable: %s\n" + " Address Valid: %s\n", + offset + i, i, + reg & RAH_RAH, + reg & RAH_ASEL ? "source" : "destination", + (reg & RAH_QSEL) >> RAH_QSEL_SHIFT, + bit_to_boolean(reg & RAH_QSEL_EN), + bit_to_boolean(reg & RAH_AV)); + } + + offset = 204; + + reg = regs_buff[offset]; + printf("%04d: VLANPQF (VLAN Priority Queue Filter) \n" + " Priority 0 \n" + " Queue: %d\n" + " Packet Buffer: %s\n" + " Valid: %s\n" + " Priority 1 \n" + " Queue: %d\n" + " Packet Buffer: %s\n" + " Valid: %s\n" + " Priority 2 \n" + " Queue: %d\n" + " Packet Buffer: %s\n" + " Valid: %s\n" + " Priority 3 \n" + " Queue: %d\n" + " Packet Buffer: %s\n" + " Valid: %s\n" + " Priority 4 \n" + " Queue: %d\n" + " Packet Buffer: %s\n" + " Valid: %s\n" + " Priority 5 \n" + " Queue: %d\n" + " Packet Buffer: %s\n" + " Valid: %s\n" + " Priority 6 \n" + " Queue: %d\n" + " Packet Buffer: %s\n" + " Valid: %s\n" + " Priority 7 \n" + " Queue: %d\n" + " Packet Buffer: %s\n" + " Valid: %s\n", + offset, + reg & VLANPQF_VP0QSEL, + bit_to_prio(reg & VLANPQF_VP0PBSEL), + bit_to_boolean(reg & VLANPQF_VLANP0V), + (reg & VLANPQF_VP1QSEL) >> VLANPQF_VP1QSEL_SHIFT, + bit_to_prio(reg & VLANPQF_VP1PBSEL), + bit_to_boolean(reg & VLANPQF_VLANP1V), + (reg & VLANPQF_VP2QSEL) >> VLANPQF_VP2QSEL_SHIFT, + bit_to_prio(reg & VLANPQF_VP2PBSEL), + bit_to_boolean(reg & VLANPQF_VLANP2V), + (reg & VLANPQF_VP3QSEL) >> VLANPQF_VP3QSEL_SHIFT, + bit_to_prio(reg & VLANPQF_VP3PBSEL), + bit_to_boolean(reg & VLANPQF_VLANP3V), + (reg & VLANPQF_VP4QSEL) >> VLANPQF_VP4QSEL_SHIFT, + bit_to_prio(reg & VLANPQF_VP4PBSEL), + bit_to_boolean(reg & VLANPQF_VLANP4V), + (reg & VLANPQF_VP5QSEL) >> VLANPQF_VP5QSEL_SHIFT, + bit_to_prio(reg & VLANPQF_VP5PBSEL), + bit_to_boolean(reg & VLANPQF_VLANP5V), + (reg & VLANPQF_VP6QSEL) >> VLANPQF_VP6QSEL_SHIFT, + bit_to_prio(reg & VLANPQF_VP6PBSEL), + bit_to_boolean(reg & VLANPQF_VLANP6V), + (reg & VLANPQF_VP7QSEL) >> VLANPQF_VP7QSEL_SHIFT, + bit_to_prio(reg & VLANPQF_VP7PBSEL), + bit_to_boolean(reg & VLANPQF_VLANP7V)); + + offset = 205; + + for (i = 0; i < 8; i++) { + reg = regs_buff[offset + i]; + printf("%04d: ETQF (EType Queue Filter %d) \n" + " EType: %04X\n" + " EType Length: %d\n" + " EType Length Enable: %s\n" + " Queue: %d\n" + " Queue Enable: %s\n" + " Immediate Interrupt: %s\n" + " 1588 Time Stamp: %s\n" + " Filter Enable: %s\n", + offset + i, i, + reg & ETQF_ETYPE, + (reg & ETQF_ETYPE_LEN) >> ETQF_ETYPE_LEN_SHIFT, + bit_to_boolean(reg & ETQF_ETYPE_LEN_EN), + (reg & ETQF_QUEUE) >> ETQF_QUEUE_SHIFT, + bit_to_boolean(reg & ETQF_QUEUE_EN), + bit_to_enable(reg & ETQF_IMMEDIATE_INTR), + bit_to_enable(reg & ETQF_1588_TIMESTAMP), + bit_to_boolean(reg & ETQF_FILTER_EN)); + } + + return 0; +} @@ -23,7 +23,10 @@ #include <sys/ioctl.h> #include <net/if.h> -#define maybe_unused __attribute__((__unused__)) +#include "json_writer.h" +#include "json_print.h" + +#define __maybe_unused __attribute__((__unused__)) /* internal for netlink interface */ #ifdef ETHTOOL_ENABLE_NETLINK @@ -218,9 +221,11 @@ struct cmd_context { const char *devname; /* net device name */ int fd; /* socket suitable for ethtool ioctl */ struct ifreq ifr; /* ifreq suitable for ethtool ioctl */ - int argc; /* number of arguments to the sub-command */ + unsigned int argc; /* number of arguments to the sub-command */ char **argp; /* arguments to the sub-command */ unsigned long debug; /* debugging mask */ + bool json; /* Output JSON, if supported */ + bool show_stats; /* include command-specific stats */ #ifdef ETHTOOL_ENABLE_NETLINK struct nl_context *nlctx; /* netlink context (opaque) */ #endif @@ -389,4 +394,10 @@ int dsa_dump_regs(struct ethtool_drvinfo *info, struct ethtool_regs *regs); /* i.MX Fast Ethernet Controller */ int fec_dump_regs(struct ethtool_drvinfo *info, struct ethtool_regs *regs); +/* Intel(R) Ethernet Controller I225-LM/I225-V adapter family */ +int igc_dump_regs(struct ethtool_drvinfo *info, struct ethtool_regs *regs); + +/* Broadcom Ethernet Controller */ +int bnxt_dump_regs(struct ethtool_drvinfo *info, struct ethtool_regs *regs); + #endif /* ETHTOOL_INTERNAL_H__ */ @@ -38,7 +38,7 @@ #define IXGB_RAH_ASEL_SRC 0x00010000 #define IXGB_RAH_AV 0x80000000 -int ixgb_dump_regs(struct ethtool_drvinfo *info maybe_unused, +int ixgb_dump_regs(struct ethtool_drvinfo *info __maybe_unused, struct ethtool_regs *regs) { u32 *regs_buff = (u32 *)regs->data; @@ -168,7 +168,7 @@ ixgbe_get_mac_type(u16 device_id) } int -ixgbe_dump_regs(struct ethtool_drvinfo *info maybe_unused, +ixgbe_dump_regs(struct ethtool_drvinfo *info __maybe_unused, struct ethtool_regs *regs) { u32 *regs_buff = (u32 *)regs->data; @@ -3,7 +3,7 @@ #include "internal.h" int -ixgbevf_dump_regs(struct ethtool_drvinfo *info maybe_unused, +ixgbevf_dump_regs(struct ethtool_drvinfo *info __maybe_unused, struct ethtool_regs *regs) { u32 *regs_buff = (u32 *)regs->data; diff --git a/json_print.c b/json_print.c new file mode 100644 index 0000000..56d5b43 --- /dev/null +++ b/json_print.c @@ -0,0 +1,228 @@ +/* + * json_print.c "print regular or json output, based on json_writer". + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Authors: Julien Fortin, <julien@cumulusnetworks.com> + */ + +#include <stdarg.h> +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> + +#include "json_print.h" + +#define SPRINT_BSIZE 64 +#define SPRINT_BUF(x) char x[SPRINT_BSIZE] + +static json_writer_t *_jw; + +#define _IS_JSON_CONTEXT(type) ((type & PRINT_JSON || type & PRINT_ANY) && _jw) +#define _IS_FP_CONTEXT(type) (!_jw && (type & PRINT_FP || type & PRINT_ANY)) + +void new_json_obj(int json) +{ + if (json) { + _jw = jsonw_new(stdout); + if (!_jw) { + perror("json object"); + exit(1); + } + jsonw_pretty(_jw, true); + jsonw_start_array(_jw); + } +} + +void delete_json_obj(void) +{ + if (_jw) { + jsonw_end_array(_jw); + jsonw_destroy(&_jw); + } +} + +bool is_json_context(void) +{ + return _jw != NULL; +} + +json_writer_t *get_json_writer(void) +{ + return _jw; +} + +void open_json_object(const char *str) +{ + if (_IS_JSON_CONTEXT(PRINT_JSON)) { + if (str) + jsonw_name(_jw, str); + jsonw_start_object(_jw); + } +} + +void close_json_object(void) +{ + if (_IS_JSON_CONTEXT(PRINT_JSON)) + jsonw_end_object(_jw); +} + +/* + * Start json array or string array using + * the provided string as json key (if not null) + * or as array delimiter in non-json context. + */ +void open_json_array(enum output_type type, const char *str) +{ + if (_IS_JSON_CONTEXT(type)) { + if (str) + jsonw_name(_jw, str); + jsonw_start_array(_jw); + } else if (_IS_FP_CONTEXT(type)) { + printf("%s", str); + } +} + +/* + * End json array or string array + */ +void close_json_array(enum output_type type, const char *str) +{ + if (_IS_JSON_CONTEXT(type)) + jsonw_end_array(_jw); + else if (_IS_FP_CONTEXT(type)) + printf("%s", str); +} + +/* + * pre-processor directive to generate similar + * functions handling different types + */ +#define _PRINT_FUNC(type_name, type) \ + __attribute__((format(printf, 3, 0))) \ + void print_##type_name(enum output_type t, \ + const char *key, \ + const char *fmt, \ + type value) \ + { \ + if (_IS_JSON_CONTEXT(t)) { \ + if (!key) \ + jsonw_##type_name(_jw, value); \ + else \ + jsonw_##type_name##_field(_jw, key, value); \ + } else if (_IS_FP_CONTEXT(t)) { \ + fprintf(stdout, fmt, value); \ + } \ + } +_PRINT_FUNC(int, int); +_PRINT_FUNC(s64, int64_t); +_PRINT_FUNC(hhu, unsigned char); +_PRINT_FUNC(hu, unsigned short); +_PRINT_FUNC(uint, unsigned int); +_PRINT_FUNC(u64, uint64_t); +_PRINT_FUNC(luint, unsigned long); +_PRINT_FUNC(lluint, unsigned long long); +_PRINT_FUNC(float, double); +#undef _PRINT_FUNC + +void print_string(enum output_type type, + const char *key, + const char *fmt, + const char *value) +{ + if (_IS_JSON_CONTEXT(type)) { + if (key && !value) + jsonw_name(_jw, key); + else if (!key && value) + jsonw_string(_jw, value); + else + jsonw_string_field(_jw, key, value); + } else if (_IS_FP_CONTEXT(type)) { + fprintf(stdout, fmt, value); + } +} + +/* + * value's type is bool. When using this function in FP context you can't pass + * a value to it, you will need to use "is_json_context()" to have different + * branch for json and regular output. grep -r "print_bool" for example + */ +void print_bool(enum output_type type, + const char *key, + const char *fmt, + bool value) +{ + if (_IS_JSON_CONTEXT(type)) { + if (key) + jsonw_bool_field(_jw, key, value); + else + jsonw_bool(_jw, value); + } else if (_IS_FP_CONTEXT(type)) { + fprintf(stdout, fmt, value ? "true" : "false"); + } +} + +/* + * In JSON context uses hardcode %#x format: 42 -> 0x2a + */ +void print_0xhex(enum output_type type, + const char *key, + const char *fmt, + unsigned long long hex) +{ + if (_IS_JSON_CONTEXT(type)) { + SPRINT_BUF(b1); + + snprintf(b1, sizeof(b1), "%#llx", hex); + print_string(PRINT_JSON, key, NULL, b1); + } else if (_IS_FP_CONTEXT(type)) { + fprintf(stdout, fmt, hex); + } +} + +void print_hex(enum output_type type, + const char *key, + const char *fmt, + unsigned int hex) +{ + if (_IS_JSON_CONTEXT(type)) { + SPRINT_BUF(b1); + + snprintf(b1, sizeof(b1), "%x", hex); + if (key) + jsonw_string_field(_jw, key, b1); + else + jsonw_string(_jw, b1); + } else if (_IS_FP_CONTEXT(type)) { + fprintf(stdout, fmt, hex); + } +} + +/* + * In JSON context we don't use the argument "value" we simply call jsonw_null + * whereas FP context can use "value" to output anything + */ +void print_null(enum output_type type, + const char *key, + const char *fmt, + const char *value) +{ + if (_IS_JSON_CONTEXT(type)) { + if (key) + jsonw_null_field(_jw, key); + else + jsonw_null(_jw); + } else if (_IS_FP_CONTEXT(type)) { + fprintf(stdout, fmt, value); + } +} + +/* Print line separator (if not in JSON mode) */ +void print_nl(void) +{ + if (!_jw) + printf("%s", "\n"); +} diff --git a/json_print.h b/json_print.h new file mode 100644 index 0000000..cc0c2ea --- /dev/null +++ b/json_print.h @@ -0,0 +1,67 @@ +/* + * json_print.h "print regular or json output, based on json_writer". + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Authors: Julien Fortin, <julien@cumulusnetworks.com> + */ + +#ifndef _JSON_PRINT_H_ +#define _JSON_PRINT_H_ + +#include "json_writer.h" + +json_writer_t *get_json_writer(void); + +/* + * use: + * - PRINT_ANY for context based output + * - PRINT_FP for non json specific output + * - PRINT_JSON for json specific output + */ +enum output_type { + PRINT_FP = 1, + PRINT_JSON = 2, + PRINT_ANY = 4, +}; + +void new_json_obj(int json); +void delete_json_obj(void); + +bool is_json_context(void); + +void fflush_fp(void); + +void open_json_object(const char *str); +void close_json_object(void); +void open_json_array(enum output_type type, const char *delim); +void close_json_array(enum output_type type, const char *delim); + +void print_nl(void); + +#define _PRINT_FUNC(type_name, type) \ + void print_##type_name(enum output_type t, \ + const char *key, \ + const char *fmt, \ + type value) \ + +_PRINT_FUNC(int, int); +_PRINT_FUNC(s64, int64_t); +_PRINT_FUNC(bool, bool); +_PRINT_FUNC(null, const char*); +_PRINT_FUNC(string, const char*); +_PRINT_FUNC(uint, unsigned int); +_PRINT_FUNC(u64, uint64_t); +_PRINT_FUNC(hhu, unsigned char); +_PRINT_FUNC(hu, unsigned short); +_PRINT_FUNC(hex, unsigned int); +_PRINT_FUNC(0xhex, unsigned long long); +_PRINT_FUNC(luint, unsigned long); +_PRINT_FUNC(lluint, unsigned long long); +_PRINT_FUNC(float, double); +#undef _PRINT_FUNC + +#endif /* _JSON_PRINT_H_ */ diff --git a/json_writer.c b/json_writer.c new file mode 100644 index 0000000..e8b3926 --- /dev/null +++ b/json_writer.c @@ -0,0 +1,391 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) // +/* + * Simple streaming JSON writer + * + * This takes care of the annoying bits of JSON syntax like the commas + * after elements + * + * Authors: Stephen Hemminger <stephen@networkplumber.org> + */ + +#include <stdio.h> +#include <stdbool.h> +#include <stdarg.h> +#include <assert.h> +#include <malloc.h> +#include <inttypes.h> +#include <stdint.h> + +#include "json_writer.h" + +struct json_writer { + FILE *out; /* output file */ + unsigned int depth; /* nesting */ + bool pretty; /* optional whitepace */ + char sep; /* either nul or comma */ +}; + +/* indentation for pretty print */ +static void jsonw_indent(json_writer_t *self) +{ + unsigned int i; + + for (i = 0; i < self->depth; ++i) + fputs(" ", self->out); +} + +/* end current line and indent if pretty printing */ +static void jsonw_eol(json_writer_t *self) +{ + if (!self->pretty) + return; + + putc('\n', self->out); + jsonw_indent(self); +} + +/* If current object is not empty print a comma */ +static void jsonw_eor(json_writer_t *self) +{ + if (self->sep != '\0') + putc(self->sep, self->out); + self->sep = ','; +} + + +/* Output JSON encoded string */ +/* Handles C escapes, does not do Unicode */ +static void jsonw_puts(json_writer_t *self, const char *str) +{ + putc('"', self->out); + for (; *str; ++str) + switch (*str) { + case '\t': + fputs("\\t", self->out); + break; + case '\n': + fputs("\\n", self->out); + break; + case '\r': + fputs("\\r", self->out); + break; + case '\f': + fputs("\\f", self->out); + break; + case '\b': + fputs("\\b", self->out); + break; + case '\\': + fputs("\\\\", self->out); + break; + case '"': + fputs("\\\"", self->out); + break; + case '\'': + fputs("\\\'", self->out); + break; + default: + putc(*str, self->out); + } + putc('"', self->out); +} + +/* Create a new JSON stream */ +json_writer_t *jsonw_new(FILE *f) +{ + json_writer_t *self = malloc(sizeof(*self)); + + if (self) { + self->out = f; + self->depth = 0; + self->pretty = false; + self->sep = '\0'; + } + return self; +} + +/* End output to JSON stream */ +void jsonw_destroy(json_writer_t **self_p) +{ + json_writer_t *self = *self_p; + + assert(self->depth == 0); + fputs("\n", self->out); + fflush(self->out); + free(self); + *self_p = NULL; +} + +void jsonw_pretty(json_writer_t *self, bool on) +{ + self->pretty = on; +} + +/* Basic blocks */ +static void jsonw_begin(json_writer_t *self, int c) +{ + jsonw_eor(self); + putc(c, self->out); + ++self->depth; + self->sep = '\0'; +} + +static void jsonw_end(json_writer_t *self, int c) +{ + assert(self->depth > 0); + + --self->depth; + if (self->sep != '\0') + jsonw_eol(self); + putc(c, self->out); + self->sep = ','; +} + + +/* Add a JSON property name */ +void jsonw_name(json_writer_t *self, const char *name) +{ + jsonw_eor(self); + jsonw_eol(self); + self->sep = '\0'; + jsonw_puts(self, name); + putc(':', self->out); + if (self->pretty) + putc(' ', self->out); +} + +__attribute__((format(printf, 2, 3))) +void jsonw_printf(json_writer_t *self, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + jsonw_eor(self); + vfprintf(self->out, fmt, ap); + va_end(ap); +} + +/* Collections */ +void jsonw_start_object(json_writer_t *self) +{ + jsonw_begin(self, '{'); +} + +void jsonw_end_object(json_writer_t *self) +{ + jsonw_end(self, '}'); +} + +void jsonw_start_array(json_writer_t *self) +{ + jsonw_begin(self, '['); + if (self->pretty) + putc(' ', self->out); +} + +void jsonw_end_array(json_writer_t *self) +{ + if (self->pretty && self->sep) + putc(' ', self->out); + self->sep = '\0'; + jsonw_end(self, ']'); +} + +/* JSON value types */ +void jsonw_string(json_writer_t *self, const char *value) +{ + jsonw_eor(self); + jsonw_puts(self, value); +} + +void jsonw_bool(json_writer_t *self, bool val) +{ + jsonw_printf(self, "%s", val ? "true" : "false"); +} + +void jsonw_null(json_writer_t *self) +{ + jsonw_printf(self, "null"); +} + +void jsonw_float(json_writer_t *self, double num) +{ + jsonw_printf(self, "%g", num); +} + +void jsonw_hhu(json_writer_t *self, unsigned char num) +{ + jsonw_printf(self, "%hhu", num); +} + +void jsonw_hu(json_writer_t *self, unsigned short num) +{ + jsonw_printf(self, "%hu", num); +} + +void jsonw_uint(json_writer_t *self, unsigned int num) +{ + jsonw_printf(self, "%u", num); +} + +void jsonw_u64(json_writer_t *self, uint64_t num) +{ + jsonw_printf(self, "%"PRIu64, num); +} + +void jsonw_xint(json_writer_t *self, uint64_t num) +{ + jsonw_printf(self, "%"PRIx64, num); +} + +void jsonw_luint(json_writer_t *self, unsigned long num) +{ + jsonw_printf(self, "%lu", num); +} + +void jsonw_lluint(json_writer_t *self, unsigned long long num) +{ + jsonw_printf(self, "%llu", num); +} + +void jsonw_int(json_writer_t *self, int num) +{ + jsonw_printf(self, "%d", num); +} + +void jsonw_s64(json_writer_t *self, int64_t num) +{ + jsonw_printf(self, "%"PRId64, num); +} + +/* Basic name/value objects */ +void jsonw_string_field(json_writer_t *self, const char *prop, const char *val) +{ + jsonw_name(self, prop); + jsonw_string(self, val); +} + +void jsonw_bool_field(json_writer_t *self, const char *prop, bool val) +{ + jsonw_name(self, prop); + jsonw_bool(self, val); +} + +void jsonw_float_field(json_writer_t *self, const char *prop, double val) +{ + jsonw_name(self, prop); + jsonw_float(self, val); +} + +void jsonw_uint_field(json_writer_t *self, const char *prop, unsigned int num) +{ + jsonw_name(self, prop); + jsonw_uint(self, num); +} + +void jsonw_u64_field(json_writer_t *self, const char *prop, uint64_t num) +{ + jsonw_name(self, prop); + jsonw_u64(self, num); +} + +void jsonw_xint_field(json_writer_t *self, const char *prop, uint64_t num) +{ + jsonw_name(self, prop); + jsonw_xint(self, num); +} + +void jsonw_hhu_field(json_writer_t *self, const char *prop, unsigned char num) +{ + jsonw_name(self, prop); + jsonw_hhu(self, num); +} + +void jsonw_hu_field(json_writer_t *self, const char *prop, unsigned short num) +{ + jsonw_name(self, prop); + jsonw_hu(self, num); +} + +void jsonw_luint_field(json_writer_t *self, + const char *prop, + unsigned long num) +{ + jsonw_name(self, prop); + jsonw_luint(self, num); +} + +void jsonw_lluint_field(json_writer_t *self, + const char *prop, + unsigned long long num) +{ + jsonw_name(self, prop); + jsonw_lluint(self, num); +} + +void jsonw_int_field(json_writer_t *self, const char *prop, int num) +{ + jsonw_name(self, prop); + jsonw_int(self, num); +} + +void jsonw_s64_field(json_writer_t *self, const char *prop, int64_t num) +{ + jsonw_name(self, prop); + jsonw_s64(self, num); +} + +void jsonw_null_field(json_writer_t *self, const char *prop) +{ + jsonw_name(self, prop); + jsonw_null(self); +} + +#ifdef TEST +int main(int argc, char **argv) +{ + json_writer_t *wr = jsonw_new(stdout); + + jsonw_start_object(wr); + jsonw_pretty(wr, true); + jsonw_name(wr, "Vyatta"); + jsonw_start_object(wr); + jsonw_string_field(wr, "url", "http://vyatta.com"); + jsonw_uint_field(wr, "downloads", 2000000ul); + jsonw_float_field(wr, "stock", 8.16); + + jsonw_name(wr, "ARGV"); + jsonw_start_array(wr); + while (--argc) + jsonw_string(wr, *++argv); + jsonw_end_array(wr); + + jsonw_name(wr, "empty"); + jsonw_start_array(wr); + jsonw_end_array(wr); + + jsonw_name(wr, "NIL"); + jsonw_start_object(wr); + jsonw_end_object(wr); + + jsonw_null_field(wr, "my_null"); + + jsonw_name(wr, "special chars"); + jsonw_start_array(wr); + jsonw_string_field(wr, "slash", "/"); + jsonw_string_field(wr, "newline", "\n"); + jsonw_string_field(wr, "tab", "\t"); + jsonw_string_field(wr, "ff", "\f"); + jsonw_string_field(wr, "quote", "\""); + jsonw_string_field(wr, "tick", "\'"); + jsonw_string_field(wr, "backslash", "\\"); + jsonw_end_array(wr); + + jsonw_end_object(wr); + + jsonw_end_object(wr); + jsonw_destroy(&wr); + return 0; +} + +#endif diff --git a/json_writer.h b/json_writer.h new file mode 100644 index 0000000..b52dc2d --- /dev/null +++ b/json_writer.h @@ -0,0 +1,76 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) */ +/* + * Simple streaming JSON writer + * + * This takes care of the annoying bits of JSON syntax like the commas + * after elements + * + * Authors: Stephen Hemminger <stephen@networkplumber.org> + */ + +#ifndef _JSON_WRITER_H_ +#define _JSON_WRITER_H_ + +#include <stdbool.h> +#include <stdint.h> + +/* Opaque class structure */ +typedef struct json_writer json_writer_t; + +/* Create a new JSON stream */ +json_writer_t *jsonw_new(FILE *f); +/* End output to JSON stream */ +void jsonw_destroy(json_writer_t **self_p); + +/* Cause output to have pretty whitespace */ +void jsonw_pretty(json_writer_t *self, bool on); + +/* Add property name */ +void jsonw_name(json_writer_t *self, const char *name); + +/* Add value */ +__attribute__((format(printf, 2, 3))) +void jsonw_printf(json_writer_t *self, const char *fmt, ...); +void jsonw_string(json_writer_t *self, const char *value); +void jsonw_bool(json_writer_t *self, bool value); +void jsonw_float(json_writer_t *self, double number); +void jsonw_float_fmt(json_writer_t *self, const char *fmt, double num); +void jsonw_uint(json_writer_t *self, unsigned int number); +void jsonw_u64(json_writer_t *self, uint64_t number); +void jsonw_xint(json_writer_t *self, uint64_t number); +void jsonw_hhu(json_writer_t *self, unsigned char num); +void jsonw_hu(json_writer_t *self, unsigned short number); +void jsonw_int(json_writer_t *self, int number); +void jsonw_s64(json_writer_t *self, int64_t number); +void jsonw_null(json_writer_t *self); +void jsonw_luint(json_writer_t *self, unsigned long num); +void jsonw_lluint(json_writer_t *self, unsigned long long num); + +/* Useful Combinations of name and value */ +void jsonw_string_field(json_writer_t *self, const char *prop, const char *val); +void jsonw_bool_field(json_writer_t *self, const char *prop, bool value); +void jsonw_float_field(json_writer_t *self, const char *prop, double num); +void jsonw_uint_field(json_writer_t *self, const char *prop, unsigned int num); +void jsonw_u64_field(json_writer_t *self, const char *prop, uint64_t num); +void jsonw_xint_field(json_writer_t *self, const char *prop, uint64_t num); +void jsonw_hhu_field(json_writer_t *self, const char *prop, unsigned char num); +void jsonw_hu_field(json_writer_t *self, const char *prop, unsigned short num); +void jsonw_int_field(json_writer_t *self, const char *prop, int num); +void jsonw_s64_field(json_writer_t *self, const char *prop, int64_t num); +void jsonw_null_field(json_writer_t *self, const char *prop); +void jsonw_luint_field(json_writer_t *self, const char *prop, + unsigned long num); +void jsonw_lluint_field(json_writer_t *self, const char *prop, + unsigned long long num); + +/* Collections */ +void jsonw_start_object(json_writer_t *self); +void jsonw_end_object(json_writer_t *self); + +void jsonw_start_array(json_writer_t *self); +void jsonw_end_array(json_writer_t *self); + +/* Override default exception handling */ +typedef void (jsonw_err_handler_fn)(const char *); + +#endif /* _JSON_WRITER_H_ */ @@ -2,7 +2,7 @@ #include <string.h> #include "internal.h" -int lan78xx_dump_regs(struct ethtool_drvinfo *info maybe_unused, +int lan78xx_dump_regs(struct ethtool_drvinfo *info __maybe_unused, struct ethtool_regs *regs) { unsigned int *lan78xx_reg = (unsigned int *)regs->data; @@ -130,7 +130,7 @@ static void dump_fifo(const char *name, const void *p) static void dump_gmac_fifo(const char *name, const void *p) { const u32 *r = p; - int i; + unsigned int i; static const char *regs[] = { "End Address", "Almost Full Thresh", @@ -259,7 +259,7 @@ static void dump_control(u8 *r) printf("General Purpose I/O 0x%08X\n", *(u32 *) (r + 0x15c)); } -int skge_dump_regs(struct ethtool_drvinfo *info maybe_unused, +int skge_dump_regs(struct ethtool_drvinfo *info __maybe_unused, struct ethtool_regs *regs) { const u32 *r = (const u32 *) regs->data; @@ -380,7 +380,7 @@ static void dump_prefetch(const char *name, const void *r) } } -int sky2_dump_regs(struct ethtool_drvinfo *info maybe_unused, +int sky2_dump_regs(struct ethtool_drvinfo *info __maybe_unused, struct ethtool_regs *regs) { const u16 *r16 = (const u16 *) regs->data; @@ -323,7 +323,7 @@ static void __print_intr(int d, int intr, const char *name, } while (0) int -natsemi_dump_regs(struct ethtool_drvinfo *info maybe_unused, +natsemi_dump_regs(struct ethtool_drvinfo *info __maybe_unused, struct ethtool_regs *regs) { u32 *data = (u32 *)regs->data; @@ -964,11 +964,11 @@ natsemi_dump_regs(struct ethtool_drvinfo *info maybe_unused, } int -natsemi_dump_eeprom(struct ethtool_drvinfo *info maybe_unused, +natsemi_dump_eeprom(struct ethtool_drvinfo *info __maybe_unused, struct ethtool_eeprom *ee) { - int i; u16 *eebuf = (u16 *)ee->data; + unsigned int i; if (ee->magic != NATSEMI_MAGIC) { fprintf(stderr, "Magic number 0x%08x does not match 0x%08x\n", diff --git a/netlink/bitset.c b/netlink/bitset.c index ed109ec..10ce8e9 100644 --- a/netlink/bitset.c +++ b/netlink/bitset.c @@ -50,6 +50,7 @@ bool bitset_get_bit(const struct nlattr *bitset, bool mask, unsigned int idx, DECLARE_ATTR_TB_INFO(bitset_tb); const struct nlattr *bits; const struct nlattr *bit; + bool nomask; int ret; *retptr = 0; @@ -57,6 +58,15 @@ bool bitset_get_bit(const struct nlattr *bitset, bool mask, unsigned int idx, if (ret < 0) goto err; + nomask = bitset_tb[ETHTOOL_A_BITSET_NOMASK]; + if (mask && nomask) { + /* Trying to determine if a bit is set in the mask of a "no + * mask" bitset doesn't make sense. + */ + ret = -EFAULT; + goto err; + } + bits = mask ? bitset_tb[ETHTOOL_A_BITSET_MASK] : bitset_tb[ETHTOOL_A_BITSET_VALUE]; if (bits) { @@ -87,7 +97,7 @@ bool bitset_get_bit(const struct nlattr *bitset, bool mask, unsigned int idx, my_idx = mnl_attr_get_u32(tb[ETHTOOL_A_BITSET_BIT_INDEX]); if (my_idx == idx) - return mask || tb[ETHTOOL_A_BITSET_BIT_VALUE]; + return mask || nomask || tb[ETHTOOL_A_BITSET_BIT_VALUE]; } return false; @@ -153,6 +163,37 @@ err: return true; } +static uint32_t *get_compact_bitset_attr(const struct nlattr *bitset, + uint16_t type) +{ + const struct nlattr *tb[ETHTOOL_A_BITSET_MAX + 1] = {}; + DECLARE_ATTR_TB_INFO(tb); + unsigned int count; + int ret; + + ret = mnl_attr_parse_nested(bitset, attr_cb, &tb_info); + if (ret < 0) + return NULL; + if (!tb[ETHTOOL_A_BITSET_SIZE] || !tb[ETHTOOL_A_BITSET_VALUE] || + !tb[type]) + return NULL; + count = mnl_attr_get_u32(tb[ETHTOOL_A_BITSET_SIZE]); + if (8 * mnl_attr_get_payload_len(tb[type]) < count) + return NULL; + + return mnl_attr_get_payload(tb[type]); +} + +uint32_t *get_compact_bitset_value(const struct nlattr *bitset) +{ + return get_compact_bitset_attr(bitset, ETHTOOL_A_BITSET_VALUE); +} + +uint32_t *get_compact_bitset_mask(const struct nlattr *bitset) +{ + return get_compact_bitset_attr(bitset, ETHTOOL_A_BITSET_MASK); +} + int walk_bitset(const struct nlattr *bitset, const struct stringset *labels, bitset_walk_callback cb, void *data) { diff --git a/netlink/bitset.h b/netlink/bitset.h index 4b587d2..4c9cdac 100644 --- a/netlink/bitset.h +++ b/netlink/bitset.h @@ -20,6 +20,8 @@ bool bitset_get_bit(const struct nlattr *bitset, bool mask, unsigned int idx, int *retptr); bool bitset_is_compact(const struct nlattr *bitset); bool bitset_is_empty(const struct nlattr *bitset, bool mask, int *retptr); +uint32_t *get_compact_bitset_value(const struct nlattr *bitset); +uint32_t *get_compact_bitset_mask(const struct nlattr *bitset); int walk_bitset(const struct nlattr *bitset, const struct stringset *labels, bitset_walk_callback cb, void *data); diff --git a/netlink/cable_test.c b/netlink/cable_test.c new file mode 100644 index 0000000..17139f7 --- /dev/null +++ b/netlink/cable_test.c @@ -0,0 +1,593 @@ +/* + * cable_test.c - netlink implementation of cable test command + * + * Implementation of ethtool --cable-test <dev> + */ + +#include <errno.h> +#include <string.h> +#include <stdio.h> + +#include "../internal.h" +#include "../common.h" +#include "netlink.h" +#include "parser.h" + +struct cable_test_context { + bool breakout; +}; + +static int nl_get_cable_test_result(const struct nlattr *nest, uint8_t *pair, + uint16_t *code) +{ + const struct nlattr *tb[ETHTOOL_A_CABLE_RESULT_MAX+1] = {}; + DECLARE_ATTR_TB_INFO(tb); + int ret; + + ret = mnl_attr_parse_nested(nest, attr_cb, &tb_info); + if (ret < 0 || + !tb[ETHTOOL_A_CABLE_RESULT_PAIR] || + !tb[ETHTOOL_A_CABLE_RESULT_CODE]) + return -EFAULT; + + *pair = mnl_attr_get_u8(tb[ETHTOOL_A_CABLE_RESULT_PAIR]); + *code = mnl_attr_get_u8(tb[ETHTOOL_A_CABLE_RESULT_CODE]); + + return 0; +} + +static int nl_get_cable_test_fault_length(const struct nlattr *nest, + uint8_t *pair, unsigned int *cm) +{ + const struct nlattr *tb[ETHTOOL_A_CABLE_FAULT_LENGTH_MAX+1] = {}; + DECLARE_ATTR_TB_INFO(tb); + int ret; + + ret = mnl_attr_parse_nested(nest, attr_cb, &tb_info); + if (ret < 0 || + !tb[ETHTOOL_A_CABLE_FAULT_LENGTH_PAIR] || + !tb[ETHTOOL_A_CABLE_FAULT_LENGTH_CM]) + return -EFAULT; + + *pair = mnl_attr_get_u8(tb[ETHTOOL_A_CABLE_FAULT_LENGTH_PAIR]); + *cm = mnl_attr_get_u32(tb[ETHTOOL_A_CABLE_FAULT_LENGTH_CM]); + + return 0; +} + +static char *nl_code2txt(uint16_t code) +{ + switch (code) { + case ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC: + default: + return "Unknown"; + case ETHTOOL_A_CABLE_RESULT_CODE_OK: + return "OK"; + case ETHTOOL_A_CABLE_RESULT_CODE_OPEN: + return "Open Circuit"; + case ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT: + return "Short within Pair"; + case ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT: + return "Short to another pair"; + } +} + +static char *nl_pair2txt(uint8_t pair) +{ + switch (pair) { + case ETHTOOL_A_CABLE_PAIR_A: + return "Pair A"; + case ETHTOOL_A_CABLE_PAIR_B: + return "Pair B"; + case ETHTOOL_A_CABLE_PAIR_C: + return "Pair C"; + case ETHTOOL_A_CABLE_PAIR_D: + return "Pair D"; + default: + return "Unexpected pair"; + } +} + +static int nl_cable_test_ntf_attr(struct nlattr *evattr) +{ + unsigned int cm; + uint16_t code; + uint8_t pair; + int ret; + + switch (mnl_attr_get_type(evattr)) { + case ETHTOOL_A_CABLE_NEST_RESULT: + ret = nl_get_cable_test_result(evattr, &pair, &code); + if (ret < 0) + return ret; + + open_json_object(NULL); + print_string(PRINT_ANY, "pair", "%s ", nl_pair2txt(pair)); + print_string(PRINT_ANY, "code", "code %s\n", nl_code2txt(code)); + close_json_object(); + break; + + case ETHTOOL_A_CABLE_NEST_FAULT_LENGTH: + ret = nl_get_cable_test_fault_length(evattr, &pair, &cm); + if (ret < 0) + return ret; + open_json_object(NULL); + print_string(PRINT_ANY, "pair", "%s, ", nl_pair2txt(pair)); + print_float(PRINT_ANY, "length", "fault length: %0.2fm\n", + (float)cm / 100); + close_json_object(); + break; + } + return 0; +} + +static void cable_test_ntf_nest(const struct nlattr *nest) +{ + struct nlattr *pos; + int ret; + + mnl_attr_for_each_nested(pos, nest) { + ret = nl_cable_test_ntf_attr(pos); + if (ret < 0) + return; + } +} + +/* Returns MNL_CB_STOP when the test is complete. Used when executing + * a test, but not suitable for monitor. + */ +static int cable_test_ntf_stop_cb(const struct nlmsghdr *nlhdr, void *data) +{ + const struct nlattr *tb[ETHTOOL_A_CABLE_TEST_NTF_MAX + 1] = {}; + u8 status = ETHTOOL_A_CABLE_TEST_NTF_STATUS_UNSPEC; + struct cable_test_context *ctctx; + struct nl_context *nlctx = data; + DECLARE_ATTR_TB_INFO(tb); + bool silent; + int err_ret; + int ret; + + ctctx = nlctx->cmd_private; + + silent = nlctx->is_dump || nlctx->is_monitor; + err_ret = silent ? MNL_CB_OK : MNL_CB_ERROR; + ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info); + if (ret < 0) + return err_ret; + + nlctx->devname = get_dev_name(tb[ETHTOOL_A_CABLE_TEST_HEADER]); + if (!dev_ok(nlctx)) + return err_ret; + + if (tb[ETHTOOL_A_CABLE_TEST_NTF_STATUS]) + status = mnl_attr_get_u8(tb[ETHTOOL_A_CABLE_TEST_NTF_STATUS]); + + switch (status) { + case ETHTOOL_A_CABLE_TEST_NTF_STATUS_STARTED: + print_string(PRINT_FP, "status", + "Cable test started for device %s.\n", + nlctx->devname); + break; + case ETHTOOL_A_CABLE_TEST_NTF_STATUS_COMPLETED: + print_string(PRINT_FP, "status", + "Cable test completed for device %s.\n", + nlctx->devname); + break; + default: + break; + } + + if (tb[ETHTOOL_A_CABLE_TEST_NTF_NEST]) + cable_test_ntf_nest(tb[ETHTOOL_A_CABLE_TEST_NTF_NEST]); + + if (status == ETHTOOL_A_CABLE_TEST_NTF_STATUS_COMPLETED) { + if (ctctx) + ctctx->breakout = true; + return MNL_CB_STOP; + } + + return MNL_CB_OK; +} + +/* Wrapper around cable_test_ntf_stop_cb() which does not return STOP, + * used for monitor + */ +int cable_test_ntf_cb(const struct nlmsghdr *nlhdr, void *data) +{ + int status = cable_test_ntf_stop_cb(nlhdr, data); + + if (status == MNL_CB_STOP) + status = MNL_CB_OK; + + return status; +} + +static int nl_cable_test_results_cb(const struct nlmsghdr *nlhdr, void *data) +{ + const struct genlmsghdr *ghdr = (const struct genlmsghdr *)(nlhdr + 1); + + if (ghdr->cmd != ETHTOOL_MSG_CABLE_TEST_NTF) + return MNL_CB_OK; + + return cable_test_ntf_stop_cb(nlhdr, data); +} + +/* Receive the broadcasted messages until we get the cable test + * results + */ +static int nl_cable_test_process_results(struct cmd_context *ctx) +{ + struct nl_context *nlctx = ctx->nlctx; + struct nl_socket *nlsk = nlctx->ethnl_socket; + struct cable_test_context ctctx; + int err; + + nlctx->is_monitor = true; + nlsk->port = 0; + nlsk->seq = 0; + + ctctx.breakout = false; + nlctx->cmd_private = &ctctx; + + while (!ctctx.breakout) { + err = nlsock_process_reply(nlsk, nl_cable_test_results_cb, + nlctx); + if (err) + return err; + } + + return err; +} + +int nl_cable_test(struct cmd_context *ctx) +{ + struct nl_context *nlctx = ctx->nlctx; + struct nl_socket *nlsk = nlctx->ethnl_socket; + uint32_t grpid = nlctx->ethnl_mongrp; + int ret; + + /* Join the multicast group so we can receive the results in a + * race free way. + */ + if (!grpid) { + fprintf(stderr, "multicast group 'monitor' not found\n"); + return -EOPNOTSUPP; + } + + ret = mnl_socket_setsockopt(nlsk->sk, NETLINK_ADD_MEMBERSHIP, + &grpid, sizeof(grpid)); + if (ret < 0) + return ret; + + ret = nlsock_prep_get_request(nlsk, ETHTOOL_MSG_CABLE_TEST_ACT, + ETHTOOL_A_CABLE_TEST_HEADER, 0); + if (ret < 0) + return ret; + + ret = nlsock_sendmsg(nlsk, NULL); + if (ret < 0) + fprintf(stderr, "Cannot start cable test\n"); + else { + new_json_obj(ctx->json); + + ret = nl_cable_test_process_results(ctx); + + delete_json_obj(); + } + + return ret; +} + +static int nl_get_cable_test_tdr_amplitude(const struct nlattr *nest, + uint8_t *pair, int16_t *mV) +{ + const struct nlattr *tb[ETHTOOL_A_CABLE_AMPLITUDE_MAX+1] = {}; + DECLARE_ATTR_TB_INFO(tb); + uint16_t mV_unsigned; + int ret; + + ret = mnl_attr_parse_nested(nest, attr_cb, &tb_info); + if (ret < 0 || + !tb[ETHTOOL_A_CABLE_AMPLITUDE_PAIR] || + !tb[ETHTOOL_A_CABLE_AMPLITUDE_mV]) + return -EFAULT; + + *pair = mnl_attr_get_u8(tb[ETHTOOL_A_CABLE_AMPLITUDE_PAIR]); + mV_unsigned = mnl_attr_get_u16(tb[ETHTOOL_A_CABLE_AMPLITUDE_mV]); + *mV = (int16_t)(mV_unsigned); + + return 0; +} + +static int nl_get_cable_test_tdr_pulse(const struct nlattr *nest, uint16_t *mV) +{ + const struct nlattr *tb[ETHTOOL_A_CABLE_PULSE_MAX+1] = {}; + DECLARE_ATTR_TB_INFO(tb); + int ret; + + ret = mnl_attr_parse_nested(nest, attr_cb, &tb_info); + if (ret < 0 || + !tb[ETHTOOL_A_CABLE_PULSE_mV]) + return -EFAULT; + + *mV = mnl_attr_get_u16(tb[ETHTOOL_A_CABLE_PULSE_mV]); + + return 0; +} + +static int nl_get_cable_test_tdr_step(const struct nlattr *nest, + uint32_t *first, uint32_t *last, + uint32_t *step) +{ + const struct nlattr *tb[ETHTOOL_A_CABLE_STEP_MAX+1] = {}; + DECLARE_ATTR_TB_INFO(tb); + int ret; + + ret = mnl_attr_parse_nested(nest, attr_cb, &tb_info); + if (ret < 0 || + !tb[ETHTOOL_A_CABLE_STEP_FIRST_DISTANCE] || + !tb[ETHTOOL_A_CABLE_STEP_LAST_DISTANCE] || + !tb[ETHTOOL_A_CABLE_STEP_STEP_DISTANCE]) + return -EFAULT; + + *first = mnl_attr_get_u32(tb[ETHTOOL_A_CABLE_STEP_FIRST_DISTANCE]); + *last = mnl_attr_get_u32(tb[ETHTOOL_A_CABLE_STEP_LAST_DISTANCE]); + *step = mnl_attr_get_u32(tb[ETHTOOL_A_CABLE_STEP_STEP_DISTANCE]); + + return 0; +} + +static int nl_cable_test_tdr_ntf_attr(struct nlattr *evattr) +{ + uint32_t first, last, step; + uint8_t pair; + int ret; + + switch (mnl_attr_get_type(evattr)) { + case ETHTOOL_A_CABLE_TDR_NEST_AMPLITUDE: { + int16_t mV; + + ret = nl_get_cable_test_tdr_amplitude( + evattr, &pair, &mV); + if (ret < 0) + return ret; + + open_json_object(NULL); + print_string(PRINT_ANY, "pair", "%s ", nl_pair2txt(pair)); + print_int(PRINT_ANY, "amplitude", "Amplitude %4d\n", mV); + close_json_object(); + break; + } + case ETHTOOL_A_CABLE_TDR_NEST_PULSE: { + uint16_t mV; + + ret = nl_get_cable_test_tdr_pulse(evattr, &mV); + if (ret < 0) + return ret; + + open_json_object(NULL); + print_uint(PRINT_ANY, "pulse", "TDR Pulse %dmV\n", mV); + close_json_object(); + break; + } + case ETHTOOL_A_CABLE_TDR_NEST_STEP: + ret = nl_get_cable_test_tdr_step(evattr, &first, &last, &step); + if (ret < 0) + return ret; + + open_json_object(NULL); + print_float(PRINT_ANY, "first", "Step configuration: %.2f-", + (float)first / 100); + print_float(PRINT_ANY, "last", "%.2f meters ", + (float)last / 100); + print_float(PRINT_ANY, "step", "in %.2fm steps\n", + (float)step / 100); + close_json_object(); + break; + } + return 0; +} + +static void cable_test_tdr_ntf_nest(const struct nlattr *nest) +{ + struct nlattr *pos; + int ret; + + mnl_attr_for_each_nested(pos, nest) { + ret = nl_cable_test_tdr_ntf_attr(pos); + if (ret < 0) + return; + } +} + +/* Returns MNL_CB_STOP when the test is complete. Used when executing + * a test, but not suitable for monitor. + */ +int cable_test_tdr_ntf_stop_cb(const struct nlmsghdr *nlhdr, void *data) +{ + const struct nlattr *tb[ETHTOOL_A_CABLE_TEST_TDR_NTF_MAX + 1] = {}; + u8 status = ETHTOOL_A_CABLE_TEST_NTF_STATUS_UNSPEC; + struct cable_test_context *ctctx; + struct nl_context *nlctx = data; + + DECLARE_ATTR_TB_INFO(tb); + bool silent; + int err_ret; + int ret; + + ctctx = nlctx->cmd_private; + + silent = nlctx->is_dump || nlctx->is_monitor; + err_ret = silent ? MNL_CB_OK : MNL_CB_ERROR; + ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info); + if (ret < 0) + return err_ret; + + nlctx->devname = get_dev_name(tb[ETHTOOL_A_CABLE_TEST_TDR_HEADER]); + if (!dev_ok(nlctx)) + return err_ret; + + if (tb[ETHTOOL_A_CABLE_TEST_NTF_STATUS]) + status = mnl_attr_get_u8(tb[ETHTOOL_A_CABLE_TEST_NTF_STATUS]); + + switch (status) { + case ETHTOOL_A_CABLE_TEST_NTF_STATUS_STARTED: + print_string(PRINT_FP, "status", + "Cable test TDR started for device %s.\n", + nlctx->devname); + break; + case ETHTOOL_A_CABLE_TEST_NTF_STATUS_COMPLETED: + print_string(PRINT_FP, "status", + "Cable test TDR completed for device %s.\n", + nlctx->devname); + break; + default: + break; + } + + if (tb[ETHTOOL_A_CABLE_TEST_TDR_NTF_NEST]) + cable_test_tdr_ntf_nest(tb[ETHTOOL_A_CABLE_TEST_TDR_NTF_NEST]); + + if (status == ETHTOOL_A_CABLE_TEST_NTF_STATUS_COMPLETED) { + if (ctctx) + ctctx->breakout = true; + return MNL_CB_STOP; + } + + return MNL_CB_OK; +} + +/* Wrapper around cable_test_tdr_ntf_stop_cb() which does not return + * STOP, used for monitor + */ +int cable_test_tdr_ntf_cb(const struct nlmsghdr *nlhdr, void *data) +{ + int status = cable_test_tdr_ntf_stop_cb(nlhdr, data); + + if (status == MNL_CB_STOP) + status = MNL_CB_OK; + + return status; +} + +static int nl_cable_test_tdr_results_cb(const struct nlmsghdr *nlhdr, + void *data) +{ + const struct genlmsghdr *ghdr = (const struct genlmsghdr *)(nlhdr + 1); + + if (ghdr->cmd != ETHTOOL_MSG_CABLE_TEST_TDR_NTF) + return MNL_CB_OK; + + cable_test_tdr_ntf_cb(nlhdr, data); + + return MNL_CB_STOP; +} + +/* Receive the broadcasted messages until we get the cable test + * results + */ +static int nl_cable_test_tdr_process_results(struct cmd_context *ctx) +{ + struct nl_context *nlctx = ctx->nlctx; + struct nl_socket *nlsk = nlctx->ethnl_socket; + struct cable_test_context ctctx; + int err; + + nlctx->is_monitor = true; + nlsk->port = 0; + nlsk->seq = 0; + + ctctx.breakout = false; + nlctx->cmd_private = &ctctx; + + while (!ctctx.breakout) { + err = nlsock_process_reply(nlsk, nl_cable_test_tdr_results_cb, + nlctx); + if (err) + return err; + } + + return err; +} + +static const struct param_parser tdr_params[] = { + { + .arg = "first", + .type = ETHTOOL_A_CABLE_TEST_TDR_CFG_FIRST, + .group = ETHTOOL_A_CABLE_TEST_TDR_CFG, + .handler = nl_parse_direct_m2cm, + }, + { + .arg = "last", + .type = ETHTOOL_A_CABLE_TEST_TDR_CFG_LAST, + .group = ETHTOOL_A_CABLE_TEST_TDR_CFG, + .handler = nl_parse_direct_m2cm, + }, + { + .arg = "step", + .type = ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP, + .group = ETHTOOL_A_CABLE_TEST_TDR_CFG, + .handler = nl_parse_direct_m2cm, + }, + { + .arg = "pair", + .type = ETHTOOL_A_CABLE_TEST_TDR_CFG_PAIR, + .group = ETHTOOL_A_CABLE_TEST_TDR_CFG, + .handler = nl_parse_direct_u8, + }, + {} +}; + +int nl_cable_test_tdr(struct cmd_context *ctx) +{ + struct nl_context *nlctx = ctx->nlctx; + struct nl_socket *nlsk = nlctx->ethnl_socket; + uint32_t grpid = nlctx->ethnl_mongrp; + struct nl_msg_buff *msgbuff; + int ret; + + nlctx->cmd = "--cable-test-tdr"; + nlctx->argp = ctx->argp; + nlctx->argc = ctx->argc; + nlctx->devname = ctx->devname; + msgbuff = &nlsk->msgbuff; + + /* Join the multicast group so we can receive the results in a + * race free way. + */ + if (!grpid) { + fprintf(stderr, "multicast group 'monitor' not found\n"); + return -EOPNOTSUPP; + } + + ret = mnl_socket_setsockopt(nlsk->sk, NETLINK_ADD_MEMBERSHIP, + &grpid, sizeof(grpid)); + if (ret < 0) + return ret; + + ret = msg_init(nlctx, msgbuff, ETHTOOL_MSG_CABLE_TEST_TDR_ACT, + NLM_F_REQUEST | NLM_F_ACK); + if (ret < 0) + return 2; + + if (ethnla_fill_header(msgbuff, ETHTOOL_A_CABLE_TEST_TDR_HEADER, + ctx->devname, 0)) + return -EMSGSIZE; + + ret = nl_parser(nlctx, tdr_params, NULL, PARSER_GROUP_NEST, NULL); + if (ret < 0) + return ret; + + ret = nlsock_sendmsg(nlsk, NULL); + if (ret < 0) + fprintf(stderr, "Cannot start cable test TDR\n"); + else { + new_json_obj(ctx->json); + + ret = nl_cable_test_tdr_process_results(ctx); + + delete_json_obj(); + } + + return ret; +} diff --git a/netlink/channels.c b/netlink/channels.c new file mode 100644 index 0000000..894c74b --- /dev/null +++ b/netlink/channels.c @@ -0,0 +1,141 @@ +/* + * channels.c - netlink implementation of channel commands + * + * Implementation of "ethtool -l <dev>" and "ethtool -L <dev> ..." + */ + +#include <errno.h> +#include <string.h> +#include <stdio.h> + +#include "../internal.h" +#include "../common.h" +#include "netlink.h" +#include "parser.h" + +/* CHANNELS_GET */ + +int channels_reply_cb(const struct nlmsghdr *nlhdr, void *data) +{ + const struct nlattr *tb[ETHTOOL_A_CHANNELS_MAX + 1] = {}; + DECLARE_ATTR_TB_INFO(tb); + struct nl_context *nlctx = data; + bool silent; + int err_ret; + int ret; + + silent = nlctx->is_dump || nlctx->is_monitor; + err_ret = silent ? MNL_CB_OK : MNL_CB_ERROR; + ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info); + if (ret < 0) + return err_ret; + nlctx->devname = get_dev_name(tb[ETHTOOL_A_CHANNELS_HEADER]); + if (!dev_ok(nlctx)) + return err_ret; + + if (silent) + putchar('\n'); + printf("Channel parameters for %s:\n", nlctx->devname); + printf("Pre-set maximums:\n"); + show_u32(tb[ETHTOOL_A_CHANNELS_RX_MAX], "RX:\t\t"); + show_u32(tb[ETHTOOL_A_CHANNELS_TX_MAX], "TX:\t\t"); + show_u32(tb[ETHTOOL_A_CHANNELS_OTHER_MAX], "Other:\t\t"); + show_u32(tb[ETHTOOL_A_CHANNELS_COMBINED_MAX], "Combined:\t"); + printf("Current hardware settings:\n"); + show_u32(tb[ETHTOOL_A_CHANNELS_RX_COUNT], "RX:\t\t"); + show_u32(tb[ETHTOOL_A_CHANNELS_TX_COUNT], "TX:\t\t"); + show_u32(tb[ETHTOOL_A_CHANNELS_OTHER_COUNT], "Other:\t\t"); + show_u32(tb[ETHTOOL_A_CHANNELS_COMBINED_COUNT], "Combined:\t"); + + return MNL_CB_OK; +} + +int nl_gchannels(struct cmd_context *ctx) +{ + struct nl_context *nlctx = ctx->nlctx; + struct nl_socket *nlsk = nlctx->ethnl_socket; + int ret; + + if (netlink_cmd_check(ctx, ETHTOOL_MSG_CHANNELS_GET, true)) + return -EOPNOTSUPP; + if (ctx->argc > 0) { + fprintf(stderr, "ethtool: unexpected parameter '%s'\n", + *ctx->argp); + return 1; + } + + ret = nlsock_prep_get_request(nlsk, ETHTOOL_MSG_CHANNELS_GET, + ETHTOOL_A_CHANNELS_HEADER, 0); + if (ret < 0) + return ret; + return nlsock_send_get_request(nlsk, channels_reply_cb); +} + +/* CHANNELS_SET */ + +static const struct param_parser schannels_params[] = { + { + .arg = "rx", + .type = ETHTOOL_A_CHANNELS_RX_COUNT, + .handler = nl_parse_direct_u32, + .min_argc = 1, + }, + { + .arg = "tx", + .type = ETHTOOL_A_CHANNELS_TX_COUNT, + .handler = nl_parse_direct_u32, + .min_argc = 1, + }, + { + .arg = "other", + .type = ETHTOOL_A_CHANNELS_OTHER_COUNT, + .handler = nl_parse_direct_u32, + .min_argc = 1, + }, + { + .arg = "combined", + .type = ETHTOOL_A_CHANNELS_COMBINED_COUNT, + .handler = nl_parse_direct_u32, + .min_argc = 1, + }, + {} +}; + +int nl_schannels(struct cmd_context *ctx) +{ + struct nl_context *nlctx = ctx->nlctx; + struct nl_msg_buff *msgbuff; + struct nl_socket *nlsk; + int ret; + + if (netlink_cmd_check(ctx, ETHTOOL_MSG_CHANNELS_SET, false)) + return -EOPNOTSUPP; + + nlctx->cmd = "-L"; + nlctx->argp = ctx->argp; + nlctx->argc = ctx->argc; + nlctx->devname = ctx->devname; + nlsk = nlctx->ethnl_socket; + msgbuff = &nlsk->msgbuff; + + ret = msg_init(nlctx, msgbuff, ETHTOOL_MSG_CHANNELS_SET, + NLM_F_REQUEST | NLM_F_ACK); + if (ret < 0) + return 2; + if (ethnla_fill_header(msgbuff, ETHTOOL_A_CHANNELS_HEADER, + ctx->devname, 0)) + return -EMSGSIZE; + + ret = nl_parser(nlctx, schannels_params, NULL, PARSER_GROUP_NONE, NULL); + if (ret < 0) + return 1; + + ret = nlsock_sendmsg(nlsk, NULL); + if (ret < 0) + return 1; + ret = nlsock_process_reply(nlsk, nomsg_reply_cb, nlctx); + if (ret == 0) + return 0; + else + return nlctx->exit_code ?: 1; +} diff --git a/netlink/coalesce.c b/netlink/coalesce.c new file mode 100644 index 0000000..75922a9 --- /dev/null +++ b/netlink/coalesce.c @@ -0,0 +1,269 @@ +/* + * coalesce.c - netlink implementation of coalescing commands + * + * Implementation of "ethtool -c <dev>" and "ethtool -C <dev> ..." + */ + +#include <errno.h> +#include <string.h> +#include <stdio.h> + +#include "../internal.h" +#include "../common.h" +#include "netlink.h" +#include "parser.h" + +/* COALESCE_GET */ + +int coalesce_reply_cb(const struct nlmsghdr *nlhdr, void *data) +{ + const struct nlattr *tb[ETHTOOL_A_COALESCE_MAX + 1] = {}; + DECLARE_ATTR_TB_INFO(tb); + struct nl_context *nlctx = data; + bool silent; + int err_ret; + int ret; + + silent = nlctx->is_dump || nlctx->is_monitor; + err_ret = silent ? MNL_CB_OK : MNL_CB_ERROR; + ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info); + if (ret < 0) + return err_ret; + nlctx->devname = get_dev_name(tb[ETHTOOL_A_COALESCE_HEADER]); + if (!dev_ok(nlctx)) + return err_ret; + + if (silent) + putchar('\n'); + printf("Coalesce parameters for %s:\n", nlctx->devname); + show_bool("rx", "Adaptive RX: %s ", + tb[ETHTOOL_A_COALESCE_USE_ADAPTIVE_RX]); + show_bool("tx", "TX: %s\n", tb[ETHTOOL_A_COALESCE_USE_ADAPTIVE_TX]); + show_u32(tb[ETHTOOL_A_COALESCE_STATS_BLOCK_USECS], + "stats-block-usecs: "); + show_u32(tb[ETHTOOL_A_COALESCE_RATE_SAMPLE_INTERVAL], + "sample-interval: "); + show_u32(tb[ETHTOOL_A_COALESCE_PKT_RATE_LOW], "pkt-rate-low: "); + show_u32(tb[ETHTOOL_A_COALESCE_PKT_RATE_HIGH], "pkt-rate-high: "); + putchar('\n'); + show_u32(tb[ETHTOOL_A_COALESCE_RX_USECS], "rx-usecs: "); + show_u32(tb[ETHTOOL_A_COALESCE_RX_MAX_FRAMES], "rx-frames: "); + show_u32(tb[ETHTOOL_A_COALESCE_RX_USECS_IRQ], "rx-usecs-irq: "); + show_u32(tb[ETHTOOL_A_COALESCE_RX_MAX_FRAMES_IRQ], "rx-frames-irq: "); + putchar('\n'); + show_u32(tb[ETHTOOL_A_COALESCE_TX_USECS], "tx-usecs: "); + show_u32(tb[ETHTOOL_A_COALESCE_TX_MAX_FRAMES], "tx-frames: "); + show_u32(tb[ETHTOOL_A_COALESCE_TX_USECS_IRQ], "tx-usecs-irq: "); + show_u32(tb[ETHTOOL_A_COALESCE_TX_MAX_FRAMES_IRQ], "tx-frames-irq: "); + putchar('\n'); + show_u32(tb[ETHTOOL_A_COALESCE_RX_USECS_LOW], "rx-usecs-low: "); + show_u32(tb[ETHTOOL_A_COALESCE_RX_MAX_FRAMES_LOW], "rx-frame-low: "); + show_u32(tb[ETHTOOL_A_COALESCE_TX_USECS_LOW], "tx-usecs-low: "); + show_u32(tb[ETHTOOL_A_COALESCE_TX_MAX_FRAMES_LOW], "tx-frame-low: "); + putchar('\n'); + show_u32(tb[ETHTOOL_A_COALESCE_RX_USECS_HIGH], "rx-usecs-high: "); + show_u32(tb[ETHTOOL_A_COALESCE_RX_MAX_FRAMES_HIGH], "rx-frame-high: "); + show_u32(tb[ETHTOOL_A_COALESCE_TX_USECS_HIGH], "tx-usecs-high: "); + show_u32(tb[ETHTOOL_A_COALESCE_TX_MAX_FRAMES_HIGH], "tx-frame-high: "); + putchar('\n'); + + return MNL_CB_OK; +} + +int nl_gcoalesce(struct cmd_context *ctx) +{ + struct nl_context *nlctx = ctx->nlctx; + struct nl_socket *nlsk = nlctx->ethnl_socket; + int ret; + + if (netlink_cmd_check(ctx, ETHTOOL_MSG_COALESCE_GET, true)) + return -EOPNOTSUPP; + if (ctx->argc > 0) { + fprintf(stderr, "ethtool: unexpected parameter '%s'\n", + *ctx->argp); + return 1; + } + + ret = nlsock_prep_get_request(nlsk, ETHTOOL_MSG_COALESCE_GET, + ETHTOOL_A_COALESCE_HEADER, 0); + if (ret < 0) + return ret; + return nlsock_send_get_request(nlsk, coalesce_reply_cb); +} + +/* COALESCE_SET */ + +static const struct param_parser scoalesce_params[] = { + { + .arg = "adaptive-rx", + .type = ETHTOOL_A_COALESCE_USE_ADAPTIVE_RX, + .handler = nl_parse_u8bool, + .min_argc = 1, + }, + { + .arg = "adaptive-tx", + .type = ETHTOOL_A_COALESCE_USE_ADAPTIVE_TX, + .handler = nl_parse_u8bool, + .min_argc = 1, + }, + { + .arg = "sample-interval", + .type = ETHTOOL_A_COALESCE_RATE_SAMPLE_INTERVAL, + .handler = nl_parse_direct_u32, + .min_argc = 1, + }, + { + .arg = "stats-block-usecs", + .type = ETHTOOL_A_COALESCE_STATS_BLOCK_USECS, + .handler = nl_parse_direct_u32, + .min_argc = 1, + }, + { + .arg = "pkt-rate-low", + .type = ETHTOOL_A_COALESCE_PKT_RATE_LOW, + .handler = nl_parse_direct_u32, + .min_argc = 1, + }, + { + .arg = "pkt-rate-high", + .type = ETHTOOL_A_COALESCE_PKT_RATE_HIGH, + .handler = nl_parse_direct_u32, + .min_argc = 1, + }, + { + .arg = "rx-usecs", + .type = ETHTOOL_A_COALESCE_RX_USECS, + .handler = nl_parse_direct_u32, + .min_argc = 1, + }, + { + .arg = "rx-frames", + .type = ETHTOOL_A_COALESCE_RX_MAX_FRAMES, + .handler = nl_parse_direct_u32, + .min_argc = 1, + }, + { + .arg = "rx-usecs-irq", + .type = ETHTOOL_A_COALESCE_RX_USECS_IRQ, + .handler = nl_parse_direct_u32, + .min_argc = 1, + }, + { + .arg = "rx-frames-irq", + .type = ETHTOOL_A_COALESCE_RX_MAX_FRAMES_IRQ, + .handler = nl_parse_direct_u32, + .min_argc = 1, + }, + { + .arg = "tx-usecs", + .type = ETHTOOL_A_COALESCE_TX_USECS, + .handler = nl_parse_direct_u32, + .min_argc = 1, + }, + { + .arg = "tx-frames", + .type = ETHTOOL_A_COALESCE_TX_MAX_FRAMES, + .handler = nl_parse_direct_u32, + .min_argc = 1, + }, + { + .arg = "tx-usecs-irq", + .type = ETHTOOL_A_COALESCE_TX_USECS_IRQ, + .handler = nl_parse_direct_u32, + .min_argc = 1, + }, + { + .arg = "tx-frames-irq", + .type = ETHTOOL_A_COALESCE_TX_MAX_FRAMES_IRQ, + .handler = nl_parse_direct_u32, + .min_argc = 1, + }, + { + .arg = "rx-usecs-low", + .type = ETHTOOL_A_COALESCE_RX_USECS_LOW, + .handler = nl_parse_direct_u32, + .min_argc = 1, + }, + { + .arg = "rx-frames-low", + .type = ETHTOOL_A_COALESCE_RX_MAX_FRAMES_LOW, + .handler = nl_parse_direct_u32, + .min_argc = 1, + }, + { + .arg = "tx-usecs-low", + .type = ETHTOOL_A_COALESCE_TX_USECS_LOW, + .handler = nl_parse_direct_u32, + .min_argc = 1, + }, + { + .arg = "tx-frames-low", + .type = ETHTOOL_A_COALESCE_TX_MAX_FRAMES_LOW, + .handler = nl_parse_direct_u32, + .min_argc = 1, + }, + { + .arg = "rx-usecs-high", + .type = ETHTOOL_A_COALESCE_RX_USECS_HIGH, + .handler = nl_parse_direct_u32, + .min_argc = 1, + }, + { + .arg = "rx-frames-high", + .type = ETHTOOL_A_COALESCE_RX_MAX_FRAMES_HIGH, + .handler = nl_parse_direct_u32, + .min_argc = 1, + }, + { + .arg = "tx-usecs-high", + .type = ETHTOOL_A_COALESCE_TX_USECS_HIGH, + .handler = nl_parse_direct_u32, + .min_argc = 1, + }, + { + .arg = "tx-frames-high", + .type = ETHTOOL_A_COALESCE_TX_MAX_FRAMES_HIGH, + .handler = nl_parse_direct_u32, + .min_argc = 1, + }, + {} +}; + +int nl_scoalesce(struct cmd_context *ctx) +{ + struct nl_context *nlctx = ctx->nlctx; + struct nl_msg_buff *msgbuff; + struct nl_socket *nlsk; + int ret; + + if (netlink_cmd_check(ctx, ETHTOOL_MSG_COALESCE_SET, false)) + return -EOPNOTSUPP; + + nlctx->cmd = "-C"; + nlctx->argp = ctx->argp; + nlctx->argc = ctx->argc; + nlctx->devname = ctx->devname; + nlsk = nlctx->ethnl_socket; + msgbuff = &nlsk->msgbuff; + + ret = msg_init(nlctx, msgbuff, ETHTOOL_MSG_COALESCE_SET, + NLM_F_REQUEST | NLM_F_ACK); + if (ret < 0) + return 2; + if (ethnla_fill_header(msgbuff, ETHTOOL_A_COALESCE_HEADER, + ctx->devname, 0)) + return -EMSGSIZE; + + ret = nl_parser(nlctx, scoalesce_params, NULL, PARSER_GROUP_NONE, NULL); + if (ret < 0) + return 1; + + ret = nlsock_sendmsg(nlsk, NULL); + if (ret < 0) + return 1; + ret = nlsock_process_reply(nlsk, nomsg_reply_cb, nlctx); + if (ret == 0) + return 0; + else + return nlctx->exit_code ?: 1; +} diff --git a/netlink/desc-ethtool.c b/netlink/desc-ethtool.c index 76c6f13..96291b9 100644 --- a/netlink/desc-ethtool.c +++ b/netlink/desc-ethtool.c @@ -4,9 +4,9 @@ * Descriptions of ethtool netlink messages and attributes for pretty print. */ +#include "../internal.h" #include <linux/ethtool_netlink.h> -#include "../internal.h" #include "prettymsg.h" static const struct pretty_nla_desc __header_desc[] = { @@ -85,12 +85,18 @@ static const struct pretty_nla_desc __linkmodes_desc[] = { NLATTR_DESC_NESTED(ETHTOOL_A_LINKMODES_PEER, bitset), NLATTR_DESC_U32(ETHTOOL_A_LINKMODES_SPEED), NLATTR_DESC_U8(ETHTOOL_A_LINKMODES_DUPLEX), + NLATTR_DESC_U8(ETHTOOL_A_LINKMODES_MASTER_SLAVE_CFG), + NLATTR_DESC_U8(ETHTOOL_A_LINKMODES_MASTER_SLAVE_STATE), }; static const struct pretty_nla_desc __linkstate_desc[] = { NLATTR_DESC_INVALID(ETHTOOL_A_LINKSTATE_UNSPEC), NLATTR_DESC_NESTED(ETHTOOL_A_LINKSTATE_HEADER, header), NLATTR_DESC_BOOL(ETHTOOL_A_LINKSTATE_LINK), + NLATTR_DESC_U32(ETHTOOL_A_LINKSTATE_SQI), + NLATTR_DESC_U32(ETHTOOL_A_LINKSTATE_SQI_MAX), + NLATTR_DESC_U8(ETHTOOL_A_LINKSTATE_EXT_STATE), + NLATTR_DESC_U8(ETHTOOL_A_LINKSTATE_EXT_SUBSTATE), }; static const struct pretty_nla_desc __debug_desc[] = { @@ -106,6 +112,211 @@ static const struct pretty_nla_desc __wol_desc[] = { NLATTR_DESC_BINARY(ETHTOOL_A_WOL_SOPASS), }; +static const struct pretty_nla_desc __features_desc[] = { + NLATTR_DESC_INVALID(ETHTOOL_A_FEATURES_UNSPEC), + NLATTR_DESC_NESTED(ETHTOOL_A_FEATURES_HEADER, header), + NLATTR_DESC_NESTED(ETHTOOL_A_FEATURES_HW, bitset), + NLATTR_DESC_NESTED(ETHTOOL_A_FEATURES_WANTED, bitset), + NLATTR_DESC_NESTED(ETHTOOL_A_FEATURES_ACTIVE, bitset), + NLATTR_DESC_NESTED(ETHTOOL_A_FEATURES_NOCHANGE, bitset), +}; + +static const struct pretty_nla_desc __privflags_desc[] = { + NLATTR_DESC_INVALID(ETHTOOL_A_PRIVFLAGS_UNSPEC), + NLATTR_DESC_NESTED(ETHTOOL_A_PRIVFLAGS_HEADER, header), + NLATTR_DESC_NESTED(ETHTOOL_A_PRIVFLAGS_FLAGS, bitset), +}; + +static const struct pretty_nla_desc __rings_desc[] = { + NLATTR_DESC_INVALID(ETHTOOL_A_RINGS_UNSPEC), + NLATTR_DESC_NESTED(ETHTOOL_A_RINGS_HEADER, header), + NLATTR_DESC_U32(ETHTOOL_A_RINGS_RX_MAX), + NLATTR_DESC_U32(ETHTOOL_A_RINGS_RX_MINI_MAX), + NLATTR_DESC_U32(ETHTOOL_A_RINGS_RX_JUMBO_MAX), + NLATTR_DESC_U32(ETHTOOL_A_RINGS_TX_MAX), + NLATTR_DESC_U32(ETHTOOL_A_RINGS_RX), + NLATTR_DESC_U32(ETHTOOL_A_RINGS_RX_MINI), + NLATTR_DESC_U32(ETHTOOL_A_RINGS_RX_JUMBO), + NLATTR_DESC_U32(ETHTOOL_A_RINGS_TX), +}; + +static const struct pretty_nla_desc __channels_desc[] = { + NLATTR_DESC_INVALID(ETHTOOL_A_CHANNELS_UNSPEC), + NLATTR_DESC_NESTED(ETHTOOL_A_CHANNELS_HEADER, header), + NLATTR_DESC_U32(ETHTOOL_A_CHANNELS_RX_MAX), + NLATTR_DESC_U32(ETHTOOL_A_CHANNELS_TX_MAX), + NLATTR_DESC_U32(ETHTOOL_A_CHANNELS_OTHER_MAX), + NLATTR_DESC_U32(ETHTOOL_A_CHANNELS_COMBINED_MAX), + NLATTR_DESC_U32(ETHTOOL_A_CHANNELS_RX_COUNT), + NLATTR_DESC_U32(ETHTOOL_A_CHANNELS_TX_COUNT), + NLATTR_DESC_U32(ETHTOOL_A_CHANNELS_OTHER_COUNT), + NLATTR_DESC_U32(ETHTOOL_A_CHANNELS_COMBINED_COUNT), +}; + +static const struct pretty_nla_desc __coalesce_desc[] = { + NLATTR_DESC_INVALID(ETHTOOL_A_COALESCE_UNSPEC), + NLATTR_DESC_NESTED(ETHTOOL_A_COALESCE_HEADER, header), + NLATTR_DESC_U32(ETHTOOL_A_COALESCE_RX_USECS), + NLATTR_DESC_U32(ETHTOOL_A_COALESCE_RX_MAX_FRAMES), + NLATTR_DESC_U32(ETHTOOL_A_COALESCE_RX_USECS_IRQ), + NLATTR_DESC_U32(ETHTOOL_A_COALESCE_RX_MAX_FRAMES_IRQ), + NLATTR_DESC_U32(ETHTOOL_A_COALESCE_TX_USECS), + NLATTR_DESC_U32(ETHTOOL_A_COALESCE_TX_MAX_FRAMES), + NLATTR_DESC_U32(ETHTOOL_A_COALESCE_TX_USECS_IRQ), + NLATTR_DESC_U32(ETHTOOL_A_COALESCE_TX_MAX_FRAMES_IRQ), + NLATTR_DESC_U32(ETHTOOL_A_COALESCE_STATS_BLOCK_USECS), + NLATTR_DESC_BOOL(ETHTOOL_A_COALESCE_USE_ADAPTIVE_RX), + NLATTR_DESC_BOOL(ETHTOOL_A_COALESCE_USE_ADAPTIVE_TX), + NLATTR_DESC_U32(ETHTOOL_A_COALESCE_PKT_RATE_LOW), + NLATTR_DESC_U32(ETHTOOL_A_COALESCE_RX_USECS_LOW), + NLATTR_DESC_U32(ETHTOOL_A_COALESCE_RX_MAX_FRAMES_LOW), + NLATTR_DESC_U32(ETHTOOL_A_COALESCE_TX_USECS_LOW), + NLATTR_DESC_U32(ETHTOOL_A_COALESCE_TX_MAX_FRAMES_LOW), + NLATTR_DESC_U32(ETHTOOL_A_COALESCE_PKT_RATE_HIGH), + NLATTR_DESC_U32(ETHTOOL_A_COALESCE_RX_USECS_HIGH), + NLATTR_DESC_U32(ETHTOOL_A_COALESCE_RX_MAX_FRAMES_HIGH), + NLATTR_DESC_U32(ETHTOOL_A_COALESCE_TX_USECS_HIGH), + NLATTR_DESC_U32(ETHTOOL_A_COALESCE_TX_MAX_FRAMES_HIGH), + NLATTR_DESC_U32(ETHTOOL_A_COALESCE_RATE_SAMPLE_INTERVAL), +}; + +static const struct pretty_nla_desc __pause_stats_desc[] = { + NLATTR_DESC_BINARY(ETHTOOL_A_PAUSE_STAT_PAD), + NLATTR_DESC_U64(ETHTOOL_A_PAUSE_STAT_TX_FRAMES), + NLATTR_DESC_U64(ETHTOOL_A_PAUSE_STAT_RX_FRAMES), +}; + +static const struct pretty_nla_desc __pause_desc[] = { + NLATTR_DESC_INVALID(ETHTOOL_A_PAUSE_UNSPEC), + NLATTR_DESC_NESTED(ETHTOOL_A_PAUSE_HEADER, header), + NLATTR_DESC_BOOL(ETHTOOL_A_PAUSE_AUTONEG), + NLATTR_DESC_BOOL(ETHTOOL_A_PAUSE_RX), + NLATTR_DESC_BOOL(ETHTOOL_A_PAUSE_TX), + NLATTR_DESC_NESTED(ETHTOOL_A_PAUSE_STATS, pause_stats), +}; + +static const struct pretty_nla_desc __eee_desc[] = { + NLATTR_DESC_INVALID(ETHTOOL_A_EEE_UNSPEC), + NLATTR_DESC_NESTED(ETHTOOL_A_EEE_HEADER, header), + NLATTR_DESC_NESTED(ETHTOOL_A_EEE_MODES_OURS, bitset), + NLATTR_DESC_NESTED(ETHTOOL_A_EEE_MODES_PEER, bitset), + NLATTR_DESC_BOOL(ETHTOOL_A_EEE_ACTIVE), + NLATTR_DESC_BOOL(ETHTOOL_A_EEE_ENABLED), + NLATTR_DESC_BOOL(ETHTOOL_A_EEE_TX_LPI_ENABLED), + NLATTR_DESC_U32(ETHTOOL_A_EEE_TX_LPI_TIMER), +}; + +static const struct pretty_nla_desc __tsinfo_desc[] = { + NLATTR_DESC_INVALID(ETHTOOL_A_TSINFO_UNSPEC), + NLATTR_DESC_NESTED(ETHTOOL_A_TSINFO_HEADER, header), + NLATTR_DESC_NESTED(ETHTOOL_A_TSINFO_TIMESTAMPING, bitset), + NLATTR_DESC_NESTED(ETHTOOL_A_TSINFO_TX_TYPES, bitset), + NLATTR_DESC_NESTED(ETHTOOL_A_TSINFO_RX_FILTERS, bitset), + NLATTR_DESC_U32(ETHTOOL_A_TSINFO_PHC_INDEX), +}; + +static const struct pretty_nla_desc __cable_test_desc[] = { + NLATTR_DESC_INVALID(ETHTOOL_A_CABLE_TEST_UNSPEC), + NLATTR_DESC_NESTED(ETHTOOL_A_CABLE_TEST_HEADER, header), +}; + +static const struct pretty_nla_desc __cable_test_result_desc[] = { + NLATTR_DESC_INVALID(ETHTOOL_A_CABLE_RESULT_UNSPEC), + NLATTR_DESC_U8(ETHTOOL_A_CABLE_RESULT_PAIR), + NLATTR_DESC_U8(ETHTOOL_A_CABLE_RESULT_CODE), +}; + +static const struct pretty_nla_desc __cable_test_flength_desc[] = { + NLATTR_DESC_INVALID(ETHTOOL_A_CABLE_FAULT_LENGTH_UNSPEC), + NLATTR_DESC_U8(ETHTOOL_A_CABLE_FAULT_LENGTH_PAIR), + NLATTR_DESC_U32(ETHTOOL_A_CABLE_FAULT_LENGTH_CM), +}; + +static const struct pretty_nla_desc __cable_nest_desc[] = { + NLATTR_DESC_INVALID(ETHTOOL_A_CABLE_NEST_UNSPEC), + NLATTR_DESC_NESTED(ETHTOOL_A_CABLE_NEST_RESULT, cable_test_result), + NLATTR_DESC_NESTED(ETHTOOL_A_CABLE_NEST_FAULT_LENGTH, + cable_test_flength), +}; + +static const struct pretty_nla_desc __cable_test_ntf_desc[] = { + NLATTR_DESC_INVALID(ETHTOOL_A_CABLE_TEST_NTF_UNSPEC), + NLATTR_DESC_NESTED(ETHTOOL_A_CABLE_TEST_NTF_HEADER, header), + NLATTR_DESC_U8(ETHTOOL_A_CABLE_TEST_NTF_STATUS), + NLATTR_DESC_NESTED(ETHTOOL_A_CABLE_TEST_NTF_NEST, cable_nest), +}; + +static const struct pretty_nla_desc __cable_test_tdr_cfg_desc[] = { + NLATTR_DESC_INVALID(ETHTOOL_A_CABLE_TEST_TDR_CFG_UNSPEC), + NLATTR_DESC_U32(ETHTOOL_A_CABLE_TEST_TDR_CFG_FIRST), + NLATTR_DESC_U32(ETHTOOL_A_CABLE_TEST_TDR_CFG_LAST), + NLATTR_DESC_U32(ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP), + NLATTR_DESC_U8(ETHTOOL_A_CABLE_TEST_TDR_CFG_PAIR), +}; + +static const struct pretty_nla_desc __cable_test_tdr_desc[] = { + NLATTR_DESC_INVALID(ETHTOOL_A_CABLE_TEST_TDR_UNSPEC), + NLATTR_DESC_NESTED(ETHTOOL_A_CABLE_TEST_TDR_HEADER, header), + NLATTR_DESC_NESTED(ETHTOOL_A_CABLE_TEST_TDR_CFG, cable_test_tdr_cfg), +}; + +static const struct pretty_nla_desc __cable_step_desc[] = { + NLATTR_DESC_INVALID(ETHTOOL_A_CABLE_STEP_UNSPEC), + NLATTR_DESC_U32(ETHTOOL_A_CABLE_STEP_FIRST_DISTANCE), + NLATTR_DESC_U32(ETHTOOL_A_CABLE_STEP_LAST_DISTANCE), + NLATTR_DESC_U32(ETHTOOL_A_CABLE_STEP_STEP_DISTANCE), +}; + +static const struct pretty_nla_desc __cable_amplitude_desc[] = { + NLATTR_DESC_INVALID(ETHTOOL_A_CABLE_AMPLITUDE_UNSPEC), + NLATTR_DESC_U8(ETHTOOL_A_CABLE_AMPLITUDE_PAIR), + NLATTR_DESC_S16(ETHTOOL_A_CABLE_AMPLITUDE_mV), +}; + +static const struct pretty_nla_desc __cable_pulse_desc[] = { + NLATTR_DESC_INVALID(ETHTOOL_A_CABLE_PULSE_UNSPEC), + NLATTR_DESC_S16(ETHTOOL_A_CABLE_PULSE_mV), +}; + +static const struct pretty_nla_desc __cable_test_tdr_nest_desc[] = { + NLATTR_DESC_INVALID(ETHTOOL_A_CABLE_TDR_NEST_UNSPEC), + NLATTR_DESC_NESTED(ETHTOOL_A_CABLE_TDR_NEST_STEP, cable_step), + NLATTR_DESC_NESTED(ETHTOOL_A_CABLE_TDR_NEST_AMPLITUDE, cable_amplitude), + NLATTR_DESC_NESTED(ETHTOOL_A_CABLE_TDR_NEST_PULSE, cable_pulse), +}; + +static const struct pretty_nla_desc __cable_test_tdr_ntf_desc[] = { + NLATTR_DESC_INVALID(ETHTOOL_A_CABLE_TEST_TDR_UNSPEC), + NLATTR_DESC_NESTED(ETHTOOL_A_CABLE_TEST_TDR_NTF_HEADER, header), + NLATTR_DESC_U8(ETHTOOL_A_CABLE_TEST_TDR_NTF_STATUS), + NLATTR_DESC_NESTED(ETHTOOL_A_CABLE_TEST_TDR_NTF_NEST, + cable_test_tdr_nest), +}; + +const struct pretty_nla_desc __tunnel_udp_entry_desc[] = { + NLATTR_DESC_INVALID(ETHTOOL_A_TUNNEL_UDP_ENTRY_UNSPEC), + NLATTR_DESC_U16(ETHTOOL_A_TUNNEL_UDP_ENTRY_PORT), + NLATTR_DESC_U32(ETHTOOL_A_TUNNEL_UDP_ENTRY_TYPE), +}; + +const struct pretty_nla_desc __tunnel_udp_table_desc[] = { + NLATTR_DESC_INVALID(ETHTOOL_A_TUNNEL_UDP_TABLE_UNSPEC), + NLATTR_DESC_U32(ETHTOOL_A_TUNNEL_UDP_TABLE_SIZE), + NLATTR_DESC_NESTED(ETHTOOL_A_TUNNEL_UDP_TABLE_TYPES, bitset), + NLATTR_DESC_NESTED(ETHTOOL_A_TUNNEL_UDP_TABLE_ENTRY, tunnel_udp_entry), +}; + +const struct pretty_nla_desc __tunnel_udp_desc[] = { + NLATTR_DESC_INVALID(ETHTOOL_A_TUNNEL_UDP_UNSPEC), + NLATTR_DESC_NESTED(ETHTOOL_A_TUNNEL_UDP_TABLE, tunnel_udp_table), +}; + +const struct pretty_nla_desc __tunnel_info_desc[] = { + NLATTR_DESC_INVALID(ETHTOOL_A_TUNNEL_INFO_UNSPEC), + NLATTR_DESC_NESTED(ETHTOOL_A_TUNNEL_INFO_HEADER, header), + NLATTR_DESC_NESTED(ETHTOOL_A_TUNNEL_INFO_UDP_PORTS, tunnel_udp), +}; + const struct pretty_nlmsg_desc ethnl_umsg_desc[] = { NLMSG_DESC_INVALID(ETHTOOL_MSG_USER_NONE), NLMSG_DESC(ETHTOOL_MSG_STRSET_GET, strset), @@ -118,6 +329,24 @@ const struct pretty_nlmsg_desc ethnl_umsg_desc[] = { NLMSG_DESC(ETHTOOL_MSG_DEBUG_SET, debug), NLMSG_DESC(ETHTOOL_MSG_WOL_GET, wol), NLMSG_DESC(ETHTOOL_MSG_WOL_SET, wol), + NLMSG_DESC(ETHTOOL_MSG_FEATURES_GET, features), + NLMSG_DESC(ETHTOOL_MSG_FEATURES_SET, features), + NLMSG_DESC(ETHTOOL_MSG_PRIVFLAGS_GET, privflags), + NLMSG_DESC(ETHTOOL_MSG_PRIVFLAGS_SET, privflags), + NLMSG_DESC(ETHTOOL_MSG_RINGS_GET, rings), + NLMSG_DESC(ETHTOOL_MSG_RINGS_SET, rings), + NLMSG_DESC(ETHTOOL_MSG_CHANNELS_GET, channels), + NLMSG_DESC(ETHTOOL_MSG_CHANNELS_SET, channels), + NLMSG_DESC(ETHTOOL_MSG_COALESCE_GET, coalesce), + NLMSG_DESC(ETHTOOL_MSG_COALESCE_SET, coalesce), + NLMSG_DESC(ETHTOOL_MSG_PAUSE_GET, pause), + NLMSG_DESC(ETHTOOL_MSG_PAUSE_SET, pause), + NLMSG_DESC(ETHTOOL_MSG_EEE_GET, eee), + NLMSG_DESC(ETHTOOL_MSG_EEE_SET, eee), + NLMSG_DESC(ETHTOOL_MSG_TSINFO_GET, tsinfo), + NLMSG_DESC(ETHTOOL_MSG_CABLE_TEST_ACT, cable_test), + NLMSG_DESC(ETHTOOL_MSG_CABLE_TEST_TDR_ACT, cable_test_tdr), + NLMSG_DESC(ETHTOOL_MSG_TUNNEL_INFO_GET, tunnel_info), }; const unsigned int ethnl_umsg_n_desc = ARRAY_SIZE(ethnl_umsg_desc); @@ -134,6 +363,25 @@ const struct pretty_nlmsg_desc ethnl_kmsg_desc[] = { NLMSG_DESC(ETHTOOL_MSG_DEBUG_NTF, debug), NLMSG_DESC(ETHTOOL_MSG_WOL_GET_REPLY, wol), NLMSG_DESC(ETHTOOL_MSG_WOL_NTF, wol), + NLMSG_DESC(ETHTOOL_MSG_FEATURES_GET_REPLY, features), + NLMSG_DESC(ETHTOOL_MSG_FEATURES_SET_REPLY, features), + NLMSG_DESC(ETHTOOL_MSG_FEATURES_NTF, features), + NLMSG_DESC(ETHTOOL_MSG_PRIVFLAGS_GET_REPLY, privflags), + NLMSG_DESC(ETHTOOL_MSG_PRIVFLAGS_NTF, privflags), + NLMSG_DESC(ETHTOOL_MSG_RINGS_GET_REPLY, rings), + NLMSG_DESC(ETHTOOL_MSG_RINGS_NTF, rings), + NLMSG_DESC(ETHTOOL_MSG_CHANNELS_GET_REPLY, channels), + NLMSG_DESC(ETHTOOL_MSG_CHANNELS_NTF, channels), + NLMSG_DESC(ETHTOOL_MSG_COALESCE_GET_REPLY, coalesce), + NLMSG_DESC(ETHTOOL_MSG_COALESCE_NTF, coalesce), + NLMSG_DESC(ETHTOOL_MSG_PAUSE_GET_REPLY, pause), + NLMSG_DESC(ETHTOOL_MSG_PAUSE_NTF, pause), + NLMSG_DESC(ETHTOOL_MSG_EEE_GET_REPLY, eee), + NLMSG_DESC(ETHTOOL_MSG_EEE_NTF, eee), + NLMSG_DESC(ETHTOOL_MSG_TSINFO_GET_REPLY, tsinfo), + NLMSG_DESC(ETHTOOL_MSG_CABLE_TEST_NTF, cable_test_ntf), + NLMSG_DESC(ETHTOOL_MSG_CABLE_TEST_TDR_NTF, cable_test_tdr_ntf), + NLMSG_DESC(ETHTOOL_MSG_TUNNEL_INFO_GET_REPLY, tunnel_info), }; const unsigned int ethnl_kmsg_n_desc = ARRAY_SIZE(ethnl_kmsg_desc); diff --git a/netlink/desc-genlctrl.c b/netlink/desc-genlctrl.c index 9840179..43b41ab 100644 --- a/netlink/desc-genlctrl.c +++ b/netlink/desc-genlctrl.c @@ -29,6 +29,59 @@ static const struct pretty_nla_desc __mcgrps_desc[] = { NLATTR_DESC_NESTED(0, mcgrp), }; +static const char *__policy_attr_type_names[] = { + [NL_ATTR_TYPE_INVALID] = "NL_ATTR_TYPE_INVALID", + [NL_ATTR_TYPE_FLAG] = "NL_ATTR_TYPE_FLAG", + [NL_ATTR_TYPE_U8] = "NL_ATTR_TYPE_U8", + [NL_ATTR_TYPE_U16] = "NL_ATTR_TYPE_U16", + [NL_ATTR_TYPE_U32] = "NL_ATTR_TYPE_U32", + [NL_ATTR_TYPE_U64] = "NL_ATTR_TYPE_U64", + [NL_ATTR_TYPE_S8] = "NL_ATTR_TYPE_S8", + [NL_ATTR_TYPE_S16] = "NL_ATTR_TYPE_S16", + [NL_ATTR_TYPE_S32] = "NL_ATTR_TYPE_S32", + [NL_ATTR_TYPE_S64] = "NL_ATTR_TYPE_S64", + [NL_ATTR_TYPE_BINARY] = "NL_ATTR_TYPE_BINARY", + [NL_ATTR_TYPE_STRING] = "NL_ATTR_TYPE_STRING", + [NL_ATTR_TYPE_NUL_STRING] = "NL_ATTR_TYPE_NUL_STRING", + [NL_ATTR_TYPE_NESTED] = "NL_ATTR_TYPE_NESTED", + [NL_ATTR_TYPE_NESTED_ARRAY] = "NL_ATTR_TYPE_NESTED_ARRAY", + [NL_ATTR_TYPE_BITFIELD32] = "NL_ATTR_TYPE_BITFIELD32", +}; + +static const struct pretty_nla_desc __policy_attr_desc[] = { + NLATTR_DESC_INVALID(NL_POLICY_TYPE_ATTR_UNSPEC), + NLATTR_DESC_U32_ENUM(NL_POLICY_TYPE_ATTR_TYPE, policy_attr_type), + NLATTR_DESC_S64(NL_POLICY_TYPE_ATTR_MIN_VALUE_S), + NLATTR_DESC_S64(NL_POLICY_TYPE_ATTR_MAX_VALUE_S), + NLATTR_DESC_U64(NL_POLICY_TYPE_ATTR_MIN_VALUE_U), + NLATTR_DESC_U64(NL_POLICY_TYPE_ATTR_MAX_VALUE_U), + NLATTR_DESC_U32(NL_POLICY_TYPE_ATTR_MIN_LENGTH), + NLATTR_DESC_U32(NL_POLICY_TYPE_ATTR_MAX_LENGTH), + NLATTR_DESC_U32(NL_POLICY_TYPE_ATTR_POLICY_IDX), + NLATTR_DESC_U32(NL_POLICY_TYPE_ATTR_POLICY_MAXTYPE), + NLATTR_DESC_X32(NL_POLICY_TYPE_ATTR_BITFIELD32_MASK), + NLATTR_DESC_X64(NL_POLICY_TYPE_ATTR_PAD), + NLATTR_DESC_BINARY(NL_POLICY_TYPE_ATTR_MASK), +}; + +static const struct pretty_nla_desc __policy_attrs_desc[] = { + NLATTR_DESC_NESTED(0, policy_attr), +}; + +static const struct pretty_nla_desc __policies_desc[] = { + NLATTR_DESC_ARRAY(0, policy_attrs), +}; + +static const struct pretty_nla_desc __op_policy_desc[] = { + NLATTR_DESC_INVALID(CTRL_ATTR_POLICY_UNSPEC), + NLATTR_DESC_U32(CTRL_ATTR_POLICY_DO), + NLATTR_DESC_U32(CTRL_ATTR_POLICY_DUMP), +}; + +static const struct pretty_nla_desc __op_policies_desc[] = { + NLATTR_DESC_NESTED(0, op_policy), +}; + static const struct pretty_nla_desc __attr_desc[] = { NLATTR_DESC_INVALID(CTRL_ATTR_UNSPEC), NLATTR_DESC_U16(CTRL_ATTR_FAMILY_ID), @@ -38,6 +91,9 @@ static const struct pretty_nla_desc __attr_desc[] = { NLATTR_DESC_U32(CTRL_ATTR_MAXATTR), NLATTR_DESC_ARRAY(CTRL_ATTR_OPS, attrops), NLATTR_DESC_ARRAY(CTRL_ATTR_MCAST_GROUPS, mcgrps), + NLATTR_DESC_ARRAY(CTRL_ATTR_POLICY, policies), + NLATTR_DESC_ARRAY(CTRL_ATTR_OP_POLICY, op_policies), + NLATTR_DESC_U32(CTRL_ATTR_OP), }; const struct pretty_nlmsg_desc genlctrl_msg_desc[] = { @@ -51,6 +107,7 @@ const struct pretty_nlmsg_desc genlctrl_msg_desc[] = { NLMSG_DESC(CTRL_CMD_NEWMCAST_GRP, attr), NLMSG_DESC(CTRL_CMD_DELMCAST_GRP, attr), NLMSG_DESC(CTRL_CMD_GETMCAST_GRP, attr), + NLMSG_DESC(CTRL_CMD_GETPOLICY, attr), }; const unsigned int genlctrl_msg_n_desc = ARRAY_SIZE(genlctrl_msg_desc); diff --git a/netlink/eee.c b/netlink/eee.c new file mode 100644 index 0000000..04d8f0b --- /dev/null +++ b/netlink/eee.c @@ -0,0 +1,189 @@ +/* + * eee.c - netlink implementation of eee commands + * + * Implementation of "ethtool --show-eee <dev>" and + * "ethtool --set-eee <dev> ..." + */ + +#include <errno.h> +#include <string.h> +#include <stdio.h> + +#include "../internal.h" +#include "../common.h" +#include "netlink.h" +#include "bitset.h" +#include "parser.h" + +/* EEE_GET */ + +int eee_reply_cb(const struct nlmsghdr *nlhdr, void *data) +{ + const struct nlattr *tb[ETHTOOL_A_EEE_MAX + 1] = {}; + DECLARE_ATTR_TB_INFO(tb); + bool enabled, active, tx_lpi_enabled; + struct nl_context *nlctx = data; + bool silent; + int err_ret; + int ret; + + silent = nlctx->is_dump || nlctx->is_monitor; + err_ret = silent ? MNL_CB_OK : MNL_CB_ERROR; + ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info); + if (ret < 0) + return err_ret; + nlctx->devname = get_dev_name(tb[ETHTOOL_A_EEE_HEADER]); + if (!dev_ok(nlctx)) + return err_ret; + + if (!tb[ETHTOOL_A_EEE_MODES_OURS] || + !tb[ETHTOOL_A_EEE_ACTIVE] || !tb[ETHTOOL_A_EEE_ENABLED] || + !tb[ETHTOOL_A_EEE_TX_LPI_ENABLED] || + !tb[ETHTOOL_A_EEE_TX_LPI_TIMER]) { + fprintf(stderr, "Malformed response from kernel\n"); + return err_ret; + } + active = mnl_attr_get_u8(tb[ETHTOOL_A_EEE_ACTIVE]); + enabled = mnl_attr_get_u8(tb[ETHTOOL_A_EEE_ENABLED]); + tx_lpi_enabled = mnl_attr_get_u8(tb[ETHTOOL_A_EEE_TX_LPI_ENABLED]); + + if (silent) + putchar('\n'); + printf("EEE settings for %s:\n", nlctx->devname); + printf("\tEEE status: "); + if (bitset_is_empty(tb[ETHTOOL_A_EEE_MODES_OURS], true, &ret)) { + printf("not supported\n"); + return MNL_CB_OK; + } + if (!enabled) + printf("disabled\n"); + else + printf("enabled - %s\n", active ? "active" : "inactive"); + printf("\tTx LPI: "); + if (tx_lpi_enabled) + printf("%u (us)\n", + mnl_attr_get_u32(tb[ETHTOOL_A_EEE_TX_LPI_TIMER])); + else + printf("disabled\n"); + + ret = dump_link_modes(nlctx, tb[ETHTOOL_A_EEE_MODES_OURS], true, + LM_CLASS_REAL, + "Supported EEE link modes: ", NULL, "\n", + "Not reported"); + if (ret < 0) + return err_ret; + ret = dump_link_modes(nlctx, tb[ETHTOOL_A_EEE_MODES_OURS], false, + LM_CLASS_REAL, + "Advertised EEE link modes: ", NULL, "\n", + "Not reported"); + if (ret < 0) + return err_ret; + ret = dump_link_modes(nlctx, tb[ETHTOOL_A_EEE_MODES_PEER], false, + LM_CLASS_REAL, + "Link partner advertised EEE link modes: ", NULL, + "\n", "Not reported"); + if (ret < 0) + return err_ret; + + return MNL_CB_OK; +} + +int nl_geee(struct cmd_context *ctx) +{ + struct nl_context *nlctx = ctx->nlctx; + struct nl_socket *nlsk = nlctx->ethnl_socket; + int ret; + + if (netlink_cmd_check(ctx, ETHTOOL_MSG_EEE_GET, true)) + return -EOPNOTSUPP; + if (ctx->argc > 0) { + fprintf(stderr, "ethtool: unexpected parameter '%s'\n", + *ctx->argp); + return 1; + } + + ret = nlsock_prep_get_request(nlsk, ETHTOOL_MSG_EEE_GET, + ETHTOOL_A_EEE_HEADER, 0); + if (ret < 0) + return ret; + return nlsock_send_get_request(nlsk, eee_reply_cb); +} + +/* EEE_SET */ + +static const struct bitset_parser_data advertise_parser_data = { + .no_mask = false, + .force_hex = true, +}; + +static const struct param_parser seee_params[] = { + { + .arg = "advertise", + .type = ETHTOOL_A_EEE_MODES_OURS, + .handler = nl_parse_bitset, + .handler_data = &advertise_parser_data, + .min_argc = 1, + }, + { + .arg = "tx-lpi", + .type = ETHTOOL_A_EEE_TX_LPI_ENABLED, + .handler = nl_parse_u8bool, + .min_argc = 1, + }, + { + .arg = "tx-timer", + .type = ETHTOOL_A_EEE_TX_LPI_TIMER, + .handler = nl_parse_direct_u32, + .min_argc = 1, + }, + { + .arg = "eee", + .type = ETHTOOL_A_EEE_ENABLED, + .handler = nl_parse_u8bool, + .min_argc = 1, + }, + {} +}; + +int nl_seee(struct cmd_context *ctx) +{ + struct nl_context *nlctx = ctx->nlctx; + struct nl_msg_buff *msgbuff; + struct nl_socket *nlsk; + int ret; + + if (netlink_cmd_check(ctx, ETHTOOL_MSG_EEE_SET, false)) + return -EOPNOTSUPP; + if (!ctx->argc) { + fprintf(stderr, "ethtool (--set-eee): parameters missing\n"); + return 1; + } + + nlctx->cmd = "--set-eee"; + nlctx->argp = ctx->argp; + nlctx->argc = ctx->argc; + nlctx->devname = ctx->devname; + nlsk = nlctx->ethnl_socket; + msgbuff = &nlsk->msgbuff; + + ret = msg_init(nlctx, msgbuff, ETHTOOL_MSG_EEE_SET, + NLM_F_REQUEST | NLM_F_ACK); + if (ret < 0) + return 2; + if (ethnla_fill_header(msgbuff, ETHTOOL_A_EEE_HEADER, + ctx->devname, 0)) + return -EMSGSIZE; + + ret = nl_parser(nlctx, seee_params, NULL, PARSER_GROUP_NONE, NULL); + if (ret < 0) + return 1; + + ret = nlsock_sendmsg(nlsk, NULL); + if (ret < 0) + return 76; + ret = nlsock_process_reply(nlsk, nomsg_reply_cb, nlctx); + if (ret == 0) + return 0; + else + return nlctx->exit_code ?: 76; +} diff --git a/netlink/extapi.h b/netlink/extapi.h index d5b3cd9..761cafb 100644 --- a/netlink/extapi.h +++ b/netlink/extapi.h @@ -10,27 +10,56 @@ struct cmd_context; struct nl_context; +typedef int (*nl_func_t)(struct cmd_context *); + #ifdef ETHTOOL_ENABLE_NETLINK -int netlink_init(struct cmd_context *ctx); -void netlink_done(struct cmd_context *ctx); +void netlink_run_handler(struct cmd_context *ctx, nl_func_t nlfunc, + bool no_fallback); int nl_gset(struct cmd_context *ctx); int nl_sset(struct cmd_context *ctx); int nl_permaddr(struct cmd_context *ctx); +int nl_gfeatures(struct cmd_context *ctx); +int nl_sfeatures(struct cmd_context *ctx); +int nl_gprivflags(struct cmd_context *ctx); +int nl_sprivflags(struct cmd_context *ctx); +int nl_gring(struct cmd_context *ctx); +int nl_sring(struct cmd_context *ctx); +int nl_gchannels(struct cmd_context *ctx); +int nl_schannels(struct cmd_context *ctx); +int nl_gcoalesce(struct cmd_context *ctx); +int nl_scoalesce(struct cmd_context *ctx); +int nl_gpause(struct cmd_context *ctx); +int nl_spause(struct cmd_context *ctx); +int nl_geee(struct cmd_context *ctx); +int nl_seee(struct cmd_context *ctx); +int nl_tsinfo(struct cmd_context *ctx); +int nl_cable_test(struct cmd_context *ctx); +int nl_cable_test_tdr(struct cmd_context *ctx); +int nl_gtunnels(struct cmd_context *ctx); int nl_monitor(struct cmd_context *ctx); void nl_monitor_usage(void); #else /* ETHTOOL_ENABLE_NETLINK */ -static inline int netlink_init(struct cmd_context *ctx maybe_unused) +static inline void netlink_run_handler(struct cmd_context *ctx __maybe_unused, + nl_func_t nlfunc __maybe_unused, + bool no_fallback) { - return -EOPNOTSUPP; + if (no_fallback) { + fprintf(stderr, + "Command requires kernel netlink support which is not " + "enabled in this ethtool binary\n"); + exit(1); + } } -static inline void netlink_done(struct cmd_context *ctx maybe_unused) +static inline int nl_monitor(struct cmd_context *ctx __maybe_unused) { + fprintf(stderr, "Netlink not supported by ethtool, option --monitor unsupported.\n"); + return -EOPNOTSUPP; } static inline void nl_monitor_usage(void) @@ -40,6 +69,24 @@ static inline void nl_monitor_usage(void) #define nl_gset NULL #define nl_sset NULL #define nl_permaddr NULL +#define nl_gfeatures NULL +#define nl_sfeatures NULL +#define nl_gprivflags NULL +#define nl_sprivflags NULL +#define nl_gring NULL +#define nl_sring NULL +#define nl_gchannels NULL +#define nl_schannels NULL +#define nl_gcoalesce NULL +#define nl_scoalesce NULL +#define nl_gpause NULL +#define nl_spause NULL +#define nl_geee NULL +#define nl_seee NULL +#define nl_tsinfo NULL +#define nl_cable_test NULL +#define nl_cable_test_tdr NULL +#define nl_gtunnels NULL #endif /* ETHTOOL_ENABLE_NETLINK */ diff --git a/netlink/features.c b/netlink/features.c new file mode 100644 index 0000000..2a0899e --- /dev/null +++ b/netlink/features.c @@ -0,0 +1,532 @@ +/* + * features.c - netlink implementation of netdev features commands + * + * Implementation of "ethtool -k <dev>". + */ + +#include <errno.h> +#include <string.h> +#include <stdio.h> + +#include "../internal.h" +#include "../common.h" +#include "netlink.h" +#include "strset.h" +#include "bitset.h" + +/* FEATURES_GET */ + +struct feature_results { + uint32_t *hw; + uint32_t *wanted; + uint32_t *active; + uint32_t *nochange; + unsigned int count; + unsigned int words; +}; + +static int prepare_feature_results(const struct nlattr *const *tb, + struct feature_results *dest) +{ + unsigned int count; + int ret; + + memset(dest, '\0', sizeof(*dest)); + if (!tb[ETHTOOL_A_FEATURES_HW] || !tb[ETHTOOL_A_FEATURES_WANTED] || + !tb[ETHTOOL_A_FEATURES_ACTIVE] || !tb[ETHTOOL_A_FEATURES_NOCHANGE]) + return -EFAULT; + count = bitset_get_count(tb[ETHTOOL_A_FEATURES_HW], &ret); + if (ret < 0) + return -EFAULT; + if ((bitset_get_count(tb[ETHTOOL_A_FEATURES_WANTED], &ret) != count) || + (bitset_get_count(tb[ETHTOOL_A_FEATURES_ACTIVE], &ret) != count) || + (bitset_get_count(tb[ETHTOOL_A_FEATURES_NOCHANGE], &ret) != count)) + return -EFAULT; + dest->hw = get_compact_bitset_value(tb[ETHTOOL_A_FEATURES_HW]); + dest->wanted = get_compact_bitset_value(tb[ETHTOOL_A_FEATURES_WANTED]); + dest->active = get_compact_bitset_value(tb[ETHTOOL_A_FEATURES_ACTIVE]); + dest->nochange = + get_compact_bitset_value(tb[ETHTOOL_A_FEATURES_NOCHANGE]); + if (!dest->hw || !dest->wanted || !dest->active || !dest->nochange) + return -EFAULT; + dest->count = count; + dest->words = (count + 31) / 32; + + return 0; +} + +static bool feature_on(const uint32_t *bitmap, unsigned int idx) +{ + return bitmap[idx / 32] & (1 << (idx % 32)); +} + +static void dump_feature(const struct feature_results *results, + const uint32_t *ref, const uint32_t *ref_mask, + unsigned int idx, const char *name, const char *prefix) +{ + const char *suffix = ""; + + if (!name || !*name) + return; + if (ref) { + if (ref_mask && !feature_on(ref_mask, idx)) + return; + if ((!ref_mask || feature_on(ref_mask, idx)) && + (feature_on(results->active, idx) == feature_on(ref, idx))) + return; + } + + if (!feature_on(results->hw, idx) || feature_on(results->nochange, idx)) + suffix = " [fixed]"; + else if (feature_on(results->active, idx) != + feature_on(results->wanted, idx)) + suffix = feature_on(results->wanted, idx) ? + " [requested on]" : " [requested off]"; + printf("%s%s: %s%s\n", prefix, name, + feature_on(results->active, idx) ? "on" : "off", suffix); +} + +/* this assumes pattern contains no more than one asterisk */ +static bool flag_pattern_match(const char *name, const char *pattern) +{ + const char *p_ast = strchr(pattern, '*'); + + if (p_ast) { + size_t name_len = strlen(name); + size_t pattern_len = strlen(pattern); + + if (name_len + 1 < pattern_len) + return false; + if (strncmp(name, pattern, p_ast - pattern)) + return false; + pattern_len -= (p_ast - pattern) + 1; + name += name_len - pattern_len; + pattern = p_ast + 1; + } + return !strcmp(name, pattern); +} + +int dump_features(const struct nlattr *const *tb, + const struct stringset *feature_names) +{ + unsigned int *feature_flags = NULL; + struct feature_results results; + unsigned int i, j; + int ret; + + ret = prepare_feature_results(tb, &results); + if (ret < 0) + return -EFAULT; + feature_flags = calloc(results.count, sizeof(feature_flags[0])); + if (!feature_flags) + return -ENOMEM; + + /* map netdev features to legacy flags */ + for (i = 0; i < results.count; i++) { + const char *name = get_string(feature_names, i); + feature_flags[i] = UINT_MAX; + + if (!name || !*name) + continue; + for (j = 0; j < OFF_FLAG_DEF_SIZE; j++) { + const char *flag_name = off_flag_def[j].kernel_name; + + if (flag_pattern_match(name, flag_name)) { + feature_flags[i] = j; + break; + } + } + } + /* show legacy flags and their matching features first */ + for (i = 0; i < OFF_FLAG_DEF_SIZE; i++) { + unsigned int n_match = 0; + bool flag_value = false; + + /* no kernel with netlink interface supports UFO */ + if (off_flag_def[i].value == ETH_FLAG_UFO) + continue; + + for (j = 0; j < results.count; j++) { + if (feature_flags[j] == i) { + n_match++; + flag_value = flag_value || + feature_on(results.active, j); + } + } + if (n_match != 1) + printf("%s: %s\n", off_flag_def[i].long_name, + flag_value ? "on" : "off"); + if (n_match == 0) + continue; + for (j = 0; j < results.count; j++) { + const char *name = get_string(feature_names, j); + + if (feature_flags[j] != i) + continue; + if (n_match == 1) + dump_feature(&results, NULL, NULL, j, + off_flag_def[i].long_name, ""); + else + dump_feature(&results, NULL, NULL, j, name, + "\t"); + } + } + /* and, finally, remaining netdev_features not matching legacy flags */ + for (i = 0; i < results.count; i++) { + const char *name = get_string(feature_names, i); + + if (!name || !*name || feature_flags[i] != UINT_MAX) + continue; + dump_feature(&results, NULL, NULL, i, name, ""); + } + + free(feature_flags); + return 0; +} + +int features_reply_cb(const struct nlmsghdr *nlhdr, void *data) +{ + const struct nlattr *tb[ETHTOOL_A_FEATURES_MAX + 1] = {}; + DECLARE_ATTR_TB_INFO(tb); + const struct stringset *feature_names; + struct nl_context *nlctx = data; + bool silent; + int ret; + + silent = nlctx->is_dump || nlctx->is_monitor; + if (!nlctx->is_monitor) { + ret = netlink_init_ethnl2_socket(nlctx); + if (ret < 0) + return MNL_CB_ERROR; + } + feature_names = global_stringset(ETH_SS_FEATURES, nlctx->ethnl2_socket); + + ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info); + if (ret < 0) + return silent ? MNL_CB_OK : MNL_CB_ERROR; + nlctx->devname = get_dev_name(tb[ETHTOOL_A_FEATURES_HEADER]); + if (!dev_ok(nlctx)) + return MNL_CB_OK; + + if (silent) + putchar('\n'); + printf("Features for %s:\n", nlctx->devname); + ret = dump_features(tb, feature_names); + return (silent || !ret) ? MNL_CB_OK : MNL_CB_ERROR; +} + +int nl_gfeatures(struct cmd_context *ctx) +{ + struct nl_context *nlctx = ctx->nlctx; + struct nl_socket *nlsk = nlctx->ethnl_socket; + int ret; + + if (netlink_cmd_check(ctx, ETHTOOL_MSG_FEATURES_GET, true)) + return -EOPNOTSUPP; + if (ctx->argc > 0) { + fprintf(stderr, "ethtool: unexpected parameter '%s'\n", + *ctx->argp); + return 1; + } + + ret = nlsock_prep_get_request(nlsk, ETHTOOL_MSG_FEATURES_GET, + ETHTOOL_A_FEATURES_HEADER, + ETHTOOL_FLAG_COMPACT_BITSETS); + if (ret < 0) + return ret; + return nlsock_send_get_request(nlsk, features_reply_cb); +} + +/* FEATURES_SET */ + +struct sfeatures_context { + bool nothing_changed; + uint32_t req_mask[0]; +}; + +static int find_feature(const char *name, + const struct stringset *feature_names) +{ + const unsigned int count = get_count(feature_names); + unsigned int i; + + for (i = 0; i < count; i++) + if (!strcmp(name, get_string(feature_names, i))) + return i; + + return -1; +} + +static int fill_feature(struct nl_msg_buff *msgbuff, const char *name, bool val) +{ + struct nlattr *bit_attr; + + bit_attr = ethnla_nest_start(msgbuff, ETHTOOL_A_BITSET_BITS_BIT); + if (!bit_attr) + return -EMSGSIZE; + if (ethnla_put_strz(msgbuff, ETHTOOL_A_BITSET_BIT_NAME, name)) + return -EMSGSIZE; + if (ethnla_put_flag(msgbuff, ETHTOOL_A_BITSET_BIT_VALUE, val)) + return -EMSGSIZE; + mnl_attr_nest_end(msgbuff->nlhdr, bit_attr); + + return 0; +} + +static void set_sf_req_mask(struct nl_context *nlctx, unsigned int idx) +{ + struct sfeatures_context *sfctx = nlctx->cmd_private; + + sfctx->req_mask[idx / 32] |= (1 << (idx % 32)); +} + +static int fill_legacy_flag(struct nl_context *nlctx, const char *flag_name, + const struct stringset *feature_names, bool val) +{ + struct nl_msg_buff *msgbuff = &nlctx->ethnl_socket->msgbuff; + const unsigned int count = get_count(feature_names); + unsigned int i, j; + int ret; + + for (i = 0; i < OFF_FLAG_DEF_SIZE; i++) { + const char *pattern; + + if (strcmp(flag_name, off_flag_def[i].short_name) && + strcmp(flag_name, off_flag_def[i].long_name)) + continue; + pattern = off_flag_def[i].kernel_name; + + for (j = 0; j < count; j++) { + const char *name = get_string(feature_names, j); + + if (flag_pattern_match(name, pattern)) { + ret = fill_feature(msgbuff, name, val); + if (ret < 0) + return ret; + set_sf_req_mask(nlctx, j); + } + } + + return 0; + } + + return 1; +} + +int fill_sfeatures_bitmap(struct nl_context *nlctx, + const struct stringset *feature_names) +{ + struct nl_msg_buff *msgbuff = &nlctx->ethnl_socket->msgbuff; + struct nlattr *bitset_attr; + struct nlattr *bits_attr; + int ret; + + ret = -EMSGSIZE; + bitset_attr = ethnla_nest_start(msgbuff, ETHTOOL_A_FEATURES_WANTED); + if (!bitset_attr) + return ret; + bits_attr = ethnla_nest_start(msgbuff, ETHTOOL_A_BITSET_BITS); + if (!bits_attr) + goto err; + + while (nlctx->argc > 0) { + bool val; + + if (!strcmp(*nlctx->argp, "--")) { + nlctx->argp++; + nlctx->argc--; + break; + } + ret = -EINVAL; + if (nlctx->argc < 2 || + (strcmp(nlctx->argp[1], "on") && + strcmp(nlctx->argp[1], "off"))) { + fprintf(stderr, + "ethtool (%s): flag '%s' for parameter '%s' is" + " not followed by 'on' or 'off'\n", + nlctx->cmd, nlctx->argp[1], nlctx->param); + goto err; + } + + val = !strcmp(nlctx->argp[1], "on"); + ret = fill_legacy_flag(nlctx, nlctx->argp[0], feature_names, + val); + if (ret > 0) { + ret = fill_feature(msgbuff, nlctx->argp[0], val); + if (ret == 0) { + int idx = find_feature(nlctx->argp[0], + feature_names); + + if (idx >= 0) + set_sf_req_mask(nlctx, idx); + } + } + if (ret < 0) + goto err; + + nlctx->argp += 2; + nlctx->argc -= 2; + } + + ethnla_nest_end(msgbuff, bits_attr); + ethnla_nest_end(msgbuff, bitset_attr); + return 0; +err: + ethnla_nest_cancel(msgbuff, bitset_attr); + return ret; +} + +static void show_feature_changes(struct nl_context *nlctx, + const struct nlattr *const *tb) +{ + struct sfeatures_context *sfctx = nlctx->cmd_private; + const struct stringset *feature_names; + const uint32_t *wanted_mask; + const uint32_t *active_mask; + const uint32_t *wanted_val; + const uint32_t *active_val; + unsigned int count, words; + unsigned int i; + bool diff; + int ret; + + feature_names = global_stringset(ETH_SS_FEATURES, nlctx->ethnl_socket); + count = get_count(feature_names); + words = DIV_ROUND_UP(count, 32); + + if (!tb[ETHTOOL_A_FEATURES_WANTED] || !tb[ETHTOOL_A_FEATURES_ACTIVE]) + goto err; + if (bitset_get_count(tb[ETHTOOL_A_FEATURES_WANTED], &ret) != count || + ret < 0) + goto err; + if (bitset_get_count(tb[ETHTOOL_A_FEATURES_ACTIVE], &ret) != count || + ret < 0) + goto err; + wanted_val = get_compact_bitset_value(tb[ETHTOOL_A_FEATURES_WANTED]); + wanted_mask = get_compact_bitset_mask(tb[ETHTOOL_A_FEATURES_WANTED]); + active_val = get_compact_bitset_value(tb[ETHTOOL_A_FEATURES_ACTIVE]); + active_mask = get_compact_bitset_mask(tb[ETHTOOL_A_FEATURES_ACTIVE]); + if (!wanted_val || !wanted_mask || !active_val || !active_mask) + goto err; + + sfctx->nothing_changed = true; + diff = false; + for (i = 0; i < words; i++) { + if (wanted_mask[i] != sfctx->req_mask[i]) + sfctx->nothing_changed = false; + if (wanted_mask[i] || (active_mask[i] & ~sfctx->req_mask[i])) + diff = true; + } + if (!diff) + return; + + /* result is not exactly as requested, show differences */ + printf("Actual changes:\n"); + for (i = 0; i < count; i++) { + const char *name = get_string(feature_names, i); + + if (!name) + continue; + if (!feature_on(wanted_mask, i) && !feature_on(active_mask, i)) + continue; + printf("%s: ", name); + if (feature_on(wanted_mask, i)) + /* we requested a value but result is different */ + printf("%s [requested %s]", + feature_on(wanted_val, i) ? "off" : "on", + feature_on(wanted_val, i) ? "on" : "off"); + else if (!feature_on(sfctx->req_mask, i)) + /* not requested but changed anyway */ + printf("%s [not requested]", + feature_on(active_val, i) ? "on" : "off"); + else + printf("%s", feature_on(active_val, i) ? "on" : "off"); + fputc('\n', stdout); + } + + return; +err: + fprintf(stderr, "malformed diff info from kernel\n"); +} + +int sfeatures_reply_cb(const struct nlmsghdr *nlhdr, void *data) +{ + const struct genlmsghdr *ghdr = (const struct genlmsghdr *)(nlhdr + 1); + const struct nlattr *tb[ETHTOOL_A_FEATURES_MAX + 1] = {}; + DECLARE_ATTR_TB_INFO(tb); + struct nl_context *nlctx = data; + const char *devname; + int ret; + + if (ghdr->cmd != ETHTOOL_MSG_FEATURES_SET_REPLY) { + fprintf(stderr, "warning: unexpected reply message type %u\n", + ghdr->cmd); + return MNL_CB_OK; + } + ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info); + if (ret < 0) + return ret; + devname = get_dev_name(tb[ETHTOOL_A_FEATURES_HEADER]); + if (strcmp(devname, nlctx->devname)) { + fprintf(stderr, "warning: unexpected message for device %s\n", + devname); + return MNL_CB_OK; + } + + show_feature_changes(nlctx, tb); + return MNL_CB_OK; +} + +int nl_sfeatures(struct cmd_context *ctx) +{ + const struct stringset *feature_names; + struct nl_context *nlctx = ctx->nlctx; + struct sfeatures_context *sfctx; + struct nl_msg_buff *msgbuff; + struct nl_socket *nlsk; + unsigned int words; + int ret; + + if (netlink_cmd_check(ctx, ETHTOOL_MSG_FEATURES_SET, false)) + return -EOPNOTSUPP; + + nlctx->cmd = "-K"; + nlctx->argp = ctx->argp; + nlctx->argc = ctx->argc; + nlctx->cmd_private = &sfctx; + nlsk = nlctx->ethnl_socket; + msgbuff = &nlsk->msgbuff; + + feature_names = global_stringset(ETH_SS_FEATURES, nlctx->ethnl_socket); + words = (get_count(feature_names) + 31) / 32; + sfctx = malloc(sizeof(*sfctx) + words * sizeof(sfctx->req_mask[0])); + if (!sfctx) + return -ENOMEM; + memset(sfctx, '\0', + sizeof(*sfctx) + words * sizeof(sfctx->req_mask[0])); + nlctx->cmd_private = sfctx; + + nlctx->devname = ctx->devname; + ret = msg_init(nlctx, msgbuff, ETHTOOL_MSG_FEATURES_SET, + NLM_F_REQUEST | NLM_F_ACK); + if (ret < 0) + return 2; + if (ethnla_fill_header(msgbuff, ETHTOOL_A_FEATURES_HEADER, ctx->devname, + ETHTOOL_FLAG_COMPACT_BITSETS)) + return -EMSGSIZE; + ret = fill_sfeatures_bitmap(nlctx, feature_names); + if (ret < 0) + return ret; + + ret = nlsock_sendmsg(nlsk, NULL); + if (ret < 0) + return 92; + ret = nlsock_process_reply(nlsk, sfeatures_reply_cb, nlctx); + if (sfctx->nothing_changed) { + fprintf(stderr, "Could not change any device features\n"); + return nlctx->exit_code ?: 1; + } + if (ret == 0) + return 0; + return nlctx->exit_code ?: 92; +} diff --git a/netlink/monitor.c b/netlink/monitor.c index 5fce6b6..19f991f 100644 --- a/netlink/monitor.c +++ b/netlink/monitor.c @@ -31,6 +31,42 @@ static struct { .cmd = ETHTOOL_MSG_DEBUG_NTF, .cb = debug_reply_cb, }, + { + .cmd = ETHTOOL_MSG_FEATURES_NTF, + .cb = features_reply_cb, + }, + { + .cmd = ETHTOOL_MSG_PRIVFLAGS_NTF, + .cb = privflags_reply_cb, + }, + { + .cmd = ETHTOOL_MSG_RINGS_NTF, + .cb = rings_reply_cb, + }, + { + .cmd = ETHTOOL_MSG_CHANNELS_NTF, + .cb = channels_reply_cb, + }, + { + .cmd = ETHTOOL_MSG_COALESCE_NTF, + .cb = coalesce_reply_cb, + }, + { + .cmd = ETHTOOL_MSG_PAUSE_NTF, + .cb = pause_reply_cb, + }, + { + .cmd = ETHTOOL_MSG_EEE_NTF, + .cb = eee_reply_cb, + }, + { + .cmd = ETHTOOL_MSG_CABLE_TEST_NTF, + .cb = cable_test_ntf_cb, + }, + { + .cmd = ETHTOOL_MSG_CABLE_TEST_TDR_NTF, + .cb = cable_test_tdr_ntf_cb, + }, }; static void clear_filter(struct nl_context *nlctx) @@ -102,6 +138,42 @@ static struct monitor_option monitor_opts[] = { .pattern = "-s|--change", .cmd = ETHTOOL_MSG_DEBUG_NTF, }, + { + .pattern = "-k|--show-features|--show-offload|-K|--features|--offload", + .cmd = ETHTOOL_MSG_FEATURES_NTF, + }, + { + .pattern = "--show-priv-flags|--set-priv-flags", + .cmd = ETHTOOL_MSG_PRIVFLAGS_NTF, + }, + { + .pattern = "-g|--show-ring|-G|--set-ring", + .cmd = ETHTOOL_MSG_RINGS_NTF, + }, + { + .pattern = "-l|--show-channels|-L|--set-channels", + .cmd = ETHTOOL_MSG_CHANNELS_NTF, + }, + { + .pattern = "-c|--show-coalesce|-C|--coalesce", + .cmd = ETHTOOL_MSG_COALESCE_NTF, + }, + { + .pattern = "-a|--show-pause|-A|--pause", + .cmd = ETHTOOL_MSG_PAUSE_NTF, + }, + { + .pattern = "--show-eee|--set-eee", + .cmd = ETHTOOL_MSG_EEE_NTF, + }, + { + .pattern = "--cable-test", + .cmd = ETHTOOL_MSG_CABLE_TEST_NTF, + }, + { + .pattern = "--cable-test-tdr", + .cmd = ETHTOOL_MSG_CABLE_TEST_TDR_NTF, + }, }; static bool pattern_match(const char *s, const char *pattern) @@ -167,16 +239,25 @@ static int parse_monitor(struct cmd_context *ctx) int nl_monitor(struct cmd_context *ctx) { - struct nl_context *nlctx = ctx->nlctx; - struct nl_socket *nlsk = nlctx->ethnl_socket; - uint32_t grpid = nlctx->ethnl_mongrp; + struct nl_context *nlctx; + struct nl_socket *nlsk; + uint32_t grpid; bool is_dev; int ret; + ret = netlink_init(ctx); + if (ret < 0) { + fprintf(stderr, "Netlink interface initialization failed, option --monitor not supported.\n"); + return ret; + } + nlctx = ctx->nlctx; + nlsk = nlctx->ethnl_socket; + grpid = nlctx->ethnl_mongrp; if (!grpid) { fprintf(stderr, "multicast group 'monitor' not found\n"); return -EOPNOTSUPP; } + if (parse_monitor(ctx) < 0) return 1; is_dev = ctx->devname && strcmp(ctx->devname, WILDCARD_DEVNAME); diff --git a/netlink/msgbuff.c b/netlink/msgbuff.c index 7406570..216f5b9 100644 --- a/netlink/msgbuff.c +++ b/netlink/msgbuff.c @@ -79,6 +79,7 @@ int msgbuff_append(struct nl_msg_buff *dest, struct nl_msg_buff *src) unsigned int dest_len = MNL_ALIGN(msgbuff_len(dest)); int ret; + src_len -= GENL_HDRLEN; ret = msgbuff_realloc(dest, dest_len + src_len); if (ret < 0) return ret; diff --git a/netlink/msgbuff.h b/netlink/msgbuff.h index 24b99c5..7d6731f 100644 --- a/netlink/msgbuff.h +++ b/netlink/msgbuff.h @@ -81,6 +81,12 @@ static inline bool ethnla_put_u32(struct nl_msg_buff *msgbuff, uint16_t type, return ethnla_put(msgbuff, type, sizeof(uint32_t), &data); } +static inline bool ethnla_put_u16(struct nl_msg_buff *msgbuff, uint16_t type, + uint16_t data) +{ + return ethnla_put(msgbuff, type, sizeof(uint16_t), &data); +} + static inline bool ethnla_put_u8(struct nl_msg_buff *msgbuff, uint16_t type, uint8_t data) { diff --git a/netlink/netlink.c b/netlink/netlink.c index 39f4063..ffe0633 100644 --- a/netlink/netlink.c +++ b/netlink/netlink.c @@ -16,7 +16,7 @@ /* Used as reply callback for requests where no reply is expected (e.g. most * "set" type commands) */ -int nomsg_reply_cb(const struct nlmsghdr *nlhdr, void *data) +int nomsg_reply_cb(const struct nlmsghdr *nlhdr, void *data __maybe_unused) { const struct genlmsghdr *ghdr = (const struct genlmsghdr *)(nlhdr + 1); @@ -33,9 +33,9 @@ int nomsg_reply_cb(const struct nlmsghdr *nlhdr, void *data) int attr_cb(const struct nlattr *attr, void *data) { const struct attr_tb_info *tb_info = data; - int type = mnl_attr_get_type(attr); + uint16_t type = mnl_attr_get_type(attr); - if (type >= 0 && type <= tb_info->max_type) + if (type <= tb_info->max_type) tb_info->tb[type] = attr; return MNL_CB_OK; @@ -92,16 +92,243 @@ int get_dev_info(const struct nlattr *nest, int *ifindex, char *ifname) return 0; } -/* initialization */ +/** + * netlink_cmd_check() - check support for netlink command + * @ctx: ethtool command context + * @cmd: netlink command id + * @devname: device name from user + * @allow_wildcard: wildcard dumps supported + * + * Check if command @cmd is known to be unsupported based on ops information + * from genetlink family id request. Set nlctx->ioctl_fallback if ethtool + * should fall back to ioctl, i.e. when we do not know in advance that + * netlink request is supported. Set nlctx->wildcard_unsupported if "*" was + * used as device name but the request does not support wildcards (on either + * side). + * + * Return: true if we know the netlink request is not supported and should + * fail (and possibly fall back) without actually sending it to kernel. + */ +bool netlink_cmd_check(struct cmd_context *ctx, unsigned int cmd, + bool allow_wildcard) +{ + bool is_dump = !strcmp(ctx->devname, WILDCARD_DEVNAME); + uint32_t cap = is_dump ? GENL_CMD_CAP_DUMP : GENL_CMD_CAP_DO; + struct nl_context *nlctx = ctx->nlctx; -struct fam_info { - const char *fam_name; - const char *grp_name; - uint16_t fam_id; - uint32_t grp_id; + if (is_dump && !allow_wildcard) { + nlctx->wildcard_unsupported = true; + return true; + } + if (!nlctx->ops_info) { + nlctx->ioctl_fallback = true; + return false; + } + if (cmd > ETHTOOL_MSG_USER_MAX || !nlctx->ops_info[cmd].op_flags) { + nlctx->ioctl_fallback = true; + return true; + } + + if (is_dump && !(nlctx->ops_info[cmd].op_flags & GENL_CMD_CAP_DUMP)) + nlctx->wildcard_unsupported = true; + + return !(nlctx->ops_info[cmd].op_flags & cap); +} + +struct ethtool_op_policy_query_ctx { + struct nl_context *nlctx; + unsigned int op; + unsigned int op_hdr_attr; + + bool op_policy_found; + bool hdr_policy_found; + unsigned int op_policy_idx; + unsigned int hdr_policy_idx; + uint64_t flag_mask; }; -static void find_mc_group(struct nlattr *nest, struct fam_info *info) +static int family_policy_find_op(struct ethtool_op_policy_query_ctx *policy_ctx, + const struct nlattr *op_policy) +{ + const struct nlattr *attr; + unsigned int type; + int ret; + + type = policy_ctx->nlctx->is_dump ? + CTRL_ATTR_POLICY_DUMP : CTRL_ATTR_POLICY_DO; + + mnl_attr_for_each_nested(attr, op_policy) { + const struct nlattr *tb[CTRL_ATTR_POLICY_DUMP_MAX + 1] = {}; + DECLARE_ATTR_TB_INFO(tb); + + if (mnl_attr_get_type(attr) != policy_ctx->op) + continue; + + ret = mnl_attr_parse_nested(attr, attr_cb, &tb_info); + if (ret < 0) + return ret; + + if (!tb[type]) + continue; + + policy_ctx->op_policy_found = true; + policy_ctx->op_policy_idx = mnl_attr_get_u32(tb[type]); + break; + } + + return 0; +} + +static int family_policy_cb(const struct nlmsghdr *nlhdr, void *data) +{ + const struct nlattr *tba[NL_POLICY_TYPE_ATTR_MAX + 1] = {}; + DECLARE_ATTR_TB_INFO(tba); + const struct nlattr *tb[CTRL_ATTR_MAX + 1] = {}; + DECLARE_ATTR_TB_INFO(tb); + struct ethtool_op_policy_query_ctx *policy_ctx = data; + const struct nlattr *policy_attr, *attr_attr, *attr; + unsigned int attr_idx, policy_idx; + int ret; + + ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info); + if (ret < 0) + return MNL_CB_ERROR; + + if (!policy_ctx->op_policy_found) { + if (!tb[CTRL_ATTR_OP_POLICY]) { + fprintf(stderr, "Error: op policy map not present\n"); + return MNL_CB_ERROR; + } + ret = family_policy_find_op(policy_ctx, tb[CTRL_ATTR_OP_POLICY]); + return ret < 0 ? MNL_CB_ERROR : MNL_CB_OK; + } + + if (!tb[CTRL_ATTR_POLICY]) + return MNL_CB_OK; + + policy_attr = mnl_attr_get_payload(tb[CTRL_ATTR_POLICY]); + policy_idx = mnl_attr_get_type(policy_attr); + attr_attr = mnl_attr_get_payload(policy_attr); + attr_idx = mnl_attr_get_type(attr_attr); + + ret = mnl_attr_parse_nested(attr_attr, attr_cb, &tba_info); + if (ret < 0) + return MNL_CB_ERROR; + + if (policy_idx == policy_ctx->op_policy_idx && + attr_idx == policy_ctx->op_hdr_attr) { + attr = tba[NL_POLICY_TYPE_ATTR_POLICY_IDX]; + if (!attr) { + fprintf(stderr, "Error: no policy index in what was expected to be ethtool header attribute\n"); + return MNL_CB_ERROR; + } + policy_ctx->hdr_policy_found = true; + policy_ctx->hdr_policy_idx = mnl_attr_get_u32(attr); + } + + if (policy_ctx->hdr_policy_found && + policy_ctx->hdr_policy_idx == policy_idx && + attr_idx == ETHTOOL_A_HEADER_FLAGS) { + attr = tba[NL_POLICY_TYPE_ATTR_MASK]; + if (!attr) { + fprintf(stderr, "Error: validation mask not reported for ethtool header flags\n"); + return MNL_CB_ERROR; + } + + policy_ctx->flag_mask = mnl_attr_get_u64(attr); + } + + return MNL_CB_OK; +} + +static int read_flags_policy(struct nl_context *nlctx, struct nl_socket *nlsk, + unsigned int nlcmd, unsigned int hdrattr) +{ + struct ethtool_op_policy_query_ctx policy_ctx; + struct nl_msg_buff *msgbuff = &nlsk->msgbuff; + int ret; + + if (nlctx->ops_info[nlcmd].hdr_policy_loaded) + return 0; + + memset(&policy_ctx, 0, sizeof(policy_ctx)); + policy_ctx.nlctx = nlctx; + policy_ctx.op = nlcmd; + policy_ctx.op_hdr_attr = hdrattr; + + ret = __msg_init(msgbuff, GENL_ID_CTRL, CTRL_CMD_GETPOLICY, + NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP, 1); + if (ret < 0) + return ret; + ret = -EMSGSIZE; + if (ethnla_put_u16(msgbuff, CTRL_ATTR_FAMILY_ID, nlctx->ethnl_fam)) + return ret; + if (ethnla_put_u32(msgbuff, CTRL_ATTR_OP, nlcmd)) + return ret; + + nlsock_sendmsg(nlsk, NULL); + nlsock_process_reply(nlsk, family_policy_cb, &policy_ctx); + + nlctx->ops_info[nlcmd].hdr_policy_loaded = 1; + nlctx->ops_info[nlcmd].hdr_flags = policy_ctx.flag_mask; + return 0; +} + +u32 get_stats_flag(struct nl_context *nlctx, unsigned int nlcmd, + unsigned int hdrattr) +{ + if (!nlctx->ctx->show_stats) + return 0; + if (nlcmd > ETHTOOL_MSG_USER_MAX || + !(nlctx->ops_info[nlcmd].op_flags & GENL_CMD_CAP_HASPOL)) + return 0; + + if (read_flags_policy(nlctx, nlctx->ethnl_socket, nlcmd, hdrattr) < 0) + return 0; + + return nlctx->ops_info[nlcmd].hdr_flags & ETHTOOL_FLAG_STATS; +} + +/* initialization */ + +static int genl_read_ops(struct nl_context *nlctx, + const struct nlattr *ops_attr) +{ + struct nl_op_info *ops_info; + struct nlattr *op_attr; + int ret; + + ops_info = calloc(__ETHTOOL_MSG_USER_CNT, sizeof(ops_info[0])); + if (!ops_info) + return -ENOMEM; + + mnl_attr_for_each_nested(op_attr, ops_attr) { + const struct nlattr *tb[CTRL_ATTR_OP_MAX + 1] = {}; + DECLARE_ATTR_TB_INFO(tb); + uint32_t op_id; + + ret = mnl_attr_parse_nested(op_attr, attr_cb, &tb_info); + if (ret < 0) + goto err; + + if (!tb[CTRL_ATTR_OP_ID] || !tb[CTRL_ATTR_OP_FLAGS]) + continue; + op_id = mnl_attr_get_u32(tb[CTRL_ATTR_OP_ID]); + if (op_id >= __ETHTOOL_MSG_USER_CNT) + continue; + + ops_info[op_id].op_flags = + mnl_attr_get_u32(tb[CTRL_ATTR_OP_FLAGS]); + } + + nlctx->ops_info = ops_info; + return 0; +err: + free(ops_info); + return ret; +} + +static void find_mc_group(struct nl_context *nlctx, struct nlattr *nest) { const struct nlattr *grp_tb[CTRL_ATTR_MCAST_GRP_MAX + 1] = {}; DECLARE_ATTR_TB_INFO(grp_tb); @@ -116,9 +343,9 @@ static void find_mc_group(struct nlattr *nest, struct fam_info *info) !grp_tb[CTRL_ATTR_MCAST_GRP_ID]) continue; if (strcmp(mnl_attr_get_str(grp_tb[CTRL_ATTR_MCAST_GRP_NAME]), - info->grp_name)) + ETHTOOL_MCGRP_MONITOR_NAME)) continue; - info->grp_id = + nlctx->ethnl_mongrp = mnl_attr_get_u32(grp_tb[CTRL_ATTR_MCAST_GRP_ID]); return; } @@ -126,16 +353,22 @@ static void find_mc_group(struct nlattr *nest, struct fam_info *info) static int family_info_cb(const struct nlmsghdr *nlhdr, void *data) { - struct fam_info *info = data; + struct nl_context *nlctx = data; struct nlattr *attr; + int ret; mnl_attr_for_each(attr, nlhdr, GENL_HDRLEN) { switch (mnl_attr_get_type(attr)) { case CTRL_ATTR_FAMILY_ID: - info->fam_id = mnl_attr_get_u16(attr); + nlctx->ethnl_fam = mnl_attr_get_u16(attr); + break; + case CTRL_ATTR_OPS: + ret = genl_read_ops(nlctx, attr); + if (ret < 0) + return MNL_CB_ERROR; break; case CTRL_ATTR_MCAST_GROUPS: - find_mc_group(attr, info); + find_mc_group(nlctx, attr); break; } } @@ -144,41 +377,37 @@ static int family_info_cb(const struct nlmsghdr *nlhdr, void *data) } #ifdef TEST_ETHTOOL -static int get_genl_family(struct nl_socket *nlsk, struct fam_info *info) +static int get_genl_family(struct nl_context *nlctx, struct nl_socket *nlsk) { return 0; } #else -static int get_genl_family(struct nl_socket *nlsk, struct fam_info *info) +static int get_genl_family(struct nl_context *nlctx, struct nl_socket *nlsk) { struct nl_msg_buff *msgbuff = &nlsk->msgbuff; int ret; - nlsk->nlctx->suppress_nlerr = 2; + nlctx->suppress_nlerr = 2; ret = __msg_init(msgbuff, GENL_ID_CTRL, CTRL_CMD_GETFAMILY, NLM_F_REQUEST | NLM_F_ACK, 1); if (ret < 0) goto out; ret = -EMSGSIZE; - if (ethnla_put_strz(msgbuff, CTRL_ATTR_FAMILY_NAME, info->fam_name)) + if (ethnla_put_strz(msgbuff, CTRL_ATTR_FAMILY_NAME, ETHTOOL_GENL_NAME)) goto out; nlsock_sendmsg(nlsk, NULL); - nlsock_process_reply(nlsk, family_info_cb, info); - ret = info->fam_id ? 0 : -EADDRNOTAVAIL; + nlsock_process_reply(nlsk, family_info_cb, nlctx); + ret = nlctx->ethnl_fam ? 0 : -EADDRNOTAVAIL; out: - nlsk->nlctx->suppress_nlerr = 0; + nlctx->suppress_nlerr = 0; return ret; } #endif int netlink_init(struct cmd_context *ctx) { - struct fam_info info = { - .fam_name = ETHTOOL_GENL_NAME, - .grp_name = ETHTOOL_MCGRP_MONITOR_NAME, - }; struct nl_context *nlctx; int ret; @@ -189,11 +418,9 @@ int netlink_init(struct cmd_context *ctx) ret = nlsock_init(nlctx, &nlctx->ethnl_socket, NETLINK_GENERIC); if (ret < 0) goto out_free; - ret = get_genl_family(nlctx->ethnl_socket, &info); + ret = get_genl_family(nlctx, nlctx->ethnl_socket); if (ret < 0) goto out_nlsk; - nlctx->ethnl_fam = info.fam_id; - nlctx->ethnl_mongrp = info.grp_id; ctx->nlctx = nlctx; return 0; @@ -201,16 +428,93 @@ int netlink_init(struct cmd_context *ctx) out_nlsk: nlsock_done(nlctx->ethnl_socket); out_free: + free(nlctx->ops_info); free(nlctx); return ret; } -void netlink_done(struct cmd_context *ctx) +static void netlink_done(struct cmd_context *ctx) { - if (!ctx->nlctx) + struct nl_context *nlctx = ctx->nlctx; + + if (!nlctx) return; - free(ctx->nlctx); + nlsock_done(nlctx->ethnl_socket); + nlsock_done(nlctx->ethnl2_socket); + nlsock_done(nlctx->rtnl_socket); + free(nlctx->ops_info); + free(nlctx); ctx->nlctx = NULL; cleanup_all_strings(); } + +/** + * netlink_run_handler() - run netlink handler for subcommand + * @ctx: command context + * @nlfunc: subcommand netlink handler to call + * @no_fallback: there is no ioctl fallback handler + * + * This function returns only if ioctl() handler should be run as fallback. + * Otherwise it exits with appropriate return code. + */ +void netlink_run_handler(struct cmd_context *ctx, nl_func_t nlfunc, + bool no_fallback) +{ + bool wildcard = ctx->devname && !strcmp(ctx->devname, WILDCARD_DEVNAME); + bool wildcard_unsupported, ioctl_fallback; + struct nl_context *nlctx; + const char *reason; + int ret; + + if (ctx->devname && strlen(ctx->devname) >= ALTIFNAMSIZ) { + fprintf(stderr, "device name '%s' longer than %u characters\n", + ctx->devname, ALTIFNAMSIZ - 1); + exit(1); + } + + if (!nlfunc) { + reason = "ethtool netlink support for subcommand missing"; + goto no_support; + } + if (netlink_init(ctx)) { + reason = "netlink interface initialization failed"; + goto no_support; + } + nlctx = ctx->nlctx; + + ret = nlfunc(ctx); + wildcard_unsupported = nlctx->wildcard_unsupported; + ioctl_fallback = nlctx->ioctl_fallback; + netlink_done(ctx); + + if (no_fallback || ret != -EOPNOTSUPP || !ioctl_fallback) { + if (wildcard_unsupported) + fprintf(stderr, "%s\n", + "subcommand does not support wildcard dump"); + exit(ret >= 0 ? ret : 1); + } + if (wildcard_unsupported) + reason = "subcommand does not support wildcard dump"; + else + reason = "kernel netlink support for subcommand missing"; + +no_support: + if (no_fallback) { + fprintf(stderr, "%s, subcommand not supported by ioctl\n", + reason); + exit(1); + } + if (wildcard) { + fprintf(stderr, "%s, wildcard dump not supported\n", reason); + exit(1); + } + if (ctx->devname && strlen(ctx->devname) >= IFNAMSIZ) { + fprintf(stderr, + "%s, device name longer than %u not supported\n", + reason, IFNAMSIZ - 1); + exit(1); + } + + /* fallback to ioctl() */ +} diff --git a/netlink/netlink.h b/netlink/netlink.h index db078d2..c025585 100644 --- a/netlink/netlink.h +++ b/netlink/netlink.h @@ -16,6 +16,21 @@ #define WILDCARD_DEVNAME "*" #define CMDMASK_WORDS DIV_ROUND_UP(__ETHTOOL_MSG_KERNEL_CNT, 32) +enum link_mode_class { + LM_CLASS_UNKNOWN, + LM_CLASS_REAL, + LM_CLASS_AUTONEG, + LM_CLASS_PORT, + LM_CLASS_PAUSE, + LM_CLASS_FEC, +}; + +struct nl_op_info { + uint32_t op_flags; + uint32_t hdr_flags; + uint8_t hdr_policy_loaded:1; +}; + struct nl_context { struct cmd_context *ctx; void *cmd_private; @@ -25,6 +40,7 @@ struct nl_context { unsigned int suppress_nlerr; uint16_t ethnl_fam; uint32_t ethnl_mongrp; + struct nl_op_info *ops_info; struct nl_socket *ethnl_socket; struct nl_socket *ethnl2_socket; struct nl_socket *rtnl_socket; @@ -35,7 +51,9 @@ struct nl_context { const char *cmd; const char *param; char **argp; - int argc; + unsigned int argc; + bool ioctl_fallback; + bool wildcard_unsupported; }; struct attr_tb_info { @@ -49,13 +67,70 @@ struct attr_tb_info { int nomsg_reply_cb(const struct nlmsghdr *nlhdr, void *data); int attr_cb(const struct nlattr *attr, void *data); +int netlink_init(struct cmd_context *ctx); +bool netlink_cmd_check(struct cmd_context *ctx, unsigned int cmd, + bool allow_wildcard); const char *get_dev_name(const struct nlattr *nest); int get_dev_info(const struct nlattr *nest, int *ifindex, char *ifname); +u32 get_stats_flag(struct nl_context *nlctx, unsigned int nlcmd, + unsigned int hdrattr); int linkmodes_reply_cb(const struct nlmsghdr *nlhdr, void *data); int linkinfo_reply_cb(const struct nlmsghdr *nlhdr, void *data); int wol_reply_cb(const struct nlmsghdr *nlhdr, void *data); int debug_reply_cb(const struct nlmsghdr *nlhdr, void *data); +int features_reply_cb(const struct nlmsghdr *nlhdr, void *data); +int privflags_reply_cb(const struct nlmsghdr *nlhdr, void *data); +int rings_reply_cb(const struct nlmsghdr *nlhdr, void *data); +int channels_reply_cb(const struct nlmsghdr *nlhdr, void *data); +int coalesce_reply_cb(const struct nlmsghdr *nlhdr, void *data); +int pause_reply_cb(const struct nlmsghdr *nlhdr, void *data); +int eee_reply_cb(const struct nlmsghdr *nlhdr, void *data); +int cable_test_reply_cb(const struct nlmsghdr *nlhdr, void *data); +int cable_test_ntf_cb(const struct nlmsghdr *nlhdr, void *data); +int cable_test_tdr_reply_cb(const struct nlmsghdr *nlhdr, void *data); +int cable_test_tdr_ntf_cb(const struct nlmsghdr *nlhdr, void *data); + +/* dump helpers */ + +int dump_link_modes(struct nl_context *nlctx, const struct nlattr *bitset, + bool mask, unsigned int class, const char *before, + const char *between, const char *after, + const char *if_none); + +static inline void show_u32(const struct nlattr *attr, const char *label) +{ + if (attr) + printf("%s%u\n", label, mnl_attr_get_u32(attr)); + else + printf("%sn/a\n", label); +} + +static inline const char *u8_to_bool(const uint8_t *val) +{ + if (val) + return *val ? "on" : "off"; + else + return "n/a"; +} + +static inline void show_bool_val(const char *key, const char *fmt, uint8_t *val) +{ + if (is_json_context()) { + if (val) + print_bool(PRINT_JSON, key, NULL, val); + } else { + print_string(PRINT_FP, NULL, fmt, u8_to_bool(val)); + } +} + +static inline void show_bool(const char *key, const char *fmt, + const struct nlattr *attr) +{ + show_bool_val(key, fmt, attr ? mnl_attr_get_payload(attr) : NULL); +} + +/* misc */ static inline void copy_devname(char *dst, const char *src) { diff --git a/netlink/nlsock.c b/netlink/nlsock.c index 22abb68..0ec2738 100644 --- a/netlink/nlsock.c +++ b/netlink/nlsock.c @@ -168,30 +168,30 @@ static void debug_msg(struct nl_socket *nlsk, const void *msg, unsigned int len, * * Return: error code extracted from the message */ -static int nlsock_process_ack(struct nlmsghdr *nlhdr, ssize_t len, +static int nlsock_process_ack(struct nlmsghdr *nlhdr, unsigned long len, unsigned int suppress_nlerr, bool pretty) { const struct nlattr *tb[NLMSGERR_ATTR_MAX + 1] = {}; DECLARE_ATTR_TB_INFO(tb); + unsigned int err_offset = 0; unsigned int tlv_offset; struct nlmsgerr *nlerr; bool silent; - if (len < NLMSG_HDRLEN + sizeof(*nlerr)) + if ((len < NLMSG_HDRLEN + sizeof(*nlerr)) || (len < nlhdr->nlmsg_len)) return -EFAULT; nlerr = mnl_nlmsg_get_payload(nlhdr); - silent = (!(nlhdr->nlmsg_flags & NLM_F_ACK_TLVS) || - suppress_nlerr >= 2 || - (suppress_nlerr && nlerr->error == -EOPNOTSUPP)); - if (silent) - goto out; + silent = suppress_nlerr >= 2 || + (suppress_nlerr && nlerr->error == -EOPNOTSUPP); + if (silent || !(nlhdr->nlmsg_flags & NLM_F_ACK_TLVS)) + goto tlv_done; tlv_offset = sizeof(*nlerr); if (!(nlhdr->nlmsg_flags & NLM_F_CAPPED)) tlv_offset += MNL_ALIGN(mnl_nlmsg_get_payload_len(&nlerr->msg)); - if (mnl_attr_parse(nlhdr, tlv_offset, attr_cb, &tb_info) < 0) - goto out; + goto tlv_done; + if (tb[NLMSGERR_ATTR_MSG]) { const char *msg = mnl_attr_get_str(tb[NLMSGERR_ATTR_MSG]); @@ -202,24 +202,21 @@ static int nlsock_process_ack(struct nlmsghdr *nlhdr, ssize_t len, mnl_attr_get_u32(tb[NLMSGERR_ATTR_OFFS])); fputc('\n', stderr); } + if (tb[NLMSGERR_ATTR_OFFS]) + err_offset = mnl_attr_get_u32(tb[NLMSGERR_ATTR_OFFS]); - if (nlerr->error && pretty) { - unsigned int err_offset = 0; - - if (tb[NLMSGERR_ATTR_OFFS]) - err_offset = mnl_attr_get_u32(tb[NLMSGERR_ATTR_OFFS]); +tlv_done: + if (nlerr->error && !silent) { + errno = -nlerr->error; + perror("netlink error"); + } + if (pretty && !(nlhdr->nlmsg_flags & NLM_F_CAPPED) && + nlhdr->nlmsg_len >= NLMSG_HDRLEN + nlerr->msg.nlmsg_len) { fprintf(stderr, "offending message%s:\n", err_offset ? " and attribute" : ""); pretty_print_genlmsg(&nlerr->msg, ethnl_umsg_desc, ethnl_umsg_n_desc, err_offset); } - -out: - if (nlerr->error) { - errno = -nlerr->error; - if (!silent) - perror("netlink error"); - } return nlerr->error; } @@ -258,12 +255,12 @@ int nlsock_process_reply(struct nl_socket *nlsk, mnl_cb_t reply_cb, void *data) nlhdr = (struct nlmsghdr *)buff; if (nlhdr->nlmsg_type == NLMSG_ERROR) { - bool silent = nlsk->nlctx->suppress_nlerr; + unsigned int suppress = nlsk->nlctx->suppress_nlerr; bool pretty; pretty = debug_on(nlsk->nlctx->ctx->debug, DEBUG_NL_PRETTY_MSG); - return nlsock_process_ack(nlhdr, len, silent, pretty); + return nlsock_process_ack(nlhdr, len, suppress, pretty); } msgbuff->nlhdr = nlhdr; @@ -398,8 +395,11 @@ out_msgbuff: */ void nlsock_done(struct nl_socket *nlsk) { + if (!nlsk) + return; if (nlsk->sk) mnl_socket_close(nlsk->sk); msgbuff_done(&nlsk->msgbuff); memset(nlsk, '\0', sizeof(*nlsk)); + free(nlsk); } diff --git a/netlink/parser.c b/netlink/parser.c index 40eb4a5..c2eae93 100644 --- a/netlink/parser.c +++ b/netlink/parser.c @@ -54,6 +54,22 @@ static bool __prefix_0x(const char *p) return p[0] == '0' && (p[1] == 'x' || p[1] == 'X'); } +static float parse_float(const char *arg, float *result, float min, + float max) +{ + char *endptr; + float val; + + if (!arg || !arg[0]) + return -EINVAL; + val = strtof(arg, &endptr); + if (*endptr || val < min || val > max) + return -EINVAL; + + *result = val; + return 0; +} + static int __parse_u32(const char *arg, uint32_t *result, uint32_t min, uint32_t max, int base) { @@ -139,8 +155,9 @@ static int lookup_u8(const char *arg, uint8_t *result, /* Parser handler for a flag. Expects a name (with no additional argument), * generates NLA_FLAG or sets a bool (if the name was present). */ -int nl_parse_flag(struct nl_context *nlctx, uint16_t type, const void *data, - struct nl_msg_buff *msgbuff, void *dest) +int nl_parse_flag(struct nl_context *nlctx __maybe_unused, uint16_t type, + const void *data __maybe_unused, struct nl_msg_buff *msgbuff, + void *dest) { if (dest) *(bool *)dest = true; @@ -150,7 +167,8 @@ int nl_parse_flag(struct nl_context *nlctx, uint16_t type, const void *data, /* Parser handler for null terminated string. Expects a string argument, * generates NLA_NUL_STRING or fills const char * */ -int nl_parse_string(struct nl_context *nlctx, uint16_t type, const void *data, +int nl_parse_string(struct nl_context *nlctx, uint16_t type, + const void *data __maybe_unused, struct nl_msg_buff *msgbuff, void *dest) { const char *arg = *nlctx->argp; @@ -167,8 +185,8 @@ int nl_parse_string(struct nl_context *nlctx, uint16_t type, const void *data, * (may use 0x prefix), generates NLA_U32 or fills an uint32_t. */ int nl_parse_direct_u32(struct nl_context *nlctx, uint16_t type, - const void *data, struct nl_msg_buff *msgbuff, - void *dest) + const void *data __maybe_unused, + struct nl_msg_buff *msgbuff, void *dest) { const char *arg = *nlctx->argp; uint32_t val; @@ -191,8 +209,8 @@ int nl_parse_direct_u32(struct nl_context *nlctx, uint16_t type, * (may use 0x prefix), generates NLA_U32 or fills an uint32_t. */ int nl_parse_direct_u8(struct nl_context *nlctx, uint16_t type, - const void *data, struct nl_msg_buff *msgbuff, - void *dest) + const void *data __maybe_unused, + struct nl_msg_buff *msgbuff, void *dest) { const char *arg = *nlctx->argp; uint8_t val; @@ -211,10 +229,37 @@ int nl_parse_direct_u8(struct nl_context *nlctx, uint16_t type, return (type && ethnla_put_u8(msgbuff, type, val)) ? -EMSGSIZE : 0; } +/* Parser handler for float meters and convert it to cm. Generates + * NLA_U32 or fills an uint32_t. + */ +int nl_parse_direct_m2cm(struct nl_context *nlctx, uint16_t type, + const void *data __maybe_unused, + struct nl_msg_buff *msgbuff, void *dest) +{ + const char *arg = *nlctx->argp; + float meters; + uint32_t cm; + int ret; + + nlctx->argp++; + nlctx->argc--; + ret = parse_float(arg, &meters, 0, 150); + if (ret < 0) { + parser_err_invalid_value(nlctx, arg); + return ret; + } + + cm = (uint32_t)(meters * 100 + 0.5); + if (dest) + *(uint32_t *)dest = cm; + return (type && ethnla_put_u32(msgbuff, type, cm)) ? -EMSGSIZE : 0; +} + /* Parser handler for (tri-state) bool. Expects "name on|off", generates * NLA_U8 which is 1 for "on" and 0 for "off". */ -int nl_parse_u8bool(struct nl_context *nlctx, uint16_t type, const void *data, +int nl_parse_u8bool(struct nl_context *nlctx, uint16_t type, + const void *data __maybe_unused, struct nl_msg_buff *msgbuff, void *dest) { const char *arg = *nlctx->argp; @@ -421,8 +466,9 @@ err: * error_parser_params (error message, return value and number of extra * arguments to skip). */ -int nl_parse_error(struct nl_context *nlctx, uint16_t type, const void *data, - struct nl_msg_buff *msgbuff, void *dest) +int nl_parse_error(struct nl_context *nlctx, uint16_t type __maybe_unused, + const void *data, struct nl_msg_buff *msgbuff __maybe_unused, + void *dest __maybe_unused) { const struct error_parser_data *parser_data = data; unsigned int skip = parser_data->extra_args; @@ -558,7 +604,7 @@ static int parse_numeric_bitset(struct nl_context *nlctx, uint16_t type, parser_err_invalid_value(nlctx, arg); return -EINVAL; } - len1 = maskptr ? (maskptr - arg) : strlen(arg); + len1 = maskptr ? (unsigned int)(maskptr - arg) : strlen(arg); nwords = DIV_ROUND_UP(len1, 8); nbits = 0; @@ -584,8 +630,10 @@ static int parse_numeric_bitset(struct nl_context *nlctx, uint16_t type, } value = calloc(nwords, sizeof(uint32_t)); - if (!value) + if (!value) { + free(mask); return -ENOMEM; + } ret = __parse_num_string(arg, len1, value, force_hex1); if (ret < 0) { parser_err_invalid_value(nlctx, arg); @@ -872,7 +920,7 @@ static void __parser_set(uint64_t *map, unsigned int idx) } struct tmp_buff { - struct nl_msg_buff msgbuff; + struct nl_msg_buff *msgbuff; unsigned int id; unsigned int orig_len; struct tmp_buff *next; @@ -903,7 +951,12 @@ static struct tmp_buff *tmp_buff_find_or_create(struct tmp_buff **phead, if (!new_buff) return NULL; new_buff->id = id; - msgbuff_init(&new_buff->msgbuff); + new_buff->msgbuff = malloc(sizeof(*new_buff->msgbuff)); + if (!new_buff->msgbuff) { + free(new_buff); + return NULL; + } + msgbuff_init(new_buff->msgbuff); new_buff->next = NULL; *pbuff = new_buff; @@ -917,7 +970,10 @@ static void tmp_buff_destroy(struct tmp_buff *head) while (buff) { next = buff->next; - msgbuff_done(&buff->msgbuff); + if (buff->msgbuff) { + msgbuff_done(buff->msgbuff); + free(buff->msgbuff); + } free(buff); buff = next; } @@ -932,13 +988,22 @@ static void tmp_buff_destroy(struct tmp_buff *head) * param_parser::offset) * @group_style: defines if identifiers in .group represent separate messages, * nested attributes or are not allowed + * @msgbuffs: (only used for @group_style = PARSER_GROUP_MSG) array to store + * pointers to composed messages; caller must make sure this + * array is sufficient, i.e. that it has at least as many entries + * as the number of different .group values in params array; + * entries are filled from the start, remaining entries are not + * modified; caller should zero initialize the array before + * calling nl_parser() */ int nl_parser(struct nl_context *nlctx, const struct param_parser *params, - void *dest, enum parser_group_style group_style) + void *dest, enum parser_group_style group_style, + struct nl_msg_buff **msgbuffs) { struct nl_socket *nlsk = nlctx->ethnl_socket; const struct param_parser *parser; struct tmp_buff *buffs = NULL; + unsigned int n_msgbuffs = 0; struct tmp_buff *buff; unsigned int n_params; uint64_t *params_seen; @@ -956,20 +1021,20 @@ int nl_parser(struct nl_context *nlctx, const struct param_parser *params, buff = tmp_buff_find_or_create(&buffs, parser->group); if (!buff) goto out_free_buffs; - msgbuff = &buff->msgbuff; + msgbuff = buff->msgbuff; + ret = msg_init(nlctx, msgbuff, parser->group, + NLM_F_REQUEST | NLM_F_ACK); + if (ret < 0) + goto out_free_buffs; switch (group_style) { case PARSER_GROUP_NEST: ret = -EMSGSIZE; - nest = ethnla_nest_start(&buff->msgbuff, parser->group); + nest = ethnla_nest_start(buff->msgbuff, parser->group); if (!nest) goto out_free_buffs; break; case PARSER_GROUP_MSG: - ret = msg_init(nlctx, msgbuff, parser->group, - NLM_F_REQUEST | NLM_F_ACK); - if (ret < 0) - goto out_free_buffs; if (ethnla_fill_header(msgbuff, ETHTOOL_A_LINKINFO_HEADER, nlctx->devname, 0)) @@ -1014,17 +1079,24 @@ int nl_parser(struct nl_context *nlctx, const struct param_parser *params, buff = NULL; if (parser->group) buff = tmp_buff_find(buffs, parser->group); - msgbuff = buff ? &buff->msgbuff : &nlsk->msgbuff; + msgbuff = buff ? buff->msgbuff : &nlsk->msgbuff; - param_dest = dest ? (dest + parser->dest_offset) : NULL; + param_dest = dest ? ((char *)dest + parser->dest_offset) : NULL; ret = parser->handler(nlctx, parser->type, parser->handler_data, msgbuff, param_dest); if (ret < 0) goto out_free; } + if (group_style == PARSER_GROUP_MSG) { + ret = -EOPNOTSUPP; + for (buff = buffs; buff; buff = buff->next) + if (msgbuff_len(buff->msgbuff) > buff->orig_len && + netlink_cmd_check(nlctx->ctx, buff->id, false)) + goto out_free; + } for (buff = buffs; buff; buff = buff->next) { - struct nl_msg_buff *msgbuff = &buff->msgbuff; + struct nl_msg_buff *msgbuff = buff->msgbuff; if (group_style == PARSER_GROUP_NONE || msgbuff_len(msgbuff) == buff->orig_len) @@ -1037,12 +1109,8 @@ int nl_parser(struct nl_context *nlctx, const struct param_parser *params, goto out_free; break; case PARSER_GROUP_MSG: - ret = nlsock_sendmsg(nlsk, msgbuff); - if (ret < 0) - goto out_free; - ret = nlsock_process_reply(nlsk, nomsg_reply_cb, NULL); - if (ret < 0) - goto out_free; + msgbuffs[n_msgbuffs++] = msgbuff; + buff->msgbuff = NULL; break; default: break; diff --git a/netlink/parser.h b/netlink/parser.h index 3cc26d2..28f26cc 100644 --- a/netlink/parser.h +++ b/netlink/parser.h @@ -111,6 +111,10 @@ int nl_parse_direct_u32(struct nl_context *nlctx, uint16_t type, int nl_parse_direct_u8(struct nl_context *nlctx, uint16_t type, const void *data, struct nl_msg_buff *msgbuff, void *dest); +/* NLA_U32 represented as float number of meters, converted to cm. */ +int nl_parse_direct_m2cm(struct nl_context *nlctx, uint16_t type, + const void *data, struct nl_msg_buff *msgbuff, + void *dest); /* NLA_U8 represented as on | off */ int nl_parse_u8bool(struct nl_context *nlctx, uint16_t type, const void *data, struct nl_msg_buff *msgbuff, void *dest); @@ -139,6 +143,7 @@ int nl_parse_char_bitset(struct nl_context *nlctx, uint16_t type, /* main entry point called to parse the command line */ int nl_parser(struct nl_context *nlctx, const struct param_parser *params, - void *dest, enum parser_group_style group_style); + void *dest, enum parser_group_style group_style, + struct nl_msg_buff **msgbuffs); #endif /* ETHTOOL_NETLINK_PARSER_H__ */ diff --git a/netlink/pause.c b/netlink/pause.c new file mode 100644 index 0000000..867d0da --- /dev/null +++ b/netlink/pause.c @@ -0,0 +1,308 @@ +/* + * pause.c - netlink implementation of pause commands + * + * Implementation of "ethtool -a <dev>" and "ethtool -A <dev> ..." + */ + +#include <errno.h> +#include <inttypes.h> +#include <string.h> +#include <stdio.h> + +#include "../internal.h" +#include "../common.h" +#include "netlink.h" +#include "bitset.h" +#include "parser.h" + +/* PAUSE_GET */ + +struct pause_autoneg_status { + bool pause; + bool asym_pause; +}; + +static void pause_autoneg_walker(unsigned int idx, + const char *name __maybe_unused, bool val, + void *data) +{ + struct pause_autoneg_status *status = data; + + if (idx == ETHTOOL_LINK_MODE_Pause_BIT) + status->pause = val; + if (idx == ETHTOOL_LINK_MODE_Asym_Pause_BIT) + status->asym_pause = val; +} + +static int pause_autoneg_cb(const struct nlmsghdr *nlhdr, void *data) +{ + const struct nlattr *tb[ETHTOOL_A_LINKMODES_MAX + 1] = {}; + DECLARE_ATTR_TB_INFO(tb); + struct pause_autoneg_status ours = {}; + struct pause_autoneg_status peer = {}; + struct nl_context *nlctx = data; + uint8_t rx_status = false; + uint8_t tx_status = false; + bool silent; + int err_ret; + int ret; + + silent = nlctx->is_dump || nlctx->is_monitor; + err_ret = silent ? MNL_CB_OK : MNL_CB_ERROR; + ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info); + if (ret < 0) + return err_ret; + + if (!tb[ETHTOOL_A_LINKMODES_OURS] || !tb[ETHTOOL_A_LINKMODES_PEER]) + return MNL_CB_OK; + ret = walk_bitset(tb[ETHTOOL_A_LINKMODES_OURS], NULL, + pause_autoneg_walker, &ours); + if (ret < 0) + return err_ret; + ret = walk_bitset(tb[ETHTOOL_A_LINKMODES_PEER], NULL, + pause_autoneg_walker, &peer); + if (ret < 0) + return err_ret; + + if (ours.pause && peer.pause) { + rx_status = true; + tx_status = true; + } else if (ours.asym_pause && peer.asym_pause) { + if (ours.pause) + rx_status = true; + else if (peer.pause) + tx_status = true; + } + + open_json_object("negotiated"); + show_bool_val("rx", "RX negotiated: %s\n", &rx_status); + show_bool_val("tx", "TX negotiated: %s\n", &tx_status); + close_json_object(); + + return MNL_CB_OK; +} + +static int show_pause_autoneg_status(struct nl_context *nlctx) +{ + const char *saved_devname; + int ret; + + saved_devname = nlctx->ctx->devname; + nlctx->ctx->devname = nlctx->devname; + ret = netlink_init_ethnl2_socket(nlctx); + if (ret < 0) + goto out; + + ret = nlsock_prep_get_request(nlctx->ethnl2_socket, + ETHTOOL_MSG_LINKMODES_GET, + ETHTOOL_A_LINKMODES_HEADER, + ETHTOOL_FLAG_COMPACT_BITSETS); + if (ret < 0) + goto out; + ret = nlsock_send_get_request(nlctx->ethnl2_socket, pause_autoneg_cb); + +out: + nlctx->ctx->devname = saved_devname; + return ret; +} + +static int show_pause_stats(const struct nlattr *nest) +{ + const struct nlattr *tb[ETHTOOL_A_PAUSE_STAT_MAX + 1] = {}; + DECLARE_ATTR_TB_INFO(tb); + static const struct { + unsigned int attr; + char *name; + } stats[] = { + { ETHTOOL_A_PAUSE_STAT_TX_FRAMES, "tx_pause_frames" }, + { ETHTOOL_A_PAUSE_STAT_RX_FRAMES, "rx_pause_frames" }, + }; + bool header = false; + unsigned int i; + size_t n; + int ret; + + ret = mnl_attr_parse_nested(nest, attr_cb, &tb_info); + if (ret < 0) + return ret; + + open_json_object("statistics"); + for (i = 0; i < ARRAY_SIZE(stats); i++) { + char fmt[32]; + + if (!tb[stats[i].attr]) + continue; + + if (!header && !is_json_context()) { + printf("Statistics:\n"); + header = true; + } + + if (mnl_attr_validate(tb[stats[i].attr], MNL_TYPE_U64)) { + fprintf(stderr, "malformed netlink message (statistic)\n"); + goto err_close_stats; + } + + n = snprintf(fmt, sizeof(fmt), " %s: %%" PRIu64 "\n", + stats[i].name); + if (n >= sizeof(fmt)) { + fprintf(stderr, "internal error - malformed label\n"); + goto err_close_stats; + } + + print_u64(PRINT_ANY, stats[i].name, fmt, + mnl_attr_get_u64(tb[stats[i].attr])); + } + close_json_object(); + + return 0; + +err_close_stats: + close_json_object(); + return -1; +} + +int pause_reply_cb(const struct nlmsghdr *nlhdr, void *data) +{ + const struct nlattr *tb[ETHTOOL_A_PAUSE_MAX + 1] = {}; + DECLARE_ATTR_TB_INFO(tb); + struct nl_context *nlctx = data; + bool silent; + int err_ret; + int ret; + + silent = nlctx->is_dump || nlctx->is_monitor; + err_ret = silent ? MNL_CB_OK : MNL_CB_ERROR; + ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info); + if (ret < 0) + return err_ret; + nlctx->devname = get_dev_name(tb[ETHTOOL_A_PAUSE_HEADER]); + if (!dev_ok(nlctx)) + return err_ret; + + if (silent) + print_nl(); + + open_json_object(NULL); + + print_string(PRINT_ANY, "ifname", "Pause parameters for %s:\n", + nlctx->devname); + + show_bool("autonegotiate", "Autonegotiate:\t%s\n", + tb[ETHTOOL_A_PAUSE_AUTONEG]); + show_bool("rx", "RX:\t\t%s\n", tb[ETHTOOL_A_PAUSE_RX]); + show_bool("tx", "TX:\t\t%s\n", tb[ETHTOOL_A_PAUSE_TX]); + + if (!nlctx->is_monitor && tb[ETHTOOL_A_PAUSE_AUTONEG] && + mnl_attr_get_u8(tb[ETHTOOL_A_PAUSE_AUTONEG])) { + ret = show_pause_autoneg_status(nlctx); + if (ret < 0) + goto err_close_dev; + } + if (tb[ETHTOOL_A_PAUSE_STATS]) { + ret = show_pause_stats(tb[ETHTOOL_A_PAUSE_STATS]); + if (ret < 0) + goto err_close_dev; + } + if (!silent) + print_nl(); + + close_json_object(); + + return MNL_CB_OK; + +err_close_dev: + close_json_object(); + return err_ret; +} + +int nl_gpause(struct cmd_context *ctx) +{ + struct nl_context *nlctx = ctx->nlctx; + struct nl_socket *nlsk = nlctx->ethnl_socket; + u32 flags; + int ret; + + if (netlink_cmd_check(ctx, ETHTOOL_MSG_PAUSE_GET, true)) + return -EOPNOTSUPP; + if (ctx->argc > 0) { + fprintf(stderr, "ethtool: unexpected parameter '%s'\n", + *ctx->argp); + return 1; + } + + flags = get_stats_flag(nlctx, ETHTOOL_MSG_PAUSE_GET, + ETHTOOL_A_PAUSE_HEADER); + ret = nlsock_prep_get_request(nlsk, ETHTOOL_MSG_PAUSE_GET, + ETHTOOL_A_PAUSE_HEADER, flags); + if (ret < 0) + return ret; + + new_json_obj(ctx->json); + ret = nlsock_send_get_request(nlsk, pause_reply_cb); + delete_json_obj(); + return ret; +} + +/* PAUSE_SET */ + +static const struct param_parser spause_params[] = { + { + .arg = "autoneg", + .type = ETHTOOL_A_PAUSE_AUTONEG, + .handler = nl_parse_u8bool, + .min_argc = 1, + }, + { + .arg = "rx", + .type = ETHTOOL_A_PAUSE_RX, + .handler = nl_parse_u8bool, + .min_argc = 1, + }, + { + .arg = "tx", + .type = ETHTOOL_A_PAUSE_TX, + .handler = nl_parse_u8bool, + .min_argc = 1, + }, + {} +}; + +int nl_spause(struct cmd_context *ctx) +{ + struct nl_context *nlctx = ctx->nlctx; + struct nl_msg_buff *msgbuff; + struct nl_socket *nlsk; + int ret; + + if (netlink_cmd_check(ctx, ETHTOOL_MSG_PAUSE_SET, false)) + return -EOPNOTSUPP; + + nlctx->cmd = "-A"; + nlctx->argp = ctx->argp; + nlctx->argc = ctx->argc; + nlctx->devname = ctx->devname; + nlsk = nlctx->ethnl_socket; + msgbuff = &nlsk->msgbuff; + + ret = msg_init(nlctx, msgbuff, ETHTOOL_MSG_PAUSE_SET, + NLM_F_REQUEST | NLM_F_ACK); + if (ret < 0) + return 2; + if (ethnla_fill_header(msgbuff, ETHTOOL_A_PAUSE_HEADER, + ctx->devname, 0)) + return -EMSGSIZE; + + ret = nl_parser(nlctx, spause_params, NULL, PARSER_GROUP_NONE, NULL); + if (ret < 0) + return 1; + + ret = nlsock_sendmsg(nlsk, NULL); + if (ret < 0) + return 76; + ret = nlsock_process_reply(nlsk, nomsg_reply_cb, nlctx); + if (ret == 0) + return 0; + else + return nlctx->exit_code ?: 76; +} diff --git a/netlink/prettymsg.c b/netlink/prettymsg.c index 9e62beb..0a1fae3 100644 --- a/netlink/prettymsg.c +++ b/netlink/prettymsg.c @@ -9,6 +9,7 @@ #include <errno.h> #include <stdint.h> #include <limits.h> +#include <inttypes.h> #include <linux/genetlink.h> #include <linux/rtnetlink.h> #include <linux/if_link.h> @@ -110,6 +111,9 @@ static int pretty_print_attr(const struct nlattr *attr, case NLA_U32: printf("%u", mnl_attr_get_u32(attr)); break; + case NLA_U64: + printf("%" PRIu64, mnl_attr_get_u64(attr)); + break; case NLA_X8: printf("0x%02x", mnl_attr_get_u8(attr)); break; @@ -119,6 +123,9 @@ static int pretty_print_attr(const struct nlattr *attr, case NLA_X32: printf("0x%08x", mnl_attr_get_u32(attr)); break; + case NLA_X64: + printf("%" PRIx64, mnl_attr_get_u64(attr)); + break; case NLA_S8: printf("%d", (int)mnl_attr_get_u8(attr)); break; @@ -128,6 +135,9 @@ static int pretty_print_attr(const struct nlattr *attr, case NLA_S32: printf("%d", (int)mnl_attr_get_u32(attr)); break; + case NLA_S64: + printf("%" PRId64, (int64_t)mnl_attr_get_u64(attr)); + break; case NLA_STRING: printf("\"%.*s\"", alen, (const char *)adata); break; @@ -137,6 +147,15 @@ static int pretty_print_attr(const struct nlattr *attr, case NLA_BOOL: printf("%s", mnl_attr_get_u8(attr) ? "on" : "off"); break; + case NLA_U32_ENUM: { + uint32_t val = mnl_attr_get_u32(attr); + + if (adesc && val < adesc->n_names && adesc->names[val]) + printf("%s", adesc->names[val]); + else + printf("%u", val); + break; + } default: if (alen <= __DUMP_LINE) __print_binary_short(adata, alen); @@ -202,7 +221,7 @@ static void rtm_link_summary(const struct ifinfomsg *ifinfo) printf(" ifindex=%d", ifinfo->ifi_index); if (ifinfo->ifi_flags) printf(" flags=0x%x", ifinfo->ifi_flags); - if (ifinfo->ifi_flags) + if (ifinfo->ifi_change) printf(" change=0x%x", ifinfo->ifi_change); } diff --git a/netlink/prettymsg.h b/netlink/prettymsg.h index b5e5f73..25990cc 100644 --- a/netlink/prettymsg.h +++ b/netlink/prettymsg.h @@ -17,24 +17,34 @@ enum pretty_nla_format { NLA_U8, NLA_U16, NLA_U32, + NLA_U64, NLA_X8, NLA_X16, NLA_X32, + NLA_X64, NLA_S8, NLA_S16, NLA_S32, + NLA_S64, NLA_STRING, NLA_FLAG, NLA_BOOL, NLA_NESTED, NLA_ARRAY, + NLA_U32_ENUM, }; struct pretty_nla_desc { enum pretty_nla_format format; const char *name; - const struct pretty_nla_desc *children; - unsigned int n_children; + union { + const struct pretty_nla_desc *children; + const char *const *names; + }; + union { + unsigned int n_children; + unsigned int n_names; + }; }; struct pretty_nlmsg_desc { @@ -55,12 +65,15 @@ struct pretty_nlmsg_desc { #define NLATTR_DESC_U8(_name) NLATTR_DESC(_name, NLA_U8) #define NLATTR_DESC_U16(_name) NLATTR_DESC(_name, NLA_U16) #define NLATTR_DESC_U32(_name) NLATTR_DESC(_name, NLA_U32) +#define NLATTR_DESC_U64(_name) NLATTR_DESC(_name, NLA_U64) #define NLATTR_DESC_X8(_name) NLATTR_DESC(_name, NLA_X8) #define NLATTR_DESC_X16(_name) NLATTR_DESC(_name, NLA_X16) #define NLATTR_DESC_X32(_name) NLATTR_DESC(_name, NLA_X32) +#define NLATTR_DESC_X64(_name) NLATTR_DESC(_name, NLA_X64) #define NLATTR_DESC_S8(_name) NLATTR_DESC(_name, NLA_U8) #define NLATTR_DESC_S16(_name) NLATTR_DESC(_name, NLA_U16) #define NLATTR_DESC_S32(_name) NLATTR_DESC(_name, NLA_U32) +#define NLATTR_DESC_S64(_name) NLATTR_DESC(_name, NLA_S64) #define NLATTR_DESC_STRING(_name) NLATTR_DESC(_name, NLA_STRING) #define NLATTR_DESC_FLAG(_name) NLATTR_DESC(_name, NLA_FLAG) #define NLATTR_DESC_BOOL(_name) NLATTR_DESC(_name, NLA_BOOL) @@ -81,6 +94,13 @@ struct pretty_nlmsg_desc { .children = __ ## _children_desc ## _desc, \ .n_children = 1, \ } +#define NLATTR_DESC_U32_ENUM(_name, _names_table) \ + [_name] = { \ + .format = NLA_U32_ENUM, \ + .name = #_name, \ + .names = __ ## _names_table ## _names, \ + .n_children = ARRAY_SIZE(__ ## _names_table ## _names), \ + } #define NLMSG_DESC(_name, _attrs) \ [_name] = { \ diff --git a/netlink/privflags.c b/netlink/privflags.c new file mode 100644 index 0000000..299ccdc --- /dev/null +++ b/netlink/privflags.c @@ -0,0 +1,158 @@ +/* + * privflags.c - netlink implementation of private flags commands + * + * Implementation of "ethtool --show-priv-flags <dev>" and + * "ethtool --set-priv-flags <dev> ..." + */ + +#include <errno.h> +#include <string.h> +#include <stdio.h> + +#include "../internal.h" +#include "../common.h" +#include "netlink.h" +#include "strset.h" +#include "bitset.h" +#include "parser.h" + +/* PRIVFLAGS_GET */ + +static void privflags_maxlen_walk_cb(unsigned int idx, const char *name, + bool val __maybe_unused, void *data) +{ + unsigned int *maxlen = data; + unsigned int len, n; + + if (name) + len = strlen(name); + else { + len = 3; /* strlen("bit") */ + for (n = idx ?: 1; n; n /= 10) + len++; /* plus number of ditigs */ + } + if (len > *maxlen) + *maxlen = len; +} + +static void privflags_dump_walk_cb(unsigned int idx, const char *name, bool val, + void *data) +{ + unsigned int *maxlen = data; + char buff[16]; + + if (!name) { + snprintf(buff, sizeof(buff) - 1, "bit%u", idx); + name = buff; + } + printf("%-*s: %s\n", *maxlen, name, val ? "on" : "off"); +} + +int privflags_reply_cb(const struct nlmsghdr *nlhdr, void *data) +{ + const struct nlattr *tb[ETHTOOL_A_PRIVFLAGS_MAX + 1] = {}; + DECLARE_ATTR_TB_INFO(tb); + const struct stringset *flag_names = NULL; + struct nl_context *nlctx = data; + unsigned int maxlen = 0; + bool silent; + int err_ret; + int ret; + + silent = nlctx->is_dump || nlctx->is_monitor; + err_ret = silent ? MNL_CB_OK : MNL_CB_ERROR; + + ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info); + if (ret < 0 || !tb[ETHTOOL_A_PRIVFLAGS_FLAGS]) + return err_ret; + nlctx->devname = get_dev_name(tb[ETHTOOL_A_PRIVFLAGS_HEADER]); + if (!dev_ok(nlctx)) + return MNL_CB_OK; + + if (bitset_is_compact(tb[ETHTOOL_A_PRIVFLAGS_FLAGS])) { + ret = netlink_init_ethnl2_socket(nlctx); + if (ret < 0) + return err_ret; + flag_names = perdev_stringset(nlctx->devname, ETH_SS_PRIV_FLAGS, + nlctx->ethnl2_socket); + } + + ret = walk_bitset(tb[ETHTOOL_A_PRIVFLAGS_FLAGS], flag_names, + privflags_maxlen_walk_cb, &maxlen); + if (ret < 0) + return err_ret; + if (silent) + putchar('\n'); + printf("Private flags for %s:\n", nlctx->devname); + ret = walk_bitset(tb[ETHTOOL_A_PRIVFLAGS_FLAGS], flag_names, + privflags_dump_walk_cb, &maxlen); + return (ret < 0) ? err_ret : MNL_CB_OK; +} + +int nl_gprivflags(struct cmd_context *ctx) +{ + struct nl_context *nlctx = ctx->nlctx; + struct nl_socket *nlsk = nlctx->ethnl_socket; + int ret; + + if (netlink_cmd_check(ctx, ETHTOOL_MSG_PRIVFLAGS_GET, true)) + return -EOPNOTSUPP; + if (ctx->argc > 0) { + fprintf(stderr, "ethtool: unexpected parameter '%s'\n", + *ctx->argp); + return 1; + } + + ret = nlsock_prep_get_request(nlsk, ETHTOOL_MSG_PRIVFLAGS_GET, + ETHTOOL_A_PRIVFLAGS_HEADER, 0); + if (ret < 0) + return ret; + return nlsock_send_get_request(nlsk, privflags_reply_cb); +} + +/* PRIVFLAGS_SET */ + +static const struct bitset_parser_data privflags_parser_data = { + .force_hex = false, + .no_mask = false, +}; + +int nl_sprivflags(struct cmd_context *ctx) +{ + struct nl_context *nlctx = ctx->nlctx; + struct nl_msg_buff *msgbuff; + struct nl_socket *nlsk; + int ret; + + if (netlink_cmd_check(ctx, ETHTOOL_MSG_PRIVFLAGS_SET, false)) + return -EOPNOTSUPP; + + nlctx->cmd = "--set-priv-flags"; + nlctx->argp = ctx->argp; + nlctx->argc = ctx->argc; + nlctx->devname = ctx->devname; + nlsk = nlctx->ethnl_socket; + msgbuff = &nlsk->msgbuff; + + ret = msg_init(nlctx, msgbuff, ETHTOOL_MSG_PRIVFLAGS_SET, + NLM_F_REQUEST | NLM_F_ACK); + if (ret < 0) + return 2; + if (ethnla_fill_header(msgbuff, ETHTOOL_A_PRIVFLAGS_HEADER, + ctx->devname, 0)) + return -EMSGSIZE; + + ret = nl_parse_bitset(nlctx, ETHTOOL_A_PRIVFLAGS_FLAGS, + &privflags_parser_data, msgbuff, NULL); + if (ret < 0) + return -EINVAL; + + ret = nlsock_sendmsg(nlsk, NULL); + if (ret < 0) + return 2; + ret = nlsock_process_reply(nlsk, nomsg_reply_cb, nlctx); + if (ret == 0) + return 0; + else + return nlctx->exit_code ?: 1; +} diff --git a/netlink/rings.c b/netlink/rings.c new file mode 100644 index 0000000..b8c458f --- /dev/null +++ b/netlink/rings.c @@ -0,0 +1,141 @@ +/* + * rings.c - netlink implementation of ring commands + * + * Implementation of "ethtool -g <dev>" and "ethtool -G <dev> ..." + */ + +#include <errno.h> +#include <string.h> +#include <stdio.h> + +#include "../internal.h" +#include "../common.h" +#include "netlink.h" +#include "parser.h" + +/* RINGS_GET */ + +int rings_reply_cb(const struct nlmsghdr *nlhdr, void *data) +{ + const struct nlattr *tb[ETHTOOL_A_RINGS_MAX + 1] = {}; + DECLARE_ATTR_TB_INFO(tb); + struct nl_context *nlctx = data; + bool silent; + int err_ret; + int ret; + + silent = nlctx->is_dump || nlctx->is_monitor; + err_ret = silent ? MNL_CB_OK : MNL_CB_ERROR; + ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info); + if (ret < 0) + return err_ret; + nlctx->devname = get_dev_name(tb[ETHTOOL_A_RINGS_HEADER]); + if (!dev_ok(nlctx)) + return err_ret; + + if (silent) + putchar('\n'); + printf("Ring parameters for %s:\n", nlctx->devname); + printf("Pre-set maximums:\n"); + show_u32(tb[ETHTOOL_A_RINGS_RX_MAX], "RX:\t\t"); + show_u32(tb[ETHTOOL_A_RINGS_RX_MINI_MAX], "RX Mini:\t"); + show_u32(tb[ETHTOOL_A_RINGS_RX_JUMBO_MAX], "RX Jumbo:\t"); + show_u32(tb[ETHTOOL_A_RINGS_TX_MAX], "TX:\t\t"); + printf("Current hardware settings:\n"); + show_u32(tb[ETHTOOL_A_RINGS_RX], "RX:\t\t"); + show_u32(tb[ETHTOOL_A_RINGS_RX_MINI], "RX Mini:\t"); + show_u32(tb[ETHTOOL_A_RINGS_RX_JUMBO], "RX Jumbo:\t"); + show_u32(tb[ETHTOOL_A_RINGS_TX], "TX:\t\t"); + + return MNL_CB_OK; +} + +int nl_gring(struct cmd_context *ctx) +{ + struct nl_context *nlctx = ctx->nlctx; + struct nl_socket *nlsk = nlctx->ethnl_socket; + int ret; + + if (netlink_cmd_check(ctx, ETHTOOL_MSG_RINGS_GET, true)) + return -EOPNOTSUPP; + if (ctx->argc > 0) { + fprintf(stderr, "ethtool: unexpected parameter '%s'\n", + *ctx->argp); + return 1; + } + + ret = nlsock_prep_get_request(nlsk, ETHTOOL_MSG_RINGS_GET, + ETHTOOL_A_RINGS_HEADER, 0); + if (ret < 0) + return ret; + return nlsock_send_get_request(nlsk, rings_reply_cb); +} + +/* RINGS_SET */ + +static const struct param_parser sring_params[] = { + { + .arg = "rx", + .type = ETHTOOL_A_RINGS_RX, + .handler = nl_parse_direct_u32, + .min_argc = 1, + }, + { + .arg = "rx-mini", + .type = ETHTOOL_A_RINGS_RX_MINI, + .handler = nl_parse_direct_u32, + .min_argc = 1, + }, + { + .arg = "rx-jumbo", + .type = ETHTOOL_A_RINGS_RX_JUMBO, + .handler = nl_parse_direct_u32, + .min_argc = 1, + }, + { + .arg = "tx", + .type = ETHTOOL_A_RINGS_TX, + .handler = nl_parse_direct_u32, + .min_argc = 1, + }, + {} +}; + +int nl_sring(struct cmd_context *ctx) +{ + struct nl_context *nlctx = ctx->nlctx; + struct nl_msg_buff *msgbuff; + struct nl_socket *nlsk; + int ret; + + if (netlink_cmd_check(ctx, ETHTOOL_MSG_RINGS_SET, false)) + return -EOPNOTSUPP; + + nlctx->cmd = "-G"; + nlctx->argp = ctx->argp; + nlctx->argc = ctx->argc; + nlctx->devname = ctx->devname; + nlsk = nlctx->ethnl_socket; + msgbuff = &nlsk->msgbuff; + + ret = msg_init(nlctx, msgbuff, ETHTOOL_MSG_RINGS_SET, + NLM_F_REQUEST | NLM_F_ACK); + if (ret < 0) + return 2; + if (ethnla_fill_header(msgbuff, ETHTOOL_A_RINGS_HEADER, + ctx->devname, 0)) + return -EMSGSIZE; + + ret = nl_parser(nlctx, sring_params, NULL, PARSER_GROUP_NONE, NULL); + if (ret < 0) + return 1; + + ret = nlsock_sendmsg(nlsk, NULL); + if (ret < 0) + return 81; + ret = nlsock_process_reply(nlsk, nomsg_reply_cb, nlctx); + if (ret == 0) + return 0; + else + return nlctx->exit_code ?: 81; +} diff --git a/netlink/settings.c b/netlink/settings.c index 0212465..90c28b1 100644 --- a/netlink/settings.c +++ b/netlink/settings.c @@ -17,15 +17,6 @@ /* GET_SETTINGS */ -enum link_mode_class { - LM_CLASS_UNKNOWN, - LM_CLASS_REAL, - LM_CLASS_AUTONEG, - LM_CLASS_PORT, - LM_CLASS_PAUSE, - LM_CLASS_FEC, -}; - struct link_mode_info { enum link_mode_class class; u32 speed; @@ -37,6 +28,21 @@ static const char *const names_duplex[] = { [DUPLEX_FULL] = "Full", }; +static const char *const names_master_slave_state[] = { + [MASTER_SLAVE_STATE_UNKNOWN] = "unknown", + [MASTER_SLAVE_STATE_MASTER] = "master", + [MASTER_SLAVE_STATE_SLAVE] = "slave", + [MASTER_SLAVE_STATE_ERR] = "resolution error", +}; + +static const char *const names_master_slave_cfg[] = { + [MASTER_SLAVE_CFG_UNKNOWN] = "unknown", + [MASTER_SLAVE_CFG_MASTER_PREFERRED] = "preferred master", + [MASTER_SLAVE_CFG_SLAVE_PREFERRED] = "preferred slave", + [MASTER_SLAVE_CFG_MASTER_FORCE] = "forced master", + [MASTER_SLAVE_CFG_SLAVE_FORCE] = "forced slave", +}; + static const char *const names_port[] = { [PORT_TP] = "Twisted Pair", [PORT_AUI] = "AUI", @@ -58,158 +64,113 @@ static const char *const names_transceiver[] = { * there is little chance of getting them separated any time soon so let's * sort them out ourselves */ +#define __REAL(_speed) \ + { .class = LM_CLASS_REAL, .speed = _speed, .duplex = DUPLEX_FULL } +#define __HALF_DUPLEX(_speed) \ + { .class = LM_CLASS_REAL, .speed = _speed, .duplex = DUPLEX_HALF } +#define __SPECIAL(_class) \ + { .class = LM_CLASS_ ## _class } + static const struct link_mode_info link_modes[] = { - [ETHTOOL_LINK_MODE_10baseT_Half_BIT] = - { LM_CLASS_REAL, 10, DUPLEX_HALF }, - [ETHTOOL_LINK_MODE_10baseT_Full_BIT] = - { LM_CLASS_REAL, 10, DUPLEX_FULL }, - [ETHTOOL_LINK_MODE_100baseT_Half_BIT] = - { LM_CLASS_REAL, 100, DUPLEX_HALF }, - [ETHTOOL_LINK_MODE_100baseT_Full_BIT] = - { LM_CLASS_REAL, 100, DUPLEX_FULL }, - [ETHTOOL_LINK_MODE_1000baseT_Half_BIT] = - { LM_CLASS_REAL, 1000, DUPLEX_HALF }, - [ETHTOOL_LINK_MODE_1000baseT_Full_BIT] = - { LM_CLASS_REAL, 1000, DUPLEX_FULL }, - [ETHTOOL_LINK_MODE_Autoneg_BIT] = - { LM_CLASS_AUTONEG }, - [ETHTOOL_LINK_MODE_TP_BIT] = - { LM_CLASS_PORT }, - [ETHTOOL_LINK_MODE_AUI_BIT] = - { LM_CLASS_PORT }, - [ETHTOOL_LINK_MODE_MII_BIT] = - { LM_CLASS_PORT }, - [ETHTOOL_LINK_MODE_FIBRE_BIT] = - { LM_CLASS_PORT }, - [ETHTOOL_LINK_MODE_BNC_BIT] = - { LM_CLASS_PORT }, - [ETHTOOL_LINK_MODE_10000baseT_Full_BIT] = - { LM_CLASS_REAL, 10000, DUPLEX_FULL }, - [ETHTOOL_LINK_MODE_Pause_BIT] = - { LM_CLASS_PAUSE }, - [ETHTOOL_LINK_MODE_Asym_Pause_BIT] = - { LM_CLASS_PAUSE }, - [ETHTOOL_LINK_MODE_2500baseX_Full_BIT] = - { LM_CLASS_REAL, 2500, DUPLEX_FULL }, - [ETHTOOL_LINK_MODE_Backplane_BIT] = - { LM_CLASS_PORT }, - [ETHTOOL_LINK_MODE_1000baseKX_Full_BIT] = - { LM_CLASS_REAL, 1000, DUPLEX_FULL }, - [ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT] = - { LM_CLASS_REAL, 10000, DUPLEX_FULL }, - [ETHTOOL_LINK_MODE_10000baseKR_Full_BIT] = - { LM_CLASS_REAL, 10000, DUPLEX_FULL }, - [ETHTOOL_LINK_MODE_10000baseR_FEC_BIT] = - { LM_CLASS_REAL, 10000, DUPLEX_FULL }, - [ETHTOOL_LINK_MODE_20000baseMLD2_Full_BIT] = - { LM_CLASS_REAL, 20000, DUPLEX_FULL }, - [ETHTOOL_LINK_MODE_20000baseKR2_Full_BIT] = - { LM_CLASS_REAL, 20000, DUPLEX_FULL }, - [ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT] = - { LM_CLASS_REAL, 40000, DUPLEX_FULL }, - [ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT] = - { LM_CLASS_REAL, 40000, DUPLEX_FULL }, - [ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT] = - { LM_CLASS_REAL, 40000, DUPLEX_FULL }, - [ETHTOOL_LINK_MODE_40000baseLR4_Full_BIT] = - { LM_CLASS_REAL, 40000, DUPLEX_FULL }, - [ETHTOOL_LINK_MODE_56000baseKR4_Full_BIT] = - { LM_CLASS_REAL, 56000, DUPLEX_FULL }, - [ETHTOOL_LINK_MODE_56000baseCR4_Full_BIT] = - { LM_CLASS_REAL, 56000, DUPLEX_FULL }, - [ETHTOOL_LINK_MODE_56000baseSR4_Full_BIT] = - { LM_CLASS_REAL, 56000, DUPLEX_FULL }, - [ETHTOOL_LINK_MODE_56000baseLR4_Full_BIT] = - { LM_CLASS_REAL, 56000, DUPLEX_FULL }, - [ETHTOOL_LINK_MODE_25000baseCR_Full_BIT] = - { LM_CLASS_REAL, 25000, DUPLEX_FULL }, - [ETHTOOL_LINK_MODE_25000baseKR_Full_BIT] = - { LM_CLASS_REAL, 25000, DUPLEX_FULL }, - [ETHTOOL_LINK_MODE_25000baseSR_Full_BIT] = - { LM_CLASS_REAL, 25000, DUPLEX_FULL }, - [ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT] = - { LM_CLASS_REAL, 50000, DUPLEX_FULL }, - [ETHTOOL_LINK_MODE_50000baseKR2_Full_BIT] = - { LM_CLASS_REAL, 50000, DUPLEX_FULL }, - [ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT] = - { LM_CLASS_REAL, 100000, DUPLEX_FULL }, - [ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT] = - { LM_CLASS_REAL, 100000, DUPLEX_FULL }, - [ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT] = - { LM_CLASS_REAL, 100000, DUPLEX_FULL }, - [ETHTOOL_LINK_MODE_100000baseLR4_ER4_Full_BIT] = - { LM_CLASS_REAL, 100000, DUPLEX_FULL }, - [ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT] = - { LM_CLASS_REAL, 50000, DUPLEX_FULL }, - [ETHTOOL_LINK_MODE_1000baseX_Full_BIT] = - { LM_CLASS_REAL, 1000, DUPLEX_FULL }, - [ETHTOOL_LINK_MODE_10000baseCR_Full_BIT] = - { LM_CLASS_REAL, 10000, DUPLEX_FULL }, - [ETHTOOL_LINK_MODE_10000baseSR_Full_BIT] = - { LM_CLASS_REAL, 10000, DUPLEX_FULL }, - [ETHTOOL_LINK_MODE_10000baseLR_Full_BIT] = - { LM_CLASS_REAL, 10000, DUPLEX_FULL }, - [ETHTOOL_LINK_MODE_10000baseLRM_Full_BIT] = - { LM_CLASS_REAL, 10000, DUPLEX_FULL }, - [ETHTOOL_LINK_MODE_10000baseER_Full_BIT] = - { LM_CLASS_REAL, 10000, DUPLEX_FULL }, - [ETHTOOL_LINK_MODE_2500baseT_Full_BIT] = - { LM_CLASS_REAL, 2500, DUPLEX_FULL }, - [ETHTOOL_LINK_MODE_5000baseT_Full_BIT] = - { LM_CLASS_REAL, 5000, DUPLEX_FULL }, - [ETHTOOL_LINK_MODE_FEC_NONE_BIT] = - { LM_CLASS_FEC }, - [ETHTOOL_LINK_MODE_FEC_RS_BIT] = - { LM_CLASS_FEC }, - [ETHTOOL_LINK_MODE_FEC_BASER_BIT] = - { LM_CLASS_FEC }, - [ETHTOOL_LINK_MODE_50000baseKR_Full_BIT] = - { LM_CLASS_REAL, 50000, DUPLEX_FULL }, - [ETHTOOL_LINK_MODE_50000baseSR_Full_BIT] = - { LM_CLASS_REAL, 50000, DUPLEX_FULL }, - [ETHTOOL_LINK_MODE_50000baseCR_Full_BIT] = - { LM_CLASS_REAL, 50000, DUPLEX_FULL }, - [ETHTOOL_LINK_MODE_50000baseLR_ER_FR_Full_BIT] = - { LM_CLASS_REAL, 50000, DUPLEX_FULL }, - [ETHTOOL_LINK_MODE_50000baseDR_Full_BIT] = - { LM_CLASS_REAL, 50000, DUPLEX_FULL }, - [ETHTOOL_LINK_MODE_100000baseKR2_Full_BIT] = - { LM_CLASS_REAL, 100000, DUPLEX_FULL }, - [ETHTOOL_LINK_MODE_100000baseSR2_Full_BIT] = - { LM_CLASS_REAL, 100000, DUPLEX_FULL }, - [ETHTOOL_LINK_MODE_100000baseCR2_Full_BIT] = - { LM_CLASS_REAL, 100000, DUPLEX_FULL }, - [ETHTOOL_LINK_MODE_100000baseLR2_ER2_FR2_Full_BIT] = - { LM_CLASS_REAL, 100000, DUPLEX_FULL }, - [ETHTOOL_LINK_MODE_100000baseDR2_Full_BIT] = - { LM_CLASS_REAL, 100000, DUPLEX_FULL }, - [ETHTOOL_LINK_MODE_200000baseKR4_Full_BIT] = - { LM_CLASS_REAL, 200000, DUPLEX_FULL }, - [ETHTOOL_LINK_MODE_200000baseSR4_Full_BIT] = - { LM_CLASS_REAL, 200000, DUPLEX_FULL }, - [ETHTOOL_LINK_MODE_200000baseLR4_ER4_FR4_Full_BIT] = - { LM_CLASS_REAL, 200000, DUPLEX_FULL }, - [ETHTOOL_LINK_MODE_200000baseDR4_Full_BIT] = - { LM_CLASS_REAL, 200000, DUPLEX_FULL }, - [ETHTOOL_LINK_MODE_200000baseCR4_Full_BIT] = - { LM_CLASS_REAL, 200000, DUPLEX_FULL }, - [ETHTOOL_LINK_MODE_100baseT1_Full_BIT] = - { LM_CLASS_REAL, 100, DUPLEX_FULL }, - [ETHTOOL_LINK_MODE_1000baseT1_Full_BIT] = - { LM_CLASS_REAL, 1000, DUPLEX_FULL }, - [ETHTOOL_LINK_MODE_400000baseKR8_Full_BIT] = - { LM_CLASS_REAL, 400000, DUPLEX_FULL }, - [ETHTOOL_LINK_MODE_400000baseSR8_Full_BIT] = - { LM_CLASS_REAL, 400000, DUPLEX_FULL }, - [ETHTOOL_LINK_MODE_400000baseLR8_ER8_FR8_Full_BIT] = - { LM_CLASS_REAL, 400000, DUPLEX_FULL }, - [ETHTOOL_LINK_MODE_400000baseDR8_Full_BIT] = - { LM_CLASS_REAL, 400000, DUPLEX_FULL }, - [ETHTOOL_LINK_MODE_400000baseCR8_Full_BIT] = - { LM_CLASS_REAL, 400000, DUPLEX_FULL }, + [ETHTOOL_LINK_MODE_10baseT_Half_BIT] = __HALF_DUPLEX(10), + [ETHTOOL_LINK_MODE_10baseT_Full_BIT] = __REAL(10), + [ETHTOOL_LINK_MODE_100baseT_Half_BIT] = __HALF_DUPLEX(100), + [ETHTOOL_LINK_MODE_100baseT_Full_BIT] = __REAL(100), + [ETHTOOL_LINK_MODE_1000baseT_Half_BIT] = __HALF_DUPLEX(1000), + [ETHTOOL_LINK_MODE_1000baseT_Full_BIT] = __REAL(1000), + [ETHTOOL_LINK_MODE_Autoneg_BIT] = __SPECIAL(AUTONEG), + [ETHTOOL_LINK_MODE_TP_BIT] = __SPECIAL(PORT), + [ETHTOOL_LINK_MODE_AUI_BIT] = __SPECIAL(PORT), + [ETHTOOL_LINK_MODE_MII_BIT] = __SPECIAL(PORT), + [ETHTOOL_LINK_MODE_FIBRE_BIT] = __SPECIAL(PORT), + [ETHTOOL_LINK_MODE_BNC_BIT] = __SPECIAL(PORT), + [ETHTOOL_LINK_MODE_10000baseT_Full_BIT] = __REAL(10000), + [ETHTOOL_LINK_MODE_Pause_BIT] = __SPECIAL(PAUSE), + [ETHTOOL_LINK_MODE_Asym_Pause_BIT] = __SPECIAL(PAUSE), + [ETHTOOL_LINK_MODE_2500baseX_Full_BIT] = __REAL(2500), + [ETHTOOL_LINK_MODE_Backplane_BIT] = __SPECIAL(PORT), + [ETHTOOL_LINK_MODE_1000baseKX_Full_BIT] = __REAL(1000), + [ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT] = __REAL(10000), + [ETHTOOL_LINK_MODE_10000baseKR_Full_BIT] = __REAL(10000), + [ETHTOOL_LINK_MODE_10000baseR_FEC_BIT] = __REAL(10000), + [ETHTOOL_LINK_MODE_20000baseMLD2_Full_BIT] = __REAL(20000), + [ETHTOOL_LINK_MODE_20000baseKR2_Full_BIT] = __REAL(20000), + [ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT] = __REAL(40000), + [ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT] = __REAL(40000), + [ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT] = __REAL(40000), + [ETHTOOL_LINK_MODE_40000baseLR4_Full_BIT] = __REAL(40000), + [ETHTOOL_LINK_MODE_56000baseKR4_Full_BIT] = __REAL(56000), + [ETHTOOL_LINK_MODE_56000baseCR4_Full_BIT] = __REAL(56000), + [ETHTOOL_LINK_MODE_56000baseSR4_Full_BIT] = __REAL(56000), + [ETHTOOL_LINK_MODE_56000baseLR4_Full_BIT] = __REAL(56000), + [ETHTOOL_LINK_MODE_25000baseCR_Full_BIT] = __REAL(25000), + [ETHTOOL_LINK_MODE_25000baseKR_Full_BIT] = __REAL(25000), + [ETHTOOL_LINK_MODE_25000baseSR_Full_BIT] = __REAL(25000), + [ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT] = __REAL(50000), + [ETHTOOL_LINK_MODE_50000baseKR2_Full_BIT] = __REAL(50000), + [ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT] = __REAL(100000), + [ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT] = __REAL(100000), + [ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT] = __REAL(100000), + [ETHTOOL_LINK_MODE_100000baseLR4_ER4_Full_BIT] = __REAL(100000), + [ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT] = __REAL(50000), + [ETHTOOL_LINK_MODE_1000baseX_Full_BIT] = __REAL(1000), + [ETHTOOL_LINK_MODE_10000baseCR_Full_BIT] = __REAL(10000), + [ETHTOOL_LINK_MODE_10000baseSR_Full_BIT] = __REAL(10000), + [ETHTOOL_LINK_MODE_10000baseLR_Full_BIT] = __REAL(10000), + [ETHTOOL_LINK_MODE_10000baseLRM_Full_BIT] = __REAL(10000), + [ETHTOOL_LINK_MODE_10000baseER_Full_BIT] = __REAL(10000), + [ETHTOOL_LINK_MODE_2500baseT_Full_BIT] = __REAL(2500), + [ETHTOOL_LINK_MODE_5000baseT_Full_BIT] = __REAL(5000), + [ETHTOOL_LINK_MODE_FEC_NONE_BIT] = __SPECIAL(FEC), + [ETHTOOL_LINK_MODE_FEC_RS_BIT] = __SPECIAL(FEC), + [ETHTOOL_LINK_MODE_FEC_BASER_BIT] = __SPECIAL(FEC), + [ETHTOOL_LINK_MODE_50000baseKR_Full_BIT] = __REAL(50000), + [ETHTOOL_LINK_MODE_50000baseSR_Full_BIT] = __REAL(50000), + [ETHTOOL_LINK_MODE_50000baseCR_Full_BIT] = __REAL(50000), + [ETHTOOL_LINK_MODE_50000baseLR_ER_FR_Full_BIT] = __REAL(50000), + [ETHTOOL_LINK_MODE_50000baseDR_Full_BIT] = __REAL(50000), + [ETHTOOL_LINK_MODE_100000baseKR2_Full_BIT] = __REAL(100000), + [ETHTOOL_LINK_MODE_100000baseSR2_Full_BIT] = __REAL(100000), + [ETHTOOL_LINK_MODE_100000baseCR2_Full_BIT] = __REAL(100000), + [ETHTOOL_LINK_MODE_100000baseLR2_ER2_FR2_Full_BIT] = __REAL(100000), + [ETHTOOL_LINK_MODE_100000baseDR2_Full_BIT] = __REAL(100000), + [ETHTOOL_LINK_MODE_200000baseKR4_Full_BIT] = __REAL(200000), + [ETHTOOL_LINK_MODE_200000baseSR4_Full_BIT] = __REAL(200000), + [ETHTOOL_LINK_MODE_200000baseLR4_ER4_FR4_Full_BIT] = __REAL(200000), + [ETHTOOL_LINK_MODE_200000baseDR4_Full_BIT] = __REAL(200000), + [ETHTOOL_LINK_MODE_200000baseCR4_Full_BIT] = __REAL(200000), + [ETHTOOL_LINK_MODE_100baseT1_Full_BIT] = __REAL(100), + [ETHTOOL_LINK_MODE_1000baseT1_Full_BIT] = __REAL(1000), + [ETHTOOL_LINK_MODE_400000baseKR8_Full_BIT] = __REAL(400000), + [ETHTOOL_LINK_MODE_400000baseSR8_Full_BIT] = __REAL(400000), + [ETHTOOL_LINK_MODE_400000baseLR8_ER8_FR8_Full_BIT] = __REAL(400000), + [ETHTOOL_LINK_MODE_400000baseDR8_Full_BIT] = __REAL(400000), + [ETHTOOL_LINK_MODE_400000baseCR8_Full_BIT] = __REAL(400000), + [ETHTOOL_LINK_MODE_FEC_LLRS_BIT] = __SPECIAL(FEC), + [ETHTOOL_LINK_MODE_100000baseKR_Full_BIT] = __REAL(100000), + [ETHTOOL_LINK_MODE_100000baseSR_Full_BIT] = __REAL(100000), + [ETHTOOL_LINK_MODE_100000baseLR_ER_FR_Full_BIT] = __REAL(100000), + [ETHTOOL_LINK_MODE_100000baseCR_Full_BIT] = __REAL(100000), + [ETHTOOL_LINK_MODE_100000baseDR_Full_BIT] = __REAL(100000), + [ETHTOOL_LINK_MODE_200000baseKR2_Full_BIT] = __REAL(200000), + [ETHTOOL_LINK_MODE_200000baseSR2_Full_BIT] = __REAL(200000), + [ETHTOOL_LINK_MODE_200000baseLR2_ER2_FR2_Full_BIT] = __REAL(200000), + [ETHTOOL_LINK_MODE_200000baseDR2_Full_BIT] = __REAL(200000), + [ETHTOOL_LINK_MODE_200000baseCR2_Full_BIT] = __REAL(200000), + [ETHTOOL_LINK_MODE_400000baseKR4_Full_BIT] = __REAL(400000), + [ETHTOOL_LINK_MODE_400000baseSR4_Full_BIT] = __REAL(400000), + [ETHTOOL_LINK_MODE_400000baseLR4_ER4_FR4_Full_BIT] = __REAL(400000), + [ETHTOOL_LINK_MODE_400000baseDR4_Full_BIT] = __REAL(400000), + [ETHTOOL_LINK_MODE_400000baseCR4_Full_BIT] = __REAL(400000), + [ETHTOOL_LINK_MODE_100baseFX_Half_BIT] = __HALF_DUPLEX(100), + [ETHTOOL_LINK_MODE_100baseFX_Full_BIT] = __REAL(100), }; const unsigned int link_modes_count = ARRAY_SIZE(link_modes); +#undef __REAL +#undef __HALF_DUPLEX +#undef __SPECIAL + static bool lm_class_match(unsigned int mode, enum link_mode_class class) { unsigned int mode_class = (mode < link_modes_count) ? @@ -261,25 +222,33 @@ static void print_banner(struct nl_context *nlctx) nlctx->no_banner = true; } -static int dump_link_modes(struct nl_context *nlctx, - const struct nlattr *bitset, bool mask, - unsigned int class, const char *before, - const char *between, const char *after, - const char *if_none) +int dump_link_modes(struct nl_context *nlctx, const struct nlattr *bitset, + bool mask, unsigned int class, const char *before, + const char *between, const char *after, const char *if_none) { const struct nlattr *bitset_tb[ETHTOOL_A_BITSET_MAX + 1] = {}; DECLARE_ATTR_TB_INFO(bitset_tb); const unsigned int before_len = strlen(before); + unsigned int prev = UINT_MAX - 1; const struct nlattr *bits; const struct nlattr *bit; bool first = true; - int prev = -2; + bool nomask; int ret; ret = mnl_attr_parse_nested(bitset, attr_cb, &bitset_tb_info); - bits = bitset_tb[ETHTOOL_A_BITSET_BITS]; if (ret < 0) goto err_nonl; + + nomask = bitset_tb[ETHTOOL_A_BITSET_NOMASK]; + /* Trying to print the mask of a "no mask" bitset doesn't make sense */ + if (mask && nomask) { + ret = -EFAULT; + goto err_nonl; + } + + bits = bitset_tb[ETHTOOL_A_BITSET_BITS]; + if (!bits) { const struct stringset *lm_strings; unsigned int count; @@ -317,7 +286,7 @@ static int dump_link_modes(struct nl_context *nlctx, if (first) first = false; /* ugly hack to preserve old output format */ - if (class == LM_CLASS_REAL && (prev == idx - 1) && + if (class == LM_CLASS_REAL && (idx == prev + 1) && prev < link_modes_count && link_modes[prev].class == LM_CLASS_REAL && link_modes[prev].duplex == DUPLEX_HALF) @@ -348,7 +317,7 @@ static int dump_link_modes(struct nl_context *nlctx, if (!tb[ETHTOOL_A_BITSET_BIT_INDEX] || !tb[ETHTOOL_A_BITSET_BIT_NAME]) goto err; - if (!mask && !tb[ETHTOOL_A_BITSET_BIT_VALUE]) + if (!mask && !nomask && !tb[ETHTOOL_A_BITSET_BIT_VALUE]) continue; idx = mnl_attr_get_u32(tb[ETHTOOL_A_BITSET_BIT_INDEX]); @@ -359,7 +328,7 @@ static int dump_link_modes(struct nl_context *nlctx, first = false; } else { /* ugly hack to preserve old output format */ - if ((class == LM_CLASS_REAL) && (prev == idx - 1) && + if ((class == LM_CLASS_REAL) && (idx == prev + 1) && (prev < link_modes_count) && (link_modes[prev].class == LM_CLASS_REAL) && (link_modes[prev].duplex == DUPLEX_HALF)) @@ -463,9 +432,9 @@ static int dump_peer_modes(struct nl_context *nlctx, const struct nlattr *attr) printf("\tLink partner advertised auto-negotiation: %s\n", autoneg ? "Yes" : "No"); - ret = dump_link_modes(nlctx, attr, true, LM_CLASS_FEC, + ret = dump_link_modes(nlctx, attr, false, LM_CLASS_FEC, "Link partner advertised FEC modes: ", - " ", "\n", "No"); + " ", "\n", "Not reported"); return ret; } @@ -518,6 +487,25 @@ int linkmodes_reply_cb(const struct nlmsghdr *nlhdr, void *data) printf("\tAuto-negotiation: %s\n", (autoneg == AUTONEG_DISABLE) ? "off" : "on"); } + if (tb[ETHTOOL_A_LINKMODES_MASTER_SLAVE_CFG]) { + uint8_t val; + + val = mnl_attr_get_u8(tb[ETHTOOL_A_LINKMODES_MASTER_SLAVE_CFG]); + + print_banner(nlctx); + print_enum(names_master_slave_cfg, + ARRAY_SIZE(names_master_slave_cfg), val, + "master-slave cfg"); + } + if (tb[ETHTOOL_A_LINKMODES_MASTER_SLAVE_STATE]) { + uint8_t val; + + val = mnl_attr_get_u8(tb[ETHTOOL_A_LINKMODES_MASTER_SLAVE_STATE]); + print_banner(nlctx); + print_enum(names_master_slave_state, + ARRAY_SIZE(names_master_slave_state), val, + "master-slave status"); + } return MNL_CB_OK; err: @@ -579,6 +567,149 @@ int linkinfo_reply_cb(const struct nlmsghdr *nlhdr, void *data) return MNL_CB_OK; } +static const char *get_enum_string(const char *const *string_table, unsigned int n_string_table, + unsigned int val) +{ + if (val >= n_string_table || !string_table[val]) + return NULL; + else + return string_table[val]; +} + +static const char *const names_link_ext_state[] = { + [ETHTOOL_LINK_EXT_STATE_AUTONEG] = "Autoneg", + [ETHTOOL_LINK_EXT_STATE_LINK_TRAINING_FAILURE] = "Link training failure", + [ETHTOOL_LINK_EXT_STATE_LINK_LOGICAL_MISMATCH] = "Logical mismatch", + [ETHTOOL_LINK_EXT_STATE_BAD_SIGNAL_INTEGRITY] = "Bad signal integrity", + [ETHTOOL_LINK_EXT_STATE_NO_CABLE] = "No cable", + [ETHTOOL_LINK_EXT_STATE_CABLE_ISSUE] = "Cable issue", + [ETHTOOL_LINK_EXT_STATE_EEPROM_ISSUE] = "EEPROM issue", + [ETHTOOL_LINK_EXT_STATE_CALIBRATION_FAILURE] = "Calibration failure", + [ETHTOOL_LINK_EXT_STATE_POWER_BUDGET_EXCEEDED] = "Power budget exceeded", + [ETHTOOL_LINK_EXT_STATE_OVERHEAT] = "Overheat", +}; + +static const char *const names_autoneg_link_ext_substate[] = { + [ETHTOOL_LINK_EXT_SUBSTATE_AN_NO_PARTNER_DETECTED] = + "No partner detected", + [ETHTOOL_LINK_EXT_SUBSTATE_AN_ACK_NOT_RECEIVED] = + "Ack not received", + [ETHTOOL_LINK_EXT_SUBSTATE_AN_NEXT_PAGE_EXCHANGE_FAILED] = + "Next page exchange failed", + [ETHTOOL_LINK_EXT_SUBSTATE_AN_NO_PARTNER_DETECTED_FORCE_MODE] = + "No partner detected during force mode", + [ETHTOOL_LINK_EXT_SUBSTATE_AN_FEC_MISMATCH_DURING_OVERRIDE] = + "FEC mismatch during override", + [ETHTOOL_LINK_EXT_SUBSTATE_AN_NO_HCD] = + "No HCD", +}; + +static const char *const names_link_training_link_ext_substate[] = { + [ETHTOOL_LINK_EXT_SUBSTATE_LT_KR_FRAME_LOCK_NOT_ACQUIRED] = + "KR frame lock not acquired", + [ETHTOOL_LINK_EXT_SUBSTATE_LT_KR_LINK_INHIBIT_TIMEOUT] = + "KR link inhibit timeout", + [ETHTOOL_LINK_EXT_SUBSTATE_LT_KR_LINK_PARTNER_DID_NOT_SET_RECEIVER_READY] = + "KR Link partner did not set receiver ready", + [ETHTOOL_LINK_EXT_SUBSTATE_LT_REMOTE_FAULT] = + "Remote side is not ready yet", +}; + +static const char *const names_link_logical_mismatch_link_ext_substate[] = { + [ETHTOOL_LINK_EXT_SUBSTATE_LLM_PCS_DID_NOT_ACQUIRE_BLOCK_LOCK] = + "PCS did not acquire block lock", + [ETHTOOL_LINK_EXT_SUBSTATE_LLM_PCS_DID_NOT_ACQUIRE_AM_LOCK] = + "PCS did not acquire AM lock", + [ETHTOOL_LINK_EXT_SUBSTATE_LLM_PCS_DID_NOT_GET_ALIGN_STATUS] = + "PCS did not get align_status", + [ETHTOOL_LINK_EXT_SUBSTATE_LLM_FC_FEC_IS_NOT_LOCKED] = + "FC FEC is not locked", + [ETHTOOL_LINK_EXT_SUBSTATE_LLM_RS_FEC_IS_NOT_LOCKED] = + "RS FEC is not locked", +}; + +static const char *const names_bad_signal_integrity_link_ext_substate[] = { + [ETHTOOL_LINK_EXT_SUBSTATE_BSI_LARGE_NUMBER_OF_PHYSICAL_ERRORS] = + "Large number of physical errors", + [ETHTOOL_LINK_EXT_SUBSTATE_BSI_UNSUPPORTED_RATE] = + "Unsupported rate", +}; + +static const char *const names_cable_issue_link_ext_substate[] = { + [ETHTOOL_LINK_EXT_SUBSTATE_CI_UNSUPPORTED_CABLE] = + "Unsupported cable", + [ETHTOOL_LINK_EXT_SUBSTATE_CI_CABLE_TEST_FAILURE] = + "Cable test failure", +}; + +static const char *link_ext_substate_get(uint8_t link_ext_state_val, uint8_t link_ext_substate_val) +{ + switch (link_ext_state_val) { + case ETHTOOL_LINK_EXT_STATE_AUTONEG: + return get_enum_string(names_autoneg_link_ext_substate, + ARRAY_SIZE(names_autoneg_link_ext_substate), + link_ext_substate_val); + case ETHTOOL_LINK_EXT_STATE_LINK_TRAINING_FAILURE: + return get_enum_string(names_link_training_link_ext_substate, + ARRAY_SIZE(names_link_training_link_ext_substate), + link_ext_substate_val); + case ETHTOOL_LINK_EXT_STATE_LINK_LOGICAL_MISMATCH: + return get_enum_string(names_link_logical_mismatch_link_ext_substate, + ARRAY_SIZE(names_link_logical_mismatch_link_ext_substate), + link_ext_substate_val); + case ETHTOOL_LINK_EXT_STATE_BAD_SIGNAL_INTEGRITY: + return get_enum_string(names_bad_signal_integrity_link_ext_substate, + ARRAY_SIZE(names_bad_signal_integrity_link_ext_substate), + link_ext_substate_val); + case ETHTOOL_LINK_EXT_STATE_CABLE_ISSUE: + return get_enum_string(names_cable_issue_link_ext_substate, + ARRAY_SIZE(names_cable_issue_link_ext_substate), + link_ext_substate_val); + default: + return NULL; + } +} + +static void linkstate_link_ext_substate_print(const struct nlattr *tb[], + uint8_t link_ext_state_val) +{ + uint8_t link_ext_substate_val; + const char *link_ext_substate_str; + + if (!tb[ETHTOOL_A_LINKSTATE_EXT_SUBSTATE]) + return; + + link_ext_substate_val = mnl_attr_get_u8(tb[ETHTOOL_A_LINKSTATE_EXT_SUBSTATE]); + + link_ext_substate_str = link_ext_substate_get(link_ext_state_val, link_ext_substate_val); + if (!link_ext_substate_str) + printf(", %u", link_ext_substate_val); + else + printf(", %s", link_ext_substate_str); +} + +static void linkstate_link_ext_state_print(const struct nlattr *tb[]) +{ + uint8_t link_ext_state_val; + const char *link_ext_state_str; + + if (!tb[ETHTOOL_A_LINKSTATE_EXT_STATE]) + return; + + link_ext_state_val = mnl_attr_get_u8(tb[ETHTOOL_A_LINKSTATE_EXT_STATE]); + + link_ext_state_str = get_enum_string(names_link_ext_state, + ARRAY_SIZE(names_link_ext_state), + link_ext_state_val); + if (!link_ext_state_str) + printf(" (%u", link_ext_state_val); + else + printf(" (%s", link_ext_state_str); + + linkstate_link_ext_substate_print(tb, link_ext_state_val); + printf(")"); +} + int linkstate_reply_cb(const struct nlmsghdr *nlhdr, void *data) { const struct nlattr *tb[ETHTOOL_A_LINKSTATE_MAX + 1] = {}; @@ -599,13 +730,32 @@ int linkstate_reply_cb(const struct nlmsghdr *nlhdr, void *data) uint8_t val = mnl_attr_get_u8(tb[ETHTOOL_A_LINKSTATE_LINK]); print_banner(nlctx); - printf("\tLink detected: %s\n", val ? "yes" : "no"); + printf("\tLink detected: %s", val ? "yes" : "no"); + linkstate_link_ext_state_print(tb); + printf("\n"); + } + + if (tb[ETHTOOL_A_LINKSTATE_SQI]) { + uint32_t val = mnl_attr_get_u32(tb[ETHTOOL_A_LINKSTATE_SQI]); + + print_banner(nlctx); + printf("\tSQI: %u", val); + + if (tb[ETHTOOL_A_LINKSTATE_SQI_MAX]) { + uint32_t max; + + max = mnl_attr_get_u32(tb[ETHTOOL_A_LINKSTATE_SQI_MAX]); + printf("/%u\n", max); + } else { + printf("\n"); + } } return MNL_CB_OK; } -void wol_modes_cb(unsigned int idx, const char *name, bool val, void *data) +void wol_modes_cb(unsigned int idx, const char *name __maybe_unused, bool val, + void *data) { struct ethtool_wolinfo *wol = data; @@ -653,7 +803,8 @@ int wol_reply_cb(const struct nlmsghdr *nlhdr, void *data) return MNL_CB_OK; } -void msgmask_cb(unsigned int idx, const char *name, bool val, void *data) +void msgmask_cb(unsigned int idx, const char *name __maybe_unused, bool val, + void *data) { u32 *msg_mask = data; @@ -663,7 +814,8 @@ void msgmask_cb(unsigned int idx, const char *name, bool val, void *data) *msg_mask |= (1U << idx); } -void msgmask_cb2(unsigned int idx, const char *name, bool val, void *data) +void msgmask_cb2(unsigned int idx __maybe_unused, const char *name, + bool val, void *data __maybe_unused) { if (val) printf(" %s", name); @@ -726,6 +878,13 @@ int nl_gset(struct cmd_context *ctx) struct nl_socket *nlsk = nlctx->ethnl_socket; int ret; + if (netlink_cmd_check(ctx, ETHTOOL_MSG_LINKMODES_GET, true) || + netlink_cmd_check(ctx, ETHTOOL_MSG_LINKINFO_GET, true) || + netlink_cmd_check(ctx, ETHTOOL_MSG_WOL_GET, true) || + netlink_cmd_check(ctx, ETHTOOL_MSG_DEBUG_GET, true) || + netlink_cmd_check(ctx, ETHTOOL_MSG_LINKSTATE_GET, true)) + return -EOPNOTSUPP; + nlctx->suppress_nlerr = 1; ret = gset_request(nlsk, ETHTOOL_MSG_LINKMODES_GET, @@ -818,6 +977,14 @@ static const struct lookup_entry_u32 duplex_values[] = { {} }; +static const struct lookup_entry_u8 master_slave_values[] = { + { .arg = "preferred-master", .val = MASTER_SLAVE_CFG_MASTER_PREFERRED }, + { .arg = "preferred-slave", .val = MASTER_SLAVE_CFG_SLAVE_PREFERRED }, + { .arg = "forced-master", .val = MASTER_SLAVE_CFG_MASTER_FORCE }, + { .arg = "forced-slave", .val = MASTER_SLAVE_CFG_SLAVE_FORCE }, + {} +}; + char wol_bit_chars[WOL_MODE_COUNT] = { [WAKE_PHY_BIT] = 'p', [WAKE_UCAST_BIT] = 'u', @@ -909,6 +1076,14 @@ static const struct param_parser sset_params[] = { .min_argc = 1, }, { + .arg = "master-slave", + .group = ETHTOOL_MSG_LINKMODES_SET, + .type = ETHTOOL_A_LINKMODES_MASTER_SLAVE_CFG, + .handler = nl_parse_lookup_u8, + .handler_data = master_slave_values, + .min_argc = 1, + }, + { .arg = "wol", .group = ETHTOOL_MSG_WOL_SET, .type = ETHTOOL_A_WOL_MODES, @@ -935,9 +1110,103 @@ static const struct param_parser sset_params[] = { {} }; +/* Maximum number of request messages sent to kernel; must be equal to the + * number of different .group values in sset_params[] array. + */ +#define SSET_MAX_MSGS 4 + +static int linkmodes_reply_advert_all_cb(const struct nlmsghdr *nlhdr, + void *data) +{ + const struct nlattr *tb[ETHTOOL_A_LINKMODES_MAX + 1] = {}; + DECLARE_ATTR_TB_INFO(tb); + struct nl_msg_buff *req_msgbuff = data; + const struct nlattr *ours_attr; + struct nlattr *req_bitset; + uint32_t *supported_modes; + unsigned int modes_count; + unsigned int i; + int ret; + + ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info); + if (ret < 0) + return MNL_CB_ERROR; + ours_attr = tb[ETHTOOL_A_LINKMODES_OURS]; + if (!ours_attr) + return MNL_CB_ERROR; + modes_count = bitset_get_count(tb[ETHTOOL_A_LINKMODES_OURS], &ret); + if (ret < 0) + return MNL_CB_ERROR; + supported_modes = get_compact_bitset_mask(tb[ETHTOOL_A_LINKMODES_OURS]); + if (!supported_modes) + return MNL_CB_ERROR; + + /* keep only "real" link modes */ + for (i = 0; i < modes_count; i++) + if (!lm_class_match(i, LM_CLASS_REAL)) + supported_modes[i / 32] &= ~((uint32_t)1 << (i % 32)); + + req_bitset = ethnla_nest_start(req_msgbuff, ETHTOOL_A_LINKMODES_OURS); + if (!req_bitset) + return MNL_CB_ERROR; + + if (ethnla_put_u32(req_msgbuff, ETHTOOL_A_BITSET_SIZE, modes_count) || + ethnla_put(req_msgbuff, ETHTOOL_A_BITSET_VALUE, + DIV_ROUND_UP(modes_count, 32) * sizeof(uint32_t), + supported_modes) || + ethnla_put(req_msgbuff, ETHTOOL_A_BITSET_MASK, + DIV_ROUND_UP(modes_count, 32) * sizeof(uint32_t), + supported_modes)) { + ethnla_nest_cancel(req_msgbuff, req_bitset); + return MNL_CB_ERROR; + } + + ethnla_nest_end(req_msgbuff, req_bitset); + return MNL_CB_OK; +} + +/* For compatibility reasons with ioctl-based ethtool, when "autoneg on" is + * specified without "advertise", "speed" and "duplex", we need to query the + * supported link modes from the kernel and advertise all the "real" ones. + */ +static int nl_sset_compat_linkmodes(struct nl_context *nlctx, + struct nl_msg_buff *msgbuff) +{ + const struct nlattr *tb[ETHTOOL_A_LINKMODES_MAX + 1] = {}; + DECLARE_ATTR_TB_INFO(tb); + struct nl_socket *nlsk = nlctx->ethnl_socket; + int ret; + + ret = mnl_attr_parse(msgbuff->nlhdr, GENL_HDRLEN, attr_cb, &tb_info); + if (ret < 0) + return ret; + if (!tb[ETHTOOL_A_LINKMODES_AUTONEG] || tb[ETHTOOL_A_LINKMODES_OURS] || + tb[ETHTOOL_A_LINKMODES_SPEED] || tb[ETHTOOL_A_LINKMODES_DUPLEX]) + return 0; + if (!mnl_attr_get_u8(tb[ETHTOOL_A_LINKMODES_AUTONEG])) + return 0; + + /* all conditions satisfied, create ETHTOOL_A_LINKMODES_OURS */ + if (netlink_cmd_check(nlctx->ctx, ETHTOOL_MSG_LINKMODES_GET, false) || + netlink_cmd_check(nlctx->ctx, ETHTOOL_MSG_LINKMODES_SET, false)) + return -EOPNOTSUPP; + ret = nlsock_prep_get_request(nlsk, ETHTOOL_MSG_LINKMODES_GET, + ETHTOOL_A_LINKMODES_HEADER, + ETHTOOL_FLAG_COMPACT_BITSETS); + if (ret < 0) + return ret; + ret = nlsock_sendmsg(nlsk, NULL); + if (ret < 0) + return ret; + return nlsock_process_reply(nlsk, linkmodes_reply_advert_all_cb, + msgbuff); +} + int nl_sset(struct cmd_context *ctx) { + struct nl_msg_buff *msgbuffs[SSET_MAX_MSGS] = {}; struct nl_context *nlctx = ctx->nlctx; + unsigned int i; int ret; nlctx->cmd = "-s"; @@ -945,11 +1214,34 @@ int nl_sset(struct cmd_context *ctx) nlctx->argc = ctx->argc; nlctx->devname = ctx->devname; - ret = nl_parser(nlctx, sset_params, NULL, PARSER_GROUP_MSG); - if (ret < 0) - return 1; + ret = nl_parser(nlctx, sset_params, NULL, PARSER_GROUP_MSG, msgbuffs); + if (ret < 0) { + ret = 1; + goto out_free; + } - if (ret == 0) - return 0; + for (i = 0; i < SSET_MAX_MSGS && msgbuffs[i]; i++) { + struct nl_socket *nlsk = nlctx->ethnl_socket; + + if (msgbuffs[i]->genlhdr->cmd == ETHTOOL_MSG_LINKMODES_SET) { + ret = nl_sset_compat_linkmodes(nlctx, msgbuffs[i]); + if (ret < 0) + goto out_free; + } + ret = nlsock_sendmsg(nlsk, msgbuffs[i]); + if (ret < 0) + goto out_free; + ret = nlsock_process_reply(nlsk, nomsg_reply_cb, NULL); + if (ret < 0) + goto out_free; + } + +out_free: + for (i = 0; i < SSET_MAX_MSGS && msgbuffs[i]; i++) { + msgbuff_done(msgbuffs[i]); + free(msgbuffs[i]); + } + if (ret >= 0) + return ret; return nlctx->exit_code ?: 75; } diff --git a/netlink/tsinfo.c b/netlink/tsinfo.c new file mode 100644 index 0000000..c6571ff --- /dev/null +++ b/netlink/tsinfo.c @@ -0,0 +1,124 @@ +/* + * tsinfo.c - netlink implementation of timestamping commands + * + * Implementation of "ethtool -T <dev>" + */ + +#include <errno.h> +#include <string.h> +#include <stdio.h> + +#include "../internal.h" +#include "../common.h" +#include "netlink.h" +#include "bitset.h" + +/* TSINFO_GET */ + +static void tsinfo_dump_cb(unsigned int idx, const char *name, bool val, + void *data __maybe_unused) +{ + if (!val) + return; + + if (name) + printf("\t%s\n", name); + else + printf("\tbit%u\n", idx); +} + +static int tsinfo_dump_list(struct nl_context *nlctx, const struct nlattr *attr, + const char *label, const char *if_empty, + unsigned int stringset_id) +{ + const struct stringset *strings = NULL; + int ret; + + printf("%s:", label); + ret = 0; + if (!attr || bitset_is_empty(attr, false, &ret)) { + printf("%s\n", if_empty); + return ret; + } + putchar('\n'); + if (ret < 0) + return ret; + + if (bitset_is_compact(attr)) { + ret = netlink_init_ethnl2_socket(nlctx); + if (ret < 0) + return ret; + strings = global_stringset(stringset_id, nlctx->ethnl2_socket); + } + return walk_bitset(attr, strings, tsinfo_dump_cb, NULL); +} + +int tsinfo_reply_cb(const struct nlmsghdr *nlhdr, void *data) +{ + const struct nlattr *tb[ETHTOOL_A_TSINFO_MAX + 1] = {}; + DECLARE_ATTR_TB_INFO(tb); + struct nl_context *nlctx = data; + bool silent; + int err_ret; + int ret; + + silent = nlctx->is_dump; + err_ret = silent ? MNL_CB_OK : MNL_CB_ERROR; + ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info); + if (ret < 0) + return err_ret; + nlctx->devname = get_dev_name(tb[ETHTOOL_A_TSINFO_HEADER]); + if (!dev_ok(nlctx)) + return err_ret; + + if (silent) + putchar('\n'); + printf("Time stamping parameters for %s:\n", nlctx->devname); + + ret = tsinfo_dump_list(nlctx, tb[ETHTOOL_A_TSINFO_TIMESTAMPING], + "Capabilities", "", ETH_SS_SOF_TIMESTAMPING); + if (ret < 0) + return err_ret; + + printf("PTP Hardware Clock: "); + if (tb[ETHTOOL_A_TSINFO_PHC_INDEX]) + printf("%d\n", + mnl_attr_get_u32(tb[ETHTOOL_A_TSINFO_PHC_INDEX])); + else + printf("none\n"); + + ret = tsinfo_dump_list(nlctx, tb[ETHTOOL_A_TSINFO_TX_TYPES], + "Hardware Transmit Timestamp Modes", " none", + ETH_SS_TS_TX_TYPES); + if (ret < 0) + return err_ret; + + ret = tsinfo_dump_list(nlctx, tb[ETHTOOL_A_TSINFO_RX_FILTERS], + "Hardware Receive Filter Modes", " none", + ETH_SS_TS_RX_FILTERS); + if (ret < 0) + return err_ret; + + return MNL_CB_OK; +} + +int nl_tsinfo(struct cmd_context *ctx) +{ + struct nl_context *nlctx = ctx->nlctx; + struct nl_socket *nlsk = nlctx->ethnl_socket; + int ret; + + if (netlink_cmd_check(ctx, ETHTOOL_MSG_TSINFO_GET, true)) + return -EOPNOTSUPP; + if (ctx->argc > 0) { + fprintf(stderr, "ethtool: unexpected parameter '%s'\n", + *ctx->argp); + return 1; + } + + ret = nlsock_prep_get_request(nlsk, ETHTOOL_MSG_TSINFO_GET, + ETHTOOL_A_TSINFO_HEADER, 0); + if (ret < 0) + return ret; + return nlsock_send_get_request(nlsk, tsinfo_reply_cb); +} diff --git a/netlink/tunnels.c b/netlink/tunnels.c new file mode 100644 index 0000000..b464046 --- /dev/null +++ b/netlink/tunnels.c @@ -0,0 +1,236 @@ +/* + * tunnel.c - device tunnel information + * + * Implementation of "ethtool --show-tunnels <dev>" + */ + +#include <errno.h> +#include <string.h> +#include <stdio.h> +#include <arpa/inet.h> + +#include "../common.h" +#include "../internal.h" + +#include "bitset.h" +#include "netlink.h" +#include "strset.h" + +/* TUNNEL_INFO_GET */ + +static int +tunnel_info_strings_load(struct nl_context *nlctx, + const struct stringset **strings) +{ + int ret; + + if (*strings) + return 0; + ret = netlink_init_ethnl2_socket(nlctx); + if (ret < 0) + return ret; + *strings = global_stringset(ETH_SS_UDP_TUNNEL_TYPES, + nlctx->ethnl2_socket); + return 0; +} + +static int +tunnel_info_dump_udp_entry(struct nl_context *nlctx, const struct nlattr *entry, + const struct stringset **strings) +{ + const struct nlattr *entry_tb[ETHTOOL_A_TUNNEL_UDP_ENTRY_MAX + 1] = {}; + DECLARE_ATTR_TB_INFO(entry_tb); + const struct nlattr *attr; + unsigned int port, type; + int ret; + + if (tunnel_info_strings_load(nlctx, strings)) + return 1; + + ret = mnl_attr_parse_nested(entry, attr_cb, &entry_tb_info); + if (ret < 0) { + fprintf(stderr, "malformed netlink message (udp entry)\n"); + return 1; + } + + attr = entry_tb[ETHTOOL_A_TUNNEL_UDP_ENTRY_PORT]; + if (!attr || mnl_attr_validate(attr, MNL_TYPE_U16) < 0) { + fprintf(stderr, "malformed netlink message (port)\n"); + return 1; + } + port = ntohs(mnl_attr_get_u16(attr)); + + attr = entry_tb[ETHTOOL_A_TUNNEL_UDP_ENTRY_TYPE]; + if (!attr || mnl_attr_validate(attr, MNL_TYPE_U32) < 0) { + fprintf(stderr, "malformed netlink message (tunnel type)\n"); + return 1; + } + type = mnl_attr_get_u32(attr); + + printf(" port %d, %s\n", port, get_string(*strings, type)); + + return 0; +} + +static void tunnel_info_dump_cb(unsigned int idx, const char *name, bool val, + void *data) +{ + bool *first = data; + + if (!val) + return; + + if (!*first) + putchar(','); + *first = false; + + if (name) + printf(" %s", name); + else + printf(" bit%u", idx); +} + +static int +tunnel_info_dump_list(struct nl_context *nlctx, const struct nlattr *attr, + const struct stringset **strings) +{ + bool first = true; + int ret; + + if (bitset_is_empty(attr, false, &ret) || ret) { + if (ret) + return 1; + + printf(" Types: none (static entries)\n"); + return 0; + } + + if (bitset_is_compact(attr) && + tunnel_info_strings_load(nlctx, strings)) + return 1; + + printf(" Types:"); + ret = walk_bitset(attr, *strings, tunnel_info_dump_cb, &first); + putchar('\n'); + return ret; +} + +static int +tunnel_info_dump_udp_table(const struct nlattr *table, struct nl_context *nlctx, + const struct stringset **strings) +{ + const struct nlattr *table_tb[ETHTOOL_A_TUNNEL_UDP_TABLE_MAX + 1] = {}; + DECLARE_ATTR_TB_INFO(table_tb); + const struct nlattr *attr; + int i, ret; + + ret = mnl_attr_parse_nested(table, attr_cb, &table_tb_info); + if (ret < 0) { + fprintf(stderr, "malformed netlink message (udp table)\n"); + return 1; + } + + attr = table_tb[ETHTOOL_A_TUNNEL_UDP_TABLE_SIZE]; + if (!attr || mnl_attr_validate(attr, MNL_TYPE_U32) < 0) { + fprintf(stderr, "malformed netlink message (table size)\n"); + return 1; + } + printf(" Size: %d\n", mnl_attr_get_u32(attr)); + + attr = table_tb[ETHTOOL_A_TUNNEL_UDP_TABLE_TYPES]; + if (!attr || tunnel_info_dump_list(nlctx, attr, strings)) { + fprintf(stderr, "malformed netlink message (types)\n"); + return 1; + } + + if (!table_tb[ETHTOOL_A_TUNNEL_UDP_TABLE_ENTRY]) { + printf(" No entries\n"); + return 0; + } + + i = 0; + mnl_attr_for_each_nested(attr, table) { + if (mnl_attr_get_type(attr) == ETHTOOL_A_TUNNEL_UDP_TABLE_ENTRY) + i++; + } + + printf(" Entries (%d):\n", i++); + mnl_attr_for_each_nested(attr, table) { + if (mnl_attr_get_type(attr) == ETHTOOL_A_TUNNEL_UDP_TABLE_ENTRY) + if (tunnel_info_dump_udp_entry(nlctx, attr, strings)) + return 1; + } + + return 0; +} + +static int +tunnel_info_dump_udp(const struct nlattr *udp_ports, struct nl_context *nlctx) +{ + const struct stringset *strings = NULL; + const struct nlattr *table; + int i, err; + + i = 0; + mnl_attr_for_each_nested(table, udp_ports) { + if (mnl_attr_get_type(table) != ETHTOOL_A_TUNNEL_UDP_TABLE) + continue; + + printf(" UDP port table %d: \n", i++); + err = tunnel_info_dump_udp_table(table, nlctx, &strings); + if (err) + return err; + } + + return 0; +} + +static int tunnel_info_reply_cb(const struct nlmsghdr *nlhdr, void *data) +{ + const struct nlattr *tb[ETHTOOL_A_TUNNEL_INFO_MAX + 1] = {}; + DECLARE_ATTR_TB_INFO(tb); + const struct nlattr *udp_ports; + struct nl_context *nlctx = data; + bool silent; + int err_ret; + int ret; + + silent = nlctx->is_dump; + err_ret = silent ? MNL_CB_OK : MNL_CB_ERROR; + ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info); + if (ret < 0) + return err_ret; + nlctx->devname = get_dev_name(tb[ETHTOOL_A_TUNNEL_INFO_HEADER]); + if (!dev_ok(nlctx)) + return err_ret; + + printf("Tunnel information for %s:\n", nlctx->devname); + + udp_ports = tb[ETHTOOL_A_TUNNEL_INFO_UDP_PORTS]; + if (udp_ports) + if (tunnel_info_dump_udp(udp_ports, nlctx)) + return err_ret; + + return MNL_CB_OK; +} + +int nl_gtunnels(struct cmd_context *ctx) +{ + struct nl_context *nlctx = ctx->nlctx; + struct nl_socket *nlsk = nlctx->ethnl_socket; + int ret; + + if (netlink_cmd_check(ctx, ETHTOOL_MSG_TUNNEL_INFO_GET, true)) + return -EOPNOTSUPP; + if (ctx->argc > 0) { + fprintf(stderr, "ethtool: unexpected parameter '%s'\n", + *ctx->argp); + return 1; + } + + ret = nlsock_prep_get_request(nlsk, ETHTOOL_MSG_TUNNEL_INFO_GET, + ETHTOOL_A_TUNNEL_INFO_HEADER, 0); + if (ret < 0) + return ret; + return nlsock_send_get_request(nlsk, tunnel_info_reply_cb); +} diff --git a/qsfp-dd.c b/qsfp-dd.c new file mode 100644 index 0000000..900bbb5 --- /dev/null +++ b/qsfp-dd.c @@ -0,0 +1,333 @@ +/** + * Description: + * + * This module adds QSFP-DD support to ethtool. The changes are similar to + * the ones already existing in qsfp.c, but customized to use the memory + * addresses and logic as defined in the specification's document. + * + */ + +#include <stdio.h> +#include <math.h> +#include "internal.h" +#include "sff-common.h" +#include "qsfp-dd.h" + +static void qsfp_dd_show_identifier(const __u8 *id) +{ + sff8024_show_identifier(id, QSFP_DD_ID_OFFSET); +} + +static void qsfp_dd_show_connector(const __u8 *id) +{ + sff8024_show_connector(id, QSFP_DD_CTOR_OFFSET); +} + +static void qsfp_dd_show_oui(const __u8 *id) +{ + sff8024_show_oui(id, QSFP_DD_VENDOR_OUI_OFFSET); +} + +/** + * Print the revision compliance. Relevant documents: + * [1] CMIS Rev. 3, pag. 45, section 1.7.2.1, Table 18 + * [2] CMIS Rev. 4, pag. 81, section 8.2.1, Table 8-2 + */ +static void qsfp_dd_show_rev_compliance(const __u8 *id) +{ + __u8 rev = id[QSFP_DD_REV_COMPLIANCE_OFFSET]; + int major = (rev >> 4) & 0x0F; + int minor = rev & 0x0F; + + printf("\t%-41s : Rev. %d.%d\n", "Revision compliance", major, minor); +} + +/** + * Print information about the device's power consumption. + * Relevant documents: + * [1] CMIS Rev. 3, pag. 59, section 1.7.3.9, Table 30 + * [2] CMIS Rev. 4, pag. 94, section 8.3.9, Table 8-18 + * [3] QSFP-DD Hardware Rev 5.0, pag. 22, section 4.2.1 + */ +static void qsfp_dd_show_power_info(const __u8 *id) +{ + float max_power = 0.0f; + __u8 base_power = 0; + __u8 power_class; + + /* Get the power class (first 3 most significat bytes) */ + power_class = (id[QSFP_DD_PWR_CLASS_OFFSET] >> 5) & 0x07; + + /* Get the base power in multiples of 0.25W */ + base_power = id[QSFP_DD_PWR_MAX_POWER_OFFSET]; + max_power = base_power * 0.25f; + + printf("\t%-41s : %d\n", "Power class", power_class + 1); + printf("\t%-41s : %.02fW\n", "Max power", max_power); +} + +/** + * Print the cable assembly length, for both passive copper and active + * optical or electrical cables. The base length (bits 5-0) must be + * multiplied with the SMF length multiplier (bits 7-6) to obtain the + * correct value. Relevant documents: + * [1] CMIS Rev. 3, pag. 59, section 1.7.3.10, Table 31 + * [2] CMIS Rev. 4, pag. 94, section 8.3.10, Table 8-19 + */ +static void qsfp_dd_show_cbl_asm_len(const __u8 *id) +{ + static const char *fn = "Cable assembly length"; + float mul = 1.0f; + float val = 0.0f; + + /* Check if max length */ + if (id[QSFP_DD_CBL_ASM_LEN_OFFSET] == QSFP_DD_6300M_MAX_LEN) { + printf("\t%-41s : > 6.3km\n", fn); + return; + } + + /* Get the multiplier from the first two bits */ + switch (id[QSFP_DD_CBL_ASM_LEN_OFFSET] & QSFP_DD_LEN_MUL_MASK) { + case QSFP_DD_MULTIPLIER_00: + mul = 0.1f; + break; + case QSFP_DD_MULTIPLIER_01: + mul = 1.0f; + break; + case QSFP_DD_MULTIPLIER_10: + mul = 10.0f; + break; + case QSFP_DD_MULTIPLIER_11: + mul = 100.0f; + break; + default: + break; + } + + /* Get base value from first 6 bits and multiply by mul */ + val = (id[QSFP_DD_CBL_ASM_LEN_OFFSET] & QSFP_DD_LEN_VAL_MASK); + val = (float)val * mul; + printf("\t%-41s : %0.2fm\n", fn, val); +} + +/** + * Print the length for SMF fiber. The base length (bits 5-0) must be + * multiplied with the SMF length multiplier (bits 7-6) to obtain the + * correct value. Relevant documents: + * [1] CMIS Rev. 3, pag. 63, section 1.7.4.2, Table 39 + * [2] CMIS Rev. 4, pag. 99, section 8.4.2, Table 8-27 + */ +static void qsfp_dd_print_smf_cbl_len(const __u8 *id) +{ + static const char *fn = "Length (SMF)"; + float mul = 1.0f; + float val = 0.0f; + + /* Get the multiplier from the first two bits */ + switch (id[QSFP_DD_SMF_LEN_OFFSET] & QSFP_DD_LEN_MUL_MASK) { + case QSFP_DD_MULTIPLIER_00: + mul = 0.1f; + break; + case QSFP_DD_MULTIPLIER_01: + mul = 1.0f; + break; + default: + break; + } + + /* Get base value from first 6 bits and multiply by mul */ + val = (id[QSFP_DD_SMF_LEN_OFFSET] & QSFP_DD_LEN_VAL_MASK); + val = (float)val * mul; + printf("\t%-41s : %0.2fkm\n", fn, val); +} + +/** + * Print relevant signal integrity control properties. Relevant documents: + * [1] CMIS Rev. 3, pag. 71, section 1.7.4.10, Table 46 + * [2] CMIS Rev. 4, pag. 105, section 8.4.10, Table 8-34 + */ +static void qsfp_dd_show_sig_integrity(const __u8 *id) +{ + /* CDR Bypass control: 2nd bit from each byte */ + printf("\t%-41s : ", "Tx CDR bypass control"); + printf("%s\n", YESNO(id[QSFP_DD_SIG_INTEG_TX_OFFSET] & 0x02)); + + printf("\t%-41s : ", "Rx CDR bypass control"); + printf("%s\n", YESNO(id[QSFP_DD_SIG_INTEG_RX_OFFSET] & 0x02)); + + /* CDR Implementation: 1st bit from each byte */ + printf("\t%-41s : ", "Tx CDR"); + printf("%s\n", YESNO(id[QSFP_DD_SIG_INTEG_TX_OFFSET] & 0x01)); + + printf("\t%-41s : ", "Rx CDR"); + printf("%s\n", YESNO(id[QSFP_DD_SIG_INTEG_RX_OFFSET] & 0x01)); +} + +/** + * Print relevant media interface technology info. Relevant documents: + * [1] CMIS Rev. 3 + * --> pag. 61, section 1.7.3.14, Table 36 + * --> pag. 64, section 1.7.4.3, 1.7.4.4 + * [2] CMIS Rev. 4 + * --> pag. 97, section 8.3.14, Table 8-24 + * --> pag. 98, section 8.4, Table 8-25 + * --> page 100, section 8.4.3, 8.4.4 + */ +static void qsfp_dd_show_mit_compliance(const __u8 *id) +{ + static const char *cc = " (Copper cable,"; + + printf("\t%-41s : 0x%02x", "Transmitter technology", + id[QSFP_DD_MEDIA_INTF_TECH_OFFSET]); + + switch (id[QSFP_DD_MEDIA_INTF_TECH_OFFSET]) { + case QSFP_DD_850_VCSEL: + printf(" (850 nm VCSEL)\n"); + break; + case QSFP_DD_1310_VCSEL: + printf(" (1310 nm VCSEL)\n"); + break; + case QSFP_DD_1550_VCSEL: + printf(" (1550 nm VCSEL)\n"); + break; + case QSFP_DD_1310_FP: + printf(" (1310 nm FP)\n"); + break; + case QSFP_DD_1310_DFB: + printf(" (1310 nm DFB)\n"); + break; + case QSFP_DD_1550_DFB: + printf(" (1550 nm DFB)\n"); + break; + case QSFP_DD_1310_EML: + printf(" (1310 nm EML)\n"); + break; + case QSFP_DD_1550_EML: + printf(" (1550 nm EML)\n"); + break; + case QSFP_DD_OTHERS: + printf(" (Others/Undefined)\n"); + break; + case QSFP_DD_1490_DFB: + printf(" (1490 nm DFB)\n"); + break; + case QSFP_DD_COPPER_UNEQUAL: + printf("%s unequalized)\n", cc); + break; + case QSFP_DD_COPPER_PASS_EQUAL: + printf("%s passive equalized)\n", cc); + break; + case QSFP_DD_COPPER_NF_EQUAL: + printf("%s near and far end limiting active equalizers)\n", cc); + break; + case QSFP_DD_COPPER_F_EQUAL: + printf("%s far end limiting active equalizers)\n", cc); + break; + case QSFP_DD_COPPER_N_EQUAL: + printf("%s near end limiting active equalizers)\n", cc); + break; + case QSFP_DD_COPPER_LINEAR_EQUAL: + printf("%s linear active equalizers)\n", cc); + break; + } + + if (id[QSFP_DD_MEDIA_INTF_TECH_OFFSET] >= QSFP_DD_COPPER_UNEQUAL) { + printf("\t%-41s : %udb\n", "Attenuation at 5GHz", + id[QSFP_DD_COPPER_ATT_5GHZ]); + printf("\t%-41s : %udb\n", "Attenuation at 7GHz", + id[QSFP_DD_COPPER_ATT_7GHZ]); + printf("\t%-41s : %udb\n", "Attenuation at 12.9GHz", + id[QSFP_DD_COPPER_ATT_12P9GHZ]); + printf("\t%-41s : %udb\n", "Attenuation at 25.8GHz", + id[QSFP_DD_COPPER_ATT_25P8GHZ]); + } else { + printf("\t%-41s : %.3lfnm\n", "Laser wavelength", + (((id[QSFP_DD_NOM_WAVELENGTH_MSB] << 8) | + id[QSFP_DD_NOM_WAVELENGTH_LSB]) * 0.05)); + printf("\t%-41s : %.3lfnm\n", "Laser wavelength tolerance", + (((id[QSFP_DD_WAVELENGTH_TOL_MSB] << 8) | + id[QSFP_DD_WAVELENGTH_TOL_LSB]) * 0.005)); + } +} + +/* + * 2-byte internal temperature conversions: + * First byte is a signed 8-bit integer, which is the temp decimal part + * Second byte is a multiple of 1/256th of a degree, which is added to + * the dec part. + */ +#define OFFSET_TO_TEMP(offset) ((__s16)OFFSET_TO_U16(offset)) + +/** + * Print relevant module level monitoring values. Relevant documents: + * [1] CMIS Rev. 3: + * --> pag. 50, section 1.7.2.4, Table 22 + * + * [2] CMIS Rev. 4: + * --> pag. 84, section 8.2.4, Table 8-6 + */ +static void qsfp_dd_show_mod_lvl_monitors(const __u8 *id) +{ + PRINT_TEMP("Module temperature", + OFFSET_TO_TEMP(QSFP_DD_CURR_TEMP_OFFSET)); + PRINT_VCC("Module voltage", + OFFSET_TO_U16(QSFP_DD_CURR_CURR_OFFSET)); +} + +/** + * Print relevant info about the maximum supported fiber media length + * for each type of fiber media at the maximum module-supported bit rate. + * Relevant documents: + * [1] CMIS Rev. 3, page 64, section 1.7.4.2, Table 39 + * [2] CMIS Rev. 4, page 99, section 8.4.2, Table 8-27 + */ +static void qsfp_dd_show_link_len(const __u8 *id) +{ + qsfp_dd_print_smf_cbl_len(id); + sff_show_value_with_unit(id, QSFP_DD_OM5_LEN_OFFSET, + "Length (OM5)", 2, "m"); + sff_show_value_with_unit(id, QSFP_DD_OM4_LEN_OFFSET, + "Length (OM4)", 2, "m"); + sff_show_value_with_unit(id, QSFP_DD_OM3_LEN_OFFSET, + "Length (OM3 50/125um)", 2, "m"); + sff_show_value_with_unit(id, QSFP_DD_OM2_LEN_OFFSET, + "Length (OM2 50/125um)", 1, "m"); +} + +/** + * Show relevant information about the vendor. Relevant documents: + * [1] CMIS Rev. 3, page 56, section 1.7.3, Table 27 + * [2] CMIS Rev. 4, page 91, section 8.2, Table 8-15 + */ +static void qsfp_dd_show_vendor_info(const __u8 *id) +{ + sff_show_ascii(id, QSFP_DD_VENDOR_NAME_START_OFFSET, + QSFP_DD_VENDOR_NAME_END_OFFSET, "Vendor name"); + qsfp_dd_show_oui(id); + sff_show_ascii(id, QSFP_DD_VENDOR_PN_START_OFFSET, + QSFP_DD_VENDOR_PN_END_OFFSET, "Vendor PN"); + sff_show_ascii(id, QSFP_DD_VENDOR_REV_START_OFFSET, + QSFP_DD_VENDOR_REV_END_OFFSET, "Vendor rev"); + sff_show_ascii(id, QSFP_DD_VENDOR_SN_START_OFFSET, + QSFP_DD_VENDOR_SN_END_OFFSET, "Vendor SN"); + sff_show_ascii(id, QSFP_DD_DATE_YEAR_OFFSET, + QSFP_DD_DATE_VENDOR_LOT_OFFSET + 1, "Date code"); + + if (id[QSFP_DD_CLEI_PRESENT_BYTE] & QSFP_DD_CLEI_PRESENT_MASK) + sff_show_ascii(id, QSFP_DD_CLEI_START_OFFSET, + QSFP_DD_CLEI_END_OFFSET, "CLEI code"); +} + +void qsfp_dd_show_all(const __u8 *id) +{ + qsfp_dd_show_identifier(id); + qsfp_dd_show_power_info(id); + qsfp_dd_show_connector(id); + qsfp_dd_show_cbl_asm_len(id); + qsfp_dd_show_sig_integrity(id); + qsfp_dd_show_mit_compliance(id); + qsfp_dd_show_mod_lvl_monitors(id); + qsfp_dd_show_link_len(id); + qsfp_dd_show_vendor_info(id); + qsfp_dd_show_rev_compliance(id); +} diff --git a/qsfp-dd.h b/qsfp-dd.h new file mode 100644 index 0000000..f589c4e --- /dev/null +++ b/qsfp-dd.h @@ -0,0 +1,125 @@ +#ifndef QSFP_DD_H__ +#define QSFP_DD_H__ + +/* Identifier and revision compliance (Page 0) */ +#define QSFP_DD_ID_OFFSET 0x00 +#define QSFP_DD_REV_COMPLIANCE_OFFSET 0x01 + +#define QSFP_DD_MODULE_TYPE_OFFSET 0x55 +#define QSFP_DD_MT_MMF 0x01 +#define QSFP_DD_MT_SMF 0x02 + +/* Module-Level Monitors (Page 0) */ +#define QSFP_DD_CURR_TEMP_OFFSET 0x0E +#define QSFP_DD_CURR_CURR_OFFSET 0x10 + +#define QSFP_DD_CTOR_OFFSET 0xCB + +/* Vendor related information (Page 0) */ +#define QSFP_DD_VENDOR_NAME_START_OFFSET 0x81 +#define QSFP_DD_VENDOR_NAME_END_OFFSET 0x90 + +#define QSFP_DD_VENDOR_OUI_OFFSET 0x91 + +#define QSFP_DD_VENDOR_PN_START_OFFSET 0x94 +#define QSFP_DD_VENDOR_PN_END_OFFSET 0xA3 + +#define QSFP_DD_VENDOR_REV_START_OFFSET 0xA4 +#define QSFP_DD_VENDOR_REV_END_OFFSET 0xA5 + +#define QSFP_DD_VENDOR_SN_START_OFFSET 0xA6 +#define QSFP_DD_VENDOR_SN_END_OFFSET 0xB5 + +#define QSFP_DD_DATE_YEAR_OFFSET 0xB6 +#define QSFP_DD_DATE_VENDOR_LOT_OFFSET 0xBC + +/* CLEI Code (Page 0) */ +#define QSFP_DD_CLEI_PRESENT_BYTE 0x02 +#define QSFP_DD_CLEI_PRESENT_MASK 0x20 +#define QSFP_DD_CLEI_START_OFFSET 0xBE +#define QSFP_DD_CLEI_END_OFFSET 0xC7 + +/* Cable assembly length */ +#define QSFP_DD_CBL_ASM_LEN_OFFSET 0xCA +#define QSFP_DD_6300M_MAX_LEN 0xFF + +/* Cable length with multiplier */ +#define QSFP_DD_MULTIPLIER_00 0x00 +#define QSFP_DD_MULTIPLIER_01 0x40 +#define QSFP_DD_MULTIPLIER_10 0x80 +#define QSFP_DD_MULTIPLIER_11 0xC0 +#define QSFP_DD_LEN_MUL_MASK 0xC0 +#define QSFP_DD_LEN_VAL_MASK 0x3F + +/* Module power characteristics */ +#define QSFP_DD_PWR_CLASS_OFFSET 0xC8 +#define QSFP_DD_PWR_MAX_POWER_OFFSET 0xC9 +#define QSFP_DD_PWR_CLASS_MASK 0xE0 +#define QSFP_DD_PWR_CLASS_1 0x00 +#define QSFP_DD_PWR_CLASS_2 0x01 +#define QSFP_DD_PWR_CLASS_3 0x02 +#define QSFP_DD_PWR_CLASS_4 0x03 +#define QSFP_DD_PWR_CLASS_5 0x04 +#define QSFP_DD_PWR_CLASS_6 0x05 +#define QSFP_DD_PWR_CLASS_7 0x06 +#define QSFP_DD_PWR_CLASS_8 0x07 + +/* Copper cable attenuation */ +#define QSFP_DD_COPPER_ATT_5GHZ 0xCC +#define QSFP_DD_COPPER_ATT_7GHZ 0xCD +#define QSFP_DD_COPPER_ATT_12P9GHZ 0xCE +#define QSFP_DD_COPPER_ATT_25P8GHZ 0xCF + +/* Cable assembly lane */ +#define QSFP_DD_CABLE_ASM_NEAR_END_OFFSET 0xD2 +#define QSFP_DD_CABLE_ASM_FAR_END_OFFSET 0xD3 + +/* Media interface technology */ +#define QSFP_DD_MEDIA_INTF_TECH_OFFSET 0xD4 +#define QSFP_DD_850_VCSEL 0x00 +#define QSFP_DD_1310_VCSEL 0x01 +#define QSFP_DD_1550_VCSEL 0x02 +#define QSFP_DD_1310_FP 0x03 +#define QSFP_DD_1310_DFB 0x04 +#define QSFP_DD_1550_DFB 0x05 +#define QSFP_DD_1310_EML 0x06 +#define QSFP_DD_1550_EML 0x07 +#define QSFP_DD_OTHERS 0x08 +#define QSFP_DD_1490_DFB 0x09 +#define QSFP_DD_COPPER_UNEQUAL 0x0A +#define QSFP_DD_COPPER_PASS_EQUAL 0x0B +#define QSFP_DD_COPPER_NF_EQUAL 0x0C +#define QSFP_DD_COPPER_F_EQUAL 0x0D +#define QSFP_DD_COPPER_N_EQUAL 0x0E +#define QSFP_DD_COPPER_LINEAR_EQUAL 0x0F + +/*----------------------------------------------------------------------- + * Upper Memory Page 0x01: contains advertising fields that define properties + * that are unique to active modules and cable assemblies. + * RealOffset = 1 * 0x80 + LocalOffset + */ +#define PAG01H_UPPER_OFFSET (0x01 * 0x80) + +/* Supported Link Length (Page 1) */ +#define QSFP_DD_SMF_LEN_OFFSET (PAG01H_UPPER_OFFSET + 0x84) +#define QSFP_DD_OM5_LEN_OFFSET (PAG01H_UPPER_OFFSET + 0x85) +#define QSFP_DD_OM4_LEN_OFFSET (PAG01H_UPPER_OFFSET + 0x86) +#define QSFP_DD_OM3_LEN_OFFSET (PAG01H_UPPER_OFFSET + 0x87) +#define QSFP_DD_OM2_LEN_OFFSET (PAG01H_UPPER_OFFSET + 0x88) + +/* Wavelength (Page 1) */ +#define QSFP_DD_NOM_WAVELENGTH_MSB (PAG01H_UPPER_OFFSET + 0x8A) +#define QSFP_DD_NOM_WAVELENGTH_LSB (PAG01H_UPPER_OFFSET + 0x8B) +#define QSFP_DD_WAVELENGTH_TOL_MSB (PAG01H_UPPER_OFFSET + 0x8C) +#define QSFP_DD_WAVELENGTH_TOL_LSB (PAG01H_UPPER_OFFSET + 0x8D) + +/* Signal integrity controls */ +#define QSFP_DD_SIG_INTEG_TX_OFFSET (PAG01H_UPPER_OFFSET + 0xA1) +#define QSFP_DD_SIG_INTEG_RX_OFFSET (PAG01H_UPPER_OFFSET + 0xA2) + +#define YESNO(x) (((x) != 0) ? "Yes" : "No") +#define ONOFF(x) (((x) != 0) ? "On" : "Off") + +void qsfp_dd_show_all(const __u8 *id); + +#endif /* QSFP_DD_H__ */ @@ -58,6 +58,7 @@ #include "internal.h" #include "sff-common.h" #include "qsfp.h" +#include "qsfp-dd.h" #define MAX_DESC_SIZE 42 @@ -375,6 +376,107 @@ static void sff8636_show_transceiver(const __u8 *id) printf("%s 100G Ethernet: 100G ACC or 25GAUI C2M ACC with worst BER of 10^(-12)\n", pfx); break; + case SFF8636_ETHERNET_100GE_DWDM2: + printf("%s 100GE-DWDM2 (DWDM transceiver using 2 wavelengths on a 1550 nm DWDM grid with a reach up to 80 km)\n", + pfx); + break; + case SFF8636_ETHERNET_100G_1550NM_WDM: + printf("%s 100G 1550nm WDM (4 wavelengths)\n", pfx); + break; + case SFF8636_ETHERNET_10G_BASET_SR: + printf("%s 10GBASE-T Short Reach (30 meters)\n", pfx); + break; + case SFF8636_ETHERNET_5G_BASET: + printf("%s 5GBASE-T\n", pfx); + break; + case SFF8636_ETHERNET_2HALFG_BASET: + printf("%s 2.5GBASE-T\n", pfx); + break; + case SFF8636_ETHERNET_40G_SWDM4: + printf("%s 40G SWDM4\n", pfx); + break; + case SFF8636_ETHERNET_100G_SWDM4: + printf("%s 100G SWDM4\n", pfx); + break; + case SFF8636_ETHERNET_100G_PAM4_BIDI: + printf("%s 100G PAM4 BiDi\n", pfx); + break; + case SFF8636_ETHERNET_4WDM10_MSA: + printf("%s 4WDM-10 MSA (10km version of 100G CWDM4 with same RS(528,514) FEC in host system)\n", + pfx); + break; + case SFF8636_ETHERNET_4WDM20_MSA: + printf("%s 4WDM-20 MSA (20km version of 100GBASE-LR4 with RS(528,514) FEC in host system)\n", + pfx); + break; + case SFF8636_ETHERNET_4WDM40_MSA: + printf("%s 4WDM-40 MSA (40km reach with APD receiver and RS(528,514) FEC in host system)\n", + pfx); + break; + case SFF8636_ETHERNET_100G_DR: + printf("%s 100GBASE-DR (clause 140), CAUI-4 (no FEC)\n", pfx); + break; + case SFF8636_ETHERNET_100G_FR_NOFEC: + printf("%s 100G-FR or 100GBASE-FR1 (clause 140), CAUI-4 (no FEC)\n", pfx); + break; + case SFF8636_ETHERNET_100G_LR_NOFEC: + printf("%s 100G-LR or 100GBASE-LR1 (clause 140), CAUI-4 (no FEC)\n", pfx); + break; + case SFF8636_ETHERNET_200G_ACC1: + printf("%s Active Copper Cable with 50GAUI, 100GAUI-2 or 200GAUI-4 C2M. Providing a worst BER of 10-6 or below\n", + pfx); + break; + case SFF8636_ETHERNET_200G_AOC1: + printf("%s Active Optical Cable with 50GAUI, 100GAUI-2 or 200GAUI-4 C2M. Providing a worst BER of 10-6 or below\n", + pfx); + break; + case SFF8636_ETHERNET_200G_ACC2: + printf("%s Active Copper Cable with 50GAUI, 100GAUI-2 or 200GAUI-4 C2M. Providing a worst BER of 2.6x10-4 for ACC, 10-5 for AUI, or below\n", + pfx); + break; + case SFF8636_ETHERNET_200G_A0C2: + printf("%s Active Optical Cable with 50GAUI, 100GAUI-2 or 200GAUI-4 C2M. Providing a worst BER of 2.6x10-4 for ACC, 10-5 for AUI, or below\n", + pfx); + break; + case SFF8636_ETHERNET_200G_CR4: + printf("%s 50GBASE-CR, 100GBASE-CR2, or 200GBASE-CR4\n", pfx); + break; + case SFF8636_ETHERNET_200G_SR4: + printf("%s 50GBASE-SR, 100GBASE-SR2, or 200GBASE-SR4\n", pfx); + break; + case SFF8636_ETHERNET_200G_DR4: + printf("%s 50GBASE-FR or 200GBASE-DR4\n", pfx); + break; + case SFF8636_ETHERNET_200G_FR4: + printf("%s 200GBASE-FR4\n", pfx); + break; + case SFF8636_ETHERNET_200G_PSM4: + printf("%s 200G 1550 nm PSM4\n", pfx); + break; + case SFF8636_ETHERNET_50G_LR: + printf("%s 50GBASE-LR\n", pfx); + break; + case SFF8636_ETHERNET_200G_LR4: + printf("%s 200GBASE-LR4\n", pfx); + break; + case SFF8636_ETHERNET_64G_EA: + printf("%s 64GFC EA\n", pfx); + break; + case SFF8636_ETHERNET_64G_SW: + printf("%s 64GFC SW\n", pfx); + break; + case SFF8636_ETHERNET_64G_LW: + printf("%s 64GFC LW\n", pfx); + break; + case SFF8636_ETHERNET_128FC_EA: + printf("%s 128GFC EA\n", pfx); + break; + case SFF8636_ETHERNET_128FC_SW: + printf("%s 128GFC SW\n", pfx); + break; + case SFF8636_ETHERNET_128FC_LW: + printf("%s 128GFC LW\n", pfx); + break; default: printf("%s (reserved or unknown)\n", pfx); break; @@ -478,9 +580,9 @@ static void sff8636_show_rate_identifier(const __u8 *id) id[SFF8636_EXT_RS_OFFSET]); } -static void sff8636_show_oui(const __u8 *id) +static void sff8636_show_oui(const __u8 *id, int id_offset) { - sff8024_show_oui(id, SFF8636_VENDOR_OUI_OFFSET); + sff8024_show_oui(id, id_offset); } static void sff8636_show_wavelength_or_copper_compliance(const __u8 *id) @@ -561,38 +663,7 @@ static void sff8636_show_wavelength_or_copper_compliance(const __u8 *id) static void sff8636_show_revision_compliance(const __u8 *id) { - static const char *pfx = - "\tRevision Compliance :"; - - switch (id[SFF8636_REV_COMPLIANCE_OFFSET]) { - case SFF8636_REV_UNSPECIFIED: - printf("%s Revision not specified\n", pfx); - break; - case SFF8636_REV_8436_48: - printf("%s SFF-8436 Rev 4.8 or earlier\n", pfx); - break; - case SFF8636_REV_8436_8636: - printf("%s SFF-8436 Rev 4.8 or earlier\n", pfx); - break; - case SFF8636_REV_8636_13: - printf("%s SFF-8636 Rev 1.3 or earlier\n", pfx); - break; - case SFF8636_REV_8636_14: - printf("%s SFF-8636 Rev 1.4\n", pfx); - break; - case SFF8636_REV_8636_15: - printf("%s SFF-8636 Rev 1.5\n", pfx); - break; - case SFF8636_REV_8636_20: - printf("%s SFF-8636 Rev 2.0\n", pfx); - break; - case SFF8636_REV_8636_27: - printf("%s SFF-8636 Rev 2.5/2.6/2.7\n", pfx); - break; - default: - printf("%s Unallocated\n", pfx); - break; - } + sff_show_revision_compliance(id, SFF8636_REV_COMPLIANCE_OFFSET); } /* @@ -745,10 +816,15 @@ static void sff8636_show_dom(const __u8 *id, __u32 eeprom_len) sff_show_thresholds(sd); } - } + void sff8636_show_all(const __u8 *id, __u32 eeprom_len) { + if (id[SFF8636_ID_OFFSET] == SFF8024_ID_QSFP_DD) { + qsfp_dd_show_all(id); + return; + } + sff8636_show_identifier(id); if ((id[SFF8636_ID_OFFSET] == SFF8024_ID_QSFP) || (id[SFF8636_ID_OFFSET] == SFF8024_ID_QSFP_PLUS) || @@ -773,7 +849,7 @@ void sff8636_show_all(const __u8 *id, __u32 eeprom_len) sff8636_show_wavelength_or_copper_compliance(id); sff_show_ascii(id, SFF8636_VENDOR_NAME_START_OFFSET, SFF8636_VENDOR_NAME_END_OFFSET, "Vendor name"); - sff8636_show_oui(id); + sff8636_show_oui(id, SFF8636_VENDOR_OUI_OFFSET); sff_show_ascii(id, SFF8636_VENDOR_PN_START_OFFSET, SFF8636_VENDOR_PN_END_OFFSET, "Vendor PN"); sff_show_ascii(id, SFF8636_VENDOR_REV_START_OFFSET, @@ -31,14 +31,6 @@ #define SFF8636_ID_OFFSET 0x00 #define SFF8636_REV_COMPLIANCE_OFFSET 0x01 -#define SFF8636_REV_UNSPECIFIED 0x00 -#define SFF8636_REV_8436_48 0x01 -#define SFF8636_REV_8436_8636 0x02 -#define SFF8636_REV_8636_13 0x03 -#define SFF8636_REV_8636_14 0x04 -#define SFF8636_REV_8636_15 0x05 -#define SFF8636_REV_8636_20 0x06 -#define SFF8636_REV_8636_27 0x07 #define SFF8636_STATUS_2_OFFSET 0x02 /* Flat Memory:0- Paging, 1- Page 0 only */ @@ -496,6 +488,42 @@ #define SFF8636_ETHERNET_100G_AOC2 0x18 #define SFF8636_ETHERNET_100G_ACC2 0x19 +#define SFF8636_ETHERNET_100GE_DWDM2 0x1A +#define SFF8636_ETHERNET_100G_1550NM_WDM 0x1B +#define SFF8636_ETHERNET_10G_BASET_SR 0x1C +#define SFF8636_ETHERNET_5G_BASET 0x1D +#define SFF8636_ETHERNET_2HALFG_BASET 0x1E +#define SFF8636_ETHERNET_40G_SWDM4 0x1F +#define SFF8636_ETHERNET_100G_SWDM4 0x20 +#define SFF8636_ETHERNET_100G_PAM4_BIDI 0x21 +#define SFF8636_ETHERNET_4WDM10_MSA 0x22 +#define SFF8636_ETHERNET_4WDM20_MSA 0x23 +#define SFF8636_ETHERNET_4WDM40_MSA 0x24 +#define SFF8636_ETHERNET_100G_DR 0x25 +#define SFF8636_ETHERNET_100G_FR_NOFEC 0x26 +#define SFF8636_ETHERNET_100G_LR_NOFEC 0x27 +/* 28h-2Fh reserved */ +#define SFF8636_ETHERNET_200G_ACC1 0x30 +#define SFF8636_ETHERNET_200G_AOC1 0x31 +#define SFF8636_ETHERNET_200G_ACC2 0x32 +#define SFF8636_ETHERNET_200G_A0C2 0x33 +/* 34h-3Fh reserved */ +#define SFF8636_ETHERNET_200G_CR4 0x40 +#define SFF8636_ETHERNET_200G_SR4 0x41 +#define SFF8636_ETHERNET_200G_DR4 0x42 +#define SFF8636_ETHERNET_200G_FR4 0x43 +#define SFF8636_ETHERNET_200G_PSM4 0x44 +#define SFF8636_ETHERNET_50G_LR 0x45 +#define SFF8636_ETHERNET_200G_LR4 0x46 +/* 47h-4Fh reserved */ +#define SFF8636_ETHERNET_64G_EA 0x50 +#define SFF8636_ETHERNET_64G_SW 0x51 +#define SFF8636_ETHERNET_64G_LW 0x52 +#define SFF8636_ETHERNET_128FC_EA 0x53 +#define SFF8636_ETHERNET_128FC_SW 0x54 +#define SFF8636_ETHERNET_128FC_LW 0x55 +/* 56h-5Fh reserved */ + #define SFF8636_OPTION_2_OFFSET 0xC1 /* Rx output amplitude */ #define SFF8636_O2_RX_OUTPUT_AMP (1 << 0) @@ -241,7 +241,7 @@ print_intr_bits(u16 mask) } int -realtek_dump_regs(struct ethtool_drvinfo *info maybe_unused, +realtek_dump_regs(struct ethtool_drvinfo *info __maybe_unused, struct ethtool_regs *regs) { u32 *data = (u32 *) regs->data; @@ -348,8 +348,9 @@ int rxclass_rule_getall(struct cmd_context *ctx) { struct ethtool_rxnfc *nfccmd; __u32 *rule_locs; - int err, i; + unsigned int i; __u32 count; + int err; /* determine rule count */ err = rxclass_get_dev_info(ctx, &count, NULL); @@ -481,8 +482,9 @@ static int rmgr_find_empty_slot(struct rmgr_ctrl *rmgr, static int rmgr_init(struct cmd_context *ctx, struct rmgr_ctrl *rmgr) { struct ethtool_rxnfc *nfccmd; - int err, i; __u32 *rule_locs; + unsigned int i; + int err; /* clear rule manager settings */ memset(rmgr, 0, sizeof(*rmgr)); @@ -941,7 +943,7 @@ static int rxclass_get_long(char *str, long long *val, int size) static int rxclass_get_ulong(char *str, unsigned long long *val, int size) { - long long max = ~0ULL >> (64 - size); + unsigned long long max = ~0ULL >> (64 - size); char *endp; errno = 0; @@ -3890,7 +3890,8 @@ print_complex_table(unsigned revision, const struct efx_nic_reg_table *table, } int -sfc_dump_regs(struct ethtool_drvinfo *info maybe_unused, struct ethtool_regs *regs) +sfc_dump_regs(struct ethtool_drvinfo *info __maybe_unused, + struct ethtool_regs *regs) { const struct efx_nic_reg *reg; const struct efx_nic_reg_table *table; diff --git a/sff-common.c b/sff-common.c index 7700cbe..5285645 100644 --- a/sff-common.c +++ b/sff-common.c @@ -136,6 +136,9 @@ void sff8024_show_identifier(const __u8 *id, int id_offset) case SFF8024_ID_MICRO_QSFP: printf(" (microQSFP)\n"); break; + case SFF8024_ID_QSFP_DD: + printf(" (QSFP-DD Double Density 8X Pluggable Transceiver (INF-8628))\n"); + break; default: printf(" (reserved or unknown)\n"); break; @@ -203,6 +206,18 @@ void sff8024_show_connector(const __u8 *id, int ctor_offset) case SFF8024_CTOR_MXC_2x16: printf(" (MXC 2x16)\n"); break; + case SFF8024_CTOR_CS_OPTICAL: + printf(" (CS optical connector)\n"); + break; + case SFF8024_CTOR_CS_OPTICAL_MINI: + printf(" (Mini CS optical connector)\n"); + break; + case SFF8024_CTOR_MPO_2X12: + printf(" (MPO 2x12)\n"); + break; + case SFF8024_CTOR_MPO_1X16: + printf(" (MPO 1x16)\n"); + break; default: printf(" (reserved or unknown)\n"); break; @@ -302,3 +317,39 @@ void sff_show_thresholds(struct sff_diags sd) PRINT_xX_PWR("Laser rx power low warning threshold", sd.rx_power[LWARN]); } + +void sff_show_revision_compliance(const __u8 *id, int rev_offset) +{ + static const char *pfx = + "\tRevision Compliance :"; + + switch (id[rev_offset]) { + case SFF8636_REV_UNSPECIFIED: + printf("%s Revision not specified\n", pfx); + break; + case SFF8636_REV_8436_48: + printf("%s SFF-8436 Rev 4.8 or earlier\n", pfx); + break; + case SFF8636_REV_8436_8636: + printf("%s SFF-8436 Rev 4.8 or earlier\n", pfx); + break; + case SFF8636_REV_8636_13: + printf("%s SFF-8636 Rev 1.3 or earlier\n", pfx); + break; + case SFF8636_REV_8636_14: + printf("%s SFF-8636 Rev 1.4\n", pfx); + break; + case SFF8636_REV_8636_15: + printf("%s SFF-8636 Rev 1.5\n", pfx); + break; + case SFF8636_REV_8636_20: + printf("%s SFF-8636 Rev 2.0\n", pfx); + break; + case SFF8636_REV_8636_27: + printf("%s SFF-8636 Rev 2.5/2.6/2.7\n", pfx); + break; + default: + printf("%s Unallocated\n", pfx); + break; + } +} diff --git a/sff-common.h b/sff-common.h index 5562b4d..cfb5d0e 100644 --- a/sff-common.h +++ b/sff-common.h @@ -26,7 +26,17 @@ #include <stdio.h> #include "internal.h" -#define SFF8024_ID_OFFSET 0x00 +/* Revision compliance */ +#define SFF8636_REV_UNSPECIFIED 0x00 +#define SFF8636_REV_8436_48 0x01 +#define SFF8636_REV_8436_8636 0x02 +#define SFF8636_REV_8636_13 0x03 +#define SFF8636_REV_8636_14 0x04 +#define SFF8636_REV_8636_15 0x05 +#define SFF8636_REV_8636_20 0x06 +#define SFF8636_REV_8636_27 0x07 + +#define SFF8024_ID_OFFSET 0x00 #define SFF8024_ID_UNKNOWN 0x00 #define SFF8024_ID_GBIC 0x01 #define SFF8024_ID_SOLDERED_MODULE 0x02 @@ -51,7 +61,8 @@ #define SFF8024_ID_HD8X_FANOUT 0x15 #define SFF8024_ID_CDFP_S3 0x16 #define SFF8024_ID_MICRO_QSFP 0x17 -#define SFF8024_ID_LAST SFF8024_ID_MICRO_QSFP +#define SFF8024_ID_QSFP_DD 0x18 +#define SFF8024_ID_LAST SFF8024_ID_QSFP_DD #define SFF8024_ID_UNALLOCATED_LAST 0x7F #define SFF8024_ID_VENDOR_START 0x80 #define SFF8024_ID_VENDOR_LAST 0xFF @@ -76,8 +87,14 @@ #define SFF8024_CTOR_RJ45 0x22 #define SFF8024_CTOR_NO_SEPARABLE 0x23 #define SFF8024_CTOR_MXC_2x16 0x24 -#define SFF8024_CTOR_LAST SFF8024_CTOR_MXC_2x16 -#define SFF8024_CTOR_UNALLOCATED_LAST 0x7F +#define SFF8024_CTOR_CS_OPTICAL 0x25 +#define SFF8024_CTOR_CS_OPTICAL_MINI 0x26 +#define SFF8024_CTOR_MPO_2X12 0x27 +#define SFF8024_CTOR_MPO_1X16 0x28 +#define SFF8024_CTOR_LAST SFF8024_CTOR_MPO_1X16 + +#define SFF8024_CTOR_NO_SEP_QSFP_DD 0x6F +#define SFF8024_CTOR_UNALLOCATED_LAST 0x7F #define SFF8024_CTOR_VENDOR_START 0x80 #define SFF8024_CTOR_VENDOR_LAST 0xFF @@ -185,5 +202,6 @@ void sff8024_show_oui(const __u8 *id, int id_offset); void sff8024_show_identifier(const __u8 *id, int id_offset); void sff8024_show_connector(const __u8 *id, int ctor_offset); void sff8024_show_encoding(const __u8 *id, int encoding_offset, int sff_type); +void sff_show_revision_compliance(const __u8 *id, int rev_offset); #endif /* SFF_COMMON_H__ */ @@ -190,8 +190,8 @@ static float befloattoh(const __u32 *source) static void sff8472_calibration(const __u8 *id, struct sff_diags *sd) { - int i; __u16 rx_reading; + unsigned int i; /* Calibration should occur for all values (threshold and current) */ for (i = 0; i < ARRAY_SIZE(sd->bias_cur); ++i) { @@ -191,8 +191,76 @@ static void sff8079_show_transceiver(const __u8 *id) printf("%s Extended: 100G AOC or 25GAUI C2M AOC with worst BER of 10^(-12)\n", pfx); if (id[36] == 0x19) printf("%s Extended: 100G ACC or 25GAUI C2M ACC with worst BER of 10^(-12)\n", pfx); + if (id[36] == 0x1a) + printf("%s Extended: 100GE-DWDM2 (DWDM transceiver using 2 wavelengths on a 1550 nm DWDM grid with a reach up to 80 km)\n", + pfx); + if (id[36] == 0x1b) + printf("%s Extended: 100G 1550nm WDM (4 wavelengths)\n", pfx); if (id[36] == 0x1c) printf("%s Extended: 10Gbase-T Short Reach\n", pfx); + if (id[36] == 0x1d) + printf("%s Extended: 5GBASE-T\n", pfx); + if (id[36] == 0x1e) + printf("%s Extended: 2.5GBASE-T\n", pfx); + if (id[36] == 0x1f) + printf("%s Extended: 40G SWDM4\n", pfx); + if (id[36] == 0x20) + printf("%s Extended: 100G SWDM4\n", pfx); + if (id[36] == 0x21) + printf("%s Extended: 100G PAM4 BiDi\n", pfx); + if (id[36] == 0x22) + printf("%s Extended: 4WDM-10 MSA (10km version of 100G CWDM4 with same RS(528,514) FEC in host system)\n", + pfx); + if (id[36] == 0x23) + printf("%s Extended: 4WDM-20 MSA (20km version of 100GBASE-LR4 with RS(528,514) FEC in host system)\n", + pfx); + if (id[36] == 0x24) + printf("%s Extended: 4WDM-40 MSA (40km reach with APD receiver and RS(528,514) FEC in host system)\n", + pfx); + if (id[36] == 0x25) + printf("%s Extended: 100GBASE-DR (clause 140), CAUI-4 (no FEC)\n", pfx); + if (id[36] == 0x26) + printf("%s Extended: 100G-FR or 100GBASE-FR1 (clause 140), CAUI-4 (no FEC)\n", pfx); + if (id[36] == 0x27) + printf("%s Extended: 100G-LR or 100GBASE-LR1 (clause 140), CAUI-4 (no FEC)\n", pfx); + if (id[36] == 0x30) + printf("%s Extended: Active Copper Cable with 50GAUI, 100GAUI-2 or 200GAUI-4 C2M. Providing a worst BER of 10-6 or below\n", + pfx); + if (id[36] == 0x31) + printf("%s Extended: Active Optical Cable with 50GAUI, 100GAUI-2 or 200GAUI-4 C2M. Providing a worst BER of 10-6 or below\n", + pfx); + if (id[36] == 0x32) + printf("%s Extended: Active Copper Cable with 50GAUI, 100GAUI-2 or 200GAUI-4 C2M. Providing a worst BER of 2.6x10-4 for ACC, 10-5 for AUI, or below\n", + pfx); + if (id[36] == 0x33) + printf("%s Extended: Active Optical Cable with 50GAUI, 100GAUI-2 or 200GAUI-4 C2M. Providing a worst BER of 2.6x10-4 for ACC, 10-5 for AUI, or below\n", + pfx); + if (id[36] == 0x40) + printf("%s Extended: 50GBASE-CR, 100GBASE-CR2, or 200GBASE-CR4\n", pfx); + if (id[36] == 0x41) + printf("%s Extended: 50GBASE-SR, 100GBASE-SR2, or 200GBASE-SR4\n", pfx); + if (id[36] == 0x42) + printf("%s Extended: 50GBASE-FR or 200GBASE-DR4\n", pfx); + if (id[36] == 0x43) + printf("%s Extended: 200GBASE-FR4\n", pfx); + if (id[36] == 0x44) + printf("%s Extended: 200G 1550 nm PSM4\n", pfx); + if (id[36] == 0x45) + printf("%s Extended: 50GBASE-LR\n", pfx); + if (id[36] == 0x46) + printf("%s Extended: 200GBASE-LR4\n", pfx); + if (id[36] == 0x50) + printf("%s Extended: 64GFC EA\n", pfx); + if (id[36] == 0x51) + printf("%s Extended: 64GFC SW\n", pfx); + if (id[36] == 0x52) + printf("%s Extended: 64GFC LW\n", pfx); + if (id[36] == 0x53) + printf("%s Extended: 128GFC EA\n", pfx); + if (id[36] == 0x54) + printf("%s Extended: 128GFC SW\n", pfx); + if (id[36] == 0x55) + printf("%s Extended: 128GFC LW\n", pfx); } static void sff8079_show_encoding(const __u8 *id) @@ -2,7 +2,7 @@ #include <string.h> #include "internal.h" -int smsc911x_dump_regs(struct ethtool_drvinfo *info maybe_unused, +int smsc911x_dump_regs(struct ethtool_drvinfo *info __maybe_unused, struct ethtool_regs *regs) { unsigned int *smsc_reg = (unsigned int *)regs->data; @@ -18,7 +18,7 @@ #define GMAC_REG_NUM 55 #define GMAC_DMA_REG_NUM 23 -int st_mac100_dump_regs(struct ethtool_drvinfo *info maybe_unused, +int st_mac100_dump_regs(struct ethtool_drvinfo *info __maybe_unused, struct ethtool_regs *regs) { int i; @@ -51,7 +51,7 @@ int st_mac100_dump_regs(struct ethtool_drvinfo *info maybe_unused, return 0; } -int st_gmac_dump_regs(struct ethtool_drvinfo *info maybe_unused, +int st_gmac_dump_regs(struct ethtool_drvinfo *info __maybe_unused, struct ethtool_regs *regs) { int i; @@ -4,10 +4,10 @@ #define TG3_MAGIC 0x669955aa -int tg3_dump_eeprom(struct ethtool_drvinfo *info maybe_unused, +int tg3_dump_eeprom(struct ethtool_drvinfo *info __maybe_unused, struct ethtool_eeprom *ee) { - int i; + unsigned int i; if (ee->magic != TG3_MAGIC) { fprintf(stderr, "Magic number 0x%08x does not match 0x%08x\n", @@ -23,10 +23,10 @@ int tg3_dump_eeprom(struct ethtool_drvinfo *info maybe_unused, return 0; } -int tg3_dump_regs(struct ethtool_drvinfo *info maybe_unused, +int tg3_dump_regs(struct ethtool_drvinfo *info __maybe_unused, struct ethtool_regs *regs) { - int i; + unsigned int i; u32 reg; fprintf(stdout, "Offset\tValue\n"); @@ -25,7 +25,7 @@ bitset(u32 val, int bit) return 0; } -int altera_tse_dump_regs(struct ethtool_drvinfo *info maybe_unused, +int altera_tse_dump_regs(struct ethtool_drvinfo *info __maybe_unused, struct ethtool_regs *regs) { int i; diff --git a/uapi/linux/ethtool.h b/uapi/linux/ethtool.h index 1619806..052689b 100644 --- a/uapi/linux/ethtool.h +++ b/uapi/linux/ethtool.h @@ -577,6 +577,76 @@ struct ethtool_pauseparam { __u32 tx_pause; }; +/** + * enum ethtool_link_ext_state - link extended state + */ +enum ethtool_link_ext_state { + ETHTOOL_LINK_EXT_STATE_AUTONEG, + ETHTOOL_LINK_EXT_STATE_LINK_TRAINING_FAILURE, + ETHTOOL_LINK_EXT_STATE_LINK_LOGICAL_MISMATCH, + ETHTOOL_LINK_EXT_STATE_BAD_SIGNAL_INTEGRITY, + ETHTOOL_LINK_EXT_STATE_NO_CABLE, + ETHTOOL_LINK_EXT_STATE_CABLE_ISSUE, + ETHTOOL_LINK_EXT_STATE_EEPROM_ISSUE, + ETHTOOL_LINK_EXT_STATE_CALIBRATION_FAILURE, + ETHTOOL_LINK_EXT_STATE_POWER_BUDGET_EXCEEDED, + ETHTOOL_LINK_EXT_STATE_OVERHEAT, +}; + +/** + * enum ethtool_link_ext_substate_autoneg - more information in addition to + * ETHTOOL_LINK_EXT_STATE_AUTONEG. + */ +enum ethtool_link_ext_substate_autoneg { + ETHTOOL_LINK_EXT_SUBSTATE_AN_NO_PARTNER_DETECTED = 1, + ETHTOOL_LINK_EXT_SUBSTATE_AN_ACK_NOT_RECEIVED, + ETHTOOL_LINK_EXT_SUBSTATE_AN_NEXT_PAGE_EXCHANGE_FAILED, + ETHTOOL_LINK_EXT_SUBSTATE_AN_NO_PARTNER_DETECTED_FORCE_MODE, + ETHTOOL_LINK_EXT_SUBSTATE_AN_FEC_MISMATCH_DURING_OVERRIDE, + ETHTOOL_LINK_EXT_SUBSTATE_AN_NO_HCD, +}; + +/** + * enum ethtool_link_ext_substate_link_training - more information in addition to + * ETHTOOL_LINK_EXT_STATE_LINK_TRAINING_FAILURE. + */ +enum ethtool_link_ext_substate_link_training { + ETHTOOL_LINK_EXT_SUBSTATE_LT_KR_FRAME_LOCK_NOT_ACQUIRED = 1, + ETHTOOL_LINK_EXT_SUBSTATE_LT_KR_LINK_INHIBIT_TIMEOUT, + ETHTOOL_LINK_EXT_SUBSTATE_LT_KR_LINK_PARTNER_DID_NOT_SET_RECEIVER_READY, + ETHTOOL_LINK_EXT_SUBSTATE_LT_REMOTE_FAULT, +}; + +/** + * enum ethtool_link_ext_substate_logical_mismatch - more information in addition + * to ETHTOOL_LINK_EXT_STATE_LINK_LOGICAL_MISMATCH. + */ +enum ethtool_link_ext_substate_link_logical_mismatch { + ETHTOOL_LINK_EXT_SUBSTATE_LLM_PCS_DID_NOT_ACQUIRE_BLOCK_LOCK = 1, + ETHTOOL_LINK_EXT_SUBSTATE_LLM_PCS_DID_NOT_ACQUIRE_AM_LOCK, + ETHTOOL_LINK_EXT_SUBSTATE_LLM_PCS_DID_NOT_GET_ALIGN_STATUS, + ETHTOOL_LINK_EXT_SUBSTATE_LLM_FC_FEC_IS_NOT_LOCKED, + ETHTOOL_LINK_EXT_SUBSTATE_LLM_RS_FEC_IS_NOT_LOCKED, +}; + +/** + * enum ethtool_link_ext_substate_bad_signal_integrity - more information in + * addition to ETHTOOL_LINK_EXT_STATE_BAD_SIGNAL_INTEGRITY. + */ +enum ethtool_link_ext_substate_bad_signal_integrity { + ETHTOOL_LINK_EXT_SUBSTATE_BSI_LARGE_NUMBER_OF_PHYSICAL_ERRORS = 1, + ETHTOOL_LINK_EXT_SUBSTATE_BSI_UNSUPPORTED_RATE, +}; + +/** + * enum ethtool_link_ext_substate_cable_issue - more information in + * addition to ETHTOOL_LINK_EXT_STATE_CABLE_ISSUE. + */ +enum ethtool_link_ext_substate_cable_issue { + ETHTOOL_LINK_EXT_SUBSTATE_CI_UNSUPPORTED_CABLE = 1, + ETHTOOL_LINK_EXT_SUBSTATE_CI_CABLE_TEST_FAILURE, +}; + #define ETH_GSTRING_LEN 32 /** @@ -594,6 +664,10 @@ struct ethtool_pauseparam { * @ETH_SS_LINK_MODES: link mode names * @ETH_SS_MSG_CLASSES: debug message class names * @ETH_SS_WOL_MODES: wake-on-lan modes + * @ETH_SS_SOF_TIMESTAMPING: SOF_TIMESTAMPING_* flags + * @ETH_SS_TS_TX_TYPES: timestamping Tx types + * @ETH_SS_TS_RX_FILTERS: timestamping Rx filters + * @ETH_SS_UDP_TUNNEL_TYPES: UDP tunnel types */ enum ethtool_stringset { ETH_SS_TEST = 0, @@ -608,6 +682,10 @@ enum ethtool_stringset { ETH_SS_LINK_MODES, ETH_SS_MSG_CLASSES, ETH_SS_WOL_MODES, + ETH_SS_SOF_TIMESTAMPING, + ETH_SS_TS_TX_TYPES, + ETH_SS_TS_RX_FILTERS, + ETH_SS_UDP_TUNNEL_TYPES, /* add new constants above here */ ETH_SS_COUNT @@ -1328,6 +1406,7 @@ enum ethtool_fec_config_bits { ETHTOOL_FEC_OFF_BIT, ETHTOOL_FEC_RS_BIT, ETHTOOL_FEC_BASER_BIT, + ETHTOOL_FEC_LLRS_BIT, }; #define ETHTOOL_FEC_NONE (1 << ETHTOOL_FEC_NONE_BIT) @@ -1335,6 +1414,7 @@ enum ethtool_fec_config_bits { #define ETHTOOL_FEC_OFF (1 << ETHTOOL_FEC_OFF_BIT) #define ETHTOOL_FEC_RS (1 << ETHTOOL_FEC_RS_BIT) #define ETHTOOL_FEC_BASER (1 << ETHTOOL_FEC_BASER_BIT) +#define ETHTOOL_FEC_LLRS (1 << ETHTOOL_FEC_LLRS_BIT) /* CMDs currently supported */ #define ETHTOOL_GSET 0x00000001 /* DEPRECATED, Get settings. @@ -1519,7 +1599,24 @@ enum ethtool_link_mode_bit_indices { ETHTOOL_LINK_MODE_400000baseLR8_ER8_FR8_Full_BIT = 71, ETHTOOL_LINK_MODE_400000baseDR8_Full_BIT = 72, ETHTOOL_LINK_MODE_400000baseCR8_Full_BIT = 73, - + ETHTOOL_LINK_MODE_FEC_LLRS_BIT = 74, + ETHTOOL_LINK_MODE_100000baseKR_Full_BIT = 75, + ETHTOOL_LINK_MODE_100000baseSR_Full_BIT = 76, + ETHTOOL_LINK_MODE_100000baseLR_ER_FR_Full_BIT = 77, + ETHTOOL_LINK_MODE_100000baseCR_Full_BIT = 78, + ETHTOOL_LINK_MODE_100000baseDR_Full_BIT = 79, + ETHTOOL_LINK_MODE_200000baseKR2_Full_BIT = 80, + ETHTOOL_LINK_MODE_200000baseSR2_Full_BIT = 81, + ETHTOOL_LINK_MODE_200000baseLR2_ER2_FR2_Full_BIT = 82, + ETHTOOL_LINK_MODE_200000baseDR2_Full_BIT = 83, + ETHTOOL_LINK_MODE_200000baseCR2_Full_BIT = 84, + ETHTOOL_LINK_MODE_400000baseKR4_Full_BIT = 85, + ETHTOOL_LINK_MODE_400000baseSR4_Full_BIT = 86, + ETHTOOL_LINK_MODE_400000baseLR4_ER4_FR4_Full_BIT = 87, + ETHTOOL_LINK_MODE_400000baseDR4_Full_BIT = 88, + ETHTOOL_LINK_MODE_400000baseCR4_Full_BIT = 89, + ETHTOOL_LINK_MODE_100baseFX_Half_BIT = 90, + ETHTOOL_LINK_MODE_100baseFX_Full_BIT = 91, /* must be last entry */ __ETHTOOL_LINK_MODE_MASK_NBITS }; @@ -1656,6 +1753,18 @@ static __inline__ int ethtool_validate_duplex(__u8 duplex) return 0; } +#define MASTER_SLAVE_CFG_UNSUPPORTED 0 +#define MASTER_SLAVE_CFG_UNKNOWN 1 +#define MASTER_SLAVE_CFG_MASTER_PREFERRED 2 +#define MASTER_SLAVE_CFG_SLAVE_PREFERRED 3 +#define MASTER_SLAVE_CFG_MASTER_FORCE 4 +#define MASTER_SLAVE_CFG_SLAVE_FORCE 5 +#define MASTER_SLAVE_STATE_UNSUPPORTED 0 +#define MASTER_SLAVE_STATE_UNKNOWN 1 +#define MASTER_SLAVE_STATE_MASTER 2 +#define MASTER_SLAVE_STATE_SLAVE 3 +#define MASTER_SLAVE_STATE_ERR 4 + /* Which connector port. */ #define PORT_TP 0x00 #define PORT_AUI 0x01 @@ -1894,7 +2003,9 @@ struct ethtool_link_settings { __u8 eth_tp_mdix_ctrl; __s8 link_mode_masks_nwords; __u8 transceiver; - __u8 reserved1[3]; + __u8 master_slave_cfg; + __u8 master_slave_state; + __u8 reserved1[1]; __u32 reserved[7]; __u32 link_mode_masks[0]; /* layout of link_mode_masks fields: diff --git a/uapi/linux/ethtool_netlink.h b/uapi/linux/ethtool_netlink.h index ad6d3a0..c022883 100644 --- a/uapi/linux/ethtool_netlink.h +++ b/uapi/linux/ethtool_netlink.h @@ -2,7 +2,7 @@ /* * include/uapi/linux/ethtool_netlink.h - netlink interface for ethtool * - * See Documentation/networking/ethtool-netlink.txt in kernel source tree for + * See Documentation/networking/ethtool-netlink.rst in kernel source tree for * doucumentation of the interface. */ @@ -24,6 +24,24 @@ enum { ETHTOOL_MSG_DEBUG_SET, ETHTOOL_MSG_WOL_GET, ETHTOOL_MSG_WOL_SET, + ETHTOOL_MSG_FEATURES_GET, + ETHTOOL_MSG_FEATURES_SET, + ETHTOOL_MSG_PRIVFLAGS_GET, + ETHTOOL_MSG_PRIVFLAGS_SET, + ETHTOOL_MSG_RINGS_GET, + ETHTOOL_MSG_RINGS_SET, + ETHTOOL_MSG_CHANNELS_GET, + ETHTOOL_MSG_CHANNELS_SET, + ETHTOOL_MSG_COALESCE_GET, + ETHTOOL_MSG_COALESCE_SET, + ETHTOOL_MSG_PAUSE_GET, + ETHTOOL_MSG_PAUSE_SET, + ETHTOOL_MSG_EEE_GET, + ETHTOOL_MSG_EEE_SET, + ETHTOOL_MSG_TSINFO_GET, + ETHTOOL_MSG_CABLE_TEST_ACT, + ETHTOOL_MSG_CABLE_TEST_TDR_ACT, + ETHTOOL_MSG_TUNNEL_INFO_GET, /* add new constants above here */ __ETHTOOL_MSG_USER_CNT, @@ -43,6 +61,25 @@ enum { ETHTOOL_MSG_DEBUG_NTF, ETHTOOL_MSG_WOL_GET_REPLY, ETHTOOL_MSG_WOL_NTF, + ETHTOOL_MSG_FEATURES_GET_REPLY, + ETHTOOL_MSG_FEATURES_SET_REPLY, + ETHTOOL_MSG_FEATURES_NTF, + ETHTOOL_MSG_PRIVFLAGS_GET_REPLY, + ETHTOOL_MSG_PRIVFLAGS_NTF, + ETHTOOL_MSG_RINGS_GET_REPLY, + ETHTOOL_MSG_RINGS_NTF, + ETHTOOL_MSG_CHANNELS_GET_REPLY, + ETHTOOL_MSG_CHANNELS_NTF, + ETHTOOL_MSG_COALESCE_GET_REPLY, + ETHTOOL_MSG_COALESCE_NTF, + ETHTOOL_MSG_PAUSE_GET_REPLY, + ETHTOOL_MSG_PAUSE_NTF, + ETHTOOL_MSG_EEE_GET_REPLY, + ETHTOOL_MSG_EEE_NTF, + ETHTOOL_MSG_TSINFO_GET_REPLY, + ETHTOOL_MSG_CABLE_TEST_NTF, + ETHTOOL_MSG_CABLE_TEST_TDR_NTF, + ETHTOOL_MSG_TUNNEL_INFO_GET_REPLY, /* add new constants above here */ __ETHTOOL_MSG_KERNEL_CNT, @@ -55,9 +92,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, @@ -185,6 +225,8 @@ enum { ETHTOOL_A_LINKMODES_PEER, /* bitset */ ETHTOOL_A_LINKMODES_SPEED, /* u32 */ ETHTOOL_A_LINKMODES_DUPLEX, /* u8 */ + ETHTOOL_A_LINKMODES_MASTER_SLAVE_CFG, /* u8 */ + ETHTOOL_A_LINKMODES_MASTER_SLAVE_STATE, /* u8 */ /* add new constants above here */ __ETHTOOL_A_LINKMODES_CNT, @@ -197,6 +239,10 @@ enum { ETHTOOL_A_LINKSTATE_UNSPEC, ETHTOOL_A_LINKSTATE_HEADER, /* nest - _A_HEADER_* */ ETHTOOL_A_LINKSTATE_LINK, /* u8 */ + ETHTOOL_A_LINKSTATE_SQI, /* u32 */ + ETHTOOL_A_LINKSTATE_SQI_MAX, /* u32 */ + ETHTOOL_A_LINKSTATE_EXT_STATE, /* u8 */ + ETHTOOL_A_LINKSTATE_EXT_SUBSTATE, /* u8 */ /* add new constants above here */ __ETHTOOL_A_LINKSTATE_CNT, @@ -228,6 +274,360 @@ enum { ETHTOOL_A_WOL_MAX = __ETHTOOL_A_WOL_CNT - 1 }; +/* FEATURES */ + +enum { + ETHTOOL_A_FEATURES_UNSPEC, + ETHTOOL_A_FEATURES_HEADER, /* nest - _A_HEADER_* */ + ETHTOOL_A_FEATURES_HW, /* bitset */ + ETHTOOL_A_FEATURES_WANTED, /* bitset */ + ETHTOOL_A_FEATURES_ACTIVE, /* bitset */ + ETHTOOL_A_FEATURES_NOCHANGE, /* bitset */ + + /* add new constants above here */ + __ETHTOOL_A_FEATURES_CNT, + ETHTOOL_A_FEATURES_MAX = __ETHTOOL_A_FEATURES_CNT - 1 +}; + +/* PRIVFLAGS */ + +enum { + ETHTOOL_A_PRIVFLAGS_UNSPEC, + ETHTOOL_A_PRIVFLAGS_HEADER, /* nest - _A_HEADER_* */ + ETHTOOL_A_PRIVFLAGS_FLAGS, /* bitset */ + + /* add new constants above here */ + __ETHTOOL_A_PRIVFLAGS_CNT, + ETHTOOL_A_PRIVFLAGS_MAX = __ETHTOOL_A_PRIVFLAGS_CNT - 1 +}; + +/* RINGS */ + +enum { + ETHTOOL_A_RINGS_UNSPEC, + ETHTOOL_A_RINGS_HEADER, /* nest - _A_HEADER_* */ + ETHTOOL_A_RINGS_RX_MAX, /* u32 */ + ETHTOOL_A_RINGS_RX_MINI_MAX, /* u32 */ + ETHTOOL_A_RINGS_RX_JUMBO_MAX, /* u32 */ + ETHTOOL_A_RINGS_TX_MAX, /* u32 */ + ETHTOOL_A_RINGS_RX, /* u32 */ + ETHTOOL_A_RINGS_RX_MINI, /* u32 */ + ETHTOOL_A_RINGS_RX_JUMBO, /* u32 */ + ETHTOOL_A_RINGS_TX, /* u32 */ + + /* add new constants above here */ + __ETHTOOL_A_RINGS_CNT, + ETHTOOL_A_RINGS_MAX = (__ETHTOOL_A_RINGS_CNT - 1) +}; + +/* CHANNELS */ + +enum { + ETHTOOL_A_CHANNELS_UNSPEC, + ETHTOOL_A_CHANNELS_HEADER, /* nest - _A_HEADER_* */ + ETHTOOL_A_CHANNELS_RX_MAX, /* u32 */ + ETHTOOL_A_CHANNELS_TX_MAX, /* u32 */ + ETHTOOL_A_CHANNELS_OTHER_MAX, /* u32 */ + ETHTOOL_A_CHANNELS_COMBINED_MAX, /* u32 */ + ETHTOOL_A_CHANNELS_RX_COUNT, /* u32 */ + ETHTOOL_A_CHANNELS_TX_COUNT, /* u32 */ + ETHTOOL_A_CHANNELS_OTHER_COUNT, /* u32 */ + ETHTOOL_A_CHANNELS_COMBINED_COUNT, /* u32 */ + + /* add new constants above here */ + __ETHTOOL_A_CHANNELS_CNT, + ETHTOOL_A_CHANNELS_MAX = (__ETHTOOL_A_CHANNELS_CNT - 1) +}; + +/* COALESCE */ + +enum { + ETHTOOL_A_COALESCE_UNSPEC, + ETHTOOL_A_COALESCE_HEADER, /* nest - _A_HEADER_* */ + ETHTOOL_A_COALESCE_RX_USECS, /* u32 */ + ETHTOOL_A_COALESCE_RX_MAX_FRAMES, /* u32 */ + ETHTOOL_A_COALESCE_RX_USECS_IRQ, /* u32 */ + ETHTOOL_A_COALESCE_RX_MAX_FRAMES_IRQ, /* u32 */ + ETHTOOL_A_COALESCE_TX_USECS, /* u32 */ + ETHTOOL_A_COALESCE_TX_MAX_FRAMES, /* u32 */ + ETHTOOL_A_COALESCE_TX_USECS_IRQ, /* u32 */ + ETHTOOL_A_COALESCE_TX_MAX_FRAMES_IRQ, /* u32 */ + ETHTOOL_A_COALESCE_STATS_BLOCK_USECS, /* u32 */ + ETHTOOL_A_COALESCE_USE_ADAPTIVE_RX, /* u8 */ + ETHTOOL_A_COALESCE_USE_ADAPTIVE_TX, /* u8 */ + ETHTOOL_A_COALESCE_PKT_RATE_LOW, /* u32 */ + ETHTOOL_A_COALESCE_RX_USECS_LOW, /* u32 */ + ETHTOOL_A_COALESCE_RX_MAX_FRAMES_LOW, /* u32 */ + ETHTOOL_A_COALESCE_TX_USECS_LOW, /* u32 */ + ETHTOOL_A_COALESCE_TX_MAX_FRAMES_LOW, /* u32 */ + ETHTOOL_A_COALESCE_PKT_RATE_HIGH, /* u32 */ + ETHTOOL_A_COALESCE_RX_USECS_HIGH, /* u32 */ + ETHTOOL_A_COALESCE_RX_MAX_FRAMES_HIGH, /* u32 */ + ETHTOOL_A_COALESCE_TX_USECS_HIGH, /* u32 */ + ETHTOOL_A_COALESCE_TX_MAX_FRAMES_HIGH, /* u32 */ + ETHTOOL_A_COALESCE_RATE_SAMPLE_INTERVAL, /* u32 */ + + /* add new constants above here */ + __ETHTOOL_A_COALESCE_CNT, + ETHTOOL_A_COALESCE_MAX = (__ETHTOOL_A_COALESCE_CNT - 1) +}; + +/* PAUSE */ + +enum { + ETHTOOL_A_PAUSE_UNSPEC, + ETHTOOL_A_PAUSE_HEADER, /* nest - _A_HEADER_* */ + 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 { + ETHTOOL_A_EEE_UNSPEC, + ETHTOOL_A_EEE_HEADER, /* nest - _A_HEADER_* */ + ETHTOOL_A_EEE_MODES_OURS, /* bitset */ + ETHTOOL_A_EEE_MODES_PEER, /* bitset */ + ETHTOOL_A_EEE_ACTIVE, /* u8 */ + ETHTOOL_A_EEE_ENABLED, /* u8 */ + ETHTOOL_A_EEE_TX_LPI_ENABLED, /* u8 */ + ETHTOOL_A_EEE_TX_LPI_TIMER, /* u32 */ + + /* add new constants above here */ + __ETHTOOL_A_EEE_CNT, + ETHTOOL_A_EEE_MAX = (__ETHTOOL_A_EEE_CNT - 1) +}; + +/* TSINFO */ + +enum { + ETHTOOL_A_TSINFO_UNSPEC, + ETHTOOL_A_TSINFO_HEADER, /* nest - _A_HEADER_* */ + ETHTOOL_A_TSINFO_TIMESTAMPING, /* bitset */ + ETHTOOL_A_TSINFO_TX_TYPES, /* bitset */ + ETHTOOL_A_TSINFO_RX_FILTERS, /* bitset */ + ETHTOOL_A_TSINFO_PHC_INDEX, /* u32 */ + + /* add new constants above here */ + __ETHTOOL_A_TSINFO_CNT, + ETHTOOL_A_TSINFO_MAX = (__ETHTOOL_A_TSINFO_CNT - 1) +}; + +/* CABLE TEST */ + +enum { + ETHTOOL_A_CABLE_TEST_UNSPEC, + ETHTOOL_A_CABLE_TEST_HEADER, /* nest - _A_HEADER_* */ + + /* add new constants above here */ + __ETHTOOL_A_CABLE_TEST_CNT, + ETHTOOL_A_CABLE_TEST_MAX = __ETHTOOL_A_CABLE_TEST_CNT - 1 +}; + +/* CABLE TEST NOTIFY */ +enum { + ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC, + ETHTOOL_A_CABLE_RESULT_CODE_OK, + ETHTOOL_A_CABLE_RESULT_CODE_OPEN, + ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT, + ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT, +}; + +enum { + ETHTOOL_A_CABLE_PAIR_A, + ETHTOOL_A_CABLE_PAIR_B, + ETHTOOL_A_CABLE_PAIR_C, + ETHTOOL_A_CABLE_PAIR_D, +}; + +enum { + ETHTOOL_A_CABLE_RESULT_UNSPEC, + ETHTOOL_A_CABLE_RESULT_PAIR, /* u8 ETHTOOL_A_CABLE_PAIR_ */ + ETHTOOL_A_CABLE_RESULT_CODE, /* u8 ETHTOOL_A_CABLE_RESULT_CODE_ */ + + __ETHTOOL_A_CABLE_RESULT_CNT, + ETHTOOL_A_CABLE_RESULT_MAX = (__ETHTOOL_A_CABLE_RESULT_CNT - 1) +}; + +enum { + ETHTOOL_A_CABLE_FAULT_LENGTH_UNSPEC, + ETHTOOL_A_CABLE_FAULT_LENGTH_PAIR, /* u8 ETHTOOL_A_CABLE_PAIR_ */ + ETHTOOL_A_CABLE_FAULT_LENGTH_CM, /* u32 */ + + __ETHTOOL_A_CABLE_FAULT_LENGTH_CNT, + ETHTOOL_A_CABLE_FAULT_LENGTH_MAX = (__ETHTOOL_A_CABLE_FAULT_LENGTH_CNT - 1) +}; + +enum { + ETHTOOL_A_CABLE_TEST_NTF_STATUS_UNSPEC, + ETHTOOL_A_CABLE_TEST_NTF_STATUS_STARTED, + ETHTOOL_A_CABLE_TEST_NTF_STATUS_COMPLETED +}; + +enum { + ETHTOOL_A_CABLE_NEST_UNSPEC, + ETHTOOL_A_CABLE_NEST_RESULT, /* nest - ETHTOOL_A_CABLE_RESULT_ */ + ETHTOOL_A_CABLE_NEST_FAULT_LENGTH, /* nest - ETHTOOL_A_CABLE_FAULT_LENGTH_ */ + __ETHTOOL_A_CABLE_NEST_CNT, + ETHTOOL_A_CABLE_NEST_MAX = (__ETHTOOL_A_CABLE_NEST_CNT - 1) +}; + +enum { + ETHTOOL_A_CABLE_TEST_NTF_UNSPEC, + ETHTOOL_A_CABLE_TEST_NTF_HEADER, /* nest - ETHTOOL_A_HEADER_* */ + ETHTOOL_A_CABLE_TEST_NTF_STATUS, /* u8 - _STARTED/_COMPLETE */ + ETHTOOL_A_CABLE_TEST_NTF_NEST, /* nest - of results: */ + + __ETHTOOL_A_CABLE_TEST_NTF_CNT, + ETHTOOL_A_CABLE_TEST_NTF_MAX = (__ETHTOOL_A_CABLE_TEST_NTF_CNT - 1) +}; + +/* CABLE TEST TDR */ + +enum { + ETHTOOL_A_CABLE_TEST_TDR_CFG_UNSPEC, + ETHTOOL_A_CABLE_TEST_TDR_CFG_FIRST, /* u32 */ + ETHTOOL_A_CABLE_TEST_TDR_CFG_LAST, /* u32 */ + ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP, /* u32 */ + ETHTOOL_A_CABLE_TEST_TDR_CFG_PAIR, /* u8 */ + + /* add new constants above here */ + __ETHTOOL_A_CABLE_TEST_TDR_CFG_CNT, + ETHTOOL_A_CABLE_TEST_TDR_CFG_MAX = __ETHTOOL_A_CABLE_TEST_TDR_CFG_CNT - 1 +}; + +enum { + ETHTOOL_A_CABLE_TEST_TDR_UNSPEC, + ETHTOOL_A_CABLE_TEST_TDR_HEADER, /* nest - _A_HEADER_* */ + ETHTOOL_A_CABLE_TEST_TDR_CFG, /* nest - *_TDR_CFG_* */ + + /* add new constants above here */ + __ETHTOOL_A_CABLE_TEST_TDR_CNT, + ETHTOOL_A_CABLE_TEST_TDR_MAX = __ETHTOOL_A_CABLE_TEST_TDR_CNT - 1 +}; + +/* CABLE TEST TDR NOTIFY */ + +enum { + ETHTOOL_A_CABLE_AMPLITUDE_UNSPEC, + ETHTOOL_A_CABLE_AMPLITUDE_PAIR, /* u8 */ + ETHTOOL_A_CABLE_AMPLITUDE_mV, /* s16 */ + + __ETHTOOL_A_CABLE_AMPLITUDE_CNT, + ETHTOOL_A_CABLE_AMPLITUDE_MAX = (__ETHTOOL_A_CABLE_AMPLITUDE_CNT - 1) +}; + +enum { + ETHTOOL_A_CABLE_PULSE_UNSPEC, + ETHTOOL_A_CABLE_PULSE_mV, /* s16 */ + + __ETHTOOL_A_CABLE_PULSE_CNT, + ETHTOOL_A_CABLE_PULSE_MAX = (__ETHTOOL_A_CABLE_PULSE_CNT - 1) +}; + +enum { + ETHTOOL_A_CABLE_STEP_UNSPEC, + ETHTOOL_A_CABLE_STEP_FIRST_DISTANCE, /* u32 */ + ETHTOOL_A_CABLE_STEP_LAST_DISTANCE, /* u32 */ + ETHTOOL_A_CABLE_STEP_STEP_DISTANCE, /* u32 */ + + __ETHTOOL_A_CABLE_STEP_CNT, + ETHTOOL_A_CABLE_STEP_MAX = (__ETHTOOL_A_CABLE_STEP_CNT - 1) +}; + +enum { + ETHTOOL_A_CABLE_TDR_NEST_UNSPEC, + ETHTOOL_A_CABLE_TDR_NEST_STEP, /* nest - ETHTTOOL_A_CABLE_STEP */ + ETHTOOL_A_CABLE_TDR_NEST_AMPLITUDE, /* nest - ETHTOOL_A_CABLE_AMPLITUDE */ + ETHTOOL_A_CABLE_TDR_NEST_PULSE, /* nest - ETHTOOL_A_CABLE_PULSE */ + + __ETHTOOL_A_CABLE_TDR_NEST_CNT, + ETHTOOL_A_CABLE_TDR_NEST_MAX = (__ETHTOOL_A_CABLE_TDR_NEST_CNT - 1) +}; + +enum { + ETHTOOL_A_CABLE_TEST_TDR_NTF_UNSPEC, + ETHTOOL_A_CABLE_TEST_TDR_NTF_HEADER, /* nest - ETHTOOL_A_HEADER_* */ + ETHTOOL_A_CABLE_TEST_TDR_NTF_STATUS, /* u8 - _STARTED/_COMPLETE */ + ETHTOOL_A_CABLE_TEST_TDR_NTF_NEST, /* nest - of results: */ + + /* add new constants above here */ + __ETHTOOL_A_CABLE_TEST_TDR_NTF_CNT, + ETHTOOL_A_CABLE_TEST_TDR_NTF_MAX = __ETHTOOL_A_CABLE_TEST_TDR_NTF_CNT - 1 +}; + +/* TUNNEL INFO */ + +enum { + ETHTOOL_UDP_TUNNEL_TYPE_VXLAN, + ETHTOOL_UDP_TUNNEL_TYPE_GENEVE, + ETHTOOL_UDP_TUNNEL_TYPE_VXLAN_GPE, + + __ETHTOOL_UDP_TUNNEL_TYPE_CNT +}; + +enum { + ETHTOOL_A_TUNNEL_UDP_ENTRY_UNSPEC, + + ETHTOOL_A_TUNNEL_UDP_ENTRY_PORT, /* be16 */ + ETHTOOL_A_TUNNEL_UDP_ENTRY_TYPE, /* u32 */ + + /* add new constants above here */ + __ETHTOOL_A_TUNNEL_UDP_ENTRY_CNT, + ETHTOOL_A_TUNNEL_UDP_ENTRY_MAX = (__ETHTOOL_A_TUNNEL_UDP_ENTRY_CNT - 1) +}; + +enum { + ETHTOOL_A_TUNNEL_UDP_TABLE_UNSPEC, + + ETHTOOL_A_TUNNEL_UDP_TABLE_SIZE, /* u32 */ + ETHTOOL_A_TUNNEL_UDP_TABLE_TYPES, /* bitset */ + ETHTOOL_A_TUNNEL_UDP_TABLE_ENTRY, /* nest - _UDP_ENTRY_* */ + + /* add new constants above here */ + __ETHTOOL_A_TUNNEL_UDP_TABLE_CNT, + ETHTOOL_A_TUNNEL_UDP_TABLE_MAX = (__ETHTOOL_A_TUNNEL_UDP_TABLE_CNT - 1) +}; + +enum { + ETHTOOL_A_TUNNEL_UDP_UNSPEC, + + ETHTOOL_A_TUNNEL_UDP_TABLE, /* nest - _UDP_TABLE_* */ + + /* add new constants above here */ + __ETHTOOL_A_TUNNEL_UDP_CNT, + ETHTOOL_A_TUNNEL_UDP_MAX = (__ETHTOOL_A_TUNNEL_UDP_CNT - 1) +}; + +enum { + ETHTOOL_A_TUNNEL_INFO_UNSPEC, + ETHTOOL_A_TUNNEL_INFO_HEADER, /* nest - _A_HEADER_* */ + + ETHTOOL_A_TUNNEL_INFO_UDP_PORTS, /* nest - _UDP_TABLE */ + + /* add new constants above here */ + __ETHTOOL_A_TUNNEL_INFO_CNT, + ETHTOOL_A_TUNNEL_INFO_MAX = (__ETHTOOL_A_TUNNEL_INFO_CNT - 1) +}; + /* generic netlink info */ #define ETHTOOL_GENL_NAME "ethtool" #define ETHTOOL_GENL_VERSION 1 diff --git a/uapi/linux/genetlink.h b/uapi/linux/genetlink.h index 1317119..9fa720e 100644 --- a/uapi/linux/genetlink.h +++ b/uapi/linux/genetlink.h @@ -48,6 +48,7 @@ enum { CTRL_CMD_NEWMCAST_GRP, CTRL_CMD_DELMCAST_GRP, CTRL_CMD_GETMCAST_GRP, /* unused */ + CTRL_CMD_GETPOLICY, __CTRL_CMD_MAX, }; @@ -62,6 +63,9 @@ enum { CTRL_ATTR_MAXATTR, CTRL_ATTR_OPS, CTRL_ATTR_MCAST_GROUPS, + CTRL_ATTR_POLICY, + CTRL_ATTR_OP_POLICY, + CTRL_ATTR_OP, __CTRL_ATTR_MAX, }; @@ -83,6 +87,15 @@ enum { __CTRL_ATTR_MCAST_GRP_MAX, }; +enum { + CTRL_ATTR_POLICY_UNSPEC, + CTRL_ATTR_POLICY_DO, + CTRL_ATTR_POLICY_DUMP, + + __CTRL_ATTR_POLICY_DUMP_MAX, + CTRL_ATTR_POLICY_DUMP_MAX = __CTRL_ATTR_POLICY_DUMP_MAX - 1 +}; + #define CTRL_ATTR_MCAST_GRP_MAX (__CTRL_ATTR_MCAST_GRP_MAX - 1) diff --git a/uapi/linux/if_link.h b/uapi/linux/if_link.h index cb88bcb..307e5c2 100644 --- a/uapi/linux/if_link.h +++ b/uapi/linux/if_link.h @@ -7,24 +7,23 @@ /* This struct should be in sync with struct rtnl_link_stats64 */ struct rtnl_link_stats { - __u32 rx_packets; /* total packets received */ - __u32 tx_packets; /* total packets transmitted */ - __u32 rx_bytes; /* total bytes received */ - __u32 tx_bytes; /* total bytes transmitted */ - __u32 rx_errors; /* bad packets received */ - __u32 tx_errors; /* packet transmit problems */ - __u32 rx_dropped; /* no space in linux buffers */ - __u32 tx_dropped; /* no space available in linux */ - __u32 multicast; /* multicast packets received */ + __u32 rx_packets; + __u32 tx_packets; + __u32 rx_bytes; + __u32 tx_bytes; + __u32 rx_errors; + __u32 tx_errors; + __u32 rx_dropped; + __u32 tx_dropped; + __u32 multicast; __u32 collisions; - /* detailed rx_errors: */ __u32 rx_length_errors; - __u32 rx_over_errors; /* receiver ring buff overflow */ - __u32 rx_crc_errors; /* recved pkt with crc error */ - __u32 rx_frame_errors; /* recv'd frame alignment error */ - __u32 rx_fifo_errors; /* recv'r fifo overrun */ - __u32 rx_missed_errors; /* receiver missed packet */ + __u32 rx_over_errors; + __u32 rx_crc_errors; + __u32 rx_frame_errors; + __u32 rx_fifo_errors; + __u32 rx_missed_errors; /* detailed tx_errors */ __u32 tx_aborted_errors; @@ -37,29 +36,200 @@ struct rtnl_link_stats { __u32 rx_compressed; __u32 tx_compressed; - __u32 rx_nohandler; /* dropped, no handler found */ + __u32 rx_nohandler; }; -/* The main device statistics structure */ +/** + * struct rtnl_link_stats64 - The main device statistics structure. + * + * @rx_packets: Number of good packets received by the interface. + * For hardware interfaces counts all good packets received from the device + * by the host, including packets which host had to drop at various stages + * of processing (even in the driver). + * + * @tx_packets: Number of packets successfully transmitted. + * For hardware interfaces counts packets which host was able to successfully + * hand over to the device, which does not necessarily mean that packets + * had been successfully transmitted out of the device, only that device + * acknowledged it copied them out of host memory. + * + * @rx_bytes: Number of good received bytes, corresponding to @rx_packets. + * + * For IEEE 802.3 devices should count the length of Ethernet Frames + * excluding the FCS. + * + * @tx_bytes: Number of good transmitted bytes, corresponding to @tx_packets. + * + * For IEEE 802.3 devices should count the length of Ethernet Frames + * excluding the FCS. + * + * @rx_errors: Total number of bad packets received on this network device. + * This counter must include events counted by @rx_length_errors, + * @rx_crc_errors, @rx_frame_errors and other errors not otherwise + * counted. + * + * @tx_errors: Total number of transmit problems. + * This counter must include events counter by @tx_aborted_errors, + * @tx_carrier_errors, @tx_fifo_errors, @tx_heartbeat_errors, + * @tx_window_errors and other errors not otherwise counted. + * + * @rx_dropped: Number of packets received but not processed, + * e.g. due to lack of resources or unsupported protocol. + * For hardware interfaces this counter should not include packets + * dropped by the device which are counted separately in + * @rx_missed_errors (since procfs folds those two counters together). + * + * @tx_dropped: Number of packets dropped on their way to transmission, + * e.g. due to lack of resources. + * + * @multicast: Multicast packets received. + * For hardware interfaces this statistic is commonly calculated + * at the device level (unlike @rx_packets) and therefore may include + * packets which did not reach the host. + * + * For IEEE 802.3 devices this counter may be equivalent to: + * + * - 30.3.1.1.21 aMulticastFramesReceivedOK + * + * @collisions: Number of collisions during packet transmissions. + * + * @rx_length_errors: Number of packets dropped due to invalid length. + * Part of aggregate "frame" errors in `/proc/net/dev`. + * + * For IEEE 802.3 devices this counter should be equivalent to a sum + * of the following attributes: + * + * - 30.3.1.1.23 aInRangeLengthErrors + * - 30.3.1.1.24 aOutOfRangeLengthField + * - 30.3.1.1.25 aFrameTooLongErrors + * + * @rx_over_errors: Receiver FIFO overflow event counter. + * + * Historically the count of overflow events. Such events may be + * reported in the receive descriptors or via interrupts, and may + * not correspond one-to-one with dropped packets. + * + * The recommended interpretation for high speed interfaces is - + * number of packets dropped because they did not fit into buffers + * provided by the host, e.g. packets larger than MTU or next buffer + * in the ring was not available for a scatter transfer. + * + * Part of aggregate "frame" errors in `/proc/net/dev`. + * + * This statistics was historically used interchangeably with + * @rx_fifo_errors. + * + * This statistic corresponds to hardware events and is not commonly used + * on software devices. + * + * @rx_crc_errors: Number of packets received with a CRC error. + * Part of aggregate "frame" errors in `/proc/net/dev`. + * + * For IEEE 802.3 devices this counter must be equivalent to: + * + * - 30.3.1.1.6 aFrameCheckSequenceErrors + * + * @rx_frame_errors: Receiver frame alignment errors. + * Part of aggregate "frame" errors in `/proc/net/dev`. + * + * For IEEE 802.3 devices this counter should be equivalent to: + * + * - 30.3.1.1.7 aAlignmentErrors + * + * @rx_fifo_errors: Receiver FIFO error counter. + * + * Historically the count of overflow events. Those events may be + * reported in the receive descriptors or via interrupts, and may + * not correspond one-to-one with dropped packets. + * + * This statistics was used interchangeably with @rx_over_errors. + * Not recommended for use in drivers for high speed interfaces. + * + * This statistic is used on software devices, e.g. to count software + * packet queue overflow (can) or sequencing errors (GRE). + * + * @rx_missed_errors: Count of packets missed by the host. + * Folded into the "drop" counter in `/proc/net/dev`. + * + * Counts number of packets dropped by the device due to lack + * of buffer space. This usually indicates that the host interface + * is slower than the network interface, or host is not keeping up + * with the receive packet rate. + * + * This statistic corresponds to hardware events and is not used + * on software devices. + * + * @tx_aborted_errors: + * Part of aggregate "carrier" errors in `/proc/net/dev`. + * For IEEE 802.3 devices capable of half-duplex operation this counter + * must be equivalent to: + * + * - 30.3.1.1.11 aFramesAbortedDueToXSColls + * + * High speed interfaces may use this counter as a general device + * discard counter. + * + * @tx_carrier_errors: Number of frame transmission errors due to loss + * of carrier during transmission. + * Part of aggregate "carrier" errors in `/proc/net/dev`. + * + * For IEEE 802.3 devices this counter must be equivalent to: + * + * - 30.3.1.1.13 aCarrierSenseErrors + * + * @tx_fifo_errors: Number of frame transmission errors due to device + * FIFO underrun / underflow. This condition occurs when the device + * begins transmission of a frame but is unable to deliver the + * entire frame to the transmitter in time for transmission. + * Part of aggregate "carrier" errors in `/proc/net/dev`. + * + * @tx_heartbeat_errors: Number of Heartbeat / SQE Test errors for + * old half-duplex Ethernet. + * Part of aggregate "carrier" errors in `/proc/net/dev`. + * + * For IEEE 802.3 devices possibly equivalent to: + * + * - 30.3.2.1.4 aSQETestErrors + * + * @tx_window_errors: Number of frame transmission errors due + * to late collisions (for Ethernet - after the first 64B of transmission). + * Part of aggregate "carrier" errors in `/proc/net/dev`. + * + * For IEEE 802.3 devices this counter must be equivalent to: + * + * - 30.3.1.1.10 aLateCollisions + * + * @rx_compressed: Number of correctly received compressed packets. + * This counters is only meaningful for interfaces which support + * packet compression (e.g. CSLIP, PPP). + * + * @tx_compressed: Number of transmitted compressed packets. + * This counters is only meaningful for interfaces which support + * packet compression (e.g. CSLIP, PPP). + * + * @rx_nohandler: Number of packets received on the interface + * but dropped by the networking stack because the device is + * not designated to receive packets (e.g. backup link in a bond). + */ struct rtnl_link_stats64 { - __u64 rx_packets; /* total packets received */ - __u64 tx_packets; /* total packets transmitted */ - __u64 rx_bytes; /* total bytes received */ - __u64 tx_bytes; /* total bytes transmitted */ - __u64 rx_errors; /* bad packets received */ - __u64 tx_errors; /* packet transmit problems */ - __u64 rx_dropped; /* no space in linux buffers */ - __u64 tx_dropped; /* no space available in linux */ - __u64 multicast; /* multicast packets received */ + __u64 rx_packets; + __u64 tx_packets; + __u64 rx_bytes; + __u64 tx_bytes; + __u64 rx_errors; + __u64 tx_errors; + __u64 rx_dropped; + __u64 tx_dropped; + __u64 multicast; __u64 collisions; /* detailed rx_errors: */ __u64 rx_length_errors; - __u64 rx_over_errors; /* receiver ring buff overflow */ - __u64 rx_crc_errors; /* recved pkt with crc error */ - __u64 rx_frame_errors; /* recv'd frame alignment error */ - __u64 rx_fifo_errors; /* recv'r fifo overrun */ - __u64 rx_missed_errors; /* receiver missed packet */ + __u64 rx_over_errors; + __u64 rx_crc_errors; + __u64 rx_frame_errors; + __u64 rx_fifo_errors; + __u64 rx_missed_errors; /* detailed tx_errors */ __u64 tx_aborted_errors; @@ -71,8 +241,7 @@ struct rtnl_link_stats64 { /* for cslip etc */ __u64 rx_compressed; __u64 tx_compressed; - - __u64 rx_nohandler; /* dropped, no handler found */ + __u64 rx_nohandler; }; /* The struct should be in sync with struct ifmap */ @@ -170,12 +339,22 @@ enum { IFLA_PROP_LIST, IFLA_ALT_IFNAME, /* Alternative ifname */ IFLA_PERM_ADDRESS, + IFLA_PROTO_DOWN_REASON, __IFLA_MAX }; #define IFLA_MAX (__IFLA_MAX - 1) +enum { + IFLA_PROTO_DOWN_REASON_UNSPEC, + IFLA_PROTO_DOWN_REASON_MASK, /* u32, mask for reason bits */ + IFLA_PROTO_DOWN_REASON_VALUE, /* u32, reason bit value */ + + __IFLA_PROTO_DOWN_REASON_CNT, + IFLA_PROTO_DOWN_REASON_MAX = __IFLA_PROTO_DOWN_REASON_CNT - 1 +}; + /* backwards compatibility for userspace */ #define IFLA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifinfomsg)))) #define IFLA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ifinfomsg)) @@ -341,6 +520,8 @@ enum { IFLA_BRPORT_NEIGH_SUPPRESS, IFLA_BRPORT_ISOLATED, IFLA_BRPORT_BACKUP_PORT, + IFLA_BRPORT_MRP_RING_OPEN, + IFLA_BRPORT_MRP_IN_OPEN, __IFLA_BRPORT_MAX }; #define IFLA_BRPORT_MAX (__IFLA_BRPORT_MAX - 1) @@ -461,6 +642,7 @@ enum { IFLA_MACSEC_REPLAY_PROTECT, IFLA_MACSEC_VALIDATION, IFLA_MACSEC_PAD, + IFLA_MACSEC_OFFLOAD, __IFLA_MACSEC_MAX, }; @@ -487,6 +669,7 @@ enum macsec_validation_type { enum macsec_offload { MACSEC_OFFLOAD_OFF = 0, MACSEC_OFFLOAD_PHY = 1, + MACSEC_OFFLOAD_MAC = 2, __MACSEC_OFFLOAD_END, MACSEC_OFFLOAD_MAX = __MACSEC_OFFLOAD_END - 1, }; @@ -901,7 +1084,14 @@ enum { #define IFLA_IPOIB_MAX (__IFLA_IPOIB_MAX - 1) -/* HSR section */ +/* HSR/PRP section, both uses same interface */ + +/* Different redundancy protocols for hsr device */ +enum { + HSR_PROTOCOL_HSR, + HSR_PROTOCOL_PRP, + HSR_PROTOCOL_MAX, +}; enum { IFLA_HSR_UNSPEC, @@ -911,6 +1101,9 @@ enum { IFLA_HSR_SUPERVISION_ADDR, /* Supervision frame multicast addr */ IFLA_HSR_SEQ_NR, IFLA_HSR_VERSION, /* HSR version */ + IFLA_HSR_PROTOCOL, /* Indicate different protocol than + * HSR. For example PRP. + */ __IFLA_HSR_MAX, }; @@ -970,11 +1163,12 @@ enum { #define XDP_FLAGS_SKB_MODE (1U << 1) #define XDP_FLAGS_DRV_MODE (1U << 2) #define XDP_FLAGS_HW_MODE (1U << 3) +#define XDP_FLAGS_REPLACE (1U << 4) #define XDP_FLAGS_MODES (XDP_FLAGS_SKB_MODE | \ XDP_FLAGS_DRV_MODE | \ XDP_FLAGS_HW_MODE) #define XDP_FLAGS_MASK (XDP_FLAGS_UPDATE_IF_NOEXIST | \ - XDP_FLAGS_MODES) + XDP_FLAGS_MODES | XDP_FLAGS_REPLACE) /* These are stored into IFLA_XDP_ATTACHED on dump. */ enum { @@ -994,6 +1188,7 @@ enum { IFLA_XDP_DRV_PROG_ID, IFLA_XDP_SKB_PROG_ID, IFLA_XDP_HW_PROG_ID, + IFLA_XDP_EXPECTED_FD, __IFLA_XDP_MAX, }; diff --git a/uapi/linux/net_tstamp.h b/uapi/linux/net_tstamp.h index f96e650..7ed0b3d 100644 --- a/uapi/linux/net_tstamp.h +++ b/uapi/linux/net_tstamp.h @@ -98,6 +98,9 @@ enum hwtstamp_tx_types { * receive a time stamp via the socket error queue. */ HWTSTAMP_TX_ONESTEP_P2P, + + /* add new constants above here */ + __HWTSTAMP_TX_CNT }; /* possible values for hwtstamp_config->rx_filter */ @@ -140,6 +143,9 @@ enum hwtstamp_rx_filters { /* NTP, UDP, all versions and packet modes */ HWTSTAMP_FILTER_NTP_ALL, + + /* add new constants above here */ + __HWTSTAMP_FILTER_CNT }; /* SCM_TIMESTAMPING_PKTINFO control message */ diff --git a/uapi/linux/netlink.h b/uapi/linux/netlink.h index 2c28d32..dfef006 100644 --- a/uapi/linux/netlink.h +++ b/uapi/linux/netlink.h @@ -129,6 +129,7 @@ struct nlmsgerr { * @NLMSGERR_ATTR_COOKIE: arbitrary subsystem specific cookie to * be used - in the success case - to identify a created * object or operation or similar (binary) + * @NLMSGERR_ATTR_POLICY: policy for a rejected attribute * @__NLMSGERR_ATTR_MAX: number of attributes * @NLMSGERR_ATTR_MAX: highest attribute number */ @@ -137,6 +138,7 @@ enum nlmsgerr_attrs { NLMSGERR_ATTR_MSG, NLMSGERR_ATTR_OFFS, NLMSGERR_ATTR_COOKIE, + NLMSGERR_ATTR_POLICY, __NLMSGERR_ATTR_MAX, NLMSGERR_ATTR_MAX = __NLMSGERR_ATTR_MAX - 1 @@ -245,4 +247,109 @@ struct nla_bitfield32 { __u32 selector; }; +/* + * policy descriptions - it's specific to each family how this is used + * Normally, it should be retrieved via a dump inside another attribute + * specifying where it applies. + */ + +/** + * enum netlink_attribute_type - type of an attribute + * @NL_ATTR_TYPE_INVALID: unused + * @NL_ATTR_TYPE_FLAG: flag attribute (present/not present) + * @NL_ATTR_TYPE_U8: 8-bit unsigned attribute + * @NL_ATTR_TYPE_U16: 16-bit unsigned attribute + * @NL_ATTR_TYPE_U32: 32-bit unsigned attribute + * @NL_ATTR_TYPE_U64: 64-bit unsigned attribute + * @NL_ATTR_TYPE_S8: 8-bit signed attribute + * @NL_ATTR_TYPE_S16: 16-bit signed attribute + * @NL_ATTR_TYPE_S32: 32-bit signed attribute + * @NL_ATTR_TYPE_S64: 64-bit signed attribute + * @NL_ATTR_TYPE_BINARY: binary data, min/max length may be specified + * @NL_ATTR_TYPE_STRING: string, min/max length may be specified + * @NL_ATTR_TYPE_NUL_STRING: NUL-terminated string, + * min/max length may be specified + * @NL_ATTR_TYPE_NESTED: nested, i.e. the content of this attribute + * consists of sub-attributes. The nested policy and maxtype + * inside may be specified. + * @NL_ATTR_TYPE_NESTED_ARRAY: nested array, i.e. the content of this + * attribute contains sub-attributes whose type is irrelevant + * (just used to separate the array entries) and each such array + * entry has attributes again, the policy for those inner ones + * and the corresponding maxtype may be specified. + * @NL_ATTR_TYPE_BITFIELD32: &struct nla_bitfield32 attribute + */ +enum netlink_attribute_type { + NL_ATTR_TYPE_INVALID, + + NL_ATTR_TYPE_FLAG, + + NL_ATTR_TYPE_U8, + NL_ATTR_TYPE_U16, + NL_ATTR_TYPE_U32, + NL_ATTR_TYPE_U64, + + NL_ATTR_TYPE_S8, + NL_ATTR_TYPE_S16, + NL_ATTR_TYPE_S32, + NL_ATTR_TYPE_S64, + + NL_ATTR_TYPE_BINARY, + NL_ATTR_TYPE_STRING, + NL_ATTR_TYPE_NUL_STRING, + + NL_ATTR_TYPE_NESTED, + NL_ATTR_TYPE_NESTED_ARRAY, + + NL_ATTR_TYPE_BITFIELD32, +}; + +/** + * enum netlink_policy_type_attr - policy type attributes + * @NL_POLICY_TYPE_ATTR_UNSPEC: unused + * @NL_POLICY_TYPE_ATTR_TYPE: type of the attribute, + * &enum netlink_attribute_type (U32) + * @NL_POLICY_TYPE_ATTR_MIN_VALUE_S: minimum value for signed + * integers (S64) + * @NL_POLICY_TYPE_ATTR_MAX_VALUE_S: maximum value for signed + * integers (S64) + * @NL_POLICY_TYPE_ATTR_MIN_VALUE_U: minimum value for unsigned + * integers (U64) + * @NL_POLICY_TYPE_ATTR_MAX_VALUE_U: maximum value for unsigned + * integers (U64) + * @NL_POLICY_TYPE_ATTR_MIN_LENGTH: minimum length for binary + * attributes, no minimum if not given (U32) + * @NL_POLICY_TYPE_ATTR_MAX_LENGTH: maximum length for binary + * attributes, no maximum if not given (U32) + * @NL_POLICY_TYPE_ATTR_POLICY_IDX: sub policy for nested and + * nested array types (U32) + * @NL_POLICY_TYPE_ATTR_POLICY_MAXTYPE: maximum sub policy + * attribute for nested and nested array types, this can + * in theory be < the size of the policy pointed to by + * the index, if limited inside the nesting (U32) + * @NL_POLICY_TYPE_ATTR_BITFIELD32_MASK: valid mask for the + * bitfield32 type (U32) + * @NL_POLICY_TYPE_ATTR_MASK: mask of valid bits for unsigned integers (U64) + * @NL_POLICY_TYPE_ATTR_PAD: pad attribute for 64-bit alignment + */ +enum netlink_policy_type_attr { + NL_POLICY_TYPE_ATTR_UNSPEC, + NL_POLICY_TYPE_ATTR_TYPE, + NL_POLICY_TYPE_ATTR_MIN_VALUE_S, + NL_POLICY_TYPE_ATTR_MAX_VALUE_S, + NL_POLICY_TYPE_ATTR_MIN_VALUE_U, + NL_POLICY_TYPE_ATTR_MAX_VALUE_U, + NL_POLICY_TYPE_ATTR_MIN_LENGTH, + NL_POLICY_TYPE_ATTR_MAX_LENGTH, + NL_POLICY_TYPE_ATTR_POLICY_IDX, + NL_POLICY_TYPE_ATTR_POLICY_MAXTYPE, + NL_POLICY_TYPE_ATTR_BITFIELD32_MASK, + NL_POLICY_TYPE_ATTR_PAD, + NL_POLICY_TYPE_ATTR_MASK, + + /* keep last */ + __NL_POLICY_TYPE_ATTR_MAX, + NL_POLICY_TYPE_ATTR_MAX = __NL_POLICY_TYPE_ATTR_MAX - 1 +}; + #endif /* __LINUX_NETLINK_H */ diff --git a/uapi/linux/rtnetlink.h b/uapi/linux/rtnetlink.h index 9d802cd..5ad84e6 100644 --- a/uapi/linux/rtnetlink.h +++ b/uapi/linux/rtnetlink.h @@ -257,12 +257,12 @@ enum { /* rtm_protocol */ -#define RTPROT_UNSPEC 0 -#define RTPROT_REDIRECT 1 /* Route installed by ICMP redirects; - not used by current IPv4 */ -#define RTPROT_KERNEL 2 /* Route installed by kernel */ -#define RTPROT_BOOT 3 /* Route installed during boot */ -#define RTPROT_STATIC 4 /* Route installed by administrator */ +#define RTPROT_UNSPEC 0 +#define RTPROT_REDIRECT 1 /* Route installed by ICMP redirects; + not used by current IPv4 */ +#define RTPROT_KERNEL 2 /* Route installed by kernel */ +#define RTPROT_BOOT 3 /* Route installed during boot */ +#define RTPROT_STATIC 4 /* Route installed by administrator */ /* Values of protocol >= RTPROT_STATIC are not interpreted by kernel; they are just passed from user and back as is. @@ -271,22 +271,23 @@ enum { avoid conflicts. */ -#define RTPROT_GATED 8 /* Apparently, GateD */ -#define RTPROT_RA 9 /* RDISC/ND router advertisements */ -#define RTPROT_MRT 10 /* Merit MRT */ -#define RTPROT_ZEBRA 11 /* Zebra */ -#define RTPROT_BIRD 12 /* BIRD */ -#define RTPROT_DNROUTED 13 /* DECnet routing daemon */ -#define RTPROT_XORP 14 /* XORP */ -#define RTPROT_NTK 15 /* Netsukuku */ -#define RTPROT_DHCP 16 /* DHCP client */ -#define RTPROT_MROUTED 17 /* Multicast daemon */ -#define RTPROT_BABEL 42 /* Babel daemon */ -#define RTPROT_BGP 186 /* BGP Routes */ -#define RTPROT_ISIS 187 /* ISIS Routes */ -#define RTPROT_OSPF 188 /* OSPF Routes */ -#define RTPROT_RIP 189 /* RIP Routes */ -#define RTPROT_EIGRP 192 /* EIGRP Routes */ +#define RTPROT_GATED 8 /* Apparently, GateD */ +#define RTPROT_RA 9 /* RDISC/ND router advertisements */ +#define RTPROT_MRT 10 /* Merit MRT */ +#define RTPROT_ZEBRA 11 /* Zebra */ +#define RTPROT_BIRD 12 /* BIRD */ +#define RTPROT_DNROUTED 13 /* DECnet routing daemon */ +#define RTPROT_XORP 14 /* XORP */ +#define RTPROT_NTK 15 /* Netsukuku */ +#define RTPROT_DHCP 16 /* DHCP client */ +#define RTPROT_MROUTED 17 /* Multicast daemon */ +#define RTPROT_KEEPALIVED 18 /* Keepalived daemon */ +#define RTPROT_BABEL 42 /* Babel daemon */ +#define RTPROT_BGP 186 /* BGP Routes */ +#define RTPROT_ISIS 187 /* ISIS Routes */ +#define RTPROT_OSPF 188 /* OSPF Routes */ +#define RTPROT_RIP 189 /* RIP Routes */ +#define RTPROT_EIGRP 192 /* EIGRP Routes */ /* rtm_scope @@ -609,11 +610,17 @@ enum { TCA_HW_OFFLOAD, TCA_INGRESS_BLOCK, TCA_EGRESS_BLOCK, + TCA_DUMP_FLAGS, __TCA_MAX }; #define TCA_MAX (__TCA_MAX - 1) +#define TCA_DUMP_FLAGS_TERSE (1 << 0) /* Means that in dump user gets only basic + * data necessary to identify the objects + * (handle, cookie, etc.) and stats. + */ + #define TCA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct tcmsg)))) #define TCA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct tcmsg)) @@ -769,6 +776,7 @@ enum { #define RTEXT_FILTER_BRVLAN (1 << 1) #define RTEXT_FILTER_BRVLAN_COMPRESSED (1 << 2) #define RTEXT_FILTER_SKIP_STATS (1 << 3) +#define RTEXT_FILTER_MRP (1 << 4) /* End of information exported to user level */ @@ -11,7 +11,7 @@ struct regs_line { #define VIOC_REGS_LINE_SIZE sizeof(struct regs_line) -int vioc_dump_regs(struct ethtool_drvinfo *info maybe_unused, +int vioc_dump_regs(struct ethtool_drvinfo *info __maybe_unused, struct ethtool_regs *regs) { unsigned int i; @@ -3,7 +3,7 @@ #include "internal.h" int -vmxnet3_dump_regs(struct ethtool_drvinfo *info maybe_unused, +vmxnet3_dump_regs(struct ethtool_drvinfo *info __maybe_unused, struct ethtool_regs *regs) { u32 *regs_buff = (u32 *)regs->data; |