ARM64 Linux Kernel 이 10일 이상 Running 하다가 Kernel Panic에 빠지는 문제가 있어서,
분석 중에 있습니다.
이유를 정확히 알 수 없지만, 지금은 Nested Interrupt 를 의심 중에 있습니다.
제 가설은 이렇습니다.
Kernel Task Stack, Interrupt Stack Size는 16K 입니다.
만약 Nested Interrupt 52회 이상 발생한다면 Kernel Panic에 빠질 수 있다는 생각이 듭니다.
Nested Interrupt 가 발생하면 Current Task Stack에 pt_regs 사이즈만큼 중복해서 52회 쌓아 놓을 것 같습니다.
Interrupt Stack(IRQ Stack)보다도 Kernel Task Stack의 Overflow 가 가능하지 않을까 생각 중입니다.
pt_regs 320Bytes인데 16K/320=52 계산때문에 이렇게 생각하게 되었습니다.
위와 같은 가설이 가능한 시나리오 일까요? 혹은 Nested Interrupt 가 ARM64에서 발생가능할까요?
댓글 7
-
이파란
2022.07.24 19:50
Kdump 로 추출하신 vmcore 부탁드립니다. -
아띨라
2022.07.26 08:22
우선, 의견 감사합니다.
제가 kdump로 vmcore 추출하는 방법을 모릅니다.
그리고 현상을 다시 재현하는 것이 고객 장비에서 10일 후에 발생하는 이유로 재현이 어렵습니다.
혹시 첨부파일 정보면 의견 부탁하는데, 가능할려는지요?
Interrut context 수행 중에, EL1 Data Abort로 빠지는 경우 였습니다.
Linux Version 4.19 쯤 됩니다. Xilinx Zynq 제품입니다.
-
문c(문영일)
2022.08.08 17:58
안녕하세요? 문c 블로그(http://jake.dothome.co.kr)의 문영일입니다.
arm 아키텍처는 nested-irq를 지원하지만,
arm 커널은 nested-irq를 지원히지 않아 위와 같은 중첩이 일어날 가능성은 없다고 보시면 됩니다.
(물론 아주 약간의 틈으로 nested-irq가 발생할 수 있고, 실제 nest도 가능합니다)
irq handler에서 만일 스택이 부족한 현상이 발생한 것을 stack-canary 등으로 포착했으면,
대부분의 이유는 재귀호출 함수 사용이 문제일겁니다.
감사합니다.
문영일 드림.
-
문c(문영일)
2022.08.09 17:09
nested-irq를 오해할 수 있어 조금 더 남깁니다.
리눅스 커널은 nested-irq를 처리할 수 있도록, 설계되어 있습니다만,
arm64 리눅스 커널은 이를 제한하여 사용하고 있습니다.즉, 제한하여 사용 가능한 상태이기 때문에 nested-irq를 지원하는지에 대해 묻는다면,
단순 답변으로는 yes일 수도 있고, no일 수도 있습니다.예를 들어 당장 nested-irq를 지원하는 상황은 적어도 다음 4가지는 됩니다.
1) irq와 fiq
2) rt preemption 커널
3) pseduo nmi 사용4) system call 처리 중 irq
위의 4가지를 제외하고 일반 irq끼리 우선 순위만 가지고 nest는 거의 허용하지 않는 상황입니다.
(물론 irq 핸들러 처리 이후, cpu의 irq mask를 꺼서 인터럽트를 허용하고 다시 복구(irq context restore)하기 전에 약간의 틈이 있습니다. 이 때 진입한 인터럽트는 irq 우선 순위와 상관없이 새로 진입되는 nest 상황입니다)먼저 x86과는 다르게 arm/arm64 cpu는 인터럽트가 우선 순위 번호별로 cpu로 진입하지 못합니다.
다만 2개의 인터럽트 시그널로 분리하여 진입할 수 있습니다. irq와 fiq 입니다.위에서 fiq를 nest 가능하다고, 거론했으니 fiq는 제외하고 말씀드립니다.
irq들끼리는 GIC(ARM사의 Global Interrupt Controller) 수준에서 우선 순위를 경쟁합니다.그 들 중 가장 높은 우선 순위의 인터럽트가 cpu에 전달되며, 해당 인터럽트를 리눅스 커널이 처리합니다.
인터럽트가 진입할 때 cpu내에서 자동으로 인터럽트가 mask되도록 동작합니다.
arm/arm64 리눅스 커널은 bottom-half 처리 직전까지는 irq를 enable하지 않으므로(history상 예전 커널에서는 enable하여 nest 하였다고 합니다) 그 동안은 더 높은 우선 순위의 irq라도 중첩되어 진입하지 못합니다.
이러한 매커니즘을 이해 하시면 왜 nested-irq에 대해 말들이 길어지는지 이해하실 수 있습니다.즉 전반적으로 arm/arm64 리눅스 커널내에서 irq 우선 순위만으로 중첩시킬 수 있는 일반적인 상황은 지원하지 못하게됩니다.
인터럽트의 처리는 2단계 또는 3단계로 분류합니다.
2단계로 분류하면 top-half와 bottom-half가 있고,
3단계로 분류하면 critical, immediate, deffered가 있습니다.
deffered가 bottom-half에 해당합니다.이들은 처리 시간이 오래 걸릴것을 예상하고 후 처리(bottom-half) 프로세스에게 처리를 넘기고,
irq를 enable 하여 새로운 인터럽트를 받을 수 있습니다.bottom-half까지 포괄하여 nested-irq 처리가 가능하다라고 하면 할 말이 없습니다. ^^;
내용을 간단하게 서술해서 당장 이해가 힘들지 모르지만 계속 분석해보시면 이해하실 수 있을겁니다.
감사합니다.
문영일 드림.
-
아띨라
2023.04.14 11:06
제가 갖고 있는 Kernel Source 기준으로 Interrupt가 발생한 시점에 Context가 Task 문맥이면 IRQ Stack을 설정하고 IRQ Stack을 처음부터 사용하고 만약 IRQ 문맥이면 IRQ Stack을 사용하게 되어 있어서 이전 ISR 문맥도 저장하고 있을 것 같아요.. 그래서 Nested Interrupt가 발생하여 Stack-OverFlow가 발생한다면 IRQ Stack에서 발생할 것으로 판단됩니다.
Interrupt 가 발생한 시점에 Currunt Task의 Stack에서 Overflow가 발생할 것 같다는 이전 제 얘기는 잘 못된 것입니다.
Nested Interrupt 발생 여부에 대해서는 Interrupt 관련 지식이 없어서 무식하게 접근했습니다.
GIC(General Interrupt Controller)에 ISR에서 Nested 일어 나는지 확인 했습니다.
Nested Interrupt가 발생한다면 최초발생시에 IRQ Stack을 설정하므로 이때 16 Bytes의 공간을 Nested Interrupt Counter를 기록하도록 Assemble Code를 수정했습니다. Entry.s에 소스에 박혀 있는 ARM사 엔지니어에게 문의를 하니 우연히 답변이 와서 일주일간 메일로 문답을 했는데 제가 짠 Assemble 코드는 문제 없다고 했습니다. 어째든 이 코드로 확인시 약 37회의 Nested Interrupt가 발생했습니다.
제가 Linux Interrupt 관련 Architecture나 개념은 잘 모르지만 Stack-OverFlow 얘기할 때 Botttom-Half는 상관 없는 얘기 같습니다. 이것은 Task 문맥으로 수행되는 놈으로 이해해서요. Nested Interrupt가 발생하면 현재의 Task의 Stack이든 혹은 별도로 사용하는 IRQ Stack이든 한 Stack만 계속 사용하게 되고 이것이 Nested Interrupt가 많이 발생한다면 Stack이 망가지는 문제로 이해하고 있습니다.
제 생각으로 이 문제를 대처하는 방법으로 Stack 사이즈를 키우는 방법도 가능하나, 순수 ISR 처리 말고 ARM Core에서 pt_regs만큼 저장하는 것만 고려해도 Stack이 16K이면 50회, 32K이면 100회, 64K이면 200회입니다.
Stack 키우는 방법이 정도는 아닌 것으로 보이며 Linux Interrupt 관련 구조를 잘 이해하고 있으면 Nesting Interrupt를 가능한 적게 일어나도록 막는 방법이 최선인 것으로 판단되는데 제가 이것을 몰라서 제 자신이 안타깝습니다.
-
문c(문영일)
2023.04.22 16:26
arm64 core에서 nested irq가 2~3회 밖에 안된다는 것은 위의 댓글에서 설명하였습니다.
stack overflow가 난 원인은 ISR 내의 코드에 있을것이고, 다음 2가지를 조사해보시길 바랍니다.
1) 재귀 호출
2) sleepable API 사용
ISR에서 sleepable API를 사용하면 안되는데 모르고 사용한 경우,
해당 API 내부에서 irq enable/disable을 사용하는 코드가 있는 경우가 있습니다.
이렇게 잠깐씩 irq를 허용할 때 ISR 중간에 pending 되어 기다리던 irq가 nest된채로 반복 진입하여 문제가 생길 수 있습니다.
참고하시기 바랍니다.
-
문c(문영일)
2023.05.02 15:49
추가 정보로 arm64의 GIC는 같은 인터럽트에 대해서는 nest를 허용하지 않습니다.
높은 proirity irq에 대해서만 nest를 허용하고,
priority level도 16~32단계의 priority leveling을 사용하고 있습니다.
참고하세요.
.