Skip to content

Commit c7dfb77

Browse files
futex: initial implementation
Signed-off-by: Andy-Python-Programmer <[email protected]>
1 parent 4b48e17 commit c7dfb77

File tree

7 files changed

+208
-15
lines changed

7 files changed

+208
-15
lines changed

patches/mlibc/mlibc.patch

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
From 7483398f8048d7b25eae2015a6d67fc1feb061e6 Mon Sep 17 00:00:00 2001
1+
From 60f70d42c981a6c2645201d2d0997d48f91c53a0 Mon Sep 17 00:00:00 2001
22
From: Andy-Python-Programmer <[email protected]>
33
Date: Thu, 10 Feb 2022 19:12:25 +1100
44
Subject: [PATCH] yes
@@ -7,12 +7,12 @@ Signed-off-by: Andy-Python-Programmer <[email protected]>
77
---
88
.gitignore | 3 +
99
options/rtdl/generic/linker.cpp | 2 +-
10-
sysdeps/aero/generic/aero.cpp | 26 ++++++--
10+
sysdeps/aero/generic/aero.cpp | 40 +++++++++---
1111
sysdeps/aero/generic/filesystem.cpp | 97 ++++++++++++++++++++++++++---
1212
sysdeps/aero/generic/signals.cpp | 8 ++-
1313
sysdeps/aero/generic/sockets.cpp | 59 ++++++++++++++++++
14-
sysdeps/aero/include/aero/syscall.h | 12 ++++
15-
7 files changed, 191 insertions(+), 16 deletions(-)
14+
sysdeps/aero/include/aero/syscall.h | 14 +++++
15+
7 files changed, 205 insertions(+), 18 deletions(-)
1616

1717
diff --git a/.gitignore b/.gitignore
1818
index dbb35e8b..20c8d4c3 100644
@@ -39,10 +39,35 @@ index 53264175..38ba8c9f 100644
3939

