blob: bca4684e7e69fa5246ad4d2b312d33539412e544 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
|
/* SPDX-License-Identifier: GPL-2.0-or-later */
/* Copyright (C) 2026 Richard Qin */
/*
* Spinlock Implementation
*
* TTAS (Test-and-Test-and-Set) spinlock with:
* - Acquire: __ATOMIC_ACQUIRE memory ordering
* - Release: __ATOMIC_RELEASE memory ordering
* - Automatic interrupt disable/enable via push_intr/pop_intr
* - Dead-lock and invalid-release detection via kernel_panic
*
* Reference: docs/specs/spinlock.md
*/
#include "spinlock.h"
#include "cpu_state.h"
#include <arch/cpu.h>
#include <lume/klog.h>
// ============================================================
// Per-CPU sync state (BSS zero-initialized)
// ============================================================
namespace sync {
CpuSyncState g_cpu_sync_states[kMaxCpus];
void push_intr() {
bool was_on = arch::cpu::intr_enabled();
arch::cpu::intr_off();
auto& state = g_cpu_sync_states[arch::cpu::id()];
if (state.lock_depth == 0) {
state.intr_was_on = was_on;
}
state.lock_depth++;
}
void pop_intr() {
auto& state = g_cpu_sync_states[arch::cpu::id()];
state.lock_depth--;
if (state.lock_depth == 0 && state.intr_was_on) {
arch::cpu::intr_on();
}
}
} // namespace sync
// ============================================================
// Spinlock methods
// ============================================================
void Spinlock::init(const char* name) {
name_ = name;
locked_.store(0, __ATOMIC_RELAXED);
cpu_id_ = sync::kInvalidCpuId;
}
void Spinlock::acquire() {
#ifdef DEBUG
// Optional: detect interrupt-state leakage
if (arch::cpu::intr_enabled()) {
auto& state = sync::g_cpu_sync_states[arch::cpu::id()];
if (state.lock_depth > 0) {
kernel_panic("spinlock: interrupts enabled while holding lock");
}
}
#endif
// Step 1: disable interrupts, track nesting depth
sync::push_intr();
// Step 2: dead-lock detection (same CPU re-acquiring)
if (is_held_by_current_cpu()) {
kernel_panic("spinlock: double lock", name_);
}
// Step 3: TTAS spin loop
while (true) {
// Outer: spin on local cache copy (no bus traffic)
while (locked_.load(__ATOMIC_RELAXED))
;
// Inner: attempt atomic exchange with acquire semantics
if (locked_.exchange(1, __ATOMIC_ACQUIRE) == 0)
break;
}
// Step 4: record owner
cpu_id_ = arch::cpu::id();
}
void Spinlock::release() {
// Step 1: verify caller is the holder
if (!is_held_by_current_cpu()) {
kernel_panic("spinlock: invalid release", name_);
}
// Step 2: clear owner
cpu_id_ = sync::kInvalidCpuId;
// Step 3: release the lock with release semantics
locked_.store(0, __ATOMIC_RELEASE);
// Step 4: restore interrupt state if this was the last lock
sync::pop_intr();
}
bool Spinlock::is_held_by_current_cpu() const {
return cpu_id_ == arch::cpu::id();
}
|