[커널 18차] 95주차
2023.03.20 10:34
do_idle 완
git : https://github.com/iamroot18/5.10/commit/5128910c59f87c424a4fac644a6efa77e88b80ce
diff --git a/drivers/cpuidle/coupled.c b/drivers/cpuidle/coupled.c
index 74068742cef3..81a10393a9e3 100644
--- a/drivers/cpuidle/coupled.c
+++ b/drivers/cpuidle/coupled.c
@@ -172,6 +172,16 @@ void cpuidle_coupled_parallel_barrier(struct cpuidle_device *dev, atomic_t *a)
*
* Returns true if the target state is coupled with cpus besides this one
*/
+/*
+ * IAMROOT, 2023.03.18:
+ * - papago
+ * cpuidle_state_is_coupled - 상태가 결합 세트의 일부인지 확인합니다.
+ * @drv: 플랫폼용 struct cpuidle_driver.
+ * @state: drv->states에서 대상 상태의 인덱스 대상 상태가 이 이외의
+ * cpus와 결합된 경우 true를 반환합니다.
+ *
+ * @return true. CPUIDLE_FLAG_COUPLED 존재.(multiple cpu. cluster)
+ */
bool cpuidle_state_is_coupled(struct cpuidle_driver *drv, int state)
{
return drv->states[state].flags & CPUIDLE_FLAG_COUPLED;
@@ -467,6 +477,30 @@ static bool cpuidle_coupled_any_pokes_pending(struct cpuidle_coupled *coupled)
* interrupts while preparing for idle, and it will always return with
* interrupts enabled.
*/
+/*
+ * IAMROOT, 2023.03.18:
+ * - papago
+ * cpuidle_enter_state_coupled - cpus가 연결된 상태로 들어가려고 시도합니다.
+ * @dev: 현재 CPU에 대한 struct cpuidle_device.
+ * @drv: 플랫폼용 struct cpuidle_driver.
+ * @next_state: drv->states에서 요청된 상태의 인덱스.
+ *
+ * 결합된 cpus와 협력하여 대상 상태로 진입합니다. 이것은 2단계
+ * 프로세스입니다. 첫 번째 단계에서 CPU는 독립적으로 작동하며 완전히
+ * 다른 시간에 cpuidle_enter_state_coupled를 호출할 수 있습니다.
+ * 가능한 한 많은 전력을 절약하기 위해 이 함수를 호출하는 첫 번째 CPU는
+ * 중간 상태(cpuidle_device의 안전 상태)로 이동하고 다른 모든 CPU가
+ * 이 함수를 호출할 때까지 기다립니다. 연결된 모든 CPU가 유휴 상태가 되면
+ * 두 번째 단계가 시작됩니다. 연결된 각 CPU는 모든 CPU가 target_state를
+ * 호출할 것이라고 보장할 때까지 회전합니다.
+ *
+ * 이 함수는 인터럽트를 비활성화한 상태에서 호출해야 합니다. 유휴 상태를
+ * 준비하는 동안 인터럽트를 활성화할 수 있으며 항상 인터럽트가 활성화된
+ * 상태로 반환됩니다.
+ *
+ * - coupled된(power domain, cluster 단위의) cpu들을 더 깊게 잠들게 한다.
+ * - PASS
+ */
int cpuidle_enter_state_coupled(struct cpuidle_device *dev,
struct cpuidle_driver *drv, int next_state)
{
@@ -502,6 +536,14 @@ int cpuidle_enter_state_coupled(struct cpuidle_device *dev,
* exiting the waiting state due to an interrupt and
* decrementing waiting_count, see comment below.
*/
+/*
+ * IAMROOT, 2023.03.18:
+ * - papago
+ * 이것이 대기 상태로 들어가는 마지막 CPU인 경우 다른 모든 CPU를 대기
+ * 상태에서 빼내어 더 깊은 상태로 들어갈 수 있도록 합니다. 이것은
+ * 인터럽트로 인해 대기 상태를 종료하는 CPU 중 하나와 Waiting_count 감소와
+ * 경쟁할 수 있습니다. 아래 설명을 참조하십시오.
+ */
if (w == coupled->online_count) {
cpumask_set_cpu(dev->cpu, &cpuidle_coupled_poked);
cpuidle_coupled_poke_others(dev->cpu, coupled);
@@ -516,6 +558,14 @@ int cpuidle_enter_state_coupled(struct cpuidle_device *dev,
* but the first of the two to arrive could skip the loop without
* processing the pokes from the last to arrive.
*/
+/*
+ * IAMROOT, 2023.03.18:
+ * - papago
+ * 단일 CPU에 허용되는 가장 깊은 상태를 사용하여 결합된 모든 CPU가 유휴
+ * 상태가 될 때까지 기다립니다. 이것이 Poking CPU가 아닌 경우, 두 개의
+ * CPU가 동시에 대기 루프에 도착할 수 있는 경쟁을 피하기 위해 떠나기 전에
+ * 최소한 한 번의 Poke를 기다리십시오. 마지막부터 도착.
+ */
while (!cpuidle_coupled_cpus_waiting(coupled) ||
!cpumask_test_cpu(dev->cpu, &cpuidle_coupled_poked)) {
if (cpuidle_coupled_clear_pokes(dev->cpu))
@@ -546,6 +596,12 @@ int cpuidle_enter_state_coupled(struct cpuidle_device *dev,
* Make sure final poke status for this cpu is visible before setting
* cpu as ready.
*/
+/*
+ * IAMROOT, 2023.03.18:
+ * - papago
+ * 설정하기 전에 이 CPU의 최종 poke 상태가 보이는지 확인하십시오.
+ * cpu가 준비되었습니다.
+ */
smp_wmb();
/*
@@ -556,7 +612,16 @@ int cpuidle_enter_state_coupled(struct cpuidle_device *dev,
* spin until either all cpus have incremented the ready counter, or
* another cpu leaves idle and decrements the waiting counter.
*/
-
+/*
+ * IAMROOT, 2023.03.18:
+ * - papago
+ * 연결된 모든 CPU는 아마도 유휴 상태일 것입니다. 다른 CPU 중 하나가 방금
+ * 활성화되었을 가능성이 적습니다. 준비 카운트를 증가시키고 연결된 모든
+ * CPU가 카운터를 증가시킬 때까지 회전합니다. CPU가 준비 카운터를
+ * 증가시키면 유휴를 중단할 수 없으며 모든 CPU가 준비 카운터를
+ * 증가시키거나 다른 CPU가 유휴 상태를 유지하고 대기 카운터를 감소시킬
+ * 때까지 회전해야 합니다.
+ */
cpuidle_coupled_set_ready(coupled);
while (!cpuidle_coupled_cpus_ready(coupled)) {
/* Check if any other cpus bailed out of idle. */
@@ -570,6 +635,11 @@ int cpuidle_enter_state_coupled(struct cpuidle_device *dev,
/*
* Make sure read of all cpus ready is done before reading pending pokes
*/
+/*
+ * IAMROOT, 2023.03.18:
+ * - papago
+ * 보류 중인 pokes를 읽기 전에 모든 CPU 준비 읽기가 완료되었는지 확인하십시오.
+ */
smp_rmb();
/*
@@ -582,6 +652,17 @@ int cpuidle_enter_state_coupled(struct cpuidle_device *dev,
* it, and it's too late to turn on interrupts here, so reset the
* coupled idle state of all cpus and retry.
*/
+/*
+ * IAMROOT, 2023.03.18:
+ * - papago
+ * 이 CPU가 모든 CPU가 대기 중임을 확인한 후 CPU가 유휴 상태를 떠났다가
+ * 다시 들어갈 가능성이 적습니다. 유휴 상태로 다시 들어간 CPU는 이 CPU에
+ * 찌르기를 보냈을 것이며 이는 준비 루프 이후에 여전히 보류 중일 것입니다.
+ * 보류 중인 인터럽트는 깊은 유휴 상태에 들어갈 때 인터럽트 컨트롤러에
+ * 의해 손실될 수 있습니다. 인터럽트를 켜고 처리하지 않고 보류 중인
+ * 인터럽트를 지우는 것은 불가능하며 여기에서 인터럽트를 켜기에는 너무
+ * 늦었으므로 모든 CPU의 연결된 유휴 상태를 재설정하고 다시 시도하십시오.
+ */
if (cpuidle_coupled_any_pokes_pending(coupled)) {
cpuidle_coupled_set_done(dev->cpu, coupled);
/* Wait for all cpus to see the pending pokes */
@@ -611,6 +692,21 @@ int cpuidle_enter_state_coupled(struct cpuidle_device *dev,
* interrupts disabled, but won't cause problems for drivers that
* exit with interrupts enabled.
*/
+/*
+ * IAMROOT, 2023.03.18:
+ * - papago
+ * 일반 cpuidle 상태는 irqs가 활성화된 상태로 반환될 것으로 예상됩니다.
+ * 이는 유휴 상태에서 벗어나게 하는 인터럽트를 수신하는 CPU가 유휴 입력
+ * 기능을 종료하고 ready_count를 감소시키기 전에 해당 인터럽트를 처리하는
+ * 비효율성을 초래합니다. 다른 모든 CPU는 인터럽트를 처리하는 CPU를
+ * 기다리며 회전해야 합니다. 드라이버가 인터럽트가 비활성화된 상태로
+ * 돌아오면 다른 모든 CPU는 회전하지 않고 안전한 유휴 상태로 되돌아가
+ * 전력을 절약합니다.
+ *
+ * 여기에서 local_irq_enable을 호출하면 인터럽트가 비활성화된 상태로
+ * 결합된 상태가 반환될 수 있지만 인터럽트가 활성화된 상태에서 종료되는
+ * 드라이버에는 문제가 발생하지 않습니다.
+ */
local_irq_enable();
/*
@@ -618,6 +714,13 @@ int cpuidle_enter_state_coupled(struct cpuidle_device *dev,
* a cpu exits and re-enters the ready state because this cpu has
* already decremented its waiting_count.
*/
+/*
+ * IAMROOT, 2023.03.18:
+ * - papago
+ * 연결된 모든 CPU가 유휴 상태에서 벗어날 때까지 기다리십시오. 이 CPU는
+ * 이미 wait_count를 줄였기 때문에 CPU가 종료했다가 다시 준비 상태로
+ * 들어갈 위험이 없습니다.
+ */
while (!cpuidle_coupled_no_cpus_ready(coupled))
cpu_relax();
diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c
index 93db6bd336ce..64037eabbf43 100644
--- a/drivers/cpuidle/cpuidle.c
+++ b/drivers/cpuidle/cpuidle.c
@@ -74,6 +74,18 @@ int cpuidle_play_dead(void)
return -ENODEV;
}
+/*
+ * IAMROOT, 2023.03.18:
+ * @forbidden_flags 해당 flag가 지정되면 건너 뛴다.
+ * @return 0 : 조건에 맞는 C state 못찾음
+ * != 0 : 조건에 맞는 C state index.
+ *
+ * - 인자 조건에 맞는 최대 exit_latency_ns를 구해서 해당 C state index를
+ * return 한다.
+ * max_latency_ns값이 U64_MAX가 아니라면 < max_latency_ns의 이내의 범위에서
+ * 최대 값을 찾는다.
+ * - s2idle이 true라면 s2idle에서 사용할수있는 C state를 찾는다.
+ */
static int find_deepest_state(struct cpuidle_driver *drv,
struct cpuidle_device *dev,
u64 max_latency_ns,
@@ -136,6 +148,12 @@ void cpuidle_use_deepest_state(u64 latency_limit_ns)
*
* Return: the index of the deepest available idle state.
*/
+/*
+ * IAMROOT, 2023.03.18:
+ * @return 0 : 조건에 맞는 C state 못찾음
+ * != 0 : 조건에 맞는 C state index.
+ * - @latency_limit_ns 이내의 C state idx를 구한다.
+ */
int cpuidle_find_deepest_state(struct cpuidle_driver *drv,
struct cpuidle_device *dev,
u64 latency_limit_ns)
@@ -144,6 +162,12 @@ int cpuidle_find_deepest_state(struct cpuidle_driver *drv,
}
#ifdef CONFIG_SUSPEND
+/*
+ * IAMROOT, 2023.03.18:
+ * - @index의 cpudile state를 가져와 enter_s2idle(deep sleep)을 수행한다.
+ * - deel sleep전후로 sched timer를 off / on(or system suspend, resume)한다.
+ * - 깨어난 이후에는 deep sleep의 기간누적시키고 발생 빈도를 증가시킨다.
+ */
static void enter_s2idle_proper(struct cpuidle_driver *drv,
struct cpuidle_device *dev, int index)
{
@@ -161,6 +185,10 @@ static void enter_s2idle_proper(struct cpuidle_driver *drv,
stop_critical_timings();
if (!(target_state->flags & CPUIDLE_FLAG_RCU_IDLE))
rcu_idle_enter();
+/*
+ * IAMROOT, 2023.03.18:
+ * - 여기서 deepsleep에 들어간다.
+ */
target_state->enter_s2idle(dev, drv, index);
if (WARN_ON_ONCE(!irqs_disabled()))
local_irq_disable();
@@ -193,7 +221,10 @@ static void enter_s2idle_proper(struct cpuidle_driver *drv,
* ->enter_s2idle 콜백이 있는 상태가 있는 경우 가장 깊은 상태를 찾아 고정된
* 틱으로 입력합니다.
*
- * - ING
+ * - @return 0 : s2idle로 동작하는 C state를 못찾음.
+ * @return != 0 : s2idle로 동작하는 C state중에서 최대 latency ms state.
+ * - s2idle로 동작할 수 있는 최대 latency ms를 가진 C state찾는다.
+ * 찾은 경우 deep sleep을 하고 마친 후에는 irq를 enable한다.
*/
int cpuidle_enter_s2idle(struct cpuidle_driver *drv, struct cpuidle_device *dev)
{
@@ -225,6 +256,12 @@ int cpuidle_enter_s2idle(struct cpuidle_driver *drv, struct cpuidle_device *dev)
* @drv: cpuidle driver for this cpu
* @index: index into the states table in @drv of the state to enter
*/
+/*
+ * IAMROOT, 2023.03.18:
+ * - c state @index에 해당하는 idle을 수행하고 통계를 계산한다.
+ * - broadcast인 경우 wakeup oneshot device에 맡기는걸 시도하여
+ * CPUIDLE_FLAG_TIMER_STOP이 없는 c state index를 새로 찾아본다.
+ */
int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv,
int index)
{
@@ -239,9 +276,28 @@ int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv,
* local timer will be shut down. If a local timer is used from another
* CPU as a broadcast timer, this call may fail if it is not available.
*/
+/*
+ * IAMROOT, 2023.03.18:
+ * - papago
+ * 로컬 타이머가 종료되므로 브로드캐스트 타이머로 전환하도록 시간
+ * 프레임워크에 알립니다. 로컬 타이머가 다른 CPU에서 브로드캐스트
+ * 타이머로 사용되는 경우 이 호출을 사용할 수 없으면 이 호출이 실패할
+ * 수 있습니다.
+ *
+ * - broadcast라면, timer stop을 하고 oneshot wakeup device or
+ * broadcast devie에 this cpu를 next_event에 깨우도록 요청한다.
+ */
if (broadcast && tick_broadcast_enter()) {
+/*
+ * IAMROOT, 2023.03.18:
+ * - CPUIDLE_FLAG_TIMER_STOP가 제외된 exit_latency_ns이내의 c state를 찾는다.
+ */
index = find_deepest_state(drv, dev, target_state->exit_latency_ns,
CPUIDLE_FLAG_TIMER_STOP, false);
+/*
+ * IAMROOT, 2023.03.18:
+ * - 못찾앗으면 default idle로 동작하고 끝낸다.
+ */
if (index < 0) {
default_idle_call();
return -EBUSY;
@@ -262,6 +318,11 @@ int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv,
stop_critical_timings();
if (!(target_state->flags & CPUIDLE_FLAG_RCU_IDLE))
rcu_idle_enter();
+
+/*
+ * IAMROOT, 2023.03.18:
+ * - c state index에 따른 idle상태에 진입한다.
+ */
entered_state = target_state->enter(dev, drv, index);
if (!(target_state->flags & CPUIDLE_FLAG_RCU_IDLE))
rcu_idle_exit();
@@ -272,8 +333,17 @@ int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv,
trace_cpu_idle(PWR_EVENT_EXIT, dev->cpu);
/* The cpu is no longer idle or about to enter idle. */
+
+/*
+ * IAMROOT, 2023.03.18:
+ * - 이제 idle이 끝났으므로 null로 설정한다.
+ */
sched_idle_set_state(NULL);
+/*
+ * IAMROOT, 2023.03.18:
+ * - broadcast였지만 위에서 전환이 실패한경우.
+ */
if (broadcast) {
if (WARN_ON_ONCE(!irqs_disabled()))
local_irq_disable();
@@ -284,6 +354,10 @@ int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv,
if (!cpuidle_state_is_coupled(drv, index))
local_irq_enable();
+/*
+ * IAMROOT, 2023.03.18:
+ * - idle이 끝난후, idle이 동장했던 entered_state에 대한 통계처리를 진행한다.
+ */
if (entered_state >= 0) {
s64 diff, delay = drv->states[entered_state].exit_latency_ns;
int i;
@@ -324,6 +398,10 @@ int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv,
}
}
} else {
+/*
+ * IAMROOT, 2023.03.18:
+ * - 실패 통계 처리
+ */
dev->last_residency_ns = 0;
dev->states_usage[index].rejected++;
}
@@ -344,6 +422,24 @@ int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv,
* 'false' boolean value if the scheduler tick should not be stopped before
* entering the returned state.
*/
+/*
+ * IAMROOT, 2023.03.18:
+ * - papago
+ * cpuidle_select - 유휴 상태를 선택하도록 cpuidle 프레임워크에 요청합니다.
+ * @drv: cpuidle 드라이버.
+ * @dev: cpuidle 장치.
+ * @stop_tick: 틱 중지 여부 표시.
+ *
+ * 유휴 상태의 인덱스를 반환합니다. 반환 값은 음수가 아니어야 합니다.
+ *
+ * 반환된 상태에 들어가기 전에 스케줄러 틱이 중지되지 않아야 하는 경우
+ * @stop_tick이 가리키는 메모리 위치는 'false' 부울 값으로 기록될 것으로 예상됩니다.
+ *
+ * - curr governor에서 cpuidle을 고른다. 주석에 따르면 schedule tick이 중지되지
+ * 않아야되는 경우 stop_tick은 false로 업데이트 된다.
+ * - curr governor에서 cpuidle state를 골라서 return한다.
+ * - ex) menu_select
+ */
int cpuidle_select(struct cpuidle_driver *drv, struct cpuidle_device *dev,
bool *stop_tick)
{
@@ -360,6 +456,10 @@ int cpuidle_select(struct cpuidle_driver *drv, struct cpuidle_device *dev,
* Returns the index in the idle state, < 0 in case of error.
* The error code depends on the backend driver
*/
+/*
+ * IAMROOT, 2023.03.18:
+ * - c state index해 당하는 idle을 수행한다.
+ */
int cpuidle_enter(struct cpuidle_driver *drv, struct cpuidle_device *dev,
int index)
{
@@ -371,13 +471,33 @@ int cpuidle_enter(struct cpuidle_driver *drv, struct cpuidle_device *dev,
* useful for consumers outside cpuidle, we rely on that the governor's
* ->select() callback have decided, whether to stop the tick or not.
*/
+/*
+ * IAMROOT, 2023.03.18:
+ * - papago
+ * 먼저 만료되는 것이 무엇이든 다음 틱 또는 다음 타이머 이벤트가 되는
+ * 다음 hrtimer를 저장합니다. 또한 이 데이터를 cpuidle 외부의
+ * 소비자에게 유용하게 만들기 위해 우리는 거버너의 ->select() 콜백이 틱
+ * 중지 여부를 결정했는지에 의존합니다.
+ *
+ * - 가장 먼저 완료될 시간을 dev->next_hrtimer에 저장한다.
+ * idle을 중에 next_hrtimer가 oneshot wakeup device나 braodcast들에 의해
+ * next_hrtimer에 this cpu가 깨어나어 처리하게 할 것이다.
+ */
WRITE_ONCE(dev->next_hrtimer, tick_nohz_get_next_hrtimer());
+/*
+ * IAMROOT, 2023.03.18:
+ * - c state index에 해당하는 idle을 수행하고 통계를 작성한다.
+ */
if (cpuidle_state_is_coupled(drv, index))
ret = cpuidle_enter_state_coupled(dev, drv, index);
else
ret = cpuidle_enter_state(dev, drv, index);
+/*
+ * IAMROOT, 2023.03.18:
+ * - 깨어나면 next_timer을 초기화한다.
+ */
WRITE_ONCE(dev->next_hrtimer, 0);
return ret;
}
@@ -390,6 +510,12 @@ int cpuidle_enter(struct cpuidle_driver *drv, struct cpuidle_device *dev,
* @index: the index in the idle state table
*
*/
+/*
+ * IAMROOT, 2023.03.18:
+ * - curr governor에서 reflect callback을 호출한다.
+ * idle 끝난 직후 기록 및 처리.
+ * ex) menu_reflect
+ */
void cpuidle_reflect(struct cpuidle_device *dev, int index)
{
if (cpuidle_curr_governor->reflect && index >= 0)
diff --git a/drivers/cpuidle/governor.c b/drivers/cpuidle/governor.c
index 29acaf48e575..0ff722cc46ab 100644
--- a/drivers/cpuidle/governor.c
+++ b/drivers/cpuidle/governor.c
@@ -19,6 +19,66 @@
char param_governor[CPUIDLE_NAME_LEN];
LIST_HEAD(cpuidle_governors);
+
+/*
+ * IAMROOT, 2023.03.18:
+ * --- chat openai ----
+ * - governor
+ * CPUIdle 거버너는 전력 소비를 최적화하기 위해 CPU의 전원 상태 전환을 관리하는
+ * Linux 커널의 소프트웨어 구성 요소입니다. 거버너는 현재 워크로드 및 시스템 활동에
+ * 따라 CPU를 저전력 상태로 전환할 시기를 결정합니다. 거버너는 CPU의 유휴 시간을
+ * 모니터링하고 유휴 시간이 특정 임계값에 도달하면 전환할 적절한 유휴 상태를
+ * 선택합니다. 사용 가능한 유휴 상태는 특정 프로세서 및 하드웨어 플랫폼에 따라
+ * 다르지만 일반적으로 C0(활성), C1(정지), C2(저전력 상태) 등과 같은 여러 수준의
+ * 유휴 상태를 포함합니다. 거버너는 또한 처리가 필요한 들어오는 작업이 있는 경우
+ * 저전력 상태에서 CPU를 깨우도록 결정할 수 있습니다. CPUIdle 거버너는 Linux 커널
+ * 전원 관리 프레임워크의 중요한 구성 요소로, 에너지 소비를 줄이고 모바일 장치 및
+ * 랩톱의 배터리 수명을 연장하는 데 도움이 됩니다.
+ * ---------------------
+ *
+ * - cpuidle governor
+ * cpuidle에서 사용하는 governor는 다음과 같습니다.
+ *
+ * menu governor : 최소한의 전력 소비를 위해 가능한한 적은 C-state으로
+ * CPU를 유지하고, 사용자 요청이 있을 때 빠르게 복귀합니다.
+ *
+ * ladder governor : 시스템 부하에 따라서 다른 C-state으로 이동합니다.
+ * 부하가 낮을 때는 더 깊은 C-state으로 이동하여 전력 소비를 최소화하고, 부하가
+ * 높을 때는 더 얕은 C-state으로 이동하여 빠른 응답성을 유지합니다.
+ *
+ * menu-ladder governor : menu와 ladder governor의 조합입니다. 일반적으로
+ * ladder보다 더 나은 전력 절약을 제공합니다.
+ *
+ * power aware governor : Intel의 P-state 기능을 활용하여 CPU의 주파수와 전압을
+ * 동적으로 조절하면서, 전력 소비를 최소화합니다.
+ *
+ * conservative governor : CPU 사용률이 낮을 때는 더 깊은 C-state으로 이동하여
+ * 전력 소비를 최소화하고, 사용률이 높을 때는 더 얕은 C-state으로 이동하여 빠른
+ * 응답성을 유지합니다.
+ *
+ * performance governor : CPU를 항상 최대 주파수에서 동작하도록 유지합니다.
+ * 이 governor는 전력 소비를 최소화하지 않으며, CPU 성능을 우선시하는 경우에
+ * 사용됩니다.
+ *
+ * ondemand governor : CPU 사용률에 따라 C-state을 선택합니다. 사용률이 높을 때는
+ * 더 얕은 C-state으로 이동하여 빠른 응답성을 유지하고, 사용률이 낮을 때는 더 깊은
+ * C-state으로 이동하여 전력 소비를 최소화합니다.
+ *
+ * 따라서 cpuidle에서는 총 7가지의 governor가 있습니다.
+ *
+ * - sysfss
+ * 1.
+ * cat /sys/devices/system/cpu/cpuidle/available_governors
+ * menu
+ * cat /sys/devices/system/cpu/cpuidle/current_governors
+ * menu
+ *
+ * 2.
+ * cat /sys/devices/system/cpu/cpuidle/available_governors
+ * ladder menu teo haltpoll
+ * cat /sys/devices/system/cpu/cpuidle/current_governors
+ * menu
+ */
struct cpuidle_governor *cpuidle_curr_governor;
struct cpuidle_governor *cpuidle_prev_governor;
diff --git a/include/linux/clockchips.h b/include/linux/clockchips.h
index 5b8f418d8b99..793451bd7e0e 100644
--- a/include/linux/clockchips.h
+++ b/include/linux/clockchips.h
@@ -60,7 +60,8 @@ enum clock_event_state {
/*
* IAMROOT, 2022.12.03:
* - C3
- * clock 절전기능. 전원은 끄지 않고 clock만 멈추는 기능.
+ * struct cpuidle_state 주석 참고
+ * clock 절전.
*/
# define CLOCK_EVT_FEAT_C3STOP 0x000008
/*
diff --git a/include/linux/cpuidle.h b/include/linux/cpuidle.h
index ea436c764c19..162703c17e98 100644
--- a/include/linux/cpuidle.h
+++ b/include/linux/cpuidle.h
@@ -47,6 +47,38 @@ struct cpuidle_state_usage {
/*
* IAMROOT, 2023.03.11:
+ * ----- c state -------
+ * Intel의 C State
+ *
+ * Intel에서 제조한 프로세서는 절전 모드에서 세 가지 C-state (C1, C2, C3)를
+ * 지원합니다.
+ *
+ * C1 상태는 "Auto Halt" 또는 "MWAIT"라고도 불리며, 프로세서가 대기하면서
+ * 최소한의 전력을 소비하도록 하는 가장 간단한 절전 모드입니다. 프로세서는
+ * 일시 중지되어 있지만, 빠른 복귀를 위해 기다리고 있는 시스템 이벤트를
+ * 감지합니다.
+ *
+ * C2 상태는 "Stop-Clock" 또는 "HALT"라고도 불리며, 프로세서의 클럭을
+ * 중지하고 캐시를 비우는 것을 포함하여 C1보다 더 많은 전력을 절약합니다.
+ * 프로세서는 일시 중지 상태이지만, C1보다 더 많은 시간을 소비하므로
+ * 기다리고 있는 이벤트를 탐지하기 위해 더 많은 시간이 걸립니다.
+ *
+ * C3 상태는 "Deep Sleep" 또는 "Sleep"라고도 불리며, 프로세서의 코어 전압과
+ * 클럭을 낮추어서 더 많은 전력을 절약합니다. C3 상태는 대개 C2보다 더 깊은
+ * 수면 상태이며, 프로세서는 기다리고 있는 이벤트를 탐지하기 위해 C2보다
+ * 더 많은 시간이 걸립니다.
+ *
+ * 이 세 가지 C-state는 프로세서가 실행되는 로드와 같은 여러 가지 요소에
+ * 따라 다릅니다. 예를 들어, 높은 로드를 처리하는 경우 C1 상태가 더 많이
+ * 사용됩니다. 그러나 낮은 로드를 처리하는 경우에는 C3 상태가 더 많이
+ * 사용됩니다.
+ *
+ * - C1 : core 정지
+ * C2 : c1 + cache clear
+ * C3 : C2 + 전압 다운. option으로 timer off. 최대 절전
+ * C5 : core 전원 off
+ * ---------------------
+ *
* - 디바이스 트리에서 아래 변수를 가져옮.
* 1. "entry-latency-us" + "exit-latency-us" => idle_state->exit_latency
* 또는 "wakeup-latency-us" => idle_state->exit_latency
@@ -102,7 +134,17 @@ struct cpuidle_state {
/* Idle State Flags */
#define CPUIDLE_FLAG_NONE (0x00)
#define CPUIDLE_FLAG_POLLING BIT(0) /* polling state */
+/*
+ * IAMROOT, 2023.03.18:
+ * - cluster로 묶은 cpu.
+ */
#define CPUIDLE_FLAG_COUPLED BIT(1) /* state applies to multiple cpus */
+
+/*
+ * IAMROOT, 2023.03.18:
+ * - dt
+ * local-timer-stop prop
+ */
#define CPUIDLE_FLAG_TIMER_STOP BIT(2) /* timer is stopped on this state */
#define CPUIDLE_FLAG_UNUSABLE BIT(3) /* avoid using this state */
#define CPUIDLE_FLAG_OFF BIT(4) /* disable this state by default */
@@ -121,6 +163,10 @@ struct cpuidle_device {
ktime_t next_hrtimer;
int last_state_idx;
+/*
+ * IAMROOT, 2023.03.18:
+ * - 마지막에 했던 idle시간. 실패했을 경우 0
+ */
u64 last_residency_ns;
u64 poll_limit_ns;
u64 forced_idle_latency_limit_ns;
diff --git a/include/linux/tick.h b/include/linux/tick.h
index 3b20d1554c38..a387dd127aee 100644
--- a/include/linux/tick.h
+++ b/include/linux/tick.h
@@ -96,10 +96,23 @@ static inline void tick_broadcast_force(void)
{
tick_broadcast_control(TICK_BROADCAST_FORCE);
}
+
+/*
+ * IAMROOT, 2023.03.18:
+ * @return 0 : 성공
+ * < 0 : c3 stop 미지원이나 못한 경우.
+ * - tick device에서 c3 stop을 지원한다면 tick deviec를 정지시키고
+ * oneshot wakeup device에서 해당 cpu를 깨우게 맡긴다.
+ */
static inline int tick_broadcast_enter(void)
{
return tick_broadcast_oneshot_control(TICK_BROADCAST_ENTER);
}
+
+/*
+ * IAMROOT, 2023.03.18:
+ * - braodcast에서 exit.
+ */
static inline void tick_broadcast_exit(void)
{
tick_broadcast_oneshot_control(TICK_BROADCAST_EXIT);
@@ -191,6 +204,11 @@ static inline bool tick_nohz_full_enabled(void)
* the cpu expression (typically smp_processor_id()) _after_ the static
* key.
*/
+/*
+ * IAMROOT, 2023.03.18:
+ * - nohz full이 enable이 되있고, @_cpu가 tick_nohz_full_mask에 포함된다면
+ * return true.
+ */
#define tick_nohz_full_cpu(_cpu) ({ \
bool __ret = false; \
if (tick_nohz_full_enabled()) \
diff --git a/kernel/pid.c b/kernel/pid.c
index efe87db44683..3ecc8a0c29ef 100644
--- a/kernel/pid.c
+++ b/kernel/pid.c
@@ -45,6 +45,10 @@
#include <net/sock.h>
#include <uapi/linux/pidfd.h>
+/*
+ * IAMROOT, 2023.03.18:
+ * - numbers를 tag찍고 들어가면 struct pid가 보인다.
+ */
struct pid init_struct_pid = {
.count = REFCOUNT_INIT(1),
.tasks = {
diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c
index d04237de9672..296103876142 100644
--- a/kernel/power/suspend.c
+++ b/kernel/power/suspend.c
@@ -83,9 +83,7 @@ static DECLARE_SWAIT_QUEUE_HEAD(s2idle_wait_head);
*
* S2idle 상태는 저전력 유휴 상태로, 시스템이 전력을 절약하면서 사용자 입력에
* 일정 수준의 응답성을 유지합니다. 이 상태에서 시스템은 사용자 활동에 응답하여
- * 전체 전원 모드로 빠르게 다시 전환할 수 있습니다. 이것은 suspend-to-RAM 또는
- * suspend-to-disk와 같은 더 깊은 유휴 상태와는 대조적으로, 깨우고 정상 작동을
- * 재개하는 데 더 오랜 시간이 필요합니다.
+ * 전체 전원 모드로 빠르게 다시 전환할 수 있습니다.
*
* S2Idle(suspend-to-idle) 기능의 세 가지 가능한 상태를 정의합니다.
* 이 기능은 웨이크업 시간이 빠른 최신 시스템에 최적화된 저전력 절전
@@ -107,8 +105,104 @@ static DECLARE_SWAIT_QUEUE_HEAD(s2idle_wait_head);
* 나가는 적절한 작업을 수행할 수 있을 뿐만 아니라 시스템이 S2Idle 상태에
* 있는 동안 발생하는 인터럽트 및 기타 이벤트를 처리할 수 있습니다.
*
+ * ---- Documentation/admin-guide/pm/sleep-states.rst ----
+ * - s2idle (suspend-to-idle)
+ * 이는 일반, 순수 소프트웨어, 시스템 일시 중단의 경량
+ * 변형입니다(S2I 또는 S2Idle이라고도 함). 사용자 공간을 동결하고 시간
+ * 기록을 중단하고 모든 I/O 장치를 저전력 상태(작동 상태에서 사용 가능한
+ * 것보다 더 낮은 전력)로 전환하여 런타임 유휴 상태에 비해 더 많은
+ * 에너지를 절약할 수 있습니다. 시스템이 일시 중단된 동안 가장 깊은
+ * 유휴 상태에 있는 시간입니다.
+ *
+ * 시스템은 대역 내 인터럽트에 의해 이 상태에서 깨어나므로 이론적으로
+ * 작동 상태에서 인터럽트를 생성할 수 있는 모든 장치를 S2Idle용 웨이크업
+ * 장치로 설정할 수 있습니다.
+ *
+ * 이 상태는 :ref:`standby <standby>` 또는 :ref:`suspend-to-RAM <s2ram>`을
+ * 지원하지 않는 플랫폼에서 사용할 수 있습니다. 재개 대기 시간 감소를
+ * 제공합니다. :c:macro:`CONFIG_SUSPEND` 커널 구성 옵션이 설정되어 있으면
+ * 항상 지원됩니다.
+ *
+ * - s2ram (suspend-to-ram)
+ * 이 상태(STR 또는 S2RAM이라고도 함)는 지원되는 경우 메모리를 제외하고
+ * 시스템의 모든 것이 저전력 상태로 전환되므로 상당한 에너지 절감 효과를
+ * 제공합니다. 내용물. 대기 <대기>'에 들어갈 때 수행되는 모든 단계는
+ * S2RAM으로 전환하는 동안에도 수행됩니다. 플랫폼 기능에 따라 추가 작업이
+ * 발생할 수 있습니다. 특히, ACPI 기반 시스템에서 커널은 S2RAM 전환 중
+ * 마지막 단계로 플랫폼 펌웨어(BIOS)에 제어권을 넘기고 그 결과 일반적으로
+ * 커널이 직접 제어하지 않는 일부 하위 수준 구성 요소의 전원이 꺼집니다.
+ *
+ * 장치 및 CPU의 상태는 메모리에 저장되고 유지됩니다. 모든 장치가 일시
+ * 중단되고 저전력 상태로 전환됩니다. 많은 경우에 모든 주변 장치 버스는
+ * S2RAM에 진입할 때 전력이 손실되므로 장치는 "켜짐" 상태로의 전환을
+ * 처리할 수 있어야 합니다.
+ *
+ * ACPI 기반 시스템에서 S2RAM은 플랫폼 펌웨어에서 시스템을 재개하기 위해
+ * 최소한의 부트스트래핑 코드가 필요합니다. 다른 플랫폼에서도 마찬가지일
+ * 수 있습니다.
+ *
+ * S2RAM에서 시스템을 깨울 수 있는 장치 세트는 일반적으로 일시 중지에서
+ * 유휴 <s2idle>` 및 대기 <standby>`에 비해 줄어들며 플랫폼에 의존해야 할
+ * 수도 있습니다. 웨이크업 기능을 적절하게 설정하기 위해.
+ *
+ * S2RAM은 :c:macro:`CONFIG_SUSPEND` 커널 구성 옵션이 설정되고 이에 대한
+ * 지원이 코어 시스템 일시 중지 하위 시스템이 있는 플랫폼에서 등록된 경우
+ * 지원됩니다. ACPI 기반 시스템에서는 ACPI에서 정의한 S3 시스템 상태에
+ * 매핑됩니다.
+ *
+ * - standby
+ * 지원되는 경우 이 상태는 작동 상태로의 비교적 간단한 전환을 제공하면서
+ * 중간 정도의 실질적인 에너지 절감 효과를 제공합니다. 작동 상태가
+ * 손실되지 않으므로(시스템 코어 로직이 전원을 유지함) 시스템이 중단된
+ * 위치로 충분히 쉽게 돌아갈 수 있습니다. 사용자 공간 정지, 시간 기록
+ * 일시 중단 및 모든 I/O 장치를 저전력 상태로 전환하는 것 외에도 유휴
+ * 일시 중단 <s2idle>`에 대해서도 수행되며 부팅되지 않는 CPU는 오프라인
+ * 상태가 되며 모두 로우 상태가 됩니다. -레벨 시스템 기능은 이 상태로
+ * 전환하는 동안 일시 중지됩니다. 이러한 이유로 일시 중단에서 유휴
+ * <s2idle>`에 비해 더 많은 에너지를 절약할 수 있어야 하지만 재개 대기
+ * 시간은 일반적으로 해당 상태보다 큽니다.
+ *
+ * 이 상태에서 시스템을 깨울 수 있는 장치 집합은 일반적으로
+ * 'suspend-to-idle <s2idle>'에 비해 줄어들며 깨우기 기능을 적절하게
+ * 설정하기 위해 플랫폼에 의존해야 할 수도 있습니다.
+ *
+ * 이 상태는 :c:macro:`CONFIG_SUSPEND` 커널 구성 옵션이 설정되고 이에
+ * 대한 지원이 코어 시스템 일시 중지 하위 시스템이 있는 플랫폼에서 등록된
+ * 경우에 지원됩니다. ACPI 기반 시스템에서 이 상태는 ACPI에서 정의한 S1
+ * 시스템 상태에 매핑됩니다.
+ *
+ * - 절전 진입 및 복귀 속도
+ * s2idle > standby > s2ram > s2disk
+ * --- sysfs -------
+ * - sysfs에서 확인 및 진입방법
+ * - s2idle
+ * /sys/power/state freeze
+ * /sys/power/mem_sleep s2idle 로 변경 후 /sys/power/state mem 입력.
+ * - standby
+ * /sys/power/state standby
+ * /sys/power/mem_sleep shallow 로 변경 후 /sys/power/state mem 입력.
+ * - s2ram
+ * /sys/power/mem_sleep deep 로 변경후 /sys/power/state mem 입력.
+ * - s2disk
+ * /sys/power/state disk
+ * /sys/power/disk에 선택된 설정이 동작한다.
+ * (shutdown, reboot, suspend, test_resume)
+ * - 사용예
+ * 1.
+ * KVM /sys/power$ echo reboot > disk
+ * KVM /sys/power$ cat disk
+ * shutdown [reboot] suspend test_resume
+ * 2.
+ * KVM /sys/power$ echo disk > state
+ * [ 87.915123] PM: hibernation: hibernation entry
+ *
+ * 3.
+ * KVM /sys/power$ cat mem_sleep
+ * [s2idle]
+ * KVM /sys/power$ echo mem > state
+ * [ 153.478136] PM: suspend entry (s2idle)
+ * ----------------------------------------------------------
*
- * --------------------
*/
enum s2idle_states __read_mostly s2idle_state;
static DEFINE_RAW_SPINLOCK(s2idle_lock);
diff --git a/kernel/sched/clock.c b/kernel/sched/clock.c
index e5d41fee25f9..7feadd0fb4d2 100644
--- a/kernel/sched/clock.c
+++ b/kernel/sched/clock.c
@@ -441,6 +441,10 @@ EXPORT_SYMBOL_GPL(sched_clock_idle_sleep_event);
/*
* We just idled; resync with ktime.
*/
+/*
+ * IAMROOT, 2023.03.18:
+ * - arm64는 stable clock이다. return.
+ */
void sched_clock_idle_wakeup_event(void)
{
unsigned long flags;
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index 30960a642312..5ecb977702d6 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -7228,6 +7228,20 @@ EXPORT_SYMBOL(schedule);
* schedule_idle() is similar to schedule_preempt_disable() except that it
* never enables preemption because it does not call sched_submit_work().
*/
+/*
+ * IAMROOT, 2023.03.18:
+ * - papago
+ * synchronize_rcu_tasks()는 모든 작업이 실행 대기열을 떠났거나 사용자
+ * 공간으로 이동했는지 확인하여 어떤 작업도 선점된 상태(비자발적으로
+ * 예약됨)에 걸리지 않도록 합니다.
+ * 유휴 작업은 어느 쪽도 수행하지 않으므로 선점되어서는 안
+ * 됩니다(비자발적으로 스케줄 아웃).
+ *
+ * schedule_idle()은 sched_submit_work()를 호출하지 않기 때문에 선점을
+ * 활성화하지 않는다는 점을 제외하면 schedule_preempt_disable()과 유사합니다.
+ *
+ * - 자발적 schedule을 뜻하는 SM_NONE을 들고 schedule한다.
+ */
void __sched schedule_idle(void)
{
/*
@@ -7237,6 +7251,14 @@ void __sched schedule_idle(void)
* current task can be in any other state. Note, idle is always in the
* TASK_RUNNING state.
*/
+/*
+ * IAMROOT, 2023.03.18:
+ * - papago
+ * 이것은 작업이 TASK_RUNNING 상태일 때 해당 함수가 nop이기 때문에 유휴
+ * 작업이 수행하는 sched_submit_work() 호출을 건너뛰기 때문에 현재 작업이
+ * 다른 상태에 있을 수 있는 곳에서 사용되지 않는지 확인하세요. 유휴 상태는
+ * 항상 TASK_RUNNING 상태입니다.
+ */
WARN_ON_ONCE(current->__state);
do {
__schedule(SM_NONE);
diff --git a/kernel/sched/idle.c b/kernel/sched/idle.c
index 202a61e9a1ab..687882b8f330 100644
--- a/kernel/sched/idle.c
+++ b/kernel/sched/idle.c
@@ -17,6 +17,10 @@ extern char __cpuidle_text_start[], __cpuidle_text_end[];
* sched_idle_set_state - Record idle state for the current CPU.
* @idle_state: State to record.
*/
+/*
+ * IAMROOT, 2023.03.18:
+ * - this rq의 현재 idle state를 @idle_state로 설정한다.
+ */
void sched_idle_set_state(struct cpuidle_state *idle_state)
{
idle_set_state(this_rq(), idle_state);
@@ -55,6 +59,10 @@ void sched_idle_set_state(struct cpuidle_state *idle_state)
*/
static int __read_mostly cpu_idle_force_poll;
+/*
+ * IAMROOT, 2023.03.18:
+ * - cpuidle을 poll force 하기 위한 enalbe / disable
+ */
void cpu_idle_poll_ctrl(bool enable)
{
if (enable) {
@@ -66,6 +74,10 @@ void cpu_idle_poll_ctrl(bool enable)
}
#ifdef CONFIG_GENERIC_IDLE_POLL_SETUP
+/*
+ * IAMROOT, 2023.03.18:
+ * - cpuidle을 poll force 시킨다.
+ */
static int __init cpu_idle_poll_setup(char *__unused)
{
cpu_idle_force_poll = 1;
@@ -74,6 +86,10 @@ static int __init cpu_idle_poll_setup(char *__unused)
}
__setup("nohlt", cpu_idle_poll_setup);
+/*
+ * IAMROOT, 2023.03.18:
+ * - cpuidle의 poll force를 disable 한다.
+ */
static int __init cpu_idle_nopoll_setup(char *__unused)
{
cpu_idle_force_poll = 0;
@@ -111,6 +127,11 @@ void __weak arch_cpu_idle_prepare(void) { }
void __weak arch_cpu_idle_enter(void) { }
void __weak arch_cpu_idle_exit(void) { }
void __weak arch_cpu_idle_dead(void) { }
+
+/*
+ * IAMROOT, 2023.03.18:
+ * - idle을 한다해도 deep sleep 하지 않기 위한 방식.
+ */
void __weak arch_cpu_idle(void)
{
cpu_idle_force_poll = 1;
@@ -204,7 +225,8 @@ void __cpuidle default_idle_call(void)
/*
* IAMROOT, 2023.03.17:
- * - ING
+ * - reschedule 요청있으면 return busy.
+ * 그게 아니면 deep sleep을 수행한다.
*/
static int call_cpuidle_s2idle(struct cpuidle_driver *drv,
struct cpuidle_device *dev)
@@ -215,6 +237,11 @@ static int call_cpuidle_s2idle(struct cpuidle_driver *drv,
return cpuidle_enter_s2idle(drv, dev);
}
+/*
+ * IAMROOT, 2023.03.18:
+ * - reschedule 요청이 있으면 잠들지 않고 return busy.
+ * @next_state해 해당하는 idle을 수행한다.
+ */
static int call_cpuidle(struct cpuidle_driver *drv, struct cpuidle_device *dev,
int next_state)
{
@@ -222,6 +249,10 @@ static int call_cpuidle(struct cpuidle_driver *drv, struct cpuidle_device *dev,
* The idle task must be scheduled, it is pointless to go to idle, just
* update no idle residency and return.
*/
+/*
+ * IAMROOT, 2023.03.18:
+ * - reschedule 요청이 있으면 return busy
+ */
if (current_clr_polling_and_test()) {
dev->last_residency_ns = 0;
local_irq_enable();
@@ -233,6 +264,16 @@ static int call_cpuidle(struct cpuidle_driver *drv, struct cpuidle_device *dev,
* This function will block until an interrupt occurs and will take
* care of re-enabling the local interrupts
*/
+
+/*
+ * IAMROOT, 2023.03.18:
+ * - papago
+ * governor 결정에 의해 이전에 반환된 유휴 상태를 입력합니다.
+ * 이 기능은 인터럽트가 발생할 때까지 차단되며 로컬 인터럽트를 다시
+ * 활성화합니다.
+ *
+ * - @next_state해 해당하는 idle을 수행한다.
+ */
return cpuidle_enter(drv, dev, next_state);
}
@@ -257,6 +298,7 @@ static int call_cpuidle(struct cpuidle_driver *drv, struct cpuidle_device *dev,
*/
/*
* IAMROOT, 2023.03.11:
+ * - devicetree/bindings/arm/idle-states.yaml 참고
* - min-residency-us : 최소 상주 시간. 켜진후 꺼지기 전까지 최소 유지해야할 시간
* - rk3399.rtsi
* idle-states {
@@ -281,6 +323,14 @@ static int call_cpuidle(struct cpuidle_driver *drv, struct cpuidle_device *dev,
* };
* };
*
+ * - 1. resched 요청이 있으면 irq enable후 return.
+ * 2. cpuidle driver가 없으면 default idle(wfi) 수행후 return.
+ * 3. suspend-to-idle상황인 경우 s2idle을 지원하는 cpuidle state를 선택해서
+ * idle 수행.
+ * 4. forced_idle_latency_limit_ns가 있는 경우 이 값을 max로 하여 cpuidle state
+ * 를 선택해서 idle 수행.
+ * 5. 일반적인 경우(윗 상황 들이 아닌 경우) curr governor를 통해서 cpuidle state
+ * 를 찾아서 idle 수행.
*/
static void cpuidle_idle_call(void)
{
@@ -313,7 +363,7 @@ static void cpuidle_idle_call(void)
* - google-translate
* RCU 프레임워크는 우리가 유휴 섹션에 들어가고 있다는 것을 알려야 합니다. 따라서
* rcu는 더 이상 중요한 섹션을 읽지 않고 유예 기간에 한 단계 더 있습니다.
- * - cpuidle이 활성화 안된경우 wfi만 한번하고 끝낸다.
+ * - cpuidle이 활성화 안됫거나 driver가 없는경우 wfi만 한번하고 끝낸다.
*/
if (cpuidle_not_available(drv, dev)) {
@@ -354,17 +404,31 @@ static void cpuidle_idle_call(void)
if (idle_should_enter_s2idle()) {
+/*
+ * IAMROOT, 2023.03.18:
+ * - deep sleep을 수행한다. 수행이 됬으면 goto exit_idle.
+ * 그게 아니면 최대 sleep으로 cpuidle state를 검색한다.
+ */
entered_state = call_cpuidle_s2idle(drv, dev);
if (entered_state > 0)
goto exit_idle;
max_latency_ns = U64_MAX;
} else {
+
+/*
+ * IAMROOT, 2023.03.18:
+ * - forced_idle_latency_limit_ns이하의 cpuidle state로 검색한다.
+ */
max_latency_ns = dev->forced_idle_latency_limit_ns;
}
tick_nohz_idle_stop_tick();
+/*
+ * IAMROOT, 2023.03.18:
+ * - max_latency_ns이내의 c state를 골라서 idle을 수행한다.
+ */
next_state = cpuidle_find_deepest_state(drv, dev, max_latency_ns);
call_cpuidle(drv, dev, next_state);
} else {
@@ -373,6 +437,12 @@ static void cpuidle_idle_call(void)
/*
* Ask the cpuidle framework to choose a convenient idle state.
*/
+
+/*
+ * IAMROOT, 2023.03.18:
+ * - curr governor에서 c state를 구해온다. 만약 tick을 멈추지 말아야되면 stop_tick
+ * 이 false로 update되있을 것이다.
+ */
next_state = cpuidle_select(drv, dev, &stop_tick);
if (stop_tick || tick_nohz_tick_stopped())
@@ -380,6 +450,10 @@ static void cpuidle_idle_call(void)
else
tick_nohz_idle_retain_tick();
+/*
+ * IAMROOT, 2023.03.18:
+ * - governor에서 선택된 state로 cpuidle을 수행한다.
+ */
entered_state = call_cpuidle(drv, dev, next_state);
/*
* Give the governor an opportunity to reflect on the outcome
@@ -408,6 +482,14 @@ static void cpuidle_idle_call(void)
* 일반 유휴 루프 구현
*
* 폴링이 지워진 상태로 호출됩니다.
+ *
+ * - 0. resched 요청이 있으면 idle에 진입안하고 자발적 schedule을 수행하러간다.,
+ * 1. cpu가 offline이라면 cpu die로 진입한다.
+ * 2. polling을 해야된다면 cpu_relax()를 polling을 해야되는 원인이 풀릴때까지
+ * 수행한다.
+ * 3. 그게 아니면 cpuidle driver를 사용해서 idle을 수행한다.
+ * 4. idle이 끝난후 pending된것들을 처리한다.
+ * 5. 그 후 자발적 reschedule한다.
*/
static void do_idle(void)
{
@@ -481,6 +563,10 @@ static void do_idle(void)
if (cpu_idle_force_poll || tick_check_broadcast_expired()) {
tick_nohz_idle_restart_tick();
cpu_idle_poll();
+/*
+ * IAMROOT, 2023.03.18:
+ * - poll이 아니면 cpuidle state를 선택해 idle을 수행한다.
+ */
} else {
cpuidle_idle_call();
}
@@ -501,6 +587,8 @@ static void do_idle(void)
* 것을 알고 이를 PREEMPT_NEED_RESCHED로 전파합니다.
*
* 이는 유휴 루프를 폴링하기 위해 상태를 접을 IPI가 없기 때문에 필요합니다.
+ *
+ * - idle이 끝나서 resched을 한번 한다.
*/
preempt_set_need_resched();
tick_nohz_idle_exit();
@@ -511,12 +599,25 @@ static void do_idle(void)
* need_resched() is set while polling is set. That means that clearing
* polling needs to be visible before doing these things.
*/
+/*
+ * IAMROOT, 2023.03.18:
+ * - papago
+ * 폴링이 설정되어 있는 동안 need_resched()가 설정되면 sched_ttwu_pending()을
+ * 호출하고 일정을 변경할 것을 약속합니다. 이는 이러한 작업을 수행하기 전에 폴링
+ * 지우기가 표시되어야 함을 의미합니다.
+ */
smp_mb__after_atomic();
/*
* RCU relies on this call to be done outside of an RCU read-side
* critical section.
*/
+/*
+ * IAMROOT, 2023.03.18:
+ * - papgo
+ * RCU는 RCU 읽기측 중요 섹션 외부에서 수행되는 이 호출에 의존합니다.
+ * - 여러 pending에 대해(softirq등) 처리한다.
+ */
flush_smp_call_function_from_idle();
schedule_idle();
@@ -587,6 +688,8 @@ EXPORT_SYMBOL_GPL(play_idle_precise);
* - boot up 마지막에서 호출된다.
* cpu 0 : start_kernel -> arch_call_rest_init -> rest_init 에서 호출
* 그외 cpu : secondary_start_kernel 에서 호출
+ * - idle로 진입전 online이 되기 위한 @state처리를 하고 idle을 수행하며
+ * resched을 기다린다.
*/
void cpu_startup_entry(enum cpuhp_state state)
{
@@ -601,6 +704,7 @@ void cpu_startup_entry(enum cpuhp_state state)
*/
#ifdef CONFIG_SMP
+
static int
select_task_rq_idle(struct task_struct *p, int cpu, int flags)
{
@@ -634,6 +738,14 @@ static void set_next_task_idle(struct rq *rq, struct task_struct *next, bool fir
}
#ifdef CONFIG_SMP
+
+/*
+ * IAMROOT, 2023.03.18:
+ * - start_kernel()->rest_init()->kernel_init thread생성
+ * kernel_init -> kernel_init_freeable() -> smp_init()->
+ * idle_threads_init()->idle_init() - cpu for돌면서 fork_idle()->init_idle()에서
+ * idle이 설정됬다. 즉 booting cpu에 의해 각 cpu마다 idle task가 한개씩 생성된다.
+ */
static struct task_struct *pick_task_idle(struct rq *rq)
{
return rq->idle;
diff --git a/kernel/smp.c b/kernel/smp.c
index 73b0ed941f05..a4ce3363b00f 100644
--- a/kernel/smp.c
+++ b/kernel/smp.c
@@ -681,6 +681,11 @@ static void flush_smp_call_function_queue(bool warn_cpu_offline)
smp_processor_id(), CFD_SEQ_HDLEND);
}
+/*
+ * IAMROOT, 2023.03.18:
+ * - softirq가 pending되있었다면 실행한다.
+ * - TODO
+ */
void flush_smp_call_function_from_idle(void)
{
unsigned long flags;
diff --git a/kernel/time/hrtimer.c b/kernel/time/hrtimer.c
index 4832c3532528..48bd75228e8a 100644
--- a/kernel/time/hrtimer.c
+++ b/kernel/time/hrtimer.c
@@ -1302,6 +1302,10 @@ void clock_was_set_delayed(void)
* or in the case of s2idle from tick_unfreeze() to ensure that the
* hrtimers are up to date.
*/
+/*
+ * IAMROOT, 2023.03.18:
+ * - hrtimer retrigger
+ */
void hrtimers_resume_local(void)
{
lockdep_assert_irqs_disabled();
diff --git a/kernel/time/sched_clock.c b/kernel/time/sched_clock.c
index c8dbdf6b0888..7d9b11e55ae9 100644
--- a/kernel/time/sched_clock.c
+++ b/kernel/time/sched_clock.c
@@ -405,6 +405,8 @@ void __init generic_sched_clock_init(void)
* 이 함수는 sched_clock()의 크리티컬 섹션에서만 호출해야 합니다.
* 'epoch_cyc'의 올바른 복사본을 관찰하기 위해 중요한 섹션 끝에 있는
* read_seqcount_retry()에 의존합니다.
+ *
+ * - suspend시 사용하는 clock read.
*/
static u64 notrace suspended_sched_clock_read(void)
{
@@ -413,6 +415,11 @@ static u64 notrace suspended_sched_clock_read(void)
return cd.read_data[seq & 1].epoch_cyc;
}
+/*
+ * IAMROOT, 2023.03.18:
+ * - sched clock timer를 멈추면서 callback을 suspended_sched_clock_read로
+ * 바꾼다.
+ */
int sched_clock_suspend(void)
{
struct clock_read_data *rd = &cd.read_data[0];
@@ -424,6 +431,11 @@ int sched_clock_suspend(void)
return 0;
}
+/*
+ * IAMROOT, 2023.03.18:
+ * - sched clock timer를 다시 동작시키면서 callback을
+ * cd.actual_read_sched_clock로 변경한다.
+ */
void sched_clock_resume(void)
{
struct clock_read_data *rd = &cd.read_data[0];
diff --git a/kernel/time/tick-broadcast.c b/kernel/time/tick-broadcast.c
index cfab35393753..78a11fc4f26c 100644
--- a/kernel/time/tick-broadcast.c
+++ b/kernel/time/tick-broadcast.c
@@ -1003,6 +1003,10 @@ static void broadcast_shutdown_local(struct clock_event_device *bc,
clockevents_switch_state(dev, CLOCK_EVT_STATE_SHUTDOWN);
}
+/*
+ * IAMROOT, 2023.03.18:
+ * - TODO
+ */
static int ___tick_broadcast_oneshot_control(enum tick_broadcast_state state,
struct tick_device *td,
int cpu)
@@ -1141,6 +1145,16 @@ static int ___tick_broadcast_oneshot_control(enum tick_broadcast_state state,
return ret;
}
+/*
+ * IAMROOT, 2023.03.18:
+ * @return < 0 : @td나 wd둘중하나이상이 oneshot이 아니다.
+ * 0 : 아래 주석 참고
+ * - TICK_BROADCAST_ENTER
+ * @dev의 oneshot은 정지, @wd는 oneshot으로 하여 next_event로 program한다.
+ * 즉 @dev의 next_event시간에 @cpu를 깨우두록 @wd에서 요청을 한다.
+ * - TICK_BROADCAST_EXIT
+ * @wd가 oneshot이 아닌지만 확인한다.
+ */
static int tick_oneshot_wakeup_control(enum tick_broadcast_state state,
struct tick_device *td,
int cpu)
@@ -1170,14 +1184,31 @@ static int tick_oneshot_wakeup_control(enum tick_broadcast_state state,
return 0;
}
+/*
+ * IAMROOT, 2023.03.18:
+ * @return 0 : 성공
+ *
+ * - @state가 TICK_BROADCAST_ENTER일 경우 this cpu가 next_event에 일어나도록
+ * oneshot wakeup device에 맡겨 깨우도록 한다.
+ * wakeup device가 없거나 oneshot이 아닌 경우 braodcast를 통해서 시도해본다.
+ */
int __tick_broadcast_oneshot_control(enum tick_broadcast_state state)
{
struct tick_device *td = this_cpu_ptr(&tick_cpu_device);
int cpu = smp_processor_id();
+/*
+ * IAMROOT, 2023.03.18:
+ * - @state가 TICK_BROADCAST_ENTER인 경우 wakeup device에 next_event에
+ * this cpu가 깨우도록 한다.
+ */
if (!tick_oneshot_wakeup_control(state, td, cpu))
return 0;
+/*
+ * IAMROOT, 2023.03.18:
+ * - 위에서 실행이 안된경우. 즉 oneshot이 아닌 경우 braodcast로 동작한다.
+ */
if (tick_broadcast_device.evtdev)
return ___tick_broadcast_oneshot_control(state, td, cpu);
@@ -1185,6 +1216,10 @@ int __tick_broadcast_oneshot_control(enum tick_broadcast_state state)
* If there is no broadcast or wakeup device, tell the caller not
* to go into deep idle.
*/
+/*
+ * IAMROOT, 2023.03.18:
+ * - broadcast도 아니고 wakeup oneshot device도 아닌 경우.
+ */
return -EBUSY;
}
diff --git a/kernel/time/tick-common.c b/kernel/time/tick-common.c
index 9da300c1e1cf..37896f5c3358 100644
--- a/kernel/time/tick-common.c
+++ b/kernel/time/tick-common.c
@@ -664,6 +664,21 @@ void tick_check_new_device(struct clock_event_device *newdev)
* required here because the local clock event device cannot go away
* under us.
*/
+/*
+ * IAMROOT, 2023.03.18:
+ * - papago
+ * tick_broadcast_oneshot_control - 브로드캐스트 원샷 모드 시작/종료
+ * @state: 대상 상태(들어가기/나가기) 시스템은 영향을 받는 장치가 중지될
+ * 수 있는 상태에 들어가거나 나갑니다. 성공 시 0을 반환하고, CPU가 웨이크업을
+ * 브로드캐스트하는 데 사용되는 경우 -EBUSY를 반환합니다.
+ *
+ * 인터럽트가 비활성화된 상태에서 호출되므로 로컬 시계 이벤트 장치가 우리
+ * 아래에서 사라질 수 없기 때문에 clockevents_lock이 필요하지 않습니다.
+ *
+ * - @state가 TICK_BROADCAST_ENTER이라면 C3STOP을 지원 한다면 정지를 시킨후
+ * oneshot wakeup device에 next_event에 this cpu를 깨우도록 맡긴다.
+ * 그게 아니면 braodcast에서 맡겨 본다.
+ */
int tick_broadcast_oneshot_control(enum tick_broadcast_state state)
{
struct tick_device *td = this_cpu_ptr(&tick_cpu_device);
@@ -721,6 +736,10 @@ void tick_shutdown(unsigned int cpu)
*
* No locks required. Nothing can change the per cpu device.
*/
+/*
+ * IAMROOT, 2023.03.18:
+ * - tick device를 멈춘다.
+ */
void tick_suspend_local(void)
{
struct tick_device *td = this_cpu_ptr(&tick_cpu_device);
@@ -735,12 +754,21 @@ void tick_suspend_local(void)
*
* No locks required. Nothing can change the per cpu device.
*/
+/*
+ * IAMROOT, 2023.03.18:
+ * - tick device enable.
+ */
void tick_resume_local(void)
{
struct tick_device *td = this_cpu_ptr(&tick_cpu_device);
bool broadcast = tick_resume_check_broadcast();
clockevents_tick_resume(td->evtdev);
+/*
+ * IAMROOT, 2023.03.18:
+ * - tick device는 처음에 periodic으로 동작하다 oneshot으로 변경된다.
+ * 그에 따른 처리들.
+ */
if (!broadcast) {
if (td->mode == TICKDEV_MODE_PERIODIC)
tick_setup_periodic(td->evtdev, 0);
@@ -798,11 +826,32 @@ static unsigned int tick_freeze_depth;
* Call with interrupts disabled. Must be balanced with %tick_unfreeze().
* Interrupts must not be enabled before the subsequent %tick_unfreeze().
*/
+/*
+ * IAMROOT, 2023.03.18:
+ * - papago
+ * tick_freeze - 로컬 틱 및 (아마도) 시간 기록을 중지합니다.
+ *
+ * 이것이 기능을 실행하는 마지막 온라인 CPU인지 확인하고 그렇다면
+ * 시간 기록을 일시 중단합니다. 그렇지 않으면 로컬 틱을 일시 중지합니다.
+ *
+ * 인터럽트가 비활성화된 상태에서 호출합니다. %tick_unfreeze()로 균형을
+ * 맞춰야 합니다.
+ *
+ * 후속 %tick_unfreeze() 전에 인터럽트를 활성화하면 안 됩니다.
+ *
+ * - 기본적으로 local tick device를 멈춰 sched tick을 정지시킨다.
+ * 만약 모든 cpu가 멈추게되면 system을 suspend한다.
+ */
void tick_freeze(void)
{
raw_spin_lock(&tick_freeze_lock);
tick_freeze_depth++;
+/*
+ * IAMROOT, 2023.03.18:
+ * - tick freez가 전체 cpu인경우 system을 suspend한다. 그게 아니면
+ * local cpu만 suspend한다.
+ */
if (tick_freeze_depth == num_online_cpus()) {
trace_suspend_resume(TPS("timekeeping_freeze"),
smp_processor_id(), true);
@@ -825,6 +874,11 @@ void tick_freeze(void)
* Call with interrupts disabled. Must be balanced with %tick_freeze().
* Interrupts must not be enabled after the preceding %tick_freeze().
*/
+/*
+ * IAMROOT, 2023.03.18:
+ * - 기본적으로 local tick device를 resume 하여 sched tick을 다시 start한다.
+ * 만약 모든 cpu가 freeze였으면 system resume한다.
+ */
void tick_unfreeze(void)
{
raw_spin_lock(&tick_freeze_lock);
diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c
index 82570425f7b7..47a0975b0eeb 100644
--- a/kernel/time/tick-sched.c
+++ b/kernel/time/tick-sched.c
@@ -725,12 +725,23 @@ static void tick_nohz_update_jiffies(ktime_t now)
/*
* Updates the per-CPU time idle statistics counters
*/
+/*
+ * IAMROOT, 2023.03.18:
+ * - @ts->idle_active에 iowait상황에 따른 시간 가산후 idle_entrytime을 갱신한다.
+ */
static void
update_ts_time_stats(int cpu, struct tick_sched *ts, ktime_t now, u64 *last_update_time)
{
ktime_t delta;
if (ts->idle_active) {
+/*
+ * IAMROOT, 2023.03.18:
+ * - iowait cpu
+ * before idle entrytime을 iowait_sleeptime에 가산
+ * - 그게 아니면
+ * before idle entrytime을 idle_sleeptime에 가산
+ */
delta = ktime_sub(now, ts->idle_entrytime);
if (nr_iowait_cpu(cpu) > 0)
ts->iowait_sleeptime = ktime_add(ts->iowait_sleeptime, delta);
@@ -744,6 +755,10 @@ update_ts_time_stats(int cpu, struct tick_sched *ts, ktime_t now, u64 *last_upda
}
+/*
+ * IAMROOT, 2023.03.18:
+ * - nohz 시스템에서 stop idle에 대한 사후 처리를 한다.
+ */
static void tick_nohz_stop_idle(struct tick_sched *ts, ktime_t now)
{
update_ts_time_stats(smp_processor_id(), ts, now, NULL);
@@ -845,6 +860,10 @@ u64 get_cpu_iowait_time_us(int cpu, u64 *last_update_time)
}
EXPORT_SYMBOL_GPL(get_cpu_iowait_time_us);
+/*
+ * IAMROOT, 2023.03.18:
+ * - tick nohz timer cancle후 restart.
+ */
static void tick_nohz_restart(struct tick_sched *ts, ktime_t now)
{
hrtimer_cancel(&ts->sched_timer);
@@ -864,6 +883,11 @@ static void tick_nohz_restart(struct tick_sched *ts, ktime_t now)
* Reset to make sure next tick stop doesn't get fooled by past
* cached clock deadline.
*/
+/*
+ * IAMROOT, 2023.03.18:
+ * - papago
+ * 다음 틱 중지가 캐시된 클록 데드라인을 지나서 속지 않도록 재설정하십시오.
+ */
ts->next_tick = 0;
}
@@ -1035,6 +1059,10 @@ static void tick_nohz_stop_tick(struct tick_sched *ts, int cpu)
}
}
+/*
+ * IAMROOT, 2023.03.18:
+ * - timer stop
+ */
static void tick_nohz_retain_tick(struct tick_sched *ts)
{
ts->timer_expires_base = 0;
@@ -1050,6 +1078,10 @@ static void tick_nohz_stop_sched_tick(struct tick_sched *ts, int cpu)
}
#endif /* CONFIG_NO_HZ_FULL */
+/*
+ * IAMROOT, 2023.03.18:
+ * - @now로 tick을 update 및 tick_nohz_restart.
+ */
static void tick_nohz_restart_sched_tick(struct tick_sched *ts, ktime_t now)
{
/* Update jiffies first */
@@ -1070,6 +1102,10 @@ static void tick_nohz_restart_sched_tick(struct tick_sched *ts, ktime_t now)
tick_nohz_restart(ts, now);
}
+/*
+ * IAMROOT, 2023.03.18:
+ * - 기본 config에 NO HZ FULL이 포함되잇진 않는다.
+ */
static void __tick_nohz_full_update_tick(struct tick_sched *ts,
ktime_t now)
{
@@ -1231,6 +1267,10 @@ void tick_nohz_idle_stop_tick(void)
__tick_nohz_idle_stop_tick(this_cpu_ptr(&tick_cpu_sched));
}
+/*
+ * IAMROOT, 2023.03.18:
+ * - tick nohz idle 에서 tick을 유지한다.
+ */
void tick_nohz_idle_retain_tick(void)
{
tick_nohz_retain_tick(this_cpu_ptr(&tick_cpu_sched));
@@ -1310,6 +1350,15 @@ bool tick_nohz_idle_got_tick(void)
*
* Called from power state control code with interrupts disabled
*/
+/*
+ * IAMROOT, 2023.03.18:
+ * - papago
+ * tick_nohz_get_next_hrtimer - 먼저 만료되는 hrtimer 또는 틱의 다음
+ * 만료 시간을 반환합니다. 틱이 중지된 경우 다음 hrtimer를 반환합니다.
+ * 인터럽트가 비활성화된 전원 상태 제어 코드에서 호출됩니다.
+ *
+ * - 가장 먼저 완료될 시간을 알아온다.
+ */
ktime_t tick_nohz_get_next_hrtimer(void)
{
return __this_cpu_read(tick_cpu_device.evtdev)->next_event;
@@ -1383,6 +1432,10 @@ unsigned long tick_nohz_get_idle_calls(void)
return ts->idle_calls;
}
+/*
+ * IAMROOT, 2023.03.18:
+ * - idle이 종료되는 시각을 기록한다.
+ */
static void tick_nohz_account_idle_time(struct tick_sched *ts,
ktime_t now)
{
@@ -1420,6 +1473,10 @@ void tick_nohz_idle_restart_tick(void)
}
}
+/*
+ * IAMROOT, 2023.03.18:
+ * - tick nohz restart를 수행한다.
+ */
static void tick_nohz_idle_update_tick(struct tick_sched *ts, ktime_t now)
{
if (tick_nohz_full_cpu(smp_processor_id()))
@@ -1437,6 +1494,16 @@ static void tick_nohz_idle_update_tick(struct tick_sched *ts, ktime_t now)
* This also exit the RCU extended quiescent state. The CPU
* can use RCU again after this function is called.
*/
+/*
+ * IAMROOT, 2023.03.18:
+ * - papago
+ * tick_nohz_idle_exit - 유휴 작업에서 유휴 틱을 다시 시작합니다.
+ * CPU가 유휴 상태에서 깨어날 때 유휴 틱을 다시 시작합니다.
+ * 이것은 또한 RCU 확장 정지 상태를 종료합니다. CPU는 이 함수가
+ * 호출된 후 RCU를 다시 사용할 수 있습니다.
+ *
+ * - idle이 끝나면서 nohz에서 해야될일을 수행한다.
+ */
void tick_nohz_idle_exit(void)
{
struct tick_sched *ts = this_cpu_ptr(&tick_cpu_sched);
diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c
index 74502c14b49c..9f69e82c96c9 100644
--- a/kernel/time/timekeeping.c
+++ b/kernel/time/timekeeping.c
@@ -1780,6 +1780,11 @@ void timekeeping_inject_sleeptime64(const struct timespec64 *delta)
/**
* timekeeping_resume - Resumes the generic timekeeping subsystem.
*/
+/*
+ * IAMROOT, 2023.03.18:
+ * - linux timer enable
+ * - PASS`
+ */
void timekeeping_resume(void)
{
struct timekeeper *tk = &tk_core.timekeeper;
@@ -1842,6 +1847,11 @@ void timekeeping_resume(void)
timerfd_resume();
}
+/*
+ * IAMROOT, 2023.03.18:
+ * - linux system이 관리하는 time을 멈춘다.
+ * - PASS
+ */
int timekeeping_suspend(void)
{
struct timekeeper *tk = &tk_core.timekeeper;
diff --git a/kernel/time/timer.c b/kernel/time/timer.c
index 1d2eb8409a90..2207154bed40 100644
--- a/kernel/time/timer.c
+++ b/kernel/time/timer.c
@@ -2267,6 +2267,10 @@ u64 get_next_timer_interrupt(unsigned long basej, u64 basem)
*
* Called with interrupts disabled
*/
+/*
+ * IAMROOT, 2023.03.18:
+ * - standard의 idle을 false로 하여 nohz를 푼다.
+ */
void timer_clear_idle(void)
{
struct timer_base *base = this_cpu_ptr(&timer_bases[BASE_STD]);
@@ -2277,6 +2281,13 @@ void timer_clear_idle(void)
* sending the IPI a few instructions smaller for the cost of taking
* the lock in the exit from idle path.
*/
+/*
+ * IAMROOT, 2023.03.18:
+ * - papago
+ * 우리는 이것을 잠금 해제합니다. 최악의 결과는 무의미한 IPI를 보내는 원격
+ * 인큐(enqueue)이지만, 잠금을 사용하면 유휴 경로에서 종료할 때 잠금을 사용하는
+ * 비용에 비해 IPI를 보내는 데 필요한 몇 가지 명령이 더 작아집니다.
+ */
base->is_idle = false;
}
#endif
댓글 0
번호 | 제목 | 글쓴이 | 날짜 | 조회 수 |
---|---|---|---|---|
공지 | [공지] 스터디 정리 노트 공간입니다. | woos | 2016.05.14 | 617 |
202 | [커널 18차] 106주차 | kkr | 2023.06.03 | 59 |
201 | [커널 20차] 3주차 | 김희찬 | 2023.06.03 | 69 |
200 | [커널 19차] 52 ~ 53 주차 | Min | 2023.05.27 | 54 |
199 | [커널 20차] 2주차 | 김희찬 | 2023.05.20 | 131 |
198 | [커널 19차] 51 주차 | Min | 2023.05.13 | 45 |
197 | [커널 20차] 1주차 | 김희찬 | 2023.05.13 | 182 |
196 | [커널 18차] 102주차 | kkr | 2023.05.07 | 70 |
195 | [커널 19차] 50 주차 | Min | 2023.05.07 | 31 |
194 | [커널 19차] 49 주차 | Min | 2023.04.29 | 55 |
193 | [커널 19차] 48 주차 | Min | 2023.04.23 | 83 |
192 | [커널 18차] 100주차 | kkr | 2023.04.22 | 82 |
191 | [커널 18차] 99주차 | kkr | 2023.04.16 | 75 |
190 | [커널 19차] 47 주차 | Min | 2023.04.15 | 41 |
189 | [커널 19차] 45, 46 주차 | Min | 2023.04.10 | 59 |
188 | [커널 19차] 43, 44 주차 | 이태백 | 2023.03.26 | 184 |
» | [커널 18차] 95주차 | kkr | 2023.03.20 | 119 |
186 | [커널 19차] 41 주차 | 이태백 | 2023.03.04 | 100 |
185 | [커널 18차] 93주차 | kkr | 2023.03.04 | 53 |
184 | [커널 18차] 91주차 | kkr | 2023.02.18 | 94 |
183 | [커널 19차] 39 주차 | Min | 2023.02.18 | 53 |
.