Linux阅码场 - Linux内核月报(2020年09月)

关于Linux内核月报

Linux阅码场

Linux阅码场内核月报栏目,是汇总当月Linux内核社区最重要的一线开发动态,方便读者们更容易跟踪Linux内核的最前沿发展动向。

限于篇幅,只会对最新技术做些粗略概括,技术细节敬请期待后续文章,也欢迎广大读者踊跃投稿为阅码场社区添砖加瓦。


本期月报(总第4期)主要贡献人员:

陈玮、张健、廖威雄

(月报的完善和专业,离不开大牛们的持续贡献,欢迎更多大牛加入月报贡献团队)


1-3期链接:

Linux阅码场 - Linux内核月报(2020年06月)

Linux阅码场 - Linux内核月报(2020年07月)

Linux阅码场 - Linux内核月报(2020年08月)


1. 虚拟化

Boqun Feng-Hyper-V:Support PAGE_SIZE larger than 4K

该补丁在9月份进行了第二版和第三版更新,经过v1和v2的深度review在第三版已经从RFC变成了正式的PATCH。该补丁系列为在Hyper-V虚拟机中运行的客户机,使用大于4K的页面提供必要的支持。Hyper-V一直采用4K作为它的页面大小,并且期望在和虚拟机中客户系统的通信中采用同样的页面大小。也就是说,在所有的hypervisor-guest通信协议中使用的“PFN”s,代表的是以HV_HYP_PAGE_SIZE为大小的页面的数量,而不是以PAGE_SIZE为大小的页面的数量。为了支持虚拟机客户系统使用更大的页面,我们需要在hypervisor-guest通信中正确地在这两个不同的页面大小间进行转换。这基本就是这个PATCH系列所做的工作。

V1 -》V2的改变:

1. 更新重新基于v5.9-rc2内核,并集成 Michael最新的核心补丁(Enable Linux guests on Hyper-V on ARM64
2. 根据Michael的建议,引入hv_ring_gpadl_send_offset()来提高可读性。在inputvsc ringbuffer中使用max(…, 2 * PAGE_SIZE),而不是硬编码的大小来与其他的ringbuffer设置对齐
3. 根据Michael的建议,使用storvsc有效负载的精确大小以节省storvsc_queuecomman中的内存。
4. 根据Michael的建议,对页面内的循环索引使用“unsigned int”类型,这样在PAGE_SIZE == HV_HYP_PAGE_SIZE情况下我们就可以利用编译器进行优化。
V2-》V3的改变:
1. 在Michael Kelley的启发下,使用一种简单而直接的方法为storvsc设置有效载荷数组。
2. 根据Michael的建议修改了一些书写错误
3. 修正由于max()的两个操作数类型不同而导致的编译器警告。

Andra Paraschiv-Add support for Nitro Enclaves

该补丁系列在9月份进行了v9和v10更新。Nitro Enclaves (NE)是Amazon弹性计算云(EC2)的一种新功能。它允许客户在EC2实例[1]内部再分割出一块隔离的计算环境。例如,一个在虚拟机中运行的用于处理敏感数据的应用程序,可以和运行在同一个虚拟机中的其它应用程序分离开。我们称呼这个运行EC2实例的虚拟机为主虚拟机。分离后,这个应用程序运行在和主虚拟机不同的另外一个单独虚拟机里,也可以叫做enclave。

v9 -> v10:

  1. 更新重新基于v5.9-rc6内核。

  2. 更新commit信息,以包括SoB标记之前的更改日志。

v8 -> v9:

  1. 更新重新基于v5.9-rc4内核

  2. 添加数据结构,以保持对Nitro Enclaves misc和PCI设备的引用。当使用IOCTL逻辑创建Enclave VM和设置NE CPU池的使用,这些refs都会被用于NE PCI设备驱动程序的探测/删除/关闭流程。

  3. 将NE文档的位置更新到“virt”目录,并将其包含在相应的索引文件中。

  4. 从NE驱动程序Makefile中移除-Wall标志,可以使用W=1作为选项。

Jie Deng-i2c:virtio: add a virtio i2c frontend driver

该补丁在内核中添加一个半虚拟化的VIRTIOI2C总线驱动(客户机前端驱动)。任何一个遵循virtio协议的设备模拟软件,都可以通过VIRTIOI2C后端驱动模拟该VIRTIO I2C控制器。该驱动程序通过VIRTIOI2C消息结构与后端驱动程序通信,该消息结构包括以下部分:

- Header: i2c_msg addr, flags, len。

-数据缓冲区:指向i2cmsg数据的指针。

-状态:后端驱动的处理结果。

人们可以按照自己的需求实现不同的VIRTIOI2C后端驱动来模拟不同的I2C控制器。在开源项目ProjectACRN中的device model中已经由一个后端驱动的实现可以作为示例。项目ACRN。更多信息,请参考https://projectacrn.org

我们使用VIRTIOID 34作为VIRTIOI2C控制器设备的ID,因为34之前的ID已被其他virtio设备保留。

Chenyi Qiang-add bus lock VM exit support

虚拟机可以使用总线锁降低系统的性能。对writeback(WB)内存的分割锁定访问或在uncacheable(UC)内存上使用锁都会造成总线锁定。总线锁通常比在缓存中的原子操作慢1000个时钟周期。它还会影响其他CPU核的性能(它们必须等待总线锁被释放,才能完成它们的内存操作)。

为了解决这个威胁,我们引入总线锁触发VMExit,以便在VM获得总线锁时通知VMM,以便它执行节流或其他基于策略的缓解措施。

通过设置VM-executioncontrol中的 “BusLock Detection”比特 (SecondaryProcessor-based VM execution controls 第30比特),我们可以使能总线锁触发VMExit。如果该VM Exit的退出事件被其它更高优先级的VMExit事件抢占,例如,EPT表配置错误,EPT行为异常,APIC读写访问,exceptionbitmap退出等,在这种情况下vmcs中退出原因字段的第26比特设置为1。

在当前的实现中,KVM通过KVM_CAP_X86_BUS_LOCK_EXIT暴露这个功能。用户可以获得支持的模式bitmap(即关闭和退出)并显式启用它(默认禁用)。如果KVM检测到虚拟机中有总线锁,则会退出到用户空间,即使当前的VM退出原因是由KVM内部处理的。因此在vcpu->run->flags中设置一个新的字段KVM_RUN_BUS_LOCK用于通知用户空间。

具体的用户空间节流策略还需要进一步探讨。比如我们可以在获得总线所的虚拟机上强制限流,只要注入一些睡眠时间,或者其他的想法。

总线锁检测的文档现在可以在最新的“Intel Architecture Instruction Set Extensions Programming Reference”中找到。需要注意的,由于Linux内核社区的反馈,第9.1节中关于“BusLock Debug Exception”需要修改:

https://lore.kernel.org/lkml/87r1stmi1x.fsf@nanos.tec.linutronix.de/

Intel Architecture Instruction SetExtensions Programming Reference文档链接:

https://software.intel.com/content/www/us/en/develop/download/intel-architecture-instruction-set-extensions-programming-reference.html

Tom Lendacky-SEV-EShypervisor support

这个补丁系列在KVM中添加了对运行AMDSEV-ES特性虚拟机的支持。

Secure Encrypted Virtualization -Encrypted State (安全加密虚拟化- 状态加密, SEV-ES)扩展了SEV的支持,来保护虚拟机的寄存器状态不受Hypervisor的影响。

参见“AMD64 Architecture Programmer's Manual Volume 2: System Programming”,章节“15.35Encrypted State (SEV-ES)" [1]。

为了允许hypervisor代表虚拟机执行一些功能,在体系结构上支持在某些类型的VMExit在即将发生时通知虚拟机内的操作系统。这允许虚拟机操作系统可以有选择地与hypervisor共享信息,用于满足所请求的功能。这个通知使用一个新的异常,VMM通信异常(#VC)来实现。使用VMGEXIT指令并通过来虚拟机-Hypervisor通信块(Guest-Hypervisor Communication Block, GHCB)来共享信息。GHCB格式和使用它的协议在“SEV-ES Guest-Hypervisor Communication Block Standardization" [2]中有说明。

在SEV-ES模式下,vCPU的保存区域(VMSA)必须被加密。SVM也被更新,它在虚拟机运行前构建初始的VMSA,并对其进行加密。一旦完成加密,Hypervisor就不能再修改它。修改VMSA将导致VMRUN指令失败,并出现SHUTDOWN退出代码。KVM必须支持VMGEXIT退出代码,以便执行虚拟机所需的必要功能。GHCB用于交换hypervisor和虚拟机操作系统都需要的信息。

为了简化VMSA和GHCB的访问,SVM使用访问器功能来获取VMSA或GHCB的地址,这依赖于虚拟机当前执行的阶段。

在SEV-ES模式下,需要对一些虚拟机操作的拦截做更改。例如,CR0写操作不能被拦截,因此需要在代码中确保在执行期间没有启用该寄存器的拦截,或者hypervisor不会在退出处理流程中尝试读取该寄存器。另一个例子是关闭处理流程,在这里vCPU不能被直接重置。

添加了对MGEXIT事件处理的支持并实现了GHCB协议。这包括支持标准退出事件,例如一个CPUID指令拦截,也有新的支持,比如AP处理器的引导。通过在VMGEXIT设置退出代码信息并调用适当的拦截处理程序,大部分现有的SVM拦截支持都可以被重用。

最后,要启动和运行SEV-ES模式虚拟机,vCPU的初始化、加载和执行都需要修改。

[1]https://www.amd.com/system/files/TechDocs/24593.pdf

[2]https://developer.amd.com/wp-content/resources/56421.pdf

Ben Gardon-Introducethe TDP MMU

多年来,对于KVM x86 MMU的需求已经从运行小型虚拟机发展为热迁移具有数百个vCPU和几TB内存的虚拟机。以前我们依赖影子分页来运行所有虚拟机,现在我们有了二维分页技术(TDP)。这个补丁集引入了KVM MMU的大部分新实现,针对使用TDP技术的虚拟机进行了优化。我们利用TDP相对简单的特性已经重新实现了许多的MMU函数,并消除对rmap的需求。在这个简化的基础上,未来会有一个补丁集来改变“TDP MMU”的同步模型,以支持比单一MMU锁更多的并行性。目前谷歌正在使用TDP MMU,满足了我们热迁移416vCPU12 TB内存的虚拟机(m2 - ultramem- 416 实例)的性能需求。

这项工作的动机是为了满足非常大的虚拟机并行地处理PAGE FAULT。当虚拟机有数百个vCPU和数TB的内存时,KVMMMU锁会遭受到极大的竞争,在处理PAGE FAULT时容易导致soft-lockup和长延迟。在使用几百个vCPU运行KVM selftests中的demand_paging_test时,可以很容易地重现这种竞争。

demand_paging_test获取的1秒描述文件中,我们配备416vCPU,每个vCPU4G内存, 它们有98%的时间花费在等待MMU锁上。在使用TDP MMU后,测试持续时间缩短了89%,主要执行事件由get_user_pages和用户错误的FD ioctl替代了MMU锁。

本系列是两个补丁系列中的第一个。在本系列中,我们将添加TDP MMU的基本实现。在下一个系列中,我们将改进TDP MMU的性能,并允许它并行地执行MMU操作。

KVM MMU的总体目的是对分页结构(CR3/EPT/NPT)进行编程,以便对虚拟机地址到主机物理地址(HPA)的映射进行编码,并为其他KVM特性提供实用工具,例如dirty loggingL1虚拟机物理地址(GPA)HPA映射的定义包括两个部分:KVMmemslotGPA映射到HVA,内核MM/x86主机页表映射HVA -> HPA。如果没有TDP, MMU必须对x86页表进行编程,以将虚拟机地址(GVA)的完整转换为HPA。这需要影子页表,以创建复合的x86分页结构。这种解决方案很复杂,需要为每个虚拟机的CR3创建独立的分页结构,并且需要模拟虚拟机页表的修改。TDP的情况要简单得多。在这种情况下,KVM允许虚拟机控制CR3,并使用GPA -> HPA映射编写EPT/NPT分页结构。客户机无法更改此映射,而且每个L1分页模型只需要一个版本的分页结构。在这种情况下,分页模型是分页结构中的层级数、地址空间(x86上的正常执行模式或系统管理模式)和其他属性的某种组合。大多数虚拟机只使用一种分页模型,因此只需要一个TDP结构。

本系列通过另外一种MMU函数实现为使用TDP运行的L1虚拟机实现了一个“TDP MMU”。当TDP不可用时,TDP MMU返回到现有的影子页表实现,并与现有的影子页表实现互操作以支持嵌套虚拟化。TDP MMU的使用可以通过一个模块参数来控制,该参数是虚拟机创建时的快照,并跟踪虚拟机的生命周期。这个快照用于许多函数中,以决定是否对给定的操作使用TDP MMU处理程序。

Sean Christopherson-KVM:Introduce "VM bugged" concept

本系列将介绍一个我们在x86领域中讨论过多次的问题。这个问题的关键在于,在一些情况下,x86 KVM理论上可能在调用栈中遇到深度的软件或硬件bug,但没有任何健全的方法将错误通知到用户空间。

另一个使用场景是,如果我们让虚拟机一直存活将是弊大于利。比如我们已经在早期TDX笨拙地修补安全相关的分页问题中使能了KVM_BUG_ON。任何安全相关的分页问题都会有大量的警告和错误消息,因为上层页表的操作失败了会造成低级PTE操作失败。

基本思想是,如果遇到bug,我们使用WARN_ONCE只打印一次告警消息,但将所有vCPU踢到用户空间,并将该虚拟机标记为bug,这样就不能在虚拟机或其设备/ vCPUIOCTL接口上做任何操作。

目前还没有做足够的测试来验证拒绝ioctls(),驱逐正在运行的vCPU等等,因此该补丁还是RFC状态。

Wei Liu-Introducing Linux root partition support for Microsoft Hypervisor

在这里,我们打算使用这个补丁系列使Linux可以作为Microsoft Hypervisor (Hyper-V)的Root分区(类似于Xen的Domain 0)。

Microsoft想用Linux和Microsoft Hypervisor构建一个完整的虚拟化软件栈。除了这个补丁系列之外,接下来还有一些补丁系列将会在Linux中提供一个设备节点(/dev/mshv),以便用户空间程序可以创建和运行虚拟机。我们还移植了CloudHypervisor,并且从7月下旬开始已经能够使用VIRTIO设备引导Linux虚拟机。

目前这个补丁系列还属于RFC,它只实现了运行所需的必要的组件。以下是对这个补丁系列的一些分析。这个补丁系列有很大一部分工作是增强hyperv-tlfs.h。它们应该没什么有争议,可以直接应用。

以下是该补丁系列中除了对hyperv-tlfs.h的修改之外的其它一些关键的要素:

  1. Linux需要以不同的方式设置现有的Hyper-V基础组件。

  2. Linux需要一些Hypercall用来启动AP。

  3. Hypervisor控制IOMMU对中断做重新映射。Linux支持hypercall来做中断的映射和解除映射。这通过引入新的MSIirqdomain和新的irqchips来实现的

第三点可能是我们最没有信心的一点。我们从Linux中的Xen代码中获得了灵感。我们非常愿意接受任何可以使upstream接受该补丁的的批评和建议。

据我们所知,tglx的补丁系列将会修改一些MSI相关的代码,因此,在tglx补丁系列被upstream接受后,我们可能需要修改一些代码。但是这不妨碍我们尽快公开我们的补丁系列以获得及时的反馈。


2.文件系统和Block Layer


pwritev2新加标志RWF_NOAPPEND

  • 补丁:https://lwn.net/Articles/830153

我们知道,pwrite 用于实现任意特定位置写入,而不用频繁 lseek。然而,在man pwrite的最后可以看到一个bug描述:在Linux上,如果文件以O_APPEND打开,则 pwrite 无法实现任意 offset 写入。

作者在 pwritev2 上支持了一个新的标志RWF_NOAPPEND,用以实现跟 pwrite 一样的功能 且 解决这个 bug。

补丁实现并不复杂,当使能RWF_NOAPPEND标志时,去除 kiocb 的IOCB_APPEND标志。

基于成本模型的IO权重限速:blk-iocost

  • 补丁1:https://lwn.net/Articles/793460/

  • 补丁2:https://lwn.net/Articles/830397/

blk-iocost 并不是最近新增的功能,其早在2019年就已经提上社区。刚好在今年9月份作者上传了优化补丁,我们就一起了解下什么是 blk-iocost?

blk-iocost 是对内核中IO子系统(blkcg)基于权重的磁盘限速功能的进一步完善。

要控制IO资源,一个最大的困难是缺乏可观察的成本度量指标。这根CPU和内存不同,CPU可用时钟时间,内存可用字节数作为足够精度的成本度量。IO带宽和IOPS是最常用的度量指标,但其会因为不同的设备不同的IO模型导致多个数量级的变化,使其不能用于IO容量分配。就好像A进程要做随机读,B进程要做顺序读,即使操作一样的数据量,执行两者需要的IO资源成本是不一样的;在机械硬盘上限制20M带宽,但换到固态硬盘上却造成资源浪费。虽然在设备上使用大量离合器的时间可以用作非排队旋转设备的有用近似值,但是对于现代设备,即使是旋转设备,这不再可行。

虽然我们无法确定每一次IO的成本,但我们可以根据相对成本做权重。例如在机械硬盘上,寻址成本远大于数据传输成本。如果我们能描述不同IO请求之间的相对成本,是可以实现IO资源的权重分配的。

blk-iocost 实现了基于IO成本模型的IO资源控制器。到Linux 5.4时只有一个简单的线性成本模型,其为每个IO分为顺序或随机,并为每个IO提供一个基本的成本量叠加大小相关的比例成本。根据模型和控制器为每个IO分配成本,根据每个cgroup的等级权重为其分配IO。

当然,默认的模型并不一定适用于所有情况和设备,控制器也提供了一些模型调整参数。引用网上的例子,如果我们要为设备 254:48 开启 blk-iocost 功能,并且当读写延迟rlat|wlat的请求有 95% 超过 5ms 时,认为磁盘饱和。内核将进行磁盘发送请求速率的调整,调整区间为最低降至原速率的 50%,最高升至原速率的 150%,我们可以这样配置:

echo "254:48 enable=1 ctrl=user rpct=95.00 rlat=5000wpct=95.00wlat=5000min=50.00max=150.00">/sys/fs/cgroup/io.cost.qos

zoned block device & ZoneFS

  • 补丁:https://lwn.net/Articles/831038/

  • 参考链接1:https://lwn.net/Articles/788851

  • 参考链接2:https://www.kernel.org/doc/html/latest/filesystems/zonefs.html

  • 参考链接3:https://www.cnblogs.com/goaheadbus/p/12127717.html

9月的补丁支持 ZoneFS 挂载的explicit-open参数,用于解决潜在活动区域数量限制导致写入出时现IO错误。使用此参数后如果打开成功,即可保证不会因为此潜在限制导致写入失败。

看到这补丁的时候,估计不少人跟我一样很懵逼,ZoneFS 有什么特点,ZoneFS服务的 zoned block device 又是什么东西?

根据 内核上 ZoneFS 的文档描述(参考链接2),zoned block device 是指一类特殊的存储设备,这些存储设备的地址区间是按zone划分的,而且这些zone可能还有不同类型和不同特性。

  • Conventional zones:这些zones跟常见的块设备一样,可以任意读写。

  • Sequential zones:这些zones可以随机读,但只能顺序写。而且这些zones不能复写,必须先擦除(zone reset)。

为什么会有这样奇怪的设备?这就涉及到 HDD(机械硬盘)了。厂家为了增加HDD的容量,为了增加磁盘数据存储的密度,而之前采用的技术(PMR)已经达到了瓶颈,于是转头使用新的技术(SMR)。新的技术是牺牲了一定稳定性来换取更大的容量。也因为新技术的特性,造成了HDD不同zone的不同特性。

PMR 和 SMR 的原理和差别,由于篇幅关系不展开介绍,但网上也有比较优秀的文章,例如:《一文搞懂PMR和SMR有什么区别》(参考链接3)

而ZoneFS则是专门为 zoned blcok device 设计的文件系统。他跟我们常见的文件系统不同,他仅仅只是把磁盘内所有的zones以文件形式呈现到用户空间。每个zone是一个按编号顺序的文件,仅此而已。我们不能在ZoneFS上创建文件,删除文件,只是可以读写每个Zones。

其实,除了ZoneFS之外,btffs也支持 zoned block devices,后续是否会有更多的FS支持 zoned block devices,我们拭目以待吧。

新的vfs系统调用:fchmodat2

  • 补丁1:https://lwn.net/Articles/831224/

  • 补丁2:https://lwn.net/Articles/831625/

通过man fchmodat可以发现,fchmodat 的 flag 参数支持AT_SYMLINK_NOFOLLOW。这个参数表示了如果我改变权限的文件是一个软连接时,修改是软连接本身的权限而不是软连接目标的权限。这在进行一些权限控制的场景挺实用的。通过限制软连接的权限而控制访问权限,而不用担心修改软连接目标权限引发与其他用户的冲突。

我们也可以在用户空间模拟AT_SYMLINK_NOFOLLOW的效果,实际上C库就是这么做的。通过open文件,然后修改 /proc/self/fd 的权限来实现。但是这样的实现依赖于有挂载 procfs。

存在一些文件系统,对链接本身执行chmod会导致inode的访问模式发生更改,但会返回一个EOPNOTSUPP错误。这是错误且不符合要求的。与其修复所有的底层文件系统,作者选择添加一个fchmodat2的新的系统调用,在文件系统上层实现。他的做法跟C库的实现一样。作者也提出了,如果所有的底层文件系统已经修复了bug,这个系统调用也就可以去掉了。

fuse支持 passthrough 读/写

  • 补丁:https://lwn.net/Articles/832430/

透传读写请求给内核是为了提高读写的速度,避免每次读写都经过FUSE。

有些基于FUSE的文件系统旨在强制执行特殊策略或在文件操作级别触发复杂的决策。例如,Android使用FUSE来实施细粒度的访问策略,这些策略也依赖于文件内容。有时,在打开或创建时,文件被标识为不需要对随后的读/写进行额外检查,因此FUSE只是充当访问FUSE文件系统的进程与较低文件系统之间的被动桥梁。拼接和缓存有助于减少FUSE开销,但是仍然可以避免将读/写操作转发到用户空间的FUSE守护进程。

当open时,FUSE的内部策略判断是否可以使能透传,如果可以,则对/dev/fuse执行 FUSE_DEV_IOC_PASSTHROUGH_OPEN ioctl() 。此后所有进一步的读写操作将由内核直接转发到较低层文件系统使用VFS层而不是FUSE守护程序。用户空间FUSE守护程序仍会处理除读取或写入以外的所有请求。

这可以提高读取和写入的性能,尤其是在以随机偏移量读取的情况下,对那些没有(预读)缓存机制的情况效果明显。

以下是补丁作者测试的性能数据,具体的测试场景可以看补丁详情。


3. 体系架构

本月和架构相关的补丁共有31个。有些是之前月报介绍过的老朋友,例如RISC-V的UEFI,KVM和NUMA等工作,X86平台Intel SGX和AMD SEV,ARM64的MTE补丁,这些补丁在讨论中继续迭代。有些是和性能相关的,例如x86/mmu:Introduce parallel memory virtualization to boost performance(尤其改善了热迁移的性能),Leonardo Bras的DMA pagecache和Zi Yan 1GB THP support on x86_64则通过分配方式的改变优化了性能。此外Yu-cheng Yu的两组CFE补丁通过阻止ROP和JOP攻击(return/jump-oriented programming attacks)增强了安全性。这次重点聊两个补丁,一个关于arm/arm64中断的改进(牵扯的知识相对较少,相对容易看懂,推荐大家都看看补丁),一个关于Intel对于加速器场景的优化(DSA),这个话题很有意思,毕竟从Intel收购Altera到Nvidia收购ARM,再到AMD商谈收购Xilinx。CPU和加速器越来越是一家了。

arm/arm64: Turning IPIs into normal interrupts
特殊待遇听起来是个好东西。毕竟满足了人想追求独特性的一面。如果发现自己太特殊,也可以发掘一下自己普通的一面,毕竟不同的侧面可以满足不同的需求。上面补丁是把原有ARM/ARM64架构的IPI中断转为普通中断。作为普通中断,可以作为pseudu NMI使用了。具体技术实现上,Marc首先把原有的handle_IPI改为do_handle_IPI,并使其满足内核中断处理函数的要求,并定义了新的函数set_smp_ipi_range,后者会调用request_percpu_irq注册中断。set_smp_ipi_range在ARM64中断控制器GIC初始化时调用。

Tag application address space for devices
Intel的Fenghua Yu和Dave Jiang等童鞋在推动Intel的DSA(Data Streaming Accelerator)合入主线。DSA属于Intel I/O加速技术(Intel® I/O Acceleration Technology),用于替换原有QuickData。DSA既可以是与本地的内存,PMEM,也可以通过NTB(备注一)与远端memory做数据搬移。
CPU和加速器之间的数据拷贝是个很低效的事情,如果大家能共用同一个虚拟地址空间,就可以按需访问,不需要提前拷贝。SVM就是这样的技术。当设备在同一时间只和一个主机进程玩耍时,大家都很开心。在Intel引入Scable IOV之后,事情有点变化:Scable IOV使用在不同应用和虚拟机之前使用共享的工作队列与设备通信。现在的问题是,设备如何区分不同的应用(进程)呢?
有小伙伴可能想到了PASID,PCIe的PASID的确是用来解决这个问题。但是仅仅是设备能通过IOMMU(带着PASID)访问到CPU地址空间还不够。CPU每次给设备下发任务是也需要携带PASID。具体的携带方式是在ENQCMD/ENGCMDS命令发送work descriptor的同时携带PASID到设备。ENQCMD是non-posted写,CPU通过返回值判断写入是否成功(例如队列满了导致写入失败)。
ENGCMD传输的数据格式
architecture-instruction-set-extensions-programming-reference p60
这一次Fenghua Yu提交的补丁是上述DSA补丁的第一波,主要做了两件事情:第一件:是能ENQCMD命令;第二件,使用新的MSR(MSR_IA32_PASID)管理每个CPU的PASID。具体做法是在线程第一次绑定SVM设备时配置PASID到MSR_IA32_PASID,当最后一个线程与SVM设备接触绑定时把MSR_IA32_PASID置为0表示禁止PASID(参见"drivers/iommu/intel/svm.c")。pasid定义在mm_struct中,上下文切换时,会被替换(参见”arch/x86/kernel/fpu/xstate.c“)。

备注
1:NTB(“Non-Transparent” Bridge)通常用于连接两个CPU socket,参考:

https://events.static.linuxfound.org/sites/events/files/slides/Linux%20NTB_0.pdf
2:原有操作设备dedicated workqueue的命令是MOVDIR64B,参见这里的解释:

https://www.felixcloutier.com/x86/movdir64b


(END)


Linux阅码场原创精华文章汇总

更多精彩,尽在"Linux阅码场",扫描下方二维码关注


球分享

球点赞

球在看