본문으로 건너뛰기
Embedded Performance Engineering · 50/57

실전 사례 — ISR Latency 100µs Deadline Miss 추적

· Hawk · 4분 읽기

#한 줄 요약

“평균은 멀쩡한데 0.1%가 deadline을 놓친다 — 범인은 같은 코어를 잠시 점유한 다른 IRQ 핸들러였습니다.”

#증상 — 보고된 문제

산업용 진동 센서 보드에서 사용 현장에서 산발적인 데이터 드롭이 보고됐습니다.

HW: Cortex-A53 quad-core, Linux 5.10, PREEMPT
센서: vibration sensor, 10 kHz sampling
요구: sample → user buffer 전달 deadline 100 µs
SLA: 99.99% 만족
현장 보고:
- 평균 latency 25 µs (여유 충분)
- 그러나 시간당 수십 회 deadline miss
- 미스 발생 시 latency 200~500 µs까지 spike
- 재현 시점이 일정하지 않음

평균은 한참 여유였지만 worst case가 spec을 깨고 있었습니다. 평균 metric만 보면 절대 발견할 수 없는 형태의 문제입니다.

#가설 1 — CPU 부하 과다

처음 의심한 것은 시스템 CPU 부하였습니다. 다른 프로세스가 CPU를 점유해 ISR이 늦게 처리되는 그림입니다.

Terminal window
mpstat -P ALL 1
# 12:30:01 CPU %usr %nice %sys %iowait %irq %soft %idle
# 12:30:02 all 18.5 0.0 7.2 0.5 0.3 1.1 72.4
# 12:30:02 0 22.0 0.0 8.0 0.0 0.5 2.1 67.4
# 12:30:02 1 17.2 0.0 7.0 1.0 0.2 0.8 73.8
# 12:30:02 2 18.0 0.0 7.5 0.5 0.3 0.9 72.8
# 12:30:02 3 17.0 0.0 6.5 0.3 0.2 0.6 75.4

전 코어 평균 idle 70% 이상. CPU는 충분히 여유가 있었습니다.

top에서도 CPU 사용량이 높은 프로세스는 보이지 않았고, 우리 사용자 데몬과 시스템 서비스 모두 5% 이하였습니다.

가설 1 기각: CPU 부하가 원인이 아닙니다.

#가설 2 — 다른 IRQ가 CPU를 점유

다음으로 의심한 것은 다른 IRQ 핸들러였습니다. Linux에서 한 IRQ가 처리되는 동안 같은 코어의 다른 IRQ는 막힙니다. 누군가가 IRQ disabled section을 길게 쥐고 있다면 우리 센서 IRQ는 그만큼 늦게 처리됩니다.

이 가설은 irqsoff tracer로 직접 측정할 수 있습니다.

Terminal window
# IRQ disabled 구간을 길이 순으로 추적
echo 0 > /sys/kernel/debug/tracing/tracing_max_latency
echo irqsoff > /sys/kernel/debug/tracing/current_tracer
echo 1 > /sys/kernel/debug/tracing/tracing_on
# 10분 가동 후
cat /sys/kernel/debug/tracing/tracing_max_latency
# 312

312 µs. ISR이 막힐 수 있는 최대 시간이 312 µs라는 뜻입니다. 우리 deadline 100 µs로는 절대 만족이 불가능한 수치였습니다.

trace 파일을 열어 어느 함수가 IRQ를 그렇게 오래 막았는지 확인합니다.

# tracer: irqsoff
#
# tracing_max_latency: 312 us, # at: <ffffffc010234abc>
# => sdhci_irq_handler+0x18/0x180 [sdhci]
# => __handle_irq_event_percpu+0x60/0x1c0
# => handle_irq_event+0x40/0x90
# => handle_fasteoi_irq+0xc4/0x180
#
# CPU#: 1
# Pid: 0 (swapper/1)
#
# => 0us : sdhci_irq_handler
# => 50us : sdhci_data_irq
# => 180us : sdhci_finish_data
# => 280us : sdhci_request_done
# => 312us : irq_exit

SD 카드 컨트롤러의 IRQ 핸들러가 312 µs 동안 IRQ를 막고 있었습니다. SD 카드는 시스템 로그를 주기적으로 쓰는 곳이었고, 로그 flush 타이밍이 우리 deadline miss와 정확히 일치했습니다.

가설 2 확정: SD 카드 IRQ 핸들러가 범인입니다.

#원인 — 진단 확정

소스를 보니 sdhci_irq_handler가 데이터 전송 완료 후 다음 작업을 IRQ context에서 모두 처리하고 있었습니다.

/* 간략화한 흐름 */
irqreturn_t sdhci_irq_handler(int irq, void *dev)
{
spin_lock(&host->lock);
if (intmask & SDHCI_INT_DATA_END) {
sdhci_data_irq(host); /* ~50 µs */
sdhci_finish_data(host); /* ~130 µs */
sdhci_request_done(host); /* ~100 µs */
}
spin_unlock(&host->lock);
return IRQ_HANDLED;
}

