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 2303cba..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) { @@ -2948,8 +2971,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 +2983,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 +3001,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]; @@ -2989,6 +3012,45 @@ ethtool_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) { @@ -3197,7 +3259,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);