[parisc-linux] atomic_t

Matthew Wilcox willy@debian.org
Sun, 13 Jan 2002 21:15:08 +0000


Just thinking about atomic_set taking the spinlock to assure
exclusivity...  I thought we could eliminate it.

Here's the current code:

static __inline__ void __atomic_set(atomic_t *v, int i) 
{
        unsigned long flags;
        SPIN_LOCK_IRQSAVE(ATOMIC_HASH(v), flags);

        v->counter = i;

        SPIN_UNLOCK_IRQRESTORE(ATOMIC_HASH(v), flags);
}

static __inline__ int __atomic_add_return(int i, atomic_t *v)
{
        int ret;
        unsigned long flags;
        SPIN_LOCK_IRQSAVE(ATOMIC_HASH(v), flags);

        ret = (v->counter += i);

        SPIN_UNLOCK_IRQRESTORE(ATOMIC_HASH(v), flags);
        return ret;
}

The legitimate effect of having two processors do this at the same time
are:

atomic_t v = 3

case 1:

atomic_add_return(1, &v)
atomic_set(&v, 1)
result: v = 1, a_a_r returns 4.

or case 2:

atomic_set(&v, 1)
atomic_add_return(1, &v)
result: v = 2, a_a_r returns 2.

The spinlocks guarantee this behaviour, but let's look at what happens
if we remove the spinlock from atomic_set.  Now atomic_set is just an
assignment, and can occur at any point during atomic_add_ret.  We'll have
to drop to psuedo-assembler for this.

atomic_add_ret becomes:

a) lock
b) load v into register r
c) add 1 to r
d) store r to v
e) unlock
f) return r

atomic_set (store 1 to v) can appear to occur at any point -- the CPU
ref manual guarantees it won't cause any problems.

Before step b leads to case 2.  after step d leads to case 1.  between
steps b and d, it's as if the atomic_set _never_happened_.  It results
in v=4, a_a_r returns 4.

The question is whether any code relies on this behaviour.  It probably
does in some of the messier bits of networking :-(

-- 
Revolutions do not require corporate support.