Использование move_pages () для перемещения огромных страниц? - PullRequest
1 голос
/ 14 января 2020

Этот вопрос относится к:

  1. ядру 3.10.0-1062.4.3.el7.x86_64
  2. непрозрачным огромным страницам, выделенным через параметры загрузки, которые могут отображаться или не отображаться файл (например, смонтированные огромные страницы)
  3. x86_64

В соответствии с этим ядром source , move_pages() будет вызывать do_pages_move() для перемещения страницы, но я не понимаю, как это косвенно вызывает migrate_huge_page () .

Итак, мои вопросы:

  1. Может ли move_pages() перемещать огромные страницы? если да, должна ли граница страницы быть 4 КБ или 2 МБ при передаче массива адресов страниц? Кажется, что существует патч для поддержки движущихся огромных страниц 5 лет go.
  2. , если move_pages() не может перемещать огромные страницы, как я могу перемещать огромные страницы?
  3. после перемещения огромных страниц могу ли я запрашивать идентификаторы NUMA огромных страниц таким же образом, как я запрашиваю обычные страницы, например, answer ?

В соответствии с приведенным ниже кодом, кажется, что я перемещаю огромные страницы через move_pages() с размером страницы = 2 МБ, но это правильный путь?:

#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;
                }
        }

}

И нужно ли запрашивать идентификаторы NUMA на основе размера страницы 4 КБ (как в коде выше)? Или 2 МБ?

1 Ответ

1 голос
/ 14 января 2020

Для оригинальной версии ядра 3.10 linux (не исправлено, поскольку у меня нет LXR для ядер rhel) syscall move_pages приведет к разделению огромной страницы (2 МБ; стили THP и hugetlbfs) на маленькие страницы (4 КБ). move_pages использует слишком короткие чанки (около 0,5 МБ, если я правильно рассчитал), и график функции выглядит так:

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 возвращает true для обоих видов огромных страниц (thp и libhugetlbs): https://elixir.bootlin.com/linux/v3.10/source/include/linux/page-flags.h#L411

PageTransHuge () возвращает true как для прозрачных огромных страниц, так и для страниц hugetlbfs, но не обычные страницы.

И split_huge_page будут вызывать split_huge_page_to_list , которые :

Разделить огромную страницу на нормальные страницы. Это не меняет положение главной страницы.

Split также выдаст приращение счетчика vm_event вида THP_SPLIT. Счетчики экспортируются в /proc/vmstat ( "файл отображает различную статистику виртуальной памяти" ). Вы можете проверить этот счетчик с помощью этой команды UUO C 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 () перемещать огромные страницы? если да, должна ли граница страницы быть 4 КБ или 2 МБ при передаче массива адресов страниц? Похоже, что был патч для поддержки движущихся огромных страниц 5 лет go.

Стандартное ядро ​​3.10 имеет move_pages, который будет принимать массивы "страниц" 4KB указателей страниц и будет разбиваться (разбиваться) огромная страница на 512 маленьких страниц, а затем он будет мигрировать маленькие страницы. Шансы на их слияние с thp очень малы, так как move_pages выполняет отдельные запросы к страницам физической памяти, и они почти всегда будут непостоянными.

Не указывайте "2MB", это будет все еще разделяет каждую упомянутую огромную страницу и переносит только первые 4 КБ маленькие страницы этой памяти. Патч

2013 не был добавлен в оригинальное ядро ​​3.10.

Патч, кажется, будет принят в сентябре 2013 года: https://github.com/torvalds/linux/search?q=+extend+hugepage+migration&type=Commits

, если move_pages () не может двигаться огромные страницы, как я могу перемещать огромные страницы?

move_pages будет перемещать данные с огромных страниц в виде маленьких страниц. Вы можете: разместить огромную страницу в ручном режиме на правильном узле numa и скопировать ваши данные (скопируйте дважды, если вы хотите сохранить виртуальный адрес); или обновите ядро ​​до какой-то версии с помощью патча и используйте методы и тесты автора патча, Наоя Хоригучи (JP) . Есть копия его тестов: https://github.com/srikanth007m/test_hugepage_migration_extension (https://github.com/Naoya-Horiguchi/test_core требуется)

https://github.com/srikanth007m/test_hugepage_migration_extension/blob/master/test_move_pages.c

Теперь я не уверен, как запустить тест и как проверить, что он работает правильно. Для ./test_move_pages -v -m private -h 2048, работающего с последним ядром, он не увеличивает счетчик THP_SPLIT.

Его тест выглядит очень похоже на наши тесты: mmap, memset для страниц с ошибками, заполнение массива страниц указателями на маленькие страницы, numa_move_pages

после перемещения огромных страниц, могу ли я запрашивать идентификаторы NUMA огромных страниц таким же образом, как я запрашиваю обычные страницы, как этот ответ?

Вы можете запросить состояние любой памяти, указав правильный массив "pages" для move_pages системного вызова в режиме запроса (с нулевыми узлами). Массив должен перечислять каждую маленькую страницу области памяти, которую вы хотите проверить.

Если вы знаете какой-либо надежный способ проверить, соответствует ли память огромной странице или нет, вы можете запросить любую маленькую страницу огромной страницы. Я думаю, что может быть вероятностный метод c, если вы можете экспортировать физический адрес из ядра в пользовательское пространство (например, с помощью некоторого модуля LKM ): для огромных страниц виртуальные и физические адреса всегда будут 21 общая LSB бит , а для маленьких страниц биты будут совпадать только для 1 теста на миллион. Или просто напишите LKM для экспорта PMD Directory .

...