[LibreQoS] Fwd: [patch 1/3] net: dst: Prevent false sharing vs. dst_entry::__refcnt
Dave Taht
dave.taht at gmail.com
Tue Feb 28 16:50:17 EST 2023
---------- Forwarded message ---------
From: Thomas Gleixner <tglx at linutronix.de>
Date: Tue, Feb 28, 2023 at 6:43 AM
Subject: [patch 1/3] net: dst: Prevent false sharing vs. dst_entry::__refcnt
To: LKML <linux-kernel at vger.kernel.org>
Cc: Linus Torvalds <torvalds at linuxfoundation.org>, <x86 at kernel.org>,
Wangyang Guo <wangyang.guo at intel.com>, Arjan van De Ven
<arjan at linux.intel.com>, David S. Miller <davem at davemloft.net>, Eric
Dumazet <edumazet at google.com>, Jakub Kicinski <kuba at kernel.org>, Paolo
Abeni <pabeni at redhat.com>, <netdev at vger.kernel.org>, Will Deacon
<will at kernel.org>, Peter Zijlstra <peterz at infradead.org>, Boqun Feng
<boqun.feng at gmail.com>, Mark Rutland <mark.rutland at arm.com>, Marc
Zyngier <maz at kernel.org>
From: Wangyang Guo <wangyang.guo at intel.com>
dst_entry::__refcnt is highly contended in scenarios where many connections
happen from and to the same IP. The reference count is an atomic_t, so the
reference count operations have to take the cache-line exclusive.
Aside of the unavoidable reference count contention there is another
significant problem which is caused by that: False sharing.
perf top identified two affected read accesses. dst_entry::lwtstate and
rtable::rt_genid.
dst_entry:__refcnt is located at offset 64 of dst_entry, which puts it into
a seperate cacheline vs. the read mostly members located at the beginning
of the struct.
That prevents false sharing vs. the struct members in the first 64
bytes of the structure, but there is also
dst_entry::lwtstate
which is located after the reference count and in the same cache line. This
member is read after a reference count has been acquired.
struct rtable embeds a struct dst_entry at offset 0. struct dst_entry has a
size of 112 bytes, which means that the struct members of rtable which
follow the dst member share the same cache line as dst_entry::__refcnt.
Especially
rtable::rt_genid
is also read by the contexts which have a reference count acquired
already.
When dst_entry:__refcnt is incremented or decremented via an atomic
operation these read accesses stall.
This was found when analysing the memtier benchmark in 1:100 mode, which
amplifies the problem extremly.
Rearrange and pad the structure so that the lwtstate member is in the next
cache-line. This increases the struct size from 112 to 136 bytes on 64bit.
The resulting improvement depends on the micro-architecture and the number
of CPUs. It ranges from +20% to +120% with a localhost memtier/memcached
benchmark.
[ tglx: Rearrange struct ]
Signed-off-by: Wangyang Guo <wangyang.guo at intel.com>
Signed-off-by: Arjan van De Ven <arjan at linux.intel.com>
Signed-off-by: Thomas Gleixner <tglx at linutronix.de>
Cc: "David S. Miller" <davem at davemloft.net>
Cc: Eric Dumazet <edumazet at google.com>
Cc: Jakub Kicinski <kuba at kernel.org>
Cc: Paolo Abeni <pabeni at redhat.com>
Cc: netdev at vger.kernel.org
---
include/net/dst.h | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)
--- a/include/net/dst.h
+++ b/include/net/dst.h
@@ -69,15 +69,25 @@ struct dst_entry {
#endif
int __use;
unsigned long lastuse;
- struct lwtunnel_state *lwtstate;
struct rcu_head rcu_head;
short error;
short __pad;
__u32 tclassid;
#ifndef CONFIG_64BIT
+ struct lwtunnel_state *lwtstate;
atomic_t __refcnt; /* 32-bit offset 64 */
#endif
netdevice_tracker dev_tracker;
+#ifdef CONFIG_64BIT
+ /*
+ * Ensure that lwtstate is not in the same cache line as __refcnt,
+ * because that would lead to false sharing under high contention
+ * of __refcnt. This also ensures that rtable::rt_genid is not
+ * sharing the same cache-line.
+ */
+ int pad2[6];
+ struct lwtunnel_state *lwtstate;
+#endif
};
struct dst_metrics {
--
A pithy note on VOQs vs SQM: https://blog.cerowrt.org/post/juniper/
Dave Täht CEO, TekLibre, LLC
More information about the LibreQoS
mailing list