1. 墊話
本文標(biāo)題叫“硬件 PMU”操作,而不是“PMU 硬件”操作,是為了有意強(qiáng)調(diào)上一篇文章所申明的概念:PMU 只是一種抽象,其可以是純軟件實(shí)現(xiàn)的,也可以是硬件實(shí)現(xiàn)的。所以本文分析“硬件 PMU”,是有其推廣意義的:“硬件 PMU”是 PMU 的一個(gè)特殊解,清楚了“硬件 PMU”的抽象及操作,基本也就清楚了 perf 框架對(duì)其他 PMU(軟件 PMU、trace point PMU)的抽象及操作。 軟件架構(gòu)的設(shè)計(jì)無非就是前后端的雙向打通,前端是面向用戶的 user friendly 接口,考驗(yàn)的是程序員對(duì)業(yè)務(wù)的理解與設(shè)計(jì);后端是實(shí)現(xiàn)功能所依賴的基礎(chǔ)能力,考驗(yàn)的是程序員對(duì)底層技術(shù)的掌握與應(yīng)用;而前后端的打通,考驗(yàn)的是程序員的能力、經(jīng)驗(yàn)與審美。 上一篇文章介紹的是 perf 的前端,本文介紹的是 perf 的后端,也就是硬件事件監(jiān)控功能所仰仗的底層能力。
2. 綜述
本文乃內(nèi)核 perf 框架解構(gòu)系列文章第二篇。 PMU 的底層操作是很枯燥(architectural specific)的,但卻是繞不過去的一個(gè)話題,因?yàn)閷?duì) PMU 的抽象及操作,皆是 perf 框架設(shè)計(jì)的一部分。如果不弄清楚底層,代碼解構(gòu)起來會(huì)非常吃力。 對(duì)硬件 PMU 的解構(gòu),我們進(jìn)一步細(xì)分為若干文章,本文介紹硬件 PMU 的基本概念,后續(xù)文章會(huì)介紹具體的代碼編程,故本文是后續(xù)文章的前導(dǎo)。 本文所涉及 PMU 相關(guān)知識(shí)的細(xì)節(jié),可參閱本號(hào)《Intel SDM 之 Performance Monitoring》,或 Intel SDM。 本文中的 “PMU” 皆指代硬件實(shí)現(xiàn)的 PMU,“事件”皆指代硬件事件。 本文代碼基于 4.9 內(nèi)核。
3. 基本概念
3.1 PMU 是什么
所謂 PMU,就是 performance monitoring unit,顧名思義,其功能就是做性能(CPU 指標(biāo))的監(jiān)控。PMU 是實(shí)現(xiàn)在 CPU 中的硬件,通過 MSR 接口操作。 PMU 可以監(jiān)控 CPU core 上的性能數(shù)據(jù),比如 instructions、cycles 等,還可以監(jiān)控 CPU 與 uncore 之間交互的性能數(shù)據(jù)(如 L3 讀寫 miss)。所謂 uncore,就是 CPU package 中 CPU core 之外的部分,也就是 off-core sub-system,uncore 被一個(gè) package 中的多個(gè) core 所共享,典型如 L3 cache、Intel QuickPath Interconnect link logic 以及 integrated memory controller 等。
3.2 PMU 與 PMC
一個(gè) HT(hyper threading)通常包含一個(gè) PMU,而一個(gè) PMU 中包含多個(gè) PMC,所謂 PMC,就是 performance monitoring counter,一個(gè) PMC 經(jīng)過編程(MSR 接口)后,可以對(duì)一個(gè)指定的事件進(jìn)行監(jiān)控。換句話說,同一時(shí)刻,PMU 可以同時(shí)對(duì)多個(gè)事件進(jìn)行監(jiān)控。若要獲取性能數(shù)據(jù),讀取指定 PMC 的計(jì)數(shù)即可(實(shí)際上 PMC 有計(jì)數(shù)和中斷兩種工作模式,但本文只討論簡單的計(jì)數(shù)模式)。 受限于硬件設(shè)計(jì),一個(gè) PMU 中 PMC 數(shù)量是有限的,在筆者的機(jī)器上,一個(gè) HT(hyper threading)中只有 7 個(gè) PMC。
3.3 PMC 使用范式
類似《淺度剖析內(nèi)核 RDT 框架》一文 3.1.1 節(jié)中探討的 RDT 硬件使用范式,PMU 的常見用法亦有如下三種:
per-cpu 的性能監(jiān)控
監(jiān)控指定 CPU 的指定事件,該用法比較簡單。底層實(shí)現(xiàn)原理是,在指定 CPU 運(yùn)行期間,無論其上運(yùn)行的是什么 task,始終保持其某 PMC 被編程為監(jiān)控指定事件即可。前后兩次讀取指定事件的計(jì)數(shù)值,即可得出這兩次采樣之間指定 CPU 指定事件的計(jì)數(shù)。
per-task 的性能監(jiān)控
監(jiān)控指定 task 的指定事件,該用法需要 OS 的支持。底層實(shí)現(xiàn)原理是,在指定 task 被調(diào)度到 CPU 上運(yùn)行時(shí)(sched_in),將 PMC 編程為指定事件,在 task 被調(diào)度出 CPU 時(shí)(sched_out),disable 此 PMC,并將 PMC 的計(jì)數(shù)值保存到事件中。前后兩次讀取指定事件的計(jì)數(shù)值,即可得出這兩次采樣之間指定 task 指定事件的計(jì)數(shù)。
per-cgroup 的性能監(jiān)控
監(jiān)控指定 cgroup 的指定事件,該用法需要 OS 的支持。實(shí)現(xiàn)原理同 per-task,不贅述。
3.4 perf 框架對(duì) PMC 使用范式的增強(qiáng)
前文 4.2 節(jié)中提到,perf 框架允許用戶指定一次性監(jiān)控一個(gè)事件組。 所謂事件組,就是一組事件,用戶通過 perf 前端接口指定組的 leader(group leader,本質(zhì)上就是 perf_event_open 打開事件后返回的一個(gè) fd),并通過前端接口向這個(gè)組內(nèi)添加成員。一個(gè)組內(nèi)的所有事件,必須被同時(shí)“調(diào)度”到 CPU 上,這里事件的“調(diào)度”,就是為事件分配、使能相應(yīng)的 PMC;并且在讀 group leader 時(shí),會(huì)將整組事件的計(jì)數(shù)同時(shí)讀出。 前一篇文章有粗略提過,但又言之未盡。perf 前端允許對(duì)指定 cpu 的指定事件(組)、指定 task 的指定事件(組)、指定 cgroup 的指定事件(組)進(jìn)行監(jiān)控。不同的前端接口調(diào)用者(應(yīng)用),其監(jiān)控需求不盡相同,而 perf 框架通過其設(shè)計(jì)與實(shí)現(xiàn),兼容了不同應(yīng)用的不同需求。
3.5 PMU version
本文主要基于 Intel x86 架構(gòu)的 PMU 操作展開。Intel 的 PMU 架構(gòu),隨著 PMU version 的演進(jìn),又有著不同的 feature 增強(qiáng),與本文強(qiáng)相關(guān)是 version 2 相對(duì) version 1 引入的 fixed-function PMC,本文稱其為 fixed PMC。 因?yàn)楸救藱C(jī)器的 PMU version 是 4,所以其必然是支持 fixed PMC feature 的。非 fixed PMC 本文稱之為 generic PMC。
3.6 fixed 與 generic PMC
所謂 fixed PMC,是指這類 PMC 只能監(jiān)控其對(duì)應(yīng)的特定事件。如 圖 1:
圖 1 圖 1 蘊(yùn)含的信息有:
version 2 定義了 4 個(gè) fixed PMC(機(jī)器上實(shí)際 fixed PMC 個(gè)數(shù)取決于具體實(shí)現(xiàn),在我的機(jī)器上是 3 個(gè),后面會(huì)介紹如何獲取這些信息),分別為 IA32_FIXED_CTR[0-3]。
IA32_FIXED_CTR0,也即 0 號(hào) fixed PMC,其地址為 309H,其可以且只可以監(jiān)控 INST_RETIRED.ANY 事件。其他 PMC 類推。
具體 INST_RETIRED.ANY 事件:
圖 2 而 generic PMC,其可以通過編程(也即指定 umask、event)監(jiān)控 CPU 手冊(cè)中所規(guī)定的任意事件,也包括 fixed PMC 所能監(jiān)控的事件。
4. 基本操作及編程
4.1 PMU 信息獲取
PMU 信息包括但不限于 version、generic PMC 數(shù)量、fixed PMC 數(shù)量、PMC 寄存器的數(shù)據(jù)長度等。 通過 CPUID.0AH 獲取。具體代碼沒有貼的意義,后續(xù)文章會(huì)有,一看就明白。
4.2 事件(PMC)的配置及讀取
4.2.1 generic PMC
1. 配置
通過 IA32_PERFEVTSELx 寄存器(event select 寄存器)對(duì) generic PMC 進(jìn)行設(shè)置,x 為 PMC 的編號(hào)。 IA32_PERFEVTSELx 寄存器的基地址為 186H(內(nèi)核代碼:MSR_ARCH_PERFMON_EVENTSEL0),編號(hào)為 x 的 PMC,其 event select 寄存器地址為 MSR_ARCH_PERFMON_EVENTSEL0 + x。 IA32_PERFEVTSELx 格式如下:
圖 3
通過 umask + event select 域指定所要監(jiān)控的事件。
USR、OS 位,分別表示是否使能處理器用戶模式及內(nèi)核模式下的監(jiān)控。perf 框架下,是否使能此二者 bit,取決于前端(perf_event_open 接口)傳入 perf_event_attr 的 exclude_user、exclude_kernel 參數(shù)。
EN 位,表示是否使能此 PMC。
其他位,參考 SDM Section 18.2.1.1。
generic PMC 配置及使能代碼,參考內(nèi)核 x86_pmu_hw_config、__x86_pmu_enable_event、x86_pmu_disable_event(不看亦無妨,后續(xù)會(huì)有代碼實(shí)現(xiàn)的文章)。
2. 讀取
根據(jù) SDM Section 18.2.1.1,通過 generic PMC 對(duì)應(yīng)的 IA32_PMCx 寄存器來獲取 PMC 的計(jì)數(shù)值,x 為 generic PMC 的編號(hào),這組寄存器基地址為 0C1H(內(nèi)核代碼:MSR_ARCH_PERFMON_PERFCTR0)。 內(nèi)核中對(duì) PMC 的讀取不是通過 IA32_PMCx 寄存器實(shí)現(xiàn),而是通過 rdpmc 指令,rdpmc 的入?yún)⑹?PMC 的編號(hào),如果是讀 0 號(hào) generic PMC,則傳入 0 即可。 generic PMC 的讀取代碼,參考內(nèi)核 x86_assign_hw_event、x86_perf_event_update。
4.2.2 fixed PMC
1. 配置
通過 IA32_FIXED_CTR_CTRL ?寄存器,該寄存器地址為 38DH(內(nèi)核代碼:MSR_ARCH_PERFMON_FIXED_CTR_CTRL)。 不同于 generic PMC 的編程,每個(gè) generic PMC 都有一個(gè)各自對(duì)應(yīng)的 event select 寄存器。而所有 fixed PMC 皆通過 IA32_FIXED_CTR_CTRL 寄存器完成:
圖 4 可以看到,從 bit 0 到 bit 12,每 4 個(gè) bit 控制一個(gè) fixed PMC,編號(hào)為 x 的 fixed PMC,其在 IA32_FIXED_CTR_CTRL 中配置 bit 的偏移為 1 << (x * 4)。 你可能會(huì)問,該寄存器為啥沒有類似 generic PMC event select 寄存器的 umask 和 event select?因?yàn)樗鼈兪?fixed 的,fixed PMC 的編號(hào)與事件的對(duì)應(yīng)關(guān)系見 圖 1。 fixed PMC 配置及使能代碼,參考內(nèi)核 x86_pmu_hw_config、intel_pmu_enable_fixed、intel_pmu_disable_fixed。
2. 讀取
根據(jù) SDM Section 18.2.2,通過 fixed PMC 對(duì)應(yīng)的 IA32_FIXED_CTRx 寄存器來獲取 PMC 的計(jì)數(shù)值,x 為 PMC 的編號(hào)。如 圖 1,這組寄存器基地址為 309H(內(nèi)核代碼:MSR_ARCH_PERFMON_FIXED_CTR0)。 內(nèi)核中對(duì) PMC 的讀取不是通過 IA32_FIXED_CTRx 寄存器實(shí)現(xiàn),而是通過 rdpmc 指令,rdpmc 的入?yún)⑹?(x - INTEL_PMC_IDX_FIXED) | 1<<30,其中 x 為 fixed PMC 的編號(hào),INTEL_PMC_IDX_FIXED 為 32。 fixed PMC 的讀取,同 generic PMC,參考內(nèi)核 x86_assign_hw_event、x86_perf_event_update。
4.3 PMU 的配置及使能
所謂使能/禁能 PMU,其本質(zhì)是一次性使能/禁能 PMU 的所有 PMC。一個(gè)自然而然的想法是對(duì)上述提到的所有寄存器分別進(jìn)行相應(yīng)使能/禁能配置。 但 Intel 提供了更方便的總控寄存器 IA32_PERF_GLOBAL_CTRL(內(nèi)核代碼:MSR_CORE_PERF_GLOBAL_CTRL):
圖 5 參考內(nèi)核 __intel_pmu_enable_all、__intel_pmu_disable_all。 值得注意的是:IA32_PERF_GLOBAL_CTRL 寄存器中,為 fixed PMC 預(yù)留的偏移是從 32 開始的,換句話說,至少從目前的 Intel CPU 設(shè)計(jì)來說,其為 generic PMC 預(yù)留的數(shù)量為 32。這也是 4.2.2 中提到的,內(nèi)核 INTEL_PMC_IDX_FIXED 宏為 32 的原因。
5. 伏筆
上一章就 PMU 的最基本操作進(jìn)行了闡述,實(shí)際上 perf 框架中對(duì) PMU 的管理、操作遠(yuǎn)超這些范疇。 本章意在拋出問題,為后續(xù)文章埋個(gè)伏筆:
PMC 的分時(shí)復(fù)用問題:如你所知,PMC 的數(shù)量有限,如果要監(jiān)控的事件數(shù)超出 PMC 數(shù)(具體來說,對(duì)某個(gè) CPU 有多個(gè)監(jiān)控事件組,所有事件組的事件數(shù)總和完全有可能超過 PMC 總數(shù)),必然要求 perf 框架有 PMC 分時(shí)復(fù)用的機(jī)制。
事件的 PMC 分配問題:當(dāng)用戶指定要監(jiān)控某個(gè)事件時(shí),到底應(yīng)該為其分配哪個(gè) PMC?如果是 fixed 事件,是否要傾向于優(yōu)先為其分配 fixed PMC?
事件的 PMC 寄存器配置生成問題:拿 generic PMC 來說,其 event select 寄存器的配置,是如何從前端接口的參數(shù)配置生成出來的(以在合適的時(shí)機(jī)寫入硬件寄存器)?尤其是 hardware、hw_cache 這類采用通用 ID(前文 3.2 節(jié))的事件,是如何轉(zhuǎn)成底層的 umask 和 event select 的?
6. 總結(jié)
本文探討了 PMU 的基本概念,并討論了 PMU 的基本操作及編程,文章的最后拋出了 perf 框架實(shí)現(xiàn)中所面臨的若干實(shí)際問題。 編輯:黃飛
?
評(píng)論
查看更多