From 838f815db5ef989ca89e427bc7af66e2a0b2fe10 Mon Sep 17 00:00:00 2001 From: Christian Marangi Date: Wed, 25 Oct 2023 03:27:58 +0200 Subject: [PATCH] system-linux: add support for configurable GRO option Add support for configurable GRO option. Some device doesn't have HW Checksum support and may suffer from performance regression by using GRO. Disabling GRO restore the original performance and make the device usable again. The option can be configured by adding the config for the device in the network config. Example: config device option name 'eth0' option gro '0' The option can also be configured by adding the config to the board.json. Notice that a new "kind" of settings are introduced "system_if_apply_settings_after_up". Option set in this function will be executed AFTER the interface is UP. This is needed as some option (example GRO) needs to be applied after the interface is UP and applying them before results in error in ioctl. Signed-off-by: Christian Marangi --- config.c | 18 +++++++++++++++++ config.h | 1 + device.c | 20 +++++++++++++++++++ device.h | 3 +++ system-linux.c | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++ system.h | 1 + 6 files changed, 96 insertions(+) diff --git a/config.c b/config.c index b4724b5..f559a3d 100644 --- a/config.c +++ b/config.c @@ -719,6 +719,24 @@ struct ether_addr *config_get_default_macaddr(const char *ifname) return ether_aton(blobmsg_get_string(cur)); } +int config_get_default_gro(const char *ifname) +{ + struct blob_attr *cur; + + if (!board_netdevs) + return -1; + + cur = config_find_blobmsg_attr(board_netdevs, ifname, BLOBMSG_TYPE_TABLE); + if (!cur) + return -1; + + cur = config_find_blobmsg_attr(cur, "gro", BLOBMSG_TYPE_BOOL); + if (!cur) + return -1; + + return blobmsg_get_bool(cur); +} + static void config_init_board(void) { diff --git a/config.h b/config.h index ae77ed1..e689315 100644 --- a/config.h +++ b/config.h @@ -21,5 +21,6 @@ extern bool config_init; int config_init_all(void); struct ether_addr *config_get_default_macaddr(const char *ifname); +int config_get_default_gro(const char *ifname); #endif diff --git a/device.c b/device.c index 9a9e249..09ac11d 100644 --- a/device.c +++ b/device.c @@ -72,6 +72,7 @@ static const struct blobmsg_policy dev_attrs[__DEV_ATTR_MAX] = { [DEV_ATTR_RXPAUSE] = { .name = "rxpause", .type = BLOBMSG_TYPE_BOOL }, [DEV_ATTR_TXPAUSE] = { .name = "txpause", .type = BLOBMSG_TYPE_BOOL }, [DEV_ATTR_AUTONEG] = { .name = "autoneg", .type = BLOBMSG_TYPE_BOOL }, + [DEV_ATTR_GRO] = { .name = "gro", .type = BLOBMSG_TYPE_BOOL }, }; const struct uci_blob_param_list device_attr_list = { @@ -164,6 +165,8 @@ static int set_device_state(struct device *dev, bool state) system_if_apply_settings(dev, &dev->settings, dev->settings.flags); system_if_up(dev); + + system_if_apply_settings_after_up(dev, &dev->settings); } else { system_if_down(dev); system_if_apply_settings(dev, &dev->orig_settings, dev->orig_settings.flags); @@ -294,6 +297,7 @@ device_merge_settings(struct device *dev, struct device_settings *n) n->rxpause = s->flags & DEV_OPT_RXPAUSE ? s->rxpause : os->rxpause; n->txpause = s->flags & DEV_OPT_TXPAUSE ? s->txpause : os->txpause; n->autoneg = s->flags & DEV_OPT_AUTONEG ? s->autoneg : os->autoneg; + n->gro = s->flags & DEV_OPT_GRO ? s->gro : os->gro; n->flags = s->flags | os->flags | os->valid_flags; } @@ -544,6 +548,11 @@ device_init_settings(struct device *dev, struct blob_attr **tb) s->flags |= DEV_OPT_AUTONEG; } + if ((cur = tb[DEV_ATTR_GRO])) { + s->gro = blobmsg_get_bool(cur); + s->flags |= DEV_OPT_GRO; + } + cur = tb[DEV_ATTR_AUTH_VLAN]; free(dev->config_auth_vlans); dev->config_auth_vlans = cur ? blob_memdup(cur) : NULL; @@ -616,6 +625,7 @@ device_fill_default_settings(struct device *dev) { struct device_settings *s = &dev->settings; struct ether_addr *ea; + int ret; if (!(s->flags & DEV_OPT_MACADDR)) { ea = config_get_default_macaddr(dev->ifname); @@ -624,6 +634,14 @@ device_fill_default_settings(struct device *dev) s->flags |= DEV_OPT_DEFAULT_MACADDR; } } + + if (!(s->flags & DEV_OPT_GRO)) { + ret = config_get_default_gro(dev->ifname); + if (ret >= 0) { + s->gro = ret; + s->flags |= DEV_OPT_GRO; + } + } } int device_claim(struct device_user *dep) @@ -1344,6 +1362,8 @@ device_dump_status(struct blob_buf *b, struct device *dev) blobmsg_add_u8(b, "arp_accept", st.arp_accept); if (st.flags & DEV_OPT_AUTH) blobmsg_add_u8(b, "auth", st.auth); + if (st.flags & DEV_OPT_GRO) + blobmsg_add_u8(b, "gro", st.gro); } s = blobmsg_open_table(b, "statistics"); diff --git a/device.h b/device.h index 12927de..d985f50 100644 --- a/device.h +++ b/device.h @@ -69,6 +69,7 @@ enum { DEV_ATTR_RXPAUSE, DEV_ATTR_TXPAUSE, DEV_ATTR_AUTONEG, + DEV_ATTR_GRO, __DEV_ATTR_MAX, }; @@ -138,6 +139,7 @@ enum { DEV_OPT_RXPAUSE = (1ULL << 34), DEV_OPT_TXPAUSE = (1ULL << 35), DEV_OPT_AUTONEG = (1ULL << 36), + DEV_OPT_GRO = (1ULL << 37), }; /* events broadcasted to all users of a device */ @@ -220,6 +222,7 @@ struct device_settings { bool rxpause; bool txpause; bool autoneg; + bool gro; }; struct device_vlan_range { diff --git a/system-linux.c b/system-linux.c index 9097f6a..94f262c 100644 --- a/system-linux.c +++ b/system-linux.c @@ -1736,6 +1736,40 @@ static bool ethtool_link_mode_test_bit(__s8 nwords, int nr, const __u32 *mask) return !!(mask[nr / 32] & (1U << (nr % 32))); } +static int +system_get_ethtool_gro(struct device *dev) +{ + struct ethtool_value ecmd; + struct ifreq ifr = { + .ifr_data = (caddr_t)&ecmd, + }; + + memset(&ecmd, 0, sizeof(ecmd)); + ecmd.cmd = ETHTOOL_GGRO; + strncpy(ifr.ifr_name, dev->ifname, sizeof(ifr.ifr_name) - 1); + + if (ioctl(sock_ioctl, SIOCETHTOOL, &ifr)) + return -1; + + return ecmd.data; +} + +static void +system_set_ethtool_gro(struct device *dev, struct device_settings *s) +{ + struct ethtool_value ecmd; + struct ifreq ifr = { + .ifr_data = (caddr_t)&ecmd, + }; + + memset(&ecmd, 0, sizeof(ecmd)); + ecmd.cmd = ETHTOOL_SGRO; + ecmd.data = s->gro; + strncpy(ifr.ifr_name, dev->ifname, sizeof(ifr.ifr_name) - 1); + + ioctl(sock_ioctl, SIOCETHTOOL, &ifr); +} + static void system_set_ethtool_pause(struct device *dev, struct device_settings *s) { @@ -1860,11 +1894,19 @@ system_set_ethtool_settings(struct device *dev, struct device_settings *s) ioctl(sock_ioctl, SIOCETHTOOL, &ifr); } +static void +system_set_ethtool_settings_after_up(struct device *dev, struct device_settings *s) +{ + if (s->flags & DEV_OPT_GRO) + system_set_ethtool_gro(dev, s); +} + void system_if_get_settings(struct device *dev, struct device_settings *s) { struct ifreq ifr; char buf[10]; + int ret; memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, dev->ifname, sizeof(ifr.ifr_name) - 1); @@ -1985,6 +2027,12 @@ system_if_get_settings(struct device *dev, struct device_settings *s) s->arp_accept = strtoul(buf, NULL, 0); s->flags |= DEV_OPT_ARP_ACCEPT; } + + ret = system_get_ethtool_gro(dev); + if (ret >= 0) { + s->gro = ret; + s->flags |= DEV_OPT_GRO; + } } void @@ -2086,6 +2134,11 @@ system_if_apply_settings(struct device *dev, struct device_settings *s, uint64_t system_set_ethtool_settings(dev, s); } +void system_if_apply_settings_after_up(struct device *dev, struct device_settings *s) +{ + system_set_ethtool_settings_after_up(dev, s); +} + int system_if_up(struct device *dev) { return system_if_flags(dev->ifname, IFF_UP, 0); diff --git a/system.h b/system.h index 19aafa4..890966b 100644 --- a/system.h +++ b/system.h @@ -278,6 +278,7 @@ struct device *system_if_get_parent(struct device *dev); bool system_if_force_external(const char *ifname); void system_if_apply_settings(struct device *dev, struct device_settings *s, uint64_t apply_mask); +void system_if_apply_settings_after_up(struct device *dev, struct device_settings *s); int system_add_address(struct device *dev, struct device_addr *addr); int system_del_address(struct device *dev, struct device_addr *addr); -- 2.30.2