EOF of 2009 Dec 31, 2009

이렇게 의지와는 관계없이 2009년도 가버린다.
마음같아서는 잡을 수만 있으면 보내고 싶지 않은 해이다.

올해에 무엇보다 가장 큰 변화는 지금 내 무릎위에 앉아 나의
모니터를 함께 쳐다보고 있는 녀석이 생겼다는 것이다. 그 만큼 또
내 욕심이 늘었다. 자꾸 자꾸 버리는 것 없이 늘어만 가는 욕심에 갇혀
힘들적도 있지만 아직은 견딜만 하다.

돌이켜보면 그다지 후회가 남는 일은 없는 것으로 보아 선방한
해였다. 최근 몇가지 안 좋은 모습으로 스스로에게 약간 짜증이 나있는
상태이긴 하지만 금방 좋아질 것이라고 낙관한다.

올 한해 가장 와 닿았던 것은 내가 어찌 할 수 없는 일을 어찌해보려고
하는 것은 분명 정신건강에 좋지 않다는 것이다.
어찌 할 수 없었던 것은 능력이 부족한 나의 잘못임이 자명하니 능력밖의
일을 못한다고 투덜거려봐야... 부족하면 키우면 되는 것이고.
그런데 문제는 별로 키우고 싶지 않은 것들이 더 많다는 것이다.
절대적인 상생을 위한 타협이 필요하다.

내년을 위한 소망들도 몇가지 있다. 지금 막 떠오른 것도 있고 예전부터
계획한 것도 있고, 최근 수정된 것도 있다. 내가 어찌한들 가는 2009년
막을 재간은 없고, 계획된 2010년은 오고 있으니 선택의 여지가 없다.

다윈의 진화론을 기반으로 "한해 한해 진보하지 못한다면
나는 인간이 아니지 않을까?"는 궤변으로 올 한해 마무리하며, 내년에도
계속 인간일 수 있길 기대한다.

주위 모든 고마운 분들 내년에도 계속 행복하길 바란다.

Drop Andorid drivers in Staging tree Dec 27, 2009

최근 Greg은 Andorid를 staging tree에서 제거하기를 결정하였다.
그 이유는 다음과 같다.

"Google and no one else stepped up to maintain them, so they will be dropped. So sad..."


나도 슬프다. 어쩌다가 일이 이렇게 되었는지 모르겠다.

사실 구글의 커널 패치들은 mainline guy들과 여러차례 trouble을 일으켰다.
Barrios는 모든 패치를 review하진 못하였지만 low memory killer 패치는
review한적이 있으며, 그것이 얼마나 ugly한 코드였는지 언급한 바 있다.

몇몇 MM guys들은 심지어 구글의 커널 코드들을 staging tree에
add하는 것 조차 거부하였었다.

그 이유는 Google의 소극적인 태도와 코드 개선의 의지를 보여주지 않았었기 때문이다.
그럼에도 불구하고 Greg의 의지로 staging tree로의 입봉은 임베디드 리눅스 입장에서
상당히 고무적인 일이었다. Google의 의지와는 관계없이 Greg의 판단에
의해 merge되었기 때문이다.

하지만 결과가 이러하다. Open Source Community의 힘을 모르지 않는
구글에서 나온 결과이기 때문에 더욱 놀랍다. 무슨 생각으로, 왜 그러한 방향으로
driven한 것인지 잘 모르겠지만 Embedded Linux의 Best Practice가 하나 사라진 것에 대
해 상당히 아쉽다.

RCU mistakes Dec 24, 2009

http://lwn.net/Articles/366718/
http://lwn.net/Articles/366717/

지난 기사에 Thomas의 rwlock 제거 노력에 대한 언급을 하였었다.
지난주 corbet은 Thomas의 패치에 대한 기사를 또 다시 실었다.

주의하고 넘어가야 할 문제인 듯 싶어 언급한다.

Thomas는 tasklist_lock을 RCU로 바꾸려는 노력 가운데, 몇몇 코드들이 RCU를 잘못사용하고 있는 case를
발견하였다. 가장 흔히 하는 실수가 rcu_read_[un]lock을 사용하지 않는 것이다.

개발자들이 그런 실수를 하는 이유는 quiescent state를 보장받기 위해 해당 CPU의 preemption을
disable하면 충분하다고 판단하였기 때문이다. 맞다. Hierarchical RCU가 merge되기 전까지는 말이다.

HRCU는 RCU의 grace period를 위해 cpumask와 cpumask의 각 CPU에 해당하는 bit을 보호하기 위한
spinlock 사용으로 인하여 떨어지는 scalability의 문제를 극복하기 위해 추가된 feature이다.
또한 Classical RCU는 모든 CPU가 quiescent state를 거쳐야 하기 때문에 잠자고 있는 CPU마저도
모두 깨워야 하는 문제가 있다. 이는 16개의 core중 4개의 core만이 주로 사용되고 있다고 가정할 경우(
흔한 경우라 한다.) power consumption에 있어 큰 문제가 된다.

이 feature를 사용하게 될 경우, preemption disable만으로는 충분하지 못하다.
개발자들이 흔히 했던 실수라는 것은 caller가 이미 preemption을 disable하고 있을 경우(또는
preemption_disable primitive가 이미 포함된 spinlock 구간 또는 interrupt disable 구간),
굳이 rcu_read_lock을 호출하지 않았던 것이다. 이는 충분히 문제가 되는 경우이며, 진작에 문제가
터졌어야 정상인데 불행히도 아직까지 CONFIG_TREE_PREEMPT_RCU가 널리 사용되지 않고 있었기 때문이다.

이러한 부분들이 수정되가며 점점 tasklist_lock 또한 RCU로 바뀌어가고 있다.
이제 정말 얼마 안 남은 것 같다. rwlock이 사라질 날이.

Eliminating rwlocks and IRQF_DISABLED Dec 13, 2009

http://lwn.net/Articles/364583/

조만간 rwlock이 커널에서 사라질 것 같다.
rwlock은 unfair 하기로 유명한 lock이다.

write side는 reader side가 모두 release될 때 까지 기다려야 하며, 심지어는
기다리는 도중 새로운 reader side들 막지 못한다. 그러므로 계속되는 reader side는
system의 live lock 상황을 유발할 수 있게 된다.

위와 같은 상황은 상당히 발생하기 드물기는 하지만 최근 Nick은 이 문제로 인하여
심하게 performance regression이 발생하는 문제를 report하였으며 regression을
발생시키지 않으며 fair한 rwlock 구현을 생각하고 있다.

이를 위한 soluation으로는 reader side 의 lock grab을 write side가 기다리고 있는
한 허용하지 않게되면 간단하다. 허용하지 않겠다는 것은 reader side를 block시키겠다는
것인데 말만큼 간단한 문제가 아니다. 이는 rwlock이 nesting이 허용되는 primitive를
근간으로 한다.

예를 들어 Processor 1에서 reader side의 lock을 hold하고 있는 상태에서
Processor 2에서 해당 자원의 write lock을 요구한다고 하자.
Processor 2의 task는 spinning하게 될 것이다. 이때 다시 Processor 1이 interrupt되며
interrupt handler안에서 해당 자원을 다시 read lock하게 될 경우 deadlock 상황이 발생
할 수 있게 된다.

또 다른 해결책으로써는 lock을 잡을 때 IRQ를 disable해버리면 쉽게 해결 된다.
하지만 커널 전체의 regression은 이로 말할 수 없게 될 것이다.

rwlock에 대해서는 오랫동안 커널내에서 없애자는 움직이이 있어왔다. rwlock의 cost가
일반 spinlock에 비해 상당하기 때문이다. 그러므로 lock에서 소모되는 cost보다
훨씬 큰 benefit이 있지 않는 한 rwlock을 사용하는 것은 무모한 일이다.
그래서 많은 rwlock들이 spinlock, RCU등으로 바뀌어 왔다. rwlock을 spinlock으로
바꾸는 것은 굉장히 쉬운 일이지만 문제는 tasklist_lock이다.

이 lock은 커널의 core에서 많이 사용되어지고 있으며, 굉장히 critical한 fast path에서
사용되고 있다. 더욱 큰 문제는 tasklist_lock의 오남용은 실제 lock이 보호하는 것이
어떤 data인지 현재 명확하지 않고 있다는 것이다. 그러므로 함부로 lock을 바꿔서 생기게
되는 performance regression, stale data 문제등을 한번에 해결하기는 쉽지 않은
일이 되어 버렸다. 그래서 많은 커널 개발자들은 tasklist_lock을 손대는 것을
꺼려하여 왔다.

하지만 Thomas Gleixner가 이 문제를 해결하기 위해 칼을 빼들었다. LWN의 기사가
나올때까지는 어떤 움직임도 없었지만 최근 LKML에 tasklist_lock을 없애기 위한 patch들이
포스팅 되고 있다. Thomas Gleixner가 이 문제를 해결하게 되면 나머지 rwlock은 쉽게
제거될 수 있다. 결국 커널의 rwlock을 볼 날도 이제 얼마 남지 않았다.

IRQF_DISABLED

오래전 리눅스는 top half자체도 자체도 "fast", "slow"로 나눠져 있었다고 한다.
해서 fast interrupt handler는 모든 IRQ를 disable한 상태에서 빠르게 처리를 했고,
slow irq interrupt handler는 IRQ를 enable하여 다른 IRQ의 처리를 허용한 상태에서
동작하였다고 한다. 그렇지 않게 되면 다른 더 중요한 인터럽트들의 처리를 지연시켜
전체적인 시스템의 성능을 down시켰기 때문이다.

하지만 점차 CPU의 성능이 좋아지고, Device들이 똑똑해지며 device driver들 또한
성숙하여 점차 IRQ를 enable하고 처리해야 하는 slow path가 점차 사라지게 된 것이다.
이에 요즘 드라이버 개발자들은 인터럽트 핸들러를 등록할 때 IRQ를 enable하고 실행해야
할지 말아야 할지 조차 신경쓰지 않는다. 그럼에도 불구하고 여전히 인터럽트 핸들러를
등록할 때 우리는 인수를 신경써야 한다. 게다가 더 재밌는 것은 shared interrupt handler들은
IRQ를 disable되어 실행된다는 것을 보장받을 수 조차 없다는 것이다. 왜냐하면 해당
IRQ line에서 실행된 다른 interrupt handler가 IRQ를 enable할 수 있기 때문이다.

해결책은 간단하다. IRQF_DISABLED 인수를 없애버리고 모든 IRQ handler가 실행될 때
IRQ를 disable하는 것이다. 하지만 여전히 IRQ를 enable하고 실행해야 할 상황은 있다.
예를 들어 A interrupt handling의 time이 여전히 길다던가, 또는 A interrupt handling은
B의 interrupt handling에 의존적이라던가. 이와 같은 경우를 해결하기 위해서는 먼저 긴
시간이 길어지지 않게, 서로 다른 2 IRQ에 의존적이지 않게 redesign을 하는 것과,
또는 interrupt handler를 thread IRQ handling으로 처리되게 바꾸는 것이다.
thread IRQ handling은 선천적으로 모든 IRQ가 enable된 상태에서 동작하는 특성을
가지고 있기 때문이다. 그래도 안될 경우 마지막 보루로써 IRQF_NEEDS_IRQS_ENABLE를
사용하는 것이다.

현재로선 어떻게 될지 명확하지 않지만 머지 않아 모든 IRQ hanlder들은 IRQ를
disable한 상태로 수행되게 될 것이며, 해당 요구를 만족하지 못하는 드라이버들의 개선이
이루어질 것으로 보인다.

Toward a smarter OOM killer Nov 15, 2009

http://lwn.net/Articles/359998/

최근 Kame는 OOM patch series의 review를 부탁했다.

http://lwn.net/Articles/359886/

이 문제는 얼마전 mailing list에 올라왔던 어떤 응용 개발자의 한건의 report에서 시작한다.

시스템의 메모리가 부족하여 OOM이 발생하였는데, 실제 물리 메모리를 많이 사용하던
프로세스는 따로 있는데 X 또는 Gnome-Session이 죽는 경우가 발생한다는 것이다.
이 문제를 파악해본 결과 우리는 현재 OOM의 victim 선정에 다소 문제가 있다고
판단하였다.

Victim을 선정하는 데 있어, 현재 실행중인 프로세스의 가상 메모리 주소와
그 프로세스의 하위 프로세스들이 사용하는 가상 메모리 크기를 고려하는 것이
make sense하지 않다고 생각한 것이다.

위와 같은 알고리즘 덕택에 gnome-session이나 X와 같이 많은 자식 또는 많은 라이브러리를
사용하는 프로세스들은 실제 사용되는 물리 메모리와는 상관없이 상당히 큰
oom_score를 갖게 된다.

이에 barrios는 vm size가 아닌 rss size를 고려하기를 제안하였으며,
이를 바탕으로 kame가 구현하기 시작한 것이 이번 LWN의 주제인 OOM killer 패치이다.

http://lkml.org/lkml/2009/10/27/26



Kame와 barrios는 fork-bomb process를 구별하는 방법에 관해서도 의견을 나누었었지만,
barrios의 의견은 커널이 그것 까지 고려하는 것은 overhead가 크다는 것이었고,
그렇게 해서 알수 있다하더라도 정확한 구별을 할 수 없어 결국 innocent process가 여전
히 죽임을 당할 수 있다고 판단하였다. 이에 kame는 barrios 의견에 동의하였으며,
최근 KDE가 session manager를 위해 oom_adjust를 조정하도록 변경되었다는 점에 주목하며
일단락을 맺는 것으로 생각하였으나, 얼마 후 이 patch series를 만들었던 것이다.

이 패치에 대한 첫 인상은 굉장히 인상적이라는 것이다. 패치의 주된 내용은 다음과 같다.

1. fork-bomb process 식별 방법,
2. 오랫동안 실행되며 찔끔찔금 메모리 누수를 하는 프로세스 식별 방법
3. low memory를 가장 많이 사용하는 프로세스 식별 방법.
4. kill해서 가장 많은 메모리를 회수할 수 있는 프로세스
5. 최근 실행된 프로세스들에게 페널티를 주는 루틴의 삭제

위에 열거한 것들이 의미하는 것은 fork-bomb 프로세스, 메모리 누수 프로세스,
low memory를 많이 사용하는 프로세스, 현재 가장 많은 물리 메모리를
사용하고 있는 프로세스를 kill하겠다는 것이다. 이 중에 low memory 문제는
메모리 할당에 있어 low memory를 필요로 할때, (즉 커널이 물리 메모리를 필요로
하는 경우가 대부분 이 경우에 속한다|) low memory를 많이 사용하는 프로세스를
kill하겠다는 것이고, 나머지는 일반적인 경우이다.

LWN에서도 언급한 것과 같이 이 패치에 대해서 우려의 목소리가 있다. 너무 급진적인
변경이라는 것이다. 그 우려의 목소리가 barrios의 목소리이다.

개인적으로 barrios는 fork bomb detector는 현재의 OOM heuristic에 add되어 사용될 수
있을 것이라고 생각하였지만, 나머지들은 OOM hurisitic 계산의 큰 변경은 현재와는
상당히 다르기 때문에 차근차근 하나씩 변경해 나가는 것이 이미 널리 사용되고 있는
상용 운영체제의 문제 해결 과정이라고 생각한다.

최근 이 패치는 아직 RFC 단계이며, swapout page의 수를 체크하는 것 까지 mm tree에
merge되었으며 아직 갈길이 멀다. 이 문제는 위에서 언급하지 않았는데 swapout된
페이지를 많이 갖는 것은 그 만큼 많은 메모리를 사용했다고 볼 수도 있지만, 꼭
그렇다고만은 볼 수 없으며, OOM killer가 이런 프로세스를 죽인다고 해서 현재 가용한
물리 메모리를 확보하는 것은 아니다.

어쨌든 swap page count는 꼭 이 경우가 아니더라도 여러가지 유용함으로 현재 merge가
된 상태이다.

barrios는 OOM에 관한 또 다른 생각을 가지고 있는데 기회가 되면 나중에 한번 더
정리하도록 한다. 왜냐하면 고이자던 아들이 깨고 말았다.

How Google uses Linux Nov 8, 2009

http://lwn.net/Articles/357658/

구글보다 더 많이 Linux를 사용하는 조직은 없을 것이다. 하지만 kernel community는
구글이 어떻게 리눅스를 사용하고 있으며 어떤 문제들을 겪고 있는지 아는 바가 거의 없었다.
왜냐하면 구글은 kernel community에 모습을 잘 드러내지 않기 때문이다. 기껏해야
David Rientjes, Paul Manage정도가 주로 활동을 하고 있으며, 지금 우리나라의 개발자들이
관심이 많은 구글의 Android kernel 개발자들은 메일링 리스트에서 가끔 눈에 띄긴 하지만
그리 active하게 활동하진 않는다. 구글의 Android 커널 팀은 오늘의 주제와는 관련 없는
팀이며 오늘의 얘기는 구글의 서버를 maintain하고 있는 커널팀에 대한 얘기이다.

이번 JLS(Japan Linux Symposium)에서 구글의 커널팀 Mike Waychison은 Linux kernel community
에게 아주 흥미로운 주제들 몇가지 던지고 갔다. 비록 barrios는 JLS를 참가할 수 없어
직접들을수는 없었지만역시나 Jonathan Corbet이 잘 정리를 해주어서, LWN 기사로나마
그날의 흥미로움을 대신 전한다.

생각보다 구글의 커널 조직은 크지 않았다. 대략 30명 정도의 개발자로 이루어져있으며
Andrew Morton은 이 팀에 속하지 않는 것으로 판단된다. Kernel의 형상관리는 Git이 아니라
Perforce라는 툴을 사용한다고 한다. 시작을 관리 툴을 언급하며 Git을 사용하지 않는다는
것에 대해 멋적어서인지 웃음으로 시작하였다고 한다. 리눅스 커널을 Git을 사용하여 관리하지
않는다는 것이 그 만큼 창피한 일이 되어버린 것이다. 구글의 Perforce에는 단지 하나의
base tree만이 있을 뿐이며, 모든 개발자들이 그곳에 commit하고 있다.
17개월마다 mainline kernel을 base로 그 tree를 다시 rebase하고 있어 항상 porting에
애를 먹는다고 한다. Porting이 정상적으로 동작하기 시작하면 6개월마다 내부 release를
한번씩 한다. 그들이 Porting에 애를 먹는 것은 그들의 feature가 드라이버들이 아닌
core 패치들이기 때문이다. 이를 미루어 볼때 30여명의 core 개발자들로 이루어진 구글의
커널팀이 마냥 부럽기만 하다.

