kernel/abi/linux/riscv64/
futex.rs1use crate::abi::linux::riscv64::LinuxRiscv64Abi;
2use crate::arch::Trapframe;
3use crate::sync::waker::Waker;
4use alloc::collections::BTreeMap;
5use spin::{Mutex, Once};
6
7const FUTEX_WAIT: u32 = 0;
9const FUTEX_WAKE: u32 = 1;
10const FUTEX_WAIT_BITSET: u32 = 9;
12const FUTEX_WAKE_BITSET: u32 = 10;
13const FUTEX_CMD_MASK: u32 = 0x3f; static FUTEX_WAKERS: Once<Mutex<BTreeMap<usize, Waker>>> = Once::new();
17
18fn init_futex_wakers() -> Mutex<BTreeMap<usize, Waker>> {
19 Mutex::new(BTreeMap::new())
20}
21
22fn get_futex_waker(uaddr: usize) -> &'static Waker {
23 let futex_map_mutex = FUTEX_WAKERS.call_once(init_futex_wakers);
24 let mut map = futex_map_mutex.lock();
25 if !map.contains_key(&uaddr) {
26 let name = alloc::format!("futex_{:#x}", uaddr);
28 let static_name = alloc::boxed::Box::leak(name.into_boxed_str());
29 map.insert(uaddr, Waker::new_interruptible(static_name));
30 }
31 unsafe {
32 let ptr = map.get(&uaddr).unwrap() as *const Waker;
33 &*ptr
34 }
35}
36
37pub fn wake_address(uaddr: usize, max: usize) -> usize {
39 let waker = get_futex_waker(uaddr);
40 if max == 0 {
41 return 0;
42 }
43 let mut woken = 0;
44 if max == usize::MAX {
45 woken = waker.wake_all();
47 } else {
48 for _ in 0..max {
49 if waker.wake_one() {
50 woken += 1;
51 } else {
52 break;
53 }
54 }
55 }
56 woken
57}
58
59pub fn sys_futex(_abi: &mut LinuxRiscv64Abi, trapframe: &mut Trapframe) -> usize {
61 let task = match crate::task::mytask() {
62 Some(t) => t,
63 None => return super::errno::to_result(super::errno::EPERM),
64 };
65
66 let uaddr = trapframe.get_arg(0) as usize;
68 let op_raw = trapframe.get_arg(1) as u32;
69 let val = trapframe.get_arg(2) as i32;
70 let _timeout = trapframe.get_arg(3) as usize; let _uaddr2 = trapframe.get_arg(4) as usize;
72 let _val3 = trapframe.get_arg(5) as u32; trapframe.increment_pc_next(task);
76
77 let cmd = op_raw & FUTEX_CMD_MASK; match cmd {
79 FUTEX_WAIT | FUTEX_WAIT_BITSET => {
80 let paddr = match task.vm_manager.translate_vaddr(uaddr) {
82 Some(pa) => pa,
83 None => return super::errno::to_result(super::errno::EFAULT),
84 };
85 let cur_val = unsafe { *(paddr as *const i32) };
86 if cur_val != val {
87 return super::errno::to_result(super::errno::EAGAIN);
89 }
90
91 let waker = get_futex_waker(uaddr);
93 let tid = task.get_id();
94 waker.wait(tid, trapframe);
95 0
97 }
98 FUTEX_WAKE | FUTEX_WAKE_BITSET => {
99 let max = if val < 0 { 0 } else { val as usize };
100 let woken = wake_address(uaddr, max);
101 woken
103 }
104 _ => {
105 super::errno::to_result(super::errno::ENOSYS)
107 }
108 }
109}