mirror of
https://github.com/systemd/systemd
synced 2025-10-06 00:13:24 +02:00
ethtool-util: several cleanups for applying link settings (#38879)
This commit is contained in:
@@ -652,150 +652,51 @@ int ethtool_set_features(int *ethtool_fd, const char *ifname, const int features
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int get_glinksettings(int fd, struct ifreq *ifr, union ethtool_link_usettings **ret) {
|
static int get_link_settings(int fd, struct ifreq *ifr, struct ethtool_link_settings **ret) {
|
||||||
union ethtool_link_usettings ecmd = {
|
struct ethtool_link_settings ecmd = {
|
||||||
.base.cmd = ETHTOOL_GLINKSETTINGS,
|
.cmd = ETHTOOL_GLINKSETTINGS,
|
||||||
};
|
};
|
||||||
|
|
||||||
assert(fd >= 0);
|
assert(fd >= 0);
|
||||||
assert(ifr);
|
assert(ifr);
|
||||||
assert(ret);
|
assert(ret);
|
||||||
|
|
||||||
/* The interaction user/kernel via the new API requires a small ETHTOOL_GLINKSETTINGS
|
/* The interaction user/kernel via the new API requires a small ETHTOOL_GLINKSETTINGS handshake first
|
||||||
handshake first to agree on the length of the link mode bitmaps. If kernel doesn't
|
* to agree on the length of the link mode bitmaps. If kernel doesn't agree with user, it returns the
|
||||||
agree with user, it returns the bitmap length it is expecting from user as a negative
|
* bitmap length it is expecting from user as a negative length. When kernel and user agree, kernel
|
||||||
length (and cmd field is 0). When kernel and user agree, kernel returns valid info in
|
* returns valid info in all fields (with bitmap length > 0).
|
||||||
all fields (ie. link mode length > 0 and cmd is ETHTOOL_GLINKSETTINGS). Based on
|
* https://github.com/torvalds/linux/commit/3f1ac7a700d039c61d8d8b99f28d605d489a60cf
|
||||||
https://github.com/torvalds/linux/commit/3f1ac7a700d039c61d8d8b99f28d605d489a60cf
|
* https://github.com/torvalds/linux/commit/793cf87de9d1a62dc9079c3ec5fcc01cfc62fafb */
|
||||||
*/
|
|
||||||
|
|
||||||
ifr->ifr_data = (void*) &ecmd;
|
ifr->ifr_data = &ecmd;
|
||||||
|
|
||||||
if (ioctl(fd, SIOCETHTOOL, ifr) < 0)
|
if (ioctl(fd, SIOCETHTOOL, ifr) < 0)
|
||||||
return -errno;
|
return -errno;
|
||||||
|
|
||||||
if (ecmd.base.link_mode_masks_nwords >= 0 || ecmd.base.cmd != ETHTOOL_GLINKSETTINGS)
|
if (ecmd.link_mode_masks_nwords >= 0 || ecmd.cmd != ETHTOOL_GLINKSETTINGS)
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
ecmd.base.link_mode_masks_nwords = -ecmd.base.link_mode_masks_nwords;
|
int8_t n = -ecmd.link_mode_masks_nwords;
|
||||||
|
size_t sz = offsetof(struct ethtool_link_settings, link_mode_masks) + sizeof(uint32_t) * n * 3;
|
||||||
ifr->ifr_data = (void *) &ecmd;
|
_cleanup_free_ struct ethtool_link_settings *settings = calloc(sz, 1);
|
||||||
|
if (!settings)
|
||||||
if (ioctl(fd, SIOCETHTOOL, ifr) < 0)
|
|
||||||
return -errno;
|
|
||||||
|
|
||||||
if (ecmd.base.link_mode_masks_nwords <= 0 || ecmd.base.cmd != ETHTOOL_GLINKSETTINGS)
|
|
||||||
return -EOPNOTSUPP;
|
|
||||||
|
|
||||||
union ethtool_link_usettings *u = new0(union ethtool_link_usettings, 1);
|
|
||||||
if (!u)
|
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
u->base = ecmd.base;
|
settings->cmd = ETHTOOL_GLINKSETTINGS;
|
||||||
|
settings->link_mode_masks_nwords = n;
|
||||||
|
|
||||||
uint32_t *p = ecmd.base.link_mode_masks;
|
ifr->ifr_data = settings;
|
||||||
memcpy(u->link_modes.supported, p, sizeof(uint32_t) * ecmd.base.link_mode_masks_nwords);
|
if (ioctl(fd, SIOCETHTOOL, ifr) < 0)
|
||||||
p += ecmd.base.link_mode_masks_nwords;
|
return -errno;
|
||||||
memcpy(u->link_modes.advertising, p, sizeof(uint32_t) * ecmd.base.link_mode_masks_nwords);
|
|
||||||
p += ecmd.base.link_mode_masks_nwords;
|
|
||||||
memcpy(u->link_modes.lp_advertising, p, sizeof(uint32_t) * ecmd.base.link_mode_masks_nwords);
|
|
||||||
|
|
||||||
*ret = u;
|
if (settings->link_mode_masks_nwords != n || settings->cmd != ETHTOOL_GLINKSETTINGS)
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
|
*ret = TAKE_PTR(settings);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int get_gset(int fd, struct ifreq *ifr, union ethtool_link_usettings **ret) {
|
int ethtool_set_link_settings(
|
||||||
struct ethtool_cmd ecmd = {
|
|
||||||
.cmd = ETHTOOL_GSET,
|
|
||||||
};
|
|
||||||
|
|
||||||
assert(fd >= 0);
|
|
||||||
assert(ifr);
|
|
||||||
assert(ret);
|
|
||||||
|
|
||||||
ifr->ifr_data = (void *) &ecmd;
|
|
||||||
|
|
||||||
if (ioctl(fd, SIOCETHTOOL, ifr) < 0)
|
|
||||||
return -errno;
|
|
||||||
|
|
||||||
union ethtool_link_usettings *u = new(union ethtool_link_usettings, 1);
|
|
||||||
if (!u)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
*u = (union 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,
|
|
||||||
.base.eth_tp_mdix = ecmd.eth_tp_mdix,
|
|
||||||
.base.eth_tp_mdix_ctrl = ecmd.eth_tp_mdix_ctrl,
|
|
||||||
};
|
|
||||||
|
|
||||||
u->link_modes.supported[0] = ecmd.supported;
|
|
||||||
u->link_modes.advertising[0] = ecmd.advertising;
|
|
||||||
u->link_modes.lp_advertising[0] = ecmd.lp_advertising;
|
|
||||||
|
|
||||||
*ret = u;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int set_slinksettings(int fd, struct ifreq *ifr, const union ethtool_link_usettings *u) {
|
|
||||||
assert(fd >= 0);
|
|
||||||
assert(ifr);
|
|
||||||
assert(u);
|
|
||||||
|
|
||||||
if (u->base.cmd != ETHTOOL_GLINKSETTINGS || u->base.link_mode_masks_nwords <= 0)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
union ethtool_link_usettings ecmd = { .base = u->base };
|
|
||||||
ecmd.base.cmd = ETHTOOL_SLINKSETTINGS;
|
|
||||||
|
|
||||||
uint32_t *p = ecmd.base.link_mode_masks;
|
|
||||||
p = mempcpy(p, u->link_modes.supported, sizeof(uint32_t) * ecmd.base.link_mode_masks_nwords);
|
|
||||||
p = mempcpy(p, u->link_modes.advertising, sizeof(uint32_t) * ecmd.base.link_mode_masks_nwords);
|
|
||||||
memcpy(p, u->link_modes.lp_advertising, sizeof(uint32_t) * ecmd.base.link_mode_masks_nwords);
|
|
||||||
|
|
||||||
ifr->ifr_data = (void *) &ecmd;
|
|
||||||
|
|
||||||
return RET_NERRNO(ioctl(fd, SIOCETHTOOL, ifr));
|
|
||||||
}
|
|
||||||
|
|
||||||
static int set_sset(int fd, struct ifreq *ifr, const union ethtool_link_usettings *u) {
|
|
||||||
struct ethtool_cmd ecmd = {
|
|
||||||
.cmd = ETHTOOL_SSET,
|
|
||||||
};
|
|
||||||
|
|
||||||
assert(fd >= 0);
|
|
||||||
assert(ifr);
|
|
||||||
assert(u);
|
|
||||||
|
|
||||||
if (u->base.cmd != ETHTOOL_GSET || u->base.link_mode_masks_nwords <= 0)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
ecmd.supported = u->link_modes.supported[0];
|
|
||||||
ecmd.advertising = u->link_modes.advertising[0];
|
|
||||||
ecmd.lp_advertising = u->link_modes.lp_advertising[0];
|
|
||||||
|
|
||||||
ethtool_cmd_speed_set(&ecmd, u->base.speed);
|
|
||||||
|
|
||||||
ecmd.duplex = u->base.duplex;
|
|
||||||
ecmd.port = u->base.port;
|
|
||||||
ecmd.phy_address = u->base.phy_address;
|
|
||||||
ecmd.autoneg = u->base.autoneg;
|
|
||||||
ecmd.mdio_support = u->base.mdio_support;
|
|
||||||
ecmd.eth_tp_mdix = u->base.eth_tp_mdix;
|
|
||||||
ecmd.eth_tp_mdix_ctrl = u->base.eth_tp_mdix_ctrl;
|
|
||||||
|
|
||||||
ifr->ifr_data = (void *) &ecmd;
|
|
||||||
|
|
||||||
return RET_NERRNO(ioctl(fd, SIOCETHTOOL, ifr));
|
|
||||||
}
|
|
||||||
|
|
||||||
int ethtool_set_glinksettings(
|
|
||||||
int *fd,
|
int *fd,
|
||||||
const char *ifname,
|
const char *ifname,
|
||||||
int autonegotiation,
|
int autonegotiation,
|
||||||
@@ -805,7 +706,7 @@ int ethtool_set_glinksettings(
|
|||||||
NetDevPort port,
|
NetDevPort port,
|
||||||
uint8_t mdi) {
|
uint8_t mdi) {
|
||||||
|
|
||||||
_cleanup_free_ union ethtool_link_usettings *u = NULL;
|
_cleanup_free_ struct ethtool_link_settings *settings = NULL;
|
||||||
struct ifreq ifr = {};
|
struct ifreq ifr = {};
|
||||||
bool changed = false;
|
bool changed = false;
|
||||||
int r;
|
int r;
|
||||||
@@ -842,53 +743,50 @@ int ethtool_set_glinksettings(
|
|||||||
|
|
||||||
strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
|
strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
|
||||||
|
|
||||||
r = get_glinksettings(*fd, &ifr, &u);
|
r = get_link_settings(*fd, &ifr, &settings);
|
||||||
if (r < 0) {
|
if (r < 0)
|
||||||
r = get_gset(*fd, &ifr, &u);
|
return log_debug_errno(r, "ethtool: Cannot get link settings for %s: %m", ifname);
|
||||||
if (r < 0)
|
|
||||||
return log_debug_errno(r, "ethtool: Cannot get device settings for %s: %m", ifname);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (speed > 0)
|
if (speed > 0)
|
||||||
UPDATE(u->base.speed, DIV_ROUND_UP(speed, 1000000), changed);
|
UPDATE(settings->speed, DIV_ROUND_UP(speed, 1000000), changed);
|
||||||
|
|
||||||
if (duplex >= 0)
|
if (duplex >= 0)
|
||||||
UPDATE(u->base.duplex, duplex, changed);
|
UPDATE(settings->duplex, duplex, changed);
|
||||||
|
|
||||||
if (port >= 0)
|
if (port >= 0)
|
||||||
UPDATE(u->base.port, port, changed);
|
UPDATE(settings->port, port, changed);
|
||||||
|
|
||||||
if (autonegotiation >= 0)
|
if (autonegotiation >= 0)
|
||||||
UPDATE(u->base.autoneg, autonegotiation, changed);
|
UPDATE(settings->autoneg, autonegotiation, changed);
|
||||||
|
|
||||||
if (!memeqzero(advertise, sizeof(uint32_t) * N_ADVERTISE)) {
|
if (!memeqzero(advertise, sizeof(uint32_t) * N_ADVERTISE)) {
|
||||||
UPDATE(u->base.autoneg, AUTONEG_ENABLE, changed);
|
UPDATE(settings->autoneg, AUTONEG_ENABLE, changed);
|
||||||
|
|
||||||
|
uint32_t *a = settings->link_mode_masks + settings->link_mode_masks_nwords;
|
||||||
|
size_t n = MIN(settings->link_mode_masks_nwords, N_ADVERTISE);
|
||||||
|
|
||||||
changed = changed ||
|
changed = changed ||
|
||||||
memcmp(&u->link_modes.advertising, advertise, sizeof(uint32_t) * N_ADVERTISE) != 0 ||
|
memcmp(a, advertise, sizeof(uint32_t) * n) != 0 ||
|
||||||
!memeqzero((uint8_t*) &u->link_modes.advertising + sizeof(uint32_t) * N_ADVERTISE,
|
!memeqzero(a + n, sizeof(uint32_t) * (settings->link_mode_masks_nwords - n));
|
||||||
ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NBYTES - sizeof(uint32_t) * N_ADVERTISE);
|
|
||||||
memcpy(&u->link_modes.advertising, advertise, sizeof(uint32_t) * N_ADVERTISE);
|
memcpy(a, advertise, sizeof(uint32_t) * n);
|
||||||
memzero((uint8_t*) &u->link_modes.advertising + sizeof(uint32_t) * N_ADVERTISE,
|
memzero(a + n, sizeof(uint32_t) * (settings->link_mode_masks_nwords - n));
|
||||||
ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NBYTES - sizeof(uint32_t) * N_ADVERTISE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mdi != ETH_TP_MDI_INVALID) {
|
if (mdi != ETH_TP_MDI_INVALID) {
|
||||||
if (u->base.eth_tp_mdix_ctrl == ETH_TP_MDI_INVALID)
|
if (settings->eth_tp_mdix_ctrl == ETH_TP_MDI_INVALID)
|
||||||
log_debug("ethtool: setting MDI not supported for %s, ignoring.", ifname);
|
log_debug("ethtool: setting MDI not supported for %s, ignoring.", ifname);
|
||||||
else
|
else
|
||||||
UPDATE(u->base.eth_tp_mdix_ctrl, mdi, changed);
|
UPDATE(settings->eth_tp_mdix_ctrl, mdi, changed);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!changed)
|
if (!changed)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (u->base.cmd == ETHTOOL_GLINKSETTINGS)
|
settings->cmd = ETHTOOL_SLINKSETTINGS;
|
||||||
r = set_slinksettings(*fd, &ifr, u);
|
ifr.ifr_data = settings;
|
||||||
else
|
if (ioctl(*fd, SIOCETHTOOL, &ifr) < 0)
|
||||||
r = set_sset(*fd, &ifr, u);
|
return log_debug_errno(errno, "ethtool: Cannot set link settings for %s: %m", ifname);
|
||||||
if (r < 0)
|
|
||||||
return log_debug_errno(r, "ethtool: Cannot set device settings for %s: %m", ifname);
|
|
||||||
|
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
@@ -100,21 +100,6 @@ typedef enum NetDevPort {
|
|||||||
_NET_DEV_PORT_INVALID = -EINVAL,
|
_NET_DEV_PORT_INVALID = -EINVAL,
|
||||||
} NetDevPort;
|
} NetDevPort;
|
||||||
|
|
||||||
#define ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32 (SCHAR_MAX)
|
|
||||||
#define ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NBYTES (4 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32)
|
|
||||||
|
|
||||||
/* layout of the struct passed from/to userland */
|
|
||||||
union ethtool_link_usettings {
|
|
||||||
struct ethtool_link_settings base;
|
|
||||||
|
|
||||||
struct {
|
|
||||||
uint8_t header[offsetof(struct ethtool_link_settings, link_mode_masks)];
|
|
||||||
uint32_t supported[ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32];
|
|
||||||
uint32_t advertising[ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32];
|
|
||||||
uint32_t lp_advertising[ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32];
|
|
||||||
} link_modes;
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef struct u32_opt {
|
typedef struct u32_opt {
|
||||||
uint32_t value; /* a value of 0 indicates the hardware advertised maximum should be used. */
|
uint32_t value; /* a value of 0 indicates the hardware advertised maximum should be used. */
|
||||||
bool set;
|
bool set;
|
||||||
@@ -167,7 +152,7 @@ int ethtool_get_permanent_hw_addr(int *ethtool_fd, const char *ifname, struct hw
|
|||||||
int ethtool_set_wol(int *ethtool_fd, const char *ifname, uint32_t wolopts, const uint8_t password[SOPASS_MAX]);
|
int ethtool_set_wol(int *ethtool_fd, const char *ifname, uint32_t wolopts, const uint8_t password[SOPASS_MAX]);
|
||||||
int ethtool_set_nic_buffer_size(int *ethtool_fd, const char *ifname, const netdev_ring_param *ring);
|
int ethtool_set_nic_buffer_size(int *ethtool_fd, const char *ifname, const netdev_ring_param *ring);
|
||||||
int ethtool_set_features(int *ethtool_fd, const char *ifname, const int features[static _NET_DEV_FEAT_MAX]);
|
int ethtool_set_features(int *ethtool_fd, const char *ifname, const int features[static _NET_DEV_FEAT_MAX]);
|
||||||
int ethtool_set_glinksettings(
|
int ethtool_set_link_settings(
|
||||||
int *fd,
|
int *fd,
|
||||||
const char *ifname,
|
const char *ifname,
|
||||||
int autonegotiation,
|
int autonegotiation,
|
||||||
|
@@ -491,7 +491,7 @@ static int link_apply_ethtool_settings(Link *link, int *ethtool_fd) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
r = ethtool_set_glinksettings(ethtool_fd, name,
|
r = ethtool_set_link_settings(ethtool_fd, name,
|
||||||
config->autonegotiation, config->advertise,
|
config->autonegotiation, config->advertise,
|
||||||
config->speed, config->duplex, config->port, config->mdi);
|
config->speed, config->duplex, config->port, config->mdi);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
|
Reference in New Issue
Block a user