前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >crash分析rw_semaphore引发的系统hung问题

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

原创
作者头像
cdh
修改2021-09-29 12:12:36
2.8K4
修改2021-09-29 12:12:36
举报
文章被收录于专栏:笔记+

该问题发生于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

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档