Part 4: DPAS 커널 실습

QEMU VM에서 INT/CP/PAS/DPAS 성능을 직접 비교

2026-1 시스템최신기술
← 전체 실습 안내 | ← DPAS 소개
English

사전 준비

플랫폼별 QEMU 설치 및 Ubuntu VM 구축이 완료된 상태여야 합니다.

필수 환경 Ubuntu 22.04 VM + mainline 커널 5.18 (uname -r5.18.0-051800-generic)
왜 커널 5.18인가? 이 실습에서는 preadv2(RWF_HIPRI)를 통한 동기식 Direct I/O polling을 사용합니다. 이 기능은 Linux 커널 5.19부터 제거되었기 때문에, 5.18이 이 기능을 지원하는 마지막 커널입니다. QEMU Guest VM 안에서 커널 5.18을 사용하므로, 호스트 OS의 커널 버전과는 무관하게 polling 실습이 가능합니다.

I/O Completion 배경 읽기 자료 → — Interrupt/Polling의 역사, FAST '26 최신 연구, io_uring과 polling의 미래
dpas.patch 개선 과정 → — QEMU 환경에서 발견한 3가지 문제와 커널 디버깅 과정

Part 1: DPAS 커널 설치

제공된 deb 패키지로 DPAS 패치가 적용된 커널을 설치합니다.

deb 패키지란? .deb 파일은 Ubuntu/Debian 계열 Linux에서 사용하는 소프트웨어 설치 패키지입니다. Windows의 .msi 설치 파일, macOS의 .pkg와 같은 역할입니다. sudo dpkg -i 파일.deb 명령으로 설치하며, 커널 이미지·헤더 등 시스템 파일을 자동으로 올바른 위치에 배치해 줍니다.
아키텍처 선택 — 호스트 OS가 아닌 CPU 아키텍처 기준 커널 패키지는 호스트 운영체제(macOS/Windows/Linux)와 무관하게, VM이 실행되는 CPU 아키텍처에 따라 선택합니다. Guest VM 안에서 uname -m을 실행하면 아키텍처를 확인할 수 있습니다.
호스트 환경uname -m 결과필요한 패키지
Apple Silicon Mac (M1/M2/M3/M4)aarch64ARM64
Intel Mac / Linux PC / Windows (WSL2)x86_64x86_64

커널 패키지 다운로드

VM의 CPU 아키텍처에 맞는 패키지를 다운로드하세요.

ARM64 커널 이미지 (23MB) ARM64 헤더 (8MB)
x86_64 커널 이미지 (12MB) x86_64 헤더 (8MB)

고급: 직접 커널을 빌드하려는 경우

dpas.patch 다운로드

1-1. 패키지 전송

다운로드한 deb 파일을 호스트에서 VM으로 전송합니다.

# 호스트 터미널에서 실행
scp -P 2222 linux-image-5.18.0-dpas_*.deb <username>@localhost:~/
scp -P 2222 linux-headers-5.18.0-dpas_*.deb <username>@localhost:~/

1-2. 설치 및 재부팅

# VM 내부에서 실행
sudo dpkg -i ~/linux-image-5.18.0-dpas_*.deb ~/linux-headers-5.18.0-dpas_*.deb
sudo reboot

1-3. 설치 확인

uname -r
# 5.18.0-dpas
부팅 커널이 변경되지 않은 경우 GRUB이 이전 커널로 부팅할 수 있습니다. 아래 명령으로 DPAS 커널을 선택 후 재부팅하세요:
sudo grub-reboot "Advanced options for Ubuntu>Ubuntu, with Linux 5.18.0-dpas"
sudo reboot

Part 2: 실습 — 모드별 성능 비교

2-1. 벤치마크 스크립트

통합 벤치마크 스크립트 bench.sh를 사용합니다. 5개 I/O completion 모드를 선택적으로 실행하며, 각 모드에서 job 수(1,2,4,8,16)별 IOPS, latency, CPU 사용률, context switch 횟수를 측정합니다.

bench.sh 다운로드

지원 모드

