diff options
author | Yu Watanabe <watanabe.yu+github@gmail.com> | 2019-06-20 06:56:37 +0900 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-06-20 06:56:37 +0900 |
commit | b19eab1f74f5105fc5737b3790071175ea1a9292 (patch) | |
tree | aaf07ce7487b224143688e24b89bc9c88a821585 | |
parent | f9dc94408d70dd2f44915f4c6d67dc498c1c6243 (diff) | |
parent | c967d2c7ce19a460a3d6b63ef6a0b752f5427a1a (diff) | |
download | systemd-b19eab1f74f5105fc5737b3790071175ea1a9292.tar.gz |
Merge pull request #12806 from yuwata/networkctl-ethtool-12657
networkctl: show speed, duplex, auto negotiation, and port
-rw-r--r-- | src/basic/format-util.c | 60 | ||||
-rw-r--r-- | src/basic/format-util.h | 13 | ||||
-rw-r--r-- | src/basic/parse-util.c | 41 | ||||
-rw-r--r-- | src/basic/parse-util.h | 3 | ||||
-rw-r--r-- | src/import/import-fs.c | 2 | ||||
-rw-r--r-- | src/import/pull-job.c | 1 | ||||
-rw-r--r-- | src/journal/journal-file.c | 2 | ||||
-rw-r--r-- | src/journal/journal-vacuum.c | 2 | ||||
-rw-r--r-- | src/journal/journalctl.c | 1 | ||||
-rw-r--r-- | src/network/networkctl.c | 122 | ||||
-rw-r--r-- | src/network/networkd-link-bus.c | 17 | ||||
-rw-r--r-- | src/shared/ethtool-util.c (renamed from src/udev/net/ethtool-util.c) | 255 | ||||
-rw-r--r-- | src/shared/ethtool-util.h (renamed from src/udev/net/ethtool-util.h) | 11 | ||||
-rw-r--r-- | src/shared/format-table.c | 23 | ||||
-rw-r--r-- | src/shared/format-table.h | 1 | ||||
-rw-r--r-- | src/shared/meson.build | 2 | ||||
-rw-r--r-- | src/test/meson.build | 4 | ||||
-rw-r--r-- | src/test/test-btrfs.c | 2 | ||||
-rw-r--r-- | src/test/test-format-util.c | 38 | ||||
-rw-r--r-- | src/test/test-procfs-util.c | 2 | ||||
-rw-r--r-- | src/udev/meson.build | 2 | ||||
-rw-r--r-- | src/udev/net/link-config-gperf.gperf | 10 | ||||
-rw-r--r-- | src/udev/net/link-config.c | 4 | ||||
-rw-r--r-- | src/udev/net/link-config.h | 2 |
24 files changed, 406 insertions, 214 deletions
diff --git a/src/basic/format-util.c b/src/basic/format-util.c index 39ef2fceef..4231fa8d3f 100644 --- a/src/basic/format-util.c +++ b/src/basic/format-util.c @@ -1,5 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ +#include <stdio.h> + #include "format-util.h" #include "memory-util.h" @@ -8,3 +10,61 @@ char *format_ifname(int ifindex, char buf[static IF_NAMESIZE + 1]) { memzero(buf, IF_NAMESIZE + 1); return if_indextoname(ifindex, buf); } + +char *format_bytes_full(char *buf, size_t l, uint64_t t, FormatBytesFlag flag) { + typedef struct { + const char *suffix; + uint64_t factor; + } suffix_table; + static const suffix_table table_iec[] = { + { "E", UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024) }, + { "P", UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024) }, + { "T", UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024) }, + { "G", UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024) }, + { "M", UINT64_C(1024)*UINT64_C(1024) }, + { "K", UINT64_C(1024) }, + }, table_non_iec[] = { + { "E", UINT64_C(1000)*UINT64_C(1000)*UINT64_C(1000)*UINT64_C(1000)*UINT64_C(1000)*UINT64_C(1000) }, + { "P", UINT64_C(1000)*UINT64_C(1000)*UINT64_C(1000)*UINT64_C(1000)*UINT64_C(1000) }, + { "T", UINT64_C(1000)*UINT64_C(1000)*UINT64_C(1000)*UINT64_C(1000) }, + { "G", UINT64_C(1000)*UINT64_C(1000)*UINT64_C(1000) }, + { "M", UINT64_C(1000)*UINT64_C(1000) }, + { "K", UINT64_C(1000) }, + }; + const suffix_table *table; + size_t n, i; + + assert_cc(ELEMENTSOF(table_iec) == ELEMENTSOF(table_non_iec)); + + if (t == (uint64_t) -1) + return NULL; + + table = flag & FORMAT_BYTES_USE_IEC ? table_iec : table_non_iec; + n = ELEMENTSOF(table_iec); + + for (i = 0; i < n; i++) + if (t >= table[i].factor) { + if (flag & FORMAT_BYTES_BELOW_POINT) { + snprintf(buf, l, + "%" PRIu64 ".%" PRIu64 "%s", + t / table[i].factor, + i != n - 1 ? + (t / table[i + 1].factor * UINT64_C(10) / table[n - 1].factor) % UINT64_C(10): + (t * UINT64_C(10) / table[i].factor) % UINT64_C(10), + table[i].suffix); + } else + snprintf(buf, l, + "%" PRIu64 "%s", + t / table[i].factor, + table[i].suffix); + + goto finish; + } + + snprintf(buf, l, "%" PRIu64 "%s", t, flag & FORMAT_BYTES_TRAILING_B ? "B" : ""); + +finish: + buf[l-1] = 0; + return buf; + +} diff --git a/src/basic/format-util.h b/src/basic/format-util.h index 9925a5e991..e0d184a541 100644 --- a/src/basic/format-util.h +++ b/src/basic/format-util.h @@ -3,6 +3,7 @@ #include <inttypes.h> #include <net/if.h> +#include <stdbool.h> #if SIZEOF_PID_T == 4 # define PID_PRI PRIi32 @@ -68,3 +69,15 @@ #endif char *format_ifname(int ifindex, char buf[static IF_NAMESIZE + 1]); + +typedef enum { + FORMAT_BYTES_USE_IEC = 1 << 0, + FORMAT_BYTES_BELOW_POINT = 1 << 1, + FORMAT_BYTES_TRAILING_B = 1 << 2, +} FormatBytesFlag; + +#define FORMAT_BYTES_MAX 8 +char *format_bytes_full(char *buf, size_t l, uint64_t t, FormatBytesFlag flag); +static inline char *format_bytes(char *buf, size_t l, uint64_t t) { + return format_bytes_full(buf, l, t, FORMAT_BYTES_USE_IEC | FORMAT_BYTES_BELOW_POINT | FORMAT_BYTES_TRAILING_B); +} diff --git a/src/basic/parse-util.c b/src/basic/parse-util.c index 7774e794d4..115a1494a2 100644 --- a/src/basic/parse-util.c +++ b/src/basic/parse-util.c @@ -362,47 +362,6 @@ int parse_syscall_and_errno(const char *in, char **name, int *error) { return 0; } -char *format_bytes(char *buf, size_t l, uint64_t t) { - unsigned i; - - /* This only does IEC units so far */ - - static const struct { - const char *suffix; - uint64_t factor; - } table[] = { - { "E", UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024) }, - { "P", UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024) }, - { "T", UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024) }, - { "G", UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024) }, - { "M", UINT64_C(1024)*UINT64_C(1024) }, - { "K", UINT64_C(1024) }, - }; - - if (t == (uint64_t) -1) - return NULL; - - for (i = 0; i < ELEMENTSOF(table); i++) { - - if (t >= table[i].factor) { - snprintf(buf, l, - "%" PRIu64 ".%" PRIu64 "%s", - t / table[i].factor, - ((t*UINT64_C(10)) / table[i].factor) % UINT64_C(10), - table[i].suffix); - - goto finish; - } - } - - snprintf(buf, l, "%" PRIu64 "B", t); - -finish: - buf[l-1] = 0; - return buf; - -} - int safe_atou_full(const char *s, unsigned base, unsigned *ret_u) { char *x = NULL; unsigned long l; diff --git a/src/basic/parse-util.h b/src/basic/parse-util.h index 5a05dfeac5..3a70b79276 100644 --- a/src/basic/parse-util.h +++ b/src/basic/parse-util.h @@ -22,9 +22,6 @@ int parse_range(const char *t, unsigned *lower, unsigned *upper); int parse_errno(const char *t); int parse_syscall_and_errno(const char *in, char **name, int *error); -#define FORMAT_BYTES_MAX 8 -char *format_bytes(char *buf, size_t l, uint64_t t); - int safe_atou_full(const char *s, unsigned base, unsigned *ret_u); static inline int safe_atou(const char *s, unsigned *ret_u) { diff --git a/src/import/import-fs.c b/src/import/import-fs.c index 04344492c8..abb4efac99 100644 --- a/src/import/import-fs.c +++ b/src/import/import-fs.c @@ -6,13 +6,13 @@ #include "alloc-util.h" #include "btrfs-util.h" #include "fd-util.h" +#include "format-util.h" #include "fs-util.h" #include "hostname-util.h" #include "import-common.h" #include "import-util.h" #include "machine-image.h" #include "mkdir.h" -#include "parse-util.h" #include "ratelimit.h" #include "rm-rf.h" #include "string-util.h" diff --git a/src/import/pull-job.c b/src/import/pull-job.c index 9f759a77e0..881bba0eeb 100644 --- a/src/import/pull-job.c +++ b/src/import/pull-job.c @@ -6,6 +6,7 @@ #include "alloc-util.h" #include "fd-util.h" +#include "format-util.h" #include "gcrypt-util.h" #include "hexdecoct.h" #include "import-util.h" diff --git a/src/journal/journal-file.c b/src/journal/journal-file.c index c2dcf76deb..91ca53e5eb 100644 --- a/src/journal/journal-file.c +++ b/src/journal/journal-file.c @@ -17,13 +17,13 @@ #include "chattr-util.h" #include "compress.h" #include "fd-util.h" +#include "format-util.h" #include "fs-util.h" #include "journal-authenticate.h" #include "journal-def.h" #include "journal-file.h" #include "lookup3.h" #include "memory-util.h" -#include "parse-util.h" #include "path-util.h" #include "random-util.h" #include "set.h" diff --git a/src/journal/journal-vacuum.c b/src/journal/journal-vacuum.c index a932314e19..87d65896c6 100644 --- a/src/journal/journal-vacuum.c +++ b/src/journal/journal-vacuum.c @@ -9,11 +9,11 @@ #include "alloc-util.h" #include "dirent-util.h" #include "fd-util.h" +#include "format-util.h" #include "fs-util.h" #include "journal-def.h" #include "journal-file.h" #include "journal-vacuum.h" -#include "parse-util.h" #include "sort-util.h" #include "string-util.h" #include "time-util.h" diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c index 3d053c59f0..a3eb61e0f1 100644 --- a/src/journal/journalctl.c +++ b/src/journal/journalctl.c @@ -35,6 +35,7 @@ #include "device-private.h" #include "fd-util.h" #include "fileio.h" +#include "format-util.h" #include "fs-util.h" #include "fsprg.h" #include "glob-util.h" diff --git a/src/network/networkctl.c b/src/network/networkctl.c index 901e88cc98..5d91779395 100644 --- a/src/network/networkctl.c +++ b/src/network/networkctl.c @@ -22,6 +22,7 @@ #include "bus-util.h" #include "device-util.h" #include "ether-addr-util.h" +#include "ethtool-util.h" #include "fd-util.h" #include "format-table.h" #include "format-util.h" @@ -119,8 +120,14 @@ typedef struct LinkInfo { struct rtnl_link_stats stats; }; - double tx_bitrate; - double rx_bitrate; + uint64_t tx_bitrate; + uint64_t rx_bitrate; + + /* ethtool info */ + int autonegotiation; + size_t speed; + Duplex duplex; + NetDevPort port; bool has_mac_address:1; bool has_tx_queues:1; @@ -128,6 +135,7 @@ typedef struct LinkInfo { bool has_stats64:1; bool has_stats:1; bool has_bitrates:1; + bool has_ethtool_link_info:1; } LinkInfo; static int link_info_compare(const LinkInfo *a, const LinkInfo *b) { @@ -229,11 +237,11 @@ static int acquire_link_bitrates(sd_bus *bus, LinkInfo *link) { r, "Failed to query link bit rates: %s", bus_error_message(&error, r)); } - r = sd_bus_message_enter_container(reply, 'v', "(dd)"); + r = sd_bus_message_enter_container(reply, 'v', "(tt)"); if (r < 0) return bus_log_parse_error(r); - r = sd_bus_message_read(reply, "(dd)", &link->tx_bitrate, &link->rx_bitrate); + r = sd_bus_message_read(reply, "(tt)", &link->tx_bitrate, &link->rx_bitrate); if (r < 0) return bus_log_parse_error(r); @@ -241,7 +249,7 @@ static int acquire_link_bitrates(sd_bus *bus, LinkInfo *link) { if (r < 0) return bus_log_parse_error(r); - link->has_bitrates = link->tx_bitrate >= 0 && link->rx_bitrate >= 0; + link->has_bitrates = true; return 0; } @@ -249,6 +257,7 @@ static int acquire_link_bitrates(sd_bus *bus, LinkInfo *link) { static int acquire_link_info(sd_bus *bus, sd_netlink *rtnl, char **patterns, LinkInfo **ret) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL; _cleanup_free_ LinkInfo *links = NULL; + _cleanup_close_ int fd = -1; size_t allocated = 0, c = 0, j; sd_netlink_message *i; int r; @@ -275,8 +284,16 @@ static int acquire_link_info(sd_bus *bus, sd_netlink *rtnl, char **patterns, Lin r = decode_link(i, links + c, patterns); if (r < 0) return r; - if (r > 0) - c++; + if (r == 0) + continue; + + r = ethtool_get_link_info(&fd, links[c].name, + &links[c].autonegotiation, &links[c].speed, + &links[c].duplex, &links[c].port); + if (r >= 0) + links[c].has_ethtool_link_info = true; + + c++; } typesafe_qsort(links, c, link_info_compare); @@ -898,32 +915,6 @@ static int dump_statistics(Table *table, const LinkInfo *info) { return 0; } -static const struct { - double val; - const char *str; -} prefix_table[] = { - { .val = 1e15, .str = "P" }, - { .val = 1e12, .str = "T" }, - { .val = 1e9, .str = "G" }, - { .val = 1e6, .str = "M" }, - { .val = 1e3, .str = "k" }, -}; - -static void get_prefix(double val, double *ret_div, const char **ret_prefix) { - assert(ret_div); - assert(ret_prefix); - - for (size_t i = 0; i < ELEMENTSOF(prefix_table); i++) - if (val > prefix_table[i].val) { - *ret_div = prefix_table[i].val; - *ret_prefix = prefix_table[i].str; - return; - } - - *ret_div = 1; - *ret_prefix = NULL; -} - static int link_status_one( sd_netlink *rtnl, sd_hwdb *hwdb, @@ -1140,11 +1131,7 @@ static int link_status_one( } if (info->has_bitrates) { - const char *tx_prefix, *rx_prefix; - double tx_div, rx_div; - - get_prefix(info->tx_bitrate, &tx_div, &tx_prefix); - get_prefix(info->rx_bitrate, &rx_div, &rx_prefix); + char tx[FORMAT_BYTES_MAX], rx[FORMAT_BYTES_MAX]; r = table_add_cell(table, NULL, TABLE_EMPTY, NULL); if (r < 0) @@ -1153,9 +1140,9 @@ static int link_status_one( if (r < 0) return r; - r = table_add_cell_stringf(table, NULL, "%.4g %sbps/%.4g %sbps", - info->tx_bitrate / tx_div, strempty(tx_prefix), - info->rx_bitrate / rx_div, strempty(rx_prefix)); + r = table_add_cell_stringf(table, NULL, "%sbps/%sbps", + format_bytes_full(tx, sizeof tx, info->tx_bitrate, 0), + format_bytes_full(rx, sizeof rx, info->rx_bitrate, 0)); if (r < 0) return r; } @@ -1172,6 +1159,59 @@ static int link_status_one( return r; } + if (info->has_ethtool_link_info) { + const char *duplex = duplex_to_string(info->duplex); + const char *port = port_to_string(info->port); + + if (IN_SET(info->autonegotiation, AUTONEG_DISABLE, AUTONEG_ENABLE)) { + r = table_add_cell(table, NULL, TABLE_EMPTY, NULL); + if (r < 0) + return r; + r = table_add_cell(table, NULL, TABLE_STRING, "Auto negotiation:"); + if (r < 0) + return r; + r = table_add_cell(table, NULL, TABLE_BOOLEAN, &info->autonegotiation); + if (r < 0) + return r; + } + + if (info->speed > 0) { + r = table_add_cell(table, NULL, TABLE_EMPTY, NULL); + if (r < 0) + return r; + r = table_add_cell(table, NULL, TABLE_STRING, "Speed:"); + if (r < 0) + return r; + r = table_add_cell(table, NULL, TABLE_BPS, &info->speed); + if (r < 0) + return r; + } + + if (duplex) { + r = table_add_cell(table, NULL, TABLE_EMPTY, NULL); + if (r < 0) + return r; + r = table_add_cell(table, NULL, TABLE_STRING, "Duplex:"); + if (r < 0) + return r; + r = table_add_cell(table, NULL, TABLE_STRING, duplex); + if (r < 0) + return r; + } + + if (port) { + r = table_add_cell(table, NULL, TABLE_EMPTY, NULL); + if (r < 0) + return r; + r = table_add_cell(table, NULL, TABLE_STRING, "Port:"); + if (r < 0) + return r; + r = table_add_cell(table, NULL, TABLE_STRING, port); + if (r < 0) + return r; + } + } + r = dump_addresses(rtnl, table, info->ifindex); if (r < 0) return r; diff --git a/src/network/networkd-link-bus.c b/src/network/networkd-link-bus.c index 2f414cb116..076845421c 100644 --- a/src/network/networkd-link-bus.c +++ b/src/network/networkd-link-bus.c @@ -22,7 +22,8 @@ static int property_get_bit_rates( Link *link = userdata; Manager *manager; - double tx, rx, interval_sec; + double interval_sec; + uint64_t tx, rx; assert(bus); assert(reply); @@ -40,19 +41,19 @@ static int property_get_bit_rates( return sd_bus_error_set(error, BUS_ERROR_SPEED_METER_INACTIVE, "Failed to measure bit-rates."); assert(manager->speed_meter_usec_new > manager->speed_meter_usec_old); - interval_sec = (double) (manager->speed_meter_usec_new - manager->speed_meter_usec_old) / USEC_PER_SEC; + interval_sec = (manager->speed_meter_usec_new - manager->speed_meter_usec_old) / USEC_PER_SEC; if (link->stats_new.tx_bytes > link->stats_old.tx_bytes) - tx = (link->stats_new.tx_bytes - link->stats_old.tx_bytes) / interval_sec; + tx = (uint64_t) ((link->stats_new.tx_bytes - link->stats_old.tx_bytes) / interval_sec); else - tx = (UINT64_MAX - (link->stats_old.tx_bytes - link->stats_new.tx_bytes)) / interval_sec; + tx = (uint64_t) ((UINT64_MAX - (link->stats_old.tx_bytes - link->stats_new.tx_bytes)) / interval_sec); if (link->stats_new.rx_bytes > link->stats_old.rx_bytes) - rx = (link->stats_new.rx_bytes - link->stats_old.rx_bytes) / interval_sec; + rx = (uint64_t) ((link->stats_new.rx_bytes - link->stats_old.rx_bytes) / interval_sec); else - rx = (UINT64_MAX - (link->stats_old.rx_bytes - link->stats_new.rx_bytes)) / interval_sec; + rx = (uint64_t) ((UINT64_MAX - (link->stats_old.rx_bytes - link->stats_new.rx_bytes)) / interval_sec); - return sd_bus_message_append(reply, "(dd)", tx, rx); + return sd_bus_message_append(reply, "(tt)", tx, rx); } const sd_bus_vtable link_vtable[] = { @@ -60,7 +61,7 @@ const sd_bus_vtable link_vtable[] = { SD_BUS_PROPERTY("OperationalState", "s", property_get_operational_state, offsetof(Link, operstate), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("AdministrativeState", "s", property_get_administrative_state, offsetof(Link, state), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("BitRates", "(dd)", property_get_bit_rates, 0, 0), + SD_BUS_PROPERTY("BitRates", "(tt)", property_get_bit_rates, 0, 0), SD_BUS_VTABLE_END }; diff --git a/src/udev/net/ethtool-util.c b/src/shared/ethtool-util.c index c94977e7be..b2a81e4215 100644 --- a/src/udev/net/ethtool-util.c +++ b/src/shared/ethtool-util.c @@ -7,7 +7,7 @@ #include "conf-parser.h" #include "ethtool-util.h" -#include "link-config.h" +#include "extract-word.h" #include "log.h" #include "memory-util.h" #include "missing.h" @@ -111,18 +111,19 @@ static const char* const ethtool_link_mode_bit_table[] = { [ETHTOOL_LINK_MODE_FEC_BASER_BIT] = "fec-baser", }; /* Make sure the array is large enough to fit all bits */ -assert_cc((ELEMENTSOF(ethtool_link_mode_bit_table)-1) / 32 < ELEMENTSOF(((struct link_config){}).advertise)); +assert_cc((ELEMENTSOF(ethtool_link_mode_bit_table)-1) / 32 < N_ADVERTISE); DEFINE_STRING_TABLE_LOOKUP(ethtool_link_mode_bit, enum ethtool_link_mode_bit_indices); -int ethtool_connect(int *ret) { +static int ethtool_connect_or_warn(int *ret, bool warn) { int fd; assert_return(ret, -EINVAL); fd = socket_ioctl_fd(); if (fd < 0) - return fd; + return log_full_errno(warn ? LOG_WARNING: LOG_DEBUG, fd, + "ethtool: could not create control socket: %m"); *ret = fd; @@ -140,9 +141,9 @@ int ethtool_get_driver(int *fd, const char *ifname, char **ret) { int r; if (*fd < 0) { - r = ethtool_connect(fd); + r = ethtool_connect_or_warn(fd, true); if (r < 0) - return log_warning_errno(r, "link_config: could not connect to ethtool: %m"); + return r; } strscpy(ifr.ifr_name, IFNAMSIZ, ifname); @@ -159,6 +160,44 @@ int ethtool_get_driver(int *fd, const char *ifname, char **ret) { return 0; } +int ethtool_get_link_info(int *fd, const char *ifname, + int *ret_autonegotiation, size_t *ret_speed, + Duplex *ret_duplex, NetDevPort *ret_port) { + struct ethtool_cmd ecmd = { + .cmd = ETHTOOL_GSET, + }; + struct ifreq ifr = { + .ifr_data = (void*) &ecmd, + }; + int r; + + if (*fd < 0) { + r = ethtool_connect_or_warn(fd, false); + if (r < 0) + return r; + } + + strscpy(ifr.ifr_name, IFNAMSIZ, ifname); + + r = ioctl(*fd, SIOCETHTOOL, &ifr); + if (r < 0) + return -errno; + + if (ret_autonegotiation) + *ret_autonegotiation = ecmd.autoneg; + + if (ret_speed) + *ret_speed = ethtool_cmd_speed(&ecmd) * 1000 * 1000; + + if (ret_duplex) + *ret_duplex = ecmd.duplex; + + if (ret_port) + *ret_port = ecmd.port; + + return 0; +} + int ethtool_set_speed(int *fd, const char *ifname, unsigned speed, Duplex duplex) { struct ethtool_cmd ecmd = { .cmd = ETHTOOL_GSET @@ -173,9 +212,9 @@ int ethtool_set_speed(int *fd, const char *ifname, unsigned speed, Duplex duplex return 0; if (*fd < 0) { - r = ethtool_connect(fd); + r = ethtool_connect_or_warn(fd, true); if (r < 0) - return log_warning_errno(r, "link_config: could not connect to ethtool: %m"); + return r; } strscpy(ifr.ifr_name, IFNAMSIZ, ifname); @@ -231,9 +270,9 @@ int ethtool_set_wol(int *fd, const char *ifname, WakeOnLan wol) { return 0; if (*fd < 0) { - r = ethtool_connect(fd); + r = ethtool_connect_or_warn(fd, true); if (r < 0) - return log_warning_errno(r, "link_config: could not connect to ethtool: %m"); + return r; } strscpy(ifr.ifr_name, IFNAMSIZ, ifname); @@ -368,16 +407,16 @@ int ethtool_set_features(int *fd, const char *ifname, int *features) { struct ifreq ifr = {}; if (*fd < 0) { - r = ethtool_connect(fd); + r = ethtool_connect_or_warn(fd, true); if (r < 0) - return log_warning_errno(r, "link_config: could not connect to ethtool: %m"); + return r; } strscpy(ifr.ifr_name, IFNAMSIZ, ifname); r = get_stringset(*fd, &ifr, ETH_SS_FEATURES, &strings); if (r < 0) - return log_warning_errno(r, "link_config: could not get ethtool features for %s", ifname); + return log_warning_errno(r, "ethtool: could not get ethtool features for %s", ifname); sfeatures = alloca0(sizeof(struct ethtool_sfeatures) + DIV_ROUND_UP(strings->len, 32U) * sizeof(sfeatures->features[0])); sfeatures->cmd = ETHTOOL_SFEATURES; @@ -389,7 +428,7 @@ int ethtool_set_features(int *fd, const char *ifname, int *features) { r = find_feature_index(strings, netdev_feature_table[i]); if (r < 0) { - log_warning_errno(r, "link_config: could not find feature: %s", netdev_feature_table[i]); + log_warning_errno(r, "ethtool: could not find feature: %s", netdev_feature_table[i]); continue; } @@ -409,7 +448,7 @@ int ethtool_set_features(int *fd, const char *ifname, int *features) { r = ioctl(*fd, SIOCETHTOOL, &ifr); if (r < 0) - return log_warning_errno(r, "link_config: could not set ethtool features for %s", ifname); + return log_warning_errno(r, "ethtool: could not set ethtool features for %s", ifname); return 0; } @@ -453,11 +492,13 @@ static int get_glinksettings(int fd, struct ifreq *ifr, struct ethtool_link_uset if (ecmd.req.link_mode_masks_nwords <= 0 || ecmd.req.cmd != ETHTOOL_GLINKSETTINGS) return -EOPNOTSUPP; - u = new0(struct ethtool_link_usettings , 1); + u = new(struct ethtool_link_usettings, 1); if (!u) return -ENOMEM; - u->base = ecmd.req; + *u = (struct ethtool_link_usettings) { + .base = ecmd.req, + }; offset = 0; memcpy(u->link_modes.supported, &ecmd.link_mode_data[offset], 4 * ecmd.req.link_mode_masks_nwords); @@ -486,23 +527,24 @@ static int get_gset(int fd, struct ifreq *ifr, struct ethtool_link_usettings **u if (r < 0) return -errno; - e = new0(struct ethtool_link_usettings, 1); + e = new(struct ethtool_link_usettings, 1); if (!e) return -ENOMEM; - e->base.cmd = ETHTOOL_GSET; - - e->base.link_mode_masks_nwords = 1; - e->base.speed = ethtool_cmd_speed(&ecmd); - e->base.duplex = ecmd.duplex; - e->base.port = ecmd.port; - e->base.phy_address = ecmd.phy_address; - e->base.autoneg = ecmd.autoneg; - e->base.mdio_support = ecmd.mdio_support; - - e->link_modes.supported[0] = ecmd.supported; - e->link_modes.advertising[0] = ecmd.advertising; - e->link_modes.lp_advertising[0] = ecmd.lp_advertising; + *e = (struct ethtool_link_usettings) { + .base.cmd = ETHTOOL_GSET, + .base.link_mode_masks_nwords = 1, + .base.speed = ethtool_cmd_speed(&ecmd), + .base.duplex = ecmd.duplex, + .base.port = ecmd.port, + .base.phy_address = ecmd.phy_address, + .base.autoneg = ecmd.autoneg, + .base.mdio_support = ecmd.mdio_support, + + .link_modes.supported[0] = ecmd.supported, + .link_modes.advertising[0] = ecmd.advertising, + .link_modes.lp_advertising[0] = ecmd.lp_advertising, + }; *u = e; @@ -578,20 +620,27 @@ static int set_sset(int fd, struct ifreq *ifr, const struct ethtool_link_usettin * link mode; if the link is down, the speed is 0, %SPEED_UNKNOWN or the highest * enabled speed and @duplex is %DUPLEX_UNKNOWN or the best enabled duplex mode. */ -int ethtool_set_glinksettings(int *fd, const char *ifname, struct link_config *link) { +int ethtool_set_glinksettings( + int *fd, + const char *ifname, + int autonegotiation, + uint32_t advertise[static N_ADVERTISE], + size_t speed, + Duplex duplex, + NetDevPort port) { _cleanup_free_ struct ethtool_link_usettings *u = NULL; struct ifreq ifr = {}; int r; - if (link->autonegotiation != AUTONEG_DISABLE && eqzero(link->advertise)) { - log_info("link_config: autonegotiation is unset or enabled, the speed and duplex are not writable."); + if (autonegotiation != AUTONEG_DISABLE && memeqzero(advertise, sizeof(uint32_t) * N_ADVERTISE)) { + log_info("ethtool: autonegotiation is unset or enabled, the speed and duplex are not writable."); return 0; } if (*fd < 0) { - r = ethtool_connect(fd); + r = ethtool_connect_or_warn(fd, true); if (r < 0) - return log_warning_errno(r, "link_config: could not connect to ethtool: %m"); + return r; } strscpy(ifr.ifr_name, IFNAMSIZ, ifname); @@ -600,26 +649,26 @@ int ethtool_set_glinksettings(int *fd, const char *ifname, struct link_config *l if (r < 0) { r = get_gset(*fd, &ifr, &u); if (r < 0) - return log_warning_errno(r, "link_config: Cannot get device settings for %s : %m", ifname); + return log_warning_errno(r, "ethtool: Cannot get device settings for %s : %m", ifname); } - if (link->speed) - u->base.speed = DIV_ROUND_UP(link->speed, 1000000); + if (speed > 0) + u->base.speed = DIV_ROUND_UP(speed, 1000000); - if (link->duplex != _DUP_INVALID) - u->base.duplex = link->duplex; + if (duplex != _DUP_INVALID) + u->base.duplex = duplex; - if (link->port != _NET_DEV_PORT_INVALID) - u->base.port = link->port; + if (port != _NET_DEV_PORT_INVALID) + u->base.port = port; - if (link->autonegotiation >= 0) - u->base.autoneg = link->autonegotiation; + if (autonegotiation >= 0) + u->base.autoneg = autonegotiation; - if (!eqzero(link->advertise)) { + if (!memeqzero(advertise, sizeof(uint32_t) * N_ADVERTISE)) { u->base.autoneg = AUTONEG_ENABLE; - memcpy(&u->link_modes.advertising, link->advertise, sizeof(link->advertise)); - memzero((uint8_t*) &u->link_modes.advertising + sizeof(link->advertise), - ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NBYTES - sizeof(link->advertise)); + memcpy(&u->link_modes.advertising, advertise, sizeof(uint32_t) * N_ADVERTISE); + memzero((uint8_t*) &u->link_modes.advertising + sizeof(uint32_t) * N_ADVERTISE, + ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NBYTES - sizeof(uint32_t) * N_ADVERTISE); } if (u->base.cmd == ETHTOOL_GLINKSETTINGS) @@ -627,59 +676,11 @@ int ethtool_set_glinksettings(int *fd, const char *ifname, struct link_config *l else r = set_sset(*fd, &ifr, u); if (r < 0) - return log_warning_errno(r, "link_config: Cannot set device settings for %s : %m", ifname); + return log_warning_errno(r, "ethtool: Cannot set device settings for %s : %m", ifname); return r; } -int config_parse_channel(const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - link_config *config = data; - uint32_t k; - int r; - - assert(filename); - assert(section); - assert(lvalue); - assert(rvalue); - assert(data); - - r = safe_atou32(rvalue, &k); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse channel value, ignoring: %s", rvalue); - return 0; - } - - if (k < 1) { - log_syntax(unit, LOG_ERR, filename, line, -EINVAL, "Invalid %s value, ignoring: %s", lvalue, rvalue); - return 0; - } - - if (streq(lvalue, "RxChannels")) { - config->channels.rx_count = k; - config->channels.rx_count_set = true; - } else if (streq(lvalue, "TxChannels")) { - config->channels.tx_count = k; - config->channels.tx_count_set = true; - } else if (streq(lvalue, "OtherChannels")) { - config->channels.other_count = k; - config->channels.other_count_set = true; - } else if (streq(lvalue, "CombinedChannels")) { - config->channels.combined_count = k; - config->channels.combined_count_set = true; - } - - return 0; -} - int ethtool_set_channels(int *fd, const char *ifname, netdev_channels *channels) { struct ethtool_channels ecmd = { .cmd = ETHTOOL_GCHANNELS @@ -692,9 +693,9 @@ int ethtool_set_channels(int *fd, const char *ifname, netdev_channels *channels) int r; if (*fd < 0) { - r = ethtool_connect(fd); + r = ethtool_connect_or_warn(fd, true); if (r < 0) - return log_warning_errno(r, "link_config: could not connect to ethtool: %m"); + return r; } strscpy(ifr.ifr_name, IFNAMSIZ, ifname); @@ -734,6 +735,54 @@ int ethtool_set_channels(int *fd, const char *ifname, netdev_channels *channels) return 0; } +int config_parse_channel(const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + netdev_channels *channels = data; + uint32_t k; + int r; + + assert(filename); + assert(section); + assert(lvalue); + assert(rvalue); + assert(data); + + r = safe_atou32(rvalue, &k); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse channel value, ignoring: %s", rvalue); + return 0; + } + + if (k < 1) { + log_syntax(unit, LOG_ERR, filename, line, -EINVAL, "Invalid %s value, ignoring: %s", lvalue, rvalue); + return 0; + } + + if (streq(lvalue, "RxChannels")) { + channels->rx_count = k; + channels->rx_count_set = true; + } else if (streq(lvalue, "TxChannels")) { + channels->tx_count = k; + channels->tx_count_set = true; + } else if (streq(lvalue, "OtherChannels")) { + channels->other_count = k; + channels->other_count_set = true; + } else if (streq(lvalue, "CombinedChannels")) { + channels->combined_count = k; + channels->combined_count_set = true; + } + + return 0; +} + int config_parse_advertise(const char *unit, const char *filename, unsigned line, @@ -744,7 +793,7 @@ int config_parse_advertise(const char *unit, const char *rvalue, void *data, void *userdata) { - link_config *config = data; + uint32_t *advertise = data; const char *p; int r; @@ -756,7 +805,7 @@ int config_parse_advertise(const char *unit, if (isempty(rvalue)) { /* Empty string resets the value. */ - zero(config->advertise); + memzero(advertise, sizeof(uint32_t) * N_ADVERTISE); return 0; } @@ -782,7 +831,7 @@ int config_parse_advertise(const char *unit, continue; } - config->advertise[mode / 32] |= 1UL << (mode % 32); + advertise[mode / 32] |= 1UL << (mode % 32); } return 0; diff --git a/src/udev/net/ethtool-util.h b/src/shared/ethtool-util.h index 7ca703d22c..03976c6ee3 100644 --- a/src/udev/net/ethtool-util.h +++ b/src/shared/ethtool-util.h @@ -6,7 +6,7 @@ #include "conf-parser.h" -struct link_config; +#define N_ADVERTISE 2 /* we can't use DUPLEX_ prefix, as it * clashes with <linux/ethtool.h> */ @@ -79,13 +79,16 @@ typedef struct netdev_channels { bool combined_count_set; } netdev_channels; -int ethtool_connect(int *ret); - int ethtool_get_driver(int *fd, const char *ifname, char **ret); +int ethtool_get_link_info(int *fd, const char *ifname, + int *ret_autonegotiation, size_t *ret_speed, + Duplex *ret_duplex, NetDevPort *ret_port); int ethtool_set_speed(int *fd, const char *ifname, unsigned speed, Duplex duplex); int ethtool_set_wol(int *fd, const char *ifname, WakeOnLan wol); int ethtool_set_features(int *fd, const char *ifname, int *features); -int ethtool_set_glinksettings(int *fd, const char *ifname, struct link_config *link); +int ethtool_set_glinksettings(int *fd, const char *ifname, + int autonegotiation, uint32_t advertise[static N_ADVERTISE], + size_t speed, Duplex duplex, NetDevPort port); int ethtool_set_channels(int *fd, const char *ifname, netdev_channels *channels); const char *duplex_to_string(Duplex d) _const_; diff --git a/src/shared/format-table.c b/src/shared/format-table.c index d6612a76d3..54ca1972bd 100644 --- a/src/shared/format-table.c +++ b/src/shared/format-table.c @@ -15,6 +15,7 @@ #include "pretty-print.h" #include "sort-util.h" #include "string-util.h" +#include "strxcpyx.h" #include "terminal-util.h" #include "time-util.h" #include "utf8.h" @@ -235,6 +236,7 @@ static size_t table_data_size(TableDataType type, const void *data) { case TABLE_SIZE: case TABLE_INT64: case TABLE_UINT64: + case TABLE_BPS: return sizeof(uint64_t); case TABLE_INT32: @@ -723,6 +725,7 @@ int table_add_many_internal(Table *t, TableDataType first_type, ...) { break; case TABLE_SIZE: + case TABLE_BPS: buffer.size = va_arg(ap, uint64_t); data = &buffer.size; break; @@ -885,6 +888,7 @@ static int cell_data_compare(TableData *a, size_t index_a, TableData *b, size_t return CMP(a->timespan, b->timespan); case TABLE_SIZE: + case TABLE_BPS: return CMP(a->size, b->size); case TABLE_INT: @@ -1023,6 +1027,24 @@ static const char *table_data_format(TableData *d) { break; } + case TABLE_BPS: { + _cleanup_free_ char *p; + size_t n; + + p = new(char, FORMAT_BYTES_MAX+2); + if (!p) + return NULL; + + if (!format_bytes_full(p, FORMAT_BYTES_MAX, d->size, 0)) + return "n/a"; + + n = strlen(p); + strscpy(p + n, FORMAT_BYTES_MAX + 2 - n, "bps"); + + d->formatted = TAKE_PTR(p); + break; + } + case TABLE_INT: { _cleanup_free_ char *p; @@ -1622,6 +1644,7 @@ static int table_data_to_json(TableData *d, JsonVariant **ret) { return json_variant_new_unsigned(ret, d->timespan); case TABLE_SIZE: + case TABLE_BPS: if (d->size == (size_t) -1) return json_variant_new_null(ret); diff --git a/src/shared/format-table.h b/src/shared/format-table.h index ec2bbba292..ada59f3423 100644 --- a/src/shared/format-table.h +++ b/src/shared/format-table.h @@ -15,6 +15,7 @@ typedef enum TableDataType { TABLE_TIMESTAMP, TABLE_TIMESPAN, TABLE_SIZE, + TABLE_BPS, TABLE_INT, TABLE_INT32, TABLE_INT64, diff --git a/src/shared/meson.build b/src/shared/meson.build index aa0423ccad..59b50a754e 100644 --- a/src/shared/meson.build +++ b/src/shared/meson.build @@ -59,6 +59,8 @@ shared_sources = files(''' enable-mempool.c env-file-label.c env-file-label.h + ethtool-util.c + ethtool-util.h exec-util.c exec-util.h exit-status.c diff --git a/src/test/meson.build b/src/test/meson.build index ee6cdb6d54..34dd5cbc45 100644 --- a/src/test/meson.build +++ b/src/test/meson.build @@ -216,6 +216,10 @@ tests += [ [], []], + [['src/test/test-format-util.c'], + [], + []], + [['src/test/test-ratelimit.c'], [], []], diff --git a/src/test/test-btrfs.c b/src/test/test-btrfs.c index 5e5638cd72..5bd0e3458c 100644 --- a/src/test/test-btrfs.c +++ b/src/test/test-btrfs.c @@ -5,8 +5,8 @@ #include "btrfs-util.h" #include "fd-util.h" #include "fileio.h" +#include "format-util.h" #include "log.h" -#include "parse-util.h" #include "string-util.h" #include "util.h" diff --git a/src/test/test-format-util.c b/src/test/test-format-util.c new file mode 100644 index 0000000000..2986b1bd8d --- /dev/null +++ b/src/test/test-format-util.c @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include "format-util.h" +#include "macro.h" +#include "string-util.h" + +static void test_format_bytes_one(size_t val, bool trailing_B, const char *iec_with_p, const char *iec_without_p, + const char *non_iec_with_p, const char *non_iec_without_p) { + char buf[FORMAT_BYTES_MAX]; + + assert_se(streq_ptr(format_bytes_full(buf, sizeof buf, val, FORMAT_BYTES_USE_IEC | FORMAT_BYTES_BELOW_POINT | (trailing_B ? FORMAT_BYTES_TRAILING_B : 0)), iec_with_p)); + assert_se(streq_ptr(format_bytes_full(buf, sizeof buf, val, FORMAT_BYTES_USE_IEC | (trailing_B ? FORMAT_BYTES_TRAILING_B : 0)), iec_without_p)); + assert_se(streq_ptr(format_bytes_full(buf, sizeof buf, val, FORMAT_BYTES_BELOW_POINT | (trailing_B ? FORMAT_BYTES_TRAILING_B : 0)), non_iec_with_p)); + assert_se(streq_ptr(format_bytes_full(buf, sizeof buf, val, trailing_B ? FORMAT_BYTES_TRAILING_B : 0), non_iec_without_p)); +} + +static void test_format_bytes(void) { + test_format_bytes_one(900, true, "900B", "900B", "900B", "900B"); + test_format_bytes_one(900, false, "900", "900", "900", "900"); + test_format_bytes_one(1023, true, "1023B", "1023B", "1.0K", "1K"); + test_format_bytes_one(1023, false, "1023", "1023", "1.0K", "1K"); + test_format_bytes_one(1024, true, "1.0K", "1K", "1.0K", "1K"); + test_format_bytes_one(1024, false, "1.0K", "1K", "1.0K", "1K"); + test_format_bytes_one(1100, true, "1.0K", "1K", "1.1K", "1K"); + test_format_bytes_one(1500, true, "1.4K", "1K", "1.5K", "1K"); + test_format_bytes_one((size_t) 3*1024*1024, true, "3.0M", "3M", "3.1M", "3M"); + test_format_bytes_one((size_t) 3*1024*1024*1024, true, "3.0G", "3G", "3.2G", "3G"); + test_format_bytes_one((size_t) 3*1024*1024*1024*1024, true, "3.0T", "3T", "3.2T", "3T"); + test_format_bytes_one((size_t) 3*1024*1024*1024*1024*1024, true, "3.0P", "3P", "3.3P", "3P"); + test_format_bytes_one((size_t) 3*1024*1024*1024*1024*1024*1024, true, "3.0E", "3E", "3.4E", "3E"); + test_format_bytes_one(SIZE_MAX, true, NULL, NULL, NULL, NULL); +} + +int main(void) { + test_format_bytes(); + + return 0; +} diff --git a/src/test/test-procfs-util.c b/src/test/test-procfs-util.c index 1d0612985b..662688e0f0 100644 --- a/src/test/test-procfs-util.c +++ b/src/test/test-procfs-util.c @@ -2,8 +2,8 @@ #include <errno.h> +#include "format-util.h" #include "log.h" -#include "parse-util.h" #include "procfs-util.h" int main(int argc, char *argv[]) { diff --git a/src/udev/meson.build b/src/udev/meson.build index 13068c039b..511fe428b9 100644 --- a/src/udev/meson.build +++ b/src/udev/meson.build @@ -40,8 +40,6 @@ libudev_core_sources = ''' udev-builtin-usb_id.c net/link-config.c net/link-config.h - net/ethtool-util.c - net/ethtool-util.h net/naming-scheme.c net/naming-scheme.h '''.split() diff --git a/src/udev/net/link-config-gperf.gperf b/src/udev/net/link-config-gperf.gperf index dff849a34a..9698211d1d 100644 --- a/src/udev/net/link-config-gperf.gperf +++ b/src/udev/net/link-config-gperf.gperf @@ -47,8 +47,8 @@ Link.TCP6SegmentationOffload, config_parse_tristate, 0, Link.UDPSegmentationOffload, config_parse_warn_compat, DISABLED_LEGACY, 0 Link.GenericReceiveOffload, config_parse_tristate, 0, offsetof(link_config, features[NET_DEV_FEAT_GRO]) Link.LargeReceiveOffload, config_parse_tristate, 0, offsetof(link_config, features[NET_DEV_FEAT_LRO]) -Link.RxChannels, config_parse_channel, 0, 0 -Link.TxChannels, config_parse_channel, 0, 0 -Link.OtherChannels, config_parse_channel, 0, 0 -Link.CombinedChannels, config_parse_channel, 0, 0 -Link.Advertise, config_parse_advertise, 0, 0 +Link.RxChannels, config_parse_channel, 0, offsetof(link_config, channels) +Link.TxChannels, config_parse_channel, 0, offsetof(link_config, channels) +Link.OtherChannels, config_parse_channel, 0, offsetof(link_config, channels) +Link.CombinedChannels, config_parse_channel, 0, offsetof(link_config, channels) +Link.Advertise, config_parse_advertise, 0, offsetof(link_config, advertise) diff --git a/src/udev/net/link-config.c b/src/udev/net/link-config.c index b983f28f2f..611add9ae0 100644 --- a/src/udev/net/link-config.c +++ b/src/udev/net/link-config.c @@ -354,7 +354,9 @@ int link_config_apply(link_config_ctx *ctx, link_config *config, if (r < 0) return r; - r = ethtool_set_glinksettings(&ctx->ethtool_fd, old_name, config); + r = ethtool_set_glinksettings(&ctx->ethtool_fd, old_name, + config->autonegotiation, config->advertise, + config->speed, config->duplex, config->port); if (r < 0) { if (config->port != _NET_DEV_PORT_INVALID) diff --git a/src/udev/net/link-config.h b/src/udev/net/link-config.h index efe5f2ce3a..a45a0e709a 100644 --- a/src/udev/net/link-config.h +++ b/src/udev/net/link-config.h @@ -52,7 +52,7 @@ struct link_config { size_t speed; Duplex duplex; int autonegotiation; - uint32_t advertise[2]; + uint32_t advertise[N_ADVERTISE]; WakeOnLan wol; NetDevPort port; int features[_NET_DEV_FEAT_MAX]; |