NUMA(3)

πŸ“¦
version 2.0.19 Β· 5 versions
, in alpine 3.22, alpine 3.23, alpine 3.24, arch rolling, debian trixie, fedora 43, fedora 44, nixpkgs unstable, ubuntu noble, ubuntu questing, ubuntu resolute, ubuntu stonking

πŸ’– NUMA(3) πŸ’– β€” Linux Programmer's Manual

okay bestie, sit down, hydrate, because we're about to talk about the NUMA policy library and honestly? it's giving main character energy 🫢


✨ NAME ✨

numa β€” the NUMA policy library πŸ’…


🌸 SYNOPSIS 🌸

#include <numa.h>

cc ... -lnuma

int numa_available(void);

int numa_max_possible_node(void);
int numa_num_possible_nodes();

int numa_max_node(void);
int numa_num_configured_nodes();
struct bitmask *numa_get_mems_allowed(void);

int numa_num_configured_cpus(void);
struct bitmask *numa_all_nodes_ptr;
struct bitmask *numa_no_nodes_ptr;
struct bitmask *numa_all_cpus_ptr;

int numa_num_task_cpus();
int numa_num_task_nodes();

int numa_parse_bitmap(char *line , struct bitmask *mask);
struct bitmask *numa_parse_nodestring(const char *string);
struct bitmask *numa_parse_nodestring_all(const char *string);
struct bitmask *numa_parse_cpustring(const char *string);
struct bitmask *numa_parse_cpustring_all(const char *string);

long long numa_node_size(int node, long long*freep);
long long numa_node_size64(int node, long long *freep);

int numa_preferred(void);
int numa_has_preferred_many(void);
struct bitmask *numa_preferred_many(void);
void numa_set_preferred(int node);
void numa_set_preferred_many(struct bitmask *nodemask);
int numa_has_home_node(void);
int numa_set_mempolicy_home_node(void *start, unsigned long len, int
home_node, int flags);
int numa_get_interleave_node(void);
struct bitmask *numa_get_interleave_mask(void);
void numa_set_interleave_mask(struct bitmask *nodemask);
void numa_interleave_memory(void *start, size_t size, struct bitmask
*nodemask);
void numa_bind(struct bitmask *nodemask);
void numa_set_localalloc(void);
void numa_set_membind(struct bitmask *nodemask);
void numa_set_membind_balancing(struct bitmask *nodemask);
struct bitmask *numa_get_membind(void);

void *numa_alloc_onnode(size_t size, int node);
void *numa_alloc_local(size_t size);
void *numa_alloc_interleaved(size_t size);
void *numa_alloc_interleaved_subset(size_t size,  struct bitmask
*nodemask); void *numa_alloc(size_t size);
void *numa_realloc(void *old_addr, size_t old_size, size_t new_size);
void numa_free(void *start, size_t size);

int numa_run_on_node(int node);
int numa_run_on_node_mask(struct bitmask *nodemask);
int numa_run_on_node_mask_all(struct bitmask *nodemask);
struct bitmask *numa_get_run_node_mask(void);

void numa_tonode_memory(void *start, size_t size, int node);
void numa_tonodemask_memory(void *start, size_t size, struct bitmask
*nodemask);
void numa_setlocal_memory(void *start, size_t size);
void numa_police_memory(void *start, size_t size);
void numa_set_bind_policy(int strict);
void numa_set_strict(int strict);

int numa_distance(int node1, int node2);

int numa_sched_getaffinity(pid_t pid, struct bitmask *mask);
int numa_sched_setaffinity(pid_t pid, struct bitmask *mask);
int numa_node_to_cpus(int node, struct bitmask *mask);
void numa_node_to_cpu_update();
int numa_node_of_cpu(int cpu);

struct bitmask *numa_allocate_cpumask();

void numa_free_cpumask();
struct bitmask *numa_allocate_nodemask();