모드설명io_poll_delaypas_enabledhipri
INTInterrupt completion (no polling)-10No
CPClassic Polling (busy-poll)-10Yes
LHPLinux Hybrid Polling (adaptive sleep)00Yes
PASPAS (poll-after-sleep, adaptive)01Yes
DPASDynamic PAS (auto mode switching)01Yes

주요 특징

사용법

# VM으로 전송
scp -P 2222 bench.sh <username>@localhost:~/

# VM 내부에서 실행
chmod +x bench.sh

# 기본: 모드 지정 필수, R(read)/W(write) 선택, 반복 횟수 선택
sudo bash bench.sh [모드...] [R|W] [반복횟수]

# 예시
sudo bash bench.sh ALL                  # 전체 모드, randread, 1회
sudo bash bench.sh INT CP DPAS W 3      # 3개 모드, randwrite, 3회
sudo bash bench.sh DPAS 2               # DPAS만, randread, 2회
sudo bash bench.sh ALL W 2              # 전체 모드, randwrite, 2회

# 인자 순서 자유 (아래는 모두 동일)
sudo bash bench.sh INT 2 R CP
sudo bash bench.sh R CP INT 2
sudo bash bench.sh 2 CP R INT

출력 예시

=== INT ===
Jobs         IOPS       lat_us     cpu%        ctx
----       ------       ------    -----     ------
1            7253        137.3      0.2     145087
2           16294        122.2      0.8     250851
...

=== DPAS ===
Jobs         IOPS       lat_us     cpu%        ctx   breakdown
----       ------       ------    -----     ------   ----------
1           18694         53.1     94.7      18058   cp:170035 pas:17100 ol:0 int:0
2           24119         82.1      2.3     398800   cp:1000 pas:800 ol:2801 int:236614
...

2-2. 결과 기록

스크립트 출력의 IOPS를 아래 표에 기록합니다.

Modej=1j=2j=4j=8j=16
INT
CP
LHP
PAS
DPAS

2-3. DPAS mode transition 확인

bench.sh 실행 결과의 DPAS breakdown에서 모드 전환을 확인합니다:

MODE 값의미
0INT (Interrupt)
1CP (Continuous Polling)
2PAS (초기 상태)
3OL (Overloaded)

기대 동작

Jobs기대 최종 모드이유
1CP (MODE 1)QD=1 → PAS→CP 전환, polling으로 최저 latency
2+INT (MODE 0)QD>1 → PAS→OL→INT 전환, 인터럽트로 CPU 양보

2-4. 분석 질문

  1. INT와 CP 모드에서 j=1일 때 IOPS 차이의 원인은 무엇인가?
  2. CP 모드에서 job 수를 늘려도 IOPS가 크게 증가하지 않는 이유는? (CPU 사용률 관점)
  3. DPAS가 j=1에서 CP에 근접하고, j≥4에서 INT에 근접하는 이유를 mode transition 관점에서 설명하시오.
  4. switch_stat의 DPAS breakdown에서 pas io 수가 polled io보다 항상 적은 이유는?
  5. d_init 값이 DPAS 모드 전환에 미치는 영향을 설명하시오.

Part 3: DPAS 커널 내부 구조

3-0. IO 경로와 DPAS 개입 지점

fio가 IO를 제출하면 커널 block layer를 거쳐 NVMe 드라이버에 도달합니다. DPAS는 polling 경로에서 completion 대기 방식을 제어합니다.

/* Userspace */
fio (pvsync2 --hipri)
  └─ preadv2(RWF_HIPRI)                   /* polling 요청 플래그 */

/* Kernel: IO 제출 (block/fops.c) */
  └─ blk_mq_submit_bio()                  /* block layer 진입 */
       ├─ ★ INT→OL 전환 (int_cnt ≥ 10000)  /* 제출 경로에서 재평가 */
       └─ nvme_queue_rq()                   /* NVMe 드라이버로 전달 */

/* Kernel: IO 완료 대기 (block/blk-mq.c) */
  └─ blk_mq_poll()                        /* polling 진입점 */
       └─ blk_mq_poll_hybrid()             /* ★ DPAS 핵심: 모드 판단 */
            ├─ blk_mq_poll_pas_nsecs()      /* adaptive sleep 계산 (dur, tf) */
            ├─ mode transition logic         /* CP↔PAS→OL→INT 전환 */
            └─ hrtimer_nanosleep()           /* PAS: sleep 후 poll */
               또는 즉시 poll (CP)
               또는 return → interrupt 대기 (INT)
소스 코드 위치 모드 전환 로직은 두 곳에 분산되어 있습니다: block/blk-mq.c의 polling 경로 (CP↔PAS→OL→INT)와 block/fops.c의 제출 경로 (INT→OL 재평가).

sysfs 인터페이스

DPAS 커널은 /sys/block/nvme0n1/queue/ 아래에 다음 파라미터를 제공합니다.

파라미터설명기본값
switch_enabledDPAS mode switching 활성화0
switch_statCPU별 모드 통계 출력 (읽기 전용)
pas_enabledPAS 모드 활성화0
pas_adaptive_enabledAdaptive sleep 활성화0
switch_param1PAS→OL 임계치 (tf > param1). 논문 설정: 00
switch_param2OL→PAS 임계치 (avg QD ≤ param2)10
switch_param3OL→INT 임계치 (avg QD > param3)10
switch_param4PAS→CP 전환 활성화 (0/1)1
pas_d_initAdaptive sleep 초기값 및 최소값 (ns). bench.sh는 avg_lat/10으로 자동 설정100
QD 10x multiplier QD 관련 파라미터는 10배 스케일을 사용합니다. param2=10은 average QD ≤ 1.0을 의미합니다. 이는 정수 연산으로 소수점 정밀도를 확보하기 위한 설계입니다.
모드 전이 다이어그램 DPAS의 4가지 모드(PAS/CP/OL/INT)와 전환 조건을 시각화한 다이어그램은 DPAS 소개 페이지에서 확인하세요.

3-1. 핵심 제어 로직 (block/blk-mq.c)

아래 코드는 blk_mq_poll_hybrid() 내부의 DPAS 모드 전환 로직입니다. sysfs 파라미터가 실제로 어떻게 사용되는지 확인하세요.

① PAS 평가 (100 IO마다)

/* PAS 모드에서 100개 IO를 수집하면 평가 */
if(sc->mode == _PAS && sc->pas_cnt >= 100) {
    average_qd = sc->qd_sum * 10 / sc->pas_cnt;  /* 10x 정밀도 */

    if(sc->param4 >= 1 && average_qd == 10) {  /* ← switch_param4: PAS→CP 활성화 */
        sc->mode = _CP;          /* QD=1.0 → polling이 유리 */

    } else if(sc->tf > sc->param1) {            /* ← switch_param1: PAS→OL 임계치 */
        sc->mode = _OL;          /* timer failure → overloaded */

    } else {                                  /* 유지: PAS에 머무름 */
        sc->pas_cnt = 0;
        sc->qd_sum = 0;
        sc->tf = 0;              /* tf 리셋 (매 평가마다) */
    }
}

② OL 평가 (100 IO마다)

/* OL(Overloaded) 모드에서 100개 IO를 수집하면 평가 */
if(sc->mode == _OL && sc->ol_cnt >= 100) {
    average_qd = sc->qd_sum * 10 / sc->ol_cnt;

    if(average_qd <= sc->param2) {             /* ← switch_param2: OL→PAS 복귀 */
        sc->mode = _PAS;         /* 부하 감소 → sleep 재시도 */

    } else if(average_qd > sc->param3) {       /* ← switch_param3: OL→INT 전환 */
        sc->mode = _INT;         /* 고부하 → interrupt 모드 */

    } else {                                  /* 유지: OL에 머무름 */
        sc->ol_cnt = 0;
        sc->qd_sum = 0;
    }
}

③ Adaptive Sleep & Timer Failure

/* sr_pnlt: 이전 IO가 oversleep(0) / undersleep(1)
   sr_last:  현재 IO가 oversleep(0) / undersleep(1)
   case: sr_pnlt*2 + sr_last → 0,1,2,3 */
cur_case = stat.sr_pnlt * 2 + stat.sr_last;
switch(cur_case) {
    case 0: adj -= dn;                         /* over,over → sleep 감소 */
    case 1: adj = div + up;                     /* over,under → sleep 약간 증가 */
    case 2: adj = div - dn;                     /* under,over → sleep 약간 감소 */
    case 3: adj += up;                          /* under,under → sleep 증가 */
}

