2

Here is the Linux implementation of a spinlock from arch/arm/include/asm/spinlock.h:

static inline void arch_spin_lock(arch_spinlock_t *lock)
{
    unsigned long tmp;
    u32 newval;
    arch_spinlock_t lockval;

    prefetchw(&lock->slock);
    __asm__ __volatile__(
"1: ldrex   %0, [%3]\n"
"   add %1, %0, %4\n"
"   strex   %2, %1, [%3]\n"
"   teq %2, #0\n"
"   bne 1b"
    : "=&r" (lockval), "=&r" (newval), "=&r" (tmp)
    : "r" (&lock->slock), "I" (1 << TICKET_SHIFT)
    : "cc");

    while (lockval.tickets.next != lockval.tickets.owner) {
        wfe();
        lockval.tickets.owner = READ_ONCE(lock->tickets.owner);
    }

    smp_mb();
}

...

static inline void arch_spin_unlock(arch_spinlock_t *lock)
{
    smp_mb();
    lock->tickets.owner++;
    dsb_sev();
}

My concern is that the following two lines in arch_spin_lock:

    while (lockval.tickets.next != lockval.tickets.owner) {
        wfe();

are not atomic. So what if arch_spin_unlock was called in between these two lines? This means in the function arch_spin_lock the WFE instruction would be run but the SEV has already been run and won't be run again. So at the very worst arch_spin_lock would wait forever, or until some unrelated event occurs.

Is this correct, or am I misunderstanding something? If it is a problem even only in theory, is there a way to avoid the problem?

awelkie
  • 2,422
  • 1
  • 22
  • 32
  • https://github.com/torvalds/linux/commit/20e260b6f4f717c100620122f626a2c06a4cfd72 – KamilCuk Jul 22 '19 at 09:38
  • The actual lock happened before these lines. And what you pointed out is the wait for unlock event. This is exactly what code expects (other task will call unlock). This is my understanding how this works. – 0andriy Jul 22 '19 at 22:43

1 Answers1

2

I think you are missing this bit of WFE documentation:

If the Event Register is set, WFE clears it and returns immediately.

In the "race" you describe WFE will get executed, but will return immediately, then while loop will exit.

domen
  • 1,819
  • 12
  • 19