구글은 이와 같은 작업을 2.4.18부터 시작해왔으며 2000개 이상의 패치들 492,000 line정도를
수정했다고 말하고 있다. 대부분의 작업은 kernel core에 관한 작업들이다.
그래서 더욱 Porting에 있어 더욱 민감하며, 결국 구글은 일의 효율성을 위하여
mainline kernel community와 더욱 tightly 협력하도록 모든 것을 변경할 계획을 가지고 있다고
말하고 있다.

구글은 지금 2.6.26을 base로 하여 다시한번 rebase를 진행하고 있다.
2.6.26에 1208개의 패치를 하고 있으며, 300,000 line의 코드가 들어가고 있고, 이중의 25%는
새로운 feature라고 언급하고 있다. 이에 Linus는 물었다.
"왜 구글은 그러한 기능들을 upstream 올리지 않느냐? 구글은 그 코드들이 창피하냐?
아니면 보안해야 할 사항이냐? 그것도 아니면 내부 프로세스 문제이냐?" 라는 질문에
Mike는 간단히 "창피하다"라고 말했다. 2.4.18로부터 온 굉장히 ugly stuff라는 것이다.
또한 그러한 feature들이 다른 곳에 도움이 될지도 내부적으로 의문이 든다고 판단하고
있다는 것이다. 하지만 아마 코드의 반정도는 upstream될 수 있다고 보고 있다.
Mike가 솔직하게 대답하고 있다. 대부분의 회사가 그럴 것이다. 사실 mainline에 merge될
정도의 code quality로 일을 하는 회사가 Redhat, SUSE, IBM, Intel 정도를 빼고는
몇이나 있을까? 다들 자신들의 제품의 특성에 맞게 동작할 수준의 코드들로 QA나 Review없이
일단 동작하기만 하면 넘어갈 것이다. 회사의 특성상 바쁘다는 핑계로, 여력이 없다는 핑계로
말이다. 이렇게 넘어가자고 한 관리자의 말에 두 손을 들어버리면 결국 나중 그 관리안된 코드를
기반으로 눈덩이처럼 불어난 코드들과 그로 인한 버그들로 그때 그 관리자한테 뒤통수를 맞는
것은 다시 그 개발자가 될 뿐이다.

구글은 그 사실을 잘 알고 있으며, 더욱 중요한 것은 실천을 한다는 것이다.
별것도 아닌 기술을 IP라고 포장하여 언제까지나 Close하는 회사들에 비해서는 약간 앞서
있지만 솔직히 구글도 그러고 있을 것이라고는 생각치 못했었는데. barrios는 개인적으로
그렇게 생각하고 있었다.

현재 구글은 커널 소스 관리를 위해 Git으로 옮겨가고 있으며(이렇게 새로운 형상관리 툴로
전진해나가는 곳이 있는가 하면 후진하는 곳도 있다. 관리의 효율화와 일원화라는 명목하에 주객
이 전도되는 상황을 barrios는 늘상 보곤한다), 3달마다 한번씩 mainline kernel에
대해 rebase할 계획을 가지고 있다고 한다. 3달마다 한다는 것은 결국 mainline version의
출시와 발을 맞추겠다는 의도로 생각할 수 있으며, 그렇게 하려는 이유는 결국 개발자들의
code maintainace를 좋게 만들기 위해서 그리고 upstream kernel과 보조를 맞추기 위해서이다.
왜 upstream kernel과 보조를 맞추어야만 하는지에 대해 따로 설명하지 않겠다. 최소한
이 블로그의 글을 보는 사람이라면 이 문제에 대해서는 barrios가 굳이 설명을 안해도 될 것이다.

지금까지 구글이 이렇게 mainline kernel과 괴리가 있었던 것은 3가지 이유에서 기인한단고
barrios는 생각한다.

첫째로 그들이 사용하는 커널은 소프트웨어 회사 중 가장 큰 server farm을 자랑하는 server
들을 위한 커널이며,
둘재로 그들은 그 커널을 마음대로 주무를 수 있는 capability를 가지고 있다는 것이다.
셋째는 하지만 그들은 게을렀다는 것이며 이제는 그 결정을 다시 생각해야 할 때라는 것을
알고 JLS에서 세션 하나를 맡아 주었다는 것이다. ^^;

기사에 구글이 mainline kernel을 사용하며 겪었던 문제들을 열거하고 있다.
하나씩 살펴보기로 하자.

구글은 CFS로 넘어간 스케줄러에 대해서 별로 좋아하지 않고 있다.
구글은 user space lock mechanism을 위한 별도의 library를 가지고 있는 듯하다.
이에 문제가 발생한 것은 sched_yield의 semantics가 CFS가 도입되며 변했다는 것이다.
기존의 semantic은 가장 높은 우선순위의 태스크가 sched_yield를 호출할 경우,
같은 우선순위의 다른 task가 없다면, 자신이 다시 제어권을 받을 수 있음을 보장하였다.
하지만 CFS에서는 그렇지가 못하다. 이는 그들이 사용하는 locking관련 라이브러리에서의
high priority 쓰레드들의 sched_yield 함수 호출은 다른 task로의 스케줄링으로 이루어지며
많은 load balancing을 현상을 만들어 낸다는 것이다. 구글은 16~32개의 core의
시스템에 5000개의 쓰레드들을 돌리고 있기 때문에 이는 심각한 성능 문제를 야기할 수 있다.

무엇보다 그런 big thread design의 문제는 mmap_sem의 lock contention이 심하다는 것과 signal을 large thread group에보낼때 많은 run queue들의 lock contention이 심하다는 것이다.
특히 mmap_sem에 관해서는 다음의 시나리오도 가능하다. 쓰레드 A,B,C가 있다고 가정하자.

A - readlock - Wait I/O
B - writelock -> Wait to complete readlock
C - readlock -> Wait to complete B's writelock

즉, A의 readlock으로 인해 B가 대기하게 되고, B의 writelock으로
인해 C의 readlock이 대기하는 상황에서, 갑작스레 A가 OOM kill이 된다고
가정하자. A,B,C를 포함하는 프로세스에 대한 mm관련 일들은 모두 halt될 것이며
이 프로세스가 어떤 프로세스냐에 따라 시스템의 halt까지 유발할 수도 있다.

mmap_sem 관려해서는 현재 Christoph가 per-cpu allocator와 mutex를 혼용하는
방법에 대한 새로운 패치를 내었다. barrios는 그 패치에 대한 몇가지 문제점을
지적해 놓은 상태이며 그 중의 일부 아이디어가 반영되어 다음 릴리즈에 반영되었다.
하지만 그보다 심각한 문제가 있기때문에 아직은 RFC 단계이며, 그 패치가 merge가
될 경우, mmap_sem의 readpath는 RW semaphore를 사용하는 것이 비하여 굉장히 빨라질 수 있다.

메모리 관리 측면에서는 dirty bit의 관리가 변경되며 kswapd는 small I/O operation들에
대해 예전에 비하여 잦은 writeout을 발생시키게 된다. 이는 다른 writeback들을
starving시키게 될 수 있다. 이 문제는 2.6.32에 merge될 per-BDI writeback에
의해 fix될 수 있다.

구글은 시스템의 성능이 overload되는 것을 막기 위해서 OOM killer를 사용하고 있다.
하지만 위에 언급한 것과 같이 oom kill되는 프로세스가 mutex를 hold하고 있을 경우,
시스템은 큰 문제에 빠질 수 있게 되는 문제를 가지고 있다. Mike가 궁금해했던
"memory가 tight하다면 단순히 allocation을 실패시키면되지, 왜 그렇게 열심히
OOM kill을 수행하는지"에 대한 답변은 kernel의 allocation은 그렇게 할 수 있으며
이미 그렇게 하고 있다. 이는 OOM이 아니라 page allocaion failure이다.
하지만 user page는 그렇게 할 수 없다. 그 이유는 Linux kernel은 Demand Paging을
사용하기 때문에 allocation을 하는 시점과 paging in되는 시점이 다르기 때문이다.
더 자세한 이야기는 오늘의 주제가 아니니 넘어가도록 하자.

그래서 구글은 지금 무엇을 하고 있는가?
구글은 그들의 work들을 3가지로 분류한다.

"latency sessitive", "production batch", "best effort" 그래서 이 분류들을
fake NUMA node 기능을 이용하여 분리하고 각 job들을 성격에 맞춰 하나 이상의
노드들에 assign하는 것이다. 구글이 add한 기능은 barrios의 충분한 관심을 끈다.
NUMA-aware VFS lru를 적요하고 있기 때문이다.
이는 예전에 Nick Piggin이 했던 것과 유사하며 그는 구글의 코드들을 보길 원해왔다.

구글은 SCHED_GIDLE 스케줄링 클래스를 만들었다. 어떤 spare CPU도 있지 않을 경우,
이 클래스에 job들은 실행되지 않을 것이다. 이때 발생할 수 있는 priority inversion 문제를
풀기 위해 SCHED_GIDLE 프로세스들이 커널에서 sleep하게 될때는 일시적으로 priority를
높이게 된다.

구글의 I/O scheduleing의 경우에는 proportional I/O scheduling을 사용하고 있다고
한다. 그것 말고도 구글은 시스템 monitoring을 위한 많은 코드들을 add하고 있어,
disk, network traffic을 monitor하고 기록해두어 나중에 operation 분석을 위해
사용한다고 한다.

다음은 구글은 2010년까지의 목표이다.

* CPU limitations - latency-sensitive한 태스크들에게 더 높은 우선 순위 접근을
주면서도 전체 시스템을 장악하는 것은 막게 하겠다는 것.

* RPC-aware CPU scheduleing - 들어오는 RPC traffic을 살펴보고 어떤 프로세스를 응답으로 깨울 것이며 그 wakeup이 얼마나 중요한 지를 결정하는 것.

* Delayed scheduling - 대부분의 쓰레드들에게 있어 latency는 중요하지 않다. 하지만 kernel은 RPC msg가 들어왔을 때 즉시 그 쓰레드들을 실행시키려고 시도한다. 이 msg들은 CPU들에 고르게 분산되지 않아 심각한 load balancing 문제를 야기한다. 그래서 쓰레드들은 delayed scheduling으로 tagging될 수 있다. 즉, wakeup이 도달하면 즉시 run queue에 들어가는 것이 아니라 실제로 runnable해지기 전에 다음 global load balancing operation이 발생할때 까지 기다린다.

* Idel cycle injection - machine을 항상 최고 power로 실행할 수 있도록 power-management를 하겠다는 것, 하지만 system이 melt down되게 하지는 않겠다는.

* 커널 메모리 사용까지 accouting하는 향상된 memory controller

* offline memory - 저렴한 가격으로 메모리를 구입하는 것이 점점 어려워 진다는 판단하에 단지 몇 개의 cell만이 bad가 났음에도 전체 메모리를 교체하는 것은 옳지 않다는. 해서 HWPOISON을 통해 bad page들만을 따로 관리하겠다는.

* 동적으로 huge page들을 assemble하고 broken down하기 위한 방법

* block layer overhead를 줄여 high-speed flash의 속도를 최대한 내겠다는. block layer에서 disk 가속을 위해 flash를 사용하겠다는 것이다.

마지막으로 Mike는 몇가지 흥미있는 주제들을 던지며 그날 세션을 끝마쳤다.
구글이 원하는 것 중의 하나는 file system metadata를 memory에 pinning하는 것이다.
해서 I/O request를 service하는데 걸리는 시간을 bound하겠다는 것이다.
디스크로부터 하나의 블록을 읽는데 걸리는 시간은 알수 있지만,
관련 metadata가 메모리에 있지 않은 경우, 한번 이상의 disk I/O 이상이 필요해진다.
이것은 시스템을 느리게 만들고 있다고 말하고 있다. 구글은 현재 file data를
user space에서 raw disk device로부터 직접 읽음으로써 이러한 문제를 우회하고 있다고 한다.
하지만 언제까지 그렇게 할 수는 없고, 이제는 멈추고 싶어한다.

다른 문제는 caching advise를 제공하기 위한 system call overhead를 낮추는 것이다.
이 문제는 명확하지 않다.

Anyway, 이번 구글의 세션은 kernel community가 가장 큰 customer로부터 많은 걸 배울 수
있었던 성공적인 session이었다. 구글은 앞으로 더욱 큰 결실을 맺기 위해 community와
협업을 더 적극적으로 할 계획을 가지고 있으며 이는 모두에게 더 훌륭한 커널을 안겨줄 수
있는 좋은 기회가 될 것이다.

barrios는 위와 같은 선진 소프트웨어 회사들의 그 중에서도 커널 팀들의 행보를 지켜보며,
우리의 현수준을 직시하고, 우리가 가야할 길에 대한 생각을 멈추지 않을 것이다.
비록 지금은 먼 것 처럼 느껴지더라도, 조만간 쓸 일이 있지 않을까 한다.

2.6.32 merge plan Sep 16, 2009

http://lkml.org/lkml/2009/9/15/471

2.6.32를 위한 merge plan이 발표되었습니다. by Andrew Morton.
것이 의미하는 것은 그 동안 mm tree에서 잘 성숙되어 온 patch들중,
Andrew Morton과 Subsystem maintainer들에 의해 어떤 patch들을
이번에 mainline으로 밀어 넣을 것인지를 결정하는 일입니다.

밀어 넣어진 patch들 또한 rc1~rc8 or rc9이 될 때 까지 약 3개동안 mainline
tree에서 테스트를 거치게 됩니다.

이번 2.6.32를 위한 patch들 중 특이한 점은 memory management patch들이
대거 포함되었다는 겁니다. 즉, 속된 말로 잘 되면 대박, 못되면 쪽박이 될 수도 있습니다.
(제가 개발팀장이라면 2.6.32 커널은 되도록 피하라고 지시할 것 같습니다.)

memory management 쪽의 패치가 많을 수 있었던 것은 “후지쯔 가이”들,
즉 일본인들이 mm 쪽의 개발에 부지런히 참여했었고,
늘상 부지런하던 Hugh, Rik, Mel, 그리고 VFS layer에 주로 놀던 Wu의 mm 참여,
반대로 mm쪽에서 많은 활약을 하던 Nick의 VFS layer로의 이동.
그리고 한국의 hobbyist정도가 정도가 바쁘게 움직인 덕입니다. ^^

그럼 3달동안 어떤 regression들이 생기는 지 두고 보기로 하죠.

꼬랑지)

MM쪽의 패치를 만들어 나가는 과정은 약간 복잡합니다.
물론 다른 쪽도 얼마든지 복잡해 질 수 있지만, MM쪽은 시스템에 critical한
변경을 가하는 것이기 때문에, 하나의 패치에도 여러 사람들이 관계하게 됩니다.
먼저, 최초 문제의 목격자 reporter(Reported-by),
그 문제를 풀기 위한 패치의 저자(signed-off-by),
패치를 review해주는 리뷰어(Reviewed-by)
패치에 대한 승인을 해주는 영향력 있는 인물들의 승락(Acked-by)
패치를 test해주는 테스터(Tested-by),
등이 관계합니다 이밖에도 Andrew Morton은 해당 패치의 관계자들,
즉 그 패치에 대해서 argue가 있었던 사람이나 해당 패치를 꼭 알고 해야
하는 사람들을 patch description에 Cc하곤 합니다.
MM쪽은 이렇듯 하나의 패치를 만들기 위해 많은 사람들이 작업을 하게 되며,
관계하게 됩니다.

아 그리고 mm tree가 Memory Management Tree가 아닙니다.
쓰고 나니 mm tree가 그럼 무엇의 약자냐고 물어보는 이가 있어 부연합니다.
mm tree는 memory management의 약어이긴 합니다.
하지만 mm tree의 기능은 더 이상 mm만 care하지 않는다는 것입니다.
mm tree는 memory management 뿐만이 아니라 다양한 subsystem들도 포함하며,
마치 예전 홀수 버젼의 test version이라고 생각하시면 대충 맞습니다.
참고로 mmtom은 "MM of the moment or MM of the minute"의 약어입니다.
Andrew도 잊어먹었다고 하더군요.

Offline Scheduler Sep 12, 2009

http://lkml.org/lkml/2009/8/22/89

최근 mainline의 hot issue중의 하나이다.
multi or many core system에서 CPU중 하나를 스케줄러에서 offline시키고 해당 CPU에
특정 job을 binding하여 OS noise없이 사용하자는 것이다. 즉, task를
특정 CPU에 dedication시키는 설비를 만들자는 것이다.
시간이 있으신 분들은 그 쓰레드들을 모두 읽어보아라. 현재 약 81개의 쓰레드가 달려
있는데 상당히 재미있다. barrios의 나쁜 기억력은 모든 내용을 기억할 수 없다.
생각나는 부분들만을 위주로 정리한다.

먼저 Christoph와 같은 HPC guy는 굉장히 찬성하는 입장이고, Peter, Ingo, Thomas와
같은 기존 scheduler나 HRT maintainer들은 반대의 입장이다.
Christoph가 찬성하는 이유는 간단하다. 기존까지 약간의 솔루션들이 있었지만,
offline scheduler와 같이 간단하게 모든 OS noise를 제거할 수 있는 방법은 없었다는
것이다. HPC에서는 CPU 1 cycle이라도 불필요하게 OS를 위해 App가 양보할 수 없다는
생각을 가지고 있기 때문이다. 그 생각은 barrios도 공감한다. 시스템의 주인공은
응용 프로그램들이니깐. 아. Offline scheduler의 동작원리를 간단히 설명하면(
그럴수 밖에 없다. 원래 간단하니까). CPU Hotplug facility를 사용하게 되어 있다.
즉, Offline 시키고 싶은 CPU를 Unplug시켜버리며 미리 등록되어 있던 커널 함수를
실행시키는 것이다. 이 커널 함수가 결국 태스크의 entry point가 될 것이다.

그렇다면 Peter, Ingo와 같은 maintainer들이 반대하는 이유는 무엇인가?
문제를 우회하고 있다는 것이다. OS noise에 대한 latency가 문제라면,
해당 문제를 해결하여 Linus system 전반의 문제들을 해결해야지, 그런식의
work around는 오히려 앞으로 나오게 될 좋은 접근의 다른 시도들을 막을 수
있다는 것이다. 중요한 이유는 그런 식의 OS의 제어하에 벗어나게 되는 것을
달가워하지 않는 것 같다. 얘기는 다소 잘못된 방향으로 나아가며 Chrisoph와
Perter, Ingo가 감정의 날을 세우다가 Andrew Morton이 한마디 했다.
"Problem State가 무엇이냐?"고 다들 구현을 가지고 따지고 있는 가운데
다시 토론을 본론으로 가져왔다. 또한 Andrew는 것이 필요한 일이고, Design이
깔끔하고, 유지보수가 용이하다면 merge하지 못할 이유가 없다고 입장을 밝혔다.

