Avatar image Harlan V. Wei (aka. Wei Chen)

Page migration types in Linux

• Photo credits: Harrison Broadbent on Unsplash

Hero image for Page migration types in Linux

Page migration allows the moving of the physical location of pages (e.g. between nodes in a NUMA system) while the process is running. The main intention of page migration is to reduce the latency of memory access by moving pages near to the processor where the process accessing that memory is running.

However, not all pages can be moved freely. For example, the kernel has to make sure a DMA page is always present at the specified location as long as it is mapped into the IOMMU. To distinguish which pages can be moved freely and which can not, Linux marks pages with migration types. You can think of migration type as an identifier to a certain migration policy.

There are several migration types in Linux. The most important two are MIGRATE_MOVABLE and MIGRATE_UNMOVABLE. Apparently the former means this page can be moved freely while the latter does not. Generally speaking, programs in the user space allocate MIGRATE_MOVABLE pages, while the kernel allocates MIGRATE_UNMOVABLE pages.

By default, all page blocks are initialized as MIGRATE_MOVABLE (see deferred_free_range in page_alloc.c). When pages of a certain migrate type is exhausted, Linux will convert pages (or page blocks) from another type into the desired type. This is called stealing sometimes. Below is a code snippet from Linux 6.1.52 that finds the proper fallback migration type for a request.

// mm/page_alloc.c
/*
 * Check whether there is a suitable fallback freepage with requested order.
 * If only_stealable is true, this function returns fallback_mt only if
 * we can steal other freepages all together. This would help to reduce
 * fragmentation due to mixed migratetype pages in one pageblock.
 */
int find_suitable_fallback(struct free_area *area, unsigned int order,
                           int migratetype, bool only_stealable, bool *can_steal)
{
    int i;
    int fallback_mt;

    if (area->nr_free == 0)
        return -1;

    *can_steal = false;
    for (i = 0;; i++) {
        fallback_mt = fallbacks[migratetype][i];
        if (fallback_mt == MIGRATE_TYPES)
            break;

        if (free_area_empty(area, fallback_mt))
            continue;

        if (can_steal_fallback(order, migratetype))
            *can_steal = true;

        if (!only_stealable)
            return fallback_mt;

        if (*can_steal)
            return fallback_mt;
    }

    return -1;
}

When else do migration types matter?

There are a lot of situations where migration types matter. Some examples include:

References

  1. Page migration - kernel.org
  2. Linux Memory Management - The Buddy Allocator