[POWERPC] atomic_dec_if_positive sign extension fix
authorRobert Jennings <rcj@linux.vnet.ibm.com>
Wed, 17 Jan 2007 16:50:20 +0000 (10:50 -0600)
committerPaul Mackerras <paulus@samba.org>
Mon, 22 Jan 2007 10:27:36 +0000 (21:27 +1100)
On 64-bit machines, if an atomic counter is explicitly set to a
negative value, the atomic_dec_if_positive function will decrement and
store the next smallest value in the atomic counter, contrary to its
intended operation.

The comparison to determine if the decrement will make the result
negative was done by the "addic." instruction, which operates on a
64-bit value, namely the zero-extended word loaded from the atomic
variable.  This patch uses an explicit word compare (cmpwi) and
changes the addic. to an addi (also changing "=&r" to "=&b" so that r0
isn't used, and addi doesn't become li).

This also fixes a bug for both 32-bit and 64-bit in that previously
0x80000000 was considered positive, since the result after
decrementing is positive.  Now it is considered negative.

Also, I clarify the return value in the comments just to make it clear
that the value returned is always the decremented value, even if that
value is not stored back to the atomic counter.

Signed-off-by: Robert Jennings <rcj@linux.vnet.ibm.com>
Signed-off-by: Paul Mackerras <paulus@samba.org>
include/asm-powerpc/atomic.h

index 53283e2540b381ff086508d69052dd1b188bc6d1..f038e33e6d48677d0da1d5150dfc0b5f1f8a6931 100644 (file)
@@ -207,7 +207,8 @@ static __inline__ int atomic_add_unless(atomic_t *v, int a, int u)
 
 /*
  * Atomically test *v and decrement if it is greater than 0.
- * The function returns the old value of *v minus 1.
+ * The function returns the old value of *v minus 1, even if
+ * the atomic variable, v, was not decremented.
  */
 static __inline__ int atomic_dec_if_positive(atomic_t *v)
 {
@@ -216,14 +217,15 @@ static __inline__ int atomic_dec_if_positive(atomic_t *v)
        __asm__ __volatile__(
        LWSYNC_ON_SMP
 "1:    lwarx   %0,0,%1         # atomic_dec_if_positive\n\
-       addic.  %0,%0,-1\n\
+       cmpwi   %0,1\n\
+       addi    %0,%0,-1\n\
        blt-    2f\n"
        PPC405_ERR77(0,%1)
 "      stwcx.  %0,0,%1\n\
        bne-    1b"
        ISYNC_ON_SMP
        "\n\
-2:"    : "=&r" (t)
+2:"    : "=&b" (t)
        : "r" (&v->counter)
        : "cc", "memory");