summaryrefslogtreecommitdiff
path: root/src/kernel/spinlock.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/kernel/spinlock.cc')
-rw-r--r--src/kernel/spinlock.cc230
1 files changed, 159 insertions, 71 deletions
diff --git a/src/kernel/spinlock.cc b/src/kernel/spinlock.cc
index 210e0b9..55dfc6b 100644
--- a/src/kernel/spinlock.cc
+++ b/src/kernel/spinlock.cc
@@ -1,107 +1,195 @@
#include "kernel/spinlock.h"
#include "kernel/riscv.h"
-#include "kernel/proc.h"
+#include "kernel/cpu.h"
+#include "kernel/proc_def.h"
#include "drivers/uart.h"
-#include "common/types.h"
-#define NCPU 8
+extern struct cpu cpus[];
-namespace Spinlock
+namespace ProcManager
{
- int g_noff[NCPU];
+ void sleep(void *chan, Spinlock *lk);
+ void wakeup(void *chan);
+}
+
+// Interrupt Depth Management
+void push_off(void);
+void pop_off(void);
+
+// // Get the pointer of the current CPU
+// // Depends on r_tp() in riscv.h to read the tp register
+// struct cpu *mycpu()
+// {
+// int id = r_tp();
+// return &cpus[id];
+// }
+
+// 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 g_intena[NCPU];
+// Spinlock Implementation
- int cpuid()
- {
- return r_tp();
- }
+void Spinlock::init(const char *lock_name)
+{
+ name = lock_name;
+ locked = 0;
+ cpu = nullptr;
+}
+
+void Spinlock::acquire()
+{
+ push_off(); // Disable interrupts: prevent deadlocks (interrupt handler requests a lock that is already held)
- void init(struct spinlock *lk, char* name)
+ 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();
+}
+
+// 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;
+}
- __sync_synchronize();
+// Interrupt Depth Management
- __sync_lock_release(&lk->locked);
+void push_off(void)
+{
+ int old = intr_get();
+ intr_off();
+
+ struct cpu *c = mycpu();
+
+ if (c->noff == 0)
+ c->intena = old;
+
+ c->noff += 1;
+}
+
+void pop_off(void)
+{
+ struct cpu *c = mycpu();
- pop_off();
+ 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