3

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

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

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

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

+------+------+------+------+
| 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-generic 内核的 Linux 机器。

4

1 回答 1

5

使用 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.
于 2014-07-31T12:34:31.823 回答