/* $Id: dec_and_lock.S,v 1.5 2001/11/18 00:12:56 davem Exp $ * dec_and_lock.S: Sparc64 version of "atomic_dec_and_lock()" * using cas and ldstub instructions. * * Copyright (C) 2000 David S. Miller (davem@redhat.com) */ #include #include .text .align 64 /* CAS basically works like this: * * void CAS(MEM, REG1, REG2) * { * START_ATOMIC(); * if (*(MEM) == REG1) { * TMP = *(MEM); * *(MEM) = REG2; * REG2 = TMP; * } else * REG2 = *(MEM); * END_ATOMIC(); * } */ .globl _atomic_dec_and_lock _atomic_dec_and_lock: /* %o0 = counter, %o1 = lock */ loop1: lduw [%o0], %g2 subcc %g2, 1, %g7 be,pn %icc, start_to_zero nop nzero: cas [%o0], %g2, %g7 cmp %g2, %g7 bne,pn %icc, loop1 mov 0, %g1 out: membar #StoreLoad | #StoreStore retl mov %g1, %o0 start_to_zero: #ifdef CONFIG_PREEMPT ldsw [%g6 + TI_PRE_COUNT], %g3 add %g3, 1, %g3 stw %g3, [%g6 + TI_PRE_COUNT] #endif to_zero: ldstub [%o1], %g3 membar #StoreLoad | #StoreStore brnz,pn %g3, spin_on_lock nop loop2: cas [%o0], %g2, %g7 /* ASSERT(g7 == 0) */ cmp %g2, %g7 be,pt %icc, out mov 1, %g1 lduw [%o0], %g2 subcc %g2, 1, %g7 be,pn %icc, loop2 nop membar #StoreStore | #LoadStore stb %g0, [%o1] #ifdef CONFIG_PREEMPT ldsw [%g6 + TI_PRE_COUNT], %g3 sub %g3, 1, %g3 stw %g3, [%g6 + TI_PRE_COUNT] #endif b,pt %xcc, nzero nop spin_on_lock: ldub [%o1], %g3 membar #LoadLoad brnz,pt %g3, spin_on_lock nop ba,pt %xcc, to_zero nop nop