首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >使用move_pages()移动拥抱?

使用move_pages()移动拥抱?
EN

Stack Overflow用户
提问于 2020-01-14 01:08:47
回答 1查看 866关注 0票数 1

这个问题是为了:

  1. 内核3.10.0-1062.4.3.el7.x86_64
  2. 通过引导参数分配的非透明隐藏页,并且可能映射到文件,也可能不映射到文件(例如,挂载的hugepages)
  3. x86_64

根据这个内核来源move_pages()将调用do_pages_move()来移动页面,但我不知道它是如何间接调用页()的。

所以我的问题是:

  1. move_pages()能移动巨大的网页吗?如果是,在传递页面地址数组时,页面边界应该是4KB还是2MB?5年前,似乎有一个支持移动拥抱的补丁
  2. 如果move_pages()不能移动拥抱,我如何移动拥抱?
  3. 在移动hugepages之后,我是否可以像查询常规页面一样查询hugepages的NUMA I,比如这个回答

根据下面的代码,我似乎通过页面大小= 2MB的move_pages()移动hugepages,但这是正确的方式吗?

代码语言:javascript
运行
复制
#include <cstdint>
#include <iostream>
#include <numaif.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <limits>

int main(int argc, char** argv) {
        const int32_t dst_node = strtoul(argv[1], nullptr, 10);
        const constexpr uint64_t size = 4lu * 1024 * 1024;
        const constexpr uint64_t pageSize = 2lu * 1024 * 1024;
        const constexpr uint32_t nPages = size / pageSize;
        int32_t status[nPages];
        std::fill_n(status, nPages, std::numeric_limits<int32_t>::min());;
        void* pages[nPages];
        int32_t dst_nodes[nPages];
        void* ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_HUGETLB, -1, 0);

        if (ptr == MAP_FAILED) {
                throw "failed to map hugepages";
        }
        memset(ptr, 0x41, nPages*pageSize);
        for (uint32_t i = 0; i < nPages; i++) {
                pages[i] = &((char*)ptr)[i*pageSize];
                dst_nodes[i] = dst_node;
        }

        std::cout << "Before moving" << std::endl;

        if (0 != move_pages(0, nPages, pages, nullptr, status, 0)) {
            std::cout << "failed to inquiry pages because " << strerror(errno) << std::endl;
        }
        else {
                for (uint32_t i = 0; i < nPages; i++) {
                        std::cout << "page # " << i << " locates at numa node " << status[i] << std::endl;
                }
        }

        // real move
        if (0 != move_pages(0, nPages, pages, dst_nodes, status, MPOL_MF_MOVE_ALL)) {
                std::cout << "failed to move pages because " << strerror(errno) << std::endl;
                exit(-1);
        }

        const constexpr uint64_t smallPageSize = 4lu * 1024;
        const constexpr uint32_t nSmallPages = size / smallPageSize;
        void* smallPages[nSmallPages];
        int32_t smallStatus[nSmallPages] = {std::numeric_limits<int32_t>::min()};
        for (uint32_t i = 0; i < nSmallPages; i++) {
                smallPages[i] = &((char*)ptr)[i*smallPageSize];
        }


        std::cout << "after moving" << std::endl;
        if (0 != move_pages(0, nSmallPages, smallPages, nullptr, smallStatus, 0)) {
            std::cout << "failed to inquiry pages because " << strerror(errno) << std::endl;
        }
        else {
                for (uint32_t i = 0; i < nSmallPages; i++) {
                        std::cout << "page # " << i << " locates at numa node " << smallStatus[i] << std::endl;
                }
        }

}

我是否应该根据4KB的页面大小(如上面的代码)来查询NUMA I?或者2MB?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2020-01-14 08:53:18

对于最初版本的3.10Linux内核(不是红帽补丁,因为我没有LXR的流变内核),move_pages将强制将巨大的页面(2MB;THP和hugetlbfs样式)分割成小页面(4KB)。move_pages使用太短的块(如果计算正确的话,大约是0.5MB ),函数图如下:

