All of lore.kernel.org
 help / color / mirror / Atom feed
* cmpxchg broken in some situation
@ 2007-09-30 10:34 Fuxin Zhang
  2007-10-01  2:53 ` Ralf Baechle
  0 siblings, 1 reply; 14+ messages in thread
From: Fuxin Zhang @ 2007-09-30 10:34 UTC (permalink / raw)
  To: linux-mips

hi,   
   Today I run across a possible bug of cmpxchg implementation. When 
playing with DRM on our Fulong, the following function 
(drivers/char/drm/drm_lock.c) is not working correctly in 64BIT mips:
   cmpxchg failed to set *lock to new value. (return 0 with *lock unchanged)
It is probably due to type conversions between unisigned int and 
unsigned long.  When I change cmpxchg to mycmpxchg(attached below), 
problem disappeared.                                 

int drm_lock_free(struct drm_lock_data *lock_data, unsigned int context)
{
        unsigned int old, new, prev;
        volatile unsigned int *lock = &lock_data->hw_lock->lock;

        spin_lock(&lock_data->spinlock);
        if (lock_data->kernel_waiters != 0) {
                drm_lock_transfer(lock_data, 0);
                lock_data->idle_has_lock = 1;
                spin_unlock(&lock_data->spinlock);
                return 1;
        }
        spin_unlock(&lock_data->spinlock);

        do {
                old = *lock;
                new = _DRM_LOCKING_CONTEXT(old);
                prev = cmpxchg(lock, old, new);
        } while (prev != old);

        if (_DRM_LOCK_IS_HELD(old) && _DRM_LOCKING_CONTEXT(old) != 
context) {
                DRM_ERROR("%d freed heavyweight lock held by %d\n",
                          context, _DRM_LOCKING_CONTEXT(old));
                return 1;
        }
        wake_up_interruptible(&lock_data->lock_queue);
        return 0;
}

static inline unsigned int mycmpxchg(volatile int * m, unsigned int old,
        unsigned int new)  //unsigned long to unsigned int
{
        __u32 retval;

        __asm__ __volatile__(
                        "       .set    
push                                    \n"
                        "       .set    
noat                                    \n"
                        "       .set    
mips3                                   \n"
                        "1:     ll      %0, %2                  # 
__mycmpxchg_u32       \n"
                        "       bne     %0, %z3, 
2f                             \n"
                        "       .set    
mips0                                   \n"
                        "       move    $1, 
%z4                                 \n"
                        "       .set    
mips3                                   \n"
                        "       sc      $1, 
%1                                  \n"
                        "       beqz    $1, 
3f                                  \n"
                        
"2:                                                     \n"
                        "       .subsection 
2                                   \n"
                        "3:     b       
1b                                      \n"
                        "       
.previous                                       \n"
                        "       .set    
pop                                     \n"
                        : "=&r" (retval), "=R" (*m)
                        : "R" (*m), "Jr" (old), "Jr" (new)
                        : "memory");

        smp_llsc_mb();

        return retval;
}

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

* Re: cmpxchg broken in some situation
  2007-09-30 10:34 cmpxchg broken in some situation Fuxin Zhang
@ 2007-10-01  2:53 ` Ralf Baechle
  2007-10-01  3:56   ` David Daney
  2007-10-01 15:11   ` Fuxin Zhang
  0 siblings, 2 replies; 14+ messages in thread
From: Ralf Baechle @ 2007-10-01  2:53 UTC (permalink / raw)
  To: Fuxin Zhang; +Cc: linux-mips

On Sun, Sep 30, 2007 at 06:34:42PM +0800, Fuxin Zhang wrote:

> hi,   
>   Today I run across a possible bug of cmpxchg implementation. When 
> playing with DRM on our Fulong, the following function 
> (drivers/char/drm/drm_lock.c) is not working correctly in 64BIT mips:
>   cmpxchg failed to set *lock to new value. (return 0 with *lock unchanged)
> It is probably due to type conversions between unisigned int and 
> unsigned long.  When I change cmpxchg to mycmpxchg(attached below), 
> problem disappeared.                                 

Below a patch to implement the get_user() style big solution to type
safety in cmpxchg.  Can you test it?  Thanks,

  Ralf

Signed-off-by: Ralf Baechle <ralf@linux-mips.org>

 include/asm-mips/cmpxchg.h |  104 ++++++++++++++++++
 include/asm-mips/local.h   |    1 
 include/asm-mips/system.h  |  261 --------------------------------------------
 3 files changed, 106 insertions(+), 260 deletions(-)

diff --git a/include/asm-mips/cmpxchg.h b/include/asm-mips/cmpxchg.h
new file mode 100644
index 0000000..d1523dd
--- /dev/null
+++ b/include/asm-mips/cmpxchg.h
@@ -0,0 +1,104 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2003, 06, 07 by Ralf Baechle (ralf@linux-mips.org)
+ */
+#ifndef __ASM_CMPXCHG_H
+#define __ASM_CMPXCHG_H
+
+#include <linux/irqflags.h>
+
+#define __HAVE_ARCH_CMPXCHG 1
+
+#define __cmpxchg(ld, st, m, old, new)					\
+({									\
+	__typeof(*(m)) __ret;						\
+									\
+	if (cpu_has_llsc && R10000_LLSC_WAR) {				\
+		__asm__ __volatile__(					\
+		"	.set	push				\n"	\
+		"	.set	noat				\n"	\
+		"	.set	mips3				\n"	\
+		"1:	" ld "	%0, %2		# __cmpxchg_u32	\n"	\
+		"	bne	%0, %z3, 2f			\n"	\
+		"	.set	mips0				\n"	\
+		"	move	$1, %z4				\n"	\
+		"	.set	mips3				\n"	\
+		"	" st "	$1, %1				\n"	\
+		"	beqzl	$1, 1b				\n"	\
+		"2:						\n"	\
+		"	.set	pop				\n"	\
+		: "=&r" (__ret), "=R" (*m)				\
+		: "R" (*m), "Jr" (old), "Jr" (new)			\
+		: "memory");						\
+	} else if (cpu_has_llsc) {					\
+		__asm__ __volatile__(					\
+		"	.set	push				\n"	\
+		"	.set	noat				\n"	\
+		"	.set	mips3				\n"	\
+		"1:	" ld "	%0, %2		# __cmpxchg_u32	\n"	\
+		"	bne	%0, %z3, 2f			\n"	\
+		"	.set	mips0				\n"	\
+		"	move	$1, %z4				\n"	\
+		"	.set	mips3				\n"	\
+		"	" st "	$1, %1				\n"	\
+		"	beqz	$1, 3f				\n"	\
+		"2:						\n"	\
+		"	.subsection 2				\n"	\
+		"3:	b	1b				\n"	\
+		"	.previous				\n"	\
+		"	.set	pop				\n"	\
+		: "=&r" (__ret), "=R" (*m)				\
+		: "R" (*m), "Jr" (old), "Jr" (new)			\
+		: "memory");						\
+	} else {							\
+		unsigned long __flags;					\
+									\
+		raw_local_irq_save(__flags);				\
+		__ret = *m;						\
+		if (__ret == old)					\
+			*m = new;					\
+		raw_local_irq_restore(__flags);				\
+	}								\
+									\
+	smp_llsc_mb();							\
+									\
+	__ret;								\
+})
+
+/*
+ * This function doesn't exist, so you'll get a linker error
+ * if something tries to do an invalid cmpxchg().
+ */
+extern void __cmpxchg_called_with_bad_pointer(void);
+
+#define cmpxchg(ptr,old,new)						\
+({									\
+	__typeof__(ptr) __ptr = (ptr);					\
+	__typeof__(*(ptr)) __old = (old);				\
+	__typeof__(*(ptr)) __new = (new);				\
+	__typeof__(*(ptr)) __res = 0;					\
+									\
+	switch (sizeof(__ptr)) {					\
+	case 4:								\
+		__res = __cmpxchg("ll", "sc", __ptr, __old, __new);	\
+		break;							\
+	case 8:								\
+		if (sizeof(long) == 8) {				\
+			__res = __cmpxchg("lld", "scd", __ptr,		\
+					   __old, __new);		\
+			break;						\
+		}							\
+	default:							\
+		__cmpxchg_called_with_bad_pointer();			\
+		break;							\
+	}								\
+									\
+	__res;								\
+})
+
+#define cmpxchg_local(ptr, old, new) cmpxchg(ptr, old, new)
+
+#endif /* __ASM_CMPXCHG_H */
diff --git a/include/asm-mips/local.h b/include/asm-mips/local.h
index ed882c8..7034a01 100644
--- a/include/asm-mips/local.h
+++ b/include/asm-mips/local.h
@@ -4,6 +4,7 @@
 #include <linux/percpu.h>
 #include <linux/bitops.h>
 #include <asm/atomic.h>
+#include <asm/cmpxchg.h>
 #include <asm/war.h>
 
 typedef struct
diff --git a/include/asm-mips/system.h b/include/asm-mips/system.h
index 357251f..480b574 100644
--- a/include/asm-mips/system.h
+++ b/include/asm-mips/system.h
@@ -17,6 +17,7 @@
 
 #include <asm/addrspace.h>
 #include <asm/barrier.h>
+#include <asm/cmpxchg.h>
 #include <asm/cpu-features.h>
 #include <asm/dsp.h>
 #include <asm/war.h>
