[Linux:Kernel] Kernel 너무 일찍 죽을 때 Console Log message

1. __log_buf 가 kmsg log buffer. T32 attach 후 다음과 같이 dump 뜰 수 있다(__log_buf가 0xc060eaa4 일 때).

d.save.binary d:\log_ll.log 0xc060eaa4++0x200000

2. console이 Enable 되기 전에 죽는다면, Kernel hacking -> Kernel low-level debugging functions (read help!) (DEBUG_LL [=y]) 를 켜면 메시지를 볼 수 있다. 서브 메뉴의 UART 번호는 맞춰줘야 함!

[Linux] vmlinux -> Image

명령어만 덜렁 써놓기 뭐해서 설명을 덧붙인다.
가끔 undefined instrunction 예외가 날 때, 코드 메모리의 이상 여부를 확인해야 할 때가 있다.
커널은 알다시피 zImage를 Decompressed 하여 메모리상에 올린 후 실행하는데, 그 Decompressed Image가 Image 이다. Image는 커널의 빌드 과정에서 아래와 같이 objcopy를 이용해서 만든다. Makefile 을 참고해보면 알 수 있을 것이다. ARM의 경우는 arch/arm/boot/ 와 그 아래의 Makefile을 살펴보면 된다.
Android JB MR1의 경우 아래 경로의 prebuilt 된 툴체인을 사용한다.

prebuilts/gcc/linux-x86/arm/arm-eabi-4.6/bin/arm-eabi-objcopy -O binary -R .comment -S vmlinux arch/arm/boot/Image

나온 Image를 Trace32로 실제 커널 메모리를 dump 해서 Binary Diff 해보면 메모리의 H/W적인 이상여부나 코드 메모리를 어디서 건드려서 깨지진 않았는지 확인해 볼 수 있다.
커널 프로그램이 물리메모리 0x40008000과 같은 주소에 로딩되므로, 특정 모듈이나 특정 루틴의 경우 objcopy로 걸러낸 후, 해당 주소의 프로그램을 실제 메모리에서 offset을 뺀 값으로 Image파일에서 찾아야 한다.

Windows에서는 HxD와 같은 툴로 Binary Diff 할 수 있다. Beyond compare를 써도 되고(근데 유료라는 점)…

[Linux kernel] GPIO 정리

lwn.net의 GPIO in the kernel: an introductionGPIO in the kernel: future directions 를 보고 나름의 정리를 해둔다.

1. 현재 GPIO의 Kernel Internal API

GPIO를 가지고 뭔가 하기 위해서 다음 헤더 파일을 include 한다.


#include <linux/gpio.h>

현재의 커널은 모든 GPIO를 unsigned integer로 나타낸다. 그래서 platform data나 device tree를 통해 GPIO 번호를 넘기는 것이 가능하다.

GPIO는 사용 전에 allocation 되어야 한다. 현재 구현은 이를 강제하고 있지는 않다. allocation 은 다음 함수를 이용한다.


int gpio_reqeust(unsigned int gpio, const char *label);

label은 나중에 sysfs 로 나타날 수 있다. 반환값 0은 성공, 다른 값은 음수로 에러 번호를 반환한다. 다음을 통해 GPIO를 다시 돌려줄 수 있다.


void gpio_free(unsigned int gpio);

몇 가지 변형이 있는데 gpio_request_one() 은 초기 설정을 같이 할 수 있고, gpio_request_array() 는 몇 개의 GPIO를 한번에 설정하면서 request 할 수 있다. “managed” 버전(예를 들면, devm_gpio_request())은 개발자가 잊어버렸을 때 cleanup을 자동적으로 처리한단다(managed 버전이 있는 건 몰랐네…).

GPIO가 입력으로 사용되면,


int gpio_direction_input(unsigned int gpio);


출력으로 사용되면,


int gpio_direction_output(unsigned int gpio, int value);

output 일 때 value는 꼭 0 이나 1로 지정되어야 한다. 둘 다 성공 시에는 0을, 아니면 음수로 에러 번호를 반환한다.

GPIO 입력값을 읽을 때는,


int gpio_get_value(unsigned int gpio);

에러가 발생하려면 gpio_direction_input()을 호출했을 때 에러가 발생한다고 생각하기 때문에 별도 에러 체크는 하지 않는다. 그러므로 gpio_direction_input()의 반환값을 꼭 체크해야 한다.

GPIO 출력값을 설정할 때는 gpio_direction_output()을 사용할 수도 있지만, 이미 출력모드로 되어 있으면, 다음을 사용하는게 더 낫다.


void gpio_set_value(unsigned int gpio, int value);


몇몇 GPIO 컨트롤러는 GPIO 입력 값이 바뀌면 인터럽트를 걸어주는데, 이 IRQ 번호를 얻고 싶으면,


int gpio_to_irq(unsigned int gpio);

여기서의 gpio는 gpio_request() 로 꼭 얻어야 하고, gpio는 일단 입력 모드로 된다. irq 번호 대신 음수로 된 에러 번호가 반환될 수 있다. 반환된 irq는 request_irq()로 인터럽트 핸들러를 셋팅할 수 있다.

GPIO를 sysfs로 export 해서 user space에서 제어할 수 있도록 하려면,


int gpio_export(unsigned int gpio, bool direction_may_change);

direction_may_change는 보이는 것처럼, user space에서 direction을 바꿀 수 있는지 셋팅해주기 위한 것이다. 안하는 게 낫다. gpio_unexport() 로 sysfs를 없앨  수 있고, gpio_export_link() 를 사용하면 다른 이름으로 export 할 수도 있다.

더 많은 사항은 Documentation/gpio.txt 를 참고.

2. Descriptor-based API (gpiod_*)

현재의 GPIO API 가 integer를 사용하는데, 이 대신에 descriptor-based GPIO 인터페이스를 제공하는 방법이다. 이를 위해 struct gpio_desc * 포인터 타입을 사용한다. 이 인터페이스에 대한 반응은 꽤 좋다고 한다. 거의 곧 머지될 거 같다나..
현재 GPIO API와 매우 비슷하다.


#include <linux/gpio/consumer.h>

int gpiod_direction_input(struct gpio_desc *desc);
int gpiod_direction_direction_output(struct gpio_desc *desc, int value);
int gpiod_get_value(struct gpio_desc *desc);
void gpiod_set_value(struct gpio_desc *desc, int value);
int gpiod_to_irq(struct gpio_desc *desc);
int gpiod_export(struct gpio_desc *desc, bool direction_may_change);
int gpiod_export_link(struct device *dev, const char *name, struct gpio_desc *desc);
void gpiod_unexport(struct gpio_desc *desc);


gpio_ prefix 대신 gpiod_ prefix를 사용하고 GPIO 번호 integer대신 struct gpio_desc * 를 사용한다.

GPIO 번호로 descriptor를 얻을 수도 있다.


struct gpio_desc *gpio_to_desc(unsigned gpio);

반대인 desc_to_gpio() 도 있다. desc로 gpio 번호를 얻으면 포인터에서 얻어진 것이므로 현재의 기존 GPIO API에 안심하고 쓸 수 있다. 그러나 GPIO 번호로 descriptor를 얻는 건 별로 좋지 않다. 그래서 다음을 제공한다.


struct gpio_desc *gpiod_get(struct device *dev, const char *name);

dev는 GPIO 라인을 제공하는 device여야 하고, “name”은 라인을 말한다.
GPIO 라인 접근을 제거하기 위해서 gpiod_put() 도 제공한다.

3. Block GPIO

위에 설명한 것들은 개별 GPIO 라인을 관리하는데 초점을 둔다. 근데 GPIO들은 그룹으로 함께 쓰일 때가 많다.
어떤 하드웨어들은 또 한번의 I/O 메모리 write 연산으로 여러 라인을 동시에 바꿀 수 있다. 그래서 여러 GPIO를 하나의 그룹처럼 조작할 때, 하나의 블록으로 엮어서 사용하기 위해 Block GPIO 패치가 시도되었다. Descriptor-based GPIO API 에 비하면 별로 크게 다루어지지는 않았다.


struct gpio_block *gpio_block_create(unsigned int *gpios, size_t size, const char *name);

gpios는 하나의 블록으로 그룹지어지는, GPIO 번호들의 size 크기의 Array 이다. name은 user spcae에서 그 블록으로 동작하는데 사용될 수 있다. gpios 안의 GPIO 들은 gpio_request()로 이전에 이미 요청되어져 있어야 하고, 그 direction 도 각각 셋팅되어져 있어야 한다. GPIO 들이 흩어져 있거나, 드라이버가 Internal Block APIO를 구현하지 않으면, 그냥 지금처럼 개별 라인으로 접근한다.

GPIO 블록의 조작은,


unsigned long gpio_block_get(struct gpio_block *block, unsigned long mask);
void gpio_block_set(struct gpio_block *block, unsigned long mask, unsigned long values);

mask 로 블록 안의 GPIO를 고를 수 있는데, mask는 위의 gpio_block_create() 로 전달되는 array에 해당하는 비트가 쓰인다. 여기서 알 수 있는 점은 한 개 블록의 GPIO 의 갯수는 long의 비트 수까지만 될 수 있다는 점이다.
gpio_block_get() 은 가능하면 한꺼번에 지정된 라인들을 읽고, 비트 마스크로 결과를 반환한다. gpio_block_set()은 한꺼번에 셋팅할 수 있다.

GPIO 볼록은 다음으로 제거 가능하다.


void gpio_block_free(struct gpio_block *block);


User space에서 sysfs 로 GPIO 블록을 셋팅하거나 읽을 수 있도록하는, 등록 함수도 있다.


int gpio_block_register(struct gpio_block *block);
void gpio_block_unregister(struct gpio_block *block);

등록하면 gpio_block_create()에서 사용한 이름으로 device_node도 생성된다. 그 디바이스를 읽으면, 블록 안의 GPIO의 상태를 반환하고, 쓰면, 그에 따른 GPIO를 셋팅할 수도 있다. read, write 동작에 사용하는 mask를 셋팅하는데-커맨드 번호로 0을 사용- ioctl() 도 제공한다.

[Linux kernel] Softlockup 디텍터와 (nmi_watchdog로 알려진) hardlockup 디텍터

이 문서의 저작권은 GPL을 따릅니다(This document is released under the GPL license).

번역 : 양정석(dasomoli@gmailREMOVETHIS.com)

===============================================================

Softlockup 디텍터와 (nmi_watchdog로 알려진) hardlockup 디텍터
===============================================================
리눅스 커널은 soft와 hard lockup을 검출하기 위해서 watchdog처럼 동작할
수 있습니다.
‘softlockup’ 은 커널로부터 야기되는 다른 태스크들에게 실행할 기회를
주지 않고 커널 모드 안에서 20초 이상 도는 버그로 정의(자세한 사항은
아래 “구현” 참조)됩니다. 기본값으로 검출된 상태의 현재 스택 트레이스를
보여주고, 시스템은 멈춘 상태로 머물게 됩니다. 아니면 다르게 커널이 패닉
을 일으키도록 설정될 수도 있습니다; 이를 위해, sysctl의
“kernel.softlockup_panic”, 커널 파라미터의 “softlockup_panic”, 그리고
컴파일 옵션 “BOOTPARAM_HARDLOCKUP_PANIC”이 제공됩니다.
‘hardlockup’ 은 CPU로부터 야기되는 다른 인터럽트들이 실행할 기회를
주지 않고 커널 모드 안에서 10초 이상 도는 버그로 정의(자세한 사항은
아래 “구현” 참조)됩니다. softlockup 상황과 비슷하게, 기본값은 컴파일
타임 옵션의 “BOOTPARAM_HARDLOCKUP_PANIC”, 커널 파라미터의
“nmi_watchdog”(자세한 사항은 “Documentation/kernel-parameters.txt”
참조)을 통해 바꿀 수 있는데, 바뀌지 않는 한, 검출 상태의 현재 스택
트레이스를 보여주고, 시스템은 멈춘 상태로 머물게 됩니다. 
패닉 옵션은 지정된 시간이 지난 후에 시스템을 자동으로 리부팅하기 위해서
panic_timeout(이 timeout 은 헷갈리게 이름지어진 “kernel.panic” sysctl을
통해 셋팅됩니다)의 조합으로 사용될 수 있습니다.
=== 구현 ===
soft와 hard lockup 디텍터는 각각 hrtimer와 perf 서브시스템의 위에
빌드됩니다. 이의 직접적인 결과는 이론적으로 이들 서브 시스템이 제공되는
어떤 아키텍처 위에서도 동작한다는 것입니다.
주기적인 hrtimer는 인터럽트를 일으키고 watchdog 태스크를 수행(kick)하기
위해서 실행됩니다. NMI perf 이벤트는 매 (컴파일 타임에 10으로 초기화되고
같은 이름의 sysctl을 통해 설정될 수 있는)”watchdog_thresh” 초마다
hardlockup을 검사하기 위해서 생성됩니다. 시스템 안의 어떤 CPU가 그 
시간 동안 hrtimer 인터럽트를 받지 못한다면 ‘hardlockup 디텍터'(NMI perf
이벤트 핸들러)가 설정에 따라, 커널 경고를 생성하거나 패닉을 호출할
것입니다.
watchdog 태스크는 스케줄될 때마다 타임 스탬프를 업데이트 하는 최고
우선 순위 커널 스레드입니다. 타임 스탬프가 2 * watchdog_thresh 초
(softlockup threshold)동안 업데이트되지 않으면, 만약 패닉을 일으키도록
되어 있다면 패닉을 호출하고, 아니면 다른 커널 코드의 실행을 재개한 후에
(hrtimer 콜백 함수 안에 작성된) ‘softlockup 디텍터’가 유용한 디버그 정보를
시스템 로그로 덤프할 것입니다.
hrtimer의 주기는 hardlockup 디텍터가 수행되기 전에 인터럽트 발생을 위한
두번 혹은 세번의 기회를 가지는 2 * watchdog_thresh / 5 입니다.
위에 설명된 대로, 관리자가 hrtimer와 perf 이벤트의 주기를 설정할 수 있도록
커널 옵션이 제공됩니다. 각 환경에 맞는 값은 lockup을 위한 빠른 응답과 검출
오버헤드 사이의 트레이드 오프입니다.

[Bash] Redirection을 통한 빌드 로깅

가끔 컴파일할 때 Warning/Error를 같이 파일에 담고 싶을 때가 있는데 stderr을 stdout으로 Redirection 한 후 tee 를 사용한다


$ make ARCH=arm CROSS_COMPILE=어쩌고 -j8 2>&1 | tee build.log

Warning/Error 만 따로 파일에 넣고 싶을 때는 그냥 파일로 저장한다


$ make ARCH=arm CROSS_COMPILE=어쩌고 -j8 2> warnings.log

참고 : http://tldp.org/HOWTO/Bash-Prog-Intro-HOWTO-3.html
 

[Linux kernel] Runtime PM framework for I/O devices

이 문서의 저작권은 GPL을 따릅니다(This document is released under the GPL license).

번역: 양정석(dasomoli@gmailREMOVETHIS.com)

I/O 디바이스를 위한 런타임 전력 관리 프레임워크

(C) 2009-2011 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc.
(C) 2010 Alan Stern <stern@rowland.harvard.edu>
1. 도입
전력 관리 코어 레벨에 다음과 같은 방법으로 I/O 디바이스를 위한 런타임 전력 관리
(런타임 PM)의 지원을 제공합니다:
* 버스 타입과 디바이스 드라이버 내의 전력 관리 큐 pm_wq 는 그들의 PM에 관련된
  작업 아이템을 넣을 수 있습니다. 런타임 PM에 관련된 모든 작업 아이템의 큐 삽입
  에 pm_wq를 사용하는 것을 강력하게 권장합니다. 왜냐하면 이 것은 시스템 전체적인
  전력 상태 전이(RAM으로 suspend, 하이버네이션과 시스템 슬립 상태에서의 깨어남)를
  동기화하는 것을 허용하기 때문입니다. pm_wq 는 include/linux/pm_runtime.h 에
  선언되어 있고, kernel/power/main.c 에 정의되어 있습니다.
  
* ‘struct device’ 의 (include/linux/pm.h 에 선언된 ‘struct dev_pm_info’ 타입의)
  ‘power’ 멤버 안의 수많은 런타임 PM 필드는 다른 것과의 런타임 PM 동작의 동기화에
  사용할 수 있습니다.
  
* (include/linux/pm.h 에 선언된) 세 개의 디바이스 런타임 콜백
* drivers/base/power/runtime.c 안에 정의된 헬퍼 함수 집합은 PM Core에 의해서
  그들간의 동기화가 되는 방법 같은 것 내에서 런타임 PM 동작이 수행되는데
  사용할 수 있습니다. 버스 타입과 디바이스 드라이버는 이 함수들을 사용할만
  합니다.
‘struct dev_pm_ops’ 안에 있는 런타임 PM 콜백, ‘struct dev_pm_info’ 안의
디바이스 런타임 PM 필드들, 그리고 런타임 PM을 위해 제공되는 헬퍼 함수들은
아래에 설명되어 있습니다.
2. 디바이스 런타임 PM 콜백
‘struct dev_pm_ops’ 안에 정의된 세 개의 디바이스 런타임 PM 콜백이 있습니다;
struct dev_pm_ops {
        …
        int (*runtime_suspend)(struct device *dev);
        int (*runtime_resume)(struct device *dev);
        int (*runtime_idle)(struct device *dev);
        …
};
->runtime_suspend(), ->runtime_resume() 그리고 ->runtime_idle() 콜백은 다음 중
하나일 디바이스의 서브 시스템을 위해 PM 코어에 의해서 실행됩니다:
  1. 그 디바이스의 PM 도메인 객체인 dev->pm_domain 가 있다면, 그 디바이스의
     PM 도메인
  2. dev->type 과 dev->type->pm 이 둘 다 있다면, 그 디바이스의 디바이스 타입
  3. dev->class 과 dev->class->pm 이 둘 다 있다면, 그 디바이스의 디바이스 클래스
     
  4. dev->bus 과 dev->bus->pm 이 둘다 있다면, 그 디바이스의 버스 타입
위의 규칙에 의해서 선택된 서브 시스템이 관련 콜백을 제공하지 않으면, PM 코어는
(있다면) dev->driver->pm 에 담긴 해당 드라이버 콜백을 직접 호출할 것입니다.
PM 코어는 위에 있는 순서에 따라 사용할 콜백을 검사합니다. 그래서 콜백의 우선 순위
순서는 높은 것에서 낮은 순으로 PM 도메인, 디바이스 타입, 클래스 그리고 버스 타입
입니다. 더불어 높은 우선 순위의 것은 항상 낮은 우선 순위의 것보다 우선합니다.
PM 도메인, 버스 타입, 디바이스 타입과 클래스 콜백은 서스시스템 레벨 콜백처럼
참조됩니다.
기본적으로 그 콜백들은 항상 인터럽트가 활성화된 상태의 프로세스 컨텍스트에서
실행됩니다. 그러나, pm_runtime_irq_safe() 헬퍼 함수는 PM 코에에게 주어진
디바이스를 위해 인터럽트 비활성화 상태에서 원자적 컨텍스트 안에서
->runtime_suspend(), ->runtime_resume(), and ->runtime_idle() 콜백을 실행하는
것이 안전하다는 것을 알려주는데 사용할 수 있습니다. 이것은 질의 안에서의 콜백
루틴은 블러킹되거나 슬립할 수 없다는 것을 내포할 뿐만 아니라, 4절 끝에 나열된
싱크로너스 헬퍼 함수가 그 디바이스를 위해 인터럽트 핸들러 안에서 또는 일반적인
원자적 컨텍스트 안에서 사용될 수 있다는 것을 의미합니다.
서브 시스템 레벨 suspend 콜백이 있다면, 그 디바이스의 적절한 suspend 처리는
해당 콜백이 “전부” “책임집니다”. 그러나 그 디바이스 드라이버 자체의
->runtime_suspend() 콜백 실행은 포함할 필요 없습니다(PM 코어의 관점에서
그 서브 시스템 레벨의 suspend 콜백이 그 디바이스 처리를 위해 무엇을 해야 하는 지
아는 한, 디바이스 드라이버 안에 ->runtime_suspend() 콜백을 구현하는 것은
필요 없습니다).
  * 주어진 드라이버의 서브 시스템 레벨 suspend 콜백(또는 직접 호출되는
    그 드라이버의 suspend 콜백)이 성공적으로 완료되면, PM 코어는 그 디바이스가
    저전력 상태로 들어간 것을 의미할 필요는 없는, Suspend 된 것처럼 봅니다. 그러나
    이것은 그 디바이스가 적절한 resume 콜백이 실행되기 전까지 데이타를 처리하지도
    않고, (여러) CPU, RAM과 통신하지 않을 것을 의미한다고 가정합니다. suspend 콜백의
    성공적인 실행 후의 디바이스의 런타임 PM 상태는 ‘suspended’ 입니다.
  * suspend 콜백이 -EBUSY 나 -EAGAIN을 반환하면, 그 디바이스의 런타임 PM 상태는
    그 디바이스가 나중에 완전히 “수행되어야만 한다”는 것을 의미하는 ‘active’로
    남아 있습니다.
  * suspend 콜백이 -EBUSY나 -EAGAIN 외의 에러 코드를 반환하면, PM 코어는 이 것을
    치명적 에러로 보고 그 상태가 직접 ‘active’나 ‘suspended’ 로 설정(PM 코어는
    이런 목적의 특별한 헬퍼 함수를 제공합니다)되기 전까지 4절에 설명된 헬퍼
    함수의 실행을 거부할 것입니다.

특히, 드라이버가 적절한 동작을 위해 원격 웨이크업 능력을 요구하고, (즉, 하드웨어
메카니즘이 디바이스가 PCI PME처럼 그 파워 상태의 변경 요청을 허용한다면)
device_run_wake() 가 그 디바이스를 위해 ‘false’를 반환한다면, ->runtime_suspend() 는
-EBUSY를 반환해야 합니다. 다른 말로 하자면, device_run_wake() 가 ‘true’를 반환하고,
그 디바이스가 suspend 콜백의 실행 동안 저전력 상태에 놓여져 있다면, 원격 웨이크업이
디바이스를 위해 커져 있을 것으로 기대됩니다. 일반적으로 원격 웨이크업은 런타임에
저전력 상태로 들어가는 모든 입력 장치에 켜져 있어야 합니다.

서브 시스템 레벨 resume 콜백이 있다면, 그 디바이스의 적절한 resume 처리는
해당 콜백이 “전부” “책임집니다”. 그러나 그 디바이스 드라이버 자체의
->runtime_resume() 콜백 실행은 포함할 필요 없습니다(PM 코어의 관점에서
그 서브 시스템 레벨의 resume 콜백이 그 디바이스 처리를 위해 무엇을 해야 하는지
아는 한 디바이스 드라이버 안에 ->runtime_resume() 콜백을 구현하는 것은
필요 없습니다).
  * 서브 시스템 레벨의 resume 콜백(또는 직접 호출되는 그 드라이버의 resume 콜백)
    이 성공적으로 완료되면, PM 코어는 그 디바이스가 필요한 I/O 동작을 “완료할 수
    있어야만” 한다는 것을 의미하는, 완전히 동작하는 것으로 봅니다. 그 디바이스의
    런타임 PM 상태는 그럼 ‘active’ 입니다.
  * resume 콜백이 에러 코드를 반환하면, PM 코어는 이것을 치명적 에러로 보고
    그 상태가 ‘active’ 나  ‘suspended’로 (PM 코어에 의해 제공되는 특별한 헬퍼
    함수의 방법으로)직접 설정되기 전까지 4절에서 설명된 헬퍼 함수의 실행을
    거부할 것입니다.
  
idle 콜백(서브 시스템 레벨의 또는 드라이버의 idle 콜백이 있다면)은 PM 코어에
의해서 그 디바이스가 그 디바이스의 사용 카운터와 그 디바이스의 ‘active’인 자식의
카운터, 두 개의 카운터에 의해 PM 코어에 의해 표시되는 idle 상태가 될 때마다
실행됩니다.
  * PM 코어에 의해 제공되는 헬퍼 함수를 사용하는 이들 카운터들 중 하나가 감소되어
    0과 같아지면, 다른 카운터가 검사됩니다. 만약 그 카운터도 0이라면 PM 코어는
    그 디바이스의 idle 콜백을 그 인자와 함께 실행합니다.
idle 콜백에 의해 수행되는 동작은 질의 안에서 완전히 서브 시스템(또는 드라이버)
에 완전히 의존적입니다. 그러나 그 디바이스가 suspend 될 수 있는지(예를 들면,
그 디바이스의 suspend 진입을 위한 모든 필요한 조건이 만족되었는지)를 검사하는 것
과 그런 경우에 그 디바이스에 대한 suspend 요청을 큐에 쌓는 것을 권장합니다.
이 콜백에 의해 반환되는 값은 PM 코어에서 무시됩니다.
4절에서 설명된 PM 코어에 의해 제공되는 헬퍼 함수들은 한 디바이스를 위한
런타임 PM 콜백에 대해서 다음 제약 사항을 만족함을 보장합니다.
(1) ->runtime_suspend() 나 ->runtime_resume() 가 ->runtime_idle()와 동시에
    실행될 수 있다는 것을 제외(->runtime_idle()이 같은 디바이스에 대해서 어떤
    다른 콜백이 실행되는 동안 실행되지 않는다 하더라도)하면, 그 콜백들은
    서로 동시에 실행될 수 없습니다(실제로 ->runtime_suspend() 와 동시에
    ->runtime_resume() 또는 같은 디바이스의 다른 인스턴스에서
    ->runtime_suspend() 가 실행되는 것은 금지됩니다).
(2) ->runtime_idle() 과 ->runtime_suspend() 는 ‘active’ 디바이스만 실행될 수
    있습니다(즉, PM 코어는 런타임 PM 상태가 ‘active’인 디바이스만 ->runtime_idle()
    이나 ->runtime_suspend()를 실행할 것입니다).
(3) ->runtime_idle() 과 ->runtime_suspend() 는 그 사용 카운터가 0이 되고, “또한”
    ‘active’ 자식의 수가 0이 되거나 ‘power.ignore_children’ 플래그가 셋팅된
    디바이스만 실행될 수 있습니다.
(4) ->runtime_resume() 은 ‘suspended’ 디바이스만 실행될 수 있습니다(실제로
    PM 코어는 런타임 PM 상태가 ‘suspended’ 인 디바이스만 ->runtime_resume() 을
    실행할 것입니다.
추가적으로, PM 코어에 의해 제공되는 헬퍼 함수들은 다음 규칙을 따릅니다.
  * ->runtime_suspend() 가 막 실행되려고 하거나 실행이 보류된 요청이 있으면,
    ->runtime_idle() 은 같은 디바이스에서 실행되지 않을 것입니다.
  * ->runtime_suspend() 의 실행을 스케줄링하는 것이나 실행에 대한 요청은 같은
    디바이스의 ->runtime_idle() 을 실행하기 위한 다른 보류된 요청을 취소할 것입니다.
  * ->runtime_resume() 이 막 실행되려고 하거나 실행이 보류된 요청이 있으면,
    같은 디바이스의 다른 콜백은 실행되지 않을 것입니다.
  * ->runtime_resume() 실행의 요청은 스케줄링된 autosuspend를 제외하면,
    같은 디바이스의 다른 콜백을 실행하기 위한 보류되거나 스케줄된 요청을 취소할
    것입니다.
3. 런타임 PM 디바이스 필드
다음 디바이스 런타임 PM 필드들은 include/linux/pm.h 안에 정의된
‘struct dev_pm_info’ 안에 있습니다.
  struct timer_list suspend_timer;
    – 스케줄링된 (지연된) suspend 와 autosuspend 요청을 위해 사용되는 타이머
  unsigned long timer_expires;
    – jiffies로 된 타이머 만료 시간 (0이 아니라면, 그 타이머는 실행 중이고
      그 시간에 만료되는 것이거나, 그 타이머가 실행 중이 아닌 겁니다)
  struct work_struct work;
    – 요청을 큐에 쌓는데 사용되는 워크 구조체(실제로, pm_wq 안의 워크 아이템)
  wait_queue_head_t wait_queue;
    – 어떤 헬퍼 함수가 다른 것이 완료되기를 기다리는 것이 필요할 때 사용되는
      대기 큐
  spinlock_t lock;
    – 동기화에 사용되는 락
  atomic_t usage_count;
    – 그 디바이스의 사용 카운터
  atomic_t child_count;
    – 그 디바이스의 ‘active’ 자식의 수
  unsigned int ignore_children;
    – 셋팅되면, child_count 의 값이 무시됩니다(그러나 여전히 업데이트는 됩니다)
  unsigned int disable_depth;
    – 헬퍼 함수들을 비활성화하는데 사용됩니다(그것들은 이 값이 0일 때 일반적으로
      실행됩니다); 초기값은 1(실제로 런타임 PM은 초기에 모든 디바이스에 비활성화되어
      있습니다)
  unsigned int runtime_error;
    – 셋팅되면, 치명적 에러가 있는 것(2절에서 설명한 것처럼 콜백 중 하나가
      에러 코드를 반환한 것)입니다. 그래서 헬퍼 함수들은 이 플래그가 클리어될
      때까지 동작하지 않습니다; 이것은 실패한 콜백에 의해 반환된 에러 코드입니다.
  unsigned int idle_notification;
    – 셋팅되면, ->runtime_idle()이 실행 중입니다.
  unsigned int request_pending;
    – 셋팅되면, 보류된 요청(실제로 pm_wq 안에 워크 아이템이 담긴 것)이 있는 것입니다.
  enum rpm_request request;
    – 그 보류 요청의 타입(request_pending 이 셋팅되어 있을 때 유효)
  unsigned int deferred_resume;
    – ->runtime_resume() 이 ->runtime_suspend() 가 그 디바이스에서 실행되는 중에
      막 실행되려고 하고, 그것이 suspend가 완료되기를 기다리는 것-“suspend되자 마자
      resume을 시작함”을 의미-이 아니면 셋팅하세요.
  unsigned int run_wake;
    – 디바이스가 런타임 웨이크업 이벤트를 생성할 수 있으면 셋팅
  enum rpm_status runtime_status;
    – 디바이스의 런타임 PM 상태; 이 필드의 초기값은 RPM_SUSPENDED로, 각 디바이스는
      PM 코어에 의해 초기에 하드웨어 상태와 상관없이 ‘suspended’ 로 인식된다는
      뜻입니다.
  unsigned int runtime_auto;
    – 셋팅되면, 유저 공간이 디바이스 드라이버를 /sys/devices/…/power/control
      인터페이스를 통해 런타임에 디바이스의 파워를 관리할 수 있음을 나타냅니다;
      pm_rumtime_allow()와 rpm_runtime_forbid() 헬퍼 함수의 도움으로만 바뀝니다.
  unsigned int no_callbacks;
    – 디바이스가 런타임 PM 콜백을 사용하지 않음을 나타냅니다(8절 참조);
      pm_runtime_no_callbacks() 헬퍼 함수에 의해서만 바뀝니다.
  unsigned int irq_safe;
    – ->runtime_suspend() 와 ->runtime_resume() 콜백이 스핀락을 잡고 인터럽트가
      비활성화된 상태에서 호출됨을 나타냅니다.
  unsigned int use_autosuspend;
    – 디바이스의 드라이버가 지연된 autosuspend를 지원함을 나타냅니다(9절 참조);
      pm_runtime{_dont}_use_autosuspend() 헬퍼 함수에 의해서만 바뀝니다.
  unsigned int timer_autosuspends;
    – PM 코어가 보통의 suspend보다는 타이머가 만료되었을 때 autosuspend를 수행하는 것을
      시도하여야만 함을 나타냅니다.
  int autosuspend_delay;
    – autosuspend에 사용되는 지연 시간(밀리초)
  unsigned long last_busy;
    – 이 디바이스를 위해 pm_runtime_mark_last_busy() 헬퍼 함수가
      마지막으로 불린 시간(jiffies 값); autosuspend를 위해 비활성화 시간을 계산하는데
      사용됨
위 필드 모두는 ‘struct device’의 ‘power’ 멤버의 멤버들입니다.
4. 런타임 PM 디바이스 헬퍼 함수
다음의 런타임 PM 헬퍼 함수들은 drivers/base/power/runtime.c와 include/linux/pm_runtime.h에
정의되어 있습니다:
  void pm_runtime_init(struct device *dev);
    – ‘struct dev_pm_info’ 안의 그 디바이스의 런타임 PM 필드를 초기화
  void pm_runtime_remove(struct device *dev);
    – 디바이스의 런타임 PM이 디바이스 계층에서 그 디바이스가 제거된 후에 비활성화될 것임을 확인
  int pm_runtime_idle(struct device *dev);
    – 그 디바이스의 서브 시스템 레벨 idle 콜백을 실행; 성공 시에는 0을, 실패 시에는
      에러 코드를 반환합니다. -EINPROGRESS 는 ->runtime_idle()이 이미 실행 중임을 나타냅니다.
  int pm_runtime_suspend(struct device *dev);
    – 그 디바이스의 서브 시스템 레벨 suspend 콜백을 실행: 성공 시에는 0을, 그 디바이스의
      런타임 PM 상태가 이미 ‘suspended’ 일 때는 1을, 실패 시에는 에러 코드를 반환합니다.
      -EAGAIN 이나 -EBUSY는 나중에 그 디바이스의 suspend 시도가 안전할 것임을
      나타냅니다. -EACCES 는 ‘power.disable_depth’가 0이 아님을 나타냅니다.
  int pm_runtime_autosuspend(struct device *dev);
    – pm_runtime_suspend()와 autosuspend delay가 사용됨을 제외하고는 동일;
      pm_runtime_autosuspend_expiration() 이 delay가 아직 만료되지 않았다고 보고하면
      autosuspend는 적절한 시간으로 스케줄되고 0이 반환됩니다.
  int pm_runtime_resume(struct device *dev);
    – 그 디바이스의 서브 시스템 레벨 resume 콜백을 실행; 성공 시에는 0을, 그 디바이스의
      런타임 PM 상태가 이미 ‘active’이면 1을, 실패 시에는 에러 코드를 반환합니다.
      -EAGAIN 은 나중에 그 디바이스의 resume 시도가 안전할 것임을 나타냅니다. 그러나
      ‘power.runtime_error’는 추가적으로 체크되어야 합니다. -EACCES 는 ‘power.disable_depth’
      가 0이 아님을 나타냅니다.
  int pm_request_idle(struct device *dev);
    – 그 디바이스의 서브 시스템 레벨 idle 콜백 실행의 요청을 제출합니다(그 요청은
      pm_wq 안의 워크 아이템으로 나타납니다); 성공 시에는 0이, 그 요청이 큐에 들어가지
      못하면 에러 코드가 반환됩니다.
    
  int pm_request_autosuspend(struct device *dev);
    – autosuspend delay가 만료되었을 때 그 디바이스의 서브 시스템 레벨 suspend 콜백 실행을
      스케줄합니다; delay가 이미 만료되었으면, 워크 아이템은 즉시 큐에 들어갑니다.
  int pm_schedule_suspend(struct device *dev, unsigned int delay);
    – 그 디바이스의 서브 시스템 레벨 suspend 콜백을 좀 나중에 실행할 것을 스케줄합니다.
      여기서 ‘delay’는 pm_wq 안에 suspend 워크 아이템이 큐에 들어가기 전에 기다릴
      시간(밀리초) 입니다(‘delay’가 0이면, 그 워크 아이템은 즉시 큐에 들어갑니다);
      성공 시에는 0이, 그 디바이스의 PM 런타임 상태가 이미 ‘suspended’ 이면 1이,
      그 요청이 스케줄되지 않으면(또는 ‘delay’가 0일 때 큐에 들어가지 않으면)
      에러 코드가 반환됩니다; ->runtime_suspend()의 실행이 이미 스케줄되어 있고, 아직
      만료되지 않았다면, ‘delay’의 새 값은 기다릴 시간으로 사용됩니다.
  int pm_request_resume(struct device *dev);
    – 그 디바이스의 서브 시스템 레벨 resume 콜백의 실행 요청을 제출합니다(그 요청은
      pm_wq 안의 워크 아이템으로 나타납니다); 성공 시에는 0이, 그 디바이스의 런타임
      PM 상태가 이미 ‘active’이면 1이, 요청이 큐에 들어가지 못하면 에러 코드가 반환됩니다.
  void pm_runtime_get_noresume(struct device *dev);
    – 디바이스의 사용 카운터를 증가시킵니다.
  int pm_runtime_get(struct device *dev);
    – 디바이스의 사용 카운터를 증가시키고, pm_request_resume(dev) 를 실행하고,
      그 결과를 반환합니다.
  int pm_runtime_get_sync(struct device *dev);
    – 디바이스의 사용 카운터를 증가시키고, pm_runtime_resume(dev) 를 실행하고,
      그 결과를 반환합니다.
  void pm_runtime_put_noidle(struct device *dev);
    – 디바이스의 사용 카운터를 감소시킵니다.
  int pm_runtime_put(struct device *dev);
    – 디바이스의 사용 카운터를 감소시킵니다; 그 결과가 0이면, pm_request_idle(dev) 를
      실행하고, 그 결과를 반환합니다.
  int pm_runtime_put_autosuspend(struct device *dev);
    – 디바이스의 사용 카운터를 감소시킵니다; 그 결과가 0이면, pm_request_autosuspend(dev) 를
      실행하고, 그 결과를 반환합니다.
  int pm_runtime_put_sync(struct device *dev);
    – 디바이스의 사용 카운터를 감소시킵니다; 그 결과가 0이면, pm_runtime_idle(dev) 를
      실행하고, 그 결과를 반환합니다.
  int pm_runtime_put_sync_suspend(struct device *dev);
    – 디바이스의 사용 카운터를 감소시킵니다; 그 결과가 0이면, pm_runtime_suspend(dev) 를
      실행하고, 그 결과를 반환합니다.
  int pm_runtime_put_sync_autosuspend(struct device *dev);
    – 디바이스의 사용 카운터를 감소시킵니다; 그 결과가 0이면, pm_runtime_autosuspend(dev) 를
      실행하고, 그 결과를 반환합니다.
  void pm_runtime_enable(struct device *dev);
    – 디바이스의 ‘power.disable_depth’ 필드를 감소시킵니다; 그 필드가 0이면, 런타임 PM
      헬퍼 함수들이 2절에서 설명된 서브 시스템 레벨 콜백들을 실행할 수 있습니다.
  int pm_runtime_disable(struct device *dev);
    – 디바이스의 ‘power.disable_depth’ 필드를 증가시키고(그 필드의 값이 실행 전에
      0이었다면, 이 것은 서브 시스템 레벨 런타임 PM 콜백의 실행을 막습니다), 디바이스 상의
      모든 런타임 PM 동작이 완료되었거나 취소되었음을 확인합니다;
      resume 요청이 밀려있고, 요청을 만족하기 위해서 서브 시스템 레벨 resume 콜백이 실행될
      필요가 있다면 1을, 다른 경우에는 0이 반환됩니다.
  int pm_runtime_barrier(struct device *dev);
    – resume 요청이 밀려있는지 검사하고, 그런 경우에는 그 요청들을 (동기적으로)
      resume 하고, 그에 관련된 다른 밀린 런타임 PM 요청들을 취소하고, 완료를 위해
      진행 중인 모든 런타임 PM 동작들을 기다립니다; resume 요청이 밀려있고, 요청을 만족하기
      위해서 서브 시스템 레벨 resume 콜백들이 실행될 필요가 있다면 1을, 다른 경우에는
      0이 반환됩니다.
  void pm_suspend_ignore_children(struct device *dev, bool enable);
    – 디바이스의 power.ignore_children 플래그를 셋팅/해제합니다.
  int pm_runtime_set_active(struct device *dev);
    – 디바이스의 ‘power.runtime_error’ 플래그를 클리어하고, 디바이스의 런타임 PM 상태를
      ‘active’ 상태로 셋팅하고, 부모의 ‘active’ 자식들의 카운터를 적절히 업데이트합니다
      (‘power.rumtime_error’가 셋팅되어 있거나 ‘power.disable_depth’가 0보다 크면,
      이 함수를 사용하는 것만 유효합니다); 그 디바이스가 active 하지 않고
      ‘power.ignore_children’ 플래그가 해제되어 있는 부모를 가진다면, 실패하고
      에러 코드를 반환할 것입니다.
  void pm_runtime_set_suspended(struct device *dev);
    – 디바이스의 ‘power.runtime_error’ 플래그를 클리어하고, 디바이스의 런타임 PM 상태를
      ‘suspended’ 상태로 셋팅하고, 부모의 ‘active’ 자식들의 카운터를 적절히 업데이트합니다
      (‘power.rumtime_error’가 셋팅되어 있거나 ‘power.disable_depth’가 0보다 크면,
      이 함수를 사용하는 것만 유효합니다).
  bool pm_runtime_suspended(struct device *dev);
    – 디바이스의 런타임 PM 상태가 ‘suspended’ 이고, 그 ‘power.disable_depth’가 0이면,
      true를, 다른 경우는 false를 반환합니다.
  bool pm_runtime_status_suspended(struct device *dev);
    – 디바이스의 런타임 PM 상태가 ‘suspended’ 이면 true를 반환합니다.
  void pm_runtime_allow(struct device *dev);
    – 디바이스의 power.runtime_auto 플래그를 셋팅하고, 그 사용 카운터를 감소시킵니다
      (/sys/devices/…/power/control 인터페이스가 런타임에 디바이스의 파워 관리를
      효과적으로 허용하기 위해서 사용됩니다).
  void pm_runtime_forbid(struct device *dev);
    – 디바이스의 power.runtime_auto 플래그를 해제하고, 그 사용 카운터를 증가시킵니다
      (/sys/devices/…/power/control 인터페이스가 런타임에 디바이스의 파워 관리를
      효과적으로 막기 위해서 사용됩니다).
  void pm_runtime_no_callbacks(struct device *dev);
    – 디바이스의 power.no_callbacks 플래그를 셋팅하고, /sys/devices/…/power 의
      런타임 PM 속성들을 제거(또는 디바이스가 등록될 때 속성들이 추가되는 것을
      막습니다)합니다.
  void pm_runtime_irq_safe(struct device *dev);
    – 인터럽트 비활성화 상태에서 호출되는 런타임 PM 콜백을 일으키는
      디바이스의 power.irq_safe 플래그를 셋팅합니다.
  void pm_runtime_mark_last_busy(struct device *dev);
    – power.last_busy 필드를 현재 시간으로 셋팅합니다.
  void pm_runtime_use_autosuspend(struct device *dev);
    – autosuspend delay를 켜는 power.use_autosuspend 플래그를 셋팅합니다.
  void pm_runtime_dont_use_autosuspend(struct device *dev);
    – autosuspend delay를 끄는 power.use_autosuspend 플래그를 클리어합니다.
  void pm_runtime_set_autosuspend_delay(struct device *dev, int delay);
    – power.autosuspend_delay 값을 (밀리초로 나타내는) ‘delay’로 셋팅합니다;
      delay가 음수이면 런타임 suspend는 막힙니다.
  unsigned long pm_runtime_autosuspend_expiration(struct device *dev);
    – 현재 autosuspend delay 시간이 만료될 시간을 power.last_busy와
      power.autosuspend_delay에 기초해서 계산합니다; delay 시간이 1000 ms 나
      그 이상인 경우, 만료 시간은 가장 가까운 초로 올림됩니다;
      그 딜레이 시간이 이미 만료되었거나 power.use_autosuspend가 셋팅되지 않았으면
      0을, 다른 경우에는 jiffies로 된 만료 시간을 반환합니다.
다음 헬퍼 함수들은 인터럽트 컨텍스트에서의 실행이 안전합니다:
pm_request_idle()
pm_request_autosuspend()
pm_schedule_suspend()
pm_request_resume()
pm_runtime_get_noresume()
pm_runtime_get()
pm_runtime_put_noidle()
pm_runtime_put()
pm_runtime_put_autosuspend()
pm_runtime_enable()
pm_suspend_ignore_children()
pm_runtime_set_active()
pm_runtime_set_suspended()
pm_runtime_suspended()
pm_runtime_mark_last_busy()
pm_runtime_autosuspend_expiration()
pm_runtime_irq_safe()가 호출되면 다음 헬퍼 함수들이 인터럽트 컨텍스트에서
사용될 겁니다:
pm_runtime_idle()
pm_runtime_suspend()
pm_runtime_autosuspend()
pm_runtime_resume()
pm_runtime_get_sync()
pm_runtime_put_sync()
pm_runtime_put_sync_suspend()
pm_runtime_put_sync_autosuspend()
5. 런타임 PM 초기화, 디바이스 감지하기와 제거
초기에 런타임 PM은 모든 디바이스에 비활성화됩니다. 이 것은 4절에서 설명한 런타임
PM 헬퍼 함수들의 대부분이 pm_runtime_enable()이 그 디바이스에 호출될 때까지
-EAGAIN을 반활할 것을 의미합니다.
게다가 모든 디바이스의 초기 런타임 PM 상태는 ‘suspended’입니다. 그러나 그 것이
디바이스의 실제 물리적인 상태를 반영할 필요는 없습니다. 그래서 그 디바이스가
만약 초기에 활성 상태(즉, I/O를 처리 가능한)라면, pm_runtime_enable() 이 호출되기 전에
pm_runtime_set_active()의 도움으로, 그 런타임 PM 상태를 ‘active’로 바꿔야 합니다.
그러나 그 디바이스가 부모를 갖고, 그 부모의 런타임 PM이 켜져 있다면,
그 부모의 ‘power.ignore_children’ 플래그가 셋팅되기 전까지 pm_runtime_set_active()의
호출이 그 부모에 영향을 미칠 것입니다. 다시 말해, 그런 경우 그 부모는 그 자식의 상태가
‘active’인 한, 그 자식의 런타임 PM 이 여전히 꺼져있다 하더라도
PM 코어의 헬퍼 함수들을 통해 런타임에 suspend할 수 없을 것입니다
(즉, pm_runtime_enable() 은 그 자식을 위해 아직 호출되지 않았거나,
pm_runtime_disable()이 그를 위해 호출되었습니다). 이런 이유로 pm_runtime_set_active()가
호출되고 나면, pm_runtime_enable()이 꽤 가능한 한 빨리 호출되거나, 그 런타임 PM 상태가
pm_runtime_set_suspended()의 도움을 통해 ‘suspended’로 다시 바뀌어야 합니다.
그 디바이스의 초기 기본 런타임 PM 상태(즉, ‘suspended’)가 디바이스의 실제 상태를
반영한다면, 그 버스 타입의 또는 그 드라이버의 ->probe() 콜백이 4절에서 설명한 PM 코어의
헬퍼 함수들 중 하나를 사용해서 깨울 필요가 있습니다. 이런 경우, pm_runtime_resume()이
사용되어야 합니다. 물론, 이를 위해 디바이스의 런타임 PM이 pm_runtime_enable() 호출보다
먼저 켜져 있어야 합니다.
그 디바이스 버스 타입의 또는 드라이버의 ->probe() 콜백이 pm_runtime_suspend()나
pm_runtime_idle() 또는 그 비동기적인 대응물을 실행한다면, -EAGAIN을 반환하면서
실패할 것입니다. 그 디바이스의 사용 카운터가 ->probe() 실행 전에 드라이버 코어에 의해
증가되기 때문입니다. 여전히 ->probe()가 끝나자마자 디바이스가 suspend되는 것이
바람직 할 것입니다. 그래서 드라이버 코어는 서브 시스템 레벨 idle 콜백을 그 때
불리도록 하기 위해서 pm_runtime_put_sync()를 사용합니다.
더 나아가, 드라이버 코어는 런타임 PM 콜백들을 __device_release_driver() 안의
버스 notifier 콜백과 경쟁하는 것을 막습니다. 이는 꼭 필요한데, 그 notifier는
어떤 서브 시스템들에 의해서 런타임 PM 기능에 영향을 미치는 동작을 수행하기 위해
사용되기 때문입니다. 이는 driver_sysfs_remove() 전의 pm_runtime_get_sync() 호출과 BUS_NOTIFY_UNBIND_DRIVER 통지를 통해 수행합니다. 그 루틴들이 실행되는 동안
디바이스가 suspended 상태에 있고, 다시 suspended 되는 것을 막으면, 그 디바이스를
resume 합니다.
->remove() 루틴으로부터 pm_runtime_suspend()를 호출함으로써 버스 타입과 드라이버가
디바이스들을 suspended 상태로 들어가도록 허용하기 위해, 드라이버 코어는
__device_release_driver() 안에서 BUS_NOTIFY_UNBIND_DRIVER 통지를 실행한 후에
pm_runtime_put_sync()를 실행합니다. 이것은 버스 타입과 드라이버가 ->remove() 콜백을
직접 런타임 콜백과의 경쟁과 피하게 만들기 위해서 필요할 뿐만 아니라
그 드라이버의 제거를 하는 동안 디바이스의 처리에 더욱 큰 유연함을 제공합니다.
유저 스페이스는 pm_runtime_forbid()의 호출을 일으키는 /sys/devices/…/power/control
속성의 값을 “on”으로 바꾸는 것으로써 효과적으로 디바이스의 드라이버가 런타임에
파워를 관리하는 것을 허용하지 않을 수 있습니다. 원칙적으로, 이 메카니즘은 또한
드라이버에 의해 유저 공간이 켤 때까지 효과적으로 디바이스의 런타임 파워 관리를
끄는데 사용되기도 합니다. 다시말해, 초기화 동안 드라이버는 디바이스의 런타임 PM 상태가
‘active’인지 확인하고, pm_runtime_forbid()를 호출할 수 있습니다. 그러나 유저 공간이
런타임에 디바이스의 파워를 관리하기 위해 의도적으로 이미 /sys/devices/…/power/control의
값을 “auto”로 바꾸었다면, 드라이버는 pm_runtime_forbid()를 이 방법으로 사용함으로써 혼란에
빠질 것입니다.
6. 런타임 PM과 시스템 슬립
런타임 PM과 시스템 슬립(즉, suspend-to-RAM과 suspend-to-disk로도 알려진
시스템 suspend와 하이버네이션)은 서로 두 가지 방법으로 소통합니다. 시스템 슬립이
시작될 때 디바이스가 active 상태면, 모든 것이 간단합니다. 그러나 디바이스가 이미
suspended 상태라면 어떻게 될까요?
디바이스는 아마 런타임 PM과 시스템 슬립을 위한 다른 웨이크업 셋팅을 가지고 있을 것입니다.
예를 들면, 원격 웨이크업은 런타임 suspend를 위해 켜져 있을 것이지만, 시스템 슬립을
위해서는 허용되지 않는 것(device_may_wakeup(dev)는 ‘false’를 반환)입니다.
이런 것이 발생할 때, 서브 시스템 레벨 시스템 suspend 콜백은 디바이스의 웨이크업
셋팅을 바꿀 책임이 있습니다(이것은 디바이스 드라이버의 시스템 suspend 루틴에
남겨집니다). 디바이스를 resume 하고 suspend를 다시 순서대로 할 필요도 있습니다.
드라이버가 다른 파워 레벨을 사용하거나 런타임 suspend와 시스템 슬립을 위한 다른 셋팅을
사용하면, 이 역시 같습니다.
시스템 suspend가 시작되기 전에 suspend되어 있다하더라도, 시스템이 resume하는 동안의
가장 간단한 방법은 모든 디바이스를 풀파워로 다시 돌리는 것입니다. 이를 위해서는
다음을 포함하는 여러 이유가 있습니다:
  * 디바이스는 파워 레벨, 웨이크업 셋팅이나 기타를 바꿀 필요가 있을 수 있습니다.
  
  * 원격 웨이크업 이벤트를 펌웨어가 놓칠 수 있습니다.
  
  * 어떤 디바이스의 자식이 그들 스스로를 resume함에 따라 그 디바이스를 풀파워에 있게 할
    필요가 있을 수 있습니다.
    
  * 디바이스의 상태에 대한 드라이버의 생각은 디바이스의 물리 상태와 다를 수 있습니다.
    이것은 하이버네이션에서 resume하는 동안 발생할 수 있습니다.
  * 디바이스는 리셋될 필요가 있을 수 있습니다.
  
  * 디바이스가 suspend됐었다 하더라도, 사용 카운터 > 0 이면 거의 대부분
    어쨌든 런타임 resume이 가까운 미래에 필요할 것입니다.
    
디바이스가 시스템 suspend가 시작되기 전에 suspend되었고 resume동안 풀파워로 다시
돌아왔다면, 그 런타임 PM 상태는 실제 시스템 슬립 이후의 상태를 반영하도록
업데이트되어야만 할 것입니다. 이를 위한 방법은:
pm_runtime_disable(dev);
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
PM 코어는 ->suspend() 콜백을 호출하기 전에 언제나 런타임 사용 카운터를 증가시키고,
->resume() 콜백을 호출한 후에는 감소시킵니다. 그래서 이처럼 런타임 PM을 일시적으로
끄는 것은 런타임 suspend를 영원히 잃어버릴 수 있는 것이 일어나지 않도록 합니다.
사용 카운터가 0이 되고 ->resume() 콜백의 반환이 따라오면, ->runtime_idle() 콜백은
평소처럼 호출될 것입니다.
그러나 어떤 시스템 상에서는 시스템 슬립이 글로벌 펌웨어나 하드웨어 동작을 통해서
들어가지 않습니다. 대신에 모든 하드웨어 컴포넌트들은 커널에 의해서 맞춰진 방법으로
직접 저전력 상태로 들어갑니다. 그럼 하드웨어 컴포넌트가 끝난 상태에서 시스템 슬립
상태로 효과적으로 따라오고, 그 시스템은 커널의 제어 아래에서 하드웨어 인터럽트나
그와 유사한 메카니즘에 의해 전체적으로 그 상태에서 깨어나게 됩니다. 그 결과로,
커널은 절대 제어권을 넘겨주지 않고, resume 하는 동안 모든 디바이스의 상태는 명확히
알려지게 됩니다. 이 경우이면서 위에 나열된 상황들이 일어나지 않는다면(특히,
시스템이 하이버네이션으로부터 깨어나지 않고 있는), 시스템 suspend가 suspended 상태에서
시작되기 전에 suspend에 들어가 있는 디바이스는 그상태로 남아있기 더 효율적이게
것입니다.
PM 코어는 런타임 PM과 시스템 suspend/resume (그리고 하이버네이션) 콜백 사이의
경쟁 상태 확률을 줄이기 위해서 다음 동작들을 수행함으로써 최선의 방법을 수행합니다:
  * 시스템 suspend 동안, 서브 시스템 레벨 .suspend() 콜백을 실행하기 바로 직전에
    각각의 디바이스마다 pm_runtime_get_noresume() 과 pm_runtime_barrier() 를
    호출합니다. 게다가 서브 시스템 레벨 .suspend() 콜백 실행 바로 직후에 각각의
    디바이스마다 pm_runtime_disable() 을 호출합니다.
  * 시스템 resume 동안, 서브 시스템 레벨 .resume() 콜백 실행 바로 직전과 직후에
    각각의 디바이스마다 pm_runtime_enable() 과 pm_runtime_put_sync()를 호출합니다.
7. 일반적인 서브 시스템 콜백들
서브 시스템들은 PM 코어에 의해 제공되는, driver/base/power_generic_ops.c에 정의된
일반적인 파워 관리 콜백들을 사용해서 코드 공간을 아껴 쓰고 싶어할 것입니다:
  int pm_generic_runtime_idle(struct device *dev);
    – 정의된다면, 이 디바이스의 드라이버에 의해 제공되는 ->runtime_idle() 콜백을
      실행하고, 반환값이 0이거나 콜백이 정의되어 있지 않으면 pm_runtime_suspend()
      를 호출합니다.
  int pm_generic_runtime_suspend(struct device *dev);
    – 이 디바이스의 드라이버에 의해 제공되는 ->runtime_suspend() 콜백을 실행하고,
      그 결과를 반환하거나, 정의되지 않은 경우 -EINVAL을 반환합니다.
  int pm_generic_runtime_resume(struct device *dev);
    – 이 디바이스의 드라이버에 의해 제공되는 ->runtime_resume() 콜백을 실행하고,
      그 결과를 반환하거나, 정의되지 않은 경우 -EINVAL을 반환합니다.
  int pm_generic_suspend(struct device *dev);
    – 그 디바이스가 런타임에 suspend되지 않았다면, 그 드라이버에 의해 제공되는
      ->suspend() 콜백을 실행하고 그 결과를 반환하거나, 정의되지 않은 경우 0을
      반환합니다.
  int pm_generic_suspend_noirq(struct device *dev);
    – pm_runtime_suspended(dev)가 “false”를 반환하면, 그 디바이스 드라이버에 의해
      제공되는 ->suspend_noirq()를 실행하고 그 결과를 반환하거나, 정의되지 않은 경우
      0을 반환합니다.
  int pm_generic_resume(struct device *dev);
    – 디바이스 드라이버에 의해 제공되는 ->resume() 콜백을 실행하고, 성공하면,
      디바이스의 런타임 PM 상태를 ‘active’로 바꿉니다.
  int pm_generic_resume_noirq(struct device *dev);
    – 이 디바이스의 드라이버에 의해 제공되는 ->resume_noirq()를 실행합니다.
  int pm_generic_freeze(struct device *dev);
    – 그 디바이스가 런타임에 suspend되지 않았다면, 그 드라이버에 의해 제공되는
      ->freeze() 콜백을 실행하고 그 결과를 반환하거나, 정의되지 않은 경우
      0을 반환합니다.
  int pm_generic_freeze_noirq(struct device *dev);
    – pm_runtime_suspended(dev)가 “false”를 반환하면, 그 드라이버에 의해 제공되는
      ->freeze_noirq() 콜백을 실행하고 그 결과를 반환하거나, 정의되지 않은 경우
      0을 반환합니다.
  int pm_generic_thaw(struct device *dev);
    – 그 디바이스가 런타임에 suspend되지 않았다면, 그 드라이버에 의해 제공되는
      ->thaw() 콜백을 실행하고 그 결과를 반환하거나, 정의되지 않은 경우 0을
      반환합니다.
  int pm_generic_thaw_noirq(struct device *dev);
    – pm_runtime_suspended(dev)가 “false”를 반환하면, 그 디바이스 드라이버가
      제공하는 ->thaw_noirq() 콜백을 실행하고 그 결과를 반환하거나, 정의되지 않은
      경우 0을 반환합니다.
  int pm_generic_poweroff(struct device *dev);
    – 그 디바이스가 런타임에 suspend되지 않았다면, 그 드라이버에 의해 제공되는
      ->poweroff() 콜백을 실행하고 그 결과를 반환하거나, 정의되지 않은 경우
      0을 반환합니다.
  int pm_generic_poweroff_noirq(struct device *dev);
    – pm_runtime_suspended(dev)가 “false”를 반환하면, 그 디바이스 드라이버가
      제공하는 ->powerfoff_noirq() 콜백을 실행하고 그 결과를 반환하거나,
      정의되지 않은 경우 0을 반환합니다.
  int pm_generic_restore(struct device *dev);
    – 그 디바이스의 드라이버가 제공하는 ->restore() 콜백을 실행하고, 성공하면,
      그 디바이스의 런타임 PM 상태를 ‘active’로 바꿉니다.
  int pm_generic_restore_noirq(struct device *dev);
    – 그 디바이스 드라이버가 제공하는 ->restore_noirq() 콜백을 실행합니다.
이 함수들은 서브 시스템 레벨 dev_pm_ops 구조체 안에 있는
->runtime_idle(), ->runtime_suspend(), ->runtime_resume(), ->suspend(),
->suspend_noirq(), ->resume(), ->resume_noirq(), ->freeze(), ->freeze_noirq(),
->thaw(), ->thaw_noirq(), ->poweroff(), ->poweroff_noirq(), ->restore(),
->restore_noirq() 콜백 포인터로 지정될 수 있습니다.
서브 시스템이 동시에 이 모두를 사용하길 원한다면, 그 dev_pm_ops 구조체 포인터에
간단히 include/linux/pm.h 에 정의된 GENERIC_SUBSYS_PM_OPS 매크로를 사용할 수
있습니다.
시스템 suspend, freeze, poweroff와 런타임 suspend 콜백을, 이와 유사하게 시스템
resume, thaw, restore와 런타임 resume 콜백을 같은 함수로 사용하고 싶은 디바이스
드라이버들은 include/linux/pm.h 안에 정의된 UNIVERSAL_DEV_PM_OPS 매크로의
도음으로 이를 담을 수 있습니다(최대한 가장 마지막 인자는 NULL로 셋팅).
8. “No-Callback” 디바이스
어떤 “디바이스들”은 그냥 그들 부모의 논리적인 서브 디바이스이고 그들 자신만의
파워를 관리할 수 없습니다. (전형적인 예제는 USB 인터페이스입니다. 전체 USB
디바이스는 저전력 모드로 들어가거나 웨이크업 요청을 보낼 수 있지만, 각자의
인터페이스들을 위해서는 불가능합니다.) 이들 디바이스들을 위한 드라이버들은
런타임 PM 콜백을 가질 필요 없습니다; 콜백이 있다면, ->runtime_suspend() 와
->runtime_resume()은 언제나 어떤 것도 하지 않고 0을 반환하고, ->runtime_idle()
은 언제나 pm_runtime_suspend()를 호출할 것입니다.
서브 시스템들은 pm_runtime_no_callbacks() 를 호출함으로써 이들 디바이스에 대해서
PM 코어에게 알려주게 됩니다. 이것은 디바이스 구조가 초기화된 후, 그리고 그것이
등록되기 전에 (디바이스 등록이 초기화 된 후라 하더라도 또한 괜찮음) 수행되어야
합니다. 이 루틴은 디바이스의 power.no_callbacks 플래그를 셋팅하고, 디버깅 용도가
아닌 런타임 PM sysfs 속성들의 생성을 막을 것입니다.
power.no_callbacks가 셋팅되었을 때, PM 코어는 ->runtime_idle(), ->runtime_suspend(),
또는 ->runtime_resume() 콜백을 실행하지 않을 것입니다. 대신에, suspend와 resume은
언제나 성공할 것으로 여겨질 것이고 idle 디바이스는 suspend되어져야만 합니다.
그 결과로서, PM 코어는 디바이스의 서브 시스템이나 드라이버에게
런타임 파워 변경에 관해서 절대 직접 알리지 않을 것입니다. 대신에, 그 디바이스의
부모의 드라이버는 그 부모의 파워 상태가 바뀌면 그 디바이스 드라이버에게 알려줄
책임을 가져야만 합니다.
9. Autosuspend, 또는 자동적으로 지연된 suspend
디바이스의 파워 상태를 바꾸는 것은 자유롭지 않습니다; 그것은 시간과 에너지 모두
요구됩니다. 디바이스는 오직 상당한 시간동안 그 상태로 남아있을 것으로 생각되는
어떤 이유가 있을 때에만 저전력 모드로 들어가야 합니다. 공통적인 휴리스틱은
어느 정도 사용되지 않은 디바이스는 사용되지 않을 책임이 있다고 말합니다;
이에 따르면, 드라이버는 어떤 최소한의 시간동안 비활성화 상태가 되기 전까지는
디바이스가 런타임에 suspend하도록 허용해서는 안됩니다. 그 휴리스틱이 최적이
아닌 것으로 밝혀지더라도, 여전히 디바이스가 저전력과 풀파워 상태 사이에 너무
빠르게 “튀는” 것을 막아야 할 것입니다.
“autosuspend” 용어는 역사적으로 남은 부분입니다. 이 것은 디바이스가 자동적으로
suspend되는 것을 의미하지 않습니다(서브 시스템이나 드라이버 역시 여전히 적절한
PM 루틴의 호출을 가집니다); 차라리 런타임 suspend는 원하는 비활동 시간이 지나면
자동적으로 지연됨을 의미합니다.
비활동은 power.last_busy 필드에 기초해서 결정됩니다. 드라이버들은 일반적으로
pm_runtime_put_autosuspend()를 호출하기 전에 I/O 를 수행한 후에, 이 필드를
업데이트 하기 위해서 pm_runtime_mark_last_busy() 를 호출해야만 합니다. 비활동
시간의 원하는 길이는 정책의 문제입니다. 서브 시스템들은 이 길이를
pm_runtime_set_autosuspend_delay() 를 호출함으로써 초기에 설정할 수 있습니다.
그러나 디바이스 등록 후에 그 길이는 유저 공간에 의해
/sys/devices/…/power/autosuspend_delay_ms 속성을 사용해서 제어되어야만 합니다.
autususpend를 사용함에 따라, 서브 시스템과 드라이버는 반드시
pm_runtime_use_autosuspend()를 (더 좋게는 디바이스 등록 전에) 호출해야만 하고,
그 이후부터 다양한 *_autosuspend() 헬퍼 함수들을 autosuspend가 아닌 대응물
대신에 사용해야만 합니다:
이 것 대신: pm_runtime_suspend    사용: pm_runtime_autosuspend;
이 것 대신: pm_schedule_suspend   사용: pm_request_autosuspend;
이 것 대신: pm_runtime_put        사용: pm_runtime_put_autosuspend;
이 것 대신: pm_runtime_put_sync   사용: pm_runtime_put_sync_autosuspend.
드라이버들은 또한 autosuspend가 아닌 헬퍼 함수들의 사용을 계속할 것입니다;
그 것들은 autosuspend delay를 취하지 않고 보통의 경우처럼 수행될 것입니다.
비슷하게 power.use_autosuspend 필드가 셋팅되어 있지 않으면 autosuspend 헬퍼
함수들은 autosuspend가 아닌 대응물들처럼 수행될 것입니다. 

어떤 상황 하에서 드라이버나 서브 시스템은 디바이스가 그 사용 카운터가 0이고
autosuspend delay 시간이 만료되었다 하더라도, 즉시 autosuspend되는 것을 막고
싶어할 것입니다. ->runtime_suspend() 콜백이 -EAGAIN 이나 -EBUSY를 반환하고,
다음 autosuspend delay 만료 시간이 (그 콜백이 pm_runtime_mark_last_busy()를
호출했다면 보통 벌어지는 것처럼) 좀 나중이라면, PM 코어는 자동으로 autosuspend를
리스케줄링할 것입니다. ->runtime_suspend() 콜백은 디바이스가 suspend되어 있는 동안,
(즉, 콜백이 실행 중인 동안) 어떤 종류의 suspend 요청도 받아들이지 않으므로 그 스스로를
리스케줄링할 수 없습니다. 

그 구현은 인터럽트 컨텍스트 안에서 비동기적인 용도를 위해 잘 짜여져 있습니다.
그러나 PM 코어는 ->runtime_suspend() 콜백을 I/O 요청의 도착과 동기화할 수 없으므로,
이러한 사용은 어쩔 수 없이 경쟁상태와 관련됩니다. 동기화는 반드시 드라이버에 의해서
그 안의 개별적인 락킹을 사용하여 처리되어야만 합니다.
뼈대 Pseudo-code 예제:

foo_read_or_write(struct foo_priv *foo, void *data)
{
        lock(&foo->private_lock);
        add_request_to_io_queue(foo, data);
        if (foo->num_pending_requests++ == 0)
                pm_runtime_get(&foo->dev);
        if (!foo->is_suspended)
                foo_process_next_request(foo);
        unlock(&foo->private_lock);
}
foo_io_completion(struct foo_priv *foo, void *req)
{
        lock(&foo->private_lock);
        if (–foo->num_pending_requests == 0) {
                pm_runtime_mark_last_busy(&foo->dev);
                pm_runtime_put_autosuspend(&foo->dev);
        } else {
                foo_process_next_request(foo);
        }
        unlock(&foo->private_lock);
        /* Send req result back to the user … */
}
int foo_runtime_suspend(struct device *dev)
{
       struct foo_priv foo = container_of(dev, …);
       int ret = 0;
       lock(&foo->private_lock);
       if (foo->num_pending_requests > 0) {
               ret = -EBUSY;
       } else {
               /* … suspend the device … */
               foo->is_suspended = 1;
       }
       unlock(&foo->private_lock);
       return ret;
}
int foo_runtime_resume(struct device *dev)
{
       struct foo_priv foo = container_of(dev, …);
       lock(&foo->private_lock);
       /* … resume the device … */
       foo->is_suspended = 0;
       pm_runtime_mark_last_busy(&foo->dev);
       if (foo->num_pending_requests > 0)
               foo_process_requests(foo);
       unlock(&foo->private_lock);
       return 0;
}
중요한 점은 autosuspend를 위해 foo_io_completion() 요청 후에 foo_runtime_suspend()
콜백이 foo_read_or_write()와 경쟁할 것이라는 것입니다. 그러므로 foo_runtime_suspend()
는 suspend 진행이 허용되기 전에 (개별 락을 잡고 있는 동안) 펜딩된 I/O 요청이 있는지
확인해야만 합니다.
덧붙여, power.autosuspend_delay 필드는 유저 공간에 의해서 어느 시점에나 바뀔 수 있습니다.
드라이버가 이 것에 대해 처리하려면 개별 락을 잡고 있는 동안 ->runtime_suspend() 콜백 안에서
pm_runtime_autosuspend_expiration() 를 호출할 수 있습니다. 이 함수는 0이 아닌 값을 반환하면,
delay는 아직 만료되지 않았고, 그 콜백은 -EAGAIN을 반환해야 합니다.

[Android] adb & fastboot 관련 명령 정리

자주 쓰는 명령 몇 개만 정리하자.

adb shell [명령] : shell 또는 명령 실행(‘cat /proc/kmsg’ 라던가..)
adb reboot :  reboot
adb reboot [bootloader | recovery] : reboot & [fastboot | recovery]모드 들어가기
adb logcat [TAG:LEVEL] [*:S] : logcat, 특정 TAG를 LEVEL(S,V,I,D,…)로 보기, *:S 넣으면 나머지는 Silent.
adb root : root 로 adbd 재실행
adb remount : system 파티션을 rw로 remount
adb push, pull : 파일 넣기, 빼오기, read/write 시스템 콜 이용으로도 사용 가능.
adb wait-for-devices [명령] : adb 연결 기다렸다 명령 실행
adb devices : 붙었나?
adb sideload <파일이름.zip> : OTA패키지인 파일이름.zip 을 adb 를 통해 업데이트. JB부터 추가

fastboot flash <파티션 이름> <파일명> : 파티션 이름에는 bootloader, boot, zimage, ramdisk, system, userdata, cache, recovery 등이 올 수 있다.
fastboot wait-for-devices : 윈도우즈는 그냥 명령 주면 대기하므로 필요없다. 리눅스용에서 사용.
fastboot [-w] flashall : 윈도우즈에서는 “set ANDROID_PRODUCT_OUT=D:\out\target\product\crespo” 해두고 하면 된다. -w 옵션주면 userdata 등 파티션 날리고 모두 flash
fastboot [-w] update <zip 파일이름> : update용 zip 파일(make updatepackage로 생성)로 업데이트, 역시 -w 옵션 주면 나머지 날림.
fastboot reboot-bootloader : fastboot 모드로 reboot
fastboot reboot : reboot.
fastboot getvar <Variable> : Variable 을 얻어옴. 전체는 all(“fastboot getvar all”)
fastboot devices : 붙었나?