여기서 또 다시 Andrew의 view를 알 수 있다. 유지보수의 용이성이다. 제 아무리
좋은 기능을 잘 구현한다 하더라도, 조그만 변경이 시스템에 cirtical한 변경을
가할 수 있거나, 다른 subsystem의 변경에 민감하게 반응하는 패치라면 Andrew는
상당히 꺼려한다. 왜? 그는 Linux kernel의 maintainer이기 때문이다. 다양한 시도들
중 정말 nice한 것만을 선정하여 잘 유지하는 것이 그의 일이기 때문이다.

우리도 본론으로 다시 돌아와서, 이때 Thomas가 involve된다.
Thomas는 문제의 핵심을 언급했다. 결국 이 문제는 한 CPU에서 하나의 Task가
timer interrupt의 방해만 받지 않으면 되지 않겠느냐고 얘기하고 있다.
물론 해당 CPU에 HARD IRQ, SOFT IRQ등의 event가 발생할 수도 있다.
하지만 해당 CPU의 task가 irq를 발생시키는 device와 interaction하지 않는다면
irq는 다른 CPU쪽으로 affinity를 주면 된다. 그 얘기는 결국 SOFTIRQ도 해당 CPU에서
disable 할 수 있다는 얘기이다. 반대로 그 Task가 특정 device와 interaction한다면
해당 irq를 그 CPU와 affinity를 주면된다. 단 가정은 그 Task만 그 device와 interaction
해야만 할 것이다. 그렇지 않으면 다른 task의 job에 해당 CPU가 시달리게 될 것이다.

Thomas와 다른 개발자들의 논의 방향은 이 방향으로 흘렀다.
scheduler에서 특정 priority이상으로 설정된 task가 CPU에 binding될때, tick interrupt를
off시켜버리자는 것이다. 다음 그 task가 schedule out될 때 다시 tick interrupt를 enable하고
그때 accounting을 갱신하자는 얘기 중심으로 무게가 쏠리고 있다.

이 쓰레드에서 중요한 것은 대부분의 개발자들이 needs에 대해서는 공감하고 있다는 것이다.
결국 이 문제는 manycore환경에서 CPU들을 어떻게 잘 활용할 것인가와 연결될 것 같다는게
barrios의 생각이며 offline scheduler가 굳이 아니더라도 새로운 형태의 task가 OS noise없이
실행 할 수 있는 형태의 기능이 mainine에 merge될 것이라고 생각된다.

RT task and mlock race Sep 10, 2009

Linux에서의 Real Time?
시작부터 거창하다. 오늘 이 거창한 주제에 대해서 얘기하자는 것은 아니다.
이 주제는 거의 반나절 이상 얘기가 되야하는 주제이고..

오늘 얘기하고 싶은 것은 RT task와 Mlock system call과의 문제이다.
사실, Linux RT에 가장 큰 걸림돌은 Memory Subsystem이라고 생각한다.
Ingo나 Robert과 같은 많은 개발자들이 Linux의 RT feature를 향상시키기
위해 다각도로 노력을 해왔지만 아직까지 개척되지 못한 부분이 Memory 부분이다.

현재 Linux kernel의 memory allocation/reclaim routine은 절대 deterministic
하지 못하기 때문이다. 오늘 이 얘기를 하려고 하는 것이 아니니 여기서 각설하고.

RT task를 사용하는 환경에서 page fault overhead를 줄이기 위해서
mlock system call을 흔히 사용한다. 또는 Montavista의 RT programming guide를 봐도,
또는 세미나를 들어도 mlock을 사용하라고 권한다.
하지만 다음과 같은 경우 큰 문제를 발생시킬 수 있다.

1. taskset 0x2 ./test
A. => Top과 Top 환경에서 숫자 1번 키를 통해 CPU1이 100% 소모되고 있는 것을 확인
2. sudo chrt –fp 55 `pidof test`
A. RT task로 변환
3. dd if=/dev/zero of=test.dat bs=4K count=1000000
A. 4G의 파일을 생성하는 명령하나 수행
4. .taskset 0x4 /test_mlock
A. 3번째 CPU에서 Memory lock을 수행하는 application 수행
3번째 CPU에서 실행되는 test_mlock 응용이 mlockall system call에서
멈추어 더 이상 실행되지 않는 것을 확인할 수 있다. 만일 시스템에 top을 수행시킬
수 있다면 해당 프로세스의 state를 봐라. 'D' state일 것이다.
그리고 시간이 좀 지나게 되면 운이 좋지 못하면 시스템 전체가 먹통이 되는 현상을
볼 수 있을 것이다.

./test

#include

int main()
{
int i = 0;
while(1) {
i+=1;
}
}

./test_mlock
#include
#include

int main()
{

printf("mlock test\n");

printf("before mlock\n");
mlockall(MCL_CURRENT|MCL_FUTURE);
printf("after mlock\n");
return 0;
}


자. 도대체 시스템에서 무슨 일이 일어났을까?
CPU2에서 RT task가 user space에서 CPU를 100% 소모하며 실행되고 있다.
이때 CPU3에서의 응용 프로그램이 mlockall system call을 수행한다.
mlock system call은 kernel 내부에서 동작할 때, 먼저 per CPU page_vec
에 저장되어 있는 lru pages들을 lru list로 drain하게 되어 있다.
이때 문제가 발생한다. 모든 CPU들의 page_vec에 pages들을 drain하기 위해서
커널은 workqueue를 사용하며 lru drain이 모두 완료될 때 까지 기다린다.
즉 해당 work들을 flush한다는 말이다. 이때 CPU[1,3,4]는 문제가 안된다.
문제가 되는 녀석은 RT task를 수행하고 있는 CPU2이다.
CPU2의 RT task는 자발적으로 제어권을 놓지 않는 한 kenrel은 CPU2를 위한 제어권을
받을 수 없다(timer tick을 제외하고. 하지만 timer tick이 우리의 상황을 해결해
줄 수는 없다.). 그렇게 되면 CPU2의 eventd가 수행될 수 없고, 그 결과 CPU3에서 실행한
lru_add_drain_all(모든 CPU의 page_vec을 flush하는)은 완결될 수 없다.
즉 CPU3의 응용 프로그램은 CPU2에서 수행되고 있는 RT task의 execution time에
dependency를 갖게 된다.

문제는 더욱 커질 수 있다. CPU2의 eventd에 pending되어 있던 work들이 수행되지 못할 경우,
그 work의 event 완료를 기다리는 모든 task들은 모두 freeze되는 효과를 볼 것이다.

이는 결국, 시스템 전체의 hang으로 이어질 수 있다는 것이다. RT task를 무한루프로 생성하는
테스트가 다소 비약일 수 있다고 생각할 수도 있다. 하지만 문제의 요지는 RT task의 무한루프가
아니다. 문제는 CPU3에서 실행된 task가 CPU1에서 실행되고 있는 task보다 더 높은 RT prio를
가질수도 있으며 이와 같은 상황에서의 mlock system call은 priority inversion problem을
만든다는 것이다. 물론 kernel에 많은 priority inversion problem 중의 하나일 뿐이지만,
문제는 mlock system call이 RT task들이 흔히 사용할 수 있다는 데서 있다고 본다.

barrios는 사실, mlock system call의 경우 굳이 page_vec을 drain할 필요가 없다고 생각한다.
그 이유는 해당 page들은 결국에는 PG_mlocked될 것이며, unevictable list로 옮겨갈 것이기 때문이다.

다른 개발자들의 의견을 기다려 보자.

Static Trace Point에 관하여 Sep 1, 2009

http://lwn.net/Articles/245671/

좀 지나간 기사이지만, 갑자기 생각이 나서 읽어보았다.

Adding a test-and-jump operation to a kernel hot path will always be a hard sell; the cache effects of referencing a set of global marker state variables could also be significant.

http://thread.gmane.org/gmane.linux.kernel/568586

Immediate values are used as read mostly variables that are rarely updated. They
use code patching to modify the values inscribed in the instruction stream. It
provides a way to save precious cache lines that would otherwise have to be used
by these variables.

위의 2개의 link는 kernel marker가 구현될 당시,
관련 기사와 관련 패치의 일부이다.

인용구를 보면 알겠지만 mainline kernel developer들은
test-and-jump instruction 하나를 없애기 위해서도
저렇게 애를 쓴다. 왜냐하면 그 문제를 풀지 못하면 mainline에
merge되기 어려울 테니. 왜 merge되기 어려울까?

kernel marker는 kernel의 static trace point의 일부이다. 그 얘기는 커널이
release되더라도 enable 되어야 한다는 것이다. 이 논리에 대해서
이해가 가지 않는가?

static trace point란 것은 dynamic probing과는 성격이 틀리다.
static trace point는 이미 커널의 잘(?) 정의된
곳에 삽입되어 있어야 한다. 그래서 system administrator들이나
kenrel developer들은 기존에 잘 정의되어 있는 trace point를 기반으로
시스템의 성능을 분석하기 때문이다. 그러므로 사실 static trace point의
hot issue는 성능문제도 있지만 어떤 곳에 static trace point를
정의할 것이냐가 더 중요하다.

다시 본론으로 들어가서 test-and-jump instruction을 제거하기
위해서 왜 그렇게 애를 쓰는가? 기껏해야 instruction 하나 더
수행하는 것인데? 라고 묻는다면.

test-and-jump중 test를 위해 사용되는 shared variable은 귀중한
cache line을 한 line 소모한다. 그런데 shared varible은 하나일까?

1. static trace point 전체를 enable/disable할수 있으면 좋을까? 아님,
각각의 trace point를 enable/disable할 수 있으면 더 좋을까?
2. 그럼 그런 shared varibale가 하나가 필요할까? 두개가 필요할까?
3. 그럼 cache line을 하나만 소비할까? 두개 소비할까?
4. 그런데 이 trace point가 kernel의 hot path 중 하나에 들어간다면.
test를 위해서는 memory를 referencing해야 하고, 그럼 cache line이
하나가 소비될 것이고, 그 얘기는 worst case 기존에 cache line이
하나 eviction되야 하고, hot path라면 이게 반복될 것이고...

결국, trace point가 없을 때에 비해 working set의 많은 부분들이
cache line에서 eviction될 수 있다는 것이다.

왜 갑자기 뜬금없이 barrios가 잘알지도 못하는 kernel의 marker에
대해 이런 부분들을 언급하는가?

Engineer로써 생각해 볼 건 생각해보고 지킬 건 지키자.

꼬랑지) kernel marker와 tracepoint에 대해 많은 사람들이
그 쓰임새를 혼돈하고 있는 것 같다. 다음을 참조하자.
http://ltt.polymtl.ca/tracingwiki/index.php/Tracepoints_and_Markers
Markers are very much like Tracepoints, except that they declare a format string and export the data through a variable argument list. There is a small overhead associated with variable argument lists and the associated interpretation of the format string. The type of the arguments is also normally restricted to scalar types which can easily be described by the format string. The advantage of Markers is that they are self described. They do not require a prior declaration in an include (.h) file, and they can be processed by a generic probe, expecting a printf-like variable argument list.
Markers are thus used for simple ad-hoc instrumentation. Tracepoints are typically used when a formal hook is desired at an important location in the code. A tracepoint in the code is less visually invasive than a marker since it only contains the relevant arguments (no format string). Furthermore, a tracepoint is a general hooking mechanism which may be used for different purposes, one of which being tracing. The disadvantage is that for each tracepoint the developer must provide a prior definition and a corresponding probe.

Bad page state Aug 26, 2009

이러니 Linus에게 반하지 않을 수 밖에.

문제는 8월 초로 거슬러 올라간다.

Title : Bad page state (was Re: Linux 2.6.31-rc7)

8월 1일

Greetings;

2.6.31-rc5 rebooted to before amanda ran last night. The amanda run looks ok,
but I had also plugged in an empty 8GB usb key so I did a quick dmesg to see
where it was, and found this at the end of the dmesg report:

[ 7423.221754] BUG: Bad page state in process tar pfn:a1293
[ 7423.221760] page:c28fc260 flags:80004000 count:0 mapcount:0 mapping:(null)
index:0
[ 7423.221764] Pid: 19211, comm: tar Not tainted 2.6.31-rc5 #1
[ 7423.221766] Call Trace:
[ 7423.221774] [] ? printk+0x23/0x40
[ 7423.221780] [] bad_page+0xcf/0x150
[ 7423.221784] [] get_page_from_freelist+0x37d/0x480
[ 7423.221788] [] ? add_to_page_cache_lru+0x84/0x90
[ 7423.221791] [] __alloc_pages_nodemask+0xdf/0x520
[ 7423.221795] [] __do_page_cache_readahead+0x104/0x220
[ 7423.221798] [] ra_submit+0x34/0x50
[ 7423.221801] [] ondemand_readahead+0x120/0x240
[ 7423.221804] [] page_cache_async_readahead+0x9c/0xb0
[ 7423.221807] [] generic_file_aio_read+0x33c/0x6a0
[ 7423.221830] [] do_sync_read+0xe9/0x140
[ 7423.221835] [] ? autoremove_wake_function+0x0/0x60
[ 7423.221839] [] ? security_file_permission+0x1e/0x40
[ 7423.221842] [] ? rw_verify_area+0x60/0xe0
[ 7423.221845] [] vfs_read+0xb7/0x180
[ 7423.221848] [] ? do_sync_read+0x0/0x140
[ 7423.221850] [] sys_read+0x58/0xa0
[ 7423.221854] [] sysenter_do_call+0x12/0x22
[ 7423.221856] Disabling lock debugging due to kernel taint

The machine seems 100% so far. This kernel was built without that patch that
would make an oom more verbose. htop looks ok, as does slabtop.

Where should I take this?


위와 같은 BUG reporting이 올라왔다.
또한 8월 21일, 다시 한번 비슷한 문제가 같은 사람(Gene)에 의해 보고 되었다.

Aug 21 22:37:47 coyote kernel: [ 1030.152737] BUG: Bad page state in process lzma pfn:a1093
Aug 21 22:37:47 coyote kernel: [ 1030.152743] page:c28fc260 flags:80004000 count:0 mapcount:0 mapping:(null) index:0
Aug 21 22:37:47 coyote kernel: [ 1030.152747] Pid: 17927, comm: lzma Not tainted 2.6.31-rc7 #1
Aug 21 22:37:47 coyote kernel: [ 1030.152750] Call Trace:
Aug 21 22:37:47 coyote kernel: [ 1030.152758] [] ? printk+0x23/0x40
Aug 21 22:37:47 coyote kernel: [ 1030.152763] [] bad_page+0xcf/0x150
Aug 21 22:37:47 coyote kernel: [ 1030.152767] [] get_page_from_freelist+0x37d/0x480
Aug 21 22:37:47 coyote kernel: [ 1030.152771] [] __alloc_pages_nodemask+0xdf/0x520
Aug 21 22:37:47 coyote kernel: [ 1030.152775] [] handle_mm_fault+0x4a9/0x9f0
Aug 21 22:37:47 coyote kernel: [ 1030.152780] [] do_page_fault+0x141/0x290
Aug 21 22:37:47 coyote kernel: [ 1030.152784] [] ? do_page_fault+0x0/0x290
Aug 21 22:37:47 coyote kernel: [ 1030.152787] [] error_code+0x73/0x78
Aug 21 22:37:47 coyote kernel: [ 1030.152789] Disabling lock debugging due to kernel taint


bad_page는 사실 커널에서 거의 볼 수 없는 문제이다.
왜냐하면 mm guy들이 패치를 할 때 굉장히 꼼꼼히 챙기는 부분이기 때문이다.
그럼에도 불구하고 발생했다.

먼저 Linus가 의심한 것은 Wu의 20a0307c0396c2edb651401d2f2db193dda2f3c9 이 패치이다.
이 패치는 /proc/pagemap을 Huge Page까지 확장하기 위한 단순한 변경이었다.
그러므로 그리 가능성이 그리 크지 않아 보였다.

barrios를 비롯한 다른 mm guy들도 그 문제에 대해서
별 다른 언급을 하지 않았다. 그랬던 이유는 특별히 의심가는
부분을 생각할 수 없었기 때문이다.

하지만 Linus는 그 문제에 대해서 꾸준히 관심을 가지고
개인적으로 Gene에게 메일을 더 보내 여러 테스트를 요구한 것으로 보인다.

Gene가 보내온 또 다른 Oops.

Aug 22 22:29:07 coyote kernel: [ 2449.053652] BUG: Bad page state in process python pfn:a0e93
Aug 22 22:29:07 coyote kernel: [ 2449.053658] page:c28fc260 flags:80004000 count:0 mapcount:0 mapping:(null) index:0
Aug 22 22:29:07 coyote kernel: [ 2449.053662] Pid: 4818, comm: python Not tainted 2.6.31-rc7 #3
Aug 22 22:29:07 coyote kernel: [ 2449.053664] Call Trace:
Aug 22 22:29:07 coyote kernel: [ 2449.053672] [] ? printk+0x23/0x40
Aug 22 22:29:07 coyote kernel: [ 2449.053678] [] bad_page+0xcf/0x150
Aug 22 22:29:07 coyote kernel: [ 2449.053682] [] get_page_from_freelist+0x37d/0x480
Aug 22 22:29:07 coyote kernel: [ 2449.053686] [] __alloc_pages_nodemask+0xdf/0x520
Aug 22 22:29:07 coyote kernel: [ 2449.053691] [] handle_mm_fault+0x4a9/0x9f0
Aug 22 22:29:07 coyote kernel: [ 2449.053695] [] ? tick_dev_program_event+0x43/0xf0
Aug 22 22:29:07 coyote kernel: [ 2449.053699] [] ? tick_program_event+0x36/0x60
Aug 22 22:29:07 coyote kernel: [ 2449.053703] [] do_page_fault+0x141/0x290
Aug 22 22:29:07 coyote kernel: [ 2449.053707] [] ? do_page_fault+0x0/0x290
Aug 22 22:29:07 coyote kernel: [ 2449.053710] [] error_code+0x73/0x78
Aug 22 22:29:07 coyote kernel: [ 2449.053712] Disabling lock debugging due to kernel taint


결국, 8월 24일 Linus는 문제를 찾아냈다.

"It's not a kernel BUG!"

그것을 어떻게 알 수 있었을까?
위의 3 Oops를 보면 모두 bad page에서 detection된 것을 알 수 있다.
아주 운이 좋은 경우이다. bad page의 detection은 flag값을
통해 detect 되었음을 알 수 있다.

flag값의 0x4000의 bit이 set되어져 있었기 때문에 bad_page가
호출된 경우이다. 또한 3경우 모든 page의 가상 주소는 c28fc260이다.
즉, x86의 경우 28fc260의 물리 주소에서 문제가 발생하였다는 것이다.
왜 그렇게 생각할 수 있을까?

그럼 생각해보자. 모두 다른 경로를 통해 Oops가 발생하였지만,
희안하게도 Oops를 발생시켰던 곳은 28fc260 번지이고 모든
경우 flag 값이 같다. 즉, 0x4000이 set되어져 있다. PFN은
달라질 수 있다. 왜냐하면 pfn을 계산하기 위한
mem_map을 할당하는 위치가 booting 중 timing에따라 또는
kernel configuration에 따라 달라질 수 있기 때문이다.

하지만 barrios의 생각은 아무래도 booting 중 타이밍
문제보다는 kernel configuration이 틀려지지 않는 한 잘
바뀌지 않을 것 같다. kernel configuration 중에서도
bootmem allocator에 영향을 주는 경우에 한해서 그 값이
달라질 수 있을 것이다.


또한 flag는 page descriptor의 첫번째 멤버이다. 위에서 본 것 처럼
모든 error는 flag값에 의해 발생하였다. 그렇다는 얘기는 28fc260의
물리 메모리에 문제가 있다는 것을 의미할 수 있다. 그러므로 이 문제는
운이 좋게 mem_map이 할당된 메모리의 일부가 깨졌기 때문에 또한 해당
page가 사용중이 아니며 buddy에 free page로 존재하고 있었기 때문에
쉽게(?) 문제를 찾을 수 있는 경우이다. memory corruption이 다른 메모리
번지 였다면 시스템이 runtime에 random하게 crash되는 현상이 발생하였을
것이다.

현재 Linus는 문제가 28fc260의 bad memory로 인해 발생한 것이
거의 틀림 없다고 보고 있으며 Gene 또한 memory test로 인해
memory에 문제가 있음을 확인하였다.

Flexible Array Aug 25, 2009

8월에 글이 많이 부족했다.
회사일로도 많이 바뻤고, 집안일로도 많이 바뻤고, 도저히
블로깅을 할 시간은 내기 힘들었다.

8월이 가기전에 또 다른 시간을 내기 힘들 것 같아, 아들 밥 먹고
있는 틈을 타서 지난번 봐두었던 기사에 대해 정리하기로 한다.

http://lwn.net/Articles/345273/


커널 프로그래밍을 할때 흔히들 큰 메모리 버퍼를 할당해서 사용해야
핲 필요가 있는 경우가 있다. 이때 큰 버퍼를 할당하기 위해서는
메모리 공간을 어떤 API로 확보해야 할까?
이미 우리는 여러 서적이가 기타 article들을 바탕으로 커널에서 big
order page allocation은 실패할 확률이 높다는 것을 알고 있다.
그러므로 kmalloc은 선택할 수 있는 방법은 아니다.
그래도 어쩔 수 없지 않는가? 우리는 big memory chunk가 필요한데.

Big memory가 물리적으로 연속될 필요가 없다면 가장 쉽게 선택할 수
있는 방법은 단연 vmalloc이다. vmalloc은 여러 문제가 있지만,
이번 기사에서 지적하고 있는 문제는 일반적인 32 bit system에서의
linear address space의 부족함이다. 커널이 사용하는 메모리 1G 중
896M는 direct mapped 되어지고, 나머지 공간 중에서도 일부는
kmap, kmap_atomic, fixed_map등을 위해 띠어주고 나면 대략 120M
정도가 vmalloc을 위해 사용가능하다.

또 다른 이유는 SMP system에서의 overhead가 크다는 것이다.
(TLB flush 및 IPI로 인하여. 이 중 많은 부분이 Nick Piggin에 의해
lazy flush방식으로 optimization되었었지만, 아무리 그렇더라도
vmalloc을 사용하지 않는 경우에 비해 overhead가 큰 것은 사실이다.)

그럼 여지껏 개발자들은 어떻게 그런 요구를 만족시켰는가?
답은 여러가지가 있을 수 있지만 일부 취했던 방식은 kmalloc을 통해
할당된 4K page들을 manage하여 array based로 접근하였다. array 방식으로
사용했던 이유는 간단하다. 여러분이 big memory chunk가 필요하다고 하자.
단일 object을 위해 그 메모리 공간을 사용하려고 그 큰 공간이 필요한건가?
그렇다. 대부분은 object pooling을 위해서이다. 그 얘기는 결국 array based
접근하는 것이 효율적이라는 것을 의미한다. object size가 고정이라면 말이다.

이러한 공통적인 need들이 증가하는 것은 결국 새로운 feature가
필요하다는 것을 암시하며, 그래서 나온 것이 vmalloc을 사용하지 않으면서도
large array를 할당하기 위한 general한 framework으로써, Andrew Morton에
의한 idea로 IBM의 Dave Hansen에 의해 구현된 "flexible array"이다.

Flexible array는 임의의 수의 고정된 크기의 array를 할당할 수 있으며,
배열과 같이 index를 통해 접근된다. 내부적으로는 single page allocation을
통해 page들 연결시켜 사용하기 때문에 fragmentation 문제를 발생시키지 않으며,
kmalloc을 통해 할당된 4K page들이기 때문에 vmalloc과 같은 overhead도 존재하지
않는다. 하지만 단점은 array는 직접 addressing할 수 없으며, object의 크기는
시스템의 page size 이내여야 한며, array에 data를 저장하기 위해서는
copy operation이 필요하다는 것이다.

현재 Flexible Array는 mmtom에서 계속 패치되고 있으며, 아직까지 특정 사용자가
존재하지 않음으로 API는 계속해서 바뀔 수 있다. 그러므로 사용법을 굳이 언급하지는
않는다.

OOM livelock problem Aug 3, 2009

2ff05b2b4eac2e63d345fc731ea151a060247f53

이 패치는 6월 16일 Google의 David에 의해 적용된 패치이다.
이 패치의 요는 다음과 같다.

1. oom_adj를 task_struct에서 mm_struct으로 옮긴다.
2. OOM의 livelock 상황을 막는다.

1.의 rationale은 쉽게 생각할 수 있다.
oom_adj의 값은 per-task의 member로는 의미가 없기 때문이다.
생각을 해보자. 특정 프로세스 A의 임의의 한 쓰레드의 oom_adj값을 -17(OOM_DISABLE)로 했다고 가정하자.
OOM killer의 입장에서 한 task를 kill해야 하는 경우,
특정 프로세스 A를 kill하는 것이 make sense한가? 당연히 No!.

왜냐하면 프로세스의 A가 포함하고 있는 쓰레드 가운데 한 녀석이라도
OOM_DISABLE이 설정되어 있다면, mm_struct 즉 해당 쓰레드가 사용하고 있는 주소 공간은
보존되어야 하기 때문이다. 주소 공간이 보존된다는 것은 OOM killer는 메모리를 회수하기위해
프로세스 A를 kill할 수 없다는 것이다.

이는 아래와 같이 oom_kill_task의 함수에서도 볼 수 있다.


366 /*
367 * Don't kill the process if any threads are set to OOM_DISABLE
368 */
369 do_each_thread(g, q) {
370 if (q->mm == mm && q->oomkilladj == OOM_DISABLE)
371 return 1;
372 } while_each_thread(g, q);
373


그러므로 oom_adj란 member는 task_struct의 member보다는 mm_struct의 member가 되는 것이
더 자연스러운 것이다.

2.에서 말한 livelock 상황은 위에서 언급한 문제로 인해 발생한다.
select_bad_process가 프로세스 A를 선택했다고 가정하자.
이전의 예와 같이 A는 OOM immutable한 쓰레드를 가지고 있다고 가정하면
oom_kill_process는 결국 retry하게 될 것이다.

530 retry:
531 /*
532 * Rambo mode: Shoot down a process and hope it solves whatever
533 * issues we may have.
534 */
535 p = select_bad_process(&points, NULL);
536
537 if (PTR_ERR(p) == -1UL)
538 return;
539
540 /* Found nothing?!?! Either we hang forever, or we panic. */
541 if (!p) {
542 read_unlock(&tasklist_lock);
543 panic("Out of memory and no killable processes...\n");
544 }
545
546 if (oom_kill_process(p, gfp_mask, order, points, NULL,
547 "Out of memory"))
548 goto retry;
549 }


이 상황은 OOM immuable 쓰레드가 종료될 때까지 계속 될 수 있으며, 해당 쓰레드가 언제 종료될지는
아무도 모른다. 그러므로 livelock 상황을 유발할 수 있었다.

하지만 이 패치가 또 다른 side effect을 만들어 내기 시작했다.
것은 다음 기회에.

회상이 지나간 오후 Jul 18, 2009

http://tvpot.daum.net/clip/ClipView.do?clipid=12845336

Pop보다는 한국 대중 가요를 더 좋아하는 이유는 영어를
잘 못해서이기도 하지만, 그 가사에서 그 상황에서 느껴지는 그 멜로디와 그 의미를
내 의지대로 해석하지못하기 때문이다.

20살 때의 일이다.

완벽하게 이상적인 생각들로 가득찼던 시절이 있었다.

내 눈으로 바라본 세상을 내 의지대로 해석하는 지금의 모습과는 분명 같지 않으며,
지금의 모습을 상상할 수 없을 정도로 순수하게 이상을 쫓았던 시절이 내겐 분명 있었다.

힘들지만 보람된 시간들이었으며, 너무 다행스러웠던 것은
내 주위 나와 같은 사람들도함께 하였지만, 그렇진 않지만
나를 이해해줄 수 있는 사람들도 있었다는 것이다.

어느덧 시간이 흘러, 군을 나오고, 졸업을 하고, 취업을 하고, 정신 없이 시간을 흘려보내고 나니
그들은 여전히 이렇게 작은 기쁨을 선사한다.

모두들 잘들 살고는 있는 것인지. 나와 같이 이미 그때의 모습을 잃어버린 것은 아닌지..

내밀면 닿을 곳에 있는 그들을 한번 만나봐야 할 것 같다.
누가 먼저 손을 내미느냐는중요하진 않다. 우리들에겐 의미 없는 허세일 뿐이다.

술 많이 먹고 쓰는 건 아니다.그저 적당한 술이 나를 부지런하게 만들어 줄 뿐.

꼬랑지) 적당한 제목을 만들어 주는 툴이 내겐 늘 필요하다.

come back zero_page Jul 14, 2009

http://lwn.net/Articles/340370/

ZERO_PAGE. Linux kernel의 VM에 관심있는 사람들은 한번쯤 들어봤을 법한
설비이다. Linux kernel은 2.6.24시절까지는 zero page를 사용하여 vma에
최초 발생한 read fault는 zero page에 매핑하였다.

이는 다음과 같은 장점을 가져온다.

1. memory save
2. reduced cache pressure
3. eliminating the need to clear the new page

하지만 24때 Nick에 의해 MuliProcessor system에서 cache line bouncing문제가
심각하다고 밝혀져 제거되었다.(물론 revert되기까지는 많은 discussion이 있었다.
avoiding zero page reference counting, per-cpu zero page등)
cache line bouncing이 발생하는 이유는 많은 CPU들이 하나의 zero page를 공유하게
되면서, zero page의 reference counting 때문에 발생하게 되었다.

그 당시에 Nick의 이런 노력에 사실 Linus는 심기가 불편했지만 일단 Nick의 이 패치가
어떤 문제들을 발생시킬지 보기로 하고는 우선 merge했었다. Linus가 zero page의 제거를
싫어했던 이유는 zero page는 리눅스와 함께 태동했기 때문이다. Linux가 세상에 알려지기
시작할 무렵부터 zero page는 거기에 있었으며, 2.6.24까지도 큰(?) 문제 없이 잘 사용되고
있어왔던 것이다. 그러므로 이미 몇몇 application들은 Linux kernel의 이런 특성(zero page)
을 이미 활용하고 있어왔다. 그러므로 kernel에서 zero page를 제거하게 된다면, 그런 특성을
활용하던 application들의 regression은 피할 수 없게 될 것이다.

정확히 18개월이 지나고, 문제가 터지기 시작했다. 마지막으로 카운터를 날린 것은 Kame였다
Kame는 다음과 같이 주장했다. "Kernel이 바뀌어서 regression이 생겼다고, app 개발자들한테
너희들이 다시 app를 개발해야해!" 라고 말할 수는 없는 것 아니냐고.

Linus의 전폭적인 지지와 함께 zero page의 대한 구현이 다시 시작되었다.(물론 reference counting을
피하기 위해 아소 깨끗하지 못한 구현이 되어가고 있을 무렵..). 다시 한번 Nick은 zero page의 도래를
마땅해하지 못하고 있음을 여러모로 피력하였다. 하지만 Linus는 이미 마음을 굳힌 것 같다. 이에 대해
Nick과 Linus가 다소 격양된 어조의 토론이 있었지만...
언제나 그렇듯이, Linus의 승이다.

결국 우리는 머지 않아 zero page를 다시 보게 될 것이며, 그 구현은 그렇게 평이한 수준이 되지는
않을 것이다.

꼬랑지)
barrios의 기억으로 zero page의 제거가 문제가 된 부분 중의 하나는, core파일을 만들어 낼때이다.
core dump는 기본적으로 프로세스의 모든 vma에 page들을 읽어 file로 덤프를 뜬다.
이때 zero page의 제거는 실제 page를 할당받게 됨으로써, vma의 크기에 따라 순식간에 시스템의
memory를 모두 소비할 수 있게 된다. 그러므로 embedded system에서는 oops로 swap을 가지고 있는
일반 시스템 또한 순식간에 swap out이 발생하게 되어 working set들을 버릴 수도 있게 되며,
그 순간 시스템의 응답성을 떨어뜨리게 된다.

core dump에서 이 문제를 어떻게 해결했는지 barrios는 follow up하지 못하였으며, 이 부분에 대한
언급이 없는 것으로 봐서는 해결이 되었을 것으로 기대한다. (그렇지 않다면 역시 나의 몫!)

Perfcounters Jul 6, 2009

http://lwn.net/Articles/339361/

2.6.31의 새로운 기능 중 가장 주목할 만한 것은 모니모니 해도 Ingo의 perfcounters가 아닐까 싶다. 오랫동안 tip-tree에서 성숙된 perfcounters가 드디어 2.6.31의 merge window에 merge되었다.

perfcounters는 Ingo, Peter 등에 의해 최근 굉장히 활발히 개발된 performance analyser이다. 물론 여러분들이 아는 툴 중에 가장 유명한 oprofile과 같은 툴도 있겠지만, perfcounters의 취지는 정말 사용하기 쉬운, 그리고 정말 필요한 기능을 넣겠자는 의도에서 시작되었다.

perfcounters를 개발하면서 perfmon의 개발자 Stéphane Eranianr과 Ingo는 많은 부분에서 의견충돌이 있었다. Ingo는 Stéphane Eranian가 PMU 기능의 많은 부분들을 export하자는 제안에 단호히 저지했다. 요는 다음과 같다.


"A tool might want to do this" is not a good enough answer. We now have a working OSS tool-space with 'perf' where such arguments for more PMU features can be made in very specific terms: patches, numbers and comparisons. Actual hands-on utility, happy developers and faster apps is what matters in the end - not just the list of PMU features we expose.


다시한번 Ingo의 kernel에 대한 철학을 엿볼수 있다. Ingo는 언제난 이런 투의 말을 싫어한다. - "Tool이란 이러한 것을 하길 원할지도 모른다". 막연한 추측 보다는 실제 필요한 것들을 먼저 해보고 데이터를 수집해보고 어떤 데이터가 어떻게 어떤 사용자들한테 유용할지 말하는 것이 결국 happy developer, faster apps들을 만들어 내는 것이다라고 생각한다.

여기서 말하지 않고 넘어간 것이 하나 있다. perf라는 툴이다. perf는 perfcounters를 이용하는 user space tool이다. 이것이 중요한 이유는 이 tool이 커널의 tool 디렉토리에 merge되었다는 것이다. 지금까지 많은 커널 개발자들은 user space 툴들이 kernel에 merge되는 것을 좋아하지 않아왔다. 그럼에도 불구하고 이번 perf는 Linus의 지지를 받으며 입봉하게 된 것이다. Linus는 Oprofile의 예를 들며, 이번 perf의 입봉을 전격 추진하였다. Oprofile userspace tool은 커널의 밖에서 maintain되어왔다. 그러다 보니, 커널의 최신 패치를 항상 한발 늦게, 또는 3~4발 늦게 쫓아왔다. Linus는 항상 이게 불만이었다. Anyway 이게 적절한 예는 아닐지 몰라도, 다른 개발자들의 만류에도 불구하고 Linus는 이번에 한번 그렇게 해보는 게 어떻겠냐고 반문하며 perf의 입봉을 대환영하였다.

"Let's give a _new_ approach a chance, and see if we can avoid the mistakes of yesteryear this time."



어떻게 됐든, 지금 perf와 perfcounter는 merge되었고 이변이 없는 한 revert되지는 않을 것이다. 또한 현재의 perfcounter는 개발 초기와는 달리 굉장히 많은 기능이 추가되고 있으며 barrios가 생각하건데, 1년 안에 Oprofile을 능가하는 멋진 툴이 될 것이라고 생각한다.

현재 barrios 또한 perfcounter에 allocation과 reclaim의 분석을 위한 도구들을 add할 생각을 가지고 있다.

기분 좋은 하루 Jul 4, 2009

간만에 즐거운 술 한잔.

고등교육을 마치고 나서부터, 언제나 즐거웠던.(단 즐거운 사람들과 함께 있는 경우만. 굉장히 까탈스러운 성격으로 인해 어렵거나 유쾌하지 못한 술자리는 되도록 피하는 편이다.)

여러가지 일로 미뤄왔었던 기분좋은 마무리다.

창문을 열어 놓으니 바람이 솔솔 들어온다.
아내는 TV를 보고 있고 아들은 조용히 잠을 자고 있는 이 순간이 삶의 낙이다.

TV에 한 가수가 얼마전 종용했던 드라마의 주제가를 부르고 있다.

나를 믿고 의지하는 가족이 있어서 행복하다.

당장 패치를 하나 만들어야 하는데.. 코드를 볼 정신이 없다.
(Andrew한테 상당히 미안하다.)

끝으로 기분 좋게 울 아들 왕눈이 사진 한장.

Performance degradation seen after using one list for hot/cold pages Jun 25, 2009

얼마전 보고된 내용이다.

