set_mempolicy(2)

โœจ set_mempolicy โ€” system calls manual โœจ

okay bestie so this one is set_mempolicy(2) from the System Calls Manual and it's genuinely one of the most iconic NUMA girlies in the whole kernel. sit down, get a lil drink, we're doing memory policy ๐Ÿ’…


๐Ÿซถ NAME

set_mempolicy โ€” set the default NUMA memory policy for a thread and its children

no cap, that's the whole vibe. it sets your thread's whole memory-brain for what happens after.

๐ŸŒธ LIBRARY

NUMA (Non-Uniform Memory Access) policy library โ€” that's libnuma, and you link it with -lnuma.


โญ SYNOPSIS

#include <numaif.h>

long set_mempolicy(int mode, const unsigned long *nodemask,
                   unsigned long maxnode);

๐Ÿ’– DESCRIPTION

so set_mempolicy() sets the NUMA memory policy of the calling thread โ€” and that policy is two things: a mode, plus zero or more nodes. you hand it mode, nodemask, and maxnode, and it justโ€ฆ does that. slay.

a NUMA machine has different memory controllers with different distances to specific CPUs. the memory policy is literally the answer to "which node does this thread's memory come from". that's it, that's the assignment.

This system call defines the default policy for the thread. and here's the hierarchy tea โ˜•: the thread policy governs allocation of pages in the process's address space outside of memory ranges controlled by a more specific policy set by mbind(2). (specific policy beats default policy, always. main character energy for mbind.)

the thread default policy also controls allocation of:

  • any pages for memory-mapped files mapped using the mmap(2) call with the MAP_PRIVATE flag, that are only read (loaded) from by the thread, and
  • memory-mapped files mapped using mmap(2) with the MAP_SHARED flag, regardless of the access type.

the policy is applied only when a new page is allocated for the thread. for anonymous memory, that's when the page is first touched by the thread. so likeโ€ฆ policy is a vibe check at allocation time, and after that the page is on its own fr.

the mode argument must specify one of MPOL_DEFAULT, MPOL_BIND, MPOL_INTERLEAVE, MPOL_WEIGHTED_INTERLEAVE, MPOL_PREFERRED, MPOL_PREFERRED_MANY, or MPOL_LOCAL โ€” all of them are described in detail below, don't worry, I got you. every mode except MPOL_DEFAULT requires the caller to specify the node or nodes the mode applies to, via the nodemask argument. no nodes, no policy, no service ๐Ÿ™ˆ.


๐ŸŽ€ mode flags (optional, but make it fashion)

mode may also include an optional mode flag. the supported ones are:

  • MPOL_F_NUMA_BALANCING (since Linux 5.12) โ€” when mode is MPOL_BIND, enable the kernel NUMA balancing for the task if it's supported by the kernel. if the flag isn't supported by the kernel, or is used with a mode other than MPOL_BIND, -1 is returned and errno is set to EINVAL. so if you mix it wrong it's giving error, not giving results ๐Ÿ’€
  • MPOL_F_RELATIVE_NODES (since Linux 2.6.26) โ€” a nonempty nodemask specifies node IDs that are relative to the set of node IDs allowed by the process's current cpuset.
  • MPOL_F_STATIC_NODES (since Linux 2.6.26) โ€” a nonempty nodemask specifies physical node IDs. Linux will not remap the nodemask when the process moves to a different cpuset context, nor when the set of nodes allowed by the process's current cpuset context changes. it's the "I said what I said" flag.

๐Ÿฆ‹ the nodemask + maxnode situation

