NUMA (Fallback Node)

NUMA 아키텍처를 채용한 시스템에서 특정 노드에 메모리 뱅크가 없는 경우 인접한 메모리 뱅크를 사용하기 위해 사용하는 API가 있다.

 

Fallback node

현재 노드에서 메모리를 할당하고자 시도할 때 자신의 노드에 메모리가 존재하지 않는 경우 대체 노드(Fallback node)로 메모리가 있는 가장 인접한(빠른) 노드를 선택하게 한다.

fallback-node-1b

  • 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를 반환한다.
  • 참고:

 

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를 담고 있다.

 

답글 남기기

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다.