# 독자가 빠져나왔는지 추적할 기반을 만듭니다

v6.18.37 / kernel/rcu/tree.c

RCU는 읽는 쪽의 부담을 줄이는 대신, 예전 객체를 볼 수 있는 독자가 끝난 뒤에 회수하도록 시간을 나눕니다. rcu_init은 CPU별 상태와 트리, 콜백 처리 기반을 준비합니다. 초기화 함수 안에서 한 객체를 바로 해제하는 예제는 아닙니다.

## rcu_init

```c

void __init rcu_init(void)
{
	int cpu = smp_processor_id();

	rcu_early_boot_tests();

	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);
	rcutree_report_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 | WQ_PERCPU, 0);
	WARN_ON(!rcu_gp_wq);

	sync_wq = alloc_workqueue("sync_wq", WQ_MEM_RECLAIM | WQ_UNBOUND, 0);
	WARN_ON(!sync_wq);

	/* Respect if explicitly disabled via a boot parameter. */
	if (rcu_normal_wake_from_gp < 0) {
		if (num_possible_cpus() <= WAKE_FROM_GP_CPU_THRESHOLD)
			rcu_normal_wake_from_gp = 1;
	}

	/* 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();

	tasks_cblist_init_generic();
}

```

### 4866행

```c

void __init rcu_init(void)

```

RCU가 CPU들의 읽기 구간 종료를 추적하고 지연된 콜백을 처리할 전역·CPU별 상태를 마련합니다. 독자가 끝나기 전에 공유 객체를 회수하지 않도록 하는 기반입니다.

### 4868행

```c

	int cpu = smp_processor_id();

```

RCU 초기 상태를 설정할 현재 부트 CPU 번호를 얻습니다. 이 시점에는 아래 검사처럼 여러 CPU가 이미 온라인인 상황을 예상하지 않습니다.

### 4870행

```c

	rcu_early_boot_tests();

```

RCU의 이른 부팅 테스트를 실행해 초기 상태의 동작을 점검합니다.

### 4872행

```c

	rcu_bootup_announce();

```

사용할 RCU 구성과 주요 설정을 부팅 로그에 알립니다.

### 4873행

```c

	sanitize_kthread_prio();

```

RCU 커널 스레드 우선순위 설정이 사용할 수 있는 범위에 있는지 정리합니다.

### 4874행

```c

	rcu_init_geometry();

```

CPU들을 추적할 RCU 노드 트리의 크기와 구조를 정합니다.

### 4875행

```c

	rcu_init_one();

```

RCU 전역 상태와 노드 구조를 초기화합니다.

### 4876행

```c

	if (dump_tree)

```

RCU 트리 진단 출력이 요청된 경우에만 아래 출력을 수행합니다.

### 4877행

```c

		rcu_dump_rcu_node_tree();

```

요청된 경우 구성한 RCU 노드 트리를 진단용으로 출력합니다.

### 4878행

```c

	if (use_softirq)

```

RCU 핵심 처리를 softirq로 수행하는 구성이면 해당 핸들러를 연결합니다.

### 4879행

```c

		open_softirq(RCU_SOFTIRQ, rcu_core_si);

```

지정 softirq 번호에 RCU 처리 함수를 연결합니다.

### 4886행

```c

	pm_notifier(rcu_pm_notify, 0);

```

절전·복귀 상태 변화에 대응할 RCU 통지 함수를 등록합니다.

### 4887행

```c

	WARN_ON(num_online_cpus() > 1); // Only one CPU this early in boot.

```

RCU 트리를 준비하는 이른 부팅 단계에서 온라인 CPU가 둘 이상이면 경고합니다. 아래의 CPU별 초기 등록이 부트 CPU부터 시작된다는 순서 조건을 확인합니다.

### 4888행

```c

	rcutree_prepare_cpu(cpu);

```

해당 CPU의 RCU 자료구조를 준비합니다.

### 4889행

```c

	rcutree_report_cpu_starting(cpu);

```

CPU가 시작하는 상태를 RCU 트리에 알립니다.

### 4890행

```c

	rcutree_online_cpu(cpu);

```

해당 CPU를 RCU의 온라인 추적에 반영합니다.

### 4893행

```c

	rcu_gp_wq = alloc_workqueue("rcu_gp", WQ_MEM_RECLAIM | WQ_PERCPU, 0);

```

Tree SRCU와 expedited grace period 처리를 위한 작업 큐를 만듭니다. grace period는 기존 독자의 읽기 구간이 끝나기를 기다리는 기간입니다. WQ_PERCPU는 작업을 특정 CPU에 묶고 WQ_MEM_RECLAIM은 메모리가 부족할 때도 실행 문맥을 확보하게 하며, 마지막 0은 동시 실행 한도에 기본값을 쓰라는 뜻입니다.

### 4894행

```c

	WARN_ON(!rcu_gp_wq);

```

유예 기간의 후속 작업을 맡길 rcu_gp 작업 큐 생성이 실패했는지 확인합니다. NULL 큐로 정상 후속 처리를 진행할 수 없다는 진단입니다.

### 4896행

```c

	sync_wq = alloc_workqueue("sync_wq", WQ_MEM_RECLAIM | WQ_UNBOUND, 0);

```

일반 synchronize_rcu 대기자 처리 후 남은 정리 작업을 수행할 별도 작업 큐를 만듭니다. WQ_UNBOUND는 작업을 특정 CPU에 고정하지 않고 실행할 수 있게 하며, WQ_MEM_RECLAIM은 메모리 부족 상황에서도 정리가 진행될 실행 문맥을 확보합니다.

### 4897행

```c

	WARN_ON(!sync_wq);

```

동기화 관련 후속 작업을 수행할 sync_wq 생성이 실패했는지 확인합니다. 이 버전에서 바로 앞 alloc_workqueue 호출 결과를 검사하는 줄입니다.

### 4900행

```c

	if (rcu_normal_wake_from_gp < 0) {

```

유예 기간 뒤 일반 깨우기 정책이 사용자가 정하지 않은 초기 값인지 검사합니다.

### 4901행

```c

		if (num_possible_cpus() <= WAKE_FROM_GP_CPU_THRESHOLD)

```

가능한 CPU 수가 정책 선택 임계값 이하인 시스템인지 확인합니다.

### 4902행

```c

			rcu_normal_wake_from_gp = 1;

```

해당 규모의 시스템에서 사용할 일반 깨우기 정책을 기본값으로 켭니다.

### 4907행

```c

	if (qovld < 0)

```

콜백 과부하 임계값을 사용자가 따로 지정하지 않은 경우인지 검사합니다.

### 4908행

```c

		qovld_calc = DEFAULT_RCU_QOVLD_MULT * qhimark;

```

콜백 큐의 높은 수위 기준에 기본 배수를 적용해 과부하 임계값을 계산합니다.

### 4909행

```c

	else

```

앞 조건이 성립하지 않은 경로를 선택합니다. 앞쪽 처리와 아래 처리를 모두 수행하는 것이 아닙니다.

### 4910행

```c

		qovld_calc = qovld;

```

사용자가 지정한 과부하 임계값을 그대로 사용합니다.

### 4913행

```c

	(void)start_poll_synchronize_rcu_expedited();

```

완료 여부를 나중에 확인할 수 있는 expedited 유예 기간 요청을 시작합니다.

### 4915행

```c

	rcu_test_sync_prims();

```

RCU 동기화 기본 동작의 초기 테스트를 수행합니다.

### 4917행

```c

	tasks_cblist_init_generic();

```

Tasks RCU 계열 콜백 목록의 초기 상태를 준비합니다.

