Преглед изворни кода

Merge pull request #25 from Shao-ZW/robust-list

Implement syscall get_robust_list
greatbridf пре 7 месеци
родитељ
комит
532b88c4c3

+ 17 - 2
src/kernel/syscall/procops.rs

@@ -10,7 +10,7 @@ use crate::kernel::constants::{
 use crate::kernel::mem::PageBuffer;
 use crate::kernel::task::{
     do_clone, futex_wait, futex_wake, FutexFlags, FutexOp, ProcessList, ProgramLoader,
-    SignalAction, Thread, WaitType,
+    RobustListHead, SignalAction, Thread, WaitType,
 };
 use crate::kernel::task::{parse_futexop, CloneArgs};
 use crate::kernel::timer::sleep;
@@ -25,7 +25,7 @@ use bitflags::bitflags;
 use core::ptr::NonNull;
 use eonix_hal::processor::UserTLS;
 use eonix_hal::traits::trap::RawTrapContext;
-use eonix_mm::address::Addr as _;
+use eonix_mm::address::{Addr as _, VAddr};
 use eonix_runtime::task::Task;
 use eonix_sync::AsProof as _;
 use posix_types::constants::{P_ALL, P_PID};
@@ -172,6 +172,11 @@ fn execve(exec: *const u8, argv: *const PtrT, envp: *const PtrT) -> KResult<Sysc
     //       should be terminated and `execve` is performed in the thread group leader.
     let load_info = ProgramLoader::parse(dentry.clone())?.load(argv, envp)?;
 
+    if let Some(robust_list) = thread.get_robust_list() {
+        let _ = Task::block_on(robust_list.wake_all());
+        thread.set_robust_list(None);
+    }
+
     unsafe {
         // SAFETY: We are doing execve, all other threads are terminated.
         thread.process.mm_list.replace(Some(load_info.mm_list));
@@ -745,6 +750,16 @@ fn futex(
     }
 }
 
+#[eonix_macros::define_syscall(SYS_SET_ROBUST_LIST)]
+fn set_robust_list(head: usize, len: usize) -> KResult<()> {
+    if len != size_of::<RobustListHead>() {
+        return Err(EINVAL);
+    }
+
+    thread.set_robust_list(Some(VAddr::from(head)));
+    Ok(())
+}
+
 #[eonix_macros::define_syscall(SYS_RT_SIGRETURN)]
 fn rt_sigreturn() -> KResult<SyscallNoReturn> {
     thread

+ 1 - 1
src/kernel/task.rs

@@ -10,7 +10,7 @@ mod signal;
 mod thread;
 
 pub use clone::{do_clone, CloneArgs, CloneFlags};
-pub use futex::{futex_wait, futex_wake, parse_futexop, FutexFlags, FutexOp};
+pub use futex::{futex_wait, futex_wake, parse_futexop, FutexFlags, FutexOp, RobustListHead};
 pub use kernel_stack::KernelStack;
 pub use loader::ProgramLoader;
 pub use process::{alloc_pid, Process, ProcessBuilder, WaitObject, WaitType};

+ 61 - 0
src/kernel/task/futex.rs

@@ -256,3 +256,64 @@ async fn futex_requeue(
 
     todo!()
 }
+
+// The purpose of the robust futex list is to ensure that if a thread
+// accidentally fails to unlock a futex before terminating or calling
+// execve(2), another thread that is waiting on that futex is
+// notified that the former owner of the futex has died.  This
+// notification consists of two pieces: the FUTEX_OWNER_DIED bit is
+// set in the futex word, and the kernel performs a futex(2)
+// FUTEX_WAKE operation on one of the threads waiting on the futex.
+// https://man7.org/linux/man-pages/man2/get_robust_list.2.html
+
+#[repr(C)]
+#[derive(Debug, Clone, Copy)]
+struct RobustList {
+    next: usize, // Pointer to the next RobustList entry
+}
+
+#[repr(C)]
+#[derive(Debug, Clone, Copy)]
+pub struct RobustListHead {
+    robust_list: RobustList,
+    futex_offset: isize,
+    list_op_pending: usize,
+}
+
+impl RobustListHead {
+    fn futex_addr(&self, entry_ptr: usize) -> usize {
+        (self.futex_offset + entry_ptr as isize) as usize
+    }
+
+    pub async fn wake_all(&self) -> KResult<()> {
+        let end_ptr = self.robust_list.next;
+        let mut entry_ptr = end_ptr;
+
+        if entry_ptr == 0 {
+            return Ok(());
+        }
+
+        loop {
+            // Wake up the futex at the entry_ptr address.
+            let futex_addr = self.futex_addr(entry_ptr);
+            futex_wake(futex_addr, None, usize::MAX as u32).await?;
+
+            // Move to the next entry in the robust list.
+            let robust_list = UserPointer::new(entry_ptr as *const RobustList)?.read()?;
+
+            entry_ptr = robust_list.next;
+
+            if entry_ptr == end_ptr || entry_ptr == 0 {
+                break;
+            }
+        }
+
+        if self.list_op_pending != 0 {
+            // If there is a pending operation, we need to wake it up.
+            let pending_futex_addr = self.futex_addr(self.list_op_pending);
+            futex_wake(pending_futex_addr, None, usize::MAX as u32).await?;
+        }
+
+        Ok(())
+    }
+}

+ 4 - 0
src/kernel/task/process_list.rs

@@ -142,6 +142,10 @@ impl ProcessList {
             let _ = futex_wake(clear_ctid, None, 1).await;
         }
 
+        if let Some(robust_list) = thread.get_robust_list() {
+            let _ = robust_list.wake_all().await;
+        }
+
         // main thread exit
         if thread.tid == process.pid {
             assert_eq!(thread.tid, process.pid);

+ 16 - 2
src/kernel/task/thread.rs

@@ -6,9 +6,9 @@ use crate::{
     kernel::{
         interrupt::default_irq_handler,
         syscall::{syscall_handlers, SyscallHandler},
-        task::{clone::CloneArgs, CloneFlags},
+        task::{clone::CloneArgs, futex::RobustListHead, CloneFlags},
         timer::{should_reschedule, timer_interrupt},
-        user::UserPointerMut,
+        user::{UserPointer, UserPointerMut},
         vfs::{filearray::FileArray, FsContext},
     },
     prelude::*,
@@ -74,6 +74,8 @@ struct ThreadInner {
     set_child_tid: Option<usize>,
 
     clear_child_tid: Option<usize>,
+
+    robust_list_address: Option<VAddr>,
 }
 
 pub struct Thread {
@@ -251,6 +253,7 @@ impl ThreadBuilder {
                 tls: self.tls,
                 set_child_tid: self.set_child_tid,
                 clear_child_tid: self.clear_child_tid,
+                robust_list_address: None,
             }),
         });
 
@@ -288,6 +291,17 @@ impl Thread {
         Ok(())
     }
 
+    pub fn set_robust_list(&self, robust_list_address: Option<VAddr>) {
+        self.inner.lock().robust_list_address = robust_list_address;
+    }
+
+    pub fn get_robust_list(&self) -> Option<RobustListHead> {
+        let addr = self.inner.lock().robust_list_address?;
+        let user_pointer = UserPointer::new(addr.addr() as *const RobustListHead).ok()?;
+
+        user_pointer.read().ok()
+    }
+
     pub fn set_name(&self, name: Arc<[u8]>) {
         self.inner.lock().name = name;
     }