Linux内核 eBPF基础 perf(1):perf_event在内核中的初始化

荣涛 2021年5月12日

本文相关注释代码:https://github.com/Rtoax/linux-5.10.13

1. 初始化函数调用关系

1.1. start_kernel

可参见《Linux开机启动过程(10):start_kernel 初始化(至setup_arch初期)》系列文章。

在内核启动阶段,会调用perf_event_init对perf_event进行初始化。

start_kernel() ...perf_event_init()      <<<<<<<<<<perf相关perf_tp_register()   <<<<<<<<<<perf相关init_hw_breakpoint() <<<<<<<<<<perf相关arch_call_rest_init()rest_init()kernel_thread()kernel_init() => PID=1kernel_init()->kernel_init_freeable()->do_basic_setup()->do_initcalls()->do_initcall_level()->do_one_initcall()->xxx__initcall() <<<<<<<<<<perf相关

1.2. initcall

被这些宏标注的函数将在do_initcall_level中被调用

#define early_initcall(fn) /* fn */ __define_initcall(fn, early)/* (fn,early) */
/* 数字越小,优先级越高 */
#define pure_initcall(fn)       __define_initcall(fn, 0)
#define core_initcall(fn)       __define_initcall(fn, 1)
#define core_initcall_sync(fn)      __define_initcall(fn, 1s)
#define postcore_initcall(fn)       __define_initcall(fn, 2)
#define postcore_initcall_sync(fn)  __define_initcall(fn, 2s)
#define arch_initcall(fn)       __define_initcall(fn, 3)
#define arch_initcall_sync(fn)      __define_initcall(fn, 3s)
#define subsys_initcall(fn)     __define_initcall(fn, 4)
#define subsys_initcall_sync(fn)    __define_initcall(fn, 4s)
#define fs_initcall(fn)         __define_initcall(fn, 5)
#define fs_initcall_sync(fn)        __define_initcall(fn, 5s)
#define rootfs_initcall(fn)     __define_initcall(fn, rootfs)
#define device_initcall(fn)     __define_initcall(fn, 6)
#define device_initcall_sync(fn)    __define_initcall(fn, 6s)
#define late_initcall(fn)       __define_initcall(fn, 7)
#define late_initcall_sync(fn)      __define_initcall(fn, 7s)

关于perf的initcall函数包括:

early_initcall(init_hw_perf_events);
arch_initcall(bts_init);
arch_initcall(pt_init);
device_initcall(perf_event_sysfs_init);
device_initcall(amd_ibs_init);
device_initcall(amd_iommu_pc_init);
device_initcall(msr_init);
device_initcall(amd_uncore_init);

1.3. 模块

此外,有些perf相关的是

static int __init cstate_pmu_init(void)
{return cstate_init();
}
module_init(cstate_pmu_init);static int __init amd_power_pmu_init(void)
{
}
module_init(amd_power_pmu_init);static int __init rapl_pmu_init(void)
{
}
module_init(rapl_pmu_init);static int __init intel_uncore_init(void)
{uncore_pci_inituncore_pci_sub_driver_inituncore_pci_pmu_registeruncore_pmu_registeruncore_cpu_inituncore_msr_pmus_registertype_pmu_registeruncore_pmu_registeruncore_mmio_inittype_pmu_registeruncore_pmu_register
}
module_init(intel_uncore_init);

这些在我的系统中统统没有找到,我认为它们不是必须的,所以本系列文章我不打算讨论。

lsmod | grep -e core -e power -e stat

2. perf_event_init

perf_event_initstart_kernel中被调用

asmlinkage __visible void __init __no_sanitize_address start_kernel(void)
{perf_event_init();...arch_call_rest_init();
}

调用关系:

start_kernel() ...perf_event_init()      <<<<<<<<<<perf相关perf_tp_register()   <<<<<<<<<<perf相关init_hw_breakpoint() <<<<<<<<<<perf相关arch_call_rest_init()rest_init()kernel_thread()kernel_init() => PID=1kernel_init()->kernel_init_freeable()->do_basic_setup()->do_initcalls()->do_initcall_level()->do_one_initcall()->xxx__initcall() <<<<<<<<<<perf相关

这里有必要先说下perf类型(include\uapi\linux\perf_event.h):

/** attr.type*/
enum perf_type_id { /* perf 类型 */PERF_TYPE_HARDWARE         = 0,    /* 硬件 */PERF_TYPE_SOFTWARE         = 1,    /* 软件 */PERF_TYPE_TRACEPOINT       = 2,    /* 跟踪点 */PERF_TYPE_HW_CACHE            = 3,    /* 硬件cache */PERF_TYPE_RAW             = 4,    /* RAW */PERF_TYPE_BREAKPOINT      = 5,    /* 断点 */PERF_TYPE_MAX,             /* non-ABI */
};

