link: Add support to configure NIC ring buffer size
authorSusant Sahani <ssahani@vmware.com>
Mon, 23 Sep 2019 14:51:02 +0000 (16:51 +0200)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Tue, 24 Sep 2019 14:33:35 +0000 (16:33 +0200)
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 7ea9a71..5013e7e 100644 (file)
           <para>Sets the number of combined set channels (a number between 1 and 4294967295).</para>
         </listitem>
       </varlistentry>
+      <varlistentry>
+        <term><varname>RxBufferSize=</varname></term>
+        <listitem>
+          <para>Takes a integer. Specifies the NIC receive ring buffer size. When unset, the kernel's default will be used.</para>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><varname>TxBufferSize=</varname></term>
+        <listitem>
+          <para>Takes a integer. Specifies the NIC transmit ring buffer size. When unset, the kernel's default will be used.</para>
+        </listitem>
+      </varlistentry>
+
     </variablelist>
   </refsect1>
 
index b0961df..4cab2ef 100644 (file)
@@ -365,6 +365,54 @@ int ethtool_set_wol(int *fd, const char *ifname, WakeOnLan wol) {
         return 0;
 }
 
+int ethtool_set_nic_buffer_size(int *fd, const char *ifname, netdev_ring_param *ring) {
+        struct ethtool_ringparam ecmd = {
+                .cmd = ETHTOOL_GRINGPARAM
+        };
+        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 (ring->rx_pending_set) {
+                if (ecmd.rx_pending != ring->rx_pending) {
+                        ecmd.rx_pending = ring->rx_pending;
+                        need_update = true;
+                }
+        }
+
+        if (ring->tx_pending_set) {
+                   if (ecmd.tx_pending != ring->rx_pending) {
+                           ecmd.tx_pending = ring->tx_pending;
+                           need_update = true;
+                }
+        }
+
+        if (need_update) {
+                ecmd.cmd = ETHTOOL_SRINGPARAM;
+
+                r = ioctl(*fd, SIOCETHTOOL, &ifr);
+                if (r < 0)
+                        return -errno;
+        }
+
+        return 0;
+}
+
+
 static int get_stringset(int fd, struct ifreq *ifr, int stringset_id, struct ethtool_gstrings **gstrings) {
         _cleanup_free_ struct ethtool_gstrings *strings = NULL;
         struct {
@@ -858,3 +906,45 @@ int config_parse_advertise(const char *unit,
 
         return 0;
 }
+
+int config_parse_nic_buffer_size(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) {
+        netdev_ring_param *ring = data;
+        uint32_t k;
+        int r;
+
+        assert(filename);
+        assert(section);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        r = safe_atou32(rvalue, &k);
+        if (r < 0) {
+                log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse interface buffer value, ignoring: %s", rvalue);
+                return 0;
+        }
+
+        if (k < 1) {
+                log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid %s value, ignoring: %s", lvalue, rvalue);
+                return 0;
+        }
+
+        if (streq(lvalue, "RxBufferSize")) {
+                ring->rx_pending = k;
+                ring->rx_pending_set = true;
+        } else if (streq(lvalue, "TxBufferSize")) {
+                ring->tx_pending = k;
+                ring->tx_pending_set = true;
+        }
+
+        return 0;
+}
index 8b32b24..5dd7800 100644 (file)
@@ -79,12 +79,22 @@ typedef struct netdev_channels {
         bool combined_count_set;
 } netdev_channels;
 
+typedef struct netdev_ring_param {
+        uint32_t rx_pending;
+        uint32_t tx_pending;
+
+        bool rx_pending_set;
+        bool tx_pending_set;
+} netdev_ring_param;
+
+
 int ethtool_get_driver(int *fd, const char *ifname, char **ret);
 int ethtool_get_link_info(int *fd, const char *ifname,
                           int *ret_autonegotiation, size_t *ret_speed,
                           Duplex *ret_duplex, NetDevPort *ret_port);
 int ethtool_set_speed(int *fd, const char *ifname, unsigned speed, Duplex duplex);
 int ethtool_set_wol(int *fd, const char *ifname, WakeOnLan wol);
+int ethtool_set_nic_buffer_size(int *fd, const char *ifname, netdev_ring_param *ring);
 int ethtool_set_features(int *fd, const char *ifname, int *features);
 int ethtool_set_glinksettings(int *fd, const char *ifname,
                               int autonegotiation, uint32_t advertise[static N_ADVERTISE],
@@ -108,3 +118,4 @@ CONFIG_PARSER_PROTOTYPE(config_parse_wol);
 CONFIG_PARSER_PROTOTYPE(config_parse_port);
 CONFIG_PARSER_PROTOTYPE(config_parse_channel);
 CONFIG_PARSER_PROTOTYPE(config_parse_advertise);
+CONFIG_PARSER_PROTOTYPE(config_parse_nic_buffer_size);
index a3d7dec..c6ff796 100644 (file)
@@ -53,3 +53,5 @@ Link.TxChannels,                 config_parse_channel,            0,
 Link.OtherChannels,              config_parse_channel,            0,                             offsetof(link_config, channels)
 Link.CombinedChannels,           config_parse_channel,            0,                             offsetof(link_config, channels)
 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)
index d44af64..cced62c 100644 (file)
@@ -381,6 +381,12 @@ int link_config_apply(link_config_ctx *ctx, link_config *config,
                         log_warning_errno(r, "Could not set channels of %s: %m", old_name);
         }
 
+        if (config->ring.rx_pending_set || config->ring.tx_pending_set) {
+                r = ethtool_set_nic_buffer_size(&ctx->ethtool_fd, old_name, &config->ring);
+                if (r < 0)
+                        log_warning_errno(r, "Could not set ring buffer 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 cd99cd5..52a02b9 100644 (file)
@@ -58,6 +58,7 @@ struct link_config {
         NetDevPort port;
         int features[_NET_DEV_FEAT_MAX];
         netdev_channels channels;
+        netdev_ring_param ring;
 
         LIST_FIELDS(link_config, links);
 };
index 6115506..e7d0737 100644 (file)
@@ -34,3 +34,5 @@ TxChannels=
 OtherChannels=
 CombinedChannels=
 Advertise=
+RxBufferSize=
+TxBufferSize=