mirror of
https://github.com/systemd/systemd.git
synced 2024-11-01 09:21:26 +03:00
ethtool: do not set unavailable or never_changed bits
This commit is contained in:
parent
4253dab576
commit
008d3a370c
@ -427,30 +427,31 @@ int ethtool_set_nic_buffer_size(int *ethtool_fd, const char *ifname, const netde
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_stringset(int ethtool_fd, struct ifreq *ifr, int stringset_id, struct ethtool_gstrings **ret) {
|
||||
static int get_stringset(int ethtool_fd, const char *ifname, enum ethtool_stringset stringset_id, struct ethtool_gstrings **ret) {
|
||||
_cleanup_free_ struct ethtool_gstrings *strings = NULL;
|
||||
struct {
|
||||
struct ethtool_sset_info info;
|
||||
uint32_t space;
|
||||
} buffer = {
|
||||
.info = {
|
||||
.cmd = ETHTOOL_GSSET_INFO,
|
||||
.sset_mask = UINT64_C(1) << stringset_id,
|
||||
},
|
||||
.info.cmd = ETHTOOL_GSSET_INFO,
|
||||
.info.sset_mask = UINT64_C(1) << stringset_id,
|
||||
};
|
||||
unsigned len;
|
||||
struct ifreq ifr = {
|
||||
.ifr_data = (void*) &buffer,
|
||||
};
|
||||
uint32_t len;
|
||||
|
||||
assert(ethtool_fd >= 0);
|
||||
assert(ifr);
|
||||
assert(ifname);
|
||||
assert(ret);
|
||||
|
||||
ifr->ifr_data = (void *) &buffer.info;
|
||||
strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
|
||||
|
||||
if (ioctl(ethtool_fd, SIOCETHTOOL, ifr) < 0)
|
||||
if (ioctl(ethtool_fd, SIOCETHTOOL, &ifr) < 0)
|
||||
return -errno;
|
||||
|
||||
if (!buffer.info.sset_mask)
|
||||
return -EINVAL;
|
||||
if (buffer.info.sset_mask == 0)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#if HAVE_ZERO_LENGTH_BOUNDS
|
||||
@ -458,8 +459,10 @@ static int get_stringset(int ethtool_fd, struct ifreq *ifr, int stringset_id, st
|
||||
#endif
|
||||
len = buffer.info.data[0];
|
||||
#pragma GCC diagnostic pop
|
||||
if (len == 0)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
strings = malloc0(sizeof(struct ethtool_gstrings) + len * ETH_GSTRING_LEN);
|
||||
strings = malloc0(offsetof(struct ethtool_gstrings, data) + len * ETH_GSTRING_LEN);
|
||||
if (!strings)
|
||||
return -ENOMEM;
|
||||
|
||||
@ -467,47 +470,92 @@ static int get_stringset(int ethtool_fd, struct ifreq *ifr, int stringset_id, st
|
||||
strings->string_set = stringset_id;
|
||||
strings->len = len;
|
||||
|
||||
ifr->ifr_data = (void *) strings;
|
||||
ifr.ifr_data = (void*) strings;
|
||||
|
||||
if (ioctl(ethtool_fd, SIOCETHTOOL, ifr) < 0)
|
||||
if (ioctl(ethtool_fd, SIOCETHTOOL, &ifr) < 0)
|
||||
return -errno;
|
||||
|
||||
*ret = TAKE_PTR(strings);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_features(int ethtool_fd, const char *ifname, uint32_t n_features, struct ethtool_gfeatures **ret) {
|
||||
_cleanup_free_ struct ethtool_gfeatures *gfeatures = NULL;
|
||||
struct ifreq ifr;
|
||||
|
||||
assert(ethtool_fd >= 0);
|
||||
assert(ifname);
|
||||
assert(ret);
|
||||
assert(n_features > 0);
|
||||
|
||||
gfeatures = malloc0(offsetof(struct ethtool_gfeatures, features) +
|
||||
DIV_ROUND_UP(n_features, 32U) * sizeof(gfeatures->features[0]));
|
||||
if (!gfeatures)
|
||||
return -ENOMEM;
|
||||
|
||||
gfeatures->cmd = ETHTOOL_GFEATURES;
|
||||
gfeatures->size = DIV_ROUND_UP(n_features, 32U);
|
||||
|
||||
ifr = (struct ifreq) {
|
||||
.ifr_data = (void*) gfeatures,
|
||||
};
|
||||
strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
|
||||
|
||||
if (ioctl(ethtool_fd, SIOCETHTOOL, &ifr) < 0)
|
||||
return -errno;
|
||||
|
||||
*ret = TAKE_PTR(gfeatures);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set_features_bit(
|
||||
const struct ethtool_gstrings *strings,
|
||||
const struct ethtool_gfeatures *gfeatures,
|
||||
struct ethtool_sfeatures *sfeatures,
|
||||
const char *feature,
|
||||
bool flag,
|
||||
struct ethtool_sfeatures *sfeatures) {
|
||||
int flag) {
|
||||
|
||||
bool found = false;
|
||||
int r = -ENODATA;
|
||||
|
||||
assert(strings);
|
||||
assert(feature);
|
||||
assert(gfeatures);
|
||||
assert(sfeatures);
|
||||
assert(feature);
|
||||
|
||||
for (size_t i = 0; i < strings->len; i++)
|
||||
if (streq((char *) &strings->data[i * ETH_GSTRING_LEN], feature) ||
|
||||
(endswith(feature, "-") && startswith((char *) &strings->data[i * ETH_GSTRING_LEN], feature))) {
|
||||
size_t block, bit;
|
||||
if (flag < 0)
|
||||
return 0;
|
||||
|
||||
block = i / 32;
|
||||
bit = i % 32;
|
||||
for (uint32_t i = 0; i < strings->len; i++) {
|
||||
uint32_t block, mask;
|
||||
|
||||
sfeatures->features[block].valid |= 1 << bit;
|
||||
SET_FLAG(sfeatures->features[block].requested, 1 << bit, flag);
|
||||
found = true;
|
||||
if (!strneq((const char*) &strings->data[i * ETH_GSTRING_LEN], feature, ETH_GSTRING_LEN) &&
|
||||
!(endswith(feature, "-") && startswith((const char*) &strings->data[i * ETH_GSTRING_LEN], feature)))
|
||||
continue;
|
||||
|
||||
block = i / 32;
|
||||
mask = UINT32_C(1) << (i % 32);
|
||||
|
||||
if (!FLAGS_SET(gfeatures->features[block].available, mask) ||
|
||||
FLAGS_SET(gfeatures->features[block].never_changed, mask)) {
|
||||
r = -EOPNOTSUPP;
|
||||
continue;
|
||||
}
|
||||
|
||||
return found ? 0 : -ENODATA;
|
||||
sfeatures->features[block].valid |= mask;
|
||||
SET_FLAG(sfeatures->features[block].requested, mask, flag);
|
||||
|
||||
found = true;
|
||||
}
|
||||
|
||||
return found ? 0 : r;
|
||||
}
|
||||
|
||||
int ethtool_set_features(int *ethtool_fd, const char *ifname, const int features[static _NET_DEV_FEAT_MAX]) {
|
||||
_cleanup_free_ struct ethtool_gstrings *strings = NULL;
|
||||
struct ethtool_sfeatures *sfeatures;
|
||||
struct ifreq ifr = {};
|
||||
_cleanup_free_ struct ethtool_gfeatures *gfeatures = NULL;
|
||||
_cleanup_free_ struct ethtool_sfeatures *sfeatures = NULL;
|
||||
struct ifreq ifr;
|
||||
bool have = false;
|
||||
int r;
|
||||
|
||||
@ -528,26 +576,32 @@ int ethtool_set_features(int *ethtool_fd, const char *ifname, const int features
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
|
||||
|
||||
r = get_stringset(*ethtool_fd, &ifr, ETH_SS_FEATURES, &strings);
|
||||
r = get_stringset(*ethtool_fd, ifname, ETH_SS_FEATURES, &strings);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "ethtool: could not get ethtool features for %s", ifname);
|
||||
return log_debug_errno(r, "ethtool: could not get ethtool feature strings: %m");
|
||||
|
||||
r = get_features(*ethtool_fd, ifname, strings->len, &gfeatures);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "ethtool: could not get ethtool features for %s: %m", ifname);
|
||||
|
||||
sfeatures = malloc0(offsetof(struct ethtool_sfeatures, features) +
|
||||
DIV_ROUND_UP(strings->len, 32U) * sizeof(sfeatures->features[0]));
|
||||
if (!sfeatures)
|
||||
return log_oom_debug();
|
||||
|
||||
sfeatures = alloca0(sizeof(struct ethtool_sfeatures) + DIV_ROUND_UP(strings->len, 32U) * sizeof(sfeatures->features[0]));
|
||||
sfeatures->cmd = ETHTOOL_SFEATURES;
|
||||
sfeatures->size = DIV_ROUND_UP(strings->len, 32U);
|
||||
|
||||
for (size_t i = 0; i < _NET_DEV_FEAT_MAX; i++)
|
||||
if (features[i] >= 0) {
|
||||
r = set_features_bit(strings, netdev_feature_table[i], features[i], sfeatures);
|
||||
if (r < 0) {
|
||||
log_debug_errno(r, "ethtool: could not find feature, ignoring: %s", netdev_feature_table[i]);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
for (size_t i = 0; i < _NET_DEV_FEAT_MAX; i++) {
|
||||
r = set_features_bit(strings, gfeatures, sfeatures, netdev_feature_table[i], features[i]);
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "ethtool: could not set feature %s for %s, ignoring: %m", netdev_feature_table[i], ifname);
|
||||
}
|
||||
|
||||
ifr.ifr_data = (void *) sfeatures;
|
||||
ifr = (struct ifreq) {
|
||||
.ifr_data = (void*) sfeatures,
|
||||
};
|
||||
strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
|
||||
|
||||
if (ioctl(*ethtool_fd, SIOCETHTOOL, &ifr) < 0)
|
||||
return log_debug_errno(errno, "ethtool: could not set ethtool features for %s", ifname);
|
||||
|
Loading…
Reference in New Issue
Block a user