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

6pp0gazn  于 2023-05-23  发布在  其他
关注(0)|答案(1)|浏览(201)

对于大学,我目前正在试验MONITOR/MWAIT指令对。具体来说,我想测量CPU在不同场景下使用了多少能量,并且已经编写了一个相对良好的测试设置。作为设置的一部分,我让所有内核进入MWAIT,然后在指定时间后使用NMI再次唤醒它们。到目前为止,一切正常,但现在我想测试电源管理提示如何影响功耗。
不幸的是,除了0之外的每个提示似乎都导致MWAIT不等待NMI,而是在3-4 ms之后自行唤醒。就我对文档的理解,电源管理提示不应该对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));
}

这显然只是我编写的Linux内核模块的一小部分摘录,但应该包含所有重要的要点。dummy是一个变量,在这里你可以看到的范围之外从未使用过。它的存在只是为了让我有一个有效的地址传递给监视器。do_mwait()是在我进行测量时在每个可用内核上执行的函数。正如我所说的,只需将do_mwait()的第二行中的0x100交换,就可以使其按照我期望的方式工作。
因为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"
   ...

我希望这是足够的背景。请告诉我是否需要分享更多信息。谢谢你的支持,即使这只是一个(有教养的)猜测!

yruzcnhs

yruzcnhs1#

来自英特尔手册(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-specification-update.pdf)确实提到了一些与C状态相关的问题,其中“BIOS可以包含一个解决方案”,这实际上意味着BIOS可以包含一个改变CPU行为的微码更新。英特尔通常包括微代码在发现问题时禁用某些优化或功能的方法,也许在奇怪的角落情况下修复一些锁定的唯一方法是总是在中断时从C2或更深处唤醒核心,即使它们应该被屏蔽。
这纯粹是猜测的原因,但英特尔确实清楚地证明了你所看到的是可能的。
其他的可能性包括SMI(系统管理模式中断),但希望您的系统不会定期触发这些中断。
参见:

我还在评论中建议,使用超线程,这个逻辑核心可能会在其他逻辑核心必须唤醒时唤醒,因为物理核心已经通电,所以大部分唤醒成本已经支付。两种情况都有可能:唤醒两个兄弟意味着它不能在单线程模式下运行,以更快地为一个明确希望唤醒。
这纯粹是猜测,可能是两种情况。文档明确指出,一般情况下可能会出现虚假唤醒。
(Your代码将物理内核的两个逻辑内核同时置于睡眠状态,这应该可以避免您的情况下的问题。但如果其他人在单个逻辑核心上遇到同样的问题,也许可以尝试禁用超线程。)

相关问题