diff --git a/man/systemd.link.xml b/man/systemd.link.xml index 700defeda6b..55ca597c97a 100644 --- a/man/systemd.link.xml +++ b/man/systemd.link.xml @@ -946,6 +946,21 @@ + + MDI= + + Specifies the medium dependent interface (MDI) mode for the interface. A MDI describes + the interface from a physical layer implementation to the physical medium used to carry the + transmission. Takes one of the following words: straight (or equivalently: + mdi), crossover (or equivalently: + mdi-x, mdix), and auto. When + straight, the MDI straight through mode will be used. When + crossover, the MDI crossover (MDI-X) mode will be used. When + auto, the MDI status is automatically detected. Defaults to unset, and the + kernel's default will be used. + + + SR-IOVVirtualFunctions= diff --git a/src/shared/ethtool-util.c b/src/shared/ethtool-util.c index 489a29fa2e8..6c9aeb1c6c6 100644 --- a/src/shared/ethtool-util.c +++ b/src/shared/ethtool-util.c @@ -74,6 +74,15 @@ static const char* const port_table[] = { DEFINE_STRING_TABLE_LOOKUP(port, NetDevPort); DEFINE_CONFIG_PARSE_ENUM(config_parse_port, port, NetDevPort, "Failed to parse Port setting"); +static const char* const mdi_table[] = { + [ETH_TP_MDI_INVALID] = "unknown", + [ETH_TP_MDI] = "mdi", + [ETH_TP_MDI_X] = "mdi-x", + [ETH_TP_MDI_AUTO] = "auto", +}; + +DEFINE_STRING_TABLE_LOOKUP_TO_STRING(mdi, int); + static const char* const netdev_feature_table[_NET_DEV_FEAT_MAX] = { [NET_DEV_FEAT_SG] = "tx-scatter-gather", [NET_DEV_FEAT_IP_CSUM] = "tx-checksum-ipv4", @@ -835,6 +844,8 @@ static int get_gset(int fd, struct ifreq *ifr, struct ethtool_link_usettings **r .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, .link_modes.supported[0] = ecmd.supported, .link_modes.advertising[0] = ecmd.advertising, @@ -914,7 +925,8 @@ int ethtool_set_glinksettings( const uint32_t advertise[static N_ADVERTISE], uint64_t speed, Duplex duplex, - NetDevPort port) { + NetDevPort port, + uint8_t mdi) { _cleanup_free_ struct ethtool_link_usettings *u = NULL; struct ifreq ifr = {}; @@ -926,7 +938,7 @@ int ethtool_set_glinksettings( assert(advertise); if (autonegotiation < 0 && memeqzero(advertise, sizeof(uint32_t) * N_ADVERTISE) && - speed == 0 && duplex < 0 && port < 0) + speed == 0 && duplex < 0 && port < 0 && mdi == ETH_TP_MDI_INVALID) return 0; /* If autonegotiation is disabled, the speed and duplex represent the fixed link mode and are @@ -957,7 +969,7 @@ int ethtool_set_glinksettings( if (r < 0) { r = get_gset(*fd, &ifr, &u); if (r < 0) - return log_debug_errno(r, "ethtool: Cannot get device settings for %s : %m", ifname); + return log_debug_errno(r, "ethtool: Cannot get device settings for %s: %m", ifname); } if (speed > 0) @@ -984,6 +996,13 @@ int ethtool_set_glinksettings( ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NBYTES - sizeof(uint32_t) * N_ADVERTISE); } + if (mdi != ETH_TP_MDI_INVALID) { + if (u->base.eth_tp_mdix_ctrl == ETH_TP_MDI_INVALID) + log_debug("ethtool: setting MDI not supported for %s, ignoring.", ifname); + else + UPDATE(u->base.eth_tp_mdix_ctrl, mdi, changed); + } + if (!changed) return 0; @@ -1261,6 +1280,48 @@ int config_parse_advertise( } } +int config_parse_mdi( + 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) { + + uint8_t *mdi = ASSERT_PTR(data); + + assert(filename); + assert(rvalue); + + if (isempty(rvalue)) { + *mdi = ETH_TP_MDI_INVALID; + return 0; + } + + if (STR_IN_SET(rvalue, "mdi", "straight")) { + *mdi = ETH_TP_MDI; + return 0; + } + + if (STR_IN_SET(rvalue, "mdi-x", "mdix", "crossover")) { + *mdi = ETH_TP_MDI_X; + return 0; + } + + if (streq(rvalue, "auto")) { + *mdi = ETH_TP_MDI_AUTO; + return 0; + } + + log_syntax(unit, LOG_WARNING, filename, line, 0, + "Failed to parse %s= setting, ignoring assignment: %s", lvalue, rvalue); + return 0; +} + int config_parse_ring_buffer_or_channel( const char *unit, const char *filename, diff --git a/src/shared/ethtool-util.h b/src/shared/ethtool-util.h index 8ca221bc090..d07cfaefb26 100644 --- a/src/shared/ethtool-util.h +++ b/src/shared/ethtool-util.h @@ -168,9 +168,15 @@ 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_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_glinksettings(int *ethtool_fd, const char *ifname, - int autonegotiation, const uint32_t advertise[static N_ADVERTISE], - uint64_t speed, Duplex duplex, NetDevPort port); +int ethtool_set_glinksettings( + int *fd, + const char *ifname, + int autonegotiation, + const uint32_t advertise[static N_ADVERTISE], + uint64_t speed, + Duplex duplex, + NetDevPort port, + uint8_t mdi); int ethtool_set_channels(int *ethtool_fd, const char *ifname, const netdev_channels *channels); int ethtool_set_flow_control(int *fd, const char *ifname, int rx, int tx, int autoneg); int ethtool_set_nic_coalesce_settings(int *ethtool_fd, const char *ifname, const netdev_coalesce_param *coalesce); @@ -183,12 +189,15 @@ int wol_options_to_string_alloc(uint32_t opts, char **ret); const char *port_to_string(NetDevPort port) _const_; NetDevPort port_from_string(const char *port) _pure_; +const char *mdi_to_string(int mdi) _const_; + const char *ethtool_link_mode_bit_to_string(enum ethtool_link_mode_bit_indices val) _const_; enum ethtool_link_mode_bit_indices ethtool_link_mode_bit_from_string(const char *str) _pure_; CONFIG_PARSER_PROTOTYPE(config_parse_duplex); CONFIG_PARSER_PROTOTYPE(config_parse_wol); CONFIG_PARSER_PROTOTYPE(config_parse_port); +CONFIG_PARSER_PROTOTYPE(config_parse_mdi); CONFIG_PARSER_PROTOTYPE(config_parse_advertise); CONFIG_PARSER_PROTOTYPE(config_parse_ring_buffer_or_channel); CONFIG_PARSER_PROTOTYPE(config_parse_coalesce_u32); diff --git a/src/udev/net/link-config-gperf.gperf b/src/udev/net/link-config-gperf.gperf index 7dde4ed59f5..fe33507694d 100644 --- a/src/udev/net/link-config-gperf.gperf +++ b/src/udev/net/link-config-gperf.gperf @@ -102,6 +102,7 @@ Link.RxMaxCoalescedHighFrames, config_parse_coalesce_u32, Link.TxCoalesceHighSec, config_parse_coalesce_sec, 0, offsetof(LinkConfig, coalesce.tx_coalesce_usecs_high) Link.TxMaxCoalescedHighFrames, config_parse_coalesce_u32, 0, offsetof(LinkConfig, coalesce.tx_max_coalesced_frames_high) Link.CoalescePacketRateSampleIntervalSec, config_parse_coalesce_sec, 0, offsetof(LinkConfig, coalesce.rate_sample_interval) +Link.MDI, config_parse_mdi, 0, offsetof(LinkConfig, mdi) Link.SR-IOVVirtualFunctions, config_parse_sr_iov_num_vfs, 0, offsetof(LinkConfig, sr_iov_num_vfs) SR-IOV.VirtualFunction, config_parse_sr_iov_uint32, 0, offsetof(LinkConfig, sr_iov_by_section) SR-IOV.VLANId, config_parse_sr_iov_uint32, 0, offsetof(LinkConfig, sr_iov_by_section) diff --git a/src/udev/net/link-config.c b/src/udev/net/link-config.c index 7e2da6088c4..29e960acc04 100644 --- a/src/udev/net/link-config.c +++ b/src/udev/net/link-config.c @@ -250,6 +250,7 @@ int link_load_one(LinkConfigContext *ctx, const char *filename) { .txqueuelen = UINT32_MAX, .coalesce.use_adaptive_rx_coalesce = -1, .coalesce.use_adaptive_tx_coalesce = -1, + .mdi = ETH_TP_MDI_INVALID, .sr_iov_num_vfs = UINT32_MAX, }; @@ -475,7 +476,7 @@ static int link_apply_ethtool_settings(Link *link, int *ethtool_fd) { r = ethtool_set_glinksettings(ethtool_fd, name, config->autonegotiation, config->advertise, - config->speed, config->duplex, config->port); + config->speed, config->duplex, config->port, config->mdi); if (r < 0) { if (config->autonegotiation >= 0) log_link_warning_errno(link, r, "Could not %s auto negotiation, ignoring: %m", @@ -495,6 +496,10 @@ static int link_apply_ethtool_settings(Link *link, int *ethtool_fd) { if (config->port >= 0) log_link_warning_errno(link, r, "Could not set port to '%s', ignoring: %m", port_to_string(config->port)); + + if (config->mdi != ETH_TP_MDI_INVALID) + log_link_warning_errno(link, r, "Could not set MDI-X to '%s', ignoring: %m", + mdi_to_string(config->mdi)); } r = ethtool_set_wol(ethtool_fd, name, config->wol, config->wol_password); diff --git a/src/udev/net/link-config.h b/src/udev/net/link-config.h index 0d1d117f2e1..90cb438e4b6 100644 --- a/src/udev/net/link-config.h +++ b/src/udev/net/link-config.h @@ -76,6 +76,7 @@ struct LinkConfig { int tx_flow_control; int autoneg_flow_control; netdev_coalesce_param coalesce; + uint8_t mdi; uint32_t sr_iov_num_vfs; OrderedHashmap *sr_iov_by_section; diff --git a/test/fuzz/fuzz-link-parser/directives.link b/test/fuzz/fuzz-link-parser/directives.link index a5773826c58..bffdce0ec95 100644 --- a/test/fuzz/fuzz-link-parser/directives.link +++ b/test/fuzz/fuzz-link-parser/directives.link @@ -80,6 +80,7 @@ RxMaxCoalescedHighFrames= TxCoalesceHighSec= TxMaxCoalescedHighFrames= CoalescePacketRateSampleIntervalSec= +MDI= SR-IOVVirtualFunctions= [SR-IOV] VirtualFunction=