【问题标题】:mwait x86 instruction doesn't wait for DMAmwait x86 指令不等待 DMA
【发布时间】:2014-02-10 08:02:17
【问题描述】:

我正在尝试使用monitor/mwait 指令来监控从设备到内存位置的 DMA 写入。在内核模块(字符设备)中,我有以下代码(非常类似于内核代码的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 根本不等待。 可能是什么原因?

【问题讨论】:

  • 您是否检查过MONITOR/MWAIT 是否可用(即您的BIOS 没有关闭它)?其次,您应该在监控之前执行clflush before,否则您只会使缓存无效,强制将其写回内存(如果它是脏的)从而触发等待条件。
  • MWAIT 可以“提前”返回。一方面,由于不可屏蔽事件(NMI、SMI 和其他一些“低于操作系统控制”的中断机制,以及异步故障),但第二,更重要的是,由于 普通 中断,除非它们已被明确禁用(Linux 中的__cli() 和/或local_irq_disable() ...通常不是一个好主意,有很多副作用)。 在操作系统之外使用它 idle() loop 是......一项任务几乎等同于在驱动程序代码中重新实现调度程序的那一部分(您的代码引用是 Linux 的一部分 idle() ...)。你在写内核绕过代码吗?
  • @Necrolis 谢谢,当然clflush 不在正确的位置;但是修复它并没有帮助。根据 CPUID,MONITOR 似乎已启用。
  • @FrankH。这不是内核低音。我有一个写入特定内存位置(通过 DMA)的设备,我正在尝试各种方法来尝试找出它何时写入以及写入什么。
  • @Jianchen 不,我最终在用户空间进行了忙碌等待——这对我的目的来说已经足够了。

标签: linux-kernel x86 dma


【解决方案1】:

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

英特尔官方软件开发人员手册中对 MONITOR 和 MWAIT 的当前描述在这方面相当模糊。但是,MONITOR 部分中有两个子句引起了我的注意:

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

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

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

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


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

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

【讨论】:

  • 这听起来不对。 DMA 是否使用物理地址真的无关紧要。重要的是 DMA 请求是缓存一致的,它很可能是。
  • @HadiBrais 可能会出现 DMA 触发 MONITOR 的范围的情况。它将对应于文档允许的来自 MWAIT 的虚假唤醒,但不能保证在实现之间始终如一地发生。线性/物理地址是为了强调文档中关于指令预期用途的明显提示。 “重要的是 DMA 请求是缓存一致的”——MWAIT 操作和底层缓存基础设施之间没有记录的链接。即使存在联系,在例如以下情况下也可能非常复杂。多插槽系统。
  • 没关系。我的观点是,也许 MONITOR 硬件确实可以始终与一致的 DMA 存储一起使用,即使文档没有明确说明。最初的问题是“为什么 mwait 不等待?”这可能与 DMA 无关。例如,OP 可能没有禁用定时器中断,这是为什么 while 循环中的 printk 被执行多次的一种合理解释。另一个可能但不太可能的原因是location_p 没有指向英特尔处理器所需的 WB 类型的内存。
猜你喜欢
  • 2019-12-19
  • 1970-01-01
  • 2011-07-22
  • 2022-07-08
  • 2015-12-18
  • 1970-01-01
  • 2012-02-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多