IRQ가 disabled된 채 약 300 µs 동안 모든 후처리가 진행되고 있었습니다. 같은 코어에 우리 센서 IRQ가 도착하면 이 작업이 끝날 때까지 무조건 대기해야 했습니다.

원인이 분명해지자 해결책은 두 갈래로 나뉘었습니다.

  1. SD 카드 IRQ를 다른 코어로 격리
  2. SD 카드 핸들러를 threaded IRQ로 전환해 짧게 만들기

두 가지 모두 적용했습니다.

#해결 — IRQ Affinity와 Threaded IRQ

먼저 SD 카드 IRQ를 우리 센서가 쓰지 않는 코어로 옮겼습니다.

Terminal window
# 센서 IRQ 번호 확인
grep sensor /proc/interrupts
# 142: ... vibration_sensor
# SD 카드 IRQ 번호 확인
grep mmc /proc/interrupts
# 89: ... mmc0
# 센서는 코어 0, SD는 코어 3으로 고정
echo 1 > /proc/irq/142/smp_affinity
echo 8 > /proc/irq/89/smp_affinity

다음으로 SD 카드 드라이버의 threaded IRQ 옵션을 활성화했습니다. 최소한의 ack만 IRQ context에서 처리하고, 나머지는 커널 thread에서 일반 스케줄로 돌리는 방식입니다.

/* 커널 부팅 파라미터 */
threadirqs
/* 또는 드라이버 자체에서 */
request_threaded_irq(irq, sdhci_irq, sdhci_thread_irq,
IRQF_SHARED, "mmc0", host);

ISR에서는 SDHCI 상태 register만 읽고 인터럽트 소스를 마스킹한 뒤 즉시 반환합니다. 데이터 후처리는 thread context에서 일반 우선순위로 처리되므로 다른 IRQ를 막지 않습니다.

마지막으로 측정 결과를 검증할 수 있도록 ftrace histogram을 상시 켰습니다.

Terminal window
echo 'hist:keys=common_pid:vals=hitcount:sort=hitcount' \
> /sys/kernel/debug/tracing/events/irq/irq_handler_entry/trigger

#검증 — Before / After

10분 동안 deadline 만족률을 측정했습니다.

지표BeforeAfter
Avg latency25 µs22 µs
P99 latency85 µs41 µs
P999 latency240 µs58 µs
Max latency512 µs78 µs
Deadline miss rate0.12%0.0001%
SLA 99.99%미달충족

평균은 거의 변하지 않았지만 worst case가 절반 이하로 떨어졌습니다. SLA 99.99% deadline 100 µs를 안정적으로 만족하게 됐습니다.

추가로 SD 카드 throughput은 약 5% 감소했지만, 시스템 로깅 용도였으므로 문제가 되지 않았습니다.

#교훈

이번 사례에서 배운 것을 정리합니다.

  • 평균은 거짓말입니다. RT 시스템에서는 P99, P999, max만 의미 있습니다. perf stat처럼 누적 metric만 보면 worst case 문제는 보이지 않습니다.
  • ISR은 공유 자원입니다. 한 핸들러가 길게 IRQ를 disabled 상태로 두면 같은 코어의 다른 모든 IRQ가 영향을 받습니다.
  • irqsoff tracer는 강력합니다. “어디서 IRQ가 막혔는지”를 직접 보여 주는 거의 유일한 도구이며, 이런 산발적 문제에 처음 꺼낼 카드입니다.
  • IRQ affinity로 격리. RT critical IRQ는 다른 시끄러운 IRQ와 코어를 분리하는 것이 정석입니다.
  • Threaded IRQ는 비용을 분산. 무거운 후처리를 thread context로 옮기면 IRQ disabled 시간을 짧게 유지할 수 있습니다.
  • 가설을 측정으로 끝까지 검증. 첫 가설(CPU 부하)이 빨리 기각된 것이 다음 가설로 빠르게 넘어갈 수 있게 했습니다.

가장 중요한 교훈은 “평균이 OK라도 안심하면 안 된다”입니다. RT 시스템의 SLA는 worst case로 정의되므로, 측정 도구도 worst case를 보여 주는 것을 골라야 합니다.

#관련 항목

