From 06560afa2aa4db352526f4be8b6262719b8b3eac Mon Sep 17 00:00:00 2001 From: hmt <114841534+1037827920@users.noreply.github.com> Date: Sat, 6 Apr 2024 22:26:34 +0800 Subject: [PATCH] Patch feat robust futex (#682) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 实现robust lock机制 * 前面更改vscode,修改回来 * 修改dadk的路径 * 提交.gitnore和.cargo,删除LICENSE,修改README * 修改一个warn * 删除.rustc_info.json * 删除target文件夹 * 恢复DragonOS的LICENSE,删除Cargo.lock * 将校验用户空间地址的代码写入函数内;将部分match分支用ok_or代替 * 修改wakeup函数获取running queue时unwrap一个None值发生panic * 测试程序使用syscalls库进行系统调用 --- kernel/src/libs/futex/futex.rs | 259 +++++++++++- kernel/src/libs/futex/syscall.rs | 35 +- kernel/src/process/mod.rs | 22 +- kernel/src/syscall/mod.rs | 28 +- .../test-for-robustfutex/.cargo/config.toml | 2 + user/apps/test-for-robustfutex/.gitignore | 3 + user/apps/test-for-robustfutex/Cargo.toml | 12 + user/apps/test-for-robustfutex/Makefile | 56 +++ user/apps/test-for-robustfutex/README.md | 5 + user/apps/test-for-robustfutex/src/main.rs | 394 ++++++++++++++++++ .../config/test_for_robustfutex_0_1_0.dadk | 24 ++ 11 files changed, 825 insertions(+), 15 deletions(-) create mode 100644 user/apps/test-for-robustfutex/.cargo/config.toml create mode 100644 user/apps/test-for-robustfutex/.gitignore create mode 100644 user/apps/test-for-robustfutex/Cargo.toml create mode 100644 user/apps/test-for-robustfutex/Makefile create mode 100644 user/apps/test-for-robustfutex/README.md create mode 100644 user/apps/test-for-robustfutex/src/main.rs create mode 100644 user/dadk/config/test_for_robustfutex_0_1_0.dadk diff --git a/kernel/src/libs/futex/futex.rs b/kernel/src/libs/futex/futex.rs index 613888c7..4e428cfd 100644 --- a/kernel/src/libs/futex/futex.rs +++ b/kernel/src/libs/futex/futex.rs @@ -3,7 +3,12 @@ use alloc::{ sync::{Arc, Weak}, }; use core::hash::{Hash, Hasher}; -use core::{intrinsics::likely, sync::atomic::AtomicU64}; +use core::{ + intrinsics::{likely, unlikely}, + mem, + sync::atomic::AtomicU64, +}; + use hashbrown::HashMap; use system_error::SystemError; @@ -12,9 +17,9 @@ use crate::{ exception::InterruptArch, libs::spinlock::{SpinLock, SpinLockGuard}, mm::{ucontext::AddressSpace, MemoryManagementArch, VirtAddr}, - process::{ProcessControlBlock, ProcessManager}, + process::{Pid, ProcessControlBlock, ProcessManager}, sched::{schedule, SchedMode}, - syscall::user_access::UserBufferReader, + syscall::user_access::{UserBufferReader, UserBufferWriter}, time::{ timer::{next_n_us_timer_jiffies, Timer, WakeUpHelper}, TimeSpec, @@ -635,3 +640,251 @@ impl Futex { Ok(*oldval) } } + +//用于指示在处理robust list是最多处理多少个条目 +const ROBUST_LIST_LIMIT: isize = 2048; + +#[derive(Debug, Copy, Clone)] +pub struct RobustList { + next: VirtAddr, +} + +#[derive(Debug, Copy, Clone)] +pub struct RobustListHead { + list: RobustList, + futex_offset: isize, + list_op_pending: VirtAddr, +} + +impl RobustListHead { + /// # 获得futex的用户空间地址 + pub fn futex_uaddr(&self, entry: VirtAddr) -> VirtAddr { + return VirtAddr::new(entry.data() + self.futex_offset as usize); + } + + /// #获得list_op_peding的用户空间地址 + pub fn pending_uaddr(&self) -> Option { + if self.list_op_pending.is_null() { + return None; + } else { + return Some(self.futex_uaddr(self.list_op_pending)); + } + } + + /// # 在内核注册robust list + /// ## 参数 + /// - head_uaddr:robust list head用户空间地址 + /// - len:robust list head的长度 + pub fn set_robust_list(head_uaddr: VirtAddr, len: usize) -> Result { + let robust_list_head_len = mem::size_of::<&RobustListHead>(); + if unlikely(len != robust_list_head_len) { + return Err(SystemError::EINVAL); + } + + let user_buffer_reader = UserBufferReader::new( + head_uaddr.as_ptr::(), + mem::size_of::(), + true, + )?; + let robust_list_head = *user_buffer_reader.read_one_from_user::(0)?; + + // 向内核注册robust list + ProcessManager::current_pcb().set_robust_list(Some(robust_list_head)); + + return Ok(0); + } + + /// # 获取robust list head到用户空间 + /// ## 参数 + /// - pid:当前进程/线程的pid + /// - + pub fn get_robust_list( + pid: usize, + head_uaddr: VirtAddr, + len_ptr_uaddr: VirtAddr, + ) -> Result { + // 获取当前进程的process control block + let pcb: Arc = if pid == 0 { + ProcessManager::current_pcb() + } else { + ProcessManager::find(Pid::new(pid)).ok_or(SystemError::ESRCH)? + }; + + // TODO: 检查当前进程是否能ptrace另一个进程 + let ptrace = true; + if !ptrace { + return Err(SystemError::EPERM); + } + + //获取当前线程的robust list head 和 长度 + let robust_list_head = (*pcb.get_robust_list()).ok_or(SystemError::EINVAL)?; + + // 将len拷贝到用户空间len_ptr + let mut user_writer = UserBufferWriter::new( + len_ptr_uaddr.as_ptr::(), + core::mem::size_of::(), + true, + )?; + user_writer.copy_one_to_user(&mem::size_of::(), 0)?; + // 将head拷贝到用户空间head + let mut user_writer = UserBufferWriter::new( + head_uaddr.as_ptr::(), + mem::size_of::(), + true, + )?; + user_writer.copy_one_to_user(&robust_list_head, 0)?; + + return Ok(0); + } + + /// # 进程/线程退出时清理工作 + /// ## 参数 + /// - current:当前进程/线程的pcb + /// - pid:当前进程/线程的pid + pub fn exit_robust_list(pcb: Arc) { + //指向当前进程的robust list头部的指针 + let head = match *pcb.get_robust_list() { + Some(rl) => rl, + None => { + return; + } + }; + // 遍历当前进程/线程的robust list + for futex_uaddr in head.futexes() { + let ret = Self::handle_futex_death(futex_uaddr, pcb.pid().into() as u32); + if ret.is_err() { + return; + } + } + pcb.set_robust_list(None); + } + + /// # 返回robust list的迭代器,将robust list list_op_pending 放到最后(如果存在) + fn futexes(&self) -> FutexIterator<'_> { + return FutexIterator::new(self); + } + + /// # 处理进程即将死亡时,进程已经持有的futex,唤醒其他等待该futex的线程 + /// ## 参数 + /// - futex_uaddr:futex的用户空间地址 + /// - pid: 当前进程/线程的pid + fn handle_futex_death(futex_uaddr: VirtAddr, pid: u32) -> Result { + let futex_val = { + if futex_uaddr.is_null() { + return Err(SystemError::EINVAL); + } + let user_buffer_reader = UserBufferReader::new( + futex_uaddr.as_ptr::(), + core::mem::size_of::(), + true, + )?; + *user_buffer_reader.read_one_from_user::(0)? + }; + + let mut uval = futex_val; + loop { + // 该futex可能被其他进程占有 + let owner = uval & FUTEX_TID_MASK; + if owner != pid { + break; + } + + // 判断是否有FUTEX_WAITERS和标记FUTEX_OWNER_DIED + let mval = (uval & FUTEX_WAITERS) | FUTEX_OWNER_DIED; + + // 读取用户空间目前的futex字段,判断在此之前futex是否有被修改 + let user_buffer_reader = UserBufferReader::new( + futex_uaddr.as_ptr::(), + core::mem::size_of::(), + true, + )?; + let nval = *user_buffer_reader.read_one_from_user::(0)?; + if nval != mval { + uval = nval; + let mut user_buffer_writer = UserBufferWriter::new( + futex_uaddr.as_ptr::(), + core::mem::size_of::(), + true, + )?; + user_buffer_writer.copy_one_to_user(&mval, 0)?; + continue; + } + + // 有等待者,进行唤醒操作 + if nval & FUTEX_WAITERS != 0 { + let mut flags = FutexFlag::FLAGS_MATCH_NONE; + flags.insert(FutexFlag::FLAGS_SHARED); + Futex::futex_wake(futex_uaddr, flags, 1, FUTEX_BITSET_MATCH_ANY)?; + } + break; + } + + return Ok(0); + } +} + +pub struct FutexIterator<'a> { + robust_list_head: &'a RobustListHead, + entry: VirtAddr, + count: isize, +} + +impl<'a> FutexIterator<'a> { + pub fn new(robust_list_head: &'a RobustListHead) -> Self { + return Self { + robust_list_head, + entry: robust_list_head.list.next, + count: 0, + }; + } + + fn is_end(&mut self) -> bool { + return self.count < 0; + } +} + +impl<'a> Iterator for FutexIterator<'a> { + type Item = VirtAddr; + + fn next(&mut self) -> Option { + if self.is_end() { + return None; + } + + while self.entry.data() != &self.robust_list_head.list as *const RobustList as usize { + if self.count == ROBUST_LIST_LIMIT { + break; + } + if self.entry.is_null() { + return None; + } + + //获取futex val地址 + let futex_uaddr = if self.entry.data() != self.robust_list_head.list_op_pending.data() { + Some(self.robust_list_head.futex_uaddr(self.entry)) + } else { + None + }; + + let user_buffer_reader = UserBufferReader::new( + self.entry.as_ptr::(), + mem::size_of::(), + true, + ) + .ok()?; + let next_entry = user_buffer_reader + .read_one_from_user::(0) + .ok()?; + + self.entry = next_entry.next; + + self.count += 1; + + if futex_uaddr.is_some() { + return futex_uaddr; + } + } + self.count -= 1; + self.robust_list_head.pending_uaddr() + } +} diff --git a/kernel/src/libs/futex/syscall.rs b/kernel/src/libs/futex/syscall.rs index ddb9f44d..8ac5739b 100644 --- a/kernel/src/libs/futex/syscall.rs +++ b/kernel/src/libs/futex/syscall.rs @@ -1,8 +1,15 @@ use system_error::SystemError; -use crate::{mm::VirtAddr, syscall::Syscall, time::TimeSpec}; +use crate::{ + mm::{verify_area, VirtAddr}, + syscall::Syscall, + time::TimeSpec, +}; -use super::{constant::*, futex::Futex}; +use super::{ + constant::*, + futex::{Futex, RobustListHead}, +}; impl Syscall { pub fn do_futex( @@ -14,6 +21,9 @@ impl Syscall { val2: u32, val3: u32, ) -> Result { + verify_area(uaddr, core::mem::size_of::())?; + verify_area(uaddr2, core::mem::size_of::())?; + let cmd = FutexArg::from_bits(operation.bits() & FutexFlag::FUTEX_CMD_MASK.bits()) .ok_or(SystemError::ENOSYS)?; @@ -101,4 +111,25 @@ impl Syscall { } } } + + pub fn set_robust_list(head_uaddr: VirtAddr, len: usize) -> Result { + //判断用户空间地址的合法性 + verify_area(head_uaddr, core::mem::size_of::())?; + + let ret = RobustListHead::set_robust_list(head_uaddr, len); + return ret; + } + + pub fn get_robust_list( + pid: usize, + head_uaddr: VirtAddr, + len_ptr_uaddr: VirtAddr, + ) -> Result { + //判断用户空间地址的合法性 + verify_area(head_uaddr, core::mem::size_of::())?; + verify_area(len_ptr_uaddr, core::mem::size_of::())?; + + let ret = RobustListHead::get_robust_list(pid, head_uaddr, len_ptr_uaddr); + return ret; + } } diff --git a/kernel/src/process/mod.rs b/kernel/src/process/mod.rs index 9d92d28b..063bb313 100644 --- a/kernel/src/process/mod.rs +++ b/kernel/src/process/mod.rs @@ -16,6 +16,7 @@ use system_error::SystemError; use crate::{ arch::{ + cpu::current_cpu_id, ipc::signal::{AtomicSignal, SigSet, Signal}, process::ArchPCBInfo, CurrentIrqArch, @@ -33,7 +34,7 @@ use crate::{ casting::DowncastArc, futex::{ constant::{FutexFlag, FUTEX_BITSET_MATCH_ANY}, - futex::Futex, + futex::{Futex, RobustListHead}, }, lock_free_flags::LockFreeFlags, rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard}, @@ -230,7 +231,8 @@ impl ProcessManager { // avoid deadlock drop(writer); - let rq = cpu_rq(pcb.sched_info().on_cpu().unwrap().data() as usize); + let rq = + cpu_rq(pcb.sched_info().on_cpu().unwrap_or(current_cpu_id()).data() as usize); let (rq, _guard) = rq.self_lock(); rq.update_rq_clock(); @@ -405,6 +407,8 @@ impl ProcessManager { unsafe { clear_user(addr, core::mem::size_of::()).expect("clear tid failed") }; } + RobustListHead::exit_robust_list(pcb.clone()); + // 如果是vfork出来的进程,则需要处理completion if thread.vfork_done.is_some() { thread.vfork_done.as_ref().unwrap().complete_all(); @@ -629,6 +633,9 @@ pub struct ProcessControlBlock { /// 线程信息 thread: RwLock, + + /// 进程的robust lock列表 + robust_list: RwLock>, } impl ProcessControlBlock { @@ -694,6 +701,7 @@ impl ProcessControlBlock { children: RwLock::new(Vec::new()), wait_queue: WaitQueue::default(), thread: RwLock::new(ThreadInfo::new()), + robust_list: RwLock::new(None), }; // 初始化系统调用栈 @@ -940,6 +948,16 @@ impl ProcessControlBlock { pub fn sig_struct_irqsave(&self) -> SpinLockGuard { self.sig_struct.lock_irqsave() } + + #[inline(always)] + pub fn get_robust_list(&self) -> RwLockReadGuard> { + return self.robust_list.read_irqsave(); + } + + #[inline(always)] + pub fn set_robust_list(&self, new_robust_list: Option) { + *self.robust_list.write_irqsave() = new_robust_list; + } } impl Drop for ProcessControlBlock { diff --git a/kernel/src/syscall/mod.rs b/kernel/src/syscall/mod.rs index b272a391..9956c301 100644 --- a/kernel/src/syscall/mod.rs +++ b/kernel/src/syscall/mod.rs @@ -690,9 +690,6 @@ impl Syscall { let uaddr2 = VirtAddr::new(args[4]); let val3 = args[5] as u32; - verify_area(uaddr, core::mem::size_of::())?; - verify_area(uaddr2, core::mem::size_of::())?; - let mut timespec = None; if utime != 0 && operation.contains(FutexFlag::FLAGS_HAS_TIMEOUT) { let reader = UserBufferReader::new( @@ -707,6 +704,26 @@ impl Syscall { Self::do_futex(uaddr, operation, val, timespec, uaddr2, utime as u32, val3) } + SYS_SET_ROBUST_LIST => { + let head = args[0]; + let head_uaddr = VirtAddr::new(head); + let len = args[1]; + + let ret = Self::set_robust_list(head_uaddr, len); + return ret; + } + + SYS_GET_ROBUST_LIST => { + let pid = args[0]; + let head = args[1]; + let head_uaddr = VirtAddr::new(head); + let len_ptr = args[2]; + let len_ptr_uaddr = VirtAddr::new(len_ptr); + + let ret = Self::get_robust_list(pid, head_uaddr, len_ptr_uaddr); + return ret; + } + SYS_READV => Self::readv(args[0] as i32, args[1], args[2]), SYS_WRITEV => Self::writev(args[0] as i32, args[1], args[2]), @@ -952,11 +969,6 @@ impl Syscall { Ok(0) } - SYS_SET_ROBUST_LIST => { - kwarn!("SYS_SET_ROBUST_LIST has not yet been implemented"); - Ok(0) - } - SYS_RSEQ => { kwarn!("SYS_RSEQ has not yet been implemented"); Ok(0) diff --git a/user/apps/test-for-robustfutex/.cargo/config.toml b/user/apps/test-for-robustfutex/.cargo/config.toml new file mode 100644 index 00000000..c1ca86b1 --- /dev/null +++ b/user/apps/test-for-robustfutex/.cargo/config.toml @@ -0,0 +1,2 @@ +[build] +target = "x86_64-unknown-linux-musl" \ No newline at end of file diff --git a/user/apps/test-for-robustfutex/.gitignore b/user/apps/test-for-robustfutex/.gitignore new file mode 100644 index 00000000..1ac35461 --- /dev/null +++ b/user/apps/test-for-robustfutex/.gitignore @@ -0,0 +1,3 @@ +/target +Cargo.lock +/install/ \ No newline at end of file diff --git a/user/apps/test-for-robustfutex/Cargo.toml b/user/apps/test-for-robustfutex/Cargo.toml new file mode 100644 index 00000000..6814ed4c --- /dev/null +++ b/user/apps/test-for-robustfutex/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "test-for-robustfutex" +version = "0.1.0" +edition = "2021" +description = "some tests for robust futex" +authors = [ "hmt <1037827920@qq.com>" ] + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +syscalls = '0.6.18' +libc = '0.2.153' \ No newline at end of file diff --git a/user/apps/test-for-robustfutex/Makefile b/user/apps/test-for-robustfutex/Makefile new file mode 100644 index 00000000..1b0274d2 --- /dev/null +++ b/user/apps/test-for-robustfutex/Makefile @@ -0,0 +1,56 @@ +TOOLCHAIN="+nightly-2023-08-15-x86_64-unknown-linux-gnu" +RUSTFLAGS+="" + +ifdef DADK_CURRENT_BUILD_DIR +# 如果是在dadk中编译,那么安装到dadk的安装目录中 + INSTALL_DIR = $(DADK_CURRENT_BUILD_DIR) +else +# 如果是在本地编译,那么安装到当前目录下的install目录中 + INSTALL_DIR = ./install +endif + +ifeq ($(ARCH), x86_64) + export RUST_TARGET=x86_64-unknown-linux-musl +else ifeq ($(ARCH), riscv64) + export RUST_TARGET=riscv64gc-unknown-linux-gnu +else +# 默认为x86_86,用于本地编译 + export RUST_TARGET=x86_64-unknown-linux-musl +endif + +run: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) run --target $(RUST_TARGET) + +build: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) build --target $(RUST_TARGET) + +clean: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) clean --target $(RUST_TARGET) + +test: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) test --target $(RUST_TARGET) + +doc: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) doc --target $(RUST_TARGET) + +fmt: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) fmt + +fmt-check: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) fmt --check + +run-release: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) run --target $(RUST_TARGET) --release + +build-release: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) build --target $(RUST_TARGET) --release + +clean-release: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) clean --target $(RUST_TARGET) --release + +test-release: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) test --target $(RUST_TARGET) --release + +.PHONY: install +install: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) install --target $(RUST_TARGET) --path . --no-track --root $(INSTALL_DIR) --force diff --git a/user/apps/test-for-robustfutex/README.md b/user/apps/test-for-robustfutex/README.md new file mode 100644 index 00000000..6a77fc48 --- /dev/null +++ b/user/apps/test-for-robustfutex/README.md @@ -0,0 +1,5 @@ +# 测试robust lock机制 + +此代码主要是用于测试: +1、robust lock机制中的两个系统调用是否能正常使用 +2、当持有锁的线程异常终止时,等待该锁的其他线程能够被通知到,以避免死锁或数据不一样 \ No newline at end of file diff --git a/user/apps/test-for-robustfutex/src/main.rs b/user/apps/test-for-robustfutex/src/main.rs new file mode 100644 index 00000000..e5ef859f --- /dev/null +++ b/user/apps/test-for-robustfutex/src/main.rs @@ -0,0 +1,394 @@ +extern crate libc; +extern crate syscalls; + +use std::{ + ffi::c_void, + mem::{self, size_of}, + process, + ptr::{self, NonNull}, + sync::atomic::{AtomicI32, Ordering}, + thread, + time::Duration, +}; + +use syscalls::{ + syscall0, syscall2, syscall3, syscall6, + Sysno::{futex, get_robust_list, gettid, set_robust_list}, +}; + +use libc::{ + c_int, mmap, perror, EXIT_FAILURE, MAP_ANONYMOUS, MAP_FAILED, MAP_SHARED, PROT_READ, PROT_WRITE, +}; + +const FUTEX_WAIT: usize = 0; +const FUTEX_WAKE: usize = 1; + +// 封装futex +#[derive(Clone, Copy, Debug)] +struct Futex { + addr: *mut u32, +} + +impl Futex { + pub fn new(addr: *mut u32) -> Self { + return Futex { addr }; + } + + pub fn get_addr(&self, offset: isize) -> *mut u32 { + return unsafe { self.addr.offset(offset) }; + } + + pub fn get_val(&self, offset: isize) -> u32 { + return unsafe { self.addr.offset(offset).read() }; + } + + pub fn set_val(&self, val: u32, offset: isize) { + unsafe { + self.addr.offset(offset).write(val); + } + } +} + +unsafe impl Send for Futex {} +unsafe impl Sync for Futex {} + +#[derive(Clone, Copy, Debug)] +struct Lock { + addr: *mut i32, +} + +impl Lock { + pub fn new(addr: *mut i32) -> Self { + return Lock { addr }; + } + + pub fn get_val(&self, offset: isize) -> i32 { + return unsafe { self.addr.offset(offset).read() }; + } + + pub fn set_val(&self, val: i32, offset: isize) { + unsafe { + self.addr.offset(offset).write(val); + } + } +} + +unsafe impl Send for Lock {} +unsafe impl Sync for Lock {} + +#[derive(Debug, Clone, Copy)] +struct RobustList { + next: *const RobustList, +} + +#[derive(Debug, Clone, Copy)] +struct RobustListHead { + list: RobustList, + /// 向kernel提供了要检查的futex字段的相对位置,保持用户空间的灵活性,可以自由 + /// 地调整其数据结构,而无需向内核硬编码任何特定的偏移量 + /// futexes中前面的地址是用来存入robust list中(list.next),后面是存放具体的futex val + /// 这个字段的作用就是从前面的地址偏移到后面的地址中从而获取futex val + #[allow(dead_code)] + futex_offset: isize, + /// 潜在的竞争条件:由于添加和删除列表是在获取锁之后进行的,这給线程留下了一个小窗口,在此期间可能会导致异常退出, + /// 从而使锁被悬挂,为了防止这种可能性。用户空间还维护了一个简单的list_op_pending字段,允许线程在获取锁后但还未添加到 + /// 列表时就异常退出时进行清理。并且在完成列表添加或删除操作后将其清除 + /// 这里没有测试这个,在内核中实现实际上就是把list_op_pending地址进行一次唤醒(如果有等待者) + #[allow(dead_code)] + list_op_pending: *const RobustList, +} + +fn error_handle(msg: &str) -> ! { + unsafe { perror(msg.as_ptr() as *const i8) }; + process::exit(EXIT_FAILURE) +} + +fn futex_wait(futexes: Futex, thread: &str, offset_futex: isize, lock: Lock, offset_count: isize) { + loop { + let atomic_count = AtomicI32::new(lock.get_val(offset_count)); + if atomic_count + .compare_exchange(1, 0, Ordering::SeqCst, Ordering::SeqCst) + .is_ok() + { + lock.set_val(0, offset_count); + + // 设置futex锁当前被哪个线程占用 + let tid = unsafe { syscall0(gettid).unwrap() as u32 }; + futexes.set_val(futexes.get_val(offset_futex) | tid, offset_futex); + + break; + } + + println!("{} wating...", thread); + let futex_val = futexes.get_val(offset_futex); + futexes.set_val(futex_val | 0x8000_0000, offset_futex); + let ret = unsafe { + syscall6( + futex, + futexes.get_addr(offset_futex) as usize, + FUTEX_WAIT, + futexes.get_val(offset_futex) as usize, + 0, + 0, + 0, + ) + }; + if ret.is_err() { + error_handle("futex_wait failed"); + } + + // 被唤醒后释放锁 + let atomic_count = AtomicI32::new(lock.get_val(offset_count)); + if atomic_count + .compare_exchange(0, 1, Ordering::SeqCst, Ordering::SeqCst) + .is_ok() + { + lock.set_val(1, offset_count); + + // 释放futex锁,不被任何线程占用 + futexes.set_val(futexes.get_val(offset_futex) & 0xc000_0000, offset_futex); + + break; + } + } +} + +fn futex_wake(futexes: Futex, thread: &str, offset_futex: isize, lock: Lock, offset_count: isize) { + let atomic_count = AtomicI32::new(lock.get_val(offset_count)); + if atomic_count + .compare_exchange(0, 1, Ordering::SeqCst, Ordering::SeqCst) + .is_ok() + { + lock.set_val(1, offset_count); + + // 释放futex锁,不被任何线程占用 + futexes.set_val(futexes.get_val(offset_futex) & 0xc000_0000, offset_futex); + + // 如果没有线程/进程在等这个futex,则不必唤醒, 释放改锁即可 + let futex_val = futexes.get_val(offset_futex); + if futex_val & 0x8000_0000 == 0 { + return; + } + + futexes.set_val(futex_val & !(1 << 31), offset_futex); + let ret = unsafe { + syscall6( + futex, + futexes.get_addr(offset_futex) as usize, + FUTEX_WAKE, + 1, + 0, + 0, + 0, + ) + }; + if ret.is_err() { + error_handle("futex wake failed"); + } + println!("{} waked", thread); + } +} + +fn set_list(futexes: Futex) { + let head = RobustListHead { + list: RobustList { next: ptr::null() }, + futex_offset: 44, + list_op_pending: ptr::null(), + }; + let head = NonNull::from(&head).as_ptr(); + unsafe { + // 加入第一个futex + let head_ref_mut = &mut *head; + head_ref_mut.list.next = futexes.get_addr(0) as *const RobustList; + + // 加入第二个futex + let list_2 = NonNull::from(&*head_ref_mut.list.next).as_ptr(); + let list_2_ref_mut = &mut *list_2; + list_2_ref_mut.next = futexes.get_addr(1) as *const RobustList; + + //println!("robust list next: {:?}", (*head).list.next ); + //println!("robust list next next: {:?}", (*(*head).list.next).next ); + + // 向内核注册robust list + let len = mem::size_of::<*mut RobustListHead>(); + let ret = syscall2(set_robust_list, head as usize, len); + if ret.is_err() { + println!("failed to set_robust_list, ret = {:?}", ret); + } + } +} + +fn main() { + test01(); + + println!("-------------"); + + test02(); + + println!("-------------"); +} + +//测试set_robust_list和get_robust_list两个系统调用是否能正常使用 +fn test01() { + // 创建robust list 头指针 + let head = RobustListHead { + list: RobustList { next: ptr::null() }, + futex_offset: 8, + list_op_pending: ptr::null(), + }; + let head = NonNull::from(&head).as_ptr(); + + let futexes = unsafe { + mmap( + ptr::null_mut::(), + (size_of::() * 2) as libc::size_t, + PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_SHARED, + -1, + 0, + ) as *mut u32 + }; + if futexes == MAP_FAILED as *mut u32 { + error_handle("futexes_addr mmap failed"); + } + + unsafe { + futexes.offset(11).write(0x0000_0000); + futexes.offset(12).write(0x8000_0000); + println!("futex1 next addr: {:?}", futexes.offset(0)); + println!("futex2 next addr: {:?}", futexes.offset(1)); + println!("futex1 val addr: {:?}", futexes.offset(11)); + println!("futex2 val addr: {:?}", futexes.offset(12)); + println!("futex1 val: {:#x?}", futexes.offset(11).read()); + println!("futex2 val: {:#x?}", futexes.offset(12).read()); + } + + // 打印注册之前的robust list + println!("robust list next(get behind): {:?}", &unsafe { *head }); + + unsafe { + let head_ref_mut = &mut *head; + head_ref_mut.list.next = futexes.offset(0) as *const RobustList; + let list_2 = NonNull::from(&*head_ref_mut.list.next).as_ptr(); + let list_2_ref_mut = &mut *list_2; + list_2_ref_mut.next = futexes.offset(1) as *const RobustList; + println!("robust list next addr: {:?}", (*head).list.next); + println!( + "robust list next next addr: {:?}", + (*(*head).list.next).next + ); + } + + unsafe { + let len = mem::size_of::<*mut RobustListHead>(); + let ret = syscall2(set_robust_list, head as usize, len); + if ret.is_err() { + println!("failed to set_robust_list, ret = {:?}", ret); + } + } + + println!("get before, set after: {:?}", head); + println!("get before, set after: {:?}", &unsafe { *head }); + unsafe { + let len: usize = 0; + println!("len = {}", len); + let len_ptr = NonNull::from(&len).as_ptr(); + let ret = syscall3(get_robust_list, 0, head as usize, len_ptr as usize); + println!("get len = {}", len); + if ret.is_err() { + println!("failed to get_robust_list, ret = {:?}", ret); + } + + println!("futex1 val: {:#x}", futexes.offset(11).read()); + println!("futex2 val: {:#x}", futexes.offset(12).read()); + println!("robust list next: {:?}", futexes.offset(0)); + println!("robust list next next: {:#x?}", futexes.offset(0).read()); + } + println!("robust list head(get after): {:?}", head); + println!("robust list next(get after): {:?}", &unsafe { *head }); +} + +//测试一个线程异常退出时futex的robustness(多线程测试,目前futex还不支持多进程) +fn test02() { + let futexes = unsafe { + mmap( + ptr::null_mut::(), + (size_of::() * 2) as libc::size_t, + PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_SHARED, + -1, + 0, + ) as *mut u32 + }; + if futexes == MAP_FAILED as *mut u32 { + error_handle("mmap failed"); + } + let count = unsafe { + mmap( + ptr::null_mut::(), + (size_of::() * 2) as libc::size_t, + PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_SHARED, + -1, + 0, + ) as *mut i32 + }; + if count == MAP_FAILED as *mut i32 { + error_handle("mmap failed"); + } + + unsafe { + // 在这个示例中,第一段和第二段地址放入robust list,第11段地址和第12段地址存放futex val + futexes.offset(11).write(0x0000_0000); + futexes.offset(12).write(0x0000_0000); + println!("futex1 next addr: {:?}", futexes.offset(0)); + println!("futex2 next addr: {:?}", futexes.offset(1)); + println!("futex1 val addr: {:?}", futexes.offset(11)); + println!("futex2 val addr: {:?}", futexes.offset(12)); + println!("futex1 val: {:#x?}", futexes.offset(11).read()); + println!("futex2 val: {:#x?}", futexes.offset(12).read()); + + count.offset(0).write(1); + count.offset(1).write(0); + println!("count1 val: {:?}", count.offset(0).read()); + println!("count2 val: {:?}", count.offset(1).read()); + } + + let futexes = Futex::new(futexes); + let locks = Lock::new(count); + + // tid1 = 7 + let thread1 = thread::spawn(move || { + set_list(futexes); + thread::sleep(Duration::from_secs(2)); + for i in 0..2 { + futex_wait(futexes, "thread1", 11, locks, 0); + println!("thread1 times: {}", i); + thread::sleep(Duration::from_secs(3)); + + let tid = unsafe { syscall0(gettid).unwrap() as u32 }; + futexes.set_val(futexes.get_val(12) | tid, 12); + + if i == 1 { + // 让thread1异常退出,从而无法唤醒thread2,检测robustness + println!("Thread1 exiting early due to simulated error."); + return; + } + futex_wake(futexes, "thread2", 12, locks, 1); + } + }); + + // tid2 = 6 + set_list(futexes); + for i in 0..2 { + futex_wait(futexes, "thread2", 12, locks, 1); + println!("thread2 times: {}", i); + + let tid = unsafe { syscall0(gettid).unwrap() as u32 }; + futexes.set_val(futexes.get_val(11) | tid, 11); + + futex_wake(futexes, "thread1", 11, locks, 0); + } + + thread1.join().unwrap(); +} diff --git a/user/dadk/config/test_for_robustfutex_0_1_0.dadk b/user/dadk/config/test_for_robustfutex_0_1_0.dadk new file mode 100644 index 00000000..3859db15 --- /dev/null +++ b/user/dadk/config/test_for_robustfutex_0_1_0.dadk @@ -0,0 +1,24 @@ +{ + "name": "test-for-robustfutex", + "version": "0.1.0", + "description": "some tests for robust futex", + "rust_target": null, + "task_type": { + "BuildFromSource": { + "Local": { + "path": "apps/test-for-robustfutex" + } + } + }, + "depends": [], + "build": { + "build_command": "make install" + }, + "install": { + "in_dragonos_path": "/" + }, + "clean": { + "clean_command": "make clean" + }, + "envs": [] +} \ No newline at end of file