move_pages ..-> migrate_pages -> unmap_and_move ->

代码语言:javascript
运行
复制
static int unmap_and_move(new_page_t get_new_page, unsigned long private,
            struct page *page, int force, enum migrate_mode mode)
{
    struct page *newpage = get_new_page(page, private, &result);
    ....
    if (unlikely(PageTransHuge(page)))
        if (unlikely(split_huge_page(page)))
            goto out;

PageTransHuge对两种巨型页面(thp和libhugetlb)都返回true:https://elixir.bootlin.com/linux/v3.10/source/include/linux/page-flags.h#L411

对于透明的、巨大的和大的页面,PageTransHuge()都返回true,但不返回普通的页面。

split_huge_page 将要 call split_huge_page_to_list 哪一个

将一个大漏电分成普通页。这不会改变头页的位置。

Split还将发出vm_event计数器类型的THP_SPLIT增量。计数器以/proc/vmstat (“文件显示各种虚拟内存统计信息”)导出。您可以在测试前后检查此计数器使用这个UUOC命令 cat /proc/vmstat |grep thp_split

在3.10版本中,有一些代码可以作为unmap_and_move_huge_page函数,而不是从move_pages调用。3.10中的只使用它migrate_huge_page中,被调用只来自内存故障处理程序 soft_offline_huge_page (__soft_offline_page) (添加了2010年):

软离线一个页面,通过迁移或无效,没有杀死任何东西。这适用于页面尚未损坏(因此访问仍然有效)的情况,但已更正了许多错误,最好删除。

答案:

move_pages()能移动拥抱吗?如果是,在传递页面地址数组时,页面边界应该是4KB还是2MB?5年前,似乎有一种支持移动拥抱的补丁。

标准的3.10内核有move_pages,它将接受4KB页面指针的数组“页面”,它会将(拆分)大页面拆分成512个小页面,然后迁移小页面。它们被thp合并的可能性很小,因为move_pages为物理内存页做了单独的请求,而且它们几乎总是不连续的。

不要给出指向"2MB“的指针,它仍然会分割提到的每一个巨大的页面,并且只迁移这个内存的前4KB小页。

2013年补丁没有添加到原来的3.10内核中。

这个补丁似乎在2013年9月被接受:https://github.com/torvalds/linux/search?q=+extend+hugepage+migration&type=Commits

如果move_pages()不能移动抱页,我如何移动抱页?

move_pages将以小页面的形式将数据从抱页中移动。您可以:在正确的numa节点以手动模式分配巨大的页面,并复制数据(如果您想要保留虚拟地址,可以复制两次);或者用补丁更新内核到某个版本,并使用补丁作者Naoya Horiguchi (JP)的方法和测试。有他的测试的副本:扩展 (核心是必需的)

pages.c

现在我不知道如何开始测试,以及如何检查它是否正确工作。对于使用最近内核运行的./test_move_pages -v -m private -h 2048,它不增加THP_SPLIT计数器。

他的测试看起来非常类似于我们的测试: mmap,memset到错误页,用指向小页面的指针填充页面数组,numa_move_pages

在移动hugepages之后,我可以像查询常规页面一样查询hugepages的NUMA I吗?

您可以通过在查询模式下(带有空节点)向move_pages syscall提供正确的数组“页面”来查询任何内存的状态。数组应列出要检查的内存区域的每个小页。

如果您知道任何可靠的方法来检查内存是否映射到大页,您可以查询任何大页的小页面。我认为,如果您可以将物理地址从内核导出到用户空间(例如使用一些LKM模块 ),那么可以使用概率方法:对于巨大的页面,虚拟地址和物理地址总是有21个通用的LSB位,而对于小页面位,只有百万的测试结果是一致的。或者只需编写LKM来导出PMD目录

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/59726288

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档