资讯详情

资讯详情

Linux x86 共享虚拟地址(SVA)与 ENQCMD:PASID、IA32_PASID 与用户态设备工作提交机制

Linux x86 共享虚拟地址SVA与 ENQCMDPASID、IA32_PASID 与用户态设备工作提交机制【免费下载链接】linuxLinux kernel source tree项目地址: https://gitcode.com/GitHub_Trending/li/linux本文以 Linux 内核文档Documentation/arch/x86/sva.rst为主体系统讲解 x86 平台上共享虚拟地址SVA、进程地址空间标识PASID与 ENQCMD 指令三者如何协同使用户态应用无需内核介入即可直接向日志型设备如 Intel DSA提交工作并共享应用虚拟地址空间。读完本文后你将掌握 SVA/ATS/PRI 的完整地址翻译与缺页协作流程、PASID 的全生命周期管理规则并能结合traps.c中 #GP 修复代码与iommu.h中的 SVA API 理解内核侧的具体实现。一、SVA 背景设备直接使用应用虚拟地址共享虚拟地址Shared Virtual Addressing, SVA允许处理器与设备使用同一套虚拟地址从而省去软件将虚拟地址翻译为物理地址的开销。SVA 就是 PCIe 规范中所称的 Shared Virtual MemorySVM。SVA 的价值有两点设备可以直接使用应用层的虚拟地址无需软件地址翻译DMA 不再要求钉住pin物理页。实现 SVA 依赖三个平台能力的配合PCIe ATSAddress Translation Services设备可以缓存虚拟地址的翻译结果TLBPCIe PRIPage Request Interface当 ATS 查表失败时设备通过 PRI 请求 CPU 侧把目标虚拟地址对应的页面调入页表行为与 CPU 处理缺页类似IOMMU使用 SVA 要求平台必须有 IOMMU 支持IOMMU 同时承载 ATS 与 PRI 功能。从源码结构看翻译失败后的协作流程是ATS 查表失败 → 设备用 PRI 请求映射该虚拟地址 → 操作系统完成映射后设备再次发起 ATS 获取翻译 → 设备才能使用该地址执行 I/O。在此过程中IOMMU 驱动借助mmu_notifier()机制保持设备 TLB 与 CPU 页表的一致性——例如当某个页表项被失效时IOMMU 会把失效传播到设备 TLB强制设备此后对该虚拟地址的任何访问都重新参与 ATS 查询若 IOMMU 返回页不存在设备便通过 PCIe PRI 协议请求内核把页面调入内存。二、SIOV 与共享硬件工作队列SWQ与单根 I/O 虚拟化SR-IOV不同可扩展 I/O 虚拟化Scalable IOV, SIOV允许应用与虚拟机VM共同使用共享工作队列Shared Work Queue, SWQ。相比把硬件资源硬分区给各个进程共享队列能带来更好的硬件利用率避免硬分区导致的资源闲置。为了让硬件区分共享队列上正在执行的工作属于哪个上下文SIOV 使用 PCIe SIG 定义的 20 位 Process Address Space IDPASID。PASID 会被编码到设备发出的所有事务中使 IOMMU 能在 PCIe 资源标识符RID即 Bus/Device/Function之外按 PASID 粒度追踪 I/O。FAQ 部分对共享工作队列与传统方式的差异给出了更具体的工程解释传统模式下用户态应用与硬件交互需要每个进程独立的硬件实例。以 doorbell门铃为例每个 doorbell 必须按 4K页大小间隔排布以实现进程隔离硬件必须在 MMIO 中预留相应空间线程数增大时无法扩展共享工作队列模式下硬件负责管理队列深度消费者无需自行跟踪。队列满时设备返回可重试的错误码使用 SWQ 时硬件只需在设备中提供单一地址配合 ENQCMD 提交工作时设备可以通过工作描述符中的 PASID 区分提交进程从而支撑大量进程扩展。此外用户程序应先检查设备的 Deferrable Memory WriteDMWr能力仅当设备支持时才提交 ENQCMD。按新的 DMWr 术语设备需支持 DMWr completer capability同时要求路径上所有交换机端口支持 DMWr 路由并由 PCIe 子系统使其生效管理方式与 PCIe atomic operations 类似。三、ENQCMD把应用上下文粘贴进硬件的指令ENQCMD 是 Intel 平台上的一条新指令原子地把一个工作描述符work descriptor提交到设备。描述符中包含要执行的操作类型所有参数的虚拟地址完成记录completion record的虚拟地址当前进程的 PASID。ENQCMD 采用非postednon-posted语义若硬件接受了命令会带回状态提交方据此知道是否需要重试也可结合设备自身机制实现公平性或保证前向进展。文档将其定位为胶水ENQCMD 既让应用直接向硬件提交命令又让硬件通过 PASID 感知应用上下文从而以该上下文执行 I/O 操作。四、进程地址空间打标线程作用域的 IA32_PASID MSR线程作用域的 MSRIA32_PASID定义见 arch/x86/include/asm/msr-index.h是用户进程与其余硬件之间的连接点。其工作流程为当应用首次访问 SVA 能力设备时内核为进程新分配一个 PASID 并初始化该 MSR设备驱动调用 IOMMU 特定 API 建立 DMA 与页请求的路由。以 Intel 数据流加速器DSA为例它调用iommu_sva_bind_device()该函数会完成以下工作分配 PASID并把进程的页表%cr3寄存器编程到 IOMMU 的 PASID 上下文条目中注册mmu_notifier()以跟踪所有页表失效保持设备 TLB 同步。例如某页表项失效时IOMMU 将失效传播到设备 TLB设备此后访问该虚拟地址将重新参与 ATS若 IOMMU 回复页不存在设备便先经 PCIe PRI 请求页面调入再执行 I/O。该 MSR 作为supervisor state由 XSAVE 特性集管理确保上下文切换时 MSR 随之更新。这一点在内核异常处理代码中有直接体现见下文第六节中try_fixup_enqcmd_gp()的注释MSR_IA32_PASID由 XSAVE 管理因此直接写 MSR 只在 fpregs 有效且 fpstate 未保存时才安全。五、PASID 管理谁分配、谁编程、如何传播内核必须为每个将使用 ENQCMD 的进程分配 PASID并把它编程进IA32_PASIDMSR向平台硬件传达进程身份。具体机制ENQCMD 使用 MSR 中保存的 PASID 为本进程发出的请求打标。用户用 ENQCMD 提交工作描述符时描述符中的 PASID 字段会被自动填充为MSR_IA32_PASID的值设备发出的 DMA 请求也携带同一 PASID平台 IOMMU 利用事务中的 PASID 执行地址翻译IOMMU API 会把 IOMMU 中对应的 PASID 条目设置为 CPU 使用的进程地址x86 上即%cr3MSR 必须在任何应用线程与设备交互前在每个逻辑 CPU上配置好同一进程的不同线程共享同一页表因而共享同一 MSR 值。内核数据结构与辅助接口集中在 include/linux/iommu.hmm_pasid_init()在dup_mm()复制 mm 时把新 mm 的iommu_mm指针清零避免新旧 mm 指向同一iommu_mm_data引发 use-after-free/double-freemm_valid_pasid()/mm_get_enqcmd_pasid()通过mm-iommu_mm判断是否已分配 PASID 并取出其值未分配时返回IOMMU_PASID_INVALIDiommu_sva_bind_device()/iommu_sva_unbind_device()/iommu_sva_get_pasid()驱动侧绑定/解绑 SVA 并取得 PASID 的主入口。六、PASID 生命周期管理含 #GP 修复的内核实现文档对 PASID 的整个生命周期给出了明确规则初始状态进程创建时PASID 被初始化为IOMMU_PASID_INVALID-1。延迟分配只有实际访问 SVA 能力设备的进程才需要分配 PASID。分配发生在进程打开/绑定 SVA 设备且发现尚无 PASID 时之后绑定同一设备或其他设备时复用同一个 PASID。延迟加载虽然 PASID 在打开设备时分配给进程但它在该进程任何线程上都不生效。只有当某个线程用 ENQCMD 提交工作描述符时才会把 PASID 惰性加载到IA32_PASIDMSR。#GP 修复路径首次 ENQCMD 访问会触发 #GP 异常因为IA32_PASIDMSR 尚未初始化为该进程被分配的 PASID 值。Linux 的 #GP 处理程序发现进程已分配 PASID 后初始化 MSR 并返回使 ENQCMD 指令重新执行。这一行为在内核源码中可直接验证——arch/x86/kernel/traps.c 中的try_fixup_enqcmd_gp()完整实现了这套修复逻辑static bool try_fixup_enqcmd_gp(void) { #ifdef CONFIG_ARCH_HAS_CPU_PASID u32 pasid; /* * MSR_IA32_PASID is managed using XSAVE. Directly * writing to the MSR is only possible when fpregs * are valid and the fpstate is not. This is * guaranteed when handling a userspace exception * in *before* interrupts are re-enabled. */ lockdep_assert_irqs_disabled(); /* * Hardware without ENQCMD will not generate * #GPs that can be fixed up here. */ if (!cpu_feature_enabled(X86_FEATURE_ENQCMD)) return false; /* * If the mm has not been allocated a * PASID, the #GP can not be fixed up. */ if (!mm_valid_pasid(current-mm)) return false; pasid mm_get_enqcmd_pasid(current-mm); /* * Did this thread already have its PASID activated? * If so, the #GP must be from something else. */ if (current-pasid_activated) return false; wrmsrq(MSR_IA32_PASID, pasid | MSR_IA32_PASID_VALID); current-pasid_activated 1; return true; #else return false; #endif }该函数在 arch/x86/kernel/traps.c 的exc_general_protection()入口处被调用if (user_mode(regs) try_fixup_enqcmd_gp())) return;——修复成功则直接返回用户态 ENQCMD 随即重执行。修复的四个前置条件ENQCMD CPU 特性、mm 已有合法 PASID、本线程尚未激活、关中断状态下 fpregs 有效也逐一印证了上文supervisor state XSAVE的说法。fork/exec 时移除fork(2)或exec(2)之后进程不再拥有打开设备时的那个地址空间PASID 会从进程上移除。clone(2) 时共享新线程共享同一地址空间可以直接使用进程已分配的 PASID。内核不会在 clone 时主动初始化IA32_PASID——因为 PASID 可能尚未分配且内核无从知晓该线程是否会访问设备MSR 保持清零能通过 xstate init 优化降低上下文切换开销。同时在 PASID 分配到进程 mm 之前创建的线程都必须能处理 #GP新线程按相同方式处理也就保持了一致性。延迟释放由于在 unbind 时释放 PASID 并清除所有线程的IA32_PASIDMSR 过于复杂PASID 只在 mm 退出时惰性释放。close munmap 的良性残留若进程 close(2) 了设备文件描述符并 munmap(2) 了设备 MMIO portal驱动会解绑设备。此时进程内已访问过设备的线程在 PASID_MSR 中仍标记为 VALID——但这是无害的没有 MMIO portal 就无法向设备提交新工作。七、进程、线程、设备与 portal 的对应关系文档 Relationships 一节明确了各对象之间的基数关系这是理解 DSA 一类设备编程模型的关键每个进程有很多线程但只有一个 PASID设备具有数量有限大约几十到上千个的硬件工作队列由设备驱动负责分配单次mmap()将单个硬件工作队列映射为一个 portal每个 portal 对应一个工作队列进程与每个设备交互时必须存在一个或多个已 mmap 的 portal进程内多个线程可共享同一 portal 访问同一设备多个进程可以分别 mmap 同一 portal此时它们仍共享同一条设备硬件工作队列进程级唯一 PASID 被所有线程用于与所有设备交互——不存在每线程一个 PASID也不存在每线程↔设备对一个 PASID。八、FAQ关键概念辨析SVA 与 SVM 的区别命名SVA 允许 I/O 硬件与处理器工作在同一地址空间即共享地址空间。有人称其为 Shared Virtual MemorySVM但 Linux 社区为避免与 POSIX Shared Memory 和 Secure Virtual Machines 这两个已有术语混淆选择了 SVA 的叫法。PASID 的精确定义PASID 是 PCIe 定义的 Transaction Layer PacketTLP前缀是操作系统分配并管理的 20 位编号包含在平台与设备之间的所有事务中。与用户态设备驱动的区别通过共享工作队列与设备通信远比完整的用户态驱动简单。硬件初始化全部由内核驱动完成用户态只需关心提交工作与处理完成。与 SR-IOV 的区别SR-IOV 聚焦于为硬件虚拟化提供彼此独立的硬件接口VF 必须是几乎全功能的接口——传统的 BAR、MSI-X 中断空间、独立寄存器布局且 VF 依赖 PF 驱动协助。SIOV 则建立在 PASID 概念之上创建虚拟设备实例每个虚拟设备由一个 PASID 加上设备的 bus/device/function 共同表示硬件可以按需动态创建资源而 SR-IOV 的创建与管理是静态的。为什么不为每个应用创建一个 VF创建 PCIe SR-IOV 型 VF 成本高昂——VF 需要复制 PCI 配置空间与 MSI-X 中断等硬件中断等资源必须在创建时硬分区无法按需动态扩展VF 与 PF 也不完全独立多数 VF 需要 PF 驱动的通信协助。相比之下SIOV 创建的是软件定义设备所有配置与控制都走慢路径slow path经中介完成而工作提交与完成则无需任何中介。虚拟化支持ENQCMD 可以在客户机 VM 内使用。此时 VMM 负责建立翻译表将 Guest PASID 翻译为 Host PASID更多细节参见 ENQCMD 指令集参考文档。是否需要钉住内存当设备支持 SVA 且平台 IOMMU 支持相应设备时DMA 不再需要 pin 内存。完整流程为设备在 I/O 前经 ATS 请求 IOMMU 查表若映射存在但 OS 未分配页面IOMMU 硬件返回无映射设备随后经 PRI 请求映射该虚拟地址OS 完成映射后回响应给设备设备再次请求翻译并继续执行。此外IOMMU 与 OS 协作管理页表一致性移除页面时IOMMU 先与设备交互清除其可能缓存的设备 TLB 条目再从 OS 移除映射。九、延伸阅读与内核源码索引围绕本文主题仓库中值得继续深入的路径包括Documentation/arch/x86/sva.rst本文的主体来源SVA/ENQCMD/PASID 的完整设计说明arch/x86/kernel/traps.ctry_fixup_enqcmd_gp()实现ENQCMD 首次提交的 #GP 修复逻辑arch/x86/include/asm/msr-index.hMSR_IA32_PASIDMSR 索引定义include/linux/iommu.hmm_pasid_init()、mm_valid_pasid()、mm_get_enqcmd_pasid()、iommu_sva_bind_device()等 SVA/PASID 核心接口受CONFIG_IOMMU_MM_DATA/CONFIG_IOMMU_SVA门控未启用时提供返回IOMMU_PASID_INVALID或-ENODEV的桩实现。原文档末尾列出的参考资料按主题为Intel VT-D 近期增强说明、SIOVAssignable Interfaces介绍、ENQCMD 指令集扩展编程参考、Intel DSA 规格书这些均为外部规范文档可依据主题名称检索获取。【免费下载链接】linuxLinux kernel source tree项目地址: https://gitcode.com/GitHub_Trending/li/linux创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →