Linux kernel mwait x86指令不';不要等待DMA
我正在尝试使用/指令监视从设备到内存位置的DMA写入。在内核模块(char设备)中,我有以下在内核线程中运行的代码(非常类似于内核代码):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
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的描述在这方面相当模糊。然而,我注意到监视器部分有两个条款:
考虑到您的高层次目标——能够判断设备何时写入了给定的内存范围——我记不起任何可靠的方法来实现它,除了对设备(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