Embedded Performance Engineering · 51 of 57

  1. 1Embedded Performance Engineering — 임베디드 성능 엔지니어링 시리즈 소개
  2. 2임베디드 성능 분석 방법론 — Measure → Analyze → Optimize 사이클
  3. 3성능 지표 정의 — Latency·Throughput·Utilization 분석
  4. 4성능 측정의 기본 — Wall-Clock·CPU Cycle·Instruction Count
  5. 5성능 데이터 통계적 분석 — Percentile·Histogram·평균의 함정
  6. 6실시간 성능 분석 — WCET·Jitter·Deadline Miss 측정
  7. 7임베디드 벤치마킹 기초 — 재현성·Warmup·노이즈 제거
  8. 8성능 모델링 — Amdahl·Gustafson·Roofline Model 적용
  9. 9프로파일링 기법 개요 — Sampling vs Instrumentation·PGO·LTO
  10. 10CPU 파이프라인 분석 — 5-stage·Cortex-M·Cortex-A 비교
  11. 11Pipeline Stall 분석 — Data·Structural·Control Hazard·Forwarding
  12. 12Branch Prediction 분석 — Static·2-bit·BTB·BHT·Mispredict 비용
  13. 13Speculative Execution 분석 — OoO·Reorder Buffer·Register Renaming
  14. 14CPU Cache 기초 — L1·L2·L3·Set Associative·Replacement Policy
  15. 15Cache Miss 3C Model 분석 — Compulsory·Capacity·Conflict
  16. 16Cache Line 최적화 — Alignment·Prefetch·False Sharing 처리
  17. 17메모리 대역폭 분석 — STREAM·Roofline·Bus Saturation 측정
  18. 18SIMD·NEON 활용 — 128-bit Vector·Auto-Vectorization·SVE/SVE2
  19. 19PMU·HPM 하드웨어 카운터 분석 — 정밀 성능 진단
  20. 20임베디드 Bus Architecture — AHB·AXI·CHI 진화와 5-Channel
  21. 21Bus Contention 진단 — Arbitration·QoS·Starvation 측정
  22. 22DMA 성능 최적화 — Burst·Scatter-Gather·Chain·Cache 일관성
  23. 23DMA vs CPU Copy 성능 비교 — Break-even·Setup Overhead 실측
  24. 24Interrupt Latency 분석 — 진입·종료·Tail-Chaining·Late Arrival
  25. 25Interrupt Storm 처리 — NAPI·Rate-Limit·Polling 전환
  26. 26MMIO 접근 성능 — Cache Policy·Write-Combining·Volatile·Barrier
  27. 27Peripheral Clock 분석 — PLL·Divider·Gating·DVFS
  28. 28Power vs Performance 트레이드오프 — DVFS·Race-to-Idle·Big.LITTLE
  29. 29Thermal Throttling 분석 — Junction Temp·Trip Point·냉각
  30. 30CXL Interconnect 분석 — AI 시대 메모리 대역폭 확장
  31. 31Concurrency 기초 — Concurrency vs Parallelism·Race·Memory Model
  32. 32False Sharing 진단 — Cache Line Ping-Pong·Padding·측정
  33. 33Lock Contention 분석 — Wait·Hold·Convoy·측정 기법
  34. 34Spinlock 성능 분석 — Spin-Wait vs Context Switch·Ticket·MCS
  35. 35Mutex 성능 분석 — Futex·Adaptive·Priority Inheritance
  36. 36Reader-Writer Lock 성능 — Reader/Writer Priority·RCU·Seqlock
  37. 37Lock-Free 자료구조 성능 — CAS·ABA·Hazard Pointer·Epoch Reclamation
  38. 38Memory Ordering 분석 — Acquire·Release·Seq-Cst·ARM Relaxed Model
  39. 39Cache Coherency 프로토콜 — MESI·MOESI·Snoop·Directory
  40. 40SMP 성능 분석 — Per-Core·Affinity·Load Balance·Scalability
  41. 41Linux perf 기초 — stat·record·report 활용
  42. 42Linux perf 고급 — Raw Event·Tracepoint·perf script
  43. 43ftrace 활용 — function·function_graph·latency tracer
  44. 44eBPF·bpftrace 동적 트레이싱 — 커널 무수정 관측
  45. 45Flamegraph 분석 — On-CPU·Off-CPU·Differential
  46. 46ARM DS·Lauterbach 분석 — Hardware Trace 전문 도구
  47. 47Bare-metal 프로파일링 — GPIO·DWT·SysTick·ITM 활용
  48. 48NVIDIA Nsight Systems — GPU·NPU 포함 시스템 분석
  49. 49모던 프로파일러 비교 — Tracy·Hotspot·uftrace·Coz
  50. 50연속 프로파일링 — Parca·Pixie·Pyroscope·Tetragon
  51. 51실전 사례 — ISR Latency 100µs Deadline Miss 추적
  52. 52실전 사례 — Matrix Multiply가 예상의 10배 느린 이유
  53. 53실전 사례 — 8-core가 4-core를 넘으면 throughput이 떨어지는 이유
  54. 54실전 사례 — 카메라 1080p 60fps가 30fps로 떨어지는 이유
  55. 55CXL.mem 지연·대역폭 실측 — Direct·Switch·Pooled 토폴로지 비교
  56. 56CXL 성능 프로파일링 도구 — cxl-cli·DAMON·perf-mem 활용
  57. 57실전 사례 — CXL.mem 추가로 LLM inference KV cache 처리량 회복