http://marc.info/?l=linux-mm&m=124565003222392&w=4

I/O performance가 degradation되었다는 내용이다.
문제의 원인은 Mel의 PCP 패치때문이었다.
PCP는 원래 hot/cold의 분리된 list로 관리되고 있었지만, Mel은 그 2개의 list를 하나로 합치면서
cold page는 뒤에서부터 할당하고, hot page는 앞에서부터 할당하였다.

그래서 발생한 문제가 cold page에 대해서 기존에는 A-B-C-D의 연속된 페이지의 순서로 할당되었던 PFN들이
이제는 D-C-B-A의 order로 할당될 수 있다는 것이다. 이는 연속된 order의 buffer들을 merge하여 처리할 수 있는
I/O device의 성능 저하를 초래하게 된다.

현재 page allocator는 PCP를 채울때 연속된 페이지의 order를 유지하려고 애쓴다. 이 또한 그러한 I/O device들을 염두에 둔 것이다. 하지만 Mel의 PCP 패치가 이 약속을 깨버린 것이다.

Mel은 이 report에 대해서 이미 알고 있었지만 왜 인지 당시에 patch는 만들지 못하였다.
이번 패치는 이 문제를 해결하기 위해서 PCP에 page를 add할때 cold 페이지의 경우에 역순으로 배치하여,
나중에 연속된 page의 순서대로 할당 가능하게 해준다.

이문제에 대해서 barrios가 이 바쁜 시간(대체 요근래 모하고 사는지를 모르겠다. 내가 정말 엔지니어인지에 대하여 심각하게 고민을 하고 있는 중)을 짬내어 언급하는 이유는 이문제가 국내의 기업이 report한 문제이기 때문이며, report를 했던 이는 이미 이 문제의 원인에 대해서 알고 있었기 때문이다.

국내 기업이 이런 critical한 report를 OpenSource 진영에 하는 것은 사실 처음 봤기 때문이다.
국내 기업의 Open Source에 대한 자세가 긍정적으로 변한 것일까?..
하지만 아쉽게도 올린이가 외국인인 것으로 봐서 해외 연구소의 연구원일 것으로 생각된다.
아마도 국내에서는 쉽게 저런 글을 함부로 report하지 못했을 것이다.
아침부터 쓸쓸하구만.

Common OOM killer Problem in Embedded System Jun 23, 2009

금일 report된 Oops이다.

http://lkml.org/lkml/2009/6/22/604

친절하게 Kame-san이 원인을 잘 설명해주고 있다.

이번 문제는 사실 swap device를 가지고 있지 않은 Embedded System에서는 흔한 문제이다.
문제를 살펴보면,
먼저, 메모리 할당 실패는 order 2, 즉 16K의 연속된 메모리 할당에 있어 실패하였다.

Total : 256M
LRU(active + inactive) : 75M
unevictable : 124M
SLAB : 6M
Free + pagetable : 29M

위의 메모리 상태를 보면 시스템의 Memory leack은 아닐것이라고 추측해 볼 수 있다.
또한 위의 정보를 통해 알 수 있는 내용은
1. unevitable이 저렇게 많이 채워져 있는 이유는 tmpfs를 rootfs로 사용했기 때문이라고 추측할 수 있으며
2. file lru가 거의 비워져 있는 것으로 봐서 이미 memory pressure가 상당했다는 것이다.

다음으로 buddy를 살펴보자.

Normal: 1445*4kB 1781*8kB 15*16kB 2*32kB 1*64kB 0*128kB 0*256kB 0*512kB 0*1024kB 0*2048kB 0*4096kB = 20396kB


16k의 연속된 페이지가 15개나 있다. 또한 64k도 하나 있고..
그럼 왜 OOM이 발생한 것일까?

Kame-san이 친절히 얘기해 준 것처럼 zone_watermark_ok의 zone defensive algorithm 때문이다.
(사실 Kame의 계산은 조금 틀렸다. 하지만 zone_watermark_ok가 fail한 건 사실이니.)

많은 embedded system들이 swap device가 없음으로 인해 anon page들을 회수할 수 없게 되고 그로인해 memory fragmentation 문제가 심각해져 발생하는 문제이다. (갑자기 떠오른 생각을 적어 놓는다. no swap system의 lumpy reclaim은 anon page를 고려해야 한다!)

이를 해결하는 방법은 swap을 사용하거나 app 메모리 사용량을 줄이거나..
또는 아직 검증되진 않았지만 compcache(http://lwn.net/Articles/334649/)와 같은 방법을 사용하거나...

Misleading OOM messages May 17, 2009

http://lkml.org/lkml/2009/5/12/353

Christoph가 최근 android로 인해 이슈가 되고 있는 OOM 메시지에 대한 오해를 화두로 던졌다.

"Out of memory" => 메모리를 다 소비했다는 것이다.

Christoph는 OOM은 대부분 커널 내부의 메모리 회수의 실패이지 실제 메모리가 부족해서 발생하는 문제는 아니라고 말하고 있다. 그런 반면, 많은 user들은 이 잘못된 메시지를 보고 "시스템이 충분한 메모리를 가지고 있지 않구나!" 생각하고 user들이 많은 메모리를 할당할 수 없도록 ulimit 값을 낮추는 경우가 있다고 말하고 있다.
이것은 OOM의 메시지에 대한 오해에서 비롯되는 현상이며 이 오해는 커널에서 출력하는 OOM message에서 기인하는 것이다. 그러므로 Christoph는 OOM message를

"Failure to reclaim memory"
으로 바꾸자고 제안하고 있다.

이에 대해 예전부터 OOM을 작성해왔던 suse의 Pavel Machek은 현재 OOM의 메시지는 이상이 없다고 생각하고 있으며 여러 상황을 예로 들며 실제 메모리를 추가해야 하는 상황을 언급하고 있다. 그러므로 현재 OOM message는 이상이 없다고 주장하고 있다.

또한, IBM의 Dave Hansen은 자신이 만난 OOM message중 단한번도 시스템의 free page가 0이어서 발생한 적은 없다고 말하고 있다. 단지 시스템의 free page가 zone->watermark이하인 상태에서 회수가 더 이상 진행되지 못한 경우에 발생하고 있다고 전하고 있다. 현재 OOM message의 문제점은 전체 스토리를 이야기하지 못하고 마지막 결과만을 이야기 한다는 것이 문제라고 지적하고 있며 다음과 같은 메시지가 좋지 않겠냐고 제안하고 있다.

"Unable to satisfy memory allocation request and not making
progress reclaiming from other sources."

하지만 console에 위와 같은 한두줄의 출력에 큰 의미를 두는 것 보다는 http://linux-mm.org/OOM 와 같이 웹 페이지에 보다 자세한 정보를 담는 것이 좋을 것 같다고 한다.

이에 대해 Andorid의 low memory killer를 담당하고 있는 David Rientjes은 OOM message를 다음과 같이 바꾸자고 제안하고 있다.
"No [available/allowable] memory"

cpuset allocator와 같은 경우도 현재 OOM을 출력하고 있는데 시스템에 물리 메모리가 많은 상태에서 cpuset hardwall로 인해서 메모리 할당이 실패했음에도 불구하고 out of memory를 출력한다는 것이다. 또한 watermark로 인해서 실패하는 것도 out of memory보다는 No available memory리가 보다 make sense하다는 것이다.

No available memory는 out이라는 단어 사용을 하지 않았기 때문에 user들로 하여금 memory를 더 두어야 한다는 사실보다는 swap을 추가하거나, cgroup limit을 바꾸거나, 실제 메모리를 더 추가하거나와 같은 뉘앙스를 줄 수 있게 된다. 그러므로 완벽한 solution은 아니지만 out 이란 단어를 사용하는 것 보다는 훨씬 좋다.

아직까지 어떤 메시지를 출력하는 것이 좋을지에 대해서는 consensus가 만들어지지는 않고 있지만 현재 OOM message의 문제점에 대해서는 대부분 공감하고 있는 것으로 보인다. 그러므로 머지 않아 우리는 친숙한 Out of memory 메시지를 보지 않게 될지도 모르겠다.

Kernel Shared Memory May 7, 2009

현재 barrios가 관심을 가지고 있는 feature는 KSM(kernel shared memory)이다.

http://lkml.org/lkml/2009/5/4/400


기능의 이름만으로는 어떤 역할을 하는 것인지 쉽게 예측하기 어려울 수 있다.
안그래도 Hugh는 이 패치의 이름을 바꿀 것을 제안하였다.

이 기능의 목적은 user level의 shared memory와는 전혀 관련이 없다.
이 패치가 이루고자 하는 목적은 application들의 page중 동일한 데이터를 가지고 있는 page들을
하나의 page로 merge하고 나머지 page들은 free시켜 주자는 것이다.(이것은 user level의 shared memory와는 전혀 관련이 없다)
이것이 user application에게 transparent하게 동작할수 있는 이유는 merge된 하나의 페이지를 COW로 만들기 때문이다.

이 패치는 KVM을 primary target으로 개발되어졌지만 general한 approach로 만들어졌기 때문에
일반 application들도 sysfs의 KSM node에게 ioctl을 사용하여 자신의 address range를 KSM에게 등록할 수 있다.

이 패치를 개발하고 있는 Redhat의 Izik Eidus은 이 기능으로 KVM이 많은 메모리 이득을 본다고 얘기하고 있지만, 아직까지 어떤 benchmark 결과는 내놓지 않고 있다. 하지만 KVM과 같은 virtualization solution들에게 이득이 지될 수 밖에 없는 이유는 VMM 위의 guest들은 host memory를 유사한 kernels, librarys, cache와 같은 데이터로 채우게 될 것이기 때문이다. 또한 Jared Hulbert는 embedded 환경에서도 10% 정도의 memory save를 할 수 있었다고 말하고 있지만, 어떤 test 환경에서 어떤 application들을 테스트 했는지에 대해서 언급이 없기 때문에 사실 얼마나 이득이 될지 예측할 수는 없다.

현재 이 패치는 여러 design issue를 가지고 있다. 그 중 가장 대표적인 것이 user application과의 interface이다. 현재는 sysfs의 ksm node를 통해 ioctl을 통해 application들은 자신의 virtual address range를 등록할 수 있게 되어 있다. 하지만 Rik과 Hugh는 이 interface보다는 madivse와 가은 기존의 system call을 더 선호하고 있다. interface는 결국 madivse로 바뀔 것으로 보이며, 이 기능이 module로 들어갈 것인지 커널에 built-in으로 해서 VM code들의 약간의 수정과 함께 들어가게 될 것인지도 확실지 않다. 또한 rmap의 hooking을 통해 page들을 shared page들을 tracking하게 될지 또한 명확하지 않다.

COW and Direct I/O problem Apr 17, 2009

barrios는 이 문제에 대해서 작년에 살짝 언급한 바 있다.
http://barriosstory.blogspot.com/2008/12/corruption-with-odirect-and-unaligned.html

사안이 사안이니만큼 한번 더 언급하기로 한다.
사실 이것은 리눅스 커널의 전형적인 core bug이다. 그러므로 이 문제를 이해하기 위해서는 kernel core의 page handling과정에 대한 이해가 필요하다.

이 문제를 다시 언급하는 이유는 두가지이다.

1. 이 문제는 아직 리눅스 커널에서 해결되지 못하고 있다.
2. 이 문제를 보고서도 뭐가 문제인지조차 알기 쉽지 않다는 것이다.

1에 대한 comment

해결되지 못하는 이유는 Linus때문이다. 이미 Nick과 Andrea는 이 문제를 풀기위한 solution을 진작에 내놓았었다. 성능을 최대한 떨어뜨리지 않으면서 이 문제를 풀기 위해 code가 다소 복잡하다.
반면 Kosaki가 내놓은 이 패치는 간단하긴 하지만(하지만 아직도 fast gup에 대한 concurrency 문제와 fork에 대한 latency, 특히 이 fork의 문제는 다소 심각해질 것 같다. 오늘 날짜로 보고된 Christoph의 report에 따르면 fork의 overhead가 2.6.22에 비해서 2.6.29는 2배 이하로 커진 것을 알 수 있기 때문이다.) 아직 많은 지적이 있다. 하지만 Linus는 코드수의 변경이 적은 Kosaki의 패치에 손들 들어주었다. Linus는 그때까지만 해도 fork에 대한 overhead는 크지 않다고 봤다. 물론이다. fork와 O_direct가 race condition이 될 확률은 크지 않다. 하지만 direct I/O가 계속해서 발생하게 되면 fork는 starvation 문제를 가질 수도 있으며, 안 그래도 overhead가 심각한 현재 fork의 구조에서 또 다른 lock을 check하는 것은 좋은 생각이 아닐 수도 있다.

2에 대한 comment

이 문제에 관심을 가질수 있는 사람들을 위해 barrios는 최대한 쉽게 설명할 수 있는 그림을 그려 보았다. 여기까지가 barrios가 할 수 있는 최선이다.

아래 그림은 이 패치의 테스트 프로그램이 왜 실패하는지에 대한 설명을 하는 그림이다. 패치의 테스트 프로그램과 함께 이 내용을 보아야 한다.

mm: more likely reclaim MADV_SEQUENTIAL mappings Apr 11, 2009

http://lkml.org/lkml/2008/7/19/130

이번 2.6.29에 패치된 기능중의 하나이다.
Hannes에 의해 추가되었으며, Rik과 Nick간의 다소 격양된 언쟁이 있었던 패치이기도 하다.
어쨌든, Andrew는 이 패치의 유용성에 대해 인정하였으며, 이번 2.6.29에 반영되었다.

Barrios가 2008년 7월에 언급되었던 이 패치를 지금 다시 이야기 하는 나름데로의 이유가 있다.

먼저 이 패치가 이루고자 하는 목표는 Page 회수를 좀더 똑똑하게 하자는 것이며,
리눅스 커널의 Man page의 madvise에 대한 기능을 완수하기 위한 것이기도 하다.

먼저 madvise의 man page를 살펴보면 다음과 같은 언급이 있다.


The madvise() system call advises the kernel about how to handle paging input/output in the address range beginning at address start and with size length bytes.
...
MADV_SEQUENTIAL
Expect page references in sequential order. (Hence, pages in the given range can be aggressively read ahead, and may be freed soon after they are accessed.)

위와 같이 sequential한 access는 첫째, readahead를 보다 공격적으로 하기 시작할 것이다.
둘째, 그리고 그 페이지들을 곧 회수될 수 있다.

리눅스 커널은 현재까지 첫째에 대해서는 충실했었지만 둘째에 대해서는 충실하지 못했다.
이번 패치는 Hannes에 의해 두번째 문제를 충족시켜주기 위한 패치이다.
패치는 정말 간단하다. 페이지를 promotion 시키기 전, vma의 seqential access 체크를 하여
그렇다면 promotion 시키지 않는다는 것이다.
즉, inactive list에 있는 page들을 active list로 옮기기 위한 확률을 줄이는 것이다. 그러므로 page reclaimer는 sequential access 페이지들을 보다 빨리 회수할 수 있게 된다.
이는 결과적으로 시스템의 working set들을 유지하는 데 도움을 주게 되므로 시스템의 전반적인 성능 또한 향상 시킬 수 있다.


많은 시스템 프로그래머들은 데이터 파일을 읽어들여본 경험이 있을 것이다.
물론 random한 offset으로 파일을 읽어들이는 경우도 많지만, 동영상을 재생하는 경우나 파일을 복사하는 경우, 또는 파일을 읽어들여 네트워크로 전송하는 경우와 같이 데이터 파일을 sequential하게 읽어들여야 하는 경우도 많이 있다.

이 블로그를 읽고 있는 사람이라면 너무나도 잘 알듯이 리눅스 커널의 메모리 관리 정책은 파일에 대한 접근을 모두 page cache에 보관하게 된다는 점을 잘 알고 있을 것이다.

위와 같은 스트림 데이터들을 page cache에 보관하면 어떤 이점이 있을까?
없다.

하지만 커널에서는 유저가 스트림 데이터와 같이 한번 접근하고 나서는 불필요한 파일을 접근하는지, 아니면 두고두고 참조할 파일을 정확하게 알 수 있는 방법은 없다. 그래서 나름데로 커널은 readahead 정책을 활용하여 heuristic하게 적용한다.

그러므로 파일의 사용패턴을 그 누구보다 잘 아는 것은 application 그 자신이다.
Application은 자신이 사용할 파일이기 때문에 어떻게 그 파일을 사용할 것인지 이미 알고 있다.
그러므로 이런 Application들이 커널에게 hint를 준다면 얼마나 고마운 일인가 ?

그러면 커널은 그러한 hint가 없다면 무엇을 하는가?

리눅스 커널의 page reclaim 관련 루틴들은 자주 참조되는 페이지와 그렇지 않은 페이지들을 잘 관리해보려고 애쓴다. 애는쓰되 효율적이지 못할 경우도 있다.
커널은 최선을 다할 뿐이지 결과는 장담할 수 없다.

너무 커널이 무책임하다고 생각하는가?

그렇게 커널이 무책임하지는 않다.
그래서 커널이 제공하는 시스템 콜이 mdvise, fadvise, open(O_DIRECT)와 같은 것들이
있는 것이다.

최소한 지각있는 application 개발자라면 커널에게 hint 정도는 줄 수 있는 것 아닌가?

내가 지금부터 이 파일을 sequential하게 읽을거야!
정도는.

응용 프로그램 개발자들은 최소한 커널이 제공하는 수준의 시스템 콜을 잘 이용할 수 있는 의무와 권리가 있다.
이를 이행하지 않으며 모든 것을 커널에게 맡기겠다는 것은 사실 응용 어플리케이션 개발자들의 직무유기이다.

꼬랑지)
Kernel이 applictaion들을 위해 얼마나 많은 것들을 제공해주어야 하는지에 대한 논쟁은 언제나 뜨거운 감자이지만, 새로운 기능을 넣고자 하는 자는 언제나 자신의 기능에 대한 benefit이 기존의 커널에 대한 regression보다 우월하다는 쉽지 않은 증명을 해야만 할 것이다.

Memory Failure Report throuth MCE Apr 9, 2009

http://lkml.org/lkml/2009/4/7/368

Intel의 앞으로 나올 CPU에 background로 memory를 check하여 error를 report해주는 기능이 들어간다고 Andi는 전하고 있다.
Report는 기존의 x86 specific한 Machine Check Exception을 그대로 사용할 것이며, MCE register를 통해 error가 발생한 물리 주소를 얻을 수 있게 된다.

그러므로 OS가 이 기능을 적절히 활용하기 위해서 Andi는 하드웨어가 나오기전 먼저 Linux OS에 대해서 상기 기능을 어떻게 활용할 것인지에 대해 RFC를 내놓았다.

