在内核驱动程序中顺序访问大页面
我正在使用一个使用由大页面支持的缓冲区的驱动程序,我发现大页面的后续性存在一些问题。
在用户空间中,程序使用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(¤t->mm->mmap_sem); retval = get_user_pages(current, current->mm, buffer_start, npages, 1 /* Write enable */, 0 /* Force */, pages, NULL); up_read(¤t->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.