Linux SVA特性分析

-v0.1 2019.9.15 Sherlock init
-v0.2 2019.9.18 Sherlock update数据结构和动态分析
-v0.3 2019.10.13 Sherlock add性能分析
-v0.4 2019.10.14 Sherlock add数据结构说明
-v0.5 2019.10.31 Sherlock add iommu_sva_set_ops, iommu_sva_get_pasid

简介: 最近Linux社区在上传SVA特性(share virtual memory), 简单讲这个特性可以达到
这样的功能,就是可以是设备直接使用进程里的虚拟地址。如果把设备操作映射到用户态,
就可以在用户态叫设备直接使用进程虚拟地址。设备可以使用进程虚拟地址做DMA,如果
设备功能强大,比如可以执行一段指令,设备甚至可以直接执行相应进程虚拟地址上的
指令。目前SVA特性正处于上传阶段,这个特性的补丁涉及到IOMMU, PCI, 内存管理(MM),
SMMU, VFIO, 虚拟化,DT, ACPI等方面的修改,所以补丁比较分散,不过好在这个特性的
上传者把相关补丁收集在了一个分支上,即https://jpbrucker.net/sva/ 的sva/current
分支上。想要了解SVA的整体,可以直接查看这个分支上的各个提交。需要说明是,这个
分支的base是5.2-rc7, 在5.3中这个分支上的少量补丁已经合入Linux主线,如果想用最
新内核测试SVA需要做一定的适配工作。本文的分析以base是5.2-rc7的sva/current分支
上的补丁为基础。

0. 使用场景介绍


如上,SVA特性可以做到进程虚拟地址在进程和设备之间共享。最直观的使用场景就是在
用户态做DMA。

             +------------------+     +----------------+|   进程           |     |  进程          |       ...|                  |     |                || va = malloc();   |     |                || set dma dst: va  |     |                ||      +---------+ |     |                |          用户态+------+ mmap io +-+     +----------------+<---------+         +------------------------------------------> +---------+内核<--------------------------------------------------------------> +--------------+|  DMA dst: va ||              ||  设备        |+--------------+

如上图所示,在SVA的支持下,我们可以在用户态进程malloc一段内存,然后把得到va
直接配置给设备的DMA,然后启动DMA向va对应的虚拟地址写数据,当然也可以从va对应
的虚拟地址上往设备读数据。这里我们把设备DMA相关的寄存器先mmap到用户态,这样
DMA操作在用户态就可以完成。

可以注意到,SVA可以支持功能很大一部分取决于设备的功能,SVA就是提供一个进程和
设备一致的虚拟地址空间,其他的设备想怎么用都可以。如上,如果设备做的足够强,
设备完全可以执行va上对应的代码。

可以看到,设备完全可以把自身的资源分配给不同的进程同时使用。

为了满足上面的使用场景,SVA特性需要硬件支持IOMMU以及设备发起缺页。在下一节
先介绍硬件,再基于此分析SVA的软件实现。

1. 硬件基础介绍


本文以ARM64体系结构为基础分析,在ARM64下,IOMMU指的就是SMMU。对于设备,ARM64
下有平台设备和PCI设备。整体的硬件示意图如下,图中也画出了硬件工作时相关的内存
里的数据结构。

               +-----+| CPU |+--+--+|             v             +-----+           +---------------------------------------------+| MMU |-----------+------------------------------------+        |+--+--+           | DDR                                |        ||              |                                    |        |v              |                                    |        |system bus ------------------> |                                    |        |^              |                                    v        ||   SID/SSID   |      +-----+     +----+      +------------+ ||       +------+----> | STE |---->| CD |----->| page table | ||       |      |      +-----+     +----+      +------------+ ||       |      |       ...        | CD |----->| page table | ||       |      |                  +----+      +------------+ |IRQs    |       |      |                  | .. |      | ..         | |^ |^  ^   |      |                  +----+      +------------+ || ||  |   |      |                  | CD |----->| page table | |+---------+-++--+---+---+  |                  +----+      +------------+ || SMMU    |  |  |   |   |  |                                             ||         |  |  |       |  |     +-------------+                         ||  +------+--+-CMD Q ---+--+---->| CMD queue   |                         |+--v--+   |  |          |  |     +-------------+                         || PRI |---> PRI Q ------+--+---->| PRI queue   |                         |+-----+   |             |  |     +-------------+                         || ATS |  EVENT Q -------+--+---->| EVENT queue |                         |+-----+-----------------+  |     +-------------+                         |^ |     ^            ^   +---------------------------------------------+| |     |            || v     | BDF/PASID  +--------------++---------+-------------+             | |         RP            |             | +-----------------------+             | ^ |       ^                         | | v       |  BDF/PASID              | +-+----+----+-+---------+  +-----+----+----------+| ATC  |      |         |  |     |               |                       +------+      |         |  | DMA |               |                       | PRI  |      |  EP     |  |     |               |                       +------+ DMA  |         |  +-----+  platform dev |                       +-------------+         |  |                     |                       +-----------------------+  +---------------------+

