# 案例：应用系统卡死

#### 问题现象

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


#### 定位过程

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”函数中加了内存屏障修复相关问题，也可以通过更新内核版本解决该问题。
