今天一样是 Supporting PMUs on RISC-V platforms
相关的内容,先来简单回顾昨天的简介:
导致了 perf stat 堪用,而 perf record 无法使用的状况。
接下来,一样先就文件本身来研究,再来记录 perf stat 和 perf record 的区别。
pmu 初始化 (Initialization)
:
riscv_pmu
是 RISC-V 平台上,pmu 的一个实例(instance),预设是指向 riscv_base_pmu
这一个最基础的(baseline) pmu 实作,不同的实作者可以根据自己的需求来扩充这个资料结构。/* ${linux}/arch/riscv/kernel/perf_event.c */
static const struct riscv_pmu *riscv_pmu __read_mostly;
static const struct riscv_pmu riscv_base_pmu = { // pmu 内部资料结构
.pmu = &min_pmu,
.max_events = ARRAY_SIZE(riscv_hw_event_map),
.map_hw_event = riscv_map_hw_event,
.hw_events = riscv_hw_event_map,
.map_cache_event = riscv_map_cache_event,
.cache_events = &riscv_cache_event_map,
.counter_width = 63,
.num_counters = RISCV_BASE_COUNTERS + 0,
.handle_irq = &riscv_base_pmu_handle_irq,
/* This means this PMU has no IRQ. */
.irq = -1,
};
static int __init init_hw_perf_events(void)
{
struct device_node *node = of_find_node_by_type(NULL, "pmu");
const struct of_device_id *of_id;
riscv_pmu = &riscv_base_pmu; // 预设为 riscv_base_pmu
if (node) {
of_id = of_match_node(riscv_pmu_of_ids, node); // 找寻 dts 里面的 pmu node
if (of_id)
riscv_pmu = of_id->data;
of_node_put(node);
}
perf_pmu_register(riscv_pmu->pmu, "cpu", PERF_TYPE_RAW);
return 0;
}
arch_initcall(init_hw_perf_events); // kernel 在 initcall 初始化 arch 时,会执行这个 function
pmu 事件初始化 (Event Initialization)
+ 使用 perf
时,perf 会执行 perf_event_open
这个系统呼叫 (system call),接下来就会执行 event_init 里面的 member。
+ 目前 RISC-V 仅支援 cycle、instruction count 这两项 event
/* ${linux}/arch/riscv/kernel/perf_event.c */
static const int riscv_hw_event_map[] = { // baseline 仅支援计算 cycle、instruction count
[PERF_COUNT_HW_CPU_CYCLES] = RISCV_PMU_CYCLE,
[PERF_COUNT_HW_INSTRUCTIONS] = RISCV_PMU_INSTRET,
[PERF_COUNT_HW_CACHE_REFERENCES] = RISCV_OP_UNSUPP,
[PERF_COUNT_HW_CACHE_MISSES] = RISCV_OP_UNSUPP,
[PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = RISCV_OP_UNSUPP,
[PERF_COUNT_HW_BRANCH_MISSES] = RISCV_OP_UNSUPP,
[PERF_COUNT_HW_BUS_CYCLES] = RISCV_OP_UNSUPP,
};
static const int riscv_cache_event_map[PERF_COUNT_HW_CACHE_MAX]
[PERF_COUNT_HW_CACHE_OP_MAX]
[PERF_COUNT_HW_CACHE_RESULT_MAX] = {
[C(L1D)] = { // L1 Dcache
[C(OP_READ)] = { // READ 操作
[C(RESULT_ACCESS)] = RISCV_OP_UNSUPP,
[C(RESULT_MISS)] = RISCV_OP_UNSUPP,
},
[C(OP_WRITE)] = {
[C(RESULT_ACCESS)] = RISCV_OP_UNSUPP,
[C(RESULT_MISS)] = RISCV_OP_UNSUPP,
},
[C(OP_PREFETCH)] = {
[C(RESULT_ACCESS)] = RISCV_OP_UNSUPP,
[C(RESULT_MISS)] = RISCV_OP_UNSUPP,
},
},
...
};
static int riscv_event_init(struct perf_event *event)
{
...
switch (event->attr.type) {
case PERF_TYPE_HARDWARE:
code = riscv_pmu->map_hw_event(attr->config); // init hardware 相关 event
break;
case PERF_TYPE_HW_CACHE:
code = riscv_pmu->map_cache_event(attr->config);
break;
case PERF_TYPE_RAW:
return -EOPNOTSUPP;
default:
return -ENOENT;
}
event->destroy = riscv_event_destroy;
if (code < 0) {
event->destroy(event);
return code;
}
...
}
static int riscv_map_hw_event(u64 config) // 实际 init (mapping) 过程
{
...
return riscv_pmu->hw_events[config];
}
中断 (Interrupt)
reserve_pmc_hardware
,将这个 service routine 变成 globally 可存取的。static int reserve_pmc_hardware(void)
{
int err = 0;
mutex_lock(&pmc_reserve_mutex);
if (riscv_pmu->irq >= 0 && riscv_pmu->handle_irq) {
err = request_irq(riscv_pmu->irq, riscv_pmu->handle_irq,
IRQF_PERCPU, "riscv-base-perf", NULL);
}
mutex_unlock(&pmc_reserve_mutex);
return err;
}
存取计数器 (Reading/Writing Counters)
pmu->start
时,要将 counter 设置成一个适当的数值,并且等待 overflow 发生;另一个则是,在 overflow 的 handler 中,把 counter 设回一开始那个适当的数值。static inline u64 read_counter(int idx)
{
u64 val = 0;
switch (idx) {
case RISCV_PMU_CYCLE:
val = csr_read(CSR_CYCLE);
break;
case RISCV_PMU_INSTRET:
val = csr_read(CSR_INSTRET);
break;
default:
WARN_ON_ONCE(idx < 0 || idx > RISCV_MAX_COUNTERS);
return -EINVAL;
}
return val;
}
static inline void write_counter(int idx, u64 value) // 目前在 S mode 不支援 write counter
{
/* currently not supported */
WARN_ON_ONCE(1);
}
add()/del()/start()/stop()
今天简单回顾了昨天提到的部分,然後简单地把文件下半部份记录完成 (缺少了 perf 的部分,明天再来补罗),明天我们来看看新的 extension 长什麽样子,以及 Alan 提出的 Andes 解决方案到社群和大家讨论的过程! 明天见!
<<: D15 - 如何用 Apps Script 自动化地创造与客制 Google Docs?(二)快速生出大量寄件信封资料
>>: Day 15 | 同步与非同步- Coroutines
前情提要 昨天带各位用 Selenium 写了自动发留言的 Discord 机器人,可以在指定的文字...
参加WUSON CISSP的培训课程,请大家务必上课作好笔记、课後复习及课前预习喔! 笔记是通过考...
tags: OC 30 day 为什麽放这张图?应为我觉得MRC就像是古老的仪式。既然MRC已经没什...
要让语音讯号能够输入到模型中进行训练,就必须将其转换成电脑看得懂的数值格式,也就是语音特徵。 我们使...
上一篇我们有提到用 KAPT 参数去呼叫 纯 Kotlin 和 Android 的 code gen...