All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC PATCH v1 1/5] locking/atomic: Implement atomic_fetch_and_or
@ 2021-07-28 11:48 Rui Wang
  2021-07-28 12:14 ` Boqun Feng
                   ` (3 more replies)
  0 siblings, 4 replies; 17+ messages in thread
From: Rui Wang @ 2021-07-28 11:48 UTC (permalink / raw)
  To: Peter Zijlstra, Ingo Molnar, Will Deacon, Arnd Bergmann
  Cc: Waiman Long, Boqun Feng, Guo Ren, linux-arch, Rui Wang, hev,
	Xuefeng Li, Huacai Chen, Jiaxun Yang, Huacai Chen

From: wangrui <wangrui@loongson.cn>

This patch introduce a new atomic primitive 'and_or', It may be have three
types of implemeations:

 * The generic implementation is based on arch_cmpxchg.
 * The hardware supports atomic 'and_or' of single instruction.
 * The hardware supports LL/SC style atomic operations:

   1:  ll  v1, mem
       and t1, v1, arg1
       or  t1, t1, arg2
       sc  t1, mem
       beq t1, 0, 1b

Now that all the architectures have implemented it.

Signed-by-off: Rui Wang <wangrui@loongson.cn>
Signed-by-off: hev <r@hev.cc>
---
 arch/alpha/include/asm/atomic.h           | 27 ++++++++++++
 arch/arc/include/asm/atomic.h             | 52 +++++++++++++++++++++++
 arch/arm/include/asm/atomic.h             | 44 +++++++++++++++++++
 arch/arm64/include/asm/atomic.h           | 16 +++++++
 arch/arm64/include/asm/atomic_ll_sc.h     | 33 ++++++++++++++
 arch/hexagon/include/asm/atomic.h         | 24 +++++++++++
 arch/ia64/include/asm/atomic.h            | 18 ++++++++
 arch/m68k/include/asm/atomic.h            | 36 ++++++++++++++++
 arch/mips/include/asm/atomic.h            | 41 ++++++++++++++++++
 arch/openrisc/include/asm/atomic.h        | 22 ++++++++++
 arch/parisc/include/asm/atomic.h          | 20 +++++++++
 arch/powerpc/include/asm/atomic.h         | 26 ++++++++++++
 arch/riscv/include/asm/atomic.h           | 25 +++++++++++
 arch/s390/include/asm/atomic.h            |  2 +
 arch/s390/include/asm/atomic_ops.h        | 25 +++++++++++
 arch/sparc/include/asm/atomic_32.h        |  2 +
 arch/sparc/lib/atomic32.c                 | 17 ++++++++
 arch/x86/include/asm/atomic.h             | 10 +++++
 arch/xtensa/include/asm/atomic.h          | 49 +++++++++++++++++++++
 include/asm-generic/atomic-instrumented.h | 28 ++++++++++++
 include/asm-generic/atomic.h              | 29 +++++++++++++
 include/linux/atomic-arch-fallback.h      | 42 ++++++++++++++++++
 22 files changed, 588 insertions(+)

diff --git a/arch/alpha/include/asm/atomic.h b/arch/alpha/include/asm/atomic.h
index f2861a43a61e..deb05ac292b8 100644
--- a/arch/alpha/include/asm/atomic.h
+++ b/arch/alpha/include/asm/atomic.h
@@ -91,6 +91,25 @@ static inline int arch_atomic_fetch_##op##_relaxed(int i, atomic_t *v)	\
 	return result;							\
 }
 
+#define ATOMIC_FETCH_OP2(op, asm_op1, asm_op2)					\
+static inline int arch_atomic_fetch_##op##_relaxed(int i, int j, atomic_t *v)	\
+{									\
+	long temp, result;						\
+	__asm__ __volatile__(						\
+	"1:	ldl_l %2,%1\n"						\
+	"	" #asm_op1 " %2,%3,%0\n"				\
+	"	" #asm_op2 " %0,%4,%0\n"				\
+	"	stl_c %0,%1\n"						\
+	"	beq %0,2f\n"						\
+	".subsection 2\n"						\
+	"2:	br 1b\n"						\
+	".previous"							\
+	:"=&r" (temp), "=m" (v->counter), "=&r" (result)		\
+	:"Ir" (i), "Ir" (j), "m" (v->counter) : "memory");		\
+	smp_mb();							\
+	return result;							\
+}
+
 #define ATOMIC64_OP(op, asm_op)						\
 static __inline__ void arch_atomic64_##op(s64 i, atomic64_t * v)	\
 {									\
@@ -182,10 +201,17 @@ ATOMIC_OPS(andnot, bic)
 ATOMIC_OPS(or, bis)
 ATOMIC_OPS(xor, xor)
 
+#undef ATOMIC_OPS
+#define ATOMIC_OPS(op, asm_op1, asm_op2)			\
+	ATOMIC_FETCH_OP2(op, asm_op1, asm_op2)			\
+
+ATOMIC_OPS(and_or, and, bis)
+
 #define arch_atomic_fetch_and_relaxed		arch_atomic_fetch_and_relaxed
 #define arch_atomic_fetch_andnot_relaxed	arch_atomic_fetch_andnot_relaxed
 #define arch_atomic_fetch_or_relaxed		arch_atomic_fetch_or_relaxed
 #define arch_atomic_fetch_xor_relaxed		arch_atomic_fetch_xor_relaxed
+#define arch_atomic_fetch_and_or_relaxed	arch_atomic_fetch_and_or_relaxed
 
 #define arch_atomic64_fetch_and_relaxed		arch_atomic64_fetch_and_relaxed
 #define arch_atomic64_fetch_andnot_relaxed	arch_atomic64_fetch_andnot_relaxed
@@ -197,6 +223,7 @@ ATOMIC_OPS(xor, xor)
 #undef ATOMIC64_OP_RETURN
 #undef ATOMIC64_OP
 #undef ATOMIC_FETCH_OP
+#undef ATOMIC_FETCH_OP2
 #undef ATOMIC_OP_RETURN
 #undef ATOMIC_OP
 
diff --git a/arch/arc/include/asm/atomic.h b/arch/arc/include/asm/atomic.h
index 7a36d79b5b2f..1aa9e0f396d7 100644
--- a/arch/arc/include/asm/atomic.h
+++ b/arch/arc/include/asm/atomic.h
@@ -89,6 +89,35 @@ static inline int arch_atomic_fetch_##op(int i, atomic_t *v)		\
 	return orig;							\
 }
 
+#define ATOMIC_FETCH_OP2(op, c_op1, c_op2, asm_op1, asm_op2)		\
+static inline int arch_atomic_fetch_##op(int i, int j, atomic_t *v)	\
+{									\
+	unsigned int val, orig;						\
+									\
+	/*								\
+	 * Explicit full memory barrier needed before/after as		\
+	 * LLOCK/SCOND themselves don't provide any such semantics	\
+	 */								\
+	smp_mb();							\
+									\
+	__asm__ __volatile__(						\
+	"1:	llock   %[orig], [%[ctr]]		\n"		\
+	"	" #asm_op1 " %[val], %[orig], %[i]	\n"		\
+	"	" #asm_op2 " %[val], %[val], %[j]	\n"		\
+	"	scond   %[val], [%[ctr]]		\n"		\
+	"	bnz     1b				\n"		\
+	: [val]	"=&r"	(val),						\
+	  [orig] "=&r" (orig)						\
+	: [ctr]	"r"	(&v->counter),					\
+	  [i]	"ir"	(i),						\
+	  [j]	"ir"	(j),						\
+	: "cc");							\
+									\
+	smp_mb();							\
+									\
+	return orig;							\
+}
+
 #else	/* !CONFIG_ARC_HAS_LLSC */
 
 #ifndef CONFIG_SMP
@@ -170,6 +199,23 @@ static inline int arch_atomic_fetch_##op(int i, atomic_t *v)		\
 	return orig;							\
 }
 
+#define ATOMIC_FETCH_OP2(op, c_op1, c_op2, asm_op1, asm_op2)		\
+static inline int arch_atomic_fetch_##op(int i, int j, atomic_t *v)	\
+{									\
+	unsigned long flags;						\
+	unsigned long orig;						\
+									\
+	/*								\
+	 * spin lock/unlock provides the needed smp_mb() before/after	\
+	 */								\
+	atomic_ops_lock(flags);						\
+	orig = v->counter;						\
+	v->counter = (orig c_op1 i) c_op2 j;				\
+	atomic_ops_unlock(flags);					\
+									\
+	return orig;							\
+}
+
 #endif /* !CONFIG_ARC_HAS_LLSC */
 
 #define ATOMIC_OPS(op, c_op, asm_op)					\
@@ -190,6 +236,12 @@ ATOMIC_OPS(andnot, &= ~, bic)
 ATOMIC_OPS(or, |=, or)
 ATOMIC_OPS(xor, ^=, xor)
 
+#undef ATOMIC_OPS
+#define ATOMIC_OPS(op, c_op1, c_op2, asm_op1, asm_op2)			\
+	ATOMIC_FETCH_OP2(op, c_op1, c_op2, asm_op1, asm_op2)
+
+ATOMIC_OPS(and_or, &, |, and, or)
+
 #define arch_atomic_andnot		arch_atomic_andnot
 #define arch_atomic_fetch_andnot	arch_atomic_fetch_andnot
 
diff --git a/arch/arm/include/asm/atomic.h b/arch/arm/include/asm/atomic.h
index db8512d9a918..faddbc183ced 100644
--- a/arch/arm/include/asm/atomic.h
+++ b/arch/arm/include/asm/atomic.h
@@ -93,6 +93,28 @@ static inline int arch_atomic_fetch_##op##_relaxed(int i, atomic_t *v)	\
 	return result;							\
 }
 
+#define ATOMIC_FETCH_OP2(op, c_op1, c_op2, asm_op1, asm_op2)			\
+static inline int arch_atomic_fetch_##op##_relaxed(int i, int j, atomic_t *v)	\
+{									\
+	unsigned long tmp;						\
+	int result, val;						\
+									\
+	prefetchw(&v->counter);						\
+									\
+	__asm__ __volatile__("@ atomic_fetch_" #op "\n"			\
+"1:	ldrex	%0, [%4]\n"						\
+"	" #asm_op1 "	%1, %0, %5\n"					\
+"	" #asm_op2 "	%1, %1, %6\n"					\
+"	strex	%2, %1, [%4]\n"						\
+"	teq	%2, #0\n"						\
+"	bne	1b"							\
+	: "=&r" (result), "=&r" (val), "=&r" (tmp), "+Qo" (v->counter)	\
+	: "r" (&v->counter), "Ir" (i), "Ir" (j)				\
+	: "cc");							\
+									\
+	return result;							\
+}
+
 #define arch_atomic_add_return_relaxed		arch_atomic_add_return_relaxed
 #define arch_atomic_sub_return_relaxed		arch_atomic_sub_return_relaxed
 #define arch_atomic_fetch_add_relaxed		arch_atomic_fetch_add_relaxed
@@ -102,6 +124,7 @@ static inline int arch_atomic_fetch_##op##_relaxed(int i, atomic_t *v)	\
 #define arch_atomic_fetch_andnot_relaxed	arch_atomic_fetch_andnot_relaxed
 #define arch_atomic_fetch_or_relaxed		arch_atomic_fetch_or_relaxed
 #define arch_atomic_fetch_xor_relaxed		arch_atomic_fetch_xor_relaxed
+#define arch_atomic_fetch_and_or_relaxed	arch_atomic_fetch_and_or_relaxed
 
 static inline int arch_atomic_cmpxchg_relaxed(atomic_t *ptr, int old, int new)
 {
@@ -197,6 +220,20 @@ static inline int arch_atomic_fetch_##op(int i, atomic_t *v)		\
 	return val;							\
 }
 
+#define ATOMIC_FETCH_OP2(op, c_op1, c_op2, asm_op1, asm_op2)		\
+static inline int arch_atomic_fetch_##op(int i, int j, atomic_t *v)	\
+{									\
+	unsigned long flags;						\
+	int val;							\
+									\
+	raw_local_irq_save(flags);					\
+	val = v->counter;						\
+	v->counter = (val c_op1 i) c_op2 j;				\
+	raw_local_irq_restore(flags);					\
+									\
+	return val;							\
+}
+
 static inline int arch_atomic_cmpxchg(atomic_t *v, int old, int new)
 {
 	int ret;
@@ -235,8 +272,15 @@ ATOMIC_OPS(andnot, &= ~, bic)
 ATOMIC_OPS(or,  |=, orr)
 ATOMIC_OPS(xor, ^=, eor)
 
+#undef ATOMIC_OPS
+#define ATOMIC_OPS(op, c_op1, c_op2, asm_op1, asm_op2)		\
+	ATOMIC_FETCH_OP2(op, c_op1, c_op2, asm_op1, asm_op2)
+
+ATOMIC_OPS(and_or, &, |, and, orr)
+
 #undef ATOMIC_OPS
 #undef ATOMIC_FETCH_OP
+#undef ATOMIC_FETCH_OP2
 #undef ATOMIC_OP_RETURN
 #undef ATOMIC_OP
 
diff --git a/arch/arm64/include/asm/atomic.h b/arch/arm64/include/asm/atomic.h
index c9979273d389..3f1cdd3e2ef9 100644
--- a/arch/arm64/include/asm/atomic.h
+++ b/arch/arm64/include/asm/atomic.h
@@ -43,6 +43,12 @@ static __always_inline int arch_##op##name(int i, atomic_t *v)		\
 	ATOMIC_FETCH_OP(_release, op)					\
 	ATOMIC_FETCH_OP(        , op)
 
+#define ATOMIC_FETCH_OP2(name, op)					\
+static __always_inline int arch_##op##name(int i, int j, atomic_t *v)	\
+{									\
+	return __ll_sc_##op##name(i, j, v);				\
+}
+
 ATOMIC_FETCH_OPS(atomic_fetch_andnot)
 ATOMIC_FETCH_OPS(atomic_fetch_or)
 ATOMIC_FETCH_OPS(atomic_fetch_xor)
@@ -52,7 +58,17 @@ ATOMIC_FETCH_OPS(atomic_fetch_sub)
 ATOMIC_FETCH_OPS(atomic_add_return)
 ATOMIC_FETCH_OPS(atomic_sub_return)
 
+#undef ATOMIC_FETCH_OPS
+#define ATOMIC_FETCH_OPS(op)						\
+	ATOMIC_FETCH_OP2(_relaxed, op)					\
+	ATOMIC_FETCH_OP2(_acquire, op)					\
+	ATOMIC_FETCH_OP2(_release, op)					\
+	ATOMIC_FETCH_OP2(        , op)
+
+ATOMIC_FETCH_OPS(atomic_fetch_and_or)
+
 #undef ATOMIC_FETCH_OP
+#undef ATOMIC_FETCH_OP2
 #undef ATOMIC_FETCH_OPS
 
 #define ATOMIC64_OP(op)							\
diff --git a/arch/arm64/include/asm/atomic_ll_sc.h b/arch/arm64/include/asm/atomic_ll_sc.h
index 13869b76b58c..90289c536ed6 100644
--- a/arch/arm64/include/asm/atomic_ll_sc.h
+++ b/arch/arm64/include/asm/atomic_ll_sc.h
@@ -97,6 +97,29 @@ __ll_sc_atomic_fetch_##op##name(int i, atomic_t *v)			\
 	return result;							\
 }
 
+#define ATOMIC_FETCH_OP2(name, mb, acq, rel, cl, op, asm_op1, asm_op2, cstr) \
+static inline int							\
+__ll_sc_atomic_fetch_##op##name(int i, int j, atomic_t *v)		\
+{									\
+	unsigned long tmp;						\
+	int val, result;						\
+									\
+	asm volatile("// atomic_fetch_" #op #name "\n"			\
+	__LL_SC_FALLBACK(						\
+"	prfm	pstl1strm, %3\n"					\
+"1:	ld" #acq "xr	%w0, %3\n"					\
+"	" #asm_op1 "	%w1, %w0, %w4\n"				\
+"	" #asm_op2 "	%w1, %w1, %w5\n"				\
+"	st" #rel "xr	%w2, %w1, %3\n"					\
+"	cbnz	%w2, 1b\n"						\
+"	" #mb )								\
+	: "=&r" (result), "=&r" (val), "=&r" (tmp), "+Q" (v->counter)	\
+	: __stringify(cstr) "r" (i), __stringify(cstr) "r" (j)		\
+	: cl);								\
+									\
+	return result;							\
+}
+
 #define ATOMIC_OPS(...)							\
 	ATOMIC_OP(__VA_ARGS__)						\
 	ATOMIC_OP_RETURN(        , dmb ish,  , l, "memory", __VA_ARGS__)\
@@ -129,8 +152,18 @@ ATOMIC_OPS(xor, eor, K)
  */
 ATOMIC_OPS(andnot, bic, )
 
+#undef ATOMIC_OPS
+#define ATOMIC_OPS(...)							\
+	ATOMIC_FETCH_OP2 (        , dmb ish,  , l, "memory", __VA_ARGS__)\
+	ATOMIC_FETCH_OP2 (_relaxed,        ,  ,  ,         , __VA_ARGS__)\
+	ATOMIC_FETCH_OP2 (_acquire,        , a,  , "memory", __VA_ARGS__)\
+	ATOMIC_FETCH_OP2 (_release,        ,  , l, "memory", __VA_ARGS__)
+
+ATOMIC_OPS(and_or, and, orr, K)
+
 #undef ATOMIC_OPS
 #undef ATOMIC_FETCH_OP
+#undef ATOMIC_FETCH_OP2
 #undef ATOMIC_OP_RETURN
 #undef ATOMIC_OP
 
diff --git a/arch/hexagon/include/asm/atomic.h b/arch/hexagon/include/asm/atomic.h
index 6e94f8d04146..d944e210085a 100644
--- a/arch/hexagon/include/asm/atomic.h
+++ b/arch/hexagon/include/asm/atomic.h
@@ -130,6 +130,24 @@ static inline int arch_atomic_fetch_##op(int i, atomic_t *v)		\
 	return output;							\
 }
 
+#define ATOMIC_FETCH_OP2(op1, op2)					\
+static inline int arch_atomic_fetch_##op1##_##op2(int i, int j, atomic_t *v)\
+{									\
+	int output, val;						\
+									\
+	__asm__ __volatile__ (						\
+		"1:	%0 = memw_locked(%2);\n"			\
+		"	%1 = "#op1 "(%0,%3);\n"				\
+		"	%1 = "#op2 "(%1,%4);\n"				\
+		"	memw_locked(%2,P3)=%1;\n"			\
+		"	if (!P3) jump 1b;\n"				\
+		: "=&r" (output), "=&r" (val)				\
+		: "r" (&v->counter), "r" (i), "r" (j)			\
+		: "memory", "p3"					\
+	);								\
+	return output;							\
+}
+
 #define ATOMIC_OPS(op) ATOMIC_OP(op) ATOMIC_OP_RETURN(op) ATOMIC_FETCH_OP(op)
 
 ATOMIC_OPS(add)
@@ -142,8 +160,14 @@ ATOMIC_OPS(and)
 ATOMIC_OPS(or)
 ATOMIC_OPS(xor)
 
+#undef ATOMIC_OPS
+#define ATOMIC_OPS(op1, op2) ATOMIC_FETCH_OP2(op1, op2)
+
+ATOMIC_OPS(and, or)
+
 #undef ATOMIC_OPS
 #undef ATOMIC_FETCH_OP
+#undef ATOMIC_FETCH_OP2
 #undef ATOMIC_OP_RETURN
 #undef ATOMIC_OP
 
diff --git a/arch/ia64/include/asm/atomic.h b/arch/ia64/include/asm/atomic.h
index 266c429b9137..6190108dcd53 100644
--- a/arch/ia64/include/asm/atomic.h
+++ b/arch/ia64/include/asm/atomic.h
@@ -57,6 +57,21 @@ ia64_atomic_fetch_##op (int i, atomic_t *v)				\
 	return old;							\
 }
 
+#define ATOMIC_FETCH_OP2(op, c_op1, c_op2)				\
+static __inline__ int							\
+ia64_atomic_fetch_##op (int i, int j, atomic_t *v)			\
+{									\
+	__s32 old, new;							\
+	CMPXCHG_BUGCHECK_DECL						\
+									\
+	do {								\
+		CMPXCHG_BUGCHECK(v);					\
+		old = arch_atomic_read(v);				\
+		new = (old c_op1 i) c_op2 j;				\
+	} while (ia64_cmpxchg(acq, v, old, new, sizeof(atomic_t)) != old); \
+	return old;							\
+}
+
 #define ATOMIC_OPS(op, c_op)						\
 	ATOMIC_OP(op, c_op)						\
 	ATOMIC_FETCH_OP(op, c_op)
@@ -109,6 +124,7 @@ ATOMIC_OPS(sub, -)
 ATOMIC_FETCH_OP(and, &)
 ATOMIC_FETCH_OP(or, |)
 ATOMIC_FETCH_OP(xor, ^)
+ATOMIC_FETCH_OP2(and_or, &, |)
 
 #define arch_atomic_and(i,v)	(void)ia64_atomic_fetch_and(i,v)
 #define arch_atomic_or(i,v)	(void)ia64_atomic_fetch_or(i,v)
@@ -117,9 +133,11 @@ ATOMIC_FETCH_OP(xor, ^)
 #define arch_atomic_fetch_and(i,v)	ia64_atomic_fetch_and(i,v)
 #define arch_atomic_fetch_or(i,v)	ia64_atomic_fetch_or(i,v)
 #define arch_atomic_fetch_xor(i,v)	ia64_atomic_fetch_xor(i,v)
+#define arch_atomic_fetch_and_or(i,j,v)	ia64_atomic_fetch_and_or(i,j,v)
 
 #undef ATOMIC_OPS
 #undef ATOMIC_FETCH_OP
+#undef ATOMIC_FETCH_OP2
 #undef ATOMIC_OP
 
 #define ATOMIC64_OP(op, c_op)						\
diff --git a/arch/m68k/include/asm/atomic.h b/arch/m68k/include/asm/atomic.h
index 8637bf8a2f65..480ecb6534a3 100644
--- a/arch/m68k/include/asm/atomic.h
+++ b/arch/m68k/include/asm/atomic.h
@@ -67,6 +67,22 @@ static inline int arch_atomic_fetch_##op(int i, atomic_t *v)		\
 	return tmp;							\
 }
 
+#define ATOMIC_FETCH_OP2(op, c_op1, c_op2, asm_op1, asm_op2)		\
+static inline int arch_atomic_fetch_##op(int i, int j, atomic_t *v)	\
+{									\
+	int t, tmp;							\
+									\
+	__asm__ __volatile__(						\
+			"1:	movel %2,%1\n"				\
+			"	" #asm_op1 "l %3,%1\n"			\
+			"	" #asm_op2 "l %4,%1\n"			\
+			"	casl %2,%1,%0\n"			\
+			"	jne 1b"					\
+			: "+m" (*v), "=&d" (t), "=&d" (tmp)		\
+			: "g" (i), "g" (j), "2" (arch_atomic_read(v)));	\
+	return tmp;							\
+}
+
 #else
 
 #define ATOMIC_OP_RETURN(op, c_op, asm_op)				\
@@ -96,6 +112,20 @@ static inline int arch_atomic_fetch_##op(int i, atomic_t * v)		\
 	return t;							\
 }
 
+#define ATOMIC_FETCH_OP2(op, c_op1, c_op2, asm_op1, asm_op2)		\
+static inline int arch_atomic_fetch_##op(int i, int j, atomic_t * v)	\
+{									\
+	unsigned long flags;						\
+	int t;								\
+									\
+	local_irq_save(flags);						\
+	t = v->counter;							\
+	v->counter = (t c_op1 i) c_op2 j;				\
+	local_irq_restore(flags);					\
+									\
+	return t;							\
+}
+
 #endif /* CONFIG_RMW_INSNS */
 
 #define ATOMIC_OPS(op, c_op, asm_op)					\
@@ -115,6 +145,12 @@ ATOMIC_OPS(and, &=, and)
 ATOMIC_OPS(or, |=, or)
 ATOMIC_OPS(xor, ^=, eor)
 
+#undef ATOMIC_OPS
+#define ATOMIC_OPS(op, c_op1, c_op2, asm_op1, asm_op2)			\
+	ATOMIC_FETCH_OP2(op, c_op1, c_op2, asm_op1, asm_op2)
+
+ATOMIC_OPS(and_or, &, |, and, or)
+
 #undef ATOMIC_OPS
 #undef ATOMIC_FETCH_OP
 #undef ATOMIC_OP_RETURN
diff --git a/arch/mips/include/asm/atomic.h b/arch/mips/include/asm/atomic.h
index 95e1f7f3597f..84319b1ab9b6 100644
--- a/arch/mips/include/asm/atomic.h
+++ b/arch/mips/include/asm/atomic.h
@@ -147,6 +147,39 @@ arch_##pfx##_fetch_##op##_relaxed(type i, pfx##_t * v)			\
 	return result;							\
 }
 
+#define ATOMIC_FETCH_OP2(pfx, op, type, c_op1, c_op2, asm_op1, asm_op2, ll, sc)\
+static __inline__ type							\
+arch_##pfx##_fetch_##op##_relaxed(type i, type j, pfx##_t * v)		\
+{									\
+	int temp, result;						\
+									\
+	if (!kernel_uses_llsc) {					\
+		unsigned long flags;					\
+									\
+		raw_local_irq_save(flags);				\
+		result = v->counter;					\
+		v->counter = (result c_op1 i) c_op2 j;			\
+		raw_local_irq_restore(flags);				\
+		return result;						\
+	}								\
+									\
+	__asm__ __volatile__(						\
+	"	.set	push					\n"	\
+	"	.set	" MIPS_ISA_LEVEL "			\n"	\
+	"	" __SYNC(full, loongson3_war) "			\n"	\
+	"1:	" #ll "	%0, %2		# " #pfx "_fetch_" #op "\n"	\
+	"	" #asm_op1 " %1, %0, %3				\n"	\
+	"	" #asm_op2 " %1, %1, %4				\n"	\
+	"	" #sc "	%1, %2					\n"	\
+	"\t" __SC_BEQZ "%1, 1b					\n"	\
+	"	.set	pop					\n"	\
+	: "=&r" (result), "=&r" (temp),					\
+	  "+" GCC_OFF_SMALL_ASM() (v->counter)				\
+	: "Ir" (i), "Ir" (j) : __LLSC_CLOBBER);				\
+									\
+	return result;							\
+}
+
 #undef ATOMIC_OPS
 #define ATOMIC_OPS(pfx, op, type, c_op, asm_op, ll, sc)			\
 	ATOMIC_OP(pfx, op, type, c_op, asm_op, ll, sc)			\
@@ -179,9 +212,16 @@ ATOMIC_OPS(atomic, and, int, &=, and, ll, sc)
 ATOMIC_OPS(atomic, or, int, |=, or, ll, sc)
 ATOMIC_OPS(atomic, xor, int, ^=, xor, ll, sc)
 
+#undef ATOMIC_OPS
+#define ATOMIC_OPS(pfx, op, type, c_op1, c_op2, asm_op1, asm_op2, ll, sc)	\
+	ATOMIC_FETCH_OP2(pfx, op, type, c_op1, c_op2, asm_op1, asm_op2, ll, sc)
+
+ATOMIC_OPS(atomic, and_or, int, &, |, and, or, ll, sc)
+
 #define arch_atomic_fetch_and_relaxed	arch_atomic_fetch_and_relaxed
 #define arch_atomic_fetch_or_relaxed	arch_atomic_fetch_or_relaxed
 #define arch_atomic_fetch_xor_relaxed	arch_atomic_fetch_xor_relaxed
+#define arch_atomic_fetch_and_or_relaxed	arch_atomic_fetch_and_or_relaxed
 
 #ifdef CONFIG_64BIT
 ATOMIC_OPS(atomic64, and, s64, &=, and, lld, scd)
@@ -194,6 +234,7 @@ ATOMIC_OPS(atomic64, xor, s64, ^=, xor, lld, scd)
 
 #undef ATOMIC_OPS
 #undef ATOMIC_FETCH_OP
+#undef ATOMIC_FETCH_OP2
 #undef ATOMIC_OP_RETURN
 #undef ATOMIC_OP
 
diff --git a/arch/openrisc/include/asm/atomic.h b/arch/openrisc/include/asm/atomic.h
index 326167e4783a..04598ef16977 100644
--- a/arch/openrisc/include/asm/atomic.h
+++ b/arch/openrisc/include/asm/atomic.h
@@ -66,6 +66,25 @@ static inline int arch_atomic_fetch_##op(int i, atomic_t *v)		\
 	return old;							\
 }
 
+#define ATOMIC_FETCH_OP2(op1, op2)					\
+static inline int arch_atomic_fetch_##op1##_##op2(int i, int j, atomic_t *v)	\
+{									\
+	int tmp, old;							\
+									\
+	__asm__ __volatile__(						\
+		"1:	l.lwa	%0,0(%2)	\n"			\
+		"	l." #op1 " %1,%0,%3	\n"			\
+		"	l." #op2 " %1,%1,%4	\n"			\
+		"	l.swa	0(%2),%1	\n"			\
+		"	l.bnf	1b		\n"			\
+		"	 l.nop			\n"			\
+		: "=&r"(old), "=&r"(tmp)				\
+		: "r"(&v->counter), "r"(i), "r"(j)			\
+		: "cc", "memory");					\
+									\
+	return old;							\
+}
+
 ATOMIC_OP_RETURN(add)
 ATOMIC_OP_RETURN(sub)
 
@@ -74,6 +93,7 @@ ATOMIC_FETCH_OP(sub)
 ATOMIC_FETCH_OP(and)
 ATOMIC_FETCH_OP(or)
 ATOMIC_FETCH_OP(xor)
+ATOMIC_FETCH_OP2(and, or)
 
 ATOMIC_OP(add)
 ATOMIC_OP(sub)
@@ -82,6 +102,7 @@ ATOMIC_OP(or)
 ATOMIC_OP(xor)
 
 #undef ATOMIC_FETCH_OP
+#undef ATOMIC_FETCH_OP2
 #undef ATOMIC_OP_RETURN
 #undef ATOMIC_OP
 
@@ -92,6 +113,7 @@ ATOMIC_OP(xor)
 #define arch_atomic_fetch_and	arch_atomic_fetch_and
 #define arch_atomic_fetch_or	arch_atomic_fetch_or
 #define arch_atomic_fetch_xor	arch_atomic_fetch_xor
+#define arch_atomic_fetch_and_or	arch_atomic_fetch_and_or
 #define arch_atomic_add		arch_atomic_add
 #define arch_atomic_sub		arch_atomic_sub
 #define arch_atomic_and		arch_atomic_and
diff --git a/arch/parisc/include/asm/atomic.h b/arch/parisc/include/asm/atomic.h
index dd5a299ada69..59b9685ed2b1 100644
--- a/arch/parisc/include/asm/atomic.h
+++ b/arch/parisc/include/asm/atomic.h
@@ -114,6 +114,20 @@ static __inline__ int arch_atomic_fetch_##op(int i, atomic_t *v)	\
 	return ret;							\
 }
 
+#define ATOMIC_FETCH_OP2(op, c_op1, c_op2)				\
+static __inline__ int arch_atomic_fetch_##op(int i, int j, atomic_t *v)\
+{									\
+	unsigned long flags;						\
+	int ret;							\
+									\
+	_atomic_spin_lock_irqsave(v, flags);				\
+	ret = v->counter;						\
+	v->counter = (ret c_op1 i) c_op2 j;				\
+	_atomic_spin_unlock_irqrestore(v, flags);			\
+									\
+	return ret;							\
+}
+
 #define ATOMIC_OPS(op, c_op)						\
 	ATOMIC_OP(op, c_op)						\
 	ATOMIC_OP_RETURN(op, c_op)					\
@@ -131,6 +145,12 @@ ATOMIC_OPS(and, &=)
 ATOMIC_OPS(or, |=)
 ATOMIC_OPS(xor, ^=)
 
+#undef ATOMIC_OPS
+#define ATOMIC_OPS(op, c_op1, c_op2)					\
+	ATOMIC_FETCH_OP2(op, c_op1, c_op2)
+
+ATOMIC_OPS(and_or, &, |)
+
 #undef ATOMIC_OPS
 #undef ATOMIC_FETCH_OP
 #undef ATOMIC_OP_RETURN
diff --git a/arch/powerpc/include/asm/atomic.h b/arch/powerpc/include/asm/atomic.h
index a1732a79e92a..c2e966ab4b81 100644
--- a/arch/powerpc/include/asm/atomic.h
+++ b/arch/powerpc/include/asm/atomic.h
@@ -86,6 +86,24 @@ static inline int arch_atomic_fetch_##op##_relaxed(int a, atomic_t *v)	\
 	return res;							\
 }
 
+#define ATOMIC_FETCH_OP2_RELAXED(op, asm_op1, asm_op2)			\
+static inline int arch_atomic_fetch_##op##_relaxed(int a, int b, atomic_t *v)\
+{									\
+	int res, t;							\
+									\
+	__asm__ __volatile__(						\
+"1:	lwarx	%0,0,%5		# atomic_fetch_" #op "_relaxed\n"	\
+	#asm_op1 " %1,%3,%0\n"						\
+	#asm_op2 " %1,%4,%1\n"						\
+"	stwcx.	%1,0,%5\n"						\
+"	bne-	1b\n"							\
+	: "=&r" (res), "=&r" (t), "+m" (v->counter)			\
+	: "r" (a), "r" (b), "r" (&v->counter)				\
+	: "cc");							\
+									\
+	return res;							\
+}
+
 #define ATOMIC_OPS(op, asm_op)						\
 	ATOMIC_OP(op, asm_op)						\
 	ATOMIC_OP_RETURN_RELAXED(op, asm_op)				\
@@ -109,12 +127,20 @@ ATOMIC_OPS(and, and)
 ATOMIC_OPS(or, or)
 ATOMIC_OPS(xor, xor)
 
+#undef ATOMIC_OPS
+#define ATOMIC_OPS(op, asm_op1, asm_op2)			\
+	ATOMIC_FETCH_OP2_RELAXED(op, asm_op1, asm_op2)
+
+ATOMIC_OPS(and_or, and, or)
+
 #define arch_atomic_fetch_and_relaxed arch_atomic_fetch_and_relaxed
 #define arch_atomic_fetch_or_relaxed  arch_atomic_fetch_or_relaxed
 #define arch_atomic_fetch_xor_relaxed arch_atomic_fetch_xor_relaxed
+#define arch_atomic_fetch_and_or_relaxed arch_atomic_fetch_and_or_relaxed
 
 #undef ATOMIC_OPS
 #undef ATOMIC_FETCH_OP_RELAXED
+#undef ATOMIC_FETCH_OP2_RELAXED
 #undef ATOMIC_OP_RETURN_RELAXED
 #undef ATOMIC_OP
 
diff --git a/arch/riscv/include/asm/atomic.h b/arch/riscv/include/asm/atomic.h
index ac9bdf4fc404..572ca0ae2e76 100644
--- a/arch/riscv/include/asm/atomic.h
+++ b/arch/riscv/include/asm/atomic.h
@@ -110,6 +110,24 @@ c_type arch_atomic##prefix##_fetch_##op(c_type i, atomic##prefix##_t *v)	\
 	return ret;							\
 }
 
+#define ATOMIC_FETCH_OP2(op, asm_op1, asm_op2, asm_type, c_type, prefix)	\
+static __always_inline								\
+c_type arch_atomic##prefix##_fetch_##op##_relaxed(c_type i,	c_type j,	\
+					     atomic##prefix##_t *v)	\
+{									\
+	register c_type ret, tmp;					\
+	__asm__ __volatile__ (						\
+		"0:	lr." #asm_type " %0,  %2\n"			\
+		"	" #asm_op1 "%1, %0, %3\n"			\
+		"	" #asm_op2 "%1, %1, %4\n"			\
+		"	sc." #asm_type " %1, %1, %2\n"			\
+		"	bnez     %1, 0b\n"				\
+		: "=r" (ret), "=&r" (tmp), "+A" (v->counter)		\
+		: "r" (i), "r" (j)					\
+		: "memory");						\
+	return ret;							\
+}
+
 #define ATOMIC_OP_RETURN(op, asm_op, c_op, I, asm_type, c_type, prefix)	\
 static __always_inline							\
 c_type arch_atomic##prefix##_##op##_return_relaxed(c_type i,		\
@@ -175,9 +193,15 @@ ATOMIC_OPS(and, and, i)
 ATOMIC_OPS( or,  or, i)
 ATOMIC_OPS(xor, xor, i)
 
+#define ATOMIC_OPS(op, asm_op1, asm_op2, I)			\
+        ATOMIC_FETCH_OP2(op, asm_op1, asm_op2, I, w, int,)
+
+ATOMIC_OPS(and_or, and, or, w)
+
 #define arch_atomic_fetch_and_relaxed	arch_atomic_fetch_and_relaxed
 #define arch_atomic_fetch_or_relaxed	arch_atomic_fetch_or_relaxed
 #define arch_atomic_fetch_xor_relaxed	arch_atomic_fetch_xor_relaxed
+#define arch_atomic_fetch_and_or_relaxed	arch_atomic_fetch_and_or_relaxed
 #define arch_atomic_fetch_and		arch_atomic_fetch_and
 #define arch_atomic_fetch_or		arch_atomic_fetch_or
 #define arch_atomic_fetch_xor		arch_atomic_fetch_xor
@@ -194,6 +218,7 @@ ATOMIC_OPS(xor, xor, i)
 #undef ATOMIC_OPS
 
 #undef ATOMIC_FETCH_OP
+#undef ATOMIC_FETCH_OP2
 #undef ATOMIC_OP_RETURN
 
 /* This is required to provide a full barrier on success. */
diff --git a/arch/s390/include/asm/atomic.h b/arch/s390/include/asm/atomic.h
index 7138d189cc42..abebd658c1fa 100644
--- a/arch/s390/include/asm/atomic.h
+++ b/arch/s390/include/asm/atomic.h
@@ -62,6 +62,7 @@ static inline int arch_atomic_fetch_##op(int i, atomic_t *v)		\
 ATOMIC_OPS(and)
 ATOMIC_OPS(or)
 ATOMIC_OPS(xor)
+ATOMIC_OPS(and_or)
 
 #undef ATOMIC_OPS
 
@@ -71,6 +72,7 @@ ATOMIC_OPS(xor)
 #define arch_atomic_fetch_and		arch_atomic_fetch_and
 #define arch_atomic_fetch_or		arch_atomic_fetch_or
 #define arch_atomic_fetch_xor		arch_atomic_fetch_xor
+#define arch_atomic_fetch_and_or	arch_atomic_fetch_and_or
 
 #define arch_atomic_xchg(v, new)	(arch_xchg(&((v)->counter), new))
 
diff --git a/arch/s390/include/asm/atomic_ops.h b/arch/s390/include/asm/atomic_ops.h
index 50510e08b893..d396f2e2eb9a 100644
--- a/arch/s390/include/asm/atomic_ops.h
+++ b/arch/s390/include/asm/atomic_ops.h
@@ -154,6 +154,31 @@ __ATOMIC64_OPS(__atomic64_xor, "xgr")
 
 #endif /* CONFIG_HAVE_MARCH_Z196_FEATURES */
 
+#define __ATOMIC_OP2(op_name, op1, op2)				\
+static inline int op_name(int i, int j, int *ptr)			\
+{									\
+	int old, new;							\
+									\
+	asm volatile(							\
+		"0:	lr	%[new],%[old]\n"			\
+		op1 "	%[new],%[i]\n"					\
+		op2 "	%[new],%[j]\n"					\
+		"	cs	%[old],%[new],%[ptr]\n"			\
+		"	jl	0b"					\
+		: [old] "=d" (old), [new] "=&d" (new), [ptr] "+Q" (*ptr)\
+		: [i] "d" (i), [j] "d" (j), "0" (*ptr) : "cc", "memory");\
+	return old;							\
+}
+
+#define __ATOMIC_OPS(op_name, op1_string, op2_string)			\
+	__ATOMIC_OP2(op_name, op1_string, op2_string)			\
+	__ATOMIC_OP2(op_name##_barrier, op1_string, op2_string)
+
+__ATOMIC_OPS(__atomic_and_or, "ngr", "ogr")
+
+#undef __ATOMIC_OPS
+#undef __ATOMIC_OP2
+
 static inline int __atomic_cmpxchg(int *ptr, int old, int new)
 {
 	asm volatile(
diff --git a/arch/sparc/include/asm/atomic_32.h b/arch/sparc/include/asm/atomic_32.h
index d775daa83d12..d062b20eb64c 100644
--- a/arch/sparc/include/asm/atomic_32.h
+++ b/arch/sparc/include/asm/atomic_32.h
@@ -23,6 +23,7 @@ int arch_atomic_fetch_add(int, atomic_t *);
 int arch_atomic_fetch_and(int, atomic_t *);
 int arch_atomic_fetch_or(int, atomic_t *);
 int arch_atomic_fetch_xor(int, atomic_t *);
+int arch_atomic_fetch_and_or(int, int, atomic_t *);
 int arch_atomic_cmpxchg(atomic_t *, int, int);
 int arch_atomic_xchg(atomic_t *, int);
 int arch_atomic_fetch_add_unless(atomic_t *, int, int);
@@ -40,6 +41,7 @@ void arch_atomic_set(atomic_t *, int);
 #define arch_atomic_and(i, v)	((void)arch_atomic_fetch_and((i), (v)))
 #define arch_atomic_or(i, v)	((void)arch_atomic_fetch_or((i), (v)))
 #define arch_atomic_xor(i, v)	((void)arch_atomic_fetch_xor((i), (v)))
+#define arch_atomic_and_or(i, j, v)	((void)arch_atomic_fetch_and_or((i), (j), (v)))
 
 #define arch_atomic_sub_return(i, v)	(arch_atomic_add_return(-(int)(i), (v)))
 #define arch_atomic_fetch_sub(i, v)	(arch_atomic_fetch_add (-(int)(i), (v)))
diff --git a/arch/sparc/lib/atomic32.c b/arch/sparc/lib/atomic32.c
index 8b81d0f00c97..aefb6d91985e 100644
--- a/arch/sparc/lib/atomic32.c
+++ b/arch/sparc/lib/atomic32.c
@@ -43,6 +43,21 @@ int arch_atomic_fetch_##op(int i, atomic_t *v)				\
 }									\
 EXPORT_SYMBOL(arch_atomic_fetch_##op);
 
+#define ATOMIC_FETCH_OP2(op, c_op1, c_op2)				\
+int arch_atomic_fetch_##op(int i, int j, atomic_t *v)			\
+{									\
+	int ret;							\
+	unsigned long flags;						\
+	spin_lock_irqsave(ATOMIC_HASH(v), flags);			\
+									\
+	ret = v->counter;						\
+	v->counter = (ret c_op1 i) c_op2 j;				\
+									\
+	spin_unlock_irqrestore(ATOMIC_HASH(v), flags);			\
+	return ret;							\
+}									\
+EXPORT_SYMBOL(arch_atomic_fetch_##op);
+
 #define ATOMIC_OP_RETURN(op, c_op)					\
 int arch_atomic_##op##_return(int i, atomic_t *v)			\
 {									\
@@ -63,8 +78,10 @@ ATOMIC_FETCH_OP(add, +=)
 ATOMIC_FETCH_OP(and, &=)
 ATOMIC_FETCH_OP(or, |=)
 ATOMIC_FETCH_OP(xor, ^=)
+ATOMIC_FETCH_OP2(and_or, &, |)
 
 #undef ATOMIC_FETCH_OP
+#undef ATOMIC_FETCH_OP2
 #undef ATOMIC_OP_RETURN
 
 int arch_atomic_xchg(atomic_t *v, int new)
diff --git a/arch/x86/include/asm/atomic.h b/arch/x86/include/asm/atomic.h
index 5e754e895767..145dce45d02a 100644
--- a/arch/x86/include/asm/atomic.h
+++ b/arch/x86/include/asm/atomic.h
@@ -263,6 +263,16 @@ static __always_inline int arch_atomic_fetch_xor(int i, atomic_t *v)
 }
 #define arch_atomic_fetch_xor arch_atomic_fetch_xor
 
+static __always_inline int arch_atomic_fetch_and_or(int i, int j, atomic_t *v)
+{
+	int val = arch_atomic_read(v);
+
+	do { } while (!arch_atomic_try_cmpxchg(v, &val, (val & i) | j));
+
+	return val;
+}
+#define arch_atomic_fetch_and_or arch_atomic_fetch_and_or
+
 #ifdef CONFIG_X86_32
 # include <asm/atomic64_32.h>
 #else
diff --git a/arch/xtensa/include/asm/atomic.h b/arch/xtensa/include/asm/atomic.h
index 4361fe4247e3..6b043cf74df2 100644
--- a/arch/xtensa/include/asm/atomic.h
+++ b/arch/xtensa/include/asm/atomic.h
@@ -177,6 +177,28 @@ static inline int arch_atomic_fetch_##op(int i, atomic_t * v)		\
 	return result;							\
 }
 
+#define ATOMIC_FETCH_OP2(op1, op2)					\
+static inline int arch_atomic_fetch_##op1##_##op2(int i, int j, atomic_t * v)\
+{									\
+	unsigned long tmp;						\
+	int result;							\
+									\
+	__asm__ __volatile__(						\
+			"1:     l32i    %[tmp], %[mem]\n"		\
+			"       wsr     %[tmp], scompare1\n"		\
+			"       " #op1 " %[result], %[tmp], %[i]\n"	\
+			"       " #op2 " %[result], %[result], %[j]\n"	\
+			"       s32c1i  %[result], %[mem]\n"		\
+			"       bne     %[result], %[tmp], 1b\n"	\
+			: [result] "=&a" (result), [tmp] "=&a" (tmp),	\
+			  [mem] "+m" (*v)				\
+			: [i] "a" (i), [j] "a" (j)			\
+			: "memory"					\
+			);						\
+									\
+	return result;							\
+}
+
 #else /* XCHAL_HAVE_S32C1I */
 
 #define ATOMIC_OP(op)							\
@@ -238,6 +260,28 @@ static inline int arch_atomic_fetch_##op(int i, atomic_t * v)		\
 	return vval;							\
 }
 
+#define ATOMIC_FETCH_OP2(op1, op2)					\
+static inline int arch_atomic_fetch_##op1##_##op2(int i, int j, atomic_t * v)\
+{									\
+	unsigned int tmp, vval;						\
+									\
+	__asm__ __volatile__(						\
+			"       rsil    a15,"__stringify(TOPLEVEL)"\n"	\
+			"       l32i    %[result], %[mem]\n"		\
+			"       " #op1 " %[tmp], %[result], %[i]\n"	\
+			"       " #op2 " %[tmp], %[tmp], %[j]\n"	\
+			"       s32i    %[tmp], %[mem]\n"		\
+			"       wsr     a15, ps\n"			\
+			"       rsync\n"				\
+			: [result] "=&a" (vval), [tmp] "=&a" (tmp),	\
+			  [mem] "+m" (*v)				\
+			: [i] "a" (i), [j] "a" (j)			\
+			: "a15", "memory"				\
+			);						\
+									\
+	return vval;							\
+}
+
 #endif /* XCHAL_HAVE_S32C1I */
 
 #define ATOMIC_OPS(op) ATOMIC_OP(op) ATOMIC_FETCH_OP(op) ATOMIC_OP_RETURN(op)
@@ -252,6 +296,11 @@ ATOMIC_OPS(and)
 ATOMIC_OPS(or)
 ATOMIC_OPS(xor)
 
+#undef ATOMIC_OPS
+#define ATOMIC_OPS(op1, op2) ATOMIC_FETCH_OP2(op1, op2)
+
+ATOMIC_OPS(and, or)
+
 #undef ATOMIC_OPS
 #undef ATOMIC_FETCH_OP
 #undef ATOMIC_OP_RETURN
diff --git a/include/asm-generic/atomic-instrumented.h b/include/asm-generic/atomic-instrumented.h
index bc45af52c93b..231a8386ac80 100644
--- a/include/asm-generic/atomic-instrumented.h
+++ b/include/asm-generic/atomic-instrumented.h
@@ -441,6 +441,34 @@ atomic_fetch_xor_relaxed(int i, atomic_t *v)
 	return arch_atomic_fetch_xor_relaxed(i, v);
 }
 
+static __always_inline int
+atomic_fetch_and_or(int i, int j, atomic_t *v)
+{
+	instrument_atomic_read_write(v, sizeof(*v));
+	return arch_atomic_fetch_and_or(i, j, v);
+}
+
+static __always_inline int
+atomic_fetch_and_or_acquire(int i, int j, atomic_t *v)
+{
+	instrument_atomic_read_write(v, sizeof(*v));
+	return arch_atomic_fetch_and_or_acquire(i, j, v);
+}
+
+static __always_inline int
+atomic_fetch_and_or_release(int i, int j, atomic_t *v)
+{
+	instrument_atomic_read_write(v, sizeof(*v));
+	return arch_atomic_fetch_and_or_release(i, j, v);
+}
+
+static __always_inline int
+atomic_fetch_and_or_relaxed(int i, int j, atomic_t *v)
+{
+	instrument_atomic_read_write(v, sizeof(*v));
+	return arch_atomic_fetch_and_or_relaxed(i, j, v);
+}
+
 static __always_inline int
 atomic_xchg(atomic_t *v, int i)
 {
diff --git a/include/asm-generic/atomic.h b/include/asm-generic/atomic.h
index 04b8be9f1a77..474e8cd8e58d 100644
--- a/include/asm-generic/atomic.h
+++ b/include/asm-generic/atomic.h
@@ -50,6 +50,18 @@ static inline int generic_atomic_fetch_##op(int i, atomic_t *v)		\
 	return c;							\
 }
 
+#define ATOMIC_FETCH_OP2(op, c_op1, c_op2)				\
+static inline int generic_atomic_fetch_##op(int i, int j, atomic_t *v)	\
+{									\
+	int c, old;							\
+									\
+	c = v->counter;							\
+	while ((old = arch_cmpxchg(&v->counter, c, (c c_op1 i) c_op2 j)) != c)	\
+		c = old;						\
+									\
+	return c;							\
+}
+
 #else
 
 #include <linux/irqflags.h>
@@ -91,6 +103,20 @@ static inline int generic_atomic_fetch_##op(int i, atomic_t *v)		\
 	return ret;							\
 }
 
+#define ATOMIC_FETCH_OP2(op, c_op1, c_op2)				\
+static inline int generic_atomic_fetch_##op(int i, int j, atomic_t *v)	\
+{									\
+	unsigned long flags;						\
+	int ret;							\
+									\
+	raw_local_irq_save(flags);					\
+	ret = v->counter;						\
+	v->counter = (v->counter c_op1 i) c_op2 j;			\
+	raw_local_irq_restore(flags);					\
+									\
+	return ret;							\
+}
+
 #endif /* CONFIG_SMP */
 
 ATOMIC_OP_RETURN(add, +)
@@ -101,6 +127,7 @@ ATOMIC_FETCH_OP(sub, -)
 ATOMIC_FETCH_OP(and, &)
 ATOMIC_FETCH_OP(or, |)
 ATOMIC_FETCH_OP(xor, ^)
+ATOMIC_FETCH_OP2(and_or, &, |)
 
 ATOMIC_OP(add, +)
 ATOMIC_OP(sub, -)
@@ -109,6 +136,7 @@ ATOMIC_OP(or, |)
 ATOMIC_OP(xor, ^)
 
 #undef ATOMIC_FETCH_OP
+#undef ATOMIC_FETCH_OP2
 #undef ATOMIC_OP_RETURN
 #undef ATOMIC_OP
 
@@ -120,6 +148,7 @@ ATOMIC_OP(xor, ^)
 #define arch_atomic_fetch_and			generic_atomic_fetch_and
 #define arch_atomic_fetch_or			generic_atomic_fetch_or
 #define arch_atomic_fetch_xor			generic_atomic_fetch_xor
+#define arch_atomic_fetch_and_or		generic_atomic_fetch_and_or
 
 #define arch_atomic_add				generic_atomic_add
 #define arch_atomic_sub				generic_atomic_sub
diff --git a/include/linux/atomic-arch-fallback.h b/include/linux/atomic-arch-fallback.h
index a3dba31df01e..92043a8d5b79 100644
--- a/include/linux/atomic-arch-fallback.h
+++ b/include/linux/atomic-arch-fallback.h
@@ -891,6 +891,48 @@ arch_atomic_fetch_xor(int i, atomic_t *v)
 
 #endif /* arch_atomic_fetch_xor_relaxed */
 
+#ifndef arch_atomic_fetch_and_or_relaxed
+#define arch_atomic_fetch_and_or_acquire arch_atomic_fetch_and_or
+#define arch_atomic_fetch_and_or_release arch_atomic_fetch_and_or
+#define arch_atomic_fetch_and_or_relaxed arch_atomic_fetch_and_or
+#else /* arch_atomic_fetch_and_or_relaxed */
+
+#ifndef arch_atomic_fetch_and_or_acquire
+static __always_inline int
+arch_atomic_fetch_and_or_acquire(int i, int j, atomic_t *v)
+{
+	int ret = arch_atomic_fetch_and_or_relaxed(i, j, v);
+	__atomic_acquire_fence();
+	return ret;
+}
+#define arch_atomic_fetch_and_or_acquire arch_atomic_fetch_and_or_acquire
+#endif
+
+#ifndef arch_atomic_fetch_and_or_release
+static __always_inline int
+arch_atomic_fetch_and_or_release(int i, int j, atomic_t *v)
+{
+	__atomic_release_fence();
+	return arch_atomic_fetch_and_or_relaxed(i, j, v);
+}
+#define arch_atomic_fetch_and_or_release arch_atomic_fetch_and_or_release
+#endif
+
+#ifndef arch_atomic_fetch_and_or
+static __always_inline int
+arch_atomic_fetch_and_or(int i, int j, atomic_t *v)
+{
+	int ret;
+	__atomic_pre_full_fence();
+	ret = arch_atomic_fetch_and_or_relaxed(i, j, v);
+	__atomic_post_full_fence();
+	return ret;
+}
+#define arch_atomic_fetch_and_or arch_atomic_fetch_and_or
+#endif
+
+#endif /* arch_atomic_fetch_and_or_relaxed */
+
 #ifndef arch_atomic_xchg_relaxed
 #define arch_atomic_xchg_acquire arch_atomic_xchg
 #define arch_atomic_xchg_release arch_atomic_xchg
-- 
2.32.0


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* Re: [RFC PATCH v1 1/5] locking/atomic: Implement atomic_fetch_and_or
  2021-07-28 11:48 [RFC PATCH v1 1/5] locking/atomic: Implement atomic_fetch_and_or Rui Wang
@ 2021-07-28 12:14 ` Boqun Feng
  2021-07-28 14:12   ` Hev
  2021-07-28 12:58 ` Peter Zijlstra
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 17+ messages in thread
From: Boqun Feng @ 2021-07-28 12:14 UTC (permalink / raw)
  To: Rui Wang
  Cc: Peter Zijlstra, Ingo Molnar, Will Deacon, Arnd Bergmann,
	Waiman Long, Guo Ren, linux-arch, hev, Xuefeng Li, Huacai Chen,
	Jiaxun Yang, Huacai Chen

Hi,

Thanks for the patchset. Seems that your git send-email command doesn't
add the "In-Reply-to" tag for patch #2 to #5, so they are not threaded
to patch #1. Not a big deal, but archives or email clients use that
information to organize emails. You may want to check the command. Also,
note that you can always use "--dry-run" option to preview the headers
of your patchset ("--dry-run" won't do the actual send).

On Wed, Jul 28, 2021 at 07:48:22PM +0800, Rui Wang wrote:
> From: wangrui <wangrui@loongson.cn>
> 
> This patch introduce a new atomic primitive 'and_or', It may be have three
> types of implemeations:
> 
>  * The generic implementation is based on arch_cmpxchg.
>  * The hardware supports atomic 'and_or' of single instruction.
>  * The hardware supports LL/SC style atomic operations:
> 
>    1:  ll  v1, mem
>        and t1, v1, arg1
>        or  t1, t1, arg2
>        sc  t1, mem
>        beq t1, 0, 1b
> 
> Now that all the architectures have implemented it.
> 
> Signed-by-off: Rui Wang <wangrui@loongson.cn>
> Signed-by-off: hev <r@hev.cc>

First, this should be "Signed-off-by" ;-) Second, is the second
"Signed-off-by" a mistake?

I will look into this for a review, in the meanwhile, but please add
some tests in lib/atomic64_test.c, not only it will do the test at
runtime, also it will generate asm code which helps people to review.

Regards,
Boqun

> ---
>  arch/alpha/include/asm/atomic.h           | 27 ++++++++++++
>  arch/arc/include/asm/atomic.h             | 52 +++++++++++++++++++++++
>  arch/arm/include/asm/atomic.h             | 44 +++++++++++++++++++
>  arch/arm64/include/asm/atomic.h           | 16 +++++++
>  arch/arm64/include/asm/atomic_ll_sc.h     | 33 ++++++++++++++
>  arch/hexagon/include/asm/atomic.h         | 24 +++++++++++
>  arch/ia64/include/asm/atomic.h            | 18 ++++++++
>  arch/m68k/include/asm/atomic.h            | 36 ++++++++++++++++
>  arch/mips/include/asm/atomic.h            | 41 ++++++++++++++++++
>  arch/openrisc/include/asm/atomic.h        | 22 ++++++++++
>  arch/parisc/include/asm/atomic.h          | 20 +++++++++
>  arch/powerpc/include/asm/atomic.h         | 26 ++++++++++++
>  arch/riscv/include/asm/atomic.h           | 25 +++++++++++
>  arch/s390/include/asm/atomic.h            |  2 +
>  arch/s390/include/asm/atomic_ops.h        | 25 +++++++++++
>  arch/sparc/include/asm/atomic_32.h        |  2 +
>  arch/sparc/lib/atomic32.c                 | 17 ++++++++
>  arch/x86/include/asm/atomic.h             | 10 +++++
>  arch/xtensa/include/asm/atomic.h          | 49 +++++++++++++++++++++
>  include/asm-generic/atomic-instrumented.h | 28 ++++++++++++
>  include/asm-generic/atomic.h              | 29 +++++++++++++
>  include/linux/atomic-arch-fallback.h      | 42 ++++++++++++++++++
>  22 files changed, 588 insertions(+)
> 
> diff --git a/arch/alpha/include/asm/atomic.h b/arch/alpha/include/asm/atomic.h
> index f2861a43a61e..deb05ac292b8 100644
> --- a/arch/alpha/include/asm/atomic.h
> +++ b/arch/alpha/include/asm/atomic.h
> @@ -91,6 +91,25 @@ static inline int arch_atomic_fetch_##op##_relaxed(int i, atomic_t *v)	\
>  	return result;							\
>  }
>  
> +#define ATOMIC_FETCH_OP2(op, asm_op1, asm_op2)					\
> +static inline int arch_atomic_fetch_##op##_relaxed(int i, int j, atomic_t *v)	\
> +{									\
> +	long temp, result;						\
> +	__asm__ __volatile__(						\
> +	"1:	ldl_l %2,%1\n"						\
> +	"	" #asm_op1 " %2,%3,%0\n"				\
> +	"	" #asm_op2 " %0,%4,%0\n"				\
> +	"	stl_c %0,%1\n"						\
> +	"	beq %0,2f\n"						\
> +	".subsection 2\n"						\
> +	"2:	br 1b\n"						\
> +	".previous"							\
> +	:"=&r" (temp), "=m" (v->counter), "=&r" (result)		\
> +	:"Ir" (i), "Ir" (j), "m" (v->counter) : "memory");		\
> +	smp_mb();							\
> +	return result;							\
> +}
> +
>  #define ATOMIC64_OP(op, asm_op)						\
>  static __inline__ void arch_atomic64_##op(s64 i, atomic64_t * v)	\
>  {									\
> @@ -182,10 +201,17 @@ ATOMIC_OPS(andnot, bic)
>  ATOMIC_OPS(or, bis)
>  ATOMIC_OPS(xor, xor)
>  
> +#undef ATOMIC_OPS
> +#define ATOMIC_OPS(op, asm_op1, asm_op2)			\
> +	ATOMIC_FETCH_OP2(op, asm_op1, asm_op2)			\
> +
> +ATOMIC_OPS(and_or, and, bis)
> +
>  #define arch_atomic_fetch_and_relaxed		arch_atomic_fetch_and_relaxed
>  #define arch_atomic_fetch_andnot_relaxed	arch_atomic_fetch_andnot_relaxed
>  #define arch_atomic_fetch_or_relaxed		arch_atomic_fetch_or_relaxed
>  #define arch_atomic_fetch_xor_relaxed		arch_atomic_fetch_xor_relaxed
> +#define arch_atomic_fetch_and_or_relaxed	arch_atomic_fetch_and_or_relaxed
>  
>  #define arch_atomic64_fetch_and_relaxed		arch_atomic64_fetch_and_relaxed
>  #define arch_atomic64_fetch_andnot_relaxed	arch_atomic64_fetch_andnot_relaxed
> @@ -197,6 +223,7 @@ ATOMIC_OPS(xor, xor)
>  #undef ATOMIC64_OP_RETURN
>  #undef ATOMIC64_OP
>  #undef ATOMIC_FETCH_OP
> +#undef ATOMIC_FETCH_OP2
>  #undef ATOMIC_OP_RETURN
>  #undef ATOMIC_OP
>  
> diff --git a/arch/arc/include/asm/atomic.h b/arch/arc/include/asm/atomic.h
> index 7a36d79b5b2f..1aa9e0f396d7 100644
> --- a/arch/arc/include/asm/atomic.h
> +++ b/arch/arc/include/asm/atomic.h
> @@ -89,6 +89,35 @@ static inline int arch_atomic_fetch_##op(int i, atomic_t *v)		\
>  	return orig;							\
>  }
>  
> +#define ATOMIC_FETCH_OP2(op, c_op1, c_op2, asm_op1, asm_op2)		\
> +static inline int arch_atomic_fetch_##op(int i, int j, atomic_t *v)	\
> +{									\
> +	unsigned int val, orig;						\
> +									\
> +	/*								\
> +	 * Explicit full memory barrier needed before/after as		\
> +	 * LLOCK/SCOND themselves don't provide any such semantics	\
> +	 */								\
> +	smp_mb();							\
> +									\
> +	__asm__ __volatile__(						\
> +	"1:	llock   %[orig], [%[ctr]]		\n"		\
> +	"	" #asm_op1 " %[val], %[orig], %[i]	\n"		\
> +	"	" #asm_op2 " %[val], %[val], %[j]	\n"		\
> +	"	scond   %[val], [%[ctr]]		\n"		\
> +	"	bnz     1b				\n"		\
> +	: [val]	"=&r"	(val),						\
> +	  [orig] "=&r" (orig)						\
> +	: [ctr]	"r"	(&v->counter),					\
> +	  [i]	"ir"	(i),						\
> +	  [j]	"ir"	(j),						\
> +	: "cc");							\
> +									\
> +	smp_mb();							\
> +									\
> +	return orig;							\
> +}
> +
>  #else	/* !CONFIG_ARC_HAS_LLSC */
>  
>  #ifndef CONFIG_SMP
> @@ -170,6 +199,23 @@ static inline int arch_atomic_fetch_##op(int i, atomic_t *v)		\
>  	return orig;							\
>  }
>  
> +#define ATOMIC_FETCH_OP2(op, c_op1, c_op2, asm_op1, asm_op2)		\
> +static inline int arch_atomic_fetch_##op(int i, int j, atomic_t *v)	\
> +{									\
> +	unsigned long flags;						\
> +	unsigned long orig;						\
> +									\
> +	/*								\
> +	 * spin lock/unlock provides the needed smp_mb() before/after	\
> +	 */								\
> +	atomic_ops_lock(flags);						\
> +	orig = v->counter;						\
> +	v->counter = (orig c_op1 i) c_op2 j;				\
> +	atomic_ops_unlock(flags);					\
> +									\
> +	return orig;							\
> +}
> +
>  #endif /* !CONFIG_ARC_HAS_LLSC */
>  
>  #define ATOMIC_OPS(op, c_op, asm_op)					\
> @@ -190,6 +236,12 @@ ATOMIC_OPS(andnot, &= ~, bic)
>  ATOMIC_OPS(or, |=, or)
>  ATOMIC_OPS(xor, ^=, xor)
>  
> +#undef ATOMIC_OPS
> +#define ATOMIC_OPS(op, c_op1, c_op2, asm_op1, asm_op2)			\
> +	ATOMIC_FETCH_OP2(op, c_op1, c_op2, asm_op1, asm_op2)
> +
> +ATOMIC_OPS(and_or, &, |, and, or)
> +
>  #define arch_atomic_andnot		arch_atomic_andnot
>  #define arch_atomic_fetch_andnot	arch_atomic_fetch_andnot
>  
> diff --git a/arch/arm/include/asm/atomic.h b/arch/arm/include/asm/atomic.h
> index db8512d9a918..faddbc183ced 100644
> --- a/arch/arm/include/asm/atomic.h
> +++ b/arch/arm/include/asm/atomic.h
> @@ -93,6 +93,28 @@ static inline int arch_atomic_fetch_##op##_relaxed(int i, atomic_t *v)	\
>  	return result;							\
>  }
>  
> +#define ATOMIC_FETCH_OP2(op, c_op1, c_op2, asm_op1, asm_op2)			\
> +static inline int arch_atomic_fetch_##op##_relaxed(int i, int j, atomic_t *v)	\
> +{									\
> +	unsigned long tmp;						\
> +	int result, val;						\
> +									\
> +	prefetchw(&v->counter);						\
> +									\
> +	__asm__ __volatile__("@ atomic_fetch_" #op "\n"			\
> +"1:	ldrex	%0, [%4]\n"						\
> +"	" #asm_op1 "	%1, %0, %5\n"					\
> +"	" #asm_op2 "	%1, %1, %6\n"					\
> +"	strex	%2, %1, [%4]\n"						\
> +"	teq	%2, #0\n"						\
> +"	bne	1b"							\
> +	: "=&r" (result), "=&r" (val), "=&r" (tmp), "+Qo" (v->counter)	\
> +	: "r" (&v->counter), "Ir" (i), "Ir" (j)				\
> +	: "cc");							\
> +									\
> +	return result;							\
> +}
> +
>  #define arch_atomic_add_return_relaxed		arch_atomic_add_return_relaxed
>  #define arch_atomic_sub_return_relaxed		arch_atomic_sub_return_relaxed
>  #define arch_atomic_fetch_add_relaxed		arch_atomic_fetch_add_relaxed
> @@ -102,6 +124,7 @@ static inline int arch_atomic_fetch_##op##_relaxed(int i, atomic_t *v)	\
>  #define arch_atomic_fetch_andnot_relaxed	arch_atomic_fetch_andnot_relaxed
>  #define arch_atomic_fetch_or_relaxed		arch_atomic_fetch_or_relaxed
>  #define arch_atomic_fetch_xor_relaxed		arch_atomic_fetch_xor_relaxed
> +#define arch_atomic_fetch_and_or_relaxed	arch_atomic_fetch_and_or_relaxed
>  
>  static inline int arch_atomic_cmpxchg_relaxed(atomic_t *ptr, int old, int new)
>  {
> @@ -197,6 +220,20 @@ static inline int arch_atomic_fetch_##op(int i, atomic_t *v)		\
>  	return val;							\
>  }
>  
> +#define ATOMIC_FETCH_OP2(op, c_op1, c_op2, asm_op1, asm_op2)		\
> +static inline int arch_atomic_fetch_##op(int i, int j, atomic_t *v)	\
> +{									\
> +	unsigned long flags;						\
> +	int val;							\
> +									\
> +	raw_local_irq_save(flags);					\
> +	val = v->counter;						\
> +	v->counter = (val c_op1 i) c_op2 j;				\
> +	raw_local_irq_restore(flags);					\
> +									\
> +	return val;							\
> +}
> +
>  static inline int arch_atomic_cmpxchg(atomic_t *v, int old, int new)
>  {
>  	int ret;
> @@ -235,8 +272,15 @@ ATOMIC_OPS(andnot, &= ~, bic)
>  ATOMIC_OPS(or,  |=, orr)
>  ATOMIC_OPS(xor, ^=, eor)
>  
> +#undef ATOMIC_OPS
> +#define ATOMIC_OPS(op, c_op1, c_op2, asm_op1, asm_op2)		\
> +	ATOMIC_FETCH_OP2(op, c_op1, c_op2, asm_op1, asm_op2)
> +
> +ATOMIC_OPS(and_or, &, |, and, orr)
> +
>  #undef ATOMIC_OPS
>  #undef ATOMIC_FETCH_OP
> +#undef ATOMIC_FETCH_OP2
>  #undef ATOMIC_OP_RETURN
>  #undef ATOMIC_OP
>  
> diff --git a/arch/arm64/include/asm/atomic.h b/arch/arm64/include/asm/atomic.h
> index c9979273d389..3f1cdd3e2ef9 100644
> --- a/arch/arm64/include/asm/atomic.h
> +++ b/arch/arm64/include/asm/atomic.h
> @@ -43,6 +43,12 @@ static __always_inline int arch_##op##name(int i, atomic_t *v)		\
>  	ATOMIC_FETCH_OP(_release, op)					\
>  	ATOMIC_FETCH_OP(        , op)
>  
> +#define ATOMIC_FETCH_OP2(name, op)					\
> +static __always_inline int arch_##op##name(int i, int j, atomic_t *v)	\
> +{									\
> +	return __ll_sc_##op##name(i, j, v);				\
> +}
> +
>  ATOMIC_FETCH_OPS(atomic_fetch_andnot)
>  ATOMIC_FETCH_OPS(atomic_fetch_or)
>  ATOMIC_FETCH_OPS(atomic_fetch_xor)
> @@ -52,7 +58,17 @@ ATOMIC_FETCH_OPS(atomic_fetch_sub)
>  ATOMIC_FETCH_OPS(atomic_add_return)
>  ATOMIC_FETCH_OPS(atomic_sub_return)
>  
> +#undef ATOMIC_FETCH_OPS
> +#define ATOMIC_FETCH_OPS(op)						\
> +	ATOMIC_FETCH_OP2(_relaxed, op)					\
> +	ATOMIC_FETCH_OP2(_acquire, op)					\
> +	ATOMIC_FETCH_OP2(_release, op)					\
> +	ATOMIC_FETCH_OP2(        , op)
> +
> +ATOMIC_FETCH_OPS(atomic_fetch_and_or)
> +
>  #undef ATOMIC_FETCH_OP
> +#undef ATOMIC_FETCH_OP2
>  #undef ATOMIC_FETCH_OPS
>  
>  #define ATOMIC64_OP(op)							\
> diff --git a/arch/arm64/include/asm/atomic_ll_sc.h b/arch/arm64/include/asm/atomic_ll_sc.h
> index 13869b76b58c..90289c536ed6 100644
> --- a/arch/arm64/include/asm/atomic_ll_sc.h
> +++ b/arch/arm64/include/asm/atomic_ll_sc.h
> @@ -97,6 +97,29 @@ __ll_sc_atomic_fetch_##op##name(int i, atomic_t *v)			\
>  	return result;							\
>  }
>  
> +#define ATOMIC_FETCH_OP2(name, mb, acq, rel, cl, op, asm_op1, asm_op2, cstr) \
> +static inline int							\
> +__ll_sc_atomic_fetch_##op##name(int i, int j, atomic_t *v)		\
> +{									\
> +	unsigned long tmp;						\
> +	int val, result;						\
> +									\
> +	asm volatile("// atomic_fetch_" #op #name "\n"			\
> +	__LL_SC_FALLBACK(						\
> +"	prfm	pstl1strm, %3\n"					\
> +"1:	ld" #acq "xr	%w0, %3\n"					\
> +"	" #asm_op1 "	%w1, %w0, %w4\n"				\
> +"	" #asm_op2 "	%w1, %w1, %w5\n"				\
> +"	st" #rel "xr	%w2, %w1, %3\n"					\
> +"	cbnz	%w2, 1b\n"						\
> +"	" #mb )								\
> +	: "=&r" (result), "=&r" (val), "=&r" (tmp), "+Q" (v->counter)	\
> +	: __stringify(cstr) "r" (i), __stringify(cstr) "r" (j)		\
> +	: cl);								\
> +									\
> +	return result;							\
> +}
> +
>  #define ATOMIC_OPS(...)							\
>  	ATOMIC_OP(__VA_ARGS__)						\
>  	ATOMIC_OP_RETURN(        , dmb ish,  , l, "memory", __VA_ARGS__)\
> @@ -129,8 +152,18 @@ ATOMIC_OPS(xor, eor, K)
>   */
>  ATOMIC_OPS(andnot, bic, )
>  
> +#undef ATOMIC_OPS
> +#define ATOMIC_OPS(...)							\
> +	ATOMIC_FETCH_OP2 (        , dmb ish,  , l, "memory", __VA_ARGS__)\
> +	ATOMIC_FETCH_OP2 (_relaxed,        ,  ,  ,         , __VA_ARGS__)\
> +	ATOMIC_FETCH_OP2 (_acquire,        , a,  , "memory", __VA_ARGS__)\
> +	ATOMIC_FETCH_OP2 (_release,        ,  , l, "memory", __VA_ARGS__)
> +
> +ATOMIC_OPS(and_or, and, orr, K)
> +
>  #undef ATOMIC_OPS
>  #undef ATOMIC_FETCH_OP
> +#undef ATOMIC_FETCH_OP2
>  #undef ATOMIC_OP_RETURN
>  #undef ATOMIC_OP
>  
> diff --git a/arch/hexagon/include/asm/atomic.h b/arch/hexagon/include/asm/atomic.h
> index 6e94f8d04146..d944e210085a 100644
> --- a/arch/hexagon/include/asm/atomic.h
> +++ b/arch/hexagon/include/asm/atomic.h
> @@ -130,6 +130,24 @@ static inline int arch_atomic_fetch_##op(int i, atomic_t *v)		\
>  	return output;							\
>  }
>  
> +#define ATOMIC_FETCH_OP2(op1, op2)					\
> +static inline int arch_atomic_fetch_##op1##_##op2(int i, int j, atomic_t *v)\
> +{									\
> +	int output, val;						\
> +									\
> +	__asm__ __volatile__ (						\
> +		"1:	%0 = memw_locked(%2);\n"			\
> +		"	%1 = "#op1 "(%0,%3);\n"				\
> +		"	%1 = "#op2 "(%1,%4);\n"				\
> +		"	memw_locked(%2,P3)=%1;\n"			\
> +		"	if (!P3) jump 1b;\n"				\
> +		: "=&r" (output), "=&r" (val)				\
> +		: "r" (&v->counter), "r" (i), "r" (j)			\
> +		: "memory", "p3"					\
> +	);								\
> +	return output;							\
> +}
> +
>  #define ATOMIC_OPS(op) ATOMIC_OP(op) ATOMIC_OP_RETURN(op) ATOMIC_FETCH_OP(op)
>  
>  ATOMIC_OPS(add)
> @@ -142,8 +160,14 @@ ATOMIC_OPS(and)
>  ATOMIC_OPS(or)
>  ATOMIC_OPS(xor)
>  
> +#undef ATOMIC_OPS
> +#define ATOMIC_OPS(op1, op2) ATOMIC_FETCH_OP2(op1, op2)
> +
> +ATOMIC_OPS(and, or)
> +
>  #undef ATOMIC_OPS
>  #undef ATOMIC_FETCH_OP
> +#undef ATOMIC_FETCH_OP2
>  #undef ATOMIC_OP_RETURN
>  #undef ATOMIC_OP
>  
> diff --git a/arch/ia64/include/asm/atomic.h b/arch/ia64/include/asm/atomic.h
> index 266c429b9137..6190108dcd53 100644
> --- a/arch/ia64/include/asm/atomic.h
> +++ b/arch/ia64/include/asm/atomic.h
> @@ -57,6 +57,21 @@ ia64_atomic_fetch_##op (int i, atomic_t *v)				\
>  	return old;							\
>  }
>  
> +#define ATOMIC_FETCH_OP2(op, c_op1, c_op2)				\
> +static __inline__ int							\
> +ia64_atomic_fetch_##op (int i, int j, atomic_t *v)			\
> +{									\
> +	__s32 old, new;							\
> +	CMPXCHG_BUGCHECK_DECL						\
> +									\
> +	do {								\
> +		CMPXCHG_BUGCHECK(v);					\
> +		old = arch_atomic_read(v);				\
> +		new = (old c_op1 i) c_op2 j;				\
> +	} while (ia64_cmpxchg(acq, v, old, new, sizeof(atomic_t)) != old); \
> +	return old;							\
> +}
> +
>  #define ATOMIC_OPS(op, c_op)						\
>  	ATOMIC_OP(op, c_op)						\
>  	ATOMIC_FETCH_OP(op, c_op)
> @@ -109,6 +124,7 @@ ATOMIC_OPS(sub, -)
>  ATOMIC_FETCH_OP(and, &)
>  ATOMIC_FETCH_OP(or, |)
>  ATOMIC_FETCH_OP(xor, ^)
> +ATOMIC_FETCH_OP2(and_or, &, |)
>  
>  #define arch_atomic_and(i,v)	(void)ia64_atomic_fetch_and(i,v)
>  #define arch_atomic_or(i,v)	(void)ia64_atomic_fetch_or(i,v)
> @@ -117,9 +133,11 @@ ATOMIC_FETCH_OP(xor, ^)
>  #define arch_atomic_fetch_and(i,v)	ia64_atomic_fetch_and(i,v)
>  #define arch_atomic_fetch_or(i,v)	ia64_atomic_fetch_or(i,v)
>  #define arch_atomic_fetch_xor(i,v)	ia64_atomic_fetch_xor(i,v)
> +#define arch_atomic_fetch_and_or(i,j,v)	ia64_atomic_fetch_and_or(i,j,v)
>  
>  #undef ATOMIC_OPS
>  #undef ATOMIC_FETCH_OP
> +#undef ATOMIC_FETCH_OP2
>  #undef ATOMIC_OP
>  
>  #define ATOMIC64_OP(op, c_op)						\
> diff --git a/arch/m68k/include/asm/atomic.h b/arch/m68k/include/asm/atomic.h
> index 8637bf8a2f65..480ecb6534a3 100644
> --- a/arch/m68k/include/asm/atomic.h
> +++ b/arch/m68k/include/asm/atomic.h
> @@ -67,6 +67,22 @@ static inline int arch_atomic_fetch_##op(int i, atomic_t *v)		\
>  	return tmp;							\
>  }
>  
> +#define ATOMIC_FETCH_OP2(op, c_op1, c_op2, asm_op1, asm_op2)		\
> +static inline int arch_atomic_fetch_##op(int i, int j, atomic_t *v)	\
> +{									\
> +	int t, tmp;							\
> +									\
> +	__asm__ __volatile__(						\
> +			"1:	movel %2,%1\n"				\
> +			"	" #asm_op1 "l %3,%1\n"			\
> +			"	" #asm_op2 "l %4,%1\n"			\
> +			"	casl %2,%1,%0\n"			\
> +			"	jne 1b"					\
> +			: "+m" (*v), "=&d" (t), "=&d" (tmp)		\
> +			: "g" (i), "g" (j), "2" (arch_atomic_read(v)));	\
> +	return tmp;							\
> +}
> +
>  #else
>  
>  #define ATOMIC_OP_RETURN(op, c_op, asm_op)				\
> @@ -96,6 +112,20 @@ static inline int arch_atomic_fetch_##op(int i, atomic_t * v)		\
>  	return t;							\
>  }
>  
> +#define ATOMIC_FETCH_OP2(op, c_op1, c_op2, asm_op1, asm_op2)		\
> +static inline int arch_atomic_fetch_##op(int i, int j, atomic_t * v)	\
> +{									\
> +	unsigned long flags;						\
> +	int t;								\
> +									\
> +	local_irq_save(flags);						\
> +	t = v->counter;							\
> +	v->counter = (t c_op1 i) c_op2 j;				\
> +	local_irq_restore(flags);					\
> +									\
> +	return t;							\
> +}
> +
>  #endif /* CONFIG_RMW_INSNS */
>  
>  #define ATOMIC_OPS(op, c_op, asm_op)					\
> @@ -115,6 +145,12 @@ ATOMIC_OPS(and, &=, and)
>  ATOMIC_OPS(or, |=, or)
>  ATOMIC_OPS(xor, ^=, eor)
>  
> +#undef ATOMIC_OPS
> +#define ATOMIC_OPS(op, c_op1, c_op2, asm_op1, asm_op2)			\
> +	ATOMIC_FETCH_OP2(op, c_op1, c_op2, asm_op1, asm_op2)
> +
> +ATOMIC_OPS(and_or, &, |, and, or)
> +
>  #undef ATOMIC_OPS
>  #undef ATOMIC_FETCH_OP
>  #undef ATOMIC_OP_RETURN
> diff --git a/arch/mips/include/asm/atomic.h b/arch/mips/include/asm/atomic.h
> index 95e1f7f3597f..84319b1ab9b6 100644
> --- a/arch/mips/include/asm/atomic.h
> +++ b/arch/mips/include/asm/atomic.h
> @@ -147,6 +147,39 @@ arch_##pfx##_fetch_##op##_relaxed(type i, pfx##_t * v)			\
>  	return result;							\
>  }
>  
> +#define ATOMIC_FETCH_OP2(pfx, op, type, c_op1, c_op2, asm_op1, asm_op2, ll, sc)\
> +static __inline__ type							\
> +arch_##pfx##_fetch_##op##_relaxed(type i, type j, pfx##_t * v)		\
> +{									\
> +	int temp, result;						\
> +									\
> +	if (!kernel_uses_llsc) {					\
> +		unsigned long flags;					\
> +									\
> +		raw_local_irq_save(flags);				\
> +		result = v->counter;					\
> +		v->counter = (result c_op1 i) c_op2 j;			\
> +		raw_local_irq_restore(flags);				\
> +		return result;						\
> +	}								\
> +									\
> +	__asm__ __volatile__(						\
> +	"	.set	push					\n"	\
> +	"	.set	" MIPS_ISA_LEVEL "			\n"	\
> +	"	" __SYNC(full, loongson3_war) "			\n"	\
> +	"1:	" #ll "	%0, %2		# " #pfx "_fetch_" #op "\n"	\
> +	"	" #asm_op1 " %1, %0, %3				\n"	\
> +	"	" #asm_op2 " %1, %1, %4				\n"	\
> +	"	" #sc "	%1, %2					\n"	\
> +	"\t" __SC_BEQZ "%1, 1b					\n"	\
> +	"	.set	pop					\n"	\
> +	: "=&r" (result), "=&r" (temp),					\
> +	  "+" GCC_OFF_SMALL_ASM() (v->counter)				\
> +	: "Ir" (i), "Ir" (j) : __LLSC_CLOBBER);				\
> +									\
> +	return result;							\
> +}
> +
>  #undef ATOMIC_OPS
>  #define ATOMIC_OPS(pfx, op, type, c_op, asm_op, ll, sc)			\
>  	ATOMIC_OP(pfx, op, type, c_op, asm_op, ll, sc)			\
> @@ -179,9 +212,16 @@ ATOMIC_OPS(atomic, and, int, &=, and, ll, sc)
>  ATOMIC_OPS(atomic, or, int, |=, or, ll, sc)
>  ATOMIC_OPS(atomic, xor, int, ^=, xor, ll, sc)
>  
> +#undef ATOMIC_OPS
> +#define ATOMIC_OPS(pfx, op, type, c_op1, c_op2, asm_op1, asm_op2, ll, sc)	\
> +	ATOMIC_FETCH_OP2(pfx, op, type, c_op1, c_op2, asm_op1, asm_op2, ll, sc)
> +
> +ATOMIC_OPS(atomic, and_or, int, &, |, and, or, ll, sc)
> +
>  #define arch_atomic_fetch_and_relaxed	arch_atomic_fetch_and_relaxed
>  #define arch_atomic_fetch_or_relaxed	arch_atomic_fetch_or_relaxed
>  #define arch_atomic_fetch_xor_relaxed	arch_atomic_fetch_xor_relaxed
> +#define arch_atomic_fetch_and_or_relaxed	arch_atomic_fetch_and_or_relaxed
>  
>  #ifdef CONFIG_64BIT
>  ATOMIC_OPS(atomic64, and, s64, &=, and, lld, scd)
> @@ -194,6 +234,7 @@ ATOMIC_OPS(atomic64, xor, s64, ^=, xor, lld, scd)
>  
>  #undef ATOMIC_OPS
>  #undef ATOMIC_FETCH_OP
> +#undef ATOMIC_FETCH_OP2
>  #undef ATOMIC_OP_RETURN
>  #undef ATOMIC_OP
>  
> diff --git a/arch/openrisc/include/asm/atomic.h b/arch/openrisc/include/asm/atomic.h
> index 326167e4783a..04598ef16977 100644
> --- a/arch/openrisc/include/asm/atomic.h
> +++ b/arch/openrisc/include/asm/atomic.h
> @@ -66,6 +66,25 @@ static inline int arch_atomic_fetch_##op(int i, atomic_t *v)		\
>  	return old;							\
>  }
>  
> +#define ATOMIC_FETCH_OP2(op1, op2)					\
> +static inline int arch_atomic_fetch_##op1##_##op2(int i, int j, atomic_t *v)	\
> +{									\
> +	int tmp, old;							\
> +									\
> +	__asm__ __volatile__(						\
> +		"1:	l.lwa	%0,0(%2)	\n"			\
> +		"	l." #op1 " %1,%0,%3	\n"			\
> +		"	l." #op2 " %1,%1,%4	\n"			\
> +		"	l.swa	0(%2),%1	\n"			\
> +		"	l.bnf	1b		\n"			\
> +		"	 l.nop			\n"			\
> +		: "=&r"(old), "=&r"(tmp)				\
> +		: "r"(&v->counter), "r"(i), "r"(j)			\
> +		: "cc", "memory");					\
> +									\
> +	return old;							\
> +}
> +
>  ATOMIC_OP_RETURN(add)
>  ATOMIC_OP_RETURN(sub)
>  
> @@ -74,6 +93,7 @@ ATOMIC_FETCH_OP(sub)
>  ATOMIC_FETCH_OP(and)
>  ATOMIC_FETCH_OP(or)
>  ATOMIC_FETCH_OP(xor)
> +ATOMIC_FETCH_OP2(and, or)
>  
>  ATOMIC_OP(add)
>  ATOMIC_OP(sub)
> @@ -82,6 +102,7 @@ ATOMIC_OP(or)
>  ATOMIC_OP(xor)
>  
>  #undef ATOMIC_FETCH_OP
> +#undef ATOMIC_FETCH_OP2
>  #undef ATOMIC_OP_RETURN
>  #undef ATOMIC_OP
>  
> @@ -92,6 +113,7 @@ ATOMIC_OP(xor)
>  #define arch_atomic_fetch_and	arch_atomic_fetch_and
>  #define arch_atomic_fetch_or	arch_atomic_fetch_or
>  #define arch_atomic_fetch_xor	arch_atomic_fetch_xor
> +#define arch_atomic_fetch_and_or	arch_atomic_fetch_and_or
>  #define arch_atomic_add		arch_atomic_add
>  #define arch_atomic_sub		arch_atomic_sub
>  #define arch_atomic_and		arch_atomic_and
> diff --git a/arch/parisc/include/asm/atomic.h b/arch/parisc/include/asm/atomic.h
> index dd5a299ada69..59b9685ed2b1 100644
> --- a/arch/parisc/include/asm/atomic.h
> +++ b/arch/parisc/include/asm/atomic.h
> @@ -114,6 +114,20 @@ static __inline__ int arch_atomic_fetch_##op(int i, atomic_t *v)	\
>  	return ret;							\
>  }
>  
> +#define ATOMIC_FETCH_OP2(op, c_op1, c_op2)				\
> +static __inline__ int arch_atomic_fetch_##op(int i, int j, atomic_t *v)\
> +{									\
> +	unsigned long flags;						\
> +	int ret;							\
> +									\
> +	_atomic_spin_lock_irqsave(v, flags);				\
> +	ret = v->counter;						\
> +	v->counter = (ret c_op1 i) c_op2 j;				\
> +	_atomic_spin_unlock_irqrestore(v, flags);			\
> +									\
> +	return ret;							\
> +}
> +
>  #define ATOMIC_OPS(op, c_op)						\
>  	ATOMIC_OP(op, c_op)						\
>  	ATOMIC_OP_RETURN(op, c_op)					\
> @@ -131,6 +145,12 @@ ATOMIC_OPS(and, &=)
>  ATOMIC_OPS(or, |=)
>  ATOMIC_OPS(xor, ^=)
>  
> +#undef ATOMIC_OPS
> +#define ATOMIC_OPS(op, c_op1, c_op2)					\
> +	ATOMIC_FETCH_OP2(op, c_op1, c_op2)
> +
> +ATOMIC_OPS(and_or, &, |)
> +
>  #undef ATOMIC_OPS
>  #undef ATOMIC_FETCH_OP
>  #undef ATOMIC_OP_RETURN
> diff --git a/arch/powerpc/include/asm/atomic.h b/arch/powerpc/include/asm/atomic.h
> index a1732a79e92a..c2e966ab4b81 100644
> --- a/arch/powerpc/include/asm/atomic.h
> +++ b/arch/powerpc/include/asm/atomic.h
> @@ -86,6 +86,24 @@ static inline int arch_atomic_fetch_##op##_relaxed(int a, atomic_t *v)	\
>  	return res;							\
>  }
>  
> +#define ATOMIC_FETCH_OP2_RELAXED(op, asm_op1, asm_op2)			\
> +static inline int arch_atomic_fetch_##op##_relaxed(int a, int b, atomic_t *v)\
> +{									\
> +	int res, t;							\
> +									\
> +	__asm__ __volatile__(						\
> +"1:	lwarx	%0,0,%5		# atomic_fetch_" #op "_relaxed\n"	\
> +	#asm_op1 " %1,%3,%0\n"						\
> +	#asm_op2 " %1,%4,%1\n"						\
> +"	stwcx.	%1,0,%5\n"						\
> +"	bne-	1b\n"							\
> +	: "=&r" (res), "=&r" (t), "+m" (v->counter)			\
> +	: "r" (a), "r" (b), "r" (&v->counter)				\
> +	: "cc");							\
> +									\
> +	return res;							\
> +}
> +
>  #define ATOMIC_OPS(op, asm_op)						\
>  	ATOMIC_OP(op, asm_op)						\
>  	ATOMIC_OP_RETURN_RELAXED(op, asm_op)				\
> @@ -109,12 +127,20 @@ ATOMIC_OPS(and, and)
>  ATOMIC_OPS(or, or)
>  ATOMIC_OPS(xor, xor)
>  
> +#undef ATOMIC_OPS
> +#define ATOMIC_OPS(op, asm_op1, asm_op2)			\
> +	ATOMIC_FETCH_OP2_RELAXED(op, asm_op1, asm_op2)
> +
> +ATOMIC_OPS(and_or, and, or)
> +
>  #define arch_atomic_fetch_and_relaxed arch_atomic_fetch_and_relaxed
>  #define arch_atomic_fetch_or_relaxed  arch_atomic_fetch_or_relaxed
>  #define arch_atomic_fetch_xor_relaxed arch_atomic_fetch_xor_relaxed
> +#define arch_atomic_fetch_and_or_relaxed arch_atomic_fetch_and_or_relaxed
>  
>  #undef ATOMIC_OPS
>  #undef ATOMIC_FETCH_OP_RELAXED
> +#undef ATOMIC_FETCH_OP2_RELAXED
>  #undef ATOMIC_OP_RETURN_RELAXED
>  #undef ATOMIC_OP
>  
> diff --git a/arch/riscv/include/asm/atomic.h b/arch/riscv/include/asm/atomic.h
> index ac9bdf4fc404..572ca0ae2e76 100644
> --- a/arch/riscv/include/asm/atomic.h
> +++ b/arch/riscv/include/asm/atomic.h
> @@ -110,6 +110,24 @@ c_type arch_atomic##prefix##_fetch_##op(c_type i, atomic##prefix##_t *v)	\
>  	return ret;							\
>  }
>  
> +#define ATOMIC_FETCH_OP2(op, asm_op1, asm_op2, asm_type, c_type, prefix)	\
> +static __always_inline								\
> +c_type arch_atomic##prefix##_fetch_##op##_relaxed(c_type i,	c_type j,	\
> +					     atomic##prefix##_t *v)	\
> +{									\
> +	register c_type ret, tmp;					\
> +	__asm__ __volatile__ (						\
> +		"0:	lr." #asm_type " %0,  %2\n"			\
> +		"	" #asm_op1 "%1, %0, %3\n"			\
> +		"	" #asm_op2 "%1, %1, %4\n"			\
> +		"	sc." #asm_type " %1, %1, %2\n"			\
> +		"	bnez     %1, 0b\n"				\
> +		: "=r" (ret), "=&r" (tmp), "+A" (v->counter)		\
> +		: "r" (i), "r" (j)					\
> +		: "memory");						\
> +	return ret;							\
> +}
> +
>  #define ATOMIC_OP_RETURN(op, asm_op, c_op, I, asm_type, c_type, prefix)	\
>  static __always_inline							\
>  c_type arch_atomic##prefix##_##op##_return_relaxed(c_type i,		\
> @@ -175,9 +193,15 @@ ATOMIC_OPS(and, and, i)
>  ATOMIC_OPS( or,  or, i)
>  ATOMIC_OPS(xor, xor, i)
>  
> +#define ATOMIC_OPS(op, asm_op1, asm_op2, I)			\
> +        ATOMIC_FETCH_OP2(op, asm_op1, asm_op2, I, w, int,)
> +
> +ATOMIC_OPS(and_or, and, or, w)
> +
>  #define arch_atomic_fetch_and_relaxed	arch_atomic_fetch_and_relaxed
>  #define arch_atomic_fetch_or_relaxed	arch_atomic_fetch_or_relaxed
>  #define arch_atomic_fetch_xor_relaxed	arch_atomic_fetch_xor_relaxed
> +#define arch_atomic_fetch_and_or_relaxed	arch_atomic_fetch_and_or_relaxed
>  #define arch_atomic_fetch_and		arch_atomic_fetch_and
>  #define arch_atomic_fetch_or		arch_atomic_fetch_or
>  #define arch_atomic_fetch_xor		arch_atomic_fetch_xor
> @@ -194,6 +218,7 @@ ATOMIC_OPS(xor, xor, i)
>  #undef ATOMIC_OPS
>  
>  #undef ATOMIC_FETCH_OP
> +#undef ATOMIC_FETCH_OP2
>  #undef ATOMIC_OP_RETURN
>  
>  /* This is required to provide a full barrier on success. */
> diff --git a/arch/s390/include/asm/atomic.h b/arch/s390/include/asm/atomic.h
> index 7138d189cc42..abebd658c1fa 100644
> --- a/arch/s390/include/asm/atomic.h
> +++ b/arch/s390/include/asm/atomic.h
> @@ -62,6 +62,7 @@ static inline int arch_atomic_fetch_##op(int i, atomic_t *v)		\
>  ATOMIC_OPS(and)
>  ATOMIC_OPS(or)
>  ATOMIC_OPS(xor)
> +ATOMIC_OPS(and_or)
>  
>  #undef ATOMIC_OPS
>  
> @@ -71,6 +72,7 @@ ATOMIC_OPS(xor)
>  #define arch_atomic_fetch_and		arch_atomic_fetch_and
>  #define arch_atomic_fetch_or		arch_atomic_fetch_or
>  #define arch_atomic_fetch_xor		arch_atomic_fetch_xor
> +#define arch_atomic_fetch_and_or	arch_atomic_fetch_and_or
>  
>  #define arch_atomic_xchg(v, new)	(arch_xchg(&((v)->counter), new))
>  
> diff --git a/arch/s390/include/asm/atomic_ops.h b/arch/s390/include/asm/atomic_ops.h
> index 50510e08b893..d396f2e2eb9a 100644
> --- a/arch/s390/include/asm/atomic_ops.h
> +++ b/arch/s390/include/asm/atomic_ops.h
> @@ -154,6 +154,31 @@ __ATOMIC64_OPS(__atomic64_xor, "xgr")
>  
>  #endif /* CONFIG_HAVE_MARCH_Z196_FEATURES */
>  
> +#define __ATOMIC_OP2(op_name, op1, op2)				\
> +static inline int op_name(int i, int j, int *ptr)			\
> +{									\
> +	int old, new;							\
> +									\
> +	asm volatile(							\
> +		"0:	lr	%[new],%[old]\n"			\
> +		op1 "	%[new],%[i]\n"					\
> +		op2 "	%[new],%[j]\n"					\
> +		"	cs	%[old],%[new],%[ptr]\n"			\
> +		"	jl	0b"					\
> +		: [old] "=d" (old), [new] "=&d" (new), [ptr] "+Q" (*ptr)\
> +		: [i] "d" (i), [j] "d" (j), "0" (*ptr) : "cc", "memory");\
> +	return old;							\
> +}
> +
> +#define __ATOMIC_OPS(op_name, op1_string, op2_string)			\
> +	__ATOMIC_OP2(op_name, op1_string, op2_string)			\
> +	__ATOMIC_OP2(op_name##_barrier, op1_string, op2_string)
> +
> +__ATOMIC_OPS(__atomic_and_or, "ngr", "ogr")
> +
> +#undef __ATOMIC_OPS
> +#undef __ATOMIC_OP2
> +
>  static inline int __atomic_cmpxchg(int *ptr, int old, int new)
>  {
>  	asm volatile(
> diff --git a/arch/sparc/include/asm/atomic_32.h b/arch/sparc/include/asm/atomic_32.h
> index d775daa83d12..d062b20eb64c 100644
> --- a/arch/sparc/include/asm/atomic_32.h
> +++ b/arch/sparc/include/asm/atomic_32.h
> @@ -23,6 +23,7 @@ int arch_atomic_fetch_add(int, atomic_t *);
>  int arch_atomic_fetch_and(int, atomic_t *);
>  int arch_atomic_fetch_or(int, atomic_t *);
>  int arch_atomic_fetch_xor(int, atomic_t *);
> +int arch_atomic_fetch_and_or(int, int, atomic_t *);
>  int arch_atomic_cmpxchg(atomic_t *, int, int);
>  int arch_atomic_xchg(atomic_t *, int);
>  int arch_atomic_fetch_add_unless(atomic_t *, int, int);
> @@ -40,6 +41,7 @@ void arch_atomic_set(atomic_t *, int);
>  #define arch_atomic_and(i, v)	((void)arch_atomic_fetch_and((i), (v)))
>  #define arch_atomic_or(i, v)	((void)arch_atomic_fetch_or((i), (v)))
>  #define arch_atomic_xor(i, v)	((void)arch_atomic_fetch_xor((i), (v)))
> +#define arch_atomic_and_or(i, j, v)	((void)arch_atomic_fetch_and_or((i), (j), (v)))
>  
>  #define arch_atomic_sub_return(i, v)	(arch_atomic_add_return(-(int)(i), (v)))
>  #define arch_atomic_fetch_sub(i, v)	(arch_atomic_fetch_add (-(int)(i), (v)))
> diff --git a/arch/sparc/lib/atomic32.c b/arch/sparc/lib/atomic32.c
> index 8b81d0f00c97..aefb6d91985e 100644
> --- a/arch/sparc/lib/atomic32.c
> +++ b/arch/sparc/lib/atomic32.c
> @@ -43,6 +43,21 @@ int arch_atomic_fetch_##op(int i, atomic_t *v)				\
>  }									\
>  EXPORT_SYMBOL(arch_atomic_fetch_##op);
>  
> +#define ATOMIC_FETCH_OP2(op, c_op1, c_op2)				\
> +int arch_atomic_fetch_##op(int i, int j, atomic_t *v)			\
> +{									\
> +	int ret;							\
> +	unsigned long flags;						\
> +	spin_lock_irqsave(ATOMIC_HASH(v), flags);			\
> +									\
> +	ret = v->counter;						\
> +	v->counter = (ret c_op1 i) c_op2 j;				\
> +									\
> +	spin_unlock_irqrestore(ATOMIC_HASH(v), flags);			\
> +	return ret;							\
> +}									\
> +EXPORT_SYMBOL(arch_atomic_fetch_##op);
> +
>  #define ATOMIC_OP_RETURN(op, c_op)					\
>  int arch_atomic_##op##_return(int i, atomic_t *v)			\
>  {									\
> @@ -63,8 +78,10 @@ ATOMIC_FETCH_OP(add, +=)
>  ATOMIC_FETCH_OP(and, &=)
>  ATOMIC_FETCH_OP(or, |=)
>  ATOMIC_FETCH_OP(xor, ^=)
> +ATOMIC_FETCH_OP2(and_or, &, |)
>  
>  #undef ATOMIC_FETCH_OP
> +#undef ATOMIC_FETCH_OP2
>  #undef ATOMIC_OP_RETURN
>  
>  int arch_atomic_xchg(atomic_t *v, int new)
> diff --git a/arch/x86/include/asm/atomic.h b/arch/x86/include/asm/atomic.h
> index 5e754e895767..145dce45d02a 100644
> --- a/arch/x86/include/asm/atomic.h
> +++ b/arch/x86/include/asm/atomic.h
> @@ -263,6 +263,16 @@ static __always_inline int arch_atomic_fetch_xor(int i, atomic_t *v)
>  }
>  #define arch_atomic_fetch_xor arch_atomic_fetch_xor
>  
> +static __always_inline int arch_atomic_fetch_and_or(int i, int j, atomic_t *v)
> +{
> +	int val = arch_atomic_read(v);
> +
> +	do { } while (!arch_atomic_try_cmpxchg(v, &val, (val & i) | j));
> +
> +	return val;
> +}
> +#define arch_atomic_fetch_and_or arch_atomic_fetch_and_or
> +
>  #ifdef CONFIG_X86_32
>  # include <asm/atomic64_32.h>
>  #else
> diff --git a/arch/xtensa/include/asm/atomic.h b/arch/xtensa/include/asm/atomic.h
> index 4361fe4247e3..6b043cf74df2 100644
> --- a/arch/xtensa/include/asm/atomic.h
> +++ b/arch/xtensa/include/asm/atomic.h
> @@ -177,6 +177,28 @@ static inline int arch_atomic_fetch_##op(int i, atomic_t * v)		\
>  	return result;							\
>  }
>  
> +#define ATOMIC_FETCH_OP2(op1, op2)					\
> +static inline int arch_atomic_fetch_##op1##_##op2(int i, int j, atomic_t * v)\
> +{									\
> +	unsigned long tmp;						\
> +	int result;							\
> +									\
> +	__asm__ __volatile__(						\
> +			"1:     l32i    %[tmp], %[mem]\n"		\
> +			"       wsr     %[tmp], scompare1\n"		\
> +			"       " #op1 " %[result], %[tmp], %[i]\n"	\
> +			"       " #op2 " %[result], %[result], %[j]\n"	\
> +			"       s32c1i  %[result], %[mem]\n"		\
> +			"       bne     %[result], %[tmp], 1b\n"	\
> +			: [result] "=&a" (result), [tmp] "=&a" (tmp),	\
> +			  [mem] "+m" (*v)				\
> +			: [i] "a" (i), [j] "a" (j)			\
> +			: "memory"					\
> +			);						\
> +									\
> +	return result;							\
> +}
> +
>  #else /* XCHAL_HAVE_S32C1I */
>  
>  #define ATOMIC_OP(op)							\
> @@ -238,6 +260,28 @@ static inline int arch_atomic_fetch_##op(int i, atomic_t * v)		\
>  	return vval;							\
>  }
>  
> +#define ATOMIC_FETCH_OP2(op1, op2)					\
> +static inline int arch_atomic_fetch_##op1##_##op2(int i, int j, atomic_t * v)\
> +{									\
> +	unsigned int tmp, vval;						\
> +									\
> +	__asm__ __volatile__(						\
> +			"       rsil    a15,"__stringify(TOPLEVEL)"\n"	\
> +			"       l32i    %[result], %[mem]\n"		\
> +			"       " #op1 " %[tmp], %[result], %[i]\n"	\
> +			"       " #op2 " %[tmp], %[tmp], %[j]\n"	\
> +			"       s32i    %[tmp], %[mem]\n"		\
> +			"       wsr     a15, ps\n"			\
> +			"       rsync\n"				\
> +			: [result] "=&a" (vval), [tmp] "=&a" (tmp),	\
> +			  [mem] "+m" (*v)				\
> +			: [i] "a" (i), [j] "a" (j)			\
> +			: "a15", "memory"				\
> +			);						\
> +									\
> +	return vval;							\
> +}
> +
>  #endif /* XCHAL_HAVE_S32C1I */
>  
>  #define ATOMIC_OPS(op) ATOMIC_OP(op) ATOMIC_FETCH_OP(op) ATOMIC_OP_RETURN(op)
> @@ -252,6 +296,11 @@ ATOMIC_OPS(and)
>  ATOMIC_OPS(or)
>  ATOMIC_OPS(xor)
>  
> +#undef ATOMIC_OPS
> +#define ATOMIC_OPS(op1, op2) ATOMIC_FETCH_OP2(op1, op2)
> +
> +ATOMIC_OPS(and, or)
> +
>  #undef ATOMIC_OPS
>  #undef ATOMIC_FETCH_OP
>  #undef ATOMIC_OP_RETURN
> diff --git a/include/asm-generic/atomic-instrumented.h b/include/asm-generic/atomic-instrumented.h
> index bc45af52c93b..231a8386ac80 100644
> --- a/include/asm-generic/atomic-instrumented.h
> +++ b/include/asm-generic/atomic-instrumented.h
> @@ -441,6 +441,34 @@ atomic_fetch_xor_relaxed(int i, atomic_t *v)
>  	return arch_atomic_fetch_xor_relaxed(i, v);
>  }
>  
> +static __always_inline int
> +atomic_fetch_and_or(int i, int j, atomic_t *v)
> +{
> +	instrument_atomic_read_write(v, sizeof(*v));
> +	return arch_atomic_fetch_and_or(i, j, v);
> +}
> +
> +static __always_inline int
> +atomic_fetch_and_or_acquire(int i, int j, atomic_t *v)
> +{
> +	instrument_atomic_read_write(v, sizeof(*v));
> +	return arch_atomic_fetch_and_or_acquire(i, j, v);
> +}
> +
> +static __always_inline int
> +atomic_fetch_and_or_release(int i, int j, atomic_t *v)
> +{
> +	instrument_atomic_read_write(v, sizeof(*v));
> +	return arch_atomic_fetch_and_or_release(i, j, v);
> +}
> +
> +static __always_inline int
> +atomic_fetch_and_or_relaxed(int i, int j, atomic_t *v)
> +{
> +	instrument_atomic_read_write(v, sizeof(*v));
> +	return arch_atomic_fetch_and_or_relaxed(i, j, v);
> +}
> +
>  static __always_inline int
>  atomic_xchg(atomic_t *v, int i)
>  {
> diff --git a/include/asm-generic/atomic.h b/include/asm-generic/atomic.h
> index 04b8be9f1a77..474e8cd8e58d 100644
> --- a/include/asm-generic/atomic.h
> +++ b/include/asm-generic/atomic.h
> @@ -50,6 +50,18 @@ static inline int generic_atomic_fetch_##op(int i, atomic_t *v)		\
>  	return c;							\
>  }
>  
> +#define ATOMIC_FETCH_OP2(op, c_op1, c_op2)				\
> +static inline int generic_atomic_fetch_##op(int i, int j, atomic_t *v)	\
> +{									\
> +	int c, old;							\
> +									\
> +	c = v->counter;							\
> +	while ((old = arch_cmpxchg(&v->counter, c, (c c_op1 i) c_op2 j)) != c)	\
> +		c = old;						\
> +									\
> +	return c;							\
> +}
> +
>  #else
>  
>  #include <linux/irqflags.h>
> @@ -91,6 +103,20 @@ static inline int generic_atomic_fetch_##op(int i, atomic_t *v)		\
>  	return ret;							\
>  }
>  
> +#define ATOMIC_FETCH_OP2(op, c_op1, c_op2)				\
> +static inline int generic_atomic_fetch_##op(int i, int j, atomic_t *v)	\
> +{									\
> +	unsigned long flags;						\
> +	int ret;							\
> +									\
> +	raw_local_irq_save(flags);					\
> +	ret = v->counter;						\
> +	v->counter = (v->counter c_op1 i) c_op2 j;			\
> +	raw_local_irq_restore(flags);					\
> +									\
> +	return ret;							\
> +}
> +
>  #endif /* CONFIG_SMP */
>  
>  ATOMIC_OP_RETURN(add, +)
> @@ -101,6 +127,7 @@ ATOMIC_FETCH_OP(sub, -)
>  ATOMIC_FETCH_OP(and, &)
>  ATOMIC_FETCH_OP(or, |)
>  ATOMIC_FETCH_OP(xor, ^)
> +ATOMIC_FETCH_OP2(and_or, &, |)
>  
>  ATOMIC_OP(add, +)
>  ATOMIC_OP(sub, -)
> @@ -109,6 +136,7 @@ ATOMIC_OP(or, |)
>  ATOMIC_OP(xor, ^)
>  
>  #undef ATOMIC_FETCH_OP
> +#undef ATOMIC_FETCH_OP2
>  #undef ATOMIC_OP_RETURN
>  #undef ATOMIC_OP
>  
> @@ -120,6 +148,7 @@ ATOMIC_OP(xor, ^)
>  #define arch_atomic_fetch_and			generic_atomic_fetch_and
>  #define arch_atomic_fetch_or			generic_atomic_fetch_or
>  #define arch_atomic_fetch_xor			generic_atomic_fetch_xor
> +#define arch_atomic_fetch_and_or		generic_atomic_fetch_and_or
>  
>  #define arch_atomic_add				generic_atomic_add
>  #define arch_atomic_sub				generic_atomic_sub
> diff --git a/include/linux/atomic-arch-fallback.h b/include/linux/atomic-arch-fallback.h
> index a3dba31df01e..92043a8d5b79 100644
> --- a/include/linux/atomic-arch-fallback.h
> +++ b/include/linux/atomic-arch-fallback.h
> @@ -891,6 +891,48 @@ arch_atomic_fetch_xor(int i, atomic_t *v)
>  
>  #endif /* arch_atomic_fetch_xor_relaxed */
>  
> +#ifndef arch_atomic_fetch_and_or_relaxed
> +#define arch_atomic_fetch_and_or_acquire arch_atomic_fetch_and_or
> +#define arch_atomic_fetch_and_or_release arch_atomic_fetch_and_or
> +#define arch_atomic_fetch_and_or_relaxed arch_atomic_fetch_and_or
> +#else /* arch_atomic_fetch_and_or_relaxed */
> +
> +#ifndef arch_atomic_fetch_and_or_acquire
> +static __always_inline int
> +arch_atomic_fetch_and_or_acquire(int i, int j, atomic_t *v)
> +{
> +	int ret = arch_atomic_fetch_and_or_relaxed(i, j, v);
> +	__atomic_acquire_fence();
> +	return ret;
> +}
> +#define arch_atomic_fetch_and_or_acquire arch_atomic_fetch_and_or_acquire
> +#endif
> +
> +#ifndef arch_atomic_fetch_and_or_release
> +static __always_inline int
> +arch_atomic_fetch_and_or_release(int i, int j, atomic_t *v)
> +{
> +	__atomic_release_fence();
> +	return arch_atomic_fetch_and_or_relaxed(i, j, v);
> +}
> +#define arch_atomic_fetch_and_or_release arch_atomic_fetch_and_or_release
> +#endif
> +
> +#ifndef arch_atomic_fetch_and_or
> +static __always_inline int
> +arch_atomic_fetch_and_or(int i, int j, atomic_t *v)
> +{
> +	int ret;
> +	__atomic_pre_full_fence();
> +	ret = arch_atomic_fetch_and_or_relaxed(i, j, v);
> +	__atomic_post_full_fence();
> +	return ret;
> +}
> +#define arch_atomic_fetch_and_or arch_atomic_fetch_and_or
> +#endif
> +
> +#endif /* arch_atomic_fetch_and_or_relaxed */
> +
>  #ifndef arch_atomic_xchg_relaxed
>  #define arch_atomic_xchg_acquire arch_atomic_xchg
>  #define arch_atomic_xchg_release arch_atomic_xchg
> -- 
> 2.32.0
> 

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [RFC PATCH v1 1/5] locking/atomic: Implement atomic_fetch_and_or
  2021-07-28 11:48 [RFC PATCH v1 1/5] locking/atomic: Implement atomic_fetch_and_or Rui Wang
  2021-07-28 12:14 ` Boqun Feng
@ 2021-07-28 12:58 ` Peter Zijlstra
  2021-07-28 13:00   ` Peter Zijlstra
  2021-07-28 13:16   ` Peter Zijlstra
  2021-07-29  9:39 ` Will Deacon
  2021-08-09 11:37 ` Geert Uytterhoeven
  3 siblings, 2 replies; 17+ messages in thread
From: Peter Zijlstra @ 2021-07-28 12:58 UTC (permalink / raw)
  To: Rui Wang
  Cc: Ingo Molnar, Will Deacon, Arnd Bergmann, Waiman Long, Boqun Feng,
	Guo Ren, linux-arch, hev, Xuefeng Li, Huacai Chen, Jiaxun Yang,
	Huacai Chen, Mark Rutland

On Wed, Jul 28, 2021 at 07:48:22PM +0800, Rui Wang wrote:
> From: wangrui <wangrui@loongson.cn>
> 
> This patch introduce a new atomic primitive 'and_or', It may be have three
> types of implemeations:
> 
>  * The generic implementation is based on arch_cmpxchg.
>  * The hardware supports atomic 'and_or' of single instruction.
>  * The hardware supports LL/SC style atomic operations:
> 
>    1:  ll  v1, mem
>        and t1, v1, arg1
>        or  t1, t1, arg2
>        sc  t1, mem
>        beq t1, 0, 1b
> 
> Now that all the architectures have implemented it.
> 
> Signed-by-off: Rui Wang <wangrui@loongson.cn>
> Signed-by-off: hev <r@hev.cc>
> ---

>  include/asm-generic/atomic-instrumented.h | 28 ++++++++++++
>  include/asm-generic/atomic.h              | 29 +++++++++++++
>  include/linux/atomic-arch-fallback.h      | 42 ++++++++++++++++++
>  22 files changed, 588 insertions(+)

> diff --git a/include/asm-generic/atomic-instrumented.h b/include/asm-generic/atomic-instrumented.h
> index bc45af52c93b..231a8386ac80 100644
> --- a/include/asm-generic/atomic-instrumented.h
> +++ b/include/asm-generic/atomic-instrumented.h
> @@ -441,6 +441,34 @@ atomic_fetch_xor_relaxed(int i, atomic_t *v)
>  	return arch_atomic_fetch_xor_relaxed(i, v);
>  }
>  
> +static __always_inline int
> +atomic_fetch_and_or(int i, int j, atomic_t *v)
> +{
> +	instrument_atomic_read_write(v, sizeof(*v));
> +	return arch_atomic_fetch_and_or(i, j, v);
> +}
> +
> +static __always_inline int
> +atomic_fetch_and_or_acquire(int i, int j, atomic_t *v)
> +{
> +	instrument_atomic_read_write(v, sizeof(*v));
> +	return arch_atomic_fetch_and_or_acquire(i, j, v);
> +}
> +
> +static __always_inline int
> +atomic_fetch_and_or_release(int i, int j, atomic_t *v)
> +{
> +	instrument_atomic_read_write(v, sizeof(*v));
> +	return arch_atomic_fetch_and_or_release(i, j, v);
> +}
> +
> +static __always_inline int
> +atomic_fetch_and_or_relaxed(int i, int j, atomic_t *v)
> +{
> +	instrument_atomic_read_write(v, sizeof(*v));
> +	return arch_atomic_fetch_and_or_relaxed(i, j, v);
> +}
> +
>  static __always_inline int
>  atomic_xchg(atomic_t *v, int i)
>  {
> diff --git a/include/asm-generic/atomic.h b/include/asm-generic/atomic.h
> index 04b8be9f1a77..474e8cd8e58d 100644
> --- a/include/asm-generic/atomic.h
> +++ b/include/asm-generic/atomic.h
> @@ -50,6 +50,18 @@ static inline int generic_atomic_fetch_##op(int i, atomic_t *v)		\
>  	return c;							\
>  }
>  
> +#define ATOMIC_FETCH_OP2(op, c_op1, c_op2)				\
> +static inline int generic_atomic_fetch_##op(int i, int j, atomic_t *v)	\
> +{									\
> +	int c, old;							\
> +									\
> +	c = v->counter;							\
> +	while ((old = arch_cmpxchg(&v->counter, c, (c c_op1 i) c_op2 j)) != c)	\
> +		c = old;						\
> +									\
> +	return c;							\
> +}
> +
>  #else
>  
>  #include <linux/irqflags.h>
> @@ -91,6 +103,20 @@ static inline int generic_atomic_fetch_##op(int i, atomic_t *v)		\
>  	return ret;							\
>  }
>  
> +#define ATOMIC_FETCH_OP2(op, c_op1, c_op2)				\
> +static inline int generic_atomic_fetch_##op(int i, int j, atomic_t *v)	\
> +{									\
> +	unsigned long flags;						\
> +	int ret;							\
> +									\
> +	raw_local_irq_save(flags);					\
> +	ret = v->counter;						\
> +	v->counter = (v->counter c_op1 i) c_op2 j;			\
> +	raw_local_irq_restore(flags);					\
> +									\
> +	return ret;							\
> +}
> +
>  #endif /* CONFIG_SMP */
>  
>  ATOMIC_OP_RETURN(add, +)
> @@ -101,6 +127,7 @@ ATOMIC_FETCH_OP(sub, -)
>  ATOMIC_FETCH_OP(and, &)
>  ATOMIC_FETCH_OP(or, |)
>  ATOMIC_FETCH_OP(xor, ^)
> +ATOMIC_FETCH_OP2(and_or, &, |)
>  
>  ATOMIC_OP(add, +)
>  ATOMIC_OP(sub, -)
> @@ -109,6 +136,7 @@ ATOMIC_OP(or, |)
>  ATOMIC_OP(xor, ^)
>  
>  #undef ATOMIC_FETCH_OP
> +#undef ATOMIC_FETCH_OP2
>  #undef ATOMIC_OP_RETURN
>  #undef ATOMIC_OP
>  
> @@ -120,6 +148,7 @@ ATOMIC_OP(xor, ^)
>  #define arch_atomic_fetch_and			generic_atomic_fetch_and
>  #define arch_atomic_fetch_or			generic_atomic_fetch_or
>  #define arch_atomic_fetch_xor			generic_atomic_fetch_xor
> +#define arch_atomic_fetch_and_or		generic_atomic_fetch_and_or
>  
>  #define arch_atomic_add				generic_atomic_add
>  #define arch_atomic_sub				generic_atomic_sub
> diff --git a/include/linux/atomic-arch-fallback.h b/include/linux/atomic-arch-fallback.h
> index a3dba31df01e..92043a8d5b79 100644
> --- a/include/linux/atomic-arch-fallback.h
> +++ b/include/linux/atomic-arch-fallback.h
> @@ -891,6 +891,48 @@ arch_atomic_fetch_xor(int i, atomic_t *v)
>  
>  #endif /* arch_atomic_fetch_xor_relaxed */
>  
> +#ifndef arch_atomic_fetch_and_or_relaxed
> +#define arch_atomic_fetch_and_or_acquire arch_atomic_fetch_and_or
> +#define arch_atomic_fetch_and_or_release arch_atomic_fetch_and_or
> +#define arch_atomic_fetch_and_or_relaxed arch_atomic_fetch_and_or
> +#else /* arch_atomic_fetch_and_or_relaxed */
> +
> +#ifndef arch_atomic_fetch_and_or_acquire
> +static __always_inline int
> +arch_atomic_fetch_and_or_acquire(int i, int j, atomic_t *v)
> +{
> +	int ret = arch_atomic_fetch_and_or_relaxed(i, j, v);
> +	__atomic_acquire_fence();
> +	return ret;
> +}
> +#define arch_atomic_fetch_and_or_acquire arch_atomic_fetch_and_or_acquire
> +#endif
> +
> +#ifndef arch_atomic_fetch_and_or_release
> +static __always_inline int
> +arch_atomic_fetch_and_or_release(int i, int j, atomic_t *v)
> +{
> +	__atomic_release_fence();
> +	return arch_atomic_fetch_and_or_relaxed(i, j, v);
> +}
> +#define arch_atomic_fetch_and_or_release arch_atomic_fetch_and_or_release
> +#endif
> +
> +#ifndef arch_atomic_fetch_and_or
> +static __always_inline int
> +arch_atomic_fetch_and_or(int i, int j, atomic_t *v)
> +{
> +	int ret;
> +	__atomic_pre_full_fence();
> +	ret = arch_atomic_fetch_and_or_relaxed(i, j, v);
> +	__atomic_post_full_fence();
> +	return ret;
> +}
> +#define arch_atomic_fetch_and_or arch_atomic_fetch_and_or
> +#endif
> +
> +#endif /* arch_atomic_fetch_and_or_relaxed */
> +
>  #ifndef arch_atomic_xchg_relaxed
>  #define arch_atomic_xchg_acquire arch_atomic_xchg
>  #define arch_atomic_xchg_release arch_atomic_xchg

Urgh.. please start from something like the below and then run:

  scripts/atomic/gen-atomics.sh

The below isn't quite right, because it'll use try_cmpxchg() for
atomic_andnot_or(), which by being a void atomic should be _relaxed. I'm
not entirely sure how to make that happen in a hurry.

---

diff --git a/scripts/atomic/atomics.tbl b/scripts/atomic/atomics.tbl
index fbee2f6190d9..3aaa0caa6b2d 100755
--- a/scripts/atomic/atomics.tbl
+++ b/scripts/atomic/atomics.tbl
@@ -39,3 +39,4 @@ inc_not_zero		b	v
 inc_unless_negative	b	v
 dec_unless_positive	b	v
 dec_if_positive		i	v
+andnot_or		vF	v	i:m	i:o
diff --git a/scripts/atomic/fallbacks/andnot_or b/scripts/atomic/fallbacks/andnot_or
new file mode 100644
index 000000000000..f50e78d6c53a
--- /dev/null
+++ b/scripts/atomic/fallbacks/andnot_or
@@ -0,0 +1,15 @@
+cat <<EOF
+static __always_inline ${ret}
+arch_${atomic}_${pfx}andnot_or${sfx}${order}(${atomic}_t *v, ${int} m, ${int} o)
+{
+	${retstmt}({
+		${int} N, O = atomic_read(v);
+		do {
+			N = O;
+			N &= ~m;
+			N |= o;
+		} while (!arch_${atomic}_try_cmpxchg${order}(v, &O, N));
+		O;
+	});
+}
+EOF
diff --git a/scripts/atomic/fallbacks/fetch_andnot_or b/scripts/atomic/fallbacks/fetch_andnot_or
deleted file mode 100644
index e69de29bb2d1..000000000000


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* Re: [RFC PATCH v1 1/5] locking/atomic: Implement atomic_fetch_and_or
  2021-07-28 12:58 ` Peter Zijlstra
