]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
udev: support to update flow control parameter
authorYu Watanabe <watanabe.yu+github@gmail.com>
Fri, 7 Feb 2020 11:06:44 +0000 (20:06 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Fri, 6 Mar 2020 16:43:26 +0000 (01:43 +0900)
Closes #14770.

man/systemd.link.xml
src/shared/ethtool-util.c
src/shared/ethtool-util.h
src/udev/net/link-config-gperf.gperf
src/udev/net/link-config.c
src/udev/net/link-config.h
test/fuzz/fuzz-link-parser/directives.link

index 1dca495a0392a874c4c3b9666e75af514f95ced4..e04618340bd2cdcb3dbd2418e9be3927ecdf8593 100644 (file)
           <para>Takes a integer. Specifies the NIC transmit ring buffer size. When unset, the kernel's default will be used.</para>
         </listitem>
       </varlistentry>
+      <varlistentry>
+        <term><varname>RxFlowControl=</varname></term>
+        <listitem>
+          <para>Takes a boolean. When set, enables the receive flow control, also known as the ethernet
+          receive PAUSE message (generate and send ethernet PAUSE frames). When unset, the kernel's
+          default will be used.</para>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><varname>TxFlowControl=</varname></term>
+        <listitem>
+          <para>Takes a boolean. When set, enables the transmit flow control, also known as the ethernet
+          transmit PAUSE message (respond to received ethernet PAUSE frames). When unset, the kernel's
+          default will be used.</para>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><varname>AutoNegotiationFlowControl=</varname></term>
+        <listitem>
+          <para>Takes a boolean. When set, the auto negotiation enables the interface to exchange state
+          advertisements with the connected peer so that the two devices can agree on the ethernet
+          PAUSE configuration. When unset, the kernel's default will be used.</para>
+        </listitem>
+      </varlistentry>
 
     </variablelist>
   </refsect1>
index 00a71d64a638a6e718f44ec3340d7fa2e8b3b58b..fe29af24d079fa675a804d115a27a97d1e2e2643 100644 (file)
@@ -851,6 +851,55 @@ int ethtool_set_channels(int *fd, const char *ifname, netdev_channels *channels)
         return 0;
 }
 
+int ethtool_set_flow_control(int *fd, const char *ifname, int rx, int tx, int autoneg) {
+        struct ethtool_pauseparam ecmd = {
+                .cmd = ETHTOOL_GPAUSEPARAM
+        };
+        struct ifreq ifr = {
+                .ifr_data = (void*) &ecmd
+        };
+
+        bool need_update = false;
+        int r;
+
+        if (*fd < 0) {
+                r = ethtool_connect_or_warn(fd, true);
+                if (r < 0)
+                        return r;
+        }
+
+        strscpy(ifr.ifr_name, IFNAMSIZ, ifname);
+
+        r = ioctl(*fd, SIOCETHTOOL, &ifr);
+        if (r < 0)
+                return -errno;
+
+        if (rx >= 0 && ecmd.rx_pause != (uint32_t) rx) {
+                ecmd.rx_pause = rx;
+                need_update = true;
+        }
+
+        if (tx >= 0 && ecmd.tx_pause != (uint32_t) tx) {
+                ecmd.tx_pause = tx;
+                need_update = true;
+        }
+
+        if (autoneg >= 0 && ecmd.autoneg != (uint32_t) autoneg) {
+                ecmd.autoneg = autoneg;
+                need_update = true;
+        }
+
+        if (need_update) {
+                ecmd.cmd = ETHTOOL_SPAUSEPARAM;
+
+                r = ioctl(*fd, SIOCETHTOOL, &ifr);
+                if (r < 0)
+                        return -errno;
+        }
+
+        return 0;
+}
+
 int config_parse_channel(const char *unit,
                          const char *filename,
                          unsigned line,
index c1d5d7590ef9a2a777d8fa40d1922a0294a59922..55c41f5bc751ef082e7ed32c1a50100947cf1139 100644 (file)
@@ -103,6 +103,7 @@ int ethtool_set_glinksettings(int *ethtool_fd, const char *ifname,
                               int autonegotiation, uint32_t advertise[static N_ADVERTISE],
                               uint64_t speed, Duplex duplex, NetDevPort port);
 int ethtool_set_channels(int *ethtool_fd, const char *ifname, netdev_channels *channels);
+int ethtool_set_flow_control(int *fd, const char *ifname, int rx, int tx, int autoneg);
 
 const char *duplex_to_string(Duplex d) _const_;
 Duplex duplex_from_string(const char *d) _pure_;
index 43d1c59b9401c80f4be2c49ae94556e6c692b6ed..2784246dd7155547d175f74b58af4704d8deb649 100644 (file)
@@ -60,3 +60,6 @@ Link.CombinedChannels,           config_parse_channel,                  0,
 Link.Advertise,                  config_parse_advertise,                0,                             offsetof(link_config, advertise)
 Link.RxBufferSize,               config_parse_nic_buffer_size,          0,                             offsetof(link_config, ring)
 Link.TxBufferSize,               config_parse_nic_buffer_size,          0,                             offsetof(link_config, ring)
+Link.RxFlowControl,              config_parse_tristate,                 0,                             offsetof(link_config, rx_flow_control)
+Link.TxFlowControl,              config_parse_tristate,                 0,                             offsetof(link_config, tx_flow_control)
+Link.AutoNegotiationFlowControl, config_parse_tristate,                 0,                             offsetof(link_config, autoneg_flow_control)
index 0332e99269c96ea0922f24874d7f456b1bb150c2..9c82759818a2875bfb4b24b333da7d072aa09847 100644 (file)
@@ -148,6 +148,9 @@ int link_load_one(link_config_ctx *ctx, const char *filename) {
                 .duplex = _DUP_INVALID,
                 .port = _NET_DEV_PORT_INVALID,
                 .autonegotiation = -1,
+                .rx_flow_control = -1,
+                .tx_flow_control = -1,
+                .autoneg_flow_control = -1,
         };
 
         for (i = 0; i < ELEMENTSOF(link->features); i++)
@@ -409,6 +412,10 @@ int link_config_apply(link_config_ctx *ctx, link_config *config,
                         log_warning_errno(r, "Could not set ring buffer of %s: %m", old_name);
         }
 
+        r = ethtool_set_flow_control(&ctx->ethtool_fd, old_name, config->rx_flow_control, config->tx_flow_control, config->autoneg_flow_control);
+        if (r < 0)
+                log_warning_errno(r, "Could not set flow control of %s: %m", old_name);
+
         r = sd_device_get_ifindex(device, &ifindex);
         if (r < 0)
                 return log_device_warning_errno(device, r, "Could not find ifindex: %m");
index a85bd4b46b021bca937c713e2e1497fefbd5dbfc..827ebf436c715bb12bcdc774807879ca7139a9a1 100644 (file)
@@ -62,6 +62,9 @@ struct link_config {
         int features[_NET_DEV_FEAT_MAX];
         netdev_channels channels;
         netdev_ring_param ring;
+        int rx_flow_control;
+        int tx_flow_control;
+        int autoneg_flow_control;
 
         LIST_FIELDS(link_config, links);
 };
index b304ad0ddb141923743e5ec30d01099e641652de..fe71d26d89e866e4dee775b5f1ee83f78be65b8f 100644 (file)
@@ -41,3 +41,6 @@ CombinedChannels=
 Advertise=
 RxBufferSize=
 TxBufferSize=
+RxFlowControl=
+TxFlowControl=
+AutoNegotiationFlowControl=