[PATCH] atomics: fix atomic64_{read_acquire,set_release} fallbacks

Mark Rutland posted 1 patch 4 years, 4 months ago
There is a newer version of this series
include/linux/atomic/atomic-arch-fallback.h | 38 ++++++++++++++++++---
scripts/atomic/fallbacks/read_acquire       | 11 +++++-
scripts/atomic/fallbacks/set_release        |  7 +++-
3 files changed, 49 insertions(+), 7 deletions(-)
[PATCH] atomics: fix atomic64_{read_acquire,set_release} fallbacks
Posted by Mark Rutland 4 years, 4 months ago
Arnd reports that on 32-bit architectures, the fallbacks for
atomic64_read_acquire and atomic64_set_release() are broken as they use
smp_load_acquire() and smp_store_release() respectively, which do not
work on types larger than the native word size.

Since those contain compiletime_assert_atomic_type(), any attempt to use
those fallbacks will result in a build-time error. e.g. with the
following added to arch/arm/kernel/setup.c:

| void test_atomic64(atomic64_t *v)
| {
|        atomic64_set_release(v, 5);
|        atomic64_read_acquire(v);
| }

The compiler will complain as follows:

| In file included from <command-line>:
| In function 'arch_atomic64_set_release',
|     inlined from 'test_atomic64' at ./include/linux/atomic/atomic-instrumented.h:669:2:
| ././include/linux/compiler_types.h:346:38: error: call to '__compiletime_assert_9' declared with attribute error: Need native word sized stores/loads for atomicity.
|   346 |  _compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__)
|       |                                      ^
| ././include/linux/compiler_types.h:327:4: note: in definition of macro '__compiletime_assert'
|   327 |    prefix ## suffix();    \
|       |    ^~~~~~
| ././include/linux/compiler_types.h:346:2: note: in expansion of macro '_compiletime_assert'
|   346 |  _compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__)
|       |  ^~~~~~~~~~~~~~~~~~~
| ././include/linux/compiler_types.h:349:2: note: in expansion of macro 'compiletime_assert'
|   349 |  compiletime_assert(__native_word(t),    \
|       |  ^~~~~~~~~~~~~~~~~~
| ./include/asm-generic/barrier.h:133:2: note: in expansion of macro 'compiletime_assert_atomic_type'
|   133 |  compiletime_assert_atomic_type(*p);    \
|       |  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| ./include/asm-generic/barrier.h:164:55: note: in expansion of macro '__smp_store_release'
|   164 | #define smp_store_release(p, v) do { kcsan_release(); __smp_store_release(p, v); } while (0)
|       |                                                       ^~~~~~~~~~~~~~~~~~~
| ./include/linux/atomic/atomic-arch-fallback.h:1270:2: note: in expansion of macro 'smp_store_release'
|  1270 |  smp_store_release(&(v)->counter, i);
|       |  ^~~~~~~~~~~~~~~~~
| make[2]: *** [scripts/Makefile.build:288: arch/arm/kernel/setup.o] Error 1
| make[1]: *** [scripts/Makefile.build:550: arch/arm/kernel] Error 2
| make: *** [Makefile:1831: arch/arm] Error 2

Fix this by only using smp_load_acquire() and smp_store_release() for
native atomic types, and otherwise falling back to the regular barriers
necessary for acquire/release semantics, as we do in the more generic
acquire and release fallbacks.

For the example above this works as expected on 32-bit, e.g.

| <test_atomic64>:
|         dmb     ish
|         mov     r2, #5
|         mov     r3, #0
|         strd    r2, [r0]
|         ldrd    r2, [r0]
|         dmb     ish
|         bx      lr

... and also on 64-bit, e.g.

| <test_atomic64>:
|         bti     c
|         paciasp
|         mov     x1, #0x5
|         stlr    x1, [x0]
|         ldar    x0, [x0]
|         autiasp
|         ret

Reported-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Cc: Boqun Feng <boqun.feng@gmail.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Will Deacon <will@kernel.org>
---
 include/linux/atomic/atomic-arch-fallback.h | 38 ++++++++++++++++++---
 scripts/atomic/fallbacks/read_acquire       | 11 +++++-
 scripts/atomic/fallbacks/set_release        |  7 +++-
 3 files changed, 49 insertions(+), 7 deletions(-)

diff --git a/include/linux/atomic/atomic-arch-fallback.h b/include/linux/atomic/atomic-arch-fallback.h
index a3dba31df01e..8dfb03317f2a 100644
--- a/include/linux/atomic/atomic-arch-fallback.h
+++ b/include/linux/atomic/atomic-arch-fallback.h
@@ -151,7 +151,16 @@
 static __always_inline int
 arch_atomic_read_acquire(const atomic_t *v)
 {
-	return smp_load_acquire(&(v)->counter);
+	int ret;
+
+	if (__native_word(atomic_t)) {
+		ret = smp_load_acquire(&(v)->counter);
+	} else {
+		ret = arch_atomic_read(v);
+		__atomic_acquire_fence();
+	}
+
+	return ret;
 }
 #define arch_atomic_read_acquire arch_atomic_read_acquire
 #endif
@@ -160,7 +169,12 @@ arch_atomic_read_acquire(const atomic_t *v)
 static __always_inline void
 arch_atomic_set_release(atomic_t *v, int i)
 {
-	smp_store_release(&(v)->counter, i);
+	if (__native_word(atomic_t)) {
+		smp_store_release(&(v)->counter, i);
+	} else {
+		__atomic_release_fence();
+		arch_atomic_set(v, i);
+	}
 }
 #define arch_atomic_set_release arch_atomic_set_release
 #endif
@@ -1258,7 +1272,16 @@ arch_atomic_dec_if_positive(atomic_t *v)
 static __always_inline s64
 arch_atomic64_read_acquire(const atomic64_t *v)
 {
-	return smp_load_acquire(&(v)->counter);
+	s64 ret;
+
+	if (__native_word(atomic64_t)) {
+		ret = smp_load_acquire(&(v)->counter);
+	} else {
+		ret = arch_atomic_read(v);
+		__atomic_acquire_fence();
+	}
+
+	return ret;
 }
 #define arch_atomic64_read_acquire arch_atomic64_read_acquire
 #endif
@@ -1267,7 +1290,12 @@ arch_atomic64_read_acquire(const atomic64_t *v)
 static __always_inline void
 arch_atomic64_set_release(atomic64_t *v, s64 i)
 {
-	smp_store_release(&(v)->counter, i);
+	if (__native_word(atomic64_t)) {
+		smp_store_release(&(v)->counter, i);
+	} else {
+		__atomic_release_fence();
+		arch_atomic_set(v, i);
+	}
 }
 #define arch_atomic64_set_release arch_atomic64_set_release
 #endif
@@ -2358,4 +2386,4 @@ arch_atomic64_dec_if_positive(atomic64_t *v)
 #endif
 
 #endif /* _LINUX_ATOMIC_FALLBACK_H */
-// cca554917d7ea73d5e3e7397dd70c484cad9b2c4
+// ba488b6398359cb776d457971f30cef78d0d36dc
diff --git a/scripts/atomic/fallbacks/read_acquire b/scripts/atomic/fallbacks/read_acquire
index 803ba7561076..7b8b87143c0c 100755
--- a/scripts/atomic/fallbacks/read_acquire
+++ b/scripts/atomic/fallbacks/read_acquire
@@ -2,6 +2,15 @@ cat <<EOF
 static __always_inline ${ret}
 arch_${atomic}_read_acquire(const ${atomic}_t *v)
 {
-	return smp_load_acquire(&(v)->counter);
+	${int} ret;
+
+	if (__native_word(${atomic}_t)) {
+		ret = smp_load_acquire(&(v)->counter);
+	} else {
+		ret = arch_atomic_read(v);
+		__atomic_acquire_fence();
+	}
+
+	return ret;
 }
 EOF
diff --git a/scripts/atomic/fallbacks/set_release b/scripts/atomic/fallbacks/set_release
index 86ede759f24e..5f692db3ce29 100755
--- a/scripts/atomic/fallbacks/set_release
+++ b/scripts/atomic/fallbacks/set_release
@@ -2,6 +2,11 @@ cat <<EOF
 static __always_inline void
 arch_${atomic}_set_release(${atomic}_t *v, ${int} i)
 {
-	smp_store_release(&(v)->counter, i);
+	if (__native_word(${atomic}_t)) {
+		smp_store_release(&(v)->counter, i);
+	} else {
+		__atomic_release_fence();
+		arch_atomic_set(v, i);
+	}
 }
 EOF
-- 
2.30.2

Re: [PATCH] atomics: fix atomic64_{read_acquire,set_release} fallbacks
Posted by kernel test robot 4 years, 4 months ago
Hi Mark,

I love your patch! Yet something to improve:

[auto build test ERROR on linux/master]
[also build test ERROR on soc/for-next linus/master v5.17-rc2 next-20220203]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/0day-ci/linux/commits/Mark-Rutland/atomics-fix-atomic64_-read_acquire-set_release-fallbacks/20220203-224037
base:   https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git 2c271fe77d52a0555161926c232cd5bc07178b39
config: um-i386_defconfig (https://download.01.org/0day-ci/archive/20220203/202202032312.7YDqjour-lkp@intel.com/config)
compiler: gcc-9 (Debian 9.3.0-22) 9.3.0
reproduce (this is a W=1 build):
        # https://github.com/0day-ci/linux/commit/a4f59a24830e45bbd94645639aae50597b148388
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review Mark-Rutland/atomics-fix-atomic64_-read_acquire-set_release-fallbacks/20220203-224037
        git checkout a4f59a24830e45bbd94645639aae50597b148388
        # save the config file to linux build tree
        mkdir build_dir
        make W=1 O=build_dir ARCH=um SUBARCH=i386 prepare

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All errors (new ones prefixed by >>):

   arch/x86/um/user-offsets.c:18:6: warning: no previous prototype for 'foo' [-Wmissing-prototypes]
      18 | void foo(void)
         |      ^~~
   In file included from include/linux/atomic.h:80,
                    from include/linux/rcupdate.h:25,
                    from include/linux/rculist.h:11,
                    from include/linux/pid.h:5,
                    from include/linux/sched.h:14,
                    from arch/x86/um/shared/sysdep/kernel-offsets.h:3,
                    from arch/um/kernel/asm-offsets.c:1:
   include/linux/atomic/atomic-arch-fallback.h: In function 'arch_atomic64_read_acquire':
>> include/linux/atomic/atomic-arch-fallback.h:1280:26: error: passing argument 1 of 'arch_atomic_read' from incompatible pointer type [-Werror=incompatible-pointer-types]
    1280 |   ret = arch_atomic_read(v);
         |                          ^
         |                          |
         |                          const atomic64_t * {aka const struct <anonymous> *}
   In file included from include/linux/atomic.h:7,
                    from include/linux/rcupdate.h:25,
                    from include/linux/rculist.h:11,
                    from include/linux/pid.h:5,
                    from include/linux/sched.h:14,
                    from arch/x86/um/shared/sysdep/kernel-offsets.h:3,
                    from arch/um/kernel/asm-offsets.c:1:
   arch/x86/include/asm/atomic.h:23:61: note: expected 'const atomic_t *' {aka 'const struct <anonymous> *'} but argument is of type 'const atomic64_t *' {aka 'const struct <anonymous> *'}
      23 | static __always_inline int arch_atomic_read(const atomic_t *v)
         |                                             ~~~~~~~~~~~~~~~~^
   In file included from include/linux/atomic.h:80,
                    from include/linux/rcupdate.h:25,
                    from include/linux/rculist.h:11,
                    from include/linux/pid.h:5,
                    from include/linux/sched.h:14,
                    from arch/x86/um/shared/sysdep/kernel-offsets.h:3,
                    from arch/um/kernel/asm-offsets.c:1:
   include/linux/atomic/atomic-arch-fallback.h: In function 'arch_atomic64_set_release':
>> include/linux/atomic/atomic-arch-fallback.h:1297:19: error: passing argument 1 of 'arch_atomic_set' from incompatible pointer type [-Werror=incompatible-pointer-types]
    1297 |   arch_atomic_set(v, i);
         |                   ^
         |                   |
         |                   atomic64_t * {aka struct <anonymous> *}
   In file included from include/linux/atomic.h:7,
                    from include/linux/rcupdate.h:25,
                    from include/linux/rculist.h:11,
                    from include/linux/pid.h:5,
                    from include/linux/sched.h:14,
                    from arch/x86/um/shared/sysdep/kernel-offsets.h:3,
                    from arch/um/kernel/asm-offsets.c:1:
   arch/x86/include/asm/atomic.h:39:55: note: expected 'atomic_t *' {aka 'struct <anonymous> *'} but argument is of type 'atomic64_t *' {aka 'struct <anonymous> *'}
      39 | static __always_inline void arch_atomic_set(atomic_t *v, int i)
         |                                             ~~~~~~~~~~^
   In file included from arch/um/kernel/asm-offsets.c:1:
   arch/x86/um/shared/sysdep/kernel-offsets.h: At top level:
   arch/x86/um/shared/sysdep/kernel-offsets.h:9:6: warning: no previous prototype for 'foo' [-Wmissing-prototypes]
       9 | void foo(void)
         |      ^~~
   cc1: some warnings being treated as errors
   make[2]: *** [scripts/Makefile.build:121: arch/um/kernel/asm-offsets.s] Error 1
   make[2]: Target '__build' not remade because of errors.
   make[1]: *** [Makefile:1191: prepare0] Error 2
   make[1]: Target 'prepare' not remade because of errors.
   make: *** [Makefile:219: __sub-make] Error 2
   make: Target 'prepare' not remade because of errors.


vim +/arch_atomic_read +1280 include/linux/atomic/atomic-arch-fallback.h

  1270	
  1271	#ifndef arch_atomic64_read_acquire
  1272	static __always_inline s64
  1273	arch_atomic64_read_acquire(const atomic64_t *v)
  1274	{
  1275		s64 ret;
  1276	
  1277		if (__native_word(atomic64_t)) {
  1278			ret = smp_load_acquire(&(v)->counter);
  1279		} else {
> 1280			ret = arch_atomic_read(v);
  1281			__atomic_acquire_fence();
  1282		}
  1283	
  1284		return ret;
  1285	}
  1286	#define arch_atomic64_read_acquire arch_atomic64_read_acquire
  1287	#endif
  1288	
  1289	#ifndef arch_atomic64_set_release
  1290	static __always_inline void
  1291	arch_atomic64_set_release(atomic64_t *v, s64 i)
  1292	{
  1293		if (__native_word(atomic64_t)) {
  1294			smp_store_release(&(v)->counter, i);
  1295		} else {
  1296			__atomic_release_fence();
> 1297			arch_atomic_set(v, i);
  1298		}
  1299	}
  1300	#define arch_atomic64_set_release arch_atomic64_set_release
  1301	#endif
  1302	

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org