intel 的iommu 是iommu框架的一个实现案例。

由于intel 的iommu 实现得比arm smmv3复杂得多,里面概念也多,所以针对intel 实现的iommu 案例的初始化部分进行一些讲解,本文针对4.19内核。

Intel IOMMU的初始化函数在哪调用的呢?

它的初始化函数是:

int __init intel_iommu_init(void)
{
int ret = -ENODEV;
struct dmar_drhd_unit *drhd;
struct intel_iommu *iommu; /* VT-d is required for a TXT/tboot launch, so enforce that */
force_on = tboot_force_iommu();

那这个函数是在常见的模块初始化里面调用的么?

事实上,它的调用链是这样的,

int __init detect_intel_iommu(void)//caq:IOMMU_INIT_POST调用这个,有amd的,ibm大型机的
{//caq:此时还没有memory allocator
int ret;
struct dmar_res_callback validate_drhd_cb = {
.cb[ACPI_DMAR_TYPE_HARDWARE_UNIT] = &dmar_validate_one_drhd,
.ignore_unhandled = true,
}; down_write(&dmar_global_lock);
ret = dmar_table_detect();//caq:bios/uefi提供的dmar table
if (!ret)
ret = dmar_walk_dmar_table((struct acpi_table_dmar *)dmar_tbl,
&validate_drhd_cb);
if (!ret && !no_iommu && !iommu_detected && !dmar_disabled) {
iommu_detected = 1;//caq:返回正常就设定这个标志位
/* Make sure ACS will be enabled */
pci_request_acs();
} #ifdef CONFIG_X86
if (!ret)
**x86_init.iommu.iommu_init = intel_iommu_init;//caq:pci_iommu_init 会统一执行iommu_init**
#endif if (dmar_tbl) {
acpi_put_table(dmar_tbl);//caq:释放资源
dmar_tbl = NULL;
}
up_write(&dmar_global_lock); return ret ? ret : 1;
}

在 detect_intel_iommu 函数中,有一个关键行:

x86_init.iommu.iommu_init = intel_iommu_init;//caq:pci_iommu_init 会统一执行iommu_init

然后在 pci_iommu_init 会统一执行 iommu_init 。如下:

static int __init pci_iommu_init(void)
{
struct iommu_table_entry *p; **x86_init.iommu.iommu_init();//caq:执行前面 detect_intel_iommu 里面对 x86_init.iommu 赋值的指针。** for (p = __iommu_table; p < __iommu_table_end; p++) {
if (p && (p->flags & IOMMU_DETECTED) && p->late_init)
p->late_init();
} return 0;
}

如下的调用链:

kernel_init ->
kernel_init_freeable ->
do_one_initcall ->
pci_iommu_init ->
x86_init.iommu.iommu_init

以上就是intel 的iommu 初始化调用链分析。

常见问题:

1、intel怎么管理那么多iommu硬件?

使用一个全局的 dmar_drhd_units 链表来管理对应的iommu硬件,dmar_drhd_unit 的list 成员串接在这个全局链表中。

crash> list -H dmar_drhd_units -s dmar_drhd_unit.segment,devices_cnt
ffff8b83ff078780
segment = 0
devices_cnt = 0
ffff8b83ff078060
segment = 0
devices_cnt = 0
ffff8b83ff065680---------管理了三个设备
segment = 0
devices_cnt = 3
ffff8b83ff078ae0
segment = 0
devices_cnt = 0
ffff8b83ff078c60
segment = 0
devices_cnt = 0
ffff8b83ff078480
segment = 0
devices_cnt = 0
ffff8b83ff072180--------------管理了8个设备
segment = 0
devices_cnt = 8
ffff8b83ff078240
segment = 0
devices_cnt = 0

我们可以看到,bios/uefi至少枚举了8个iommu的硬件,其中两个intel_iommu设备 分别管理的3个和8个设备。

2、是不是devices_cnt就是所有的设备呢?

实际单板上,pci设备就远远不止11个设备,那么其他设备怎么办呢?devices_cnt 为0,不代表它就不管理设备,相反,还得继续看include_all标志,

事实上,这个在如下函数有体现:

