2

我需要在 IA32_PERF_GLOBAL_STATUS MSR 中设置一个溢出标志(例如位 #33),它是只读的。

在 Skylake(支持架构性能监控版本 4)上这很容易,因为有 IA32_PERF_GLOBAL_STATUS_SET MSR 可用于此目的。不幸的是,我必须在旧处理器上这样做。所以我做了以下hack(这里是一些伪asm)。

初始条件:

IA32_FIXED_CTR_CTRL 包含 0xB0(启用 IA32_FIXED_CTR1 并在溢出时中断)。APIC 本地向量表 (0x340) 中的性能计数器寄存器未屏蔽,固定交付模式。

在 IA32_PERF_GLOBAL_STATUS 中设置位 #33:

L1:  WRMSR IA32_PERF_GLOBAL_CTRL, 0     // Stop all counters
L2:  CLI                                // Disable interrupts
L3:  RDMSR saved_ctr1, IA32_FIXED_CTR1  // Save current value of the counter
L4:  WRMSR IA32_FIXED_CTR1, 0xFFFFFFFFFFFF // Write the maximum supported value
L5:  WRMSR IA32_FIXED_CTR_CTRL, 0x30    // Enable CTR1 *without* interrupt on overflow
L6:  WRMSR IA32_PERF_GLOBAL_CTRL, 0x200000000 // Enable CTR1
L7:  // CTR1 overflows here and STATUS gets the desired value
L8:  WRMSR IA32_PERF_GLOBAL_CTRL, 0     // Stop CTR1
L9:  WRMSR IA32_FIXED_CTR_CTRL, 0xB0    // Restore original value
L10: WRMSR IA32_FIXED_CTR1, saved_ctr1  // Restore original value
L11: STI                                // Enable interrupts

这段代码工作得很好。但是,有时(大约在 1% 的情况下)当在 L11 重新启用中断时,它会产生“计数器溢出中断”。

我尝试在 L2 和 L10 之后从 APIC LVT 0x340 读取:当这个虚假中断发生时,中断掩码变为 1,这意味着该中断确实是由 L6 和 L8 之间的 CTR1 溢出引起的,但是“溢出中断”在L5!为什么会发生这种情况?

也许对 IA32_FIXED_CTR_CTRL 和 IA32_PERF_GLOBAL_CTRL 的写入过于接近,并且处理器在启动计数器之前没有足够的周期来禁用中断?我尝试在 L5-L6 和 L8-L9 之间添加 5ms 延迟,它解决了问题 - 没有发生中断。但是,如果我将其中一个降低到 4 毫秒,中断就会回来。

4

0 回答 0