NUMA -2- (Fallback Node)

<kernel v5.0>

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

 

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

 

참고

댓글 남기기

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