struct dmar_drhd_unit *
dmar_find_matched_drhd_unit(struct pci_dev *dev)//caq:pci设备找归属的drhd_unit 硬件
{
struct dmar_drhd_unit *dmaru;
struct acpi_dmar_hardware_unit *drhd; dev = pci_physfn(dev); rcu_read_lock();
for_each_drhd_unit(dmaru) {
drhd = container_of(dmaru->hdr,
struct acpi_dmar_hardware_unit,
header); if (dmaru->**include_all **&&
drhd->segment == pci_domain_nr(dev->bus))//caq:include_all是segment内的,不是整个系统的
goto out; if (dmar_pci_device_match(dmaru->devices,//caq:在对应的unit 的devices 数组中找到了dev
dmaru->devices_cnt, dev))
goto out;
}
dmaru = NULL;//caq:否则返回NULL
out:
rcu_read_unlock(); return dmaru;
}

也就是说,通过一个dmar硬件,支持include_all属性,将很多设备收纳了其中,比如看的其他一个服务器的打印:

crash> dmar_drhd_unit.iommu,include_all,devices_cnt ffff8d1240078360
iommu = 0xffff8d42ff043500
include_all = 1 '\001'
devices_cnt = 0
crash> intel_iommu.name 0xffff8d42ff043500
name = "dmar7\000\000\000\000\000\000\000"
上面可以看到,dmar7的 devices_cnt 为0,但是真实的管理设备个数却很多,如下:
[root@localhost devices]# pwd
/sys/class/iommu/dmar7/devices
[root@localhost devices]# ll |wc -l
281

3、如何查看一个设备归属的iommu?

除此之外,真正让一个device跟iommu关联的函数,其实可以查看:

//caq:bus和devfn是出参
static struct intel_iommu *device_to_iommu(struct device *dev, u8 *bus, u8 *devfn)
{
struct dmar_drhd_unit *drhd = NULL;
struct intel_iommu *iommu;
struct device *tmp;
struct pci_dev *ptmp, *pdev = NULL;
u16 segment = 0;
int i; if (iommu_dummy(dev))//caq:该设备没有关联归属iommu,
return NULL; if (dev_is_pci(dev)) {//caq:pci设备,也就是他的bus type 是pci_bus_type
struct pci_dev *pf_pdev; pdev = to_pci_dev(dev); #ifdef CONFIG_X86
/* VMD child devices currently cannot be handled individually */
if (is_vmd(pdev->bus))//caq:VMD:Intel Volume Management Device
return NULL;
#endif /* VFs aren't listed in scope tables; we need to look up
* the PF instead to find the IOMMU. */
pf_pdev = pci_physfn(pdev);//caq:以 pf 为dev,在drhd管理的dev中,是看不到vf的
dev = &pf_pdev->dev;
segment = pci_domain_nr(pdev->bus);//caq:segment其实就是bus归属的domain
} else if (has_acpi_companion(dev))
dev = &ACPI_COMPANION(dev)->dev; rcu_read_lock();
for_each_active_iommu(iommu, drhd) {//caq:除掉ignored 的所有 intel_iommu
if (pdev && segment != drhd->segment)
continue; for_each_active_dev_scope(drhd->devices,
drhd->devices_cnt, i, tmp) {
if (tmp == dev) {
/* For a VF use its original BDF# not that of the PF
* which we used for the IOMMU lookup. Strictly speaking
* we could do this for all PCI devices; we only need to
* get the BDF# from the scope table for ACPI matches. */
if (pdev && pdev->is_virtfn)//caq:如果是vf 设备,则 拿 pf
goto got_pdev; *bus = drhd->devices[i].bus;//caq:取bus 号
*devfn = drhd->devices[i].devfn;//caq:取 device和func 号
goto out;
} ........ ptmp = to_pci_dev(tmp);
if (ptmp->subordinate &&//caq:范围查找
ptmp->subordinate->number <= pdev->bus->number &&
ptmp->subordinate->busn_res.end >= pdev->bus->number)
goto got_pdev;
} if (pdev && drhd->include_all) {
got_pdev:
*bus = pdev->bus->number;
*devfn = pdev->devfn;
goto out;
}
}
iommu = NULL;
out:
rcu_read_unlock(); return iommu;
}

从如上函数可以看出,跟iommu关联的设备数,可能会远远大于在drhd中看到的devices_cnt,事实上,这个devices_cnt很容易让人误解。

4、dmar_disabled 的意思?

但是需要注意的是,如果intel_iommu=on 没有设置,且 CONFIG_INTEL_IOMMU_DEFAULT_ON 这个config没有开启,则会导致 dmar_disabled 为1.

#ifdef CONFIG_INTEL_IOMMU_DEFAULT_ON
int dmar_disabled = 0;//caq:默认是否开启取决于 CONFIG_INTEL_IOMMU_DEFAULT_ON 是否配置
#else
int dmar_disabled = 1;
#endif /*CONFIG_INTEL_IOMMU_DEFAULT_ON*/

