问题现象
pytorch进程通过k8s方式部署,由于该任务无法退出,导致所在的pod也无法退出;再次向pytorch任务发送SIGKILL信号也无法杀死。kenrel版本5.10。
任务状态信息
pytorch进程状态

可以看到pending了一个SIGKILL信号(0x100表示第9为被置1,即SIGKILL信号),man手册中也明确了SIGKILL无法被捕获、阻塞忽略等。另外即使是两个不同的pid namespace,信号也可以传递。

pytorch进程父子关系
[]中表示不同的pid namespace。
1064331(pytorch) [4.0654435]—>4173735[4.0654435]—>4173734[4026581836]—>1[4026581836]
top进程查看
1064333(pytorch)显示R状态,比较可疑的几点是:没有内存信息(内核线程不会有内存信息),同时cpu打满,但是sys又不是很高,并且pytorch任务proc下的一些资源信息又获取不到,比如cat /proc/1064333/stack信息为空。

1.cpu打满像是pytorch任务陷在了某个循环逻辑;
2.stack信息为空只有当任务在退出过程中才会清理自身资源;
进一步分析
既然无法查查看pytorch栈信息,那么我们可以查看其父进程的状态。

可以看到1064331进程的父进程4173735(管理容器的进程?)在退出流程。
上面栈信息的逻辑:父进程4173735退出,并且在清理pid_namespace下的其他任务,等着其他任务退出。4173735看着是当前pid_namespace的init任务。
用sysak profiling 工具观测到1064331任务的栈信息,确实也是在exit流程中,但陷在了uvm_spin_loop代码中,可以通过函数百分比看出。uvm_spin_loop为某gpu厂商的驱动代码,这块逻辑看着也不是持锁死等,单看sys指标没有问题,像是uvm_spin_loop某个条件为达成然后一直再做重复的事情。

代码分析
struct pid_namespace {
...
// ‘child_reaper’ is the init process of a pid_namespace
struct task_struct *child_reaper;
...
}
forget_original_parent,父进程退出时为其下面的子进程寻找新的父进程,zap_pid_ns_process中涉及pid_namespace退出情况的资源清理。
forget_original_parent
// 如果退出的不是pid_namespace的init任务,则选择init 进程作为新父新
// 如果是init 进程退出选择一个线程作为新父亲
-->find_child_reaper
-->struct pid_namespace *pid_ns = task_active_pid_ns(father)
-->find_alive_thread
// 走到这里意味着是当前pid_namespace中退出的是init任务,则需要将当前pid_namespace中任务都kill
-->zap_pid_ns_processes(pid_ns)
idr_for_each_entry_continue(&pid_ns->idr, pid, nr) {
task = pid_task(pid, PIDTYPE_PID);
if (task && !__fatal_signal_pending(task))
// 向所有进程发sigkill信号
group_send_sig_info(SIGKILL, SEND_SIG_PRIV, task, PIDTYPE_MAX);
}
// wait接收sigkill信号的任务去清理他们的资源,然后自己退出
do {
clear_thread_flag(TIF_SIGPENDING);
rc = kernel_wait4(-1, NULL, __WALL, NULL);
} while (rc != -ECHILD);
// 查看选择的child_reaper是否可以作为真正的新父亲
-->find_new_reaper
结论
当前问题中pytorch已经处于退出流程中但由于驱动的bug造成在uvm_spin_loop中无法返回进而造成pytorch退出失败,最终就导致整个pod资源无法成功清理,临时规避就重启,该问题还未修复。
https://github.com/NVIDIA/open-gpu-kernel-modules/issues/456
扩展
这里高版本有个内核pid namespace退出杀死子进程失败的情况但不影响上面的问题:
https://lore.kernel.org/all/20220713175305.1327649-1-tycho@tycho.pizza/
该补丁修复的是如果子进程在退出流程中并发生了等待资源回收的情况;恰巧父进程也在退出并给子进程发了sigkill信号,那么会造成子进程无法响应kill信号。如果等回收资源出了些问题那么父进程也会死等。详情见[4]
参考
[1]https://blog.csdn.net/wennuanddianbo/article/details/101305653
[2]http://blog.chinaunix.net/uid-69947851-id-5825901.html
[4]https://netflixtechblog.com/debugging-a-fuse-deadlock-in-the-linux-kernel-c75cd7989b6d