이 코드는 어떤 문제를 푸나요?
RCU는 읽는 쪽의 부담을 줄이는 대신, 예전 객체를 볼 수 있는 독자가 끝난 뒤에 회수하도록 시간을 나눕니다. rcu_init은 CPU별 상태와 트리, 콜백 처리 기반을 준비합니다. 초기화 함수 안에서 한 객체를 바로 해제하는 예제는 아닙니다.
읽을 범위: v6.6 · kernel/rcu/tree.c · rcu_init 4969–5012행입니다. 아래에 이 범위의 원문과 각 줄의 설명을 실었습니다. 주제 전체의 흐름과 다른 경로는 기존 분석에서 함께 읽으실 수 있습니다.
먼저 알아둘 개념
유예 기간
기존 독자들이 더 이상 예전 객체를 참조하지 않음을 확인하기 위해 기다리는 구간입니다.
콜백
유예 기간 뒤에 실행하도록 넘긴 작업입니다. 단순한 타이머 지연과는 보장하는 조건이 다릅니다.
처음 읽을 때
코드를 읽을 때 CPU들을 묶는 RCU 트리와 전역 상태를 준비합니다. 그다음 어느 상태가 바뀌는지 각 줄에서 확인하세요.
더 깊이 살펴볼 때
이 경로의 호출 문맥, 잠금·인터럽트 상태, 오류 시 남는 자원을 함께 추적해 보세요. 호출된 함수가 수행하는 작업과 현재 함수가 직접 보장하는 범위를 구분하는 것이 중요합니다.
그림으로 보는 변화
rcu_init() 호출 순서와 조건 분기 · tree.c 4969–5012행- 부트 CPU와 초기 준비
smp_processor_id()→ 이른 부팅 테스트 →kfree_rcu배치 준비 → 설정 알림·우선순위 정리 (4971–4977행) - 트리 구조 결정 → 전역·노드 초기화
rcu_init_geometry()→rcu_init_one(). 그다음dump_tree가 참이면 트리를 출력하고,use_softirq가 참이면 RCU softirq 핸들러를 등록합니다. (4978–4983행) - 전원 통지 등록 → 부트 CPU 반영
pm_notifier()후 온라인 CPU가 둘 이상이면WARN_ON. 이어서 현재 CPU를 준비 → 시작 → 온라인 순서로 RCU에 반영합니다. (4990–4994행) - 작업 큐 진단과 과부하 값 선택
rcu_gp_wq할당 후 NULL이면WARN_ON. 이어rcu_alloc_par_gp_wq()를 호출하고,qovld < 0인지에 따라qovld_calc값을 선택합니다. (4997–5006행) - 이른 polling 보완 → 동기화 기본 동작 테스트
start_poll_synchronize_rcu_expedited()→rcu_test_sync_prims(). 이 함수의 종료가 모든 독자의 유예 기간 완료를 뜻하지는 않습니다. (5009–5011행)
| 조건 | 경로 |
|---|---|
dump_tree (4980행) | 참이면 rcu_dump_rcu_node_tree()로 진단 출력. 거짓이면 출력을 건너뜁니다. |
use_softirq (4982행) | 참이면 open_softirq(RCU_SOFTIRQ, rcu_core_si) 등록. 거짓이면 이 호출을 건너뜁니다. |
num_online_cpus() > 1 (4991행) | 참이면 경고. WARN_ON 문장 자체에는 조기 반환 분기가 없으며 다음 CPU 준비 호출이 이어집니다. |
!rcu_gp_wq (4998행) | 할당 실패(NULL)이면 경고. 이 함수에는 여기서 반환하는 분기가 없고 다음 rcu_alloc_par_gp_wq() 호출이 이어집니다. |
qovld < 0 (5003행) | 참이면 qovld_calc = 2 × qhimark. 0 이상이면 qovld_calc = qovld; v6.6의 기본 qovld 값도 양수입니다. |
화살표는 이 함수 안의 실행 순서입니다. 각 하위 함수의 내부 동작과 오류 처리까지 나타내지는 않습니다. 경고는 여기의 WARN_ON 문장이 반환하지 않는다는 뜻이며 시스템 전체의 계속 실행을 보장한다는 뜻은 아닙니다.
- 1 · 트리 전 준비
부트 CPU 확인 → 이른 테스트 →
tree.c:4971–4977kfree_rcu_batch_init()→ 설정 알림·우선순위 정리 - 2 · 트리와 처리 경로
4978–4983rcu_init_geometry()→rcu_init_one(). 진단 출력·softirq 등록은 각 조건이 참일 때만 합니다. - 3 · 부트 CPU 반영
전원 통지 등록·경고 검사 →
4990–4994rcutree_prepare_cpu()→rcu_cpu_starting()→rcutree_online_cpu() - 4 · 뒤쪽 설정과 테스트
작업 큐 →
4997–5011qovld_calc선택 → 이른 expedited polling 요청 → 동기화 테스트
kfree_rcu 배치 준비는 1단계입니다. 4단계가 call_rcu()를 호출하거나 이미 등록된 콜백을 실행한다는 뜻은 아닙니다. 화살표는 이 함수의 호출 순서만 나타내며 유예 기간 완료를 뜻하지 않습니다.
rcu_init를 한 줄씩 읽기
줄 번호는 v6.6 원문 기준입니다. 주석·빈 줄을 포함한 함수 전체를 먼저 보고, 그 아래에서 각 줄을 설명합니다.
void __init rcu_init(void)
{
int cpu = smp_processor_id();
rcu_early_boot_tests();
kfree_rcu_batch_init();
rcu_bootup_announce();
sanitize_kthread_prio();
rcu_init_geometry();
rcu_init_one();
if (dump_tree)
rcu_dump_rcu_node_tree();
if (use_softirq)
open_softirq(RCU_SOFTIRQ, rcu_core_si);
/*
* We don't need protection against CPU-hotplug here because
* this is called early in boot, before either interrupts
* or the scheduler are operational.
*/
pm_notifier(rcu_pm_notify, 0);
WARN_ON(num_online_cpus() > 1); // Only one CPU this early in boot.
rcutree_prepare_cpu(cpu);
rcu_cpu_starting(cpu);
rcutree_online_cpu(cpu);
/* Create workqueue for Tree SRCU and for expedited GPs. */
rcu_gp_wq = alloc_workqueue("rcu_gp", WQ_MEM_RECLAIM, 0);
WARN_ON(!rcu_gp_wq);
rcu_alloc_par_gp_wq();
/* Fill in default value for rcutree.qovld boot parameter. */
/* -After- the rcu_node ->lock fields are initialized! */
if (qovld < 0)
qovld_calc = DEFAULT_RCU_QOVLD_MULT * qhimark;
else
qovld_calc = qovld;
// Kick-start in case any polled grace periods started early.
(void)start_poll_synchronize_rcu_expedited();
rcu_test_sync_prims();
}void __init rcu_init(void)RCU가 CPU들의 읽기 구간 종료를 추적하고 지연된 콜백을 처리할 전역·CPU별 상태를 마련합니다. 독자가 끝나기 전에 공유 객체를 회수하지 않도록 하는 기반입니다.
int cpu = smp_processor_id();RCU 초기 상태를 설정할 현재 부트 CPU 번호를 얻습니다. 이 시점에는 아래 검사처럼 여러 CPU가 이미 온라인인 상황을 예상하지 않습니다.
rcu_early_boot_tests();RCU의 이른 부팅 테스트를 실행해 초기 상태의 동작을 점검합니다.
kfree_rcu_batch_init();유예 기간 뒤 객체 회수를 묶어 처리할 kfree_rcu 배치 기반을 준비합니다.
rcu_bootup_announce();사용할 RCU 구성과 주요 설정을 부팅 로그에 알립니다.
sanitize_kthread_prio();RCU 커널 스레드 우선순위 설정이 사용할 수 있는 범위에 있는지 정리합니다.
rcu_init_geometry();CPU들을 추적할 RCU 노드 트리의 크기와 구조를 정합니다.
rcu_init_one();RCU 전역 상태와 노드 구조를 초기화합니다.
if (dump_tree)RCU 트리 진단 출력이 요청된 경우에만 아래 출력을 수행합니다.
rcu_dump_rcu_node_tree();요청된 경우 구성한 RCU 노드 트리를 진단용으로 출력합니다.
if (use_softirq)RCU 핵심 처리를 softirq로 수행하는 구성이면 해당 핸들러를 연결합니다.
open_softirq(RCU_SOFTIRQ, rcu_core_si);지정 softirq 번호에 RCU 처리 함수를 연결합니다.
pm_notifier(rcu_pm_notify, 0);절전·복귀 상태 변화에 대응할 RCU 통지 함수를 등록합니다.
WARN_ON(num_online_cpus() > 1); // Only one CPU this early in boot.RCU 트리를 준비하는 이른 부팅 단계에서 온라인 CPU가 둘 이상이면 경고합니다. 아래의 CPU별 초기 등록이 부트 CPU부터 시작된다는 순서 조건을 확인합니다.
rcutree_prepare_cpu(cpu);해당 CPU의 RCU 자료구조를 준비합니다.
rcu_cpu_starting(cpu);현재 CPU가 시작하는 상태를 RCU에 알립니다. 이 버전의 함수 이름과 호출 위치를 기준으로 읽습니다.
rcutree_online_cpu(cpu);해당 CPU를 RCU의 온라인 추적에 반영합니다.
rcu_gp_wq = alloc_workqueue("rcu_gp", WQ_MEM_RECLAIM, 0);Tree SRCU와 expedited grace period 처리를 위한 작업 큐를 만듭니다. grace period는 기존 독자의 읽기 구간이 끝나기를 기다리는 기간입니다. v6.6에서는 WQ_UNBOUND를 주지 않으면 CPU에 묶이는 방식이며, WQ_MEM_RECLAIM은 메모리 부족 때도 실행 문맥을 확보하게 합니다. 마지막 0은 동시 실행 한도에 기본값을 쓰라는 뜻입니다.
WARN_ON(!rcu_gp_wq);유예 기간의 후속 작업을 맡길 rcu_gp 작업 큐 생성이 실패했는지 확인합니다. NULL 큐로 정상 후속 처리를 진행할 수 없다는 진단입니다.
rcu_alloc_par_gp_wq();rcu_alloc_par_gp_wq()를 호출합니다. CONFIG_RCU_EXP_KTHREAD 설정이면 이 함수 본문은 비어 있고, 설정하지 않았으면 rcu_par_gp 작업 큐를 만들며 실패 시 경고합니다.
if (qovld < 0)qovld가 음수인지 검사합니다. 음수면 현재 qhimark를 기준으로 과부하 임계값을 다시 계산하고, 0 이상이면 qovld 값을 그대로 씁니다. v6.6의 초기 qovld 값은 양수입니다.
qovld_calc = DEFAULT_RCU_QOVLD_MULT * qhimark;콜백 큐의 높은 수위 기준에 기본 배수를 적용해 과부하 임계값을 계산합니다.
else앞 조건이 성립하지 않은 경로를 선택합니다. 앞쪽 처리와 아래 처리를 모두 수행하는 것이 아닙니다.
qovld_calc = qovld;qovld 값(기본값 포함)을 그대로 qovld_calc에 사용합니다.
(void)start_poll_synchronize_rcu_expedited();완료 여부를 나중에 확인할 수 있는 expedited 유예 기간 요청을 시작합니다.
rcu_test_sync_prims();RCU 동기화 기본 동작의 초기 테스트를 수행합니다.
함께 생각해 볼 질문
일정 시간만 기다리면 RCU 유예 기간인가요?
아닙니다. RCU가 요구하는 독자 종료 상태를 확인해야 합니다.
왜 부트 CPU부터 준비하나요?
이 시점의 CPU 온라인 상태와 후속 SMP 초기화 순서에 맞춰야 하기 때문입니다.
RCU 읽기 잠금이 mutex처럼 독자를 막나요?
일반적인 RCU 읽기 경로는 mutex와 역할과 비용이 다릅니다. 객체 수명과 업데이트 규칙을 함께 읽어야 합니다.
출처와 읽은 범위
Linux v6.6 · kernel/rcu/tree.c
해당 버전 원본 파일 · 기존 코드 분석 · 설명 원고
문C블로그의 RCU 초기화(v5.4)와 RCU 기본·RCU 콜백 글은 설명 방식의 참고 자료입니다. 위 도식과 조건표는 역사적 그림·문장을 옮기지 않고 v6.6 tree.c 4969–5012행을 기준으로 독립 구성했습니다. 하위 함수 전체·모든 설정·실행 시간 동작을 검증한 것은 아닙니다.