system-linux: add support for configurable GRO option
authorChristian Marangi <ansuelsmth@gmail.com>
Wed, 25 Oct 2023 01:27:58 +0000 (03:27 +0200)
committerChristian Marangi <ansuelsmth@gmail.com>
Thu, 9 Nov 2023 13:50:20 +0000 (14:50 +0100)
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>
config.c
config.h
device.c
device.h
system-linux.c
system.h

index b4724b5c956649f9e9e74a6d535d7427287a0d89..f559a3d9ce08f855b3ffdbd5898be1b6079037bf 100644 (file)
--- 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)
 {
index ae77ed11e11c2e67bf2660ec87810f8f51ff636f..e6893159abfe29b2815c14ce270f713bace0d6d0 100644 (file)
--- 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
index 9a9e24984018ce18885cbdeed44857f5ebbd68ce..09ac11d7a1ae39acc3e0dfff940f908266e3d008 100644 (file)
--- 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");
index 12927dee639d96f5182b88a1638c23f387f1be31..d985f505e1fc23c8d46f6a270fc62ffc276e9314 100644 (file)
--- 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 {
index 9097f6a56a1f2cb233de1ab74950d1653db01a0b..94f262c1ea91069593954a9de4dced88e16a2fcd 100644 (file)
@@ -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);
index 19aafa418fc92d55e6d401bf656730423f65eec6..890966b26e3b14654bfcdef0ff8c9e37e03a76d5 100644 (file)
--- 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);