基于上一节中提到的使用场景, 我们梳理硬件中的逻辑关系。调用malloc后,其实只是
拿到了一个虚拟地址,内核并没有为申请的地址空间分配实际的物理内存,直到访问这
块地址空间时引发缺页,内核在缺页流程里分配实际的物理内存,然后建立虚拟地址到
物理内存的映射。这个过程需要MMU的参与。设想SVA的场景中,先malloc得到va, 然后
把这个va传给设备,配置设备DMA去访问该地址空间,这时内核并没有为va分配实际的
物理内存,所以设备一侧的访问流程必然需要进行类似的缺页请求。支持设备侧缺页
请求的硬件设备就是上面所示的SMMU,其中对于PCI设备,还需要ATS、PRI硬件特性支持。
平台设备需要SMMU stall mode支持(使用event queue)。PCI设备和平台设备都需要
PASID特性的支持。

如上图所示,引入SVA后,MMU和SMMU使用相同的进程页表, SMMU使用STE表和CD表管理
当前SMMU下设备的页表,其中STE表用来区分设备,CD表用来区分同一个设备上分配给
不同进程使用的硬件资源所对应的进程页表。STE表和CD表都需要SMMU驱动预先分配好。

SMMU内部使用command queue,event queue,pri queue做基本的事件管理。当有相应
硬件事件发生时,硬件把相应的描述符写入event queue或者pri queue, 然后上报中断。
软件使用command queue下发相应的命令操作硬件。

PCI设备和平台设备的硬件缺页流程有一些差异,下面分别介绍。对于PCI设备,ATS,
PRI和PASID的概念同时存在于PCIe和SMMU规范中。对于ATS的介绍可以参考这里:
https://blog.csdn.net/scarecrow_byr/article/details/74276940。简单讲,ATS特性
由设备侧的ATC和SMMU侧的ATS同时支持,其目的是在设备中缓存va对应的pa,设备随后
使用pa做内存访问时无需经过SMMU页表转换,可以提高性能。PRI(page request
interface)也是需要设备和SMMU一起工作,PCIe设备可以发出缺页请求,SMMU硬件在解
析到缺页请求后可以直接将缺页请求写入PRI queueu, 软件在建立好页表后,可以通过
CMD queue发送PRI response给PCIe设备。具体的ATS和PRI的实现是硬件相关的,目前
市面上还没有实现这两个硬件特性的PCIe设备,但是我们可以设想一下ATS和PRI的硬件
实现,最好的实现应该是软件透明的,也就是软件配置给设备DMA的访问地址是va, 软件
控制DMA发起后,硬件先发起ATC请求,从SMMU请求该va对应的pa,如果SMMU里已经有va
到pa的映射,那么设备可以得到pa,然后设备再用pa发起一次内存访问,该访问将直接
访问对应pa地址,不在SMMU做地址翻译,如果SMMU没有va到pa的映射, 那么设备得到
这个消息后会继续向SMMU发PRI请求,设备得到从SMMU来的PRI response后发送内存访问
请求,该请求就可以在SMMU中翻译得到pa, 最终访问到物理内存。

PRI请求是基于PCIe协议的, 平台设备无法用PRI发起缺页请求。实际上,平台设备是无法
靠自身发起缺页请求的,SMMU用stall模式支持平台设备的缺页,当一个平台设备的内存
访问请求到达SMMU后,如果SMMU里没有为va做到pa的映射,硬件会给SMMU的event queue
里写一个信息,SMMU的event queue中断处理里可以做缺页处理,然后SMMU可以回信息给
设备(fix me: 请求设备重发,还是smmu缺页处理后已经把该访问翻译后送到上游总线)。
实际上, SMMU使用event queue来处理各种错误异常,这里的stall模式是借用了event
queue来处理缺页。

可以注意到PRI和stall模式完成缺页的区别是,PRI缺页的时候并不是在IO实际发生的
时候,因为如果PRI response表示PRI请求失败,硬件完全可以不发起后续的IO操作。
而stall模式,完全发生在IO请求的途中。所以,他被叫做stall模式。

