gpt4 book ai didi

c - 如何在 linux 内核模块中分配由 1GB HugePages 支持的 DMA 缓冲区?

转载 作者:IT王子 更新时间:2023-10-29 00:22:07 29 4
gpt4 key购买 nike

我正在尝试为 HPC 工作负载分配 DMA 缓冲区。它需要 64GB 的缓冲区空间。在计算之间,一些数据被卸载到 PCIe 卡上。与其将数据复制到由 pci_alloc_consistent 提供的一堆极小的 4MB 缓冲区中,不如创建 64 个 1GB 缓冲区,由 1GB HugePages 支持。

一些背景信息:内核版本:CentOS 6.4/2.6.32-358.el6.x86_64内核启动选项:hugepagesz=1g hugepages=64 default_hugepagesz=1g

/proc/meminfo 的相关部分:AnonHugePages:0 kBHugePages_Total:64HugePages_Free:64HugePages_Rsvd:0HugePages_Surp:0大页大小:1048576 kBDirectMap4k:848 KBDirectMap2M:2062336 KBDirectMap1G:132120576 kB

我可以挂载 -t hugetlbfs nodev/mnt/hugepages。 CONFIG_HUGETLB_PAGE 为真。 MAP_HUGETLB 已定义。

我已经阅读了一些关于使用 libhugetlbfs 在用户空间调用 get_huge_pages() 的信息,但理想情况下,该缓冲区将在内核空间中分配。我尝试用 MAP_HUGETLB 调用 do_mmap() 但它似乎并没有改变免费大页面的数量,所以我不认为它实际上支持大页面的 mmap。

所以我想我的意思是,是否有任何方法可以将缓冲区映射到内核空间中的 1GB HugePage,还是必须在用户空间中完成?或者,如果有人知道我可以获得大量(1-64GB)的连续物理内存作为内核缓冲区可用的任何其他方式?

最佳答案

问题

  1. 通常,如果您想分配 DMA 缓冲区或获取物理地址,这是在内核空间中完成的,因为用户代码永远不必处理物理地址。
  2. Hugetlbfs只提供用户空间映射,分配1GB大页面,获取用户空间虚拟地址
  3. 不存在将用户大页面虚拟地址映射到物理地址的函数

Eureka

但是这个功能确实存在!埋deep in the 2.6 kernel source code这个函数用于从虚拟地址获取结构页面,标记为“仅用于测试”并使用#if 0 阻止:

#if 0   /* This is just for testing */
struct page *
follow_huge_addr(struct mm_struct *mm, unsigned long address, int write)
{
unsigned long start = address;
int length = 1;
int nr;
struct page *page;
struct vm_area_struct *vma;

vma = find_vma(mm, addr);
if (!vma || !is_vm_hugetlb_page(vma))
return ERR_PTR(-EINVAL);

pte = huge_pte_offset(mm, address);

/* hugetlb should be locked, and hence, prefaulted */
WARN_ON(!pte || pte_none(*pte));

page = &pte_page(*pte)[vpfn % (HPAGE_SIZE/PAGE_SIZE)];

WARN_ON(!PageHead(page));

return page;
}

解决方案:由于上面的函数实际上并没有编译到内核中,您需要将它添加到您的驱动程序源中。

用户端工作流程

  1. 在启动时使用内核启动选项分配 1gb 大页面
  2. 用hugetlbfs调用get_huge_pages()获取用户空间指针(虚拟地址)
  3. 将用户虚拟地址(转换为 unsigned long 的普通指针)传递给驱动程序 ioctl

内核驱动程序工作流程

  1. 通过ioctl接受用户虚拟地址
  2. 调用follow_huge_addr获取struct page*
  3. 在struct page*上调用page_to_phys获取物理地址
  4. 为 DMA 设备提供物理地址
  5. 如果您还需要内核虚拟指针,请在结构页面上调用 kmap*

免责声明

  • 上述步骤是几年后记忆起来的。我无法访问原始源代码。做好尽职调查,确保我没有忘记任何一步。
  • 之所以可行,是因为在启动时分配了 1GB 大页面,并且它们的物理地址被永久锁定。不要尝试将非 1GBhugepage 支持的用户虚拟地址映射到 DMA 物理地址!你会过得很糟糕!
  • 在您的系统上仔细测试以确认您的 1GB 大页面实际上已锁定在物理内存中并且一切正常。这段代码在我的设置上完美运行,但如果出现问题,这里会有很大的危险。
  • 此代码仅保证在 x86/x64 架构(其中物理地址 == 总线地址)和内核版本 2.6.XX 上运行。在以后的内核版本上可能有更简单的方法来执行此操作,或者现在可能完全不可能。

关于c - 如何在 linux 内核模块中分配由 1GB HugePages 支持的 DMA 缓冲区?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19460544/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com