diff --git a/man/systemd.link.xml b/man/systemd.link.xml index 7ea9a71107..5013e7e9b5 100644 --- a/man/systemd.link.xml +++ b/man/systemd.link.xml @@ -624,6 +624,19 @@ Sets the number of combined set channels (a number between 1 and 4294967295). + + RxBufferSize= + + Takes a integer. Specifies the NIC receive ring buffer size. When unset, the kernel's default will be used. + + + + TxBufferSize= + + Takes a integer. Specifies the NIC transmit ring buffer size. When unset, the kernel's default will be used. + + + diff --git a/src/shared/ethtool-util.c b/src/shared/ethtool-util.c index b0961df72e..4cab2ef6b0 100644 --- a/src/shared/ethtool-util.c +++ b/src/shared/ethtool-util.c @@ -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; +} diff --git a/src/shared/ethtool-util.h b/src/shared/ethtool-util.h index 8b32b243f3..5dd7800852 100644 --- a/src/shared/ethtool-util.h +++ b/src/shared/ethtool-util.h @@ -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); diff --git a/src/udev/net/link-config-gperf.gperf b/src/udev/net/link-config-gperf.gperf index a3d7dec88c..c6ff796efc 100644 --- a/src/udev/net/link-config-gperf.gperf +++ b/src/udev/net/link-config-gperf.gperf @@ -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) diff --git a/src/udev/net/link-config.c b/src/udev/net/link-config.c index d44af64d5e..cced62cab2 100644 --- a/src/udev/net/link-config.c +++ b/src/udev/net/link-config.c @@ -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"); diff --git a/src/udev/net/link-config.h b/src/udev/net/link-config.h index cd99cd54d4..52a02b9b09 100644 --- a/src/udev/net/link-config.h +++ b/src/udev/net/link-config.h @@ -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); }; diff --git a/test/fuzz/fuzz-link-parser/directives.link b/test/fuzz/fuzz-link-parser/directives.link index 61155063a8..e7d0737ace 100644 --- a/test/fuzz/fuzz-link-parser/directives.link +++ b/test/fuzz/fuzz-link-parser/directives.link @@ -34,3 +34,5 @@ TxChannels= OtherChannels= CombinedChannels= Advertise= +RxBufferSize= +TxBufferSize=