소스 검색

feat: implement syscall get_robust_list

zhuowei shao 7 달 전
부모
커밋
bf6c04837b
5개의 변경된 파일96개의 추가작업 그리고 3개의 파일을 삭제
  1. 18 1
      src/kernel/syscall/procops.rs
  2. 1 1
      src/kernel/task.rs
  3. 61 0
      src/kernel/task/futex.rs
  4. 4 0
      src/kernel/task/process_list.rs
  5. 12 1
      src/kernel/task/thread.rs

+ 18 - 1
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;
@@ -171,6 +171,11 @@ fn execve(exec: *const u8, argv: *const PtrT, envp: *const PtrT) -> KResult<Sysc
     // TODO: When `execve` is called by one of the threads in a process, the other threads
     //       should be terminated and `execve` is performed in the thread group leader.
     if let Ok(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));
@@ -760,6 +765,18 @@ fn futex(
     }
 }
 
+#[eonix_macros::define_syscall(SYS_SET_ROBUST_LIST)]
+fn set_robust_list(head: *const RobustListHead, len: usize) -> KResult<()> {
+    if len != size_of::<RobustListHead>() {
+        return Err(EINVAL);
+    }
+
+    let robust_list_head = UserPointer::new(head)?.read()?;
+
+    thread.set_robust_list(Some(robust_list_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

@@ -145,6 +145,10 @@ impl ProcessList {
                 .expect("should wake up child tid");
         }
 
+        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);

+ 12 - 1
src/kernel/task/thread.rs

@@ -6,7 +6,7 @@ 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,
         vfs::{filearray::FileArray, FsContext},
@@ -74,6 +74,8 @@ struct ThreadInner {
     set_child_tid: Option<usize>,
 
     clear_child_tid: Option<usize>,
+
+    robust_list: Option<RobustListHead>,
 }
 
 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: None,
             }),
         });
 
@@ -288,6 +291,14 @@ impl Thread {
         Ok(())
     }
 
+    pub fn set_robust_list(&self, robust_list: Option<RobustListHead>) {
+        self.inner.lock().robust_list = robust_list;
+    }
+
+    pub fn get_robust_list(&self) -> Option<RobustListHead> {
+        self.inner.lock().robust_list
+    }
+
     pub fn set_name(&self, name: Arc<[u8]>) {
         self.inner.lock().name = name;
     }