01 · QUESTION
무엇을 증명할 것인가
여러 오류 메시지가 동시에 보일 때 원인이 아니라 결과인 로그를 걷어내고, 정상에서 비정상으로 처음 바뀌는 단 하나의 경계를 어떻게 증명할 것인가?
PCIe, Ethernet, interrupt를 디버깅할 때 감으로 register를 훑지 않고 회로도·계측·controller·firmware·Linux probe·runtime queue·사용자 결과를 시간축 하나로 묶는 방법을 정리합니다. 이 글은 Linux 로그를 출발점으로 삼지 않습니다. 같은 사건이 보드, controller, firmware, kernel에서 어떤 이름과 번호로 바뀌는지 아래에서 위로 연결합니다.
02 · CONTRACT
입력과 완료 조건
| 구분 | 확인할 내용 |
|---|---|
| 입력 | 재현 가능한 stimulus, 보드 revision과 build ID, 시간 기준이 명시된 회로·register·kernel·traffic 기록 |
| 출력 | 첫 실패 경계, 그 경계의 소유자, 재현 조건, 반증 가능한 원인 가설, 수정 전후 비교 자료 |
| 소유권 | 각 계층의 측정 주체는 달라도 사건 번호와 timestamp ledger는 한 사람이 끝까지 관리 |
| 완료 조건 | cold/warm boot와 idle/load 조건에서 수정 전 실패와 수정 후 통과를 같은 절차로 반복해 차이를 설명함 |
03 · BOTTOM-UP
완전히 아래에서 시작하는 8단계
보드 revision, silicon stepping, firmware commit, kernel config, DTB hash, 전원 방식, 온도, cable/device를 고정합니다. 재현 명령과 성공/실패 판정도 숫자로 적습니다.
power rail, reset, clock, strap, pin mux, voltage domain을 net name 기준으로 정리합니다. datasheet의 절대 최대치가 아니라 operating condition과 timing requirement를 검사합니다.
오실로스코프, logic analyzer, protocol analyzer로 사건 전후를 trigger합니다. probe capacitance, ground lead, bandwidth가 신호를 바꾸지 않는지도 측정 조건에 기록합니다.
controller의 enable, status, error, queue head/tail을 사건 직전·직후·timeout 시점에 읽습니다. write-one-to-clear register는 읽은 뒤 실수로 상태를 지우지 않도록 순서를 정합니다.
Device Tree/ACPI가 주소, interrupt, clock, reset, IOMMU, DMA mask를 어떻게 OS에 넘겼는지 원본 blob과 live tree를 비교합니다. bootloader가 남긴 register state도 따로 기록합니다.
probe 진입, resource acquire, reset deassert, clock enable, DMA allocation, IRQ request 순서를 source 좌표와 trace로 연결합니다. probe 성공 로그 하나를 기능 검증으로 취급하지 않습니다.
descriptor와 buffer의 CPU/device ownership, producer/consumer index, memory barrier, interrupt mask, NAPI/poll completion을 사건 번호로 연결합니다. cache와 IOMMU fault도 같은 시간축에 둡니다.
link speed나 interface UP가 아니라 실제 config read, packet, interrupt latency, loss율을 측정합니다. 최소·평균·p99뿐 아니라 관측 시간 내 worst case와 timeout 조건을 함께 남깁니다.
1. Reproduction contract
보드 revision, silicon stepping, firmware commit, kernel config, DTB hash, 전원 방식, 온도, cable/device를 고정합니다. 재현 명령과 성공/실패 판정도 숫자로 적습니다.
이 단계의 통과 증거를 저장한 뒤에만 다음 단계인 2. Schematic boundary로 이동합니다.
2. Schematic boundary
power rail, reset, clock, strap, pin mux, voltage domain을 net name 기준으로 정리합니다. datasheet의 절대 최대치가 아니라 operating condition과 timing requirement를 검사합니다.
이 단계의 통과 증거를 저장한 뒤에만 다음 단계인 3. Physical measurement로 이동합니다.
3. Physical measurement
오실로스코프, logic analyzer, protocol analyzer로 사건 전후를 trigger합니다. probe capacitance, ground lead, bandwidth가 신호를 바꾸지 않는지도 측정 조건에 기록합니다.
이 단계의 통과 증거를 저장한 뒤에만 다음 단계인 4. Controller snapshot로 이동합니다.
4. Controller snapshot
controller의 enable, status, error, queue head/tail을 사건 직전·직후·timeout 시점에 읽습니다. write-one-to-clear register는 읽은 뒤 실수로 상태를 지우지 않도록 순서를 정합니다.
이 단계의 통과 증거를 저장한 뒤에만 다음 단계인 5. Firmware handoff로 이동합니다.
5. Firmware handoff
Device Tree/ACPI가 주소, interrupt, clock, reset, IOMMU, DMA mask를 어떻게 OS에 넘겼는지 원본 blob과 live tree를 비교합니다. bootloader가 남긴 register state도 따로 기록합니다.
이 단계의 통과 증거를 저장한 뒤에만 다음 단계인 6. Linux lifecycle로 이동합니다.
6. Linux lifecycle
probe 진입, resource acquire, reset deassert, clock enable, DMA allocation, IRQ request 순서를 source 좌표와 trace로 연결합니다. probe 성공 로그 하나를 기능 검증으로 취급하지 않습니다.
이 단계의 통과 증거를 저장한 뒤에만 다음 단계인 7. Runtime ownership로 이동합니다.
7. Runtime ownership
descriptor와 buffer의 CPU/device ownership, producer/consumer index, memory barrier, interrupt mask, NAPI/poll completion을 사건 번호로 연결합니다. cache와 IOMMU fault도 같은 시간축에 둡니다.
이 단계의 통과 증거를 저장한 뒤에만 다음 단계인 8. User-visible proof로 이동합니다.
8. User-visible proof
link speed나 interface UP가 아니라 실제 config read, packet, interrupt latency, loss율을 측정합니다. 최소·평균·p99뿐 아니라 관측 시간 내 worst case와 timeout 조건을 함께 남깁니다.
이 단계의 통과 증거를 저장한 뒤에만 최종 사용자 결과를 판정합니다.
04 · DIAGRAMS
주소와 상태가 이동하는 모습
재현 가능한 stimulus, 보드 revision과 build ID, 시간 기준이 명시된 회로·register·kernel·traffic 기록
각 계층의 측정 주체는 달라도 사건 번호와 timestamp ledger는 한 사람이 끝까지 관리
첫 실패 경계, 그 경계의 소유자, 재현 조건, 반증 가능한 원인 가설, 수정 전후 비교 자료
cold/warm boot와 idle/load 조건에서 수정 전 실패와 수정 후 통과를 같은 절차로 반복해 차이를 설명함
05 · DETAILED MANUAL
Hardware bring-up 실험을 재현 가능한 증거로 만드는 상세 매뉴얼
회로, 계측기, controller, firmware, kernel과 userspace 기록을 사건 번호와 시간축으로 합칩니다. 정상/실패의 정량 판정, 단일 변수 실험, fault injection과 수정 전후 동일 절차를 통해 처음 기대값과 달라진 경계를 찾습니다.
공개적으로 다시 확인할 수 있는 자료
공개되지 않은 vendor register나 integration별 offset을 추측해서 채우지 않았습니다. 아래 제조사 자료, architecture 문서와 Linux v6.18.37 원본에서 다시 확인할 수 있는 범위만 사용했습니다.
Linux ftrace
trace_marker, function graph와 event tracing
Linux DMA API HOWTO
DMA mapping, ownership과 cache synchronization
Linux PCIe AER
PCIe hierarchy error source와 recovery
Linux fault injection
allocation, I/O와 driver 오류 경로 검증
06 · REGISTERS
읽어야 할 상태와 해석
값 하나만 떼어 보지 말고 동일 사건의 enable, status, ownership, completion register를 한 줄에 기록합니다. read-to-clear나 write-one-to-clear 속성은 반드시 datasheet에서 먼저 확인합니다.
| 레지스터/상태 | 소유 블록 | 검증할 의미 |
|---|---|---|
| Identity | 보드/소프트웨어 | board rev, SoC stepping, bootloader/kernel/DTB hash |
| Stimulus | 재현 절차 | 명령, packet size/rate, endpoint, 사건 번호 |
| Clock/reset/power | 물리 경계 | 전압, 주파수, deassert timing, lock/ready |
| Status/error | controller | raw status와 masked status, sticky error |
| Queue ownership | DMA | base, size, head, tail, owner bit, buffer address |
| Mapping | firmware/Linux | MMIO range, IRQ hwirq/virq, IOVA/PA, clock/reset ID |
| Timestamp | 공통 원장 | 계측기, firmware, kernel, userspace clock의 기준 |
| Acceptance | 결과 | 정량 threshold와 반복 횟수, cold/warm/load matrix |
07 · LINUX SOURCE
Linux v6.18.37 원본 코드와 줄별 해설
라인 번호를 고정해 외우는 대신 함수 선언을 기준으로 Linux v6.18.37 tree에서 다시 찾았습니다. 로컬 tree에 있는 파일은 실제 코드를 직접 싣고, 나머지는 원본 파일과 읽을 함수 좌표를 연결했습니다.
Linux v6.18.37 · 원본 좌표
Documentation/trace/ftrace.rst
하드웨어 사건과 kernel 실행 경로를 같은 trace buffer에 맞추는 기반
- 읽을 함수
trace_marker- 읽을 함수
function_graph- 읽을 함수
events
Linux v6.18.37 · 원본 좌표
Documentation/core-api/dma-api-howto.rst
CPU와 device의 DMA buffer ownership 및 cache 동기화 계약
- 읽을 함수
dma_map_single()- 읽을 함수
dma_sync_single_for_cpu()- 읽을 함수
dma_sync_single_for_device()
Linux v6.18.37 · 원본 좌표
Documentation/PCI/pcieaer-howto.rst
PCIe error를 endpoint와 hierarchy 위치에 따라 수집하고 해석하는 방법
- 읽을 함수
AER service driver- 읽을 함수
error source
08 · VERIFICATION
실제 장비에서 확인하는 순서
- Baseline
실행 정상 보드 또는 이전 정상 build에서 같은 표와 파형을 먼저 수집합니다.
통과 기준 비교 대상의 revision 차이가 명시되고 정상 범위가 숫자로 남아야 합니다.
- Single event
실행 stimulus마다 monotonically increasing 사건 번호를 부여해 계측기 marker와 kernel trace_marker에 같이 기록합니다.
통과 기준 서로 다른 로그에서 같은 사건을 추측이 아니라 번호로 찾을 수 있어야 합니다.
- Boundary search
실행 아래에서 위로 checkpoint를 읽어 처음 기대값과 다른 지점에서 멈춥니다.
통과 기준 그 아래 계층은 통과 증거가 있고 그 경계부터 결과가 달라져야 합니다.
- Matrix
실행 cold/warm, idle/load, internal/external endpoint, feature on/off를 직교 조합으로 반복합니다.
통과 기준 원인 가설이 어떤 축에 민감한지 재현율로 드러나야 합니다.
- Fix proof
실행 수정 전후에 동일 capture script와 acceptance threshold를 사용합니다.
통과 기준 오류 로그 감소가 아니라 원래 실패한 사용자 결과가 반복 통과해야 합니다.
09 · FAILURE MATRIX
처음 끊긴 경계로 원인을 좁히기
| 관측 결과 | 우선 의심할 경계 | 다음 증거 |
|---|---|---|
| 로그가 매번 다름 | 재현 조건과 clock 기준이 고정되지 않음 | identity/stimulus ledger부터 작성 |
| register는 정상처럼 보임 | sticky/W1C/read side effect 또는 snapshot 시점 오류 | datasheet access semantics와 capture 순서 |
| probe 성공, 기능 실패 | runtime DMA/IRQ/PHY 경계를 검증하지 않음 | queue와 physical counter |
| 부하에서만 실패 | coalescing, ring wrap, credit, ordering, latency upper bound 문제 | burst capture와 worst-case timestamp |
| 수정 후 다른 보드에서 재발 | board revision/strap/firmware 차이를 누락 | identity matrix와 반증 시험 |
핵심은 마지막 오류 메시지가 아니라 처음 기대값과 달라진 checkpoint입니다. 그보다 아래의 통과 증거와 그 지점의 실패 증거를 한 묶음으로 남겨야 수정의 효과도 검증할 수 있습니다.