Andi가 이번 패치에서 주 target으로 정한 것은 KVM이다. Host에서 error정보를 전달 받아 그 내용을 KVM guest들에게 전달하기 위한 설비이다. 하지만 이 기능은 KVM에 종속되지 않으며 얼마든지 다른 응용 프로그램들도 사용할 수 있도록 generic하게 설계되었다.

정보를 알리는 방식은 새로운 signal을 사용하는 방식이며, error가 발생한 page의 특성에 따라 다르게 동작한다.

1. slab page
2. reserved page
3. page cache clean page
4. page cache dirty page
5. unknown page
6. free page
7. swap clean page
8. swap dirty page
9. anonymous page
10. unevictable page
11. huge page
12.compound page

하지만 현재는 RFC단계로 위의 모든 type에 대해 구현되어 있지 않은 상태이다.

이 패치의 tricky한 part는 다른 VM 사용자들의 page에 대해 비동기적으로 접근하게 된다는 점이다. 그것은 page 동기화에 대한 rule을 해치는 일이 될 수 있기 때문에 매우 조심해야 한다. Andi가 이 패치에 대해 제일 걱정하는 부분이 바로 이 부분이다. 리눅스 커널의 page관련 lock 처리는 매우 골치아픈 부분이다.

현재, mainline에서 이 패치의 기능에 대해서는 모두 수긍하는 편이며 구현 또한 RFC 단계 치고는 상당히 깖끔하다. 앞으로 남은 주제는 아직 구현되어 있지 않은 page type에 대해 어떻게 처리할 것인가와 함께 비동기적인 page에 대한 접근에서 발생할 수 있는 문제에 대한 coverage등이 될 것으로 보인다.

이 패치는 하드웨어적으로 아직 들어가 있지 않은 구현에 대한 검증을 위해서 소프트웨어적으로 프로세스의 page를 poisoning할 수 있는 별도의 test 기능까지 구현되어 있다.

꼬랑지)
이 패치를 보면 Andi가 속한 Intel의 Open Source Lab이 무엇을 하는지 잘 알 수 있다.
아직 시장에 나오지 않은 기능에 대해 OS community에 선행 학습을 시키며 앞으로 나올 자신들의 하드웨어의 판매전략을 위해 기존 OS에 미리 기능을 구현해 놓겠다는 것인데..

아직까지도 하드웨어에만 목을 매는 우리나라의 몇몇 기업들의 우둔한 전략에 대해 할말이 너무나도 많지만..

[PATCH] shmem: writepage directly to swap Mar 23, 2009


http://www.gossamer-threads.com/lists/linux/kernel/1050286?page=last


Hugh의 금일 패치이다. 한 줄의 패치이지만 이 패치를 위해서는 그의 수년간의 경험이 녹아 있다.

내용은 다음과 같다.
shmem_writepage 함수는 약간 특별한 함수이다.
왜냐하면 일반적인 다른 file system과는 달리 shmem_writepage가 page를 바로 flush하지 않기 때문이다.

shmem_writepage는 regular writeback이나 sync로부터 호출되지 않는다. 이미 backing device info에서 것을 막는다. 반면, writepage가 호출되는 것은 memory pressure에 의해 page를 swap out 할 때이다.

하지만 이때 다른 파일 시스템처럼 바로 페이지를 block layer로 내려 보내는 것이 아니라,
swap cache를 이용해 한번더 reclaim될 수 있는 기회를 준다. 그렇게 하는 이유는 일반적으로 tmpfs(shared memory를 구현하기 위해 사용된)와 같은 ramfs는(여기서는 그냥 넘어가지만 ramfs와 tmpfs는 엄연히 다른 fs이다. 비록 tmpfs가 ramfs를 모태로 하지만. 언젠가 issue가 있으면 설명할 날이 오겠지)의 목적은 일반 파일 시스템과 다르기 때문이다.

메모리 파일 시스템을 사용하는 사용자의 의도는 특별한 이유가 없으면 파일들이 RAM에 상주하길 바란다는 것이다. 그러므로 shmem_writepage는 바로 회수하는 것이 아닌 한번 더 기회를 주는 것이다.

하지만 문제는 여기서 부터 시작한다. 그리고 이 문제는 SLUB과 얽혀 있기도 하다.

Hugh가 지적한 것이 바로 이것이다. 수년동안 지켜봤는데, shmem page들에게 한번 더 기회를 줘서 얻는 이득이 없다는 것이다. 오히려 해가 되면 해가 됐지.

게다가 2.6.28에서 split lru가 merge되며, tmpfs와 shmem을 SwapBacked 페이지로 다시 분류하였고 이는 file page보다 swap out되기가 더 어렵기 때문에 그 의도는 충분히 반영되었다는 것이다. 일리가 있는 말이다.

그럼 문제가 어떻게 발생했는지 살펴보자.
테스트는 tmpfs workload에서 실험한다.

문제는 shmem_inode_cache에서 부터 시작한다. SLUB을 primary SLAB으로 사용하면서 shmem_inode_cache를 위해서는 슬랩당 4개의 페이지를 사용한다. 그러므로 tmpfs의 workload를 실행하여 많은 inode를 할당하다 보면 4개의 연속된 페이지를 계속 필요로 하게 될 것이다. memory pressure가 점차 심해지자 page scanning이 되며 tmpfs 페이지들이 바로 swap out되는 것이 아니라 swap cache로 이동을 하게 될 것이다. 이때 할당된 swap entry들은 연속된 swap area로 할당될 것이다. (이는 swap file system의 특징이다. 되도록 이면 연속된 swap offset을 할당하여 disk seek을 줄이려는 의도이다.)
부하가 점점 더 심해진다. 결국 4개의 연속된 페이지를 찾기 위해 lumpy가 동작하게 된다.
lumpy의 회수 페이지의 패턴을 분석해보니 첫번째 페이지만 LRU의 bottom에 존재하게 되고, 나머지는 굉장히 random하게 LRU의 중간 중간에서 회수된다는 것이다.
그러므로 swap file system이 아무리 연속된 swap offset을 할당한다 하더라도 결국, 페이지는 scatter되어 writeout되는 꼴이 되어버리고 만 것이다.

이는 flash를 사용하는 장치를 swap device로 사용할 때 큰 문제가 된다. 왜냐하면 flash의 특징은 write를 위해서는 보다 큰 영역을 먼저 erase해야 하기 때문이다. 그러므로 flash의 특성상 random write의 merge는 굉장히 큰 effect를 갖게 된다. 허나 위의 문제로 인해 I/O의 merge 비율이 크게 떨어지면 엎친데 덮친격으로 random write 속도 또한 굉장히 떨어진다는 것이다.

Hugh는 대략 5%의 성능향상이 있다고 말하고 있다.
패치를 보라. 단 한줄 수정했다. (Dummy function 제외하고)

이 한줄을 위해 그가 분석한 자료와 kernel에 대한 지식은 상당하다.
커널 개발자란 적어도 이 정도의 core knowledge를 가지고 있어야 커널 개발자라고
말할수 있지 않을까?

fsblock Mar 22, 2009

http://lwn.net/Articles/322668/

fsblock에 대한 기사가 LWN에 실렸다. 굉장히 기대하고 있었던 기사였는데 ..
이번 기사 내용은 다소 실망스러웠다. 기대가 너무 컸던 것일수도 있고..

Anyway, 시작한다.

fsblock을 처음 접한 것은 2007년 6월 이었다. 하지만 이제서야 기사화 된 이유는
fsblock 자체가 커널 core의 buffer layer에 대한 "완전한" 재작업이기 때문이다.
Kernel core중 가장 변하지 않고 오래됐던 코드 들 중의 하나인 buffer layer를 incremental patching이 아닌 한번에 바꾸려는 Nick의 시도(이러는 데는 나름 이유가 있다. 이유 생략)로 인하여 mainline에서의 거부감도 있지만 많은 파일 시스템 개발자들을 혹하게 하는 feature도 가지고 있다.

최근 Nick은 2007년 시도에 이어 몇번의 refactoring 및 논란의 여지가 있는 부분들은 삭제하고 최근 간단하게 5개의 패치를 통하여 minix, ext2, xfs 등을 새로운 fsblock에 포팅한 패치를 제공하였다.

이 패치와 barrios는 사실 굉장한 인연을 가지고 있다.
barrios가 본격적으로 Virtual Process Memory쪽에 집중했던 것이 바로 2007년 6월이기 때문이다. 즉, 이 패치는 barrios가 linux kernel의 memory를 들여다 보게 해준 기회를 제공하였기 때문이다.

사실, buffer layer 를 보다 정확히 이해하려면 메모리에 대한 이해가 선행되어야 한다. 그 이유는 buffer layer가 리눅스에서는 page cache의 위에 구현되어지며, memory와 block간의 연결을 담당하고 있기 때문이다. 그러므로 buffer layer의 instance인 struct buffer_head는 struct page와 많은 부분 sync가 되어야 한다. 그 말은 page에 대한 많은 operation들이 buffer_head들을 care해야 한다는 말이기도 하다. 그러므로 page를 커널이 어떻게 care하는지 먼저 정확히 알지 못하면 커널이 buffer_head를 어떻게 care하는지 또한 정확히 이해한다고 볼 수 없기 때문이다.

하지만 그게 다가 아니다. 이 layer를 이해하려면 inode에 대한 커널의 handling 또한 명확히 이해하고 있어야 하며, file system들이 어떻게 buffer_head들을 handling 하는지, page와의 Sync를 어떻게 care해야 하는지 모두 알고 있어야 한다. 휴~

결국 이것을 정확히 이해한다는 것은 커널의 core를 정확히 이해한다는 것이다.
그러므로 barrios는 아직 fsblock에 장,단점에 대해 code level에서 명확하게 이해하지 못하고 있다. 아직 넘어야 할 산이 많다.

말이 샜다.

Nick이 말하고 있는 fsblock(음..먼저 이 이름부터 명확히 하고 넘어가자. 왜 이름을 fsblock이라고 지었을까?? file system과 block layer를 연결한다고 해서 그렇게 했덴다. 그 전에 buffer_head는 이름이 너무 모호하지 않았던가..) 의 장점은 다음과 같은 것들이 있다.

1. 기존의 buffer_head에 비해서 작은 size
2. 기존에는 한 page에 대한 buffer_head들의 연결 리스트였지만, fsblock은 연속된 페이지 상에 위치함으로 cache footprint를 줄일 수 있음
3. per-inode lock을 더 이상 사용하지 않고 lookup과 locking을 위해 page cache를 사용함. 이는 곧 lockless page cache와 함께 lock의 사용을 없앨 수 있음(하지만 약간의 문제가 아직 남아 있음)
4. page cache와 buffer_head간의 중복된 플래그를 제거. 이는 fsblock이 page와 fsblock의 동기화에 대한 신경을 많이 쓰고 있음을 반증.
5. larget block support - 페이지 보다 훨씬 큰 size의 block 을 지원함. 현재 32M 까지를 지원함.
6. 코드 사이즈를 기존 대비 2/3로 줄임.

이 밖에도 barrios가 설명하기 힘든 많은 장점들을 가지고 있다고 한다.
Barrios는 조만간 다시 이 코드들을 review하기 시작할 예정이다.

왜냐하면 fsblock은 Nick이 진행하고 있는 것이니 만큼 결국 mainline에 merge가 될 것이며, 그때 kernel에 끼치는 effect이 꽤 될 것이고, kernel core를 이해하기 위해서는 꼭 필요한 지식이 될 것이라 생각하기 때문이다. Good Luck.

Barrios Junior Mar 15, 2009

요즘 게시물의 수가 매우 적다.

여러 이유가 있지만 오른쪽에 사진을 올렸듯이 큰 이유 중 하나는 이 친구 때문이기도 하다.

Barrios를 쏘옥 빼닮은 이 친구는 barrios의 junior이다. 지난 달 태어난 이 친구가 자꾸 눈 앞에 아른아른 거려 barrios의 시간 관리는 완전히 변했다.

다행히도 눈은 green-mail의 눈을 닮아 아주 크고 똘망똘망한 것이, 것을 빼고는 나의 어릴 적과 똑같다. 태어나자마자 구렛나루를 지니고 태어난 것까지 말이다.

이 친구는 틈만 나면 나의 부성을 자극하기 때문에 당분간 barrios는 많은 시간을 이 친구와 보내게 될 것이다.

지금 이 글을 쓰는 이유 또한 4시건 전 마지막으로 본 이 친구의 모습이 곰실곰실 되기 때문이다. 다시 보려면 또 한주가 지나야 하니 시간아 어서 가라~

Checkpoint/restart tries to head towards the mainline

http://lwn.net/Articles/320508/

Checkpoint/restart가 벌써 v13이다.
barrios가 처음 이 패치를 review했던 것이 08년 9월이니 벌써 6달이 지났다.
그 후로 몇번의 review요청이 왔었으나 다른 일들로 follow up하지 못하고 있었다.

그동안 Oren은 Hansen과 함께 이 패치를 계속해서 improve시켜왔다.
하지만 Checkpoint/restart는 결코 가벼운 패치가 아니다. 제대로 동작하기 위해서는 커널 전반에 걸쳐 많은 core function들의 수정이 불가피하다.

Oran은 이 패치가 mm에 merge되기를 바라고 있지만, Andrew는 여러 이유로 그것을 받아들이지 않고 있다.

첫째로, 아직까지 toy 수준의 이러한 패치를 merge하게 된다면, 결곡 non-toy로 만들기 위해 받아들일 수 없을 정도로 비용이 큰 패치들을 계속해서 받아들여아 하거나.
돌째로, 그러한 패치를 받아들이지 않아서 계속해서 toy로 남게 하거나,
셋째로, 현재 빠져 있는 기능들에 대해서 그것들을 어떻게 구현하여 해결할지에 대해서 논의되지 않았다는 이유이다.

현재 Checkpoint/restart는 매우 제한된 app에서만 동작할 수 있다.
프로세스는 single thread여야 하며, fd operation은 open, lseek 정도만 restore될 수 있고, Shared memory, hugetlbfs, VM_NONLINEAR 등은 지원하지 못하고 있는 수준이다.
이 밖에도 Dave Hansen은 여러 문제들을 자세히 나열하였다.

하지만, 현재 어떤 기능이 구현되어 있느냐보다 더욱 중요한 문제는 현재로서 어떤 해결책도 없는 문제이거나 커널을 많이 고쳐야만 풀릴 수 있는 잠재적인 문제들이 있다는 것이다.

기술적인 문제를 하나 소개하자면, 다음과 같은 문제가 있다.
프로세스들이 어떻게 자신의 state가 checkpointable 한지를 결정하냐는 것이다.
Ingo는 이 문제에 대해서 LSM security checks를 overloading하여 checkpointable 여부를 결정할 수 있는 flag를 사용하자는 것이다. 이 flag는 해당 app나 checkpoint시키길 원하는 프로세스들에게 노출시킬 수 있다는 것이다. 하지만 많은 사람들은 LSM의 많은 hook에 대한 overhead를 걱정하고 있다.

이에 대한 또 다른 concern은 이 flag의 setting을 one-way로 하는 것이 바람직하냐는 것이다. 이 문제는 현재의 LSM hook의 문제이다. 현재의 LSM hook은 open은 검사하지만 close는 검사하지 않는 다는 것이다. 그러므로 다시 checkpointable 한 state가 되는 것을 알 수 없다는 것이다. 이에 대해 Ingo는 one-way flag가 맞는 방향이라고 얘기하고 있다. 그래서 Checkpoint를 사용하는 많은 user들로 하여금 커널의 uncheckpointable한 operation들에 대해 압력을 행사하도록 하여 수정을 유도하는 것이 좋다는 것이다.

Checkpoint를 구현한 Opensource 프로젝트들이 다수 있지만 Oran과 Hansen은 그러한 오픈 소스 프로젝트들을 잘 이용하면 자신들의 구현이 훨씬 뛰어날 수 있다고 말하고 있으며, 반면, 커널 전반에 많은 수정을 가하게 될 이 패치를 Andrew는 아직 걱정하고 있다.

계속해서 노력한다면 언젠가 merge가 되겠지만, barrios의 생각은 이 패치의 merge여부를 떠나 앞으로 이 패치를 지켜보면 커널 전반을 이해하기 위한 좋은 예제가 되지 않을까 하는 것이다. ^^

Encouragement Mar 9, 2009

'99년 5월 전역하자마자, 부랴부랴 준비해 연주회에 올렸던 곡이다.

Sor의 곡 답게 기교가 많은 곡이며, first와 second의 주고 받는 부분들과 마지막 변주에서의 빠른 스케일이 압권이다.

기억에 남는 것이 높은 '미'가 굉장히 중요한 부분이었는 데, 그만 '레#'으로 쳐서 얼굴을 붉혔던 기억이 아직도 남아있다. 사실 그때 우리에게 있어, 연주회의 의미는 얼마나 멋지게 연주를 하느냐는 것은 개 개인의 욕심일 뿐, 더 중요했던 것은 연주자 한 사람 한사람으로 이루어진 합주단과 동아리 회원들간의 친목도모였다.

합주연습이나 중주 연습이 끝나고 나면 언제나 학교 앞, 술집에 모여앉아 재미있게, 유익하게 보냈던 시간들이 아직도 내겐 너무 소중하다.

그날 있었던 연습얘기부터 시작해서 음악얘기, 학업 얘기, 다시 동아리 얘기, 임원단 얘기, 또 동아리 얘기, 동아리 선배 얘기, 다시 동아리 얘기, 동아리에서 좋아하는 친구 얘기
, 또 동아리 얘기.. 이야기에 묻혀 묻혀...하다하다 차 다 끊기고, 돈 떨어지고.... 가까운 선배들한테 SOS해서 얹혀 자고..

지금 어려운 시기에 이런 모습을 후배들에게 바라는 것은 선배들의 욕심일 뿐이겠지만,
이런 경험을 가지고 있는 사람으로서 그렇지 못한 사람을 볼때에 가슴에서 끌어나오는
열정을 느낄수가 없어 가끔은 답답하기 그지 없다.

그때 그렇게 공부안하고 이야기에 묻혔던 그 친구들, 그 선배들, 그 후배들 다들 잘 살고 있는 것을 보면 우리가 그렇게 쓸데없이 등록금을 갖다 버린 것은 아닌 것 같다.

이미 다 지난가버린 기억이 나이 먹은 아저씨의 쓸데없는 감성을 또 자극하는 구나.
눈물나기 전에 얼른 오늘 하루 마무리하고 자야지~



