Linux kernel mwait x86指令不';不要等待DMA

Linux kernel mwait x86指令不';不要等待DMA,linux-kernel,x86,dma,Linux Kernel,X86,Dma,我正在尝试使用/指令监视从设备到内存位置的DMA写入。在内核模块(char设备)中,我有以下在内核线程中运行的代码(非常类似于内核代码): static int do_monitor(void *arg) { struct page *p = arg; // p is a 'struct page *'; it's also remapped to user space uint32_t *location_p = phys_to_virt(page_to_phys(p)); ui

我正在尝试使用/指令监视从设备到内存位置的DMA写入。在内核模块(char设备)中,我有以下在内核线程中运行的代码(非常类似于内核代码):

static int do_monitor(void *arg)
{
  struct page *p = arg; // p is a 'struct page *'; it's also remapped to user space
  uint32_t *location_p = phys_to_virt(page_to_phys(p)); 
  uint32_t prev = 0;
  int i = 0;
  while (i++ < 20) // to avoid infinite loop
  {
    if (*location_p == prev)
    {
        __monitor(location_p, 0, 0);
        if (this_cpu_has(X86_FEATURE_CLFLUSH_MONITOR))
          clflush(location_p);
        if (*location_p == prev)
          __mwait(0, 0);
    }
    prev = *location_p;
    printk(KERN_NOTICE "%d", prev);
  }
}
也就是说,
mwait
似乎根本不等待。
原因可能是什么?

监视器/MWAIT语义的定义没有明确规定DMA事务是否会触发它。假设触发发生在逻辑处理器的存储中

英特尔官方软件开发人员手册中目前对MONITOR和MWAIT的描述在这方面相当模糊。然而,我注意到监视器部分有两个条款:

  • EAX的内容是一个有效地址(在64位模式下,使用RAX)。默认情况下,DS段用于创建受监控的线性地址

  • 地址范围必须使用回写类型的内存。只有写回内存才能正确触发 监控硬件

  • 第一条规定,监视器用于线性地址,而不是物理地址。设备及其DMA仅用于物理地址。因此,基本上这意味着所有依赖于相同监视范围的代理都应该在相同的虚拟内存空间域中运行

    第二个子句要求被监视的内存区域是可缓存的(write-back,WB)。对于DMA,各自的内存范围通常必须标记为不可缓存,或者最多只能写组合(UC或WC)。这更强烈地表明,您打算使用DMA触发的MONITOR/MWAIT在当前硬件上不太可能工作


    考虑到您的高层次目标——能够判断设备何时写入了给定的内存范围——我记不起任何可靠的方法来实现它,除了对设备(VTd、IOMMU等)使用虚拟化之外,基本上,外围设备的经典方法是在写入内存时发出中断。在中断到来之前,CPU无法判断所有DMA字节是否已成功到达内存中的目标


    设备虚拟化允许以透明的方式从设备中提取物理地址,并且当设备尝试从内存中写入/读取时,会出现相当于页面错误的情况。

    您是否检查了
    监视器
    /
    MWAIT
    是否可用(即您的BIOS没有将其关闭)?其次,您应该在监视之前执行
    clflush
    ,否则您只会使缓存无效,强制将其写回内存(如果其脏),从而触发等待条件。
    MWAIT
    可以返回“早”。首先,由于不可屏蔽事件(NMI、SMI和其他一些“低于操作系统控制”的中断机制,以及异步故障),但第二,更重要的是,由于普通中断,除非它们被明确禁用(
    \u cli()
    和/或
    本地irq\u disable()
    在Linux中…通常不是一个好主意,有很多副作用)。在操作系统“
    idle()
    循环之外使用它是。。。这项任务相当于在驱动程序代码中重新实现调度程序的这一部分(您的代码引用是Linux的一部分,
    idle()
    …)。你在写内核旁路代码吗?@Necrolis谢谢,当然
    clflush
    不在正确的位置;然而,修复它并没有帮助。根据CPUID,监视器似乎已启用。@FrankH。这不是贝斯的内核。我有一个可以写入特定内存位置的设备(通过DMA),我正在尝试各种方法来找出它何时写入以及写入的内容。@Jianchen没有,我在用户空间里忙了一等——这对我来说已经足够了。这听起来不太对。DMA是否使用物理地址实际上无关紧要。重要的是,DMA请求是缓存一致的,这很可能是缓存一致的。@HadiBrais有些情况下DMA会触发监控范围。它将对应于来自MWAIT的虚假唤醒,这是文档允许的,但不能保证在实现之间一致发生。线性/物理地址用于强调文档中关于指令预期用途的明显提示。“重要的是DMA请求与缓存一致”-MWAIT操作和底层缓存基础设施之间没有记录的链接。即使存在连接,在多插座系统的情况下也可能非常复杂。这很好。我的观点是,即使文档中没有明确说明,监视器硬件也可能与一致的DMA存储一致工作。最初的问题是“为什么mwait没有等待?”这可能与DMA无关。例如,OP可能没有禁用计时器中断,这是while循环中的
    printk
    被多次执行的一个合理解释。另一个可能但不太可能的原因是
    location\p
    没有指向英特尔处理器所需的WB类型内存。
    int fd = open("/dev/mon_test_dev", O_RDWR);
    unsigned char *mapped = (unsigned char *)mmap(0, mmap_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
    for (int i = 1; i <= 5; ++i)
      *mapped = i;
    munmap(mapped, mmap_size);
    close(fd);
    
    1
    2
    3
    4
    5
    5
    5
    5
    5
    5
    5 5 5 5 5 5 5 5 5 5