<kernel v5.10>
NUMA 아키텍처를 채용한 시스템에서 특정 노드에 메모리 뱅크가 없는 경우 인접한 메모리 뱅크를 사용하기 위해 사용하는 API가 있다.
NUMA -2- (Fallback node)
현재 노드에서 메모리를 할당하고자 시도할 때 자신의 노드에 메모리가 존재하지 않는 경우 대체 노드(Fallback node)로 메모리가 있는 가장 인접한(빠른) 노드를 선택하게 한다.
- node_numa_mem_[3]: node#3에 메모리가 없어서 메모리가 있는 가장 인접한 노드 id 2가 담겨있다.
- per-cpu _numa_mem_: cpu#6번과 cpu#7번에 해당하는 노드 #3가 3번이지만 메모리가 없어서 메모리가 있는 가장 인접한 노드 id 2가 담겨있다.
- per-cpu numa_node: cpu#6번과 cpu#7번에 해당하는 노드 #3 그대로 담겨 있다. (메모리 유무와 관계 없음)
numa_node_id()
include/linux/topology.h
#ifdef CONFIG_USE_PERCPU_NUMA_NODE_ID #ifndef numa_node_id /* Returns the number of the current Node */ static inline int numa_node_id(void) { return raw_cpu_read(numa_node); } #endif #else /* Returns the number of the current Node. */ #ifndef numa_node_id static inline int numa_node_id(void) { return cpu_to_node(raw_smp_processor_id()); } #endif #endif
현재 cpu가 소속된 노드 id를 반환한다.
- CONFIG_USE_PERCPU_NUMA_NODE_ID 커널 옵션을 사용하는 경우 per-cpu 방식으로 numa_node 값을 읽어서 numa 노드 id를 알아오고, 이 옵션을 사용하지 않는 경우 현재 cpu 번호로 노드 id를 구해온다.
- 32bit arm에서 NUMA를 사용하는 경우가 흔한 경우가 아니지만 만일 사용하는 경우 per-cpu 방식을 사용하면 노드의 계산에 메모리를 사용하지 않고 TPIDRPRW 레지스터를 사용하므로 더 빠르게 처리할 수 있다.
- x86, powerpc, ia64 등의 아키텍처에서도 빠르게 처리할 수 있는 구현이 준비되어 있다.
numa_mem_id()
include/linux/topology.h
#ifdef CONFIG_HAVE_MEMORYLESS_NODES #ifndef numa_mem_id /* Returns the number of the nearest Node with memory */ static inline int numa_mem_id(void) { return raw_cpu_read(_numa_mem_); } #endif #else #ifndef numa_mem_id /* Returns the number of the nearest Node with memory */ static inline int numa_mem_id(void) { return numa_node_id(); } #endif #endif
현재 cpu가 소속된 노드 id를 반환하는데 만일 이 노드가 메모리가 없는 경우 메모리에 있는 가장 인접한 노드 id를 알아온다.
- CONFIG_HAVE_MEMORYLESS_NODES 커널 옵션을 사용하는 경우 per-cpu _numa_mem_에 담긴 노드 id를 반환하는데 현재 cpu가 소속된 노드가 메모리를 가지지 않는 경우 이 값은 현재 노드에서 메모리가 있는 가장 인접한 노드 id를 반환하고, 커널 옵션을 사용하지 않는 경우 단순히 현재 cpu가 소속된 노드 id를 반환한다.
- 참고:
- Enable memoryless node on x86 platforms | LWN.net
- Enable memoryless node support for x86 | LWN.net
node_to_mem_node()
include/linux/topology.h
#ifdef CONFIG_HAVE_MEMORYLESS_NODES #ifndef node_to_mem_node static inline int node_to_mem_node(int node) { return _node_numa_mem_[node]; } #endif #else #ifndef node_to_mem_node static inline int node_to_mem_node(int node) { return node; } #endif #endif
지정된 노드에 메모리가 있으면 지정된 노드 id를 그대로 반환하고 메모리가 없는 경우 메모리가 있는 가장 인접한 노드 id를 알아온다.
- CONFIG_HAVE_MEMORYLESS_NODES 커널 옵션을 사용하는 경우 _node_numa_mem_[] 배열 값을 반환한다. _node_numa_mem_[] 배열에는 배열 인자로 사용된 노드id와 똑같은 값이 들어있는데 메모리가 없는 노드인 경우는 메모리가 있는 가장 인접한 노드 id를 담고 있다.
관련 전역 변수
per-cpu numa_node 변수
mm/page_alloc.c
#ifdef CONFIG_USE_PERCPU_NUMA_NODE_ID DEFINE_PER_CPU(int, numa_node); #endif
각 cpu가 소속된 노드 id를 담고 있다.
_node_numa_mem 변수
mm/page_alloc.c
#ifdef CONFIG_HAVE_MEMORYLESS_NODES int _node_numa_mem_[MAX_NUMNODES]; #endif
배열의 각 노드에 해당되는 노드 id를 그대로 담고 있지만, 해당 노드가 메모리가 없는 노드인 경우 대체 노드로 메모리가 있는 인접한 노드 id를 담고 있다.
per-cpu _numa_mem_ 변수
mm/page_alloc.c
#ifdef CONFIG_HAVE_MEMORYLESS_NODES /* * N.B., Do NOT reference the '_numa_mem_' per cpu variable directly. * It will not be defined when CONFIG_HAVE_MEMORYLESS_NODES is not defined. * Use the accessor functions set_numa_mem(), numa_mem_id() and cpu_to_mem() * defined in <linux/topology.h>. */ DEFINE_PER_CPU(int, _numa_mem_); /* Kernel "local memory" node */ #endif
cpu가 소속된 노드 id를 그대로 담고 있지만, 해당 노드가 메모리가 없는 노드인 경우 대체 노드로 메모리가 있는 인접한 노드 id를 담고 있다.
참고
- NUMA -1- (ARM64 초기화) | 문c
- NUMA -2- (Fallback Node) | 문c – 현재 글
- NODE 비트맵 (API) | 문c