void numa_free_nodemask();
struct bitmask *numa_bitmask_alloc(unsigned int n);
struct bitmask *numa_bitmask_clearall(struct bitmask *bmp);
struct bitmask *numa_bitmask_clearbit(struct bitmask *bmp, unsigned int
n);
int numa_bitmask_equal(const struct bitmask *bmp1, const struct bitmask
*bmp2);
void numa_bitmask_free(struct bitmask *bmp);
int numa_bitmask_isbitset(const struct bitmask *bmp, unsigned int n);
unsigned int numa_bitmask_nbytes(struct bitmask *bmp);
struct bitmask *numa_bitmask_setall(struct bitmask *bmp);
struct bitmask *numa_bitmask_setbit(struct bitmask *bmp, unsigned int
n);
void copy_bitmask_to_nodemask(struct bitmask *bmp, nodemask_t *nodemask)
void copy_nodemask_to_bitmask(nodemask_t *nodemask, struct bitmask *bmp)
void copy_bitmask_to_bitmask(struct bitmask *bmpfrom, struct bitmask
*bmpto)
unsigned int numa_bitmask_weight(const struct bitmask *bmp )

int numa_move_pages(int pid, unsigned long count, void **pages, const
int *nodes, int *status, int flags);
int numa_migrate_pages(int pid, struct bitmask *fromnodes, struct
bitmask *tonodes);

void numa_error(char *where);

extern int numa_exit_on_error;
extern int numa_exit_on_warn;
void numa_warn(int number, char *where, ...);

πŸ¦‹ DESCRIPTION πŸ¦‹

so the libnuma library is basically your little bestie that gives you a simple programming interface to the NUMA (Non Uniform Memory Access) policy that the Linux kernel supports πŸ’– and like, on a NUMA architecture, some memory areas literally have different latency or bandwidth than others. not all memory is equal, and that's the whole point, periodt.

The policies you can pick from are:

  • page interleaving β€” allocate in a round-robin fashion from all, or a subset, of the nodes on the system πŸŒ€
  • preferred node allocation β€” preferably allocate on a particular node 🎯
  • local allocation β€” allocate on the node on which the task is currently executing 🏠
  • allocation only on specific nodes β€” allocate on some subset of the available nodes πŸŽ€

and bestie, it's also possible to bind tasks to specific nodes. iconic.


Numa memory allocation policy can be specified as a per-task attribute, which is inherited by children tasks and processes, or as an attribute of a range of process virtual address space. Numa memory policies specified for a range of virtual address space are shared by all tasks in the process. And get this β€” memory policies specified for a range of a shared memory attached using shmat(2) or mmap(2) from shmfs/hugetlbfs are shared by all processes that attach to that region πŸ«‚ Memory policies for shared disk backed file mappings are currently ignored though. πŸ™ˆ

The default memory allocation policy for tasks and all memory range is local allocation. This assumes that no ancestor has installed a non-default policy.

For setting a specific policy globally for all memory allocations in a process and its children, it is easiest to start it with the numactl(8) utility ✨ For more finegrained policy inside an application, this library can be used.

πŸ’€ important vibe check: all numa memory allocation policy only takes effect when a page is actually faulted into the address space of a process by accessing it. The numa_alloc_* functions take care of this automatically, so you don't have to stress.

A node is defined as an area where all memory has the same speed as seen from a particular CPU πŸ‘‘ A node can contain multiple CPUs. Caches are ignored for this definition.

Most functions in this library are only concerned about numa nodes and their memory. The exceptions to this are: numa_node_to_cpus(), numa_node_to_cpu_update(), numa_node_of_cpu(), numa_bind(), numa_run_on_node(), numa_run_on_node_mask(), numa_run_on_node_mask_all(), and numa_get_run_node_mask(). These functions deal with the CPUs associated with numa nodes 🌈 See the descriptions below for more information.


Some of these functions accept or return a pointer to struct bitmask. A struct bitmask controls a bit map of arbitrary length containing a bit representation of nodes 🧚 The predefined variable numa_all_nodes_ptr points to a bit mask that has all available nodes set; numa_no_nodes_ptr points to the empty set.

⭐ Before any other calls in this library can be used numa_available() must be called. If it returns -1, all other functions in this library are undefined. no cap, don't skip this step bestie.

numa_max_possible_node() returns the number of the highest possible node in a system πŸ’œ In other words, the size of a kernel type nodemask_t (in bits) minus 1. This number can be gotten by calling numa_num_possible_nodes() and subtracting 1.

numa_num_possible_nodes() returns the size of kernel's node mask (kernel type nodemask_t). In other words, large enough to represent the maximum number of nodes that the kernel can handle. This will match the kernel's MAX_NUMNODES value. This count is derived from /proc/self/status, field Mems_allowed.

numa_max_node() returns the highest node number available on the current system. (See the node numbers in /sys/devices/system/node/ πŸ‘€) Also see numa_num_configured_nodes().

numa_num_configured_nodes() returns the number of memory nodes in the system. This count includes any nodes that are currently disabled. This count is derived from the node numbers in /sys/devices/system/node. (Depends on the kernel being configured with /sys (CONFIG_SYSFS).)

numa_get_mems_allowed() returns the mask of nodes from which the process is allowed to allocate memory in it's current cpuset context 🌸 Any nodes that are not included in the returned bitmask will be ignored in any of the following libnuma memory policy calls. so like, pay attention.

numa_num_configured_cpus() returns the number of cpus in the system. This count includes any cpus that are currently disabled. This count is derived from the cpu numbers in /sys/devices/system/cpu. If the kernel is configured without /sys (CONFIG_SYSFS=n) then it falls back to using the number of online cpus.

numa_all_nodes_ptr points to a bitmask that is allocated by the library with bits representing all nodes on which the calling task may allocate memory πŸŽ€ This set may be up to all nodes on the system, or up to the nodes in the current cpuset. The bitmask is allocated by a call to numa_allocate_nodemask() using size numa_max_possible_node(). The set of nodes to record is derived from /proc/self/status, field "Mems_allowed". The user should not alter this bitmask. hands off bestie πŸ’…

numa_no_nodes_ptr points to a bitmask that is allocated by the library and left all zeroes. The bitmask is allocated by a call to numa_allocate_nodemask() using size numa_max_possible_node(). The user should not alter this bitmask either. πŸ™…

numa_all_cpus_ptr points to a bitmask that is allocated by the library with bits representing all cpus on which the calling task may execute ⭐ This set may be up to all cpus on the system, or up to the cpus in the current cpuset. The bitmask is allocated by a call to numa_allocate_cpumask() using size numa_num_possible_cpus(). The set of cpus to record is derived from /proc/self/status, field "Cpus_allowed". The user should not alter this bitmask. seriously.

numa_num_task_cpus() returns the number of cpus that the calling task is allowed to use. This count is derived from the map /proc/self/status, field "Cpus_allowed". Also see the bitmask numa_all_cpus_ptr.

numa_num_task_nodes() returns the number of nodes on which the calling task is allowed to allocate memory. This count is derived from the map /proc/self/status, field "Mems_allowed". Also see the bitmask numa_all_nodes_ptr 🫢

numa_parse_bitmap() β€” okay so this one takes line, which is a lil character string like the ones chillin' in /sys/devices/system/node/nodeN/cpumap, and parses it into a bitmask structure. The string is the hexadecimal representation of a bit map, bestie. You can allocate that bitmask with numa_allocate_cpumask(). It returns 0 on success, -1 on failure. Ngl this function is probably of little use to a user application, but libnuma uses it internally, so πŸ’… respect the internals.


numa_parse_nodestring() β€” this one parses a character string list of nodes into a bit mask. The bit mask gets allocated by numa_allocate_nodemask(). The string is a comma-separated list of node numbers or node ranges. A leading ! means "not" this list (so like, all nodes except this list πŸ’€), and a leading + means the node numbers in the list are relative to the task's cpuset. The string can also just be "all" to specify all the nodes β€” that's numa_num_task_nodes() of them. Node numbers are limited by the number in the system. Peep numa_max_node() and numa_num_configured_nodes().

Examples: 1-5,7,10 Β· !4-5 Β· +0-3

If the string is of 0 length, bitmask numa_no_nodes_ptr is returned. And it returns 0 if the string is invalid. 🫢

numa_parse_nodestring_all() β€” basically numa_parse_nodestring's sibling, but it can parse all possible nodes, not only the current nodeset. Iconic behavior, honestly.


numa_parse_cpustring() β€” the cpu version, obvi. Parses a character string list of cpus into a bit mask, allocated by numa_allocate_cpumask(). Comma-separated list of cpu numbers or cpu ranges. Leading ! = "not" this list (all cpus except this list), leading + = cpu numbers in the list are relative to the task's cpuset. The string can be "all" to specify all (numa_num_task_cpus()) cpus. Cpu numbers are limited by the number in the system β€” check numa_num_task_cpus() and numa_num_configured_cpus().

Examples: 1-5,7,10 Β· !4-5 Β· +0-3

Returns 0 if the string is invalid. ✨

numa_parse_cpustring_all() β€” same energy as numa_parse_cpustring, but it can parse all possible cpus, not only the current cpuset. Slay.


numa_node_size() β€” returns the memory size of a node. If the argument freep is not NULL, it used to return the amount of free memory on the node. On error it returns -1. 😭

numa_node_size64() β€” works the same as numa_node_size(), but this one's useful on 32-bit architectures with large nodes, so keep it in your back pocket.


numa_preferred() β€” returns the preferred node of the current task. That's the node the kernel preferably allocates memory from, unless some other policy overrides it. Lowkey the main character node of your task. πŸ‘‘

numa_has_preferred_many() β€” returns > 0 if the system supports multiple preferred nodes. Vibe check passed or not, that's it.

numa_preferred_many() β€” returns the current set of preferred nodes. This implies the empty set when the policy isn't one used for preference (PREFERRED, PREFERRED_MANY, BIND). Heads up bestie: the caller is responsible for freeing the mask with numa_bitmask_free(). No leaks, we don't do that here. πŸ™ˆ


numa_set_preferred() β€” sets the preferred node for the current task to node. The system will attempt to allocate memory from the preferred node, but will fall back to other nodes if no memory is available on the preferred node. Passing a node of -1 specifies local allocation and is equivalent to calling numa_set_localalloc(). Fr fr, that -1 is just a shortcut.

numa_set_preferred_many() β€” sets the preferred set of nodes for the current task to nodemask. Super similar to numa_set_preferred(), except it utilizes a different kernel interface to specify multiple preferred nodes. And again: the caller is responsible for freeing the mask with numa_bitmask_free(). 🫢

numa_has_home_node() β€” returns 1 if the system supports setting home_node for mbind and preferred_many.

numa_set_mempolicy_home_node() β€” sets the home node for a VMA policy present in the task's address range. A home node is the NUMA node closest to which page allocation will come from. You should use it after setting up a mbind or perfered_many memory policy for the specified range. Order matters, don't skip steps. ✨


numa_get_interleave_mask() β€” returns the current interleave mask if the task's memory allocation policy is page interleaved. Otherwise, this function returns an empty mask. It's giving "all or nothing".

numa_set_interleave_mask() β€” sets the memory interleave mask for the current task to nodemask. All new memory allocations are page interleaved over all nodes in the interleave mask. You can turn interleaving off again by passing an empty mask (numa_no_nodes). The page interleaving only occurs on the actual page fault that puts a new page into the current address space. Also it's only a hint: the kernel will fall back to other nodes if no memory is available on the interleave target. So like, it's a suggestion, not a contract. πŸ’…


numa_interleave_memory() β€” interleaves size bytes of memory page by page from start on the nodes specified in nodemask. The size argument will be rounded up to a multiple of the system page size. If nodemask contains nodes that are externally denied to this process, this call will fail. πŸ’€

This is a lower level function to interleave allocated but not yet faulted in memory. "Not yet faulted in" means the memory was allocated using mmap(2) or shmat(2), but hasn't been accessed by the current process yet. The memory gets page interleaved to all nodes specified in nodemask. Normally numa_alloc_interleaved() should be used for private memory instead, but this function is useful for handling shared memory areas. To be useful, the memory area should be several megabytes at least (or tens of megabytes of hugetlbfs mappings).

And if the numa_set_strict() flag is true, then the operation will cause a numa_error if there were already pages in the mapping that do not follow the policy. ⚠️

numa_bind() β€” binds the current task and its children to the nodes specified in nodemask. They will only run on the CPUs of the specified nodes and only be able to allocate memory from them. It's equivalent to calling numa_run_on_node_mask(nodemask) followed by numa_set_membind(nodemask). And if tasks should be bound to individual CPUs inside nodes, consider using numa_node_to_cpus and the sched_setaffinity(2) syscall. Full package deal, bestie. πŸ“¦

numa_set_localalloc() β€” sets the memory allocation policy for the calling task to local allocation. In this mode, the preferred node for memory allocation is effectively the node where the task is executing at the time of a page allocation. Wherever you are is where you get your memory. Main character energy. 🌸


numa_set_membind() β€” sets the memory allocation mask. The task will only allocate memory from the nodes set in nodemask.

Passing an empty nodemask, or a nodemask that contains nodes other than those in the mask returned by numa_get_mems_allowed(), will result in an error. 😭

numa_set_membind_balancing() β€” sets the memory allocation mask and enables the Linux kernel NUMA balancing for the task, if the feature is supported by the kernel. The task will only allocate memory from the nodes set in nodemask.

Same deal: passing an empty nodemask, or a nodemask that contains nodes other than those in the mask returned by numa_get_mems_allowed(), will result in an error.

numa_get_membind() β€” returns the mask of nodes from which memory can currently be allocated. If the returned mask is equal to numa_all_nodes, then memory allocation is allowed from all nodes. 🌈


numa_alloc_onnode() β€” allocates memory on a specific node. The size argument will be rounded up to a multiple of the system page size. If the specified node is externally denied to this process, this call will fail. Ngl, this function is relatively slow compared to the malloc(3) family of functions. The memory must be freed with numa_free(). On errors, NULL is returned. 🫢

numa_alloc_local() β€” allocates size bytes of memory on the local node. The size argument will be rounded up to a multiple of the system page size. Also relatively slow compared to the malloc(3) family of functions. The memory must be freed with numa_free(). On errors, NULL is returned.

numa_alloc_interleaved() β€” allocates size bytes of memory page interleaved on all nodes. This function is relatively slow and should only be used for large areas consisting of multiple pages. The interleaving works at page level and will only show an effect when the area is large. The allocated memory must be freed with numa_free(). On error, NULL is returned. ✨

numa_alloc_interleaved_subset() β€” attempts to allocate size bytes of memory page interleaved on all nodes. The size argument will be rounded up to a multiple of the system page size. The nodes on which a process is allowed to allocate memory may be constrained externally, and if that's the case, this function may fail. It's relatively slow compared to the malloc(3) family of functions and should only be used for large areas consisting of multiple pages. The interleaving works at page level and will only show an effect when the area is large. The allocated memory must be freed with numa_free(). On error, NULL is returned.

numa_alloc() β€” allocates size bytes of memory with the current NUMA policy. The size argument will be rounded up to a multiple of the system page size. Relatively slow compared to the malloc(3) family of functions. The memory must be freed with numa_free(). On errors, NULL is returned.


numa_realloc() β€” changes the size of the memory area pointed to by old_addr from old_size to new_size. The memory area pointed to by old_addr must have been allocated with one of the numa_alloc* functions. The new_size will be rounded up to a multiple of the system page size. The contents of the memory area will be unchanged to the minimum of the old and new sizes; newly allocated memory will be uninitialized. The memory policy (and node bindings) associated with the original memory area will be preserved in the resized area. For example, if the initial area was allocated with a call to numa_alloc_onnode(), then the new pages (if the area is enlarged) will be allocated on the same node. However, if no memory policy was set for the original area, then numa_realloc() cannot guarantee that the new pages will be allocated on the same node. On success, the address of the resized area is returned (which might be different from that of the initial area), otherwise NULL is returned and errno is set to indicate the error. The pointer returned by numa_realloc() is suitable for passing to numa_free(). Glow-up complete. πŸ’…

numa_free() β€” frees size bytes of memory starting at start, allocated by the numa_alloc_* functions above. The size argument will be rounded up to a multiple of the system page size.


numa_run_on_node() β€” runs the current task and its children on a specific node. They will not migrate to CPUs of other nodes until the node affinity is reset with a new call to numa_run_on_node_mask(). Passing -1 permits the kernel to schedule on all nodes again. On success, 0 is returned; on error -1 is returned, and errno is set to indicate the error. That's the whole thing, bestie. πŸ¦‹

  • numa_run_on_node_mask() β€” this one pins the current task and its children to only run on the nodes listed in nodemask. They will not migrate to CPUs of other nodes until you reset the node affinity with a fresh call to numa_run_on_node_mask() or numa_run_on_node(). And if you pass numa_all_nodes? Then you're giving the kernel permission to schedule on all nodes again. 🌈 On success you get 0 back; on error it's -1, and errno gets set to tell you what went wrong.
  • numa_run_on_node_mask_all() β€” same energy as numa_run_on_node_mask: it runs the current task and its children only on the nodes in nodemask, but with no cpuset awareness at all. Just so you know the difference. πŸ’…
  • numa_get_run_node_mask() β€” hands you back a mask of the CPUs the current task is actually allowed to run on.
  • numa_tonode_memory() β€” puts memory on one specific node. The constraints that were described for numa_interleave_memory() apply here too. Same rules, bestie. πŸ“Œ
  • numa_tonodemask_memory() β€” puts memory on a specific set of nodes. And yes, the constraints described for numa_interleave_memory() apply here too.
  • numa_setlocal_memory() β€” locates memory on the current node. Constraints from numa_interleave_memory()? You already know. Apply here too. πŸ’–
  • numa_police_memory() β€” locates memory using the current NUMA policy. Constraints described for numa_interleave_memory() apply here too. ⚠️ And heads up: this function will read-modify-write parts of the memory, so it is the caller's responsibility to avoid data races with parallel threads. Not a vibe, just a fact.
  • numa_distance() β€” reports the distance in the machine topology between two nodes. The factors are a multiple of 10. It returns 0 when the distance cannot be determined. A node has distance 10 to itself (iconic self-love tbh ✨). Reporting the distance requires a Linux kernel version of 2.6.10 or newer.
  • numa_set_bind_policy() β€” decides whether calls that bind memory to a specific node should use the preferred policy or a strict policy. Preferred lets the kernel allocate memory on other nodes when there isn't enough free on the target node. Strict will fail the allocation in that case. πŸ’€ Setting the argument specifies strict, 0 gives you preferred. Note that specifying more than one node non strict may only use the first node in some kernel versions.
  • numa_set_strict() β€” sets a flag saying whether the functions that allocate on specific nodes should use a strict policy. Strict means the allocation will fail if the memory cannot be allocated on the target node. Default operation is to fall back to other nodes. 🫢 This doesn't apply to interleave and default.
  • numa_get_interleave_node() β€” this one is used by libnuma internally. It is probably not useful for user applications. 🚫 It uses the MPOL_F_NODE flag of the get_mempolicy system call, which is not intended for application use (its operation may change or be removed altogether in future kernel versions). See get_mempolicy(2).
  • numa_pagesize() β€” returns the number of bytes in a page. This function is simply a fast alternative to repeated calls to the getpagesize system call. See getpagesize(2). ⚑
  • numa_sched_getaffinity() β€” retrieves a bitmask of the cpus on which a task may run. The task is specified by pid. It returns the return value of the sched_getaffinity system call. See sched_getaffinity(2). The bitmask must be at least the size of the kernel's cpu mask structure β€” use numa_allocate_cpumask() to allocate it. Test the bits in the mask by calling numa_bitmask_isbitset(). πŸŽ€
  • numa_sched_setaffinity() β€” sets a task's allowed cpu's to those cpu's specified in mask. The task is specified by pid. Returns the return value of the sched_setaffinity system call. See sched_setaffinity(2). You may allocate the bitmask with numa_allocate_cpumask(). Or the bitmask may be smaller than the kernel's cpu mask structure β€” for example, call numa_bitmask_alloc() using a maximum number of cpus from numa_num_configured_cpus(). Set the bits in the mask by calling numa_bitmask_setbit().
  • numa_node_to_cpus() β€” converts a node number to a bitmask of CPUs. You (the user) must pass a bitmask structure with a mask buffer long enough to represent all possible cpu's. Use numa_allocate_cpumask() to create it. If the bitmask is not long enough, errno will be set to ERANGE and -1 is returned. On success 0 is returned. 😭 the ERANGE jumpscare is real
  • numa_node_to_cpu_update() β€” marks the cpus bitmask of all nodes stale, then gets the latest bitmask by calling numa_node_to_cpus(). This lets you update the libnuma state after a CPU hotplug event. 🌑️ The application is in charge of detecting CPU hotplug events β€” libnuma is not doing that detective work for you.
  • numa_node_of_cpu() β€” returns the node that a cpu belongs to. If the user supplies an invalid cpu, errno will be set to EINVAL and -1 will be returned.
  • numa_allocate_cpumask() β€” returns a bitmask of a size equal to the kernel's cpu mask (kernel type cpumask_t). In other words, large enough to represent NR_CPUS cpus. This number of cpus can be gotten by calling numa_num_possible_cpus(). The bitmask is zero-filled. ✨
  • numa_free_cpumask β€” frees a cpumask previously allocated by numa_allocate_cpumask. Cleanup era. 🧹
  • numa_allocate_nodemask() β€” returns a bitmask of a size equal to the kernel's node mask (kernel type nodemask_t). In other words, large enough to represent MAX_NUMNODES nodes. This number of nodes can be gotten by calling numa_num_possible_nodes(). The bitmask is zero-filled. πŸ’–
  • numa_free_nodemask() β€” frees a nodemask previously allocated by numa_allocate_nodemask().
  • numa_bitmask_alloc() β€” allocates a bitmask structure and its associated bit mask. The memory allocated for the bit mask contains enough words (type unsigned long) to contain n bits. The bit mask is zero-filled. The bitmask structure points to the bit mask and contains the n value. 🎁
  • numa_bitmask_clearall() β€” sets all bits in the bit mask to 0. The bitmask structure points to the bit mask and contains its size (bmp->size). The value of bmp is always returned. Note that numa_bitmask_alloc() creates a zero-filled bit mask β€” so like, this is for when you need to wipe it again.
  • numa_bitmask_clearbit() β€” sets a specified bit in a bit mask to 0. Nothing is done if the n value is greater than the size of the bitmask (and no error is returned πŸ™ˆ). The value of bmp is always returned.
  • numa_bitmask_equal() β€” returns 1 if two bitmasks are equal. It returns 0 if they are not equal. If the bitmask structures control bit masks of different sizes, the "missing" trailing bits of the smaller bit mask are considered to be 0.
  • numa_bitmask_free() β€” deallocates the memory of both the bitmask structure pointed to by bmp and the bit mask. ⚠️ It is an error to attempt to free this bitmask twice. Double-free is not the move, bestie.
  • numa_bitmask_isbitset() β€” returns the value of a specified bit in a bit mask. If the n value is greater than the size of the bit map, 0 is returned.
  • numa_bitmask_nbytes() β€” returns the size (in bytes) of the bit mask controlled by bmp. The bit masks are always full words (type unsigned long), and the returned size is the actual size of all those words.
  • numa_bitmask_setall() β€” sets all bits in the bit mask to 1. The bitmask structure points to the bit mask and contains its size (bmp->size). The value of bmp is always returned. πŸŽ‰
  • numa_bitmask_setbit() β€” sets a specified bit in a bit mask to 1. Nothing is done if n is greater than the size of the bitmask (and no error is returned). The value of bmp is always returned.
  • copy_bitmask_to_nodemask() β€” copies the body (the bit map itself) of the bitmask structure pointed to by bmp to the nodemask_t structure pointed to by the nodemask pointer. If the two areas differ in size, the copy is truncated to the size of the receiving field or zero-filled.
  • copy_nodemask_to_bitmask() β€” copies the nodemask_t structure pointed to by the nodemask pointer to the body (the bit map itself) of the bitmask structure pointed to by the bmp pointer. If the two areas differ in size, the copy is truncated to the size of the receiving field or zero-filled.
  • copy_bitmask_to_bitmask() β€” copies the body (the bit map itself) of the bitmask structure pointed to by the bmpfrom pointer to the body of the bitmask structure pointed to by the bmpto pointer. If the two areas differ in size, the copy is truncated to the size of the receiving field or zero-filled.
  • numa_bitmask_weight() β€” returns a count of the bits that are set in the body of the bitmask pointed to by the bmp argument. It's giving popcount. πŸ’…
  • numa_move_pages() β€” moves a list of pages in the address space of the currently executing or current process. It simply uses the move_pages system call.
    • pid β€” ID of task. If not valid, use the current task.
    • count β€” Number of pages.
    • pages β€” List of pages to move.
    • nodes β€” List of nodes to which pages can be moved.
    • status β€” Field to which status is to be returned.
    • flags β€” MPOL_MF_MOVE or MPOL_MF_MOVE_ALL

See move_pages(2).

  • numa_migrate_pages() β€” simply uses the migrate_pages system call to cause the pages of the calling task, or a specified task, to be migrated from one set of nodes to another. See migrate_pages(2). The bit masks representing the nodes should be allocated with numa_allocate_nodemask(), or with numa_bitmask_alloc() using an n value returned from numa_num_possible_nodes(). A task's current node set can be gotten by calling numa_get_membind(). Bits in the tonodes mask can be set by calls to numa_bitmask_setbit(). πŸ¦‹
  • numa_error() β€” a libnuma internal function that can be overridden by the user program. It's called with a char * argument when a libnuma function fails. Overriding the library internal definition makes it possible to specify a different error handling strategy when a libnuma function fails. It does not affect numa_available(). The numa_error() function defined in libnuma prints an error on stderr and terminates the program if numa_exit_on_error is set to a non-zero value. πŸ’€ The default value of numa_exit_on_error is zero.
  • numa_warn() β€” another libnuma internal function that can also be overridden by the user program. It's called to warn the user when a libnuma function encounters a non-fatal error. The default implementation prints a warning to stderr. The first argument is a unique number identifying each warning. After that there is a printf(3)-style format string and a variable number of arguments. numa_warn exits the program when numa_exit_on_warn is set to a non-zero value. The default value of numa_exit_on_warn is zero.

🌸 Compatibility with libnuma version 1 🌸

  • Binaries that were compiled for libnuma version 1 need not be re-compiled to run with libnuma version 2. 🫢
  • Source codes written for libnuma version 1 may be re-compiled without change with version 2 installed. To do so, in the code's Makefile add this option to CFLAGS:
-DNUMA_VERSION1_COMPATIBILITY

πŸŽ€ THREAD SAFETY πŸŽ€

numa_set_bind_policy and numa_exit_on_error are process global. The other calls are thread safe. πŸ‘‘


Copyright 2002, 2004, 2007, 2008 Andi Kleen, SuSE Labs. libnuma is under the GNU Lesser General Public License, v2.1.

✨ SEE ALSO ✨

okay bestie you made it to the end, and here's the lil reading list β€” every one of these is a real page you can pull up, no gatekeeping πŸ’…


so like… that's the whole page?? we did it?? πŸŽ€ lowkey iconic of you to read all the way down here, fr fr. go forth and be the main character of your own memory policy πŸ¦‹πŸ‘‘

this lil beauty came from SuSE Labs, dated December 2007, and lives on as NUMA(3) 🌸 periodt πŸ’–