[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를 기대해보자.