nodemask points to a bit mask of node IDs that contains up to maxnode bits. the bit mask size is rounded to the next multiple of sizeof(unsigned long), but the kernel will use bits only up to maxnode. (rounding up โ‰  using the extra bits, bestie, don't get it twisted.)

a NULL value of nodemask or a maxnode value of zero specifies the empty set of nodes. and if maxnode is zero, the nodemask argument is justโ€ฆ ignored. iconic, unbothered, not reading it.

where a nodemask is required, it must contain at least one node that is:

  • on-line,
  • allowed by the process's current cpuset context (unless the MPOL_F_STATIC_NODES mode flag is specified),
  • and contains memory.

if MPOL_F_STATIC_NODES is set in mode and a required nodemask contains no nodes allowed by the process's current cpuset context, the memory policy reverts to local allocation. this effectively overrides the specified policy until the process's cpuset context includes one or more of the nodes specified by nodemask. so yeah, your policy is on pause until the cpuset catches up ๐Ÿ˜ญ


๐Ÿ‘‘ the modes, one by one

The mode argument must include one of the following values:

  • MPOL_DEFAULT โ€” removes any nondefault thread memory policy, so the memory policy "falls back" to the system default policy. the system default policy is "local allocation" โ€” allocate memory on the node of the CPU that triggered the allocation. nodemask must be specified as NULL. if the "local node" contains no free memory, the system will attempt to allocate memory from a "near by" node. (yes, near by, that's the phrase, keeping it.)
  • MPOL_BIND โ€” a strict policy that restricts memory allocation to the nodes specified in nodemask. if nodemask specifies more than one node, page allocations come from the node with the lowest numeric node ID first, until that node contains no free memory. allocations then come from the node with the next highest node ID specified in nodemask, and so forth, until none of the specified nodes contain free memory. pages will not be allocated from any node not specified in the nodemask. it's strict because it MEANS it.
  • MPOL_INTERLEAVE โ€” interleaves page allocations across the nodes specified in nodemask in numeric node ID order. this optimizes for bandwidth instead of latency by spreading out pages and memory accesses to those pages across multiple nodes. however, accesses to a single page will still be limited to the memory bandwidth of a single node. spread the pages bestie, but a single page is still a single-page girl.
  • MPOL_WEIGHTED_INTERLEAVE (since Linux 6.9) โ€” interleaves page allocations across the nodes specified in nodemask according to the weights in /sys/kernel/mm/mempolicy/weighted_interleave. for example: if bits 0, 2, and 5 are set in nodemask, and the contents of /sys/kernel/mm/mempolicy/weighted_interleave/node0, /sys/.../node2, and /sys/.../node5 are 4, 7, and 9, respectively, then pages in this region will be allocated on nodes 0, 2, and 5 in a 4:7:9 ratio. ratios!! math!! she ate ๐Ÿ”ฅ
  • MPOL_PREFERRED โ€” sets the preferred node for allocation. the kernel will try to allocate pages from this node first and fall back to "near by" nodes if the preferred node is low on free memory. if nodemask specifies more than one node ID, the first node in the mask will be selected as the preferred node. if the nodemask and maxnode arguments specify the empty set, then the policy specifies "local allocation" (like the system default policy discussed above).
  • MPOL_PREFERRED_MANY (since Linux 5.15) โ€” specifies a preference for nodes from which the kernel will try to allocate from. this differs from MPOL_PREFERRED in that it accepts a set of nodes versus a single node. this policy is intended to benefit page allocations where specific memory types (i.e., non-volatile, high-bandwidth, or accelerator memory) are of greater importance than node location. location? don't know her. memory type is the moment.
  • MPOL_LOCAL (since Linux 3.8) โ€” specifies "local allocation": the memory is allocated on the node of the CPU that triggered the allocation (the "local node"). the nodemask and maxnode arguments must specify the empty set. if the "local node" is low on free memory, the kernel will try to allocate memory from other nodes. the kernel will allocate memory from the "local node" whenever memory for this node is available. if the "local node" is not allowed by the process's current cpuset context, the kernel will try to allocate memory from other nodes. the kernel will allocate memory from the "local node" whenever it becomes allowed by the process's current cpuset context. it's loyal, it keeps coming back ๐Ÿซถ

the thread memory policy is preserved across an execve(2), and is inherited by child threads created using fork(2) or clone(2). generational wealth, but for memory policy ๐Ÿ’œ


๐ŸŒˆ RETURN VALUE

glow-up moment: on success, set_mempolicy() returns 0. on error, -1 is returned and errno is set to indicate the error. that's it. that's the whole personality.


๐Ÿ’€ ERRORS

  • EFAULT โ€” part of all of the memory range specified by nodemask and maxnode points outside your accessible address space.
  • EINVAL โ€” mode is invalid. or, mode is MPOL_DEFAULT and nodemask is nonempty, or mode is MPOL_BIND or MPOL_INTERLEAVE and nodemask is empty. or, maxnode specifies more than a page worth of bits. or, nodemask specifies one or more node IDs that are greater than the maximum supported node ID. or, none of the node IDs specified by nodemask are on-line and allowed by the process's current cpuset context, or none of the specified nodes contain memory. or, the mode argument specified both MPOL_F_STATIC_NODES and MPOL_F_RELATIVE_NODES. or, the MPOL_F_NUMA_BALANCING isn't supported by the kernel, or is used with mode other than MPOL_BIND. that's a lot of ways to be wrong, and honestly? valid of EINVAL.
  • ENOMEM โ€” insufficient kernel memory was available.

๐Ÿงš STANDARDS

Linux. (just Linux. periodt.)

โญ HISTORY

Linux 2.6.7. old money.


๐Ÿ“ NOTES

memory policy is not remembered if the page is swapped out. when such a page is paged back in, it will use the policy of the thread or memory range that is in effect at the time the page is allocated. so the policy that matters is the one on duty at allocation o'clock, not the one from before the swap. the past is the past ๐Ÿ™ˆ

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


๐Ÿ’… SEE ALSO

get_mempolicy(2), getcpu(2), mbind(2), mmap(2), numa(3), cpuset(7), numa(7), numactl(8)


okay that's the whole page, you're officially NUMA-literate ๐Ÿ’–

cute lil sign-off: this rewrite is serving Linux man-pages 6.20, dated 2026-02-08, and the original page is set_mempolicy(2). mwah ๐ŸŒธ

๐Ÿ“ฆ 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-var
  • ๐Ÿ’– girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms