diff options
Diffstat (limited to 'src/kernel/spinlock.cc')
| -rw-r--r-- | src/kernel/spinlock.cc | 221 |
1 files changed, 151 insertions, 70 deletions
diff --git a/src/kernel/spinlock.cc b/src/kernel/spinlock.cc index 210e0b9..426f639 100644 --- a/src/kernel/spinlock.cc +++ b/src/kernel/spinlock.cc @@ -2,106 +2,187 @@ #include "kernel/riscv.h" #include "kernel/proc.h" #include "drivers/uart.h" -#include "common/types.h" -#define NCPU 8 +extern struct cpu cpus[]; -namespace Spinlock +// Get the pointer of the current CPU +// Depends on r_tp() in riscv.h to read the tp register +struct cpu *mycpu() { - int g_noff[NCPU]; + int id = r_tp(); + return &cpus[id]; +} - int g_intena[NCPU]; +// Get the current process +// Must be called with interrupts disabled, or read atomically in a safe manner +struct Proc *myproc() +{ + push_off(); + struct cpu *c = mycpu(); + struct Proc *p = c->proc; + pop_off(); + return p; +} - int cpuid() - { - return r_tp(); - } +// Interrupt Depth Management +void push_off(void); +void pop_off(void); + +// Spinlock Implementation + +void Spinlock::init(const char *lock_name) +{ + name = lock_name; + locked = 0; + cpu = nullptr; +} - void init(struct spinlock *lk, char* name) +void Spinlock::acquire() +{ + push_off(); // Disable interrupts: prevent deadlocks (interrupt handler requests a lock that is already held) + + if (holding()) { - lk->name = name; - lk->locked = 0; - lk->cpu_id = -1; + Drivers::uart_puts("panic: recursive acquire "); + Drivers::uart_puts(name); + Drivers::uart_puts("\n"); + while (1) + ; } - void push_off() + // Atomic Exchange + // __ATOMIC_ACQUIRE: Ensures that reads and writes after the acquire are not reordered before the acquire + while (__atomic_exchange_n(&locked, 1, __ATOMIC_ACQUIRE)) { - int old = intr_get(); - intr_off(); - - int id = cpuid(); - if (g_noff[id] == 0) + // Busy-wait (Spinning) + // Optimization: Read-only, no writes, reducing cache coherence traffic + while (__atomic_load_n(&locked, __ATOMIC_RELAXED) == 1) { - g_intena[id] = old; + asm volatile("nop"); // RISC-V indicates that the CPU is in a spin state } - g_noff[id] += 1; } - void pop_off() + // Record holder + cpu = mycpu(); +} + +void Spinlock::release() +{ + if (!holding()) { - int id = cpuid(); - if (intr_get()) - Drivers::uart_puts("pop_off - interruptible\n"); + Drivers::uart_puts("panic: release not holding\n"); + while (1) + ; + } - if (g_noff[id] < 1) - Drivers::uart_puts("pop_off - count error\n"); + cpu = nullptr; - g_noff[id] -= 1; + // Atomic Store + // __ATOMIC_RELEASE: Ensures all reads and writes before the Release are completed + __atomic_store_n(&locked, 0, __ATOMIC_RELEASE); - if (g_noff[id] == 0 && g_intena[id]) - { - intr_on(); - } - } + pop_off(); // 2. 恢复中断状态 +} + +// Check if the current CPU holds the lock +bool Spinlock::holding() +{ + // Interrupt checks must be disabled, otherwise a process switch may occur causing CPU fluctuations. + int r; + push_off(); + r = (locked && cpu == mycpu()); + pop_off(); + return r; +} + + +// Mutex Implementation (Sleep Locks) + +void Mutex::init(const char *lock_name) +{ + name = lock_name; + locked = 0; + pid = 0; + lk.init("mutex_internal"); // 初始化内部自旋锁 +} + +void Mutex::acquire() +{ + lk.acquire(); // Acquire the internal lock to protect the locked field - int holding(struct spinlock *lk) + while (locked) { - int r; - push_off(); - r = (lk->locked && lk->cpu_id == cpuid()); - pop_off(); - return r; + // Lock is held, preparing to sleep + // Atomicity contract of sleep(): + // 1. Mark the current process as SLEEPING + // 2. Release lk (so others can operate on locked) + // 3. Switch CPU (context switch) + // 4. After being woken up, reacquire the lk lock and return + ProcManager::sleep(this, &lk); } - void acquire(struct spinlock *lk) - { - push_off(); + locked = 1; + pid = myproc()->pid; - if(holding(lk)) - { - Drivers::uart_puts("acquire: panic - recursive lock "); - Drivers::uart_puts(lk->name); - Drivers::uart_puts("\n"); - while (1) - ; - } + lk.release(); +} - while (__sync_lock_test_and_set(&lk->locked, 1) != 0) - { - // 自旋等待 - } +void Mutex::release() +{ + lk.acquire(); - __sync_synchronize(); + locked = 0; + pid = 0; - lk->cpu_id = cpuid(); - } + // Wake up all processes sleeping on this lock (chan = this) + ProcManager::wakeup(this); - void release(struct spinlock *lk) - { - if (!holding(lk)) - { - Drivers::uart_puts("release: panic - not holding\n"); - while (1) - ; - } + lk.release(); +} - lk->cpu_id = -1; +bool Mutex::holding() +{ + lk.acquire(); // Must use a lock when reading to prevent race conditions. + bool r = locked && (pid == myproc()->pid); + lk.release(); + return r; +} + +// Interrupt Depth Management + +void push_off(void) +{ + int old = intr_get(); + intr_off(); + + struct cpu *c = mycpu(); - __sync_synchronize(); + if (c->noff == 0) + c->intena = old; // 记录最外层的中断状态 - __sync_lock_release(&lk->locked); + c->noff += 1; +} - pop_off(); +void pop_off(void) +{ + struct cpu *c = mycpu(); + + if (intr_get()) + { + Drivers::uart_puts("panic: pop_off - interruptible\n"); + while (1) + ; } + if (c->noff < 1) + { + Drivers::uart_puts("panic: pop_off count\n"); + while (1) + ; + } + + c->noff -= 1; -} // namespace Spinlock + // An interrupt is only truly enabled when the nesting level returns to zero and the outermost layer allows interrupts. + if (c->noff == 0 && c->intena) + intr_on(); +}
\ No newline at end of file |
