RV设备透传:主线尚未支持

核完了。结论是:技术内核基本属实,但"目前已经可用"和"建议 6.10+"这两处判断与主线代码现状有明显偏差。下面逐条拆。


一、逐条核实结果

说法 判定 依据
2026 年 8 月 LWN 有这条报道 ✅ 基本属实,但性质要说清 LWN 条目 lwn.net/Articles/1090023,2026-08-21,作者 Fangyu Yu(fangyu.yu@linux.alibaba.com),标题 [RFC PATCH v3 00/10] iommu/riscv: Add hardware dirty tracking for second-stage domains
二级域硬件脏页跟踪 ✅ 属实 AMO_HWAD(capabilities bit 24)+ DC.tc.GADE(bit 7)
测试中 NVMe + e1000e 透传到 L2 guest ✅ 属实(作者自述) v3 cover letter 原文:"Test on QEMU RISC-V, a nvme and an e1000e device was passed through to an L2 guest via vfio-pci + iommufd"
同时启用 KVM_VFIO 接口 ✅ 属实,且是同一系列的补丁 该 RFC 系列直接打包了 Tomasz Jeznach 的 RISC-V: KVM: Enable KVM_VFIO interfaces on RISC-V arch
NVMe VFIO 透传"目前已经可用" ⚠️ 仅限打过补丁的树,主线不可用 主线 v7.3-rc2 源码核对见下文
建议 6.10+ 明显错误 RISC-V IOMMU 驱动 v6.13 才合入
用 Ventana 内核树 ✅ 属实,且有官方文档背书 QEMU 官方文档明确推荐
需要支持 AIA 的中断控制器 ✅ 属实 IMSIC / guest interrupt file 相关

二、三个需要修正的点

1. "LWN 报道"实际是 LWN 的邮件归档页

lwn.net/Articles/1090023 的页面结构是 From / To / Subject / Date / Message-ID / Archive-link------这是 LWN 对 LKML 邮件的镜像归档,不是 LWN 编辑部撰写的专题文章。措辞上写成"LWN 收录了这封邮件"更准确。

2. 内核版本:不是 6.10+,是 6.13 起步,且 VFIO 那部分至今未合入

RISC-V IOMMU 驱动是 v6.13 ​ 合入的,Joerg Roedel 的 6.13 pull request 里明确列着 "New IOMMU driver for RISC-V"。6.10 上连 drivers/iommu/riscv/ 这个目录都不存在。

而我拉取的主线 v7.3-rc2 源码显示,VFIO 透传所需的东西一项都没进主线:

复制代码
/* drivers/iommu/riscv/iommu.c ------ 主线 v7.3-rc2 */
static const struct iommu_ops riscv_iommu_ops = {
	.of_xlate = riscv_iommu_of_xlate,
	.identity_domain = &riscv_iommu_identity_domain,
	.blocked_domain = &riscv_iommu_blocking_domain,
	.release_domain = &riscv_iommu_blocking_domain,
	.domain_alloc_paging = riscv_iommu_alloc_paging_domain,   /* ← 老回调,无 flags 参数 */
	.device_group = riscv_iommu_device_group,
	.probe_device = riscv_iommu_probe_device,
	.release_device = riscv_iommu_release_device,
};

注意这份 iommu_ops没有.capable、没有 .domain_alloc_paging_flags、没有 dirty_ops。对照 v3 补丁要改成的样子:

复制代码
/* 补丁新增 ------ 主线完全没有 */
static const struct iommu_dirty_ops riscv_iommu_dirty_ops = {
	IOMMU_PT_DIRTY_OPS(riscv_64),
	.set_dirty_tracking = riscv_iommu_set_dirty_tracking,
};

static bool riscv_iommu_capable(struct device *dev, enum iommu_cap cap)
{
	case IOMMU_CAP_DIRTY_TRACKING:
		return !!(iommu->caps & RISCV_IOMMU_CAPABILITIES_AMO_HWAD);
}

其余几处 grep 结果同样是否定的:

检查项 主线 v7.3-rc2 实际内容 结论
arch/riscv/kvm/Kconfig 无任何 select KVM_VFIO KVM_VFIO 未开启
drivers/vfio/Kconfig select VFIO_IOMMU_TYPE1 if MMU && (X86 || S390 || ARM || ARM64) 无 RISCV
drivers/iommu/Kconfig config IOMMU_DMA / def_bool ARM64 || X86 || S390 无 RISCV
drivers/iommu/riscv/ 目录 只有 iommu.c / iommu-bits.h / iommu-pci.c / iommu-platform.c / iommu.h iommu-ir.c,MSI 重映射未合入
drivers/iommu/generic_pt/fmt/riscv.h is_write_dirty / make_write_clean / make_write_dirty 脏页 PTE ops 未合入

唯一的例外是 iommu-bits.h 里这两个位定义早就在主线了------但它们只是 spec 定义的寄存器位,不代表功能已接线:

复制代码
/* drivers/iommu/riscv/iommu-bits.h ------ 主线已存在 */
#define RISCV_IOMMU_CAPABILITIES_AMO_HWAD	BIT_ULL(24)
#define RISCV_IOMMU_DC_TC_GADE			BIT_ULL(7)
#define RISCV_IOMMU_DC_TC_SADE			BIT_ULL(8)

QEMU 官方文档的说法也印证了这点:"Linux kernel iommu support was merged in v6.13... As of v6.17, it does not have support for features like VFIO passthrough. There is a VFIO RFC series that is not yet merged."

所以准确表述是:NVMe VFIO 透传在打齐补丁栈的内核上可跑通,但在任何 mainline 内核上开箱即用是做不到的

3. 依赖不止"AIA",还有两个硬约束

AIA 这条没错------QEMU 那边的标准命令行就是 -M virt,aia=aplic-imsic,aia-guests=5,MSI 重映射本身就是把 IMSIC 的 MSI 目标页预先映射到 IOVA。但要补两点:

  • 仍需 vfio allow_unsafe_interrupts=1 。Andrew Jones 原话:"The interrupt-remapping domain does not yet validate MSI data, so VFIO still requires allow_unsafe_interrupts=1"。RISC-V IOMMU 规范不校验 MSI data,这个限制是架构性的。
  • irqbypass(MSI 直投 guest interrupt file)尚未支持,被明确推到后续系列。而 Ventana 树之所以被推荐,恰恰因为它额外带了 irqbypass。

三、"二级域脏页跟踪"到底在做什么

这是整件事里技术含量最高、也最容易被误读的部分,值得解释一下。

RISC-V IOMMU 有两级页表:一级 iosatp(VA→GPA,给 guest 自己管),二级 iohgatp(GPA→SPA,给 hypervisor 管)。设备透传给 VM 时,宿主机需要知道"这块内存被 DMA 写脏了没有"------这是虚拟机热迁移收敛的关键。没有硬件支持就只能靠写保护 + 缺页模拟,代价极高。

RISC-V 的做法是:硬件带 AMO_HWAD 能力时,只要把设备上下文的 DC.tc.GADE 打开,IOMMU 就会在设备首次写入某页时原子地把 PTE 的 D 位(bit 7)置上。软件只需读走并清掉:

复制代码
/* drivers/iommu/generic_pt/fmt/riscv.h ------ 补丁新增的三个 PTE ops */
static inline bool riscvpt_entry_is_write_dirty(const struct pt_state *pts)
{
	/* Svnapot 64K 时一个叶子跨 16 个 PTE,必须全查,否则丢脏标记 */
	for (; tablep != end; tablep++)
		if (READ_ONCE(*tablep) & RISCVPT_D)
			return true;
	return false;
}

static inline bool riscvpt_entry_make_write_dirty(struct pt_state *pts)
{
	pt_riscv_entry_t new = pts->entry | RISCVPT_D;
	return try_cmpxchg64(tablep, &pts->entry, new);
}

对外暴露的就是 iommufd 那两个标准 ioctl:IOMMU_HWPT_SET_DIRTY_TRACKING / IOMMU_HWPT_GET_DIRTY_BITMAP

