reverse-skills-max: initial public release (restored, redacted, hardened)
This commit is contained in:
@@ -0,0 +1,156 @@
|
||||
# Hyper-V / VMBus:VSC·VSP / GPADL / SR-IOV 数据面切换
|
||||
|
||||
<CORE RULE>
|
||||
Hyper-V 的合成设备(synthetic device)不走"硬件 MMIO + 中断",而走 VMBus:
|
||||
|
||||
```
|
||||
子分区: VSC(guest 侧驱动,如 netvsc / storvsc)
|
||||
↕ VMBus(双向通道 = 两个 ring buffer)
|
||||
根分区: VSP(宿主侧实现合成设备)+ 硬件
|
||||
```
|
||||
|
||||
所以:
|
||||
|
||||
```
|
||||
设备 I/O 看不到明显的硬件 MMIO
|
||||
→ 可能只是合成设备流量走 VMBus
|
||||
```
|
||||
|
||||
第二条:**GPADL 不是内存地址**——它是"描述并映射一片客户机缓冲区"的句柄。
|
||||
</CORE RULE>
|
||||
|
||||
## 一、VMBus 的机制
|
||||
|
||||
- VMBus 通过**映射到多个分区的内存页**实现——数据经总线传递**不需要 hypervisor 拷贝**,减少上下文切换
|
||||
- 一条通道是**双向**的,由**两个 ring buffer** 组成(进/出);高速设备(合成 SCSI、网卡)可用**多条通道**提高并行度
|
||||
- 在 Linux 侧它是总线模型(`/sys/bus/vmbus`),主要用户是合成设备:存储、网络、视频、工具服务
|
||||
|
||||
## 二、GPADL:客户机物理地址描述符列表
|
||||
|
||||
- ring buffer 空间有限,性能关键路径用 **GPADL**:让根分区把**额外的客户机内存页直接映射进自己的地址空间**
|
||||
- ring buffer 本身是 guest 分配、由 guest 把 GPA 列表**经 VMBus 控制路径**告知宿主;更大量数据传输另用独立的 GPA 列表描述数据缓冲区
|
||||
- Hyper-V 对**经 GPADL 共享的客户机内存总量有限制**(Windows Server 2019+ 约 1280 MB,更早约 384 MB),防止恶意 guest 耗尽宿主资源
|
||||
|
||||
**RE 含义**:
|
||||
|
||||
```
|
||||
GPADL = 37
|
||||
→ 不是 GPA 0x25000
|
||||
→ 是一个句柄,背后是"一片被描述并映射的客户机缓冲区"
|
||||
```
|
||||
|
||||
## 三、SR-IOV:数据面会"中途换路"
|
||||
|
||||
带 SR-IOV 的网卡上:
|
||||
|
||||
- **数据面**(VF):从 VM 内的 VF **直接走硬件到网卡**——**不经过 VMBus,也不经过 hypervisor**
|
||||
- **控制面**:VF 的控制仍然**经 VMBus** 回到根分区的 PF 驱动
|
||||
|
||||
**VF 拆除时的迁移顺序**:
|
||||
|
||||
```
|
||||
虚拟化栈通知 VPCI VSP(根分区管理 OS)
|
||||
→ 经 VMBus 通知 guest 内的 VPCI VSC
|
||||
→ NetVSC 从 VF 网卡驱动解绑
|
||||
→ 子分区流量从 VF 数据面迁移到软件合成数据面
|
||||
→ 后续:过滤规则搬到 PF 上的默认 VPort、删除 VF 的 VPort、请求函数级复位、释放 VF 资源
|
||||
```
|
||||
|
||||
**所以**:
|
||||
|
||||
```
|
||||
一开始 VMBus 很忙 → 后来包捕获里流量突然消失 → 网络却仍然正常
|
||||
```
|
||||
|
||||
**不一定是数据丢失**——很可能已经切到 SR-IOV VF 直通路径(或反向迁回)。
|
||||
|
||||
## 四、GPADL 的删除会阻塞
|
||||
|
||||
- **若该 GPADL 当前仍被 server 映射,删除调用会被阻塞,直到它被解除映射**
|
||||
- 从 buffer 创建 GPADL 时,**该 buffer 会被 probe 并锁定,直到 GPADL 被销毁**
|
||||
- 所以 teardown 里"调用删除之后线程卡住"**先查远端映射的生命周期**,而不是马上判死锁
|
||||
|
||||
## 五、packet 的生命周期:回调返回 ≠ packet 结束
|
||||
|
||||
- **完成调用可以在处理回调中立即调用,也可以稍后调用**
|
||||
- **直到完成调用被发起**,系统才会:清理未完成的映射、释放占用的缓冲区、在**对端要求完成通知时**发送完成 packet
|
||||
- 因此:
|
||||
|
||||
```
|
||||
callback returned ≠ packet lifetime ended
|
||||
```
|
||||
|
||||
**逆向资源生命周期时不能把"回调返回"当释放边界。**
|
||||
|
||||
## 六、大块 payload 不在 ring packet 里
|
||||
|
||||
- 一个 packet 可以只有 **inline 控制缓冲**,大块 payload 通过**外部内存描述**提供
|
||||
- 所以只 dump ring 时看到:
|
||||
|
||||
```
|
||||
header / command / 几个字段
|
||||
但找不到真正的数据
|
||||
```
|
||||
|
||||
**很正常**——要继续解析外部的内存描述。
|
||||
|
||||
## 七、取外部数据可能返回"待决",而且会再次回调
|
||||
|
||||
- 若为描述这段内存需要把对应区域**分页进来**,取外部数据的调用会返回 **STATUS_PENDING**
|
||||
- 此时**必须从 packet 处理回调返回**;**稍后回调可能以不同的 IRQL(例如 DPC)再次被调用**,届时再调一次就会成功并返回数据
|
||||
- 于是 packet handler 的控制流实际是:
|
||||
|
||||
```
|
||||
callback #1 → 取数据 → STATUS_PENDING → return
|
||||
…稍后,可能在不同 IRQL…
|
||||
callback #2 → 再次取数据 → 成功 → 继续处理
|
||||
```
|
||||
|
||||
**普通的同步 CFG 很容易漏掉这条边。**
|
||||
|
||||
返回的 MDL 有两个易错点:
|
||||
|
||||
- **它描述的内存已被锁定**——不需要再 probe/lock
|
||||
- **它既没有用户态也没有内核态虚拟地址**——需要虚拟地址时必须显式映射(并在之后解除映射),或者直接把 MDL 交给做 DMA 的驱动
|
||||
- **MDL 在"完成"被调用时失效**,之后由内核库释放;**驱动不需要自己释放它**
|
||||
|
||||
需要把处理放到被动级别时,可用"延迟到被动级"的接口把 packet 交过去。
|
||||
|
||||
## 决策树
|
||||
|
||||
```
|
||||
拿到 Hyper-V 环境目标
|
||||
├─ 设备 I/O 找不到硬件 MMIO
|
||||
│ └─ 先按合成设备(VSC/VSP 经 VMBus)解释
|
||||
├─ trace 里出现小整数句柄
|
||||
│ └─ 可能是 GPADL(客户机缓冲区描述),不是地址
|
||||
├─ 网络流量中途"消失"但功能正常
|
||||
│ ├─ 查是否启用 SR-IOV:VF 数据面不过 VMBus
|
||||
│ └─ 查 VF 拆除/迁移路径(VPCI VSP → VMBus → VPCI VSC → NetVSC 解绑)
|
||||
├─ 通道拥塞 / 性能异常
|
||||
│ └─ 看是否使用了多通道(高速设备常有)
|
||||
└─ 共享内存用量相关
|
||||
└─ GPADL 总量存在上限(宿主侧限制),不是无限共享
|
||||
```
|
||||
|
||||
## 工具与验证
|
||||
|
||||
- 通道与设备:VMBus 总线视图、VSC/VSP 配对关系、合成设备类型
|
||||
- 缓冲区:GPADL 建立与使用点;对照"这是句柄不是地址"
|
||||
- 网络路径:VF 是否存在、控制面是否仍在 VMBus、数据面当前走 VF 还是合成路径
|
||||
- 验证:能同时说清「这条 I/O 走合成路径还是 VF 直通 + 共享内存经 GPADL 如何描述 + 控制面在谁那里」
|
||||
|
||||
## 该平台的坑(汇总)
|
||||
|
||||
- **把 GPADL 当内存地址**:它是缓冲区描述符句柄
|
||||
- **在合成设备上找硬件 MMIO**:VMBus 走共享 ring,不走寄存器窗口
|
||||
- **把 SR-IOV 切换当数据丢失或抓包失败**:数据面可以从 VMBus 切到 VF 直通(并可能切回)
|
||||
- **以为 VF 的一切都绕开 VMBus**:数据面绕开,**控制面仍走 VMBus**
|
||||
- **忽略多通道**:高速设备可能并行使用多条通道
|
||||
- **以为 GPADL 共享无上限**:宿主对共享总量有限制
|
||||
- **把删除 GPADL 时的阻塞当死锁**:它会等 server 解除映射;从 buffer 创建的 GPADL 还会锁定该 buffer 直到销毁
|
||||
- **把处理回调返回当 packet 结束**:完成调用才是释放边界,且可能被延后
|
||||
- **在 ring packet 里找大块 payload**:大块数据走外部内存描述
|
||||
- **把取外部数据返回"待决"当失败**:需要分页时就是它会返回的,之后会在可能不同的 IRQL 再次回调
|
||||
- **从外部数据 MDL 直接取虚拟地址**:它不带任何虚拟地址,需要显式映射并解除
|
||||
- **自己去释放外部数据 MDL**:它在完成调用时失效,由内核库释放
|
||||
Reference in New Issue
Block a user