为什么 MWAIT 电源管理提示会导致过早唤醒?

Obi*_*obi 5 x86 assembly intel power-management

对于大学,我目前正在尝试 MONITOR/MWAIT 指令对。具体来说,我想测量CPU在不同场景下使用了多少能量,并且已经编写了一个相对良好的工作测试设置。作为设置的一部分,我让所有内核进入 MWAIT,然后使用 NMI 在指定时间后再次唤醒它们。到目前为止,一切工作正常,但现在我想测试电源管理提示如何影响功耗。

不幸的是,除了 0 之外的每个提示似乎都会导致 MWAIT 不等待 NMI,而是在 3-4 毫秒后自行唤醒。据我了解文档,电源管理提示不应该对 MWAIT 之后继续执行产生任何影响,所以这很奇怪。由于即使在这个问题上花了几个小时我仍然没有取得任何进展,我想也许这里有人知道发生了什么!

以下是我在代码中使用 MONITOR/MWAIT 的方法:

volatile int dummy;

void do_mwait() {
    asm volatile("monitor;" ::"a"(&dummy), "c"(0), "d"(0));
    asm volatile("mwait;" ::"a"(0x10), "c"(0));
}
Run Code Online (Sandbox Code Playgroud)

这显然只是我编写的 Linux 内核模块的一小部分摘录,但应该包含所有要点。 dummy是一个变量,除了您在此处看到的内容之外从未使用过。它的存在只是为了让我有一个有效的地址可以传递给监控。 do_mwait()是在我进行测量时在每个可用核心上执行的函数。0x10正如我所说,只需将第二行中的do_mwait()与交换0即可使其按我期望的方式工作。

由于 MONITOR/MWAIT 的行为和支持的功能取决于特定的 CPU 型号,因此以下是我的测试机器上 cpuid 的所有相关(我认为)部分。据我所知,应该支持所有必要的功能:

CPU 0:
   vendor_id = "GenuineIntel"
   version information (1/eax):
      processor type  = primary processor (0)
      family          = 0x6 (6)
      model           = 0xc (12)
      stepping id     = 0x3 (3)
      extended family = 0x0 (0)
      extended model  = 0x3 (3)
      (family synth)  = 0x6 (6)
      (model synth)   = 0x3c (60)
      (simple synth)  = Intel Core (unknown type) (Haswell C0) {Haswell}, 22nm
   ...
   feature information (1/ecx):
      ...
      MONITOR/MWAIT                           = true
      ...
   ...
   MONITOR/MWAIT (5):
      smallest monitor-line size (bytes)       = 0x40 (64)
      largest monitor-line size (bytes)        = 0x40 (64)
      enum of Monitor-MWAIT exts supported     = true
      supports intrs as break-event for MWAIT  = true
      number of C0 sub C-states using MWAIT    = 0x0 (0)
      number of C1 sub C-states using MWAIT    = 0x2 (2)
      number of C2 sub C-states using MWAIT    = 0x1 (1)
      number of C3 sub C-states using MWAIT    = 0x2 (2)
      number of C4 sub C-states using MWAIT    = 0x4 (4)
      number of C5 sub C-states using MWAIT    = 0x0 (0)
      number of C6 sub C-states using MWAIT    = 0x0 (0)
      number of C7 sub C-states using MWAIT    = 0x0 (0)
   ...
   brand = "Intel(R) Core(TM) i7-4790 CPU @ 3.60GHz"
   ...
Run Code Online (Sandbox Code Playgroud)

我希望这是足够的背景。请告诉我是否需要分享更多信息。提前感谢您的任何意见,即使这只是一个(有根据的)猜测!

Pet*_*des 2

来自英特尔的手册(https://www.felixcloutier.com/x86/mwait

以下情况会导致处理器退出与实现相关的优化状态:存储到由 MONITOR 指令准备的地址范围、NMI 或 SMI、调试异常、机器检查异常、BINIT# 信号、INIT# 信号和 RESET# 信号。其他依赖于实现的事件也可能导致处理器退出依赖于实现的优化状态。

...

即使中断被屏蔽并且 ECX[0] = 0 ,特定于实现的条件也可能会导致中断,导致处理器退出依赖于实现的优化状态。

这可能是其中任何一个的例子。

显然这不是很令人满意,如果我知道在哪里可以找到任何微架构解释,解释为什么这种情况只发生在更深的睡眠状态下,那就太好了。

就像在 C1 状态 (EAX=0) 中一样,仍然有足够的电源打开来检查中断屏蔽,但在更深的睡眠状态下,甚至一些内部中断控制器的东西也被关闭了?

您希望他们可以在不启动整个核心并恢复执行的情况下进行检查,但也许这是微代码更新启用的内容,作为后来发现的某些设计问题的解决方法。英特尔的 Haswell 勘误表(https://www.intel.com/content/dam/www/public/us/en/documents/specification-updates/4th-gen-core-family-desktop-specation-update.pdf)确实提到一些与 C 状态相关的问题,其中“BIOS 可以包含解决方法”,这实际上意味着 BIOS 可以包含改变 CPU 行为的微代码更新。英特尔经常提供微代码禁用某些优化或功能的方法,以防发现问题,也许在奇怪的极端情况下修复某些锁定的唯一方法是始终从 C2 或更深层的中断中唤醒核心,即使它们“应该被屏蔽。

这纯粹是对原因的猜测,但英特尔确实清楚地证明了您所看到的情况是可能的。


其他可能性包括 SMI(系统管理模式中断),但希望您的系统不会定期触发这些中断。

也可以看看:

我还在评论中建议,通过超线程,该逻辑核心可能会在另一个逻辑核心必须唤醒时唤醒,因为物理核心已经通电,因此大部分唤醒成本已经支付。这可以是任何一种方式:唤醒两个兄弟姐妹意味着它不能在单线程模式下运行,以便对于确实想要唤醒的人来说更快。

这纯粹是猜测,可能有两种结果。文档明确指出,一般来说,虚假唤醒是可能的。

(您的代码将物理核心的两个逻辑核心同时置于睡眠状态应该可以避免您的情况出现问题。但如果其他人只有一个逻辑核心遇到相同的问题,也许可以尝试禁用超线程。)