From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751594AbdFILGH (ORCPT ); Fri, 9 Jun 2017 07:06:07 -0400 Received: from merlin.infradead.org ([205.233.59.134]:33662 "EHLO merlin.infradead.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751524AbdFILGF (ORCPT ); Fri, 9 Jun 2017 07:06:05 -0400 Date: Fri, 9 Jun 2017 13:05:06 +0200 From: Peter Zijlstra To: Will Deacon , Paul McKenney , Boqun Feng Cc: linux-kernel@vger.kernel.org, Ingo Molnar , Thomas Gleixner , vgupta@synopsys.com, rkuo@codeaurora.org, james.hogan@imgtec.com, jejb@parisc-linux.org, davem@davemloft.net, cmetcalf@mellanox.com Subject: [RFC][PATCH] atomic: Fix atomic_set_release() for 'funny' architectures Message-ID: <20170609110506.yod47flaav3wgoj5@hirez.programming.kicks-ass.net> References: <20170609092450.jwmldgtli57ozxgq@hirez.programming.kicks-ass.net> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: <20170609092450.jwmldgtli57ozxgq@hirez.programming.kicks-ass.net> User-Agent: NeoMutt/20170113 (1.7.2) Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org On Fri, Jun 09, 2017 at 11:24:50AM +0200, Peter Zijlstra wrote: > +Non RmW ops: > + > +The non-RmW ops are (typically) regular LOADs and STOREs and are canonically > +implemented using READ_ONCE(), WRITE_ONCE(), smp_load_acquire() and > +smp_store_release() respectively. > + > +The one detail to this is that atomic_set() should be observable to the RmW > +ops. That is: > + > + CPU0 CPU1 > + > + val = atomic_read(&X) > + do { > + atomic_set(&X, 0) > + new = val + 1; > + } while (!atomic_try_cmpxchg(&X, &val, new)); > + > +Should cause the cmpxchg to *FAIL* (when @val != 0). This is typically true; > +on 'normal' platforms; a regular competing STORE will invalidate a LL/SC. > + > +The obvious case where this is not so is where we need to implement atomic ops > +with a spinlock hashtable; the typical solution is to then implement > +atomic_set() with atomic_xchg(). --- Subject: atomic: Fix atomic_set_release() for 'funny' architectures Those architectures that have a special atomic_set implementation also need a special atomic_set_release(), because for the very same reason WRITE_ONCE() is broken for them, smp_store_release() is too. The vast majority is architectures that have spinlock hash based atomic implementation except hexagon which seems to have a hardware 'feature'. The spinlock based atomics should be SC, that is, none of them appear to place extra barriers in atomic_cmpxchg() or any of the other SC atomic primitives and therefore seem to rely on their spinlock implementation being SC (I did not fully validate all that). Therefore, the normal atomic_set() is SC and can be used at atomic_set_release(). Cc: Vineet Gupta Cc: Richard Kuo Cc: James Hogan Cc: "James E.J. Bottomley" Cc: "David S. Miller" Cc: Chris Metcalf Signed-off-by: Peter Zijlstra (Intel) --- arch/arc/include/asm/atomic.h | 2 ++ arch/hexagon/include/asm/atomic.h | 2 ++ arch/metag/include/asm/atomic_lock1.h | 2 ++ arch/parisc/include/asm/atomic.h | 2 ++ arch/sparc/include/asm/atomic_32.h | 2 ++ arch/tile/include/asm/atomic_32.h | 2 ++ include/asm-generic/atomic64.h | 2 ++ 7 files changed, 14 insertions(+) diff --git a/arch/arc/include/asm/atomic.h b/arch/arc/include/asm/atomic.h index 54b54da6384c..11859287c52a 100644 --- a/arch/arc/include/asm/atomic.h +++ b/arch/arc/include/asm/atomic.h @@ -123,6 +123,8 @@ static inline void atomic_set(atomic_t *v, int i) atomic_ops_unlock(flags); } +#define atomic_set_release(v, i) atomic_set((v), (i)) + #endif /* diff --git a/arch/hexagon/include/asm/atomic.h b/arch/hexagon/include/asm/atomic.h index a62ba368b27d..fb3dfb2a667e 100644 --- a/arch/hexagon/include/asm/atomic.h +++ b/arch/hexagon/include/asm/atomic.h @@ -42,6 +42,8 @@ static inline void atomic_set(atomic_t *v, int new) ); } +#define atomic_set_release(v, i) atomic_set((v), (i)) + /** * atomic_read - reads a word, atomically * @v: pointer to atomic value diff --git a/arch/metag/include/asm/atomic_lock1.h b/arch/metag/include/asm/atomic_lock1.h index 6c1380a8a0d4..eee779f26cc4 100644 --- a/arch/metag/include/asm/atomic_lock1.h +++ b/arch/metag/include/asm/atomic_lock1.h @@ -37,6 +37,8 @@ static inline int atomic_set(atomic_t *v, int i) return i; } +#define atomic_set_release(v, i) atomic_set((v), (i)) + #define ATOMIC_OP(op, c_op) \ static inline void atomic_##op(int i, atomic_t *v) \ { \ diff --git a/arch/parisc/include/asm/atomic.h b/arch/parisc/include/asm/atomic.h index 5394b9c5f914..17b98a87e5e2 100644 --- a/arch/parisc/include/asm/atomic.h +++ b/arch/parisc/include/asm/atomic.h @@ -65,6 +65,8 @@ static __inline__ void atomic_set(atomic_t *v, int i) _atomic_spin_unlock_irqrestore(v, flags); } +#define atomic_set_release(v, i) atomic_set((v), (i)) + static __inline__ int atomic_read(const atomic_t *v) { return READ_ONCE((v)->counter); diff --git a/arch/sparc/include/asm/atomic_32.h b/arch/sparc/include/asm/atomic_32.h index ee3f11c43cda..7643e979e333 100644 --- a/arch/sparc/include/asm/atomic_32.h +++ b/arch/sparc/include/asm/atomic_32.h @@ -29,6 +29,8 @@ int atomic_xchg(atomic_t *, int); int __atomic_add_unless(atomic_t *, int, int); void atomic_set(atomic_t *, int); +#define atomic_set_release(v, i) atomic_set((v), (i)) + #define atomic_read(v) ACCESS_ONCE((v)->counter) #define atomic_add(i, v) ((void)atomic_add_return( (int)(i), (v))) diff --git a/arch/tile/include/asm/atomic_32.h b/arch/tile/include/asm/atomic_32.h index a93774255136..53a423e7cb92 100644 --- a/arch/tile/include/asm/atomic_32.h +++ b/arch/tile/include/asm/atomic_32.h @@ -101,6 +101,8 @@ static inline void atomic_set(atomic_t *v, int n) _atomic_xchg(&v->counter, n); } +#define atomic_set_release(v, i) atomic_set((v), (i)) + /* A 64bit atomic type */ typedef struct { diff --git a/include/asm-generic/atomic64.h b/include/asm-generic/atomic64.h index dad68bf46c77..8d28eb010d0d 100644 --- a/include/asm-generic/atomic64.h +++ b/include/asm-generic/atomic64.h @@ -21,6 +21,8 @@ typedef struct { extern long long atomic64_read(const atomic64_t *v); extern void atomic64_set(atomic64_t *v, long long i); +#define atomic64_set_release(v, i) atomic64_set((v), (i)) + #define ATOMIC64_OP(op) \ extern void atomic64_##op(long long a, atomic64_t *v);