rust-kernel
Rustbelt: a hobby x86_64 kernel written in Rust
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 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 | //! Futexes ("fast userspace mutexes"): the one syscall threads need to
//! sleep and wake each other.
//!
//! A lock in a C library is just an integer in memory. Taking a free lock
//! is one atomic instruction in user space -- no syscall at all. Only when
//! a thread has to *wait* does it ask the kernel:
//!
//! futex(&word, FUTEX_WAIT, expected): "put me to sleep, but only if
//! word still == expected"
//! futex(&word, FUTEX_WAKE, n): "wake up to n sleepers on word"
//!
//! The "only if still equal" check is the whole trick: the kernel does it
//! while holding its own lock, the same lock FUTEX_WAKE takes. So if the
//! lock holder releases the lock (changes the word) and calls WAKE between
//! our user-space check and our WAIT, either WAIT sees the changed word
//! and returns EAGAIN at once, or we're already queued and WAKE finds us.
//! No wake-up can get lost.
//!
//! The kernel keeps no state per futex word: just a list of sleepers,
//! each labelled with the word it sleeps on (its *key*). For memory private
//! to one process the key is (address space, virtual address); for
//! MAP_SHARED memory, which different processes may map at different
//! addresses, it's the physical address.
//!
//! pthread_join uses it too: the kernel writes 0 to the exiting thread's
//! tid word and does a FUTEX_WAKE on it (CLONE_CHILD_CLEARTID).
use crateErrno;
use cratememory;
use crateProcess;
use crate;
use Vec;
use ;
use Mutex;
use without_interrupts;
pub const FUTEX_WAIT: u64 = 0;
pub const FUTEX_WAKE: u64 = 1;
pub const FUTEX_REQUEUE: u64 = 3;
pub const FUTEX_CMP_REQUEUE: u64 = 4;
pub const FUTEX_WAIT_BITSET: u64 = 9;
pub const FUTEX_WAKE_BITSET: u64 = 10;
pub const FUTEX_PRIVATE_FLAG: u64 = 128;
pub const FUTEX_CLOCK_REALTIME: u64 = 256;
pub const FUTEX_BITSET_MATCH_ANY: u32 = u32MAX;
/// Which word a sleeper waits on.
/// All sleepers. Locked with interrupts off (read in wait conditions).
static WAITERS: = new;
/// Only used for its sleeping logic: wakers wake threads one by one.
static QUEUE: WaitQueue = new;
static NEXT_ID: AtomicU64 = new;
/// Look up the key of `uaddr` in `p` and read the word there (through the
/// physical mapping, with the address space locked so it can't change
/// under us). The page must have been faulted in.
/// Check a futex address and fault its page in.
/// FUTEX_WAIT: sleep while *uaddr == val, until woken, `deadline` (PIT
/// tick) or a signal.
/// Wake up to `n` sleepers on `key` (matching `bitset`); move up to
/// `requeue` more to `to` instead. Returns how many were woken (+ moved).
/// FUTEX_WAKE from the kernel (thread exit, CLONE_CHILD_CLEARTID).
/// How many sleepers there are (tests: nothing left behind).
/// The futex syscall (202): futex(uaddr, op, val, timeout/val2, uaddr2, val3).
|