我正在使用由大页面支持的缓冲区的驱动程序中工作,并且我发现了大页面的 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机器上工作。
答案 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(¤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.