
多租户 NPU 隔离这件事这几年但凡碰过 AI 训练集群或者推理服务平台的人迟早都会正面撞上。表面上看把一张加速卡切成几个逻辑实例分给不同租户用跟虚机分 CPU、内存差不多可真要动起手来你才会发现瓶颈不在“切分”本身而在一个很容易被忽略的底层事实加速卡上的 DMA 引擎不会老实走 CPU 那套内存管理它拿到一个地址就直接往内存里写。如果这个地址没有经过任何约束租户 A 的显存地址可以被租户 B 的 DMA 请求轻松踩掉数据泄露和互相污染就只是一条指令的距离。这篇文章就以这个痛点作为起点把“多租户 NPU 隔离”这条链路从最底层翻到最上层PASID 怎么给设备认路IOMMU 怎么把 DMA 关进笼子最后硬件强制隔离又是怎么兜底的。内容偏实操不会只堆概念。我会把每个关键机制的“为什么这么设计”讲清楚再给出可以照抄的排查步骤和配置参考。适合正在做算力池化、NPU 虚拟化、或者是被线上数据越界搞到焦头烂额的同学希望能省掉你自己从头挖半天的功夫。1. 多租户共享加速卡的流量墙真正的问题出在 DMA 无视内存权限先说个我在实际环境里见过的场景。某台物理机上插了两张加速卡卡上各自跑着不同租户的推理任务。因为是实验环境同学们一开始图省事直接在宿主机上各开进程、各占显存完全没有虚机或容器隔离。业务跑起来感觉一切正常直到某天租户 A 的显存出现了错乱模型权重里混进了一些完全不是它自己的数据。查了半天最后定位到根因是邻居租户 B 的 DMA 写请求越界了。这类事故的本质就是 DMA 默认“只认物理地址不认权限”。CPU 访问内存时有 MMU 管着页表、权限位、地址空间隔离一应俱全。但 PCIe 设备要走 DMA 的时候传统模式下它只知道“我要往物理地址 0x8FFF0000 写数据”。它没有进程的概念不知道自己是属于哪个租户更不知道这个物理地址到底是不是它允许碰的。所有约束全部依赖软件层面自觉或者依赖驱动做地址翻译和校验——这种纯软件防线在共享场景下非常脆弱。多租户场景之所以会把这个矛盾无限放大是因为“共享”这个词本身就意味着边界模糊。每个租户的任务都会被调度器塞进同一个物理设备显存要么被切块、要么走虚拟化共享。一旦某个驱动的地址计算出了偏差或者某个厂商库的显存池分配没有对齐泄漏出来的 DMA 请求就会直接穿透到别的租户的显存区域。更麻烦的是这类问题往往是随机复现、时好时坏错误数据第一次出现时根本不像是越界更像是“模型训练出了点问题”。所以要真正解决多租户隔离不能靠“大家控制住别越界”这种君子协定。必须让硬件自己知道“这个 DMA 请求是谁发起的、它被允许访问哪些地址”。这就是 PASID 和 IOMMU 登场的理由。2. PASID给设备一张看懂进程地址空间的“门禁卡”PASID 的全称是 Process Address Space ID直译是“进程地址空间标识符”但它干的事更像是一张门禁卡。在 PCIe 体系里设备请求 DMA 时传统做法是在请求的地址字段里直接塞物理地址。有了 PASID 之后请求头里可以额外携带一个标识相当于设备对下游说“我是进程 42我现在要访问的地址是 0x00007FFF1234000请帮我翻译成物理地址。”这个设计思路翻译成人话就是让设备像 CPU 一样拥有“进程视角”。CPU 访问内存时用的是虚拟地址MMU 负责把虚拟地址翻译成物理地址。设备有了 PASID 之后也可以用虚拟地址发起 DMA然后让 IOMMU 帮忙做翻译。这就绕开了“设备必须知道物理地址”这个百年老规矩也顺带解决了不同租户地址空间互相踩踏的问题——因为每个租户的进程都对应不同的 PASID不同进程的虚拟地址空间天然是隔离的设备发起请求时只要带对 PASIDIOMMU 就会按那个进程的页表去翻译。PASID 的位数在 PCIe 规范里是 20 位理论上能区分一百万个进程。听起来很多但在大规模池化场景里这其实是个稀缺资源尤其当每个租户要跑多个分布式任务时每个进程、甚至每个线程组都可能需要独立的 PASID。我后面会专门讲遇到 PASID 耗尽的排查经历。PASID 真正发挥作用还得配一个叫共享虚拟地址SVAShared Virtual Address的机制。SVA 的意思是设备使用的虚拟地址和进程里代码看到的虚拟地址是同一套。应用程序在用户空间分配一块缓冲区然后把这块缓冲区的虚拟地址直接交给加速卡驱动去 DMA驱动不再需要做“虚拟地址转物理地址”的映射也不需要提前固定内存。因为一切的翻译责任都交给了 PASID IOMMU 这套组合数据来了或去了内存页可能被换出换入设备端的页表请求机制会帮忙补页。这里有一个很容易理解偏差的地方PASID 只负责“区分进程”它本身不做隔离。隔离靠的还是下一层的 IOMMU 页表。PASID 更像门禁卡上印的编号IOMMU 才是那个拿着花名册决定“放不放行、放你到哪层”的保安。两者配合才能做到一套硬件同时承载多个进程地址空间。3. IOMMU把设备 DMA 关进笼子页表就是笼子的栅栏IOMMU 全称 Input/Output Memory Management Unit中文通常叫输入输出内存管理单元。它的位置在 PCIe 设备与物理内存之间所有从设备发往内存的 DMA 请求都得从它面前过。你别把它理解成一块简单的地址翻译芯片它做的事情其实跟 CPU 里的 MMU 高度对称对设备侧而言IOMMU 维护着一套设备页表每个设备更准确地说是每个设备域访问内存时IOMMU 按这个设备域对应的页表做翻译从而决定它能不能访问某个物理页。没有 IOMMU 的设备直通模式本质上就是“裸奔”。设备拿到一整块未经过滤的物理地址窗口它可以读写整个内存区域前提是驱动作业。一旦驱动有漏洞或者设备被固件层面攻破攻入设备侧就等于攻入整个宿主机内存。IOMMU 建立起来之后即使设备被攻破了它也只能在 IOMMU 给它分配的域里面活动其他域和它毫无关系。IOMMU 还有一个极其重要的附带功能中断重映射。DMA 能写内存还不算最危险设备还能发起 MSI/MSI-X 中断如果中断消息可以被伪造到任意 CPU 向量上攻击者可以通过中断注入直接控制宿主机。IOMMU 把中断重映射也一并拦截让每个设备的中断只能路由到它所属域对应的处理器。你可以把这一层理解为“不仅管住了数据通道的地址还管住了控制通道的入口”。在 x86 平台上IOMMU 通常对应处理器的 DMA 重映射硬件单元而 ARM 平台上的 SMMU 是等价物。名字不同作用一致。开启方式一般是在 BIOS 里打开对应开关常见叫法是 DMA Remapping 或者类似选项然后在宿主机内核引导参数里加上开启参数我会在实操部分给出具体检查命令和参数示例。关键点在于IOMMU 提供的是“能力”但它不会自动为你分配隔离域。你要把哪个设备、或者哪个虚拟设备SR-IOV 的 VF放进哪个域如何配置页表映射这些仍然需要上层的虚拟化层或驱动配合。用一句直白的话来说IOMMU 是给厨师准备好的合规厨房但做不做隔离大餐还是看厨师怎么用。4. 三种隔离手段的对比软件隔离、IOMMUPASID、硬件强制隔离说到多租户 NPU 隔离圈子里经常把方案分成几档最底层的软件隔离、中间的 IOMMUPASID 隔离、最硬核的硬件强制隔离。虽然它们之间不是简单的替代关系但理解清楚差异你才知道自己该往哪个方向使劲。4.1 第一档软件层面对地址做账速度最快但防君子不防小人这一档里没有什么额外硬件参与靠的是驱动和运行时库在软件里把显存划分清楚并通过 API 做边界检查。比方说某个加速卡设备通过厂商提供的计算库给每个租户固定分配一块显存区域驱动在提交任务时校验 DMA 目标地址是否落在自己的区域内越界直接报错。优点是性能损耗几乎为零现有硬件无需改动就能用。可靠性则完全取决于驱动的责任心和边界检查的完善度。这种方案适合租户之间可信度较高、没有恶意对抗风险的内部集群。一旦碰到驱动 bug 或者设备固件的异常行为软件边界就是一层纸。我在生产环境里见过一次显存错乱事故后来发现事故根子就是某个版本的计算库在地址换算时溢出了一个 32 位整数导致一块显存区域被错误计算到邻居租户的头上。软件隔离能防住的只是“已经被识别到的错误”防不住那些没被想到的。4.2 第二档IOMMUPASID让每个进程有独立地址视图第二档是目前容器化推理平台的主流选择。宿主机开启 IOMMU设备开启 PASID 共享虚拟地址能力每个容器进程或租户进程关联到独立的 PASIDIOMMU 为每个 PASID 维护独立页表。在这个模式下驱动不再需要负责“物理地址分配正确”因为设备根本就不接触物理地址。它发出去的虚拟地址会由 IOMMU 翻译翻译结果对应的物理页只会落在内核为这个进程预留的页面上。即使设备被攻破它手里攥着的也只是某个进程的虚拟地址空间而不是整个宿主机内存。这一档的隔离强度对绝大多数业务已经足够而且性能损失相对可接受。开销主要在 IOMMU 页表查找和 TLB 失效上。如果你的 NPU 任务里面有大量小粒度碎片化 DMATLB 的命中率会直接影响吞吐这个需要实测评估。另外启用 SVA 后IOMMU 支持页错误请求。当设备访问的虚拟地址对应的物理页被换出到磁盘时IOMMU 会发送 Page Request InterfacePRI请求让系统重新换入并通知设备重试这套机制保证设备看到的虚拟地址视角和内核实态是同步的。4.3 第三档硬件强制隔离不信任任何人如果场景里有真正不可信的租户或者合规审计强迫你达到“物理级隔离”的要求那就得考虑硬件强制隔离方案了。核心思路是用硬件机制隔离设备之间的通信访问权做到即使软件层被渗透也无法跨租户访问。实现手法主要有几条路第一条是 SR-IOV 下的物理功能划分。一张物理卡通过 SR-IOV 弹出多个虚拟设备 VF每个 VF 在硬件层面就有独立的 DMA 上下文、独立的中断路径VF 之间的数据交换如果在 PCIe 交换结构上被 ACS 能力限制住就可以达到很不错的隔离级别。第二条是依赖 PCIe ACSAccess Control Services。ACS 是 PCIe 规范里定义的一组能力用于控制同一 PCIe 拓扑下面设备之间的直接访问。如果开不了 ACS设备之间可能绕过上游的 IOMMU 通过 PCIe 交换结构的内部通道互相访问形成所谓的“DMA 侧信道”。不少共享物理服务器的加速卡集群出安全通告根因就是 ACS 没使能。第三条是走系统级安全技术比如把安全世界和普通世界隔离或者用可信执行环境给 NPU 存储和计算路径做整体保护。这类方案彻底改变了信任模型但硬件支持面不算广配置上也复杂得多。下面这张表把三档方案的关键维度收拢在一起方便直接对照维度软件隔离IOMMUPASID硬件强制隔离信任要求租户间可信任租户间互不可信面向不信任/对抗环境性能损耗几乎为零中等取决于 DMATLB 命中率较低但配置复杂隔离强度弱依赖驱动逻辑中等偏强可防驱动漏洞强硬件层面限制硬件要求无特殊要求需要 IOMMU、PASID 能力需要 SR-IOV、ACS 等适用场景内部开发、可信测试容器化推理/训练集群跨租户公共算力、审计环境这里我得强调一个常被忽略的点硬件强制隔离不等于“开了就一定安全”。ACS 能力只在某些特定的 PCIe 拓扑上才能发挥作用比如加速卡通过多个根端口直连到 CPU 的时候ACS 的默认状态可能是不完整的。你需要专门检查硬件的能力位并用工具主动做访问测试验证别只看 BIOS 里一个开关开了就觉得万事大吉。5. 实操链路从 BIOS、内核、驱动到验证命令讲完原理接下来是一套可以照着做的链路。假设你的物理机上已经装了支持 SR-IOV、PASID 和 IOMMU 的加速卡操作系统是 Linux目标是让两个租户的容器各用一张 VF互不可见。5.1 开启 BIOS 和内核参数第一步还是在 BIOS 里确认打开 DMA Remapping 和 SR-IOV。不同厂商的主板叫法有差异常见的是找类似 “VT-d”、 “IOMMU”、 “SR-IOV” 字样的选项全部设为 Enabled。如果 BIOS 里根本没有这些选项大概率说明硬件平台不支持硬件强制隔离这条路走不通只能退回软件方案。然后修改内核引导参数。在 grub 配置里的 GRUB_CMDLINE_LINUX 中加入intel_iommuon iommupt如果你的平台是 AMD 的处理器对应的开启参数是 amd_iommuon。iommupt 表示直通模式下不拦截合法 DMA 流量这种做法能减少一些性能开销。如果遇到直通失败或者数据异常可以去掉 pt 参数让 IOMMU 全量翻译兼容性更好。改完后执行 update-grub 并重启。重启后先检查 IOMMU 是否真的启用dmesg | grep -i iommu正常会看到类似于 “IOMMU enabled” 的日志。接着查看 iommu 分组情况IOMMU 分组决定了哪些设备可以被一起直通而不能拆分ls /sys/kernel/iommu_groups/5.2 创建 SR-IOV 虚拟设备并确认 PASID 支持接着需要让物理加速卡生成 VF。在物理功能PF下查看可用 VF 数量然后启用# 以某设备为例先用 lspci 找到物理功能设备号然后写入 vf 数量 echo 2 /sys/bus/pci/devices/0000:01:00.0/sriov_numvfs创建成功后用 lspci 确认出现了新的 VF 设备。这些 VF 会被分配新的 BDFBus/Device/Function此时每个 VF 在 IOMMU 视角里就是独立的设备可以分到独立的域。PASID 的启用一般在驱动加载阶段完成。可以用以下命令检查设备是否暴露了 PASID 能力lspci -vvv -s 0000:01:00.0 | grep -i pasid能看到类似 “PASID capacity: 20 bits” 的输出就说明设备端支持。接着确认内核里是否开启了对应驱动选项。对于大多数加速卡只要 IOMMU 开启且设备能力正常PASID 路径会自动建立不太需要额外配置。5.3 配置用户的 IOMMU 域和直通在宿主机上做直通配置最简单的方式是借助虚拟化层的设备直通功能。比如用 QEMU/KVM 环境时把 VF 作为 PCI 设备直通给虚拟机-device vfio-pci,host0000:01:00.1使用 vfio-pci 驱动是直通的前提。如果你用的是纯容器环境没有虚拟化层那驱动的选择就取决于加速卡的厂商是否支持“用户态驱动IOMMU 域”绑定。不少通用计算框架已经支持通过某种设备控制平面直接为每个容器分配一个带 PASID 的上下文不需要再绕一层虚拟机。配好之后可以用下面的方法确认直通设备已经分配了 IOMMU 域ls -l /sys/kernel/iommu_groups/*/devices/如果看到该 VF 出现在 一个独立 iommu_group 里说明隔离域建立成功。如果多个 VF 挤在同一个 group 里直通某一台机器时另一个 VF也会跟着被占用这是 IOMMU 拓扑限制导致的需要考虑调整插槽位置或者用 ACS 补丁打破分组。5.4 验证租户之间的隔离脏页测试与跨域访问探测配置到底有没有生效不能只看日志必须做主动探测。第一个测试是隔离有效性验证让租户 A 在自己的地址空间里写满一个特殊标记然后让租户 B 反复做 DMA 读操作检查读回来的数据里是否会掺入租户 A 的标记。理论上如果隔离有效租户 B 读到的任何内容都不会包含它自己未被授权的地址空间数据。这个测试可以用驱动接口或者写一个小的内核模块来完成。第二个测试是跨域访问拦截测试。让一个租户的进程尝试构造一个指向另一个租户物理地址的 DMA 请求正常驱动不会给你这个机会但你可以绕过驱动直接通过用户态页表映射来做验证。如果 IOMMU 拦截生效设备会收到一个页错误或请求被静默丢弃日志里会出现 IOMMU 错误记录dmesg | grep -i DMAR\|IOMMU如果看到类似 “device fault” 的信息时说明 IOMMU 已经成功拦截了一次非法访问这反而是好消息。5.5 中断与页错误路径的确认除了数据通道中断路径也得验一遍。使用命令检查每个 VF 的中断是否被正确重映射到对应租户的处理器目 标。在 vfio 模式下你可以通过 /proc/interrupts 观察直通设备的中断分布。这里主要看两点中断是否容易发到目标 CPU以及非 VF 设备能不能篡改它的中断。页错误路径则可以通过故意访问未 commit 的缓冲区来触发 PRI。正常流程下进程向设备提交一个还没有完全锁定住内存的缓冲区设备访问后触发缺页系统补页后驱动收到 retry 通知。如果这一链路卡住通常会导致 DMA 超时或者设备崩溃。遇到类似现象时优先排查优先级是否开启也就是检查内核是否支持 IOMMU_SVA 相关的 Kconfig 选项。6. 排障现场PASID 耗尽、ACS 缺失和不听话的 DMA 越界理论和配置都聊完了接下来是硬货挑几个在真实踩坑经历中比较有代表性的问题讲清楚现象、根因和排查链路。6.1 现象一PASID 分配失败租户任务一调起就报错有次环境里出现了诡异现象稳定跑了一段时间后新提交的推理任务一启动就报资源分配失败错误日志指向 PASID 申请失败。一开始同学们以为是设备资源不够但查了显存利用率显然不是那么回事。我让他们先检查 PASID 的分配上限结果发现系统里某个长期运行的控制面组件占用了大量 PASID 没释放把 20 位的空间都快吃光了。排查命令是看当前已分配的 PASID 数量和上限。不同的驱动暴露方式不同可以直接翻 /sys 下的对应设备目录或者驱动模块的 debugfs例如cat /sys/kernel/iommu/.../pasid_allocated cat /sys/kernel/iommu/.../pasid_max这类问题本质上是“地址空间泄漏”跟内存泄漏一个道理。解决方式是定位到那个长期不释放上下文的组件升级修复版驱动或者定期重启控制面组件释放资源。6.2 现象二加速卡之间出现跨租户的 DMA 旁路ACS 没开另一个场景来自安全审计。审计方要求确认加速卡 A 所在的租户不能通过 DMA 访问加速卡 B 的显存。我们原本以为只要分别给两张卡建立独立 IOMMU 域就够了结果用工具扫描 PCIe 拓扑时发现两张加速卡挂在同一个 PCIe 交换器下面而交换器的 ACS 能力没有使能。问题严重在哪呢PCIe 交换器在默认状态下允许下游端口之间直接转发 DMA 请求这种转发路径可以完全绕过上游 IOMMU 的检查。也就是说即使 IOMMU 配置得再严格一个设备依然可能通过交换器内部的转发通道直接读写另一个设备映射的内存区域这就让隔离形同虚设。定位方法是检查设备的 ACS 能力位。Linux 下有现成工具可以列出每个设备是否支持 ACS如果不支持或者没开启有两种处理方式要么换一块物理拓扑更好的主板让每张卡都直连 CPU 的独立根端口要么在可编程硬件层面开启 ACS 等价的替代串行化机制避免下游端口间的直通。这一块建议放到硬件选型阶段就重点考察后补往往很被动。6.3 现象三开启 SVA 后偶发 DMA 超时罪魁是内存回收路径还有一个比较隐蔽的问题PASIDSVA 模式下系统的内存回收器可能在没有主动告知设备的情况下换出一部分页面。等设备端访问到这些页面时IOMMU 会发起 PRI 请求但如果驱动或内核路径中 PRI 响应机制没接好就会出现 DMA 请求长时间不被响应最终超时。遇到这个问题时一个排查方向是确认该设备的驱动是否实现了对设备页错误请求的排队与反馈机制。某些早期驱动版本只在任务初始化时注册了 SVA却没有完整注册 PRI导致系统一度出现“能映射但不能缺页”的尴尬状态。解决办法通常就是升级驱动或者修改内存固定pinning策略要么干脆把相关内存区域锁定住避免被换出代价是预留的物理内存不能归还给系统。这三类故障有个共同点它们都不是配置里单靠某个开关就能一眼看穿的也不像 CPU 的业务代码那样容易做单元测试。它们需要你在原理层面理解 DMA 的路径、IOMMU 的分组、PCIe 拓扑的转发特性才能快速定位到根因。7. 选型思路别把硬件强制隔离当作万能药也别高估软件方案的边界到了选方案的时候很多人会直接问“哪个最安全”这个问法其实不对。多租户 NPU 隔离没有一个放之四海而皆准的最优解它更像是在安全目标、性能损耗、部署复杂度和硬件成本之间做折中。如果你的集群是自己内部用的租户都是可信的同学或者同事那么 IOMMUPASID 这一档通常就够用了它能把“手一抖写错地址”这种大部分的问题兜住性能影响也足够小。如果业务对延迟极度敏感连几个百分点的 IOMMU 翻译开销都不想付那软件隔离方案也未必不可接受但你要明确知道这是把信任建立在驱动正确性之上的选择日常一定要把显存压力监控和驱动版本管理做好。如果做的是面向外部用户的公共算力平台租户之间完全不可信那硬件强制隔离就不能省。这时候你要审视的不光是加速卡本身还有它所在的那张 PCIe 拓扑图。卡插在哪个槽位上、跟谁共享同一个交换器、ACS 是否支持并已开启、中断重映射是否完整每一项都要过一遍。另外还要把管理面的安全考虑进来。NPU 隔离做得再好如果控制面 API 可以被任意租户调用去篡改别人设备的配置那一切都白搭。我见过有些平台把租户权限控制得非常严格却忘记对一个管理容器开放了完整的设备节点权限相当于给隔离了的房间开了一条公共通道。最后提醒一点隔离能力是会随着驱动和固件更新而变化的。同一个设备某个版本的固件关了 ACS 或者改变了 PASID 的默认分配行为后面的行为都可能跟最初验的不一样。建议把上面讲到的那些验证命令固化成一个自动化 smoke test每次升级固件、驱动或 BIOS 之后都跑一遍确保隔离没有在升级过程中悄悄退化。我这几年的实际项目体会是多租户 NPU 隔离没有银弹但把 PASID、IOMMU、SR-IOV/ACS 这几层吃透之后再回头看所有安全架构设计思路会清晰非常多。这里的每一层都解决一个特定的问题少了任何一层都可能留出某条你想象不到的侧信道。希望你读完这篇不只是记住了几个名词而是真正理解了它们组合在一起时的那条完整的防护链路。