LINUX KERNEL SPINLOCK使用不当的后果
LINUX KERNEL SPINLOCK使用不当的后果
spinlock(自旋锁)是内核中最常见的锁,它的特点是:等待锁的过程中不休眠,而是占着CPU空转,优点是避免了上下文切换的开销,缺点是该CPU空转属于浪费,spinlock适合用来保护快进快出的临界区。
spinlock有很多限制条件,其中最重要的是,持有spinlock的CPU不能被抢占,持有spinlock的代码不能休眠。如果违反,会发生死锁,后果很严重。持有spinlock的代码不能休眠,这一条是开发者编写内核程序使用spinlock的时候要人工保证的。而持有spinlock的CPU不能被抢占是由spinlock的API本身提供保证,出于效率的考虑,spinlock的API提供了多种选择,对抢占的防止程度也不一样,开发者在选用的时候需要谨慎,下文对此详细展开。
Linux内核提供了多种spinlock的API,其中最常用的是:
spin_lock/spin_unlock— 禁止内核抢占spin_lock_irq/spin_unlock_irq— 禁止内核抢占并屏蔽中断spin_lock_irqsave/spin_unlock_irqrestore— 禁止内核抢占并屏蔽中断,事先保存中断屏蔽位并事后恢复原状
spin_lock()禁止了内核抢占,但是没有屏蔽中断,意味着持有该spinlock的CPU有可能被中断抢占。如果你的某段内核代码选用了spin_lock(),就必须保证这段代码不会被任何中断处理程序调用,否则就会发生死锁(参见后文的一个实际发生的案例)。如果某段内核代码有可能被中断处理程序调用,那就只能选择spin_lock_irq或spin_lock_irqsave。
下面是一个刚发生的实际案例,SLES11 SP4的系统失去响应,kdump生成了vmcore,分析过程中发现以下backtraces揭示了原因:
crash64> bt -c 2
PID: 47 TASK: ffff880230c78400 CPU: 2 COMMAND: "kswapd0"
#0 [ffff88023fa46e40] crash_nmi_callback at ffffffff81024bcf
#1 [ffff88023fa46e50] notifier_call_chain at ffffffff8146d6e7
#2 [ffff88023fa46e80] __atomic_notifier_call_chain at ffffffff8146d72d
#3 [ffff88023fa46e90] notify_die at ffffffff8146d77d
#4 [ffff88023fa46ec0] default_do_nmi at ffffffff8146ad13
#5 [ffff88023fa46ee0] do_nmi at ffffffff8146ae08
#6 [ffff88023fa46ef0] restart_nmi at ffffffff8146a295
[exception RIP: _raw_spin_lock+21]
RIP: ffffffff81469795 RSP: ffff88023fa43738 RFLAGS: 00000283
RAX: 0000000000002b41 RBX: ffff88023337fc00 RCX: 00000000000000d0
RDX: 0000000000002b3f RSI: ffff88023fa437d4 RDI: ffffffff81a02700
RBP: 0000000000000001 R8: 0000000000000000 R9: ffff88023fa43740
R10: ffff88023ffd95b8 R11: ffff88023ffd9520 R12: ffff88023337fc00
R13: 0000000000000001 R14: ffff88023337fce0 R15: 0000000000000008
ORIG_RAX: ffffffffffffffff CS: 0010 SS: 0018
--- <NMI exception stack> ---
#7 [ffff88023fa43738] _raw_spin_lock at ffffffff81469795
#8 [ffff88023fa43738] __shrink_dcache_sb at ffffffff81176b36
#9 [ffff88023fa437b8] prune_dcache at ffffffff81176d82
#10 [ffff88023fa43808] shrink_dcache_memory at ffffffff81176e88
#11 [ffff88023fa43818] shrink_slab at ffffffff81110874
#12 [ffff88023fa438b8] do_try_to_free_pages at ffffffff81111c43
#13 [ffff88023fa43928] try_to_free_pages at ffffffff81112072
#14 [ffff88023fa439c8] __alloc_pages_slowpath at ffffffff81104a6f
#15 [ffff88023fa43af8] __alloc_pages_nodemask at ffffffff81105079
#16 [ffff88023fa43b98] alloc_pages_current at ffffffff8113da6e
#17 [ffff88023fa43bd8] bnx2x_alloc_rx_sge at ffffffffa055d484 [bnx2x]
#18 [ffff88023fa43c18] bnx2x_fill_frag_skb at ffffffffa055d75e [bnx2x]
#19 [ffff88023fa43cb8] bnx2x_tpa_stop at ffffffffa055da86 [bnx2x]
#20 [ffff88023fa43d18] bnx2x_rx_int at ffffffffa056084b [bnx2x]
#21 [ffff88023fa43e48] bnx2x_poll at ffffffffa05613b4 [bnx2x]
#22 [ffff88023fa43e88] net_rx_action at ffffffff813adada
#23 [ffff88023fa43ed8] __do_softirq at ffffffff8106925f
#24 [ffff88023fa43f48] call_softirq at ffffffff81472a5c
#25 [ffff88023fa43f60] do_softirq at ffffffff81004695
#26 [ffff88023fa43f90] smp_apic_timer_interrupt at ffffffff81026fd8
#27 [ffff88023fa43fb0] apic_timer_interrupt at ffffffff814721f3
--- <IRQ stack> ---
#28 [ffff880230c7bad8] apic_timer_interrupt at ffffffff814721f3
[exception RIP: _raw_spin_trylock]
RIP: ffffffff81469750 RSP: ffff880230c7bb88 RFLAGS: 00000246
RAX: ffff8802c63aac40 RBX: ffffffff81469c0e RCX: ffff8802c63aad00
RDX: ffff880230c7bfd8 RSI: ffff880230c78400 RDI: ffff8802c63aac18
RBP: ffff8802c63aac18 R8: ffff880230c7a000 R9: 0000000000000000
R10: ffff88023fa509a0 R11: ffffffff81051970 R12: ffffffff814721ee
R13: ffffffff81051970 R14: ffffffff81469c0e R15: ffff880230c7bb80
ORIG_RAX: ffffffffffffff10 CS: 0010 SS: 0018
#29 [ffff880230c7bb88] __shrink_dcache_sb at ffffffff81176bec
#30 [ffff880230c7bc08] prune_dcache at ffffffff81176d82
#31 [ffff880230c7bc58] shrink_dcache_memory at ffffffff81176e88
#32 [ffff880230c7bc68] shrink_slab at ffffffff81110874
#33 [ffff880230c7bd08] kswapd_shrink_zone at ffffffff81111086
#34 [ffff880230c7bd68] balance_pgdat at ffffffff811115de
#35 [ffff880230c7be78] kswapd at ffffffff81111980
#36 [ffff880230c7bee8] kthread at ffffffff81084946
#37 [ffff880230c7bf48] kernel_thread_helper at ffffffff81472964
我来解释一下,上面的backtraces意思是:CPU 2上正在运行的进程是”kswapd0″(kswapd0是负责swapping的内核线程),它正在压缩dcache以便腾出一些空闲内存,当它执行到__shrink_dcache_sb()的时候被一个中断抢占了CPU,(注意被中断抢占的进程不会离开当前CPU,不会有机会到其它CPU上运行,只能等中断处理结束之后把CPU交还给它),中断处理程序是bnx2x驱动模块(注意看[],表示的是内核模块),它发现内存不够,于是自动清理内存,最终也走到了压缩dcache这一步,也去调用__shrink_dcache_sb(),但是__shrink_dcache_sb()的临界区受到spinlock保护,见下面源代码第0823行,这个名为dcache_lru_lock的spinlock刚才已经被”kswapd0″进程持有了,所以中断处理程序不可能抢到,问题是持有dcache_lru_lock的”kswapd0″进程又被中断抢占了CPU,不可能继续运行,也就没机会释放掉dcache_lru_lock,这就陷入了死锁状态。
// SLES11 SP4: kernel 3.0.101-71, fs/dcache.c
0814 static void __shrink_dcache_sb(struct super_block *sb, int *count, int flags)
0815 {
0816 /* called from prune_dcache() and shrink_dcache_parent() */
0817 struct dentry *dentry;
0818 LIST_HEAD(referenced);
0819 LIST_HEAD(tmp);
0820 int cnt = *count;
0821
0822 relock:
0823 spin_lock(&dcache_lru_lock);
0824 while (!list_empty(&sb->s_dentry_lru)) {
0825 dentry = list_entry(sb->s_dentry_lru.prev,
0826 struct dentry, d_lru);
0827 BUG_ON(dentry->d_sb != sb);
0828
0829 if (!spin_trylock(&dentry->d_lock)) {
0830 spin_unlock(&dcache_lru_lock);
0831 cpu_relax();
0832 goto relock;
0833 }
...
根本原因在于,既然__shrink_dcache_sb()选用了spin_lock(),就意味着设计者认为它不会被中断处理程序调用,因为spin_lock()不屏蔽中断,是不能防止中断抢占的,只要中断处理程序不调用__shrink_dcache_sb(),死锁就不会发生;如果要让__shrink_dcache_sb()可以被中断处理程序调用,那就不能选用spin_lock(),而应该用spin_lock_irq或spin_lock_irqsave。这个案例中的问题出在bnx2x驱动程序中,它在bnx2x_alloc_rx_sge() 中调用alloc_pages()时不恰当地使用了GFP_KERNEL标志,实际上应该使用GFP_ATOMIC标志,这样alloc_pages()就不会试图去主动回收内存、也就不会最终调用__shrink_dcache_sb()了。此bug记载在SUSE的bsc#975358中,在kernel 3.0.101-77中得以修复。
LINUX KERNEL SPINLOCK使用不当的后果的更多相关文章
- spinlock in linux kernel
spinlock in linux kernel 作为一种锁机制, spinlock可以制造一段临界区, 同一时刻只有一个线程能进入这个临界区, 从而达到保护数据的目的. semaphore, mut ...
- Intel 80x86 Linux Kernel Interrupt(中断)、Interrupt Priority、Interrupt nesting、Prohibit Things Whthin CPU In The Interrupt Off State
目录 . 引言 . Linux 中断的概念 . 中断处理流程 . Linux 中断相关的源代码分析 . Linux 硬件中断 . Linux 软中断 . 中断优先级 . CPU在关中断状态下编程要注意 ...
- Linux Kernel 排程機制介紹
http://loda.hala01.com/2011/12/linux-kernel-%E6%8E%92%E7%A8%8B%E6%A9%9F%E5%88%B6%E4%BB%8B%E7%B4%B9/ ...
- Install Linux Kernel - AT91SAM9260EK
两.AT91SAM9260EK 2.1下载 介绍页: http://www.at91.com/linux4sam/bin/view/Linux4SAM/LegacyLinuxKernel 下载页: a ...
- Linux kernel的中断子系统之(三):IRQ number和中断描述符
返回目录:<ARM-Linux中断系统>. 总结: 二描述了中断处理示意图,以及关中断.开中断,和IRQ number重要概念. 三介绍了三个重要的结构体,irq_desc.irq_dat ...
- Linux kernel的中断子系统之(六):ARM中断处理过程
返回目录:<ARM-Linux中断系统>. 总结:二中断处理经过两种模式:IRQ模式和SVC模式,这两种模式都有自己的stack,同时涉及到异常向量表中的中断向量. 三ARM处理器在感知到 ...
- 从基本理解到深入探究 Linux kernel 通知链(notifier chain)【转】
转自:https://blog.csdn.net/u014134180/article/details/86563754 版权声明:本文为博主原创文章,未经博主允许不得转载.——Wu_Being ht ...
- linux kernel的中断子系统之(三):IRQ number和中断描述符【转】
转自:http://www.wowotech.net/linux_kenrel/interrupt_descriptor.html 一.前言 本文主要围绕IRQ number和中断描述符(interr ...
- [轉]Exploit The Linux Kernel NULL Pointer Dereference
Exploit The Linux Kernel NULL Pointer Dereference Author: wztHome: http://hi.baidu.com/wzt85date: 20 ...
随机推荐
- WeX5 IDE 手机移动开发+JAVA +大数据
http://www.wex5.com/wex5/?qb360=wex501=3534 前端技术 HTML,CSS,JavaScript,EasyUI,Jquery,HTML5,CSS3,Jquery ...
- Oracle数据库导出导入
需求为将数据库A中的数据导出为*.dmp文件.然后将*.dmp文件导入到数据库B. 1.导出数据库A 在cmd窗体输入下面命令: 导出所有数据库 exp username/password@数 ...
- android 通用菜单条实现(一)
一.前言介绍 直奔主题啦,非常多Android app都有菜单条.菜单条除了背景图片.图标的不同外,布局基本一致.大致能够分为三部分:菜单条的左側区域.菜单条中间区域.菜单条右側区域. 为了考虑代码的 ...
- luogu2278 [HNOI2003]操作系统
题目大意 写一个程序来模拟操作系统的进程调度.假设该系统只有一个CPU,每一个进程的到达时间,执行时间和运行优先级都是已知的.其中运行优先级用自然数表示,数字越大,则优先级越高.如果一个进程到达的时候 ...
- Android View的onTouch和onClick和onLongClick事件
这三个事件的调用顺序是: onTouch->onLongClick->onClick 先看这三个事件的处理函数: public boolean onTouch(View v, Motion ...
- 源码中修改Android的开机画面和动画【转】
本文转载自:http://blog.csdn.net/dddxxxx/article/details/54343976 参照文章:http://blog.csdn.net/a345017062/art ...
- 最小割板子题——[USACO5.4]奶牛的电信
今天邱神给我们讲了图论,还讲了一下网络流算法.自己找了一个洛谷板子题. 题目描述 农夫约翰的奶牛们喜欢通过电邮保持联系,于是她们建立了一个奶牛电脑网络,以便互相交流.这些机器用如下的方式发送电邮:如果 ...
- 123D
后缀数组+单调栈 看了好长时间,最后看了张神的程序才搞懂 意思就是求所有子串*n*(n+1)/2 n是子串出现次数 事实上,lcp可以看成宽度为1,高度为lcp值的长方形,所有lcp放在一起就是一堆长 ...
- VS2010中文注释带红色下划线的解决方法
环境:Visual Studio 2010 问题:代码中出现中文后会带下划线,很多时候感觉很不舒服.找了很久的原因没找到,后来无意中在VisualAssist X里找到了解决办法. 1.安装完Visu ...
- .Net Core添加分布式Session
一.Session HTTP是一个无状态协议,Web服务器将每一个请求都视为独立请求.并且不保存之前请求中用户的值. Session 状态是ASP.NET Core提供的一个功能,它可以在用户通应用访 ...