From: Susant Sahani Date: Sat, 16 Sep 2017 18:36:56 +0000 (+0530) Subject: udev/net: add support for the equivalent of "ethtool advertise" to .link files X-Git-Tag: v240~676 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=6cf0a204912e8fe21c681ca038c2ff1b9d9ffeb9;p=thirdparty%2Fsystemd.git udev/net: add support for the equivalent of "ethtool advertise" to .link files This work adds support for the equivalent of "ethtool advertise" to .link files? http://lists.freedesktop.org/archives/systemd-devel/2015-April/030112.html --- diff --git a/man/systemd.link.xml b/man/systemd.link.xml index 16c4aeba656..66c788afcb4 100644 --- a/man/systemd.link.xml +++ b/man/systemd.link.xml @@ -370,8 +370,8 @@ Takes a boolean value. Unset by default, which means that the kernel default will be used. - Note that if autonegotiation is enabled, speed and duplex settings are - read-only. If autonegotation is disabled, speed and duplex settings are writable + Note that if autonegotiation is enabled, speed, duplex and advertise settings are + read-only. If autonegotation is disabled, speed, duplex and advertise settings are writable if the driver supports multiple link modes. @@ -478,6 +478,77 @@ + + Advertise= + + This sets what speeds and duplex modes of operation are advertised for auto-negotiation. + The supported values are: + + + Supported advertise values + + + + + + + Advertise + Speed (Mbps) + Duplex Mode + + + + 10baset-half + 10half + + 10baset-full + 10full + + 100baset-half + 100half + + 100baset-full + 100full + + 1000baset-half + 1000half + + 1000baset-full + 1000full + + 10000baset-full + 10000full + + 2500basex-full + 2500full + + 1000basekx-full + 1000full + + 10000basekx4-full + 10000full + + 10000basekr-full + 10000full + + 10000baser-fec + 10000full + + 20000basemld2-full + 20000full + + 20000basekr2-full + 20000full + + +
+ + By default this is unset, i.e. all possible modes will be advertised. + This option may be specified more than once, in which case all specified speeds and modes are advertised. + If the empty string is assigned to this option, the list is reset, and all prior assignments have no effect. +
+
+
TCPSegmentationOffload= diff --git a/src/udev/net/ethtool-util.c b/src/udev/net/ethtool-util.c index 51c0cea7feb..d5cf82ed7ac 100644 --- a/src/udev/net/ethtool-util.c +++ b/src/udev/net/ethtool-util.c @@ -56,6 +56,25 @@ static const char* const netdev_feature_table[_NET_DEV_FEAT_MAX] = { [NET_DEV_FEAT_TSO6] = "tx-tcp6-segmentation", }; +static const char* const advertise_table[_NET_DEV_ADVERTISE_MAX] = { + [NET_DEV_ADVERTISE_10BASET_HALF] = "10baset-half", + [NET_DEV_ADVERTISE_10BASET_FULL] = "10baset-full", + [NET_DEV_ADVERTISE_100BASET_HALF] = "100baset-half", + [NET_DEV_ADVERTISE_100BASET_FULL] = "100baset-full", + [NET_DEV_ADVERTISE_1000BASET_HALF] = "1000baset-half", + [NET_DEV_ADVERTISE_1000BASET_FULL] = "1000baset-full", + [NET_DEV_ADVERTISE_10000BASET_FULL] = "10000baset-full", + [NET_DEV_ADVERTISE_2500BASEX_FULL] = "2500basex-full", + [NET_DEV_ADVERTISE_1000BASEKX_FULL] = "1000basekx-full", + [NET_DEV_ADVERTISE_10000BASEKX4_FULL] = "10000basekx4-full", + [NET_DEV_ADVERTISE_10000BASEKR_FULL] = "10000basekr-full", + [NET_DEV_ADVERTISE_10000BASER_FEC] = "10000baser-fec", + [NET_DEV_ADVERTISE_20000BASEMLD2_Full] = "20000basemld2-full", + [NET_DEV_ADVERTISE_20000BASEKR2_Full] = "20000basekr2-full", +}; + +DEFINE_STRING_TABLE_LOOKUP(advertise, NetDevAdvertise); + int ethtool_connect(int *ret) { int fd; @@ -501,6 +520,8 @@ static int set_sset(int fd, struct ifreq *ifr, const struct ethtool_link_usettin 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; @@ -517,7 +538,6 @@ 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) { _cleanup_free_ struct ethtool_link_usettings *u = NULL; struct ifreq ifr = {}; @@ -554,6 +574,13 @@ int ethtool_set_glinksettings(int *fd, const char *ifname, struct link_config *l u->base.autoneg = link->autonegotiation; + if (link->advertise) { + uint32_t advertise[ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32] = {}; + + advertise[0] = link->advertise; + memcpy(&u->link_modes.advertising, advertise, ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NBYTES); + } + if (u->base.cmd == ETHTOOL_GLINKSETTINGS) r = set_slinksettings(*fd, &ifr, u); else @@ -665,3 +692,56 @@ int ethtool_set_channels(int *fd, const char *ifname, netdev_channels *channels) return 0; } + +int config_parse_advertise(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; + NetDevAdvertise mode, a = 0; + const char *p; + int r; + + assert(filename); + assert(section); + assert(lvalue); + assert(rvalue); + assert(data); + + if (isempty(rvalue)) { + /* Empty string resets the value. */ + config->advertise = 0; + return 0; + } + + for (p = rvalue;;) { + _cleanup_free_ char *w = NULL; + + r = extract_first_word(&p, &w, NULL, 0); + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to split advertise modes '%s', ignoring: %m", rvalue); + break; + } + if (r == 0) + break; + + mode = advertise_from_string(w); + if (mode == _NET_DEV_ADVERTISE_INVALID) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse advertise mode, ignoring: %s", w); + continue; + } + a |= mode; + } + + config->advertise |= a; + + return 0; +} diff --git a/src/udev/net/ethtool-util.h b/src/udev/net/ethtool-util.h index 2aa067a32b5..208bf75061c 100644 --- a/src/udev/net/ethtool-util.h +++ b/src/udev/net/ethtool-util.h @@ -53,7 +53,27 @@ typedef enum NetDevPort { _NET_DEV_PORT_INVALID = -1 } NetDevPort; +typedef enum NetDevAdvertise { + NET_DEV_ADVERTISE_10BASET_HALF = 1 << ETHTOOL_LINK_MODE_10baseT_Half_BIT, + NET_DEV_ADVERTISE_10BASET_FULL = 1 << ETHTOOL_LINK_MODE_10baseT_Full_BIT, + NET_DEV_ADVERTISE_100BASET_HALF = 1 << ETHTOOL_LINK_MODE_100baseT_Half_BIT, + NET_DEV_ADVERTISE_100BASET_FULL = 1 << ETHTOOL_LINK_MODE_100baseT_Full_BIT, + NET_DEV_ADVERTISE_1000BASET_HALF = 1 << ETHTOOL_LINK_MODE_1000baseT_Half_BIT, + NET_DEV_ADVERTISE_1000BASET_FULL = 1 << ETHTOOL_LINK_MODE_1000baseT_Full_BIT, + NET_DEV_ADVERTISE_10000BASET_FULL = 1 << ETHTOOL_LINK_MODE_10000baseT_Full_BIT, + NET_DEV_ADVERTISE_2500BASEX_FULL = 1 << ETHTOOL_LINK_MODE_2500baseX_Full_BIT, + NET_DEV_ADVERTISE_1000BASEKX_FULL = 1 << ETHTOOL_LINK_MODE_1000baseKX_Full_BIT, + NET_DEV_ADVERTISE_10000BASEKX4_FULL = 1 << ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT, + NET_DEV_ADVERTISE_10000BASEKR_FULL = 1 << ETHTOOL_LINK_MODE_10000baseKR_Full_BIT, + NET_DEV_ADVERTISE_10000BASER_FEC = 1 << ETHTOOL_LINK_MODE_10000baseR_FEC_BIT, + NET_DEV_ADVERTISE_20000BASEMLD2_Full = 1 << ETHTOOL_LINK_MODE_20000baseMLD2_Full_BIT, + NET_DEV_ADVERTISE_20000BASEKR2_Full = 1 << ETHTOOL_LINK_MODE_20000baseKR2_Full_BIT, + _NET_DEV_ADVERTISE_MAX, + _NET_DEV_ADVERTISE_INVALID = -1, +} NetDevAdvertise; + #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 */ struct ethtool_link_usettings { @@ -96,7 +116,11 @@ WakeOnLan wol_from_string(const char *wol) _pure_; const char *port_to_string(NetDevPort port) _const_; NetDevPort port_from_string(const char *port) _pure_; +const char *advertise_to_string(NetDevAdvertise advertise) _const_; +NetDevAdvertise advertise_from_string(const char *advertise) _pure_; + int config_parse_duplex(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); int config_parse_wol(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); int config_parse_port(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); 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); +int config_parse_advertise(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); diff --git a/src/udev/net/link-config-gperf.gperf b/src/udev/net/link-config-gperf.gperf index 5640fa05137..2bc18bff56a 100644 --- a/src/udev/net/link-config-gperf.gperf +++ b/src/udev/net/link-config-gperf.gperf @@ -51,3 +51,4 @@ Link.RxChannels, config_parse_channel, 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 diff --git a/src/udev/net/link-config.c b/src/udev/net/link-config.c index e9f7e7429f6..fd1c205d120 100644 --- a/src/udev/net/link-config.c +++ b/src/udev/net/link-config.c @@ -376,13 +376,21 @@ int link_config_apply(link_config_ctx *ctx, link_config *config, if (config->port != _NET_DEV_PORT_INVALID) log_warning_errno(r, "Could not set port (%s) of %s: %m", port_to_string(config->port), old_name); - speed = DIV_ROUND_UP(config->speed, 1000000); - if (r == -EOPNOTSUPP) - r = ethtool_set_speed(&ctx->ethtool_fd, old_name, speed, config->duplex); + if (config->advertise) + log_warning_errno(r, "Could not set advertise mode to 0x%X: %m", config->advertise); - if (r < 0) - log_warning_errno(r, "Could not set speed or duplex of %s to %u Mbps (%s): %m", - old_name, speed, duplex_to_string(config->duplex)); + if (config->speed) { + + speed = DIV_ROUND_UP(config->speed, 1000000); + if (r == -EOPNOTSUPP) { + r = ethtool_set_speed(&ctx->ethtool_fd, old_name, speed, config->duplex); + if (r < 0) + log_warning_errno(r, "Could not set speed of %s to %u Mbps: %m", old_name, speed); + } + } + + if (config->duplex !=_DUP_INVALID) + log_warning_errno(r, "Could not set duplex of %s to (%s): %m", old_name, duplex_to_string(config->duplex)); } r = ethtool_set_wol(&ctx->ethtool_fd, old_name, config->wol); diff --git a/src/udev/net/link-config.h b/src/udev/net/link-config.h index 3f78785faa4..ca6c8ddabe7 100644 --- a/src/udev/net/link-config.h +++ b/src/udev/net/link-config.h @@ -54,6 +54,7 @@ struct link_config { size_t speed; Duplex duplex; int autonegotiation; + uint32_t advertise; WakeOnLan wol; NetDevPort port; int features[_NET_DEV_FEAT_MAX];