注意这个系列只做二级域 ,一级域(iosatp)完全没动------因为只有二级域才是 hypervisor 为 VM 分配 IOMMU_HWPT_ALLOC_NEST_PARENTIOMMU_HWPT_ALLOC_DIRTY_TRACKING 时创建的。这也正好解释了为什么测试用的是 L2 guest:二级域服务于嵌套场景,L1 管一级、L0 管二级。


四、它还只是个 RFC,别当定论

系列从 v1(4 月)走到 v3(8 月),中间被 Jason Gunthorpe 改过一次设计:

  • v1 想动态开关 DC.tc.GADE,Jason 说没必要------直接在上 S2 域时预设 GADE,靠 D 位预置规避硬件开销;
  • v1 想加 PT_FEAT_RISCV_DIRTY_TRACKING_ACTIVE 特性位,Jason 说 D 位该由 iommufd core 管,用户态在首次清 D 之前本就该假设全脏。

作者两处都接受并改掉了,v3 里 GADE 改成静态预置。另外作者自己在 cover letter 里也承认端到端测试还没做:"Build a dedicated end-to-end test case that drives the full flow... so that the behaviour can be regression-tested beyond the KUnit PTE-level coverage" 。也就是说,NVMe 那条测试记录是手工验证 + KUnit 单元级验证,还没有自动化回归覆盖。

再补一个细节:v1/v2 的测试写的是 "a virtio-net and an e1000e"NVMe 是 v3 才出现的。如果你在别处看到 virtio-net 版本的引用,那不是笔误,是更早的版本。


五、修正后的说法

2026 年 8 月,阿里巴巴的 Fangyu Yu 向 LKML 提交了 RFC v3 补丁集(LWN 有邮件归档),为 RISC-V IOMMU 的二级(iohgatp)域接上硬件脏页跟踪,利用 AMO_HWAD 能力 + DC.tc.GADE 让 IOMMU 在 DMA 首次写入时自动置 PTE 的 D 位,并通过 iommufd 的 SET_DIRTY_TRACKING/GET_DIRTY_BITMAP 暴露给上层。作者在 QEMU RISC-V 上把 nvme 与 e1000e 通过 vfio-pci + iommufd 透传进 L2 guest 验证通过,系列本身也打包了 Tomasz Jeznach 的 KVM_VFIO 使能补丁。

但截至主线 v7.3-rc2,这套东西尚未合入arch/riscv/kvm/Kconfigselect KVM_VFIOVFIO_IOMMU_TYPE1IOMMU_DMA 的架构白名单里都没有 RISCV,MSI 重映射(iommu-ir.c)也还在 review 中。RISC-V IOMMU 驱动本身是 v6.13 ​ 才进的主线,所谓"6.10+"站不住。想实际跑 NVMe 透传,需要 AIA/IMSIC 中断控制器、打齐 MSI 重映射 + IOMMU_DMA + VFIO 使能 + 本系列补丁的内核------QEMU 官方推荐的捷径是 Ventana 的 dev-upstream 树,另需 vfio allow_unsafe_interrupts=1,且 irqbypass 暂不支持。

相关推荐
xqqxqxxq1 小时前
AI Agent学习:主动工具发现(李博杰《深入理解 AI Agent》4.8观后总结)
人工智能·学习
爱吃苹果的日记本2 小时前
数据结构第一课
c语言·数据结构·数据库·学习·c#
孫治AllenSun3 小时前
【LangChain4J-09】开发学习知识点
spring boot·后端·学习
边境悍匪3 小时前
蜗牛学苑 Java 智能体学习 Day38|Spring AI Alibaba2 思维导图复盘
java·学习·spring
摘星星的屋顶4 小时前
2026年8月31日~2026年9月13日周报
人工智能·学习
泡泡鱼(敲代码中)5 小时前
Python语法技术学习笔记
开发语言·笔记·python·学习·pycharm
Yanjun2i6 小时前
Agent学习记录三:完成 Agent Loop
python·学习·agent
一条破秋裤8 小时前
STM32 单片机两层学习规划
stm32·单片机·学习
老猿讲编程9 小时前
【Eclipse OpenSOVD学习之六】 数据访问层(DataProvider)
学习·eclipse·嵌入式软件·车载·sovd