asm-generic: avoid __generic_cmpxchg_local warnings
Subsystems (2)
PATCH 1/1 asm-generic: avoid __generic_cmpxchg_local warnings
From: Arnd Bergmann <[email protected]>
Code that passes a 32-bit constant into cmpxchg() produces a harmless sparse warning because of the truncation in the branch that is not taken:
fs/erofs/zdata.c: note: in included file (through /home/arnd/arm-soc/arch/arm/include/asm/cmpxchg.h, /home/arnd/arm-soc/arch/arm/include/asm/atomic.h, /home/arnd/arm-soc/include/linux/atomic.h, ...):include/asm-generic/cmpxchg-local.h:29:33: warning: cast truncates bits from constant value (5f0ecafe becomes fe)include/asm-generic/cmpxchg-local.h:33:34: warning: cast truncates bits from constant value (5f0ecafe becomes cafe)include/asm-generic/cmpxchg-local.h:29:33: warning: cast truncates bits from constant value (5f0ecafe becomes fe)include/asm-generic/cmpxchg-local.h:30:42: warning: cast truncates bits from constant value (5f0edead becomes ad)include/asm-generic/cmpxchg-local.h:33:34: warning: cast truncates bits from constant value (5f0ecafe becomes cafe)include/asm-generic/cmpxchg-local.h:34:44: warning: cast truncates bits from constant value (5f0edead becomes dead)
This was reported as a regression to Matt's recent __generic_cmpxchg_local patch, though this patch only added more warnings on top of the ones that were already there.
Rewording the truncation to use an explicit bitmask instead of a cast to a smaller type avoids the warning but otherwise leaves the code unchanged.
I had another look at why the cast is even needed for atomic_cmpxchg(), and as Matt describes the problem here is that atomic_t contains a signed 'int', but cmpxchg() takes an 'unsigned long' argument, and converting between the two leads to a 64-bit sign-extension of negative 32-bit atomics.
I checked the other implementations of arch_cmpxchg() and did not find any others that run into the same problem as __generic_cmpxchg_local(), but it's easy to be on the safe side here and always convert the signed int into an unsigned int when calling arch_cmpxchg(), as this will work even when any of the arch_cmpxchg() implementations run into the same problem.
--- include/asm-generic/atomic.h | 4 ++-- include/asm-generic/cmpxchg-local.h | 12 ++++++------ include/asm-generic/cmpxchg.h | 6 +++--- 3 files changed, 11 insertions(+), 11 deletions(-)
include/asm-generic/atomic.h +2 -2
@@ -130,7 +130,7 @@ ATOMIC_OP(xor, ^)#define arch_atomic_read (v) READ_ONCE((v)->counter)#define arch_atomic_set (v, i) WRITE_ONCE(((v)->counter), (i)) -#define arch_atomic_xchg (ptr, v) (arch_xchg(&(ptr)->counter, (v))) -#define arch_atomic_cmpxchg (v, old, new) (arch_cmpxchg(&((v)->counter), (old), (new))) +#define arch_atomic_xchg (ptr, v) (arch_xchg(&(ptr)->counter, (u32)(v))) +#define arch_atomic_cmpxchg (v, old, new) (arch_cmpxchg(&((v)->counter), (u32)(old), (u32)(new)))#endif /* __ASM_GENERIC_ATOMIC_H */
include/asm-generic/cmpxchg-local.h +6 -6
@@ -26,16 +26,16 @@ static inline unsigned long __generic_cmpxchg_local(volatile void *ptr,raw_local_irq_save (flags);switch (size) {case 1 : prev = *(u8 *)ptr; -if (prev == (u8 )old) - *(u8 *)ptr = (u8 )new; +if (prev == (old &0xffu )) + *(u8 *)ptr = (new &0xffu );break ;case 2 : prev = *(u16 *)ptr; -if (prev == (u16 )old) - *(u16 *)ptr = (u16 )new; +if (prev == (old &0xffffu )) + *(u16 *)ptr = (new &0xffffu );break ;case 4 : prev = *(u32 *)ptr; -if (prev == (u32 )old) - *(u32 *)ptr = (u32 )new; +if (prev == (old &0xffffffffffu )) + *(u32 *)ptr = (new &0xffffffffu );break ;case 8 : prev = *(u64 *)ptr;if (prev == old)
include/asm-generic/cmpxchg.h +3 -3
@@ -32,7 +32,7 @@ unsigned long __generic_xchg(unsigned long x, volatile void *ptr, int size)#else local_irq_save (flags); ret = *(volatile u8 *)ptr; - *(volatile u8 *)ptr = x; + *(volatile u8 *)ptr = (x &0xffu );local_irq_restore (flags);return ret;#endif /* __xchg_u8 */
@@ -43,7 +43,7 @@ unsigned long __generic_xchg(unsigned long x, volatile void *ptr, int size)#else local_irq_save (flags); ret = *(volatile u16 *)ptr; - *(volatile u16 *)ptr = x; + *(volatile u16 *)ptr = (x &0xffffu );local_irq_restore (flags);return ret;#endif /* __xchg_u16 */
@@ -54,7 +54,7 @@ unsigned long __generic_xchg(unsigned long x, volatile void *ptr, int size)#else local_irq_save (flags); ret = *(volatile u32 *)ptr; - *(volatile u32 *)ptr = x; + *(volatile u32 *)ptr = (x &0xffffffffu );local_irq_restore (flags);return ret;#endif /* __xchg_u32 */
Signature · 1 line
2.39.2
...
Quoted 5 lines
I had another look at why the cast is even needed for
atomic_cmpxchg(), and as Matt describes the problem here is thatatomic_tcontains a signed 'int', butcmpxchg()takes an 'unsigned long' argument, and converting between the two leads to a 64-bit sign-extension of negative 32-bit atomics.
How about:
signed_var + 0u + 0ull;
Converts 32bit signed to 64bit unsigned without sign extension. Compiler will throw it all away if not needed.
David
-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK Registration No: 1397386 (Wales)