这个 dmar_disabled 的值会影响 int __init intel_iommu_init(void) 函数的行为,

int __init intel_iommu_init(void)
{
.....
if (no_iommu || dmar_disabled) {//caq:
/*
* We exit the function here to ensure IOMMU's remapping and
* mempool aren't setup, which means that the IOMMU's PMRs
* won't be disabled via the call to init_dmars(). So disable
* it explicitly here. The PMRs were setup by tboot prior to
* calling SENTER, but the kernel is expected to reset/tear
* down the PMRs.
*/
if (intel_iommu_tboot_noforce) {
for_each_iommu(iommu, drhd)
iommu_disable_protect_mem_regions(iommu);
} /*
* Make sure the IOMMUs are switched off, even when we
* boot into a kexec kernel and the previous kernel left
* them enabled
*/
intel_disable_iommus();//caq:导致intel_iommu的功能被关闭,也就是调用 iommu_disable_translation
goto out_free_dmar;
}
.....

所以我们经常能看到硬件上是有很多drhd的硬件,但是软件上并没有使能,当然也就没有注册到 iommu框架中的,iommu_device_register不会被调用,

全局的 iommu_device_list 只是空的,留下寂寞空虚冷。

而如果软件上使能了的话,iommu_device_list 就会保留有当前期active的 iommu device,如下:

crash> list -H iommu_device_list -s iommu_device.ops-----------//caq:注意,这个链表只有active的成员。
ffff9994ff0709c8
ops = 0xffffffff952ebcc0
ffff99a53f043dc8
ops = 0xffffffff952ebcc0
crash> dis -l 0xffffffff952ebcc0
0xffffffff952ebcc0 <intel_iommu_ops>: adcb $0xff,-0x6b72(%rdi)

非active的iommu,不会展示在sysfs中,如下案例:

具备iommu功能的硬件打印:
crash> list -H dmar_drhd_units -s dmar_drhd_unit.segment,devices_cnt,include_all,ignored
ffff8d1240078e40
segment = 0
devices_cnt = 0
include_all = 0 '\000'
ignored = 1 '\001'
ffff8d1240078240
segment = 0
devices_cnt = 1
include_all = 0 '\000'
ignored = 0 '\000'
ffff8d1240065000
segment = 0
devices_cnt = 3
include_all = 0 '\000'
ignored = 0 '\000'
ffff8d1240078780
segment = 0
devices_cnt = 0
include_all = 0 '\000'
ignored = 1 '\001'
ffff8d12400783c0
segment = 0
devices_cnt = 0
include_all = 0 '\000'
ignored = 1 '\001'
ffff8d1240078960
segment = 0
devices_cnt = 0
include_all = 0 '\000'
ignored = 1 '\001'
ffff8d1240072fc0
segment = 0
devices_cnt = 8
include_all = 0 '\000'
**ignored = 1** '\001'//caq:虽然 devices_cnt 不为0,但是依然被ignored。
ffff8d1240078360
segment = 0
devices_cnt = 0
include_all = 1 '\001'
ignored = 0 '\000'
除掉这些ignored为1的,可以看到active的iommu设备,
angus@jm212:/sys/class/iommu$ pwd
/sys/class/iommu
angus@jm212:/sys/class/iommu$ ls
dmar4 dmar5 dmar7

iommu分析之---intel iommu初始化的更多相关文章

  1. iommu分析之---intel irq remap框架实现

    背景介绍: IRQ域层级结构: 在某些架构上,可能有多个中断控制器参与将一个中断从设备传送到目标CPU. 让我们来看看x86平台上典型的中断传递路径吧 Device --> IOAPIC -&g ...

  2. 《深入理解Spark:核心思想与源码分析》——SparkContext的初始化(叔篇)——TaskScheduler的启动

    <深入理解Spark:核心思想与源码分析>一书前言的内容请看链接<深入理解SPARK:核心思想与源码分析>一书正式出版上市 <深入理解Spark:核心思想与源码分析> ...

  3. 【spring源码分析】IOC容器初始化(总结)

    前言:在经过前面十二篇文章的分析,对bean的加载流程大致梳理清楚了.因为内容过多,因此需要进行一个小总结. 经过前面十二篇文章的漫长分析,终于将xml配置文件中的bean,转换成我们实际所需要的真正 ...

  4. 【spring源码分析】IOC容器初始化(二)

    前言:在[spring源码分析]IOC容器初始化(一)文末中已经提出loadBeanDefinitions(DefaultListableBeanFactory)的重要性,本文将以此为切入点继续分析. ...

  5. 【spring源码分析】IOC容器初始化(三)

    前言:在[spring源码分析]IOC容器初始化(二)中已经得到了XML配置文件的Document实例,下面分析bean的注册过程. XmlBeanDefinitionReader#registerB ...

  6. 【spring源码分析】IOC容器初始化(四)

    前言:在[spring源码分析]IOC容器初始化(三)中已经分析了BeanDefinition注册之前的一些准备工作,下面将进入BeanDefinition注册的核心流程. //DefaultBean ...

  7. 【spring源码分析】IOC容器初始化(五)

    前言:前几篇文章已经将BeanDefinition的加载过程大致分析完成,接下来继续分析其他过程. AbstractApplicationContext#refresh public void ref ...

  8. 【spring源码分析】IOC容器初始化(七)

    前言:在[spring源码分析]IOC容器初始化(六)中分析了从单例缓存中加载bean对象,由于篇幅原因其核心函数 FactoryBeanRegistrySupport#getObjectFromFa ...

  9. 【spring源码分析】IOC容器初始化(九)

    前言:上篇文章末尾提到createBeanInstance方法中使用工厂方法实例化Bean对象,本文将对该方法进行分析. AbstractAutowireCapableBeanFactory#inst ...

随机推荐

  1. css:音乐唱片机随着播放暂停而旋转暂停

    唱片机由两部分组成,一个是磁针,另一个是唱片 1. 先完成磁针随着播放按钮进行是否在唱片上的切换 原理:将播放暂停状态存入布尔值isbtnShow中,根据isbtnShow的值切换磁针的class. ...

  2. XtraBackup 搭建从库的一般步骤及 XtraBackup 8.0 的注意事项

    搭建从库,本质上需要的只是一个一致性备份集及这个备份集对应的位置点信息.之前介绍的几个备份工具(MySQL中如何选择合适的备份策略和备份工具)均可满足. 这里,我们重点看看如何基于 XtraBacku ...

  3. 基于MybatisPlus代码生成器(2.0新版本)

    一.模块简介 1.功能亮点 实时读取库表结构元数据信息,比如表名.字段名.字段类型.注释等,选中修改后的表,点击一键生成,代码成即可提现出表结构的变化. 单表快速转化restful风格的API接口并对 ...

  4. RPA跨系统自动生成采购订单

    1.从开发器启动机器人 2.RPA登录友采云 3.RPA根据筛选条件,导出采购订单 4.RPA请并登录NC 5.RPA把读取到的数据,逐个录入到NC系统中,并存储到Excel中 6.RPA将最终的Ex ...

  5. VisionPro · C# · 加密狗检查程序

    写VisionPro C#项目时,我们需要在程序的启动时加载各种配置文件,以及检查软件授权,以下代码即检查康耐视加密狗在线状态,如查无加密狗,关闭程序启动进程并抛出异常. 1 using System ...

  6. 【python基础】第06回 运算符和流程控制 1

    本章内容概要 1.运算符 2.流程控制 本章内容详解 1.运算符 什么是运算符? 运算符用于执行程序代码运算,会针对一个以上操作数项目来进行运算.例如:2+3,其操作数是2和3,而运算符则是" ...

  7. P2599 [ZJOI2009]取石子游戏 做题感想

    题目链接 前言 发现自己三岁时的题目都不会做. 我发现我真的是菜得真实. 正文 神仙构造,分讨题. 不敢说有构造,但是分讨我只服这道题. 看上去像是一个类似 \(Nim\) 游戏的变种,经过不断猜测结 ...

  8. HTML知识点概括——一篇文章带你完全掌握HTML

    HTML知识点概括 前端三件套分别是HTML3,CSS5,JavaScript 稍微介绍一下W3C标准: 结构化标准语言(HTML) 表现标准语言(CSS) 行为标准(DOM,JavaScript) ...

  9. 构建 API 的7个建议【翻译】

    迄今为止,越来越多的企业依靠API来为客户提供服务,以确保竞争的优势和业务可见性.出现这个情况的原因是微服务和无服务器架构正变得越来越普遍,API作为其中的关键节点,继承和承载了更多业务. 在这个前提 ...

  10. 西文字符与中文GBK编码的区别

    一般来讲二者读取的时候西文字符的数值是正,而中文字符的数值是负的,此时读取的是中文字符的前一半,需要再读取一个char类型的数据,在大多数运行环境下这个规则都是用. ps:转自算法竞赛的笔记,要注意在 ...