鲲鹏社区首页
中文
注册
我要评分
文档获取效率
文档正确性
内容完整性
文档易理解
在线提单
论坛求助

案例:应用系统卡死

问题现象

某软件在服务器运行,出现卡死问题。

定位过程

  1. 配置kdump服务,配置内核参数。
  2. 复现问题,确认生成vmcore文件,使用crash调试,如下图所示,确认空指针问题。

  3. 查看报错堆栈,如下图所示,怀疑“inet_sock_destruct”函数出错。

  4. 反汇编“inet_sock_destruct”,如下图所示。

  5. 查看“inet_sock_destruct”函数源码,如下图所示。

    结合汇编和源码分析可知,x2寄存器为空指针地址,意味着“__skb_dequeue”为空,但前面业务是有做“__skb_dequeue”是否为空的判断,按照正常业务逻辑,不应该进入kfree_skb函数。这说明同时有其他线程在操作“__skb_dequeu”e,在本次线程完成“__skb_dequeue”为非空判断后,其他线程操作使“__skb_dequeue”变为空,导致本次线程后续操作报错。

  6. 为进一步分析该问题,需要了解还有其他什么线程会对“__skb_dequeue”进行操作。这需要在内核增加调试信息,保存每次对__skb_dequeue操作时的堆栈信息。如下图所示,修改代码,增加保存对“__skb_dequeue”操作时的堆栈信息。

  7. 重新编译内核,运行程序,生成新的vmcore文件,使用Crash调试,按照上述定位思路再定位。

  8. 分析堆栈调用逻辑为:tcp_done > inet_csk_destroy_sock > sk_stream_kill_queues > kfree_skb > skb_release_all
  9. 重新运行,生成第三份vmcore文件,使用crash调试,按照上述定位思路再定位。

  10. 从两次vmcore文件的分析,确认有两条路径同时操作“__skb_dequeue”,具体调用关系如下图所示。

    当多核处理不同逻辑时,可能存在对“__skb_dequeue”操作不同步的情况,从而导致队列被重复释放,导致死机问题出现。

  11. 为保证操作的一致性,不出现重复释放的问题,在释放操作sk_free前入加内存屏障,重新编译内核,运行,确认问题解决。
    1
    2
    3
    4
    5
    6
    7
    8
    void __sock_wfree(struct sk_buff *skb)
    {
    struct sock *sk = skb->sk;
    if (refcount_sub_and_test(skb->truesize, &sk->sk_wmem_alloc)) {
    smp_rmb(); // 添加内存屏障验证问题解决
    __sk_free(sk);
    }
    }
    
  12. 后续排查内核社区,发现社区在5.1版本在“refcount_sub_and_test”函数中加了内存屏障修复相关问题,也可以通过更新内核版本解决该问题。