[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 번호는 맞춰줘야 함!

update-alternatives

프로그램이 버전업 되거나 같은 이름으로 여러 다른 프로그램을 사용하고 싶을 때 다음과 같이 이용한다.

update-alternatives –install /usr/bin/<프로그램이름> <프로그램이름> <프로그램경로> 1
update-alternatives –config <프로그램이름>

예를 들면, 나는 p4v 가 계속 버전업될 때마다 다음과 같은 식으로 사용할 p4v 를 고른다.
사실 ln -s 와 같은 식으로 소프트 링크를 바꿔주는 것과 동일하다.

update-alternatives –install /usr/bin/p4v p4v /opt/p4v-어쩌고저쩌고/bin/p4v 1
update-alternatives –config p4v

[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을 위한 빠른 응답과 검출
오버헤드 사이의 트레이드 오프입니다.