2. 数据结构


   struct device+-> struct iommu_param+-> struct iommu_fault_param+-> handler(struct iommu_fault, void *)+-> faults list+-> struct iopf_device_param+-> struct iopf_queue+-> work queue+-> devices list+-> wait queue+-> struct iommu_sva_param

在引起缺页的外设的device里,需要添加缺页相关的数据结构。handler是缺页要执行
的函数,具体见下面动态流程分析。iopf_queue在smmu驱动初始化时添加,这里iopf_queue
可能是eventq对应的,也可能是priq对应的,iopf_queue是smmu里的概念,所以同一个
iopf_queue会服务同一个smmu下的所有外设, 上面的devices list链表就是用来收集这个
smmu下的外设。

  struct iommu_bond+-> struct iommu_sva+-> device+-> struct iommu_sva_ops+-> iommu_mmu_exit_handle ?+-> struct io_mm+-> pasid+-> device list+-> mm+-> mmu_notifier+-> iommu_ops+-> attach+-> dettach+-> invalidat+-> release+-> mm list+-> device list+-> wait queue

下面的图引用自JPB的补丁,该图描述的是建立好的静态数据结构之间的关系,以及IOMMU
(e.g. SMMU的STE和CD表在这种数据结构下的具体配置情况)表格的配置。用这个图可以
很好说明iommu_bond中的各个数据结构的意义以及之间的关系。

iommu_bond这个结构并不对外, 用下面的iommu_sva_bind_device/unbind接口时,函数
参数都是iommu_sva。使用SVA的设备可以把一个设备的一些资源和一个进程地址空间绑定,
这种绑定关系是灵活的,比如可以一个设备上的不同资源和不用的进程地址空间绑定
(bond 1, bond 2), 还可以同一个设备上的资源都绑定在一个进程的地址空间上(bond 3,
bond 4)。从进程地址空间的角度看,一个进程地址空间可能和多个设备资源绑定。

iommu_bond指的就是一个绑定,io_mm指的是绑定了外设资源的一个进程地址空间。
io_pgtables是指内核dma接口申请内存的页表。

              ___________________________|  IOMMU domain A           ||  ________________         || |  IOMMU group   |        +------- io_pgtables| |                |        || |   dev 00:00.0 ----+------- bond 1 --- io_mm X| |________________|   \    ||                       '----- bond 2 ---.|___________________________|             \___________________________               \|  IOMMU domain B           |             io_mm Y|  ________________         |             / /| |  IOMMU group   |        |            / /| |                |        |           / /| |   dev 00:01.0 ------------ bond 3 -' /| |   dev 00:01.1 ------------ bond 4 --'| |________________|        ||                           +------- io_pgtables|___________________________|PASID tablesof domain A.->+--------+/ 0 |        |-------> io_pgtable/    +--------+Device tables  /   1 |        |-------> pgd X+--------+  /      +--------+00:00.0 |      A |-'     2 |        |--.+--------+         +--------+   \:        :       3 |        |    \+--------+         +--------+     --> pgd Y00:01.0 |      B |--.                    /+--------+   \                  |00:01.1 |      B |----+   PASID tables  |+--------+     \   of domain B  |'->+--------+   |0 |        |-- | --> io_pgtable+--------+   |1 |        |   |+--------+   |2 |        |---'+--------+3 |        |+--------+

