linux kernel crash问题分析解决

一,问题场景和环境

系统环境:

redhat6.4 kernel:2.6.32-358

问题:

使用iptables给mangle表添加了一条规则,使用nfqueue做为target。当一个http请求命中这个规则之后,机器直接重启了。偶发性的出了两次问题,但是却在重启的机器上重现不了这个问题。

二,排查

1,查看messages,kernel和dmesg相关日志,未发现有任何异常

2,查看重启前机器的负载,cpu,内存,磁盘io,网络io都正常

3,由于是使用了nfqueue做为target才导致的重启,怀疑是系统的问题,通过现象看应该是iptables的nfqueue导致的问题,而nfqueue用于从内核读取数据包在用户态处理。故具体定位在kernel或者libnetfilter_queue上。

4,通过服务器显示屏幕来看重启的时候会有什么有用的输出,但是服务器在客户的机房,查看太麻烦

5,使用last查看服务器的重启记录,发现一个意外现象,即:机器因为nfqueue重启的那个记录里面有一个crash记录,意思即系统奔溃了,从而导致重启。那就能断定是系统或者kernel crash了。

6,linux系统一般默认都安装配置了kdump,故当 linux 系统内核发生崩溃的时候,可以通过 kdump 等方式收集内核崩溃之前的内存,在/var/crash/日期 目录生成一个转储文件 vmcore。使用crash工具可以分享vmcore文件,来获取kernel crash前的一些重要信息。通过在机器上查找,果然发现了crash相关的vmcore文件。

三,分析vmcore文件

1,安装指定kernel的debuginfo包:
# yum install kernel-debuginfo-2.6.32-358.el6.x86_64

2,使用系统自带的crash命令分析vmcore:

# crash /usr/lib/debug/lib/modules/2.6.32-358.el6.x86_64/vmlinux vmcore
crash 7.1.0-6.el6
Copyright (C) 2002-2014  Red Hat, Inc.
Copyright (C) 2004, 2005, 2006, 2010  IBM Corporation
Copyright (C) 1999-2006  Hewlett-Packard Co
Copyright (C) 2005, 2006, 2011, 2012  Fujitsu Limited
Copyright (C) 2006, 2007  VA Linux Systems Japan K.K.
Copyright (C) 2005, 2011  NEC Corporation
Copyright (C) 1999, 2002, 2007  Silicon Graphics, Inc.
Copyright (C) 1999, 2000, 2001, 2002  Mission Critical Linux, Inc.
This program is free software, covered by the GNU General Public License,
and you are welcome to change it and/or distribute copies of it under
certain conditions.  Enter "help copying" to see the conditions.
This program has absolutely no warranty.  Enter "help warranty" for details.
GNU gdb (GDB) 7.6
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-unknown-linux-gnu"...
WARNING: kernel version inconsistency between vmlinux and dumpfile
      KERNEL: vmlinux
    DUMPFILE: vmcore  [PARTIAL DUMP]
        CPUS: 40
        DATE: Tue Oct 31 11:53:41 2017
      UPTIME: 342 days, 12:15:26
LOAD AVERAGE: 0.00, 0.02, 0.00
       TASKS: 1050
    NODENAME: web_yp_49_202.mobileztgame
     RELEASE: 2.6.32-358.el6.x86_64
     VERSION: #1 SMP Tue Jan 29 11:47:41 EST 2013
     MACHINE: x86_64  (2499 Mhz)
      MEMORY: 128 GB
       PANIC: "BUG: unable to handle kernel NULL pointer dereference at (null)"
         PID: 0
     COMMAND: "swapper"
        TASK: ffff882069324080  (1 of 40)  [THREAD_INFO: ffff881068896000]
         CPU: 5
       STATE: TASK_RUNNING (PANIC)

从crash的输出可以看到kernel崩溃的原因为kernel遇见空指针导致崩溃

bt 命令用于查看系统崩溃前的堆栈等信息

bt命令结果如下:

