云服务器 virtblk_ioctl gpf 内核crash问题分析

2022-08-10 11:53:58 浏览数 (1)

前情提要

有客户机器频繁出现重启,查看每次的堆栈都是virtio_check_driver_offered_feature访问非法地址的gpf报错,比较像是某个内核bug导致。

看到是fdisk进程触发,向客户了解到,问题机器都是执行了fdisk后跪了。

收集coredump,并根据以上现象着手进行排查。

问题原因

centos7 3.10.0-1160.6.1.el7.x86_64 以下的内核版本,在使用lvm后如果没清理vg后先umount并解挂盘,会导致内核中驱动相关的部分数据结构不完整,导致fdisk命令需要去访问到对应数据结构时,访问非法地址触发重启。

redhat对这个bug的官方说明:

https://access.redhat.com/solutions/4943981

对应修复的内核patch:

https://lore.kernel.org/linux-block/20200430140442.171016-1-stefanha@redhat.com/

分析过程

vmcore分析

堆栈:

代码语言:javascript复制
crash> sys
      KERNEL: /usr/lib/debug/lib/modules/3.10.0-1062.9.1.el7.x86_64/vmlinux
    DUMPFILE: vmcore  [PARTIAL DUMP]
        CPUS: 2
        DATE: Tue Mar 30 12:44:11 2021
      UPTIME: 379 days, 13:03:40
LOAD AVERAGE: 0.29, 0.13, 0.08
       TASKS: 366
    NODENAME: mongodb-38
     RELEASE: 3.10.0-1062.9.1.el7.x86_64
     VERSION: #1 SMP Fri Dec 6 15:49:49 UTC 2019
     MACHINE: x86_64  (2394 Mhz)
      MEMORY: 16 GB
       PANIC: "general protection fault: 0000 [#1] SMP "
crash> bt
PID: 1370   TASK: ffff922804b441c0  CPU: 1   COMMAND: "fdisk"
 #0 [ffff9226746dbae8] machine_kexec at ffffffffba265b24 
 #1 [ffff9226746dbb48] __crash_kexec at ffffffffba322342 
 #2 [ffff9226746dbc18] crash_kexec at ffffffffba322430 
 #3 [ffff9226746dbc30] oops_end at ffffffffba985798 
 #4 [ffff9226746dbc58] die at ffffffffba230a7b 
 #5 [ffff9226746dbc88] do_general_protection at ffffffffba985092 
 #6 [ffff9226746dbcc0] general_protection at ffffffffba984718 
    [exception RIP: virtio_check_driver_offered_feature 16]
    RIP: ffffffffc031f450  RSP: ffff9226746dbd70  RFLAGS: 00010282
    RAX: ffff922498d6a300  RBX: ffff922498d6a180  RCX: dead000000000200
    RDX: 0000000000005331  RSI: 0000000000000007  RDI: ffff922498d6a180
    RBP: ffff9226746dbd70   R8: ffffffffbae4d900   R9: ffffffffc089dfb0
    R10: 00007f56af4ad7b8  R11: 0000000000000246  R12: ffff92280f34cd00
    R13: 000000000000001d  R14: 0000000000005331  R15: 0000000000000000
    ORIG_RAX: ffffffffffffffff  CS: 0010  SS: 0018
 #7 [ffff9226746dbd78] virtblk_ioctl at ffffffffc0498b4c  [virtio_blk]
 #8 [ffff9226746dbdb0] __blkdev_driver_ioctl at ffffffffba562a75 
 #9 [ffff9226746dbdc0] dm_blk_ioctl at ffffffffc089e024  [dm_mod]
#10 [ffff9226746dbe10] blkdev_ioctl at ffffffffba5634ba 
#11 [ffff9226746dbe70] block_ioctl at ffffffffba48a8b1 
#12 [ffff9226746dbe80] do_vfs_ioctl at ffffffffba45fb40 
#13 [ffff9226746dbf00] sys_ioctl at ffffffffba45fde1 
#14 [ffff9226746dbf50] system_call_fastpath at ffffffffba98dede 
    RIP: 00007f56af1db2b7  RSP: 00007fff1ee928e8  RFLAGS: 00000206
    RAX: 0000000000000010  RBX: 0000000001191310  RCX: ffffffffba98de21
    RDX: 0000000000000000  RSI: 0000000000005331  RDI: 0000000000000005
    RBP: 0000000001191030   R8: 0000000000000001   R9: 00007f56af4ad7b8
    R10: 00007f56af4ad7b8  R11: 0000000000000246  R12: 0000000000000000
    R13: 0000000001191550  R14: 0000000000000005  R15: 0000000000009bef
    ORIG_RAX: 0000000000000010  CS: 0033  SS: 002b

踩内存的地址:

代码语言:javascript复制
crash> dis -rl ffffffffc031f450
0xffffffffc031f440 <virtio_check_driver_offered_feature>:       nopl   0x0(%rax,%rax,1) [FTRACE NOP]
0xffffffffc031f445 <virtio_check_driver_offered_feature 5>:     push   %rbp
0xffffffffc031f446 <virtio_check_driver_offered_feature 6>:     mov    0xa0(%rdi),%rcx
0xffffffffc031f44d <virtio_check_driver_offered_feature 13>:    mov    %rsp,%rbp
0xffffffffc031f450 <virtio_check_driver_offered_feature 16>:    mov    0x90(%rcx),�x

从偏移量

推导出踩内存的位置是vdev->dev.driver:

查看rdi和rcx:

RCX: dead000000000200

RDI: ffff922498d6a180

看到driver确实跪了:

往上追溯看看,是不是传过来的block_device就有问题?

从栈里找到block_device地址(省略找地址过程):

代码语言:javascript复制
crash> block_device.bd_disk ffff92280f34cd00
  bd_disk = 0xffff9227e7a75800
crash> gendisk.disk_name,private_data 0xffff9227e7a75800
  disk_name = "vdc000000000000000000000000000000000000000000000000000000c"
  private_data = 0xffff922498d6a300
crash> virtio_blk.vdev 0xffff922498d6a300
  vdev = 0xffff922498d6a180

看到地址能和rdi传进来的vdev对上,访问的是vdc盘。

但是,我们发现vdc盘,在系统里找不到了:

这是什么情况?

复现问题

通过流程工具查询,查到客户提供的实例有解挂盘的动作(内部工具查询,此处不上图)。

基于目前信息,我们知道,客户机器上做了lvm(通过堆栈中的dm_blk_ioctl和dev中dm-0可以看出),fdisk命令在访问dm上的virtio_driver数据结构时跪了,客户在出问题之前,做过解挂盘。

基于以上信息,购买一台同样内核版本的机器,挂载两块盘,做好lvm后直接umount再解挂,按照此步骤去复现,发现可以复现出来:

至此问题触发流程明确。

验证解决

patch是在centos7 3.10.0-1160.6.1.el7.x86_64版本合入的。

再来一把,验证升级后的内核可以解决此问题:

解决方案

1. 在不重启不升级内核的情况下,规避方案:

通过云审计https://console.cloud.tencent.com/cloudaudit,查一下有哪些机器做过卸载云盘动作。

如果确认该机器使用了lvm,就切勿再执行fdisk操作。

且后续如果在用lvm的机器上需要解挂盘,确保清理掉lvm相关数据结构后(lvremove vgremove pvremove,但是这么搞数据也没了),再umount后解挂盘。

2. 彻底解决的话,还是需要升级内核。

0 人点赞