/* * This file implements spinlocks. It makes heavy use of GCC's atomic built-ins * for syncronisation. The spinlocks have some simple mechanisms for preventing * deadlocks. Each spinlock knowns which CPU/task is holding it, and can allow * that CPU/task to acquire it multiple times and safely release it. */ #include #include #include /* Check if already holding */ static int holding(Spinlock *lock) { if (current) return (lock->locked && lock->owner == current); return (lock->locked && lock->cpu == (cpu->id + 1)); } /* Initialise a lock */ void init_lock(Spinlock *lock) { lock->locked = 0; lock->usage = 0; } /* Acquire a lock */ void acquire(Spinlock *lock) { /* * Reference count the lock so it can be safely acquired by the same * holder multiple times. This stops a lock from deadlocking itself. */ __atomic_add_fetch(&lock->usage, 1, __ATOMIC_RELAXED); if (holding(lock)) return; while (__atomic_test_and_set(&lock->locked, __ATOMIC_ACQUIRE)) asm volatile("pause"); if (current) lock->owner = current; else lock->cpu = cpu->id + 1; } /* Release a lock */ void release(Spinlock *lock) { ASSERT(holding(lock)); if (__atomic_sub_fetch(&lock->usage, 1, __ATOMIC_RELAXED)) return; __atomic_clear(&lock->locked, __ATOMIC_RELEASE); lock->owner = NULL; lock->cpu = 0; }