他们是传入性能管理单元PMU注册函数perf_pmu_register的字段type

int perf_pmu_register(struct pmu *pmu, const char *name, int type)

这里需要注意,函数perf_pmu_register是非常重要的注册函数,注册的pmu将加入全局链表pmus中,这将在另一篇文章中单独介绍。

static LIST_HEAD(pmus);

perf_event_init函数分别调用perf_pmu_register注册了如下内容:

 perf_pmu_register(&perf_swevent, "software", PERF_TYPE_SOFTWARE);perf_pmu_register(&perf_cpu_clock, NULL, -1);perf_pmu_register(&perf_task_clock, NULL, -1);perf_tp_register();init_hw_breakpoint();

其中perf_tp_register为:

static inline void perf_tp_register(void)
{perf_pmu_register(&perf_tracepoint, "tracepoint", PERF_TYPE_TRACEPOINT);
#ifdef CONFIG_KPROBE_EVENTSperf_pmu_register(&perf_kprobe, "kprobe", -1);
#endif
#ifdef CONFIG_UPROBE_EVENTSperf_pmu_register(&perf_uprobe, "uprobe", -1);
#endif
}

其中init_hw_breakpoint为:

int __init init_hw_breakpoint(void)
{...perf_pmu_register(&perf_breakpoint, "breakpoint", PERF_TYPE_BREAKPOINT);...
}

3. initcall

使用initcall方式初始化的pmu注册包括:

early_initcall(init_hw_perf_events);
arch_initcall(bts_init);
arch_initcall(pt_init);
device_initcall(perf_event_sysfs_init);
device_initcall(amd_ibs_init);
device_initcall(amd_iommu_pc_init);
device_initcall(msr_init);
device_initcall(amd_uncore_init);

下面对他们分别简述

3.1. init_hw_perf_events

这是和架构/厂商相关的硬件perf初始化,我们只看英特尔相关(值得说明 的是这里有国产兆芯,这里不讲家国情怀,只讲技术,所以不看兆芯相关代码)。

static int __init init_hw_perf_events(void)
{intel_pmu_init();pmu_check_apic();perf_events_lapic_init();   /* 注册 PMU 为 NMI 中断 */register_nmi_handler(NMI_LOCAL, perf_event_nmi_handler, 0, "PMI");  /* 注册函数 */pr_info("... version:                %d\n",     x86_pmu.version);pr_info("... bit width:              %d\n",     x86_pmu.cntval_bits);pr_info("... generic registers:      %d\n",     x86_pmu.num_counters);pr_info("... value mask:             %016Lx\n", x86_pmu.cntval_mask);pr_info("... max period:             %016Lx\n", x86_pmu.max_period);pr_info("... fixed-purpose events:   %d\n",     x86_pmu.num_counters_fixed);pr_info("... event mask:             %016Lx\n", x86_pmu.intel_ctrl);perf_pmu_register(&pmu, "cpu", PERF_TYPE_RAW);
}
early_initcall(init_hw_perf_events);

此函数中有相关x86 pmu 信息的打印:

[rongtao@localhost perf]$ dmesg | grep fixed- -A 1 -B 5
[    0.320139] ... version:                2
[    0.320140] ... bit width:              48
[    0.320141] ... generic registers:      4
[    0.320142] ... value mask:             0000ffffffffffff
[    0.320143] ... max period:             000000007fffffff
[    0.320144] ... fixed-purpose events:   3
[    0.320145] ... event mask:             000000070000000f

perf_event_nmi_handler会单独讲解。

3.2. bts_init

关于BTS的介绍可参见什么是Intel LBR,BTS和AET?,即BTS使用RAM缓存(CAR)或系统DRAM来存储更多的指令和事件(Branch Tracking Store (BTS 分支跟踪存储)),同时可参考《Processor Tracing | 处理器追踪》。

注意不要和BTS — Bit Test and Set或x86和amd64指令参考混淆。

3.3. pt_init

上节中讲到《Processor Tracing | 处理器追踪》。
这将在后续单独介绍。

3.4. perf_event_sysfs_init

perf_event``sysfs初始化,在kernel\events\core.c中有perf_event_sysfs_init

[root@localhost event_source]# pwd
/sys/bus/event_source
[root@localhost event_source]# tree
.
├── devices
│   ├── breakpoint -> ../../../devices/breakpoint
│   ├── cpu -> ../../../devices/cpu
│   ├── kprobe -> ../../../devices/kprobe
│   ├── msr -> ../../../devices/msr
│   ├── power -> ../../../devices/power
│   ├── software -> ../../../devices/software
│   ├── tracepoint -> ../../../devices/tracepoint
│   └── uprobe -> ../../../devices/uprobe
├── drivers
├── drivers_autoprobe
├── drivers_probe
└── uevent

本节内容也将在单独的文章中介绍。

3.5. msr_init

