]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
udev/net: add support for the equivalent of "ethtool advertise" to .link files
authorSusant Sahani <susant@redhat.com>
Sat, 16 Sep 2017 18:36:56 +0000 (00:06 +0530)
committerZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Wed, 26 Sep 2018 20:21:42 +0000 (22:21 +0200)
This work adds support for the equivalent of "ethtool advertise" to .link files?
http://lists.freedesktop.org/archives/systemd-devel/2015-April/030112.html

man/systemd.link.xml
src/udev/net/ethtool-util.c
src/udev/net/ethtool-util.h
src/udev/net/link-config-gperf.gperf
src/udev/net/link-config.c
src/udev/net/link-config.h

index 16c4aeba65635163d29da9dbe15d7535df416098..66c788afcb473ffbd1e332f2175c2458d0b39457 100644 (file)
           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
+          <para>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.</para>
         </listitem>
       </varlistentry>
           </variablelist>
         </listitem>
       </varlistentry>
+      <varlistentry>
+        <term><varname>Advertise=</varname></term>
+        <listitem>
+          <para>This sets what speeds and duplex modes of operation are advertised for auto-negotiation.
+          The supported values are:
+
+          <table>
+            <title>Supported advertise values</title>
+            <tgroup cols='3'>
+              <colspec colname='Advertise' />
+              <colspec colname='Speed' />
+              <colspec colname='Duplex Mode' />
+
+              <thead><row>
+                <entry>Advertise</entry>
+                <entry>Speed (Mbps)</entry>
+                <entry>Duplex Mode</entry>
+              </row></thead>
+              <tbody>
+
+                <row><entry><literal>10baset-half</literal></entry>
+                <entry>10</entry><entry>half</entry></row>
+
+                <row><entry><literal>10baset-full</literal></entry>
+                <entry>10</entry><entry>full</entry></row>
+
+                <row><entry><literal>100baset-half</literal></entry>
+                <entry>100</entry><entry>half</entry></row>
+
+                <row><entry><literal>100baset-full</literal></entry>
+                <entry>100</entry><entry>full</entry></row>
+
+                <row><entry><literal>1000baset-half</literal></entry>
+                <entry>1000</entry><entry>half</entry></row>
+
+                <row><entry><literal>1000baset-full</literal></entry>
+                <entry>1000</entry><entry>full</entry></row>
+
+                <row><entry><literal>10000baset-full</literal></entry>
+                <entry>10000</entry><entry>full</entry></row>
+
+                <row><entry><literal>2500basex-full</literal></entry>
+                <entry>2500</entry><entry>full</entry></row>
+
+                <row><entry><literal>1000basekx-full</literal></entry>
+                <entry>1000</entry><entry>full</entry></row>
+
+                <row><entry><literal>10000basekx4-full</literal></entry>
+                <entry>10000</entry><entry>full</entry></row>
+
+                <row><entry><literal>10000basekr-full</literal></entry>
+                <entry>10000</entry><entry>full</entry></row>
+
+                <row><entry><literal>10000baser-fec</literal></entry>
+                <entry>10000</entry><entry>full</entry></row>
+
+                <row><entry><literal>20000basemld2-full</literal></entry>
+                <entry>20000</entry><entry>full</entry></row>
+
+                <row><entry><literal>20000basekr2-full</literal></entry>
+                <entry>20000</entry><entry>full</entry></row>
+              </tbody>
+            </tgroup>
+          </table>
+
+          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.
+          </para>
+        </listitem>
+      </varlistentry>
       <varlistentry>
         <term><varname>TCPSegmentationOffload=</varname></term>
         <listitem>
index 51c0cea7feb2cbd41825ec928a559b3ad8af4607..d5cf82ed7ac340fdcbd381fcd26db375246e3bb7 100644 (file)
@@ -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;
+}
index 2aa067a32b54ac76eb3cdd099602ed2f95ea9ad3..208bf75061c33c532cfc1918a1cdc7a69a2d2748 100644 (file)
@@ -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);
index 5640fa051376820cc3be5c2761511bf1fe39dc14..2bc18bff56a76acf2453de3f5177a9e8b86065b2 100644 (file)
@@ -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
index e9f7e7429f63b73e24b36f732b0ab80a20bcd8eb..fd1c205d120d6362929003434c828ab7b17f49f7 100644 (file)
@@ -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);
index 3f78785faa4390c8f03c5259e676c646e43380d8..ca6c8ddabe7b7bd5ee0a83b756c81f0ab591ca85 100644 (file)
@@ -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];