move_pages(2)

๐Ÿ’ซ move_pages(2) โ€” the system call manual, but make it cute โœจ

okay bestie, welcome to the System Calls Manual ๐ŸŽ€ this one is called move_pages and it's lowkey one of the most fun little NUMA calls out there โ€” it's literally the "pick up this page and put it over THERE" spell ๐Ÿฆ‹


๐ŸŒธ NAME ๐ŸŒธ

move_pages โ€” move individual pages of a process to another node ๐Ÿ’…


๐Ÿ“ LIBRARY ๐Ÿ“

NUMA (Non-Uniform Memory Access) policy library (libnuma, -lnuma) ๐Ÿ“š


โœจ SYNOPSIS โœจ

#include <numaif.h>

long move_pages(int pid, unsigned long count, void *pages[count],
                const int nodes[count], int status[count],
                int flags);

๐Ÿ‘‘ DESCRIPTION ๐Ÿ‘‘

So move_pages() takes the pages you point at for the process pid and moves them to the memory nodes you list in nodes. ๐Ÿššโœจ The results of the whole move situation show up in status. And flags tells the kernel what kind of constraints apply to the pages getting moved. no cap.

pid is the ID of the process whose pages are gonna get moved. And here's the cute part โ€” if pid is 0, then move_pages() just moves pages of the calling process (that's you, babe ๐Ÿ’–).

But if you wanna move pages in another process, you need the following privileges:

  • Up to and including Linux 4.12: the caller must be privileged (CAP_SYS_NICE) or the real or effective user ID of the calling process must match the real or saved-set user ID of the target process.
  • The older rules allowed the caller to discover various virtual address choices made by the kernel that could lead to the defeat of address-space-layout randomization for a process owned by the same UID as the caller, the rules were changed starting with Linux 4.13. Since Linux 4.13, permission is governed by a ptrace access mode PTRACE_MODE_READ_REALCREDS check with respect to the target process; see ptrace(2).
โš ๏ธ bestie that second bullet is the glow-up lore โ€” old rules were leaky, Linux 4.13 said "not today" and switched to a PTRACE_MODE_READ_REALCREDS check. remember it. ๐Ÿ‘‘

count is how many pages you're moving. It also defines the size of the three arrays: pages, nodes, and status. โœจ

pages is an array of pointers to the pages that should be moved. These are pointers that should be aligned to page boundaries ๐Ÿ‘€. Addresses are specified as seen by the process specified by pid.

nodes is an array of integers that specify the desired location for each page. Each element is a node number. And guess what โ€” nodes can also be NULL!! ๐Ÿ™ˆ In that case move_pages() doesn't move anything, it just returns the node where each page currently resides, in the status array. This is genuinely so useful because sometimes you just gotta know which pages need moving before you move them. vibe check first, migrATE later ๐Ÿ’…

status is an array of integers that return the status of each page. The array only contains valid values if move_pages() did NOT return an error. pro tip from the manpage: preinitializing the array to a value that can't represent a real numa node or a valid error of the status array could help you spot which pages actually got migrated ๐Ÿ‘€โœจ

flags specify what types of pages to move. MPOL_MF_MOVE means only pages that are in exclusive use by the process are to be moved. MPOL_MF_MOVE_ALL means pages shared between multiple processes can also be moved ๐Ÿซถ โ€” but the process must be privileged (CAP_SYS_NICE) to use MPOL_MF_MOVE_ALL. iconic but gated.

๐Ÿฆ‹ Page states in the status array ๐Ÿฆ‹

These are the values that can be returned in each element of the status array:

  • 0..MAX_NUMNODES โ€” Identifies the node on which the page resides. ๐Ÿ 
  • -EACCES โ€” The page is mapped by multiple processes and can be moved only if MPOL_MF_MOVE_ALL is specified.
  • -EBUSY โ€” The page is currently busy and cannot be moved. Try again later. This occurs if a page is undergoing I/O or another kernel subsystem is holding a reference to the page. ๐Ÿ˜ญ
  • -EFAULT โ€” This is a zero page or the memory area is not mapped by the process.
  • -EIO โ€” Unable to write back a page. The page has to be written back in order to move it since the page is dirty and the filesystem does not provide a migration function that would allow the move of dirty pages. ๐Ÿ’€
  • -EINVAL โ€” A dirty page cannot be moved. The filesystem does not provide a migration function and has no ability to write back pages.
  • -ENOENT โ€” The page is not present.
  • -ENOMEM โ€” Unable to allocate memory on target node.

๐ŸŒˆ RETURN VALUE ๐ŸŒˆ

On success move_pages() returns zero ๐ŸŽ‰. On error, it returns -1, and sets errno to indicate the error. And if a positive value is returned, it's the number of nonmigrated pages. So likeโ€ฆ "how many of these lil guys refused to move", basically. ๐Ÿ™ˆ


๐Ÿ”ฅ ERRORS ๐Ÿ”ฅ

  • Positive value โ€” The number of nonmigrated pages if they were the result of nonfatal reasons (since Linux 4.17).
  • E2BIG โ€” Too many pages to move. Since Linux 2.6.29, the kernel no longer generates this error. (rip, gone but not forgotten ๐Ÿชฆ)
  • EACCES โ€” One of the target nodes is not allowed by the current cpuset.
  • EFAULT โ€” Parameter array could not be accessed.
  • EINVAL โ€” Flags other than MPOL_MF_MOVE and MPOL_MF_MOVE_ALL was specified or an attempt was made to migrate pages of a kernel thread. โœ‹
  • ENODEV โ€” One of the target nodes is not online. ๐Ÿซฅ
  • EPERM โ€” The caller specified MPOL_MF_MOVE_ALL without sufficient privileges (CAP_SYS_NICE). Or, the caller attempted to move pages of a process belonging to another user but did not have privilege to do so (CAP_SYS_NICE).
  • ESRCH โ€” Process does not exist. ๐Ÿ˜ญ

๐Ÿ’œ STANDARDS ๐Ÿ’œ

Linux. ๐Ÿง periodt.


โญ HISTORY โญ

Linux 2.6.18. baby's first appearance ๐Ÿ’ซ


๐ŸŒธ NOTES ๐ŸŒธ

For information on library support, see numa(7).

Use get_mempolicy(2) with the MPOL_F_MEMS_ALLOWED flag to obtain the set of nodes that are allowed by the current cpuset. Note that this information is subject to change at any time by manual or automatic reconfiguration of the cpuset. โณ (yes it can change under you. ngl stay on your toes.)

Use of this function may result in pages whose location (node) violates the memory policy established for the specified addresses (see mbind(2)) and/or the specified process (see set_mempolicy(2)). That is, memory policy does not constrain the destination nodes used by move_pages(). ๐Ÿšจ so like โ€” your memory policy does NOT hold this function back. it does what it wants. main character energy fr.

The <numaif.h> header is not included with glibc, but requires installing libnuma-devel or a similar package. ๐Ÿ“ฆ


๐Ÿซถ SEE ALSO ๐Ÿซถ

get_mempolicy(2), mbind(2), set_mempolicy(2), numa(3), numa_maps(5), cpuset(7), numa(7), migratepages(8), numastat(8)


okay that's the whole thing and you ATE it ๐Ÿ’… this page came from the Linux man-pages 6.20 collection, dated 2026-02-08 โ€” go forth and migrate responsibly, bestie ๐Ÿงšโœจ๐Ÿ’–

๐Ÿ“ฆ where this page comes from
  • arch rolling: man-pages 6.20-1 ยท BSD-2-Clause AND BSD-3-Clause AND BSD-4.3TAHOE AND BSD-4-Clause-UC AND GPL-1.0-or-later AND GPL-2.0-only AND GPL-2.0-or-later AND GPL-3.0-or-later AND LGPL-3.0-linking-exception AND LGPL-3.0-or-later AND LicenseRef-Public-Domain AND LicenseRef-UltraPermissive AND Linux-man-pages-1-para AND Linux-man-pages-copyleft AND Linux-man-pages-copyleft-2-para AND Linux-man-pages-copyleft-var AND MIT AND Spencer-94
  • nixpkgs unstable: man-pages 6.20
  • ๐Ÿ“œ the page's license: Linux-man-pages-copyleft
  • ๐Ÿ’– girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms