* [patch 0/4] atomic primitives again @ 2005-11-05 7:55 Nick Piggin 2005-11-05 7:56 ` [patch 1/5] i386: generic cmpxchg Nick Piggin 0 siblings, 1 reply; 15+ messages in thread From: Nick Piggin @ 2005-11-05 7:55 UTC (permalink / raw) To: Andrew Morton, linux-kernel; +Cc: Linus Torvalds It would be nice if these could get in week 2 of the window. Though they'll need wide compile testing (which I haven't been able to provide), actual new arch specific code is limited to ARM. Compiled and booted on i386 and i686 configs, ppc64. -- SUSE Labs, Novell Inc. Send instant messages to your online friends http://au.messenger.yahoo.com ^ permalink raw reply [flat|nested] 15+ messages in thread
* [patch 1/5] i386: generic cmpxchg 2005-11-05 7:55 [patch 0/4] atomic primitives again Nick Piggin @ 2005-11-05 7:56 ` Nick Piggin 2005-11-05 7:57 ` [patch 2/5] atomic: cmpxchg Nick Piggin 0 siblings, 1 reply; 15+ messages in thread From: Nick Piggin @ 2005-11-05 7:56 UTC (permalink / raw) To: Andrew Morton, linux-kernel, Linus Torvalds [-- Attachment #1: Type: text/plain, Size: 54 bytes --] 1/5 oops I can't count. -- SUSE Labs, Novell Inc. [-- Attachment #2: i386-generic-cmpxchg.patch --] [-- Type: text/plain, Size: 3761 bytes --] Changelog * Make cmpxchg generally available on the i386 platform. * Provide emulation of cmpxchg suitable for uniprocessor if built and run on 386. Signed-off-by: Christoph Lameter <clameter@sgi.com> * Cut down patch and small style changes. Signed-off-by: Nick Piggin <npiggin@suse.de> Index: linux-2.6/arch/i386/kernel/cpu/intel.c =================================================================== --- linux-2.6.orig/arch/i386/kernel/cpu/intel.c +++ linux-2.6/arch/i386/kernel/cpu/intel.c @@ -6,6 +6,7 @@ #include <linux/bitops.h> #include <linux/smp.h> #include <linux/thread_info.h> +#include <linux/module.h> #include <asm/processor.h> #include <asm/msr.h> @@ -264,5 +265,55 @@ __init int intel_cpu_init(void) return 0; } +#ifndef CONFIG_X86_CMPXCHG +unsigned long cmpxchg_386_u8(volatile void *ptr, u8 old, u8 new) +{ + u8 prev; + unsigned long flags; + + /* Poor man's cmpxchg for 386. Unsuitable for SMP */ + local_irq_save(flags); + prev = *(u8 *)ptr; + if (prev == old) + *(u8 *)ptr = new; + local_irq_restore(flags); + return prev; +} + +EXPORT_SYMBOL(cmpxchg_386_u8); + +unsigned long cmpxchg_386_u16(volatile void *ptr, u16 old, u16 new) +{ + u16 prev; + unsigned long flags; + + /* Poor man's cmpxchg for 386. Unsuitable for SMP */ + local_irq_save(flags); + prev = *(u16 *)ptr; + if (prev == old) + *(u16 *)ptr = new; + local_irq_restore(flags); + return prev; +} + +EXPORT_SYMBOL(cmpxchg_386_u16); + +unsigned long cmpxchg_386_u32(volatile void *ptr, u32 old, u32 new) +{ + u32 prev; + unsigned long flags; + + /* Poor man's cmpxchg for 386. Unsuitable for SMP */ + local_irq_save(flags); + prev = *(u32 *)ptr; + if (prev == old) + *(u32 *)ptr = new; + local_irq_restore(flags); + return prev; +} + +EXPORT_SYMBOL(cmpxchg_386_u32); +#endif + // arch_initcall(intel_cpu_init); Index: linux-2.6/include/asm-i386/system.h =================================================================== --- linux-2.6.orig/include/asm-i386/system.h +++ linux-2.6/include/asm-i386/system.h @@ -263,6 +263,10 @@ static inline unsigned long __xchg(unsig #ifdef CONFIG_X86_CMPXCHG #define __HAVE_ARCH_CMPXCHG 1 +#define cmpxchg(ptr,o,n)\ + ((__typeof__(*(ptr)))__cmpxchg((ptr),(unsigned long)(o),\ + (unsigned long)(n),sizeof(*(ptr)))) +#endif static inline unsigned long __cmpxchg(volatile void *ptr, unsigned long old, unsigned long new, int size) @@ -291,10 +295,42 @@ static inline unsigned long __cmpxchg(vo return old; } -#define cmpxchg(ptr,o,n)\ - ((__typeof__(*(ptr)))__cmpxchg((ptr),(unsigned long)(o),\ - (unsigned long)(n),sizeof(*(ptr)))) +#ifndef CONFIG_X86_CMPXCHG +/* + * Building a kernel capable running on 80386. It may be necessary to + * simulate the cmpxchg on the 80386 CPU. For that purpose we define + * a function for each of the sizes we support. + */ +extern unsigned long cmpxchg_386_u8(volatile void *, u8, u8); +extern unsigned long cmpxchg_386_u16(volatile void *, u16, u16); +extern unsigned long cmpxchg_386_u32(volatile void *, u32, u32); + +static inline unsigned long cmpxchg_386(volatile void *ptr, unsigned long old, + unsigned long new, int size) +{ + switch (size) { + case 1: + return cmpxchg_386_u8(ptr, old, new); + case 2: + return cmpxchg_386_u16(ptr, old, new); + case 4: + return cmpxchg_386_u32(ptr, old, new); + } + return old; +} + +#define cmpxchg(ptr,o,n) \ +({ \ + __typeof__(*(ptr)) __ret; \ + if (likely(boot_cpu_data.x86 > 3)) \ + __ret = __cmpxchg((ptr), (unsigned long)(o), \ + (unsigned long)(n), sizeof(*(ptr))); \ + else \ + __ret = cmpxchg_386((ptr), (unsigned long)(o), \ + (unsigned long)(n), sizeof(*(ptr))); \ + __ret; \ +}) #endif #ifdef CONFIG_X86_CMPXCHG64 ^ permalink raw reply [flat|nested] 15+ messages in thread
* [patch 2/5] atomic: cmpxchg 2005-11-05 7:56 ` [patch 1/5] i386: generic cmpxchg Nick Piggin @ 2005-11-05 7:57 ` Nick Piggin 2005-11-05 7:58 ` [patch 3/5] atomic: inc_not_zero Nick Piggin 2005-11-05 9:00 ` [patch 2/5] atomic: cmpxchg Russell King 0 siblings, 2 replies; 15+ messages in thread From: Nick Piggin @ 2005-11-05 7:57 UTC (permalink / raw) To: Andrew Morton, linux-kernel, Linus Torvalds [-- Attachment #1: Type: text/plain, Size: 33 bytes --] 2/5 -- SUSE Labs, Novell Inc. [-- Attachment #2: atomic_cmpxchg.patch --] [-- Type: text/plain, Size: 14061 bytes --] Introduce an atomic_cmpxchg operation. Signed-off-by: Nick Piggin <npiggin@suse.de> Index: linux-2.6/include/asm-i386/atomic.h =================================================================== --- linux-2.6.orig/include/asm-i386/atomic.h +++ linux-2.6/include/asm-i386/atomic.h @@ -215,6 +215,8 @@ static __inline__ int atomic_sub_return( return atomic_add_return(-i,v); } +#define atomic_cmpxchg(v, old, new) ((int)cmpxchg(&((v)->counter), old, new)) + #define atomic_inc_return(v) (atomic_add_return(1,v)) #define atomic_dec_return(v) (atomic_sub_return(1,v)) Index: linux-2.6/include/asm-ia64/atomic.h =================================================================== --- linux-2.6.orig/include/asm-ia64/atomic.h +++ linux-2.6/include/asm-ia64/atomic.h @@ -88,6 +88,8 @@ ia64_atomic64_sub (__s64 i, atomic64_t * return new; } +#define atomic_cmpxchg(v, old, new) ((int)cmpxchg(&((v)->counter), old, new)) + #define atomic_add_return(i,v) \ ({ \ int __ia64_aar_i = (i); \ Index: linux-2.6/include/asm-x86_64/atomic.h =================================================================== --- linux-2.6.orig/include/asm-x86_64/atomic.h +++ linux-2.6/include/asm-x86_64/atomic.h @@ -360,6 +360,8 @@ static __inline__ int atomic_sub_return( return atomic_add_return(-i,v); } +#define atomic_cmpxchg(v, old, new) ((int)cmpxchg(&((v)->counter), old, new)) + #define atomic_inc_return(v) (atomic_add_return(1,v)) #define atomic_dec_return(v) (atomic_sub_return(1,v)) Index: linux-2.6/Documentation/atomic_ops.txt =================================================================== --- linux-2.6.orig/Documentation/atomic_ops.txt +++ linux-2.6/Documentation/atomic_ops.txt @@ -115,6 +115,21 @@ boolean is return which indicates whethe is negative. It requires explicit memory barrier semantics around the operation. +Finally: + + int atomic_cmpxchg(atomic_t *v, int old, int new); + +This performs an atomic compare exchange operation on the atomic value v, +with the given old and new values. Like all atomic_xxx operations, +atomic_cmpxchg will only satisfy its atomicity semantics as long as all +other accesses of *v are performed through atomic_xxx operations. + +atomic_cmpxchg requires explicit memory barriers around the operation. + +The semantics for atomic_cmpxchg are the same as those defined for 'cas' +below. + + If a caller requires memory barrier semantics around an atomic_t operation which does not return a value, a set of interfaces are defined which accomplish this: Index: linux-2.6/arch/sparc/lib/atomic32.c =================================================================== --- linux-2.6.orig/arch/sparc/lib/atomic32.c +++ linux-2.6/arch/sparc/lib/atomic32.c @@ -38,6 +38,20 @@ int __atomic_add_return(int i, atomic_t return ret; } +int atomic_cmpxchg(atomic_t *v, int old, int new) +{ + int ret; + unsigned long flags; + spin_lock_irqsave(ATOMIC_HASH(v), flags); + + ret = v->counter; + if (likely(ret == old)) + v->counter = new; + + spin_unlock_irqrestore(ATOMIC_HASH(v), flags); + return ret; +} + void atomic_set(atomic_t *v, int i) { unsigned long flags; Index: linux-2.6/include/asm-sparc/atomic.h =================================================================== --- linux-2.6.orig/include/asm-sparc/atomic.h +++ linux-2.6/include/asm-sparc/atomic.h @@ -19,6 +19,7 @@ typedef struct { volatile int counter; } #define ATOMIC_INIT(i) { (i) } extern int __atomic_add_return(int, atomic_t *); +extern int atomic_cmpxchg(atomic_t *, int, int); extern void atomic_set(atomic_t *, int); #define atomic_read(v) ((v)->counter) Index: linux-2.6/include/asm-alpha/atomic.h =================================================================== --- linux-2.6.orig/include/asm-alpha/atomic.h +++ linux-2.6/include/asm-alpha/atomic.h @@ -177,6 +177,8 @@ static __inline__ long atomic64_sub_retu return result; } +#define atomic_cmpxchg(v, o, n) ((int)cmpxchg(&((v)->counter), (o), (n))) + #define atomic_dec_return(v) atomic_sub_return(1,(v)) #define atomic64_dec_return(v) atomic64_sub_return(1,(v)) Index: linux-2.6/include/asm-m68k/atomic.h =================================================================== --- linux-2.6.orig/include/asm-m68k/atomic.h +++ linux-2.6/include/asm-m68k/atomic.h @@ -139,6 +139,8 @@ static inline void atomic_set_mask(unsig __asm__ __volatile__("orl %1,%0" : "+m" (*v) : "id" (mask)); } +#define atomic_cmpxchg(v, o, n) ((int)cmpxchg(&((v)->counter), (o), (n))) + /* Atomic operations are already serializing */ #define smp_mb__before_atomic_dec() barrier() #define smp_mb__after_atomic_dec() barrier() Index: linux-2.6/include/asm-m68knommu/atomic.h =================================================================== --- linux-2.6.orig/include/asm-m68knommu/atomic.h +++ linux-2.6/include/asm-m68knommu/atomic.h @@ -128,6 +128,8 @@ static inline int atomic_sub_return(int return temp; } +#define atomic_cmpxchg(v, o, n) ((int)cmpxchg(&((v)->counter), (o), (n))) + #define atomic_dec_return(v) atomic_sub_return(1,(v)) #define atomic_inc_return(v) atomic_add_return(1,(v)) Index: linux-2.6/include/asm-mips/atomic.h =================================================================== --- linux-2.6.orig/include/asm-mips/atomic.h +++ linux-2.6/include/asm-mips/atomic.h @@ -287,6 +287,8 @@ static __inline__ int atomic_sub_if_posi return result; } +#define atomic_cmpxchg(v, o, n) ((int)cmpxchg(&((v)->counter), (o), (n))) + #define atomic_dec_return(v) atomic_sub_return(1,(v)) #define atomic_inc_return(v) atomic_add_return(1,(v)) Index: linux-2.6/include/asm-parisc/atomic.h =================================================================== --- linux-2.6.orig/include/asm-parisc/atomic.h +++ linux-2.6/include/asm-parisc/atomic.h @@ -164,6 +164,7 @@ static __inline__ int atomic_read(const } /* exported interface */ +#define atomic_cmpxchg(v, o, n) ((int)cmpxchg(&((v)->counter), (o), (n))) #define atomic_add(i,v) ((void)(__atomic_add_return( ((int)i),(v)))) #define atomic_sub(i,v) ((void)(__atomic_add_return(-((int)i),(v)))) Index: linux-2.6/include/asm-s390/atomic.h =================================================================== --- linux-2.6.orig/include/asm-s390/atomic.h +++ linux-2.6/include/asm-s390/atomic.h @@ -198,6 +198,8 @@ atomic_compare_and_swap(int expected_old return retval; } +#define atomic_cmpxchg(v, o, n) (atomic_compare_and_swap((o), (n), &((v)->counter))) + #define smp_mb__before_atomic_dec() smp_mb() #define smp_mb__after_atomic_dec() smp_mb() #define smp_mb__before_atomic_inc() smp_mb() Index: linux-2.6/include/asm-sparc64/atomic.h =================================================================== --- linux-2.6.orig/include/asm-sparc64/atomic.h +++ linux-2.6/include/asm-sparc64/atomic.h @@ -70,6 +70,8 @@ extern int atomic64_sub_ret(int, atomic6 #define atomic_add_negative(i, v) (atomic_add_ret(i, v) < 0) #define atomic64_add_negative(i, v) (atomic64_add_ret(i, v) < 0) +#define atomic_cmpxchg(v, o, n) ((int)cmpxchg(&((v)->counter), (o), (n))) + /* Atomic operations are already serializing */ #ifdef CONFIG_SMP #define smp_mb__before_atomic_dec() membar_storeload_loadload(); Index: linux-2.6/include/asm-arm26/atomic.h =================================================================== --- linux-2.6.orig/include/asm-arm26/atomic.h +++ linux-2.6/include/asm-arm26/atomic.h @@ -62,6 +62,20 @@ static inline int atomic_sub_return(int return val; } +static inline int atomic_cmpxchg(atomic_t *v, int old, int new) +{ + int ret; + unsigned long flags; + + local_irq_save(flags); + ret = v->counter; + if (likely(ret == old)) + v->counter = new; + local_irq_restore(flags); + + return ret; +} + static inline void atomic_clear_mask(unsigned long mask, unsigned long *addr) { unsigned long flags; Index: linux-2.6/include/asm-frv/atomic.h =================================================================== --- linux-2.6.orig/include/asm-frv/atomic.h +++ linux-2.6/include/asm-frv/atomic.h @@ -414,4 +414,6 @@ extern uint32_t __cmpxchg_32(uint32_t *v #endif +#define atomic_cmpxchg(v, old, new) ((int)cmpxchg(&((v)->counter), old, new)) + #endif /* _ASM_ATOMIC_H */ Index: linux-2.6/include/asm-h8300/atomic.h =================================================================== --- linux-2.6.orig/include/asm-h8300/atomic.h +++ linux-2.6/include/asm-h8300/atomic.h @@ -82,6 +82,18 @@ static __inline__ int atomic_dec_and_tes return ret == 0; } +static __inline__ int atomic_cmpxchg(atomic_t *v, int old, int new) +{ + int ret; + unsigned long flags; + local_irq_save(flags); + ret = v->counter; + if (likely(ret == old)) + v->counter = new; + local_irq_restore(flags); + return ret; +} + static __inline__ void atomic_clear_mask(unsigned long mask, unsigned long *v) { __asm__ __volatile__("stc ccr,r1l\n\t" Index: linux-2.6/include/asm-sh64/atomic.h =================================================================== --- linux-2.6.orig/include/asm-sh64/atomic.h +++ linux-2.6/include/asm-sh64/atomic.h @@ -99,6 +99,20 @@ static __inline__ int atomic_sub_return( #define atomic_inc(v) atomic_add(1,(v)) #define atomic_dec(v) atomic_sub(1,(v)) +static __inline__ int atomic_cmpxchg(atomic_t *v, int old, int new) +{ + int ret; + unsigned long flags; + + local_irq_save(flags); + ret = v->counter; + if (likely(ret == old)) + v->counter = new; + local_irq_restore(flags); + + return ret; +} + static __inline__ void atomic_clear_mask(unsigned int mask, atomic_t *v) { unsigned long flags; Index: linux-2.6/include/asm-v850/atomic.h =================================================================== --- linux-2.6.orig/include/asm-v850/atomic.h +++ linux-2.6/include/asm-v850/atomic.h @@ -90,6 +90,20 @@ static __inline__ void atomic_clear_mask #define atomic_dec_and_test(v) (atomic_sub_return (1, (v)) == 0) #define atomic_add_negative(i,v) (atomic_add_return ((i), (v)) < 0) +static __inline__ int atomic_cmpxchg(atomic_t *v, int old, int new) +{ + int ret; + unsigned long flags; + + local_irq_save(flags); + ret = v->counter; + if (likely(ret == old)) + v->counter = new; + local_irq_restore(flags); + + return ret; +} + /* Atomic operations are already serializing on ARM */ #define smp_mb__before_atomic_dec() barrier() #define smp_mb__after_atomic_dec() barrier() Index: linux-2.6/include/asm-xtensa/atomic.h =================================================================== --- linux-2.6.orig/include/asm-xtensa/atomic.h +++ linux-2.6/include/asm-xtensa/atomic.h @@ -223,6 +223,7 @@ static inline int atomic_sub_return(int */ #define atomic_add_negative(i,v) (atomic_add_return((i),(v)) < 0) +#define atomic_cmpxchg(v, o, n) ((int)cmpxchg(&((v)->counter), (o), (n))) static inline void atomic_clear_mask(unsigned int mask, atomic_t *v) { Index: linux-2.6/include/asm-sh/atomic.h =================================================================== --- linux-2.6.orig/include/asm-sh/atomic.h +++ linux-2.6/include/asm-sh/atomic.h @@ -87,6 +87,20 @@ static __inline__ int atomic_sub_return( #define atomic_inc(v) atomic_add(1,(v)) #define atomic_dec(v) atomic_sub(1,(v)) +static __inline__ int atomic_cmpxchg(atomic_t *v, int old, int new) +{ + int ret; + unsigned long flags; + + local_irq_save(flags); + ret = v->counter; + if (likely(ret == old)) + v->counter = new; + local_irq_restore(flags); + + return ret; +} + static __inline__ void atomic_clear_mask(unsigned int mask, atomic_t *v) { unsigned long flags; Index: linux-2.6/include/asm-arm/atomic.h =================================================================== --- linux-2.6.orig/include/asm-arm/atomic.h +++ linux-2.6/include/asm-arm/atomic.h @@ -80,6 +80,23 @@ static inline int atomic_sub_return(int return result; } +static inline int atomic_cmpxchg(atomic_t *ptr, int old, int new) +{ + u32 oldval, res; + + do { + __asm__ __volatile__("@ atomic_cmpxchg\n" + "ldrex %1, [%2]\n" + "teq %1, %3\n" + "strexeq %0, %4, [%2]\n" + : "=&r" (res), "=&r" (oldval) + : "r" (&ptr->counter), "r" (old), "r" (new) + : "cc"); + } while (res); + + return oldval; +} + static inline void atomic_clear_mask(unsigned long mask, unsigned long *addr) { unsigned long tmp, tmp2; @@ -131,6 +148,21 @@ static inline int atomic_sub_return(int return val; } +static inline int atomic_cmpxchg(atomic_t *v, int old, int new) +{ + int ret; + unsigned long flags; + + local_irq_save(flags); + ret = v->counter; + if (likely(ret == old)) + v->counter = new; + local_irq_restore(flags); + + return ret; +} + +static inline void atomic_clear_mask(unsigned long mask, unsigned long *addr) static inline void atomic_clear_mask(unsigned long mask, unsigned long *addr) { unsigned long flags; Index: linux-2.6/include/asm-cris/atomic.h =================================================================== --- linux-2.6.orig/include/asm-cris/atomic.h +++ linux-2.6/include/asm-cris/atomic.h @@ -123,6 +123,19 @@ extern __inline__ int atomic_inc_and_tes return retval; } +static __inline__ int atomic_cmpxchg(atomic_t *v, int old, int new) +{ + int ret; + unsigned long flags; + cris_atomic_save(v, flags); + ret = v->counter; + if (likely(ret == old)) + v->counter = new; + cris_atomic_restore(v, flags); + return ret; +} + +static inline void atomic_clear_mask(unsigned long mask, unsigned long *addr) /* Atomic operations are already serializing */ #define smp_mb__before_atomic_dec() barrier() #define smp_mb__after_atomic_dec() barrier() Index: linux-2.6/include/asm-powerpc/atomic.h =================================================================== --- linux-2.6.orig/include/asm-powerpc/atomic.h +++ linux-2.6/include/asm-powerpc/atomic.h @@ -172,6 +172,8 @@ static __inline__ int atomic_dec_return( return t; } +#define atomic_cmpxchg(v, o, n) ((int)cmpxchg(&((v)->counter), (o), (n))) + #define atomic_sub_and_test(a, v) (atomic_sub_return((a), (v)) == 0) #define atomic_dec_and_test(v) (atomic_dec_return((v)) == 0) ^ permalink raw reply [flat|nested] 15+ messages in thread
* [patch 3/5] atomic: inc_not_zero 2005-11-05 7:57 ` [patch 2/5] atomic: cmpxchg Nick Piggin @ 2005-11-05 7:58 ` Nick Piggin 2005-11-05 7:58 ` [patch 4/5] rcu file: use atomic Nick Piggin 2005-11-05 9:00 ` [patch 2/5] atomic: cmpxchg Russell King 1 sibling, 1 reply; 15+ messages in thread From: Nick Piggin @ 2005-11-05 7:58 UTC (permalink / raw) To: Andrew Morton, linux-kernel, Linus Torvalds [-- Attachment #1: Type: text/plain, Size: 33 bytes --] 3/5 -- SUSE Labs, Novell Inc. [-- Attachment #2: atomic_inc_not_zero.patch --] [-- Type: text/plain, Size: 19494 bytes --] Introduce an atomic_inc_not_zero operation. Make this a special case of atomic_add_unless because lockless pagecache actually wants atomic_inc_not_negativeone due to its offset refcount. Signed-off-by: Nick Piggin <npiggin@suse.de> Index: linux-2.6/include/asm-alpha/atomic.h =================================================================== --- linux-2.6.orig/include/asm-alpha/atomic.h +++ linux-2.6/include/asm-alpha/atomic.h @@ -179,6 +179,16 @@ static __inline__ long atomic64_sub_retu #define atomic_cmpxchg(v, o, n) ((int)cmpxchg(&((v)->counter), (o), (n))) +#define atomic_add_unless(v, a, u) \ +({ \ + int c, old; \ + c = atomic_read(v); \ + while (c != (u) && (old = atomic_cmpxchg((v), c, c + (a))) != c) \ + c = old; \ + c != (u); \ +}) +#define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0) + #define atomic_dec_return(v) atomic_sub_return(1,(v)) #define atomic64_dec_return(v) atomic64_sub_return(1,(v)) Index: linux-2.6/include/asm-arm26/atomic.h =================================================================== --- linux-2.6.orig/include/asm-arm26/atomic.h +++ linux-2.6/include/asm-arm26/atomic.h @@ -76,6 +76,21 @@ static inline int atomic_cmpxchg(atomic_ return ret; } +static inline int atomic_add_unless(atomic_t *v, int a, int u) +{ + int ret; + unsigned long flags; + + local_irq_save(flags); + ret = v->counter; + if (ret != u) + v->counter += a; + local_irq_restore(flags); + + return ret != u; +} +#define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0) + static inline void atomic_clear_mask(unsigned long mask, unsigned long *addr) { unsigned long flags; Index: linux-2.6/include/asm-frv/atomic.h =================================================================== --- linux-2.6.orig/include/asm-frv/atomic.h +++ linux-2.6/include/asm-frv/atomic.h @@ -416,4 +416,14 @@ extern uint32_t __cmpxchg_32(uint32_t *v #define atomic_cmpxchg(v, old, new) ((int)cmpxchg(&((v)->counter), old, new)) +#define atomic_add_unless(v, a, u) \ +({ \ + int c, old; \ + c = atomic_read(v); \ + while (c != (u) && (old = atomic_cmpxchg((v), c, c + (a))) != c) \ + c = old; \ + c != (u); \ +}) +#define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0) + #endif /* _ASM_ATOMIC_H */ Index: linux-2.6/include/asm-h8300/atomic.h =================================================================== --- linux-2.6.orig/include/asm-h8300/atomic.h +++ linux-2.6/include/asm-h8300/atomic.h @@ -94,6 +94,19 @@ static __inline__ int atomic_cmpxchg(ato return ret; } +static __inline__ int atomic_add_unless(atomic_t *v, int a, int u) +{ + int ret; + unsigned long flags; + local_irq_save(flags); + ret = v->counter; + if (ret != u) + v->counter += a; + local_irq_restore(flags); + return ret != u; +} +#define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0) + static __inline__ void atomic_clear_mask(unsigned long mask, unsigned long *v) { __asm__ __volatile__("stc ccr,r1l\n\t" Index: linux-2.6/include/asm-i386/atomic.h =================================================================== --- linux-2.6.orig/include/asm-i386/atomic.h +++ linux-2.6/include/asm-i386/atomic.h @@ -217,6 +217,25 @@ static __inline__ int atomic_sub_return( #define atomic_cmpxchg(v, old, new) ((int)cmpxchg(&((v)->counter), old, new)) +/** + * atomic_add_unless - add unless the number is a given value + * @v: pointer of type atomic_t + * @a: the amount to add to v... + * @u: ...unless v is equal to u. + * + * Atomically adds @a to @v, so long as it was not @u. + * Returns non-zero if @v was not @u, and zero otherwise. + */ +#define atomic_add_unless(v, a, u) \ +({ \ + int c, old; \ + c = atomic_read(v); \ + while (c != (u) && (old = atomic_cmpxchg((v), c, c + (a))) != c) \ + c = old; \ + c != (u); \ +}) +#define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0) + #define atomic_inc_return(v) (atomic_add_return(1,v)) #define atomic_dec_return(v) (atomic_sub_return(1,v)) Index: linux-2.6/include/asm-ia64/atomic.h =================================================================== --- linux-2.6.orig/include/asm-ia64/atomic.h +++ linux-2.6/include/asm-ia64/atomic.h @@ -90,6 +90,16 @@ ia64_atomic64_sub (__s64 i, atomic64_t * #define atomic_cmpxchg(v, old, new) ((int)cmpxchg(&((v)->counter), old, new)) +#define atomic_add_unless(v, a, u) \ +({ \ + int c, old; \ + c = atomic_read(v); \ + while (c != (u) && (old = atomic_cmpxchg((v), c, c + (a))) != c) \ + c = old; \ + c != (u); \ +}) +#define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0) + #define atomic_add_return(i,v) \ ({ \ int __ia64_aar_i = (i); \ Index: linux-2.6/include/asm-m68k/atomic.h =================================================================== --- linux-2.6.orig/include/asm-m68k/atomic.h +++ linux-2.6/include/asm-m68k/atomic.h @@ -141,6 +141,16 @@ static inline void atomic_set_mask(unsig #define atomic_cmpxchg(v, o, n) ((int)cmpxchg(&((v)->counter), (o), (n))) +#define atomic_add_unless(v, a, u) \ +({ \ + int c, old; \ + c = atomic_read(v); \ + while (c != (u) && (old = atomic_cmpxchg((v), c, c + (a))) != c) \ + c = old; \ + c != (u); \ +}) +#define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0) + /* Atomic operations are already serializing */ #define smp_mb__before_atomic_dec() barrier() #define smp_mb__after_atomic_dec() barrier() Index: linux-2.6/include/asm-m68knommu/atomic.h =================================================================== --- linux-2.6.orig/include/asm-m68knommu/atomic.h +++ linux-2.6/include/asm-m68knommu/atomic.h @@ -130,6 +130,16 @@ static inline int atomic_sub_return(int #define atomic_cmpxchg(v, o, n) ((int)cmpxchg(&((v)->counter), (o), (n))) +#define atomic_add_unless(v, a, u) \ +({ \ + int c, old; \ + c = atomic_read(v); \ + while (c != (u) && (old = atomic_cmpxchg((v), c, c + (a))) != c) \ + c = old; \ + c != (u); \ +}) +#define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0) + #define atomic_dec_return(v) atomic_sub_return(1,(v)) #define atomic_inc_return(v) atomic_add_return(1,(v)) Index: linux-2.6/include/asm-mips/atomic.h =================================================================== --- linux-2.6.orig/include/asm-mips/atomic.h +++ linux-2.6/include/asm-mips/atomic.h @@ -289,6 +289,25 @@ static __inline__ int atomic_sub_if_posi #define atomic_cmpxchg(v, o, n) ((int)cmpxchg(&((v)->counter), (o), (n))) +/** + * atomic_add_unless - add unless the number is a given value + * @v: pointer of type atomic_t + * @a: the amount to add to v... + * @u: ...unless v is equal to u. + * + * Atomically adds @a to @v, so long as it was not @u. + * Returns non-zero if @v was not @u, and zero otherwise. + */ +#define atomic_add_unless(v, a, u) \ +({ \ + int c, old; \ + c = atomic_read(v); \ + while (c != (u) && (old = atomic_cmpxchg((v), c, c + (a))) != c) \ + c = old; \ + c != (u); \ +}) +#define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0) + #define atomic_dec_return(v) atomic_sub_return(1,(v)) #define atomic_inc_return(v) atomic_add_return(1,(v)) Index: linux-2.6/include/asm-parisc/atomic.h =================================================================== --- linux-2.6.orig/include/asm-parisc/atomic.h +++ linux-2.6/include/asm-parisc/atomic.h @@ -166,6 +166,25 @@ static __inline__ int atomic_read(const /* exported interface */ #define atomic_cmpxchg(v, o, n) ((int)cmpxchg(&((v)->counter), (o), (n))) +/** + * atomic_add_unless - add unless the number is a given value + * @v: pointer of type atomic_t + * @a: the amount to add to v... + * @u: ...unless v is equal to u. + * + * Atomically adds @a to @v, so long as it was not @u. + * Returns non-zero if @v was not @u, and zero otherwise. + */ +#define atomic_add_unless(v, a, u) \ +({ \ + int c, old; \ + c = atomic_read(v); \ + while (c != (u) && (old = atomic_cmpxchg((v), c, c + (a))) != c) \ + c = old; \ + c != (u); \ +}) +#define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0) + #define atomic_add(i,v) ((void)(__atomic_add_return( ((int)i),(v)))) #define atomic_sub(i,v) ((void)(__atomic_add_return(-((int)i),(v)))) #define atomic_inc(v) ((void)(__atomic_add_return( 1,(v)))) Index: linux-2.6/include/asm-s390/atomic.h =================================================================== --- linux-2.6.orig/include/asm-s390/atomic.h +++ linux-2.6/include/asm-s390/atomic.h @@ -200,6 +200,16 @@ atomic_compare_and_swap(int expected_old #define atomic_cmpxchg(v, o, n) (atomic_compare_and_swap((o), (n), &((v)->counter))) +#define atomic_add_unless(v, a, u) \ +({ \ + int c, old; \ + c = atomic_read(v); \ + while (c != (u) && (old = atomic_cmpxchg((v), c, c + (a))) != c) \ + c = old; \ + c != (u); \ +}) +#define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0) + #define smp_mb__before_atomic_dec() smp_mb() #define smp_mb__after_atomic_dec() smp_mb() #define smp_mb__before_atomic_inc() smp_mb() Index: linux-2.6/include/asm-sh/atomic.h =================================================================== --- linux-2.6.orig/include/asm-sh/atomic.h +++ linux-2.6/include/asm-sh/atomic.h @@ -101,6 +101,21 @@ static __inline__ int atomic_cmpxchg(ato return ret; } +static __inline__ int atomic_add_unless(atomic_t *v, int a, int u) +{ + int ret; + unsigned long flags; + + local_irq_save(flags); + ret = v->counter; + if (ret != u) + v->counter += a; + local_irq_restore(flags); + + return ret != u; +} +#define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0) + static __inline__ void atomic_clear_mask(unsigned int mask, atomic_t *v) { unsigned long flags; Index: linux-2.6/include/asm-sh64/atomic.h =================================================================== --- linux-2.6.orig/include/asm-sh64/atomic.h +++ linux-2.6/include/asm-sh64/atomic.h @@ -113,6 +113,21 @@ static __inline__ int atomic_cmpxchg(ato return ret; } +static __inline__ int atomic_add_unless(atomic_t *v, int a, int u) +{ + int ret; + unsigned long flags; + + local_irq_save(flags); + ret = v->counter; + if (ret != u) + v->counter += a; + local_irq_restore(flags); + + return ret != u; +} +#define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0) + static __inline__ void atomic_clear_mask(unsigned int mask, atomic_t *v) { unsigned long flags; Index: linux-2.6/include/asm-sparc/atomic.h =================================================================== --- linux-2.6.orig/include/asm-sparc/atomic.h +++ linux-2.6/include/asm-sparc/atomic.h @@ -20,6 +20,7 @@ typedef struct { volatile int counter; } extern int __atomic_add_return(int, atomic_t *); extern int atomic_cmpxchg(atomic_t *, int, int); +extern int atomic_add_unless(atomic_t *, int, int); extern void atomic_set(atomic_t *, int); #define atomic_read(v) ((v)->counter) @@ -49,6 +50,8 @@ extern void atomic_set(atomic_t *, int); #define atomic_dec_and_test(v) (atomic_dec_return(v) == 0) #define atomic_sub_and_test(i, v) (atomic_sub_return(i, v) == 0) +#define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0) + /* This is the old 24-bit implementation. It's still used internally * by some sparc-specific code, notably the semaphore implementation. */ Index: linux-2.6/include/asm-sparc64/atomic.h =================================================================== --- linux-2.6.orig/include/asm-sparc64/atomic.h +++ linux-2.6/include/asm-sparc64/atomic.h @@ -72,6 +72,16 @@ extern int atomic64_sub_ret(int, atomic6 #define atomic_cmpxchg(v, o, n) ((int)cmpxchg(&((v)->counter), (o), (n))) +#define atomic_add_unless(v, a, u) \ +({ \ + int c, old; \ + c = atomic_read(v); \ + while (c != (u) && (old = atomic_cmpxchg((v), c, c + (a))) != c) \ + c = old; \ + c != (u); \ +}) +#define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0) + /* Atomic operations are already serializing */ #ifdef CONFIG_SMP #define smp_mb__before_atomic_dec() membar_storeload_loadload(); Index: linux-2.6/include/asm-v850/atomic.h =================================================================== --- linux-2.6.orig/include/asm-v850/atomic.h +++ linux-2.6/include/asm-v850/atomic.h @@ -104,6 +104,22 @@ static __inline__ int atomic_cmpxchg(ato return ret; } +static __inline__ int atomic_add_unless(atomic_t *v, int a, int u) +{ + int ret; + unsigned long flags; + + local_irq_save(flags); + ret = v->counter; + if (ret != u) + v->counter += a; + local_irq_restore(flags); + + return ret != u; +} + +#define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0) + /* Atomic operations are already serializing on ARM */ #define smp_mb__before_atomic_dec() barrier() #define smp_mb__after_atomic_dec() barrier() Index: linux-2.6/include/asm-x86_64/atomic.h =================================================================== --- linux-2.6.orig/include/asm-x86_64/atomic.h +++ linux-2.6/include/asm-x86_64/atomic.h @@ -362,6 +362,25 @@ static __inline__ int atomic_sub_return( #define atomic_cmpxchg(v, old, new) ((int)cmpxchg(&((v)->counter), old, new)) +/** + * atomic_add_unless - add unless the number is a given value + * @v: pointer of type atomic_t + * @a: the amount to add to v... + * @u: ...unless v is equal to u. + * + * Atomically adds @a to @v, so long as it was not @u. + * Returns non-zero if @v was not @u, and zero otherwise. + */ +#define atomic_add_unless(v, a, u) \ +({ \ + int c, old; \ + c = atomic_read(v); \ + while (c != (u) && (old = atomic_cmpxchg((v), c, c + (a))) != c) \ + c = old; \ + c != (u); \ +}) +#define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0) + #define atomic_inc_return(v) (atomic_add_return(1,v)) #define atomic_dec_return(v) (atomic_sub_return(1,v)) Index: linux-2.6/include/asm-xtensa/atomic.h =================================================================== --- linux-2.6.orig/include/asm-xtensa/atomic.h +++ linux-2.6/include/asm-xtensa/atomic.h @@ -225,6 +225,25 @@ static inline int atomic_sub_return(int #define atomic_cmpxchg(v, o, n) ((int)cmpxchg(&((v)->counter), (o), (n))) +/** + * atomic_add_unless - add unless the number is a given value + * @v: pointer of type atomic_t + * @a: the amount to add to v... + * @u: ...unless v is equal to u. + * + * Atomically adds @a to @v, so long as it was not @u. + * Returns non-zero if @v was not @u, and zero otherwise. + */ +#define atomic_add_unless(v, a, u) \ +({ \ + int c, old; \ + c = atomic_read(v); \ + while (c != (u) && (old = atomic_cmpxchg((v), c, c + (a))) != c) \ + c = old; \ + c != (u); \ +}) +#define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0) + static inline void atomic_clear_mask(unsigned int mask, atomic_t *v) { unsigned int all_f = -1; Index: linux-2.6/include/asm-cris/atomic.h =================================================================== --- linux-2.6.orig/include/asm-cris/atomic.h +++ linux-2.6/include/asm-cris/atomic.h @@ -135,6 +135,19 @@ static __inline__ int atomic_cmpxchg(ato return ret; } +static __inline__ int atomic_add_unless(atomic_t *v, int a, int u) +{ + int ret; + unsigned long flags; + cris_atomic_save(v, flags); + ret = v->counter; + if (ret != u) + v->counter += a; + cris_atomic_restore(v, flags); + return ret != u; +} +#define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0) + static inline void atomic_clear_mask(unsigned long mask, unsigned long *addr) /* Atomic operations are already serializing */ #define smp_mb__before_atomic_dec() barrier() Index: linux-2.6/arch/sparc/lib/atomic32.c =================================================================== --- linux-2.6.orig/arch/sparc/lib/atomic32.c +++ linux-2.6/arch/sparc/lib/atomic32.c @@ -52,6 +52,20 @@ int atomic_cmpxchg(atomic_t *v, int old, return ret; } +int atomic_add_unless(atomic_t *v, int a, int u) +{ + int ret; + unsigned long flags; + spin_lock_irqsave(ATOMIC_HASH(v), flags); + ret = v->counter; + if (ret != u) + v->counter += a; + spin_unlock_irqrestore(ATOMIC_HASH(v), flags); + return ret != u; +} + +static inline void atomic_clear_mask(unsigned long mask, unsigned long *addr) +/* Atomic operations are already serializing */ void atomic_set(atomic_t *v, int i) { unsigned long flags; Index: linux-2.6/Documentation/atomic_ops.txt =================================================================== --- linux-2.6.orig/Documentation/atomic_ops.txt +++ linux-2.6/Documentation/atomic_ops.txt @@ -115,7 +115,7 @@ boolean is return which indicates whethe is negative. It requires explicit memory barrier semantics around the operation. -Finally: +Then: int atomic_cmpxchg(atomic_t *v, int old, int new); @@ -129,6 +129,18 @@ atomic_cmpxchg requires explicit memory The semantics for atomic_cmpxchg are the same as those defined for 'cas' below. +Finally: + + int atomic_add_unless(atomic_t *v, int a, int u); + +If the atomic value v is not equal to u, this function adds a to v, and +returns non zero. If v is equal to u then it returns zero. This is done as +an atomic operation. + +atomic_add_unless requires explicit memory barriers around the operation. + +atomic_inc_not_zero, equivalent to atomic_add_unless(v, 1, 0) + If a caller requires memory barrier semantics around an atomic_t operation which does not return a value, a set of interfaces are Index: linux-2.6/include/asm-arm/atomic.h =================================================================== --- linux-2.6.orig/include/asm-arm/atomic.h +++ linux-2.6/include/asm-arm/atomic.h @@ -174,6 +174,16 @@ static inline void atomic_clear_mask(uns #endif /* __LINUX_ARM_ARCH__ */ +static inline int atomic_add_unless(atomic_t *v, int a, int u) +{ + int c, old; + c = atomic_read(v); + while (c != u && (old = atomic_cmpxchg((v), c, c + a)) != c) + c = old; + return c != u; +} +#define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0) + #define atomic_add(i, v) (void) atomic_add_return(i, v) #define atomic_inc(v) (void) atomic_add_return(1, v) #define atomic_sub(i, v) (void) atomic_sub_return(i, v) Index: linux-2.6/include/asm-powerpc/atomic.h =================================================================== --- linux-2.6.orig/include/asm-powerpc/atomic.h +++ linux-2.6/include/asm-powerpc/atomic.h @@ -174,6 +174,31 @@ static __inline__ int atomic_dec_return( #define atomic_cmpxchg(v, o, n) ((int)cmpxchg(&((v)->counter), (o), (n))) +/** + * atomic_add_unless - add unless the number is a given value + * @v: pointer of type atomic_t + * @a: the amount to add to v... + * @u: ...unless v is equal to u. + * + * Atomically adds @a to @v, so long as it was not @u. + * Returns non-zero if @v was not @u, and zero otherwise. + */ +#define atomic_add_unless(v, a, u) \ +({ \ + int c, old; \ + c = atomic_read(v); \ + for (;;) { \ + if (unlikely(c == (u))) \ + break; \ + old = atomic_cmpxchg((v), c, c + (a)); \ + if (likely(old == c)) \ + break; \ + c = old; \ + } \ + c != (u); \ +}) +#define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0) + #define atomic_sub_and_test(a, v) (atomic_sub_return((a), (v)) == 0) #define atomic_dec_and_test(v) (atomic_dec_return((v)) == 0) ^ permalink raw reply [flat|nested] 15+ messages in thread
* [patch 4/5] rcu file: use atomic 2005-11-05 7:58 ` [patch 3/5] atomic: inc_not_zero Nick Piggin @ 2005-11-05 7:58 ` Nick Piggin 2005-11-05 7:59 ` [patch 5/5] atomic: dec_and_lock " Nick Piggin 0 siblings, 1 reply; 15+ messages in thread From: Nick Piggin @ 2005-11-05 7:58 UTC (permalink / raw) To: Andrew Morton, linux-kernel, Linus Torvalds [-- Attachment #1: Type: text/plain, Size: 33 bytes --] 4/5 -- SUSE Labs, Novell Inc. [-- Attachment #2: rcu-file-use-atomic-primitives.patch --] [-- Type: text/plain, Size: 15853 bytes --] Use atomic_inc_not_zero for rcu files instead of specal case rcuref. Signed-off-by: Nick Piggin <npiggin@suse.de> Index: linux-2.6/include/linux/rcuref.h =================================================================== --- linux-2.6.orig/include/linux/rcuref.h +++ /dev/null @@ -1,220 +0,0 @@ -/* - * rcuref.h - * - * Reference counting for elements of lists/arrays protected by - * RCU. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * - * Copyright (C) IBM Corporation, 2005 - * - * Author: Dipankar Sarma <dipankar@in.ibm.com> - * Ravikiran Thirumalai <kiran_th@gmail.com> - * - * See Documentation/RCU/rcuref.txt for detailed user guide. - * - */ - -#ifndef _RCUREF_H_ -#define _RCUREF_H_ - -#ifdef __KERNEL__ - -#include <linux/types.h> -#include <linux/interrupt.h> -#include <linux/spinlock.h> -#include <asm/atomic.h> - -/* - * These APIs work on traditional atomic_t counters used in the - * kernel for reference counting. Under special circumstances - * where a lock-free get() operation races with a put() operation - * these APIs can be used. See Documentation/RCU/rcuref.txt. - */ - -#ifdef __HAVE_ARCH_CMPXCHG - -/** - * rcuref_inc - increment refcount for object. - * @rcuref: reference counter in the object in question. - * - * This should be used only for objects where we use RCU and - * use the rcuref_inc_lf() api to acquire a reference - * in a lock-free reader-side critical section. - */ -static inline void rcuref_inc(atomic_t *rcuref) -{ - atomic_inc(rcuref); -} - -/** - * rcuref_dec - decrement refcount for object. - * @rcuref: reference counter in the object in question. - * - * This should be used only for objects where we use RCU and - * use the rcuref_inc_lf() api to acquire a reference - * in a lock-free reader-side critical section. - */ -static inline void rcuref_dec(atomic_t *rcuref) -{ - atomic_dec(rcuref); -} - -/** - * rcuref_dec_and_test - decrement refcount for object and test - * @rcuref: reference counter in the object. - * @release: pointer to the function that will clean up the object - * when the last reference to the object is released. - * This pointer is required. - * - * Decrement the refcount, and if 0, return 1. Else return 0. - * - * This should be used only for objects where we use RCU and - * use the rcuref_inc_lf() api to acquire a reference - * in a lock-free reader-side critical section. - */ -static inline int rcuref_dec_and_test(atomic_t *rcuref) -{ - return atomic_dec_and_test(rcuref); -} - -/* - * cmpxchg is needed on UP too, if deletions to the list/array can happen - * in interrupt context. - */ - -/** - * rcuref_inc_lf - Take reference to an object in a read-side - * critical section protected by RCU. - * @rcuref: reference counter in the object in question. - * - * Try and increment the refcount by 1. The increment might fail if - * the reference counter has been through a 1 to 0 transition and - * is no longer part of the lock-free list. - * Returns non-zero on successful increment and zero otherwise. - */ -static inline int rcuref_inc_lf(atomic_t *rcuref) -{ - int c, old; - c = atomic_read(rcuref); - while (c && (old = cmpxchg(&rcuref->counter, c, c + 1)) != c) - c = old; - return c; -} - -#else /* !__HAVE_ARCH_CMPXCHG */ - -extern spinlock_t __rcuref_hash[]; - -/* - * Use a hash table of locks to protect the reference count - * since cmpxchg is not available in this arch. - */ -#ifdef CONFIG_SMP -#define RCUREF_HASH_SIZE 4 -#define RCUREF_HASH(k) \ - (&__rcuref_hash[(((unsigned long)k)>>8) & (RCUREF_HASH_SIZE-1)]) -#else -#define RCUREF_HASH_SIZE 1 -#define RCUREF_HASH(k) &__rcuref_hash[0] -#endif /* CONFIG_SMP */ - -/** - * rcuref_inc - increment refcount for object. - * @rcuref: reference counter in the object in question. - * - * This should be used only for objects where we use RCU and - * use the rcuref_inc_lf() api to acquire a reference in a lock-free - * reader-side critical section. - */ -static inline void rcuref_inc(atomic_t *rcuref) -{ - unsigned long flags; - spin_lock_irqsave(RCUREF_HASH(rcuref), flags); - rcuref->counter += 1; - spin_unlock_irqrestore(RCUREF_HASH(rcuref), flags); -} - -/** - * rcuref_dec - decrement refcount for object. - * @rcuref: reference counter in the object in question. - * - * This should be used only for objects where we use RCU and - * use the rcuref_inc_lf() api to acquire a reference in a lock-free - * reader-side critical section. - */ -static inline void rcuref_dec(atomic_t *rcuref) -{ - unsigned long flags; - spin_lock_irqsave(RCUREF_HASH(rcuref), flags); - rcuref->counter -= 1; - spin_unlock_irqrestore(RCUREF_HASH(rcuref), flags); -} - -/** - * rcuref_dec_and_test - decrement refcount for object and test - * @rcuref: reference counter in the object. - * @release: pointer to the function that will clean up the object - * when the last reference to the object is released. - * This pointer is required. - * - * Decrement the refcount, and if 0, return 1. Else return 0. - * - * This should be used only for objects where we use RCU and - * use the rcuref_inc_lf() api to acquire a reference in a lock-free - * reader-side critical section. - */ -static inline int rcuref_dec_and_test(atomic_t *rcuref) -{ - unsigned long flags; - spin_lock_irqsave(RCUREF_HASH(rcuref), flags); - rcuref->counter--; - if (!rcuref->counter) { - spin_unlock_irqrestore(RCUREF_HASH(rcuref), flags); - return 1; - } else { - spin_unlock_irqrestore(RCUREF_HASH(rcuref), flags); - return 0; - } -} - -/** - * rcuref_inc_lf - Take reference to an object of a lock-free collection - * by traversing a lock-free list/array. - * @rcuref: reference counter in the object in question. - * - * Try and increment the refcount by 1. The increment might fail if - * the reference counter has been through a 1 to 0 transition and - * object is no longer part of the lock-free list. - * Returns non-zero on successful increment and zero otherwise. - */ -static inline int rcuref_inc_lf(atomic_t *rcuref) -{ - int ret; - unsigned long flags; - spin_lock_irqsave(RCUREF_HASH(rcuref), flags); - if (rcuref->counter) - ret = rcuref->counter++; - else - ret = 0; - spin_unlock_irqrestore(RCUREF_HASH(rcuref), flags); - return ret; -} - - -#endif /* !__HAVE_ARCH_CMPXCHG */ - -#endif /* __KERNEL__ */ -#endif /* _RCUREF_H_ */ Index: linux-2.6/fs/aio.c =================================================================== --- linux-2.6.orig/fs/aio.c +++ linux-2.6/fs/aio.c @@ -29,7 +29,6 @@ #include <linux/highmem.h> #include <linux/workqueue.h> #include <linux/security.h> -#include <linux/rcuref.h> #include <asm/kmap_types.h> #include <asm/uaccess.h> @@ -500,7 +499,7 @@ static int __aio_put_req(struct kioctx * /* Must be done under the lock to serialise against cancellation. * Call this aio_fput as it duplicates fput via the fput_work. */ - if (unlikely(rcuref_dec_and_test(&req->ki_filp->f_count))) { + if (unlikely(atomic_dec_and_test(&req->ki_filp->f_count))) { get_ioctx(ctx); spin_lock(&fput_lock); list_add(&req->ki_list, &fput_head); Index: linux-2.6/fs/file_table.c =================================================================== --- linux-2.6.orig/fs/file_table.c +++ linux-2.6/fs/file_table.c @@ -117,7 +117,7 @@ EXPORT_SYMBOL(get_empty_filp); void fastcall fput(struct file *file) { - if (rcuref_dec_and_test(&file->f_count)) + if (atomic_dec_and_test(&file->f_count)) __fput(file); } @@ -166,7 +166,7 @@ struct file fastcall *fget(unsigned int rcu_read_lock(); file = fcheck_files(files, fd); if (file) { - if (!rcuref_inc_lf(&file->f_count)) { + if (!atomic_inc_not_zero(&file->f_count)) { /* File object ref couldn't be taken */ rcu_read_unlock(); return NULL; @@ -198,7 +198,7 @@ struct file fastcall *fget_light(unsigne rcu_read_lock(); file = fcheck_files(files, fd); if (file) { - if (rcuref_inc_lf(&file->f_count)) + if (atomic_inc_not_zero(&file->f_count)) *fput_needed = 1; else /* Didn't get the reference, someone's freed */ @@ -213,7 +213,7 @@ struct file fastcall *fget_light(unsigne void put_filp(struct file *file) { - if (rcuref_dec_and_test(&file->f_count)) { + if (atomic_dec_and_test(&file->f_count)) { security_file_free(file); file_kill(file); file_free(file); Index: linux-2.6/include/linux/fs.h =================================================================== --- linux-2.6.orig/include/linux/fs.h +++ linux-2.6/include/linux/fs.h @@ -9,7 +9,6 @@ #include <linux/config.h> #include <linux/limits.h> #include <linux/ioctl.h> -#include <linux/rcuref.h> /* * It's silly to have NR_OPEN bigger than NR_FILE, but you can change @@ -610,7 +609,7 @@ extern spinlock_t files_lock; #define file_list_lock() spin_lock(&files_lock); #define file_list_unlock() spin_unlock(&files_lock); -#define get_file(x) rcuref_inc(&(x)->f_count) +#define get_file(x) atomic_inc(&(x)->f_count) #define file_count(x) atomic_read(&(x)->f_count) #define MAX_NON_LFS ((1UL<<31) - 1) Index: linux-2.6/Documentation/RCU/rcuref.txt =================================================================== --- linux-2.6.orig/Documentation/RCU/rcuref.txt +++ linux-2.6/Documentation/RCU/rcuref.txt @@ -1,74 +1,67 @@ -Refcounter framework for elements of lists/arrays protected by -RCU. +Refcounter design for elements of lists/arrays protected by RCU. Refcounting on elements of lists which are protected by traditional reader/writer spinlocks or semaphores are straight forward as in: -1. 2. -add() search_and_reference() -{ { - alloc_object read_lock(&list_lock); - ... search_for_element - atomic_set(&el->rc, 1); atomic_inc(&el->rc); - write_lock(&list_lock); ... - add_element read_unlock(&list_lock); - ... ... - write_unlock(&list_lock); } +1. 2. +add() search_and_reference() +{ { + alloc_object read_lock(&list_lock); + ... search_for_element + atomic_set(&el->rc, 1); atomic_inc(&el->rc); + write_lock(&list_lock); ... + add_element read_unlock(&list_lock); + ... ... + write_unlock(&list_lock); } } 3. 4. release_referenced() delete() { { - ... write_lock(&list_lock); - atomic_dec(&el->rc, relfunc) ... - ... delete_element -} write_unlock(&list_lock); - ... - if (atomic_dec_and_test(&el->rc)) - kfree(el); - ... + ... write_lock(&list_lock); + atomic_dec(&el->rc, relfunc) ... + ... delete_element +} write_unlock(&list_lock); + ... + if (atomic_dec_and_test(&el->rc)) + kfree(el); + ... } If this list/array is made lock free using rcu as in changing the write_lock in add() and delete() to spin_lock and changing read_lock -in search_and_reference to rcu_read_lock(), the rcuref_get in +in search_and_reference to rcu_read_lock(), the atomic_get in search_and_reference could potentially hold reference to an element which -has already been deleted from the list/array. rcuref_lf_get_rcu takes +has already been deleted from the list/array. atomic_inc_not_zero takes care of this scenario. search_and_reference should look as; 1. 2. add() search_and_reference() { { - alloc_object rcu_read_lock(); - ... search_for_element - atomic_set(&el->rc, 1); if (rcuref_inc_lf(&el->rc)) { - write_lock(&list_lock); rcu_read_unlock(); - return FAIL; - add_element } - ... ... - write_unlock(&list_lock); rcu_read_unlock(); + alloc_object rcu_read_lock(); + ... search_for_element + atomic_set(&el->rc, 1); if (atomic_inc_not_zero(&el->rc)) { + write_lock(&list_lock); rcu_read_unlock(); + return FAIL; + add_element } + ... ... + write_unlock(&list_lock); rcu_read_unlock(); } } 3. 4. release_referenced() delete() { { - ... write_lock(&list_lock); - rcuref_dec(&el->rc, relfunc) ... - ... delete_element -} write_unlock(&list_lock); - ... - if (rcuref_dec_and_test(&el->rc)) - call_rcu(&el->head, el_free); - ... + ... write_lock(&list_lock); + atomic_dec(&el->rc, relfunc) ... + ... delete_element +} write_unlock(&list_lock); + ... + if (atomic_dec_and_test(&el->rc)) + call_rcu(&el->head, el_free); + ... } Sometimes, reference to the element need to be obtained in the -update (write) stream. In such cases, rcuref_inc_lf might be an overkill -since the spinlock serialising list updates are held. rcuref_inc +update (write) stream. In such cases, atomic_inc_not_zero might be an +overkill since the spinlock serialising list updates are held. atomic_inc is to be used in such cases. -For arches which do not have cmpxchg rcuref_inc_lf -api uses a hashed spinlock implementation and the same hashed spinlock -is acquired in all rcuref_xxx primitives to preserve atomicity. -Note: Use rcuref_inc api only if you need to use rcuref_inc_lf on the -refcounter atleast at one place. Mixing rcuref_inc and atomic_xxx api -might lead to races. rcuref_inc_lf() must be used in lockfree -RCU critical sections only. + Index: linux-2.6/kernel/rcupdate.c =================================================================== --- linux-2.6.orig/kernel/rcupdate.c +++ linux-2.6/kernel/rcupdate.c @@ -45,7 +45,6 @@ #include <linux/percpu.h> #include <linux/notifier.h> #include <linux/rcupdate.h> -#include <linux/rcuref.h> #include <linux/cpu.h> /* Definition for rcupdate control block. */ @@ -73,19 +72,6 @@ DEFINE_PER_CPU(struct rcu_data, rcu_bh_d static DEFINE_PER_CPU(struct tasklet_struct, rcu_tasklet) = {NULL}; static int maxbatch = 10000; -#ifndef __HAVE_ARCH_CMPXCHG -/* - * We use an array of spinlocks for the rcurefs -- similar to ones in sparc - * 32 bit atomic_t implementations, and a hash function similar to that - * for our refcounting needs. - * Can't help multiprocessors which donot have cmpxchg :( - */ - -spinlock_t __rcuref_hash[RCUREF_HASH_SIZE] = { - [0 ... (RCUREF_HASH_SIZE-1)] = SPIN_LOCK_UNLOCKED -}; -#endif - /** * call_rcu - Queue an RCU callback for invocation after a grace period. * @head: structure to be used for queueing the RCU updates. Index: linux-2.6/security/selinux/hooks.c =================================================================== --- linux-2.6.orig/security/selinux/hooks.c +++ linux-2.6/security/selinux/hooks.c @@ -1662,7 +1662,7 @@ static inline void flush_unauthorized_fi continue; } if (devnull) { - rcuref_inc(&devnull->f_count); + atomic_inc(&devnull->f_count); } else { devnull = dentry_open(dget(selinux_null), mntget(selinuxfs_mount), O_RDWR); if (!devnull) { Index: linux-2.6/kernel/rcutorture.c =================================================================== --- linux-2.6.orig/kernel/rcutorture.c +++ linux-2.6/kernel/rcutorture.c @@ -39,7 +39,6 @@ #include <linux/moduleparam.h> #include <linux/percpu.h> #include <linux/notifier.h> -#include <linux/rcuref.h> #include <linux/cpu.h> #include <linux/random.h> #include <linux/delay.h> ^ permalink raw reply [flat|nested] 15+ messages in thread
* [patch 5/5] atomic: dec_and_lock use atomic 2005-11-05 7:58 ` [patch 4/5] rcu file: use atomic Nick Piggin @ 2005-11-05 7:59 ` Nick Piggin 0 siblings, 0 replies; 15+ messages in thread From: Nick Piggin @ 2005-11-05 7:59 UTC (permalink / raw) To: Andrew Morton, linux-kernel, Linus Torvalds [-- Attachment #1: Type: text/plain, Size: 33 bytes --] 5/5 -- SUSE Labs, Novell Inc. [-- Attachment #2: atomic-dec_and_lock-use-atomic-primitives.patch --] [-- Type: text/plain, Size: 2316 bytes --] Use new atomic primitives for atomic_dec_and_lock Signed-off-by: Nick Piggin <npiggin@suse.de> Index: linux-2.6/lib/dec_and_lock.c =================================================================== --- linux-2.6.orig/lib/dec_and_lock.c +++ linux-2.6/lib/dec_and_lock.c @@ -1,47 +1,11 @@ #include <linux/module.h> #include <linux/spinlock.h> #include <asm/atomic.h> -#include <asm/system.h> -#ifdef __HAVE_ARCH_CMPXCHG /* * This is an implementation of the notion of "decrement a * reference count, and return locked if it decremented to zero". * - * This implementation can be used on any architecture that - * has a cmpxchg, and where atomic->value is an int holding - * the value of the atomic (i.e. the high bits aren't used - * for a lock or anything like that). - */ -int _atomic_dec_and_lock(atomic_t *atomic, spinlock_t *lock) -{ - int counter; - int newcount; - - for (;;) { - counter = atomic_read(atomic); - newcount = counter - 1; - if (!newcount) - break; /* do it the slow way */ - - newcount = cmpxchg(&atomic->counter, counter, newcount); - if (newcount == counter) - return 0; - } - - spin_lock(lock); - if (atomic_dec_and_test(atomic)) - return 1; - spin_unlock(lock); - return 0; -} -#else -/* - * This is an architecture-neutral, but slow, - * implementation of the notion of "decrement - * a reference count, and return locked if it - * decremented to zero". - * * NOTE NOTE NOTE! This is _not_ equivalent to * * if (atomic_dec_and_test(&atomic)) { @@ -52,21 +16,20 @@ int _atomic_dec_and_lock(atomic_t *atomi * * because the spin-lock and the decrement must be * "atomic". - * - * This slow version gets the spinlock unconditionally, - * and releases it if it isn't needed. Architectures - * are encouraged to come up with better approaches, - * this is trivially done efficiently using a load-locked - * store-conditional approach, for example. */ int _atomic_dec_and_lock(atomic_t *atomic, spinlock_t *lock) { +#ifdef CONFIG_SMP + /* Subtract 1 from counter unless that drops it to 0 (ie. it was 1) */ + if (atomic_add_unless(atomic, -1, 1)) + return 0; +#endif + /* Otherwise do it the slow way */ spin_lock(lock); if (atomic_dec_and_test(atomic)) return 1; spin_unlock(lock); return 0; } -#endif EXPORT_SYMBOL(_atomic_dec_and_lock); ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [patch 2/5] atomic: cmpxchg 2005-11-05 7:57 ` [patch 2/5] atomic: cmpxchg Nick Piggin 2005-11-05 7:58 ` [patch 3/5] atomic: inc_not_zero Nick Piggin @ 2005-11-05 9:00 ` Russell King 2005-11-05 9:10 ` Nick Piggin 1 sibling, 1 reply; 15+ messages in thread From: Russell King @ 2005-11-05 9:00 UTC (permalink / raw) To: Nick Piggin; +Cc: Andrew Morton, linux-kernel, Linus Torvalds On Sat, Nov 05, 2005 at 06:57:28PM +1100, Nick Piggin wrote: > Index: linux-2.6/include/asm-arm/atomic.h > =================================================================== > --- linux-2.6.orig/include/asm-arm/atomic.h > +++ linux-2.6/include/asm-arm/atomic.h > @@ -80,6 +80,23 @@ static inline int atomic_sub_return(int > return result; > } > > +static inline int atomic_cmpxchg(atomic_t *ptr, int old, int new) > +{ > + u32 oldval, res; > + > + do { > + __asm__ __volatile__("@ atomic_cmpxchg\n" > + "ldrex %1, [%2]\n" > + "teq %1, %3\n" > + "strexeq %0, %4, [%2]\n" > + : "=&r" (res), "=&r" (oldval) > + : "r" (&ptr->counter), "r" (old), "r" (new) > + : "cc"); > + } while (res); > + > + return oldval; > +} > + > static inline void atomic_clear_mask(unsigned long mask, unsigned long *addr) > { > unsigned long tmp, tmp2; > @@ -131,6 +148,21 @@ static inline int atomic_sub_return(int > return val; > } > > +static inline int atomic_cmpxchg(atomic_t *v, int old, int new) > +{ > + int ret; > + unsigned long flags; > + > + local_irq_save(flags); > + ret = v->counter; > + if (likely(ret == old)) > + v->counter = new; > + local_irq_restore(flags); > + > + return ret; > +} > + > +static inline void atomic_clear_mask(unsigned long mask, unsigned long *addr) > static inline void atomic_clear_mask(unsigned long mask, unsigned long *addr) This is obviously going to break ARM... -- Russell King Linux kernel 2.6 ARM Linux - http://www.arm.linux.org.uk/ maintainer of: 2.6 Serial core ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [patch 2/5] atomic: cmpxchg 2005-11-05 9:00 ` [patch 2/5] atomic: cmpxchg Russell King @ 2005-11-05 9:10 ` Nick Piggin 2005-11-05 9:13 ` Russell King 0 siblings, 1 reply; 15+ messages in thread From: Nick Piggin @ 2005-11-05 9:10 UTC (permalink / raw) To: Russell King; +Cc: Andrew Morton, linux-kernel, Linus Torvalds Russell King wrote: > On Sat, Nov 05, 2005 at 06:57:28PM +1100, Nick Piggin wrote: > >>Index: linux-2.6/include/asm-arm/atomic.h >>=================================================================== >>--- linux-2.6.orig/include/asm-arm/atomic.h >>+++ linux-2.6/include/asm-arm/atomic.h >>@@ -80,6 +80,23 @@ static inline int atomic_sub_return(int >> return result; >> } >> >>+static inline int atomic_cmpxchg(atomic_t *ptr, int old, int new) >>+{ >>+ u32 oldval, res; >>+ >>+ do { >>+ __asm__ __volatile__("@ atomic_cmpxchg\n" >>+ "ldrex %1, [%2]\n" >>+ "teq %1, %3\n" >>+ "strexeq %0, %4, [%2]\n" >>+ : "=&r" (res), "=&r" (oldval) >>+ : "r" (&ptr->counter), "r" (old), "r" (new) >>+ : "cc"); >>+ } while (res); >>+ >>+ return oldval; >>+} >>+ >> static inline void atomic_clear_mask(unsigned long mask, unsigned long *addr) >> { >> unsigned long tmp, tmp2; >>@@ -131,6 +148,21 @@ static inline int atomic_sub_return(int >> return val; >> } >> >>+static inline int atomic_cmpxchg(atomic_t *v, int old, int new) >>+{ >>+ int ret; >>+ unsigned long flags; >>+ >>+ local_irq_save(flags); >>+ ret = v->counter; >>+ if (likely(ret == old)) >>+ v->counter = new; >>+ local_irq_restore(flags); >>+ >>+ return ret; >>+} >>+ >>+static inline void atomic_clear_mask(unsigned long mask, unsigned long *addr) >> static inline void atomic_clear_mask(unsigned long mask, unsigned long *addr) > > > This is obviously going to break ARM... > Ah dang sorry. Must be a cut-n-paste-o. While you're here, does the assembly code for the SMP version look OK? You basically provided me with it but I don't think you saw its final form. -- SUSE Labs, Novell Inc. Send instant messages to your online friends http://au.messenger.yahoo.com ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [patch 2/5] atomic: cmpxchg 2005-11-05 9:10 ` Nick Piggin @ 2005-11-05 9:13 ` Russell King 2005-11-05 9:21 ` Nick Piggin 0 siblings, 1 reply; 15+ messages in thread From: Russell King @ 2005-11-05 9:13 UTC (permalink / raw) To: Nick Piggin; +Cc: Andrew Morton, linux-kernel, Linus Torvalds On Sat, Nov 05, 2005 at 08:10:53PM +1100, Nick Piggin wrote: > While you're here, does the assembly code for the SMP version look > OK? You basically provided me with it but I don't think you saw its > final form. Looks fine. The only comment is changing the "r" (old) to be "Ir" (old). The "I" tells the compiler that it may also use a constant for that argument, which may allow it to optimise the code a bit better. -- Russell King Linux kernel 2.6 ARM Linux - http://www.arm.linux.org.uk/ maintainer of: 2.6 Serial core ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [patch 2/5] atomic: cmpxchg 2005-11-05 9:13 ` Russell King @ 2005-11-05 9:21 ` Nick Piggin 0 siblings, 0 replies; 15+ messages in thread From: Nick Piggin @ 2005-11-05 9:21 UTC (permalink / raw) To: Russell King; +Cc: Andrew Morton, linux-kernel, Linus Torvalds Russell King wrote: > On Sat, Nov 05, 2005 at 08:10:53PM +1100, Nick Piggin wrote: > >>While you're here, does the assembly code for the SMP version look >>OK? You basically provided me with it but I don't think you saw its >>final form. > > > Looks fine. The only comment is changing the "r" (old) to be > "Ir" (old). The "I" tells the compiler that it may also use a > constant for that argument, which may allow it to optimise the > code a bit better. > Thanks. Will submit a new patch after this round of feedback. -- SUSE Labs, Novell Inc. Send instant messages to your online friends http://au.messenger.yahoo.com ^ permalink raw reply [flat|nested] 15+ messages in thread
* [patches] lockless pagecache prep round 1 @ 2005-10-30 0:41 Nick Piggin 2005-10-30 0:43 ` [patch 1/5] i386 generic cmpxchg Nick Piggin 0 siblings, 1 reply; 15+ messages in thread From: Nick Piggin @ 2005-10-30 0:41 UTC (permalink / raw) To: linux-kernel Hi List, Following this are some prep patches from my lockless pagecache patch stack, though they are nice patches that stand by themselves. I would be interested in getting them merged soon, they have survived quite a lot of stress testing here. Reviews and Acks from interested parties would be helpful. First is the generic atomic_cmpxchg stuff. These are really useful primitives to have in general as can be seen by their subsequent application. I've tried to do lots of compile testing, but if this causes failures, then it is exposing bugs already in the code that need fixing. Second is some radix tree improvements and cleanups. This patchset does introduce an "unused" radix tree API (lookup_slot), however I thought it was appropriate to include this patch here because there seem to be a number of users interested in this functionality (lockless pagecache, reiser4, adaptive readahead), and I don't want to see 3 different implementations! Thanks, Nick -- SUSE Labs, Novell Inc. Send instant messages to your online friends http://au.messenger.yahoo.com ^ permalink raw reply [flat|nested] 15+ messages in thread
* [patch 1/5] i386 generic cmpxchg 2005-10-30 0:41 [patches] lockless pagecache prep round 1 Nick Piggin @ 2005-10-30 0:43 ` Nick Piggin 2005-10-30 20:12 ` Zwane Mwaikambo 0 siblings, 1 reply; 15+ messages in thread From: Nick Piggin @ 2005-10-30 0:43 UTC (permalink / raw) To: linux-kernel [-- Attachment #1: Type: text/plain, Size: 33 bytes --] 1/5 -- SUSE Labs, Novell Inc. [-- Attachment #2: i386-generic-cmpxchg.patch --] [-- Type: text/plain, Size: 4052 bytes --] Changelog * Make cmpxchg generally available on the i386 platform. * Provide emulation of cmpxchg suitable for uniprocessor if built and run on 386. Signed-off-by: Christoph Lameter <clameter@sgi.com> * Cut down patch and small style changes. Signed-off-by: Nick Piggin <npiggin@suse.de> Index: linux-2.6/arch/i386/kernel/cpu/intel.c =================================================================== --- linux-2.6.orig/arch/i386/kernel/cpu/intel.c +++ linux-2.6/arch/i386/kernel/cpu/intel.c @@ -6,6 +6,7 @@ #include <linux/bitops.h> #include <linux/smp.h> #include <linux/thread_info.h> +#include <linux/module.h> #include <asm/processor.h> #include <asm/msr.h> @@ -264,5 +265,55 @@ __init int intel_cpu_init(void) return 0; } +#ifndef CONFIG_X86_CMPXCHG +unsigned long cmpxchg_386_u8(volatile void *ptr, u8 old, u8 new) +{ + u8 prev; + unsigned long flags; + + /* Poor man's cmpxchg for 386. Unsuitable for SMP */ + local_irq_save(flags); + prev = *(u8 *)ptr; + if (prev == old) + *(u8 *)ptr = new; + local_irq_restore(flags); + return prev; +} + +EXPORT_SYMBOL(cmpxchg_386_u8); + +unsigned long cmpxchg_386_u16(volatile void *ptr, u16 old, u16 new) +{ + u16 prev; + unsigned long flags; + + /* Poor man's cmpxchg for 386. Unsuitable for SMP */ + local_irq_save(flags); + prev = *(u16 *)ptr; + if (prev == old) + *(u16 *)ptr = new; + local_irq_restore(flags); + return prev; +} + +EXPORT_SYMBOL(cmpxchg_386_u16); + +unsigned long cmpxchg_386_u32(volatile void *ptr, u32 old, u32 new) +{ + u32 prev; + unsigned long flags; + + /* Poor man's cmpxchg for 386. Unsuitable for SMP */ + local_irq_save(flags); + prev = *(u32 *)ptr; + if (prev == old) + *(u32 *)ptr = new; + local_irq_restore(flags); + return prev; +} + +EXPORT_SYMBOL(cmpxchg_386_u32); +#endif + // arch_initcall(intel_cpu_init); Index: linux-2.6/include/asm-i386/system.h =================================================================== --- linux-2.6.orig/include/asm-i386/system.h +++ linux-2.6/include/asm-i386/system.h @@ -256,11 +256,6 @@ static inline unsigned long __xchg(unsig * store NEW in MEM. Return the initial value in MEM. Success is * indicated by comparing RETURN with OLD. */ - -#ifdef CONFIG_X86_CMPXCHG -#define __HAVE_ARCH_CMPXCHG 1 -#endif - static inline unsigned long __cmpxchg(volatile void *ptr, unsigned long old, unsigned long new, int size) { @@ -288,12 +283,53 @@ static inline unsigned long __cmpxchg(vo return old; } +#ifdef CONFIG_X86_CMPXCHG +#define __HAVE_ARCH_CMPXCHG 1 #define cmpxchg(ptr,o,n)\ - ((__typeof__(*(ptr)))__cmpxchg((ptr),(unsigned long)(o),\ - (unsigned long)(n),sizeof(*(ptr)))) - + ((__typeof__(*(ptr)))__cmpxchg((ptr), (unsigned long)(o), \ + (unsigned long)(n), sizeof(*(ptr)))) + +#else + +/* + * Building a kernel capable running on 80386. It may be necessary to + * simulate the cmpxchg on the 80386 CPU. For that purpose we define + * a function for each of the sizes we support. + */ + +extern unsigned long cmpxchg_386_u8(volatile void *, u8, u8); +extern unsigned long cmpxchg_386_u16(volatile void *, u16, u16); +extern unsigned long cmpxchg_386_u32(volatile void *, u32, u32); + +static inline unsigned long cmpxchg_386(volatile void *ptr, unsigned long old, + unsigned long new, int size) +{ + switch (size) { + case 1: + return cmpxchg_386_u8(ptr, old, new); + case 2: + return cmpxchg_386_u16(ptr, old, new); + case 4: + return cmpxchg_386_u32(ptr, old, new); + } + return old; +} + +#define cmpxchg(ptr,o,n) \ +({ \ + __typeof__(*(ptr)) __ret; \ + if (likely(boot_cpu_data.x86 > 3)) \ + __ret = __cmpxchg((ptr), (unsigned long)(o), \ + (unsigned long)(n), sizeof(*(ptr))); \ + else \ + __ret = cmpxchg_386((ptr), (unsigned long)(o), \ + (unsigned long)(n), sizeof(*(ptr))); \ + __ret; \ +}) +#endif + #ifdef __KERNEL__ -struct alt_instr { +struct alt_instr { __u8 *instr; /* original instruction */ __u8 *replacement; __u8 cpuid; /* cpuid bit set for replacement */ ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [patch 1/5] i386 generic cmpxchg 2005-10-30 0:43 ` [patch 1/5] i386 generic cmpxchg Nick Piggin @ 2005-10-30 20:12 ` Zwane Mwaikambo 2005-10-31 1:29 ` Nick Piggin 0 siblings, 1 reply; 15+ messages in thread From: Zwane Mwaikambo @ 2005-10-30 20:12 UTC (permalink / raw) To: Nick Piggin; +Cc: linux-kernel Hi Nick, On Sun, 30 Oct 2005, Nick Piggin wrote: +#define cmpxchg(ptr,o,n) \ +({ \ + __typeof__(*(ptr)) __ret; \ + if (likely(boot_cpu_data.x86 > 3)) \ + __ret = __cmpxchg((ptr), (unsigned long)(o), \ + (unsigned long)(n), sizeof(*(ptr))); \ + else \ + __ret = cmpxchg_386((ptr), (unsigned long)(o), \ + (unsigned long)(n), sizeof(*(ptr))); \ + __ret; \ +}) +#endif How about something similar to the following to remove the branch on optimised kernels? static inline int __is_i386(void) { #ifdef CONFIG_M386 return boot_cpu_data.x86 == 3; #else return 0 #endif } #define cmpxchg(ptr,o,n) \ ({ \ __typeof__(*(ptr)) __ret; \ if (likely(!__is_i386())) \ __ret = __cmpxchg((ptr), (unsigned long)(o), \ (unsigned long)(n), sizeof(*(ptr))); \ else \ __ret = cmpxchg_386((ptr), (unsigned long)(o), \ (unsigned long)(n), sizeof(*(ptr))); \ __ret; \ }) ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [patch 1/5] i386 generic cmpxchg 2005-10-30 20:12 ` Zwane Mwaikambo @ 2005-10-31 1:29 ` Nick Piggin 2005-10-31 6:06 ` Zwane Mwaikambo 2005-10-31 19:09 ` Christoph Lameter 0 siblings, 2 replies; 15+ messages in thread From: Nick Piggin @ 2005-10-31 1:29 UTC (permalink / raw) To: Zwane Mwaikambo; +Cc: linux-kernel Zwane Mwaikambo wrote: > Hi Nick, > > On Sun, 30 Oct 2005, Nick Piggin wrote: > > +#define cmpxchg(ptr,o,n) \ > +({ \ > + __typeof__(*(ptr)) __ret; \ > + if (likely(boot_cpu_data.x86 > 3)) \ > + __ret = __cmpxchg((ptr), (unsigned long)(o), \ > + (unsigned long)(n), sizeof(*(ptr))); \ > + else \ > + __ret = cmpxchg_386((ptr), (unsigned long)(o), \ > + (unsigned long)(n), sizeof(*(ptr))); \ > + __ret; \ > +}) > +#endif > > How about something similar to the following to remove the branch on > optimised kernels? > Hi Zwane, This is only in the !CONFIG_X86_CMPXCHG case, though, so the branch would only be there on a 386 kernel, I think? -- SUSE Labs, Novell Inc. Send instant messages to your online friends http://au.messenger.yahoo.com ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [patch 1/5] i386 generic cmpxchg 2005-10-31 1:29 ` Nick Piggin @ 2005-10-31 6:06 ` Zwane Mwaikambo 2005-10-31 19:09 ` Christoph Lameter 1 sibling, 0 replies; 15+ messages in thread From: Zwane Mwaikambo @ 2005-10-31 6:06 UTC (permalink / raw) To: Nick Piggin; +Cc: linux-kernel On Mon, 31 Oct 2005, Nick Piggin wrote: > Zwane Mwaikambo wrote: > > Hi Nick, > > > > On Sun, 30 Oct 2005, Nick Piggin wrote: > > > > +#define cmpxchg(ptr,o,n) \ > > +({ \ > > + __typeof__(*(ptr)) __ret; \ > > + if (likely(boot_cpu_data.x86 > 3)) \ > > + __ret = __cmpxchg((ptr), (unsigned long)(o), \ > > + (unsigned long)(n), sizeof(*(ptr))); \ > > + else \ > > + __ret = cmpxchg_386((ptr), (unsigned long)(o), \ > > + (unsigned long)(n), sizeof(*(ptr))); \ > > + __ret; \ > > +}) > > +#endif > > > > How about something similar to the following to remove the branch on > > optimised kernels? > > > > This is only in the !CONFIG_X86_CMPXCHG case, though, so the branch would > only be there on a 386 kernel, I think? Ah yes you are right, i missed the #else. Thanks, Zwane ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [patch 1/5] i386 generic cmpxchg 2005-10-31 1:29 ` Nick Piggin 2005-10-31 6:06 ` Zwane Mwaikambo @ 2005-10-31 19:09 ` Christoph Lameter 1 sibling, 0 replies; 15+ messages in thread From: Christoph Lameter @ 2005-10-31 19:09 UTC (permalink / raw) To: Nick Piggin; +Cc: Zwane Mwaikambo, linux-kernel On Mon, 31 Oct 2005, Nick Piggin wrote: > This is only in the !CONFIG_X86_CMPXCHG case, though, so the branch would > only be there on a 386 kernel, I think? Correct. ^ permalink raw reply [flat|nested] 15+ messages in thread
end of thread, other threads:[~2005-11-05 9:19 UTC | newest] Thread overview: 15+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2005-11-05 7:55 [patch 0/4] atomic primitives again Nick Piggin 2005-11-05 7:56 ` [patch 1/5] i386: generic cmpxchg Nick Piggin 2005-11-05 7:57 ` [patch 2/5] atomic: cmpxchg Nick Piggin 2005-11-05 7:58 ` [patch 3/5] atomic: inc_not_zero Nick Piggin 2005-11-05 7:58 ` [patch 4/5] rcu file: use atomic Nick Piggin 2005-11-05 7:59 ` [patch 5/5] atomic: dec_and_lock " Nick Piggin 2005-11-05 9:00 ` [patch 2/5] atomic: cmpxchg Russell King 2005-11-05 9:10 ` Nick Piggin 2005-11-05 9:13 ` Russell King 2005-11-05 9:21 ` Nick Piggin -- strict thread matches above, loose matches on Subject: below -- 2005-10-30 0:41 [patches] lockless pagecache prep round 1 Nick Piggin 2005-10-30 0:43 ` [patch 1/5] i386 generic cmpxchg Nick Piggin 2005-10-30 20:12 ` Zwane Mwaikambo 2005-10-31 1:29 ` Nick Piggin 2005-10-31 6:06 ` Zwane Mwaikambo 2005-10-31 19:09 ` Christoph Lameter
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox