1、原理介绍

使用iommu,可以改变虚拟机外设中断的投递方式。以msi中断为例,msi msg里不再需要填写相关的中断信息,而是转换成interrput index的方式。中断的管理信息(投递方式、目标cpu信息、vector信息)存放在一个叫irte的内存区域里,每个iommu最多可以有64k个irte,iommu通过interrupt index找到对应的irte,iommu的irte基址信息存放在iommu的IRTA(Interrupt Remapping Table Address)寄存器里。

中断请求的格式有两种(Compatibility Format和Remappable Format),如下所示,在Compatibility Format格式下,address需要包含中断的Destination ID信息,Data需要包含中断的delivery Mode、Trigger Mode等信息,并且bit 4的interrupt Format需要清0。

而在Remappable Format模式下,addess主要保存的是Handle信息,并且bit 4置1,data保存subhandle信息,iommu通过Handle及subhandle找到对应的irte。

Remappable Format时,不同的中断号可以共享同一个handle(最终对应同一个irte),比如同一个网卡设备的多个队列中断;也可设置每个中断使用独立的irte,这通过address的bit 3(SHV)位来标识(默认置1,使用共享模式)。iommu根据SHV以及handle值来查找interrupte_index。

if (address.SHV == 0) {interrupt_index = address.handle;
} else {interrupt_index = (address.handle + data.subhandle);
}

2、初始化过程

系统启动时,会根据设置的内核参数intel_iommu决定是否使用iommu,当设置了该参数后,会去解析相关的iommu信息。可以同时存在多个iommu硬件,每个iommu能管理的pci设备是固定的,iommu的相关信息bios会放在table里通知os,os通过读取table相关信息初始化iommu。

kernel_init
    kernel_init_freeable
        smp_prepare_cpus
            native_smp_prepare_cpus
                default_setup_apic_routing
                    enable_IR_x2apic
                        irq_remapping_prepare
                            intel_prepare_irq_remapping
                                dmar_table_init
                                    parse_dmar_table
                                        dmar_parse_one_drhd
                                            alloc_iommu
                                                map_iommu
                                                    dmar_register_drhd_unit(将dmar注册到到全局链表中dmar_drhd_units)

这里的流程主要是os启动时解析iommu的过程,包括bios提供的iommu table信息,映射iommu的io地址空间等。

static int map_iommu(struct intel_iommu *iommu, u64 phys_addr)
{int map_size, err=0;iommu->reg_phys = phys_addr;iommu->reg_size = VTD_PAGE_SIZE;if (!request_mem_region(iommu->reg_phys, iommu->reg_size, iommu->name)) {pr_err("Can't reserve memory\n");err = -EBUSY;goto out;}//映射iommu的io地址空间iommu->reg = ioremap(iommu->reg_phys, iommu->reg_size);if (!iommu->reg) {pr_err("Can't map the region\n");err = -ENOMEM;goto release;}//获取iommu的capilityiommu->cap = dmar_readq(iommu->reg + DMAR_CAP_REG);iommu->ecap = dmar_readq(iommu->reg + DMAR_ECAP_REG);
}

接下来主要是iommu的remapping初始化,第一步先分配好irte空间,并将地址信息设置到IRTA寄存器。

static int intel_setup_irq_remapping(struct intel_iommu *iommu)
{ir_table = kzalloc(sizeof(struct ir_table), GFP_KERNEL);if (!ir_table)return -ENOMEM;pages = alloc_pages_node(iommu->node, GFP_KERNEL | __GFP_ZERO,INTR_REMAP_PAGE_ORDER);bitmap = kcalloc(BITS_TO_LONGS(INTR_REMAP_TABLE_ENTRIES),sizeof(long), GFP_ATOMIC);if (bitmap == NULL) {pr_err("IR%d: failed to allocate bitmap\n", iommu->seq_id);goto out_free_pages;}//设置iommu的irte地址信息ir_table->base = page_address(pages);ir_table->bitmap = bitmap;iommu->ir_table = ir_table;iommu_set_irq_remapping(iommu, eim_mode);return 0;
}static void iommu_set_irq_remapping(struct intel_iommu *iommu, int mode)
{addr = virt_to_phys((void *)iommu->ir_table->base);//将irte地址信息通知给硬件dmar_writeq(iommu->reg + DMAR_IRTA_REG,(addr) | IR_X2APIC_MODE(mode) | INTR_REMAP_TABLE_REG_SIZE);/* Set interrupt-remapping table pointer */writel(iommu->gcmd | DMA_GCMD_SIRTP, iommu->reg + DMAR_GCMD_REG);
}

然后是使能iommu的remapping模式:
ry_to_enable_IR
    irq_remapping_enable    
        intel_enable_irq_remapping
            iommu_enable_irq_remapping

 /* Enable interrupt-remapping */iommu->gcmd |= DMA_GCMD_IRE;iommu->gcmd &= ~DMA_GCMD_CFI;  /* Block compatibility-format MSIs */writel(iommu->gcmd, iommu->reg + DMAR_GCMD_REG);

开启iommu的remapping模式后,还会进一步检查iommu是有有interrupt post能力,如果有,则将intel_irq_remap_ops.capabiliy或上IRQ_POSTING_CAP,后续vcpu启动时会检查该标志。

static inline void set_irq_posting_cap(void)
{struct dmar_drhd_unit *drhd;struct intel_iommu *iommu;//以下几个条件同时成立时,会将intel_irq_remap_ops.capability与上IRQ_POSTING_CAP标志//位,后续vcpu创建的时候会关注该标志位//1)disable_irq_post变量没有置1;//2)CPU支持CMPXCHG16B原子指令(一般支持);//3)iommu的cap具有post能力。if (!disable_irq_post) {/** If IRTE is in posted format, the 'pda' field goes across the* 64-bit boundary, we need use cmpxchg16b to atomically update* it. We only expose posted-interrupt when X86_FEATURE_CX16* is supported. Actually, hardware platforms supporting PI* should have X86_FEATURE_CX16 support, this has been confirmed* with Intel hardware guys.*/if ( cpu_has_cx16 )intel_irq_remap_ops.capability |= 1 << IRQ_POSTING_CAP;for_each_iommu(iommu, drhd)if (!cap_pi_support(iommu->cap)) {intel_irq_remap_ops.capability &=~(1 << IRQ_POSTING_CAP);break;}}
}

最后重新安装irq remapping的操作函数:

static void __init irq_remapping_modify_x86_ops(void)
{x86_io_apic_ops.disable        = irq_remapping_disable_io_apic;x86_io_apic_ops.set_affinity   = set_remapped_irq_affinity;x86_io_apic_ops.setup_entry    = setup_ioapic_remapped_entry;x86_io_apic_ops.eoi_ioapic_pin   = eoi_ioapic_pin_remapped;x86_msi.setup_msi_irqs       = irq_remapping_setup_msi_irqs;x86_msi.setup_hpet_msi      = setup_hpet_msi_remapped;x86_msi.compose_msi_msg      = compose_remapped_msi_msg;
}

3、Guest虚拟机启动后,会为直通设备申请msi中断,然后enable_msi,这个过程被Qemu捕获,Qemu通过ioctl,通知vfio-pci模式设置enable。

vfio_pci_ioctl
    vfio_pci_set_irqs_ioctl
        vfio_pci_set_msi_trigger
            vfio_msi_enable
                pci_enable_msix_range
                    pci_enable_msix
                        msix_capability_init
                            msix_setup_entries

static int msix_setup_entries(struct pci_dev *dev, void __iomem *base,struct msix_entry *entries, int nvec)
{struct msi_desc *entry;int i;for (i = 0; i < nvec; i++) {entry = alloc_msi_entry(dev);if (!entry) {if (!i)iounmap(base);elsefree_msi_irqs(dev);/* No enough memory. Don't try again */return -ENOMEM;}entry->msi_attrib.is_msix = 1;entry->msi_attrib.is_64     = 1;entry->msi_attrib.entry_nr  = entries[i].entry;entry->msi_attrib.default_irq    = dev->irq;//设置entry的io地址信息,base为msix_map_region(dev, msix_table_size(control))//映射到的msix io地址空间,后续需要使用该地址mask、unmask riq,以及设置msi的msg//地址信息等entry->mask_base        = base;entry->nvec_used     = 1;//将所有的entry都存放到msi_list里list_add_tail(&entry->list, &dev->msi_list);}return 0;
}

再接下来主要是setup每个entry

arch_setup_msi_irqs
    irq_remapping_setup_msi_irqs
        do_setup_msix_irqs(遍历mst_list链表,为每个msi_entry设置中断信息)、

1)、如果是第一个msi_entry:
                 msi_alloc_remapped_irq
                     intel_msi_alloc_irq
                         alloc_irte(从iommu->ir_table分配一个可用的irte表索引号)

