Procházet zdrojové kódy

fix(futex): 修复futex定时器激活逻辑导致唤醒丢失的问题&复FUTEX_WAKE_OP操作中的Linux兼容性问题 (#1326)

* fix(futex): 修复定时器激活逻辑以避免唤醒丢失

- 在阻塞进程时,确保定时器在中断关闭后被激活,以防止短超时导致的唤醒丢失
- 移除不必要的定时器激活调用,优化代码结构

Signed-off-by: longjin <longjin@DragonOS.org>

* fix(futex): 修复FUTEX_WAKE_OP操作中的Linux兼容性问题

- 修复FUTEX_OP_ANDN操作的位运算逻辑错误
- 支持私有futex中uaddr为NULL时的Linux兼容行为
- futex_test启用PrivateFutex的测例

Signed-off-by: longjin <longjin@DragonOS.org>

---------

Signed-off-by: longjin <longjin@DragonOS.org>
LoGin před 5 dny
rodič
revize
c353791804

+ 14 - 6
kernel/src/libs/futex/futex.rs

@@ -284,8 +284,6 @@ impl Futex {
             let jiffies = next_n_us_timer_jiffies(total_us);
 
             let wake_up = Timer::new(wakeup_helper, jiffies);
-
-            wake_up.activate();
             timer = Some(wake_up);
         }
 
@@ -297,6 +295,12 @@ impl Futex {
         let irq_guard = unsafe { CurrentIrqArch::save_and_disable_irq() };
         // 满足条件则将当前进程在该bucket上挂起
         bucket_mut.sleep_no_sched(futex_q.clone())?;
+
+        // 在关中断并且已经标记阻塞后,激活定时器,避免短超时在阻塞之前触发造成唤醒丢失
+        if let Some(ref t) = timer {
+            t.activate();
+        }
+
         drop(futex_map_guard);
         drop(irq_guard);
         schedule(SchedMode::SM_NONE);
@@ -472,6 +476,7 @@ impl Futex {
         nr_wake2: i32,
         op: i32,
     ) -> Result<usize, SystemError> {
+        // Linux 语义:对于私有 futex,允许 uaddr1 为 NULL,此时只执行 op,不从 uaddr1 唤醒任何等待者。
         let key1 = Futex::get_futex_key(
             uaddr1,
             flags.contains(FutexFlag::FLAGS_SHARED),
@@ -484,11 +489,13 @@ impl Futex {
         )?;
 
         let mut futex_data_guard = FutexData::futex_map();
-        let bucket1 = futex_data_guard.get_mut(&key1).ok_or(SystemError::EINVAL)?;
         let mut wake_count = 0;
 
-        // 唤醒uaddr1中的进程
-        wake_count += bucket1.wake_up(key1, None, nr_wake as u32)?;
+        // 若 uaddr1 没有关联任何等待者,则按照 Linux 行为返回 0 而不是 EINVAL。
+        if let Some(bucket1) = futex_data_guard.get_mut(&key1) {
+            // 唤醒uaddr1中的进程
+            wake_count += bucket1.wake_up(key1, None, nr_wake as u32)?;
+        }
 
         match Self::futex_atomic_op_inuser(op as u32, uaddr2) {
             Ok(ret) => {
@@ -683,8 +690,9 @@ impl Futex {
             FutexOP::FUTEX_OP_OR => unsafe {
                 *((*ptr) as *mut u32) |= oparg;
             },
+            // ANDN 语义:new = old & ~oparg
             FutexOP::FUTEX_OP_ANDN => unsafe {
-                *((*ptr) as *mut u32) &= oparg;
+                *((*ptr) as *mut u32) &= !oparg;
             },
             FutexOP::FUTEX_OP_XOR => unsafe {
                 *((*ptr) as *mut u32) ^= oparg;

+ 10 - 1
kernel/src/libs/futex/syscall/sys_futex.rs

@@ -155,7 +155,6 @@ pub(super) fn do_futex(
         compiler_fence(Ordering::SeqCst);
     });
 
-    verify_area(uaddr, core::mem::size_of::<u32>())?;
     let cmd = FutexArg::from_bits(operation & FutexFlag::FUTEX_CMD_MASK.bits())
         .ok_or(SystemError::ENOSYS)?;
 
@@ -187,6 +186,16 @@ pub(super) fn do_futex(
         }
     }
 
+    // 对于 FUTEX_WAKE_OP 的私有 futex,允许 uaddr 为 NULL(Linux 兼容行为)。
+    // 仅在不满足该例外时才校验 uaddr。
+    let skip_uaddr_check = cmd == FutexArg::FUTEX_WAKE_OP
+        && (operation & FutexFlag::FUTEX_PRIVATE_FLAG.bits()) != 0
+        && uaddr.data() == 0;
+
+    if !skip_uaddr_check {
+        verify_area(uaddr, core::mem::size_of::<u32>())?;
+    }
+
     match cmd {
         FutexArg::FUTEX_WAIT => {
             return Futex::futex_wait(uaddr, flags, val, timeout, FUTEX_BITSET_MATCH_ANY);

+ 0 - 1
user/apps/tests/syscall/gvisor/blocklists/futex_test

@@ -1,5 +1,4 @@
 # PrivateFutexTest needs FUTEX_WAKE_OP
-PrivateFutexTest.*
 RobustFutexTest.*
 SharedFutexTest.WakeInterprocessFile_NoRandomSave