2355 字
12 分钟
ITS表保存操作触发时机
Cursor
不对!这是一个很重要的误解。让我澄清一下:
ITS 表保存操作不是每次中断时触发的
1. 实际触发时机
ITS 表保存操作只在 VM 状态变化时触发,具体是:
// hw/intc/arm_gicv3_its_kvm.c:75-90
static void vm_change_state_handler(void *opaque, bool running, RunState state)
{
GICv3ITSState *s = (GICv3ITSState *)opaque;
Error *err = NULL;
if (running) {
return; // VM运行时什么都不做
}
// 只有当VM停止时才保存表
kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_CTRL,
KVM_DEV_ARM_ITS_SAVE_TABLES, NULL, true, &err);
}
关键判断:if (running)
- 只有 VM 从运行状态变为停止状态时才执行保存。
2. 中断处理 vs 表保存的区别
正常中断处理流程(每次中断都会发生):
// 中断到达 -> ITS处理 -> 更新内核中的ITS表 -> 投递给vCPU
1. 设备发送MSI中断到ITS
2. ITS查找Device Table找到设备
3. ITS查找ITT(Interrupt Translation Table)找到中断映射
4. ITS查找Collection Table找到目标CPU
5. 投递中断给目标vCPU
表保存操作(只在 VM 停止时发生):
// VM停止 -> 保存所有ITS表到客户机内存 -> 用于迁移
1. VM状态变为停止
2. 触发vm_change_state_handler
3. 调用KVM_DEV_ARM_ITS_SAVE_TABLES
4. 内核将所有ITS表保存到客户机RAM
3. 为什么不是每次中断都保存
性能考虑:
- ITS 表保存是一个重量级操作,需要遍历所有表并写入内存
- 如果每次中断都保存,性能开销巨大
- 正常运行时,ITS 表数据在内核中动态维护就足够了
设计原理:
- 运行时:ITS 表在内核中实时更新,支持中断路由
- 迁移时:才需要将内核中的表数据”快照”到客户机内存
4. 实际的时序对比
正常运行时的中断处理(高频):
设备中断 -> ITS内核处理 -> vCPU接收中断
(每秒可能数千次,无表保存操作)
迁移时的表保存(低频):
用户发起迁移 -> VM停止 -> 保存ITS表 -> 迁移RAM -> 迁移完成
(整个VM生命周期可能只发生几次)
5. 代码证据
在 ITS 的中断处理相关代码中,你不会找到表保存操作:
// hw/intc/arm_gicv3_its_kvm.c:75-90
static void vm_change_state_handler(void *opaque, bool running, RunState state)
{
GICv3ITSState *s = (GICv3ITSState *)opaque;
Error *err = NULL;
if (running) {
return; // VM运行时什么都不做
}
// 只有当VM停止时才保存表
kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_CTRL,
KVM_DEV_ARM_ITS_SAVE_TABLES, NULL, true, &err);
}
// 中断到达 -> ITS处理 -> 更新内核中的ITS表 -> 投递给vCPU
1. 设备发送MSI中断到ITS
2. ITS查找Device Table找到设备
3. ITS查找ITT(Interrupt Translation Table)找到中断映射
4. ITS查找Collection Table找到目标CPU
5. 投递中断给目标vCPU
// VM停止 -> 保存所有ITS表到客户机内存 -> 用于迁移
1. VM状态变为停止
2. 触发vm_change_state_handler
3. 调用KVM_DEV_ARM_ITS_SAVE_TABLES
4. 内核将所有ITS表保存到客户机RAM
设备中断 -> ITS内核处理 -> vCPU接收中断
(每秒可能数千次,无表保存操作)
用户发起迁移 -> VM停止 -> 保存ITS表 -> 迁移RAM -> 迁移完成
(整个VM生命周期可能只发生几次)
可以看到,KVM_DEV_ARM_ITS_SAVE_TABLES
只在 vm_change_state_handler
中被调用,这个函数只在 VM 状态变化时触发。
6. 总结
- 错误理解:每次 ITS 中断都会保存表 ❌
- 正确理解:只有 VM 停止时才保存表 ✅
具体区别:
- 中断处理:实时、高频、只更新内核中的 ITS 表
- 表保存:低频、只在迁移/停机时、将表数据复制到客户机 RAM
这种设计既保证了运行时的性能,又满足了迁移时的状态保存需求。
User
ITS 表客户机 os 里面本身不就有一张吗
Cursor
非常好的问题!你说得对,这涉及到 ITS 表的多层架构。让我详细解释:
ITS 表的多层存在
1. 客户机 OS 中的 ITS 表(Guest OS 视角)
客户机 OS 确实维护着自己的 ITS 表,包括:
// 客户机OS中的ITS驱动维护的表:
- Device Table: 客户机认为的设备到ITT的映射
- Collection Table: 客户机认为的中断集合到CPU的映射
- Interrupt Translation Tables (ITT): 客户机认为的EventID到INTID的映射
2. KVM 内核中的 ITS 表(Hypervisor 视角)
但是,KVM 还需要维护虚拟化版本的这些表:
// KVM内核中维护的虚拟ITS表:
- 虚拟Device Table: 实际的设备映射,可能与客户机不同
- 虚拟Collection Table: 实际的CPU分配,需要映射到物理CPU
- 虚拟ITT: 实际的中断路由,需要考虑虚拟化开销
3. 为什么需要两套表?
// 客户机OS中的ITS驱动维护的表:
- Device Table: 客户机认为的设备到ITT的映射
- Collection Table: 客户机认为的中断集合到CPU的映射
- Interrupt Translation Tables (ITT): 客户机认为的EventID到INTID的映射
// KVM内核中维护的虚拟ITS表:
- 虚拟Device Table: 实际的设备映射,可能与客户机不同
- 虚拟Collection Table: 实际的CPU分配,需要映射到物理CPU
- 虚拟ITT: 实际的中断路由,需要考虑虚拟化开销
让我查看 ITS 虚拟化的相关代码来解释这个问题:
原因分析:
A. 虚拟化层的需求
// 1. 地址转换:客户机物理地址 -> 宿主机物理地址
// 2. CPU映射:虚拟CPU ID -> 物理CPU ID
// 3. 中断号映射:客户机中断号 -> 宿主机中断号
// 4. 安全隔离:防止客户机访问不属于它的资源
B. 迁移的需求
// 客户机OS的表:客户机自己管理,在客户机内存中
// KVM的表:需要在迁移时特殊处理,包含虚拟化映射信息
4. 两套表的关系
客户机 OS 的 ITS 表:
- 位置:客户机内存中
- 内容:客户机视角的设备和中断映射
- 管理者:客户机 OS 的 ITS 驱动
- 特点:客户机认为这是”真实”的硬件表
KVM 的虚拟 ITS 表:
- 位置:宿主机内核内存中
- 内容:实际的虚拟化映射关系
- 管理者:KVM 的 vGIC-ITS 模块
- 特点:包含真实的物理资源映射
5. 举个具体例子
假设客户机 OS 要配置一个中断:
// 客户机OS视角:
Device ID: 0x1000
Event ID: 0x20
Target CPU: vCPU-2 (客户机的CPU-2)
LPI ID: 8192
// 客户机在自己的ITS表中记录:
Device Table[0x1000] -> 指向客户机内存中的ITT
ITT[Event 0x20] -> LPI 8192, Collection 2
Collection Table[2] -> vCPU-2
// 但KVM需要进行转换:
KVM的虚拟ITS表中记录:
Device 0x1000 -> 实际对应宿主机的PCI设备X
Event 0x20 -> 需要路由到物理CPU-5(vCPU-2运行在物理CPU-5上)
LPI 8192 -> 可能映射到不同的物理中断号
6. 迁移时的特殊处理
这就解释了为什么迁移时需要特殊的表保存操作:
// 迁移前:
vm_change_state_handler() {
// 将KVM内核中的虚拟ITS表状态
// 同步回客户机内存中的表
KVM_DEV_ARM_ITS_SAVE_TABLES
}
// 目的:确保客户机内存中的ITS表反映了
// 当前KVM中实际的中断路由状态
7. 为什么不直接用客户机的表?
安全性:
- 客户机不能直接控制物理中断路由
- 需要 KVM 进行地址和权限检查
隔离性:
- 多个客户机不能互相干扰
- 物理资源需要虚拟化层管理
性能:
- KVM 可以优化中断路由路径
- 避免客户机每次都陷入到 hypervisor
8. 总结
你的理解是对的,客户机确实有自己的 ITS 表。但是:
- 客户机 ITS 表:客户机 OS 管理,存储客户机视角的映射
- KVM 虚拟 ITS 表:KVM 管理,存储实际的物理映射
- 迁移时同步:将 KVM 的状态同步回客户机表,确保迁移后状态一致
这种双层设计是虚拟化的典型模式:客户机看到的是虚拟视图,hypervisor 维护真实的物理映射。
// 1. 地址转换:客户机物理地址 -> 宿主机物理地址
// 2. CPU映射:虚拟CPU ID -> 物理CPU ID
// 3. 中断号映射:客户机中断号 -> 宿主机中断号
// 4. 安全隔离:防止客户机访问不属于它的资源
// 客户机OS的表:客户机自己管理,在客户机内存中
// KVM的表:需要在迁移时特殊处理,包含虚拟化映射信息
// 1. 地址转换:客户机物理地址 -> 宿主机物理地址
// 2. CPU映射:虚拟CPU ID -> 物理CPU ID
// 3. 中断号映射:客户机中断号 -> 宿主机中断号
// 4. 安全隔离:防止客户机访问不属于它的资源
// 客户机OS的表:客户机自己管理,在客户机内存中
// KVM的表:需要在迁移时特殊处理,包含虚拟化映射信息
// 客户机OS视角:
Device ID: 0x1000
Event ID: 0x20
Target CPU: vCPU-2 (客户机的CPU-2)
LPI ID: 8192
// 客户机在自己的ITS表中记录:
Device Table[0x1000] -> 指向客户机内存中的ITT
ITT[Event 0x20] -> LPI 8192, Collection 2
Collection Table[2] -> vCPU-2
// 但KVM需要进行转换:
KVM的虚拟ITS表中记录:
Device 0x1000 -> 实际对应宿主机的PCI设备X
Event 0x20 -> 需要路由到物理CPU-5(vCPU-2运行在物理CPU-5上)
LPI 8192 -> 可能映射到不同的物理中断号