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

我正在使用一个使用由大页面支持的缓冲区的驱动程序,我发现大页面的后续性存在一些问题。

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

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

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

 +------+------+------+------+ | 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机器。

使用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.