diff options
| author | Christian Marangi | 2023-10-25 01:27:58 +0000 |
|---|---|---|
| committer | Christian Marangi | 2023-11-09 13:50:20 +0000 |
| commit | 838f815db5ef989ca89e427bc7af66e2a0b2fe10 (patch) | |
| tree | 929605ee04ad3feba9156cb2b3b68157880315fa | |
| parent | 5c2e5d5ea4e0dd9a0ed66154d818b96bf81664f4 (diff) | |
| download | netifd-838f815db5ef989ca89e427bc7af66e2a0b2fe10.tar.gz | |
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 <ansuelsmth@gmail.com>
| -rw-r--r-- | config.c | 18 | ||||
| -rw-r--r-- | config.h | 1 | ||||
| -rw-r--r-- | device.c | 20 | ||||
| -rw-r--r-- | device.h | 3 | ||||
| -rw-r--r-- | system-linux.c | 53 | ||||
| -rw-r--r-- | system.h | 1 |
6 files changed, 96 insertions, 0 deletions
@@ -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) { @@ -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 @@ -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"); @@ -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); @@ -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); |