[qemu+kvm]: trap 寄存器脱敏优化方法

  1. 敏感寄存器优化:
  • SYS_ICC_SGI1R_EL1
  1. 结论:无法脱敏
  2. 原因:在VHE下,对icc_sgixr的访问需要trap; 而且gicv4.1,guest需要写icc_sgixr trap到hypervisor,hypervisor通过写GITS_SGIR,触发vsgi。
  3. 优化方法:既然无法脱敏,只能减少ipi.
    修改trace ,打印guest出写入sgi1r的值,发现ipi 0、 ipi 1、 ipi 5 trap次数比较多

    IPI0: Rescheduling interrupts
    IPI1:Function call interrupts
    IPI5:IRQ work interrupts
    ipi0和ipi5 通过给guest隔离核心可以减少ipi,ipi1 需要继续抓:通过抓smp func的入口获取。

发现当guest cpu隔离后, host主要收到的ipi 1 的func是sched_ttwu_pending

使能event后,可以抓某一个cpu的event

c 复制代码
cat per_cpu/cpu1/trace_pipe > /tmp/log &

目前已经抓的event:

c 复制代码
查看cpu都发什么ipi:             events/kvm/kvm_handle_sys_reg/
查看cpu上收到了那些ipi func     events/csd/csd_function_entry/
查看cpu都发了哪些ipi func       events/ipi/ipi_send_cpu/

guest kernel 添加启动参数:isolcpus=domain,managed_irq,1-3 nohz_full=1-3 rcu_nocbs=1-3

同时在host进行棒核:qemu-affinity pid -k 1 2 3 4 5 6 7 8, 这样ipi 1在 1-3核心上少了很多: CPU0 CPU1

c 复制代码
           CPU0       CPU1        CPU2       CPU3       CPU4       CPU5       CPU6       CPU7     
IPI1:     35145       7843       7841       7841      34994      36643      36925      38220       Function call interrupts

通过抓取trace: 目前隔离收到大量ipi 1 func:do_noting 和 func: tlb_remove_table_smp_sync;

当开启cyclitest时,隔离核又发送多个ipi 0:resched_curr_lazy

  • do_noting 优化:

    经排查是:

    路径:coredump(usermode )->bpf_int_jit_compile->kick_all_cpus_sync->do_nothing

    原因:mirobot_teleoperate.service 不断crash,coredump不断调用bpf抓现场,造成频繁ipi广播。

    解决方法:systemctl disable mirobot_teleoperate.service

  • tlb_remove_table_smp_sync 优化

    原因:guest经常收到此 ipi,原因是因为当有umap 等修改页表操作发生时,需要flush tlb, 在flush tlb前,需要全局发送ipi,并等待响应。这和kernel 读取页表有关:

    当cpu0 想free 修改页表时, cpu 1 可能正在读取页表,由于kernel读取页表采用无锁机制(为了快),导致free 页表前必须发送全局ipi, 让所有core都处于local irq enable 状态,再free页表。 因为读取页表只能在local_irq_disable后读取。

    无IPI:

c 复制代码
CPU1 (gup_fast)            CPU0 (free pagetable)
-----------------          ---------------------
local_irq_disable()
read page table
                           free(page_table)  💥
read freed memory 💥
local_irq_enable()

有IPI:

c 复制代码
CPU1 (gup_fast)            CPU0
-----------------          -------------------------
local_irq_disable()
read page table
                           send IPI ───────▶ (blocked)
read page table
local_irq_enable()
                           IPI delivered ✔
                           IPI handler runs
                           smp_call_function returns ✔
                           free(page_table) ✔

优化方法: 只能尽量减少unmap 修改页表操作。

  1. guest wfi/wfe 脱敏
c 复制代码
在wfi和wfe 不脱敏时, guest经常执行wfi陷入到kvm, 并且trap时间过长,根因是:vfio-platform 设备中断会发送kick vcpu
vfio_intp_interrupt
->vfio_mmap_set_enabled (unmap smmu table)
->memory_region_transaction_commit 
    ->address_space_update_topology_pass (执行region_del)
->MEMORY_LISTENER_CALL_GLOBAL(commit, Forward);
    ->kvm_region_commit
    ->need_inhibit = true
    ->accel_ioctl_inhibit_begin
      ->accel_has_to_wait
       ->cpus_kick_thread (向每个vcpu发送siguser1)
       
 此时vcpu在wfi trap ,在wfi trap while循环里判断current是否有signal pending,如果有退出循环,进入el0,代码简单不帖了。
 这样造成wfi trap进入el0,代码路径增加,trap时间增长。

wfi/wfe可脱敏:

设置hcr.twi HCR.twe ,代码:gerrit.evad.mioffice.cn

如何debug:

偶尔host隔离core后+启动nohz_full后, host无法进入nohz_full,如何debug?

  • 静态trace:cat per_cpu/cpu1/trace_pipe , 查看什么业务在core上运行,大概率是timer
  • 查看timer情况:cat /proc/timer_list, 查看对应core的active timer 是哪个,有没有tick_stop
  • 查看cfs: 打开CONFIG_SCHED_DEBUG=y,查看cat /sys/kernel/debug/sched/debug ,观察cfs rq->nr_running, 和available task list
  1. guest OS 调度优化
    guest kernel 启动参数 :isolcpus=domain,managed_irq,1-3 nohz_full=1-3 rcu_nocbs=1-3
    Host kernel 启动参数:isolcpus=domain,managed_irq,1-3 rcu_nocbs=1-3 kvm-arm.vgic_v4_enable=1 nohz_full=1-3

guest运行:

c 复制代码
echo 1 > /sys/kernel/debug/tracing/events/irq/irq_handler_entry/enable
echo 1 > /sys/kernel/debug/tracing/events/timer/hrtimer_expire_entry/enable  

echo 1 > /sys/kernel/debug/tracing/events/timer/hrtimer_expire_entry/enable
echo 1 > /sys/kernel/debug/tracing/events/irq/irq_handler_entry/enable


cyclitest -a 1 -i 1000 -p95 -t1

cat per_cpu/cpu1/trace_pipe:

  1. 情况一:cpu idle 唤醒时间长:
    cyclitest 1ms触发一次,但是cpuidle唤醒要3ms:

原因:kernel enable nohz后,timer会迁移到非nohz core:

c 复制代码
__hrtimer_start_range_ns
  ->switch_hrtimer_base
    ->get_target_base
                   ->切换新的timer base。将timer切换到其他core

这样导致timer中断发生在其他core,然后通过IPI 对nohz_core唤醒,这也解释了,为什么log里都是IPI唤醒cpu idle,而不是arch_timer。

解决方法:禁止timer migration

guest中设置:

c 复制代码
echo 0 >  /proc/sys/kernel/timer_migration

或者修改guest代码:

c 复制代码
kernel/time/timer.c 中:
timers_migration_enabled=0
相关推荐
玖石书21 分钟前
WSL2 手动安装 Ubuntu 24.04 到指定磁盘位置
linux·运维·ubuntu·wsl
库玛西1 小时前
深入浅出传输层:UDP 与 TCP 协议全景指南
linux·服务器·网络·c++·笔记·tcp/ip·udp
薛定谔的悦5 小时前
储能 EMS 的功率策略:从一块电表到一次逆流的 200 毫秒
大数据·linux·能源·储能·bms
新时代牛马8 小时前
Linux 内核入门地图:架构、源码目录与五大子系统
linux·运维·架构
Julien20048 小时前
控制 SELinux 文件上下文
linux·运维·服务器
sulikey8 小时前
Linux Core Dump 完全指南:从原理到实战的崩溃调试手册。什么是core dump?程序报错core dumped怎么办?
linux·服务器·操作系统·调试·coredump
MayZork11 小时前
Windows 与 Linux 下 vcpkg 的安装部署指南
linux·运维·windows
zbyyd12 小时前
Linux 进程间通信学习笔记
linux·笔记·学习
w2005122415 小时前
Pod管理
linux·服务器·云原生·容器·k8s·podman
chen<>16 小时前
C++ 六种 memory_order:从原子性到线程间可见性.md
java·linux·开发语言·c++