static int alloc_irte(struct intel_iommu *iommu, int irq, u16 count)
{//从bitmap里获取一个可用的irte indexindex = bitmap_find_free_region(table->bitmap,INTR_REMAP_TABLE_ENTRIES, mask);if (index < 0) {pr_warn("IR%d: can't allocate an IRTE\n", iommu->seq_id);} else {cfg->remapped = 1;irq_iommu->iommu = iommu;//设置irte indexirq_iommu->irte_index =  index;//第一个中断信息sub_handle为0irq_iommu->sub_handle = 0;irq_iommu->irte_mask = mask;irq_iommu->mode = IRQ_REMAPPING;}return index;
}

分配完irte后,就需要去设置irte信息:

setup_msi_irq
    msi_compose_msg
        intel_compose_msi_msg

static void intel_compose_msi_msg(struct pci_dev *pdev,unsigned int irq, unsigned int dest,struct msi_msg *msg, u8 hpet_id)
{cfg = irq_get_chip_data(irq);//获取sub_handle及irte index信息ir_index = map_irq_to_irte_handle(irq, &sub_handle);BUG_ON(ir_index == -1);//获取irteirte = get_irte(irq_2_iommu(irq));//设置irte的trigger_mode、vector、及irq的dest信息prepare_irte(irte, cfg->vector, dest);/* Set source-id of interrupt request *///根据pci信息设置irte的source-idif (pdev)set_msi_sid(irte, pdev);elseset_hpet_sid(irte, hpet_id);modify_irte(irq, irte);//设置irte的address及data信息msg->address_hi = MSI_ADDR_BASE_HI;msg->data = sub_handle;//设置irte为共享模式,多个中断可以共用一个irtemsg->address_lo = MSI_ADDR_BASE_LO | MSI_ADDR_IR_EXT_INT |MSI_ADDR_IR_SHV |MSI_ADDR_IR_INDEX1(ir_index) |MSI_ADDR_IR_INDEX2(ir_index);
}

2)、如果非第1个msi_entry,则不重新分配irte,而是与第一个共享同一个irte,因此这里的流程主要是使用第一个msi_entry分配的irte,并修改其中的subhandle信息,然后通知给硬件。

4、Remapping模式下,iommu会将发往Guest的中断先转发给宿主机,因此需要在宿主机安装中断处理函数。

vfio_msi_set_block
    vfio_msi_set_vector_signal
        request_irq(为irq分配action处理函数vfio_msihandler)

vfio_msihandler主要功能就是通过eventfd的方式通知kvm,kvm进一步将中断信息注入到Guest。

/** MSI/MSI-X*/
static irqreturn_t vfio_msihandler(int irq, void *arg)
{struct eventfd_ctx *trigger = arg;eventfd_signal(trigger, 1);return IRQ_HANDLED;
}

