Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b0984c43 authored by Maciej W. Rozycki's avatar Maciej W. Rozycki Committed by Ralf Baechle
Browse files

MIPS: Fix microMIPS LL/SC immediate offsets



In the microMIPS encoding some memory access instructions have their
immediate offset reduced to 12 bits only.  That does not match the GCC
`R' constraint we use in some places to satisfy the requirement,
resulting in build failures like this:

{standard input}: Assembler messages:
{standard input}:720: Error: macro used $at after ".set noat"
{standard input}:720: Warning: macro instruction expanded into multiple instructions

Fix the problem by defining a macro, `GCC_OFF12_ASM', that expands to
the right constraint depending on whether microMIPS or standard MIPS
code is produced.  Also apply the fix to where `m' is used as in the
worst case this change does nothing, e.g. where the pointer was already
in a register such as a function argument and no further offset was
requested, and in the best case it avoids an extraneous sequence of up
to two instructions to load the high 20 bits of the address in the LL/SC
loop.  This reduces the risk of lock contention that is the higher the
more instructions there are in the critical section between LL and SC.

Strictly speaking we could just bulk-replace `R' with `ZC' as the latter
constraint adjusts automatically depending on the ISA selected.
However it was only introduced with GCC 4.9 and we keep supporing older
compilers for the standard MIPS configuration, hence the slightly more
complicated approach I chose.

