Rework READ_ONCE() to improve codegen
Subsystems (15)
Will Deacon
maintainerv4 cover Rework READ_ONCE() to improve codegen
Hi,
It's me again. This is version four of the READ_ONCE() codegen improvement patches that I previously posted here:
RFC: https://lore.kernel.org/lkml/[email protected] v2: https://lore.kernel.org/lkml/[email protected] v3: https://lore.kernel.org/lkml/[email protected]/
Changes since v3 include:
* Drop the patch warning for GCC versions prior to 4.8
* Move the patch raising the minimum GCC version earlier in the series
* Reintroduce the old READ_ONCE_NOCHECK behaviour so that it can continue to be used by stack unwinders
As before, this will break the build for sparc32 without the changes here:
https://lore.kernel.org/lkml/[email protected]
and boy are they proving to be popular (I'm interpreting the silence as monumental joy).
Ta,
Will
--->8
Will Deacon (11):
compiler/gcc: Raise minimum GCC version for kernel builds to 4.8
netfilter: Avoid assigning 'const' pointer to non-const pointer
net: tls: Avoid assigning 'const' pointer to non-const pointer
fault_inject: Don't rely on "return value" from WRITE_ONCE()
arm64: csum: Disable KASAN for do_csum()
READ_ONCE: Simplify implementations of {READ,WRITE}_ONCE()
READ_ONCE: Enforce atomicity for {READ,WRITE}_ONCE() memory accesses
READ_ONCE: Drop pointer qualifiers when reading from scalar types locking/barriers: Use '__unqual_scalar_typeof' for load-acquire macros
arm64: barrier: Use '__unqual_scalar_typeof' for acquire/release
macros
gcov: Remove old GCC 3.4 support
Documentation/process/changes.rst | 2 +- arch/arm/crypto/Kconfig | 12 +- arch/arm64/include/asm/barrier.h | 16 +- arch/arm64/lib/csum.c | 20 +- crypto/Kconfig | 1 - drivers/xen/time.c | 2 +- include/asm-generic/barrier.h | 16 +- include/linux/compiler-gcc.h | 5 +- include/linux/compiler.h | 145 ++++---- include/linux/compiler_types.h | 21 ++ init/Kconfig | 1 - kernel/gcov/Kconfig | 24 -- kernel/gcov/Makefile | 3 +- kernel/gcov/gcc_3_4.c | 573 ------------------------------ lib/fault-inject.c | 4 +- net/netfilter/core.c | 2 +- net/tls/tls_main.c | 2 +- scripts/gcc-plugins/Kconfig | 2 +- 18 files changed, 132 insertions(+), 719 deletions(-) delete mode 100644 kernel/gcov/gcc_3_4.c
Signature · 1 line
2.26.1.301.g55bc3eb7cb9-goog
Will Deacon
maintainerPATCH v4 1/11 compiler/gcc: Raise minimum GCC version for kernel builds to 4.8
It is very rare to see versions of GCC prior to 4.8 being used to build the mainline kernel. These old compilers are also know to have codegen issues which can lead to silent miscompilation:
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=58145
Raise the minimum GCC version for kernel build to 4.8 and remove some tautological Kconfig dependencies as a consequence.
--- Documentation/process/changes.rst | 2 +- arch/arm/crypto/Kconfig | 12 ++++++------ crypto/Kconfig | 1 - include/linux/compiler-gcc.h | 5 ++--- init/Kconfig | 1 - scripts/gcc-plugins/Kconfig | 2 +- 6 files changed, 10 insertions(+), 13 deletions(-)
Documentation/process/changes.rst +1 -1
@@ -29,7 +29,7 @@ you probably needn't concern yourself with pcmciautils. ====================== =============== ======================================== Program Minimal version Command to check the version ====================== =============== ======================================== -GNU C 4.6 gcc --version +GNU C 4.8 gcc --version GNU make 3.81 make --version binutils 2.23 ld -v flex 2.5.35 flex --version
arch/arm/crypto/Kconfig +6 -6
@@ -30,7 +30,7 @@ config CRYPTO_SHA1_ARM_NEON config CRYPTO_SHA1_ARM_CE tristate "SHA1 digest algorithm (ARM v8 Crypto Extensions)" - depends on KERNEL_MODE_NEON && (CC_IS_CLANG || GCC_VERSION >= 40800) + depends on KERNEL_MODE_NEON select CRYPTO_SHA1_ARM select CRYPTO_HASH help
@@ -39,7 +39,7 @@ config CRYPTO_SHA1_ARM_CE config CRYPTO_SHA2_ARM_CE tristate "SHA-224/256 digest algorithm (ARM v8 Crypto Extensions)" - depends on KERNEL_MODE_NEON && (CC_IS_CLANG || GCC_VERSION >= 40800) + depends on KERNEL_MODE_NEON select CRYPTO_SHA256_ARM select CRYPTO_HASH help
@@ -96,7 +96,7 @@ config CRYPTO_AES_ARM_BS config CRYPTO_AES_ARM_CE tristate "Accelerated AES using ARMv8 Crypto Extensions" - depends on KERNEL_MODE_NEON && (CC_IS_CLANG || GCC_VERSION >= 40800) + depends on KERNEL_MODE_NEON select CRYPTO_SKCIPHER select CRYPTO_LIB_AES select CRYPTO_SIMD
@@ -106,7 +106,7 @@ config CRYPTO_AES_ARM_CE config CRYPTO_GHASH_ARM_CE tristate "PMULL-accelerated GHASH using NEON/ARMv8 Crypto Extensions" - depends on KERNEL_MODE_NEON && (CC_IS_CLANG || GCC_VERSION >= 40800) + depends on KERNEL_MODE_NEON select CRYPTO_HASH select CRYPTO_CRYPTD select CRYPTO_GF128MUL
@@ -118,13 +118,13 @@ config CRYPTO_GHASH_ARM_CE config CRYPTO_CRCT10DIF_ARM_CE tristate "CRCT10DIF digest algorithm using PMULL instructions" - depends on KERNEL_MODE_NEON && (CC_IS_CLANG || GCC_VERSION >= 40800) + depends on KERNEL_MODE_NEON depends on CRC_T10DIF select CRYPTO_HASH config CRYPTO_CRC32_ARM_CE tristate "CRC32(C) digest algorithm using CRC and/or PMULL instructions" - depends on KERNEL_MODE_NEON && (CC_IS_CLANG || GCC_VERSION >= 40800) + depends on KERNEL_MODE_NEON depends on CRC32 select CRYPTO_HASH
crypto/Kconfig +0 -1
@@ -316,7 +316,6 @@ config CRYPTO_AEGIS128 config CRYPTO_AEGIS128_SIMD bool "Support SIMD acceleration for AEGIS-128" depends on CRYPTO_AEGIS128 && ((ARM || ARM64) && KERNEL_MODE_NEON) - depends on !ARM || CC_IS_CLANG || GCC_VERSION >= 40800 default y config CRYPTO_AEGIS128_AESNI_SSE2
include/linux/compiler-gcc.h +2 -3
@@ -10,7 +10,8 @@ + __GNUC_MINOR__ * 100 \ + __GNUC_PATCHLEVEL__) -#if GCC_VERSION <40600 +/* https://gcc.gnu.org/bugzilla/show_bug.cgi?id=58145 */ +#if GCC_VERSION <40800 # error Sorry, your compiler is too old - please upgrade it.#endif
@@ -126,9 +127,7 @@#if defined(CONFIG_ARCH_USE_BUILTIN_BSWAP ) && !defined(__CHECKER__)#define __HAVE_BUILTIN_BSWAP32__#define __HAVE_BUILTIN_BSWAP64__ -#if GCC_VERSION >=40800 #define __HAVE_BUILTIN_BSWAP16__ -#endif #endif /* CONFIG_ARCH_USE_BUILTIN_BSWAP && !__CHECKER__ */ #if GCC_VERSION >=70000
init/Kconfig +0 -1
@@ -1285,7 +1285,6 @@ config LD_DEAD_CODE_DATA_ELIMINATION bool "Dead code and data elimination (EXPERIMENTAL)" depends on HAVE_LD_DEAD_CODE_DATA_ELIMINATION depends on EXPERT - depends on !(FUNCTION_TRACER && CC_IS_GCC && GCC_VERSION < 40800) depends on $(cc-option,-ffunction-sections -fdata-sections) depends on $(ld-option,--gc-sections) help
scripts/gcc-plugins/Kconfig +1 -1
@@ -8,7 +8,7 @@ config HAVE_GCC_PLUGINS menuconfig GCC_PLUGINS bool "GCC plugins" depends on HAVE_GCC_PLUGINS - depends on CC_IS_GCC && GCC_VERSION >= 40800 + depends on CC_IS_GCC depends on $(success,$(srctree)/scripts/gcc-plugin.sh $(CC)) default y help
Signature · 1 line
2.26.1.301.g55bc3eb7cb9-goog
Re: [PATCH v4 01/11] compiler/gcc: Raise minimum GCC version for kernel builds to 4.8
Quoted 3 lines · Will Deacon
It is very rare to see versions of GCC prior to 4.8 being used to build the mainline kernel. These old compilers are also know to have codegen
"know" -> "known"
Quoted 8 lines
issues which can lead to silent miscompilation:
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=58145
Raise the minimum GCC version for kernel build to 4.8 and remove some tautological
Kconfigdependencies as a consequence.Cc Masahiro Yamada <[email protected]>
Maybe you can also clean up mm/migrate.c because GCC_VERSION >= 40700 is always met. It is up to you if you want to send v5.
Please replace CC with
Thank you!
Quoted 4 lines
Acked-by Arnd Bergmann <[email protected]>Reviewed-by Nick Desaulniers <[email protected]>Signed-off-by Will Deacon <[email protected]>---
Signature · 2 lines
Best Regards Masahiro Yamada
Will Deacon
maintainerPATCH v4 2/11 netfilter: Avoid assigning 'const' pointer to non-const pointer
In reply to Will Deaconnf_remove_net_hook() uses WRITE_ONCE() to assign a 'const' pointer to a 'non-const' pointer. Cleanups to the implementation of WRITE_ONCE() mean that this will give rise to a compiler warning, just like a plain old assignment would do:
| In file included from ./include/linux/export.h:43, | from ./include/linux/linkage.h:7, | from ./include/linux/kernel.h:8, | from net/netfilter/core.c:9: | net/netfilter/core.c: In function ‘nf_remove_net_hook’: | ./include/linux/compiler.h:216:30: warning: assignment discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers] | *(volatile typeof(x) *)&(x) = (val); \ | ^ | net/netfilter/core.c:379:3: note: in expansion of macro ‘WRITE_ONCE’ | WRITE_ONCE(orig_ops[i], &dummy_ops); | ^~~~~~~~~~
Follow the pattern used elsewhere in this file and add a cast to 'void *' to squash the warning.
--- net/netfilter/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
net/netfilter/core.c +1 -1
@@ -376,7 +376,7 @@ static bool nf_remove_net_hook(struct nf_hook_entries *old,if (orig_ops[i] != unreg)continue ;WRITE_ONCE (old->hooks[i].hook, accept_all); -WRITE_ONCE (orig_ops[i], &dummy_ops); +WRITE_ONCE (orig_ops[i], (void *)&dummy_ops);return true; }
Signature · 1 line
2.26.1.301.g55bc3eb7cb9-goog
Will Deacon
maintainerPATCH v4 3/11 net: tls: Avoid assigning 'const' pointer to non-const pointer
In reply to Will Deacontls_build_proto() uses WRITE_ONCE() to assign a 'const' pointer to a 'non-const' pointer. Cleanups to the implementation of WRITE_ONCE() mean that this will give rise to a compiler warning, just like a plain old assignment would do:
| net/tls/tls_main.c: In function ‘tls_build_proto’: | ./include/linux/compiler.h:229:30: warning: assignment discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers] | net/tls/tls_main.c:640:4: note: in expansion of macro ‘smp_store_release’ | 640 | smp_store_release(&saved_tcpv6_prot, prot); | | ^~~~~~~~~~~~~~~~~
Drop the const qualifier from the local 'prot' variable, as it isn't needed.
--- net/tls/tls_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
net/tls/tls_main.c +1 -1
@@ -629,7 +629,7 @@ struct tls_context *tls_ctx_create(struct sock *sk)static void tls_build_proto (struct sock *sk) {int ip_ver = sk->sk_family ==AF_INET6 ?TLSV6 :TLSV4 ; -const struct proto *prot =READ_ONCE (sk->sk_prot); +struct proto *prot =READ_ONCE (sk->sk_prot);/* Build IPv6 TLS whenever the address of tcpv6 _prot changes */ if (ip_ver == TLSV6 &&
Signature · 1 line
2.26.1.301.g55bc3eb7cb9-goog
Will Deacon
maintainerPATCH v4 4/11 fault_inject: Don't rely on "return value" from WRITE_ONCE()
In reply to Will DeaconIt's a bit weird that WRITE_ONCE() evaluates to the value it stores and it's different to smp_store_release(), which can't be used this way.
In preparation for preventing this in WRITE_ONCE(), change the fault injection code to use a local variable instead.
--- lib/fault-inject.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
lib/fault-inject.c +3 -1
@@ -106,7 +106,9 @@ bool should_fail(struct fault_attr *attr, ssize_t size)unsigned int fail_nth =READ_ONCE (current->fail_nth);if (fail_nth) { -if (!WRITE_ONCE (current->fail_nth, fail_nth -1 )) + fail_nth--; +WRITE_ONCE (current->fail_nth, fail_nth); +if (!fail_nth) goto fail;return false;
Signature · 1 line
2.26.1.301.g55bc3eb7cb9-goog
Will Deacon
maintainerPATCH v4 5/11 arm64: csum: Disable KASAN for do_csum()
In reply to Will Deacondo_csum() over-reads the source buffer and therefore abuses READ_ONCE_NOCHECK() to avoid tripping up KASAN. In preparation for READ_ONCE_NOCHECK() becoming a macro, and therefore losing its '__no_sanitize_address' annotation, just annotate do_csum() explicitly and fall back to normal loads.
--- arch/arm64/lib/csum.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-)
arch/arm64/lib/csum.c +12 -8
@@ -14,7 +14,11 @@ static u64 accumulate(u64 sum, u64 data)return tmp + (tmp >>64 ); } -unsigned int do_csum (const unsigned char *buff,int len) +/* +* We over-read the buffer and this makes KASAN unhappy. Instead, disable +* instrumentation and call kasan explicitly. +*/ +unsigned int __no_sanitize_address do_csum(const unsigned char *buff, int len) {unsigned int offset, shift, sum;const u64 *ptr;
@@ -42,7 +46,7 @@ unsigned int do_csum(const unsigned char *buff, int len)* odd/even alignment, and means we can ignore it until the very end. */ shift = offset *8 ; - data =READ_ONCE_NOCHECK (*ptr++); + data = *ptr++;#ifdef __LITTLE_ENDIAN data = (data >> shift) << shift;#else
@@ -58,10 +62,10 @@ unsigned int do_csum(const unsigned char *buff, int len)while (unlikely (len >64 )) {__uint128_t tmp1, tmp2, tmp3, tmp4; - tmp1 =READ_ONCE_NOCHECK (*(__uint128_t *)ptr); - tmp2 =READ_ONCE_NOCHECK (*(__uint128_t *)(ptr +2 )); - tmp3 =READ_ONCE_NOCHECK (*(__uint128_t *)(ptr +4 )); - tmp4 =READ_ONCE_NOCHECK (*(__uint128_t *)(ptr +6 )); + tmp1 = *(__uint128_t *)ptr; + tmp2 = *(__uint128_t *)(ptr +2 ); + tmp3 = *(__uint128_t *)(ptr +4 ); + tmp4 = *(__uint128_t *)(ptr +6 ); len -=64 ; ptr +=8 ;
@@ -85,7 +89,7 @@ unsigned int do_csum(const unsigned char *buff, int len)__uint128_t tmp; sum64 =accumulate (sum64, data); - tmp =READ_ONCE_NOCHECK (*(__uint128_t *)ptr); + tmp = *(__uint128_t *)ptr; len -=16 ; ptr +=2 ;
@@ -100,7 +104,7 @@ unsigned int do_csum(const unsigned char *buff, int len) }if (len >0 ) { sum64 =accumulate (sum64, data); - data =READ_ONCE_NOCHECK (*ptr); + data = *ptr; len -=8 ; }/*
Signature · 1 line
2.26.1.301.g55bc3eb7cb9-goog
Re: [PATCH v4 05/11] arm64: csum: Disable KASAN for do_csum()
Quoted 9 lines · Will Deacon
do_csum()over-reads the source buffer and therefore abusesREAD_ONCE_NOCHECK()to avoid tripping up KASAN. In preparation forREAD_ONCE_NOCHECK()becoming a macro, and therefore losing its '__no_sanitize_address' annotation, just annotatedo_csum()explicitly and fall back to normal loads.Cc Mark Rutland <[email protected]>Cc Robin Murphy <[email protected]>Signed-off-by Will Deacon <[email protected]>
From a functional perspective:
I know that Robin had a concern w.r.t. how this would affect the codegen, but I think we can follow that up after the series as a whole is merged.
Thanks,
Mark.
Quoted 66 lines
--- arch/arm64/lib/csum.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-)arch/arm64/lib/csum.c +12
-8@@ -14,7 +14,11 @@ static u64 accumulate(u64 sum, u64 data)return tmp + (tmp >>64 ); } -unsigned int do_csum (const unsigned char *buff,int len) +/* +* We over-read the buffer and this makes KASAN unhappy. Instead, disable +* instrumentation and call kasan explicitly. +*/ +unsigned int __no_sanitize_address do_csum(const unsigned char *buff, int len) {unsigned int offset, shift, sum;const u64 *ptr;@@ -42,7 +46,7 @@ unsigned int do_csum(const unsigned char *buff, int len)* odd/even alignment, and means we can ignore it until the very end. */ shift = offset *8 ; - data =READ_ONCE_NOCHECK (*ptr++); + data = *ptr++;#ifdef __LITTLE_ENDIAN data = (data >> shift) << shift;#else @@ -58,10 +62,10 @@ unsigned int do_csum(const unsigned char *buff, int len)while (unlikely (len >64 )) {__uint128_t tmp1, tmp2, tmp3, tmp4; - tmp1 =READ_ONCE_NOCHECK (*(__uint128_t *)ptr); - tmp2 =READ_ONCE_NOCHECK (*(__uint128_t *)(ptr +2 )); - tmp3 =READ_ONCE_NOCHECK (*(__uint128_t *)(ptr +4 )); - tmp4 =READ_ONCE_NOCHECK (*(__uint128_t *)(ptr +6 )); + tmp1 = *(__uint128_t *)ptr; + tmp2 = *(__uint128_t *)(ptr +2 ); + tmp3 = *(__uint128_t *)(ptr +4 ); + tmp4 = *(__uint128_t *)(ptr +6 ); len -=64 ; ptr +=8 ;@@ -85,7 +89,7 @@ unsigned int do_csum(const unsigned char *buff, int len)__uint128_t tmp; sum64 =accumulate (sum64, data); - tmp =READ_ONCE_NOCHECK (*(__uint128_t *)ptr); + tmp = *(__uint128_t *)ptr; len -=16 ; ptr +=2 ;@@ -100,7 +104,7 @@ unsigned int do_csum(const unsigned char *buff, int len) }if (len >0 ) { sum64 =accumulate (sum64, data); - data =READ_ONCE_NOCHECK (*ptr); + data = *ptr; len -=8 ; }/* Signature · 1 line
2.26.1.301.g55bc3eb7cb9-goog
Will Deacon
maintainerRe: [PATCH v4 05/11] arm64: csum: Disable KASAN for do_csum()
Quoted 7 lines · Mark Rutland
Quoted 6 lines · Will Deacon
do_csum()over-reads the source buffer and therefore abusesREAD_ONCE_NOCHECK()to avoid tripping up KASAN. In preparation forREAD_ONCE_NOCHECK()becoming a macro, and therefore losing its '__no_sanitize_address' annotation, just annotatedo_csum()explicitly and fall back to normal loads.
Quoted 7 lines
Quoted 3 lines
Cc Mark Rutland <[email protected]>Cc Robin Murphy <[email protected]>Signed-off-by Will Deacon <[email protected]>From a functional perspective:
Acked-by Mark Rutland <[email protected]>
Thanks.
Quoted 3 lines
I know that Robin had a concern w.r.t. how this would affect the codegen, but I think we can follow that up after the series as a whole is merged.
Makes sense. I did look at the codegen, fwiw, and it didn't seem especially bad. One of the LDP's gets cracked in the unlikely() path, but it didn't look like it would be a disaster (and sprinkling barrier() around to force the LDP felt really fragile!).
Will
Robin Murphy
Acked-byRe: [PATCH v4 05/11] arm64: csum: Disable KASAN for do_csum()
Quoted 18 lines · Will Deacon
Quoted 7 lines · Mark Rutland
Quoted 6 lines · Will Deacon
do_csum()over-reads the source buffer and therefore abusesREAD_ONCE_NOCHECK()to avoid tripping up KASAN. In preparation forREAD_ONCE_NOCHECK()becoming a macro, and therefore losing its '__no_sanitize_address' annotation, just annotatedo_csum()explicitly and fall back to normal loads.Quoted 7 lines
Quoted 3 lines
Cc Mark Rutland <[email protected]>Cc Robin Murphy <[email protected]>Signed-off-by Will Deacon <[email protected]>From a functional perspective:
Acked-by Mark Rutland <[email protected]>Thanks.
Quoted 8 lines
Quoted 3 lines
I know that Robin had a concern w.r.t. how this would affect the codegen, but I think we can follow that up after the series as a whole is merged.
Makes sense. I did look at the codegen, fwiw, and it didn't seem especially bad. One of the LDP's gets cracked in the
unlikely()path, but it didn't look like it would be a disaster (and sprinklingbarrier()around to force the LDP felt really fragile!).
Sure - I have a nagging feeling that it could still do better WRT pipelining the loads anyway, so I'm happy to come back and reconsider the local codegen later. It certainly doesn't deserve to stand in the way of cross-arch rework.
Other than dereferencing the ptr argument, this code has no cause to make any explicit memory accesses of its own, so I don't think we lose any practical KASAN coverage by moving the annotation to function level. Given all that,
Cheers,
Robin.
RE: [PATCH v4 05/11] arm64: csum: Disable KASAN for do_csum()
From: Robin Murphy
Quoted 1 line
Sent: 22 April 2020 12:02
..
Quoted 4 lines
Sure - I have a nagging feeling that it could still do better WRT pipelining the loads anyway, so I'm happy to come back and reconsider the local codegen later. It certainly doesn't deserve to stand in the way of cross-arch rework.
How fast does that loop actually run?
To my mind it seems to do a lot of operations on each 64bit value. I'd have thought that a loop based on:
sum64 = *ptr; sum64_high = *ptr++ >> 32;
and then fixing up the result would be faster.
The x86-64 code is also bad!
On intel cpu prior to haswell a simple:
sum_64 += *ptr32++;
is faster than the current code.
(Although you can do a lot better even on ivy bridge.)
David
-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK Registration No: 1397386 (Wales)
Re: [PATCH v4 05/11] arm64: csum: Disable KASAN for do_csum()
Quoted 9 lines · David Laight
From: Robin Murphy
Quoted 1 line
Sent: 22 April 2020 12:02
..
Quoted 4 lines
Sure - I have a nagging feeling that it could still do better WRT pipelining the loads anyway, so I'm happy to come back and reconsider the local codegen later. It certainly doesn't deserve to stand in the way of cross-arch rework.
How fast does that loop actually run?
I've not characterised it in detail, but faster than any of the other attempts so far ;)
Quoted 11 lines
To my mind it seems to do a lot of operations on each 64bit value. I'd have thought that a loop based on:
sum64 = *ptr; sum64_high = *ptr++ >> 32;and then fixing up the result would be faster.
The x86-64 code is also bad!
On intel cpu prior to haswell a simple:sum_64 += *ptr32++;is faster than the current code.
(Although you can do a lot better even on ivy bridge.)
The aim here is to minimise load bandwidth - most Arm cores can slurp 16 bytes from L1 in a single load as quickly as any smaller amount, so nibbling away in little 32-bit chunks would result in up to 4x more load cycles. Yes, the C code looks ridiculous, but the other trick is that most of those operations don't actually exist. Since a __uint128_t is really backed by any two 64-bit GPRs - or if you're careful, one 64-bit GPR and the carry flag - all those shifts and rotations are in fact resolved by register allocation, so what we end up with is a very neat loop of essentially just loads and 64-bit accumulation:
...
138: a94030c3 ldp x3, x12, [x6] 13c: a9412cc8 ldp x8, x11, [x6, #16] 140: a94228c4 ldp x4, x10, [x6, #32] 144: a94324c7 ldp x7, x9, [x6, #48] 148: ab03018d adds x13, x12, x3 14c: 510100a5 sub w5, w5, #0x40 150: 9a0c0063 adc x3, x3, x12 154: ab08016c adds x12, x11, x8 158: 9a0b0108 adc x8, x8, x11 15c: ab04014b adds x11, x10, x4 160: 9a0a0084 adc x4, x4, x10 164: ab07012a adds x10, x9, x7 168: 9a0900e7 adc x7, x7, x9 16c: ab080069 adds x9, x3, x8 170: 9a080063 adc x3, x3, x8 174: ab070088 adds x8, x4, x7 178: 9a070084 adc x4, x4, x7 17c: 910100c6 add x6, x6, #0x40 180: ab040067 adds x7, x3, x4 184: 9a040063 adc x3, x3, x4 188: ab010064 adds x4, x3, x1 18c: 9a030023 adc x3, x1, x3 190: 710100bf cmp w5, #0x40 194: aa0303e1 mov x1, x3 198: 54fffd0c b.gt 138 <do_csum+0xd8>
...
Instruction-wise, that's about as good as it can get short of maintaining multiple accumulators and moving the pairwise folding out of the loop. The main thing that I think is still left on the table is that the load-to-use distances are pretty short and there's clearly scope to spread out and amortise the load cycles better, which stands to benefit both big and little cores.
Robin.
RE: [PATCH v4 05/11] arm64: csum: Disable KASAN for do_csum()
From: Robin Murphy
Quoted 14 lines
Sent: 24 April 2020 12:01
Quoted 9 lines · David Laight
From: Robin Murphy
Quoted 1 line
Sent: 22 April 2020 12:02
..
Quoted 4 lines
Sure - I have a nagging feeling that it could still do better WRT pipelining the loads anyway, so I'm happy to come back and reconsider the local codegen later. It certainly doesn't deserve to stand in the way of cross-arch rework.
How fast does that loop actually run?
I've not characterised it in detail, but faster than any of the other attempts so far ;)
...
Quoted 4 lines
The aim here is to minimise load bandwidth - most Arm cores can slurp 16 bytes from L1 in a single load as quickly as any smaller amount, so nibbling away in little 32-bit chunks would result in up to 4x more load cycles.
The x86 'problem' is that 'adc' takes two clocks and the carry flag 'register chain' means you can only sum 4 bytes/clock regardless of the memory accesses.
Quoted 33 lines
Yes, the C code looks ridiculous, but the other trick is that most of those operations don't actually exist. Since a
__uint128_tis really backed by any two 64-bit GPRs - or if you're careful, one 64-bit GPR and the carry flag - all those shifts and rotations are in fact resolved by register allocation, so what we end up with is a very neat loop of essentially just loads and 64-bit accumulation:...
138: a94030c3 ldp x3, x12, [x6] 13c: a9412cc8 ldp x8, x11, [x6, #16] 140: a94228c4 ldp x4, x10, [x6, #32] 144: a94324c7 ldp x7, x9, [x6, #48] 148: ab03018d adds x13, x12, x3 14c: 510100a5 sub w5, w5, #0x40 150: 9a0c0063 adc x3, x3, x12 154: ab08016c adds x12, x11, x8 158: 9a0b0108 adc x8, x8, x11 15c: ab04014b adds x11, x10, x4 160: 9a0a0084 adc x4, x4, x10 164: ab07012a adds x10, x9, x7 168: 9a0900e7 adc x7, x7, x9 16c: ab080069 adds x9, x3, x8 170: 9a080063 adc x3, x3, x8 174: ab070088 adds x8, x4, x7 178: 9a070084 adc x4, x4, x7 17c: 910100c6 add x6, x6, #0x40 180: ab040067 adds x7, x3, x4 184: 9a040063 adc x3, x3, x4 188: ab010064 adds x4, x3, x1 18c: 9a030023 adc x3, x1, x3 190: 710100bf cmp w5, #0x40 194: aa0303e1 mov x1, x3 198: 54fffd0c b.gt 138 <do_csum+0xd8>
Quoted 8 lines
...
Instruction-wise, that's about as good as it can get short of maintaining multiple accumulators and moving the pairwise folding out of the loop. The main thing that I think is still left on the table is that the load-to-use distances are pretty short and there's clearly scope to spread out and amortise the load cycles better, which stands to benefit both big and little cores.
I realised most of the C would disappear - just hard to see what the result would be.
Looking at the above there are 8 (64bit) loads and 16 adds. (Plus 2 adds for the loop control, should only need one! and a spare register move.)
Without multiple carry flags the best you are going to get is one add instruction and one 'save the carry flag' instruction for each 'word'.
The thing then is to arrange the code to avoid register dependency chains so that the instructions can run in parallel.
I think you lose at the bottom of the above when you add to the global sum - might be faster with 2 sums.
Actually trying to do one load and 4 adds every clock might be possible - if the cpu can execute them.
But that would require a horrid interleaved loop.
David
-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK Registration No: 1397386 (Wales)
Will Deacon
maintainerPATCH v4 6/11 READ_ONCE: Simplify implementations of {READ,WRITE}_ONCE()
In reply to Will DeaconThe implementations of {READ,WRITE}_ONCE() suffer from a significant amount of indirection and complexity due to a historic GCC bug:
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=58145
which was originally worked around by 230fa253df63 ("kernel: Provide READ_ONCE and ASSIGN_ONCE").
Since GCC 4.8 is fairly vintage at this point and we emit a warning if we detect it during the build, return {READ,WRITE}_ONCE() to their former glory with an implementation that is easier to understand and, crucially, more amenable to optimisation. A side effect of this simplification is that WRITE_ONCE() no longer returns a value, but nobody seems to be relying on that and the new behaviour is aligned with smp_store_release().
--- include/linux/compiler.h | 118 +++++++++++++-------------------------- 1 file changed, 39 insertions(+), 79 deletions(-)
include/linux/compiler.h +39 -79
@@ -177,60 +177,6 @@ void ftrace_likely_update(struct ftrace_likely_data *f, int val,# define __UNIQUE_ID (prefix) __PASTE(__PASTE(__UNIQUE_ID_, prefix), __LINE__)#endif -#include <uapi/linux/types.h> - -#define __READ_ONCE_SIZE \ -({ \ - switch (size) { \ - case 1: *(__u8 *)res = *(volatile __u8 *)p; break; \ - case 2: *(__u16 *)res = *(volatile __u16 *)p; break; \ - case 4: *(__u32 *)res = *(volatile __u32 *)p; break; \ - case 8: *(__u64 *)res = *(volatile __u64 *)p; break; \ - default: \ - barrier(); \ - __builtin_memcpy((void *)res, (const void *)p, size); \ - barrier(); \ - } \ -}) - -static __always_inline -void __read_once_size(const volatile void *p, void *res, int size) -{ - __READ_ONCE_SIZE; -} - -#ifdef CONFIG_KASAN -/* -* We can't declare function 'inline' because __no_sanitize_address confilcts -* with inlining. Attempt to inline it may cause a build failure. -* https://gcc.gnu.org/bugzilla/show_bug.cgi?id=67368 -* '__maybe_unused' allows us to avoid defined-but-not-used warnings. -*/ -# define __no_kasan_or_inline __no_sanitize_address notrace __maybe_unused -#else -# define __no_kasan_or_inline __always_inline -#endif - -static __no_kasan_or_inline -void __read_once_size_nocheck(const volatile void *p, void *res, int size) -{ - __READ_ONCE_SIZE; -} - -static __always_inline void __write_once_size(volatile void *p, void *res, int size) -{ -switch (size) { -case 1 : *(volatile __u8 *)p = *(__u8 *)res;break ; -case 2 : *(volatile __u16 *)p = *(__u16 *)res;break ; -case 4 : *(volatile __u32 *)p = *(__u32 *)res;break ; -case 8 : *(volatile __u64 *)p = *(__u64 *)res;break ; -default : -barrier (); -__builtin_memcpy ((void *)p, (const void *)res, size); -barrier (); - } -} -/* * Prevent the compiler from merging or refetching reads or writes. The * compiler is also forbidden from reordering successive instances of
@@ -240,11 +186,7 @@ static __always_inline void __write_once_size(volatile void *p, void *res, int s* statements. * * These two macros will also work on aggregate data types like structs or -* unions. If the size of the accessed data type exceeds the word size of -* the machine (e.g., 32 bits or 64 bits) READ_ONCE() and WRITE_ONCE() will -* fall back to memcpy(). There's at least two memcpy()s: one for the -* __builtin_memcpy() and then one for the macro doing the copy of variable -* - '__u' allocated on the stack. +* unions. * * Their two major use cases are: (1) Mediating communication between * process-level code and irq/NMI handlers, all running on the same CPU,
@@ -256,23 +198,49 @@ static __always_inline void __write_once_size(volatile void *p, void *res, int s#include <asm/barrier.h> #include <linux/kasan-checks.h> -#define __READ_ONCE (x, check) \ +#define __READ_ONCE (x) (*(volatile typeof(x) *)&(x)) + +#define READ_ONCE (x) \ ({ \ - union { typeof(x) __val; char __c[1]; } __u; \ - if (check) \ - __read_once_size(&(x), __u.__c, sizeof(x)); \ - else \ - __read_once_size_nocheck(&(x), __u.__c, sizeof(x)); \ - smp_read_barrier_depends();/* Enforce dependency ordering from x */ \ - __u.__val; \ + typeof(x) __x = __READ_ONCE(x); \ + smp_read_barrier_depends(); \ + __x; \ }) -#define READ_ONCE (x) __READ_ONCE(x, 1) + +#define WRITE_ONCE (x, val) \ +do { \ + *(volatile typeof(x) *)&(x) = (val); \ +} while (0) + +#ifdef CONFIG_KASAN +/* +* We can't declare function 'inline' because __no_sanitize_address conflicts +* with inlining. Attempt to inline it may cause a build failure. +* https://gcc.gnu.org/bugzilla/show_bug.cgi?id=67368 +* '__maybe_unused' allows us to avoid defined-but-not-used warnings. +*/ +# define __no_kasan_or_inline __no_sanitize_address notrace __maybe_unused +#else +# define __no_kasan_or_inline __always_inline +#endif + +static __no_kasan_or_inline +unsigned long __read_once_word_nocheck (const void *addr) +{ +return __READ_ONCE (*(unsigned long *)addr); +}/* -* Use READ_ONCE_NOCHECK() instead of READ_ONCE() if you need -* to hide memory access from KASAN. +* Use READ_ONCE_NOCHECK() instead of READ_ONCE() if you need to load a +* word from memory atomically but without telling KASAN. This is usually +* used by unwinding code when walking the stack of a running process. */ -#define READ_ONCE_NOCHECK (x) __READ_ONCE(x, 0) +#define READ_ONCE_NOCHECK (x) \ +({ \ + unsigned long __x = __read_once_word_nocheck(&(x)); \ + smp_read_barrier_depends(); \ + __x; \ +})static __no_kasan_or_inline unsigned long read_word_at_a_time (const void *addr)
@@ -281,14 +249,6 @@ unsigned long read_word_at_a_time(const void *addr) return *(unsigned long *)addr; } -#define WRITE_ONCE (x, val) \ -({ \ - union { typeof(x) __val; char __c[1]; } __u = \ - { .__val = (__force typeof(x)) (val) }; \ - __write_once_size(&(x), __u.__c, sizeof(x)); \ - __u.__val; \ -}) -#endif /* __KERNEL__ */ /*
Signature · 1 line
2.26.1.301.g55bc3eb7cb9-goog
Re: [PATCH v4 06/11] READ_ONCE: Simplify implementations of {READ,WRITE}_ONCE()
Quoted 15 lines · Will Deacon
The implementations of {READ,WRITE}_ONCE() suffer from a significant amount of indirection and complexity due to a historic GCC bug:
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=58145
which was originally worked around by
230fa253df63("kernel: ProvideREAD_ONCEandASSIGN_ONCE").Since GCC 4.8 is fairly vintage at this point and we emit a warning if we detect it during the build, return {READ,WRITE}_ONCE() to their former glory with an implementation that is easier to understand and, crucially, more amenable to optimisation. A side effect of this simplification is that
WRITE_ONCE()no longer returns a value, but nobody seems to be relying on that and the new behaviour is aligned withsmp_store_release().
Quoted 6 lines
Suggested-by Linus Torvalds <[email protected]>Cc Peter Zijlstra <[email protected]>Cc Michael Ellerman <[email protected]>Cc Arnd Bergmann <[email protected]>Cc Christian Borntraeger <[email protected]>Signed-off-by Will Deacon <[email protected]>
The nocheck bits look fine to me now, so FWIW:
Mark.
Quoted 162 lines
--- include/linux/compiler.h | 118 +++++++++++++-------------------------- 1 file changed, 39 insertions(+), 79 deletions(-)include/linux/compiler.h +39
-79@@ -177,60 +177,6 @@ void ftrace_likely_update(struct ftrace_likely_data *f, int val,# define __UNIQUE_ID (prefix) __PASTE(__PASTE(__UNIQUE_ID_, prefix), __LINE__)#endif -#include <uapi/linux/types.h> - -#define __READ_ONCE_SIZE \ -({ \ - switch (size) { \ - case 1: *(__u8 *)res = *(volatile __u8 *)p; break; \ - case 2: *(__u16 *)res = *(volatile __u16 *)p; break; \ - case 4: *(__u32 *)res = *(volatile __u32 *)p; break; \ - case 8: *(__u64 *)res = *(volatile __u64 *)p; break; \ - default: \ - barrier(); \ - __builtin_memcpy((void *)res, (const void *)p, size); \ - barrier(); \ - } \ -}) - -static __always_inline -void __read_once_size(const volatile void *p, void *res, int size) -{ - __READ_ONCE_SIZE; -} - -#ifdef CONFIG_KASAN -/* -* We can't declare function 'inline' because __no_sanitize_address confilcts -* with inlining. Attempt to inline it may cause a build failure. -* https://gcc.gnu.org/bugzilla/show_bug.cgi?id=67368 -* '__maybe_unused' allows us to avoid defined-but-not-used warnings. -*/ -# define __no_kasan_or_inline __no_sanitize_address notrace __maybe_unused -#else -# define __no_kasan_or_inline __always_inline -#endif - -static __no_kasan_or_inline -void __read_once_size_nocheck(const volatile void *p, void *res, int size) -{ - __READ_ONCE_SIZE; -} - -static __always_inline void __write_once_size(volatile void *p, void *res, int size) -{ -switch (size) { -case 1 : *(volatile __u8 *)p = *(__u8 *)res;break ; -case 2 : *(volatile __u16 *)p = *(__u16 *)res;break ; -case 4 : *(volatile __u32 *)p = *(__u32 *)res;break ; -case 8 : *(volatile __u64 *)p = *(__u64 *)res;break ; -default : -barrier (); -__builtin_memcpy ((void *)p, (const void *)res, size); -barrier (); - } -} -/* * Prevent the compiler from merging or refetching reads or writes. The * compiler is also forbidden from reordering successive instances of @@ -240,11 +186,7 @@ static __always_inline void __write_once_size(volatile void *p, void *res, int s* statements. * * These two macros will also work on aggregate data types like structs or -* unions. If the size of the accessed data type exceeds the word size of -* the machine (e.g., 32 bits or 64 bits) READ_ONCE() and WRITE_ONCE() will -* fall back to memcpy(). There's at least two memcpy()s: one for the -* __builtin_memcpy() and then one for the macro doing the copy of variable -* - '__u' allocated on the stack. +* unions. * * Their two major use cases are: (1) Mediating communication between * process-level code and irq/NMI handlers, all running on the same CPU, @@ -256,23 +198,49 @@ static __always_inline void __write_once_size(volatile void *p, void *res, int s#include <asm/barrier.h> #include <linux/kasan-checks.h> -#define __READ_ONCE (x, check) \ +#define __READ_ONCE (x) (*(volatile typeof(x) *)&(x)) + +#define READ_ONCE (x) \ ({ \ - union { typeof(x) __val; char __c[1]; } __u; \ - if (check) \ - __read_once_size(&(x), __u.__c, sizeof(x)); \ - else \ - __read_once_size_nocheck(&(x), __u.__c, sizeof(x)); \ - smp_read_barrier_depends();/* Enforce dependency ordering from x */ \ - __u.__val; \ + typeof(x) __x = __READ_ONCE(x); \ + smp_read_barrier_depends(); \ + __x; \ }) -#define READ_ONCE (x) __READ_ONCE(x, 1) + +#define WRITE_ONCE (x, val) \ +do { \ + *(volatile typeof(x) *)&(x) = (val); \ +} while (0) + +#ifdef CONFIG_KASAN +/* +* We can't declare function 'inline' because __no_sanitize_address conflicts +* with inlining. Attempt to inline it may cause a build failure. +* https://gcc.gnu.org/bugzilla/show_bug.cgi?id=67368 +* '__maybe_unused' allows us to avoid defined-but-not-used warnings. +*/ +# define __no_kasan_or_inline __no_sanitize_address notrace __maybe_unused +#else +# define __no_kasan_or_inline __always_inline +#endif + +static __no_kasan_or_inline +unsigned long __read_once_word_nocheck (const void *addr) +{ +return __READ_ONCE (*(unsigned long *)addr); +}/* -* Use READ_ONCE_NOCHECK() instead of READ_ONCE() if you need -* to hide memory access from KASAN. +* Use READ_ONCE_NOCHECK() instead of READ_ONCE() if you need to load a +* word from memory atomically but without telling KASAN. This is usually +* used by unwinding code when walking the stack of a running process. */ -#define READ_ONCE_NOCHECK (x) __READ_ONCE(x, 0) +#define READ_ONCE_NOCHECK (x) \ +({ \ + unsigned long __x = __read_once_word_nocheck(&(x)); \ + smp_read_barrier_depends(); \ + __x; \ +})static __no_kasan_or_inline unsigned long read_word_at_a_time (const void *addr)@@ -281,14 +249,6 @@ unsigned long read_word_at_a_time(const void *addr) return *(unsigned long *)addr; } -#define WRITE_ONCE (x, val) \ -({ \ - union { typeof(x) __val; char __c[1]; } __u = \ - { .__val = (__force typeof(x)) (val) }; \ - __write_once_size(&(x), __u.__c, sizeof(x)); \ - __u.__val; \ -}) -#endif /* __KERNEL__ */ /* Signature · 1 line
2.26.1.301.g55bc3eb7cb9-goog
Will Deacon
maintainerPATCH v4 7/11 READ_ONCE: Enforce atomicity for {READ,WRITE}_ONCE() memory accesses
In reply to Will Deacon{READ,WRITE}_ONCE() cannot guarantee atomicity for arbitrary data sizes. This can be surprising to callers that might incorrectly be expecting atomicity for accesses to aggregate structures, although there are other callers where tearing is actually permissable (e.g. if they are using something akin to sequence locking to protect the access).
Linus sayeth:
| We could also look at being stricter for the normal READ/WRITE_ONCE(), | and require that they are | | (a) regular integer types | | (b) fit in an atomic word | | We actually did (b) for a while, until we noticed that we do it on | loff_t's etc and relaxed the rules. But maybe we could have a | "non-atomic" version of READ/WRITE_ONCE() that is used for the | questionable cases?
The slight snag is that we also have to support 64-bit accesses on 32-bit architectures, as these appear to be widespread and tend to work out ok if either the architecture supports atomic 64-bit accesses (x86, armv7) or if the variable being accesses represents a virtual address and therefore only requires 32-bit atomicity in practice.
Take a step in that direction by introducing a variant of 'compiletime_assert_atomic_type()' and use it to check the pointer argument to {READ,WRITE}_ONCE(). Expose __{READ,WRITE}_ONCE() variants which are allowed to tear and convert the one broken caller over to the new macros.
--- drivers/xen/time.c | 2 +- include/linux/compiler.h | 33 ++++++++++++++++++++++++++++++--- 2 files changed, 31 insertions(+), 4 deletions(-)
drivers/xen/time.c +1 -1
@@ -64,7 +64,7 @@ static void xen_get_runstate_snapshot_cpu_delta(do { state_time =get64 (&state->state_entry_time);rmb ();/* Hypervisor might update data. */ - *res =READ_ONCE (*state); + *res =__READ_ONCE (*state);rmb ();/* Hypervisor might update data. */ }while (get64 (&state->state_entry_time) != state_time || (state_time &XEN_RUNSTATE_UPDATE ));
include/linux/compiler.h +30 -3
@@ -198,20 +198,37 @@ void ftrace_likely_update(struct ftrace_likely_data *f, int val,#include <asm/barrier.h> #include <linux/kasan-checks.h> -#define __READ_ONCE (x) (*(volatile typeof(x) *)&(x)) +/* +* Use __READ_ONCE() instead of READ_ONCE() if you do not require any +* atomicity or dependency ordering guarantees. Note that this may result +* in tears! +*/ +#define __READ_ONCE (x) (*(const volatile typeof(x) *)&(x)) -#define READ_ONCE (x) \ +#define __READ_ONCE_SCALAR (x) \ ({ \ typeof(x) __x = __READ_ONCE(x); \ smp_read_barrier_depends(); \ __x; \ }) -#define WRITE_ONCE (x, val) \ +#define READ_ONCE (x) \ +({ \ + compiletime_assert_rwonce_type(x); \ + __READ_ONCE_SCALAR(x); \ +}) + +#define __WRITE_ONCE (x, val) \ do { \ *(volatile typeof(x) *)&(x) = (val); \ } while (0) +#define WRITE_ONCE (x, val) \ +do { \ + compiletime_assert_rwonce_type(x); \ + __WRITE_ONCE(x, val); \ +} while (0) +#ifdef CONFIG_KASAN /* * We can't declare function 'inline' because __no_sanitize_address conflicts
@@ -313,6 +330,16 @@ static inline void *offset_to_ptr(const int *off)compiletime_assert (__native_word (t), \"Need native word sized stores/loads for atomicity." ) +/* +* Yes, this permits 64-bit accesses on 32-bit architectures. These will +* actually be atomic in many cases (namely x86), but for others we rely on +* the access being split into 2x32-bit accesses for a 32-bit quantity (e.g. +* a virtual address) and a strong prevailing wind. + */ +#define compiletime_assert_rwonce_type (t) \ + compiletime_assert(__native_word(t) || sizeof(t) == sizeof(long long), \ + "Unsupported access size for {READ,WRITE}_ONCE().") +/* &a[0] degrades to a pointer: a different type from an array */ #define __must_be_array (a) BUILD_BUG_ON_ZERO(__same_type((a), &(a)[0]))
Signature · 1 line
2.26.1.301.g55bc3eb7cb9-goog
Re: [PATCH v4 07/11] READ_ONCE: Enforce atomicity for {READ,WRITE}_ONCE() memory accesses
Quoted 5 lines · Will Deacon
{READ,WRITE}_ONCE() cannot guarantee atomicity for arbitrary data sizes. This can be surprising to callers that might incorrectly be expecting atomicity for accesses to aggregate structures, although there are other callers where tearing is actually permissable (e.g. if they are using something akin to sequence locking to protect the access).
[...]
Quoted 11 lines
The slight snag is that we also have to support 64-bit accesses on 32-bit architectures, as these appear to be widespread and tend to work out ok if either the architecture supports atomic 64-bit accesses (x86, armv7) or if the variable being accesses represents a virtual address and therefore only requires 32-bit atomicity in practice.
Take a step in that direction by introducing a variant of '
compiletime_assert_atomic_type()' and use it to check the pointer argument to {READ,WRITE}_ONCE(). Expose __{READ,WRITE}_ONCE() variants which are allowed to tear and convert the one broken caller over to the new macros.
[...]
Quoted 3 lines
+/*
+ * Yes, this permits 64-bit accesses on 32-bit architectures. These will
+ * actually be atomic in many cases (namely x86), but for others we rely on
I don't think that's correct?
$ cat 32bit_volatile_qword_read_write.c #include <pthread.h> #include <err.h> #include <stdio.h> #include <stdint.h>
/* make sure it really has proper alignment */
__attribute__((aligned(8)))
volatile uint64_t shared_u64;
static void *thread_fn(void *dummy) {
while (1) {
uint64_t value = shared_u64;
if (value == 0xaaaaaaaaaaaaaaaaUL || value == 0xbbbbbbbbbbbbbbbbUL)
continue;
printf("got 0x%llx\n", (unsigned long long)value); }
return NULL;
}
int main(void) {
printf("shared_u64 at %p\n", &shared_u64); pthread_t thread;
if (pthread_create(&thread, NULL, thread_fn, NULL))
err(1, "pthread_create");
while (1) {
shared_u64 = 0xaaaaaaaaaaaaaaaaUL;
shared_u64 = 0xbbbbbbbbbbbbbbbbUL;
}
}
$ gcc -o 32bit_volatile_qword_read_write
32bit_volatile_qword_read_write.c -pthread -Wall
$ ./32bit_volatile_qword_read_write | head # as 64-bit binary
^C
$ gcc -m32 -o 32bit_volatile_qword_read_write
32bit_volatile_qword_read_write.c -pthread -Wall
$ ./32bit_volatile_qword_read_write | head # as 32-bit binary
shared_u64 at 0x56567030
got 0xaaaaaaaabbbbbbbb
got 0xbbbbbbbbaaaaaaaa
got 0xaaaaaaaabbbbbbbb
got 0xbbbbbbbbaaaaaaaa
got 0xbbbbbbbbaaaaaaaa
got 0xbbbbbbbbaaaaaaaa
got 0xbbbbbbbbaaaaaaaa
got 0xbbbbbbbbaaaaaaaa
got 0xbbbbbbbbaaaaaaaa
$AFAIK 32-bit X86 code that wants to atomically load 8 bytes of memory has to use CMPXCHG8B; and gcc won't generate such code just based on a volatile load/store.
Will Deacon
maintainerRe: [PATCH v4 07/11] READ_ONCE: Enforce atomicity for {READ,WRITE}_ONCE() memory accesses
Quoted 24 lines · Jann Horn
Quoted 5 lines · Will Deacon
{READ,WRITE}_ONCE() cannot guarantee atomicity for arbitrary data sizes. This can be surprising to callers that might incorrectly be expecting atomicity for accesses to aggregate structures, although there are other callers where tearing is actually permissable (e.g. if they are using something akin to sequence locking to protect the access).
[...]
Quoted 11 lines
The slight snag is that we also have to support 64-bit accesses on 32-bit architectures, as these appear to be widespread and tend to work out ok if either the architecture supports atomic 64-bit accesses (x86, armv7) or if the variable being accesses represents a virtual address and therefore only requires 32-bit atomicity in practice.
Take a step in that direction by introducing a variant of '
compiletime_assert_atomic_type()' and use it to check the pointer argument to {READ,WRITE}_ONCE(). Expose __{READ,WRITE}_ONCE() variants which are allowed to tear and convert the one broken caller over to the new macros.[...]
Quoted 3 lines
+/*
+ * Yes, this permits 64-bit accesses on 32-bit architectures. These will
+ * actually be atomic in many cases (namely x86), but for others we rely onI don't think that's correct?
[...]
Quoted 3 lines
AFAIK 32-bit X86 code that wants to atomically load 8 bytes of memory has to use CMPXCHG8B; and gcc won't generate such code just based on a volatile load/store.
My apologies, you're completely right. I thought that PAE mandated 64-bit atomicity, like it does on 32-bit ARM, but that's apparently not the case and looking at the 32-bit x86 pgtable code they have to be really careful there.
I'll update the comment.
Will
Re: [PATCH v4 07/11] READ_ONCE: Enforce atomicity for {READ,WRITE}_ONCE() memory accesses
Quoted 4 lines · Will Deacon
My apologies, you're completely right. I thought that PAE mandated 64-bit atomicity, like it does on 32-bit ARM, but that's apparently not the case and looking at the 32-bit x86 pgtable code they have to be really careful there.
They added CMPXCHG8B for PAE, but then we never used it for that because it's dead slow (MS did use it I think). Instead we play horrible split word games, grep for CONFIG_GUP_GET_PTE_LOW_HIGH.
Will Deacon
maintainerPATCH v4 8/11 READ_ONCE: Drop pointer qualifiers when reading from scalar types
In reply to Will DeaconPassing a volatile-qualified pointer to READ_ONCE() is an absolute trainwreck for code generation: the use of 'typeof()' to define a temporary variable inside the macro means that the final evaluation in macro scope ends up forcing a read back from the stack. When stack protector is enabled (the default for arm64, at least), this causes the compiler to vomit up all sorts of junk.
Unfortunately, dropping pointer qualifiers inside the macro poses quite a challenge, especially since the pointed-to type is permitted to be an aggregate, and this is relied upon by mm/ code accessing things like 'pmd_t'. Based on numerous hacks and discussions on the mailing list, this is the best I've managed to come up with.
Introduce '__unqual_scalar_typeof()' which takes an expression and, if the expression is an optionally qualified 8, 16, 32 or 64-bit scalar type, evaluates to the unqualified type. Other input types, including aggregates, remain unchanged. Hopefully READ_ONCE() on volatile aggregate pointers isn't something we do on a fast-path.
--- include/linux/compiler.h | 6 +++--- include/linux/compiler_types.h | 21 +++++++++++++++++++++ 2 files changed, 24 insertions(+), 3 deletions(-)
include/linux/compiler.h +3 -3
@@ -203,13 +203,13 @@ void ftrace_likely_update(struct ftrace_likely_data *f, int val,* atomicity or dependency ordering guarantees. Note that this may result * in tears! */ -#define __READ_ONCE (x) (*(const volatile typeof(x) *)&(x)) +#define __READ_ONCE (x) (*(const volatile __unqual_scalar_typeof(x) *)&(x))#define __READ_ONCE_SCALAR (x) \ ({ \ - typeof(x) __x = __READ_ONCE(x); \ + __unqual_scalar_typeof(x) __x = __READ_ONCE(x); \ smp_read_barrier_depends(); \ - __x; \ + (typeof(x))__x; \ })#define READ_ONCE (x) \
include/linux/compiler_types.h +21 -0
@@ -210,6 +210,27 @@ struct ftrace_likely_data {/* Are two types/vars the same type (ignoring qualifiers)? */ #define __same_type (a, b) __builtin_types_compatible_p(typeof(a), typeof(b)) +/* +* __unqual_scalar_typeof(x) - Declare an unqualified scalar type, leaving +* non-scalar types unchanged. +* +* We build this out of a couple of helper macros in a vain attempt to +* help you keep your lunch down while reading it. +*/ +#define __pick_scalar_type (x, type, otherwise) \ + __builtin_choose_expr(__same_type(x, type), (type)0, otherwise) + +#define __pick_integer_type (x, type, otherwise) \ + __pick_scalar_type(x, unsigned type, \ + __pick_scalar_type(x, signed type, otherwise)) + +#define __unqual_scalar_typeof (x) typeof( \ + __pick_integer_type(x, char, \ + __pick_integer_type(x, short, \ + __pick_integer_type(x, int, \ + __pick_integer_type(x, long, \ + __pick_integer_type(x, long long, x)))))) +/* Is this type a native word size -- useful for atomic operations */ #define __native_word (t) \ (sizeof(t) == sizeof(char) || sizeof(t) == sizeof(short) || \
Signature · 1 line
2.26.1.301.g55bc3eb7cb9-goog
Re: [PATCH v4 08/11] READ_ONCE: Drop pointer qualifiers when reading from scalar types
Quoted 7 lines · Will Deacon
Passing a volatile-qualified pointer to
READ_ONCE()is an absolute trainwreck for code generation: the use of 'typeof()' to define a temporary variable inside the macro means that the final evaluation in macro scope ends up forcing a read back from the stack. When stack protector is enabled (the default for arm64, at least), this causes the compiler to vomit up all sorts of junk.
Quoted 5 lines
Unfortunately, dropping pointer qualifiers inside the macro poses quite a challenge, especially since the pointed-to type is permitted to be an aggregate, and this is relied upon by
mm/code accessing things like 'pmd_t'. Based on numerous hacks and discussions on the mailing list, this is the best I've managed to come up with.
Hm, maybe this can be brought to work, only very lightly tested. It basically abuses what -Wignored-qualifiers points out:
warning: type qualifiers ignored on function return type
Example showing the idea:
const int c(void);
volatile int v(void);
int hack(int x, int y)
{
typeof(c()) a = x;
typeof(v()) b = y;
a += b;
b += a;
a += b;
return a;
}Since that compiles, a cannot be const-qualified, and the generated code certainly suggests that b is not volatile-qualified. So something like
#define unqual_type(x) _unqual_type(x, unique_id_dance) #define _unqual_type(x, id) typeof( ({
typeof(x) id(void);
id();
}) )
and perhaps some _Pragma("GCC diagnostic push")/_Pragma("GCC diagnostic ignored -Wignored-qualifiers")/_Pragma("GCC diagnostic pop") could prevent the warning (which is in -Wextra, so I don't think it would appear in a normal build anyway).
No idea how well any of this would work across gcc versions or with clang.
Rasmus
Re: [PATCH v4 08/11] READ_ONCE: Drop pointer qualifiers when reading from scalar types
Hi!
Quoted 18 lines · Rasmus Villemoes
Quoted 5 lines · Will Deacon
Unfortunately, dropping pointer qualifiers inside the macro poses quite a challenge, especially since the pointed-to type is permitted to be an aggregate, and this is relied upon by
mm/code accessing things like 'pmd_t'. Based on numerous hacks and discussions on the mailing list, this is the best I've managed to come up with.Hm, maybe this can be brought to work, only very lightly tested. It basically abuses what -Wignored-qualifiers points out:
warning: type qualifiers ignored on function return type
Example showing the idea:
const int c(void);
volatile int v(void);int
hack(int x, int y)
Quoted 9 lines
{
typeof(c()) a = x;
typeof(v()) b = y;
a += b;
b += a;
a += b;
return a;
}Nasty. I like it :-)
Quoted 9 lines
Since that compiles, a cannot be const-qualified, and the generated code certainly suggests that b is not volatile-qualified. So something like
#define
unqual_type(x)_unqual_type(x, unique_id_dance)#define_unqual_type(x, id)typeof( ({
typeof(x) id(void);
id();
}) )
Quoted 6 lines
and perhaps some
_Pragma("GCC diagnostic push")/_Pragma("GCC diagnostic ignored -Wignored-qualifiers")/_Pragma("GCC diagnostic pop") could prevent the warning (which is in -Wextra, so I don't think it would appear in a normal build anyway).No idea how well any of this would work across gcc versions or with clang.
https://gcc.gnu.org/legacy-ml/gcc-patches/2016-05/msg01054.html
This is defined to work this way in ISO C since C11.
But, it doesn't work with GCC before GCC 7 :-(
Segher
Will Deacon
maintainerRe: [PATCH v4 08/11] READ_ONCE: Drop pointer qualifiers when reading from scalar types
Quoted 45 lines · Segher Boessenkool
Quoted 18 lines · Rasmus Villemoes
Quoted 5 lines · Will Deacon
Unfortunately, dropping pointer qualifiers inside the macro poses quite a challenge, especially since the pointed-to type is permitted to be an aggregate, and this is relied upon by
mm/code accessing things like 'pmd_t'. Based on numerous hacks and discussions on the mailing list, this is the best I've managed to come up with.Hm, maybe this can be brought to work, only very lightly tested. It basically abuses what -Wignored-qualifiers points out:
warning: type qualifiers ignored on function return type
Example showing the idea:
const int c(void);
volatile int v(void);int
hack(int x, int y)Quoted 9 lines
{ typeof(c()) a = x; typeof(v()) b = y; a += b; b += a; a += b; return a; }Nasty. I like it :-)
Quoted 14 lines
Since that compiles, a cannot be const-qualified, and the generated code certainly suggests that b is not volatile-qualified. So something like
#define
unqual_type(x)_unqual_type(x, unique_id_dance)#define_unqual_type(x, id)typeof( ({
typeof(x) id(void);
id();
}) )and perhaps some
_Pragma("GCC diagnostic push")/_Pragma("GCC diagnostic ignored -Wignored-qualifiers")/_Pragma("GCC diagnostic pop") could prevent the warning (which is in -Wextra, so I don't think it would appear in a normal build anyway).
Quoted 7 lines
Quoted 1 line
No idea how well any of this would work across gcc versions or with clang.
https://gcc.gnu.org/legacy-ml/gcc-patches/2016-05/msg01054.html
This is defined to work this way in ISO C since C11.
But, it doesn't work with GCC before GCC 7 :-(
Damn, that's quite a cool hack! Maybe we'll be able to implement it in a few years time ;)
WIll
Will Deacon
maintainerRe: [PATCH v4 08/11] READ_ONCE: Drop pointer qualifiers when reading from scalar types
In reply to Will DeaconQuoted 14 lines · Will Deacon
include/linux/compiler_types.h +6
-0@@ -210,6 +210,27 @@ struct ftrace_likely_data {/* Are two types/vars the same type (ignoring qualifiers)? */ #define __same_type (a, b) __builtin_types_compatible_p(typeof(a), typeof(b)) +/* +* __unqual_scalar_typeof(x) - Declare an unqualified scalar type, leaving +* non-scalar types unchanged. +* +* We build this out of a couple of helper macros in a vain attempt to +* help you keep your lunch down while reading it.
include/linux/compiler_types.h struct ftrace_likely_data { · line 228
Quoted 10 lines
include/linux/compiler_types.h +10
-0+*/ +#define __pick_scalar_type (x, type, otherwise) \ + __builtin_choose_expr(__same_type(x, type), (type)0, otherwise) + +#define __pick_integer_type (x, type, otherwise) \ + __pick_scalar_type(x, unsigned type, \ + __pick_scalar_type(x, signed type, otherwise)) + +#define __unqual_scalar_typeof (x) typeof( \ + __pick_integer_type(x, char, \
Rasmus pointed out to me that 'char' is not __builtin_types_compatible_p() with either 'signed char' or 'unsigned char', so I'll roll in the diff below to handle this case.
Will
--->8
include/linux/compiler_types.h +7 -2
@@ -220,9 +220,14 @@ struct ftrace_likely_data {#define __pick_scalar_type (x, type, otherwise) \ __builtin_choose_expr(__same_type(x, type), (type)0, otherwise) +/* +* 'char' is not type-compatible with either 'signed char' or 'unsigned char', +* so we include the naked type here as well as the signed/unsigned variants. +*/ #define __pick_integer_type (x, type, otherwise) \ - __pick_scalar_type(x, unsigned type, \ - __pick_scalar_type(x, signed type, otherwise)) + __pick_scalar_type(x, type, \ + __pick_scalar_type(x, unsigned type, \ + __pick_scalar_type(x, signed type, otherwise)))#define __unqual_scalar_typeof (x) typeof( \ __pick_integer_type(x, char, \
Will Deacon
maintainerPATCH v4 9/11 locking/barriers: Use '__unqual_scalar_typeof' for load-acquire macros
In reply to Will DeaconPassing volatile-qualified pointers to the asm-generic implementations of the load-acquire macros results in a re-load from the stack due to the temporary result variable inheriting the volatile semantics thanks to the use of 'typeof()'.
Define these temporary variables using 'unqual_scalar_typeof' to drop the volatile qualifier in the case that they are scalar types.
--- include/asm-generic/barrier.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-)
include/asm-generic/barrier.h +8 -8
@@ -128,10 +128,10 @@ do { \#ifndef __smp_load_acquire#define __smp_load_acquire (p) \ ({ \ - typeof(*p) ___p1 = READ_ONCE(*p); \ + __unqual_scalar_typeof(*p) ___p1 = READ_ONCE(*p); \ compiletime_assert_atomic_type(*p); \ __smp_mb(); \ - ___p1; \ + (typeof(*p))___p1; \ })#endif
@@ -183,10 +183,10 @@ do { \#ifndef smp_load_acquire#define smp_load_acquire (p) \ ({ \ - typeof(*p) ___p1 = READ_ONCE(*p); \ + __unqual_scalar_typeof(*p) ___p1 = READ_ONCE(*p); \ compiletime_assert_atomic_type(*p); \ barrier(); \ - ___p1; \ + (typeof(*p))___p1; \ })#endif
@@ -229,14 +229,14 @@ do { \#ifndef smp_cond_load_relaxed#define smp_cond_load_relaxed (ptr, cond_expr) ({ \ typeof(ptr) __PTR = (ptr); \ - typeof(*ptr) VAL; \ + __unqual_scalar_typeof(*ptr) VAL; \ for (;;) { \ VAL = READ_ONCE(*__PTR); \ if (cond_expr) \ break; \ cpu_relax(); \ } \ - VAL; \ + (typeof(*ptr))VAL; \ })#endif
@@ -250,10 +250,10 @@ do { \*/ #ifndef smp_cond_load_acquire#define smp_cond_load_acquire (ptr, cond_expr) ({ \ - typeof(*ptr) _val; \ + __unqual_scalar_typeof(*ptr) _val; \ _val = smp_cond_load_relaxed(ptr, cond_expr); \ smp_acquire__after_ctrl_dep(); \ - _val; \ + (typeof(*ptr))_val; \ })#endif
Signature · 1 line
2.26.1.301.g55bc3eb7cb9-goog
Will Deacon
maintainerPATCH v4 10/11 arm64: barrier: Use '__unqual_scalar_typeof' for acquire/release macros
In reply to Will DeaconPassing volatile-qualified pointers to the arm64 implementations of the load-acquire/store-release macros results in a re-load from the stack and a bunch of associated stack-protector churn due to the temporary result variable inheriting the volatile semantics thanks to the use of 'typeof()'.
Define these temporary variables using 'unqual_scalar_typeof' to drop the volatile qualifier in the case that they are scalar types.
--- arch/arm64/include/asm/barrier.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-)
arch/arm64/include/asm/barrier.h +8 -8
@@ -76,8 +76,8 @@ static inline unsigned long array_index_mask_nospec(unsigned long idx,#define __smp_store_release (p, v) \ do { \ typeof(p) __p = (p); \ - union { typeof(*p) __val; char __c[1]; } __u = \ - { .__val = (__force typeof(*p)) (v) }; \ + union { __unqual_scalar_typeof(*p) __val; char __c[1]; } __u = \ + { .__val = (__force __unqual_scalar_typeof(*p)) (v) }; \ compiletime_assert_atomic_type(*p); \ kasan_check_write(__p, sizeof(*p)); \ switch (sizeof(*p)) { \
@@ -110,7 +110,7 @@ do { \#define __smp_load_acquire (p) \ ({ \ - union { typeof(*p) __val; char __c[1]; } __u; \ + union { __unqual_scalar_typeof(*p) __val; char __c[1]; } __u; \ typeof(p) __p = (p); \ compiletime_assert_atomic_type(*p); \ kasan_check_read(__p, sizeof(*p)); \
@@ -136,33 +136,33 @@ do { \ : "Q" (*__p) : "memory"); \break ; \ } \ - __u.__val; \ + (typeof(*p))__u.__val; \ })#define smp_cond_load_relaxed (ptr, cond_expr) \ ({ \ typeof(ptr) __PTR = (ptr); \ - typeof(*ptr) VAL; \ + __unqual_scalar_typeof(*ptr) VAL; \ for (;;) { \ VAL = READ_ONCE(*__PTR); \ if (cond_expr) \ break; \ __cmpwait_relaxed(__PTR, VAL); \ } \ - VAL; \ + (typeof(*ptr))VAL; \ })#define smp_cond_load_acquire (ptr, cond_expr) \ ({ \ typeof(ptr) __PTR = (ptr); \ - typeof(*ptr) VAL; \ + __unqual_scalar_typeof(*ptr) VAL; \ for (;;) { \ VAL = smp_load_acquire(__PTR); \ if (cond_expr) \ break; \ __cmpwait_relaxed(__PTR, VAL); \ } \ - VAL; \ + (typeof(*ptr))VAL; \ })#include <asm-generic/barrier.h>
Signature · 1 line
2.26.1.301.g55bc3eb7cb9-goog
Will Deacon
maintainerPATCH v4 11/11 gcov: Remove old GCC 3.4 support
In reply to Will DeaconThe kernel requires at least GCC 4.8 in order to build, and so there is no need to cater for the pre-4.7 gcov format.
Remove the obsolete code.
--- kernel/gcov/Kconfig | 24 -- kernel/gcov/Makefile | 3 +- kernel/gcov/gcc_3_4.c | 573 ------------------------------------------ 3 files changed, 1 insertion(+), 599 deletions(-) delete mode 100644 kernel/gcov/gcc_3_4.c
kernel/gcov/Kconfig +0 -24
@@ -51,28 +51,4 @@ config GCOV_PROFILE_ALL larger and run slower. Also be sure to exclude files from profiling which are not linked to the kernel image to prevent linker errors. -choice - prompt "Specify GCOV format" - depends on GCOV_KERNEL - depends on CC_IS_GCC - ---help--- - The gcov format is usually determined by the GCC version, and the - default is chosen according to your GCC version. However, there are - exceptions where format changes are integrated in lower-version GCCs. - In such a case, change this option to adjust the format used in the - kernel accordingly. - -config GCOV_FORMAT_3_4 - bool "GCC 3.4 format" - depends on GCC_VERSION < 40700 - ---help--- - Select this option to use the format defined by GCC 3.4. - -config GCOV_FORMAT_4_7 - bool "GCC 4.7 format" - ---help--- - Select this option to use the format defined by GCC 4.7. - -endchoice - endmenu
kernel/gcov/Makefile +1 -2
@@ -2,6 +2,5 @@ ccflags-y := -DSRCTREE='"$(srctree)"' -DOBJTREE='"$(objtree)"' obj-y := base.o fs.o -obj-$(CONFIG_GCOV_FORMAT_3_4) += gcc_base.o gcc_3_4.o -obj-$(CONFIG_GCOV_FORMAT_4_7) += gcc_base.o gcc_4_7.o +obj-$(CONFIG_CC_IS_GCC) += gcc_base.o gcc_4_7.o obj-$(CONFIG_CC_IS_CLANG) += clang.o
kernel/gcov/gcc_3_4.c deleted +0 -573
573 lines not on this page · raw message
Signature · 1 line
2.26.1.301.g55bc3eb7cb9-goog
Re: [PATCH v4 11/11] gcov: Remove old GCC 3.4 support
Quoted 9 lines · Will Deacon
The kernel requires at least GCC 4.8 in order to build, and so there is no need to cater for the pre-4.7 gcov format.
Remove the obsolete code.
Reviewed-by Nick Desaulniers <[email protected]>Signed-off-by Will Deacon <[email protected]>
Thanks!
Signature · 2 lines
Best Regards Masahiro Yamada
Quoted 3 lines · Will Deacon
It's me again. This is version four of the
READ_ONCE()codegen improvement patches [...]
Let's just plan on biting the bullet and do this for 5.8. I'm assuming that I'll juet get a pull request from you?
Quoted 1 line
(I'm interpreting the silence as monumental joy)
By now I think we can take that for granted.
Because "monumental joy" is certainly exactly what I felt re-reading that "unqualified scalar type" macro.
Or maybe it was just my breakfast trying to say "Hi!".
Linus
Will Deacon
maintainerQuoted 7 lines · Linus Torvalds
Quoted 3 lines · Will Deacon
It's me again. This is version four of the
READ_ONCE()codegen improvement patches [...]Let's just plan on biting the bullet and do this for 5.8. I'm assuming that I'll juet get a pull request from you?
Sure thing, thanks. I'll get it into -next along with the arm64 bits for 5.8, but I'll send it as a separate pull when the time comes. I'll also include the sparc32 changes because otherwise the build falls apart and we'll get an army of angry robots yelling at us (they seem to form the majority of the active sparc32 user base afaict).
Quoted 8 lines
Quoted 1 line
(I'm interpreting the silence as monumental joy)
By now I think we can take that for granted.
Because "monumental joy" is certainly exactly what I felt re-reading that "unqualified scalar type" macro.
Or maybe it was just my breakfast trying to say "Hi!".
Haha! It's definitely best viewed on an empty stomach, but the comment does give you ample warning.
Will
Quoted 6 lines · Will Deacon
Quoted 5 lines · Linus Torvalds
Quoted 3 lines · Will Deacon
It's me again. This is version four of the
READ_ONCE()codegen improvement patches [...]
Quoted 8 lines
Quoted 2 lines
Let's just plan on biting the bullet and do this for 5.8. I'm assuming that I'll juet get a pull request from you?
Sure thing, thanks. I'll get it into -next along with the arm64 bits for 5.8, but I'll send it as a separate pull when the time comes. I'll also include the sparc32 changes because otherwise the build falls apart and we'll get an army of angry robots yelling at us (they seem to form the majority of the active sparc32 user base afaict).
So I'm obviously all for these patches; do note however that it collides most mighty with the KCSAN stuff, which I believe is still pending.
Will Deacon
maintainerQuoted 10 lines · Peter Zijlstra
Quoted 9 lines · Will Deacon
Quoted 7 lines · Linus Torvalds
Quoted 3 lines · Will Deacon
It's me again. This is version four of the
READ_ONCE()codegen improvement patches [...]Let's just plan on biting the bullet and do this for 5.8. I'm assuming that I'll juet get a pull request from you?
Quoted 8 lines
Quoted 5 lines
Sure thing, thanks. I'll get it into -next along with the arm64 bits for 5.8, but I'll send it as a separate pull when the time comes. I'll also include the sparc32 changes because otherwise the build falls apart and we'll get an army of angry robots yelling at us (they seem to form the majority of the active sparc32 user base afaict).
So I'm obviously all for these patches; do note however that it collides most mighty with the KCSAN stuff, which I believe is still pending.
That stuff has been pending for the last two releases afaict :/
Anyway, I'm happy to either provide a branch with this series on, or do the merge myself, or send this again based on something else. What works best for you? The only thing I'd obviously like to avoid is tightly coupling this to KCSAN if there's a chance of it missing the merge window again.
Cheers,
Will
Will Deacon
maintainerHi Peter,
[+KCSAN folks]
Quoted 20 lines · Will Deacon
Quoted 10 lines · Peter Zijlstra
Quoted 9 lines · Will Deacon
Quoted 7 lines · Linus Torvalds
Quoted 3 lines · Will Deacon
It's me again. This is version four of the
READ_ONCE()codegen improvement patches [...]Let's just plan on biting the bullet and do this for 5.8. I'm assuming that I'll juet get a pull request from you?
Quoted 8 lines
Quoted 5 lines
Sure thing, thanks. I'll get it into -next along with the arm64 bits for 5.8, but I'll send it as a separate pull when the time comes. I'll also include the sparc32 changes because otherwise the build falls apart and we'll get an army of angry robots yelling at us (they seem to form the majority of the active sparc32 user base afaict).
So I'm obviously all for these patches; do note however that it collides most mighty with the KCSAN stuff, which I believe is still pending.
Quoted 7 lines
That stuff has been pending for the last two releases afaict :/
Anyway, I'm happy to either provide a branch with this series on, or do the merge myself, or send this again based on something else. What works best for you? The only thing I'd obviously like to avoid is tightly coupling this to KCSAN if there's a chance of it missing the merge window again.
FWIW, I had a go at rebasing onto linux-next, just to get an idea for how bad it is. It's fairly bad, and I don't think it's fair to inflict it on sfr. I've included the interesting part of the resulting compiler.h below for you and the KCSAN crowd to take a look at (yes, there's room for subsequent cleanup, but I was focussing on the conflict resolution for now).
So, I think the best bet is either for my changes to go into -tip on top of the KCSAN stuff, or for the KCSAN stuff to be dropped from -next (it's been there since at least January). Do you know if they are definitely supposed to be going in for 5.8?
Any other ideas?
Cheers,
Will
--->8
/*
* Prevent the compiler from merging or refetching reads or writes. The
* compiler is also forbidden from reordering successive instances of
* READ_ONCE and WRITE_ONCE, but only when the compiler is aware of some
* particular ordering. One way to make the compiler aware of ordering is to
* put the two invocations of READ_ONCE or WRITE_ONCE in different C
* statements.
*
* These two macros will also work on aggregate data types like structs or
* unions.
*
* Their two major use cases are: (1) Mediating communication between
* process-level code and irq/NMI handlers, all running on the same CPU,
* and (2) Ensuring that the compiler does not fold, spindle, or otherwise
* mutilate accesses that either do not require ordering or that interact
* with an explicit memory barrier or atomic instruction that provides the
* required ordering.
*/
#include <asm/barrier.h>
#include <linux/kasan-checks.h>
#include <linux/kcsan-checks.h>
/*
* Use __READ_ONCE() instead of READ_ONCE() if you do not require any
* atomicity or dependency ordering guarantees. Note that this may result
* in tears!
*/
#define __READ_ONCE(x) (*(const volatile __unqual_scalar_typeof(x) *)&(x))
#define __READ_ONCE_SCALAR(x) \
({ \
typeof(x) *__xp = &(x); \
kcsan_check_atomic_read(__xp, sizeof(*__xp)); \
kcsan_disable_current(); \
({ \
__unqual_scalar_typeof(x) __x = __READ_ONCE(*__xp); \
kcsan_enable_current(); \
smp_read_barrier_depends(); \
(typeof(x))__x; \
}); \
})#define READ_ONCE(x) \
({ \
compiletime_assert_rwonce_type(x); \
__READ_ONCE_SCALAR(x); \
})#define __WRITE_ONCE(x, val) \
do { \
*(volatile typeof(x) *)&(x) = (val); \
} while (0)#define __WRITE_ONCE_SCALAR(x, val) \
do { \
typeof(x) *__xp = &(x); \
kcsan_check_atomic_write(__xp, sizeof(*__xp)); \
kcsan_disable_current(); \
__WRITE_ONCE(*__xp, val); \
kcsan_enable_current(); \
} while (0)#define WRITE_ONCE(x, val) \
do { \
compiletime_assert_rwonce_type(x); \
__WRITE_ONCE_SCALAR(x, val); \
} while (0)#ifdef CONFIG_KASAN
/*
* We can't declare function 'inline' because __no_sanitize_address conflicts
* with inlining. Attempt to inline it may cause a build failure.
* https://gcc.gnu.org/bugzilla/show_bug.cgi?id=67368
* '__maybe_unused' allows us to avoid defined-but-not-used warnings. */
# define __no_kasan_or_inline __no_sanitize_address notrace __maybe_unused # define __no_sanitize_or_inline __no_kasan_or_inline #else # define __no_kasan_or_inline __always_inline #endif
#define __no_kcsan __no_sanitize_thread
#ifdef __SANITIZE_THREAD__
/*
* Rely on __SANITIZE_THREAD__ instead of CONFIG_KCSAN, to avoid not inlining in
* compilation units where instrumentation is disabled. The attribute 'noinline'
* is required for older compilers, where implicit inlining of very small
* functions renders __no_sanitize_thread ineffective.
*/
# define __no_kcsan_or_inline __no_kcsan noinline notrace __maybe_unused # define __no_sanitize_or_inline __no_kcsan_or_inline #else # define __no_kcsan_or_inline __always_inline #endif
#ifndef __no_sanitize_or_inline
#define __no_sanitize_or_inline __always_inline
#endif
static __no_sanitize_or_inline
unsigned long __read_once_word_nocheck(const void *addr)
{
return __READ_ONCE(*(unsigned long *)addr);
}/*
* Use READ_ONCE_NOCHECK() instead of READ_ONCE() if you need to load a
* word from memory atomically but without telling KASAN/KCSAN. This is
* usually used by unwinding code when walking the stack of a running process. */
#define READ_ONCE_NOCHECK(x) \
({ \
unsigned long __x = __read_once_word_nocheck(&(x)); \
smp_read_barrier_depends(); \
__x; \
})static __no_kasan_or_inline
unsigned long read_word_at_a_time(const void *addr)
{
kasan_check_read(addr, 1);
return *(unsigned long *)addr;
}/**
* data_race - mark an expression as containing intentional data races *
* This data_race() macro is useful for situations in which data races
* should be forgiven. One example is diagnostic code that accesses
* shared variables but is not a part of the core synchronization design. *
* This macro *does not* affect normal code generation, but is a hint
* to tooling that data races here are to be ignored.
*/
#define data_race(expr) \
({ \
typeof(({ expr; })) __val; \
kcsan_disable_current(); \
__val = ({ expr; }); \
kcsan_enable_current(); \
__val; \
})Quoted 34 lines · Will Deacon
Hi Peter,
[+KCSAN folks]
Quoted 20 lines · Will Deacon
Quoted 10 lines · Peter Zijlstra
Quoted 9 lines · Will Deacon
Quoted 7 lines · Linus Torvalds
Quoted 3 lines · Will Deacon
It's me again. This is version four of the
READ_ONCE()codegen improvement patches [...]Let's just plan on biting the bullet and do this for 5.8. I'm assuming that I'll juet get a pull request from you?
Quoted 8 lines
Quoted 5 lines
Sure thing, thanks. I'll get it into -next along with the arm64 bits for 5.8, but I'll send it as a separate pull when the time comes. I'll also include the sparc32 changes because otherwise the build falls apart and we'll get an army of angry robots yelling at us (they seem to form the majority of the active sparc32 user base afaict).
So I'm obviously all for these patches; do note however that it collides most mighty with the KCSAN stuff, which I believe is still pending.
Quoted 7 lines
That stuff has been pending for the last two releases afaict :/
Anyway, I'm happy to either provide a branch with this series on, or do the merge myself, or send this again based on something else. What works best for you? The only thing I'd obviously like to avoid is tightly coupling this to KCSAN if there's a chance of it missing the merge window again.
Quoted 5 lines
FWIW, I had a go at rebasing onto linux-next, just to get an idea for how bad it is. It's fairly bad, and I don't think it's fair to inflict it on sfr. I've included the interesting part of the resulting
compiler.hbelow for you and the KCSAN crowd to take a look at (yes, there's room for subsequent cleanup, but I was focussing on the conflict resolution for now).
Thanks for the heads up. From what I can tell, your proposed change may work fine for KCSAN. However, I've had trouble compiling this:
1. kcsan_disable_current() / kcsan_enable_current() do not work as-is, because READ_ONCE/WRITE_ONCE seems to be used from compilation units where the KCSAN runtime is not available (e.g. arch/x86/entry/vdso/Makefile which had to set KCSAN_SANITIZE := n for that reason).
2. Some new uaccess whitelist entries were needed.
I think this is what's needed:
https://lkml.kernel.org/r/[email protected]
With that you can change the calls to __kcsan_disable_current() / __kcsan_enable_current() for READ_ONCE() and WRITE_ONCE(). After that, I was able to compile, and my test suite passed.
Thanks,
-- Marco
Quoted 123 lines
So, I think the best bet is either for my changes to go into -tip on top of the KCSAN stuff, or for the KCSAN stuff to be dropped from -next (it's been there since at least January). Do you know if they are definitely supposed to be going in for 5.8?
Any other ideas?
Cheers,
Will
--->8
/*
* Prevent the compiler from merging or refetching reads or writes. The
* compiler is also forbidden from reordering successive instances of
*READ_ONCEandWRITE_ONCE, but only when the compiler is aware of some
* particular ordering. One way to make the compiler aware of ordering is to
* put the two invocations ofREAD_ONCEorWRITE_ONCEin different C
* statements.
*
* These two macros will also work on aggregate data types like structs or
* unions.
*
* Their two major use cases are: (1) Mediating communication between
* process-level code and irq/NMI handlers, all running on the same CPU,
* and (2) Ensuring that the compiler does not fold, spindle, or otherwise
* mutilate accesses that either do not require ordering or that interact
* with an explicit memory barrier or atomic instruction that provides the
* required ordering.
*/
#include <asm/barrier.h>
#include <linux/kasan-checks.h>
#include <linux/kcsan-checks.h>/*
* Use__READ_ONCE()instead ofREAD_ONCE()if you do not require any
* atomicity or dependency ordering guarantees. Note that this may result
* in tears!
*/
#define__READ_ONCE(x)(*(const volatile__unqual_scalar_typeof(x)*)&(x))#define __READ_ONCE_SCALAR(x) \ ({ \ typeof(x) *__xp = &(x); \ kcsan_check_atomic_read(__xp, sizeof(*__xp)); \ kcsan_disable_current(); \ ({ \ __unqual_scalar_typeof(x) __x = __READ_ONCE(*__xp); \ kcsan_enable_current(); \ smp_read_barrier_depends(); \ (typeof(x))__x; \ }); \})
#define READ_ONCE(x) \ ({ \ compiletime_assert_rwonce_type(x); \ __READ_ONCE_SCALAR(x); \})
#define __WRITE_ONCE(x, val) \ do { \ *(volatile typeof(x) *)&(x) = (val); \} while (0)
#define __WRITE_ONCE_SCALAR(x, val) \ do { \ typeof(x) *__xp = &(x); \ kcsan_check_atomic_write(__xp, sizeof(*__xp)); \ kcsan_disable_current(); \ __WRITE_ONCE(*__xp, val); \ kcsan_enable_current(); \} while (0)
#define WRITE_ONCE(x, val) \ do { \ compiletime_assert_rwonce_type(x); \ __WRITE_ONCE_SCALAR(x, val); \} while (0)
#ifdef
CONFIG_KASAN
/*
* We can't declare function 'inline' because__no_sanitize_addressconflicts
* with inlining. Attempt to inline it may cause a build failure.
* https://gcc.gnu.org/bugzilla/show_bug.cgi?id=67368
* '__maybe_unused' allows us to avoid defined-but-not-used warnings. */# define __no_kasan_or_inline __no_sanitize_address notrace __maybe_unused # define __no_sanitize_or_inline __no_kasan_or_inline #else # define __no_kasan_or_inline __always_inline #endif#define
__no_kcsan__no_sanitize_thread
#ifdef__SANITIZE_THREAD__
/*
* Rely on__SANITIZE_THREAD__instead ofCONFIG_KCSAN, to avoid not inlining in
* compilation units where instrumentation is disabled. The attribute 'noinline'
* is required for older compilers, where implicit inlining of very small
* functions renders__no_sanitize_threadineffective.
*/# define __no_kcsan_or_inline __no_kcsan noinline notrace __maybe_unused # define __no_sanitize_or_inline __no_kcsan_or_inline #else # define __no_kcsan_or_inline __always_inline #endif#ifndef
__no_sanitize_or_inline
#define__no_sanitize_or_inline__always_inline
#endifstatic
__no_sanitize_or_inline
unsigned long__read_once_word_nocheck(const void *addr){ return __READ_ONCE(*(unsigned long *)addr); }/*
* UseREAD_ONCE_NOCHECK()instead ofREAD_ONCE()if you need to load a
* word from memory atomically but without telling KASAN/KCSAN. This is
* usually used by unwinding code when walking the stack of a running process. */
Quoted 6 lines
#define READ_ONCE_NOCHECK(x) \ ({ \ unsigned long __x = __read_once_word_nocheck(&(x)); \ smp_read_barrier_depends(); \ __x; \})
Unconditionally loading an unsigned long doesn't seem right, and might also result in OOB reads.
Quoted 25 lines
static
__no_kasan_or_inline
unsigned longread_word_at_a_time(const void *addr){ kasan_check_read(addr, 1); return *(unsigned long *)addr; }/**
*data_race- mark an expression as containing intentional data races *
* Thisdata_race()macro is useful for situations in which data races
* should be forgiven. One example is diagnostic code that accesses
* shared variables but is not a part of the core synchronization design. *
* This macro *does not* affect normal code generation, but is a hint
* to tooling that data races here are to be ignored.
*/#define data_race(expr) \ ({ \ typeof(({ expr; })) __val; \ kcsan_disable_current(); \ __val = ({ expr; }); \ kcsan_enable_current(); \ __val; \ })
Will Deacon
maintainerQuoted 30 lines · Marco Elver
Quoted 13 lines · Will Deacon
Quoted 11 lines · Will Deacon
Quoted 2 lines · Peter Zijlstra
So I'm obviously all for these patches; do note however that it collides most mighty with the KCSAN stuff, which I believe is still pending.
That stuff has been pending for the last two releases afaict :/
Anyway, I'm happy to either provide a branch with this series on, or do the merge myself, or send this again based on something else. What works best for you? The only thing I'd obviously like to avoid is tightly coupling this to KCSAN if there's a chance of it missing the merge window again.
Quoted 5 lines
FWIW, I had a go at rebasing onto linux-next, just to get an idea for how bad it is. It's fairly bad, and I don't think it's fair to inflict it on sfr. I've included the interesting part of the resulting
compiler.hbelow for you and the KCSAN crowd to take a look at (yes, there's room for subsequent cleanup, but I was focussing on the conflict resolution for now).Thanks for the heads up. From what I can tell, your proposed change may work fine for KCSAN. However, I've had trouble compiling this:
1.
kcsan_disable_current()/kcsan_enable_current()do not work as-is, because READ_ONCE/WRITE_ONCE seems to be used from compilation units where the KCSAN runtime is not available (e.g.arch/x86/entry/vdso/Makefilewhich had to setKCSAN_SANITIZE:= n for that reason).
2. Some new uaccess whitelist entries were needed.
Quoted 6 lines
I think this is what's needed:
https://lkml.kernel.org/r/[email protected]With that you can change the calls to
__kcsan_disable_current()/__kcsan_enable_current()forREAD_ONCE()andWRITE_ONCE(). After that, I was able to compile, and my test suite passed.
Brill, thanks Marco! I'll take a look at your other patches, but I'm pretty stuck until we've figured out the merging plan for all of this.
Will