原文信息

作者:LoyenWang

出处:https://www.cnblogs.com/LoyenWang/

公众号:LoyenWang

版权:本文版权归作者和博客园共有

转载:欢迎转载,但未经作者同意,必须保留此段声明;必须在文章中给出原文连接;否则必究法律责任

背景

  • Read the fucking source code! --By 鲁迅
  • A picture is worth a thousand words. --By 高尔基

说明:

  1. KVM版本:5.9.1
  2. QEMU版本:5.0.0
  3. 工具:Source Insight 3.5, Visio
  4. 文章同步在博客园:https://www.cnblogs.com/LoyenWang/

1. 概述

先从操作系统的角度来看一下timer的作用吧:

通过timer的中断,OS实现的功能包括但不局限于上图:

  • 定时器的维护,包括用户态和内核态,当指定时间段过去后触发事件操作,比如IO操作注册的超时定时器等;
  • 更新系统的运行时间、wall time等,此外还保存当前的时间和日期,以便能通过time()等接口返回给用户程序,内核中也可以利用其作为文件和网络包的时间戳;
  • 调度器在调度任务分配给CPU时,也会去对task的运行时间进行统计计算,比如CFS调度,Round-Robin调度等;
  • 资源使用统计,比如系统负载的记录等,此外用户使用top命令也能进行查看;

timer就像是系统的脉搏,重要性不言而喻。ARMv8架构处理器提供了一个Generic Timer,与GIC类似,Generic Timer在硬件上也支持了虚拟化,减少了软件模拟带来的overhead。

本文将围绕着ARMv8的timer虚拟化来展开。

2. ARMv8 Timer虚拟化

2.1 Generic Timer

看一下ARMv8架构下的CPU内部图:

  • Generic Timer提供了一个系统计数器,用于测量真实时间的消逝;
  • Generic Timer支持虚拟计数器,用于测量虚拟的时间消逝,一个虚拟计数器对应一个虚拟机;
  • Timer可以在特定的时间消逝后触发事件,可以设置成count-up计数或者count-down计数;

来看一下Generic Timer的简图:

或者这个:

  • System Counter位于Always-on电源域,以固定频率进行系统计数的增加,System Counter的值会广播给系统中的所有核,所有核也能有一个共同的基准了,System Counter的频率范围为1-50MHZ,系统计数值的位宽在56-64bit之间;
  • 每个核有一组timer,这些timer都是一些比较器,与System Counter广播过来的系统计数值进行比较,软件可以配置固定时间消逝后触发中断或者触发事件;
  • 每个核提供的timer包括:1)EL1 Physical timer;2)EL1 Virtual timer;此外还有在EL2和EL3下提供的timer,具体取决于ARMv8的版本;
  • 有两种方式可以配置和使用一个timer:1)CVAL(comparatoer)寄存器,通过设置比较器的值,当System Count >= CVAL时满足触发条件;2)TVAL寄存器,设置TVAL寄存器值后,比较器的值CVAL = TVAL + System Counter,当System Count >= CVAL时满足触发条件,TVAL是一个有符号数,当递减到0时还会继续递减,因此可以记录timer是在多久之前触发的;
  • timer的中断是私有中断PPI,其中EL1 Physical Timer的中断号为30,EL1 Virtual Timer的中断号为27;
  • timer可以配置成触发事件产生,当CPU通过WFE进入低功耗状态时,除了使用SEV指令唤醒外,还可以通过Generic Timer产生的事件流来唤醒;

2.2 虚拟化支持

Generic Timer的虚拟化如下图:

  • 虚拟的timer,同样也有一个count值,计算关系:Virtual Count = Physical Count - <offset>,其中offset的值放置在CNTVOFF寄存器中,CNTPCT/CNTVCT分别用于记录当前物理/虚拟的count值;
  • 如果EL2没有实现,则将offset设置为0,,物理的计数器和虚拟的计数器值相等;
  • Physical Timer直接与System counter进行比较,Virtual TimerPhysical Timer的基础上再减去一个偏移;
  • Hypervisor负责为当前调度运行的vCPU指定对应的偏移,这种方式使得虚拟时间只会覆盖vCPU实际运行的那部分时间;

示例如下:

  • 6ms的时间段里,每个vCPU运行3ms,Hypervisor可以使用偏移寄存器来将vCPU的时间调整为其实际的运行时间;

3. 流程分析

3.1 初始化

先简单看一下数据结构吧:

  • 在ARMv8虚拟化中,使用struct arch_timer_cpu来描述Generic Timer,从结构体中也能很清晰的看到层次结构,创建vcpu时,需要去初始化vcpu架构相关的字段,其中就包含了timer;
  • struct arch_timer_cpu包含了两个timer,分别对应物理timer和虚拟timer,此外还有一个高精度定时器,用于Guest处在非运行时的计时工作;
  • struct arch_timer_context用于描述一个timer需要的内容,包括了几个字段用于存储寄存器的值,另外还描述了中断相关的信息;

初始化分为两部分:

  1. 架构相关的初始化,针对所有的CPU,在kvm初始化时设置:

  • kvm_timer_hyp_init函数完成相应的初始化工作;
  • arch_timer_get_kvm_info从Host Timer驱动中去获取信息,主要包括了虚拟中断号和物理中断号,以及timecounter信息等;
  • vtimer中断设置包括:判断中断的触发方式(只支持电平触发),注册中断处理函数kvm_arch_timer_handler,设置中断到vcpu的affinity等;
  • ptimer中断设置与vtimer中断设置一样,同时它的中断处理函数也是kvm_arch_timer_handler,该处理函数也比较简单,最终会调用kvm_vgic_inject_irq函数来完成虚拟中断注入给vcpu;
  • cpuhp_setup_state用来设置CPU热插拔时timer的响应处理,而在kvm_timer_starting_cpu/kvm_timer_dying_cpu两个函数中实现的操作就是中断的打开和关闭,仅此而已;
  1. vcpu相关的初始化,在创建vcpu时进行初始化设置:

  • 针对vcpu的timer相关初始化比较简单,回到上边那张数据结构图看一眼就明白了,所有的初始化工作都围绕着struct arch_timer_cpu结构体;
  • vcpu_timer:用于获取vcpu包含的struct arch_timer_cpu结构;
  • vcpu_vtimer/vcpu_ptimer:用于获取struct arch_timer_cpu结构体中的struct arch_timer_context,分别对应vtimer和ptimer;
  • update_vtimer_cntvoff:用于更新vtimer中的cntvoff值,读取物理timer的count值,更新VM中所有vcpu的cntvoff值;
  • hrtimer_init:用于初始化高精度定时器,包含有三个,struct arch_timer_cpu结构中有一个bg_timer,vtimer和ptimer所对应的struct arch_timer_context中分别对应一个;
  • kvm_bg_timer_expirebg_timer的到期执行函数,当需要调用kvm_vcpu_block让vcpu睡眠时,需要先启动bg_timerbg_timer到期时再将vcpu唤醒;
  • kvm_hrtimer_expire:vtimer和ptimer的到期执行函数,最终通过调用kvm_timer_update_irq来向vcpu注入中断;

3.2 用户层访问

可以从用户态对vtimer进行读写操作,比如Qemu中,流程如下:

  • 用户态创建完vcpu后,可以通过vcpu的文件描述符来进行寄存器的读写操作;
  • 以ARM为例,ioctl通过KVM_SET_ONE_REG/KVM_GET_ONE_REG将最终触发寄存器的读写;
  • 如果操作的是timer的相关寄存器,则通过kvm_arm_timer_set_regkvm_arm_timer_get_reg来完成;
  • 读写的寄存器包括虚拟timer的CTL/CVAL,以及物理timer的CTL/CVAL等;

3.3 Guest访问

Guest对Timer的访问,涉及到系统寄存器的读写,将触发异常并Trap到Hyp进行处理,流程如下:

  • Guest OS访问系统寄存器时,Trap到Hypervisor进行处理;
  • Hypervisor对异常退出进行处理,如果发现是访问系统寄存器造成的异常,则调用kvm_handle_sys_reg来处理;
  • kvm_handle_sys_reg:调用emulate_sys_reg来对系统寄存器进行模拟,在该函数中首先会查找访问的是哪一个寄存器,然后再去调用相应的回调函数;
  • kvm中维护了struct sys_reg_desc sys_reg_descs[]系统寄存器的描述表,其中struct sys_reg_desc结构体中包含了对该寄存器操作的函数指针,用于指向最终的操作函数,比如针对Timer的kvm_arm_timer_write_sysreg/kvm_arm_timer_read_sysreg读写操作函数;
  • Timer的读写操作函数,主要在kvm_arm_timer_read/kvm_arm_timer_write中完成,实现的功能就是根据物理的count值和offset来计算等;

timer的虚拟化还是比较简单,就此打住了。

PS:

按计划,接下里该写IO虚拟化了,然后紧接着Qemu的源码相关分析。不过,在写IO虚拟化之前,我会先去讲一下PCIe的驱动框架,甚至可能还会去研究一下网络,who knows,反正这些也都是IO相关。

Any way,I will be back soon!

参考

《AArch64 Programmer's Guides Generic Timer》

《Arm Architecture Reference Manual》

欢迎关注个人公众号,不定期更新内核相关技术文章

【转载】Linux虚拟化KVM-Qemu分析(七)之timer虚拟化的更多相关文章

  1. [转载]Linux 线程实现机制分析

    本文转自http://www.ibm.com/developerworks/cn/linux/kernel/l-thread/ 支持原创.尊重原创,分享知识! 自从多线程编程的概念出现在 Linux ...

  2. 【原创】Linux虚拟化KVM-Qemu分析(七)之timer虚拟化

    背景 Read the fucking source code! --By 鲁迅 A picture is worth a thousand words. --By 高尔基 说明: KVM版本:5.9 ...

  3. KVM+QEMU虚拟化概念

    概念: KVM,即Kernel-basedvirtual machine,由redhat开发,是一种开源.免费的虚拟化技术.对企业来说,是一种可选的虚拟化解决方案. 定义:基于Linux内核的虚拟机 ...

  4. KVM 介绍(3):I/O 全虚拟化和准虚拟化 [KVM I/O QEMU Full-Virtualizaiton Para-virtualization]

    学习 KVM 的系列文章: (1)介绍和安装 (2)CPU 和 内存虚拟化 (3)I/O QEMU 全虚拟化和准虚拟化(Para-virtulizaiton) (4)I/O PCI/PCIe设备直接分 ...

  5. Linux实战教学笔记53:开源虚拟化KVM(一)搭建部署与概述

    一,KVM概述 1.1 虚拟化概述 在计算机技术中,虚拟化意味着创建设备或资源的虚拟版本,如服务器,存储设备,网络或者操作系统等等 [x] 虚拟化技术分类: 系统虚拟化(我们主要讨论的反向) 存储虚拟 ...

  6. 深度分析Linux下双网卡绑定七种模式 多网卡的7种bond模式原理

    http://blog.csdn.net/abc_ii/article/details/9991845多网卡的7种bond模式原理 Linux网卡绑定mode共有七种(~) bond0.bond1.b ...

  7. 2018-2019-1 20189221 《Linux内核原理与分析》第七周作业

    2018-2019-1 20189221 <Linux内核原理与分析>第七周作业 实验六 分析Linux内核创建一个新进程的过程 代码分析 task_struct: struct task ...

  8. 2017-2018-1 20179209《Linux内核原理与分析》第七周作业

    一.实验 1.1task_struct数据结构 Linux内核通过一个被称为进程描述符的task_struct结构体来管理进程,这个结构体包含了一个进程所需的所有信息.它定义在linux-3.18.6 ...

  9. 2019-2020-1 20199329《Linux内核原理与分析》第七周作业

    <Linux内核原理与分析>第七周作业 一.本周内容概述: 对Linux系统如何创建一个新进程进行追踪 分析Linux内核创建一个新进程的过程 二.本周学习内容: 1.学习进程的描述 操作 ...

  10. 云计算大数据:Xen、KVM、VMware、hyper-v等虚拟化技术的比较

    1.Xen.KVM.VMware.hyper-v等虚拟化技术的比较,xen和kvm,是开源免费的虚拟化软件. vmware是付费的虚拟化软件. hyper-v比较特别,是微软windows 2008 ...

随机推荐

  1. 频繁设置CGroup触发linux内核bug导致CGroup running task不调度

    1. 说明 1> 本篇是实际工作中linux上碰到的一个问题,一个使用了CGroup的进程处于R状态但不执行,也不退出,还不能kill,经过深入挖掘才发现是Cgroup的内核bug 2>发 ...

  2. php正则表达式大全/php正则表达式使用方法整理集合

    匹配数字 "^\d+$" //非负整数(正整数 + 0) "[1][1-9][0-9]$" //正整数 "^((-\d+)|(0+))$" ...

  3. Gradio入门到进阶全网最详细教程[二]:快速搭建AI算法可视化部署演示(侧重参数详解和案例实践)

    Gradio入门到进阶全网最详细教程[二]:快速搭建AI算法可视化部署演示(侧重参数详解和案例实践) 相关文章:Gradio入门到进阶全网最详细教程[一]:快速搭建AI算法可视化部署演示(侧重项目搭建 ...

  4. 阿里云交互式建模(DSW)的探索和踩坑

    前言 自己的笔记本炼丹还是太吃力了些,风扇嘶吼有点心疼,看到阿里云出了一些免费试用的资源,想着能白嫖一下高端显卡跑一跑自制模型还挺有趣,于是有了下面的一些操作,其实没啥难度的,大胆的按文档来做基本就可 ...

  5. 2022-11-10:写一个 bash 脚本以统计一个文本文件 words.txt 中每个单词出现的频率。 为了简单起见,你可以假设: words.txt只包括小写字母和 ‘ ‘ 。 每个单词只由小写

    2022-11-10:写一个 bash 脚本以统计一个文本文件 words.txt 中每个单词出现的频率. 为了简单起见,你可以假设: words.txt只包括小写字母和 ' ' . 每个单词只由小写 ...

  6. 2021-01-09:linux中,某一个实时日志通过什么命令查?

    福哥答案2020-01-09:[答案来自此链接:](https://www.zhihu.com/question/438536200)1.tailtail -f首先就是 tail -f,tail 命令 ...

  7. 2022-05-03:Alice 和 Bob 再次设计了一款新的石子游戏。现有一行 n 个石子,每个石子都有一个关联的数字表示它的价值。给你一个整数数组 stones ,其中 stones[i] 是第

    2022-05-03:Alice 和 Bob 再次设计了一款新的石子游戏.现有一行 n 个石子,每个石子都有一个关联的数字表示它的价值.给你一个整数数组 stones ,其中 stones[i] 是第 ...

  8. 数字分频器设计(偶数分频、奇数分频、小数分频、半整数分频、状态机分频|verilog代码|Testbench|仿真结果)

    目录 一.前言 二.偶数分频 2.1 触发器级联法 2.2 计数器法 2.3 verilog代码 2.4 Testbench 2.5 仿真结果 三.奇数分频 3.1 占空比非50%奇数分频 3.2 占 ...

  9. pbootcms对接微信扫码登录代码核心片段和步骤(前后端)

    首先需要在微信公众平台或开放平台中创建应用,并获取到AppID和AppSecret. 在pbootcms中创建一个自定义模板页面(例如:wechat_login.html),并在该页面中添加以下代码, ...

  10. 安装Visio 2016与原本的office冲突的最终解决方案

    一. 下载office visio 2016 二. 开始安装 但是提示卸载原本的office 三. 网上找寻答案 于是按照这篇文章https://jingyan.baidu.com/article/1 ...