mwait x86指令不等待DMA

时间:2014-02-10 08:02:18

标签: linux-kernel x86 dma

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

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);
  }
}

在用户空间中,我有以下测试代码:

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

即。似乎mwait根本不等。 可能是什么原因?

1 个答案:

答案 0 :(得分:5)

MONITOR / MWAIT语义的定义没有明确指定DMA事务是否可以触发它。假设逻辑处理器的存储发生了触发。

英特尔官方软件开发人员手册中对MONITOR和MWAIT的最新描述对此非常模糊。但是,MONITOR部分有两个条款引起了我的注意:

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

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

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

    第二个子句要求受监控的内存区域可缓存(回写,WB)。对于DMA,通常必须将相应的存储器范围标记为不可缓存,或者最好是写入组合(UC或WC)。这甚至是一个更强有力的指标,表明您使用DMA触发MONITOR / MWAIT的意图不太可能在当前硬件上运行。

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

    设备虚拟化允许以透明的方式从设备中抽象物理地址,并在尝试从内存中写入/读取时具有等效的页面错误。