From a34811e4efef7fa90bb32236ccf6600100366590 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 7 Feb 2020 20:06:44 +0900 Subject: [PATCH] udev: support to update flow control parameter Closes #14770. --- man/systemd.link.xml | 24 +++++++++++ src/shared/ethtool-util.c | 49 ++++++++++++++++++++++ src/shared/ethtool-util.h | 1 + src/udev/net/link-config-gperf.gperf | 3 ++ src/udev/net/link-config.c | 7 ++++ src/udev/net/link-config.h | 3 ++ test/fuzz/fuzz-link-parser/directives.link | 3 ++ 7 files changed, 90 insertions(+) diff --git a/man/systemd.link.xml b/man/systemd.link.xml index 1dca495a039..e04618340bd 100644 --- a/man/systemd.link.xml +++ b/man/systemd.link.xml @@ -700,6 +700,30 @@ Takes a integer. Specifies the NIC transmit ring buffer size. When unset, the kernel's default will be used. + + RxFlowControl= + + 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. + + + + TxFlowControl= + + 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. + + + + AutoNegotiationFlowControl= + + 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. + + diff --git a/src/shared/ethtool-util.c b/src/shared/ethtool-util.c index 00a71d64a63..fe29af24d07 100644 --- a/src/shared/ethtool-util.c +++ b/src/shared/ethtool-util.c @@ -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, diff --git a/src/shared/ethtool-util.h b/src/shared/ethtool-util.h index c1d5d7590ef..55c41f5bc75 100644 --- a/src/shared/ethtool-util.h +++ b/src/shared/ethtool-util.h @@ -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_; diff --git a/src/udev/net/link-config-gperf.gperf b/src/udev/net/link-config-gperf.gperf index 43d1c59b940..2784246dd71 100644 --- a/src/udev/net/link-config-gperf.gperf +++ b/src/udev/net/link-config-gperf.gperf @@ -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) diff --git a/src/udev/net/link-config.c b/src/udev/net/link-config.c index 0332e99269c..9c82759818a 100644 --- a/src/udev/net/link-config.c +++ b/src/udev/net/link-config.c @@ -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"); diff --git a/src/udev/net/link-config.h b/src/udev/net/link-config.h index a85bd4b46b0..827ebf436c7 100644 --- a/src/udev/net/link-config.h +++ b/src/udev/net/link-config.h @@ -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); }; diff --git a/test/fuzz/fuzz-link-parser/directives.link b/test/fuzz/fuzz-link-parser/directives.link index b304ad0ddb1..fe71d26d89e 100644 --- a/test/fuzz/fuzz-link-parser/directives.link +++ b/test/fuzz/fuzz-link-parser/directives.link @@ -41,3 +41,6 @@ CombinedChannels= Advertise= RxBufferSize= TxBufferSize= +RxFlowControl= +TxFlowControl= +AutoNegotiationFlowControl=