linux内核驱动使用hugepages,关于C#:如何在Linux内核模块中分配由1GB HugePages支持的DMA缓冲区?...

我正在尝试为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的相关部分:

页面数:0 kB

HugePages_Total:64

HugePages_Free:64

HugePages_Rsvd:0

HugePages_Surp:0

大页面大小:1048576 kB

DirectMap4k:848 kB

DirectMap2M:2062336 kB

DirectMap1G:132120576 kB

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

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

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

有趣的问题,您的目标主要是避免在内核空间和用户空间之间进行复制吗?

所有这些API均用于用户空间。 查看如何实现ugeltlbfs,尤其是hugetlbfs_file_mmap。

@muusbolla您能找到答案吗?

CL。,谢谢,在这里,linux / fs / hugetlbfs / inode.c的定义是ugeltlbfs_file_mmap:lxr.free-electrons.com/source/fs/hugetlbfs/inode.c?v=3.18#L100

@ChuckCottrill是的,目标是在移动大量数据的同时实现最高性能。 因此,需要使用巨大的静态DMA缓冲区,这些缓冲区可以由设备和用户代码直接读取和访问。

在内核空间中通常不这样做,因此没有太多示例。

就像其他页面一样,巨大的页面也通过alloc_pages分配:

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

HPAGE_PMD_ORDER是一个宏,用于根据正常页面定义单个大页面的顺序。上面的意思是在内核中启用了透明的大页面。

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

免责声明:我从未亲自尝试过,因此您可能需要做一些尝试。要检查的一件事是:HPAGE_PMD_SHIFT表示较小的"巨大"页面的顺序。如果您想使用这些巨大的1GB页面,则可能需要尝试不同的顺序,可能是PUD_SHIFT-PAGE_SHIFT。

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

问题

通常,如果您要分配DMA缓冲区或获取物理地址,这是在内核空间中完成的,因为用户代码永远不必乱搞物理地址。

Hugetlbfs仅提供用户空间映射来分配1GB的大页面并获取用户空间虚拟地址

不存在将用户大页面虚拟地址映射到物理地址的功能

欧瑞卡

但是功能确实存在!此功能深深埋入2.6内核源代码中,该功能可从虚拟地址获取结构页面,该页面被标记为"仅用于测试"并被#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;

}

解:

由于上述函数实际上并未编译到内核中,因此您需要将其添加到驱动程序源中。

用户端工作流程

使用内核启动选项在启动时分配1gb大页面

使用ugeltlbfs调用get_huge_pages()以获取用户空间指针(虚拟地址)

将用户虚拟地址(普通指针转换为无符号长整数)传递给驱动程序ioctl

内核驱动程序工作流

通过ioctl接受用户虚拟地址

调用follow_huge_addr获取结构页面*

在结构页面*上调用page_to_phys以获取物理地址

为DMA提供设备物理地址

如果还需要内核虚拟指针,请在结构页面*上调用kmap

免责声明

几年后将重新收集以上步骤。我无法访问原始源代码。做您的尽职调查,并确保我不会忘记一步。

起作用的唯一原因是因为在引导时分配了1GB的大页面,并且其物理地址被永久锁定。不要尝试将非1GB超大页面支持的用户虚拟地址映射到DMA物理地址!您将度过一段糟糕的时光!

在系统上进行仔细测试,以确认实际上1GB的大页面已锁定在物理内存中,并且一切正常。该代码在我的设置上可以完美地工作,但是如果出现问题,这里存在很大的危险。

仅保证此代码在x86 / x64体系结构(物理地址==总线地址)以及内核版本2.6.XX上工作。在更高版本的内核上可能有更简单的方法来执行此操作,或者现在完全不可能。

如果从巨大空间中分配的用户空间获得了给定的物理地址,则此函数将在内核空间中返回正确的虚拟地址。

static inline void * phys_to_virt(unsigned long address)

在内核代码上查找功能,已通过dpdk和内核模块进行了测试。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值