以下内容摘自x86 CPU的MSR寄存器:

MSR(Model Specific Register)是x86架构中的概念,指的是在x86架构处理器中,一系列用于控制CPU运行、功能开关、调试、跟踪程序执行、监测CPU性能等方面的寄存器。
MSR寄存器的雏形开始于Intel 80386和80486处理器,到Intel Pentium处理器的时候,Intel就正式引入RDMSR和WRMSR两个指令用于读和写MSR寄存器,这个时候MSR就算被正式引入。在引入RDMSR和WRMSR指令的同时,也引入了CPUID指令,该指令用于指明具体的CPU芯片中,哪些功能是可用的,或者这些功能对应的MSR寄存器是否存在,软件可以通过CPUID指令查询某些功能是否在当前CPU上是否支持。
每个MSR寄存器都会有一个相应的ID,即MSR Index,或者也叫作MSR寄存器地址,当执行RDMSR或者WRMSR指令的时候,只要提供MSR Index就能让CPU知道目标MSR寄存器。这些MSR寄存器的编号(MSR Index)、名字及其各个数据区域的定义可以在Intel x86架构手册”Intel 64 and IA-32 Architectures Software Developer’s Manual"的Volume 4中找到。

关于MSR的详细讨论也会在单独的一篇文章中。

3.6. amd_ibs_init

IBS用于Apic初始化,用于perf和oprofile,关于amd_ibs_init的详细讨论也会在单独的一篇文章中,并主要涉及perf_event_ibs_init的介绍。

3.7. amd_iommu_pc_init

负责DMA remapping操作的硬件称为IOMMU。做个类比:大家都知道MMU是支持内存地址虚拟化的硬件,MMU是为CPU服务的;而IOMMU是为I/O设备服务的,是将DMA地址进行虚拟化的硬件。

《ARM SMMU原理与IOMMU技术(“VT-d” DMA、I/O虚拟化、内存虚拟化)》
《DMAR(DMA remapping)与 IOMMU》
《内核引导参数IOMMU与INTEL_IOMMU有何不同?》
《提升KVM异构虚拟机启动效率:透传(pass-through)、DMA映射(VFIO、PCI、IOMMU)、virtio-balloon、异步DMA映射、预处理》

关于iommu perf的详细讨论也会在单独的一篇文章中,而详细介绍的是init_one_iommuperf_pmu_register注册过程。

3.8. amd_uncore_init

以下内容摘自《Intel微处理器Uncore架构简介》:

uncore一词,是英特尔用来描述微处理器中,功能上为非处理器核心(Core)所负担,但是对处理器性能的发挥和维持有必不可少的作用的组成部分。处理器核心(Core)包含的处理器组件都涉及处理器命令的运行,包括算术逻辑单元(ALU)、浮点运算单元(FPU)、一级缓存(L1 Cache)、二级缓存(L2 Cache)。Uncore的功能包括QPI控制器、三级缓存(L3 Cache)、内存一致性监测(snoop agent pipeline)、内存控制器,以及Thunderbolt控制器。至于其余的总线控制器,像是PCI-E、SPI等,则是属于芯片组的一部分。

英特尔Uncore设计根源,来自于北桥芯片。Uncroe的设计是,将对于处理器核心有关键作用的功能重新组合编排,从物理上使它们更靠近核心(集成至处理器芯片上,而它们有部分原来是位于北桥上),以降低它们的访问延时。而北桥上余下的和处理器核心并无太大关键作用的功能,像是PCI-E控制器或者是电源控制单元(PCU),并没有集成至Uncore部分,而是继续作为芯片组的一部分。

具体而言,微架构中的uncore是被细分为数个模块单元的。uncore连接至处理器核心是通过一个叫Cache Box(CBox)的接口实现的,CBox也是末级缓存(Last Level Cache,LLC)的连接接口,同时负责管理缓存一致性。复合的内部与外部QPI链接由物理层单元(Physical Layer units)管理,称为PBox。PBox、CBox以及一个或更多的内置存储器控制器(iMC,作MBox)的连接由系统配置控制器(System Config Controller,作UBox)和路由器(Router,作RBox)负责管理。

从uncore部分移出列表总线控制器,可以更好地促进性能的提升,通过允许uncore的时钟频率(UCLK)运作于基准的2.66GHz,提升至超过超频限制值的3.44GHz,实现性能提升。这种时脉提升使得核心访问关键功能部件(像是存储器控制器)时的延时值更低(典型情况下处理器核心访问DRAM的时间可降低10纳秒或更多)。

关于uncore的详细讨论也会在单独的一篇文章中。

4. perf_pmu_register

注册一个PMU的接口,这将在本系列其他文章中详述。

5. 相关链接

  • 注释源码:https://github.com/Rtoax/linux-5.10.13
  • Linux内核 eBPF基础:perf(1):perf_event在内核中的初始化

Linux内核 eBPF基础:perf(1):perf_event在内核中的初始化相关推荐

  1. Linux内核 eBPF基础:perf(4)perf_event_open系统调用与用户手册详解

    Linux内核 eBPF基础 perf(4)perf_event_open系统调用与用户手册详解 荣涛 2021年5月19日 本文相关注释代码:https://github.com/Rtoax/lin ...

  2. Linux内核 eBPF基础:perf(2):perf性能管理单元PMU的注册

    Linux内核 eBPF基础 perf(2):性能管理单元PMU的注册 荣涛 2021年5月18日 本文相关注释代码:https://github.com/Rtoax/linux-5.10.13 Li ...

  3. Linux内核 eBPF基础:Tracepoint原理源码分析

    Linux内核 eBPF基础 Tracepoint原理源码分析 荣涛 2021年5月10日 1. 基本原理 需要注意的几点: 本文将从sched_switch相关的tracepoint展开: 关于st ...

  4. Linux内核 eBPF基础:ftrace源码分析:过滤函数和开启追踪

    Linux内核 eBPF基础 ftrace基础:过滤函数和开启追踪 荣涛 2021年5月12日 本文相关注释代码:https://github.com/Rtoax/linux-5.10.13 上篇文章 ...

  5. Linux内核 eBPF基础:ftrace基础-ftrace_init初始化

    Linux内核 eBPF基础 ftrace基础:ftrace_init初始化 荣涛 2021年5月12日 本文相关注释代码:https://github.com/Rtoax/linux-5.10.13 ...

  6. Linux内核 eBPF基础:kprobe原理源码分析:源码分析

    Linux内核 eBPF基础 kprobe原理源码分析:源码分析 荣涛 2021年5月11日 在 <Linux内核 eBPF基础:kprobe原理源码分析:基本介绍与使用>中已经介绍了kp ...

  7. Linux内核 eBPF基础:kprobe原理源码分析:基本介绍与使用示例

    Linux内核 eBPF基础 kprobe原理源码分析:基本介绍与使用示例 荣涛 2021年5月11日 kprobe调试技术是为了便于跟踪内核函数执行状态所设计的一种轻量级内核调试技术. 利用kpro ...

  8. Linux内核 eBPF基础: 探索USDT探针

    目录 Motivation Tracing System Overview Terminology术语 Evoluction of Linux Tracing Linux Tracing Techni ...

  9. linux性能优化——利用perf火焰图分析内核调用

    1.内核进程 我们知道,在 Linux 中,用户态进程的"祖先",都是 PID 号为 1 的 init 进程.比如,现在主流的 Linux 发行版中,init 都是 systemd ...

最新文章

  1. 山东计算机基础模拟题及答案,2016山东农信社考试模拟题--计算机基础知识答案(1)...
  2. 用Django开发Web应用程序异常
  3. R中方差,协方差,相关系数
  4. flink实时同步mysql_基于Canal与Flink实现数据实时增量同步(一)
  5. 网易 html5,别再想不开做H5了
  6. sql 分页查询 (每次6行 )
  7. ibatis mybatis sql语句配置 符号不兼容 大于号 小于号
  8. c语言学生成绩管理系统(顺序表实现)
  9. 互动媒体技术专题2——多视角认识十二个“一” 技术预演与方案设计
  10. N1 webpad刷机要点
  11. 中国石油大学(北京)本科毕业论文答辩PPT模板
  12. java向led屏下发字符串乱码_几种误解,以及乱码产生的原因和解决办法
  13. 由于压摆率引起的失真问题
  14. 如何将word转换成pdf?超实用的使用教程免费分享
  15. 计算机辅助设计 Photoshop 教案,计算机辅助设计(photoshop)
  16. 2022年二级建造师《专业工程管理与实务(公路)》综合测试题及答案
  17. openlayers 地图添加比例尺
  18. 服务器数据库查看版本信息,查看服务器数据库版本号
  19. HTML+CSS 编辑的(多列布局、相册、百度首页)、盒子模型
  20. cookie是什么?有什么用?

热门文章

  1. html控制弯曲图,html – CSS3 3D弯曲视角
  2. mysql8 win10_window10下安装多个MySQL8.0
  3. 详解摄像头各个引脚的作用关系
  4. ScrollView各属性,及代理方法汇总
  5. vsftpd 创建虚拟用户
  6. mysql cmd grep_通过 mysqlbinlog 和 grep 命令定位binlog文件中指定操作
  7. c语言10-100000取整数,100个C语言地编程题.docx
  8. java中读取logback日志文件_java 中使用logback日志,并实现日志按天分类压缩保存。...
  9. python 比特输出_Python小碗菜:and/or 与 amp;/| 到底有什么区别
  10. 怎样获取php页面get的值,PHP循环获取GET和POST值的代码