【问题标题】:How would the MONITOR instruction (_mm_monitor intrinsic) be used by a driver?驱动程序如何使用 MONITOR 指令(_mm_monitor 内在)?
【发布时间】:2017-12-13 03:05:48
【问题描述】:

我正在探索MONITOR 指令(或等效的内在函数_mm_monitor)的用法。虽然我找到了描述它们的文献,但我找不到任何关于如何使用它的具体示例/示例。

谁能分享如何在驱动程序中使用此指令/内在函数的示例?本质上,我想用它来观察内存范围。

【问题讨论】:

    标签: x86 driver cpu-architecture smp


    【解决方案1】:

    monitor 指令使用RAX/EAX/AX 中指定的地址武装地址监控硬件。来自英特尔的引述
    监视器的状态由指令mwait使用。

    使用的有效地址大小(16、32 或 64 位)取决于编码指令的有效地址大小(即可以用 67h 前缀覆盖它,默认情况下它与代码大小相同)。

    rax/eax/ax 中给出的地址是逻辑地址的偏移部分,用于计算用于启动监视器的线性地址。
    段部分默认为ds,可以应用段覆盖前缀来更改段。
    作为用于监视器的线性地址,分页不会影响监视。

    monitor(和mwait)指令的可用性由位CPUID.01H:ECX.MONITOR[bit 3]1指示。 这是一个特权指令,但英特尔声称:

    指令在大于 0 的级别有条件地可用。

    检测这种情况的建议方法是尝试执行monitor 并处理最终的#UD 异常(以操作系统的自定义方式将其报告给用户程序)。

    监控的地址范围必须是可写回缓存的。
    由于涉及高速缓存和高速缓存一致性子系统,地址范围的大小以最小和最大大小给出。
    CPUID.01H:EAX[bit 15: 0] 给出最小范围大小。这是硬件监视器监视的区域的长度。
    但是,缓存一致性流量可能适用于更大尺寸的“块”(行),并且如果后者包含在前者中,则与受监控区域相邻的写入仍会触发它。
    这会产生最大范围大小,可以在 CPUID.01H:EBX[bit 15:0] 中找到。
    要正确使用monitor,请确保监控的数据结构适合最小范围大小,但还要确保没有代理写入它旁边的地址直到最大范围大小。

    例如,如果最小范围大小是 8 个字节,最大大小是 16 个字节,请确保观察到的结构适合 8 个字节,但要再填充 8 个字节以达到总共 16 个字节,这样就不会从出现第 8 到第 16 个字节。

    在单个集群系统中,以上两个值相等。我的都是 64 字节。
    BIOS 负责在多集群系统中报告IA32_MONITOR_FILTER_LINE_SIZE 中的高速缓存一致性行大小。

    出于指令顺序和访问权限的目的,monitor 是一个负载。

    monitor 允许程序员指定提示扩展
    扩展名在ecx 中指定,而提示在edx 中。
    不受支持的扩展会引发 #GP 异常,不受支持的提示将被忽略。
    我不知道monitor 的任何扩展或提示,英特尔手册报告

    奔腾 4 处理器(系列 15,型号 3),未定义扩展或提示。

    我相信这条线总体上是正确的,它只是其中有一个过时的处理器型号。
    此外,monitor 的伪代码报告了 #GP If ECX ≠ 0.

    在不检查其状态的情况下启动监视器(使用mwait)不会造成任何伤害。

    内在函数是void _mm_monitor(void const *p, unsigned extensions,unsigned hints)


    监视器布防后,可以通过不同的条件触发:

    • 外部中断:NMI、SMM、INIT、BINIT、MCERR
    • 故障、中止,包括机器检查
    • 架构 TLB 失效,包括写入 CR0、CR3、CR4 和某些 MSR 写入
    • 由于快速系统调用和远调用导致的自愿转换
    • 屏蔽中断(如果启用)
    • 在受监控的地址范围内写入

    监视器的状态对程序员是不可见的,但可以用mwait 进行测试。
    mwait 进入实现定义的低功耗状态,直到监视器处于触发状态。
    如果监视器未进入待命状态或者它已经被触发mwaitnop,否则它会使处理器停止执行指令,直到监视器被触发。

    mwait 也可以被赋予扩展提示
    扩展在ecx 中设置,在eax 中设置提示。
    在撰写本文时,唯一的扩展是:

    位 0 即使被屏蔽(例如,即使 EFLAGS.IF=0),也将中断视为中断事件。仅当 CPUID.05H:ECX[bit 1] = 1.
    Bits 31-1 保留

    提示让程序员指定实现定义的低功耗模式。

    位 3:0 一个 C 状态中的子 C 状态,由位 [7:4] 指示
    位 7:4 目标 C 状态
    值为 0 表示 C1; 1 表示 C2 以此类推
    01111B 的值表示 C0
    注意:MWAIT 扩展的目标 C 状态是特定于处理器的 C 状态,而不是 ACPI C 状态

    C 模式的子状态数(因此是可用性)在 CPUID.05h.EDX 中给出:

    位 03 - 00:使用 MWAIT 支持的 C0* 子 C 状态的数量。
    位 07 - 04:使用 MWAIT 支持的 C1* 子 C 状态的数量。
    位 11 - 08:使用 MWAIT 支持的 C2* 子 C 状态的数量。
    位 15 - 12:使用 MWAIT 支持的 C3* 子 C 状态的数量。
    位 19 - 16:使用 MWAIT 支持的 C4* 子 C 状态的数量。
    位 23 - 20:使用 MWAIT 支持的 C5* 子 C 状态的数量。
    位 27 - 24:使用 MWAIT 支持的 C6* 子 C 状态的数量。
    位 31 - 28:使用 MWAIT 支持的 C7* 子 C 状态的数量。

    请注意,将 CPU 置于高于 C1 的状态也会禁用其他线程,因此触发监视器的写入必须来自其他代理。

    内在函数是void _mm_mwait(unsigned extensions, unsigned hints)


    monitor/mwait 机制是为了帮助线程之间的同步而引入的,它不太适合监视对内存范围的访问,因为触发条件包括频繁发生的事件。
    mwait 之后始终强制检查是否已写入受监视范围。
    有一个example here,其模式如下:

    1. 被监视的结构被初始化为一个特定的值(比如 0)。
    2. 使用了monitor/mwait 对。
    3. 稍后,另一个人再次将特定值(例如 1)写入监视的结构。
    4. 监视器被触发并且mwait“返回”,被监视的结构值与1(发生写入)比较,如果不相等则执行跳回2。

    一些示例,未经测试伪代码可能是:

    struct MonitoredType
    {
      int (*event)(struct MonitoredType const* m);              /*Return 0 to keep monitoring*/
      struct AnyType data;                                /*Less, in size, than MIN_MONITOR_RANGE*/
      char padding[MAX_MONITOR_RANGE - sizeof(AnyType)];
    };
    
    void wait_for_write(struct MonitoredType const* m)
    {
       /* This may miss a write if it happens before MONITOR, beware of race conditions if necessary */
       do
       {
         _mm_monitor(&m->data, 0, 0);
         _mm_mwait(0, 0);
       } while ( ! m->event(m));
    }
    

    必须注意确保mwait 的退出条件是写入事件,而不是其他事件之一。
    这就是函数指针event 的原因。

    为了监控对线性地址的写入/读取,另一种方法是使用调试寄存器
    请参阅Intel manual 3 的第 17 章并检查您的操作系统文档以正确使用这些寄存器。


    1 含义:执行cpuid,将eax设置为01h,然后测试ecx的第3位。请注意IA32_MISC_ENABLE 允许操作系统或固件禁用monitor/mwait

    【讨论】:

    • 我猜最大范围至少是一个 64B 缓存行,以防您想为示例使用更实际的数字。更可能的示例是 min=64 和 max=128,在某个外部缓存级别使用较大行的系统上。 (IIRC,P4 在某些缓存级别中使用了 128B 行。)
    猜你喜欢
    • 2019-12-19
    • 1970-01-01
    • 1970-01-01
    • 2011-06-28
    • 2016-04-17
    • 2015-11-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多