@@ -194,266 +195,6 @@ static inline unsigned long __xchg(unsigned long x, volatile void * ptr, int siz
 
 #define xchg(ptr,x) ((__typeof__(*(ptr)))__xchg((unsigned long)(x),(ptr),sizeof(*(ptr))))
 
-#define __HAVE_ARCH_CMPXCHG 1
-
-static inline unsigned long __cmpxchg_u32(volatile int * m, unsigned long old,
-	unsigned long new)
-{
-	__u32 retval;
-
-	if (cpu_has_llsc && R10000_LLSC_WAR) {
-		__asm__ __volatile__(
-		"	.set	push					\n"
-		"	.set	noat					\n"
-		"	.set	mips3					\n"
-		"1:	ll	%0, %2			# __cmpxchg_u32	\n"
-		"	bne	%0, %z3, 2f				\n"
-		"	.set	mips0					\n"
-		"	move	$1, %z4					\n"
-		"	.set	mips3					\n"
-		"	sc	$1, %1					\n"
-		"	beqzl	$1, 1b					\n"
-		"2:							\n"
-		"	.set	pop					\n"
-		: "=&r" (retval), "=R" (*m)
-		: "R" (*m), "Jr" (old), "Jr" (new)
-		: "memory");
-	} else if (cpu_has_llsc) {
-		__asm__ __volatile__(
-		"	.set	push					\n"
-		"	.set	noat					\n"
-		"	.set	mips3					\n"
-		"1:	ll	%0, %2			# __cmpxchg_u32	\n"
-		"	bne	%0, %z3, 2f				\n"
-		"	.set	mips0					\n"
-		"	move	$1, %z4					\n"
-		"	.set	mips3					\n"
-		"	sc	$1, %1					\n"
-		"	beqz	$1, 3f					\n"
-		"2:							\n"
-		"	.subsection 2					\n"
-		"3:	b	1b					\n"
-		"	.previous					\n"
-		"	.set	pop					\n"
-		: "=&r" (retval), "=R" (*m)
-		: "R" (*m), "Jr" (old), "Jr" (new)
-		: "memory");
-	} else {
-		unsigned long flags;
-
-		raw_local_irq_save(flags);
-		retval = *m;
-		if (retval == old)
-			*m = new;
-		raw_local_irq_restore(flags);	/* implies memory barrier  */
-	}
-
-	smp_llsc_mb();
-
-	return retval;
-}
-
-static inline unsigned long __cmpxchg_u32_local(volatile int * m,
-	unsigned long old, unsigned long new)
-{
-	__u32 retval;
-
-	if (cpu_has_llsc && R10000_LLSC_WAR) {
-		__asm__ __volatile__(
-		"	.set	push					\n"
-		"	.set	noat					\n"
-		"	.set	mips3					\n"
-		"1:	ll	%0, %2			# __cmpxchg_u32	\n"
-		"	bne	%0, %z3, 2f				\n"
-		"	.set	mips0					\n"
-		"	move	$1, %z4					\n"
-		"	.set	mips3					\n"
-		"	sc	$1, %1					\n"
-		"	beqzl	$1, 1b					\n"
-		"2:							\n"
-		"	.set	pop					\n"
-		: "=&r" (retval), "=R" (*m)
-		: "R" (*m), "Jr" (old), "Jr" (new)
-		: "memory");
-	} else if (cpu_has_llsc) {
-		__asm__ __volatile__(
-		"	.set	push					\n"
-		"	.set	noat					\n"
-		"	.set	mips3					\n"
-		"1:	ll	%0, %2			# __cmpxchg_u32	\n"
-		"	bne	%0, %z3, 2f				\n"
-		"	.set	mips0					\n"
-		"	move	$1, %z4					\n"
-		"	.set	mips3					\n"
-		"	sc	$1, %1					\n"
-		"	beqz	$1, 1b					\n"
-		"2:							\n"
-		"	.set	pop					\n"
-		: "=&r" (retval), "=R" (*m)
-		: "R" (*m), "Jr" (old), "Jr" (new)
-		: "memory");
-	} else {
-		unsigned long flags;
-
-		local_irq_save(flags);
-		retval = *m;
-		if (retval == old)
-			*m = new;
-		local_irq_restore(flags);	/* implies memory barrier  */
-	}
-
-	return retval;
-}
-
-#ifdef CONFIG_64BIT
-static inline unsigned long __cmpxchg_u64(volatile int * m, unsigned long old,
-	unsigned long new)
-{
-	__u64 retval;
-
-	if (cpu_has_llsc && R10000_LLSC_WAR) {
-		__asm__ __volatile__(
-		"	.set	push					\n"
-		"	.set	noat					\n"
-		"	.set	mips3					\n"
-		"1:	lld	%0, %2			# __cmpxchg_u64	\n"
-		"	bne	%0, %z3, 2f				\n"
-		"	move	$1, %z4					\n"
-		"	scd	$1, %1					\n"
-		"	beqzl	$1, 1b					\n"
-		"2:							\n"
-		"	.set	pop					\n"
-		: "=&r" (retval), "=R" (*m)
-		: "R" (*m), "Jr" (old), "Jr" (new)
-		: "memory");
-	} else if (cpu_has_llsc) {
-		__asm__ __volatile__(
-		"	.set	push					\n"
-		"	.set	noat					\n"
-		"	.set	mips3					\n"
-		"1:	lld	%0, %2			# __cmpxchg_u64	\n"
-		"	bne	%0, %z3, 2f				\n"
-		"	move	$1, %z4					\n"
-		"	scd	$1, %1					\n"
-		"	beqz	$1, 3f					\n"
-		"2:							\n"
-		"	.subsection 2					\n"
-		"3:	b	1b					\n"
-		"	.previous					\n"
-		"	.set	pop					\n"
-		: "=&r" (retval), "=R" (*m)
-		: "R" (*m), "Jr" (old), "Jr" (new)
-		: "memory");
-	} else {
-		unsigned long flags;
-
-		raw_local_irq_save(flags);
-		retval = *m;
-		if (retval == old)
-			*m = new;
-		raw_local_irq_restore(flags);	/* implies memory barrier  */
-	}
-
-	smp_llsc_mb();
-
-	return retval;
-}
-
-static inline unsigned long __cmpxchg_u64_local(volatile int * m,
-	unsigned long old, unsigned long new)
-{
-	__u64 retval;
-
-	if (cpu_has_llsc && R10000_LLSC_WAR) {
-		__asm__ __volatile__(
-		"	.set	push					\n"
-		"	.set	noat					\n"
-		"	.set	mips3					\n"
-		"1:	lld	%0, %2			# __cmpxchg_u64	\n"
-		"	bne	%0, %z3, 2f				\n"
-		"	move	$1, %z4					\n"
-		"	scd	$1, %1					\n"
-		"	beqzl	$1, 1b					\n"
-		"2:							\n"
-		"	.set	pop					\n"
-		: "=&r" (retval), "=R" (*m)
-		: "R" (*m), "Jr" (old), "Jr" (new)
-		: "memory");
-	} else if (cpu_has_llsc) {
-		__asm__ __volatile__(
-		"	.set	push					\n"
-		"	.set	noat					\n"
-		"	.set	mips3					\n"
-		"1:	lld	%0, %2			# __cmpxchg_u64	\n"
-		"	bne	%0, %z3, 2f				\n"
-		"	move	$1, %z4					\n"
-		"	scd	$1, %1					\n"
-		"	beqz	$1, 1b					\n"
-		"2:							\n"
-		"	.set	pop					\n"
-		: "=&r" (retval), "=R" (*m)
-		: "R" (*m), "Jr" (old), "Jr" (new)
-		: "memory");
-	} else {
-		unsigned long flags;
-
-		local_irq_save(flags);
-		retval = *m;
-		if (retval == old)
-			*m = new;
-		local_irq_restore(flags);	/* implies memory barrier  */
-	}
-
-	return retval;
-}
-
-#else
-extern unsigned long __cmpxchg_u64_unsupported_on_32bit_kernels(
-	volatile int * m, unsigned long old, unsigned long new);
-#define __cmpxchg_u64 __cmpxchg_u64_unsupported_on_32bit_kernels
-extern unsigned long __cmpxchg_u64_local_unsupported_on_32bit_kernels(
-	volatile int * m, unsigned long old, unsigned long new);
-#define __cmpxchg_u64_local __cmpxchg_u64_local_unsupported_on_32bit_kernels
-#endif
-
-/* This function doesn't exist, so you'll get a linker error
-   if something tries to do an invalid cmpxchg().  */
-extern void __cmpxchg_called_with_bad_pointer(void);
-
-static inline unsigned long __cmpxchg(volatile void * ptr, unsigned long old,
-	unsigned long new, int size)
-{
-	switch (size) {
-	case 4:
-		return __cmpxchg_u32(ptr, old, new);
-	case 8:
-		return __cmpxchg_u64(ptr, old, new);
-	}
-	__cmpxchg_called_with_bad_pointer();
-	return old;
-}
-
-static inline unsigned long __cmpxchg_local(volatile void * ptr,
-	unsigned long old, unsigned long new, int size)
-{
-	switch (size) {
-	case 4:
-		return __cmpxchg_u32_local(ptr, old, new);
-	case 8:
-		return __cmpxchg_u64_local(ptr, old, new);
-	}
-	__cmpxchg_called_with_bad_pointer();
-	return old;
-}
-
-#define cmpxchg(ptr,old,new) \
-	((__typeof__(*(ptr)))__cmpxchg((ptr), \
-		(unsigned long)(old), (unsigned long)(new),sizeof(*(ptr))))
-
-#define cmpxchg_local(ptr,old,new) \
-	((__typeof__(*(ptr)))__cmpxchg_local((ptr), \
-		(unsigned long)(old), (unsigned long)(new),sizeof(*(ptr))))
-
 extern void set_handler (unsigned long offset, void *addr, unsigned long len);
 extern void set_uncached_handler (unsigned long offset, void *addr, unsigned long len);
 

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

* Re: cmpxchg broken in some situation
  2007-10-01  2:53 ` Ralf Baechle
@ 2007-10-01  3:56   ` David Daney
  2007-10-01  3:59     ` David Daney
  2007-10-01 15:11   ` Fuxin Zhang
  1 sibling, 1 reply; 14+ messages in thread
From: David Daney @ 2007-10-01  3:56 UTC (permalink / raw)
  To: Ralf Baechle; +Cc: Fuxin Zhang, linux-mips

Ralf Baechle wrote:
> +	} else if (cpu_has_llsc) {					\
> +		__asm__ __volatile__(					\
> +		"	.set	push				\n"	\
> +		"	.set	noat				\n"	\
> +		"	.set	mips3				\n"	\
> +		"1:	" ld "	%0, %2		# __cmpxchg_u32	\n"	\
> +		"	bne	%0, %z3, 2f			\n"	\
> +		"	.set	mips0				\n"	\
> +		"	move	$1, %z4				\n"	\
> +		"	.set	mips3				\n"	\
> +		"	" st "	$1, %1				\n"	\
> +		"	beqz	$1, 3f				\n"	\
> +		"2:						\n"	\
> +		"	.subsection 2				\n"	\
> +		"3:	b	1b				\n"	\
> +		"	.previous				\n"	\
> +		"	.set	pop				\n"	\
> +		: "=&r" (__ret), "=R" (*m)				\
> +		: "R" (*m), "Jr" (old), "Jr" (new)			\
> +		: "memory");						\
>   
Is a 'sync' needed after the 'sc'?

According to this message:
http://www.linux-mips.org/cgi-bin/mesg.cgi?a=linux-mips&i=20070919084515.GM9972%40networkno.de
it would seem so.


David Daney

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

* Re: cmpxchg broken in some situation
  2007-10-01  3:56   ` David Daney
@ 2007-10-01  3:59     ` David Daney
  2007-10-01 10:24       ` Ralf Baechle
  0 siblings, 1 reply; 14+ messages in thread
From: David Daney @ 2007-10-01  3:59 UTC (permalink / raw)
  To: Ralf Baechle; +Cc: Fuxin Zhang, linux-mips

David Daney wrote:
> Ralf Baechle wrote:
>> +    } else if (cpu_has_llsc) {                    \
>> +        __asm__ __volatile__(                    \
>> +        "    .set    push                \n"    \
>> +        "    .set    noat                \n"    \
>> +        "    .set    mips3                \n"    \
>> +        "1:    " ld "    %0, %2        # __cmpxchg_u32    \n"    \
>> +        "    bne    %0, %z3, 2f            \n"    \
>> +        "    .set    mips0                \n"    \
>> +        "    move    $1, %z4                \n"    \
>> +        "    .set    mips3                \n"    \
>> +        "    " st "    $1, %1                \n"    \
>> +        "    beqz    $1, 3f                \n"    \
>> +        "2:                        \n"    \
>> +        "    .subsection 2                \n"    \
>> +        "3:    b    1b                \n"    \
>> +        "    .previous                \n"    \
>> +        "    .set    pop                \n"    \
>> +        : "=&r" (__ret), "=R" (*m)                \
>> +        : "R" (*m), "Jr" (old), "Jr" (new)            \
>> +        : "memory");                        \
>>   
> Is a 'sync' needed after the 'sc'?
>
> According to this message:
> http://www.linux-mips.org/cgi-bin/mesg.cgi?a=linux-mips&i=20070919084515.GM9972%40networkno.de 
>
> it would seem so.

Drat, I probably posted too soon.  That is the smp_llsc_mb(); isn't it.

David Daney

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

* Re: cmpxchg broken in some situation
  2007-10-01  3:59     ` David Daney
@ 2007-10-01 10:24       ` Ralf Baechle
  0 siblings, 0 replies; 14+ messages in thread
From: Ralf Baechle @ 2007-10-01 10:24 UTC (permalink / raw)
  To: David Daney; +Cc: Fuxin Zhang, linux-mips

On Sun, Sep 30, 2007 at 08:59:07PM -0700, David Daney wrote:

> David Daney wrote:
> >Ralf Baechle wrote:
> >>+    } else if (cpu_has_llsc) {                    \
> >>+        __asm__ __volatile__(                    \
> >>+        "    .set    push                \n"    \
> >>+        "    .set    noat                \n"    \
> >>+        "    .set    mips3                \n"    \
> >>+        "1:    " ld "    %0, %2        # __cmpxchg_u32    \n"    \
> >>+        "    bne    %0, %z3, 2f            \n"    \
> >>+        "    .set    mips0                \n"    \
> >>+        "    move    $1, %z4                \n"    \
> >>+        "    .set    mips3                \n"    \
> >>+        "    " st "    $1, %1                \n"    \
> >>+        "    beqz    $1, 3f                \n"    \
> >>+        "2:                        \n"    \
> >>+        "    .subsection 2                \n"    \
> >>+        "3:    b    1b                \n"    \
> >>+        "    .previous                \n"    \
> >>+        "    .set    pop                \n"    \
> >>+        : "=&r" (__ret), "=R" (*m)                \
> >>+        : "R" (*m), "Jr" (old), "Jr" (new)            \
> >>+        : "memory");                        \
> >>  
> >Is a 'sync' needed after the 'sc'?
> >
> >According to this message:
> >http://www.linux-mips.org/cgi-bin/mesg.cgi?a=linux-mips&i=20070919084515.GM9972%40networkno.de 
> >
> >it would seem so.
> 
> Drat, I probably posted too soon.  That is the smp_llsc_mb(); isn't it.

Yes - and the answer to your original question is a clear and definate
maybe ;-)

In the kernel we can afford to optimize for every piece of silicon on earth.
In userspace we can't make that sort of compile time choices as easily so
it's a better idea to just litter a few SYNCs over the code.

  Ralf

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

* Re: cmpxchg broken in some situation
  2007-10-01  2:53 ` Ralf Baechle
  2007-10-01  3:56   ` David Daney
@ 2007-10-01 15:11   ` Fuxin Zhang
  2007-10-01 15:26     ` Ralf Baechle
  1 sibling, 1 reply; 14+ messages in thread
From: Fuxin Zhang @ 2007-10-01 15:11 UTC (permalink / raw)
  To: Ralf Baechle; +Cc: linux-mips

Sorry that it seems not work:
the kernel oops at sysfs_open_file->sysfs_get_active with unaligned 
access(last seen exception on screen, no serial console by hand so it 
may not be the first exception). It is probably caused by 
"atomic_cmpxchg" there.
And keep the old kernel using new modules with patched cmpxchg also lead 
to glxgears die(should be lock problem like before).

Ralf Baechle 写道:
> On Sun, Sep 30, 2007 at 06:34:42PM +0800, Fuxin Zhang wrote:
>
>   
>> hi,   
>>   Today I run across a possible bug of cmpxchg implementation. When 
>> playing with DRM on our Fulong, the following function 
>> (drivers/char/drm/drm_lock.c) is not working correctly in 64BIT mips:
>>   cmpxchg failed to set *lock to new value. (return 0 with *lock unchanged)
>> It is probably due to type conversions between unisigned int and 
>> unsigned long.  When I change cmpxchg to mycmpxchg(attached below), 
>> problem disappeared.                                 
>>     
>
> Below a patch to implement the get_user() style big solution to type
> safety in cmpxchg.  Can you test it?  Thanks,
>
>   Ralf
>
> Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
>
>  include/asm-mips/cmpxchg.h |  104 ++++++++++++++++++
>  include/asm-mips/local.h   |    1 
>  include/asm-mips/system.h  |  261 --------------------------------------------
>  3 files changed, 106 insertions(+), 260 deletions(-)
>
> diff --git a/include/asm-mips/cmpxchg.h b/include/asm-mips/cmpxchg.h
> new file mode 100644
> index 0000000..d1523dd
> --- /dev/null
> +++ b/include/asm-mips/cmpxchg.h
> @@ -0,0 +1,104 @@
> +/*
> + * This file is subject to the terms and conditions of the GNU General Public
> + * License.  See the file "COPYING" in the main directory of this archive
> + * for more details.
> + *
> + * Copyright (C) 2003, 06, 07 by Ralf Baechle (ralf@linux-mips.org)
> + */
> +#ifndef __ASM_CMPXCHG_H
> +#define __ASM_CMPXCHG_H
> +
> +#include <linux/irqflags.h>
> +
> +#define __HAVE_ARCH_CMPXCHG 1
> +
> +#define __cmpxchg(ld, st, m, old, new)					\
> +({									\
> +	__typeof(*(m)) __ret;						\
> +									\
> +	if (cpu_has_llsc && R10000_LLSC_WAR) {				\
> +		__asm__ __volatile__(					\
> +		"	.set	push				\n"	\
> +		"	.set	noat				\n"	\
> +		"	.set	mips3				\n"	\
> +		"1:	" ld "	%0, %2		# __cmpxchg_u32	\n"	\
> +		"	bne	%0, %z3, 2f			\n"	\
> +		"	.set	mips0				\n"	\
> +		"	move	$1, %z4				\n"	\
> +		"	.set	mips3				\n"	\
> +		"	" st "	$1, %1				\n"	\
> +		"	beqzl	$1, 1b				\n"	\
> +		"2:						\n"	\
> +		"	.set	pop				\n"	\
> +		: "=&r" (__ret), "=R" (*m)				\
> +		: "R" (*m), "Jr" (old), "Jr" (new)			\
> +		: "memory");						\
> +	} else if (cpu_has_llsc) {					\
> +		__asm__ __volatile__(					\
> +		"	.set	push				\n"	\
> +		"	.set	noat				\n"	\
> +		"	.set	mips3				\n"	\
> +		"1:	" ld "	%0, %2		# __cmpxchg_u32	\n"	\
> +		"	bne	%0, %z3, 2f			\n"	\
> +		"	.set	mips0				\n"	\
> +		"	move	$1, %z4				\n"	\
> +		"	.set	mips3				\n"	\
> +		"	" st "	$1, %1				\n"	\
> +		"	beqz	$1, 3f				\n"	\
> +		"2:						\n"	\
> +		"	.subsection 2				\n"	\
> +		"3:	b	1b				\n"	\
> +		"	.previous				\n"	\
> +		"	.set	pop				\n"	\
> +		: "=&r" (__ret), "=R" (*m)				\
> +		: "R" (*m), "Jr" (old), "Jr" (new)			\
> +		: "memory");						\
> +	} else {							\
> +		unsigned long __flags;					\
> +									\
> +		raw_local_irq_save(__flags);				\
> +		__ret = *m;						\
> +		if (__ret == old)					\
> +			*m = new;					\
> +		raw_local_irq_restore(__flags);				\
> +	}								\
> +									\
> +	smp_llsc_mb();							\
> +									\
> +	__ret;								\
> +})
> +
> +/*
> + * This function doesn't exist, so you'll get a linker error
> + * if something tries to do an invalid cmpxchg().
> + */
> +extern void __cmpxchg_called_with_bad_pointer(void);
> +
> +#define cmpxchg(ptr,old,new)						\
> +({									\
> +	__typeof__(ptr) __ptr = (ptr);					\
> +	__typeof__(*(ptr)) __old = (old);				\
> +	__typeof__(*(ptr)) __new = (new);				\
> +	__typeof__(*(ptr)) __res = 0;					\
> +									\
> +	switch (sizeof(__ptr)) {					\
> +	case 4:								\
> +		__res = __cmpxchg("ll", "sc", __ptr, __old, __new);	\
> +		break;							\
> +	case 8:								\
> +		if (sizeof(long) == 8) {				\
> +			__res = __cmpxchg("lld", "scd", __ptr,		\
> +					   __old, __new);		\
> +			break;						\
> +		}							\
> +	default:							\
> +		__cmpxchg_called_with_bad_pointer();			\
> +		break;							\
> +	}								\
> +									\
> +	__res;								\
> +})
> +
> +#define cmpxchg_local(ptr, old, new) cmpxchg(ptr, old, new)
> +
> +#endif /* __ASM_CMPXCHG_H */
> diff --git a/include/asm-mips/local.h b/include/asm-mips/local.h
> index ed882c8..7034a01 100644
> --- a/include/asm-mips/local.h
> +++ b/include/asm-mips/local.h
> @@ -4,6 +4,7 @@
>  #include <linux/percpu.h>
>  #include <linux/bitops.h>
>  #include <asm/atomic.h>
> +#include <asm/cmpxchg.h>
>  #include <asm/war.h>
>  
>  typedef struct
> diff --git a/include/asm-mips/system.h b/include/asm-mips/system.h
> index 357251f..480b574 100644
> --- a/include/asm-mips/system.h
> +++ b/include/asm-mips/system.h
> @@ -17,6 +17,7 @@
>  
>  #include <asm/addrspace.h>
>  #include <asm/barrier.h>
> +#include <asm/cmpxchg.h>
>  #include <asm/cpu-features.h>
>  #include <asm/dsp.h>
>  #include <asm/war.h>
> @@ -194,266 +195,6 @@ static inline unsigned long __xchg(unsigned long x, volatile void * ptr, int siz
>  
>  #define xchg(ptr,x) ((__typeof__(*(ptr)))__xchg((unsigned long)(x),(ptr),sizeof(*(ptr))))
>  
> -#define __HAVE_ARCH_CMPXCHG 1
> -
> -static inline unsigned long __cmpxchg_u32(volatile int * m, unsigned long old,
> -	unsigned long new)
> -{
> -	__u32 retval;
> -
> -	if (cpu_has_llsc && R10000_LLSC_WAR) {
> -		__asm__ __volatile__(
> -		"	.set	push					\n"
> -		"	.set	noat					\n"
> -		"	.set	mips3					\n"
> -		"1:	ll	%0, %2			# __cmpxchg_u32	\n"
> -		"	bne	%0, %z3, 2f				\n"
> -		"	.set	mips0					\n"
> -		"	move	$1, %z4					\n"
> -		"	.set	mips3					\n"
> -		"	sc	$1, %1					\n"
> -		"	beqzl	$1, 1b					\n"
> -		"2:							\n"
> -		"	.set	pop					\n"
> -		: "=&r" (retval), "=R" (*m)
> -		: "R" (*m), "Jr" (old), "Jr" (new)
> -		: "memory");
> -	} else if (cpu_has_llsc) {
> -		__asm__ __volatile__(
> -		"	.set	push					\n"
> -		"	.set	noat					\n"
> -		"	.set	mips3					\n"
> -		"1:	ll	%0, %2			# __cmpxchg_u32	\n"
> -		"	bne	%0, %z3, 2f				\n"
> -		"	.set	mips0					\n"
> -		"	move	$1, %z4					\n"
> -		"	.set	mips3					\n"
> -		"	sc	$1, %1					\n"
> -		"	beqz	$1, 3f					\n"
> -		"2:							\n"
> -		"	.subsection 2					\n"
> -		"3:	b	1b					\n"
> -		"	.previous					\n"
> -		"	.set	pop					\n"
> -		: "=&r" (retval), "=R" (*m)
> -		: "R" (*m), "Jr" (old), "Jr" (new)
> -		: "memory");
> -	} else {
> -		unsigned long flags;
> -
> -		raw_local_irq_save(flags);
> -		retval = *m;
> -		if (retval == old)
> -			*m = new;
> -		raw_local_irq_restore(flags);	/* implies memory barrier  */
> -	}
> -
> -	smp_llsc_mb();
> -
> -	return retval;
> -}
> -
> -static inline unsigned long __cmpxchg_u32_local(volatile int * m,
> -	unsigned long old, unsigned long new)
> -{
> -	__u32 retval;
> -
> -	if (cpu_has_llsc && R10000_LLSC_WAR) {
> -		__asm__ __volatile__(
> -		"	.set	push					\n"
> -		"	.set	noat					\n"
> -		"	.set	mips3					\n"
> -		"1:	ll	%0, %2			# __cmpxchg_u32	\n"
> -		"	bne	%0, %z3, 2f				\n"
> -		"	.set	mips0					\n"
> -		"	move	$1, %z4					\n"
> -		"	.set	mips3					\n"
> -		"	sc	$1, %1					\n"
> -		"	beqzl	$1, 1b					\n"
> -		"2:							\n"
> -		"	.set	pop					\n"
> -		: "=&r" (retval), "=R" (*m)
> -		: "R" (*m), "Jr" (old), "Jr" (new)
> -		: "memory");
> -	} else if (cpu_has_llsc) {
> -		__asm__ __volatile__(
> -		"	.set	push					\n"
> -		"	.set	noat					\n"
> -		"	.set	mips3					\n"
> -		"1:	ll	%0, %2			# __cmpxchg_u32	\n"
> -		"	bne	%0, %z3, 2f				\n"
> -		"	.set	mips0					\n"
> -		"	move	$1, %z4					\n"
> -		"	.set	mips3					\n"
> -		"	sc	$1, %1					\n"
> -		"	beqz	$1, 1b					\n"
> -		"2:							\n"
> -		"	.set	pop					\n"
> -		: "=&r" (retval), "=R" (*m)
> -		: "R" (*m), "Jr" (old), "Jr" (new)
> -		: "memory");
> -	} else {
> -		unsigned long flags;
> -
> -		local_irq_save(flags);
> -		retval = *m;
> -		if (retval == old)
> -			*m = new;
> -		local_irq_restore(flags);	/* implies memory barrier  */
> -	}
> -
> -	return retval;
> -}
> -
> -#ifdef CONFIG_64BIT
> -static inline unsigned long __cmpxchg_u64(volatile int * m, unsigned long old,
> -	unsigned long new)
> -{
> -	__u64 retval;
> -
> -	if (cpu_has_llsc && R10000_LLSC_WAR) {
> -		__asm__ __volatile__(
> -		"	.set	push					\n"
> -		"	.set	noat					\n"
> -		"	.set	mips3					\n"
> -		"1:	lld	%0, %2			# __cmpxchg_u64	\n"
> -		"	bne	%0, %z3, 2f				\n"
> -		"	move	$1, %z4					\n"
> -		"	scd	$1, %1					\n"
> -		"	beqzl	$1, 1b					\n"
> -		"2:							\n"
> -		"	.set	pop					\n"
> -		: "=&r" (retval), "=R" (*m)
> -		: "R" (*m), "Jr" (old), "Jr" (new)
> -		: "memory");
> -	} else if (cpu_has_llsc) {
> -		__asm__ __volatile__(
> -		"	.set	push					\n"
> -		"	.set	noat					\n"
> -		"	.set	mips3					\n"
> -		"1:	lld	%0, %2			# __cmpxchg_u64	\n"
> -		"	bne	%0, %z3, 2f				\n"
> -		"	move	$1, %z4					\n"
> -		"	scd	$1, %1					\n"
> -		"	beqz	$1, 3f					\n"
> -		"2:							\n"
> -		"	.subsection 2					\n"
> -		"3:	b	1b					\n"
> -		"	.previous					\n"
> -		"	.set	pop					\n"
> -		: "=&r" (retval), "=R" (*m)
> -		: "R" (*m), "Jr" (old), "Jr" (new)
> -		: "memory");
> -	} else {
> -		unsigned long flags;
> -
> -		raw_local_irq_save(flags);
> -		retval = *m;
> -		if (retval == old)
> -			*m = new;
> -		raw_local_irq_restore(flags);	/* implies memory barrier  */
> -	}
> -
> -	smp_llsc_mb();
> -
> -	return retval;
> -}
> -
> -static inline unsigned long __cmpxchg_u64_local(volatile int * m,
> -	unsigned long old, unsigned long new)
> -{
> -	__u64 retval;
> -
> -	if (cpu_has_llsc && R10000_LLSC_WAR) {
> -		__asm__ __volatile__(
> -		"	.set	push					\n"
> -		"	.set	noat					\n"
> -		"	.set	mips3					\n"
> -		"1:	lld	%0, %2			# __cmpxchg_u64	\n"
> -		"	bne	%0, %z3, 2f				\n"
> -		"	move	$1, %z4					\n"
> -		"	scd	$1, %1					\n"
> -		"	beqzl	$1, 1b					\n"
> -		"2:							\n"
> -		"	.set	pop					\n"
> -		: "=&r" (retval), "=R" (*m)
> -		: "R" (*m), "Jr" (old), "Jr" (new)
> -		: "memory");
> -	} else if (cpu_has_llsc) {
> -		__asm__ __volatile__(
> -		"	.set	push					\n"
> -		"	.set	noat					\n"
> -		"	.set	mips3					\n"
> -		"1:	lld	%0, %2			# __cmpxchg_u64	\n"
> -		"	bne	%0, %z3, 2f				\n"
> -		"	move	$1, %z4					\n"
> -		"	scd	$1, %1					\n"
> -		"	beqz	$1, 1b					\n"
> -		"2:							\n"
> -		"	.set	pop					\n"
> -		: "=&r" (retval), "=R" (*m)
> -		: "R" (*m), "Jr" (old), "Jr" (new)
> -		: "memory");
> -	} else {
> -		unsigned long flags;
> -
> -		local_irq_save(flags);
> -		retval = *m;
> -		if (retval == old)
> -			*m = new;
> -		local_irq_restore(flags);	/* implies memory barrier  */
> -	}
> -
> -	return retval;
> -}
> -
> -#else
> -extern unsigned long __cmpxchg_u64_unsupported_on_32bit_kernels(
> -	volatile int * m, unsigned long old, unsigned long new);
> -#define __cmpxchg_u64 __cmpxchg_u64_unsupported_on_32bit_kernels
> -extern unsigned long __cmpxchg_u64_local_unsupported_on_32bit_kernels(
> -	volatile int * m, unsigned long old, unsigned long new);
> -#define __cmpxchg_u64_local __cmpxchg_u64_local_unsupported_on_32bit_kernels
> -#endif
> -
> -/* This function doesn't exist, so you'll get a linker error
> -   if something tries to do an invalid cmpxchg().  */
> -extern void __cmpxchg_called_with_bad_pointer(void);
> -
> -static inline unsigned long __cmpxchg(volatile void * ptr, unsigned long old,
> -	unsigned long new, int size)
> -{
> -	switch (size) {
> -	case 4:
> -		return __cmpxchg_u32(ptr, old, new);
> -	case 8:
> -		return __cmpxchg_u64(ptr, old, new);
> -	}
> -	__cmpxchg_called_with_bad_pointer();
> -	return old;
> -}
> -
> -static inline unsigned long __cmpxchg_local(volatile void * ptr,
> -	unsigned long old, unsigned long new, int size)
> -{
> -	switch (size) {
> -	case 4:
> -		return __cmpxchg_u32_local(ptr, old, new);
> -	case 8:
> -		return __cmpxchg_u64_local(ptr, old, new);
> -	}
> -	__cmpxchg_called_with_bad_pointer();
> -	return old;
> -}
> -
> -#define cmpxchg(ptr,old,new) \
> -	((__typeof__(*(ptr)))__cmpxchg((ptr), \
> -		(unsigned long)(old), (unsigned long)(new),sizeof(*(ptr))))
> -
> -#define cmpxchg_local(ptr,old,new) \
> -	((__typeof__(*(ptr)))__cmpxchg_local((ptr), \
> -		(unsigned long)(old), (unsigned long)(new),sizeof(*(ptr))))
> -
>  extern void set_handler (unsigned long offset, void *addr, unsigned long len);
>  extern void set_uncached_handler (unsigned long offset, void *addr, unsigned long len);
>  
>
>
>
>   

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

* Re: cmpxchg broken in some situation
  2007-10-01 15:11   ` Fuxin Zhang
@ 2007-10-01 15:26     ` Ralf Baechle
  2007-10-02  9:34       ` Fuxin Zhang
  0 siblings, 1 reply; 14+ messages in thread
From: Ralf Baechle @ 2007-10-01 15:26 UTC (permalink / raw)
  To: Fuxin Zhang; +Cc: linux-mips

On Mon, Oct 01, 2007 at 11:11:17PM +0800, Fuxin Zhang wrote:

> Sorry that it seems not work:
> the kernel oops at sysfs_open_file->sysfs_get_active with unaligned 
> access(last seen exception on screen, no serial console by hand so it 
> may not be the first exception). It is probably caused by 
> "atomic_cmpxchg" there.
> And keep the old kernel using new modules with patched cmpxchg also lead 
> to glxgears die(should be lock problem like before).

Can you look at the disassembly of the generated code?  It should hopefully
be relativly obvious in the disassembly.

  Ralf

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

* Re: cmpxchg broken in some situation
  2007-10-01 15:26     ` Ralf Baechle
@ 2007-10-02  9:34       ` Fuxin Zhang
  2007-10-02 10:35         ` Ralf Baechle
  0 siblings, 1 reply; 14+ messages in thread
From: Fuxin Zhang @ 2007-10-02  9:34 UTC (permalink / raw)
  To: Ralf Baechle; +Cc: linux-mips

The problem is here:

switch (sizeof(__ptr)) { // --> should be sizeof(*(__ptr))
case 4:
...
Recompiling..

Ralf Baechle 写道:
> On Mon, Oct 01, 2007 at 11:11:17PM +0800, Fuxin Zhang wrote:
>
>   
>> Sorry that it seems not work:
>> the kernel oops at sysfs_open_file->sysfs_get_active with unaligned 
>> access(last seen exception on screen, no serial console by hand so it 
>> may not be the first exception). It is probably caused by 
>> "atomic_cmpxchg" there.
>> And keep the old kernel using new modules with patched cmpxchg also lead 
>> to glxgears die(should be lock problem like before).
>>     
>
> Can you look at the disassembly of the generated code?  It should hopefully
> be relativly obvious in the disassembly.
>
>   Ralf
>
>
>
>   

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

* Re: cmpxchg broken in some situation
  2007-10-02  9:34       ` Fuxin Zhang
@ 2007-10-02 10:35         ` Ralf Baechle
  2007-10-02 14:22           ` Thiemo Seufer
  2007-10-02 22:48           ` Fuxin Zhang
  0 siblings, 2 replies; 14+ messages in thread
From: Ralf Baechle @ 2007-10-02 10:35 UTC (permalink / raw)
  To: Fuxin Zhang; +Cc: linux-mips

On Tue, Oct 02, 2007 at 05:34:44PM +0800, Fuxin Zhang wrote:

> The problem is here:
> 
> switch (sizeof(__ptr)) { // --> should be sizeof(*(__ptr))
> case 4:
> ...
> Recompiling..

There was another small kink, cmpxchg_local() does not imply a memory
barrier so I optimized that case.

And I don't complain about it being 151 lines shorter ;-)

  Ralf

From: Ralf Baechle <ralf@linux-mips.org>

[MIPS] Typeproof reimplementation of cmpxchg.

Signed-off-by: Ralf Baechle <ralf@linux-mips.org>

diff --git a/include/asm-mips/cmpxchg.h b/include/asm-mips/cmpxchg.h
new file mode 100644
index 0000000..46bac47
--- /dev/null
+++ b/include/asm-mips/cmpxchg.h
@@ -0,0 +1,107 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2003, 06, 07 by Ralf Baechle (ralf@linux-mips.org)
+ */
+#ifndef __ASM_CMPXCHG_H
+#define __ASM_CMPXCHG_H
+
+#include <linux/irqflags.h>
+
+#define __HAVE_ARCH_CMPXCHG 1
+
+#define __cmpxchg_asm(ld, st, m, old, new)				\
+({									\
+	__typeof(*(m)) __ret;						\
+									\
+	if (cpu_has_llsc && R10000_LLSC_WAR) {				\
+		__asm__ __volatile__(					\
+		"	.set	push				\n"	\
+		"	.set	noat				\n"	\
+		"	.set	mips3				\n"	\
+		"1:	" ld "	%0, %2		# __cmpxchg_u32	\n"	\
+		"	bne	%0, %z3, 2f			\n"	\
+		"	.set	mips0				\n"	\
+		"	move	$1, %z4				\n"	\
+		"	.set	mips3				\n"	\
+		"	" st "	$1, %1				\n"	\
+		"	beqzl	$1, 1b				\n"	\
+		"2:						\n"	\
+		"	.set	pop				\n"	\
+		: "=&r" (__ret), "=R" (*m)				\
+		: "R" (*m), "Jr" (old), "Jr" (new)			\
+		: "memory");						\
+	} else if (cpu_has_llsc) {					\
+		__asm__ __volatile__(					\
+		"	.set	push				\n"	\
+		"	.set	noat				\n"	\
+		"	.set	mips3				\n"	\
+		"1:	" ld "	%0, %2		# __cmpxchg_u32	\n"	\
+		"	bne	%0, %z3, 2f			\n"	\
+		"	.set	mips0				\n"	\
+		"	move	$1, %z4				\n"	\
+		"	.set	mips3				\n"	\
+		"	" st "	$1, %1				\n"	\
+		"	beqz	$1, 3f				\n"	\
+		"2:						\n"	\
+		"	.subsection 2				\n"	\
+		"3:	b	1b				\n"	\
+		"	.previous				\n"	\
+		"	.set	pop				\n"	\
+		: "=&r" (__ret), "=R" (*m)				\
+		: "R" (*m), "Jr" (old), "Jr" (new)			\
+		: "memory");						\
+	} else {							\
+		unsigned long __flags;					\
+									\
+		raw_local_irq_save(__flags);				\
+		__ret = *m;						\
+		if (__ret == old)					\
+			*m = new;					\
+		raw_local_irq_restore(__flags);				\
+	}								\
+									\
+	smp_llsc_mb();							\
+									\
+	__ret;								\
+})
+
+/*
+ * This function doesn't exist, so you'll get a linker error
+ * if something tries to do an invalid cmpxchg().
+ */
+extern void __cmpxchg_called_with_bad_pointer(void);
+
+#define __cmpxchg(ptr,old,new,barrier)					\
+({									\
+	__typeof__(ptr) __ptr = (ptr);					\
+	__typeof__(*(ptr)) __old = (old);				\
+	__typeof__(*(ptr)) __new = (new);				\
+	__typeof__(*(ptr)) __res = 0;					\
+									\
+	switch (sizeof(*(__ptr))) {					\
+	case 4:								\
+		__res = __cmpxchg_asm("ll", "sc", __ptr, __old, __new);	\
+		break;							\
+	case 8:								\
+		if (sizeof(long) == 8) {				\
+			__res = __cmpxchg_asm("lld", "scd", __ptr,	\
+					   __old, __new);		\
+			break;						\
+		}							\
+	default:							\
+		__cmpxchg_called_with_bad_pointer();			\
+		break;							\
+	}								\
+									\
+	barrier;							\
+									\
+	__res;								\
+})
+
+#define cmpxchg(ptr, old, new)		__cmpxchg(ptr, old, new, smp_llsc_mb())
+#define cmpxchg_local(ptr, old, new)	__cmpxchg(ptr, old, new,)
+
+#endif /* __ASM_CMPXCHG_H */
diff --git a/include/asm-mips/local.h b/include/asm-mips/local.h
index ed882c8..7034a01 100644
--- a/include/asm-mips/local.h
+++ b/include/asm-mips/local.h
@@ -4,6 +4,7 @@
 #include <linux/percpu.h>
 #include <linux/bitops.h>
 #include <asm/atomic.h>
+#include <asm/cmpxchg.h>
 #include <asm/war.h>
 
 typedef struct
diff --git a/include/asm-mips/system.h b/include/asm-mips/system.h
index 357251f..480b574 100644
--- a/include/asm-mips/system.h
+++ b/include/asm-mips/system.h
@@ -17,6 +17,7 @@
 
 #include <asm/addrspace.h>
 #include <asm/barrier.h>
+#include <asm/cmpxchg.h>
 #include <asm/cpu-features.h>
 #include <asm/dsp.h>
 #include <asm/war.h>
@@ -194,266 +195,6 @@ static inline unsigned long __xchg(unsigned long x, volatile void * ptr, int siz
 
 #define xchg(ptr,x) ((__typeof__(*(ptr)))__xchg((unsigned long)(x),(ptr),sizeof(*(ptr))))
 
-#define __HAVE_ARCH_CMPXCHG 1
-
-static inline unsigned long __cmpxchg_u32(volatile int * m, unsigned long old,
-	unsigned long new)
-{
-	__u32 retval;
-
-	if (cpu_has_llsc && R10000_LLSC_WAR) {
-		__asm__ __volatile__(
-		"	.set	push					\n"
-		"	.set	noat					\n"
-		"	.set	mips3					\n"
-		"1:	ll	%0, %2			# __cmpxchg_u32	\n"
-		"	bne	%0, %z3, 2f				\n"
-		"	.set	mips0					\n"
-		"	move	$1, %z4					\n"
-		"	.set	mips3					\n"
-		"	sc	$1, %1					\n"
-		"	beqzl	$1, 1b					\n"
-		"2:							\n"
-		"	.set	pop					\n"
-		: "=&r" (retval), "=R" (*m)
-		: "R" (*m), "Jr" (old), "Jr" (new)
-		: "memory");
-	} else if (cpu_has_llsc) {
-		__asm__ __volatile__(
-		"	.set	push					\n"
-		"	.set	noat					\n"
-		"	.set	mips3					\n"
-		"1:	ll	%0, %2			# __cmpxchg_u32	\n"
-		"	bne	%0, %z3, 2f				\n"
-		"	.set	mips0					\n"
-		"	move	$1, %z4					\n"
-		"	.set	mips3					\n"
-		"	sc	$1, %1					\n"
-		"	beqz	$1, 3f					\n"
-		"2:							\n"
-		"	.subsection 2					\n"
-		"3:	b	1b					\n"
-		"	.previous					\n"
-		"	.set	pop					\n"
-		: "=&r" (retval), "=R" (*m)
-		: "R" (*m), "Jr" (old), "Jr" (new)
-		: "memory");
-	} else {
-		unsigned long flags;
-
-		raw_local_irq_save(flags);
-		retval = *m;
-		if (retval == old)
-			*m = new;
-		raw_local_irq_restore(flags);	/* implies memory barrier  */
-	}
-
-	smp_llsc_mb();
-
-	return retval;
-}
-
-static inline unsigned long __cmpxchg_u32_local(volatile int * m,
-	unsigned long old, unsigned long new)
-{
-	__u32 retval;
-
-	if (cpu_has_llsc && R10000_LLSC_WAR) {
-		__asm__ __volatile__(
-		"	.set	push					\n"
-		"	.set	noat					\n"
-		"	.set	mips3					\n"
-		"1:	ll	%0, %2			# __cmpxchg_u32	\n"
-		"	bne	%0, %z3, 2f				\n"
-		"	.set	mips0					\n"
-		"	move	$1, %z4					\n"
-		"	.set	mips3					\n"
-		"	sc	$1, %1					\n"
-		"	beqzl	$1, 1b					\n"
-		"2:							\n"
-		"	.set	pop					\n"
-		: "=&r" (retval), "=R" (*m)
-		: "R" (*m), "Jr" (old), "Jr" (new)
-		: "memory");
-	} else if (cpu_has_llsc) {
-		__asm__ __volatile__(
-		"	.set	push					\n"
-		"	.set	noat					\n"
-		"	.set	mips3					\n"
-		"1:	ll	%0, %2			# __cmpxchg_u32	\n"
-		"	bne	%0, %z3, 2f				\n"
-		"	.set	mips0					\n"
-		"	move	$1, %z4					\n"
-		"	.set	mips3					\n"
-		"	sc	$1, %1					\n"
-		"	beqz	$1, 1b					\n"
-		"2:							\n"
-		"	.set	pop					\n"
-		: "=&r" (retval), "=R" (*m)
-		: "R" (*m), "Jr" (old), "Jr" (new)
-		: "memory");
-	} else {
-		unsigned long flags;
-
-		local_irq_save(flags);
-		retval = *m;
-		if (retval == old)
-			*m = new;
-		local_irq_restore(flags);	/* implies memory barrier  */
-	}
-
-	return retval;
-}
-
-#ifdef CONFIG_64BIT
-static inline unsigned long __cmpxchg_u64(volatile int * m, unsigned long old,
-	unsigned long new)
-{
-	__u64 retval;
-
-	if (cpu_has_llsc && R10000_LLSC_WAR) {
-		__asm__ __volatile__(
-		"	.set	push					\n"
-		"	.set	noat					\n"
-		"	.set	mips3					\n"
-		"1:	lld	%0, %2			# __cmpxchg_u64	\n"
-		"	bne	%0, %z3, 2f				\n"
-		"	move	$1, %z4					\n"
-		"	scd	$1, %1					\n"
-		"	beqzl	$1, 1b					\n"
-		"2:							\n"
-		"	.set	pop					\n"
-		: "=&r" (retval), "=R" (*m)
-		: "R" (*m), "Jr" (old), "Jr" (new)
-		: "memory");
-	} else if (cpu_has_llsc) {
-		__asm__ __volatile__(
-		"	.set	push					\n"
-		"	.set	noat					\n"
-		"	.set	mips3					\n"
-		"1:	lld	%0, %2			# __cmpxchg_u64	\n"
-		"	bne	%0, %z3, 2f				\n"
-		"	move	$1, %z4					\n"
-		"	scd	$1, %1					\n"
-		"	beqz	$1, 3f					\n"
-		"2:							\n"
-		"	.subsection 2					\n"
-		"3:	b	1b					\n"
-		"	.previous					\n"
-		"	.set	pop					\n"
-		: "=&r" (retval), "=R" (*m)
-		: "R" (*m), "Jr" (old), "Jr" (new)
-		: "memory");
-	} else {
-		unsigned long flags;
-
-		raw_local_irq_save(flags);
-		retval = *m;
-		if (retval == old)
-			*m = new;
-		raw_local_irq_restore(flags);	/* implies memory barrier  */
-	}
-
-	smp_llsc_mb();
-
-	return retval;
-}
-
-static inline unsigned long __cmpxchg_u64_local(volatile int * m,
-	unsigned long old, unsigned long new)
-{
-	__u64 retval;
-
-	if (cpu_has_llsc && R10000_LLSC_WAR) {
-		__asm__ __volatile__(
-		"	.set	push					\n"
-		"	.set	noat					\n"
-		"	.set	mips3					\n"
-		"1:	lld	%0, %2			# __cmpxchg_u64	\n"
-		"	bne	%0, %z3, 2f				\n"
-		"	move	$1, %z4					\n"
-		"	scd	$1, %1					\n"
-		"	beqzl	$1, 1b					\n"
-		"2:							\n"
-		"	.set	pop					\n"
-		: "=&r" (retval), "=R" (*m)
-		: "R" (*m), "Jr" (old), "Jr" (new)
-		: "memory");
-	} else if (cpu_has_llsc) {
-		__asm__ __volatile__(
-		"	.set	push					\n"
-		"	.set	noat					\n"
-		"	.set	mips3					\n"
-		"1:	lld	%0, %2			# __cmpxchg_u64	\n"
-		"	bne	%0, %z3, 2f				\n"
-		"	move	$1, %z4					\n"
-		"	scd	$1, %1					\n"
-		"	beqz	$1, 1b					\n"
-		"2:							\n"
-		"	.set	pop					\n"
-		: "=&r" (retval), "=R" (*m)
-		: "R" (*m), "Jr" (old), "Jr" (new)
-		: "memory");
-	} else {
-		unsigned long flags;
-
-		local_irq_save(flags);
-		retval = *m;
-		if (retval == old)
-			*m = new;
-		local_irq_restore(flags);	/* implies memory barrier  */
-	}
-
-	return retval;
-}
-
-#else
-extern unsigned long __cmpxchg_u64_unsupported_on_32bit_kernels(
-	volatile int * m, unsigned long old, unsigned long new);
-#define __cmpxchg_u64 __cmpxchg_u64_unsupported_on_32bit_kernels
-extern unsigned long __cmpxchg_u64_local_unsupported_on_32bit_kernels(
-	volatile int * m, unsigned long old, unsigned long new);
-#define __cmpxchg_u64_local __cmpxchg_u64_local_unsupported_on_32bit_kernels
-#endif
-
-/* This function doesn't exist, so you'll get a linker error
-   if something tries to do an invalid cmpxchg().  */
-extern void __cmpxchg_called_with_bad_pointer(void);
-
-static inline unsigned long __cmpxchg(volatile void * ptr, unsigned long old,
-	unsigned long new, int size)
-{
-	switch (size) {
-	case 4:
-		return __cmpxchg_u32(ptr, old, new);
-	case 8:
-		return __cmpxchg_u64(ptr, old, new);
-	}
-	__cmpxchg_called_with_bad_pointer();
-	return old;
-}
-
-static inline unsigned long __cmpxchg_local(volatile void * ptr,
-	unsigned long old, unsigned long new, int size)
-{
-	switch (size) {
-	case 4:
-		return __cmpxchg_u32_local(ptr, old, new);
-	case 8:
-		return __cmpxchg_u64_local(ptr, old, new);
-	}
-	__cmpxchg_called_with_bad_pointer();
-	return old;
-}
-
-#define cmpxchg(ptr,old,new) \
-	((__typeof__(*(ptr)))__cmpxchg((ptr), \
-		(unsigned long)(old), (unsigned long)(new),sizeof(*(ptr))))
-
-#define cmpxchg_local(ptr,old,new) \
-	((__typeof__(*(ptr)))__cmpxchg_local((ptr), \
-		(unsigned long)(old), (unsigned long)(new),sizeof(*(ptr))))
-
 extern void set_handler (unsigned long offset, void *addr, unsigned long len);
 extern void set_uncached_handler (unsigned long offset, void *addr, unsigned long len);
 

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

* Re: cmpxchg broken in some situation
  2007-10-02 10:35         ` Ralf Baechle
@ 2007-10-02 14:22           ` Thiemo Seufer
  2007-10-02 23:15             ` Ralf Baechle
  2007-10-02 22:48           ` Fuxin Zhang
  1 sibling, 1 reply; 14+ messages in thread
From: Thiemo Seufer @ 2007-10-02 14:22 UTC (permalink / raw)
  To: Ralf Baechle; +Cc: Fuxin Zhang, linux-mips

Ralf Baechle wrote:
> On Tue, Oct 02, 2007 at 05:34:44PM +0800, Fuxin Zhang wrote:
> 
> > The problem is here:
> > 
> > switch (sizeof(__ptr)) { // --> should be sizeof(*(__ptr))
> > case 4:
> > ...
> > Recompiling..
> 
> There was another small kink, cmpxchg_local() does not imply a memory
> barrier so I optimized that case.
> 
> And I don't complain about it being 151 lines shorter ;-)
> 
>   Ralf
> 
> From: Ralf Baechle <ralf@linux-mips.org>
> 
> [MIPS] Typeproof reimplementation of cmpxchg.
> 
> Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
> 
> diff --git a/include/asm-mips/cmpxchg.h b/include/asm-mips/cmpxchg.h
> new file mode 100644
> index 0000000..46bac47
> --- /dev/null
> +++ b/include/asm-mips/cmpxchg.h
> @@ -0,0 +1,107 @@
> +/*
> + * This file is subject to the terms and conditions of the GNU General Public
> + * License.  See the file "COPYING" in the main directory of this archive
> + * for more details.
> + *
> + * Copyright (C) 2003, 06, 07 by Ralf Baechle (ralf@linux-mips.org)
> + */
> +#ifndef __ASM_CMPXCHG_H
> +#define __ASM_CMPXCHG_H
> +
> +#include <linux/irqflags.h>
> +
> +#define __HAVE_ARCH_CMPXCHG 1
> +
> +#define __cmpxchg_asm(ld, st, m, old, new)				\
> +({									\
> +	__typeof(*(m)) __ret;						\
> +									\
> +	if (cpu_has_llsc && R10000_LLSC_WAR) {				\
> +		__asm__ __volatile__(					\
> +		"	.set	push				\n"	\
> +		"	.set	noat				\n"	\
> +		"	.set	mips3				\n"	\
> +		"1:	" ld "	%0, %2		# __cmpxchg_u32	\n"	\
> +		"	bne	%0, %z3, 2f			\n"	\
> +		"	.set	mips0				\n"	\
> +		"	move	$1, %z4				\n"	\
> +		"	.set	mips3				\n"	\
> +		"	" st "	$1, %1				\n"	\
> +		"	beqzl	$1, 1b				\n"	\
> +		"2:						\n"	\
> +		"	.set	pop				\n"	\
> +		: "=&r" (__ret), "=R" (*m)				\
> +		: "R" (*m), "Jr" (old), "Jr" (new)			\
> +		: "memory");						\
> +	} else if (cpu_has_llsc) {					\
> +		__asm__ __volatile__(					\
> +		"	.set	push				\n"	\
> +		"	.set	noat				\n"	\
> +		"	.set	mips3				\n"	\
> +		"1:	" ld "	%0, %2		# __cmpxchg_u32	\n"	\
> +		"	bne	%0, %z3, 2f			\n"	\
> +		"	.set	mips0				\n"	\
> +		"	move	$1, %z4				\n"	\
> +		"	.set	mips3				\n"	\
> +		"	" st "	$1, %1				\n"	\
> +		"	beqz	$1, 3f				\n"	\
> +		"2:						\n"	\
> +		"	.subsection 2				\n"	\
> +		"3:	b	1b				\n"	\
> +		"	.previous				\n"	\
> +		"	.set	pop				\n"	\
> +		: "=&r" (__ret), "=R" (*m)				\
> +		: "R" (*m), "Jr" (old), "Jr" (new)			\
> +		: "memory");						\
> +	} else {							\
> +		unsigned long __flags;					\
> +									\
> +		raw_local_irq_save(__flags);				\
> +		__ret = *m;						\
> +		if (__ret == old)					\
> +			*m = new;					\
> +		raw_local_irq_restore(__flags);				\
> +	}								\
> +									\
> +	smp_llsc_mb();							\

I think this line is surplus.

> +									\
> +	__ret;								\
> +})
> +
> +/*
> + * This function doesn't exist, so you'll get a linker error
> + * if something tries to do an invalid cmpxchg().
> + */
> +extern void __cmpxchg_called_with_bad_pointer(void);
> +
> +#define __cmpxchg(ptr,old,new,barrier)					\
> +({									\
> +	__typeof__(ptr) __ptr = (ptr);					\
> +	__typeof__(*(ptr)) __old = (old);				\
> +	__typeof__(*(ptr)) __new = (new);				\
> +	__typeof__(*(ptr)) __res = 0;					\

Maybe we need an acquire barrier here for some systems.

> +	switch (sizeof(*(__ptr))) {					\
> +	case 4:								\
> +		__res = __cmpxchg_asm("ll", "sc", __ptr, __old, __new);	\
> +		break;							\
> +	case 8:								\
> +		if (sizeof(long) == 8) {				\
> +			__res = __cmpxchg_asm("lld", "scd", __ptr,	\
> +					   __old, __new);		\
> +			break;						\
> +		}							\
> +	default:							\
> +		__cmpxchg_called_with_bad_pointer();			\
> +		break;							\
> +	}								\
> +									\
> +	barrier;							\
> +									\
> +	__res;								\
> +})
> +
> +#define cmpxchg(ptr, old, new)		__cmpxchg(ptr, old, new, smp_llsc_mb())
> +#define cmpxchg_local(ptr, old, new)	__cmpxchg(ptr, old, new,)
> +
> +#endif /* __ASM_CMPXCHG_H */


Thiemo

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

* Re: cmpxchg broken in some situation
  2007-10-02 10:35         ` Ralf Baechle
  2007-10-02 14:22           ` Thiemo Seufer
@ 2007-10-02 22:48           ` Fuxin Zhang
  2007-10-02 22:52             ` Ralf Baechle
  1 sibling, 1 reply; 14+ messages in thread
From: Fuxin Zhang @ 2007-10-02 22:48 UTC (permalink / raw)
  To: Ralf Baechle; +Cc: linux-mips

And the comment "# cmpxchg_u32" is out of date too:)

Ralf Baechle 写道:
> On Tue, Oct 02, 2007 at 05:34:44PM +0800, Fuxin Zhang wrote:
>
>   
>> The problem is here:
>>
>> switch (sizeof(__ptr)) { // --> should be sizeof(*(__ptr))
>> case 4:
>> ...
>> Recompiling..
>>     
>
> There was another small kink, cmpxchg_local() does not imply a memory
> barrier so I optimized that case.
>
> And I don't complain about it being 151 lines shorter ;-)
>
>   Ralf
>
> From: Ralf Baechle <ralf@linux-mips.org>
>
> [MIPS] Typeproof reimplementation of cmpxchg.
>
> Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
>
> diff --git a/include/asm-mips/cmpxchg.h b/include/asm-mips/cmpxchg.h
> new file mode 100644
> index 0000000..46bac47
> --- /dev/null
> +++ b/include/asm-mips/cmpxchg.h
> @@ -0,0 +1,107 @@
> +/*
> + * This file is subject to the terms and conditions of the GNU General Public
> + * License.  See the file "COPYING" in the main directory of this archive
> + * for more details.
> + *
> + * Copyright (C) 2003, 06, 07 by Ralf Baechle (ralf@linux-mips.org)
> + */
> +#ifndef __ASM_CMPXCHG_H
> +#define __ASM_CMPXCHG_H
> +
> +#include <linux/irqflags.h>
> +
> +#define __HAVE_ARCH_CMPXCHG 1
> +
> +#define __cmpxchg_asm(ld, st, m, old, new)				\
> +({									\
> +	__typeof(*(m)) __ret;						\
> +									\
> +	if (cpu_has_llsc && R10000_LLSC_WAR) {				\
> +		__asm__ __volatile__(					\
> +		"	.set	push				\n"	\
> +		"	.set	noat				\n"	\
> +		"	.set	mips3				\n"	\
> +		"1:	" ld "	%0, %2		# __cmpxchg_u32	\n"	\
> +		"	bne	%0, %z3, 2f			\n"	\
> +		"	.set	mips0				\n"	\
> +		"	move	$1, %z4				\n"	\
> +		"	.set	mips3				\n"	\
> +		"	" st "	$1, %1				\n"	\
> +		"	beqzl	$1, 1b				\n"	\
> +		"2:						\n"	\
> +		"	.set	pop				\n"	\
> +		: "=&r" (__ret), "=R" (*m)				\
> +		: "R" (*m), "Jr" (old), "Jr" (new)			\
> +		: "memory");						\
> +	} else if (cpu_has_llsc) {					\
> +		__asm__ __volatile__(					\
> +		"	.set	push				\n"	\
> +		"	.set	noat				\n"	\
> +		"	.set	mips3				\n"	\
> +		"1:	" ld "	%0, %2		# __cmpxchg_u32	\n"	\
> +		"	bne	%0, %z3, 2f			\n"	\
> +		"	.set	mips0				\n"	\
> +		"	move	$1, %z4				\n"	\
> +		"	.set	mips3				\n"	\
> +		"	" st "	$1, %1				\n"	\
> +		"	beqz	$1, 3f				\n"	\
> +		"2:						\n"	\
> +		"	.subsection 2				\n"	\
> +		"3:	b	1b				\n"	\
> +		"	.previous				\n"	\
> +		"	.set	pop				\n"	\
> +		: "=&r" (__ret), "=R" (*m)				\
> +		: "R" (*m), "Jr" (old), "Jr" (new)			\
> +		: "memory");						\
> +	} else {							\
> +		unsigned long __flags;					\
> +									\
> +		raw_local_irq_save(__flags);				\
> +		__ret = *m;						\
> +		if (__ret == old)					\
> +			*m = new;					\
> +		raw_local_irq_restore(__flags);				\
> +	}								\
> +									\
> +	smp_llsc_mb();							\
> +									\
> +	__ret;								\
> +})
> +
> +/*
> + * This function doesn't exist, so you'll get a linker error
> + * if something tries to do an invalid cmpxchg().
> + */
> +extern void __cmpxchg_called_with_bad_pointer(void);
> +
> +#define __cmpxchg(ptr,old,new,barrier)					\
> +({									\
> +	__typeof__(ptr) __ptr = (ptr);					\
> +	__typeof__(*(ptr)) __old = (old);				\
> +	__typeof__(*(ptr)) __new = (new);				\
> +	__typeof__(*(ptr)) __res = 0;					\
> +									\
> +	switch (sizeof(*(__ptr))) {					\
> +	case 4:								\
> +		__res = __cmpxchg_asm("ll", "sc", __ptr, __old, __new);	\
> +		break;							\
> +	case 8:								\
> +		if (sizeof(long) == 8) {				\
> +			__res = __cmpxchg_asm("lld", "scd", __ptr,	\
> +					   __old, __new);		\
> +			break;						\
> +		}							\
> +	default:							\
> +		__cmpxchg_called_with_bad_pointer();			\
> +		break;							\
> +	}								\
> +									\
> +	barrier;							\
> +									\
> +	__res;								\
> +})
> +
> +#define cmpxchg(ptr, old, new)		__cmpxchg(ptr, old, new, smp_llsc_mb())
> +#define cmpxchg_local(ptr, old, new)	__cmpxchg(ptr, old, new,)
> +
> +#endif /* __ASM_CMPXCHG_H */
> diff --git a/include/asm-mips/local.h b/include/asm-mips/local.h
> index ed882c8..7034a01 100644
> --- a/include/asm-mips/local.h
> +++ b/include/asm-mips/local.h
> @@ -4,6 +4,7 @@
>  #include <linux/percpu.h>
>  #include <linux/bitops.h>
>  #include <asm/atomic.h>
> +#include <asm/cmpxchg.h>
>  #include <asm/war.h>
>  
>  typedef struct
> diff --git a/include/asm-mips/system.h b/include/asm-mips/system.h
> index 357251f..480b574 100644
> --- a/include/asm-mips/system.h
> +++ b/include/asm-mips/system.h
> @@ -17,6 +17,7 @@
>  
>  #include <asm/addrspace.h>
>  #include <asm/barrier.h>
> +#include <asm/cmpxchg.h>
>  #include <asm/cpu-features.h>
>  #include <asm/dsp.h>
>  #include <asm/war.h>
> @@ -194,266 +195,6 @@ static inline unsigned long __xchg(unsigned long x, volatile void * ptr, int siz
>  
>  #define xchg(ptr,x) ((__typeof__(*(ptr)))__xchg((unsigned long)(x),(ptr),sizeof(*(ptr))))
>  
> -#define __HAVE_ARCH_CMPXCHG 1
> -
> -static inline unsigned long __cmpxchg_u32(volatile int * m, unsigned long old,
> -	unsigned long new)
> -{
> -	__u32 retval;
> -
> -	if (cpu_has_llsc && R10000_LLSC_WAR) {
> -		__asm__ __volatile__(
> -		"	.set	push					\n"
> -		"	.set	noat					\n"
> -		"	.set	mips3					\n"
> -		"1:	ll	%0, %2			# __cmpxchg_u32	\n"
> -		"	bne	%0, %z3, 2f				\n"
> -		"	.set	mips0					\n"
> -		"	move	$1, %z4					\n"
> -		"	.set	mips3					\n"
> -		"	sc	$1, %1					\n"
> -		"	beqzl	$1, 1b					\n"
> -		"2:							\n"
> -		"	.set	pop					\n"
> -		: "=&r" (retval), "=R" (*m)
> -		: "R" (*m), "Jr" (old), "Jr" (new)
> -		: "memory");
> -	} else if (cpu_has_llsc) {
> -		__asm__ __volatile__(
> -		"	.set	push					\n"
> -		"	.set	noat					\n"
> -		"	.set	mips3					\n"
> -		"1:	ll	%0, %2			# __cmpxchg_u32	\n"
> -		"	bne	%0, %z3, 2f				\n"
> -		"	.set	mips0					\n"
> -		"	move	$1, %z4					\n"
> -		"	.set	mips3					\n"
> -		"	sc	$1, %1					\n"
> -		"	beqz	$1, 3f					\n"
> -		"2:							\n"
> -		"	.subsection 2					\n"
> -		"3:	b	1b					\n"
> -		"	.previous					\n"
> -		"	.set	pop					\n"
> -		: "=&r" (retval), "=R" (*m)
> -		: "R" (*m), "Jr" (old), "Jr" (new)
> -		: "memory");
> -	} else {
> -		unsigned long flags;
> -
> -		raw_local_irq_save(flags);
> -		retval = *m;
> -		if (retval == old)
> -			*m = new;
> -		raw_local_irq_restore(flags);	/* implies memory barrier  */
> -	}
> -
> -	smp_llsc_mb();
> -
> -	return retval;
> -}
> -
> -static inline unsigned long __cmpxchg_u32_local(volatile int * m,
> -	unsigned long old, unsigned long new)
> -{
> -	__u32 retval;
> -
> -	if (cpu_has_llsc && R10000_LLSC_WAR) {
> -		__asm__ __volatile__(
> -		"	.set	push					\n"
> -		"	.set	noat					\n"
> -		"	.set	mips3					\n"
> -		"1:	ll	%0, %2			# __cmpxchg_u32	\n"
> -		"	bne	%0, %z3, 2f				\n"
> -		"	.set	mips0					\n"
> -		"	move	$1, %z4					\n"
> -		"	.set	mips3					\n"
> -		"	sc	$1, %1					\n"
> -		"	beqzl	$1, 1b					\n"
> -		"2:							\n"
> -		"	.set	pop					\n"
> -		: "=&r" (retval), "=R" (*m)
> -		: "R" (*m), "Jr" (old), "Jr" (new)
> -		: "memory");
> -	} else if (cpu_has_llsc) {
> -		__asm__ __volatile__(
> -		"	.set	push					\n"
> -		"	.set	noat					\n"
> -		"	.set	mips3					\n"
> -		"1:	ll	%0, %2			# __cmpxchg_u32	\n"
> -		"	bne	%0, %z3, 2f				\n"
> -		"	.set	mips0					\n"
> -		"	move	$1, %z4					\n"
> -		"	.set	mips3					\n"
> -		"	sc	$1, %1					\n"
> -		"	beqz	$1, 1b					\n"
> -		"2:							\n"
> -		"	.set	pop					\n"
> -		: "=&r" (retval), "=R" (*m)
> -		: "R" (*m), "Jr" (old), "Jr" (new)
> -		: "memory");
> -	} else {
> -		unsigned long flags;
> -
> -		local_irq_save(flags);
> -		retval = *m;
> -		if (retval == old)
> -			*m = new;
> -		local_irq_restore(flags);	/* implies memory barrier  */
> -	}
> -
> -	return retval;
> -}
> -
> -#ifdef CONFIG_64BIT
> -static inline unsigned long __cmpxchg_u64(volatile int * m, unsigned long old,
> -	unsigned long new)
> -{
> -	__u64 retval;
> -
> -	if (cpu_has_llsc && R10000_LLSC_WAR) {
> -		__asm__ __volatile__(
> -		"	.set	push					\n"
> -		"	.set	noat					\n"
> -		"	.set	mips3					\n"
> -		"1:	lld	%0, %2			# __cmpxchg_u64	\n"
> -		"	bne	%0, %z3, 2f				\n"
> -		"	move	$1, %z4					\n"
> -		"	scd	$1, %1					\n"
> -		"	beqzl	$1, 1b					\n"
> -		"2:							\n"
> -		"	.set	pop					\n"
> -		: "=&r" (retval), "=R" (*m)
> -		: "R" (*m), "Jr" (old), "Jr" (new)
> -		: "memory");
> -	} else if (cpu_has_llsc) {
> -		__asm__ __volatile__(
> -		"	.set	push					\n"
> -		"	.set	noat					\n"
> -		"	.set	mips3					\n"
> -		"1:	lld	%0, %2			# __cmpxchg_u64	\n"
> -		"	bne	%0, %z3, 2f				\n"
> -		"	move	$1, %z4					\n"
> -		"	scd	$1, %1					\n"
> -		"	beqz	$1, 3f					\n"
> -		"2:							\n"
> -		"	.subsection 2					\n"
> -		"3:	b	1b					\n"
> -		"	.previous					\n"
> -		"	.set	pop					\n"
> -		: "=&r" (retval), "=R" (*m)
> -		: "R" (*m), "Jr" (old), "Jr" (new)
> -		: "memory");
> -	} else {
> -		unsigned long flags;
> -
> -		raw_local_irq_save(flags);
> -		retval = *m;
> -		if (retval == old)
> -			*m = new;
> -		raw_local_irq_restore(flags);	/* implies memory barrier  */
> -	}
> -
> -	smp_llsc_mb();
> -
> -	return retval;
> -}
> -
> -static inline unsigned long __cmpxchg_u64_local(volatile int * m,
> -	unsigned long old, unsigned long new)
> -{
> -	__u64 retval;
> -
> -	if (cpu_has_llsc && R10000_LLSC_WAR) {
> -		__asm__ __volatile__(
> -		"	.set	push					\n"
> -		"	.set	noat					\n"
> -		"	.set	mips3					\n"
> -		"1:	lld	%0, %2			# __cmpxchg_u64	\n"
> -		"	bne	%0, %z3, 2f				\n"
> -		"	move	$1, %z4					\n"
> -		"	scd	$1, %1					\n"
> -		"	beqzl	$1, 1b					\n"
> -		"2:							\n"
> -		"	.set	pop					\n"
> -		: "=&r" (retval), "=R" (*m)
> -		: "R" (*m), "Jr" (old), "Jr" (new)
> -		: "memory");
> -	} else if (cpu_has_llsc) {
> -		__asm__ __volatile__(
> -		"	.set	push					\n"
> -		"	.set	noat					\n"
> -		"	.set	mips3					\n"
> -		"1:	lld	%0, %2			# __cmpxchg_u64	\n"
> -		"	bne	%0, %z3, 2f				\n"
> -		"	move	$1, %z4					\n"
> -		"	scd	$1, %1					\n"
> -		"	beqz	$1, 1b					\n"
> -		"2:							\n"
> -		"	.set	pop					\n"
> -		: "=&r" (retval), "=R" (*m)
> -		: "R" (*m), "Jr" (old), "Jr" (new)
> -		: "memory");
> -	} else {
> -		unsigned long flags;
> -
> -		local_irq_save(flags);
> -		retval = *m;
> -		if (retval == old)
> -			*m = new;
> -		local_irq_restore(flags);	/* implies memory barrier  */
> -	}
> -
> -	return retval;
> -}
> -
> -#else
> -extern unsigned long __cmpxchg_u64_unsupported_on_32bit_kernels(
> -	volatile int * m, unsigned long old, unsigned long new);
> -#define __cmpxchg_u64 __cmpxchg_u64_unsupported_on_32bit_kernels
> -extern unsigned long __cmpxchg_u64_local_unsupported_on_32bit_kernels(
> -	volatile int * m, unsigned long old, unsigned long new);
> -#define __cmpxchg_u64_local __cmpxchg_u64_local_unsupported_on_32bit_kernels
> -#endif
> -
> -/* This function doesn't exist, so you'll get a linker error
> -   if something tries to do an invalid cmpxchg().  */
> -extern void __cmpxchg_called_with_bad_pointer(void);
> -
> -static inline unsigned long __cmpxchg(volatile void * ptr, unsigned long old,
> -	unsigned long new, int size)
> -{
> -	switch (size) {
> -	case 4:
> -		return __cmpxchg_u32(ptr, old, new);
> -	case 8:
> -		return __cmpxchg_u64(ptr, old, new);
> -	}
> -	__cmpxchg_called_with_bad_pointer();
> -	return old;
> -}
> -
> -static inline unsigned long __cmpxchg_local(volatile void * ptr,
> -	unsigned long old, unsigned long new, int size)
> -{
> -	switch (size) {
> -	case 4:
> -		return __cmpxchg_u32_local(ptr, old, new);
> -	case 8:
> -		return __cmpxchg_u64_local(ptr, old, new);
> -	}
> -	__cmpxchg_called_with_bad_pointer();
> -	return old;
> -}
> -
> -#define cmpxchg(ptr,old,new) \
> -	((__typeof__(*(ptr)))__cmpxchg((ptr), \
> -		(unsigned long)(old), (unsigned long)(new),sizeof(*(ptr))))
> -
> -#define cmpxchg_local(ptr,old,new) \
> -	((__typeof__(*(ptr)))__cmpxchg_local((ptr), \
> -		(unsigned long)(old), (unsigned long)(new),sizeof(*(ptr))))
> -
>  extern void set_handler (unsigned long offset, void *addr, unsigned long len);
>  extern void set_uncached_handler (unsigned long offset, void *addr, unsigned long len);
>  
>
>
>
>   

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

* Re: cmpxchg broken in some situation
  2007-10-02 22:48           ` Fuxin Zhang
@ 2007-10-02 22:52             ` Ralf Baechle
  2007-10-02 23:07               ` Fuxin Zhang
  0 siblings, 1 reply; 14+ messages in thread
From: Ralf Baechle @ 2007-10-02 22:52 UTC (permalink / raw)
  To: Fuxin Zhang; +Cc: linux-mips

On Wed, Oct 03, 2007 at 06:48:28AM +0800, Fuxin Zhang wrote:

> And the comment "# cmpxchg_u32" is out of date too:)

I've seen worse bugs ;-)

  Ralf

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

* Re: cmpxchg broken in some situation
  2007-10-02 22:52             ` Ralf Baechle
@ 2007-10-02 23:07               ` Fuxin Zhang
  0 siblings, 0 replies; 14+ messages in thread
From: Fuxin Zhang @ 2007-10-02 23:07 UTC (permalink / raw)
  To: Ralf Baechle; +Cc: linux-mips

en? It seems that it is working for my case now.

Ralf Baechle 写道:
> On Wed, Oct 03, 2007 at 06:48:28AM +0800, Fuxin Zhang wrote:
>
>   
>> And the comment "# cmpxchg_u32" is out of date too:)
>>     
>
> I've seen worse bugs ;-)
>
>   Ralf
>
>
>
>
>   

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

* Re: cmpxchg broken in some situation
  2007-10-02 14:22           ` Thiemo Seufer
@ 2007-10-02 23:15             ` Ralf Baechle
  0 siblings, 0 replies; 14+ messages in thread
From: Ralf Baechle @ 2007-10-02 23:15 UTC (permalink / raw)
  To: Thiemo Seufer; +Cc: Fuxin Zhang, linux-mips

On Tue, Oct 02, 2007 at 03:22:10PM +0100, Thiemo Seufer wrote:

