<kernel v5.0>
NODE 비트맵 (API)
노드 관리용 자료 구조
node_data
pglist_data 구조체를 통해 노드 관리를 수행한다. 디폴트로 4개의 멀티 노드를 사용한다.
arch/arm64/mm/numa.c
struct pglist_data *node_data[MAX_NUMNODES] __read_mostly; EXPORT_SYMBOL(node_data);
노드 비트맵
arch/arm64/mm/numa.c
nodemask_t numa_nodes_parsed __initdata;
NUMA 시스템에서 사용할 노드 비트맵이다.
include/linux/nodemask.h
#define node_online_map node_states[N_ONLINE] #define node_possible_map node_states[N_POSSIBLE]
온라인 노드 비트맵과 possible 노드 비트맵이다.
- NUMA 노드가 발견되면 node_set_online(nid) 함수를 통해 node_states[N_ONLINE] 비트맵에 해당 노드를 설정한다.
노드 상태
enum node_states
include/linux/nodemask.h
/*
* Bitmasks that are kept for all the nodes.
*/
enum node_states {
N_POSSIBLE, /* The node could become online at some point */
N_ONLINE, /* The node is online */
N_NORMAL_MEMORY, /* The node has regular memory */
#ifdef CONFIG_HIGHMEM
N_HIGH_MEMORY, /* The node has regular or high memory */
#else
N_HIGH_MEMORY = N_NORMAL_MEMORY,
#endif
N_MEMORY, /* The node has memory(regular, high, movable) */
N_CPU, /* The node has one or more cpus */
NR_NODE_STATES
};
노드의 상태 인덱스 값으로 사용한다.
nodemask_t 타입
include/linux/nodemask.h
typedef struct { DECLARE_BITMAP(bits, MAX_NUMNODES); } nodemask_t;
node_states[] 배열
mm/page_alloc.c
/*
* Array of node states.
*/
nodemask_t node_states[NR_NODE_STATES] __read_mostly = {
[N_POSSIBLE] = NODE_MASK_ALL,
[N_ONLINE] = { { [0] = 1UL } },
#ifndef CONFIG_NUMA
[N_NORMAL_MEMORY] = { { [0] = 1UL } },
#ifdef CONFIG_HIGHMEM
[N_HIGH_MEMORY] = { { [0] = 1UL } },
#endif
#ifdef CONFIG_MOVABLE_NODE
[N_MEMORY] = { { [0] = 1UL } },
#endif
[N_CPU] = { { [0] = 1UL } },
#endif /* NUMA */
};
EXPORT_SYMBOL(node_states);
cpu_to_node_map[]
다음은 cpu -> node 관계를 매핑하는 배열이다.
arch/arm64/mm/numa.c
static int cpu_to_node_map[NR_CPUS] = { [0 ... NR_CPUS-1] = NUMA_NO_NODE };
numa_distance[]
다음은 동적으로 노드 간 거리(distance)를 저장한 numa_distance[] 배열을 만들어 관리한다.
- 배열의 크기는 numa_distance_cnt * numa_distance_cnt를 사용한다.
arch/arm64/mm/numa.c
static int numa_distance_cnt; static u8 *numa_distance;
노드 관련 API들
노드 관련 순회(iterator)
for_each_node()
include/linux/nodemask.h
#define for_each_node(node) for_each_node_state(node, N_POSSIBLE)
- node_states[N_POSSIBLE] 노드 비트맵에서 각 비트가 1로 기록된 노드만큼 루프를 돈다.
for_each_online_node()
include/linux/nodemask.h
#define for_each_online_node(node) for_each_node_state(node, N_ONLINE)
- node_states[N_ONLINE] 노드 비트맵에서 각 비트가 1로 기록된 노드만큼 루프를 돈다.
for_each_node_state()
include/linux/nodemask.h
#define for_each_node_state(__node, __state) \
for_each_node_mask((__node), node_states[__state])
- node_states[__state] 노드 비트맵에서 각 비트가 1로 기록된 노드만큼 루프를 돈다.
for_each_node_mask()
#if MAX_NUMNODES > 1
#define for_each_node_mask(node, mask) \
for ((node) = first_node(mask); \
(node) < MAX_NUMNODES; \
(node) = next_node((node), (mask)))
#else /* MAX_NUMNODES == 1 */
#define for_each_node_mask(node, mask) \
if (!nodes_empty(mask)) \
for ((node) = 0; (node) < 1; (node)++)
#endif /* MAX_NUMNODES */
- NUMA 시스템에서는 전체 노드 중 mask 노드 비트맵에서 1로 기록된 노드만큼 루프를 돈다.
- UMA 시스템에서는 node 번호 0에 대해 1번만 for 루프를 수행한다.
상태 관련 함수
- static inline int node_state(int node, enum node_states state)
- node_states[state] 비트맵에서 node 번째 비트 유무를 리턴한다.
- static inline void node_set_state(int node, enum node_states state)
- node_states[state] 비트맵에 node 번째 비트를 1로 설정한다.
- static inline void node_clear_state(int node, enum node_states state)
- node_states[state] 비트맵에 node 번째 비트를 0으로 클리어한다.
- static inline int num_node_state(enum node_states state)
- node_states[state] 비트맵에서 1로 설정된 비트의 수를 알아온다.
first_node()
include/linux/nodemask.h
#define first_node(src) __first_node(&(src))
static inline int __first_node(const nodemask_t *srcp)
{
return min_t(int, MAX_NUMNODES, find_first_bit(srcp->bits, MAX_NUMNODES));
}
노드를 나타내는 src 비트맵에서 첫 번째 노드 번호(based 0)를 알아온다. 알아온 노드 번호는 MAX_NUMNODES를 초과하지 않도록 제한되다.
next_node()
include/linux/nodemask.h
#define next_node(n, src) __next_node((n), &(src))
static inline int __next_node(int n, const nodemask_t *srcp)
{
return min_t(int,MAX_NUMNODES,find_next_bit(srcp->bits, MAX_NUMNODES, n+1));
}
노드를 나타내는 src 비트맵에서 n+1 번째 부터 비트를 검색하여 1이 발견되는 위치의 노드 번호를 알아온다. 알아온 노드 번호는 MAX_NUMNODES를 초과하지 않도록 제한된다.
nodes_empty()
include/linux/nodemask.h
#define nodes_empty(src) __nodes_empty(&(src), MAX_NUMNODES)
static inline int __nodes_empty(const nodemask_t *srcp, unsigned int nbits)
{
return bitmap_empty(srcp->bits, nbits);
}
- src 노드 비트맵이 비어 있는지 여부를 알아온다.
find_next_best_node()
mm/page_alloc.c
/**
* find_next_best_node - find the next node that should appear in a given node's fallback list
* @node: node whose fallback list we're appending
* @used_node_mask: nodemask_t of already used nodes
*
* We use a number of factors to determine which is the next node that should
* appear on a given node's fallback list. The node should not have appeared
* already in @node's fallback list, and it should be the next closest node
* according to the distance array (which contains arbitrary distance values
* from each node to each node in the system), and should also prefer nodes
* with no CPUs, since presumably they'll have very little allocation pressure
* on them otherwise.
* It returns -1 if no node is found.
*/
static int find_next_best_node(int node, nodemask_t *used_node_mask)
{
int n, val;
int min_val = INT_MAX;
int best_node = NUMA_NO_NODE;
const struct cpumask *tmp = cpumask_of_node(0);
/* Use the local node if we haven't already */
if (!node_isset(node, *used_node_mask)) {
node_set(node, *used_node_mask);
return node;
}
for_each_node_state(n, N_MEMORY) {
/* Don't want a node to appear more than once */
if (node_isset(n, *used_node_mask))
continue;
/* Use the distance array to find the distance */
val = node_distance(node, n);
/* Penalize nodes under us ("prefer the next node") */
val += (n < node);
/* Give preference to headless and unused nodes */
tmp = cpumask_of_node(n);
if (!cpumask_empty(tmp))
val += PENALTY_FOR_NODE_WITH_CPUS;
/* Slight preference for less loaded node */
val *= (MAX_NODE_LOAD*MAX_NUMNODES);
val += node_load[n];
if (val < min_val) {
min_val = val;
best_node = n;
}
}
if (best_node >= 0)
node_set(best_node, *used_node_mask);
return best_node;
}
사용된 노드를 제외하고 현재 노드의 가장 인접한 다음 노드 id를 찾아 리턴한다.
- const struct cpumask *tmp = cpumask_of_node(0);
- 0번 노드에 대응하는 cpumask
- if (!node_isset(node, *used_node_mask)) { node_set(node, *used_node_mask); return node; }
- 요청 노드가 used_node_mask 비트맵에 포함되어 있지 않으면 used_node_mask 비트맵에 노드에 대응하는 비트를 설정하고 노드 id를 리턴한다.
- for_each_node_state(n, N_MEMORY) {
- N_MEMORY 노드 상태 비트맵에 대해 루프를 돈다.
- if (node_isset(n, *used_node_mask)) continue;
- 이미 사용 중으로 표기된 노드인 경우 다음 노드를 진행한다.
- val = node_distance(node, n);
- 노드간 거리값을 알아온다.
- NUMA가 아닌 경우 같은 노드인 경우 10, 아닌 경우 20이다.
- NUMA인 경우 노드 설계마다 다르며 노드 간에 속도가 빠를 수록 수치가 작다. (인접 노드)
- 노드간 거리값을 알아온다.
- val += (n < node);
- node 번호보다 작은 노드에 대해 모두 거리를 합산한다.
- tmp = cpumask_of_node(n);
- n번 노드에 대한 cpu 비트맵 주소를 알아온다.
- if (!cpumask_empty(tmp)) val += PENALTY_FOR_NODE_WITH_CPUS;
- tmp가 가리키는 비트맵이 비어있는 경우 1을 더한다.
- val *= (MAX_NODE_LOAD*MAX_NUMNODES);
- MAX_NODE_LOAD
- nr_online_nodes
- MAX_NODE_LOAD
- val += node_load[n];
- node_load[n]을 추가한다.
- if (val < min_val) { min_val = val; best_node = n; }
- 가장 작은 값인 경우 min_val과 best_node를 갱신한다.
- if (best_node >= 0) node_set(best_node, *used_node_mask);
- 노드가 선택되었으면 used_node_mask가 가리키는 비트맵에 해당 노드 비트를 설정한다.
cpumask_of_node()
arch/arm64/include/asm/numa.h
/* Returns a pointer to the cpumask of CPUs on Node 'node'. */
static inline const struct cpumask *cpumask_of_node(int node)
{
return node_to_cpumask_map[node];
}
node_to_cpumask_map[] 배열에서 노드 id에 대응하는 cpu 비트맵을 가리키는 cpumask를 알아온다.
참고
- Bit Operations | 문c
- Bitmap Operations | 문c
- NUMA -1- (ARM64 초기화) | 문c
- NUMA -2- (Fallback Node) | 문c