From 0b67ade63b41196aacbd63ba566ffe06264f7a76 Mon Sep 17 00:00:00 2001 From: Til Kaiser Date: Fri, 19 Sep 2025 16:29:16 +0200 Subject: [PATCH 1/3] system-linux: refactor ethtool_feature_value Rename `ethtool_feature_value()` to `ethtool_get_feature_value()` and change its return type from `bool` to `int` for clearer error handling. - Return `-1` on error instead of `false` - Return `0` to indicate the feature isn't enabled - Return `1` to indicate the feature is enabled Signed-off-by: Til Kaiser --- system-linux.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/system-linux.c b/system-linux.c index 2303cba..630a6f4 100644 --- a/system-linux.c +++ b/system-linux.c @@ -2948,8 +2948,8 @@ ethtool_feature_index(const char *ifname, const char *keyname) return i; } -static bool -ethtool_feature_value(const char *ifname, const char *keyname) +static int +ethtool_get_feature_value(const char *ifname, const char *keyname) { struct ethtool_get_features_block *feature_block; struct ethtool_gfeatures *feature_values; @@ -2960,14 +2960,14 @@ ethtool_feature_value(const char *ifname, const char *keyname) feature_idx = ethtool_feature_index(ifname, keyname); if (feature_idx < 0) - return false; + return -1; feature_values = calloc(1, sizeof(*feature_values) + sizeof(feature_values->features[0]) * DIV_ROUND_UP(feature_idx, 32)); if (!feature_values) - return false; + return -1; feature_values->cmd = ETHTOOL_GFEATURES; feature_values->size = DIV_ROUND_UP(feature_idx, 32); @@ -2978,7 +2978,7 @@ ethtool_feature_value(const char *ifname, const char *keyname) if (ioctl(sock_ioctl, SIOCETHTOOL, &ifr) != 0) { free(feature_values); - return false; + return -1; } feature_block = &feature_values->features[feature_idx / 32]; @@ -3197,7 +3197,7 @@ system_if_dump_info(struct device *dev, struct blob_buf *b) blobmsg_close_table(b, c); blobmsg_add_u8(b, "hw-tc-offload", - ethtool_feature_value(dev->ifname, "hw-tc-offload")); + ethtool_get_feature_value(dev->ifname, "hw-tc-offload") == 1); system_add_devtype(b, dev->ifname); From a45c291233a964263dee8cb2e74c76700c78357d Mon Sep 17 00:00:00 2001 From: Til Kaiser Date: Fri, 19 Sep 2025 16:36:54 +0200 Subject: [PATCH 2/3] system-linux: add helper to set ethtool features Introduce `ethtool_set_feature_value()` to enable or disable NIC features via `ETHTOOL_SFEATURES`. Signed-off-by: Til Kaiser --- system-linux.c | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/system-linux.c b/system-linux.c index 630a6f4..651821d 100644 --- a/system-linux.c +++ b/system-linux.c @@ -2989,6 +2989,45 @@ ethtool_get_feature_value(const char *ifname, const char *keyname) return active; } +static int +ethtool_set_feature_value(const char *ifname, const char *keyname, bool activate) +{ + struct ethtool_set_features_block *feature_block; + struct ethtool_sfeatures *feature_values; + struct ifreq ifr = { 0 }; + int32_t feature_idx; + int ret; + + feature_idx = ethtool_feature_index(ifname, keyname); + + if (feature_idx < 0) + return -1; + + feature_values = calloc(1, + sizeof(*feature_values) + + sizeof(feature_values->features[0]) * DIV_ROUND_UP(feature_idx, 32)); + + if (!feature_values) + return -1; + + feature_values->cmd = ETHTOOL_SFEATURES; + feature_values->size = DIV_ROUND_UP(feature_idx, 32); + + strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name) - 1); + ifr.ifr_data = (void *)feature_values; + + feature_block = &feature_values->features[feature_idx / 32]; + feature_block->valid |= (1U << feature_idx % 32); + + if (activate) + feature_block->requested |= (1U << feature_idx % 32); + + ret = ioctl(sock_ioctl, SIOCETHTOOL, &ifr); + free(feature_values); + + return ret; +} + static void system_add_link_mode_name(struct blob_buf *b, int i, bool half) { From a10db6647e5b2ca3986319d5872a6f4f9a1a3fc6 Mon Sep 17 00:00:00 2001 From: Til Kaiser Date: Fri, 19 Sep 2025 16:39:41 +0200 Subject: [PATCH 3/3] system-linux: add support for configurable rxhash option Some devices (e.g. Marvell boards) support Receive Side Scaling (RSS) but do not enable it by default. This change introduces a new `rxhash` option in the device section of the network configuration to toggle the feature, e.g.: config device option name 'eth0' option rxhash '1' Signed-off-by: Til Kaiser --- device.c | 9 +++++++++ device.h | 3 +++ system-linux.c | 23 +++++++++++++++++++++++ 3 files changed, 35 insertions(+) diff --git a/device.c b/device.c index 174d115..468b975 100644 --- a/device.c +++ b/device.c @@ -76,6 +76,7 @@ static const struct blobmsg_policy dev_attrs[__DEV_ATTR_MAX] = { [DEV_ATTR_MASTER] = { .name = "conduit", .type = BLOBMSG_TYPE_STRING }, [DEV_ATTR_EEE] = { .name = "eee", .type = BLOBMSG_TYPE_BOOL }, [DEV_ATTR_TAGS] = { .name = "tags", .type = BLOBMSG_TYPE_ARRAY }, + [DEV_ATTR_RXHASH] = { .name = "rxhash", .type = BLOBMSG_TYPE_BOOL }, }; const struct uci_blob_param_list device_attr_list = { @@ -310,6 +311,7 @@ device_merge_settings(struct device *dev, struct device_settings *n) n->gro = s->flags & DEV_OPT_GRO ? s->gro : os->gro; n->eee = s->flags & DEV_OPT_EEE ? s->eee : os->eee; n->master_ifindex = s->flags & DEV_OPT_MASTER ? s->master_ifindex : os->master_ifindex; + n->rxhash = s->flags & DEV_OPT_RXHASH ? s->rxhash : os->rxhash; n->flags = s->flags | os->flags | os->valid_flags; } @@ -579,6 +581,11 @@ device_init_settings(struct device *dev, struct blob_attr **tb) s->flags |= DEV_OPT_EEE; } + if ((cur = tb[DEV_ATTR_RXHASH])) { + s->rxhash = blobmsg_get_bool(cur); + s->flags |= DEV_OPT_RXHASH; + } + /* Remember the settings present in UCI */ s->valid_flags = s->flags; @@ -1437,6 +1444,8 @@ device_dump_status(struct blob_buf *b, struct device *dev) blobmsg_add_u8(b, "gro", st.gro); if (st.flags & DEV_OPT_EEE) blobmsg_add_u8(b, "eee", st.eee); + if (st.flags & DEV_OPT_RXHASH) + blobmsg_add_u8(b, "rxhash", st.rxhash); } s = blobmsg_open_table(b, "statistics"); diff --git a/device.h b/device.h index bda204c..d46824d 100644 --- a/device.h +++ b/device.h @@ -73,6 +73,7 @@ enum { DEV_ATTR_MASTER, DEV_ATTR_EEE, DEV_ATTR_TAGS, + DEV_ATTR_RXHASH, __DEV_ATTR_MAX, }; @@ -145,6 +146,7 @@ enum { DEV_OPT_GRO = (1ULL << 37), DEV_OPT_MASTER = (1ULL << 38), DEV_OPT_EEE = (1ULL << 39), + DEV_OPT_RXHASH = (1ULL << 40), }; /* events broadcasted to all users of a device */ @@ -230,6 +232,7 @@ struct device_settings { bool gro; int master_ifindex; bool eee; + bool rxhash; }; struct device_vlan_range { diff --git a/system-linux.c b/system-linux.c index 651821d..94e307c 100644 --- a/system-linux.c +++ b/system-linux.c @@ -92,6 +92,9 @@ static int cb_rtnl_event(struct nl_msg *msg, void *arg); static void handle_hotplug_event(struct uloop_fd *u, unsigned int events); static int system_add_proto_tunnel(const char *name, const uint8_t proto, const unsigned int link, struct blob_attr **tb); +static int ethtool_get_feature_value(const char *ifname, const char *keyname); +static int ethtool_set_feature_value(const char *ifname, const char *keyname, + bool activate); static char dev_buf[256]; static const char *proc_path = "/proc"; @@ -2128,6 +2131,18 @@ system_set_ethtool_eee_settings(struct device *dev, struct device_settings *s) netifd_log_message(L_WARNING, "cannot set eee %d for device %s", s->eee, dev->ifname); } +static int +system_get_ethtool_rxhash(struct device *dev) +{ + return ethtool_get_feature_value(dev->ifname, "rx-hashing"); +} + +static void +system_set_ethtool_rxhash(struct device *dev, struct device_settings *s) +{ + ethtool_set_feature_value(dev->ifname, "rx-hashing", s->rxhash); +} + static void system_set_ethtool_settings(struct device *dev, struct device_settings *s) { @@ -2146,6 +2161,8 @@ system_set_ethtool_settings(struct device *dev, struct device_settings *s) if (s->flags & DEV_OPT_EEE) system_set_ethtool_eee_settings(dev, s); + if (s->flags & DEV_OPT_RXHASH) + system_set_ethtool_rxhash(dev, s); memset(&ecmd, 0, sizeof(ecmd)); ecmd.req.cmd = ETHTOOL_GLINKSETTINGS; @@ -2346,6 +2363,12 @@ system_if_get_settings(struct device *dev, struct device_settings *s) s->flags |= DEV_OPT_GRO; } + ret = system_get_ethtool_rxhash(dev); + if (ret >= 0) { + s->rxhash = ret; + s->flags |= DEV_OPT_RXHASH; + } + #if LINUX_VERSION_CODE >= KERNEL_VERSION(6,1,0) ret = system_if_get_master_ifindex(dev); if (ret >= 0) {