The choice of a zero-argument function-like rather than an object-like
macro was made so that it does not look like a function call taking the
C expression used for the constraint as an argument.  This is so as not
to confuse the reader or formatting checkers like `checkpatch.pl' and
follows previous practice.

Signed-off-by: default avatarMaciej W. Rozycki <macro@codesourcery.com>
Signed-off-by: default avatarSteven J. Hill <Steven.Hill@imgtec.com>
Cc: linux-mips@linux-mips.org
Patchwork: https://patchwork.linux-mips.org/patch/8482/


Signed-off-by: default avatarRalf Baechle <ralf@linux-mips.org>
parent aec711d5
Loading
Loading
Loading
Loading
+24 −15
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
#include <linux/irqflags.h>
#include <linux/types.h>
#include <asm/barrier.h>
#include <asm/compiler.h>
#include <asm/cpu-features.h>
#include <asm/cmpxchg.h>
#include <asm/war.h>
@@ -53,7 +54,7 @@ static __inline__ void atomic_##op(int i, atomic_t * v) \
		"	sc	%0, %1					\n"	\
		"	beqzl	%0, 1b					\n"	\
		"	.set	mips0					\n"	\
		: "=&r" (temp), "+m" (v->counter)				\
		: "=&r" (temp), "+" GCC_OFF12_ASM() (v->counter)		\
		: "Ir" (i));							\
	} else if (kernel_uses_llsc) {						\
		int temp;							\
@@ -65,7 +66,7 @@ static __inline__ void atomic_##op(int i, atomic_t * v) \
			"	" #asm_op " %0, %2			\n"	\
			"	sc	%0, %1				\n"	\
			"	.set	mips0				\n"	\
			: "=&r" (temp), "+m" (v->counter)			\
			: "=&r" (temp), "+" GCC_OFF12_ASM() (v->counter)	\
			: "Ir" (i));						\
		} while (unlikely(!temp));					\
	} else {								\
@@ -95,7 +96,8 @@ static __inline__ int atomic_##op##_return(int i, atomic_t * v) \
		"	beqzl	%0, 1b					\n"	\
		"	" #asm_op " %0, %1, %3				\n"	\
		"	.set	mips0					\n"	\
		: "=&r" (result), "=&r" (temp), "+m" (v->counter)		\
		: "=&r" (result), "=&r" (temp),					\
		  "+" GCC_OFF12_ASM() (v->counter)				\
		: "Ir" (i));							\
	} else if (kernel_uses_llsc) {						\
		int temp;							\
@@ -107,7 +109,8 @@ static __inline__ int atomic_##op##_return(int i, atomic_t * v) \
			"	" #asm_op " %0, %1, %3			\n"	\
			"	sc	%0, %2				\n"	\
			"	.set	mips0				\n"	\
			: "=&r" (result), "=&r" (temp), "+m" (v->counter)	\
			: "=&r" (result), "=&r" (temp),				\
			  "+" GCC_OFF12_ASM() (v->counter)			\
			: "Ir" (i));						\
		} while (unlikely(!result));					\
										\
@@ -167,8 +170,9 @@ static __inline__ int atomic_sub_if_positive(int i, atomic_t * v)
		"	.set	reorder					\n"
		"1:							\n"
		"	.set	mips0					\n"
		: "=&r" (result), "=&r" (temp), "+m" (v->counter)
		: "Ir" (i), "m" (v->counter)
		: "=&r" (result), "=&r" (temp),
		  "+" GCC_OFF12_ASM() (v->counter)
		: "Ir" (i), GCC_OFF12_ASM() (v->counter)
		: "memory");
	} else if (kernel_uses_llsc) {
		int temp;
@@ -185,7 +189,8 @@ static __inline__ int atomic_sub_if_positive(int i, atomic_t * v)
		"	.set	reorder					\n"
		"1:							\n"
		"	.set	mips0					\n"
		: "=&r" (result), "=&r" (temp), "+m" (v->counter)
		: "=&r" (result), "=&r" (temp),
		  "+" GCC_OFF12_ASM() (v->counter)
		: "Ir" (i));
	} else {
		unsigned long flags;
@@ -328,7 +333,7 @@ static __inline__ void atomic64_##op(long i, atomic64_t * v) \
		"	scd	%0, %1					\n"	\
		"	beqzl	%0, 1b					\n"	\
		"	.set	mips0					\n"	\
		: "=&r" (temp), "+m" (v->counter)				\
		: "=&r" (temp), "+" GCC_OFF12_ASM() (v->counter)		\
		: "Ir" (i));							\
	} else if (kernel_uses_llsc) {						\
		long temp;							\
@@ -340,7 +345,7 @@ static __inline__ void atomic64_##op(long i, atomic64_t * v) \
			"	" #asm_op " %0, %2			\n"	\
			"	scd	%0, %1				\n"	\
			"	.set	mips0				\n"	\
			: "=&r" (temp), "+m" (v->counter)			\
			: "=&r" (temp), "+" GCC_OFF12_ASM() (v->counter)	\
			: "Ir" (i));						\
		} while (unlikely(!temp));					\
	} else {								\
@@ -370,7 +375,8 @@ static __inline__ long atomic64_##op##_return(long i, atomic64_t * v) \
		"	beqzl	%0, 1b					\n"	\
		"	" #asm_op " %0, %1, %3				\n"	\
		"	.set	mips0					\n"	\
		: "=&r" (result), "=&r" (temp), "+m" (v->counter)		\
		: "=&r" (result), "=&r" (temp),					\
		  "+" GCC_OFF12_ASM() (v->counter)				\
		: "Ir" (i));							\
	} else if (kernel_uses_llsc) {						\
		long temp;							\
@@ -382,8 +388,9 @@ static __inline__ long atomic64_##op##_return(long i, atomic64_t * v) \
			"	" #asm_op " %0, %1, %3			\n"	\
			"	scd	%0, %2				\n"	\
			"	.set	mips0				\n"	\
			: "=&r" (result), "=&r" (temp), "=m" (v->counter)	\
			: "Ir" (i), "m" (v->counter)				\
			: "=&r" (result), "=&r" (temp),				\
			  "=" GCC_OFF12_ASM() (v->counter)			\
			: "Ir" (i), GCC_OFF12_ASM() (v->counter)		\
			: "memory");						\
		} while (unlikely(!result));					\
										\
@@ -443,8 +450,9 @@ static __inline__ long atomic64_sub_if_positive(long i, atomic64_t * v)
		"	.set	reorder					\n"
		"1:							\n"
		"	.set	mips0					\n"
		: "=&r" (result), "=&r" (temp), "=m" (v->counter)
		: "Ir" (i), "m" (v->counter)
		: "=&r" (result), "=&r" (temp),
		  "=" GCC_OFF12_ASM() (v->counter)
		: "Ir" (i), GCC_OFF12_ASM() (v->counter)
		: "memory");
	} else if (kernel_uses_llsc) {
		long temp;
@@ -461,7 +469,8 @@ static __inline__ long atomic64_sub_if_positive(long i, atomic64_t * v)
		"	.set	reorder					\n"
		"1:							\n"
		"	.set	mips0					\n"
		: "=&r" (result), "=&r" (temp), "+m" (v->counter)
		: "=&r" (result), "=&r" (temp),
		  "+" GCC_OFF12_ASM() (v->counter)
		: "Ir" (i));
	} else {
		unsigned long flags;
+18 −17
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
#include <linux/types.h>
#include <asm/barrier.h>
#include <asm/byteorder.h>		/* sigh ... */
#include <asm/compiler.h>
#include <asm/cpu-features.h>
#include <asm/sgidefs.h>
#include <asm/war.h>
@@ -78,8 +79,8 @@ static inline void set_bit(unsigned long nr, volatile unsigned long *addr)
		"	" __SC	"%0, %1					\n"
		"	beqzl	%0, 1b					\n"
		"	.set	mips0					\n"
		: "=&r" (temp), "=m" (*m)
		: "ir" (1UL << bit), "m" (*m));
		: "=&r" (temp), "=" GCC_OFF12_ASM() (*m)
		: "ir" (1UL << bit), GCC_OFF12_ASM() (*m));
#ifdef CONFIG_CPU_MIPSR2
	} else if (kernel_uses_llsc && __builtin_constant_p(bit)) {
		do {
@@ -87,7 +88,7 @@ static inline void set_bit(unsigned long nr, volatile unsigned long *addr)
			"	" __LL "%0, %1		# set_bit	\n"
			"	" __INS "%0, %3, %2, 1			\n"
			"	" __SC "%0, %1				\n"
			: "=&r" (temp), "+m" (*m)
			: "=&r" (temp), "+" GCC_OFF12_ASM() (*m)
			: "ir" (bit), "r" (~0));
		} while (unlikely(!temp));
#endif /* CONFIG_CPU_MIPSR2 */
@@ -99,7 +100,7 @@ static inline void set_bit(unsigned long nr, volatile unsigned long *addr)
			"	or	%0, %2				\n"
			"	" __SC	"%0, %1				\n"
			"	.set	mips0				\n"
			: "=&r" (temp), "+m" (*m)
			: "=&r" (temp), "+" GCC_OFF12_ASM() (*m)
			: "ir" (1UL << bit));
		} while (unlikely(!temp));
	} else
@@ -130,7 +131,7 @@ static inline void clear_bit(unsigned long nr, volatile unsigned long *addr)
		"	" __SC "%0, %1					\n"
		"	beqzl	%0, 1b					\n"
		"	.set	mips0					\n"
		: "=&r" (temp), "+m" (*m)
		: "=&r" (temp), "+" GCC_OFF12_ASM() (*m)
		: "ir" (~(1UL << bit)));
#ifdef CONFIG_CPU_MIPSR2
	} else if (kernel_uses_llsc && __builtin_constant_p(bit)) {
@@ -139,7 +140,7 @@ static inline void clear_bit(unsigned long nr, volatile unsigned long *addr)
			"	" __LL "%0, %1		# clear_bit	\n"
			"	" __INS "%0, $0, %2, 1			\n"
			"	" __SC "%0, %1				\n"
			: "=&r" (temp), "+m" (*m)
			: "=&r" (temp), "+" GCC_OFF12_ASM() (*m)
			: "ir" (bit));
		} while (unlikely(!temp));
#endif /* CONFIG_CPU_MIPSR2 */
@@ -151,7 +152,7 @@ static inline void clear_bit(unsigned long nr, volatile unsigned long *addr)
			"	and	%0, %2				\n"
			"	" __SC "%0, %1				\n"
			"	.set	mips0				\n"
			: "=&r" (temp), "+m" (*m)
			: "=&r" (temp), "+" GCC_OFF12_ASM() (*m)
			: "ir" (~(1UL << bit)));
		} while (unlikely(!temp));
	} else
@@ -196,7 +197,7 @@ static inline void change_bit(unsigned long nr, volatile unsigned long *addr)
		"	" __SC	"%0, %1				\n"
		"	beqzl	%0, 1b				\n"
		"	.set	mips0				\n"
		: "=&r" (temp), "+m" (*m)
		: "=&r" (temp), "+" GCC_OFF12_ASM() (*m)
		: "ir" (1UL << bit));
	} else if (kernel_uses_llsc) {
		unsigned long *m = ((unsigned long *) addr) + (nr >> SZLONG_LOG);
@@ -209,7 +210,7 @@ static inline void change_bit(unsigned long nr, volatile unsigned long *addr)
			"	xor	%0, %2				\n"
			"	" __SC	"%0, %1				\n"
			"	.set	mips0				\n"
			: "=&r" (temp), "+m" (*m)
			: "=&r" (temp), "+" GCC_OFF12_ASM() (*m)
			: "ir" (1UL << bit));
		} while (unlikely(!temp));
	} else
@@ -244,7 +245,7 @@ static inline int test_and_set_bit(unsigned long nr,
		"	beqzl	%2, 1b					\n"
		"	and	%2, %0, %3				\n"
		"	.set	mips0					\n"
		: "=&r" (temp), "+m" (*m), "=&r" (res)
		: "=&r" (temp), "+" GCC_OFF12_ASM() (*m), "=&r" (res)
		: "r" (1UL << bit)
		: "memory");
	} else if (kernel_uses_llsc) {
@@ -258,7 +259,7 @@ static inline int test_and_set_bit(unsigned long nr,
			"	or	%2, %0, %3			\n"
			"	" __SC	"%2, %1				\n"
			"	.set	mips0				\n"
			: "=&r" (temp), "+m" (*m), "=&r" (res)
			: "=&r" (temp), "+" GCC_OFF12_ASM() (*m), "=&r" (res)
			: "r" (1UL << bit)
			: "memory");
		} while (unlikely(!res));
@@ -312,7 +313,7 @@ static inline int test_and_set_bit_lock(unsigned long nr,
			"	or	%2, %0, %3			\n"
			"	" __SC	"%2, %1				\n"
			"	.set	mips0				\n"
			: "=&r" (temp), "+m" (*m), "=&r" (res)
			: "=&r" (temp), "+" GCC_OFF12_ASM() (*m), "=&r" (res)
			: "r" (1UL << bit)
			: "memory");
		} while (unlikely(!res));
@@ -354,7 +355,7 @@ static inline int test_and_clear_bit(unsigned long nr,
		"	beqzl	%2, 1b					\n"
		"	and	%2, %0, %3				\n"
		"	.set	mips0					\n"
		: "=&r" (temp), "+m" (*m), "=&r" (res)
		: "=&r" (temp), "+" GCC_OFF12_ASM() (*m), "=&r" (res)
		: "r" (1UL << bit)
		: "memory");
#ifdef CONFIG_CPU_MIPSR2
@@ -368,7 +369,7 @@ static inline int test_and_clear_bit(unsigned long nr,
			"	" __EXT "%2, %0, %3, 1			\n"
			"	" __INS "%0, $0, %3, 1			\n"
			"	" __SC	"%0, %1				\n"
			: "=&r" (temp), "+m" (*m), "=&r" (res)
			: "=&r" (temp), "+" GCC_OFF12_ASM() (*m), "=&r" (res)
			: "ir" (bit)
			: "memory");
		} while (unlikely(!temp));
@@ -385,7 +386,7 @@ static inline int test_and_clear_bit(unsigned long nr,
			"	xor	%2, %3				\n"
			"	" __SC	"%2, %1				\n"
			"	.set	mips0				\n"
			: "=&r" (temp), "+m" (*m), "=&r" (res)
			: "=&r" (temp), "+" GCC_OFF12_ASM() (*m), "=&r" (res)
			: "r" (1UL << bit)
			: "memory");
		} while (unlikely(!res));
@@ -427,7 +428,7 @@ static inline int test_and_change_bit(unsigned long nr,
		"	beqzl	%2, 1b					\n"
		"	and	%2, %0, %3				\n"
		"	.set	mips0					\n"
		: "=&r" (temp), "+m" (*m), "=&r" (res)
		: "=&r" (temp), "+" GCC_OFF12_ASM() (*m), "=&r" (res)
		: "r" (1UL << bit)
		: "memory");
	} else if (kernel_uses_llsc) {
@@ -441,7 +442,7 @@ static inline int test_and_change_bit(unsigned long nr,
			"	xor	%2, %0, %3			\n"
			"	" __SC	"\t%2, %1			\n"
			"	.set	mips0				\n"
			: "=&r" (temp), "+m" (*m), "=&r" (res)
			: "=&r" (temp), "+" GCC_OFF12_ASM() (*m), "=&r" (res)
			: "r" (1UL << bit)
			: "memory");
		} while (unlikely(!res));
+15 −12
Original line number Diff line number Diff line
@@ -10,6 +10,7 @@

#include <linux/bug.h>
#include <linux/irqflags.h>
#include <asm/compiler.h>
#include <asm/war.h>

static inline unsigned long __xchg_u32(volatile int * m, unsigned int val)
@@ -30,8 +31,8 @@ static inline unsigned long __xchg_u32(volatile int * m, unsigned int val)
		"	sc	%2, %1					\n"
		"	beqzl	%2, 1b					\n"
		"	.set	mips0					\n"
		: "=&r" (retval), "=m" (*m), "=&r" (dummy)
		: "R" (*m), "Jr" (val)
		: "=&r" (retval), "=" GCC_OFF12_ASM() (*m), "=&r" (dummy)
		: GCC_OFF12_ASM() (*m), "Jr" (val)
		: "memory");
	} else if (kernel_uses_llsc) {
		unsigned long dummy;
@@ -45,8 +46,9 @@ static inline unsigned long __xchg_u32(volatile int * m, unsigned int val)
			"	.set	arch=r4000			\n"
			"	sc	%2, %1				\n"
			"	.set	mips0				\n"
			: "=&r" (retval), "=m" (*m), "=&r" (dummy)
			: "R" (*m), "Jr" (val)
			: "=&r" (retval), "=" GCC_OFF12_ASM() (*m),
			  "=&r" (dummy)
			: GCC_OFF12_ASM() (*m), "Jr" (val)
			: "memory");
		} while (unlikely(!dummy));
	} else {
@@ -80,8 +82,8 @@ static inline __u64 __xchg_u64(volatile __u64 * m, __u64 val)
		"	scd	%2, %1					\n"
		"	beqzl	%2, 1b					\n"
		"	.set	mips0					\n"
		: "=&r" (retval), "=m" (*m), "=&r" (dummy)
		: "R" (*m), "Jr" (val)
		: "=&r" (retval), "=" GCC_OFF12_ASM() (*m), "=&r" (dummy)
		: GCC_OFF12_ASM() (*m), "Jr" (val)
		: "memory");
	} else if (kernel_uses_llsc) {
		unsigned long dummy;
@@ -93,8 +95,9 @@ static inline __u64 __xchg_u64(volatile __u64 * m, __u64 val)
			"	move	%2, %z4				\n"
			"	scd	%2, %1				\n"
			"	.set	mips0				\n"
			: "=&r" (retval), "=m" (*m), "=&r" (dummy)
			: "R" (*m), "Jr" (val)
			: "=&r" (retval), "=" GCC_OFF12_ASM() (*m),
			  "=&r" (dummy)
			: GCC_OFF12_ASM() (*m), "Jr" (val)
			: "memory");
		} while (unlikely(!dummy));
	} else {
@@ -155,8 +158,8 @@ static inline unsigned long __xchg(unsigned long x, volatile void * ptr, int siz
		"	beqzl	$1, 1b				\n"	\
		"2:						\n"	\
		"	.set	pop				\n"	\
		: "=&r" (__ret), "=R" (*m)				\
		: "R" (*m), "Jr" (old), "Jr" (new)			\
		: "=&r" (__ret), "=" GCC_OFF12_ASM() (*m)		\
		: GCC_OFF12_ASM() (*m), "Jr" (old), "Jr" (new)		\
		: "memory");						\
	} else if (kernel_uses_llsc) {					\
		__asm__ __volatile__(					\
@@ -172,8 +175,8 @@ static inline unsigned long __xchg(unsigned long x, volatile void * ptr, int siz
		"	beqz	$1, 1b				\n"	\
		"	.set	pop				\n"	\
		"2:						\n"	\
		: "=&r" (__ret), "=R" (*m)				\
		: "R" (*m), "Jr" (old), "Jr" (new)			\
		: "=&r" (__ret), "=" GCC_OFF12_ASM() (*m)		\
		: GCC_OFF12_ASM() (*m), "Jr" (old), "Jr" (new)		\
		: "memory");						\
	} else {							\
		unsigned long __flags;					\
+8 −0
Original line number Diff line number Diff line
@@ -16,4 +16,12 @@
#define GCC_REG_ACCUM "accum"
#endif

#ifndef CONFIG_CPU_MICROMIPS
#define GCC_OFF12_ASM() "R"
#elif __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9)
#define GCC_OFF12_ASM() "ZC"
#else
#error "microMIPS compilation unsupported with GCC older than 4.9"
#endif

#endif /* _ASM_COMPILER_H */
+4 −2
Original line number Diff line number Diff line
#ifndef ASM_EDAC_H
#define ASM_EDAC_H

#include <asm/compiler.h>

/* ECC atomic, DMA, SMP and interrupt safe scrub function */

static inline void atomic_scrub(void *va, u32 size)
@@ -24,8 +26,8 @@ static inline void atomic_scrub(void *va, u32 size)
		"	sc	%0, %1					\n"
		"	beqz	%0, 1b					\n"
		"	.set	mips0					\n"
		: "=&r" (temp), "=m" (*virt_addr)
		: "m" (*virt_addr));
		: "=&r" (temp), "=" GCC_OFF12_ASM() (*virt_addr)
		: GCC_OFF12_ASM() (*virt_addr));

		virt_addr++;
	}
Loading