Being able to turn off fdb "used" updates makes it possible to avoid false-sharing on each packet transmit/receive for that fdb. The best way to completely avoid false-sharing is by binding ports to CPUs so receive will write to the "updated" field only on a single CPU and transmit to that fdb will not touch the "used" field. The default is used_enabled = 1 to avoid breaking user-space, strongly suggest if not needed to set it to 0.
Signed-off-by: Nikolay Aleksandrov <niko...@cumulusnetworks.com> --- include/uapi/linux/if_link.h | 1 + net/bridge/br_device.c | 1 + net/bridge/br_input.c | 3 ++- net/bridge/br_netlink.c | 10 +++++++++- net/bridge/br_private.h | 1 + net/bridge/br_sysfs_br.c | 23 +++++++++++++++++++++++ 6 files changed, 37 insertions(+), 2 deletions(-) diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index b9aa5641ebe5..8bcd234fed03 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -277,6 +277,7 @@ enum { IFLA_BR_MCAST_STATS_ENABLED, IFLA_BR_MCAST_IGMP_VERSION, IFLA_BR_MCAST_MLD_VERSION, + IFLA_BR_USED_ENABLED, __IFLA_BR_MAX, }; diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c index 89b414fd1901..b1fa1b031fea 100644 --- a/net/bridge/br_device.c +++ b/net/bridge/br_device.c @@ -408,6 +408,7 @@ void br_dev_setup(struct net_device *dev) br->bridge_hello_time = br->hello_time = 2 * HZ; br->bridge_forward_delay = br->forward_delay = 15 * HZ; br->bridge_ageing_time = br->ageing_time = BR_DEFAULT_AGEING_TIME; + br->used_enabled = 1; dev->max_mtu = ETH_MAX_MTU; br_netfilter_rtable_init(br); diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c index 855b72fbe1da..8a320901aaa7 100644 --- a/net/bridge/br_input.c +++ b/net/bridge/br_input.c @@ -197,7 +197,8 @@ int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb if (dst->is_local) return br_pass_frame_up(skb); - dst->used = jiffies; + if (br->used_enabled) + dst->used = jiffies; br_forward(dst->dst, skb, local_rcv, false); } else { if (!mcast_hit) diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index d63ad8337dcd..49272efaad00 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -851,6 +851,7 @@ static const struct nla_policy br_policy[IFLA_BR_MAX + 1] = { [IFLA_BR_MCAST_STATS_ENABLED] = { .type = NLA_U8 }, [IFLA_BR_MCAST_IGMP_VERSION] = { .type = NLA_U8 }, [IFLA_BR_MCAST_MLD_VERSION] = { .type = NLA_U8 }, + [IFLA_BR_USED_ENABLED] = { .type = NLA_U8 }, }; static int br_changelink(struct net_device *brdev, struct nlattr *tb[], @@ -1102,6 +1103,11 @@ static int br_changelink(struct net_device *brdev, struct nlattr *tb[], br->nf_call_arptables = val ? true : false; } #endif + if (data[IFLA_BR_USED_ENABLED]) { + u8 val = nla_get_u8(data[IFLA_BR_USED_ENABLED]); + + br->used_enabled = !!val; + } return 0; } @@ -1175,6 +1181,7 @@ static size_t br_get_size(const struct net_device *brdev) nla_total_size(sizeof(u8)) + /* IFLA_BR_NF_CALL_IP6TABLES */ nla_total_size(sizeof(u8)) + /* IFLA_BR_NF_CALL_ARPTABLES */ #endif + nla_total_size(sizeof(u8)) + /* IFLA_BR_USED_ENABLED */ 0; } @@ -1246,7 +1253,8 @@ static int br_fill_info(struct sk_buff *skb, const struct net_device *brdev) nla_put_u32(skb, IFLA_BR_MCAST_STARTUP_QUERY_CNT, br->multicast_startup_query_count) || nla_put_u8(skb, IFLA_BR_MCAST_IGMP_VERSION, - br->multicast_igmp_version)) + br->multicast_igmp_version) || + nla_put_u8(skb, IFLA_BR_USED_ENABLED, br->used_enabled)) return -EMSGSIZE; #if IS_ENABLED(CONFIG_IPV6) if (nla_put_u8(skb, IFLA_BR_MCAST_MLD_VERSION, diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 1e1b9a07e2da..4b6eb2393d7e 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -281,6 +281,7 @@ struct net_bridge { struct net_device *dev; struct pcpu_sw_netstats __percpu *stats; /* These fields are accessed on each packet */ + u8 used_enabled; #ifdef CONFIG_BRIDGE_VLAN_FILTERING u8 vlan_enabled; u8 vlan_stats_enabled; diff --git a/net/bridge/br_sysfs_br.c b/net/bridge/br_sysfs_br.c index 0f4034934d56..22adc29e8721 100644 --- a/net/bridge/br_sysfs_br.c +++ b/net/bridge/br_sysfs_br.c @@ -334,6 +334,28 @@ static ssize_t flush_store(struct device *d, } static DEVICE_ATTR_WO(flush); +static ssize_t used_enabled_show(struct device *d, + struct device_attribute *attr, char *buf) +{ + struct net_bridge *br = to_bridge(d); + + return sprintf(buf, "%u\n", br->used_enabled); +} + +static int set_used_enabled(struct net_bridge *br, unsigned long val) +{ + br->used_enabled = !!val; + return 0; +} + +static ssize_t used_enabled_store(struct device *d, + struct device_attribute *attr, + const char *buf, size_t len) +{ + return store_bridge_parm(d, buf, len, set_used_enabled); +} +static DEVICE_ATTR_RW(used_enabled); + #ifdef CONFIG_BRIDGE_IGMP_SNOOPING static ssize_t multicast_router_show(struct device *d, struct device_attribute *attr, char *buf) @@ -829,6 +851,7 @@ static struct attribute *bridge_attrs[] = { &dev_attr_gc_timer.attr, &dev_attr_group_addr.attr, &dev_attr_flush.attr, + &dev_attr_used_enabled.attr, #ifdef CONFIG_BRIDGE_IGMP_SNOOPING &dev_attr_multicast_router.attr, &dev_attr_multicast_snooping.attr, -- 2.1.4