-
[리눅스 커널] PM - Linux legacy power managementLinux/kernel 2023. 3. 1. 00:40
글의 참고
- https://www.computerhope.com/unix/uhalt.htm
- http://www.wowotech.net/pm_subsystem/generic_pm_architecture.html
글의 전제
- 내가 글을 쓰다가 궁금한 점은 파란색 볼드체로 표현했다.
- 밑줄로 작성된 글은 좀 더 긴 설명이 필요해서 친 것이다.
글의 내용
- Overview
" 이 글에서는 PC 가 주력이던 시절에 리눅스 커널이 사용했던 `system power management` 방식에 대해 다룬다. 모바일이 시대에 도래하면서, 현재 리눅스 커널은 훨씬 더 강력하고 유연한 파워 매니지먼트 방식을 제공한다. 하지만, 레거시 방식(system power management) 또한 PC 에서 뿐만 아니라, Embedded 에서도 사용되고 있다. 심지어, 레거시 방식이 아직 까지도 가장 강력한 방식이기도 하다.
- 리눅스 시스템 전원 상태
" 리눅스 시스템에서 다양한 파워 매니지먼트 관련 동작, 상태, 모드들을 알아보자. 예를 들어, halt, power-off, shutdown, reboot, sleep(suspend), hibernate, hibernate-sleep. 리눅스에서는 2가지 종류의 파워 매니지먼트 동작/상태/모드로 구분할 수 있다.
1. Essential power management : halt, power-off, reboot, sleep
2. Advanced power management : sleep, hibernate, hybrid-sleep- Software architecture in linux kernel power management
" 아래 다이어그램은 리눅스 커널 reboot module 이 어떻게 구성되어 있는지를 보여준다. 기능적으로 `halt`, `reboot`, `shutdown`, `power-off` 4가지 동작에 초점을 맞춰져 있다.
Figure - Essential power management" 아래 다이어그램은 리눅스 커널 system power management 가 어떻게 구성되어 있는지를 보여준다. 기능적으로 `suspend`, `hibernate`, `wakeup source`, `autosleep` 4가지 동작에 초점을 맞춰져 있다(아래 block diagram 은 systme suspend flow 에 초점을 맞췄다. 다른 power state flow 는 생략되었음을 참고하자).
Figure - Advanced power management" 리눅스 커널 reboot modue & system power management 를 기준으로 커널의 파워 매니지먼트 레이어는 총 3 계층으로 나누어져 있다.
1. kernel layer - 기존 리눅스 커널 파워 매니지먼트의 핵심적인 기능을 담당하고 있으며, 하드웨어 디바이스가 아무 일도 하지 않을 경우, 시스템 전체를 suspend 및 hibernation 상태로 진입시키는 역할을 한다. `kernel/power/` 디렉토리에 위치한다.
2. driver layer - 리눅스 커널에서 제공하는 power management 관련 standard interface 를 구현하는 layer 다. external peripheral drivers 들은 주로 PM callbacks suspend & resume 을 구현하게 된다. on-chip SoC core drivers 들은 주로 syscore PM callbacks 을 구현한다. CPU architecture dependent drivers 는 각 CPU 제조사에서 제공하는 스펙에 맞게 실제 power management 작업을 진행하게 된다(arm 같은 경우 PSCI, x86 은 ACPI 등).
3. hardware layer - 실제 하드웨어 디바이스를 의미한다. CPU 제조사에서 제공하는 스펙을 보면, system suspend 시에 어떤 디바이스들이 먼저 off 되고, 어떤 정보들을 저장해야 하며, resume 시에는 어떻게 context 를 복구하는지가 자세히 명시되어 있다.- Essential power management
" 이 옵션에 속하는 동작들은 현재 동작하고 있는 모든 프로세스와 서비스를 종료시킨다. 시스템을 다시 시작하려면, 리부트하는 방법밖에 없다. 4개의 동작이 있는데, 다음과 같다.
1. Halt - 이 모드는 모든 프로세스와 서비스를 종료한다. 그리고 CPU를 셧다운시킨다. 그러나 시스템을 Power-off 하지는 않는다.
2. Power-off - 이 모드는 `Halt 모드 + 시스템 Power-off` 라고 보면 된다.
3. Shutdown - 이 모드는 `Power-off 모드 + 종료 전 필요한 작업 실행` 라고 볼 수 있다. 좀 더 구체적으로 말하면, 모든 프로세스와 서비스를 종료시키전에 특정 작업을 할 수 있다는 것이다. 예를 들어, 로깅이나 백업등을 시스템이 Power-off되기 전에 할 수 있다.
4. Reboot - 이 모드는 `Shutdown 모드 + 재시작`라고 보면 된다. Reboot은 하드웨어나 시스템에 영향을 끼치는 소프트웨어 변경 사항을 감지했거나 아무리 뭔짓을 해도 죽지 않는 프로세스나 서비를 죽일 때 사용한다." 위에서 `Halt` 는 시스템이 아닌, CPU 에게만 적용되는 명령어다. 실제 x86 에서 hlt 라는 명령어가 있다. 이 명령어는 프로세서를`HALT 상태로 전환시킨다. HALT 상태에서 resume 하기 위해서는 `NMI`, `SMI` `debug exception`, `BINIT#`, `INIT#` `RESET#` 시그널로 재개시킬 수 있다. 그 외에 인터럽트는 CPU 를 재개시킬 수 없다. 인텔같은 경우, `Intel Hyper-Threading` 을 지원하는 모델에서 HLT 명령어를 실행할 경우, HLT 명령어를 실행한 논리 프로세서만 HALT 상태가 된다. 예를 들어, 물리 프로세서안에 A, B 논리 프로세서가 존재할 때, A 논리 프로세서가 HLT 명령어를 실행하면 A 논리 프로세서만 HALT 상태가 된다. B 논리 프로세서는 계속 활성화상태로 남아있게 된다.
Stops instruction execution and places the processor in a HALT state. An enabled interrupt (including NMI and SMI), a debug exception, the BINIT# signal, the INIT# signal, or the RESET# signal will resume execution. If an interrupt (including NMI) is used to resume execution after a HLT instruction, the saved instruction pointer (CS:EIP) points to the instruction following the HLT instruction.
When a HLT instruction is executed on an Intel 64 or IA-32 processor supporting Intel Hyper-Threading Technology, only the logical processor that executes the instruction is halted. The other logical processors in the physical processor remain active, unless they are each individually halted by executing a HLT instruction.
The HLT instruction is a privileged instruction. When the processor is running in protected or virtual-8086 mode, the privilege level of a program or procedure must be 0 to execute the HLT instruction.
This instruction’s operation is the same in non-64-bit modes and 64-bit mode.
Intel® 64 and IA-32 Architectures Software Developer’s Manual Volume 2 (2A, 2B, 2C & 2D): Instruction Set Reference, A-Z [ HLT—Halt ]" HLT 명령어는 특권 명령어이며, HALT 상태에서 재개되었을 때, [CS:EIP]는 HLT 명령어가 실행되었던 다음 주소부터 실행을 재개한다. 그렇다면, x86 리눅스 커널에서는 HLT 명령어를 어디에 사용할까? 이름만 봐서는 CPUidle 에서 polling 용으로 사용할 것 같지만, 실제로는 ARM 의 WFI 와 동일하게 사용되기 때문에, spinlock 및 mutex 를 구현하는데 사용될 것으로 보인다.
x86 uses the more efficient HLT (halt) instruction. The equivalent instruction on ARM is WFI (wait for interrupt).
- 참고 : https://patchwork.kernel.org/project/linux-arm-kernel/patch/20170727120803.27848-1-wens@csie.org/" 참고로, intel 의 CPUidle polling 은 `rep; nop` 명령어를 사용한다.
// arch/x86/include/asm/vdso/processor.h - v6.5 /* REP NOP (PAUSE) is a good thing to insert into busy-wait loops. */ static __always_inline void rep_nop(void) { asm volatile("rep; nop" ::: "memory"); } static __always_inline void cpu_relax(void) { rep_nop(); }
" 시스템 Power-Off 는 정말로 시스템 전체(SoC) 를 Power-Off 시키게 된다. PMIC 가 SoC 에게 공급되는 모든 전력을 중지하라고 명령하는 것과 같다. PMIC 또한 시스템에 일부이기 때문에, SoC 를 Power-Off 시킨 뒤 자신도 Power-Off 가 된다. 그러나, SLEEP or DEEP-SLEEP 이 었다면, PMIC 는 Power-Off 되지 않는다. 그렇다면, shutdown 과 power-off 의 차이는 뭘까? reboot 은 또 어떤 차이가 있을까? 아래 그림을 보자.
" halt 를 직관적으로 판단해보면, 시스템을 잠깐 `중단` 시키는 것 처럼 보인다. 즉, 뭔가 멈춰있는 것처럼 보인다는 것이다. 그렇다면, shell 창에서 halt 명령어를 입력하고 wake-up 하는 방법은 뭘까? 안타깝게도 없다. 왜냐면, 아래 커널 코드에서도 볼 수 있다시피 secondary CPUs 들이 모두 power-off 되고, boot CPU 만 남은 시점에서 local interrupts 를 모두 disabled 하고, 무한 루프로 진입하기 때문에 wake-up 할 수 있는 방법이 없다[참고1].
// arch/arm64/kernel/process.c - v6.5 void machine_halt(void) { local_irq_disable(); smp_send_stop(); while (1); }
" halt, power-off, shutdown 중에 가장 추천하는 건 shutdown 이다. 왜냐면, 모든 서비스와 프로세스를 종료시키기 전에 종료 알림을 유저들에게 알려서, 사용중 인 리소스들을 정리할 수 있는 시간을 주기 때문이다.
" 모던 리눅스는 대부분의 `init process(first user process)` 가 systemd 다. systemd 를 통해서 halt, power-off, shutdown 기능을 사용하려면, 접두사로 `systemctl ` 을 입력해야 한다.
1. halt - systemctl halt
2. poweroff - systemctl poweroff
3. shutdown - systemctl poweroff
4. reboot - systemclt reboot" 특정 시간뒤에 `shutdown`을 할 수 도 있다.
#shutdown -P 03:44 " 03:44 AM에 `Power-off` #shutdown -H 10 " 10분뒤에 `Halt` #shutdown -r now " 즉각적으로 `Reboot`
- Advanced power management
" advanced power management 는 현재 실행중 인 시스템 상태를 저장하고 시스템이 다시 시작할 때 저장해놨던 상태를 통해 이전 상태를 복구시키는 타입의 power management 를 의미한다. 시스템은 저장해놨던 상태를 기준으로 복구되기 때문에, 다시 시작할 필요가 없다. Sleep, hibernate, hybrid-sleep 같은 것들이 advanced power management 이다.
1. Sleep (suspend or standby)
" sleep 모드는 `standby`, `suspend`, `suspend-to-ram` 라고 불린다. 현재 상태를 RAM 에 저장하고 RAM 을 제외한 나머지 모든 디바이스를 Power-off 한다. 시스템이 다시 깨어나면, 리눅스는 재부팅을 하는 대신 RAM 에 저장해놨던 이전 상태를 복구한다.
" 이 모드의 전제는 RAM 에 전원 공급이 계속 유지가 되어야 한다는 것이다(RAM retention 은 여기서 생략한다). 왜냐면, RAM 은 휘발성 메모리이기 때문에, 전원 공급이 차단되면 저장되어 있던 데이터도 사라지기 때문이다.
2. Hibernate
" 이 모드는 suspend-to-disk 라고 불린다. 이 모드는 시스템의 현재 상태를 디스크에 저장하고 시스템 전체를 Power-off 한다. 현재 상태가 디스크(비휘발성 메모리) 에 들어있기 때문에 배터리 나 외부 전원 공급에 의존할 필요가 없다.
" 이 모드는 시스템의 상태를 복구하는데 sleep mode 보다 더 많은 시간을 필요로 할 수 있지만, sleep mode 보다 훨씬 안정적이다.
3. Hybrid-sleep
" hybrid-sleep mode 는 sleep mode 와 hibernate mode 의 짬봉 모드다. hybrid-sleep mode 에서는 시스템의 현재 상태를 RAM 과 disk 에 모두에 저장한다. 두 곳에 모두 저장한 후, 램을 제외한 모든 디바이스를 Power-off 시킨다. 이 때 시스템이 Resume / Wakeup 되는 시나리오는 2 가지가 있다.
1. RAM에 전원 공급이 유지되고 있을 때 : 램에 저장되어 있는 이전에 저장한 상태를 복구해서 Resume/Wakeup 한다.
2. RAM에 전원 공급이 유지되고 있지 않을 때 : 디스크에 저장되어 있는 이전에 저장한 상태를 복구해서 Resume/Wakeup 한다." 이 모드의 전제는 램과 디스크에 동일한 상태가 저장되어 있어야 한다는 것이다. 왜냐면, 램을 선택해서 복구하든 디스크를 선택해서 복구하든 동일한 상태로 복구되어야 하기 때문이다. 참고로 아래의 명령어로 위의 상태들이 스위칭된다고 하는데, 테스트가 필요하다.
1. systemctl suspend - 시스템을 `suspend` 상태로 보낸다.
2. systemctl hibernate - 시스템을 `hibernate` 상태로 보낸다.
3. systemctl hybrid-sleep - 시스템을 `hybrid-sleep` 상태로 보낸다." 위의 3 개 중에 하나의 상태만 되더라도, extenal interrupt 가 발생하지 않는 이상 자동으로 시스템을 재개하는 것은 어렵다. 시스템이 위 3 가지 상태중에 하나의 상태로 들어가기전에 타이머를 설정하고 나왔다면 특정 시간뒤에 시스템을 재개할 수 도 있다. 타이머같은 디바이스들은 대개 슬립에 들어가지 않기 때문이다(broadcast timer). 혹은 external interface 를 통해 시스템을 재개할 수 도 있다. 예를 들면, power key button 가 같은 것들이 있다.
'Linux > kernel' 카테고리의 다른 글
[리눅스 커널] Loadable Kernel Module(LKM) (0) 2023.08.03 [리눅스 커널] PM - Power Management Interface (2) 2023.08.03 [리눅스 커널] PM - Wakeup events framework (2) 2023.08.03 [LINUX][KERNE][ISSUE] sysfs store() 무한으로 계속 write하는 이슈 (0) 2023.08.03 [리눅스 커널] Interrupt - GICv2 part 1 (0) 2023.02.24