前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >一个Linux死锁信息分析

一个Linux死锁信息分析

作者头像
猿哥
发布2019-06-17 18:48:42
1.4K0
发布2019-06-17 18:48:42
举报
文章被收录于专栏:Web技术布道师Web技术布道师

这两天在遇到一个死锁的问题,信息大概是这样的:

代码语言:javascript
复制
======================================================
WARNING: possible circular locking dependency detected
...
------------------------------------------------------
test_dummy/827 is trying to acquire lock:
(____ptrval____) (kn->count#4){++++}, at: kernfs_remove_by_name_ns+0x5c/0xb8
but task is already holding lock: 
(____ptrval____) (xxxxx_mutex){+.+.}, at: xxxxxxxxxx+0x30/0xb8
which lock already depends on the new lock.
the existing dependency chain (in reverse order) is:
-> #2 (xxxxx_mutex){+.+.}:
       lock_acquire+0xd4/0x250
       __mutex_lock+0x8c/0x868
       mutex_lock_nested+0x3c/0x50
...
        work_pending+0x8/0x14

-> #1 (&hw->mutex){+.+.}:
       lock_acquire+0xd4/0x250
       __mutex_lock+0x8c/0x868
...
        el0_svc+0x8/0xc

-> #0 (kn->count#4){++++}:
        __lock_acquire+0x10ac/0x11d0
        lock_acquire+0xd4/0x250
        __kernfs_remove+0x2f4/0x348
        kernfs_remove_by_name_ns+0x5c/0xb8
...
        work_pending+0x8/0x14

other info that might help us debug this:

Chain exists of:
  kn->count#4 --> &hw->mutex --> xxxxx_mutex

Possible unsafe locking scenario:
       CPU0                    CPU1
       ----                    ----
  lock(xxxxx_mutex);
                               lock(&hw->mutex);
                               lock(xxxxx_mutex);
  lock(kn->count#4);
 *** DEADLOCK ***

2 locks held by test_dummy/827:
 #0: (____ptrval____) (&hw->mutex){+.+.}, at: xxxxxxxxx+0x34/0x98
 #1: (____ptrval____) (xxxxx_mutex){+.+.}, at: xxxxxxxxxx+0x30/0xb8

这个事情很奇怪,我不觉得它提出来的Possible unsafe locking scenario真的会死锁啊。

我个人原来一直没有看过Linux的死锁跟踪机制,为了看懂这个问题,我先速成一下,整理一下笔记。内核代码基于5.2-rc3。

查了一下git历史,这个死锁跟踪功能最初是Ingo Molnar 2006年引入的。网上有人说第一个版本就解决掉了大部分Linux内核的死锁问题。不过它的设计目标不是用于产品(release)版本的,对性能有不小的影响,所以一般用于内部测试阶段。

Linux内核的lockdep-design.txt对这个东西有介绍,但我觉得文档写得很烂,前后矛盾,语焉不详,还不如直接看代码。不过这个代码也很不规整,基本上都是细节,我也耗不起这个时间。所以我还是聚焦到看个整体,然后重点搞清那个错误输出什么意思。

从文档建立的概念再去对了一下代码,大概的原理是这样的:给每种类型的锁都定义一个class(相当于锁的类型,比如所有的mutex就是一个class),为每个class定义一组rules(非抽象概念,都是具体插入的不同代码),然后根据相同class的锁有没有违反rules的行为(比如A-B, B-A互锁,在上了spinlock的情况下开中断之类的),由此判断锁设计是否有问题。由于每次上锁解锁的过程都要加上一堆的rules判断,这个对性能的影响是摆在那里的,但测试阶段能把问题挖出来,到正式产品中出问题的可能性也不大了,所以用于测试是个很好的方案。

从接口上看,这个功能主要通过在锁初始化代码上加静态定义定义那个class,然后在第一次使用的时候注册到子系统中。这是默认的情况,如果你要对你的锁做专门处理,也可以通过lockdep_set_class()自行创建一种新的class。很多复杂的子系统都自己设置自己的class,比如inode,各种文件系统等。

之后在上锁和解锁的代码里加lock_acquire()和lock_release(),建立那锁类型和lockdep_map对象的映射,然后就在这些流程里进行死锁Pattern的匹配,检测出有可能的死锁场景来。

为了增加检测的机会,在部分和锁有关的代码中,还会主动插入might_lock增加检查,这个本质是主动把lock_acquire和lock_release调一次,就是为了检查而做的。

除了这些基本接口,lockdep还有可以用来检查某个锁肯定已经上了的lockdep_assert_is_held(),或者确认锁不会被中途释放的lockdep_*pin_lock()等辅助性的函数。

具体的检查算法都是细节,大概的意思就是判断依赖关系是否有循环(注1),是否重复上锁和是否在不安全上下文中上安全的锁(比如开着中断上spinlock,这会引起spinlock进入中断,并在中断中再次spinlock,导致死锁)。我先忽略这些细节,重点解决两个问题:

第一,错误输出中,每个锁后面{+.+.}是什么意思。从代码上看(吐一句槽:这个代码写得极其晦涩,看着难受),这是4个上下文的状态标记。上下文分别是:

代码语言:javascript
复制
LOCK_USED_IN_HARDIRQ
LOCK_USED_IN_HARDIRQ_READ
LOCK_USED_IN_SOFTIRQ
LOCK_USED_IN_SOFTIRQ_READ

这个标记记录的是上锁的时候是否“曾经”在对应的状态过,具体的标记表示这个上下文当时的状态:

代码语言:javascript
复制
. 状态关,非状态上下文(也可能就没有发生过)
- 状态关,状态上下文
+ 状态开,非状态上下文
? 状态开,状态上下文

“状态”对应上面那四个上下文标记提到的中断状态,比如第一个标记是+,就表示hardirq开,非hardirq上下文。

而例子中的{+.+.},表示这个锁“在线程上下文没关软硬中断的情况下上过锁(非读写锁)”,基本上可以认为全是线程之间的交互。

第二个问题是那个“Chain exists of”打印的是什么东西。这个打印并非打印一个任意长度的列表,它只打印三个对象:source,parent,target。

source是检查的时候本线程正要上的锁

parent是当前线程上一个拿着的锁

target是发现在本线程中锁住了,但以前曾经依赖过source的锁。

这样,我们就可以面对本文开始的问题了:这个场景为什么会死锁?

我觉得这主要是打印的锅。其实这个死锁场景想表达的是:你在给kn->count#4上锁,但你已经给xxxxx_mutex上锁了,但之前我们发现过你在上了kn->count#4的情况下,给xxxxx_mutex上过锁,所以,这有可能是一个循环依赖。

这里报错是没有问题的,代码也应该修改,但lockdep的打印是误导的,基本上可以认为是个Bug,但如果你能看得懂source, parent,target的意思,这个不影响你使用就是了。

注1:lockdep用的搜索算法叫bfs,我猜了很久都没有搞明白是个什么算法,后来无意中看了一个Patch,才发现这就是简单的“Broad-First Search”。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-06-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 PHP技术大全 微信公众号,前往查看

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

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

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