Orion
Barry Importing existing Orion kernel d41a53c (2 years, 4 months ago)diff --git a/spinlock.c b/spinlock.c new file mode 100644 index 0000000..059f65e --- /dev/null +++ b/spinlock.c @@ -0,0 +1,70 @@ +/* + * This file contains the implementation of spinlocks. It makes heavy use of + * GCC's atomic built-ins for synchronization. The spinlocks have some simple + * mechanisms for preventing deadlocks. Each spinlock knows which CPU/task is + * holding it, and can allow that CPU/task to acquire it multiple times and + * safely release it. + */ + +#include <stdint.h> +#include "task/task.h" +#include "proc/proc.h" +#include "spinlock.h" +#include "screen.h" +#include "io.h" + +/* Check if already holding */ +static uint8_t +holding(Spinlock *lock) +{ + uint8_t r; + r = lock->locked; + r &= (current ? lock->task == current->tid : lock->cpu == CPUID); + return r; +} + +/* Create a new lock */ +void +init_lock(Spinlock *lock) +{ + lock->locked = 0; + lock->task = 0; + lock->cpu = 0; + lock->ref = 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. + */ + if (holding(lock)) { + lock->ref++; + return; + } + while (!__sync_bool_compare_and_swap(&lock->locked, 0, 1)) + asm volatile("pause"); + __sync_synchronize(); + if (current) + lock->task = current->tid; + lock->cpu = CPUID; +} + +/* Release a lock */ +void +release(Spinlock *lock) +{ + if (!holding(lock)) + panic("Cannot release unheld lock"); + if (lock->ref) { + lock->ref--; + return; + } + lock->task = 0; + lock->cpu = 0; + __sync_lock_release(&lock->locked); + __sync_synchronize(); +}