如何在 linux 内核模块中分配由 1GB HugePages 支持的 DMA 缓冲区? [英] How do I allocate a DMA buffer backed by 1GB HugePages in a linux kernel module?

查看:103
本文介绍了如何在 linux 内核模块中分配由 1GB HugePages 支持的 DMA 缓冲区?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm trying to allocate a DMA buffer for a HPC workload. It requires 64GB of buffer space. In between computation, some data is offloaded to a PCIe card. Rather than copy data into a bunch of dinky 4MB buffers given by pci_alloc_consistent, I would like to just create 64 1GB buffers, backed by 1GB HugePages.

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

Some background info: kernel version: CentOS 6.4 / 2.6.32-358.el6.x86_64 kernel boot options: 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

relevant portion of /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 已定义.

I can mount -t hugetlbfs nodev /mnt/hugepages. CONFIG_HUGETLB_PAGE is true. MAP_HUGETLB is defined.

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

I have read some info on using libhugetlbfs to call get_huge_pages() in user space, but ideally this buffer would be allocated in kernel space. I tried calling do_mmap() with MAP_HUGETLB but it didn't seem to change the number of free hugepages, so I don't think it was actually backing the mmap with huge pages.

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

So I guess what I'm getting at, is there any way I can map a buffer to a 1GB HugePage in kernel space, or does it have to be done in user space? Or if anyone knows of any other way I can get an immense (1-64GB) amount of contiguous physical memory available as a kernel buffer?

推荐答案

PROBLEM

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

尤里卡

但是这个功能确实存在!埋在深处2.6内核源代码在于这个函数从一个虚拟地址获取一个结构页,标记为只用于测试"并用#if 0阻塞:

But the function does exist! Buried deep in the 2.6 kernel source code lies this function to get a struct page from a virtual address, marked as "just for testing" and blocked with #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;
}

解决方案:由于上面的函数实际上并未编译到内核中,因此您需要将其添加到驱动程序源中.

SOLUTION: Since the function above isn't actually compiled into the kernel, you will need to add it to your driver source.

用户端工作流程

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

内核驱动程序工作流程

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

免责声明

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

这篇关于如何在 linux 内核模块中分配由 1GB HugePages 支持的 DMA 缓冲区?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