전체 흐름
rcu_init()은 부팅 초기에 Tree RCU의 구조를 준비합니다. call_rcu() 호출이나 grace period 완료를 이 함수의 내부 단계로 그리면 호출 관계가 틀어집니다. 아래 그림은 대표적인 네 경계만 골랐으며, 자세한 명령 순서는 원문을 따릅니다.
문C의 RCU 초기화 글은 geometry·fanout·node 계층을 읽는 보조 참고입니다. 그 글의 특정 CPU 수와 fanout 예시는 이 페이지의 v6.6 값으로 옮기지 않습니다.
rcu_init()이 준비하는 부팅 경로작은 화면에서는 그림을 좌우로 이동해 읽습니다.
rcu_init_geometry()CPU 수·fanout에 맞는 tree 형태
rcu_init_one()node 초기화·possible CPU의 mynode 연결
이때 online CPU 하나를 가정
GP kthread는 이 함수 밖에서 생성
부팅 CPU 준비 앞에는 rcu_init_one()과 조건부 open_softirq()가 있습니다. GP kthread의 early_initcall()은 별도 경계입니다. 고정 원문 rcu_init() · 후속 kthread 등록.
소스 코드 위치
먼저 확인할 Linux v6.6 소스 파일
Linux v6.6: kernel/rcu/tree.c
Linux v6.6: kernel/rcu/update.c
Linux v6.6: include/linux/rcupdate.h
설명: tree.c의 rcu_init(), geometry, rcu_init_one(), per-CPU 준비를 먼저 확인합니다. call_rcu()는 같은 파일에 있어도 초기화 함수의 직접 호출 대상이 아니며, 별도 callback 등록 API입니다.
대표 코드
void __init rcu_init
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 함수는 geometry → node/per-CPU 연결 → 부팅 CPU 준비·online → workqueue 순서로 읽습니다. 일반적인 오류 반환·goto 복구 함수로 설명하지 않습니다. v6.6에만 있는 호출은 아래 비교표에서 분리합니다.
call_rcu
void call_rcu(struct rcu_head *head, rcu_callback_t func)
{
return __call_rcu_common(head, func, IS_ENABLED(CONFIG_RCU_LAZY));
}
call_rcu()는 callback을 즉시 실행하거나 현재 호출자를 grace period 끝까지 재우는 함수가 아니다. head와 func를 __call_rcu_common()에 전달해 지연 실행을 등록한다. callback 안에서 무엇을 해제할지는 사용자가 정하며, 이미 공개한 객체는 먼저 독자가 새로 찾지 못하도록 분리해야 한다.
함수별 분석
같은 tree.c에 있다는 이유로 rcu_init()과 call_rcu()를 연속 호출로 묶지 않습니다. 위쪽 줄은 부팅 시 tree 준비, 아래쪽 줄은 나중 updater의 callback 등록을 뜻합니다.
작은 화면에서는 그림을 좌우로 이동해 읽습니다.
두 줄 사이에는 직접 호출 화살표가 없습니다. call_rcu() 등록 직후 callback이 실행되는 것도 아닙니다.
| 고정 코드 | 역할 | 오해하지 않을 점 |
|---|---|---|
| rcu_init_geometry() | 설정과 CPU 수에 맞춰 tree geometry 결정 | 역사적 fanout 숫자를 모든 빌드에 고정하지 않음 |
| rcu_init_one() | node lock·GP sequence·parent·leaf 범위, possible CPU의 mynode 준비 | possible CPU 전체를 online으로 만드는 단계는 아님 |
| rcu_init() | rcu_cpu_starting(cpu) 전후로 부팅 CPU를 준비·online 보고 | 이 시점은 online CPU가 하나인 초기 부팅 |
| call_rcu() | 나중에 updater가 callback 등록 | rcu_init() 안에서 직접 부르는 함수가 아님 |
구조체와 필드
rcu_state.node[]의 계층, CPU별 rcu_data.mynode의 leaf 참조, updater 객체의 rcu_head는 서로 다른 관계입니다. 초기화 코드는 먼저 tree node와 CPU별 leaf 포인터를 준비합니다.
rcu_init_one()이 연결하는 Tree RCU 관계작은 화면에서는 그림을 좌우로 이동해 읽습니다.
rcu_node 계층을 보유mynode가 담당 leaf를 가리킴; CPU별 callback cblist⊃는 node 배열의 포함 관계, ←는 rcu_data.mynode가 leaf를 가리키는 참조 방향입니다. rcu_head는 이 초기화 체인의 다음 node가 아닙니다. 고정 원문 rcu_init_one().
| 객체 | 이 코드에서의 관계 |
|---|---|
rcu_state.node[] | geometry에 맞춘 node 배열. rcu_init_one()이 level·parent·lock·sequence 등을 초기화 |
rcu_data | possible CPU별 mynode 연결 및 per-CPU 초기화. 그 자리에서 전부 online이 되는 것은 아님 |
rcu_head | updater 객체의 callback anchor. rcu_init()이 모든 rcu_head를 생성하거나 연결하지 않음 |
초기화 단계와 버전 차이
reader 진입·callback 등록·GP 완료는 rcu_init() 함수 안의 연속 단계가 아닙니다. 초기화가 준비한 구조를 운영 중 경로가 사용합니다. GP kthread 생성은 early_initcall(rcu_spawn_gp_kthread)로 따로 등록됩니다.
작은 화면에서는 그림을 좌우로 이동해 읽습니다.
화살표는 부팅/운영 단계의 경계를 뜻하며 rcu_init()이 call_rcu()를 직접 호출한다는 뜻이 아닙니다. 고정 원문 early_initcall.
| 관찰 지점 | v6.6 | v6.18.37 |
|---|---|---|
| 부팅 초반 추가 호출 | kfree_rcu_batch_init() 있음 | 해당 호출 없음 |
| 부팅 CPU starting 보고 | rcu_cpu_starting(cpu) | rcutree_report_cpu_starting(cpu) |
| RCU GP workqueue | WQ_MEM_RECLAIM, rcu_alloc_par_gp_wq() | WQ_MEM_RECLAIM | WQ_PERCPU, 별도 sync_wq |
| 함수 말미 | rcu_test_sync_prims() | 같은 검사 뒤 tasks_cblist_init_generic() |
현재 페이지는 v6.6의 원문 코드 블록을 그대로 제시합니다. 비교표는 두 버전의 함수 본문을 대조한 것이며, 표시한 workqueue 플래그나 helper 이름을 다른 버전에 적용하지 않습니다.
초기화에서 지킬 경계
rcu_init_geometry()가 결정한 level과 fanout을 rcu_init_one()이 사용합니다. boot parameter와 compile-time 배열 용량을 동일한 값으로 단정하지 않습니다.
rcu_init_one()은 possible CPU별 mynode를 연결합니다. 뒤의 WARN_ON(num_online_cpus() > 1)은 이 호출 시점 online CPU가 하나여야 한다는 별도 조건입니다.
call_rcu()는 독립 API이며 callback의 지연 실행을 등록합니다. 이 초기화 코드의 종료를 특정 객체의 grace period 완료나 free 시점으로 읽지 않습니다.
rcu_init()은 void이며 일반적인 error label 복구 체인이 아닙니다. 원문에는 geometry 범위 위반의 panic()과 workqueue 할당 실패 경고 등이 따로 나타납니다.
문C의 과거 geometry 도해는 fanout·node 관계를 보는 참고로 유지하고, 실제 v6.6의 숫자와 호출 이름은 고정 코드로 판별합니다.
계측과 검증
부팅 초기 rcu_init()과 운영 중 GP/callback을 다른 관찰 대상으로 둡니다. 부팅 로그의 RCU 안내·조건부 tree dump는 초기화 확인에, 부팅 뒤 RCU tracepoint는 이후 GP·callback 경로 확인에 사용합니다. 늦게 마운트한 tracefs만으로 함수의 가장 이른 부팅 시점을 재구성할 수는 없습니다.
| 관찰 | 확인 가능한 것 | 증명하지 못하는 것 |
|---|---|---|
rcu_bootup_announce() 로그 | 부팅 RCU 설정 안내 | 모든 node·CPU hotplug 완료 |
조건부 rcu_dump_rcu_node_tree() | 초기화된 geometry·node 배치 점검 | callback이 실행되었는지 |
부팅 뒤 rcu:* 이벤트 | 운영 중 GP·callback 활동 | 초기 rcu_init() 내부 모든 호출 시점 |
두 함수의 실제 위치는 rcu_init()에 있습니다. 런타임 트레이싱을 사용할 때는 빌드의 이벤트 목록을 먼저 확인하고 GP와 callback을 별도 이벤트로 기록합니다.
추가 확인 사항
- 이 빌드의
nr_cpu_ids와 fanout 설정에서 geometry와 실제 node 수는 얼마인가? - possible CPU의
rcu_data.mynode연결과 boot CPU online 보고를 구분했는가? - 이 버전의 workqueue 플래그·CPU starting helper를 원문과 맞췄는가?
- GP kthread의 별도
early_initcall과 운영 중call_rcu()를rcu_init()내부 호출로 그리지 않았는가?