> > +#define __cmpxchg(ptr,old,new,barrier)					\
> > +({									\
> > +	__typeof__(ptr) __ptr = (ptr);					\
> > +	__typeof__(*(ptr)) __old = (old);				\
> > +	__typeof__(*(ptr)) __new = (new);				\
> > +	__typeof__(*(ptr)) __res = 0;					\
> 
> Maybe we need an acquire barrier here for some systems.

Release you meant.  The acquire lock would be at the end.  Documentation
and actual implmeentations of cmpxchg seem to differ.  It's a relativly
rarely used primitve so I think I err on the side of paranoia for now
and throw in the additional SYNC you suggest.

  Ralf

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

end of thread, other threads:[~2007-10-02 23:15 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2007-09-30 10:34 cmpxchg broken in some situation Fuxin Zhang
2007-10-01  2:53 ` Ralf Baechle
2007-10-01  3:56   ` David Daney
2007-10-01  3:59     ` David Daney
2007-10-01 10:24       ` Ralf Baechle
2007-10-01 15:11   ` Fuxin Zhang
2007-10-01 15:26     ` Ralf Baechle
2007-10-02  9:34       ` Fuxin Zhang
2007-10-02 10:35         ` Ralf Baechle
2007-10-02 14:22           ` Thiemo Seufer
2007-10-02 23:15             ` Ralf Baechle
2007-10-02 22:48           ` Fuxin Zhang
2007-10-02 22:52             ` Ralf Baechle
2007-10-02 23:07               ` Fuxin Zhang

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.