NVMe I/O Completion 모드별 성능 비교 실습
2026-1 시스템최신기술플랫폼별 QEMU 설치 및 Ubuntu VM 구축이 완료된 상태여야 합니다.
uname -r → 5.18.0-051800-generic)
preadv2(RWF_HIPRI)를 통한 동기식 Direct I/O polling을 사용합니다.
이 기능은 Linux 커널 5.19부터 제거되었기 때문에, 5.18이 이 기능을 지원하는 마지막 커널입니다.
QEMU Guest VM 안에서 커널 5.18을 사용하므로, 호스트 OS의 커널 버전과는 무관하게 polling 실습이 가능합니다.
제공된 deb 패키지로 DPAS 패치가 적용된 커널을 설치합니다.
.deb 파일은 Ubuntu/Debian 계열 Linux에서 사용하는 소프트웨어 설치 패키지입니다.
Windows의 .msi 설치 파일, macOS의 .pkg와 같은 역할입니다.
sudo dpkg -i 파일.deb 명령으로 설치하며, 커널 이미지·헤더 등 시스템 파일을 자동으로 올바른 위치에 배치해 줍니다.
uname -m을 실행하면 아키텍처를 확인할 수 있습니다.
| 호스트 환경 | uname -m 결과 | 필요한 패키지 |
|---|---|---|
| Apple Silicon Mac (M1/M2/M3/M4) | aarch64 | ARM64 |
| Intel Mac / Linux PC / Windows (WSL2) | x86_64 | x86_64 |
VM의 CPU 아키텍처에 맞는 패키지를 다운로드하세요.
다운로드한 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:~/
# VM 내부에서 실행
sudo dpkg -i ~/linux-image-5.18.0-dpas_*.deb ~/linux-headers-5.18.0-dpas_*.deb
sudo reboot
uname -r
# 5.18.0-dpas
sudo grub-reboot "Advanced options for Ubuntu>Ubuntu, with Linux 5.18.0-dpas"sudo reboot
통합 벤치마크 스크립트 bench.sh를 사용합니다. 4개 모드(INT/CP/PAS/DPAS) × 5개 job 수(1,2,4,8,16)를 자동으로 실행합니다.
taskset -c 0)modprobe nvme 리로드d_init = avg_lat/10 자동 설정param1=0, param2=10, param3=10, param4=1# VM으로 전송
scp -P 2222 bench.sh <username>@localhost:~/
# VM 내부에서 실행
chmod +x bench.sh
sudo bash bench.sh
스크립트 출력의 IOPS를 아래 표에 기록합니다.
| Mode | j=1 | j=2 | j=4 | j=8 | j=16 |
|---|---|---|---|---|---|
| INT | |||||
| CP | |||||
| PAS | |||||
| DPAS |
bench.sh 실행 결과의 DPAS breakdown에서 모드 전환을 확인합니다:
| MODE 값 | 의미 |
|---|---|
| 0 | INT (Interrupt) |
| 1 | CP (Continuous Polling) |
| 2 | PAS (초기 상태) |
| 3 | OL (Overloaded) |
| Jobs | 기대 최종 모드 | 이유 |
|---|---|---|
| 1 | CP (MODE 1) | QD=1 → PAS→CP 전환, polling으로 최저 latency |
| 2+ | INT (MODE 0) | QD>1 → PAS→OL→INT 전환, 인터럽트로 CPU 양보 |
switch_stat의 DPAS breakdown에서 pas io 수가 polled io보다 항상 적은 이유는?d_init 값이 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 재평가).
DPAS 커널은 /sys/block/nvme0n1/queue/ 아래에 다음 파라미터를 제공합니다.
| 파라미터 | 설명 | 기본값 |
|---|---|---|
switch_enabled | DPAS mode switching 활성화 | 0 |
switch_stat | CPU별 모드 통계 출력 (읽기 전용) | — |
pas_enabled | PAS 모드 활성화 | 0 |
pas_adaptive_enabled | Adaptive sleep 활성화 | 0 |
switch_param1 | PAS→OL 임계치 (tf > param1). 논문 설정: 0 | 0 |
switch_param2 | OL→PAS 임계치 (avg QD ≤ param2) | 10 |
switch_param3 | OL→INT 임계치 (avg QD > param3) | 10 |
switch_param4 | PAS→CP 전환 활성화 (0/1) | 1 |
pas_d_init | Adaptive sleep 초기값 및 최소값 (ns). bench.sh는 avg_lat/10으로 자동 설정 | 100 |
param2=10은 average QD ≤ 1.0을 의미합니다.
이는 정수 연산으로 소수점 정밀도를 확보하기 위한 설계입니다.
tf (timer failure): adaptive sleep의 dur이 d_init 바닥에 닿을 때 증가.
QD가 높아 sleep이 계속 줄어들면 tf가 쌓여 PAS→OL 전환이 발생합니다.
아래 코드는 blk_mq_poll_hybrid() 내부의 DPAS 모드 전환 로직입니다.
sysfs 파라미터가 실제로 어떻게 사용되는지 확인하세요.
/* 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(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;
}
}
/* 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 전환 */
dur을 최적화하는데, 부하가 높으면 dur이 계속 줄어
d_init 바닥에 도달 → tf++ → tf > param1이면 PAS→OL 전환 →
QD가 여전히 높으면 OL→INT로 interrupt 모드 전환.
반대로 QD=1이면 PAS→CP로 전환하여 polling으로 최저 latency 달성.
deb 패키지 대신 직접 커널 소스에 DPAS 패치를 적용하고 빌드합니다.
sudo apt install -y build-essential libncurses-dev bison flex libssl-dev \
libelf-dev bc pahole dwarves zstd
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
# 호스트에서 VM으로 패치 전송 후
patch -p1 < ~/dpas.patch
echo "-dpas" > localversion
cp /boot/config-$(uname -r) .config
yes '' | make localmodconfig
No space left on device)이 발생합니다.
localmodconfig는 현재 로드된 모듈만 포함하여 빌드 시간과 디스크 사용량을 대폭 줄입니다.
make -j$(nproc) bindeb-pkg
빌드 완료 시 홈 디렉토리에 linux-image-*.deb, linux-headers-*.deb 파일이 생성됩니다.
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 | 실제 하드웨어 |
|---|---|---|
| CP vs INT 비교 | 유효 (1.7-1.8x IOPS 차이) | 유효 |
| PAS adaptive sleep | 타이머 부정확, tf 노이즈 | 정확 |
| DPAS mode transition | 동작, 절대 성능은 참고용 | 기본 파라미터로 동작 |
| Multi-core 스케일링 | 에뮬레이션 오버헤드로 왜곡 | Linear에 근접 |