crash> bt
PID: 0      TASK: ffff882069324080  CPU: 5   COMMAND: "swapper"
 #0 [ffff8800618a3750] machine_kexec at ffffffff81035b7b
 #1 [ffff8800618a37b0] crash_kexec at ffffffff810c0db2
 #2 [ffff8800618a3880] oops_end at ffffffff815111d0
 #3 [ffff8800618a38b0] no_context at ffffffff81046bfb
 #4 [ffff8800618a3900] __bad_area_nosemaphore at ffffffff81046e85
 #5 [ffff8800618a3950] bad_area_nosemaphore at ffffffff81046f53
 #6 [ffff8800618a3960] __do_page_fault at ffffffff810476b1
 #7 [ffff8800618a3a80] do_page_fault at ffffffff8151311e
 #8 [ffff8800618a3ab0] page_fault at ffffffff815104d5
    [exception RIP: nf_queue+152]
    RIP: ffffffff81475718  RSP: ffff8800618a3b60  RFLAGS: 00010207
    RAX: 0000000000000020  RBX: 0000000000000000  RCX: ffff8810638a3c00
    RDX: 0000000000000002  RSI: ffff880959189980  RDI: 0000000000000000
    RBP: ffff8800618a3bd0   R8: 0000000000021773   R9: 0000000000000001
    R10: 000000000000000e  R11: 0000000000000006  R12: ffff880959189980
    R13: 0000000000000000  R14: ffffffff8147e8b0  R15: 0000000000000000
    ORIG_RAX: ffffffffffffffff  CS: 0010  SS: 0018
 #9 [ffff8800618a3bd8] nf_hook_slow at ffffffff81474800
#10 [ffff8800618a3c58] ip_rcv at ffffffff8147ef54
#11 [ffff8800618a3c98] __netif_receive_skb at ffffffff8144819b
#12 [ffff8800618a3cf8] netif_receive_skb at ffffffff8144a578
#13 [ffff8800618a3d38] napi_skb_finish at ffffffff8144a680
#14 [ffff8800618a3d58] napi_gro_receive at ffffffff8144cc29
#15 [ffff8800618a3d78] ixgbe_poll at ffffffffa015e44c [ixgbe]
#16 [ffff8800618a3e68] net_rx_action at ffffffff8144cd43
#17 [ffff8800618a3ec8] __do_softirq at ffffffff81076fb1
#18 [ffff8800618a3f38] call_softirq at ffffffff8100c1cc
#19 [ffff8800618a3f50] do_softirq at ffffffff8100de05
#20 [ffff8800618a3f70] irq_exit at ffffffff81076d95
#21 [ffff8800618a3f80] do_IRQ at ffffffff81516c95
--- <IRQ stack> ---
#22 [ffff881068897db8] ret_from_intr at ffffffff8100b9d3
    [exception RIP: intel_idle+222]
    RIP: ffffffff812d37ae  RSP: ffff881068897e68  RFLAGS: 00000206
    RAX: 0000000000000000  RBX: ffff881068897ed8  RCX: 0000000000000000
    RDX: 00000000000e3cb1  RSI: 0000000000000000  RDI: 00000000379d13ba
    RBP: ffffffff8100b9ce   R8: 0000000000000004   R9: 0000000000000050
    R10: 0069229e5ea9dbfa  R11: 0000000000000000  R12: ffff8800618b15a0
    R13: 0000000000000000  R14: 0069229c2b297a40  R15: ffff8800618b16a0
    ORIG_RAX: ffffffffffffff62  CS: 0010  SS: 0018
#23 [ffff881068897ee0] cpuidle_idle_call at ffffffff81414ef7
#24 [ffff881068897f00] cpu_idle at ffffffff81009fc6

通过bt分析,我们从下到上来看kernel崩溃前的系统调用,定位到kernel崩溃前的一个exception是ip寄存器RIP的异常,而通过dis 命令来看一下该地址的反汇编结果:

crash> dis -l ffffffff81475718
/usr/src/debug/kernel-2.6.32-358.el6/linux-2.6.32-358.el6.x86_64/net/netfilter/nf_queue.c: 221
0xffffffff81475718 <nf_queue+152>:      mov    (%rbx),%r12

故可定位到出现异常的代码段:

# vim /usr/src/debug/kernel-2.6.32-358.el6/linux-2.6.32-358.el6.x86_64/net/netfilter/nf_queue.c +221
215         segs = skb_gso_segment(skb, 0);
216         kfree_skb(skb);
217         if (IS_ERR(segs))
218                 return 1;
219
220         do {
221                 struct sk_buff *nskb = segs->next;
222
223                 segs->next = NULL;
224                 if (!__nf_queue(segs, elem, pf, hook, indev, outdev, okfn,
225                                 queuenum))
226                         kfree_skb(segs);
227                 segs = nskb;
228         } while (segs);
229         return 1;

而通过看skb_gso_segment结构体,可以判断出是因为skb_gso_segment在某些情况下会返回NULL,从而导致如上代码segs->next获取到了空指针,从而导致kernel崩溃。而既然是gso导致的问题,应该可以通过调整系统gso属性来规避这个问题:

# vim /usr/src/debug/kernel-2.6.32-358.el6/linux-2.6.32-358.el6.x86_64/net/core/dev.c +1728
1728 /**
1729  *      skb_gso_segment - Perform segmentation on skb.
1730  *      @skb: buffer to segment
1731  *      @features: features for the output path (see dev->features)
1732  *
1733  *      This function segments the given skb and returns a list of segments.
1734  *
1735  *      It may return NULL if the skb requires no segmentation.  This is
1736  *      only possible when GSO is used for verifying header integrity.
1737  */
1738 struct sk_buff *skb_gso_segment(struct sk_buff *skb, int features)
1739 {
1740         struct sk_buff *segs = ERR_PTR(-EPROTONOSUPPORT);
1741         struct packet_type *ptype;
1742         __be16 type = skb->protocol;
1743         int err;

从网上找到的对应patch如下:

https://patchwork.kernel.org/patch/6615071/

四,问题重现

1,最早发现问题,想要重现的办法是通过如下url访问:curl “t.test.com”,发现重现不了。

2,之后,通过搜索相关TSO/GSO/LRO/GRO相关的资料,觉得有可能是由于发送的数据包太小,导致没有触发相关的数据包分段重组,从而没有导致重现问题。故增大了请求的数据包,通过如下url重现了问题:

# curl “t.test.com/v2/user-manage/css/bootstrap.min.css?test1=sdfsfsdfsdfa&test2_id=2234234234234234234&test_id=50129009890098&test_token=1670056402|_80_m_lxxj1298|1493196793|c726299f2d03b8462764bacf20e2395f|sdfsdfdsfsdffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffsdfsdfsdfdsfsdfhgjgjghjghjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjfhjgjfghjfjfhjjjjjjjjjjjjjjjjjjjjjfffffadfsfsdfsdfsdfsdfsdfdsfdssdfsdfsdfsdfsdfsdf”

iptables相关规则如下:

# ipset create lee hash:ip hashsize 819200 maxelem 100000 timeout 300
# ipset add lee 1.1.1.1 timeout 300
# iptables -t mangle -I PREROUTING -p tcp -m multiport --dports 80,443 -m set --match-set lee src -m string --string t.test.com --algo kmp --from 0 --to 1480 -j NFQUEUE

五,问题结论

linux kernel bug

六,解决办法

1,升级kernel。从patch和源代码可以看出kernel 3.0以后应该fix了这个问题,看了下3.10的kernel代码已经fix

2,使用drop,不再使用nfqueue这个target来添加iptables规则(建议使用这个办法)

3,调整网卡gso相关属性,发现通过关闭lro来解决这个重启问题。具体命令:

# ethtool -K eth0 lro on

LRO简介:

Linux 在 2.6.24 中加入了支持 IPv4 TCP 协议的 LRO (Large Receive Offload) ,它通过将多个 TCP 数据聚合在一个 skb 结构,在稍后的某个时刻作为一个大数据包交付给上层的网络协议栈,以减少上层协议栈处理 skb 的开销,提高系统接收 TCP 数据包的能力。当然,这一切都需要网卡驱动程序支持。

七,参考

https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/7/html/kernel_crash_dump_guide/sect-crash-running-the-utility

https://patchwork.kernel.org/patch/6615071/

https://www.ibm.com/developerworks/cn/linux/l-cn-network-pt/index.html

时间: 2024-10-12 07:38:40

linux kernel crash问题分析解决的相关文章

Linux Kernel CMPXCHG函数分析

最近看到Linux Kernel cmpxchg的代码,对实现很不理解.上网查了内嵌汇编以及Intel开发文档,才慢慢理解了,记录下来以享和我一样困惑的开发者.其实cmpxchg实现的原子操作原理早已被熟知: cmpxchg(void* ptr, int old, int new),如果ptr和old的值一样,则把new写到ptr内存,否则返回ptr的值,整个操作是原子的.在Intel平台下,会用lock cmpxchg来实现,这里的lock个人理解是锁住内存总线,这样如果有另一个线程想访问pt

linux kernel input 子系统分析

Linux 内核为了处理各种不同类型的的输入设备 , 比如说鼠标 , 键盘 , 操纵杆 , 触摸屏 , 设计并实现了一个对上层应用统一的试图的抽象层 , 即是Linux 输入子系统 . 输入子系统的层次结构体如下 从底层到上层 , input 子系统由 设备驱动层 , 核心层 , 以及事件处理层3个部分组成 当一个鼠标移动, 一个按键按下或弹起 , 它都需要从底层设备驱动-->核心层-->事件处理层 -->用户空间 , 层层上报 , 一直到运用程序. 应用这个input  子系统有如下优

Arm Linux Kernel 构建 情景分析

概述 构建一个内核,一般是先配置,后编译.这里以构建 Nexus5 内核为例,代号为 hammerhead. 配置 通常做法是以厂商预置的配置为基础,根据自己需要进行配置.命令: make ARCH=arm hammerhead_defconfig 执行完毕后,"arch/arm/configs/hammerhead_defconfig" 文件会被复制到 ".config" ,作为默认配置. 然后运行以下命令根据自己需要进行配置: make ARCH=arm men

Linux Kernel Netfilter Helper 分析

linux netfilter 与helper相关的hook: 点击(此处)折叠或打开 { .hook = ipv4_conntrack_in, .owner = THIS_MODULE, .pf = PF_INET, .hooknum = NF_IP_PRE_ROUTING, .priority = NF_IP_PRI_CONNTRACK, } { .hook = ipv4_conntrack_help, .owner = THIS_MODULE, .pf = PF_INET, .hooknu

使用Crash工具分析 Linux dump文件【转】

转自:https://blog.csdn.net/bytxl/article/details/45025183 前言 Linux 内核(以下简称内核)是一个不与特定进程相关的功能集合,内核的代码很难轻易的在调试器中执行和跟踪.开发者认为,内核如果发生了错误,就不应该继续运 行.因此内核发生错误时,它的行为通常被设定为系统崩溃,机器重启.基于动态存储器的电气特性,机器重启后,上次错误发生时的现场会遭到破坏,这使得查找 内核的错误变得异常困难. 内核社区和一些商业公司为此开发了很多种调试技术和工具,

linux kernel 字符设备详解

有关Linux kernel 字符设备分析: 参考:http://blog.jobbole.com/86531/ 一.linux kernel 将设备分为3大类,字符设备,块设备,网络设备. 字符设备是指只能一个字节一个字节读写的设备, 常见的外设基本上都是字符设备. 块设备:常见的存储设备,硬盘,SD卡都归为块设备,块设备是按一块一块读取的. 网络设备:linux 将对外通信的一个机制抽象成一个设备, 通过套接字对其进行相关的操作. 每一个字符设备或块设备都在/dev目录下对应一个设备文件.l

Linux内核Crash分析

转载自:http://linux.cn/article-3475-1.html 在工作中经常会遇到一些内核crash的情况,本文就是根据内核出现crash后的打印信息,对其进行了分析,使用的内核版本为:Linux2.6.32. 每一个进程的生命周期内,其生命周期的范围为几毫秒到几个月.一般都是和内核有交互,例如用户空间程序使用系统调用进入内核空间.这时使用的不再是用户空 间的栈空间,使用对应的内核栈空间.对每一个进程来说,Linux内核都会把两个不同的数据结构紧凑的存放在一个单独为进程分配的存储

6 个 Linux 运维典型问题,大牛的分析解决思路在这里

作为一名合格的 Linux 运维工程师,一定要有一套清晰.明确的解决故障思路,当问题出现时,才能迅速定位.解决问题,这里给出一个处理问题的一般思路: 重视报错提示信息:每个错误的出现,都是给出错误提示信息,一般情况下这个提示基本定位了问题的所在,因此一定要重视这个报错信息,如果对这些错误信息视而不见,问题永远得不到解决. 查阅日志文件:有时候报错信息只是给出了问题的表面现象,要想更深入的了解问题,必须查看相应的日志文件,而日志文件又分为系统日志文件(/var/log)和应用的日志文件,结合这两个

嵌入式 Linux开发Kernel移植(三)——Kernel工程Makefile分析

嵌入式 Linux开发Kernel移植(三)--Kernel工程Makefile分析 本文选择三星发布的基于SMDKV210开发板的linux 2.6.35.7版本kernel. 一.Kernel Makefle体系简介 1.Kernel Makefile体系组成 Kernel Makefile体系包含Kconfig和Kbuild两个系统. Kconfig系统 Kconfig 对应的是内核配置阶段,make xxconfig就是在使用Kconfig系统.Kconfig由三部分组成: script