@ 2021-07-28 13:00   ` Peter Zijlstra
  2021-07-28 13:16   ` Peter Zijlstra
  1 sibling, 0 replies; 17+ messages in thread
From: Peter Zijlstra @ 2021-07-28 13:00 UTC (permalink / raw)
  To: Rui Wang
  Cc: Ingo Molnar, Will Deacon, Arnd Bergmann, Waiman Long, Boqun Feng,
	Guo Ren, linux-arch, hev, Xuefeng Li, Huacai Chen, Jiaxun Yang,
	Huacai Chen, Mark Rutland

On Wed, Jul 28, 2021 at 02:58:35PM +0200, Peter Zijlstra wrote:
> Urgh.. please start from something like the below and then run:
> 
>   scripts/atomic/gen-atomics.sh
> 
> The below isn't quite right, because it'll use try_cmpxchg() for
> atomic_andnot_or(), which by being a void atomic should be _relaxed. I'm
> not entirely sure how to make that happen in a hurry.
> 

Of note, the below is on top of tip/locking/core, which includes a bunch
of atomic work from Mark.

> ---
> 
> diff --git a/scripts/atomic/atomics.tbl b/scripts/atomic/atomics.tbl
> index fbee2f6190d9..3aaa0caa6b2d 100755
> --- a/scripts/atomic/atomics.tbl
> +++ b/scripts/atomic/atomics.tbl
> @@ -39,3 +39,4 @@ inc_not_zero		b	v
>  inc_unless_negative	b	v
>  dec_unless_positive	b	v
>  dec_if_positive		i	v
> +andnot_or		vF	v	i:m	i:o
> diff --git a/scripts/atomic/fallbacks/andnot_or b/scripts/atomic/fallbacks/andnot_or
> new file mode 100644
> index 000000000000..f50e78d6c53a
> --- /dev/null
> +++ b/scripts/atomic/fallbacks/andnot_or
> @@ -0,0 +1,15 @@
> +cat <<EOF
> +static __always_inline ${ret}
> +arch_${atomic}_${pfx}andnot_or${sfx}${order}(${atomic}_t *v, ${int} m, ${int} o)
> +{
> +	${retstmt}({
> +		${int} N, O = atomic_read(v);
> +		do {
> +			N = O;
> +			N &= ~m;
> +			N |= o;
> +		} while (!arch_${atomic}_try_cmpxchg${order}(v, &O, N));
> +		O;
> +	});
> +}
> +EOF
> diff --git a/scripts/atomic/fallbacks/fetch_andnot_or b/scripts/atomic/fallbacks/fetch_andnot_or
> deleted file mode 100644
> index e69de29bb2d1..000000000000
> 

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [RFC PATCH v1 1/5] locking/atomic: Implement atomic_fetch_and_or
  2021-07-28 12:58 ` Peter Zijlstra
  2021-07-28 13:00   ` Peter Zijlstra
@ 2021-07-28 13:16   ` Peter Zijlstra
  2021-07-28 13:21     ` Peter Zijlstra
  1 sibling, 1 reply; 17+ messages in thread
From: Peter Zijlstra @ 2021-07-28 13:16 UTC (permalink / raw)
  To: Rui Wang
  Cc: Ingo Molnar, Will Deacon, Arnd Bergmann, Waiman Long, Boqun Feng,
	Guo Ren, linux-arch, hev, Xuefeng Li, Huacai Chen, Jiaxun Yang,
	Huacai Chen, Mark Rutland

On Wed, Jul 28, 2021 at 02:58:35PM +0200, Peter Zijlstra wrote:
> The below isn't quite right, because it'll use try_cmpxchg() for
> atomic_andnot_or(), which by being a void atomic should be _relaxed. I'm
> not entirely sure how to make that happen in a hurry.
> 
> ---

This seems to do the trick.

diff --git a/scripts/atomic/atomics.tbl b/scripts/atomic/atomics.tbl
index fbee2f6190d9..3aaa0caa6b2d 100755
--- a/scripts/atomic/atomics.tbl
+++ b/scripts/atomic/atomics.tbl
@@ -39,3 +39,4 @@ inc_not_zero		b	v
 inc_unless_negative	b	v
 dec_unless_positive	b	v
 dec_if_positive		i	v
+andnot_or		vF	v	i:m	i:o
diff --git a/scripts/atomic/fallbacks/andnot_or b/scripts/atomic/fallbacks/andnot_or
new file mode 100644
index 000000000000..995d80db8578
--- /dev/null
+++ b/scripts/atomic/fallbacks/andnot_or
@@ -0,0 +1,23 @@
+local try_order=${order}
+
+if meta_has_ret "${meta}"; then
+	:;
+else
+	try_order="_relaxed"
+fi
+
+cat <<EOF
+static __always_inline ${ret}
+arch_${atomic}_${pfx}andnot_or${sfx}${order}(${atomic}_t *v, ${int} m, ${int} o)
+{
+	${retstmt}({
+		${int} N, O = atomic_read(v);
+		do {
+			N = O;
+			N &= ~m;
+			N |= o;
+		} while (!arch_${atomic}_try_cmpxchg${try_order}(v, &O, N));
+		O;
+	});
+}
+EOF
diff --git a/scripts/atomic/fallbacks/fetch_andnot_or b/scripts/atomic/fallbacks/fetch_andnot_or
deleted file mode 100644
index e69de29bb2d1..000000000000

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* Re: [RFC PATCH v1 1/5] locking/atomic: Implement atomic_fetch_and_or
  2021-07-28 13:16   ` Peter Zijlstra
@ 2021-07-28 13:21     ` Peter Zijlstra
  2021-07-29  1:58       ` hev
  0 siblings, 1 reply; 17+ messages in thread
From: Peter Zijlstra @ 2021-07-28 13:21 UTC (permalink / raw)
  To: Rui Wang
  Cc: Ingo Molnar, Will Deacon, Arnd Bergmann, Waiman Long, Boqun Feng,
	Guo Ren, linux-arch, hev, Xuefeng Li, Huacai Chen, Jiaxun Yang,
	Huacai Chen, Mark Rutland

On Wed, Jul 28, 2021 at 03:16:54PM +0200, Peter Zijlstra wrote:
> On Wed, Jul 28, 2021 at 02:58:35PM +0200, Peter Zijlstra wrote:
> > The below isn't quite right, because it'll use try_cmpxchg() for
> > atomic_andnot_or(), which by being a void atomic should be _relaxed. I'm
> > not entirely sure how to make that happen in a hurry.
> > 
> > ---
> 
> This seems to do the trick.
> 

Mark suggested this, which is probably nicer still.

---
diff --git a/scripts/atomic/atomics.tbl b/scripts/atomic/atomics.tbl
index fbee2f6190d9..3aaa0caa6b2d 100755
--- a/scripts/atomic/atomics.tbl
+++ b/scripts/atomic/atomics.tbl
@@ -39,3 +39,4 @@ inc_not_zero		b	v
 inc_unless_negative	b	v
 dec_unless_positive	b	v
 dec_if_positive		i	v
+andnot_or		vF	v	i:m	i:o
diff --git a/scripts/atomic/fallbacks/andnot_or b/scripts/atomic/fallbacks/andnot_or
new file mode 100644
index 000000000000..0fb3a728c0ff
--- /dev/null
+++ b/scripts/atomic/fallbacks/andnot_or
@@ -0,0 +1,24 @@
+local try_order=${order}
+
+#
+# non-value returning atomics are implicity relaxed
+#
+if [ -z "${retstmt}" ]; then
+	try_order="_relaxed"
+fi
+
+cat <<EOF
+static __always_inline ${ret}
+arch_${atomic}_${pfx}andnot_or${sfx}${order}(${atomic}_t *v, ${int} m, ${int} o)
+{
+	${retstmt}({
+		${int} N, O = atomic_read(v);
+		do {
+			N = O;
+			N &= ~m;
+			N |= o;
+		} while (!arch_${atomic}_try_cmpxchg${try_order}(v, &O, N));
+		O;
+	});
+}
+EOF
diff --git a/scripts/atomic/fallbacks/fetch_andnot_or b/scripts/atomic/fallbacks/fetch_andnot_or
deleted file mode 100644
index e69de29bb2d1..000000000000

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* Re: [RFC PATCH v1 1/5] locking/atomic: Implement atomic_fetch_and_or
  2021-07-28 12:14 ` Boqun Feng
@ 2021-07-28 14:12   ` Hev
  0 siblings, 0 replies; 17+ messages in thread
From: Hev @ 2021-07-28 14:12 UTC (permalink / raw)
  To: Boqun Feng
  Cc: Rui Wang, Peter Zijlstra, Ingo Molnar, Will Deacon,
	Arnd Bergmann, Waiman Long, Guo Ren, linux-arch, Xuefeng Li,
	Huacai Chen, Jiaxun Yang, Huacai Chen

Hi, Boqun,

On Wed, Jul 28, 2021 at 8:15 PM Boqun Feng <boqun.feng@gmail.com> wrote:
>
> Hi,
>
> Thanks for the patchset. Seems that your git send-email command doesn't
> add the "In-Reply-to" tag for patch #2 to #5, so they are not threaded
> to patch #1. Not a big deal, but archives or email clients use that
> information to organize emails. You may want to check the command. Also,
> note that you can always use "--dry-run" option to preview the headers
> of your patchset ("--dry-run" won't do the actual send).
Thanks for your advice.

>
> On Wed, Jul 28, 2021 at 07:48:22PM +0800, Rui Wang wrote:
> > From: wangrui <wangrui@loongson.cn>
> >
> > This patch introduce a new atomic primitive 'and_or', It may be have three
> > types of implemeations:
> >
> >  * The generic implementation is based on arch_cmpxchg.
> >  * The hardware supports atomic 'and_or' of single instruction.
> >  * The hardware supports LL/SC style atomic operations:
> >
> >    1:  ll  v1, mem
> >        and t1, v1, arg1
> >        or  t1, t1, arg2
> >        sc  t1, mem
> >        beq t1, 0, 1b
> >
> > Now that all the architectures have implemented it.
> >
> > Signed-by-off: Rui Wang <wangrui@loongson.cn>
> > Signed-by-off: hev <r@hev.cc>
>
> First, this should be "Signed-off-by" ;-) Second, is the second
> "Signed-off-by" a mistake?
Beginner's luck :-)

>
> I will look into this for a review, in the meanwhile, but please add
> some tests in lib/atomic64_test.c, not only it will do the test at
> runtime, also it will generate asm code which helps people to review.
>
> Regards,
> Boqun
>

Regards,
Rui

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [RFC PATCH v1 1/5] locking/atomic: Implement atomic_fetch_and_or
  2021-07-28 13:21     ` Peter Zijlstra
@ 2021-07-29  1:58       ` hev
  2021-07-29  8:23         ` Peter Zijlstra
  0 siblings, 1 reply; 17+ messages in thread
From: hev @ 2021-07-29  1:58 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: Rui Wang, Ingo Molnar, Will Deacon, Arnd Bergmann, Waiman Long,
	Boqun Feng, Guo Ren, linux-arch, Xuefeng Li, Huacai Chen,
	Jiaxun Yang, Huacai Chen, Mark Rutland

Hi, Peter,

On Wed, Jul 28, 2021 at 9:21 PM Peter Zijlstra <peterz@infradead.org> wrote:
>
> On Wed, Jul 28, 2021 at 03:16:54PM +0200, Peter Zijlstra wrote:
> > On Wed, Jul 28, 2021 at 02:58:35PM +0200, Peter Zijlstra wrote:
> > > The below isn't quite right, because it'll use try_cmpxchg() for
> > > atomic_andnot_or(), which by being a void atomic should be _relaxed. I'm
> > > not entirely sure how to make that happen in a hurry.
> > >
> > > ---
> >
> > This seems to do the trick.
> >
>
> Mark suggested this, which is probably nicer still.
Wow, Amazing! so the architecture dependent can be implemented one by one.

Regards
Rui

>
> ---
> diff --git a/scripts/atomic/atomics.tbl b/scripts/atomic/atomics.tbl
> index fbee2f6190d9..3aaa0caa6b2d 100755
> --- a/scripts/atomic/atomics.tbl
> +++ b/scripts/atomic/atomics.tbl
> @@ -39,3 +39,4 @@ inc_not_zero          b       v
>  inc_unless_negative    b       v
>  dec_unless_positive    b       v
>  dec_if_positive                i       v
> +andnot_or              vF      v       i:m     i:o
> diff --git a/scripts/atomic/fallbacks/andnot_or b/scripts/atomic/fallbacks/andnot_or
> new file mode 100644
> index 000000000000..0fb3a728c0ff
> --- /dev/null
> +++ b/scripts/atomic/fallbacks/andnot_or
> @@ -0,0 +1,24 @@
> +local try_order=${order}
> +
> +#
> +# non-value returning atomics are implicity relaxed
> +#
> +if [ -z "${retstmt}" ]; then
> +       try_order="_relaxed"
> +fi
> +
> +cat <<EOF
> +static __always_inline ${ret}
> +arch_${atomic}_${pfx}andnot_or${sfx}${order}(${atomic}_t *v, ${int} m, ${int} o)
> +{
> +       ${retstmt}({
> +               ${int} N, O = atomic_read(v);
> +               do {
> +                       N = O;
> +                       N &= ~m;
> +                       N |= o;
> +               } while (!arch_${atomic}_try_cmpxchg${try_order}(v, &O, N));
> +               O;
> +       });
> +}
> +EOF
> diff --git a/scripts/atomic/fallbacks/fetch_andnot_or b/scripts/atomic/fallbacks/fetch_andnot_or
> deleted file mode 100644
> index e69de29bb2d1..000000000000

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [RFC PATCH v1 1/5] locking/atomic: Implement atomic_fetch_and_or
  2021-07-29  1:58       ` hev
@ 2021-07-29  8:23         ` Peter Zijlstra
  2021-07-29  8:37           ` hev
  0 siblings, 1 reply; 17+ messages in thread
From: Peter Zijlstra @ 2021-07-29  8:23 UTC (permalink / raw)
  To: hev
  Cc: Rui Wang, Ingo Molnar, Will Deacon, Arnd Bergmann, Waiman Long,
	Boqun Feng, Guo Ren, linux-arch, Xuefeng Li, Huacai Chen,
	Jiaxun Yang, Huacai Chen, Mark Rutland

On Thu, Jul 29, 2021 at 09:58:02AM +0800, hev wrote:
> Hi, Peter,
> 
> On Wed, Jul 28, 2021 at 9:21 PM Peter Zijlstra <peterz@infradead.org> wrote:
> >
> > On Wed, Jul 28, 2021 at 03:16:54PM +0200, Peter Zijlstra wrote:
> > > On Wed, Jul 28, 2021 at 02:58:35PM +0200, Peter Zijlstra wrote:
> > > > The below isn't quite right, because it'll use try_cmpxchg() for
> > > > atomic_andnot_or(), which by being a void atomic should be _relaxed. I'm
> > > > not entirely sure how to make that happen in a hurry.
> > > >
> > > > ---
> > >
> > > This seems to do the trick.
> > >
> >
> > Mark suggested this, which is probably nicer still.
> Wow, Amazing! so the architecture dependent can be implemented one by one.

Nah, this is just the fallback, you still need individual arch code to
optimize this and get proper LL/SC primitives.

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [RFC PATCH v1 1/5] locking/atomic: Implement atomic_fetch_and_or
  2021-07-29  8:23         ` Peter Zijlstra
@ 2021-07-29  8:37           ` hev
  0 siblings, 0 replies; 17+ messages in thread
From: hev @ 2021-07-29  8:37 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: Rui Wang, Ingo Molnar, Will Deacon, Arnd Bergmann, Waiman Long,
	Boqun Feng, Guo Ren, linux-arch, Xuefeng Li, Huacai Chen,
	Jiaxun Yang, Huacai Chen, Mark Rutland

Hi, Peter,

On Thu, Jul 29, 2021 at 4:24 PM Peter Zijlstra <peterz@infradead.org> wrote:
>
> On Thu, Jul 29, 2021 at 09:58:02AM +0800, hev wrote:
> > Hi, Peter,
> >
> > On Wed, Jul 28, 2021 at 9:21 PM Peter Zijlstra <peterz@infradead.org> wrote:
> > >
> > > On Wed, Jul 28, 2021 at 03:16:54PM +0200, Peter Zijlstra wrote:
> > > > On Wed, Jul 28, 2021 at 02:58:35PM +0200, Peter Zijlstra wrote:
> > > > > The below isn't quite right, because it'll use try_cmpxchg() for
> > > > > atomic_andnot_or(), which by being a void atomic should be _relaxed. I'm
> > > > > not entirely sure how to make that happen in a hurry.
> > > > >
> > > > > ---
> > > >
> > > > This seems to do the trick.
> > > >
> > >
> > > Mark suggested this, which is probably nicer still.
> > Wow, Amazing! so the architecture dependent can be implemented one by one.
>
> Nah, this is just the fallback, you still need individual arch code to
> optimize this and get proper LL/SC primitives.

Okay, already started. If the atomic andnot_or seems to be ok, I will
send the rest of the patches.

Regards
Rui

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [RFC PATCH v1 1/5] locking/atomic: Implement atomic_fetch_and_or
  2021-07-28 11:48 [RFC PATCH v1 1/5] locking/atomic: Implement atomic_fetch_and_or Rui Wang
  2021-07-28 12:14 ` Boqun Feng
  2021-07-28 12:58 ` Peter Zijlstra
@ 2021-07-29  9:39 ` Will Deacon
  2021-07-29 10:18   ` hev
  2021-08-09 11:37 ` Geert Uytterhoeven
  3 siblings, 1 reply; 17+ messages in thread
From: Will Deacon @ 2021-07-29  9:39 UTC (permalink / raw)
  To: Rui Wang
  Cc: Peter Zijlstra, Ingo Molnar, Arnd Bergmann, Waiman Long,
	Boqun Feng, Guo Ren, linux-arch, hev, Xuefeng Li, Huacai Chen,
	Jiaxun Yang, Huacai Chen

On Wed, Jul 28, 2021 at 07:48:22PM +0800, Rui Wang wrote:
> From: wangrui <wangrui@loongson.cn>
> 
> This patch introduce a new atomic primitive 'and_or', It may be have three
> types of implemeations:
> 
>  * The generic implementation is based on arch_cmpxchg.
>  * The hardware supports atomic 'and_or' of single instruction.

Do any architectures actually support this instruction?

On arm64, we can clear arbitrary bits and we can set arbitrary bits, but we
can't combine the two in a fashion which provides atomicity and
forward-progress guarantees.

Please can you explain how this new primitive will be used, in case there's
an alternative way of doing it which maps better to what CPUs can actually
do?

Cheers,

Will

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [RFC PATCH v1 1/5] locking/atomic: Implement atomic_fetch_and_or
  2021-07-29  9:39 ` Will Deacon
@ 2021-07-29 10:18   ` hev
  2021-07-29 12:52     ` Will Deacon
  2021-07-30 18:40     ` Waiman Long
  0 siblings, 2 replies; 17+ messages in thread
From: hev @ 2021-07-29 10:18 UTC (permalink / raw)
  To: Will Deacon
  Cc: Rui Wang, Peter Zijlstra, Ingo Molnar, Arnd Bergmann,
	Waiman Long, Boqun Feng, Guo Ren, linux-arch, Xuefeng Li,
	Huacai Chen, Jiaxun Yang, Huacai Chen

Hi, Will,

On Thu, Jul 29, 2021 at 5:39 PM Will Deacon <will@kernel.org> wrote:
>
> On Wed, Jul 28, 2021 at 07:48:22PM +0800, Rui Wang wrote:
> > From: wangrui <wangrui@loongson.cn>
> >
> > This patch introduce a new atomic primitive 'and_or', It may be have three
> > types of implemeations:
> >
> >  * The generic implementation is based on arch_cmpxchg.
> >  * The hardware supports atomic 'and_or' of single instruction.
>
> Do any architectures actually support this instruction?
No, I'm not sure now.

>
> On arm64, we can clear arbitrary bits and we can set arbitrary bits, but we
> can't combine the two in a fashion which provides atomicity and
> forward-progress guarantees.
>
> Please can you explain how this new primitive will be used, in case there's
> an alternative way of doing it which maps better to what CPUs can actually
> do?
I think we can easily exchange arbitrary bits of a machine word with atomic
andnot_or/and_or. Otherwise, we can only use xchg8/16 to do it. It depends on
hardware support, and the key point is that the bits to be exchanged
must be in the
same sub-word. qspinlock adjusted memory layout for this reason, and waste some
bits(_Q_PENDING_BITS == 8).

In the case of qspinlock xchg_tail, I think there is no change in the
assembly code
after switching to atomic andnot_or, for the architecture that
supports CAS instructions.
But for LL/SC style architectures, We can implement xchg for sub-word
better with new
primitive and clear[1]. And in fact, it reduces the number of retries
when the two memory
load values are not equal.

If the hardware supports this atomic semantics, we will get better
performance and flexibility.
I think the hardware is easy to support.

[1] https://github.com/heiher/linux/commit/f77e1c6e4e579543177010bef2b394479c50b6cf

Regards
Rui

>
> Cheers,
>
> Will

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [RFC PATCH v1 1/5] locking/atomic: Implement atomic_fetch_and_or
  2021-07-29 10:18   ` hev
@ 2021-07-29 12:52     ` Will Deacon
  2021-07-30 18:40     ` Waiman Long
  1 sibling, 0 replies; 17+ messages in thread
From: Will Deacon @ 2021-07-29 12:52 UTC (permalink / raw)
  To: hev
  Cc: Rui Wang, Peter Zijlstra, Ingo Molnar, Arnd Bergmann,
	Waiman Long, Boqun Feng, Guo Ren, linux-arch, Xuefeng Li,
	Huacai Chen, Jiaxun Yang, Huacai Chen

On Thu, Jul 29, 2021 at 06:18:28PM +0800, hev wrote:
> On Thu, Jul 29, 2021 at 5:39 PM Will Deacon <will@kernel.org> wrote:
> >
> > On Wed, Jul 28, 2021 at 07:48:22PM +0800, Rui Wang wrote:
> > > From: wangrui <wangrui@loongson.cn>
> > >
> > > This patch introduce a new atomic primitive 'and_or', It may be have three
> > > types of implemeations:
> > >
> > >  * The generic implementation is based on arch_cmpxchg.
> > >  * The hardware supports atomic 'and_or' of single instruction.
> >
> > Do any architectures actually support this instruction?
> No, I'm not sure now.
> 
> >
> > On arm64, we can clear arbitrary bits and we can set arbitrary bits, but we
> > can't combine the two in a fashion which provides atomicity and
> > forward-progress guarantees.
> >
> > Please can you explain how this new primitive will be used, in case there's
> > an alternative way of doing it which maps better to what CPUs can actually
> > do?
> I think we can easily exchange arbitrary bits of a machine word with atomic
> andnot_or/and_or. Otherwise, we can only use xchg8/16 to do it. It depends on
> hardware support, and the key point is that the bits to be exchanged
> must be in the
> same sub-word. qspinlock adjusted memory layout for this reason, and waste some
> bits(_Q_PENDING_BITS == 8).

No, it's not about wasting bits -- short xchg() is exactly what you want to
do here, it's just that when you get more than 13 bits of CPU number (which
is, err, unusual) then we need more space in the lockword to track the tail,
and so the other fields end up sharing bytes.

> In the case of qspinlock xchg_tail, I think there is no change in the
> assembly code
> after switching to atomic andnot_or, for the architecture that
> supports CAS instructions.
> But for LL/SC style architectures, We can implement xchg for sub-word
> better with new
> primitive and clear[1]. And in fact, it reduces the number of retries
> when the two memory
> load values are not equal.

The only system using LL/SC with this many CPUs is probably Power, and their
atomics are dirt slow anyway.

> If the hardware supports this atomic semantics, we will get better
> performance and flexibility.
> I think the hardware is easy to support.

The issue I have is exposing these new functions as first-class members of
the atomics API. On architectures with AMO instructions, falling back to
cmpxchg() will have a radically different performance profile when compared
to many of the other atomics operations and so I don't think we should add
them without very good justification.

At the very least, we could update the atomics documentation to call out
unconditional functions which are likely to loop around cmpxchg()
internally. We already have things like atomic_add_unless() and
atomic_dec_if_positive() but their conditional nature makes it much less
surprising than something like atomic_and_or() imo.

Will

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [RFC PATCH v1 1/5] locking/atomic: Implement atomic_fetch_and_or
  2021-07-29 10:18   ` hev
  2021-07-29 12:52     ` Will Deacon
@ 2021-07-30 18:40     ` Waiman Long
  2021-07-31  1:46       ` hev
  2021-08-05 13:20       ` Huacai Chen
  1 sibling, 2 replies; 17+ messages in thread
From: Waiman Long @ 2021-07-30 18:40 UTC (permalink / raw)
  To: hev, Will Deacon
  Cc: Rui Wang, Peter Zijlstra, Ingo Molnar, Arnd Bergmann, Boqun Feng,
	Guo Ren, linux-arch, Xuefeng Li, Huacai Chen, Jiaxun Yang,
	Huacai Chen

On 7/29/21 6:18 AM, hev wrote:
> Hi, Will,
>
> On Thu, Jul 29, 2021 at 5:39 PM Will Deacon <will@kernel.org> wrote:
>> On Wed, Jul 28, 2021 at 07:48:22PM +0800, Rui Wang wrote:
>>> From: wangrui <wangrui@loongson.cn>
>>>
>>> This patch introduce a new atomic primitive 'and_or', It may be have three
>>> types of implemeations:
>>>
>>>   * The generic implementation is based on arch_cmpxchg.
>>>   * The hardware supports atomic 'and_or' of single instruction.
>> Do any architectures actually support this instruction?
> No, I'm not sure now.
>
>> On arm64, we can clear arbitrary bits and we can set arbitrary bits, but we
>> can't combine the two in a fashion which provides atomicity and
>> forward-progress guarantees.
>>
>> Please can you explain how this new primitive will be used, in case there's
>> an alternative way of doing it which maps better to what CPUs can actually
>> do?
> I think we can easily exchange arbitrary bits of a machine word with atomic
> andnot_or/and_or. Otherwise, we can only use xchg8/16 to do it. It depends on
> hardware support, and the key point is that the bits to be exchanged
> must be in the
> same sub-word. qspinlock adjusted memory layout for this reason, and waste some
> bits(_Q_PENDING_BITS == 8).

It is not actually a waste of bits. With _Q_PENDING_BITS==8, more 
optimized code can be used for pending bit processing. It is only in the 
rare case that NR_CPUS >= 16k - 1 that we have to fall back to 
_Q_PENDING_BITS==1. In fact, that should be the only condition that will 
make _Q_PENDING_BITS=1.

Cheers,
Longman


^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [RFC PATCH v1 1/5] locking/atomic: Implement atomic_fetch_and_or
  2021-07-30 18:40     ` Waiman Long
@ 2021-07-31  1:46       ` hev
  2021-08-05 13:20       ` Huacai Chen
  1 sibling, 0 replies; 17+ messages in thread
From: hev @ 2021-07-31  1:46 UTC (permalink / raw)
  To: Waiman Long
  Cc: Will Deacon, Rui Wang, Peter Zijlstra, Ingo Molnar,
	Arnd Bergmann, Boqun Feng, Guo Ren, linux-arch, Xuefeng Li,
	Huacai Chen, Jiaxun Yang, Huacai Chen

Hi,

On Sat, Jul 31, 2021 at 2:40 AM Waiman Long <llong@redhat.com> wrote:
>
> On 7/29/21 6:18 AM, hev wrote:
> > Hi, Will,
> >
> > On Thu, Jul 29, 2021 at 5:39 PM Will Deacon <will@kernel.org> wrote:
> >> On Wed, Jul 28, 2021 at 07:48:22PM +0800, Rui Wang wrote:
> >>> From: wangrui <wangrui@loongson.cn>
> >>>
> >>> This patch introduce a new atomic primitive 'and_or', It may be have three
> >>> types of implemeations:
> >>>
> >>>   * The generic implementation is based on arch_cmpxchg.
> >>>   * The hardware supports atomic 'and_or' of single instruction.
> >> Do any architectures actually support this instruction?
> > No, I'm not sure now.
> >
> >> On arm64, we can clear arbitrary bits and we can set arbitrary bits, but we
> >> can't combine the two in a fashion which provides atomicity and
> >> forward-progress guarantees.
> >>
> >> Please can you explain how this new primitive will be used, in case there's
> >> an alternative way of doing it which maps better to what CPUs can actually
> >> do?
> > I think we can easily exchange arbitrary bits of a machine word with atomic
> > andnot_or/and_or. Otherwise, we can only use xchg8/16 to do it. It depends on
> > hardware support, and the key point is that the bits to be exchanged
> > must be in the
> > same sub-word. qspinlock adjusted memory layout for this reason, and waste some
> > bits(_Q_PENDING_BITS == 8).
>
> It is not actually a waste of bits. With _Q_PENDING_BITS==8, more
> optimized code can be used for pending bit processing. It is only in the
> rare case that NR_CPUS >= 16k - 1 that we have to fall back to
> _Q_PENDING_BITS==1. In fact, that should be the only condition that will
> make _Q_PENDING_BITS=1.

Yes, you are right. The memory layout is adjusted so that
locked/pending and tail do ont share bits, so normal store
instructions can be used to clear_pending and
clear_pending_set_locked. It's faster than atomic.

Regards,
Rui

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [RFC PATCH v1 1/5] locking/atomic: Implement atomic_fetch_and_or
  2021-07-30 18:40     ` Waiman Long
  2021-07-31  1:46       ` hev
@ 2021-08-05 13:20       ` Huacai Chen
  1 sibling, 0 replies; 17+ messages in thread
From: Huacai Chen @ 2021-08-05 13:20 UTC (permalink / raw)
  To: Waiman Long
  Cc: hev, Will Deacon, Rui Wang, Peter Zijlstra, Ingo Molnar,
	Arnd Bergmann, Boqun Feng, Guo Ren, linux-arch, Xuefeng Li,
	Jiaxun Yang, Huacai Chen

Hi, all,

On Sat, Jul 31, 2021 at 2:40 AM Waiman Long <llong@redhat.com> wrote:
>
> On 7/29/21 6:18 AM, hev wrote:
> > Hi, Will,
> >
> > On Thu, Jul 29, 2021 at 5:39 PM Will Deacon <will@kernel.org> wrote:
> >> On Wed, Jul 28, 2021 at 07:48:22PM +0800, Rui Wang wrote:
> >>> From: wangrui <wangrui@loongson.cn>
> >>>
> >>> This patch introduce a new atomic primitive 'and_or', It may be have three
> >>> types of implemeations:
> >>>
> >>>   * The generic implementation is based on arch_cmpxchg.
> >>>   * The hardware supports atomic 'and_or' of single instruction.
> >> Do any architectures actually support this instruction?
> > No, I'm not sure now.
> >
> >> On arm64, we can clear arbitrary bits and we can set arbitrary bits, but we
> >> can't combine the two in a fashion which provides atomicity and
> >> forward-progress guarantees.
> >>
> >> Please can you explain how this new primitive will be used, in case there's
> >> an alternative way of doing it which maps better to what CPUs can actually
> >> do?
> > I think we can easily exchange arbitrary bits of a machine word with atomic
> > andnot_or/and_or. Otherwise, we can only use xchg8/16 to do it. It depends on
> > hardware support, and the key point is that the bits to be exchanged
> > must be in the
> > same sub-word. qspinlock adjusted memory layout for this reason, and waste some
> > bits(_Q_PENDING_BITS == 8).
>
> It is not actually a waste of bits. With _Q_PENDING_BITS==8, more
> optimized code can be used for pending bit processing. It is only in the
> rare case that NR_CPUS >= 16k - 1 that we have to fall back to
> _Q_PENDING_BITS==1. In fact, that should be the only condition that will
> make _Q_PENDING_BITS=1.
Our original goal is to let LoongArch (and CSKY, RISC-V, etc) can use
qspinlock, but these archs lack sub-word xchg/cmpxchg. Arnd suggests
we not use qspinlock, but LoongArch has large SMP (and NUMA) so we
need it. Peter suggests we implement atomic_fetch_and_or, but it seems
not agreed by everyone. So, I think we can only fix the
badly-implemented xchg_small() for MIPS and LoongArch.

Huacai
>
> Cheers,
> Longman
>

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [RFC PATCH v1 1/5] locking/atomic: Implement atomic_fetch_and_or
  2021-07-28 11:48 [RFC PATCH v1 1/5] locking/atomic: Implement atomic_fetch_and_or Rui Wang
                   ` (2 preceding siblings ...)
  2021-07-29  9:39 ` Will Deacon
@ 2021-08-09 11:37 ` Geert Uytterhoeven
  3 siblings, 0 replies; 17+ messages in thread
From: Geert Uytterhoeven @ 2021-08-09 11:37 UTC (permalink / raw)
  To: Rui Wang
  Cc: Peter Zijlstra, Ingo Molnar, Will Deacon, Arnd Bergmann,
	Waiman Long, Boqun Feng, Guo Ren, Linux-Arch, hev, Xuefeng Li,
	Huacai Chen, Jiaxun Yang, Huacai Chen

Hi Rui,

On Wed, Jul 28, 2021 at 1:50 PM Rui Wang <wangrui@loongson.cn> wrote:
> From: wangrui <wangrui@loongson.cn>
>
> This patch introduce a new atomic primitive 'and_or', It may be have three
> types of implemeations:
>
>  * The generic implementation is based on arch_cmpxchg.
>  * The hardware supports atomic 'and_or' of single instruction.
>  * The hardware supports LL/SC style atomic operations:
>
>    1:  ll  v1, mem
>        and t1, v1, arg1
>        or  t1, t1, arg2
>        sc  t1, mem
>        beq t1, 0, 1b
>
> Now that all the architectures have implemented it.
>
> Signed-by-off: Rui Wang <wangrui@loongson.cn>
> Signed-by-off: hev <r@hev.cc>

> --- a/arch/m68k/include/asm/atomic.h
> +++ b/arch/m68k/include/asm/atomic.h
> @@ -67,6 +67,22 @@ static inline int arch_atomic_fetch_##op(int i, atomic_t *v)         \
>         return tmp;                                                     \
>  }
>
> +#define ATOMIC_FETCH_OP2(op, c_op1, c_op2, asm_op1, asm_op2)           \
> +static inline int arch_atomic_fetch_##op(int i, int j, atomic_t *v)    \
> +{                                                                      \
> +       int t, tmp;                                                     \
> +                                                                       \
> +       __asm__ __volatile__(                                           \
> +                       "1:     movel %2,%1\n"                          \
> +                       "       " #asm_op1 "l %3,%1\n"                  \
> +                       "       " #asm_op2 "l %4,%1\n"                  \
> +                       "       casl %2,%1,%0\n"                        \
> +                       "       jne 1b"                                 \
> +                       : "+m" (*v), "=&d" (t), "=&d" (tmp)             \
> +                       : "g" (i), "g" (j), "2" (arch_atomic_read(v))); \

"di" (i), "di" (j)

cfr. "[PATCH v2] m68k: Fix asm register constraints for atomic ops"
https://lore.kernel.org/linux-m68k/20210809112903.3898660-1-geert@linux-m68k.org/

> +       return tmp;                                                     \
> +}
> +
>  #else
>
>  #define ATOMIC_OP_RETURN(op, c_op, asm_op)                             \

Gr{oetje,eeting}s,

                        Geert

-- 
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds

^ permalink raw reply	[flat|nested] 17+ messages in thread

end of thread, other threads:[~2021-08-09 11:37 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-07-28 11:48 [RFC PATCH v1 1/5] locking/atomic: Implement atomic_fetch_and_or Rui Wang
2021-07-28 12:14 ` Boqun Feng
2021-07-28 14:12   ` Hev
2021-07-28 12:58 ` Peter Zijlstra
2021-07-28 13:00   ` Peter Zijlstra
2021-07-28 13:16   ` Peter Zijlstra
2021-07-28 13:21     ` Peter Zijlstra
2021-07-29  1:58       ` hev
2021-07-29  8:23         ` Peter Zijlstra
2021-07-29  8:37           ` hev
2021-07-29  9:39 ` Will Deacon
2021-07-29 10:18   ` hev
2021-07-29 12:52     ` Will Deacon
2021-07-30 18:40     ` Waiman Long
2021-07-31  1:46       ` hev
2021-08-05 13:20       ` Huacai Chen
2021-08-09 11:37 ` Geert Uytterhoeven

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.