커널 패닉 한번 분석 좀 해보자.
패닉 로그는 아래와 같다. 오라 ext4 파일 시스템에서 커널님이 돌아가셨구나. 돌아가실 때 프로세스 이름은 system_server이며 대략, ext4 파일 시스템에서 아이노드를 free하다가 죽었다는 것을 알 수 있다.
[ 34.187856 01-05 13:30:49.424] Unable to handle kernel NULL pointer dereference at virtual address 00000000
[ 34.197812 01-05 13:30:49.434] pgd = e6f9c000
[ 34.202275 01-05 13:30:49.438] [00000000] *pgd=00000000
[ 34.207610 01-05 13:30:49.444] Internal error: Oops: 5 [#1] PREEMPT SMP ARM
[ 34.214663 01-05 13:30:49.451] Modules linked in: bcmdhd(O)
[ 34.220400 01-05 13:30:49.457] CPU: 0 Tainted: G W O (3.4.65-gbc0bf75 #1)
[ 34.228317 01-05 13:30:49.464] PC is at ext4_free_inode+0x288/0x574
[ 34.234664 01-05 13:30:49.471] LR is at ext4_free_inode+0x260/0x574
[ 34.240967 01-05 13:30:49.477] pc : [<c01e332c>] lr : [<c01e3304>] psr: 200f0113
[ 34.240972 01-05 13:30:49.477] sp : e0e9fe38 ip : 0000000a fp : e0e9fe9c
[ 34.255914 01-05 13:30:49.492] r10: 00000021 r9 : e09382c0 r8 : e092f908
[ 34.262824 01-05 13:30:49.499] r7 : 00000000 r6 : 00008180 r5 : 00040d7b r4 : e0e79800
[ 34.271078 01-05 13:30:49.507] r3 : 00000000 r2 : 00000000 r1 : 0000000a r0 : 0000000a
[ 34.279334 01-05 13:30:49.516] Flags: nzCv IRQs on FIQs on Mode SVC_32 ISA ARM Segment user
[ 34.288153 01-05 13:30:49.524] Control: 10c5387d Table: a6f9c04a DAC: 00000015
...
[ 34.943888 01-05 13:30:50.180] Process system_server (pid: 928, stack limit = 0xe0e9e2f0)
[ 34.952098 01-05 13:30:50.188] Stack: (0xe0e9fe38 to 0xe0ea0000)
...
[ 35.096737 01-05 13:30:50.333] ffe0: 57769af4 be905398 4005c331 40067b78 200f0110 60832fa0 f0000a17 a8c00000
[ 35.106647 01-05 13:30:50.343] [<c01e332c>] (ext4_free_inode+0x288/0x574) from [<c01ec5e0>] (ext4_evict_inode+0x2f0/0x4c0)
[ 35.117770 01-05 13:30:50.354] [<c01ec5e0>] (ext4_evict_inode+0x2f0/0x4c0) from [<c0170da4>] (evict+0x9c/0x184)
[ 35.127937 01-05 13:30:50.364] [<c0170da4>] (evict+0x9c/0x184) from [<c0171328>] (iput+0x104/0x1ec)
[ 35.137061 01-05 13:30:50.373] [<c0171328>] (iput+0x104/0x1ec) from [<c01658ac>] (do_unlinkat+0x11c/0x160)
[ 35.146793 01-05 13:30:50.383] [<c01658ac>] (do_unlinkat+0x11c/0x160) from [<c016731c>] (sys_unlink+0x20/0x24)
[ 35.156873 01-05 13:30:50.393] [<c016731c>] (sys_unlink+0x20/0x24) from [<c000f040>] (ret_fast_syscall+0x0/0x30)
T32로 아래 명령어를 쓰면 어드레스가 소스 어느 라인인 지 알 수 있다.
y.l.line 0xc01e332c
___address___to_______________|source\line_____________________________________________________________________________________________________________________________|offset____|
P:0000:C01E32F0--C01E32F7|\\vmlinux\unwind\/home001/seunghwan.moon/project/build/LAPAP40IKR140103/android/kernel/include/linux/spinlock.h\275--285 | n.f.
P:0000:C01E32F8--C01E3303|\\vmlinux\ext4/ialloc\/android/kernel/fs/ext4/ialloc.c\249--249 | n.f.
P:0000:C01E3304--C01E330B|\\vmlinux\android/kernel/include/asm-generic/bitops/non-atomic.h\79--79| n.f.
P:0000:C01E330C--C01E330F|\\vmlinux\/android/kernel/include/asm-generic/bitops/non-atomic.h\65--78| n.f.
P:0000:C01E3310--C01E3313|\\vmlinux\/android/kernel/include/asm-generic/bitops/non-atomic.h\79--79| n.f.
P:0000:C01E3314--C01E3317|\\vmlinux\android/kernel/include/asm-generic/bitops/non-atomic.h\65--78| n.f.
P:0000:C01E3318--C01E331F|\\vmlinux\android/kernel/include/asm-generic/bitops/non-atomic.h\79--79| n.f.
P:0000:C01E3320--C01E332B|\\vmlinux\android/kernel/include/asm-generic/bitops/non-atomic.h\65--78| n.f.
_____P:0000:C01E332C--C01E332F|\\vmlinux\android/kernel/include/asm-generic/bitops/non-atomic.h\80--80|______n.f.
P:0000:C01E3330--C01E3337|\\vmlinux\android/kernel/include/asm-generic/bitops/non-atomic.h\65--78| n.f.
P:0000:C01E3338--C01E333B|\\vmlinux\android/kernel/include/asm-generic/bitops/non-atomic.h\83--83| n.f.
P:0000:C01E333C--C01E3343|\\vmlinux\android/kernel/include/asm-generic/bitops/non-atomic.h\81--82| n.f.
소스를 따라가 보면, 266 라인에서 패닉이 발생했다는 것을 알 수 있다.
[kernel/fs/ext4/ialloc.c]
193 void ext4_free_inode(handle_t *handle, struct inode *inode)
194 {
195 struct super_block *sb = inode->i_sb;
196 int is_directory;
...
259 fatal = -ESRCH;
260 gdp = ext4_get_group_desc(sb, block_group, &bh2);
261 if (gdp) {
262 BUFFER_TRACE(bh2, "get_write_access");
263 fatal = ext4_journal_get_write_access(handle, bh2);
264 }
265 ext4_lock_group(sb, block_group);
266 cleared = ext4_test_and_clear_bit(bit, bitmap_bh->b_data);
267 if (fatal || !cleared) {
268 ext4_unlock_group(sb, block_group);
269 goto out;
270 }
ext4_test_and_clear_bit() 함수에 전달되는 파라미터 bit, bitmap_bh->b_data를 분석해보자.
확인해보면 아래와 같다.
(register long unsigned int) bit_=_10_=_0x0A_=_'....'
(register struct buffer_head *) bitmap_bh = 0xE092F908 = __bss_stop+0x1FA78EE8 -> (
(long unsigned int) b_state = 8667177 = 0x00844029 = '..@)',
(struct buffer_head *) b_this_page = 0xE092F908 = __bss_stop+0x1FA78EE8 -> ((long unsigned int) b_state = 8667177 = 0x00844029 = '..@
(struct page *) b_page = 0xC172DF14 = __bss_stop+0x8774F4 -> ((long unsigned int) flags = 1073743972 = 0x40000864 = '@..d', (struct a
(sector_t) b_blocknr = 1081345 = 0x00108001,
(size_t) b_size = 4096 = 0x1000 = '....',
(char *) b_data = 0x0 = -> NULL,
(struct block_device *) b_bdev = 0xE08FFC80 = __bss_stop+0x1FA49260 -> ((dev_t) bd_dev = 271581186 = 0x10300002 = '.0..', (int) bd_op
bitmap_bh->b_data 이넘이 NULL이네. 그래서 패닉이 발생한 것이다. 그럼 왜 이놈이 NULL인지 알아보자.
gdp란 변수가 NULL이 아니므로 ext4_journal_get_write_access() 함수가 호출이 되었다.
fatal = -ESRCH;
gdp = ext4_get_group_desc(sb, block_group, &bh2);
if (gdp) {
BUFFER_TRACE(bh2, "get_write_access");
fatal = ext4_journal_get_write_access(handle, bh2);
}
ext4_lock_group(sb, block_group);
cleared = ext4_test_and_clear_bit(bit, bitmap_bh->b_data);
if (fatal || !cleared) {
struct buffer_head, struct에 대해서 확인을 해보면 아래와 같다.
bh->b_data은 아래와 같이 ext4_init_block_bitmap()함수에서 초기화한다.
/* Initializes an uninitialized block bitmap */
void ext4_init_block_bitmap(struct super_block *sb, struct buffer_head *bh,
ext4_group_t block_group,
struct ext4_group_desc *gdp)
{
...
/* If checksum is bad mark all blocks used to prevent allocation
* essentially implementing a per-group read-only flag. */
if (!ext4_group_desc_csum_verify(sbi, block_group, gdp)) {
ext4_error(sb, "Checksum bad for group %u", block_group);
ext4_free_group_clusters_set(sb, gdp, 0);
ext4_free_inodes_set(sb, gdp, 0);
ext4_itable_unused_set(sb, gdp, 0);
memset(bh->b_data, 0xff, sb->s_blocksize);
return;
}
memset(bh->b_data, 0, sb->s_blocksize);
bit_max = ext4_num_base_meta_clusters(sb, block_group);
}
Linux에서 대부분의 입출력은 파일을 통해서 이루어지고 그럴 경우 페이지 크기인 4KB 단위로 이루어진다. 파일시스템 수퍼블락을 하드디스크로 쓸 경우 struct buffer_head 구조체를 이용하여 1KB 씩 디스크에 입출력을 한다. 파일 데이터는 디스크 상의 특정 블록에 존재하며 이들 간의 매핑 정보를 유지하고 있는 것이 바로 buffer_head이다.
즉 디스크가 깨진 것이다.
그럼 system_server 프로세스 이전에 어떤 프로세스가 호출이 되었는 지 한번 살펴보자.
이 때 크래시를 써보자. 태스트 디스크립터(TCB)에 last_arrival란 변수가 있는 데 이 변수로 프로세스가 처리된 순서를 알 수 있다.
crash> foreach task | grep last_arrival
mmcqd/0란 태스크의 콜 스택이 깨져있네?
35181519000 workqueue_trust
__schedule) from [<c082b47c>]
schedule) from [<c082932c>]
schedule_timeout) from [<c082a7c0>]
wait_for_common) from [<c082a884>]
wait_for_completion) from [<c0098da0>]
kthread_stop) from [<c0090b78>]
destroy_worker) from [<c0821814>]
trustee_thread) from [<c0098d2c>]
kthread) from [<c000fca4>]
35181472000 wpa_supplicant
"__schedule) from [<c082b47c>]
schedule) from [<c082b764>]
schedule_preempt_disabled) from [<c0829f1c>]
__mutex_lock_slowpath) from [<c082a000>]
mutex_lock) from [<c0044b70>]
tttt_dvfs_set_rate) from [<c0029b5c>]
clk_enable_locked) from [<c0029c64>]
clk_enable) from [<c058c6f0>]
tttt_sdhci_set_clock) from [<c05887d8>]
sdhci_enable) from [<c056e0ec>]
__mmc_claim_host) from [<c0578098>]
sdio_reset_comm) from [<bf024b94>]
sdioh_start [bcmdhd]) from [<bf026fa8>]
wl_android_wifi_on [bcmdhd]) from [<bf0071f8>]
dhd_open [bcmdhd]) from [<c066108c>]
__dev_open) from [<c06612f8>]
__dev_change_flags) from [<c0661444>]
dev_change_flags) from [<c06e5a98>]
devinet_ioctl) from [<c06e6720>]
inet_ioctl) from [<c0649694>]
sock_ioctl) from [<c016908c>]
do_vfs_ioctl) from [<c016965c>]
sys_ioctl) from [<c000f040>]
"
35181467000 kworker/2:1 "__schedule) from [<c082b47c>]
schedule) from [<c0093e50>]
worker_thread) from [<c0098d2c>]
kthread) from [<c000fca4>]"
35181376000 mmcqd/0
(__schedule) from [<df79aa80>]
35179614000 AudioOut_2 "__schedule) from [<c082b47c>]
schedule) from [<c082a0bc>]
do_nanosleep) from [<c009e5e8>]
hrtimer_nanosleep) from [<c009e710>]
sys_nanosleep) from [<c000f040>]"
'Core BSP 분석 > 리눅스 커널 핵심 분석' 카테고리의 다른 글
MMU 페이지 테이블 확인 (0) | 2023.05.05 |
---|---|
MMU - 메모리 설정 (0) | 2023.05.05 |
copy_mm()함수 (0) | 2023.05.05 |
커널 에러 로그(1) (0) | 2023.05.05 |
hrtimer_restart, alarm timer (0) | 2023.05.05 |