1
1
mirror of https://github.com/systemd/systemd-stable.git synced 2024-12-22 13:33:56 +03:00

Merge pull request #12806 from yuwata/networkctl-ethtool-12657

networkctl: show speed, duplex, auto negotiation, and port
This commit is contained in:
Yu Watanabe 2019-06-20 06:56:37 +09:00 committed by GitHub
commit b19eab1f74
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 405 additions and 213 deletions

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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;

View File

@ -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) {

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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;

View File

@ -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
};

View File

@ -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 = (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,
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;
.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;

View File

@ -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_;

View File

@ -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);

View File

@ -15,6 +15,7 @@ typedef enum TableDataType {
TABLE_TIMESTAMP,
TABLE_TIMESPAN,
TABLE_SIZE,
TABLE_BPS,
TABLE_INT,
TABLE_INT32,
TABLE_INT64,

View File

@ -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

View File

@ -216,6 +216,10 @@ tests += [
[],
[]],
[['src/test/test-format-util.c'],
[],
[]],
[['src/test/test-ratelimit.c'],
[],
[]],

View File

@ -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"

View File

@ -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;
}

View File

@ -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[]) {

View File

@ -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()

View File

@ -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)

View File

@ -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)

View File

@ -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];