diff options
-rw-r--r-- | configure.ac | 3 | ||||
-rw-r--r-- | man/systemd.link.xml | 14 | ||||
-rw-r--r-- | man/systemd.service.xml | 6 | ||||
-rw-r--r-- | src/basic/missing.h | 27 | ||||
-rw-r--r-- | src/core/namespace.c | 32 | ||||
-rw-r--r-- | src/libsystemd/sd-bus/busctl-introspect.c | 86 | ||||
-rw-r--r-- | src/udev/net/ethtool-util.c | 209 | ||||
-rw-r--r-- | src/udev/net/ethtool-util.h | 18 | ||||
-rw-r--r-- | src/udev/net/link-config-gperf.gperf | 1 | ||||
-rw-r--r-- | src/udev/net/link-config.c | 28 | ||||
-rw-r--r-- | src/udev/net/link-config.h | 1 |
11 files changed, 354 insertions, 71 deletions
diff --git a/configure.ac b/configure.ac index 7f6b3b937c..6423204178 100644 --- a/configure.ac +++ b/configure.ac @@ -319,9 +319,10 @@ AC_CHECK_DECLS([ #include <linux/random.h> ]]) -AC_CHECK_TYPES([char16_t, char32_t, key_serial_t], +AC_CHECK_TYPES([char16_t, char32_t, key_serial_t, struct ethtool_link_settings], [], [], [[ #include <uchar.h> +#include <linux/ethtool.h> ]]) AC_CHECK_DECLS([IFLA_INET6_ADDR_GEN_MODE, diff --git a/man/systemd.link.xml b/man/systemd.link.xml index 8edbe758d9..023e24eeb3 100644 --- a/man/systemd.link.xml +++ b/man/systemd.link.xml @@ -359,6 +359,20 @@ </listitem> </varlistentry> <varlistentry> + <term><varname>AutoNegotiation=</varname></term> + <listitem> + <para>Enables or disables automatic negotiation of transmission parameters. + Autonegotiation is a procedure by which two connected ethernet devices choose + common transmission parameters, such as speed, duplex mode, and flow control. + Takes a boolean value. Unset by default, which means that the kernel default + will be used.</para> + + <para>Note that if autonegotiation is enabled, speed and duplex settings are + read-only. If autonegotation is disabled, speed and duplex settings are writable + if the driver supports multiple link modes.</para> + </listitem> + </varlistentry> + <varlistentry> <term><varname>WakeOnLan=</varname></term> <listitem> <para>The Wake-on-LAN policy to set for the device. The diff --git a/man/systemd.service.xml b/man/systemd.service.xml index 5c65957bda..3ba6ab34db 100644 --- a/man/systemd.service.xml +++ b/man/systemd.service.xml @@ -663,6 +663,12 @@ or signal is specified in <varname>RestartForceExitStatus=</varname> (see below).</para> + <para>Note that service restart is subject to unit start rate + limiting configured with <varname>StartLimitIntervalSec=</varname> + and <varname>StartLimitBurst=</varname>, see + <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry> + for details.</para> + <para>Setting this to <option>on-failure</option> is the recommended choice for long-running services, in order to increase reliability by attempting automatic recovery from diff --git a/src/basic/missing.h b/src/basic/missing.h index 4c013be608..a5ae5d9e79 100644 --- a/src/basic/missing.h +++ b/src/basic/missing.h @@ -1076,6 +1076,33 @@ typedef int32_t key_serial_t; #define IFA_F_MCAUTOJOIN 0x400 #endif +#ifndef HAVE_STRUCT_ETHTOOL_LINK_SETTINGS + +#define ETHTOOL_GLINKSETTINGS 0x0000004c /* Get ethtool_link_settings */ +#define ETHTOOL_SLINKSETTINGS 0x0000004d /* Set ethtool_link_settings */ + +struct ethtool_link_settings { + __u32 cmd; + __u32 speed; + __u8 duplex; + __u8 port; + __u8 phy_address; + __u8 autoneg; + __u8 mdio_support; + __u8 eth_tp_mdix; + __u8 eth_tp_mdix_ctrl; + __s8 link_mode_masks_nwords; + __u32 reserved[8]; + __u32 link_mode_masks[0]; + /* layout of link_mode_masks fields: + * __u32 map_supported[link_mode_masks_nwords]; + * __u32 map_advertising[link_mode_masks_nwords]; + * __u32 map_lp_advertising[link_mode_masks_nwords]; + */ +}; + +#endif + #endif #include "missing_syscall.h" diff --git a/src/core/namespace.c b/src/core/namespace.c index 308e4d768e..67b203ba76 100644 --- a/src/core/namespace.c +++ b/src/core/namespace.c @@ -331,7 +331,7 @@ static void drop_duplicates(BindMount *m, unsigned *n) { /* Drops duplicate entries. Expects that the array is properly ordered already. */ - for (f = m, t = m, previous = NULL; f < m+*n; f++) { + for (f = m, t = m, previous = NULL; f < m + *n; f++) { /* The first one wins (which is the one with the more restrictive mode), see mount_path_compare() * above. */ @@ -359,7 +359,7 @@ static void drop_inaccessible(BindMount *m, unsigned *n) { /* Drops all entries obstructed by another entry further up the tree. Expects that the array is properly * ordered already. */ - for (f = m, t = m; f < m+*n; f++) { + for (f = m, t = m; f < m + *n; f++) { /* If we found a path set for INACCESSIBLE earlier, and this entry has it as prefix we should drop * it, as inaccessible paths really should drop the entire subtree. */ @@ -387,7 +387,7 @@ static void drop_nop(BindMount *m, unsigned *n) { /* Drops all entries which have an immediate parent that has the same type, as they are redundant. Assumes the * list is ordered by prefixes. */ - for (f = m, t = m; f < m+*n; f++) { + for (f = m, t = m; f < m + *n; f++) { /* Only suppress such subtrees for READONLY and READWRITE entries */ if (IN_SET(f->mode, READONLY, READWRITE)) { @@ -423,12 +423,13 @@ static void drop_outside_root(const char *root_directory, BindMount *m, unsigned assert(m); assert(n); + /* Nothing to do */ if (!root_directory) return; /* Drops all mounts that are outside of the root directory. */ - for (f = m, t = m; f < m+*n; f++) { + for (f = m, t = m; f < m + *n; f++) { if (!path_startswith(f->path, root_directory)) { log_debug("%s is outside of root directory.", f->path); @@ -671,9 +672,10 @@ static int make_read_only(BindMount *m, char **blacklist) { return r; } +/* Chase symlinks and remove failed paths from mounts */ static int chase_all_symlinks(const char *root_directory, BindMount *m, unsigned *n) { BindMount *f, *t; - int r; + int r = 0; assert(m); assert(n); @@ -683,22 +685,24 @@ static int chase_all_symlinks(const char *root_directory, BindMount *m, unsigned * can't resolve the path, and which have been marked for such removal. */ for (f = m, t = m; f < m + *n; f++) { + int k; _cleanup_free_ char *chased = NULL; - r = chase_symlinks(f->path, root_directory, &chased); - if (r == -ENOENT && f->ignore) { - /* Doesn't exist? Then remove it! */ + k = chase_symlinks(f->path, root_directory, &chased); + if (k < 0) { + /* Get only real errors */ + if (r >= 0 && (k != -ENOENT || !f->ignore)) + r = k; + + log_debug_errno(r, "Failed to chase symlinks for %s: %m", f->path); + /* Doesn't exist or failed? Then remove it and continue! */ f->path = mfree(f->path); continue; } - if (r < 0) - return log_debug_errno(r, "Failed to chase symlinks for %s: %m", f->path); if (!path_equal(f->path, chased)) { log_debug("Chased %s → %s", f->path, chased); - r = free_and_replace(f->path, chased); - if (r < 0) - return r; + free_and_replace(f->path, chased); } *t = *f; @@ -706,7 +710,7 @@ static int chase_all_symlinks(const char *root_directory, BindMount *m, unsigned } *n = t - m; - return 0; + return r; } static unsigned namespace_calculate_mounts( diff --git a/src/libsystemd/sd-bus/busctl-introspect.c b/src/libsystemd/sd-bus/busctl-introspect.c index 09cbd9ab44..a05794941f 100644 --- a/src/libsystemd/sd-bus/busctl-introspect.c +++ b/src/libsystemd/sd-bus/busctl-introspect.c @@ -143,9 +143,7 @@ static int parse_xml_annotation(Context *context, uint64_t *flags) { case STATE_NAME: if (t == XML_ATTRIBUTE_VALUE) { - free(field); - field = name; - name = NULL; + free_and_replace(field, name); state = STATE_ANNOTATION; } else { @@ -158,9 +156,7 @@ static int parse_xml_annotation(Context *context, uint64_t *flags) { case STATE_VALUE: if (t == XML_ATTRIBUTE_VALUE) { - free(value); - value = name; - name = NULL; + free_and_replace(value, name); state = STATE_ANNOTATION; } else { @@ -194,6 +190,7 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth STATE_SIGNAL_ARG, STATE_SIGNAL_ARG_NAME, STATE_SIGNAL_ARG_TYPE, + STATE_SIGNAL_ARG_DIRECTION, STATE_PROPERTY, STATE_PROPERTY_NAME, STATE_PROPERTY_TYPE, @@ -350,11 +347,8 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth case STATE_INTERFACE_NAME: if (t == XML_ATTRIBUTE_VALUE) { - if (n_depth == 0) { - free(context->interface_name); - context->interface_name = name; - name = NULL; - } + if (n_depth == 0) + free_and_replace(context->interface_name, name); state = STATE_INTERFACE; } else { @@ -409,12 +403,8 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth case STATE_METHOD_NAME: if (t == XML_ATTRIBUTE_VALUE) { - - if (n_depth == 0) { - free(context->member_name); - context->member_name = name; - name = NULL; - } + if (n_depth == 0) + free_and_replace(context->member_name, name); state = STATE_METHOD; } else { @@ -432,7 +422,7 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth else if (streq_ptr(name, "type")) state = STATE_METHOD_ARG_TYPE; else if (streq_ptr(name, "direction")) - state = STATE_METHOD_ARG_DIRECTION; + state = STATE_METHOD_ARG_DIRECTION; else { log_error("Unexpected method <arg> attribute %s.", name); return -EBADMSG; @@ -458,7 +448,8 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth } else if (streq(argument_direction, "out")) { if (!strextend(&context->member_result, argument_type, NULL)) return log_oom(); - } + } else + log_error("Unexpected method <arg> direction value '%s'.", argument_direction); } argument_type = mfree(argument_type); @@ -487,9 +478,7 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth case STATE_METHOD_ARG_TYPE: if (t == XML_ATTRIBUTE_VALUE) { - free(argument_type); - argument_type = name; - name = NULL; + free_and_replace(argument_type, name); state = STATE_METHOD_ARG; } else { @@ -502,9 +491,7 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth case STATE_METHOD_ARG_DIRECTION: if (t == XML_ATTRIBUTE_VALUE) { - free(argument_direction); - argument_direction = name; - name = NULL; + free_and_replace(argument_direction, name); state = STATE_METHOD_ARG; } else { @@ -559,12 +546,8 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth case STATE_SIGNAL_NAME: if (t == XML_ATTRIBUTE_VALUE) { - - if (n_depth == 0) { - free(context->member_name); - context->member_name = name; - name = NULL; - } + if (n_depth == 0) + free_and_replace(context->member_name, name); state = STATE_SIGNAL; } else { @@ -582,6 +565,8 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth state = STATE_SIGNAL_ARG_NAME; else if (streq_ptr(name, "type")) state = STATE_SIGNAL_ARG_TYPE; + else if (streq_ptr(name, "direction")) + state = STATE_SIGNAL_ARG_DIRECTION; else { log_error("Unexpected signal <arg> attribute %s.", name); return -EBADMSG; @@ -599,8 +584,11 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth (t == XML_TAG_CLOSE && streq_ptr(name, "arg"))) { if (argument_type) { - if (!strextend(&context->member_signature, argument_type, NULL)) - return log_oom(); + if (!argument_direction || streq(argument_direction, "out")) { + if (!strextend(&context->member_signature, argument_type, NULL)) + return log_oom(); + } else + log_error("Unexpected signal <arg> direction value '%s'.", argument_direction); argument_type = mfree(argument_type); } @@ -627,9 +615,7 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth case STATE_SIGNAL_ARG_TYPE: if (t == XML_ATTRIBUTE_VALUE) { - free(argument_type); - argument_type = name; - name = NULL; + free_and_replace(argument_type, name); state = STATE_SIGNAL_ARG; } else { @@ -639,6 +625,19 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth break; + case STATE_SIGNAL_ARG_DIRECTION: + + if (t == XML_ATTRIBUTE_VALUE) { + free_and_replace(argument_direction, name); + + state = STATE_SIGNAL_ARG; + } else { + log_error("Unexpected token in signal <arg>. (4)"); + return -EINVAL; + } + + break; + case STATE_PROPERTY: if (t == XML_ATTRIBUTE_NAME) { @@ -688,12 +687,9 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth case STATE_PROPERTY_NAME: if (t == XML_ATTRIBUTE_VALUE) { + if (n_depth == 0) + free_and_replace(context->member_name, name); - if (n_depth == 0) { - free(context->member_name); - context->member_name = name; - name = NULL; - } state = STATE_PROPERTY; } else { log_error("Unexpected token in <property>. (2)"); @@ -705,12 +701,8 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth case STATE_PROPERTY_TYPE: if (t == XML_ATTRIBUTE_VALUE) { - - if (n_depth == 0) { - free(context->member_signature); - context->member_signature = name; - name = NULL; - } + if (n_depth == 0) + free_and_replace(context->member_signature, name); state = STATE_PROPERTY; } else { diff --git a/src/udev/net/ethtool-util.c b/src/udev/net/ethtool-util.c index 708a665576..d7edbb396b 100644 --- a/src/udev/net/ethtool-util.c +++ b/src/udev/net/ethtool-util.c @@ -29,6 +29,7 @@ #include "string-table.h" #include "strxcpyx.h" #include "util.h" +#include "missing.h" static const char* const duplex_table[_DUP_MAX] = { [DUP_FULL] = "full", @@ -323,3 +324,211 @@ int ethtool_set_features(int *fd, const char *ifname, NetDevFeature *features) { return 0; } + +static int get_glinksettings(int *fd, struct ifreq *ifr, struct ethtool_link_usettings **g) { + struct ecmd { + struct ethtool_link_settings req; + __u32 link_mode_data[3 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32]; + } ecmd = { + .req.cmd = ETHTOOL_GLINKSETTINGS, + }; + struct ethtool_link_usettings *u; + unsigned offset; + int r; + + /* The interaction user/kernel via the new API requires a small ETHTOOL_GLINKSETTINGS + handshake first to agree on the length of the link mode bitmaps. If kernel doesn't + agree with user, it returns the bitmap length it is expecting from user as a negative + length (and cmd field is 0). When kernel and user agree, kernel returns valid info in + all fields (ie. link mode length > 0 and cmd is ETHTOOL_GLINKSETTINGS). Based on + https://github.com/torvalds/linux/commit/3f1ac7a700d039c61d8d8b99f28d605d489a60cf + */ + + ifr->ifr_data = (void *) &ecmd; + + r = ioctl(*fd, SIOCETHTOOL, ifr); + if (r < 0) + return -errno; + + if (ecmd.req.link_mode_masks_nwords >= 0 || ecmd.req.cmd != ETHTOOL_GLINKSETTINGS) + return -ENOTSUP; + + ecmd.req.link_mode_masks_nwords = -ecmd.req.link_mode_masks_nwords; + + ifr->ifr_data = (void *) &ecmd; + + r = ioctl(*fd, SIOCETHTOOL, ifr); + if (r < 0) + return -errno; + + if (ecmd.req.link_mode_masks_nwords <= 0 || ecmd.req.cmd != ETHTOOL_GLINKSETTINGS) + return -ENOTSUP; + + u = new0(struct ethtool_link_usettings , 1); + if (!u) + return -ENOMEM; + + memcpy(&u->base, &ecmd.req, sizeof(struct ethtool_link_settings)); + + offset = 0; + memcpy(u->link_modes.supported, &ecmd.link_mode_data[offset], 4 * ecmd.req.link_mode_masks_nwords); + + offset += ecmd.req.link_mode_masks_nwords; + memcpy(u->link_modes.advertising, &ecmd.link_mode_data[offset], 4 * ecmd.req.link_mode_masks_nwords); + + offset += ecmd.req.link_mode_masks_nwords; + memcpy(u->link_modes.lp_advertising, &ecmd.link_mode_data[offset], 4 * ecmd.req.link_mode_masks_nwords); + + *g = u; + + return 0; +} + +static int get_gset(int *fd, struct ifreq *ifr, struct ethtool_link_usettings **u) { + struct ethtool_link_usettings *e; + struct ethtool_cmd ecmd = { + .cmd = ETHTOOL_GSET, + }; + int r; + + ifr->ifr_data = (void *) &ecmd; + + r = ioctl(*fd, SIOCETHTOOL, ifr); + if (r < 0) + return -errno; + + e = new0(struct ethtool_link_usettings, 1); + if (!e) + return -ENOMEM; + + e->base.cmd = ETHTOOL_GSET; + + 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; + + *u = e; + + return 0; +} + +static int set_slinksettings(int *fd, struct ifreq *ifr, const struct ethtool_link_usettings *u) { + struct { + struct ethtool_link_settings req; + __u32 link_mode_data[3 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32]; + } ecmd = { + .req.cmd = ETHTOOL_SLINKSETTINGS, + }; + unsigned int offset; + int r; + + if (u->base.cmd != ETHTOOL_GLINKSETTINGS || u->base.link_mode_masks_nwords <= 0) + return -EINVAL; + + offset = 0; + memcpy(&ecmd.link_mode_data[offset], u->link_modes.supported, 4 * ecmd.req.link_mode_masks_nwords); + + offset += ecmd.req.link_mode_masks_nwords; + memcpy(&ecmd.link_mode_data[offset], u->link_modes.advertising, 4 * ecmd.req.link_mode_masks_nwords); + + offset += ecmd.req.link_mode_masks_nwords; + memcpy(&ecmd.link_mode_data[offset], u->link_modes.lp_advertising, 4 * ecmd.req.link_mode_masks_nwords); + + ifr->ifr_data = (void *) &ecmd; + + r = ioctl(*fd, SIOCETHTOOL, ifr); + if (r < 0) + return -errno; + + return 0; +} + +static int set_sset(int *fd, struct ifreq *ifr, const struct ethtool_link_usettings *u) { + struct ethtool_cmd ecmd = { + .cmd = ETHTOOL_SSET, + }; + int r; + + 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; + + ifr->ifr_data = (void *) &ecmd; + + r = ioctl(*fd, SIOCETHTOOL, ifr); + if (r < 0) + return -errno; + + return 0; +} + +/* If autonegotiation is disabled, the speed and duplex represent the fixed link + * mode and are writable if the driver supports multiple link modes. If it is + * enabled then they are read-only. If the link is up they represent the negotiated + * 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, unsigned int speed, Duplex duplex, int autonegotiation) { + _cleanup_free_ struct ethtool_link_usettings *u = NULL; + struct ifreq ifr = {}; + int r; + + if (autonegotiation != 0) { + log_info("link_config: autonegotiation is unset or enabled, the speed and duplex are not writable."); + return 0; + } + + if (*fd < 0) { + r = ethtool_connect(fd); + if (r < 0) + return log_warning_errno(r, "link_config: could not connect to ethtool: %m"); + } + + strscpy(ifr.ifr_name, IFNAMSIZ, ifname); + + r = get_glinksettings(fd, &ifr, &u); + 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); + } + + if (speed) + u->base.speed = speed; + + if (duplex != _DUP_INVALID) + u->base.duplex = duplex; + + u->base.autoneg = autonegotiation; + + if (u->base.cmd == ETHTOOL_GLINKSETTINGS) + r = set_slinksettings(fd, &ifr, u); + 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 r; +} diff --git a/src/udev/net/ethtool-util.h b/src/udev/net/ethtool-util.h index 0744164653..75d6af396b 100644 --- a/src/udev/net/ethtool-util.h +++ b/src/udev/net/ethtool-util.h @@ -20,6 +20,9 @@ ***/ #include <macro.h> +#include <linux/ethtool.h> + +#include "missing.h" /* we can't use DUPLEX_ prefix, as it * clashes with <linux/ethtool.h> */ @@ -48,12 +51,27 @@ typedef enum NetDevFeature { _NET_DEV_FEAT_INVALID = -1 } NetDevFeature; + +#define ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32 (SCHAR_MAX) + +/* layout of the struct passed from/to userland */ +struct ethtool_link_usettings { + struct ethtool_link_settings base; + + struct { + 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; +}; + int ethtool_connect(int *ret); int ethtool_get_driver(int *fd, const char *ifname, char **ret); int ethtool_set_speed(int *fd, const char *ifname, unsigned int speed, Duplex duplex); int ethtool_set_wol(int *fd, const char *ifname, WakeOnLan wol); int ethtool_set_features(int *fd, const char *ifname, NetDevFeature *features); +int ethtool_set_glinksettings(int *fd, const char *ifname, unsigned int speed, Duplex duplex, int autoneg); const char *duplex_to_string(Duplex d) _const_; Duplex duplex_from_string(const char *d) _pure_; diff --git a/src/udev/net/link-config-gperf.gperf b/src/udev/net/link-config-gperf.gperf index f8b85cbd13..78e551df22 100644 --- a/src/udev/net/link-config-gperf.gperf +++ b/src/udev/net/link-config-gperf.gperf @@ -34,6 +34,7 @@ Link.Alias, config_parse_ifalias, 0, Link.MTUBytes, config_parse_iec_size, 0, offsetof(link_config, mtu) Link.BitsPerSecond, config_parse_si_size, 0, offsetof(link_config, speed) Link.Duplex, config_parse_duplex, 0, offsetof(link_config, duplex) +Link.AutoNegotiation, config_parse_tristate, 0, offsetof(link_config, autonegotiation) Link.WakeOnLan, config_parse_wol, 0, offsetof(link_config, wol) Link.GenericSegmentationOffload, config_parse_tristate, 0, offsetof(link_config, features[NET_DEV_FEAT_GSO]) Link.TCPSegmentationOffload, config_parse_tristate, 0, offsetof(link_config, features[NET_DEV_FEAT_TSO]) diff --git a/src/udev/net/link-config.c b/src/udev/net/link-config.c index ece9248c2a..4578d0d9b2 100644 --- a/src/udev/net/link-config.c +++ b/src/udev/net/link-config.c @@ -167,6 +167,7 @@ static int load_link(link_config_ctx *ctx, const char *filename) { link->mac_policy = _MACPOLICY_INVALID; link->wol = _WOL_INVALID; link->duplex = _DUP_INVALID; + link->autonegotiation = -1; memset(&link->features, -1, _NET_DEV_FEAT_MAX); @@ -202,9 +203,9 @@ static bool enable_name_policy(void) { } int link_config_load(link_config_ctx *ctx) { - int r; _cleanup_strv_free_ char **files; char **f; + int r; link_configs_free(ctx); @@ -364,11 +365,12 @@ static int get_mac(struct udev_device *device, bool want_random, int link_config_apply(link_config_ctx *ctx, link_config *config, struct udev_device *device, const char **name) { - const char *old_name; - const char *new_name = NULL; + bool respect_predictable = false; struct ether_addr generated_mac; struct ether_addr *mac = NULL; - bool respect_predictable = false; + const char *new_name = NULL; + const char *old_name; + unsigned speed; int r, ifindex; assert(ctx); @@ -380,11 +382,19 @@ int link_config_apply(link_config_ctx *ctx, link_config *config, if (!old_name) return -EINVAL; - r = ethtool_set_speed(&ctx->ethtool_fd, old_name, config->speed / 1024, config->duplex); - if (r < 0) - log_warning_errno(r, "Could not set speed or duplex of %s to %zu Mbps (%s): %m", - old_name, config->speed / 1024, - duplex_to_string(config->duplex)); + + speed = DIV_ROUND_UP(config->speed, 1000000); + + r = ethtool_set_glinksettings(&ctx->ethtool_fd, old_name, speed, config->duplex, config->autonegotiation); + if (r < 0) { + + 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 or duplex of %s to %u Mbps (%s): %m", + old_name, speed, duplex_to_string(config->duplex)); + } r = ethtool_set_wol(&ctx->ethtool_fd, old_name, config->wol); if (r < 0) diff --git a/src/udev/net/link-config.h b/src/udev/net/link-config.h index 91cc0357c4..a99060d943 100644 --- a/src/udev/net/link-config.h +++ b/src/udev/net/link-config.h @@ -69,6 +69,7 @@ struct link_config { size_t mtu; size_t speed; Duplex duplex; + int autonegotiation; WakeOnLan wol; NetDevFeature features[_NET_DEV_FEAT_MAX]; |