这个问题是为了:
根据这个内核来源,move_pages()
将调用do_pages_move()
来移动页面,但我不知道它是如何间接调用页()的。
所以我的问题是:
move_pages()
能移动巨大的网页吗?如果是,在传递页面地址数组时,页面边界应该是4KB还是2MB?5年前,似乎有一个支持移动拥抱的补丁。move_pages()
不能移动拥抱,我如何移动拥抱?根据下面的代码,我似乎通过页面大小= 2MB的move_pages()
移动hugepages,但这是正确的方式吗?
#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?
发布于 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
->
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)的方法和测试。有他的测试的副本:扩展 (核心是必需的)
现在我不知道如何开始测试,以及如何检查它是否正确工作。对于使用最近内核运行的./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目录。
https://stackoverflow.com/questions/59726288
复制相似问题