我正在开发一个Linux内核驱动程序,它可以为用户空间提供一大块物理内存。我有一个工作版本的驱动程序,但它目前非常慢。所以,我已经退回了几步,尝试制作一个小而简单的驱动程序来重现问题。
我使用内核参数memmap=2G$1G
在启动时保留内存。然后,在驱动程序的__init
函数中,我ioremap
一些内存,并将其初始化为已知值。我还提供了一些代码来衡量时间:
#define RESERVED_REGION_SIZE (1 * 1024 * 1024 * 1024) // 1GB
#define RESERVED_REGION_OFFSET (1 * 1024 * 1024 * 1024) // 1GB
static int __init memdrv_init(void)
{
struct timeval t1, t2;
printk(KERN_INFO "[memdriver] init\n");
// Remap reserved physical memory (that we grabbed at boot time)
do_gettimeofday( &t1 );
reservedBlock = ioremap( RESERVED_REGION_OFFSET, RESERVED_REGION_SIZE );
do_gettimeofday( &t2 );
printk( KERN_ERR "[memdriver] ioremap() took %d usec\n", usec_diff( &t2, &t1 ) );
// Set the memory to a known value
do_gettimeofday( &t1 );
memset( reservedBlock, 0xAB, RESERVED_REGION_SIZE );
do_gettimeofday( &t2 );
printk( KERN_ERR "[memdriver] memset() took %d usec\n", usec_diff( &t2, &t1 ) );
// Register the character device
...
return 0;
}
我加载驱动程序,然后检查dmesg。它报道:
[memdriver] init
[memdriver] ioremap() took 76268 usec
[memdriver] memset() took 12622779 usec
这是memset的12.6秒。这意味着memset以 81 MB /秒 运行。为什么它在如此缓慢?
这是Fedora 13上的内核2.6.34,它是一个x86_64系统。
编辑:
此方案背后的目标是获取一大块物理内存并使其可用于PCI设备(通过内存的总线/物理地址)和用户空间应用程序(通过调用mmap
,由司机支持)。然后,PCI设备将不断用数据填充此内存,用户空间应用程序将读取它。如果ioremap
是一种不好的方法(正如Ben在下面提到的那样),我会接受其他建议,这些建议将允许我获得可由硬件和软件直接访问的任何大块内存。我也可以使用更小的缓冲区。
请参阅下面的最终解决方案。
答案 0 :(得分:4)
ioremap
分配不可缓存的页面,因为您需要访问memory-mapped-io设备。这可以解释你的糟糕表现。
答案 1 :(得分:2)
我认为ioremap()
不是你想要的。您应该只使用reservedBlock
,readb
,readl
,writeb
等来访问结果(您称之为memcpy_toio
)。甚至不能保证返回虚拟映射(尽管它显然在您的平台上)。我猜这个区域被映射为未缓存(适用于IO寄存器),导致可怕的性能。
答案 2 :(得分:1)
已经有一段时间了,但我正在更新,因为我最终找到了这个ioremap问题的解决方法。
由于我们将自定义硬件直接写入内存,因此将其标记为不可缓存可能更为正确,但它无法忍受,并且无法用于我们的应用程序。我们的解决方案是只有在有足够的新数据填充我们架构上的整个缓存行时才读取该内存(环形缓冲区)(我认为这是256字节)。这保证了我们从来没有得到过时的数据,而且速度非常快。
答案 3 :(得分:1)
我已尝试使用memmap
此块的ioremap
ping给了我一个映射的内存地址空间,超过几个tera字节。
当您要求从64 GB开始保留128GB内存时。你在/ proc / vmallocinfo
中看到以下内容0xffffc9001f3a8000-0xffffc9201f3a9000 137438957568 0xffffffffa00831c9 phys=1000000000 ioremap
因此地址空间从0xffffc9001f3a8000开始(这太大了)。
其次,你的观察是正确的。甚至memset_io
也会导致触及所有内存的极大延迟(数十分钟)。
因此,所花费的时间主要是地址空间转换和非可缓存页面加载。