IOMMU之Interrupt Remapping相关推荐

  1. pve远程连接 spcie_proxmox折腾 篇一:解决j3455直通iommu分组问题,PVE内核编译教程...

    更新: 2020.02.22更新内核pve-kernel-5.3.18-2,常规更新,今后不会一有新版本就更新了,估计大版本更新再编译新内核了,每一次都挺折腾的,而且编译出的内核有bug恢复起来挺麻烦 ...

  2. Linux驱动:VFIO概述(vfio/iommu/device passthrough)

    <ARM SMMU原理与IOMMU技术("VT-d" DMA.I/O虚拟化.内存虚拟化)> <提升KVM异构虚拟机启动效率:透传(pass-through).DM ...

  3. vfio概述(vfio/iommu/device passthrough)

    文章目录 1.IOMMU 1.1 IOMMU功能简介 1.2 IOMMU作用 1.3 IOMMU工作原理 1.4 Source Identifier 2.VFIO 2.1 概念介绍 2.2 使用示例 ...

  4. ARM SMMU原理与IOMMU技术(“VT-d” DMA、I/O虚拟化、内存虚拟化)

    名词缩写 ASID:Address Space ID   地址空间标识符 CD:Context Descriptor:  上下文描述符: CTP:Context-table pointer   上下文 ...

  5. intel linux 开发板,Intel IOMMU在Linux上的实现架构

    1.检测平台是否支持DMAR设备 ./drivers/pci/dmar.c->int __init early_dmar_detect(void) { acpi_status status = ...

  6. VFIO硬件依赖——IOMMU机制

    文章目录 IOMMU动机 DMA Remapping Root Table/Context Table Second-Level Page Structure IOMMU Group DMA Map ...

  7. KVM Interrupt Virtualization

    对于x86架构,KVM为每个虚拟机维护一个pic主中断控制器.一个pic从中断控制器以及一个ioapic控制器(使用pic 加 ioapic 的两种芯片的模拟组合,来控制中断信息),ioapic根据自 ...

  8. 英特尔虚拟化技术发展蓝图

     当前非常热门的Virtualization虚拟化技术的出现和应用其实已经有数十年的历史了,在早期,这个技术主要应用在服务器以及大型主机上面,现在,随着PC性能的不断增长,Virtualizati ...

  9. 内推|底层翻身的机会来了,快来看一看!

    hi,大家好,又到了黄金的跳槽季节,喜欢底层技术的同学,可以看一看机会,不跳槽的也可以看一下自己能力是否满足市场需求,随时准备好,机会总是留给有准备的人. 做底层技术有些优势:低层技术需要更多时间学习 ...

  10. linux 内核配置简介

    Gentoo Linux Gentoo内核(gentoo-sources)特有的选项 Gentoo Linux support CONFIG_GENTOO_LINUX 选"Y"后, ...

最新文章

  1. 英伟达最大gpu_英伟达正式发布Ampere架构GPU,完成史上最大性能飞跃
  2. java 方法执行结束局部变量释放_Java方法执行的内存模型
  3. 【转】DATAGRIDVIEW控制
  4. 云上快速搭建Serverless AI实验室
  5. Hibernate简单的保存操作
  6. c语言 数组指针传递给函数_嵌入式开发-C语言-指针与数组
  7. 实战Node:Node实现留言板
  8. SQL语句查询今天、昨天、近7天、近30天、一个月内、上一月 数据
  9. C语言丨筛法求素数(质数)
  10. Jiagu 自然语言处理工具
  11. 基站查询网址、软件、API接口汇总
  12. 2022美团CTF个人决赛WP
  13. Java + OpenCV 实现图片年龄识别(JavaCV)
  14. 直销银行和网上银行区别
  15. 华为手机连电脑当摄像头用_今天才知道,华为手机摄像头还能这样用!还不会用你买什么华为?...
  16. 如何清除弹窗FF新推荐
  17. Triangle Fun UVA - 11437(一个数学定理 + 三角形求面积)
  18. GOFLY在线客服系统/外贸网站在线客服+多语言支持 外贸网站即时通讯工具/中英文切换教程...
  19. 牛客算法竞赛入门笔记1
  20. Sublime3 豆沙绿护眼主题及仿HBuilder绿优主题

热门文章

  1. 02java特性,编译与运行
  2. fp算法例题_大部分人都理解错了的FPgrowth算法
  3. discuz开启url伪静态
  4. 计算机犀牛建人体模型步骤,Clayoo加Rhino如何建模卡通人物2
  5. Windows内核结构
  6. 那些软件可以测试网速,怎么测试网速 测试网速用什么软件
  7. 搏一搏,单车变摩托!华为天才少年耗时四月将自行车强势升级为自动驾驶
  8. 百度的“知心搜索”揭秘
  9. 盘点(腾讯字节谷歌等大厂)面试中常见的智力题
  10. C语言:求X的Y次方