Skip to content

调度器死锁:fork 后所有任务睡眠、CPU0 卡 idle、CPU1 卡 pick_next_task(lmbench 全量 5/5 复现) #2167

Description

@mistcoversmyeyes

现象

在 QEMU+KVM guest 上跑 lmbench 全量测试(AUTO_TEST=benchmark,runner/run.sh),init.sh 完成后、run.sh 第一个 fork 处死锁:

  • 串口在 lmbench test environment initialized 后完全静默(run.sh 卡在 ENOUGH 预计算的 timeout ... enough 命令替换的 fork 上)
  • 用户态所有任务睡眠,无任何输出;600s 无输出被 monitor 判卡死
  • 连续复现 5/5;另有一次完整跑完 47 指标(竞争窗口未触发),说明是时序敏感的偶发死锁,不是必然

复现

# 分支 feat/lmbench @ 671b7580(QEMU 2 vCPU / 2G 内存)
make qemu-nographic AUTO_TEST=benchmark BENCHMARK_TEST_DIR=/opt/tests/benchmark/lmbench
# 等待 ~2-3 分钟 boot + init,之后串口无输出即复现

gdb 现场(QEMU -gdb attach)

CPU0(RIP 0xffff800000171856,arch_idle_func 内):

call  rcu::exit_idle
lock  decq (%rbx)
jne   arch_idle_func+64     ; 计数不为 0 则循环
...
call  Arc<ProcessControlBlock>::drop_slow

CPU0 不在正常 halt idle(正常 x86::halt() 时 QEMU CPU 应接近 0%,实测 QEMU 占用 ~140%),疑似走了 idle.rs 的 IRQ 禁用分支 spin_loop()。

CPU1:

#0 current_cpu_id
#4 <CompletelyFairScheduler as Scheduler>::pick_next_task

CPU1 卡在调度器选择路径。

初步分析

  • 卡点与用户态 fork(sys_fork → 调度器插入任务 → pick_next_task)强相关
  • kernel/src/arch/x86_64/process/idle.rs:26-28:IRQ 禁用时 idle 进入 spin_loop() 忙转——CPU0 的状态与"中断被关掉/时钟 tick 丢失"一致
  • kernel/src/sched/fair.rs:1704 pick_next_task:EEVDF 选择路径卡住
  • 怀疑方向:fork 路径上某处关中断后未恢复 / 调度器锁竞争 / idle 退出计数(RCU exit_idle)不归零导致 wakeup 丢失

影响

lmbench 全量基准无法稳定运行(第一个 fork 就可能死锁),同时解释了此前 process_fork_lat 偶发超时(rc=124)现象。

环境

  • QEMU 8.x + KVM(WSL2 / Linux 6.6 host)
  • 2 vCPU(q35, hpet=off),2G 内存,virtio-blk,user-mode NAT
  • DragonOS feat/lmbench @ 671b758(kernel.elf 编译于 7/31)

关联PR、Issue

Activity

  1. mistcoversmyeyes commented on Aug 1, 2026

    @mistcoversmyeyes
    CollaboratorAuthor

    根因修正:不是调度器死锁,是 vfat root fs 损坏

    进一步诊断推翻了"调度器死锁"的初步结论。真实根因是 root fs(vfat)在多轮 QEMU 强制关机(kill -9,模拟断电)后损坏,导致块设备 IO 极慢/卡死。

    证据链

    1. 镜像分区是 vfat(此前误认为 ext4)。DragonOS guest 的 root fs 挂在镜像的 vfat 分区上。
    2. fsck.fat -n 只读检查(host 侧):
      • Dirty bit is set. Fs was not properly unmounted and some data may be corrupt.
      • Reclaimed 218153 unused clusters (893554688 bytes)——约 893MB 簇状态错误
      • Invalid '..' entry(/sys、/ext4 目录项损坏)
      • FATs differ(主 FAT 与备份 FAT 不一致)
    3. guest 内块 IO 慢 500 倍:同一 e2e 内 dd 写 root fs 64MB 耗时 154.7s(434 kB/s),而写 ramfs 同大小只要 0.35s(194 MB/s)——vfat 写路径异常。
    4. 卡点全在依赖块 IO 的位置:
      • run.sh ENOUGH 预计算(exec enough 二进制 = vfat 读)→ 卡
      • run.sh 收尾 clean_up.sh(rm -f ext4.img = vfat 释放)→ 卡
      • process_fork_lat 单独跑完全 ok(fork 不依赖块 IO,3 samples 9971µs)——与"块 IO 卡"完全吻合
    5. 逐轮恶化:首轮完整跑完 47 指标(当时 vfat 尚可用),之后每轮 kill -9 进一步破坏 vfat,最终第一个 exec 就卡。

    修正后的结论

    • 现象(串口静默、任务全睡眠、gdb 看到 idle/pick_next_task)是所有任务阻塞在 vfat 块 IO 的表象,不是调度器本身的死锁
    • 诱因是 vfat 无日志 + 断电式关机累积损坏
    • 修复:重建镜像(已执行);长期建议 e2e 结束后优雅关机或对 vfat 做定期 fsck

    待验证

    镜像重建完成后重跑全量,若 31+ 指标恢复且不再卡死,则本 issue 关闭;若 process_fork_lat 仍超时,再回到调度器方向调查。

  2. mistcoversmyeyes commented on Aug 1, 2026

    @mistcoversmyeyes
    CollaboratorAuthor

    关闭原因:根因已定位为 vfat root fs 在多轮强制关机(kill -9)后损坏导致块 IO 卡死,非调度器 bug。重建镜像 + 预置 /ext4 目录后 lmbench 全量恢复正常推进(无卡死)。遗留问题:DragonOS vfat 驱动写大文件(1GB)极慢,将另行跟踪。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions