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

0 개의 덧글: