2013-10-18 24 views
8

我正在尝试为HPC工作负载分配DMA缓冲区。它需要64GB的缓冲空间。在计算之间,一些数据被卸载到PCIe卡。我不想将数据复制到由pci_alloc_consistent给出的一堆小小的4MB缓冲区中,我只想创建64个1GB缓冲区,由1GB HugePages支持。如何在Linux内核模块中分配由1GB HugePages支持的DMA缓冲区?

一些背景资料: 内核版本:CentOS的6.4/2.6.32-358.el6.x86_64 内核引导选项:hugepagesz =1克大页面= 64 default_hugepagesz =为1G的/ proc/meminfo中

相关部分: AnonHugePages:0 KB HugePages_Total:64 HugePages_Free:64 HugePages_Rsvd:0 HugePages_Surp:0 Hugepagesize:1048576 KB DirectMap4k:848 KB DirectMap2M:2062336 KB DirectMap1G: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)的连续物理内存作为内核缓冲区?

+0

有趣的问题,你的目标主要是为了避免内核和用户空间之间的复制? – ChuckCottrill

+2

所有这些API都是针对用户空间的。看看如何实现hugetlbfs,尤其是'hugetlbfs_file_mmap'。 –

+0

@muusbolla你能找到答案吗? –

回答

1

问题

  1. 一般来说,如果你想分配一个DMA缓冲区,或者得到一个物理地址,这是在内核空间完成的,因为用户代码不应该有渣土周围的物理地址。
  2. hugetlbfs的只提供用户空间的映射来分配1GB大内存页,并获得用户空间的虚拟地址
  3. 无功能的存在是为了用户hugepage虚拟地址映射为物理地址

EUREKA

但功能确实存在!埋deep in the 2.6 kernel source code位于此函数从虚拟地址获得一个struct page,标记为“只是用于测试”,并用#如果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; 
} 

SOLUTION: 由于以上功能实际上并没有编译成内核,你需要将它添加到你的驱动源。

用户侧工作流程

  1. 在内核引导选项
  2. 呼叫get_huge_pages()启动与hugetlbfs的分配1GB大页面来获得用户空间指针(虚拟地址)
  3. 通行证用户虚拟地址(正常指针投为unsigned long)来驱动的ioctl

内核驱动工作流

  1. 接受用户通过ioctl虚拟地址
  2. 呼叫follow_huge_addr获得结构页*的结构页面上
  3. 呼叫page_to_phys *获得的物理地址
  4. 为DMA提供物理地址设备
  5. 呼叫在结构页*的kmap如果你也希望有一个内核虚拟指针

免责声明

  • 几年后,上述步骤正在被重新收录。我无法访问原始源代码。做你的尽职调查,并确保我不会忘记一步。
  • 这样做的唯一原因是因为1GB大页面在启动时分配并且其物理地址被永久锁定。不要试图将非1GB页面支持的用户虚拟地址映射到DMA物理地址!你会有一段糟糕的时光!
  • 在您的系统上仔细测试以确认您的1GB大型页面实际上已锁定在物理内存中,并且所有内容都正常工作。这个代码完美地工作在我的设置上,但如果出现问题,这里存在很大的危险。
  • 此代码只能保证在x86/x64架构(其中物理地址==总线地址)和内核版本2.6.XX上可用。在稍后的内核版本上可能会有更简单的方法,或者现在可能完全不可能。
2

这在内核空间中并不常见,所以没有太多的例子。

就像任何其他的网页,大页面分配与alloc_pages,以调:

struct page *p = alloc_pages(GFP_TRANSHUGE, HPAGE_PMD_ORDER); 

HPAGE_PMD_ORDER是一个宏观的,在正常的网页来定义一个巨大的页面的顺序。以上意味着在内核中启用透明的巨大页面。

然后,您可以继续使用kmap()将获取的页面指针映射为常规方式。

声明:我从来没有尝试过,所以你可能不得不做一些尝试。有一件事要检查是这样的:HPAGE_PMD_SHIFT表示一个较小的“巨大”页面的顺序。如果你想使用这些巨大的1GB页面,你可能需要尝试不同的顺序,可能是PUD_SHIFT - PAGE_SHIFT。

+1

是否支持1GB页面的透明大页面? – osgx

相关问题