arm linux kernel에 system call 추가 Mar 18, 2008

기본적으로 뼈대는 x86과 동일하다. 약간의 매크로가 틀릴 뿐인데.. 중요한 것은 제일 나중에..

먼저 커널의 다음 3 파일을 수정하여 새로운 system call을 추가하자.

1. arch/arm/kernel/calls.S
2. include/asm-arm/unistd.h
3. 해당 system call을 구현 할 부분(따로 분리해서 구현해도 상관없고 기존의 아무 소스 파일에나 구현해도 상관없다. 단지 makefile만 잘 수정해준다면)

마지막으로 해당 system call을 사용하는 application을 작성하여야 한다.

#include "/home/barrios/work/xxx/linux/include/asm-arm/unistd.h"
#include

_syscall1(int,barrios_write,int,arg);
_syscall1(int,barrios_t,int,arg);

int main()
{
int i;

i = barrios_write(2);
i = barrios_t(2);

return 0;
}

자. 컴파일 하자.

arm_unknown-linux-gnu-gcc -o barrios_syscall barrios_syscall.c

에러 뜨나??
barrios_syscall.c:5: error: expected declaration specifiers or ‘...’ before ‘barrios_write’
barrios_syscall.c:5: error: expected declaration specifiers or ‘...’ before ‘arg’
barrios_syscall.c:5: warning: data definition has no type or storage class
barrios_syscall.c:6: error: expected declaration specifiers or ‘...’ before ‘barrios_t’
barrios_syscall.c:6: error: expected declaration specifiers or ‘...’ before ‘arg’
barrios_syscall.c:6: warning: data definition has no type or storage class

그럼 다음과 같이...

arm_unknown-linux-gnu-gcc -o barrios_syscall barrios_syscall.c -D__KERNEL__

cache line bouncing에 관하여 Mar 10, 2008

시스템에 관련된 서적이나 기사들을 보면 cache에 관해서 많은 용어들이 나온다.
cache thrashing, cache line bouncing, cache snooping, cache invalidation 등등.


명확한 용어를 사용하지 않는다면 듣는 사람이나 말하는 사람 모두 피곤하기 마련이다.
먼저, 가장 흔히 사용하는 용어는 cache line bouncing 이다.
Cache는 일반적으로 공간 지역성을 최대한 활용하기 위하여 cache line size만큼 한번에 메모리의 데이터를 cache line에 올려 놓게 된다. 현재 Intel의 core2duo와 같은 경우는 이 크기가 64 Byte에 해당한다. 

예를 들어 메모리의 0xC0120800의 한 바이트를 read하였다고 할지라도, cache line size가 64바이트라면 0xc0120800부터 0xc0120840까지 의 데이터를 읽어들이는 셈이된다. SMP환경에서 여러 CPU들이 해당 주소를 읽게 되면 각 CPU이 local cache 안에  같은 메모리 주소의 데이터를 캐시안에 공유하고 있게 된다. 
이때, 여러 CPU중 한 CPU가  0xc0120800~0xc0120840의 범위 중 한 바이트라도 변경하게 되면 MESI 프로토콜에 의해 다른 CPU들의 cache line은 invalid되게 된다. 그러므로 다른 CPU들이 그 주소 범위 내의 한 바이트를 읽으려거든 cache miss가 발생하며 memory로부터 다시 읽어와야만 한다.  이러한 것들이 하드웨어적으로 보장이 되며 우리는 Cache Coherency라고 한다.
Cache line bouncing이란 위의 설명과 같이 한 여러 CPU들의 local cache안에 공유되는 메모리 주소를 한 CPU가 변경하여 다른 CPU들의 Local cache도 update해야만 하는 상황을 일컫는다.  일부 사람들은  false sharing이 cache line bouncing이라고 얘기를 하곤 하는데, 그건 아니다.  False sharing은 cache line bouncing을 발생시키는 행동 중의 하나일 뿐이다.  false sharing을 설명하는 글들은 많이 있으니 굳이 설명하지 않는다. 단지 한가지 덧붙인다면 linux kernel의 hot path에서 사용되는 자료구조들은 false sharing을 막기 위해 최대한 자료 구조의 데이터와 그 데이터를 보호하는 Lock은 다른 cache line에 놓이도록 한다.  Cache line bouncing을 제일 많이 일으키는 부분이바로 contention이 심한 데이터에 대한 lock이다.  spin_lock의 test_and_set op는 해당 op를 발생시킨 CPU의 cache안에 해당 주소를 포함하는 cache line을 modify하고 그리고 다른 프로세서들은 자신의 cache안에 있는 데이터 중 방금 수정된 메모리 주소와 관련된 cache line을 소유하고 있는지 감시하게 되는 데.. 이것이 cache snooping이다.  cache snooping을 통해서 다른 CPU의 local cache들이 invalid 모드로 바뀌는 것이다. 
일반적으로, cache line bouncing이 굉장히 많이 발생하는 것을 cache thrashing이라고 한다. cache trashing은 SMP,CMP와 같은 멀티 프로세서 구조에서 심각하게 성능을 저하할 수 있으며 그 주범인 spin_lock들을 막기 위해 linux kernel은 RCU와 같은 lock free 알고리즘을 사용하여 점차 변화하고 있다. 

Kernel Synchronization에 관하여 Mar 9, 2008

커널의 동기화 관련함수들이 여럿 있다.
이 함수들을 어느 시점에서 어떤 함수들을 사용하느냐는 사실 숙련된 개발자가
아니라면 여전히 많이 혼동스러운 부분이며, 굉장히 찾기 어려운 버그를 만들어 내곤한다.

http://www.kernel.org/pub/linux/kernel/people/rusty/kernel-locking/c214.html#MINIMUM-LOCK-REQIREMENTS

위의 URL은 Rusty russel이 작성한 문서이며 여러분들이 가지고 있는 커널의 /Documentation/에서도
구할 수 있는 문서이다. 커널의 locking에 관해서 예제와 함께 상당히 잘 정리된 문서이다.
하지만 기본적으로 동기화 facility들에 대한 개념이 없다면 위의 표가 잘 이해되지 않을 것이다.

커널에 관심이 많은 개발자라면 이미 softirq와 tasklet에 관해서는 이해하고 있을 것이다.
이미 많은 linux kernel 관련 서적에서 다루고 있으니 굳이 따로 설명하진 않는다.

위의 표를 이해하기 위해 우리가 알고 있어야 하는 중요한 것이 3 가지 있다.

1. 인터럽트 핸들러가 실행중일 때 다른 인터럽트 핸들러들이 저절로 block되어 지는 것은 아니다.
(많은 개발자들이 잘못 생각하고 있는 부분이다. 물론 IRQF_DISABLED 플래그를 통해 인터럽트 핸들러를 등록하면 커널은 사용자의 의해 등록된 ISR을 실행할 때 인터럽트 전체를 disable하여 시스템의 모든 인터럽트가 발생하지 않게 해줄 수 있긴하다. 이런 행동은 굉장히 지양해야 하는 행동이지만 제공하긴 한다. 커널의 default 행동은 그렇지 않다는 것이다.)

2. 한 CPU의 인터럽트가 disable되었다고, 다른 CPU도 disable되지 않는다. 즉 인터럽트 핸들러 A를 실행하지 못하게 하려고 CPU A에서 disable해봐야, 인터럽트 핸들러 A는 CPU B에서 실행될 수도 있다는 것이다. 참고로 하나의 인터럽트 핸들러가 동시에 여러 CPU에서 실행되지 않는 다는 것은 커널이 보장해 준다.

3. softirq는 다른 softirq를 선점하지 않는 다는 것이다. 이는 softirq를 이용하여 만들어진 tasklet에도 그대로 적용된다.

4. 부록으로 하나만 더 알고 넘어가자. User context가 무얼까? 커널에 관련된 책이나 자료들을 보다 보면 Interrupt Context, User Context(Process Context)에 관해 가끔 말이 나온다. User context는 실행중인 User 영역의 프로세스를 의미하는 것이 절대 아니다. 여러분들은 구분을 current로 하면 된다. 현재 시점에서 current(current가 무엇인지 모른다면 아직 이 글을 보지 말길 권유한다)가 지금 커널이 행하고 있는 일의 주체인가를 판단하면 된다. 예를 들어 인터럽트가 발생했다고 가정하자. 커널은 인터럽트 처리를 바쁘게 하고 있다. 그 시점에서 current는 누가 될지 모른다. 왜냐하면 인터럽트는 비동기적인 이벤트이기 때문이다. 인터럽트를 유발 시킨 프로세스 B, 또는 커널의 특정 서브 시스템이라고 하더라도 실제 그 인터럽트 처리는 프로세스 C가 실행 중인 시점에서 발생하여 처리를 하게 될지도 모르기 때문이다. 이것은 Interrupt Context이다.
반면, User Context는 현재 커널이 프로세스를 대신해서 수행하고 있는 경우이다. 일반적으로 system call(page fault와 같은 exception도 user context로 볼 수 있다. 왜냐하면 이때 커널은 current를 access하기 때문이다)이 이에 속한다. 이 때 프로세스란 user-mode process일 수도 있고 kernel thread일 수도 있다. Anyway, 현재 시점에서의 current는 그 일을 유발한 프로세스를 가리키고 있다는 것이다. 이해가 되었으면 좋겠다.

이 부분 만큼은 기억하고 있어야만 위의 표를 논리적으로 풀어 나갈 수 있다. (우리가 수학을 배울때도 기본 공식은 외우고 있어야 만 하듯이 위는 기본적으로 우리가 알고 있어야 하는 기본이라고 생각하길 바란다.)

위의 3가지를 암기하였다면 위의 표를 풀어 나가기 위해서는 두가지 질문을 던져 보면 된다.

1. A가 B에 의해 선점될 수 있는가?
=> If yes, you should use "[irq/bh] disalbe"
2. A의 critical section이 다른 CPU에 의해 접근될 수 있는가?
=> If yes, you should use "spin_lock".

자, 위의 법칙을 적용해 보자. (문제를 풀 때는 항상 약한(?) 놈을 A로 하며 당신의 시스템은 무조건 SMP라고 하자. SMP로 가정하는 것은 중요하다. 리눅스 커널은 general한 OS이다. 당신의 코드가 반드시 UP의 NON-PREMMPTIBLE에서만 실행된다는 보장은 없다. 그러므로 무조건 SMP를 고려해야 한다. )

예제 1) User Context A, Takelet B

1법칙의 답변 ) yes
2법칙의 답변) yes

당연하다. user context는 인터럽트에 의해 선점될 수 있다.
인터럽트 종료시 softirq가 실행되고 결국 tasklet이 실행되게 된다. 그러므로 tasklet은 interrupt context에서
동작하며 user context A에게는 일종의 인터럽트로 인한 선점으로 볼수 있다. 그러므로 A는 B에 의해
선점될 수 있으므로 1법칙의 답변은 yes이다. 그러므로 우리는 bh를 disable해야 한다. (이 경우, 굳이 irq까지 disable할 필요는 없다. 이유는 따로 설명하지 않는다.)

2법칙의 답변 또한 당연하다.
A의 코드는 SMP 환경에서 언제나 다른 프로세서의 의해 접근 될 수 있기 때문이다. 그러므로 spin lock을 사용해야 한다.

위를 종합해보면 결국 우리는 spin_lock_bh를 사용해야 한다는 것이다.

예제 2) Taklet A, Tasklet B

1법칙의 답변 ) No
2법칙의 답변) yes

2번이 왜 yes인지는 조금만 생각하면 알 수 있다. tasket은 softirq에 의해 실행된다. softirq는 인터럽트에 의해 실행된다. 인터럽트는 어느 CPU에 의해서나 처리 될 수 있다. 즉, tasklet A가 CPU A에 의해 실행중일 때 CPU B는 tasklet B를 실행시킬 수 있다는 것이다. 그러므로 A와 B 두 tasklet들이 서로 critical section을 가지고 있다면 보호되어야 마땅하다. 만일, 두 tasklet 가운데 공유되는 변수가 없다면 굳이 lock을 사용할 필요는 없어지므로 답이 NO일수도 있다.

그럼 왜 1은 NO일까? 미리 얘기했듯이 softirq는 선점되지 않는다라고 이미 얘기하였다.
결국 위를 종합해보면 spin_lock만으로 충분하다.


예제 3) softirq A, Interrupt B

1법칙의 답변 ) yes
2법칙의 답변) yes

이뙈 왜 2법칙의 답변이 yes인지 궁굼할 수도 있을 것이다. 이건 여러분들의 몫.

예제 4) Interrupt A, Interrupt B

1법칙의 답변 ) yes
2법칙의 답변) yes

하지만 이는 주의가 필요하다. 이때는 spin_lock_irqsave를 사용해야 한다는 것이다.
그 이유는 앞의 예제 3가지는 모두 이미 커널의 디자인 단계에서 irq가 enable되어 있다는 것을 보장하고 있기 때문에 우리는 아무 생각 없이 spin_lock_irq를 사용할 수 있었다. 즉 softirq, tasklet, user context에서는 항상 irq가 enable되도록 커널이 설계되었기 때문에 우리는 아무 생각 없이 spin_lock_irq를 사용하면 됐다는 것이다.

