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한 상태로 수행되게 될 것이며, 해당 요구를 만족하지 못하는 드라이버들의 개선이
이루어질 것으로 보인다.