4040
#if defined(__x86_64__)
4141
diff --git a/sysdeps/aero/generic/aero.cpp b/sysdeps/aero/generic/aero.cpp
42-
index 7de909f5..c8d076a5 100644
42+
index 7de909f5..8605e426 100644
4343
--- a/sysdeps/aero/generic/aero.cpp
4444
+++ b/sysdeps/aero/generic/aero.cpp
45-
@@ -131,12 +131,20 @@ void sys_exit(int status) {
45+
@@ -69,12 +69,22 @@ int sys_futex_tid() {
46+
}
47+
48+
int sys_futex_wait(int *pointer, int expected, const struct timespec *time) {
49+
- mlibc::infoLogger() << "sys_futex_wait() is not implemented" << frg::endlog;
50+
+ auto result = syscall(SYS_FUTEX_WAIT, pointer, expected, time);
51+
+
52+
+ if (result < 0) {
53+
+ return -result;
54+
+ }
55+
+
56+
return 0;
57+
}
58+
59+
int sys_futex_wake(int *pointer) {
60+
- mlibc::infoLogger() << "sys_futex_wake() is not implemented" << frg::endlog;
61+
+ auto result = syscall(SYS_FUTEX_WAKE, pointer);
62+
+
63+
+ if (result < 0) {
64+
+ return -result;
65+
+ }
66+
+
67+
return 0;
68+
}
69+
70+
@@ -131,12 +141,20 @@ void sys_exit(int status) {
4671
pid_t sys_getpid() {
4772
auto result = syscall(SYS_GETPID);
4873

@@ -66,7 +91,7 @@ index 7de909f5..c8d076a5 100644
6691
pid_t sys_getpgid(pid_t pid, pid_t *pgid) {
6792
mlibc::infoLogger() << "sys_getpgid() is unimplemented" << frg::endlog;
6893
*pgid = 0;
69-
@@ -173,7 +181,15 @@ int sys_getcwd(char *buffer, size_t size) {
94+
@@ -173,7 +191,15 @@ int sys_getcwd(char *buffer, size_t size) {
7095
return 0;
7196
}
7297

@@ -83,7 +108,7 @@ index 7de909f5..c8d076a5 100644
83108

84109
int sys_gethostname(char *buffer, size_t bufsize) {
85110
auto result = syscall(SYS_GETHOSTNAME, buffer, bufsize);
86-
@@ -286,7 +302,7 @@ int sys_execve(const char *path, char *const argv[], char *const envp[]) {
111+
@@ -286,7 +312,7 @@ int sys_execve(const char *path, char *const argv[], char *const envp[]) {
87112
__builtin_unreachable();
88113
}
89114

@@ -316,10 +341,10 @@ index e69de29b..b6b18fe7 100644
316341
+}
317342
+} // namespace mlibc
318343
diff --git a/sysdeps/aero/include/aero/syscall.h b/sysdeps/aero/include/aero/syscall.h
319-
index 07b1b51b..b6763b63 100644
344+
index 07b1b51b..4576d268 100644
320345
--- a/sysdeps/aero/include/aero/syscall.h
321346
+++ b/sysdeps/aero/include/aero/syscall.h
322-
@@ -49,6 +49,18 @@
347+
@@ -49,6 +49,20 @@
323348
#define SYS_DUP 42
324349
#define SYS_FCNTL 43
325350
#define SYS_DUP2 44
@@ -335,6 +360,8 @@ index 07b1b51b..b6763b63 100644
335360
+#define SYS_EPOLL_CTL 54
336361
+#define SYS_EVENT_FD 55
337362
+#define SYS_KILL 56
363+
+#define SYS_FUTEX_WAIT 59
364+
+#define SYS_FUTEX_WAKE 60
338365

339366
// Invalid syscall used to trigger a log error in the kernel (as a hint)
340367
// so, that we can implement the syscall in the kernel.

src/aero_kernel/src/mem/paging/addr.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ pub struct VirtAddr(u64);
4848
///
4949
/// On `x86_64`, only the 52 lower bits of a physical address can be used. The top 12 bits need
5050
/// to be zero. This type guarantees that it always represents a valid physical address.
51-
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
51+
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
5252
#[repr(transparent)]
5353
pub struct PhysAddr(u64);
5454

src/aero_kernel/src/syscall/futex.rs

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
/*
2+
* Copyright (C) 2021-2022 The Aero Project Developers.
3+
*
4+
* This file is part of The Aero Project.
5+
*
6+
* Aero is free software: you can redistribute it and/or modify
7+
* it under the terms of the GNU General Public License as published by
8+
* the Free Software Foundation, either version 3 of the License, or
9+
* (at your option) any later version.
10+
*
11+
* Aero is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU General Public License
17+
* along with Aero. If not, see <https://www.gnu.org/licenses/>.
18+
*/
19+
20+
use core::sync::atomic::{AtomicU32, Ordering};
21+
22+
use aero_syscall::{AeroSyscallError, TimeSpec};
23+
use alloc::sync::Arc;
24+
use spin::Once;
25+
26+
use crate::mem::paging::{PhysAddr, Translate, VirtAddr};
27+
use crate::mem::AddressSpace;
28+
use crate::userland::scheduler;
29+
use crate::utils::sync::{BlockQueue, Mutex};
30+
31+
pub struct FutexContainer {
32+
futexes: Mutex<hashbrown::HashMap<PhysAddr, Arc<BlockQueue>>>,
33+
}
34+
35+
impl FutexContainer {
36+
fn new() -> Self {
37+
Self {
38+
futexes: Mutex::new(hashbrown::HashMap::new()),
39+
}
40+
}
41+
42+
/// Ensures the user-provided futex pointer is non-null and aligned to the alignment of
43+
/// a futex word (32-bits).
44+
fn validate_futex_ptr(ptr: VirtAddr) -> Result<(), AeroSyscallError> {
45+
let raw = ptr.as_u64() as usize;
46+
47+
if raw == 0 || (raw & (core::mem::size_of::<u32>() - 1)) == 0 {
48+
Err(AeroSyscallError::EINVAL)
49+
} else {
50+
Ok(())
51+
}
52+
}
53+
54+
/// Converts the user-provided futex word pointer to a unique futex key.
55+
fn addr_as_futex_key(ptr: VirtAddr) -> Option<PhysAddr> {
56+
let mut address_space = AddressSpace::this();
57+
let offset_table = address_space.offset_page_table();
58+
59+
offset_table.translate_addr(ptr)
60+
}
61+
62+
/// Returns the futex at the given key; allocating it if it doesn't exist.
63+
fn get_alloc(&self, key: PhysAddr) -> Arc<BlockQueue> {
64+
let mut container = self.futexes.lock();
65+
66+
if let Some(futex) = container.get(&key) {
67+
futex.clone()
68+
} else {
69+
let futex = Arc::new(BlockQueue::new());
70+
container.insert(key, futex.clone());
71+
futex
72+
}
73+
}
74+
75+
/// Returns the futex at the given key, or None if it doesn't exist.
76+
fn get(&self, key: PhysAddr) -> Option<Arc<BlockQueue>> {
77+
self.futexes.lock_irq().get(&key).map(|e| e.clone())
78+
}
79+
80+
/// Tests the that the value at the futex word pointed to by `uaddr` still contains the
81+
/// `expected` value, and if so, it sleeps waiting for a futex wake operation on the
82+
/// futex word.
83+
fn wait(
84+
&self,
85+
uaddr: VirtAddr,
86+
expected: u32,
87+
_timeout: &TimeSpec,
88+
) -> Result<(), AeroSyscallError> {
89+
Self::validate_futex_ptr(uaddr)?;
90+
91+
let key = Self::addr_as_futex_key(uaddr).ok_or(AeroSyscallError::EINVAL)?;
92+
let value = uaddr
93+
.read_mut::<AtomicU32>()
94+
.ok_or(AeroSyscallError::EINVAL)?;
95+
96+
if value.load(Ordering::SeqCst) == expected {
97+
let futex = self.get_alloc(key);
98+
99+
let scheduler = scheduler::get_scheduler();
100+
let current_task = scheduler.current_task();
101+
102+
futex.insert(current_task.clone());
103+
scheduler.inner.await_io()?;
104+
futex.remove(current_task);
105+
106+
if futex.is_empty() {
107+
self.futexes.lock().remove(&key);
108+
}
109+
110+
Ok(())
111+
} else {
112+
Err(AeroSyscallError::EAGAIN)
113+
}
114+
}
115+
116+
fn wake(&self, uaddr: VirtAddr) -> Result<(), AeroSyscallError> {
117+
Self::validate_futex_ptr(uaddr)?;
118+
119+
let key = Self::addr_as_futex_key(uaddr).ok_or(AeroSyscallError::EINVAL)?;
120+
let futex = self.get(key).ok_or(AeroSyscallError::EINVAL)?;
121+
122+
futex.notify_complete();
123+
124+
// todo: early reschedule if the futex is not empty.
125+
Ok(())
126+
}
127+
}
128+
129+
static FUTEX_CONTAINER: Once<FutexContainer> = Once::new();
130+
131+
/// Returns a reference to the futex conatiner; initializing if necessary.
132+
fn get_futex_conatiner() -> &'static FutexContainer {
133+
FUTEX_CONTAINER.call_once(|| FutexContainer::new())
134+
}
135+
136+
#[syscall]
137+
pub fn wait(ptr: usize, expected: usize, timeout: &TimeSpec) -> Result<usize, AeroSyscallError> {
138+
let ptr = VirtAddr::new(ptr as u64);
139+
140+
let futex_container = get_futex_conatiner();
141+
futex_container.wait(ptr, expected as u32, timeout)?;
142+
143+
Ok(0)
144+
}
145+
146+
#[syscall]
147+
pub fn wake(ptr: usize) -> Result<usize, AeroSyscallError> {
148+
let ptr = VirtAddr::new(ptr as u64);
149+
150+
let futex_container = get_futex_conatiner();
151+
futex_container.wake(ptr)?;
152+
153+
Ok(0)
154+
}

src/aero_kernel/src/syscall/mod.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -78,11 +78,12 @@ use core::mem::MaybeUninit;
7878

7979
use aero_syscall::prelude::*;
8080

81-
pub mod fs;
82-
pub mod ipc;
81+
mod fs;
82+
mod futex;
83+
mod ipc;
8384
mod net;
84-
pub mod process;
85-
pub mod time;
85+
mod process;
86+
mod time;
8687

8788
use alloc::boxed::Box;
8889
use alloc::vec::Vec;
@@ -223,6 +224,9 @@ pub fn generic_do_syscall(
223224
SYS_IPC_DISCOVER_ROOT => ipc::discover_root(),
224225
SYS_IPC_BECOME_ROOT => ipc::become_root(),
225226

227+
SYS_FUTEX_WAIT => futex::wait(b, c, d),
228+
SYS_FUTEX_WAKE => futex::wake(b),
229+
226230
// Syscall aliases (this should be handled in aero_syscall)
227231
SYS_MKDIR => fs::mkdirat(aero_syscall::AT_FDCWD as _, b, c),
228232

src/aero_kernel/src/utils/sync.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,10 @@ impl BlockQueue {
8282
scheduler.inner.wake_up(task.clone());
8383
}
8484
}
85+
86+
pub fn is_empty(&self) -> bool {
87+
self.queue.lock_irq().is_empty()
88+
}
8589
}
8690

8791
/// Helper guard structure used to lock interrupts. When dropped, interrupts

src/aero_syscall/src/consts.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,8 @@ pub const SYS_EPOLL_PWAIT: usize = 53;
7777
pub const SYS_EPOLL_CTL: usize = 54;
7878
pub const SYS_EVENT_FD: usize = 55;
7979
pub const SYS_KILL: usize = 56;
80+
pub const SYS_FUTEX_WAIT: usize = 59;
81+
pub const SYS_FUTEX_WAKE: usize = 60;
8082

8183
// constants for fcntl()'s command argument:
8284
pub const F_DUPFD: usize = 1;

src/aero_syscall/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,8 @@ pub fn syscall_as_str(syscall: usize) -> &'static str {
226226
prelude::SYS_EPOLL_CTL => "epoll_ctl",
227227
prelude::SYS_EVENT_FD => "event_fd",
228228
prelude::SYS_KILL => "kill",
229+
prelude::SYS_FUTEX_WAIT => "futex_wait",
230+
prelude::SYS_FUTEX_WAKE => "futex_wake",
229231

230232
_ => unreachable!("unknown syscall {syscall}"),
231233
}

0 commit comments

Comments
 (0)