diff options
Diffstat (limited to 'ethtool.c')
-rw-r--r-- | ethtool.c | 1966 |
1 files changed, 1344 insertions, 622 deletions
@@ -31,6 +31,7 @@ #include "internal.h" #include <string.h> +#include <strings.h> #include <stdlib.h> #include <sys/stat.h> #include <stdio.h> @@ -40,42 +41,23 @@ #include <sys/utsname.h> #include <limits.h> #include <ctype.h> +#include <inttypes.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> +#include <linux/ioctl.h> #include <linux/sockios.h> #include <linux/netlink.h> +#include "common.h" +#include "netlink/extapi.h" + #ifndef MAX_ADDR_LEN #define MAX_ADDR_LEN 32 #endif -#ifndef HAVE_NETIF_MSG -enum { - NETIF_MSG_DRV = 0x0001, - NETIF_MSG_PROBE = 0x0002, - NETIF_MSG_LINK = 0x0004, - NETIF_MSG_TIMER = 0x0008, - NETIF_MSG_IFDOWN = 0x0010, - NETIF_MSG_IFUP = 0x0020, - NETIF_MSG_RX_ERR = 0x0040, - NETIF_MSG_TX_ERR = 0x0080, - NETIF_MSG_TX_QUEUED = 0x0100, - NETIF_MSG_INTR = 0x0200, - NETIF_MSG_TX_DONE = 0x0400, - NETIF_MSG_RX_STATUS = 0x0800, - NETIF_MSG_PKTDATA = 0x1000, - NETIF_MSG_HW = 0x2000, - NETIF_MSG_WOL = 0x4000, -}; -#endif - -#ifndef NETLINK_GENERIC -#define NETLINK_GENERIC 16 -#endif - #define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c)) static void exit_bad_args(void) __attribute__((noreturn)); @@ -88,6 +70,16 @@ static void exit_bad_args(void) exit(1); } +static void exit_nlonly_param(const char *name) __attribute__((noreturn)); + +static void exit_nlonly_param(const char *name) +{ + fprintf(stderr, + "ethtool: parameter '%s' can be used only with netlink\n", + name); + exit(1); +} + typedef enum { CMDL_NONE, CMDL_BOOL, @@ -119,74 +111,6 @@ struct cmdline_info { void *seen_val; }; -struct flag_info { - const char *name; - u32 value; -}; - -static const struct flag_info flags_msglvl[] = { - { "drv", NETIF_MSG_DRV }, - { "probe", NETIF_MSG_PROBE }, - { "link", NETIF_MSG_LINK }, - { "timer", NETIF_MSG_TIMER }, - { "ifdown", NETIF_MSG_IFDOWN }, - { "ifup", NETIF_MSG_IFUP }, - { "rx_err", NETIF_MSG_RX_ERR }, - { "tx_err", NETIF_MSG_TX_ERR }, - { "tx_queued", NETIF_MSG_TX_QUEUED }, - { "intr", NETIF_MSG_INTR }, - { "tx_done", NETIF_MSG_TX_DONE }, - { "rx_status", NETIF_MSG_RX_STATUS }, - { "pktdata", NETIF_MSG_PKTDATA }, - { "hw", NETIF_MSG_HW }, - { "wol", NETIF_MSG_WOL }, -}; - -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 */ @@ -195,7 +119,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]; }; @@ -307,9 +231,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++) { @@ -375,7 +299,7 @@ static void parse_generic_cmdline(struct cmd_context *ctx, case CMDL_IP4: { u32 *p = info[idx].wanted_val; struct in_addr in; - if (!inet_aton(argp[i], &in)) + if (!inet_pton(AF_INET, argp[i], &in)) exit_bad_args(); *p = in.s_addr; break; @@ -424,26 +348,6 @@ static void flag_to_cmdline_info(const char *name, u32 value, cli->seen_val = mask; } -static void -print_flags(const struct flag_info *info, unsigned int n_info, u32 value) -{ - const char *sep = ""; - - while (n_info) { - if (value & info->value) { - printf("%s%s", sep, info->name); - sep = " "; - value &= ~info->value; - } - ++info; - --n_info; - } - - /* Print any unrecognised flags in hex */ - if (value) - printf("%s%#x", sep, value); -} - static int rxflow_str_to_type(const char *str) { int flow_type = 0; @@ -470,7 +374,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 @@ -496,9 +400,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, @@ -547,6 +451,38 @@ 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, + ETHTOOL_LINK_MODE_10baseT1L_Full_BIT, + ETHTOOL_LINK_MODE_800000baseCR8_Full_BIT, + ETHTOOL_LINK_MODE_800000baseKR8_Full_BIT, + ETHTOOL_LINK_MODE_800000baseDR8_Full_BIT, + ETHTOOL_LINK_MODE_800000baseDR8_2_Full_BIT, + ETHTOOL_LINK_MODE_800000baseSR8_Full_BIT, + ETHTOOL_LINK_MODE_800000baseVR8_Full_BIT, + ETHTOOL_LINK_MODE_10baseT1S_Full_BIT, + ETHTOOL_LINK_MODE_10baseT1S_Half_BIT, + ETHTOOL_LINK_MODE_10baseT1S_P2MP_Half_BIT, }; static const enum ethtool_link_mode_bit_indices additional_advertised_flags_bits[] = { @@ -562,6 +498,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; @@ -636,20 +573,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, @@ -742,10 +675,79 @@ 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" }, + { 0, ETHTOOL_LINK_MODE_10baseT1L_Full_BIT, + "10baseT1L/Full" }, + { 0, ETHTOOL_LINK_MODE_800000baseCR8_Full_BIT, + "800000baseCR8/Full" }, + { 0, ETHTOOL_LINK_MODE_800000baseKR8_Full_BIT, + "800000baseKR8/Full" }, + { 0, ETHTOOL_LINK_MODE_800000baseDR8_Full_BIT, + "800000baseDR8/Full" }, + { 0, ETHTOOL_LINK_MODE_800000baseDR8_2_Full_BIT, + "800000baseDR8_2/Full" }, + { 0, ETHTOOL_LINK_MODE_800000baseSR8_Full_BIT, + "800000baseSR8/Full" }, + { 0, ETHTOOL_LINK_MODE_800000baseVR8_Full_BIT, + "800000baseVR8/Full" }, + { 0, ETHTOOL_LINK_MODE_10baseT1S_Full_BIT, + "10baseT1S/Full" }, + { 1, ETHTOOL_LINK_MODE_10baseT1S_Half_BIT, + "10baseT1S/Half" }, + { 0, ETHTOOL_LINK_MODE_10baseT1S_P2MP_Half_BIT, + "10baseT1S/Half" }, }; 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; @@ -814,6 +816,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"); @@ -902,31 +910,9 @@ dump_link_usettings(const struct ethtool_link_usettings *link_usettings) (link_usettings->base.autoneg == AUTONEG_DISABLE) ? "off" : "on"); - if (link_usettings->base.port == PORT_TP) { - fprintf(stdout, " MDI-X: "); - if (link_usettings->base.eth_tp_mdix_ctrl == ETH_TP_MDI) { - fprintf(stdout, "off (forced)\n"); - } else if (link_usettings->base.eth_tp_mdix_ctrl - == ETH_TP_MDI_X) { - fprintf(stdout, "on (forced)\n"); - } else { - switch (link_usettings->base.eth_tp_mdix) { - case ETH_TP_MDI: - fprintf(stdout, "off"); - break; - case ETH_TP_MDI_X: - fprintf(stdout, "on"); - break; - default: - fprintf(stdout, "Unknown"); - break; - } - if (link_usettings->base.eth_tp_mdix_ctrl - == ETH_TP_MDI_AUTO) - fprintf(stdout, " (auto)"); - fprintf(stdout, "\n"); - } - } + if (link_usettings->base.port == PORT_TP) + dump_mdix(link_usettings->base.eth_tp_mdix, + link_usettings->base.eth_tp_mdix_ctrl); return 0; } @@ -998,58 +984,6 @@ static int parse_wolopts(char *optstr, u32 *data) return 0; } -static char *unparse_wolopts(int wolopts) -{ - static char buf[16]; - char *p = buf; - - memset(buf, 0, sizeof(buf)); - - if (wolopts) { - if (wolopts & WAKE_PHY) - *p++ = 'p'; - if (wolopts & WAKE_UCAST) - *p++ = 'u'; - if (wolopts & WAKE_MCAST) - *p++ = 'm'; - if (wolopts & WAKE_BCAST) - *p++ = 'b'; - if (wolopts & WAKE_ARP) - *p++ = 'a'; - if (wolopts & WAKE_MAGIC) - *p++ = 'g'; - if (wolopts & WAKE_MAGICSECURE) - *p++ = 's'; - if (wolopts & WAKE_FILTER) - *p++ = 'f'; - } else { - *p = 'd'; - } - - return buf; -} - -static int dump_wol(struct ethtool_wolinfo *wol) -{ - fprintf(stdout, " Supports Wake-on: %s\n", - unparse_wolopts(wol->supported)); - fprintf(stdout, " Wake-on: %s\n", - unparse_wolopts(wol->wolopts)); - if (wol->supported & WAKE_MAGICSECURE) { - int i; - int delim = 0; - - fprintf(stdout, " SecureOn password: "); - for (i = 0; i < SOPASS_MAX; i++) { - fprintf(stdout, "%s%02x", delim?":":"", wol->sopass[i]); - delim = 1; - } - fprintf(stdout, "\n"); - } - - return 0; -} - static int parse_rxfhashopts(char *optstr, u32 *data) { *data = 0; @@ -1183,12 +1117,12 @@ static int parse_hkey(char **rss_hkey, u32 key_size, return 0; } +#ifdef ETHTOOL_ENABLE_PRETTY_DUMP static const struct { const char *name; int (*func)(struct ethtool_drvinfo *info, struct ethtool_regs *regs); } driver_list[] = { -#ifdef ETHTOOL_ENABLE_PRETTY_DUMP { "8139cp", realtek_dump_regs }, { "8139too", realtek_dump_regs }, { "r8169", realtek_dump_regs }, @@ -1221,8 +1155,15 @@ static const struct { { "lan78xx", lan78xx_dump_regs }, { "dsa", dsa_dump_regs }, { "fec", fec_dump_regs }, -#endif + { "igc", igc_dump_regs }, + { "bnxt_en", bnxt_dump_regs }, + { "cpsw-switch", cpsw_dump_regs }, + { "lan743x", lan743x_dump_regs }, + { "fsl_enetc", fsl_enetc_dump_regs }, + { "fsl_enetc_vf", fsl_enetc_dump_regs }, + { "hns3", hns3_dump_regs }, }; +#endif void dump_hex(FILE *file, const u8 *data, int len, int offset) { @@ -1241,14 +1182,15 @@ 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; - if (gregs_dump_raw) { fwrite(regs->data, regs->len, 1, stdout); goto nested; } - if (!gregs_dump_hex) +#ifdef ETHTOOL_ENABLE_PRETTY_DUMP + if (!gregs_dump_hex) { + unsigned int i; + for (i = 0; i < ARRAY_SIZE(driver_list); i++) if (!strncmp(driver_list[i].name, info->driver, ETHTOOL_BUSINFO_LEN)) { @@ -1260,6 +1202,8 @@ static int dump_regs(int gregs_dump_raw, int gregs_dump_hex, */ break; } + } +#endif dump_hex(stdout, regs->data, regs->len, 0); @@ -1276,7 +1220,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) { @@ -1298,7 +1242,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"); @@ -1529,7 +1474,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; @@ -1545,12 +1490,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 */ @@ -1581,7 +1526,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 */ @@ -1696,6 +1641,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 @@ -1800,7 +1747,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; @@ -1834,8 +1783,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) { @@ -1867,7 +1816,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 = @@ -1989,10 +1938,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; @@ -2032,12 +1995,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; @@ -2101,12 +2082,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; @@ -2159,7 +2158,7 @@ static int do_gchannels(struct cmd_context *ctx) if (err) return err; } else { - perror("Cannot get device channel parameters\n"); + perror("Cannot get device channel parameters"); return 1; } return 0; @@ -2216,50 +2215,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) @@ -2315,7 +2402,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; @@ -2398,12 +2485,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) { @@ -2424,33 +2512,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) { @@ -2470,14 +2562,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) || @@ -2511,7 +2603,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) { @@ -2578,9 +2670,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; } @@ -2631,8 +2725,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; @@ -2837,8 +2931,7 @@ static int do_gset(struct cmd_context *ctx) fprintf(stdout, " Current message level: 0x%08x (%d)\n" " ", edata.data, edata.data); - print_flags(flags_msglvl, ARRAY_SIZE(flags_msglvl), - edata.data); + print_flags(flags_msglvl, n_flags_msglvl, edata.data); fprintf(stdout, "\n"); allfail = 0; } else if (errno != EOPNOTSUPP) { @@ -2884,13 +2977,13 @@ static int do_sset(struct cmd_context *ctx) int msglvl_changed = 0; u32 msglvl_wanted = 0; u32 msglvl_mask = 0; - struct cmdline_info cmdline_msglvl[ARRAY_SIZE(flags_msglvl)]; - int argc = ctx->argc; + struct cmdline_info cmdline_msglvl[n_flags_msglvl]; + unsigned int argc = ctx->argc; char **argp = ctx->argp; - int i; + unsigned int i; int err = 0; - for (i = 0; i < ARRAY_SIZE(flags_msglvl); i++) + for (i = 0; i < n_flags_msglvl; i++) flag_to_cmdline_info(flags_msglvl[i].name, flags_msglvl[i].value, &msglvl_wanted, &msglvl_mask, @@ -3019,6 +3112,8 @@ static int do_sset(struct cmd_context *ctx) ARRAY_SIZE(cmdline_msglvl)); break; } + } else if (!strcmp(argp[i], "master-slave")) { + exit_nlonly_param(argp[i]); } else { exit_bad_args(); } @@ -3071,6 +3166,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; @@ -3247,9 +3345,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; @@ -3343,11 +3453,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; @@ -3363,7 +3487,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) @@ -3393,16 +3517,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; @@ -3418,12 +3560,16 @@ static int do_seeprom(struct cmd_context *ctx) return 74; } - if (seeprom_value_seen) + if (seeprom_value_seen && !seeprom_length_seen) seeprom_length = 1; - - if (seeprom_length == -1) + else if (!seeprom_length_seen) seeprom_length = drvinfo.eedump_len; + if (seeprom_value_seen && (seeprom_length != 1)) { + fprintf(stderr, "value requires length 1\n"); + return 1; + } + if (drvinfo.eedump_len < seeprom_offset + seeprom_length) { fprintf(stderr, "offset & length out of bounds\n"); return 1; @@ -3760,27 +3906,6 @@ static int do_grxclass(struct cmd_context *ctx) return err ? 1 : 0; } -static void print_indir_table(struct cmd_context *ctx, - struct ethtool_rxnfc *ring_count, - u32 indir_size, u32 *indir) -{ - u32 i; - - printf("RX flow hash indirection table for %s with %llu RX ring(s):\n", - ctx->devname, ring_count->data); - - if (!indir_size) - printf("Operation not supported\n"); - - for (i = 0; i < indir_size; i++) { - if (i % 8 == 0) - printf("%5u: ", i); - printf(" %5u", indir[i]); - if (i % 8 == 7 || i == indir_size - 1) - fputc('\n', stdout); - } -} - static int do_grxfhindir(struct cmd_context *ctx, struct ethtool_rxnfc *ring_count) { @@ -3812,7 +3937,8 @@ static int do_grxfhindir(struct cmd_context *ctx, return 1; } - print_indir_table(ctx, ring_count, indir->size, indir->ring_index); + print_indir_table(ctx, ring_count->data, indir->size, + indir->ring_index); free(indir); return 0; @@ -3826,8 +3952,8 @@ static int do_grxfh(struct cmd_context *ctx) struct ethtool_rxfh *rss; u32 rss_context = 0; u32 i, indir_bytes; - int arg_num = 0; - char *hkey; + unsigned int arg_num = 0; + u8 *hkey; int err; while (arg_num < ctx->argc) { @@ -3877,21 +4003,13 @@ static int do_grxfh(struct cmd_context *ctx) return 1; } - print_indir_table(ctx, &ring_count, rss->indir_size, rss->rss_config); + print_indir_table(ctx, ring_count.data, rss->indir_size, + rss->rss_config); indir_bytes = rss->indir_size * sizeof(rss->rss_config[0]); - hkey = ((char *)rss->rss_config + indir_bytes); + hkey = ((u8 *)rss->rss_config + indir_bytes); - printf("RSS hash key:\n"); - if (!rss->key_size) - printf("Operation not supported\n"); - - for (i = 0; i < rss->key_size; i++) { - if (i == (rss->key_size - 1)) - printf("%02x\n", (u8) hkey[i]); - else - printf("%02x:", (u8) hkey[i]); - } + print_rss_hkey(hkey, rss->key_size); printf("RSS hash function:\n"); if (!rss->hfunc) { @@ -4024,7 +4142,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]); @@ -4290,7 +4408,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); @@ -4506,7 +4625,7 @@ static int do_getfwdump(struct cmd_context *ctx) err = send_ioctl(ctx, &edata); if (err < 0) { - perror("Can not get dump level\n"); + perror("Can not get dump level"); return 1; } if (dump_flag != ETHTOOL_GET_DUMP_DATA) { @@ -4516,14 +4635,14 @@ static int do_getfwdump(struct cmd_context *ctx) } data = calloc(1, offsetof(struct ethtool_dump, data) + edata.len); if (!data) { - perror("Can not allocate enough memory\n"); + perror("Can not allocate enough memory"); return 1; } data->cmd = ETHTOOL_GET_DUMP_DATA; data->len = edata.len; err = send_ioctl(ctx, data); if (err < 0) { - perror("Can not get dump data\n"); + perror("Can not get dump data"); err = 1; goto free; } @@ -4547,7 +4666,7 @@ static int do_setfwdump(struct cmd_context *ctx) dump.flag = dump_flag; err = send_ioctl(ctx, &dump); if (err < 0) { - perror("Can not set dump level\n"); + perror("Can not set dump level"); return 1; } return 0; @@ -4697,17 +4816,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, @@ -4725,7 +4862,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) @@ -4742,7 +4879,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; } @@ -4767,16 +4909,16 @@ static int do_getmodule(struct cmd_context *ctx) switch (modinfo.type) { #ifdef ETHTOOL_ENABLE_PRETTY_DUMP case ETH_MODULE_SFF_8079: - sff8079_show_all(eeprom->data); + sff8079_show_all_ioctl(eeprom->data); break; case ETH_MODULE_SFF_8472: - sff8079_show_all(eeprom->data); + sff8079_show_all_ioctl(eeprom->data); sff8472_show_all(eeprom->data); break; case ETH_MODULE_SFF_8436: case ETH_MODULE_SFF_8636: - sff8636_show_all(eeprom->data, - modinfo.eeprom_len); + sff8636_show_all_ioctl(eeprom->data, + modinfo.eeprom_len); break; #endif default: @@ -4819,10 +4961,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) @@ -4850,9 +5012,193 @@ 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_TX_COPYBREAK_BUF_SIZE] = "tx-buf-size", + [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, + }, + { .t_id = ETHTOOL_TX_COPYBREAK_BUF_SIZE, + .t_type_id = ETHTOOL_TUNABLE_U32, + .size = sizeof(u32), + .type = CMDL_U32, + }, +}; +#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]); + free(tuna); + 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); + free(tuna); + 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) @@ -4956,9 +5302,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(); @@ -5209,7 +5555,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; } @@ -5229,7 +5576,7 @@ static int do_gfec(struct cmd_context *ctx) } fprintf(stdout, "FEC parameters for %s:\n", ctx->devname); - fprintf(stdout, "Configured FEC encodings:"); + fprintf(stdout, "Supported/Configured FEC encodings:"); dump_fec(feccmd.fec); fprintf(stdout, "\n"); @@ -5245,7 +5592,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")) { @@ -5288,222 +5636,540 @@ int send_ioctl(struct cmd_context *ctx, void *cmd) static int show_usage(struct cmd_context *ctx); -static const struct option { - const char *opts; - int want_device; - int (*func)(struct cmd_context *); - char *help; - char *opthelp; -} args[] = { - { "-s|--change", 1, do_sset, "Change generic options", - " [ speed %d ]\n" - " [ duplex half|full ]\n" - " [ port tp|aui|bnc|mii|fibre ]\n" - " [ mdix auto|on|off ]\n" - " [ autoneg on|off ]\n" - " [ advertise %x ]\n" - " [ phyad %d ]\n" - " [ xcvr internal|external ]\n" - " [ wol p|u|m|b|a|g|s|f|d... ]\n" - " [ sopass %x:%x:%x:%x:%x:%x ]\n" - " [ msglvl %d | msglvl type on|off ... ]\n" }, - { "-a|--show-pause", 1, do_gpause, "Show pause options" }, - { "-A|--pause", 1, do_spause, "Set pause options", - " [ autoneg on|off ]\n" - " [ rx on|off ]\n" - " [ tx on|off ]\n" }, - { "-c|--show-coalesce", 1, do_gcoalesce, "Show coalesce options" }, - { "-C|--coalesce", 1, do_scoalesce, "Set coalesce options", - " [adaptive-rx on|off]\n" - " [adaptive-tx on|off]\n" - " [rx-usecs N]\n" - " [rx-frames N]\n" - " [rx-usecs-irq N]\n" - " [rx-frames-irq N]\n" - " [tx-usecs N]\n" - " [tx-frames N]\n" - " [tx-usecs-irq N]\n" - " [tx-frames-irq N]\n" - " [stats-block-usecs N]\n" - " [pkt-rate-low N]\n" - " [rx-usecs-low N]\n" - " [rx-frames-low N]\n" - " [tx-usecs-low N]\n" - " [tx-frames-low N]\n" - " [pkt-rate-high N]\n" - " [rx-usecs-high N]\n" - " [rx-frames-high N]\n" - " [tx-usecs-high N]\n" - " [tx-frames-high N]\n" - " [sample-interval N]\n" }, - { "-g|--show-ring", 1, do_gring, "Query RX/TX ring parameters" }, - { "-G|--set-ring", 1, do_sring, "Set RX/TX ring parameters", - " [ rx N ]\n" - " [ rx-mini N ]\n" - " [ rx-jumbo N ]\n" - " [ tx N ]\n" }, - { "-k|--show-features|--show-offload", 1, do_gfeatures, - "Get state of protocol offload and other features" }, - { "-K|--features|--offload", 1, do_sfeatures, - "Set protocol offload and other features", - " FEATURE on|off ...\n" }, - { "-i|--driver", 1, do_gdrv, "Show driver information" }, - { "-d|--register-dump", 1, do_gregs, "Do a register dump", - " [ raw on|off ]\n" - " [ file FILENAME ]\n" }, - { "-e|--eeprom-dump", 1, do_geeprom, "Do a EEPROM dump", - " [ raw on|off ]\n" - " [ offset N ]\n" - " [ length N ]\n" }, - { "-E|--change-eeprom", 1, do_seeprom, - "Change bytes in device EEPROM", - " [ magic N ]\n" - " [ offset N ]\n" - " [ length N ]\n" - " [ value N ]\n" }, - { "-r|--negotiate", 1, do_nway_rst, "Restart N-WAY negotiation" }, - { "-p|--identify", 1, do_phys_id, - "Show visible port identification (e.g. blinking)", - " [ TIME-IN-SECONDS ]\n" }, - { "-t|--test", 1, do_test, "Execute adapter self test", - " [ online | offline | external_lb ]\n" }, - { "-S|--statistics", 1, do_gnicstats, "Show adapter statistics" }, - { "--phy-statistics", 1, do_gphystats, - "Show phy statistics" }, - { "-n|-u|--show-nfc|--show-ntuple", 1, do_grxclass, - "Show Rx network flow classification options or rules", - " [ rx-flow-hash tcp4|udp4|ah4|esp4|sctp4|" - "tcp6|udp6|ah6|esp6|sctp6 [context %d] |\n" - " rule %d ]\n" }, - { "-N|-U|--config-nfc|--config-ntuple", 1, do_srxclass, - "Configure Rx network flow classification options or rules", - " rx-flow-hash tcp4|udp4|ah4|esp4|sctp4|" - "tcp6|udp6|ah6|esp6|sctp6 m|v|t|s|d|f|n|r... [context %d] |\n" - " flow-type ether|ip4|tcp4|udp4|sctp4|ah4|esp4|" - "ip6|tcp6|udp6|ah6|esp6|sctp6\n" - " [ src %x:%x:%x:%x:%x:%x [m %x:%x:%x:%x:%x:%x] ]\n" - " [ dst %x:%x:%x:%x:%x:%x [m %x:%x:%x:%x:%x:%x] ]\n" - " [ proto %d [m %x] ]\n" - " [ src-ip IP-ADDRESS [m IP-ADDRESS] ]\n" - " [ dst-ip IP-ADDRESS [m IP-ADDRESS] ]\n" - " [ tos %d [m %x] ]\n" - " [ tclass %d [m %x] ]\n" - " [ l4proto %d [m %x] ]\n" - " [ src-port %d [m %x] ]\n" - " [ dst-port %d [m %x] ]\n" - " [ spi %d [m %x] ]\n" - " [ vlan-etype %x [m %x] ]\n" - " [ vlan %x [m %x] ]\n" - " [ user-def %x [m %x] ]\n" - " [ dst-mac %x:%x:%x:%x:%x:%x [m %x:%x:%x:%x:%x:%x] ]\n" - " [ action %d ] | [ vf %d queue %d ]\n" - " [ context %d ]\n" - " [ loc %d]] |\n" - " delete %d\n" }, - { "-T|--show-time-stamping", 1, do_tsinfo, - "Show time stamping capabilities" }, - { "-x|--show-rxfh-indir|--show-rxfh", 1, do_grxfh, - "Show Rx flow hash indirection table and/or RSS hash key", - " [ context %d ]\n" }, - { "-X|--set-rxfh-indir|--rxfh", 1, do_srxfh, - "Set Rx flow hash indirection table and/or RSS hash key", - " [ context %d|new ]\n" - " [ equal N | weight W0 W1 ... | default ]\n" - " [ hkey %x:%x:%x:%x:%x:.... ]\n" - " [ hfunc FUNC ]\n" - " [ delete ]\n" }, - { "-f|--flash", 1, do_flash, - "Flash firmware image from the specified file to a region on the device", - " FILENAME [ REGION-NUMBER-TO-FLASH ]\n" }, - { "-P|--show-permaddr", 1, do_permaddr, - "Show permanent hardware address" }, - { "-w|--get-dump", 1, do_getfwdump, - "Get dump flag, data", - " [ data FILENAME ]\n" }, - { "-W|--set-dump", 1, do_setfwdump, - "Set dump flag of the device", - " N\n"}, - { "-l|--show-channels", 1, do_gchannels, "Query Channels" }, - { "-L|--set-channels", 1, do_schannels, "Set Channels", - " [ rx N ]\n" - " [ tx N ]\n" - " [ other N ]\n" - " [ combined N ]\n" }, - { "--show-priv-flags", 1, do_gprivflags, "Query private flags" }, - { "--set-priv-flags", 1, do_sprivflags, "Set private flags", - " FLAG on|off ...\n" }, - { "-m|--dump-module-eeprom|--module-info", 1, do_getmodule, - "Query/Decode Module EEPROM information and optical diagnostics if available", - " [ raw on|off ]\n" - " [ hex on|off ]\n" - " [ offset N ]\n" - " [ length N ]\n" }, - { "--show-eee", 1, do_geee, "Show EEE settings"}, - { "--set-eee", 1, do_seee, "Set EEE settings", - " [ eee on|off ]\n" - " [ advertise %x ]\n" - " [ tx-lpi on|off ]\n" - " [ tx-timer %d ]\n"}, - { "--set-phy-tunable", 1, do_set_phy_tunable, "Set PHY tunable", - " [ downshift on|off [count N] ]\n" - " [ fast-link-down on|off [msecs N] ]\n" - " [ energy-detect-power-down on|off [msecs N] ]\n"}, - { "--get-phy-tunable", 1, do_get_phy_tunable, "Get PHY tunable", - " [ downshift ]\n" - " [ fast-link-down ]\n" - " [ energy-detect-power-down ]\n"}, - { "--reset", 1, do_reset, "Reset components", - " [ flags %x ]\n" - " [ mgmt ]\n" - " [ mgmt-shared ]\n" - " [ irq ]\n" - " [ irq-shared ]\n" - " [ dma ]\n" - " [ dma-shared ]\n" - " [ filter ]\n" - " [ filter-shared ]\n" - " [ offload ]\n" - " [ offload-shared ]\n" - " [ mac ]\n" - " [ mac-shared ]\n" - " [ phy ]\n" - " [ phy-shared ]\n" - " [ ram ]\n" - " [ ram-shared ]\n" - " [ ap ]\n" - " [ ap-shared ]\n" - " [ dedicated ]\n" - " [ all ]\n"}, - { "--show-fec", 1, do_gfec, "Show FEC settings"}, - { "--set-fec", 1, do_sfec, "Set FEC settings", - " [ encoding auto|off|rs|baser [...]]\n"}, - { "-Q|--per-queue", 1, do_perqueue, "Apply per-queue command." - "The supported sub commands include --show-coalesce, --coalesce", - " [queue_mask %x] SUB_COMMAND\n"}, - { "-h|--help", 0, show_usage, "Show this help" }, - { "--version", 0, do_version, "Show version number" }, +struct option { + const char *opts; + bool no_dev; + bool json; + int (*func)(struct cmd_context *); + nl_chk_t nlchk; + nl_func_t nlfunc; + const char *help; + const char *xhelp; +}; + +static const struct option args[] = { + { + /* "default" entry when no switch is used */ + .opts = "", + .func = do_gset, + .nlfunc = nl_gset, + .help = "Display standard information about device", + }, + { + .opts = "-s|--change", + .func = do_sset, + .nlfunc = nl_sset, + .help = "Change generic options", + .xhelp = " [ speed %d ]\n" + " [ lanes %d ]\n" + " [ duplex half|full ]\n" + " [ port tp|aui|bnc|mii|fibre|da ]\n" + " [ mdix auto|on|off ]\n" + " [ autoneg on|off ]\n" + " [ advertise %x[/%x] | mode on|off ... [--] ]\n" + " [ phyad %d ]\n" + " [ xcvr internal|external ]\n" + " [ 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 preferred-master|preferred-slave|forced-master|forced-slave ]\n" + }, + { + .opts = "-a|--show-pause", + .json = true, + .func = do_gpause, + .nlfunc = nl_gpause, + .help = "Show pause options", + .xhelp = " [ --src aggregate | emac | pmac ]\n" + }, + { + .opts = "-A|--pause", + .func = do_spause, + .nlfunc = nl_spause, + .help = "Set pause options", + .xhelp = " [ autoneg on|off ]\n" + " [ rx on|off ]\n" + " [ tx on|off ]\n" + }, + { + .opts = "-c|--show-coalesce", + .json = true, + .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" + " [rx-usecs N]\n" + " [rx-frames N]\n" + " [rx-usecs-irq N]\n" + " [rx-frames-irq N]\n" + " [tx-usecs N]\n" + " [tx-frames N]\n" + " [tx-usecs-irq N]\n" + " [tx-frames-irq N]\n" + " [stats-block-usecs N]\n" + " [pkt-rate-low N]\n" + " [rx-usecs-low N]\n" + " [rx-frames-low N]\n" + " [tx-usecs-low N]\n" + " [tx-frames-low N]\n" + " [pkt-rate-high N]\n" + " [rx-usecs-high N]\n" + " [rx-frames-high N]\n" + " [tx-usecs-high N]\n" + " [tx-frames-high N]\n" + " [sample-interval N]\n" + " [cqe-mode-rx on|off]\n" + " [cqe-mode-tx on|off]\n" + " [tx-aggr-max-bytes N]\n" + " [tx-aggr-max-frames N]\n" + " [tx-aggr-time-usecs N]\n" + }, + { + .opts = "-g|--show-ring", + .json = true, + .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" + " [ rx-jumbo N ]\n" + " [ tx N ]\n" + " [ rx-buf-len N ]\n" + " [ cqe-size N ]\n" + " [ tx-push on|off ]\n" + " [ rx-push on|off ]\n" + " [ tx-push-buf-len N]\n" + }, + { + .opts = "-k|--show-features|--show-offload", + .json = true, + .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" + }, + { + .opts = "-i|--driver", + .func = do_gdrv, + .help = "Show driver information" + }, + { + .opts = "-d|--register-dump", + .func = do_gregs, + .help = "Do a register dump", + .xhelp = " [ raw on|off ]\n" + " [ file FILENAME ]\n" + }, + { + .opts = "-e|--eeprom-dump", + .func = do_geeprom, + .help = "Do a EEPROM dump", + .xhelp = " [ raw on|off ]\n" + " [ offset N ]\n" + " [ length N ]\n" + }, + { + .opts = "-E|--change-eeprom", + .func = do_seeprom, + .help = "Change bytes in device EEPROM", + .xhelp = " [ magic N ]\n" + " [ offset N ]\n" + " [ length N ]\n" + " [ value N ]\n" + }, + { + .opts = "-r|--negotiate", + .func = do_nway_rst, + .help = "Restart N-WAY negotiation" + }, + { + .opts = "-p|--identify", + .func = do_phys_id, + .help = "Show visible port identification (e.g. blinking)", + .xhelp = " [ TIME-IN-SECONDS ]\n" + }, + { + .opts = "-t|--test", + .func = do_test, + .help = "Execute adapter self test", + .xhelp = " [ online | offline | external_lb ]\n" + }, + { + .opts = "-S|--statistics", + .json = true, + .func = do_gnicstats, + .nlchk = nl_gstats_chk, + .nlfunc = nl_gstats, + .help = "Show adapter statistics", + .xhelp = " [ --all-groups | --groups [eth-phy] [eth-mac] [eth-ctrl] [rmon] ]\n" + " [ --src aggregate | emac | pmac ]\n" + }, + { + .opts = "--phy-statistics", + .func = do_gphystats, + .help = "Show phy statistics" + }, + { + .opts = "-n|-u|--show-nfc|--show-ntuple", + .func = do_grxclass, + .help = "Show Rx network flow classification options or rules", + .xhelp = " [ rx-flow-hash tcp4|udp4|ah4|esp4|sctp4|" + "tcp6|udp6|ah6|esp6|sctp6 [context %d] |\n" + " rule %d ]\n" + }, + { + .opts = "-N|-U|--config-nfc|--config-ntuple", + .func = do_srxclass, + .help = "Configure Rx network flow classification options or rules", + .xhelp = " rx-flow-hash tcp4|udp4|ah4|esp4|sctp4|" + "tcp6|udp6|ah6|esp6|sctp6 m|v|t|s|d|f|n|r... [context %d] |\n" + " flow-type ether|ip4|tcp4|udp4|sctp4|ah4|esp4|" + "ip6|tcp6|udp6|ah6|esp6|sctp6\n" + " [ src %x:%x:%x:%x:%x:%x [m %x:%x:%x:%x:%x:%x] ]\n" + " [ dst %x:%x:%x:%x:%x:%x [m %x:%x:%x:%x:%x:%x] ]\n" + " [ proto %d [m %x] ]\n" + " [ src-ip IP-ADDRESS [m IP-ADDRESS] ]\n" + " [ dst-ip IP-ADDRESS [m IP-ADDRESS] ]\n" + " [ tos %d [m %x] ]\n" + " [ tclass %d [m %x] ]\n" + " [ l4proto %d [m %x] ]\n" + " [ src-port %d [m %x] ]\n" + " [ dst-port %d [m %x] ]\n" + " [ spi %d [m %x] ]\n" + " [ vlan-etype %x [m %x] ]\n" + " [ vlan %x [m %x] ]\n" + " [ user-def %x [m %x] ]\n" + " [ dst-mac %x:%x:%x:%x:%x:%x [m %x:%x:%x:%x:%x:%x] ]\n" + " [ action %d ] | [ vf %d queue %d ]\n" + " [ context %d ]\n" + " [ loc %d ] |\n" + " delete %d\n" + }, + { + .opts = "-T|--show-time-stamping", + .func = do_tsinfo, + .nlfunc = nl_tsinfo, + .help = "Show time stamping capabilities" + }, + { + .opts = "-x|--show-rxfh-indir|--show-rxfh", + .json = true, + .func = do_grxfh, + .nlfunc = nl_grss, + .help = "Show Rx flow hash indirection table and/or RSS hash key", + .xhelp = " [ context %d ]\n" + }, + { + .opts = "-X|--set-rxfh-indir|--rxfh", + .func = do_srxfh, + .help = "Set Rx flow hash indirection table and/or RSS hash key", + .xhelp = " [ context %d|new ]\n" + " [ equal N | weight W0 W1 ... | default ]\n" + " [ hkey %x:%x:%x:%x:%x:.... ]\n" + " [ hfunc FUNC ]\n" + " [ delete ]\n" + }, + { + .opts = "-f|--flash", + .func = do_flash, + .help = "Flash firmware image from the specified file to a region on the device", + .xhelp = " FILENAME [ REGION-NUMBER-TO-FLASH ]\n" + }, + { + .opts = "-P|--show-permaddr", + .func = do_permaddr, + .nlfunc = nl_permaddr, + .help = "Show permanent hardware address" + }, + { + .opts = "-w|--get-dump", + .func = do_getfwdump, + .help = "Get dump flag, data", + .xhelp = " [ data FILENAME ]\n" + }, + { + .opts = "-W|--set-dump", + .func = do_setfwdump, + .help = "Set dump flag of the device", + .xhelp = " N\n" + }, + { + .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" + " [ other N ]\n" + " [ combined N ]\n" + }, + { + .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" + }, + { + .opts = "-m|--dump-module-eeprom|--module-info", + .func = do_getmodule, + .nlfunc = nl_getmodule, + .help = "Query/Decode Module EEPROM information and optical diagnostics if available", + .xhelp = " [ raw on|off ]\n" + " [ hex on|off ]\n" + " [ offset N ]\n" + " [ length N ]\n" + " [ page N ]\n" + " [ bank N ]\n" + " [ i2c N ]\n" + }, + { + .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" + " [ tx-lpi on|off ]\n" + " [ tx-timer %d ]\n" + }, + { + .opts = "--set-phy-tunable", + .func = do_set_phy_tunable, + .help = "Set PHY tunable", + .xhelp = " [ downshift on|off [count N] ]\n" + " [ fast-link-down on|off [msecs N] ]\n" + " [ energy-detect-power-down on|off [msecs N] ]\n" + }, + { + .opts = "--get-phy-tunable", + .func = do_get_phy_tunable, + .help = "Get PHY tunable", + .xhelp = " [ downshift ]\n" + " [ fast-link-down ]\n" + " [ energy-detect-power-down ]\n" + }, + { + .opts = "--get-tunable", + .func = do_gtunable, + .help = "Get tunable", + .xhelp = " [ rx-copybreak ]\n" + " [ tx-copybreak ]\n" + " [ tx-buf-size ]\n" + " [ pfc-prevention-tout ]\n" + }, + { + .opts = "--set-tunable", + .func = do_stunable, + .help = "Set tunable", + .xhelp = " [ rx-copybreak N ]\n" + " [ tx-copybreak N ]\n" + " [ tx-buf-size N ]\n" + " [ pfc-prevention-tout N ]\n" + }, + { + .opts = "--reset", + .func = do_reset, + .help = "Reset components", + .xhelp = " [ flags %x ]\n" + " [ mgmt ]\n" + " [ mgmt-shared ]\n" + " [ irq ]\n" + " [ irq-shared ]\n" + " [ dma ]\n" + " [ dma-shared ]\n" + " [ filter ]\n" + " [ filter-shared ]\n" + " [ offload ]\n" + " [ offload-shared ]\n" + " [ mac ]\n" + " [ mac-shared ]\n" + " [ phy ]\n" + " [ phy-shared ]\n" + " [ ram ]\n" + " [ ram-shared ]\n" + " [ ap ]\n" + " [ ap-shared ]\n" + " [ dedicated ]\n" + " [ all ]\n" + }, + { + .opts = "--show-fec", + .json = true, + .func = do_gfec, + .nlfunc = nl_gfec, + .help = "Show FEC settings", + }, + { + .opts = "--set-fec", + .func = do_sfec, + .nlfunc = nl_sfec, + .help = "Set FEC settings", + .xhelp = " [ encoding auto|off|rs|baser|llrs [...] ]\n" + }, + { + .opts = "-Q|--per-queue", + .func = do_perqueue, + .help = "Apply per-queue command. ", + .xhelp = "The supported sub commands include --show-coalesce, --coalesce" + " [queue_mask %x] SUB_COMMAND\n", + }, + { + .opts = "--cable-test", + .json = true, + .nlfunc = nl_cable_test, + .help = "Perform a cable test", + }, + { + .opts = "--cable-test-tdr", + .json = true, + .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 = "--show-module", + .json = true, + .nlfunc = nl_gmodule, + .help = "Show transceiver module settings", + }, + { + .opts = "--set-module", + .nlfunc = nl_smodule, + .help = "Set transceiver module settings", + .xhelp = " [ power-mode-policy high|auto ]\n" + }, + { + .opts = "--get-plca-cfg", + .nlfunc = nl_plca_get_cfg, + .help = "Get PLCA configuration", + }, + { + .opts = "--set-plca-cfg", + .nlfunc = nl_plca_set_cfg, + .help = "Set PLCA configuration", + .xhelp = " [ enable on|off ]\n" + " [ node-id N ]\n" + " [ node-cnt N ]\n" + " [ to-tmr N ]\n" + " [ burst-cnt N ]\n" + " [ burst-tmr N ]\n" + }, + { + .opts = "--get-plca-status", + .nlfunc = nl_plca_get_status, + .help = "Get PLCA status information", + }, + { + .opts = "--show-mm", + .json = true, + .nlfunc = nl_get_mm, + .help = "Show MAC merge layer state", + }, + { + .opts = "--set-mm", + .nlfunc = nl_set_mm, + .help = "Set MAC merge layer parameters", + " [ verify-enabled on|off ]\n" + " [ verify-time N ]\n" + " [ tx-enabled on|off ]\n" + " [ pmac-enabled on|off ]\n" + " [ tx-min-frag-size 60-252 ]\n" + }, + { + .opts = "--show-pse", + .json = true, + .nlfunc = nl_gpse, + .help = "Show settings for Power Sourcing Equipment", + }, + { + .opts = "--set-pse", + .nlfunc = nl_spse, + .help = "Set Power Sourcing Equipment settings", + .xhelp = " [ podl-pse-admin-control enable|disable ]\n" + }, + { + .opts = "-h|--help", + .no_dev = true, + .func = show_usage, + .help = "Show this help" + }, + { + .opts = "--version", + .no_dev = true, + .func = do_version, + .help = "Show version number" + }, {} }; -static int show_usage(struct cmd_context *ctx maybe_unused) +static int show_usage(struct cmd_context *ctx __maybe_unused) { int i; /* ethtool -h */ fprintf(stdout, PACKAGE " version " VERSION "\n"); - fprintf(stdout, - "Usage:\n" - " ethtool DEVNAME\t" - "Display standard information about device\n"); + fprintf(stdout, "Usage:\n"); for (i = 0; args[i].opts; i++) { - fputs(" ethtool ", stdout); + fputs(" ethtool [ FLAGS ] ", stdout); fprintf(stdout, "%s %s\t%s\n", args[i].opts, - args[i].want_device ? "DEVNAME" : "\t", + args[i].no_dev ? "\t" : "DEVNAME", args[i].help); - if (args[i].opthelp) - fputs(args[i].opthelp, stdout); + if (args[i].xhelp) + 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; } @@ -5514,7 +6180,7 @@ static int find_option(char *arg) size_t len; int k; - for (k = 0; args[k].opts; k++) { + for (k = 1; args[k].opts; k++) { opt = args[k].opts; for (;;) { len = strcspn(opt, "|"); @@ -5647,6 +6313,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, @@ -5699,67 +6367,121 @@ static int do_perqueue(struct cmd_context *ctx) return 0; } +static int ioctl_init(struct cmd_context *ctx, bool no_dev) +{ + if (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)); + strcpy(ctx->ifr.ifr_name, ctx->devname); + + /* Open control socket. */ + ctx->fd = socket(AF_INET, SOCK_DGRAM, 0); + if (ctx->fd < 0) + ctx->fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC); + if (ctx->fd < 0) { + perror("Cannot get control socket"); + return 70; + } + + return 0; +} + int main(int argc, char **argp) { - int (*func)(struct cmd_context *); - int want_device; - struct cmd_context ctx; + struct cmd_context ctx = {}; + int ret; int k; init_global_link_mode_masks(); + if (argc < 2) + exit_bad_args(); + /* Skip command name */ argp++; argc--; + 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(); + + 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; + } + if (*argp && !strcmp(*argp, "--monitor")) { + ctx.argp = ++argp; + ctx.argc = --argc; + ret = nl_monitor(&ctx); + return ret ? 1 : 0; + } + /* First argument must be either a valid option or a device * name to get settings for (which we don't expect to begin * with '-'). */ - if (argc == 0) + if (!*argp) exit_bad_args(); k = find_option(*argp); - if (k >= 0) { + if (k > 0) { argp++; argc--; - func = args[k].func; - want_device = args[k].want_device; - goto opt_found; + } else { + if ((*argp)[0] == '-') + exit_bad_args(); + k = 0; } - if ((*argp)[0] == '-') - exit_bad_args(); - func = do_gset; - want_device = 1; -opt_found: - if (want_device) { + if (!args[k].no_dev) { ctx.devname = *argp++; argc--; - if (ctx.devname == NULL) + if (!ctx.devname) exit_bad_args(); - if (strlen(ctx.devname) >= IFNAMSIZ) - exit_bad_args(); - - /* Setup our control structures. */ - memset(&ctx.ifr, 0, sizeof(ctx.ifr)); - strcpy(ctx.ifr.ifr_name, ctx.devname); - - /* Open control socket. */ - ctx.fd = socket(AF_INET, SOCK_DGRAM, 0); - if (ctx.fd < 0) - ctx.fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC); - if (ctx.fd < 0) { - perror("Cannot get control socket"); - return 70; - } - } else { - ctx.fd = -1; } - + if (ctx.json && !args[k].json) + exit_bad_args(); ctx.argc = argc; ctx.argp = argp; + netlink_run_handler(&ctx, args[k].nlchk, args[k].nlfunc, !args[k].func); + + if (ctx.json) /* no IOCTL command supports JSON output */ + exit_bad_args(); + + ret = ioctl_init(&ctx, args[k].no_dev); + if (ret) + return ret; - return func(&ctx); + return args[k].func(&ctx); } |