Linux Kernel(4.19) Hacks

rousalome.egloos.com

포토로그 Kernel Crash


통계 위젯 (화이트)

78258
1323
114580


[리눅스커널][인터럽트] 인터럽트 컨택스트에서 스케줄링을 하면? 5. Interrupt Handling

5.2.4 인터럽트 컨택스트에서 스케줄링을 하면 어떻게 될까?

인터럽트 컨택스트에서 프로세스가 휴면하면 어떤 일이 벌어질까요? 커널은 이를 감지하고 커널 패닉을 유발시킵니다.

인터럽트 컨택스트에서 스케줄링 코드를 수행하면 커널 패닉 발생
인터럽트 컨택스트에서 스케줄링 관련 함수를 호출하면 안됩니다. 짧은 시간에 인터럽트 핸들러를 실행하고 인터럽트 발생으로 중단된 코드로 돌아가야 하기 때문입니다. 인터럽트 컨택스트에서 스케줄링 관련 함수를 호출하면 커널 입장에서 많은 연산을 수행합니다. 당연히 시간이 오래 걸립니다.

이번에는 인터럽트 컨택스트에서 스케줄링 함수를 호출하면 발생한 커널 패닉 문제를 분석해보겠습니다.

프로세스가 스케쥴링 즉 휴면할 때 __schedule() 함수를 호출합니다. 이 함수를 열어보면 앞 단에 schedule_debug()란 함수를 호출해서 현재 프로세스가 휴면할 수 있는 조건인지 점검합니다. 코드 흐름은 다음과 같습니다. 
 
[그림 5.10] 인터럽트 컨텍스트에서 휴면할 시 커널 패닉이 발생하는 코드 흐름

