x86 vt-d在linux中的应用

x86 vt-d在linux中的应用

在新公司已近两年,所学甚多甚杂,已经不能用一篇水文来记录里面的点点滴滴,这是我一直没有写一篇总结的原因.现在想想随兴所至一些更好。


翻看前文,发现还差一篇风河hypervisor硬件虚拟化的文章,懒得写了,浪费时间.看过xen的代码后觉得hypervisor就是只小麻雀.以后有精力了写写xen里面对x86硬件虚拟化技术的利用,那将是一个大篇幅.今天介绍下linux里对x86硬件虚拟化技术的利用,算是个引子.xen里面用到硬件虚拟化那是理所当然,有意思的是linux也用到了里面的东西,vt-d,sr-iov.它当然不会用vt-x,这里不讲kvm,它本身就把linux变成了虚拟机而不是一个纯内核了.同样我不会写代码分析的细节,那是很无聊的事,只写我认为有意思的东西.


vt-d里主要有两个新技术,int remapping和dma remapping.
int remap在服务器配置中经常见到,如果系统中cpu超过255个或有apic_id>255的时候,intr是必须打开的,因为ioxapic系统即使在物理目标模式也只支持8bit的cpu_id,没有办法把中断分发给所有的cpu.而打开intr后,虽然ioapic中索引是16bit,但是irte中可以有32bit的物理地址或逻辑地址,最大能有2的32次个cpu.当然也有一些老系统只支持intr的xapic模式,也就是不能和x2apic配合,那和不打开也就没什么分别了,只在中断迁移时能保证不丢中断.intr关闭时,ioapic或msi/msix中触发中断后转换成内存写消息向上传到北桥,被识别为中断后自动分发给相应的cpu,中间还会经过cpu的仲裁与选择,识别是通过特殊地址信息fee来识别的.linux中已经基本有完善的支持了,只是缺msi这块,msi目前只支持单个中断,倒不是无法实现,只是代码要有大的改动,申请一段连续的vector和irq号都是比较困难的。msix比较不错,没有32个vector限制,而且每个vector和irq都不需要连续,缺点是会占用一些mem空间。另外打开msi中断后记得关闭legacy int。如果打开intr的话,rte或msix msg中要填的是一个索引,不是关于这个irq的所有信息,比如触发模式,目标模式,目标地址,vector等。这个index会去索引内存中一个irte的表,找到后取出其中的信息转发给apic。有意思的是这里面的信息就是irq的所有信息。当然那个表未必是一个,要看这个系统是不是只有一个pci段,或者说是不是只有一个drhd单元。另外irte是有缓存的,修改irte后要有指令清除irte缓存.


所谓dmar就是给设备一个地址空间映射,设备对内存的访问要通过一个页表结构转换成真正的访存地址.也就是说设备地址就是个设备虚拟地址,真正的访存地址是设备物理地址。这个页表结构和cpu的页表结构还有vmx的ept相似,可以有大页,还有地址映射的缓存即所谓iotlb,等同于cpu的tlb,但是在北桥里.有的系统为了优化,在设备中也提供了device-iotlb,这些对软件是透明的,只有特殊的刷新指令序列能改变它们.dmar在一些特殊场合有用,比如32bit pci设备要dma数据到4G以上地址,dmar可以提供这种映射,让高端地址在pci设备中映射为32bit的范围之内.关闭dmar的话只能用软件模拟了,即bounce-buffer.
整个iommu功能是在acpi表结构中提供的,目前有4种子表,drhd,rmrr,atsr,rhsa。其中drhd是最本文最关键的一个表,它枚举了每个dma重映射单元及属于它的下属设备,每个drhd有指针指向一个reg集合,里面会有irte表基址和dmar root entry,root entry数组,256项,指向context entry数组,context entry指向顶级页表。整个结构是由sid索引的,sid通常是bus,dev,fn,对pci桥下的设备,是桥的sid,也就是说所有pci桥下的设备都只能处于同一个域,具体在xen中的话只能直接分配桥下所有的设备给同一个domain。hpet和ioapic这两个特殊设备的sid是通过device scope得到的。rmrr指定保留内存,目前只在usb和显卡中用到,要注意不要将页表中含有这些保留内存的映射。否则会导致系统panic。atsr是关于device-iotlb,rhsa指定drhd单元的node亲和性,可用于各种表项内存分配的优化。写来写去,又虎头蛇尾了,但还是那句老话,随兴就好。

你可能感兴趣的:(x86 vt-d在linux中的应用)