在内核驱动程序中顺序访问大页面

时间:2014-07-28 17:10:20

标签: c memory linux-kernel huge-pages

我正在使用由大页面支持的缓冲区的驱动程序中工作,并且我发现了大页面的 sequentality 的一些问题。

在用户空间中,程序使用mmap系统调用分配由largepages支持的大缓冲区。然后通过ioctl呼叫将缓冲区传送给驾驶员。驱动程序使用get_user_pages函数来获取该缓冲区的内存地址。

这适用于缓冲区大小为1 GB(1个巨页)。 get_user_pages会返回很多页面(HUGE_PAGE_SIZE / PAGE_SIZE),但它们都是有争议的,所以没有问题。我只是使用page_address获取第一页的地址并使用它。当另一个程序在char设备上进行remap_pfn_range调用时,驱动程序还可以使用mmap将该缓冲区映射回用户空间。

但是,当缓冲区由多个巨页支持时,事情会变得复杂。似乎内核可以返回由非顺序大页面支持的缓冲区。即,如果巨页页面的布局是这样的

+------+------+------+------+
| HP 1 | HP 2 | HP 3 | HP 4 |
+------+------+------+------+

,通过预留HP1和HP4,或HP3,然后HP2,可以满足对大页面支持的缓冲区的请求。这意味着当我在最后一种情况下获得带有get_user_pages的页面时,页面0的地址实际上是在页面262.144的地址之后的1 GB(下一个巨页的头部)。

有没有办法序列化访问这些页面?我尝试重新排序地址以找到较低的地址,以便我可以使用整个缓冲区(例如,如果内核给我一个由HP3支持的缓冲区,HP2我用作HP2的基地址,但似乎会扰乱用户空间中的数据(重新排序缓冲区中的偏移量0可能偏移1GB用户空间缓冲区)。

TL; DR:给定> 1个无序的大页面,有没有办法在Linux内核驱动程序中按顺序访问它们?

顺便说一句,我正在使用3.8.0-29通用内核的Linux机器上工作。

1 个答案:

答案 0 :(得分:4)

使用CL vm_map_ram建议的功能,我能够重新映射内存,以便可以按顺序加入内存,而不管映射的大页面数量。我将代码留在这里(错误控制不包括在内),以防它帮助任何人。

struct page** pages;
int retval;
unsigned long npages;
unsigned long buffer_start = (unsigned long) huge->addr; // Address from user-space map.
void* remapped;

npages =  1 + ((bufsize- 1) / PAGE_SIZE); 

pages = vmalloc(npages * sizeof(struct page *));

down_read(&current->mm->mmap_sem);
retval = get_user_pages(current, current->mm, buffer_start, npages,
                     1 /* Write enable */, 0 /* Force */, pages, NULL);
up_read(&current->mm->mmap_sem);    

nid = page_to_nid(pages[0]); // Remap on the same NUMA node.

remapped = vm_map_ram(pages, npages, nid, PAGE_KERNEL);

// Do work on remapped.