相关接口:

    - iommu_dev_enable_feature: 准备和sva相关的smmu中的管理结构, 该接口可以在设备驱动里调用,用来使能sva的功能+-> arm_smmu_dev_enable_feature+-> arm_smmu_dev_enable_sva+-> iommu_sva_enable+-> iommu_register_device_fault_handler(dev, iommu_queue_iopf, dev)/* 动态部分将执行iommu_queue_iopf */把iommu_queue_iopf赋值给iommu_fault_param里的handler                  +-> iopf_queue_add_device(struct iopf_queue, dev)把相应的iopf_queue赋值给iopf_device_param里的iopf_queue, 这里有pri对应的iopf_queue或者是stall mode对应的iopf_queue。初始化iopf_device_param里的wait queue对应iopf_queue的初始化在在smmu驱动probe流程中: e.g.arm_smmu_init_queues+-> smmu->prq.iopf = iopf_queue_alloc+-> alloc_workqueue分配以及初始化iopf_queue里的工作队列+-> arm_smmu_enable_pri调用PCI函数是能EP设备的PRI功能- iommu_sva_bind_device: 将设备和mm绑定, 该接口可以在设备驱动里调用,把一个设备和mm绑定在一起。返回struct iommu_sva *+-> iommu_sva_bind_group+-> iommu_group_do_bind_dev+-> arm_smmu_sva_bind+-> arm_smmu_alloc_shared_cd(mm)分配相应的CD表项,并且把CD表项里的页表地址指向mm里保存的进程页表地址。这个函数主要配置SMMU的硬件+-> iommu_sva_bind_generic(dev, mm, cd, &arm_smmu_mm_ops, drvdata)+-> io_mm_alloc分配io_mm以及初始化其中的数据域段, 向mm注册io_mm的notifier/* to do: mm发生变化的时候通知io_mm */+-> mmu_notifier_register+-> io_mm_attach+-> init_waitqueue_head初始化iommu_bond里的等待队列mm_exit_wq /* to do: 作用 */+-> io_mm->ops->attach(bond->sva.dev, io_mm->pasid, io_mm->ctx)调用e.g.SMMU arm_smmu_mm_ops里的attach函数+-> arm_smmu_mm_attach+-> __arm_smmu_write_ctx_desc下发SMMU command使能CD配置。可以看到arm_smmu_mm_ops里的这一组回调函数基本都是下发SMMU命令控制CD/ATC/TLB相关的配置- iommu_sva_unbind_device+-> iopf_queue_flush_dev+-> iommu_unbind_locked(to_iommu_bond(handle))这里的handle是一个struct iommu_sva
- iommu_sva_set_ops(iommu_sva, iommu_sva_ops)这个接口把iommu_sva_ops传递给iommu_sva, iommu_sva_ops包含mm_exit回调。在上面的iommu_sva_bind_generic里会调用mmu_notifier_register给当前的mm里注册一个notifier,在mm发生改变的时候,mm子系统可以触发notifier里的回调函数。当前的代码里,是在notifier的release回调里去调用iommu_sva里保存的mm_exit回调函数。SVA特性使得设备可以看到进程虚拟地址空间,这样在进程虚拟地址空间销毁的时候应该调用设备驱动提供的函数停止设备继续访问进程虚拟地址空间。这里iommu_sva_set_ops就是把设备驱动的回调函数注册给进程mm。注意上面的mmu_notifier_register注册的iommu_mmu_notifier_ops回调里。release只在进程异常时调用到,用户态进程正常退出时并不会调用。在进程正常退出时,如何保证设备停止访问将要释放的进程地址空间,这里还有疑问。进程退出的调用链是:kernel/exit.c:do_exit+-> exit_mm+-> mmput+-> exit_mmap+-> mmu_notifier_release- iommu_sva_get_pasid这个接口返回返回smmu_sva对应的pasid数值,设备驱动需要把pasid配置给与这个smmu_sva相关的硬件资源。

需要使用SVA特性的社区驱动在调用上面的接口后,可以建立起静态的数据结构。

3. 动态分析


  • 缺页流程

    当一个PRI或者是一个stall event上报后, 软件会在缺页流程里建立页表,然后控制
    SMMU给设备返送reponse信息。我们可以从SMMU PRI queue或者是event queue的中断
    处理流程入手跟踪: e.g.PRI中断流程

    devm_request_threaded_irq(..., arm_smmu_priq_thread, ...)arm_smmu_priq_thread+-> arm_smmu_handle_ppr+-> iommu_report_device_fault+-> iommu_fault_param->handler+-> iommu_queue_iopf /* 初始化参见上面第2部分 */+-> iopf_group = kzalloc+-> list_add(faults list in group, fault)+-> INIT_WORK(&group->work, iopf_handle_group)+-> queue_work(iopf_param->queue->wq, &group->work)这段代码创建缺页的group,并把当前的缺页请求挂入group里的链表,然后创建一个任务,并调度这个任务运行在工作队列线程中:+-> iopf_handle_group+-> iopf_handle_single+-> handle_mm_fault这里会最终申请内存并建立页表+-> arm_smmu_page_response软件执行完缺页流程后,软件控制SMMU向设备回响应。
  • Invalid流程

    当软件释放申请的内存时,SMMU中关于这些内存的tlb以及设备ATC里都要Invalid。
    进程mm变动的时候,调用注册的io_mm notifier完成相关的tlb、atc的invalid。

    (to do: …)

4. 性能分析


   (to do: ...)

5. 虚拟化


    https://blog.csdn.net/scarecrow_byr/article/details/104606571

Linux SVA特性分析相关推荐

  1. linux系统it固定资产管理系统包_固定资产条码管理系统特性分析

    固定资产条码管理系统的特性都有什么?固定资产条码管理系统的应用在很多企业中都越来越普遍,固定资产条码管理系统作为信息化管理系统,能够对企业固定资产进行有效管理并提升企业管理水平,对于固定资产条码管理系 ...

  2. Linux内核源代码分析-目录

    第一部分 Linux 内核源代码 arch/i386/kernel/entry.S 2 arch/i386/kernel/init_task.c 8 arch/i386/kernel/irq.c 8 ...

  3. Linux虚拟化KVM-Qemu分析(六)之中断虚拟化

    <Linux虚拟化KVM-Qemu分析(一)> <Linux虚拟化KVM-Qemu分析(二)之ARMv8虚拟化> <Linux虚拟化KVM-Qemu分析(三)之KVM源码 ...

  4. Linux虚拟化KVM-Qemu分析(二)之ARMv8虚拟化

    Table of Contents 1. 概述 2. ARMv8虚拟化 2.1 Exception Level 2.2 Stage 2 translation 2.2.1 内存映射 2.2.2 MMI ...

  5. linux 内核部分崩溃,Linux 系统内核崩溃分析处理简介

    Written by arstercz -2019-11-12 Linux 系统内核崩溃分析处理简介 背景说明 目前绝大多数的 Linux 发行版都会将 kdump.service 服务默认开启, 以 ...

  6. Linux spi驱动分析(四)----SPI设备驱动(W25Q32BV)

    一.W25Q32BV芯片简介 W25X是一系列SPI接口Flash芯片的简称,它采用SPI接口和CPU通信,本文使用的W25Q32BV容量为32M,具体特性如下: 1.1.基本特性 该芯片最大支持10 ...

  7. Linux spi驱动分析----SPI设备驱动(W25Q32BV)

    转载地址:http://blog.chinaunix.net/uid-25445243-id-4026974.html 一.W25Q32BV芯片简介 W25X是一系列SPI接口Flash芯片的简称,它 ...

  8. 《Linux内核情景分析》阅读笔记

    <Linux内核情景分析>这本书读过了一遍,不想继续读第二遍了. <Linux Kernel Development>这本书前后读了3遍,写得实在是好,正所谓"布衣暖 ...

  9. linux网卡驱动程序分析

    linux网卡驱动程序分析 学习应该是一个先把问题简单化,在把问题复杂化的过程.一开始就着手处理复杂的问题,难免让人 有心惊胆颤,捉襟见肘的感觉.读Linux网卡驱动也是一样.那长长的源码夹杂着那些我 ...

最新文章

  1. 我是一个秒杀请求,正在逃离这颗星球...
  2. 用JQuery模仿淘宝的图片显示效果
  3. Windows系统调用学习笔记(一)—— API函数调用过程
  4. 【LeetCode】回文数
  5. 微软Visual Studio 14 CTP 2 发布
  6. php基础_变量和比较符
  7. JS -------------------设置弹出框位置屏幕的中间
  8. 用VC++ 6.0实现视频捕捉
  9. 一个新的轮回,一个新的开始
  10. android 发布版本证书,Android Studio 获取发布版和测试版证书SHA1的两种方式
  11. 如何优化网站的访问速度
  12. 关于Python3爬虫抓取豆瓣电影的案例-利用正则表达式
  13. Axure幻灯片制作
  14. mysql对结果再查询_SQL如何在查询结果里再次查询?
  15. 中国石油大学计算机评估排名,中国石油大学华东学科评估结果及排名情况怎样...
  16. 墨门云文件加密系统有哪些功能特点?
  17. Picasso加载圆角图片
  18. Born-Again Neural Networks论文初读
  19. Java的控制台输入输出语句
  20. 机器学习(聚类十)——谱聚类及代码实现

热门文章

  1. 函数周期表丨值丨数学函数系列
  2. java高并发程序设计1-线程停下来(stop,wait,suspend,await,interrupt,join,yield,sleep)的操作
  3. 基于Python+django的高校资产管理系统-计算机毕业设计
  4. ES2019新增功能
  5. 微信小程跳转页面方法总结
  6. 关于JS运算,出现多余小数点尾数,浮点问题处理
  7. (转)打印机连接不上的解决办法:超过连接最大数
  8. 短信系统定制平台组成—移讯云短信系统
  9. 我是这么利用数据——肥肉吃多了会腻
  10. 企业微信上下游怎么设置?企业微信上下游可以共享客户吗?