인터럽트 컨택스트에서 schedule_debug() 함수가 호출됐다고 가정하고 코드를 보겠습니다.
[https://elixir.bootlin.com/linux/v4.19.30/source/kernel/sched/core.c]
01 static inline void schedule_debug(struct task_struct *prev)
02 {
03 #ifdef CONFIG_SCHED_STACK_END_CHECK
04 if (task_stack_end_corrupted(prev))
05 panic("corrupted stack end detected inside scheduler\n");
06 #endif
07
08 if (unlikely(in_atomic_preempt_off())) {
09 __schedule_bug(prev);
10 preempt_count_set(PREEMPT_DISABLED);
11 }

인터럽트 컨택스트에서는 08번째 줄 in_atomic_preempt_off() 조건에 걸려 __schedule_bug () 함수가 호출돼 시스템은 커널 패닉으로 리셋됩니다.

이렇게 인터럽트 컨택스트에서 스케줄링을 하다가 발생하는 커널 패닉은 리눅스 커널 커뮤니티에서 자주 볼 수 있는 로그입니다. 

인터럽트 컨택스트에서 스케줄링 지원 함수 호출시 커널 패닉 Case Study

이번에는 리눅스 커뮤니티에서 본 유사 문제를 소개하겠습니다.
[https://patchwork.kernel.org/patch/4864261]
01 BUG: sleeping function called from invalid context at ../kernel/locking/mutex.c:583
02 in_atomic(): 1, irqs_disabled(): 128, pid: 0, name: swapper/0
03 ------------[ cut here ]------------
04 WARNING: CPU: 2 PID: 4828 at ../kernel/locking/mutex.c:479 05 mutex_lock_nested+0x3a0/0x3e8()
05 DEBUG_LOCKS_WARN_ON(in_interrupt()) // [3]
06 Modules linked in:
07 CPU: 2 PID: 4828 Comm: Xorg.bin Tainted: G        W      3.17.0-rc3-00234-gd535c45-dirty #819
08 [<c0216690>] (unwind_backtrace) from [<c0212174>] (show_stack+0x10/0x14)
09 [<c0212174>] (show_stack) from [<c0867cc0>] (dump_stack+0x98/0xb8)
10 [<c0867cc0>] (dump_stack) from [<c02492a4>] (warn_slowpath_common+0x70/0x8c)
11 [<c02492a4>] (warn_slowpath_common) from [<c02492f0>] (warn_slowpath_fmt+0x30/0x40)
12 [<c02492f0>] (warn_slowpath_fmt) from [<c086a3f8>] 13 (mutex_lock_nested+0x3a0/0x3e8)
13 [<c086a3f8>] (mutex_lock_nested) from [<c0294d08>](irq_find_host+0x20/0x9c)// [2]
14 [<c0294d08>] (irq_find_host) from [<c0769d50>] (of_irq_get+0x28/0x48)
15 [<c0769d50>] (of_irq_get) from [<c057d104>] (platform_get_irq+0x1c/0x8c)
16 [<c057d104>] (platform_get_irq) from [<c021a06c>] (cpu_pmu_enable_percpu_irq+0x14/0x38)
17 [<c021a06c>] (cpu_pmu_enable_percpu_irq) from [<c02b1634>] (flush_smp_call_function_queue+0x88/0x178)
18 [<c02b1634>] (flush_smp_call_function_queue) from [<c0214dc0>] (handle_IPI+0x88/0x160)
19 [<c0214dc0>] (handle_IPI) from [<c0208930>] (gic_handle_irq+0x64/0x68)
20 [<c0208930>] (gic_handle_irq) from [<c0212d04>] (__irq_svc+0x44/0x5c)
21 [<c0212d04>] (__irq_svc) from [<c02a2e30>] (ktime_get_ts64+0x1c8/0x200) // [1]
22 [<c02a2e30>] (ktime_get_ts64) from [<c032d4a0>] (poll_select_set_timeout+0x60/0xa8)
23 [<c032d4a0>] (poll_select_set_timeout) from [<c032df64>] (SyS_select+0xa8/0x118)
[<c032df64>] (SyS_select) from [<c020e8e0>] (ret_fast_syscall+0x0/0x48)

위 커널 로그는 인터럽트 컨택스트에서 뮤텍스락을 걸다가 커널 패닉이 발생했다고 말해줍니다. 참고로 뮤텍스락은 뮤텍스락을 다른 프로세스가 잡고 있으면 바로 휴면에 들어가기 때문에 인터럽트 컨택스트에서 쓰면 안 되는 함수입니다.

이제까지 배운 내용을 활용해서 위 로그를 분석해보겠습니다.

먼저 19~21 번째 줄 로그를 보겠습니다.
19[<c0214dc0>] (handle_IPI) from [<c0208930>] (gic_handle_irq+0x64/0x68)
20[<c0208930>] (gic_handle_irq) from [<c0212d04>] (__irq_svc+0x44/0x5c)
21[<c0212d04>] (__irq_svc) from [<c02a2e30>] (ktime_get_ts64+0x1c8/0x200) 

21 번째 줄 로그에서 보이는 __irq_svc는 인터럽트 벡터 함수입니다. 

이 정보로 인터럽트가 발생했다고 판단할 수 있습니다. 이후 gic_handle_irq() 함수와 handle_IPI() 함수 순서로 인터럽트를 처리하는 함수를 호출합니다. 

19~21번째 함수 흐름을 우리는 어떻게 해석할까요? 인터럽트가 발생해서 인터럽트 벡터가 실행됐으므로 당연히 인터럽트 컨택스트라 볼 수 있습니다. 

다음 13~14 번째 줄 로그입니다.
13 [<c086a3f8>] (mutex_lock_nested) from [<c0294d08>](irq_find_host+0x20/0x9c) 
14[<c0294d08>] (irq_find_host) from [<c0769d50>] (of_irq_get+0x28/0x48)

뮤텍스락을 획득하는 정보를 확인할 수 있습니다. 

마지막 05번째 줄 로그 봅시다.
04 WARNING: CPU: 2 PID: 4828 at ../kernel/locking/mutex.c:479 05 mutex_lock_nested+0x3a0/0x3e8()
05 DEBUG_LOCKS_WARN_ON(in_interrupt())  

현재 인터럽트 컨택스트인지 확인해 커널 패닉을 유발합니다. in_interrupt() 함수가 true를 리턴하니 DEBUG_LOCKS_WARN_ON 이란 WARN() 함수가 실행됩니다. 참고로 WARN() 매크로 함수가 실행하면 그 시점의 콜스택을 뿌려줍니다.

이번 시간에는 인터럽트 컨택스트에 대해서 알아봤습니다. 인터럽트 컨택스트는 인터럽트가 발생 후 인터럽트를 처리하는 코드 흐름이라 빨리 코드를 실행해야 합니다. 이를 알려주는 매크로 함수는 in_interrupt() 입니다.

"이 포스팅이 유익하다고 생각되시면 댓글로 응원해주시면 감사하겠습니다.  
"혹시 궁금한 점이 있으면 댓글로 질문 남겨주세요. 아는 한 성실히 답변 올려드리겠습니다!" 

Thanks,
Austin Kim(austindh.kim@gmail.com)

# Reference (인터럽트 처리)




    핑백

    덧글

    댓글 입력 영역