Kernel Lockup Debug 사용법

서버나 임베디드 장비에서 특정 CPU가 갑자기 응답하지 않으면 원인을 찾기 어렵다. 인터럽트조차 처리되지 않을 정도로 멈춰 버리면 로그 한 줄 남기지 못하고 시스템이 그대로 굳어버릴 수도 있다. Linux 커널은 이런 lockup 상황을 스스로 감지해 로그를 남기거나 panic을 발생시키는 watchdog 메커니즘을 내장하고 있다. 이 글에서는 soft lockup, hard lockup, hung task 탐지 원리와 관련 설정 방법을 정리한다.

Soft Lockup, Hard Lockup, Hung Task

Soft lockup은 인터럽트는 살아 있지만, 특정 CPU에서 커널 코드가 스케줄링 없이 지나치게 오래(기본 20초) 실행되는 상태다. CPU마다 떠 있는 watchdog 커널 스레드가 주기적으로 타임스탬프를 갱신하다가, 그 타임스탬프가 일정 시간 이상 갱신되지 않으면 soft lockup으로 판단한다.

Hard lockup은 인터럽트 자체가 막혀서 timer interrupt조차 처리되지 못하는, soft lockup보다 심각한 상태다. 일반 인터럽트가 막혀 있어도 감지할 수 있도록 NMI(Non-Maskable Interrupt) 또는 perf 이벤트를 이용해 CPU가 살아 있는지 주기적으로 확인한다.

Hung task는 CPU lockup은 아니고, 특정 프로세스가 D state(uninterruptible sleep)에서 오랫동안(기본 120초) 빠져나오지 못하는 상태다. khungtaskd라는 별도 커널 스레드가 주기적으로 D state 프로세스들을 훑어보며 판단한다.

lockup 탐지 주기는 watchdog_thresh 값(기본 10초)을 기준으로 정해진다. hrtimer/NMI는 watchdog_thresh초마다 동작하고, soft lockup 임계값은 그 2배인 20초(기본값)다.

커널 빌드 옵션 (Kconfig)

CONFIG_SOFTLOCKUP_DETECTOR=y
CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC=1
CONFIG_HARDLOCKUP_DETECTOR=y
CONFIG_BOOTPARAM_HARDLOCKUP_PANIC=y
CONFIG_DETECT_HUNG_TASK=y
CONFIG_DEFAULT_HUNG_TASK_TIMEOUT=120
CONFIG_BOOTPARAM_HUNG_TASK_PANIC=0

CONFIG_SOFTLOCKUP_DETECTORCONFIG_HARDLOCKUP_DETECTOR는 각각 soft lockup, hard lockup watchdog을 켠다(CONFIG_HARDLOCKUP_DETECTOR는 CPU/아키텍처가 NMI 기반 감지를 지원해야 켤 수 있다). 감지 시 panic 여부를 정하는 옵션의 타입이 서로 다른 점을 주의한다. CONFIG_BOOTPARAM_SOFTLOCKUP_PANICCONFIG_BOOTPARAM_HUNG_TASK_PANIC은 int 타입이라 =1/=0으로 값을 주지만, CONFIG_BOOTPARAM_HARDLOCKUP_PANIC은 bool 타입이라 =y/=n으로 설정한다.

런타임 제어 (sysctl / 부트 파라미터)

커널을 새로 빌드하지 않아도 /proc/sys/kernel/ 아래 sysctl 값으로 watchdog 동작을 조정할 수 있다.

sysctlDescription
kernel.watchdogsoft lockup, hard lockup watchdog을 한 번에 켜고 끄는 마스터 스위치. 기본값 1
kernel.soft_watchdog
kernel.nmi_watchdog
soft lockup, hard lockup watchdog을 개별로 켜고 끔. 기본값 1
kernel.watchdog_threshhrtimer/NMI 주기와 soft/hard lockup 임계값을 결정. 기본값 10(초)
kernel.watchdog_cpumaskwatchdog을 실행할 CPU 지정. 기본값은 전체 CPU
kernel.softlockup_panic
kernel.hardlockup_panic
soft/hard lockup 감지 시 panic 여부. 0 또는 1
kernel.hung_task_timeout_secsD state로 판단할 시간(초). 0이면 검사하지 않음. 기본값 120
kernel.hung_task_panic1회 검사에서 발견된 hung task 수가 이 값에 도달하면 panic
kernel.hung_task_warningshung task 경고를 출력할 최대 횟수. 검사 때마다 1씩 감소

부팅 커맨드라인에서도 동일한 항목을 제어할 수 있다.

nowatchdog              # soft/hard lockup watchdog을 모두 끔
nmi_watchdog=0          # hard lockup watchdog만 끔
softlockup_panic=1      # soft lockup 감지 시 panic
watchdog_thresh=10      # watchdog 임계값(초) 조정

watchdog 로그 해석

soft lockup이 감지되면 커널은 아래와 같은 형식의 메시지를 dmesg에 남긴다.

watchdog: BUG: soft lockup - CPU#2 stuck for 23s! [bash:1234]

CPU 번호와 몇 초나 멈춰 있었는지, 그 CPU에서 실행 중이던 태스크의 이름과 PID가 함께 출력되므로, 뒤이어 출력되는 콜 스택(call trace)과 함께 어느 코드에서 오래 붙잡고 있었는지 확인할 수 있다.

hung task가 감지되면 다음과 같은 메시지가 출력된다.

INFO: task python3:5678 blocked for more than 120 seconds.
      Not tainted 6.8.0 #1
"echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.

세 번째 줄처럼 커널이 직접 hung_task_timeout_secs를 0으로 두면 이 검사 자체를 끌 수 있다는 힌트를 로그에 남겨준다. 마찬가지로 태스크의 콜 스택이 함께 출력되므로 어떤 자원(락, I/O 등)을 기다리다가 멈췄는지 추적할 수 있다.

주의사항 및 팁

  • 가상머신, 특히 클라우드 인스턴스에서는 하이퍼바이저가 NMI를 제때 전달하지 못해 hard lockup watchdog이 오탐하거나 아예 동작하지 않는 경우가 있다. VM 환경이라면 nmi_watchdog=0으로 hard lockup 감지만 끄고 soft lockup 감지는 유지하는 것도 방법이다.
  • panic 옵션(softlockup_panic, hardlockup_panic, hung_task_panic)을 켜두면 lockup 발생 시 그 시점의 커널 상태를 kdump로 남기고 재부팅시킬 수 있어, 운영 서버에서는 로그만 남기는 것보다 원인 분석에 유리하다. 다만 panic이 곧 서비스 중단이므로 재부팅/kdump 절차가 준비된 뒤에 켜는 것이 안전하다.
  • watchdog 자체도 주기적으로 CPU를 깨우는 오버헤드가 있으므로, 레이턴시에 민감한 실시간(RT) 워크로드에서는 watchdog_cpumask로 watchdog을 돌릴 CPU를 isolcpus로 격리된 코어 밖으로 한정하는 것이 일반적이다.
  • hung_task 검사는 D state 진입 시점이 아니라 khungtaskd가 주기적으로 스캔하는 방식이라, 짧게 D state에 머물다 빠져나온 태스크는 감지되지 않는다. 실제로 멈춰 있는 것이 확인된 태스크만 리포트된다.

참고 사이트

답글 남기기