crash分析rw_semaphore引发的系统hung问题

2021-09-29 12:12:36 浏览数 (3)

该问题发生于centos7内核3.10.0-693.1.1.el7.x86_64,源码部分分析也来自该版本内核。

一、机器重启后拿到kernel coredump第一件事自然是先看下为什么重启:

1. 查看栈信息可以确认系统是由于进程等待rw_semaphore锁超时后被khungtaskd触发的重启:

crash> bt

PID: 89 TASK: ffff8801745ceeb0 CPU: 3 COMMAND: "khungtaskd"

#0 [ffff8807ff4ffcb0] machine_kexec at ffffffff8105c52b

#1 [ffff8807ff4ffd98] __crash_kexec at ffffffff81104adf

#2 [ffff8807ff4ffea8] watchdog at ffffffff8112ef00

#3 [ffff8807ff4ffec8] kthread at ffffffff810b099f

#4 [ffff8807ff4fff50] sysret_check at ffffffff816b4fd8

#5 [ffff8807ff4fff80] kthread at ffffffff810b08d0

crash>

crash> bt ffff880011b44f10

PID: 2187 TASK: ffff880011b44f10 CPU: 12 COMMAND: "java"

#0 [ffff880011223d58] __schedule at ffffffff816a9015

#1 [ffff880011223dc0] yield_to at ffffffff816a9599

#2 [ffff880011223dd0] rwsem_down_write_failed at ffffffff816aabcd

#3 [ffff880011223e58] memmove at ffffffff81331a28

#4 [ffff880011223ec0] __do_page_fault at ffffffff816b029c

#5 [ffff880011223f20] trace_do_page_fault at ffffffff816b03a5

#6 [ffff880011223f50] paranoid_swapgs at ffffffff816ac5c8

RIP: 00007f441d052585 RSP: 00007f44329dcbf0 RFLAGS: 00010282

RAX: 006100760061006a RBX: 00000000eb360f80 RCX: 000000000000001e

RDX: fffffffffffffffd RSI: 00000000eb361030 RDI: 00000000eb360fc0

RBP: 00007f44329dcbf0 R8: 000000000000001e R9: 000000000000001e

R10: 00007f441d0525c0 R11: 000000000000001e R12: 0000000000000000

R13: 00000000eb361000 R14: 00000000eb3609b0 R15: 00007f442c008800

ORIG_RAX: ffffffffffffffff CS: 0033 SS: 002b

crash>

2. 找出是reader还是writer拿锁:

2.1 找出mmap_sem.owner:

结合down_read的实现可以知道当前rw_semaphore.owner可以知道是有进程拿了读锁:

二. 因为是reader拿的锁,所以无法通过owner直接找出拿锁的进程,下面开始找出拿锁进程的过程:

分析代码可以知道struct rw_semaphore.wait_list通过struct rwsem_waiter.list将所有等待该读写信号量的进程串联起来:

rw_semaphore.wait_list.next的值为rwsem_waiter.list地址:

因此通过crash的list命令可以列出所有等待该rw信号量的rwswm_waiter信息,0xffff880009513e60为 rwsem_waiter.list地址:

crash> list -l rwsem_waiter.list -s rwsem_waiter.list,task,type 0xffff880009513e60

ffff880009513e60

list = {

next = 0xffff88000f70fe60,

prev = 0xffff88002021bdb0

}

task = 0xffff88001758af70

type = RWSEM_WAITING_FOR_WRITE

ffff88000f70fe60

list = {

next = 0xffff880011223df0,

prev = 0xffff880009513e60

}

task = 0xffff880174bdaf70

type = RWSEM_WAITING_FOR_WRITE

ffff880011223df0

list = {

next = 0xffff880015ba3df0,

prev = 0xffff88000f70fe60

}

task = 0xffff880011b44f10

type = RWSEM_WAITING_FOR_READ

.....

.....

大部分情况下,拿锁的进程不释放锁的原因都是因为在等待其他事件(其他锁或者IO等,因此可以尝试找下所有UN状态的进程

跟rw_semaphore等待队列的所有进程进行对比,筛选出不在rw_semaphore等待队列并且UN状态的进程:

列出所有UN状态的进程:

ps | grep UN | awk '{print "0x"$4}' | sort > ps-task.txt

列出所有跟0xffff880009513e60 对应的进程在同一个rw_semaphore等待队列的所有进程:

list rwsem_waiter.list -s rwsem_waiter.task -h 0xffff880009513e60 | grep task | awk '{print $3}' | sort > list-task.txt

对比找出ps-task.txt独有的进程,代表这两个UN进程跟其他UN状态的进程不在rw_semaphore等待队列里:

# comm -23 ps-task.txt list-task.txt

0xffff8807bb6d5ee0

0xffff8807fc27bf40

代码语言:javascript复制
备注: 有时因为跟需要排查的rwsem没关系并且处于UN状态的进程比较多的时候对比出来的进程会较多,用search关键字搜索
出进程栈中有对应rwsem地址的进程后再对比也是一个不错的方法。比如:
search -t $address | grep TASK | awk '{print "0x"$4}' | sort >search_$address .txt
comm -23 search_0xffff880009513e60.txt list-task.txt  //$address 为rw_semaphore地址

查看0xffff8807bb6d5ee0进程栈信息结合源码可以排除该进程拿的锁。

再看下0xffff8807fc27bf40进程栈信息可以知道其由于写信号量失败而被置为UN状态:

查看进程0xffff8807fc27bf40对应的mmap_sem信息可以知道其mmap_sem.next的值指向0xffff88001758af70进程对应的&rwsem_waiter.list,也就是0xffff8807fc27bf40跟前面list出来的进程都在等待同一个rw信号量,但是该进程对应的&rwsem_waiter.list却不在链表里。

crash> struct task_struct.mm 0xffff8807fc27bf40

mm = 0xffff8807fa443200

crash>

crash> struct mm_struct.mmap_sem 0xffff8807fa443200

mmap_sem = {

{

count = {

counter = 0xffffffff00000001

},

....

....

wait_list = {

next = 0xffff880009513e60

},

owner = 0x1

}

crash>

分析释放的rw_semaphore实现,可以知道在函数rwsem_wake中是先把进程从等待队列删除然后再唤醒进程:

__rwsem_mark_wake:

因此推断问题可能是进程被从rw_semaphore等待队列删除后没有被正常唤醒导致的。

找到commit e158488be27b157802753a59b336142dc0eb0380修复了一个会导致此类现象的问题。

http://lkml.iu.edu/hypermail/linux/kernel/1903.0/01537.html

Redhat也分别在rhel7.7和rhel7.6修复了该问题:

https://access.redhat.com/solutions/3393611

0 人点赞