하지만, 커널의 인터럽트 핸들러가 호출되는 시점은 irq가 enable되어 있을 수도 있고 disable되어 있을 수도 있다.
(이는 인터럽트 핸들러의 등록시 SA_INTERRUPT와 같은 파라미터를 통해 결정되기도 하며, kernel의 interrupt handler들은 nesting될 수 있기 때문이기도 하다. 즉, interrupt handler A가 수행되기 전, 어떤 interrupt handler들이 어떤 순서로 어떤 일을 했을지 모르기 때문이다.) 그러므로 우리는 함부로 spin_lock_irq를 사용할 수 없고 대신, spin_lock_irqsave를 사용하여 interrupt on/off 상태를 기억하고 있어야 한다.

wordwrap에 관하여 Mar 5, 2008

가끔 커널에 패치를 보낼 때 wordwrap기능 때문에 다른 사람들이 나의 패치를 적용하다 fail이 나오는 경우가 종종 있다.

wordwrap이 무엇인가에 대해서는 다음을 참조하라.

http://mwultong.blogspot.com/2007/10/vim-vi-word-wrap.html

이를 막기 위해서는 mail client 프로그램의 wordwrap기능을 disable해야 한다.
또한 주의해야 할 것이 사용하는 편집기, 본인 같은 경우 vim을 사용하는데 편집기에서도 wordwrap기능을 disable해야 한다.하나 더 주의할 것이 복사를 할 때 vim의 윈도우상에서 그대로 copy and paste를 하게 되면 wrodwrap이 되곤한다. 즉, vim의 yy 명령을 사용하여 복사해야 한다. 그러기 싫으면 gedit등을 이용해서 복사하면 된다.

kerner stack size expansion Mar 3, 2008

mainline은 점차 커널 스택 크기를 줄여가고 있는 추세이다. 반면 실무를 하는 회사에서는??
아마도 스택 크기를 늘려달라고 아우성일 것이다.

문제는 자신들의 드라이버에 있음에도 불구하고 커널의 스택 크기를 늘려 해결하려는 것이다.
개발하다보면 가끔(?) 그런 일들에 부딪히곤 한다.

하지만 사실, 그 어디에서도 커널 스택 크기를 늘리면 왜 안되는지에 대한 이유는 나와 있지 않다. 그냥 다들 그러지 말라고말 할 뿐이지..

생각해보면 이것도 커널 메모리하고 밀접한 관련이 있음을 알 수 있다.
일반적으로 user process들이 사용하는 메모리는 4K page이다.
리눅스 커널에서 가장 많이 쓰이는 페이지도 바로 이 4K page이다.
그래서 리눅스 커널은 나름 4K page들의 캐싱에 신경을 써놨고 할당 전략들은 4K page 할당에 많은 신경을 써 놓았다. 그런데 문제는 커널 스택이다.

커널 스택은 x86을 제외하곤(4K) 내가 아는 architecture는 다 8K이다. 8K 할당은 메모리가 충분하면 문제 없지만 최대한 메모리를 모두 써버리는(?) 리눅스의 페이지 캐시 전략에서는 언젠가 메모리는 회수되기 시작할 것이다.

문제는 이 때 발생한다. order가 큰 메모리 요청일 수록 회수 알고리즘은 잘 동작하지 못한다.
그나마 최근 lumpy reclaim으로 인해 좀 나아지긴 했지만, 4K 만 못한 것이 사실이다.

그래서 커널 스택을 4K로 줄이면 줄였지 8K 이상으로 확장하려는 생각은 별로 하지 않는 것이 좋다. OOM killer를 만나보고 싶지 않다면~~

KGDB가 merge될 것 같다

http://lwn.net/Articles/270089/

Linus가 그렇게 싫어하던 KGDB가 Ingo Molnar의 각고의 노력으로 2.6.26 쯤에 merge될 것 같다.
사실 Linus는 개발자들이 디버깅 툴을 쓰는 것을 좋아하지 않는다. 사람을 생각하지 않게 만들기 때문이다. 하지만 요즘은 생각이 약간 변한 듯 하다.
어쨌든 많은 개발자들은 반기는 기색이다.