[patch] fs: new inode i_state corruption fix Mar 6, 2009

역시 이런 core 쪽의 BUG는 언제나 Nick이 해결한다.

http://patchwork.kernel.org/patch/9984/

문제는 inode->i_state의 I_NEW state에 대한 race condition문제이다.

void unlock_new_inode(struct inode *inode)
{
#ifdef CONFIG_DEBUG_LOCK_ALLOC
if (inode->i_mode & S_IFDIR) {
struct file_system_type *type = inode->i_sb->s_type;

/*
* ensure nobody is actually holding i_mutex
*/
mutex_destroy(&inode->i_mutex);
mutex_init(&inode->i_mutex);
lockdep_set_class(&inode->i_mutex, &type->i_mutex_dir_key);
}
#endif
/*
* This is special! We do not need the spinlock
* when clearing I_LOCK, because we're guaranteed
* that nobody else tries to do anything about the
* state of the inode when it is locked, as we
* just created it (so there can be no old holders
* that haven't tested I_LOCK).
*/
inode->i_state &= ~(I_LOCK|I_NEW);
wake_up_inode(inode);
}
주석에서 보는 것과 같이 kernel은 의도적으로 inode의 state를 변경함에도 불구하고 spinlock을 잡지 않았다. 왜냐하면
1. 새로 생성되는 inode의 state에는 I_LOCK|I_NEW가 걸리게 되며, 새로 생성된 inode이기 때문에 이전에 이 inode에 대해서 I_LOCK을 test하지 않고 inode를 잡고 있는 녀석도 없을 테고,
2. inode가 lock되어 있을 때는 inode의 state를 바꾸기 위한 어떤 시도도 이루어질 수 없음으로

이 곳에서 spinlock이 굳이 필요 없다는 것이다.

하지만 패치에 명시된 문제가 발생한 것이다. CPU0과 CPU1이 unlock_new_inode와 __sync_single_inode를 패치에 명시된 순서대로 실행 되었을 경우, 해지된 I_NEW state가 다시 지정되어 결국 wake_up_inode가 프로세스를 깨우지 않게 되는 문제이다.

이 문제를 해결하기 위해서 Nick은 __sync_single_inode에서 I_NEW state를 기다리는 것 보다는 해당 inode를 queue에 다시 넣고 일단 skip하는 방법을 택했다. 합리적인 결정이다.

왜냐하면 동시에 막 생성되고 있는 inode들은 data integrity operation의 대상이 아니며 dirty memory에도 별로 영향을 미치지 않을 것이라는 이유에서이다.

역시 Nick, Good job!

미국인 barrios Mar 4, 2009



http://wiki.livedoor.jp/linuxfs/d/Linux_hackers_M

일본의 한 사이트이다. 웹검색하다 걸렸는데..
뭐하는 사이트인지는 잘 모르겠지만..
kernel hacker들의 정보를 수집해 놓았는데 linux kernel관련 사이트로 보인다.


Minchan Kim - U.S ?? 나야 보내주면 좋겠지만...

Oops analysis in ARM Mar 3, 2009

이 글은 barrios의 좋지 않은 기억력을 보강하기 위해 2008년 1월에 작성된 글이다.