stat.dur = stat.dur * adj / div;              /* sleep 시간 업데이트 */

if(stat.dur < q->d_init) {                   /* ← pas_d_init: 최소 sleep */
    stat.dur = q->d_init;                     /* floor clamp */
    sc->tf++;                                 /* timer failure 증가! */
}                                             /* → tf가 쌓이면 PAS→OL 전환 */
핵심 흐름 Adaptive sleep이 dur을 최적화하는데, 부하가 높으면 dur이 계속 줄어 d_init 바닥에 도달 → tf++tf > param1이면 PAS→OL 전환 → QD가 여전히 높으면 OL→INT로 interrupt 모드 전환. 반대로 QD=1이면 PAS→CP로 전환하여 polling으로 최저 latency 달성.

Part 4: 커널 소스 컴파일 도전과제

deb 패키지 대신 직접 커널 소스에 DPAS 패치를 적용하고 빌드합니다.

4-1. 빌드 의존성 설치

sudo apt install -y build-essential libncurses-dev bison flex libssl-dev \
    libelf-dev bc pahole dwarves zstd

4-2. 커널 소스 다운로드

cd ~
wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.18.tar.xz
tar xf linux-5.18.tar.xz
cd linux-5.18

4-3. DPAS 패치 적용

# 호스트에서 VM으로 패치 전송 후
patch -p1 < ~/dpas.patch
Makefile hunk 실패 패치가 5.18-rc6 기준이라 Makefile hunk 하나가 실패합니다. 무시해도 됩니다.
대신 localversion을 수동 설정: echo "-dpas" > localversion

4-4. 커널 설정 (최소화)

cp /boot/config-$(uname -r) .config
yes '' | make localmodconfig
왜 localmodconfig? 기본 config로 빌드하면 수천 개 모듈이 포함되어 디스크 부족(No space left on device)이 발생합니다. localmodconfig는 현재 로드된 모듈만 포함하여 빌드 시간과 디스크 사용량을 대폭 줄입니다.

4-5. 빌드

make -j$(nproc) bindeb-pkg

빌드 완료 시 홈 디렉토리에 linux-image-*.deb, linux-headers-*.deb 파일이 생성됩니다.

4-6. 설치

sudo dpkg -i ~/linux-image-5.18.0-dpas_*.deb ~/linux-headers-5.18.0-dpas_*.deb
sudo reboot

트러블슈팅

증상원인해결
No space left on device빌드 산출물이 디스크 초과make clean, tarball 삭제, localmodconfig 사용
Makefile Hunk #1 FAILED패치 버전 차이무시, localversion으로 대체
부팅 후 커널 미변경GRUB 기본 부팅 커널grub-reboot으로 DPAS 커널 선택
다수 hunk 실패이미 패치 적용된 소스에 재적용소스를 다시 풀고 재시도
빌드 15GB+ 필요디스크 여유 부족df -h / 확인, LVM 확장 또는 파일 정리

참고: QEMU 환경의 제약

항목QEMU실제 하드웨어
CP vs INT 비교유효 (1.7-1.8x IOPS 차이)유효
PAS adaptive sleep타이머 부정확, tf 노이즈정확
DPAS mode transition동작, 절대 성능은 참고용기본 파라미터로 동작
Multi-core 스케일링에뮬레이션 오버헤드로 왜곡Linear에 근접
WBT (Write Back Throttling)미지원 → QUEUE_FLAG_STATS 비활성활성 (wbt_lat_usec=2000)
실습 핵심 절대 성능 수치보다 모드 간 상대적 차이와 전환 동작에 집중하세요. I/O completion 메커니즘의 원리를 이해하는 것이 목표입니다.

dpas.patch 개선 과정 보기 → — QEMU 환경에서 발견된 문제와 수정 과정, 그리고 AI 도구(Claude Code)를 활용한 커널 디버깅 사례를 설명합니다. AI가 제안한 수정은 정확했지만 원인 설명에 오류가 있었고, 이를 연구자가 식별하여 검증한 과정을 소개합니다. 시스템 분야에서 AI 도구를 올바르게 활용하는 방법을 생각해 보세요.