Unable to handle kernel NULL pointer dereference at virtual address 00000000
pgd = c1c58000
[00000000] *pgd=10360031, *pte=00000000, *ppte=00000000
Internal error: Oops: 817 [#1]
Modules linked in: overflow
CPU: 0
PC is at read_overflow+0x38/0x4c [overflow]
LR is at 0x1
pc : [<bf0000c0>] lr : [<00000001>] Tainted: P
sp : c035baf0 ip : 60000093 fp : c035befc
r10: 00066178 r9 : 00000000 r8 : c1f25000
r7 : 00000c00 r6 : 00002000 r5 : 00000000 r4 : 00000000
r3 : 00000003 r2 : 00000000 r1 : 00001517 r0 : 00000000
Flags: nZCv IRQs on FIQs on Mode SVC_32 Segment user
Control: 5317F
Table: 11C58000 DAC: 00000015
Process cat (pid: 31, stack limit = 0xc035a258)
Stack: (0xc035baf0 to 0xc035c000)
bae0: 00000000 00000000 00000000 00000000
bb00: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
bb20: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
bb40: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
bb60: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
bb80: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
bba0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
bbc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
bbe0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
bc00: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
bc20: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
bc40: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
bc60: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
bc80: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
bca0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
bcc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
bce0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
bd00: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
bd20: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
bd40: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
bd60: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
bd80: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
bda0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
bdc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
bde0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
be00: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
be20: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
be40: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
be60: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
be80: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
bea0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
bec0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
bee0: 00000000 00000000 00000000 00000000 c035bf4c c035bf00 c00b24a4 bf000098
bf00: c035bf1c 00000000 00000021 c035bf78 c1fc4920 c035a000 00000000 00000000
bf20: c035bf3c c0dc5b40 00066178 c035bf78 00002000 c0023f88 c035a000 00000000
bf40: c035bf74 c035bf50 c00804c8 c00b2374 c035bfb0 00000000 00000000 00000000
bf60: c0dc5b40 00000003 c035bfa4 c035bf78 c0080ba0 c0080424 00000000 00000000
bf80: c002a4d8 00000000 c035bfa4 bed0aeb4 00002000 00066178 00000000 c035bfa8
bfa0: c0023de0 c0080b6c bed0aeb4 00002000 00000003 00066178 00002000 00000001
bfc0: bed0aeb4 00002000 00066178 00000003 00000001 ffffffff 00000003 00000000
bfe0: 00000003 bed0ab50 00048a24 40119b7c 60000010 00000003 00000000 00000000
Backtrace:
[] (read_overflow+0x0/0x4c [overflow]) from [] (proc_file_read+0x140/0x2bc)
[] (proc_file_read+0x0/0x2bc) from [] (vfs_read+0xb4/0x18c)
[] (vfs_read+0x0/0x18c) from [] (sys_read+0x44/0x70)
r7 = 00000003 r6 = C0DC5B40 r5 = 00000000 r4 = 00000000
[] (sys_read+0x0/0x70) from [] (ret_fast_syscall+0x0/0x2c)
r6 = 00066178 r5 = 00002000 r4 = BED0AEB4
Code: e59f0018 eb40fbc4 e3a00000 e3a03003 (e5803000)
Segmentation fault



1. pgd = c1c58000 값은 fault 시점에서의 current의 mm_struct 구조체 안에 들어있는 pgd 값이다. 즉 pgd가 저장되어 있는 가상 주소이다.

[00000000] *pgd=10360031, *pte=00000000, *ppte=00000000

2. 위의 첫번째 [00000000]은 fault를 일으킨 주소이며
3. *pgd 값은 pgd에 저장되어 있는 값이다.
4. pgd에 저장되어 있는 값을 통해 fault가 난 주소로 reslove한 *pte, *ppte또한 각각이 저장되어 있는 값이다.

Internal error: Oops: 817 [#1]

5. 817은 fsr(Fault Status Register, data sheet 참조)의 값, 즉 error 값이며 #1은 die counter이다. 즉 die가 연속적으로 호출되거나 루프를 돌며 호출될 때 제일 중요하고 의미 있는 것은 첫번째 로그이다. 그것을 구별하도록 해준다.

6. 다음 현재 시스템에 로드되어 있는 모듈의 이름을 print하여 준다.

CPU: 0
PC is at read_overflow+0x38/0x4c [overflow]
LR is at 0x1
pc : [] lr : [<00000001>] Tainted: P
sp : c035baf0 ip : 60000093 fp : c035befc
r10: 00066178 r9 : 00000000 r8 : c1f25000
r7 : 00000c00 r6 : 00002000 r5 : 00000000 r4 : 00000000
r3 : 00000003 r2 : 00000000 r1 : 00001517 r0 : 00000000
Flags: nZCv IRQs on FIQs on Mode SVC_32 Segment user
Control: 5317F
Table: 11C58000 DAC: 00000015

7. 현재 수행중이었던 CPU를 출력하며
8. fault가 난 시점에서의 register에 저장되어 있던 값들을 출력한다.

9. mode는 드라이버에서 fault를 일으켰음으로 SVC_32 모드이며
10. Segment user가 의미하는 것은 current가 kernel thread가 아닌 user thread에서 일어났다는 것이다. 이것을 아는 코드는 다음과 같다.

get_fs() == get_ds() ? "kernel" : "user");

get_fs()는 current의 thread_info구조체에 들어 있는 addr_limit 값을 가져온다. 반면 get_ds()는 커널의 DS 값을 가져온다.
일반적으로 커널 쓰레드들은 user memory(0x0 ~ 0xc0000000)에 접근할 필요가 없기 때문에 ds 값이

11. 그 위쪽으로는 현재의 stack pointer부터

43 00000088 :
44 88: e1a0c00d mov ip, sp
45 8c: e92dd800 stmdb sp!, {fp, ip, lr, pc}
46 90: e24cb004 sub fp, ip, #4 ; 0x4
47 94: e24ddb01 sub sp, sp, #1024 ; 0x400
48 98: e24b0b01 sub r0, fp, #1024 ; 0x400
49 9c: e3a01b01 mov r1, #1024 ; 0x400
50 a0: e240000c sub r0, r0, #12 ; 0xc
51 a4: ebfffffe bl 0 <__memzero>
52 a8: e59f001c ldr r0, [pc, #28] ; cc <.text+0xcc>
53 ac: ebfffffe bl 0
54 b0: e59f0018 ldr r0, [pc, #24] ; d0 <.text+0xd0>
55 b4: ebfffffe bl 0
56 b8: e3a00000 mov r0, #0 ; 0x0
57 bc: e3a03003 mov r3, #3 ; 0x3
58 c0: e5803000 str r3, [r0]
59 c4: e24bd00c sub sp, fp, #12 ; 0xc
60 c8: e89da800 ldmia sp, {fp, sp, pc}
61 cc: 00000040 .word 0x00000040
62 d0: 0000005c .word 0x0000005c

int read_overflow( char *page, char **start, off_t off,int count,int *eof, void *data_unused )
{
char stack_overflow[OVERFLOW_STACK];
int *die = NULL;
unsigned long fp;
memset(stack_overflow, 0, OVERFLOW_STACK);

printk("!!!!happen overflow!!!!\n");
printk("!!!!wakeup!!!!\n");

asm("mov %0, fp" : "=r" (fp) : : "cc");
*die = 3;

return 0;


12. 이 함수의 call path는 ret_fast_syscall부터 시작되었으며 ret_fast_syscall의 entry point가 위치하는 주소는 0xc0023de0이다.
sys_read 함수가 호출되었으며 이때 다음의 값들은 r6 = 00066178 r5 = 00002000 r4 = BED0AEB4 sys_read의 인수가 아니라 ret_fast_syscall에서 사용된 r5,r6,r7의 값이 sys_read 함수에서 값이 바뀌기 때문에 sys_read 의 entry에 저장되어 있는 값들이다.



13. fault를 일으킨 코드는 test module의 read_overflow 함수의 빨간 색 라인이다. 위의 backtrace 초록색 라인은 fault를 일으킨 코드를 ()로 둘러싸고 있고 그 이전 코드들을 4개 더 보여주고 있다.
다음 backtrace 정보를 보여주고 있다. fault를 일으킨 함수는 read_overflow이며 fault를 일으킨 함수의 가상 주소는 0xbf000088이다. (이는 모듈이기 때문에 그 쪽 주소에 매핑된 것이다.)


저장하는 코드는 아래의 표와 같다. 주의해야 할 것이 이 값들이 마치 함수의 인수라고 착각하는 것이다.
나머지 함수들도 마찬가지로 해석하면 된다.

127033 c0080b5c :
127034 c0080b5c: e1a0c00d mov ip, sp
127035 c0080b60: e92dd870 stmdb sp!, {r4, r5, r6, fp, ip, lr, pc}



14. Control: 5317F 이것은 ARM processor의 control register 정보이다. 이곳에는 MMU, cache등과 관련된 현제 세팅 정보가 들어가 있다.

15. Table: 11C58000 은 Translation Table Base Register의 정보이다.
Translation table base 0 UNP/SBZ RGN P S C 값들이 들어가 있다.

16 DAC: 00000015은 Domain Access Control Register이다.

D15 D14 D13 D12 D11 D10 D9 D8 D7 D6 D5 D4 D3 D2 D1 D0

17. Tip

PC에 저장되어 있는 주소가 현재 fault를 일으킨 명령이다. 그럼 그 명령은 C 소스의 어느 line에 해당할까?


addr2line 0xc30210c0 -e vmlinux -f


[RFC PATCH 00/19] Cleanup and optimise the page allocator V2 Mar 1, 2009

http://lkml.org/lkml/2009/2/24/81

오랜만의 출사다.
어쩐지 Mel이 한동안 조용하다 했었다.
그동안 그가 했었던 일은 page allocator의 성능개선이다.
문제는 그 동안의 page allocator에 여러 patch들이 들어가면서 page allocator가 비정상적으로 느려지기 시작한 것이다. 그래서 심지어는 SLUB과 같은 경우는 page allocator를 회피하기 위하여 tricky한 방법까지 사용하고 있는 상황이다.

비록 x86_64 기준으로 kernel의 text size가 180 byte 증가하긴 하였지만(text size가 증가한 것은 몇몇 core page allocator function들을 inline했기 때문이다), page zeroing을 제외하고 allocation cost는 25%, free cost는 12%가 향상되었다. Page zeroing을 제외하고의 대부분의 overhead는 counters, debugging check, interrupt disable등에서 기인하였다.

Mel은 이러한 부분들을 줄이기 위해 무려 19개의 패치를 던졌다. 사실 v1에서는 20개였다.
마지막 패치는 hot/code page를 없애는 패치였는데, 없애기 전, 더 확실한 검증이 필요하여 현재는 drop된 상태다.


1. 전체적으로 반복적인 일들의 수를 줄였으며,
2. anti-fragmentation으로 인해 allocator fast path에서 수행되던 list search를 migraion 별 per_cpu_pages로 해결하였으며,
3. page free마다 두번씩 호출되는 get_pageblock_migratetype을 한번으로 줄이고,
4. free_page_mlock에서의 irq_disable overhead를 smart하게 delay시켜 해결하였다.
5. alloaction fast path에서 사용되는 gfp_zone에서 수행되는 많은 branch를 해결하기 위해 lookup table을 사용한다.

이밖에도 allocation의 fast path와 slow path에 대한 code를 clean up하였으며 여러 stupid sanity check들도 없앴다. 하지만 아직까지 TODO가 몇몇 남아 있다.
v3를 기대해보자.

[patch] vmscan: initialize sc.order in indirect shrink_list() users Feb 17, 2009

http://lkml.org/lkml/2009/2/10/231

Hannes의 패치이다.
얼마전 Hannes는 offline mail을 통해서 barrios에게 유익한 정보를 알려주었다.
이는 다음 기회에 소개하기로 하고, 지금은 이 패치의 내용에 대해 살펴보자.

Hannes가 패치한 내용은 memory 회수 루틴 중 2곳에서 현재 메모리 requestor의 order가 반영되지 않았다는 것이다. 첫번째 장손는, 명시적으로 0을 지정할 필요가 없다는 것을 얼마전 Andrew가 친절히 설명해 주었었다.
이번 issue는 두번째인 zone_reclaim 함수에서의 order값 지정의 생략이다.

zone_reclaim 함수의 목적은 page를 할당하기 전, early reclaim을 하는 것이다. early reclaim의 대상은
slab 페이지들과 unmapped file backed page들이다. 즉, read와 같은 system call을 통해서 읽혀진 페이지나, mapped file backed page들 중에, 현재는 unmapped되어 있는 페이지들이다.

이 함수에서 order를 requestor의 order로 지정하지 않은 것이 문제의 시작이다.
하지만 이 쓰레드의 Hannes와 Mel의 대화 속에는 약간의 오해가 있다.

먼저, Hannes가 order를 의도적으로 0으로 지정하지 않았다고 생각하는 이유는 다음과 같다.

Page allocator는 page를 할당하려고 할 때 몇 단계를 거치며 할당자에 pressure를 가한다.
그 pressure는 다음의 flag들로 지정된다.


#define ALLOC_NO_WATERMARKS 0x01 /* don't check watermarks at all */
#define ALLOC_WMARK_MIN 0x02 /* use pages_min watermark */
#define ALLOC_WMARK_LOW 0x04 /* use pages_low watermark */
#define ALLOC_WMARK_HIGH 0x08 /* use pages_high watermark */
#define ALLOC_HARDER 0x10 /* try to alloc harder */


NO_WATERMARK 쪽으로 갈 수록 memory pressure가 심한 것이다.
Page allocator의 첫번째 try는 ALLOC_WMARK_LOW를 사용하게 된다. Hannes가 생각한 것은
WMARK_LOW에서 order를 지정하게 되면, 현재 memory pressure가 별로 없는 상태에서 결국 lumpy reclaim을
하게 되어, 불필요하게 working set page들이 evict되어 버린다는 것이다. 그래서 의도적으로 order를 지정하지 않았다고 생각한다.

이에 대해, Mel은 분명히 잘못된 것이라고 지적하고 있다. 즉 Hannes의 패치가 맞다는 것이다.
그것으로 인해 어떤 차이가 생길지는 알기 어렵지만, zone_reclaim의 semantics상 order를 지정하는 것이 make sense하다는 것이다. 또한 Mel은 Hannes가 걱정한 working set page의 evict에 대해서 high order reclaim에 대한 어쩔수 없는 cost라고 말하고 있다.

마지막 쓰레드의 Hannes와 Mel의 대화를 다시 보면, Mel은 Hannes의 말을 여전히 오해하고 있다. Hannes가 의도한 것은 low watermark에서 굳이 lumpy reclaim까지는 하지 않아도 upcoming allocation은 성공할 것이기 때문에 그렇게 했을 거라는 말이고, Mel은 그 말을 오해하고, 이렇게 언급하며 쓰레드를 끝낸다.


I think I get you now. You are saying that reclaiming order-0 pages increases
the free count so we might get over the watermarks and the allocation would
succeed. Thing is, watermarks calculated in zone_watermark_ok() take order
as a parameter and has this in it.

for (o = 0; o < order; o++) {
/* At the next order, this order's pages become * unavailable */
free_pages -= z->free_area[o].nr_free << o;
So, to reach the low watermarks for a high-order allocation, we still
need lumpy reclaim.


즉, Mel은 "order-0 page를 reclaim하는 것이 전체 free page의 수를 증가시킬 것이며 이는 watermark 이상이 될수 있기 때문에 allocation이 성공할 것이다" 라고 Hannes가 말하고 있다고 생각한다. 하지만 앞서 언급한 바와 같이 Hannes의 의도는 그렇지는 않다. Anyway, Mel로부터 배울 것이 있다. order-0 page를 아무리 reclaim하더라도 결국 zone_watermark_ok는 위와 같이 계산하기 때문에 requestor의 order를 만족시킬 수는 없게 된다는 것이다. 결국 allocation이 성공할 수 없다는 것이다.

Taming the OOM killer Feb 14, 2009

http://lwn.net/Articles/317814/

OOM killer도 역시 embedded에서 hot issue 중의 하나이다.
현재 Linux system의 OOM killer의 가장 큰 문제는 유연성의 부재이다.

Linux kernel은 Out of Memory 상황이 오면 몇가지 heuristic을 사용하여 비교적 시스템에 해가 적게가며, 많은 메모리를 반환할 수 있는 프로세스를 kill하게 된다. 하지만 그 결정은 kernel의 생각이지, 시스템을 운영하는 admin이나 사용하는 user의 의견이 반영된 결정은 아니다.

그렇게 까지 얘기하면 너무 심한가?
사실, User들도 커널의 결정을 거들수는 있다. 그것이 /proc/pid/oom_adj란 knob이다.
이 값은 커널의 여러 heuristic과 같이 각 프로세스의 oom_score의 값을 결정하는 데 기여한다. 커널은 결국 가장 큰 oom_score를 가진 프로세를 kill하게 되므로 사용자의 의견이 어느 정도 반영이 될 수는 있다.

이러한 설비들이 있긴 하지만, 시스템을 admin하는 입장에서 그러한 설비만으로 OOM policy를 구현하기가 그리 만만치가 않다. 생각을 해보아라. 그러한 설비만으로 시스템에 생성되는 모든 프로세스들의 oom_adj를 runtime에 조절하는 것이 과연 얼마나 수월할까? 여러 사람들의 서버로 사용되는 server computer를 생각해보자. 그 안에서 어떤 user가, 어떤 time에, 어떤 job을 어떤 order로 얼마나 많은 memory를 필요로 하며 실행될지...

그러므로 효율적인 OOM policy를 구현하기 위해서는 그 이상의 유연한 설비가 보다 OOM policy를 결정하는데 도움을 줄 것이다.

얼마전 LKML에서는 Alan Cox의 제안에 의해 Nikanth Karthikesan가 cgroup controller를 base로 oom_killer controller를 구현하였었다. cgroup을 사용하였기 때문에 기존보다는 OOM priority별 task들을 grouping하기가 수월해졌다. 하지만 이 approach는 CPU set을 고려하지 않았다.
CPU set A, B가 있다고 가정하자. CPU set A의 한 프로세스가 더 높은 oom.priority를 가지고 있을 경우, CPU set B가 OOM 상황을 만나게 되더라도 결국, CPU set A의 그 프로세스가 죽게 되는 것이다. 여러 토의 끝에, 결국 OOM 상황을 user space에 다루자는 얘기가 나왔다. 커널은 user space로 notify만 해주고 application들이 각자 자신의 cache를 drop한다거나 해서 OOM 상황에 대처하자는 것이다. Application들의 action이 충분한 free memory를 확보하는데 실패하였을 경우에만 커널이 마지막으로 기존의 OOM killer policy를 사용한다는 것이다.

2008년 초 Kosaki-san이 구현했던 mem_notify가 그러한 policy를 구현하기 위한 첫 발걸음이었다. 하지만 이 패치는 현재 mainline에 반영되지 못했다. 그 이유는 여러 이유가 있지만 그 중에서도 가장 큰 것은 2.6.28에서 memory reclaiming이 크게 변경되었기 때문이다. 그 패치의 notify 시점은 커널의 memory reclaim 루틴과 굉장히 밀접하게 관련되어 있었기 때문에 memory reclaiming이 많이 변하게 되면서 mem_notify가 동작할 수 없는 환경이 되버린 것이다. Memory reclaiming을 같이 작업한 Rik과 Kosaki 그리고 약간의 도움을 준 barrios는 이 상황에 대해서 잘 알고 있으며, barrios는 mem_notify 패치의 마지막 메일 또한 기억하고 있다.

마지막 토론에서의 문제는 실제 mem_notify가 필요할 때 notify를 해주지 못한다는 것이다. 즉, user가 notify를 받은 시점은 user가 어떤 action을 취하기에 늦어버렸다는 것이다. 그것은 현재 linux kernel의 문제이기도 하며, android가 별도의 low memory killer를 구현한 이유이기도 하다. 하지만 지금도 mem_notify의 design과 goal들은 여전히 유효하다.

한동안 Android의 Low memory killer가 staging tree에 들어간 것에 대해서 여로 토의가 있었다(http://lkml.org/lkml/2009/1/13/558). barrios 또한 staging tree에 반영되었던 low memory killer의 소스를 보았지만 사실 너무 specific하게 구현되어 있어 질타를 받기 딱 좋은 코드였다. OOM issue는 embedded뿐만이 아닌 server에서도 필요한 이슈이다. 하지만 android 팀의 코드는 embedded 중에서도 자신들의 android 플랫폼만을 위한 specific한 코드를 구현하였고, Greg은 그것을 stating tree에 반영한 것이다. Staging tree의 특성상 앞으로 더욱 발전시키면 되지, staging tree에 들어가질 못할 정도는 아니었음에도 불구하고, barrios의 생각으론 다국적을 가진 사람들이 토의하다보니 영어를 약간 잘못 해석하여 분쟁이 일어나 결국 code를 다시 revert하라는 말까지 나올 정도로 좀 소란스러웠었던 issue이다.

커널 트리에는 low_memory killer만 있고, 그 신호를 어떻게 user에서 처리하는지 잘 몰랐는데 LWN에 쉽게 잘 설명이 되어 있다. 첫번째 threshold에서는 background process들이 자신들의 state를 save하고, 점점 memory pressure가 심해지면 backgroud process 중 state를 save한 critical하지 않은 프로세스들을 kill하고, 마지막으론 foreground process들마저도 kill한다는 것이다. Android 팀이 이러한 설비가 필요했던 것 또한, 현재 linux kernel의 OOM killer의 반응은 너무 늦으며(Android팀은 보다 이른 신호가 필요하였다. 그래서 사전에 application에서 할 수 있는 일을 하려고 했던 것이다) 또한 그리 똑똑하지 못하다는 것을 반증하고 있다.

지금이다. Linux kernel의 OOM killer구조가 재설계되야 할 때는!!

lru_add_drain_all problem Feb 8, 2009

http://lkml.org/lkml/2008/10/21/205
http://lkml.org/lkml/2008/11/5/347

또 다시 circulary dependency problem이다.
문제의 원인은 lru_add_drain_all이 mmap_sem을 hold한 채로 worqueue를 사용하기 때문이다. 이 문제는 작년 10월 처음으로 보고 되었고, 쓰레드를 읽어 보면 알수 있는 바와 같이,
문제를 해결하려는 시도는 2가지의 방향으로 시도 되었다.

첫번째는 keventd가 아닌 별도의 workqueue를 만들어 schedule_on_each_cpu를 keventd가 아닌 별도의 workqueue안에서 호출하자는 것이다.

이렇게 한 이유는, 이미 많은 드라이버들이 keventd를 이용한 workqueue를 사용하고 있는 상황에서 그 driver들이 어떤 lock을 hold한채로 workqueue 사용하고 있는지를 알 수 없기 때문에, 비교적 적게 사용되는 schedule_on_each_cpu로 하여금 별도의 workqueue를 사용하자는 주장이다.

하지만 이 방법에 대해서는 Peter가 별로 달가워하지 않았다.

그 이유는 이미 커널에는 수많은 커널 쓰레드들이 있다는 것이다. 그 상황을 더욱 악화시키지 말자는 것이다. barrios의 생각도 마찬가지이다. 비록 지금은 schedule_on_each_cpu를 호출하는 것이 pagevec을 drain하는 lru_add_drain_all 함수 하나이지만, 그러한 함수들은 얼마든지 더 생길 수 있고 그럴때마다 새로운 workqueue를 만드는 것은 시스템의 커널 쓰레드들의 수를 증가시키게 된다.

하지만 이 상황은 지금은 더 나아질 수도 있을 것 같다. 왜냐하면 이전에 소개했던 기사 처럼 Arjan의 An asynchronous function call infrastructure가 mainline에 merge될 것이기 때문이다. 이 설비를 이용하게 되면 worker function을 새로운 쓰레드에서 실행하게 할 수 있으며, 해당 쓰레드는 필요 없을 때 종료될 수 있기 때문이다. 하지만 생성된 쓰레드를 재활용하는 Arjan의 패치에 약간의 개선이 필요하다. 모든 함수가 새로운 쓰레드에서 호출되며, 그 쓰레드는 다른 함수를 호출하지 않고 종료되게 끔 되어야 ciculary locking problem이 발생하지 않기 때문이다.
이와 함께 이 패치 또한 고려되어야 한다. 이는 비동기 함수 호출이 아닌 커널 쓰레드 자체에 관하여 비동기적으로 생성하자는 패치이다. 하지만 Arjan의 패치와 역할이 많이 겹치고 있다. http://lkml.org/lkml/2009/1/27/410)

두번째는 lru_add_drain_all을 호출하는 path에서 mmap_sem를 hold하지 않은 채로 호출하자는 것이다. Christoph는 migration중 mmap_sem을 hold하는 문제의 패치를 만들어 제출하였고, Kosaki 또한 munlockpath에서 lru_add_drain_all을 호출하지 않아도 시스템의 mlocked page memory leak이 발생하지 않는다는 문제에 agree하였고, 결국 다시 패치를 만들어 제출하였다.

barrios가 이 문제를 언급하는 이유는 작년 11월에 반영된 kosaki의 패치가 아직 반영되지 않았기 때문이다. 그러므로 barrios는 28-rc2-mm1의 테스트 중 그러한 circulary dependency locking report를 받았기 때문이다.

이 문제에 대해서 차주 kosaki와 얘기를 좀 더 해볼 필요가 있으며, 필요하다면 Arjan의 패치에 새로운 기능을 넣어 해결 할 수도 있을 것 같다.

An asynchronous function call infrastructure Feb 1, 2009

http://lwn.net/Articles/314808/

2.6.29-rc3가 몇일전 release되었지만 이제서야 2.6.29-rc1에 들어간 기능을 review한다.
그 동안 some bug를 digging 하느라 정신이 없었다. 사실 아직도 debugging 중이다.

kernel compile을 하는 동안 LWN 기사를 하나 review한다.

Intel의 Arjan이 시도하는 fastboot project의 일부이다.
Fastboot은 많은 사람들이 관심을 가지고 있는 주제중의 하나이며, 그 동안 많은 시도들이 있었다.
특히 embedded guy들이 많은 관심을 가지고 있던 주제중의 하나이다.
하지만 Arjan은 embedded가 아닌 desktop을 위해 Fastboot을 진행중이다. 물론 몇몇 기술들은
embedded에도 유용할 것이다. 하지만 오늘 소개하는 이 patch는 embedded와는 별로 관계가 없다.

사실, device probing을 parallel하게 하자는 시도는 예전부터 있어왔었다. 하지만 device ordering,
concurrent access등과 같은 문제들로 인하여 system stability의 문제가 있어 general하게
적용되지 못해왔다.

이 문제를 Arjan은 모든 device들을 한번에 해결하려는 시도보다는 libata subsystem과 SCSI로 국한하여
parallel probing을 시도하였으며(그래서 barrios는 embedded와는 현재로선 거리가 멀다고 얘기한 것이다. 하지만 이 비동기 framework 자체는 fastboot뿐만이 아니라 여러면에서 도움이 될 것으로 생각된다.), API를 잘 추상화하여 나머지 subsystem들은 아무 영향을 받지 않도록 설계하였다.

실제 동작 원리는 복잡하지 않다. 비동기 함수를 관리하는 async_pending list와 async_running list를 두어,
비동기 함수의 실행을 요청하는 async_schedule을 호춢하게 되면 callback function을 async_pending list에 넣고, 그 함수를 실행시키기 위해 async_manager_thread를 깨우게 된다. async_manager_thread는 새로운 async_thread를 생성하여 pending list에 있는 비동기 함수를 실행하게 된다. async_thread는 1초 async_pending list에 pending된 비동기 함수들이 없을 경우 스스로 소멸되게 된다.
또한, 한꺼번에 많은 비동기 함수들의 요청이 들어올 경우, kernel thread는 갑자기 많아 질 수 있다.
현재는 kernel thread를 MAX_THREAD(256)개로 제한하고 있다.


static async_cookie_t __async_schedule(async_func_ptr *ptr, void *data, struct list_head *running)
{
struct async_entry *entry;
unsigned long flags;
async_cookie_t newcookie;


/* allow irq-off callers */
entry = kzalloc(sizeof(struct async_entry), GFP_ATOMIC);

/*
* If we're out of memory or if there's too much work
* pending already, we execute synchronously.
*/
if (!async_enabled || !entry || atomic_read(&entry_count) > MAX_WORK) {
kfree(entry);
spin_lock_irqsave(&async_lock, flags);
newcookie = next_cookie++;
spin_unlock_irqrestore(&async_lock, flags);

/* low on memory.. run synchronously */
ptr(data, newcookie);
return newcookie;
}
entry->func = ptr;
entry->data = data;
entry->running = running;

spin_lock_irqsave(&async_lock, flags);
newcookie = entry->cookie = next_cookie++;
list_add_tail(&entry->list, &async_pending);
atomic_inc(&entry_count);
spin_unlock_irqrestore(&async_lock, flags);
wake_up(&async_new);
return newcookie;
}



하지만 아직까지 이 patch는 많은 문제를 가지고 있는 것 같다. 2.6.29-rc1에 들어가자마자 많은 문제들이 보고되기 시작했으며, 현재 Arjan은 Linus에게 이 기능을 disable 시켜달라고 요청한 상태이다.

이번주 LWN의 기사가 다시한번 이 주제를 다루고 있기 때문에, 몇일 뒤 무료로 기사를 볼 수 있게 될 경우, 이 문제가 어떻게 풀려나가고 있는지 다시한번 review를 하도록 하자.

사슴농장의 추억 Jan 30, 2009

이 곡을 접한 것은 96년 이었다.
정박으로 딱 떨어지는 이 곡은 러쎌의 연주가 일품이다. (러쎌의 연주가 동영상으로 있으면 좋았으련만...)

이 곡을 떠올리면 한 선배의 사슴농장에서의 연주가 생각난다. 비가 부슬부슬 내리는 날 사슴 농장에서의
연주는 많은 사람들에게 어필할 수 밖에 없는 아름다운 연주였을 것이다. 게다가 그 선배의 연주 실력이야 이미
준 프로 수준이라고 해도 과언이 아닐 정도로 훌륭하였다.

운지가 결코 쉽지 않다. 그래서 빠른 곡은 아니어서 오른손은 여유롭지만 왼손의 기민함을 요구한다.
그 만큼 왼손의 유연함과 정확성을 기를 수 있는 곡이다.
중간 중간 나오는 슬러와 하이 플랫의 포지션은 정말 정확하게 운지를 짚지 못하면
좋은 소리를 내지 못한다.

barrios는 아직도 그 연습을 하고 있지만 뜻대로 되지 않는 운지 중 하나이다.

정말 오랜만에 맥주를 한잔했다. 항상 술이 있으면 음악이 떠오르는 것처럼...
기타가 치고 싶어진다.
이 취기에 얼마나 앉아 연주할 수 있을지는 모르겠지만(아쉽게도 새로운 악보를 일터에 놓고 왔다. 이게
있었으면 나를 boost시켜줄 수 있었을 텐데)

마음가는데로.... 연주하다 오늘 하루는 편안하게 잠들어야지
정말이지, 클래식 기타의 소리는 너무 부드럽다 :)

꼬랑지)

러쎌을 흉내냈지만 프로의 수준은 아니다. 하지만 연주자 뒤의 background가 인상적이다. ^^;
저건 madrigal 악보가 아닌데.


[RESEND][PATCH] cpuset: fix possible deadlock in async_rebuild_sched_domains Jan 21, 2009

http://lkml.org/lkml/2009/1/19/564

이 버그는 lockdep를 사용하여 발견된 버그이다.


=======================================================
[ INFO: possible circular locking dependency detected ]
2.6.29-rc1-00224-ga652504 #111
-------------------------------------------------------
bash/2968 is trying to acquire lock:
(events){--..}, at: [] flush_work+0x24/0xd8
but task is already holding lock:
(cgroup_mutex){--..}, at: [] cgroup_lock_live_group+0x12/0x29
which lock already depends on the new lock.


보는 것과 같이 circular locking deadlock 상황이 발생할 수 있음을 탐지하였다.
그럼 살펴보자. 어떻게 그런 deadlock이 발생할 수 있을지..
먼저, cgroup_mutex를 소유하고 있는 task가 있다고 말해주고 있다.
이 mutex는 cgroup_lock_live_group에서 소유하였다. 코드를 살펴보자.

cgroup_tasks_write
->cgroup_lock_live_group을 호출하여 cgrup_mutex를 소유한다.
->attach_task_by_pid
->cpuset_attach
->do_migrate_pages
->migrate_prep
->lru_add_drain_all
->schedule_work_on
->flush_work
->lock_map_acuire

반면, async_rebuild_sched_domains에서 호출한 run_workqueue를 살펴보자.

async_rebuild_sched_domains
->schedule_work (이것은 결국 eventd의 run_workqueue에 의해 실행되게 된다.)
->run_workqueue
->lock_map_acquire
->do_rebuild_sched_domains
->cgroup_lock
->cgroup_mutex 소유


위에서 보는 것과 같이 workqueue와 cgroup_mutex에 대해 circular lock이 형성된다.
이 얘기는 flush_work에 의해 완료되야 하는 worker function이 do_rebuild_sched_domains을 실행시킨 run_workqueue에 의해 lock되며 do_rebuild_sched_domains는 cgroup_mutex를 소유하기 위해 cgroup_tasks_write와 race하게 된다.

lockdep는 이와 같이 여러 deadlock 상황을 발견하기 위해 만들어진 설비이다.
하지만 false positive를 만들어내는 상황도 있기 때문에 무조건 lockdep를 믿는 것도 좋지 않다.
많은 분들이 lockdep가 어떻게 동작하는지, 어떻게 사용하는지, 보고된 내용을 어떻게 해석해야 하는지 잘 알지 못하는 것 같다.
barrios는 조만간 lockdep에 대한 내용을 한번 다룰 예정이다.