가상화 (Virtualization)

Linux KVM/QEMU 심화: VMCS/VMCB, EPT/NPT 다단계 페이지 워크, virtqueue split/packed ring, vhost, irqfd, VFIO, SEV/TDX, 라이브 마이그레이션, 성능 튜닝 종합 가이드.

관련 표준: Intel VT-x (VMX, VMCS, EPT), AMD-V (SVM, VMCB, NPT), virtio Specification 1.2 (가상 I/O 디바이스) — KVM이 구현하는 하드웨어 가상화 및 반가상화 규격입니다. 종합 목록은 참고자료 — 표준 & 규격 섹션을 참고하세요.

가상화 개요

Linux의 KVM(Kernel-based Virtual Machine)은 커널을 Type-1 하이퍼바이저로 변환합니다. 하드웨어 가상화 확장(Intel VT-x, AMD-V)을 활용하여 게스트 OS를 네이티브에 가까운 속도로 실행합니다.

KVM 아키텍처 Guest VM 1 (Linux, Windows, ...) Guest VM 2 (Linux, Windows, ...) QEMU (디바이스 에뮬레이션) KVM (/dev/kvm) + Linux Kernel Hardware (Intel VT-x / AMD-V, EPT/NPT)
KVM: 커널 모듈이 하드웨어 가상화를 관리, QEMU가 디바이스 에뮬레이션 담당

KVM API (/dev/kvm)

#include <linux/kvm.h>

/* 1. KVM 파일 열기 */
int kvm_fd = open("/dev/kvm", O_RDWR);

/* 2. VM 생성 */
int vm_fd = ioctl(kvm_fd, KVM_CREATE_VM, 0);

/* 3. 메모리 설정 */
struct kvm_userspace_memory_region region = {
    .slot = 0,
    .guest_phys_addr = 0,
    .memory_size = 0x10000000,  /* 256MB */
    .userspace_addr = (__u64)mem,
};
ioctl(vm_fd, KVM_SET_USER_MEMORY_REGION, ®ion);

/* 4. vCPU 생성 */
int vcpu_fd = ioctl(vm_fd, KVM_CREATE_VCPU, 0);

/* 5. 실행 루프 */
while (1) {
    ioctl(vcpu_fd, KVM_RUN, 0);
    switch (run->exit_reason) {
    case KVM_EXIT_IO:    /* I/O 처리 */ break;
    case KVM_EXIT_MMIO:  /* MMIO 처리 */ break;
    case KVM_EXIT_HLT:   goto done;
    }
}

KVM ioctl 계층 구조

레벨FD주요 ioctl설명
System/dev/kvmKVM_CREATE_VM, KVM_GET_API_VERSION, KVM_CHECK_EXTENSION전역 KVM 기능 조회, VM 생성
VMvm_fdKVM_CREATE_VCPU, KVM_SET_USER_MEMORY_REGION, KVM_CREATE_IRQCHIP, KVM_IRQFD, KVM_IOEVENTFDVM 단위 메모리/인터럽트/디바이스 설정
vCPUvcpu_fdKVM_RUN, KVM_GET_REGS, KVM_SET_REGS, KVM_GET_SREGS, KVM_SET_CPUID2vCPU 실행, 레지스터 조작
Devicedev_fdKVM_CREATE_DEVICE, KVM_SET_DEVICE_ATTR커널 내 디바이스 (GIC, VFIO 등)

kvm_run 공유 메모리 구조체

/* QEMU와 커널이 mmap으로 공유하는 구조체
 * vcpu_fd에서 mmap(NULL, mmap_size, ..., vcpu_fd, 0)으로 매핑
 * KVM_RUN 후 exit_reason을 확인하여 처리 */

struct kvm_run {
    /* in */
    __u8  request_interrupt_window;  /* 인터럽트 윈도우 요청 */
    __u8  immediate_exit;            /* 즉시 exit (시그널 처리용) */

    /* out */
    __u32 exit_reason;               /* VM Exit 사유 */
    __u8  ready_for_interrupt_injection; /* 인터럽트 주입 가능 여부 */
    __u64 cr8;                       /* TPR (Task Priority Register) */

    /* exit_reason별 union */
    union {
        /* KVM_EXIT_IO */
        struct {
            __u8  direction;    /* KVM_EXIT_IO_IN / KVM_EXIT_IO_OUT */
            __u8  size;         /* 1, 2, 4 바이트 */
            __u16 port;         /* I/O 포트 번호 */
            __u32 count;        /* REP 접두사 시 반복 횟수 */
            __u64 data_offset;  /* kvm_run 내 데이터 오프셋 */
        } io;

        /* KVM_EXIT_MMIO */
        struct {
            __u64 phys_addr;    /* 게스트 물리 주소 */
            __u8  data[8];      /* 읽기/쓰기 데이터 */
            __u32 len;          /* 접근 크기 */
            __u8  is_write;     /* 쓰기 여부 */
        } mmio;

        /* KVM_EXIT_INTERNAL_ERROR */
        struct {
            __u32 suberror;     /* 에뮬레이션 실패 등 */
            __u32 ndata;
            __u64 data[16];
        } internal;
    };
};

QEMU-KVM 상호작용 상세 흐름

/* QEMU의 vCPU 스레드 실행 루프 (실제 흐름) */
/* accel/kvm/kvm-accel-ops.c */

static void *kvm_vcpu_thread_fn(void *arg)
{
    struct CPUState *cpu = arg;

    kvm_init_vcpu(cpu);  /* KVM_CREATE_VCPU + mmap kvm_run */

    while (!cpu->unplug) {
        /* 1. 인터럽트 주입 준비 */
        if (cpu->interrupt_request) {
            kvm_cpu_synchronize_state(cpu);
            kvm_arch_put_registers(cpu);  /* KVM_SET_REGS */
        }

        /* 2. KVM_RUN → 게스트 실행 (커널로 진입) */
        ret = kvm_vcpu_ioctl(cpu, KVM_RUN, 0);

        /* 3. VM Exit 처리 */
        switch (run->exit_reason) {
        case KVM_EXIT_IO:
            kvm_handle_io(run->io.port, ...);
            /* → QEMU 디바이스 모델로 I/O 디스패치 */
            break;
        case KVM_EXIT_MMIO:
            address_space_rw(&address_space_memory,
                run->mmio.phys_addr, ...);
            /* → QEMU MemoryRegion으로 MMIO 디스패치 */
            break;
        case KVM_EXIT_SHUTDOWN:
            qemu_system_reset_request();
            break;
        }
    }
}

/*
 * 전체 흐름 요약:
 *
 * QEMU (유저)            KVM (커널)              하드웨어
 * ─────────────────────────────────────────────────────────
 * ioctl(KVM_RUN) ──→ vcpu_run() ──→ VMLAUNCH/VMRESUME
 *                                        │
 *                                   [게스트 실행]
 *                                        │
 *                                   VM Exit 발생
 *                                        │
 *                    vmx_handle_exit() ←──┘
 *                        │
 *                   커널에서 처리 가능?
 *                   ├─ YES → 즉시 재진입 (fast path)
 *                   └─ NO  → QEMU로 exit_reason 전달
 *                                │
 * exit_reason 처리 ←─────────────┘
 * (I/O, MMIO 에뮬레이션)
 * 다시 ioctl(KVM_RUN) → ...
 */

커널 내부 vcpu_run 흐름

/* virt/kvm/kvm_main.c — KVM_RUN ioctl 커널 처리 */
static long kvm_vcpu_ioctl(struct file *filp,
    unsigned int ioctl, unsigned long arg)
{
    case KVM_RUN:
        return kvm_arch_vcpu_ioctl_run(vcpu);
}

/* arch/x86/kvm/x86.c */
int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)
{
    struct kvm_run *run = vcpu->run;

    vcpu_load(vcpu);  /* vCPU를 현재 pCPU에 바인딩 */

    for (;;) {
        /* 1. 시그널 체크 */
        if (signal_pending(current)) {
            run->exit_reason = KVM_EXIT_INTR;
            break;
        }

        /* 2. 인터럽트 주입 */
        kvm_inject_pending_events(vcpu);

        /* 3. 게스트 진입 준비 + VMLAUNCH/VMRESUME */
        r = vcpu_enter_guest(vcpu);

        /* 4. VM Exit 처리 */
        if (r <= 0) break;      /* QEMU로 반환 */
        /* r > 0: 커널에서 처리 완료 → 즉시 재진입 (fast path) */

        /* 5. 스케줄링 포인트 */
        if (need_resched()) {
            cond_resched();  /* 다른 태스크에 CPU 양보 */
        }
    }

    vcpu_put(vcpu);
    return r;
}

/* vcpu_enter_guest(): 실제 VM 진입/탈출 */
static int vcpu_enter_guest(struct kvm_vcpu *vcpu)
{
    /* 게스트 상태를 VMCS/VMCB에 로드 */
    kvm_x86_ops.prepare_switch_to_guest(vcpu);

    /* preempt, IRQ 비활성화 */
    preempt_disable();
    local_irq_disable();

    /* ★ VM Entry: VMLAUNCH/VMRESUME (Intel) 또는 VMRUN (AMD) */
    kvm_x86_ops.vcpu_run(vcpu);

    /* VM Exit 후 복귀 */
    local_irq_enable();
    preempt_enable();

    /* Exit reason 처리 */
    r = kvm_x86_ops.handle_exit(vcpu, exit_fastpath);
    return r;
}

Fast Path vs Slow Path: 커널에서 처리 가능한 VM Exit (예: MSR 읽기, CPUID, 일부 EPT violation)는 QEMU로 돌아가지 않고 즉시 재진입합니다. 이를 fast path라 하며, QEMU-커널 컨텍스트 스위칭을 제거하여 성능을 크게 향상시킵니다. I/O 에뮬레이션이 필요한 경우만 slow path로 QEMU에 전달됩니다.

virtio 프레임워크

virtio는 가상화 환경에서 디바이스 I/O 성능을 최적화하는 준가상화(paravirtualization) 프레임워크입니다. 게스트가 가상화 환경임을 인지하고 하이퍼바이저와 효율적으로 통신합니다.

virtio 디바이스용도
virtio-net네트워크
virtio-blk블록 스토리지
virtio-scsiSCSI 스토리지
virtio-gpu그래픽
virtio-fs파일시스템 공유
virtio-mem메모리 핫플러그
virtio-balloon동적 메모리 조정
virtio-rng하드웨어 난수 전달
virtio-vsock호스트-게스트 소켓 통신
virtio-crypto암호화 가속

Virtqueue 구조 상세

모든 virtio 디바이스는 virtqueue를 통해 게스트↔호스트 데이터를 교환합니다. virtqueue는 게스트 메모리에 위치한 링 버퍼로, Split Virtqueue(레거시)와 Packed Virtqueue(v1.1+) 두 가지 형식이 있습니다.

Split Virtqueue (전통 방식)

/* Split Virtqueue의 3가지 구성 요소 */

/* 1. Descriptor Table: 데이터 버퍼 기술자 배열 */
struct vring_desc {
    __le64 addr;     /* 게스트 물리 주소 */
    __le32 len;      /* 버퍼 길이 */
    __le16 flags;    /* NEXT: 체인, WRITE: 디바이스→드라이버, INDIRECT */
    __le16 next;     /* flags & NEXT 시 다음 디스크립터 인덱스 */
};

/* 2. Available Ring: 게스트(드라이버)가 디바이스에 버퍼 제공 */
struct vring_avail {
    __le16 flags;       /* VRING_AVAIL_F_NO_INTERRUPT: 알림 억제 */
    __le16 idx;         /* 다음 쓸 위치 (단조 증가) */
    __le16 ring[];      /* 디스크립터 체인의 head 인덱스 */
};

/* 3. Used Ring: 디바이스가 처리 완료를 드라이버에 통보 */
struct vring_used {
    __le16 flags;       /* VRING_USED_F_NO_NOTIFY: 알림 억제 */
    __le16 idx;         /* 다음 쓸 위치 (단조 증가) */
    struct vring_used_elem ring[]; /* {id, len} */
};

/*
 * Split Virtqueue 동작 흐름 (TX 예시):
 *
 * 게스트 드라이버                   호스트 (QEMU/vhost)
 * ──────────────────────────────────────────────────
 * 1. desc 테이블에 버퍼 주소/길이 기록
 * 2. avail ring에 head 인덱스 추가
 * 3. avail.idx 증가
 * 4. MMIO/PIO로 디바이스에 kick  ──→  5. avail ring 읽기
 *                                      6. desc 체인 따라가며 데이터 처리
 *                                      7. used ring에 완료 기록
 *                                      8. used.idx 증가
 * 9. 인터럽트 수신 (irqfd)  ←────────  9. 인터럽트 주입
 * 10. used ring에서 완료 확인
 * 11. 버퍼 회수
 */

Packed Virtqueue (virtio 1.1+)

/* Packed Virtqueue: 단일 디스크립터 링으로 통합
 * Split의 3개 링(desc + avail + used) → 1개 링
 * 캐시 효율성 대폭 개선, 배치 처리에 유리 */

struct vring_packed_desc {
    __le64 addr;     /* 버퍼 주소 */
    __le32 len;      /* 버퍼 길이 */
    __le16 id;       /* 버퍼 식별자 */
    __le16 flags;    /* AVAIL/USED 비트 + NEXT/WRITE/INDIRECT */
};

/*
 * Packed의 핵심: AVAIL/USED 플래그 비트와 Wrap Counter
 *
 * 드라이버 쓰기: flags.AVAIL = wrap_counter, flags.USED = !wrap_counter
 * 디바이스 완료: flags.AVAIL = wrap_counter, flags.USED = wrap_counter
 *
 * 링 끝에 도달하면 wrap_counter를 토글(0↔1)
 * → 별도의 avail/used 인덱스 불필요, 하나의 flags 필드로 상태 판별
 *
 * 장점:
 * - 하나의 캐시 라인에 desc + avail + used 정보가 모두 존재
 * - 디바이스가 in-order로 처리 시 디스크립터 순회만으로 완료 감지
 * - QEMU virtio-net + packed ring에서 ~15% 처리량 향상
 */

virtio 트랜스포트

트랜스포트설명사용 환경
virtio-pciPCI/PCIe 디바이스로 에뮬레이션x86 QEMU/KVM (가장 일반적)
virtio-mmio메모리 매핑 I/O 기반ARM/임베디드, Firecracker microVM
virtio-ccw채널 I/O 기반s390x (IBM Z)
vDPA하드웨어 virtqueue 직접 지원SmartNIC (NVIDIA ConnectX-6+)

EPT/NPT (확장 페이지 테이블)

EPT(Extended Page Table, Intel) / NPT(Nested Page Table, AMD)는 게스트 물리 주소(GPA) → 호스트 물리 주소(HPA) 변환을 하드웨어에서 수행합니다. 소프트웨어 섀도 페이지 테이블보다 훨씬 효율적입니다.

2차원 페이지 워크

/*
 * 가상화 환경의 주소 변환 (2D Page Walk):
 *
 * 게스트 가상 주소 (GVA)
 *   │ ← 게스트 페이지 테이블 (GPT, 게스트 CR3)
 *   ▼
 * 게스트 물리 주소 (GPA)
 *   │ ← EPT/NPT (호스트 관리, EPTP/nCR3)
 *   ▼
 * 호스트 물리 주소 (HPA)
 *
 * 최악의 경우 페이지 워크 비용:
 * - 4-level GPT × 4-level EPT = 최대 24회 메모리 접근
 *   (GPT 각 레벨마다 EPT 4-level 워크 필요 + 최종 데이터 접근)
 * - 5-level (LA57) GPT × 4-level EPT = 최대 30회
 * - TLB 미스 시 성능 영향 큼 → VPID로 TLB 격리하여 완화
 */

/* EPT 페이지 테이블 엔트리 (Intel) */
/*
 * 비트 레이아웃 (4KB 페이지):
 * [0]     Read access
 * [1]     Write access
 * [2]     Execute access (XD 비트와 반대 의미)
 * [5:3]   EPT Memory Type (WB=6, UC=0, WT=4)
 * [6]     Ignore PAT (1이면 EPT의 Memory Type 우선)
 * [7]     1=Large page (2MB/1GB), 0=다음 레벨 포인터
 * [8]     Accessed bit (CPU가 자동 설정)
 * [9]     Dirty bit (CPU가 자동 설정, PML 활성화 시)
 * [10]    Execute access for usermode (MBEC)
 * [N-1:12] 물리 프레임 번호 (HPA >> 12)
 * [63]    Suppress #VE (Virtualization Exception)
 */

/* EPT Violation 처리 — arch/x86/kvm/mmu/mmu.c */
static int kvm_mmu_page_fault(struct kvm_vcpu *vcpu,
    gpa_t gpa, u64 error_code)
{
    /* 1. memslot에서 GPA → HVA 매핑 조회 */
    slot = kvm_vcpu_gfn_to_memslot(vcpu, gpa >> PAGE_SHIFT);

    /* 2. HVA → HPA 변환 (호스트 페이지 테이블 워크) */
    pfn = gfn_to_pfn(kvm, gpa >> PAGE_SHIFT);

    /* 3. EPT 엔트리 설정 (GPA → HPA 매핑 추가) */
    kvm_mmu_map_page(vcpu, gpa, pfn, ...);

    /* 4. 게스트 재진입 (다음 VM Entry에서 자동 적용) */
    return 1;  /* 커널에서 처리 완료 → fast path */
}

EPT 고급 기능

기능설명용도
VPIDVirtual Processor IDentifier. TLB에 vCPU 태그 부여VM Exit/Entry 시 TLB 플러시 불필요 → 성능 향상
PMLPage Modification Logging. dirty page를 CPU가 로그 버퍼에 자동 기록라이브 마이그레이션 dirty tracking 가속
MBECMode-Based Execute Control. 유저/커널 모드별 실행 권한게스트 커널 보호 (SMEP 에뮬레이션 없이)
SPPSub-Page Protection. 128바이트 단위 쓰기 보호VM introspection, 악성코드 분석
2MB/1GB 페이지EPT 대형 페이지 매핑TLB 미스 감소, 게스트 메모리 대용량 시 필수
💡

vhost-net은 네트워크 I/O를 커널 내에서 직접 처리하여 QEMU ↔ 커널 간 컨텍스트 스위칭을 제거합니다. 네트워크 처리량이 크게 향상됩니다.

VMCS (Virtual Machine Control Structure)

VMCS는 Intel VT-x에서 게스트-호스트 전환 시 CPU 상태를 저장하는 구조체입니다. 각 vCPU마다 하나의 VMCS가 할당됩니다.

/* VMCS 주요 필드 영역 */
/* Guest-state area: 게스트 진입 시 로드 */
GUEST_CS_SELECTOR, GUEST_RIP, GUEST_RSP, GUEST_RFLAGS,
GUEST_CR0, GUEST_CR3, GUEST_CR4,
GUEST_GDTR_BASE, GUEST_IDTR_BASE,

/* Host-state area: VM Exit 시 로드 */
HOST_CS_SELECTOR, HOST_RIP, HOST_RSP,
HOST_CR0, HOST_CR3, HOST_CR4,

/* VM-execution control fields */
PIN_BASED_VM_EXEC_CONTROL,    /* 외부 인터럽트, NMI */
CPU_BASED_VM_EXEC_CONTROL,    /* HLT, I/O, MSR 감시 */
SECONDARY_VM_EXEC_CONTROL,    /* EPT, VPID, unrestricted guest */
VM_EXIT_CONTROLS,             /* exit 동작 */
VM_ENTRY_CONTROLS,            /* entry 동작 */

/* VM-exit information fields */
VM_EXIT_REASON, EXIT_QUALIFICATION, GUEST_LINEAR_ADDRESS

VM Exit 처리 흐름

/* arch/x86/kvm/vmx/vmx.c */
static int vmx_handle_exit(struct kvm_vcpu *vcpu,
    enum exit_fastpath_completion exit_fastpath)
{
    u32 exit_reason = vmx->exit_reason.full;

    switch (exit_reason) {
    case EXIT_REASON_EPT_VIOLATION:
        return handle_ept_violation(vcpu);
    case EXIT_REASON_IO_INSTRUCTION:
        return handle_io(vcpu);
    case EXIT_REASON_MSR_READ:
        return handle_rdmsr(vcpu);
    case EXIT_REASON_MSR_WRITE:
        return handle_wrmsr(vcpu);
    case EXIT_REASON_CPUID:
        return handle_cpuid(vcpu);
    case EXIT_REASON_HLT:
        return kvm_emulate_halt(vcpu);
    /* ... 약 60가지 exit reason ... */
    }
}

AMD VMCB (Virtual Machine Control Block)

/* AMD SVM의 제어 구조체 — Intel VMCS에 대응
 * VMCB는 일반 메모리 페이지 (4KB)에 위치 (VMCS는 CPU 내부 캐시)
 * VMRUN 명령어가 VMCB 물리 주소를 인자로 받음 */

struct vmcb_control_area {          /* 오프셋 0x000~0x3FF */
    u32 intercepts[5];              /* CR/DR/Exception/명령어 인터셉트 비트맵 */
    u16 pause_filter_thresh;        /* PAUSE 루프 exit 임계값 */
    u16 pause_filter_count;
    u64 iopm_base_pa;               /* I/O Permission Map 물리 주소 */
    u64 msrpm_base_pa;              /* MSR Permission Map 물리 주소 */
    u64 tsc_offset;                 /* 게스트 TSC 오프셋 */
    u32 asid;                       /* Address Space IDentifier (VPID 역할) */
    u8  tlb_ctl;                    /* TLB 플러시 제어 */
    u64 exitcode;                   /* #VMEXIT 사유 코드 */
    u64 exitinfo1, exitinfo2;       /* exit 추가 정보 */
    u64 nested_ctl;                 /* NPT 활성화 비트 */
    u64 nested_cr3;                 /* NPT 페이지 테이블 루트 (nCR3) */
    u64 virt_ext;                   /* LBR 가상화 등 */
    u64 vmcb_clean;                 /* clean bits: 변경 안 된 필드 표시 */
    u64 next_rip;                   /* NRIP Save: 다음 명령어 RIP */
};

struct vmcb_save_area {             /* 오프셋 0x400~0xFFF */
    /* 세그먼트 레지스터, CR0-CR4, EFER, RIP, RSP, RFLAGS, ... */
    /* 게스트의 전체 CPU 상태 저장/복원 */
};

Intel VT-x vs AMD-V (SVM) 비교

항목Intel VT-x (VMX)AMD-V (SVM)
제어 구조체VMCS (CPU 내부 캐시, VMREAD/VMWRITE)VMCB (일반 메모리, 직접 접근)
VM 진입VMLAUNCH / VMRESUMEVMRUN
VM 탈출자동 (VM_EXIT_REASON)#VMEXIT (exitcode)
2차 페이지 테이블EPT (EPTP)NPT (nCR3)
TLB 태깅VPID (16-bit)ASID (32-bit)
인터럽트 가상화Posted Interrupts (PI)AVIC (AMD Virtual Interrupt Controller)
MSR 비트맵VMCS 내 포인터MSRPM (8KB)
NRIP 자동 저장지원 (VM Exit 시 자동)NRIP Save 기능 (선택적)
Unrestricted GuestSecondary 컨트롤 비트기본 지원 (Real Mode 직접 실행)
Clean Bits 최적화없음 (항상 전체 로드)VMCB Clean Field (변경 안 된 부분 스킵)
Confidential VMTDX (Trust Domain Extensions)SEV / SEV-ES / SEV-SNP
커널 모듈kvm_intelkvm_amd
커널 소스arch/x86/kvm/vmx/arch/x86/kvm/svm/

인터럽트 가상화

게스트에 인터럽트를 효율적으로 전달하는 것은 KVM 성능의 핵심입니다. KVM은 커널 내에서 가상 인터럽트 컨트롤러를 에뮬레이션하고, Posted Interrupts/AVIC로 VM Exit 없이 직접 전달합니다.

/* KVM 인터럽트 전달 경로 */
/*
 * 1. 가상 PIC (i8259): 레거시 IRQ 0-15
 *    → 현대 게스트에서는 거의 사용 안 함
 *
 * 2. 가상 IOAPIC: IRQ 라우팅 테이블 기반
 *    → KVM_CREATE_IRQCHIP으로 커널 내 에뮬레이션 활성화
 *
 * 3. 가상 LAPIC: vCPU당 하나, 타이머 + IPI
 *    → KVM_CREATE_IRQCHIP에 포함
 *
 * 4. MSI/MSI-X: PCI 디바이스의 직접 인터럽트
 *    → QEMU가 KVM_SIGNAL_MSI 또는 irqfd로 주입
 *
 * 5. Posted Interrupts (Intel):
 *    → VM Exit 없이 게스트에 직접 인터럽트 전달
 *    → 256비트 Posted Interrupt Descriptor (PID)
 *    → 특별한 notification vector가 CPU에 알림
 */

/* Posted Interrupt 흐름 (Intel VT-x) */
/*
 * 디바이스 인터럽트 발생
 *   → IOMMU Interrupt Remapping → PID에 비트 설정
 *   → Notification Vector를 물리 CPU에 전송
 *   → CPU가 비root 모드(게스트)면:
 *       게스트 IDT로 바로 전달 (VM Exit 없음!)
 *   → CPU가 root 모드(호스트)면:
 *       다음 VM Entry 시 자동 주입
 */

/* irqfd를 통한 인터럽트 주입 최적화 */
/* vhost-net → eventfd 쓰기 → irqfd → KVM 인터럽트 주입
 * 이 경로는 QEMU를 완전히 우회 → 낮은 지연시간 */

KVM 메모리 관리

/* KVM의 메모리 관리 핵심 개념 */

/* 1. Memory Slots: 게스트 물리 주소 공간을 호스트 가상 메모리에 매핑 */
struct kvm_userspace_memory_region {
    __u32 slot;              /* 슬롯 번호 (0~N) */
    __u32 flags;             /* KVM_MEM_LOG_DIRTY_PAGES, KVM_MEM_READONLY */
    __u64 guest_phys_addr;   /* 게스트 물리 시작 주소 */
    __u64 memory_size;       /* 바이트 크기 */
    __u64 userspace_addr;    /* 호스트 가상 주소 (QEMU mmap) */
};
/* 게스트 RAM은 QEMU의 mmap 영역 → KVM이 EPT에 on-demand 매핑
 * 게스트가 GPA 접근 → EPT miss → KVM이 HVA→HPA 변환하여 EPT 추가 */

/* 2. Dirty Page Tracking: 라이브 마이그레이션 핵심 */
/* KVM_MEM_LOG_DIRTY_PAGES 플래그 → EPT dirty bit 활용 */
/* KVM_GET_DIRTY_LOG ioctl → 변경된 페이지 비트맵 반환 */
/*
 * PML (Page Modification Logging, Intel):
 *   dirty page 발생 시 CPU가 자동으로 GPA를 PML 로그 버퍼에 기록
 *   버퍼가 가득 차면 VM Exit → KVM이 비트맵 업데이트
 *   소프트웨어 EPT 스캔 대비 오버헤드 대폭 감소
 */

/* 3. KSM (Kernel Same-page Merging): 동일 페이지 공유 */
/* 여러 VM의 동일한 메모리 페이지를 하나의 물리 프레임으로 통합
 * echo 1 > /sys/kernel/mm/ksm/run
 * → 동일한 OS를 실행하는 다수 VM에서 메모리 30-50% 절감 가능
 * → COW (Copy-On-Write)로 투명하게 분리 */

/* 4. Huge Pages: TLB 효율 극대화 */
/* QEMU에서 -mem-path /dev/hugepages → 2MB/1GB 대형 페이지 사용
 * EPT도 2MB/1GB 매핑 → TLB 미스 감소, 페이지 워크 레벨 감소 */

/* 5. Memory Ballooning: 동적 메모리 조정 */
/* virtio-balloon: 게스트에 "풍선"을 부풀려 사용하지 않는 페이지를 회수
 * 호스트가 메모리 부족 시 → 게스트 balloon inflate → 페이지 반환
 * 호스트 여유 시 → 게스트 balloon deflate → 페이지 재할당 */

vhost: 커널 내 virtio 백엔드

vhost는 QEMU의 디바이스 에뮬레이션 일부를 커널로 옮겨 성능을 극대화합니다. 특히 vhost-net은 네트워크 패킷이 유저스페이스를 거치지 않고 커널에서 직접 처리됩니다.

구성요소위치역할
virtio 프론트엔드게스트 커널게스트의 virtio 드라이버
virtqueue공유 메모리게스트-호스트 간 링 버퍼 통신
vhost 백엔드호스트 커널패킷 처리 (커널 스레드)
QEMU호스트 유저제어 경로, 초기 설정
/* vhost-net: virtqueue 폴링 루프 (커널 스레드) */
static void handle_tx(struct vhost_net *net)
{
    struct vhost_virtqueue *vq = &net->vqs[VHOST_NET_VQ_TX];
    struct msghdr msg = { .msg_flags = MSG_DONTWAIT };

    for (;;) {
        head = vhost_get_vq_desc(vq, vq->iov,
            ARRAY_SIZE(vq->iov), &out, &in, NULL, NULL);
        if (head < 0) break;

        /* 게스트의 TX 버퍼를 호스트 소켓으로 전송 */
        len = sock_sendmsg(sock, &msg);
        vhost_add_used_and_signal(vq, head, len);
    }
}

irqfd와 ioeventfd

KVM은 이벤트 기반 통신으로 VM Exit를 최소화합니다:

메커니즘방향용도
irqfd호스트 → 게스트eventfd를 통해 게스트에 인터럽트 주입
ioeventfd게스트 → 호스트특정 MMIO/PIO 주소 write 시 eventfd 시그널
/* irqfd: eventfd에 쓰면 게스트에 인터럽트 전달 */
struct kvm_irqfd irqfd = {
    .fd  = eventfd_fd,
    .gsi = 24,       /* 게스트 IRQ 번호 */
};
ioctl(vm_fd, KVM_IRQFD, &irqfd);

/* 호스트에서 인터럽트 트리거 */
uint64_t val = 1;
write(eventfd_fd, &val, sizeof(val));  /* → 게스트 IRQ 24 발생 */

중첩 가상화 (Nested Virtualization)

L0 호스트 위의 L1 게스트에서 다시 하이퍼바이저를 실행하는 기술입니다. KVM은 VMCS shadowing과 EPT의 중첩 처리를 지원합니다.

# 중첩 가상화 활성화
modprobe kvm_intel nested=1
# 또는
echo "options kvm_intel nested=1" > /etc/modprobe.d/kvm.conf

# 확인
cat /sys/module/kvm_intel/parameters/nested   # Y

중첩 가상화 시 성능 오버헤드가 있습니다 (EPT violation 처리가 2단계). 프로덕션보다는 개발/테스트 환경에서 주로 사용합니다. AMD의 경우 kvm_amd nested=1을 사용합니다.

라이브 마이그레이션

VM을 중단 없이 다른 호스트로 이전하는 기술입니다. KVM의 dirty page tracking이 핵심 메커니즘입니다.

단계동작KVM 인터페이스
1. Pre-copy전체 메모리를 대상 호스트로 복사KVM_GET_DIRTY_LOG
2. Iterative copy변경된 페이지만 반복 전송dirty bitmap 조회
3. Stop & copyVM 정지, 나머지 상태 전송KVM_GET_REGS/SREGS
4. Resume대상에서 VM 재개KVM_SET_REGS/SREGS

Confidential VM (SEV / TDX)

클라우드 환경에서 호스트/하이퍼바이저로부터 게스트 메모리를 보호하는 하드웨어 기반 보안 기술입니다. 게스트 메모리가 암호화되어 호스트 관리자도 내용을 읽을 수 없습니다.

기술벤더메모리 암호화레지스터 보호무결성 검증커널 지원
SEVAMDAES-128 (VM별 키)--4.16+
SEV-ESAMDAES-128VMCB 암호화-5.11+
SEV-SNPAMDAES-128VMCB 암호화RMP (역방향 맵)5.19+
TDXIntelAES-128 (TME-MK)TD VMCS 암호화EPT 무결성 + SEPT6.2+
/* AMD SEV: 게스트 메모리 암호화 흐름 */
/*
 * 1. PSP(Platform Security Processor)가 VM별 고유 암호화 키 생성
 * 2. 게스트 메모리 쓰기 → AES 엔진이 메모리 컨트롤러에서 암호화
 * 3. 게스트 메모리 읽기 → AES 엔진이 자동 복호화
 * 4. 호스트가 같은 물리 주소 읽기 → 암호문만 보임 (다른 키)
 *
 * CPUID Leaf 0x8000001F로 SEV 지원 확인:
 *   EAX bit 0: SEV, bit 1: SEV-ES, bit 3: SEV-SNP
 */

/* SEV VM 생성 (QEMU 명령줄) */
/* qemu-system-x86_64 \
 *   -machine q35,confidential-guest-support=sev0 \
 *   -object sev-guest,id=sev0,policy=0x1,cbitpos=51,reduced-phys-bits=1 \
 *   -enable-kvm ...
 */

/* Intel TDX: Trust Domain 구조 */
/*
 * TDX Module: CPU 내부의 신뢰 실행 환경 (ACM 기반)
 * TD (Trust Domain) = Confidential VM
 *   - Secure EPT (SEPT): TDX Module이 관리하는 별도 EPT
 *   - TD VMCS: 호스트가 접근 불가 (TDX Module만 접근)
 *   - TDCALL: 게스트 → TDX Module 호출
 *   - SEAMCALL: 호스트 → TDX Module 호출
 *
 * 호스트(VMM)의 역할 축소:
 *   - 메모리 할당/매핑은 가능하지만 내용 접근 불가
 *   - 인터럽트 주입은 가능하지만 레지스터 수정 불가
 *   - TDX Module이 실제 VM Entry/Exit를 중재
 */

Confidential VM의 제약: SEV/TDX는 게스트 메모리를 보호하지만, 사이드 채널(캐시 타이밍, 전력 분석)은 완전히 방어하지 못합니다. SEV-SNP와 TDX는 무결성 검증을 추가하여 메모리 리플레이/리맵 공격을 방어합니다. 디바이스 DMA는 여전히 호스트 메모리를 통과하므로, 완전한 보호를 위해서는 Bounce Buffer 또는 TDISP(TDX Device Interface Security Protocol)가 필요합니다.

KVM/QEMU 성능 튜닝

CPU 최적화

# vCPU 핀닝: vCPU 스레드를 물리 CPU에 고정
# NUMA 로컬리티 보장 → 캐시/메모리 지연시간 최소화
virsh vcpupin myvm 0 2       # vCPU 0 → pCPU 2
virsh vcpupin myvm 1 3       # vCPU 1 → pCPU 3

# 에뮬레이터 스레드 격리 (QEMU I/O 스레드)
virsh emulatorpin myvm 0-1   # QEMU 워커를 pCPU 0-1에 제한

# NUMA 토폴로지 매칭
virsh numatune myvm --nodeset 0 --mode strict
# 게스트 메모리를 NUMA 노드 0에 한정 → 원격 메모리 접근 제거

# CPU 호스트 패스스루 (최대 성능)
# QEMU: -cpu host (호스트 CPUID 그대로 전달)
# → 라이브 마이그레이션 시 동일 CPU 모델 필요

# halt-polling: vCPU가 HLT 후 즉시 sleep 하지 않고 일정 시간 대기
echo 200000 > /sys/module/kvm/parameters/halt_poll_ns
# 200μs 동안 바쁜 대기 → 짧은 idle 구간에서 VM Exit/Entry 감소
# 레이턴시 민감한 워크로드에 유효, CPU 전력은 증가

메모리 최적화

# Huge Pages 사용 (필수 최적화)
# 2MB Huge Pages:
echo 4096 > /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages
mount -t hugetlbfs hugetlbfs /dev/hugepages

# QEMU에서 Huge Pages 사용:
# -mem-path /dev/hugepages -mem-prealloc
# 또는 libvirt: 

# 1GB Huge Pages (부트 파라미터로만 가능):
# hugepagesz=1G hugepages=32  (GRUB 부트 파라미터)

# Transparent Huge Pages (THP): 자동 대형 페이지
echo madvise > /sys/kernel/mm/transparent_hugepage/enabled
# QEMU가 madvise(MADV_HUGEPAGE)로 요청 시에만 적용

# KSM (동일 페이지 병합):
echo 1 > /sys/kernel/mm/ksm/run
echo 200 > /sys/kernel/mm/ksm/sleep_millisecs  # 스캔 주기
# 동일 OS의 다수 VM에서 메모리 30-50% 절감
# CPU 오버헤드 있음, 사이드 채널 취약 가능성

I/O 최적화

구성방식성능비고
virtio-blkvirtio 블록 드라이버좋음기본 블록 디바이스
virtio-scsivirtio SCSI 컨트롤러좋음핫플러그, 다수 디스크에 유리
vhost-user-blk유저 공간 블록 백엔드매우 좋음SPDK와 조합
NVMe 패스스루VFIO 직접 할당최고 (네이티브)마이그레이션 불가
virtio-net (기본)QEMU 백엔드보통유저 공간 I/O 경로
vhost-net커널 백엔드좋음-netdev tap,vhost=on
vhost-user-netDPDK/OVS 백엔드매우 좋음유저 공간 스위칭
SR-IOV VF 패스스루VFIO 직접 할당최고 (네이티브)마이그레이션 불가
# I/O 스레드 활성화 (QEMU): 메인 루프에서 I/O 분리
# -object iothread,id=io1 -device virtio-blk-pci,iothread=io1,...

# 디스크 I/O 스케줄러: 게스트 내 none 권장 (호스트가 스케줄링)
echo none > /sys/block/vda/queue/scheduler   # 게스트 내부

# virtio-net 멀티큐: 다중 vCPU에서 네트워크 처리 병렬화
# -device virtio-net-pci,mq=on,vectors=10,netdev=net0
# 게스트에서: ethtool -L eth0 combined 4

KVM/QEMU 디버깅

# KVM 통계 확인
cat /sys/kernel/debug/kvm/vm*/vcpu*/stats
# 또는 perf kvm stat live (실시간 VM Exit 통계)
perf kvm stat live
# 출력 예시:
#  Event             Count  Median
#  EPT_VIOLATION      1523   1.2us
#  IO_INSTRUCTION      892   3.5us
#  HLT                 456  12.1us
#  EXTERNAL_INTERRUPT  234   0.8us

# VM Exit 이유별 카운터
perf kvm stat report

# KVM tracepoint 활성화
echo 1 > /sys/kernel/debug/tracing/events/kvm/enable
cat /sys/kernel/debug/tracing/trace_pipe
# kvm_entry, kvm_exit, kvm_mmio, kvm_pio, kvm_msr 등

# 특정 tracepoint만 활성화
echo 1 > /sys/kernel/debug/tracing/events/kvm/kvm_exit/enable
echo 1 > /sys/kernel/debug/tracing/events/kvm/kvm_entry/enable

# QEMU Monitor (디버그 콘솔)
# QEMU 실행 시 -monitor stdio 또는 -monitor telnet::4444,server
# 주요 명령어:
(qemu) info registers       # vCPU 레지스터
(qemu) info mem              # 메모리 매핑
(qemu) info mtree            # 메모리 토폴로지 트리
(qemu) info qtree            # 디바이스 트리
(qemu) info irq              # 인터럽트 통계
(qemu) info kvm              # KVM 상태
(qemu) xp /16xg 0x1000       # 게스트 물리 메모리 덤프

# GDB로 게스트 커널 디버깅
# QEMU: -s -S (GDB 서버 :1234, 시작 시 중지)
gdb vmlinux
(gdb) target remote :1234
(gdb) hbreak start_kernel    # 하드웨어 브레이크포인트 */
(gdb) continue

KVM 커널 소스 구조

경로설명
virt/kvm/kvm_main.cKVM 코어: ioctl 디스패치, 메모리 슬롯, vCPU 관리
arch/x86/kvm/x86.cx86 공통: CPUID, MSR, 인터럽트, 에뮬레이션
arch/x86/kvm/vmx/Intel VT-x: VMCS, VM Entry/Exit, Posted Interrupts
arch/x86/kvm/svm/AMD SVM: VMCB, NPT, AVIC, SEV
arch/x86/kvm/mmu/MMU: EPT/NPT 관리, 섀도 페이지 테이블, TDP
arch/x86/kvm/lapic.c가상 LAPIC 에뮬레이션
arch/x86/kvm/ioapic.c가상 IOAPIC 에뮬레이션
arch/x86/kvm/irq_comm.cIRQ 라우팅, MSI 전달
arch/x86/kvm/emulate.cx86 명령어 에뮬레이터 (MMIO 등)
drivers/vhost/vhost 커널 백엔드 (net, scsi, vsock)
drivers/vfio/VFIO 프레임워크 (PCI, mdev, IOMMU)
arch/arm64/kvm/ARM64 KVM (EL2 하이퍼바이저)

VFIO (Virtual Function I/O)

VFIO는 유저 공간에서 직접 디바이스를 안전하게 제어할 수 있게 하는 커널 프레임워크입니다. IOMMU를 활용하여 DMA 격리를 보장하면서 디바이스를 VM(KVM)이나 유저 공간 드라이버(DPDK, SPDK)에 할당합니다. 과거 UIO(Userspace I/O)는 DMA 격리 없이 디바이스를 노출했지만, VFIO는 IOMMU 기반 격리를 통해 멀티테넌트 환경에서도 안전한 디바이스 패스스루를 제공합니다.

VFIO 아키텍처

VFIO는 Container → Group → Device 3계층 모델로 디바이스를 관리합니다.

User Kernel HW QEMU / libvirt VM 패스스루 DPDK / SPDK 유저 공간 드라이버 사용자 어플리케이션 ioctl(/dev/vfio/*) /dev/vfio/vfio Container FD /dev/vfio/N Group FD Device FD BAR/IRQ/Config VFIO Core drivers/vfio/vfio_main.c VFIO-PCI drivers/vfio/pci/ VFIO-mdev drivers/vfio/mdev/ IOMMU Subsystem drivers/iommu/ Type1 IOMMU DMA Mapping PCI Device (GPU, NIC, NVMe) BAR, MSI-X, Config Space IOMMU HW (VT-d / AMD-Vi / SMMU) DMA Remapping + Interrupt Remapping Physical Memory DMA Target Pages
VFIO 3계층 구조: Container(IOMMU 도메인) → Group(IOMMU 그룹) → Device(개별 디바이스 FD)

Container / Group / Device 모델

계층파일 디스크립터역할주요 ioctl
Container /dev/vfio/vfio IOMMU 도메인 — DMA 매핑의 단위. 하나의 Container에 여러 Group을 연결하면 같은 IOMMU 주소 공간 공유 VFIO_SET_IOMMU, VFIO_IOMMU_MAP_DMA, VFIO_IOMMU_UNMAP_DMA
Group /dev/vfio/<N> IOMMU 그룹 — 하드웨어가 구분하는 최소 격리 단위. 그룹 내 모든 디바이스가 바인딩되어야 Group이 viable(사용 가능) 상태 VFIO_GROUP_SET_CONTAINER, VFIO_GROUP_GET_DEVICE_FD
Device Group FD에서 획득 개별 PCI 디바이스 — BAR 매핑, 인터럽트 설정, config space 접근 VFIO_DEVICE_GET_INFO, VFIO_DEVICE_GET_REGION_INFO, VFIO_DEVICE_SET_IRQS
Container 공유: 여러 Group을 하나의 Container에 연결하면 동일한 IOVA(I/O Virtual Address) 공간을 공유합니다. VM 패스스루에서는 일반적으로 모든 패스스루 디바이스를 하나의 Container에 연결하여 게스트 물리 메모리를 단일 DMA 매핑으로 관리합니다.

VFIO ioctl API

#include <linux/vfio.h>
#include <sys/ioctl.h>

/* === 1단계: Container 생성 및 IOMMU 설정 === */
int container_fd = open("/dev/vfio/vfio", O_RDWR);

/* API 버전 확인 (VFIO_API_VERSION = 0) */
ioctl(container_fd, VFIO_GET_API_VERSION);  /* 반환값 == 0 */

/* Type1 IOMMU 지원 확인 */
ioctl(container_fd, VFIO_CHECK_EXTENSION, VFIO_TYPE1v2_IOMMU);

/* === 2단계: Group 열기 및 Container에 연결 === */
int group_fd = open("/dev/vfio/15", O_RDWR);  /* IOMMU 그룹 15 */

/* 그룹 상태 확인 — viable 여부 */
struct vfio_group_status grp_status = { .argsz = sizeof(grp_status) };
ioctl(group_fd, VFIO_GROUP_GET_STATUS, &grp_status);
if (!(grp_status.flags & VFIO_GROUP_FLAGS_VIABLE)) {
    /* 그룹 내 일부 디바이스가 아직 호스트 드라이버에 바인딩됨 */
    fprintf(stderr, "Group not viable\n");
    return -1;
}

/* Group → Container 연결 */
ioctl(group_fd, VFIO_GROUP_SET_CONTAINER, &container_fd);

/* IOMMU 모델 설정 (Container에 첫 Group 연결 후) */
ioctl(container_fd, VFIO_SET_IOMMU, VFIO_TYPE1v2_IOMMU);

/* === 3단계: Device FD 획득 === */
int device_fd = ioctl(group_fd, VFIO_GROUP_GET_DEVICE_FD, "0000:03:00.0");

/* 디바이스 정보 조회 */
struct vfio_device_info dev_info = { .argsz = sizeof(dev_info) };
ioctl(device_fd, VFIO_DEVICE_GET_INFO, &dev_info);
printf("regions=%u, irqs=%u, flags=0x%x\n",
       dev_info.num_regions, dev_info.num_irqs, dev_info.flags);

DMA 매핑 (IOVA 설정)

VFIO Container의 핵심 기능은 유저 공간 메모리를 IOMMU에 매핑하여 디바이스가 DMA로 접근할 수 있게 하는 것입니다. 이를 IOVA(I/O Virtual Address) 매핑이라 합니다.

/* 유저 공간 메모리를 IOVA에 매핑 */
void *dma_buf = mmap(NULL, 0x100000,
                     PROT_READ | PROT_WRITE,
                     MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);

struct vfio_iommu_type1_dma_map dma_map = {
    .argsz = sizeof(dma_map),
    .flags = VFIO_DMA_MAP_FLAG_READ | VFIO_DMA_MAP_FLAG_WRITE,
    .vaddr = (__u64)dma_buf,       /* 유저 공간 가상 주소 */
    .iova  = 0x10000000,            /* 디바이스가 보는 I/O 가상 주소 */
    .size  = 0x100000,              /* 1MB */
};
ioctl(container_fd, VFIO_IOMMU_MAP_DMA, &dma_map);

/* DMA 매핑 해제 */
struct vfio_iommu_type1_dma_unmap dma_unmap = {
    .argsz = sizeof(dma_unmap),
    .iova  = 0x10000000,
    .size  = 0x100000,
};
ioctl(container_fd, VFIO_IOMMU_UNMAP_DMA, &dma_unmap);
KVM + VFIO DMA 매핑: QEMU는 게스트 RAM 전체를 IOVA 공간에 매핑합니다. 게스트 물리 주소(GPA)가 곧 IOVA가 되어 디바이스의 DMA가 직접 게스트 메모리에 도달합니다. hugepage를 사용하면 IOMMU 페이지 테이블 엔트리 수가 줄어 매핑 성능이 크게 향상됩니다.

BAR 영역 접근 (MMIO 매핑)

VFIO Device FD를 통해 PCI BAR(Base Address Register) 영역을 mmap()으로 유저 공간에 직접 매핑하거나 read()/write()로 접근할 수 있습니다.

/* BAR 0 정보 조회 */
struct vfio_region_info reg_info = {
    .argsz = sizeof(reg_info),
    .index = VFIO_PCI_BAR0_REGION_INDEX,
};
ioctl(device_fd, VFIO_DEVICE_GET_REGION_INFO, &reg_info);
printf("BAR0: size=%llu, offset=0x%llx, flags=0x%x\n",
       reg_info.size, reg_info.offset, reg_info.flags);

/* 방법 1: mmap으로 직접 매핑 (성능 최적) */
if (reg_info.flags & VFIO_REGION_INFO_FLAG_MMAP) {
    void *bar0 = mmap(NULL, reg_info.size,
                       PROT_READ | PROT_WRITE, MAP_SHARED,
                       device_fd, reg_info.offset);
    /* bar0[offset]로 MMIO 레지스터 직접 접근 */
    __u32 status = *((volatile __u32 *)bar0 + 0x04);
}

/* 방법 2: pread/pwrite (trap 기반, 느리지만 항상 사용 가능) */
__u32 val;
pread(device_fd, &val, sizeof(val), reg_info.offset + 0x04);

/* PCI Config Space 접근 */
struct vfio_region_info cfg_info = {
    .argsz = sizeof(cfg_info),
    .index = VFIO_PCI_CONFIG_REGION_INDEX,
};
ioctl(device_fd, VFIO_DEVICE_GET_REGION_INFO, &cfg_info);
__u16 vendor_id;
pread(device_fd, &vendor_id, 2, cfg_info.offset + PCI_VENDOR_ID);
Region Index대상mmap 가능설명
VFIO_PCI_BAR0_REGION_INDEX ~ BAR5PCI BAR 0~5대부분 가능MMIO/IO 포트 영역
VFIO_PCI_ROM_REGION_INDEXExpansion ROM읽기 전용옵션 ROM (GPU VBIOS 등)
VFIO_PCI_CONFIG_REGION_INDEXConfig Space불가pread/pwrite만 가능, vfio-pci가 필터링
VFIO_PCI_VGA_REGION_INDEXVGA 레거시가능0xA0000~0xBFFFF + I/O 포트

인터럽트 처리 (MSI/MSI-X)

VFIO는 디바이스 인터럽트를 eventfd를 통해 유저 공간으로 전달합니다. KVM과 결합 시 irqfd 메커니즘으로 커널에서 직접 게스트 인터럽트를 주입하여 QEMU 개입 없이 처리합니다.

#include <sys/eventfd.h>

/* MSI-X 인터럽트 정보 조회 */
struct vfio_irq_info irq_info = {
    .argsz = sizeof(irq_info),
    .index = VFIO_PCI_MSIX_IRQ_INDEX,
};
ioctl(device_fd, VFIO_DEVICE_GET_IRQ_INFO, &irq_info);
printf("MSI-X vectors: %u\n", irq_info.count);

/* eventfd 생성 (각 MSI-X 벡터마다 하나) */
int evtfd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);

/* MSI-X 벡터 0에 eventfd 연결 */
struct {
    struct vfio_irq_set hdr;
    int32_t fd;
} irq_set;
irq_set.hdr.argsz = sizeof(irq_set);
irq_set.hdr.flags = VFIO_IRQ_SET_DATA_EVENTFD | VFIO_IRQ_SET_ACTION_TRIGGER;
irq_set.hdr.index = VFIO_PCI_MSIX_IRQ_INDEX;
irq_set.hdr.start = 0;    /* 벡터 0부터 */
irq_set.hdr.count = 1;    /* 1개 벡터 */
irq_set.fd = evtfd;
ioctl(device_fd, VFIO_DEVICE_SET_IRQS, &irq_set);

/* epoll로 인터럽트 대기 */
struct epoll_event ev = { .events = EPOLLIN, .data.fd = evtfd };
epoll_ctl(epoll_fd, EPOLL_CTL_ADD, evtfd, &ev);
/* epoll_wait() → eventfd readable = 인터럽트 발생! */
PCI Device IOMMU IRQ Remap vfio-pci IRQ handler eventfd signal QEMU (epoll) KVM irqfd MSI KVM irqfd: eventfd → 커널에서 직접 게스트 vCPU에 인터럽트 주입 (QEMU context switch 불필요 → 지연 시간 대폭 감소)
VFIO 인터럽트 경로: MSI → IOMMU 리매핑 → vfio-pci → eventfd → KVM irqfd (최적) 또는 QEMU epoll

디바이스 패스스루 절차

# 1. IOMMU 활성화 (부트 파라미터)
# Intel: intel_iommu=on iommu=pt
# AMD:   amd_iommu=on iommu=pt

# IOMMU 활성화 확인
dmesg | grep -i iommu
# [    0.123456] DMAR: IOMMU enabled
# [    0.234567] DMAR-IR: Enabled IRQ remapping in x2apic mode

# 2. IOMMU 그룹 확인 (전체 매핑 스크립트)
for g in /sys/kernel/iommu_groups/*/devices/*; do
    n=${g#*/iommu_groups/}; n=${n%%/*}
    printf "IOMMU Group %s: " "$n"
    lspci -nns ${g##*/}
done
# IOMMU Group 15: 03:00.0 Ethernet controller [0200]: Intel Corporation I210 [8086:1533]
# IOMMU Group 15: 03:00.1 Ethernet controller [0200]: Intel Corporation I210 [8086:1533]
# ⚠ 같은 그룹의 모든 디바이스는 함께 패스스루해야 함!

# 3. 기존 드라이버에서 디바이스 분리
echo "0000:03:00.0" > /sys/bus/pci/devices/0000:03:00.0/driver/unbind

# 4. vfio-pci 드라이버 바인딩 (방법 A: vendor:device ID)
modprobe vfio-pci
echo "8086 1533" > /sys/bus/pci/drivers/vfio-pci/new_id
# 방법 B: driver_override (특정 BDF만 바인딩, 권장)
echo "vfio-pci" > /sys/bus/pci/devices/0000:03:00.0/driver_override
echo "0000:03:00.0" > /sys/bus/pci/drivers/vfio-pci/bind

# 바인딩 확인
ls -la /dev/vfio/
# crw------- 1 root root 236, 0 ... /dev/vfio/15   ← IOMMU Group 15
# crw-rw-rw- 1 root root 10, 196 ... /dev/vfio/vfio ← Container

# 5. QEMU에서 디바이스 패스스루
qemu-system-x86_64 \
    -device vfio-pci,host=0000:03:00.0 \
    -m 4G -enable-kvm ...

# GPU 패스스루 (x-vga + 디스플레이)
qemu-system-x86_64 \
    -device vfio-pci,host=0000:01:00.0,x-vga=on,multifunction=on \
    -device vfio-pci,host=0000:01:00.1 \
    -vga none -display gtk ...

# NVMe 패스스루
qemu-system-x86_64 \
    -device vfio-pci,host=0000:04:00.0 \
    -m 8G -enable-kvm ...

SR-IOV + VFIO

SR-IOV(Single Root I/O Virtualization)는 하나의 물리 디바이스(PF)를 여러 가상 기능(VF)으로 분할합니다. 각 VF는 독립적인 PCI 기능으로 나타나며, 별도의 IOMMU 그룹을 가지므로 개별 VM에 안전하게 패스스루할 수 있습니다.

# SR-IOV VF 생성
echo 4 > /sys/bus/pci/devices/0000:03:00.0/sriov_numvfs
# VF가 PCI 디바이스로 나타남
lspci | grep "Virtual Function"
# 03:10.0 Ethernet controller: Intel ... Virtual Function
# 03:10.1 Ethernet controller: Intel ... Virtual Function
# 03:10.2 Ethernet controller: Intel ... Virtual Function
# 03:10.3 Ethernet controller: Intel ... Virtual Function

# 각 VF를 별도 VM에 패스스루
echo "0000:03:10.0" > /sys/bus/pci/devices/0000:03:10.0/driver/unbind
echo "vfio-pci" > /sys/bus/pci/devices/0000:03:10.0/driver_override
echo "0000:03:10.0" > /sys/bus/pci/drivers/vfio-pci/bind

qemu-system-x86_64 \
    -device vfio-pci,host=0000:03:10.0 \
    -netdev tap,id=net0,vhost=on \
    -m 4G -enable-kvm ...
항목PF (Physical Function)VF (Virtual Function)
PCI CapabilitySR-IOV Extended CapabilityPF에 의해 생성된 경량 기능
Config Space전체 접근제한됨 (PF가 관리)
BAR 리소스자체 BARPF의 VF BAR에서 분할 할당
IOMMU 그룹자체 그룹 (ACS 의존)각 VF가 개별 그룹 (ACS 지원 시)
성능네이티브거의 네이티브 (하드웨어 분할)
용도호스트 사용 / VF 관리VM 패스스루

mdev (Mediated Device)

mdev는 소프트웨어 기반 디바이스 분할 프레임워크입니다. SR-IOV와 달리 하드웨어 지원 없이도 벤더 드라이버가 가상 디바이스 인스턴스를 생성할 수 있습니다. GPU 가상화(NVIDIA vGPU, Intel GVT-g)가 대표적 사용 사례입니다.

# mdev 아키텍처: 물리 디바이스 → 벤더 드라이버 → 가상 디바이스 인스턴스
# NVIDIA vGPU, Intel GVT-g, s390 vfio-ccw, vfio-ap 등이 사용

# 지원 가능한 mdev 타입 확인
ls /sys/class/mdev_bus/0000:00:02.0/mdev_supported_types/
# i915-GVTg_V5_4    ← Intel GVT-g 가상 GPU
# i915-GVTg_V5_8    ← 더 많은 리소스 할당 타입

# 타입별 상세 정보
cat /sys/class/mdev_bus/0000:00:02.0/mdev_supported_types/i915-GVTg_V5_4/description
# low_gm_size: 64MB, high_gm_size: 384MB, fence: 4, resolution: 1920x1200
cat /sys/class/mdev_bus/0000:00:02.0/mdev_supported_types/i915-GVTg_V5_4/available_instances
# 2  ← 현재 생성 가능한 인스턴스 수

# mdev 인스턴스 생성
UUID=$(uuidgen)
echo "$UUID" > /sys/class/mdev_bus/0000:00:02.0/mdev_supported_types/i915-GVTg_V5_4/create
# /sys/bus/mdev/devices/$UUID 가 생성됨

# QEMU에서 mdev 사용
qemu-system-x86_64 \
    -device vfio-pci,sysfsdev=/sys/bus/mdev/devices/$UUID \
    -display gtk,gl=on ...

# mdev 인스턴스 제거
echo 1 > /sys/bus/mdev/devices/$UUID/remove
디바이스 분할 방식 비교 SR-IOV 하드웨어 기반 분할 VF 0 VF 1 PF (Physical Function) mdev 소프트웨어 기반 분할 vGPU 0 vGPU 1 벤더 드라이버 (trap + 에뮬레이트) 직접 패스스루 전체 디바이스 할당 VM (독점 사용) 물리 디바이스 (1:1)
SR-IOV: 하드웨어 VF 분할 | mdev: 벤더 드라이버 소프트웨어 분할 | 직접 패스스루: 디바이스 전체 할당

디바이스 리셋 메커니즘

VM 종료 후 패스스루 디바이스를 깨끗한 상태로 되돌리려면 리셋이 필요합니다. VFIO는 여러 리셋 방식을 지원하며, 디바이스가 지원하는 가장 세밀한 리셋을 사용합니다.

리셋 방식범위PCIe Capability설명
FLR (Function Level Reset) 단일 Function PCI Express / AF Capability 가장 세밀한 리셋. VF도 개별 리셋 가능. 모든 PCIe 디바이스가 지원하지는 않음
PM Reset 단일 Function PM Capability (D3hot → D0) 전원 상태 전환으로 리셋. FLR 미지원 시 대안. 일부 상태가 보존될 수 있음
Bus Reset 버스 상의 모든 디바이스 Secondary Bus Reset PCI 브릿지의 2차 버스 전체 리셋. 영향 범위가 크므로 IOMMU 그룹 단위로 적용
Hot Reset 슬롯/브릿지 하위 PCIe Hot Reset In-Band 리셋. 다른 디바이스에 영향 가능
# 디바이스의 리셋 지원 여부 확인
cat /sys/bus/pci/devices/0000:03:00.0/reset_method
# flr pm  ← FLR과 PM 리셋 지원

# 수동 리셋 트리거
echo 1 > /sys/bus/pci/devices/0000:03:00.0/reset

# VFIO ioctl로 리셋
# ioctl(device_fd, VFIO_DEVICE_RESET);

# 리셋 문제 디버깅
dmesg | grep -i "reset\|flr"
# vfio-pci 0000:03:00.0: not resettable ← 리셋 미지원
리셋 실패 시 대응: FLR 미지원 디바이스(일부 구형 GPU, 특수 어댑터)는 VM 종료 후 디바이스 상태가 오염될 수 있습니다. 이 경우 호스트 재부팅 없이는 재사용 불가능합니다. vfio-pci 모듈 파라미터 nointxmask, disable_idle_d3 등으로 일부 완화 가능합니다.

IOMMU 그룹 격리 상세

IOMMU 그룹은 IOMMU가 격리할 수 있는 최소 단위입니다. 같은 그룹 내 디바이스들은 서로의 DMA 트래픽을 가로챌 수 있으므로, 보안 격리를 위해 그룹 단위로 관리해야 합니다.

Root Complex PCIe Bridge (ACS O) PCIe Bridge (ACS X) GPU IOMMU Group 1 NIC IOMMU Group 2 SATA IOMMU Group 5 USB IOMMU Group 5 ACS 미지원 → 같은 그룹 (함께 패스스루)
PCIe ACS(Access Control Service): ACS 지원 브릿지 뒤 디바이스는 개별 IOMMU 그룹, 미지원 시 하나의 그룹으로 묶임
# IOMMU 그룹 상세 매핑 스크립트
for grp in /sys/kernel/iommu_groups/*; do
    echo "=== IOMMU Group $(basename $grp) ==="
    for dev in $grp/devices/*; do
        bdf=$(basename $dev)
        driver=$(readlink $dev/driver 2>/dev/null | xargs basename 2>/dev/null)
        echo "  $bdf  $(lspci -s $bdf)  [driver: ${driver:-none}]"
    done
done

# ACS 지원 여부 확인
lspci -vvv -s 0000:00:01.0 | grep "Access Control Services"
# Access Control Services
#   ACSCap: SrcValid+ TransBlk+ ReqRedir+ CmpltRedir+ UpstreamFwd+ EgressCtrl+ DirectTrans+
#   ACSCtl: SrcValid+ TransBlk+ ReqRedir+ CmpltRedir+ UpstreamFwd+ EgressCtrl+ DirectTrans+
ACS Override 패치: 컨슈머 마더보드에서 IOMMU 그룹이 거대해지는 문제를 해결하기 위해 pcie_acs_override=downstream,multifunction 커널 패치가 존재하지만, 보안 격리를 완전히 포기하는 것입니다. 프로덕션 환경에서는 ACS를 지원하는 서버급 하드웨어를 사용하세요.

VFIO 운영 고려사항

핵심 고려사항:
  • 인터럽트 리매핑 — IOMMU interrupt remapping 미지원 시 인터럽트 주입 공격 가능. CONFIG_IRQ_REMAP 필수. dmesg | grep "IRQ remapping"으로 확인
  • MMIO BAR 보안 — 게스트가 BAR 접근 시 일부 영역은 MMIO trap이 발생하여 vfio-pci가 필터링. 성능 민감한 경우 mmap 가능 영역은 직접 매핑됨
  • P2P DMA — GPU↔NVMe 등 디바이스 간 직접 DMA는 같은 IOMMU 도메인(Container) 내에서만 가능. ATS(Address Translation Service) 지원 필요
  • 핫플러그 — VFIO 디바이스의 핫플러그/핫언플러그는 QEMU 모니터에서 device_add/device_del로 수행. 커널 4.10+ 필요
  • DMA 매핑 오버헤드 — VM 시작 시 대용량 메모리 IOMMU 매핑이 수 초 소요 가능. hugepage 사용(2MB/1GB)으로 페이지 테이블 엔트리 수를 대폭 감소
  • No-IOMMU 모드 — IOMMU 없는 환경에서 enable_unsafe_noiommu_mode로 VFIO 사용 가능하나 DMA 격리 없음(보안 위험). DPDK 개발/테스트 용도로만 사용
  • Config Space 에뮬레이션 — vfio-pci는 PCI config space의 일부를 가상화합니다(예: BAR, MSI/MSI-X capability). 게스트의 잘못된 config 쓰기로부터 호스트를 보호
  • IOVA 레이아웃 — 게스트 RAM 크기만큼의 연속적인 IOVA 공간이 필요. IOMMU가 지원하는 주소 폭(보통 48-bit)을 초과하지 않도록 주의

VFIO 라이브 마이그레이션

커널 5.x+ 에서 VFIO migration interface가 도입되어 패스스루 디바이스를 포함한 VM의 라이브 마이그레이션이 가능해졌습니다. 디바이스 상태를 저장/복원하기 위한 표준화된 인터페이스를 제공합니다.

/* VFIO Migration v2 (커널 6.2+) - drivers/vfio/pci/vfio_pci_core.c */
/* 마이그레이션 상태 머신: RUNNING → STOP → STOP_COPY → RESUMING */

enum vfio_device_mig_state {
    VFIO_DEVICE_STATE_ERROR = 0,
    VFIO_DEVICE_STATE_STOP = 1,
    VFIO_DEVICE_STATE_RUNNING = 2,
    VFIO_DEVICE_STATE_STOP_COPY = 3,     /* 디바이스 상태 직렬화 */
    VFIO_DEVICE_STATE_RESUMING = 4,      /* 디바이스 상태 복원 */
    VFIO_DEVICE_STATE_RUNNING_P2P = 5,   /* P2P 쿼리 (dirty tracking) */
    VFIO_DEVICE_STATE_PRE_COPY = 6,      /* 사전 복사 (VM 실행 중) */
    VFIO_DEVICE_STATE_PRE_COPY_P2P = 7, /* P2P + 사전 복사 */
};

/* 마이그레이션 상태 전환 */
struct vfio_device_feature feat = {
    .argsz = sizeof(feat) + sizeof(struct vfio_device_feature_mig_state),
    .flags = VFIO_DEVICE_FEATURE_SET | VFIO_DEVICE_FEATURE_MIG_DEVICE_STATE,
};
struct vfio_device_feature_mig_state *mig =
    (void *)feat.data;
mig->device_state = VFIO_DEVICE_STATE_STOP_COPY;
ioctl(device_fd, VFIO_DEVICE_FEATURE, &feat);

/* data_fd로 디바이스 상태 읽기 (직렬화된 상태) */
int data_fd = mig->data_fd;
char buf[4096];
ssize_t n;
while ((n = read(data_fd, buf, sizeof(buf))) > 0) {
    /* 대상 호스트로 전송 */
    send_to_dest(buf, n);
}
Dirty Page Tracking: 라이브 마이그레이션 중 디바이스가 DMA로 변경한 페이지를 추적해야 합니다. VFIO는 VFIO_IOMMU_DIRTY_PAGES ioctl을 통해 비트맵 기반의 dirty page tracking을 제공합니다. 이는 IOMMU의 Access/Dirty 비트(Intel ECAP, AMD v2 page table)를 활용합니다.

VFIO 성능 비교

I/O 방식네트워크 처리량지연 시간CPU 오버헤드디바이스 공유
VFIO 직접 패스스루 ~네이티브 (10~100Gbps) ~네이티브 (~2μs) 최소 (IOMMU 변환만) 불가 (1:1)
SR-IOV VF 패스스루 ~네이티브 (VF당 대역폭) ~네이티브 (~2~5μs) 최소 VF 수만큼 공유
vhost-net (virtio) ~5~20Gbps ~10~30μs 중간 (데이터 복사) 가능 (소프트웨어)
에뮬레이션 (e1000) ~1~3Gbps ~50~200μs 높음 (전체 에뮬레이션) 가능
mdev (vGPU) 디바이스 의존 네이티브의 70~90% 중간 (trap 처리) 인스턴스 수만큼

VFIO + DPDK / SPDK

DPDK(Data Plane Development Kit)와 SPDK(Storage Performance Development Kit)는 VFIO를 사용하여 NIC/NVMe를 유저 공간에서 직접 제어합니다. 커널 네트워크/스토리지 스택을 완전히 바이패스하여 수백만 pps(패킷/초)의 처리량을 달성합니다.

# === DPDK: 고성능 패킷 처리 ===

# hugepage 할당 (DPDK DMA 버퍼용)
echo 1024 > /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages
mount -t hugetlbfs nodev /dev/hugepages

# NIC를 VFIO에 바인딩
dpdk-devbind.py --status                      # 현재 바인딩 상태 확인
dpdk-devbind.py --bind=vfio-pci 0000:03:00.0  # VFIO-PCI에 바인딩
dpdk-devbind.py --status                      # 바인딩 확인

# DPDK 테스트 실행 (l2fwd 예제)
dpdk-l2fwd -l 0-3 -n 4 -- -p 0x1 -T 1

# === SPDK: 고성능 스토리지 ===

# NVMe 디바이스를 VFIO에 바인딩
spdk/scripts/setup.sh
# NVMe 장치가 자동으로 vfio-pci에 바인딩됨

# SPDK NVMe 벤치마크
spdk/build/examples/perf -q 128 -o 4096 -w randread -t 10

# === VFIO no-IOMMU 모드 (IOMMU 없는 환경) ===
echo 1 > /sys/module/vfio/parameters/enable_unsafe_noiommu_mode
# ⚠ DMA 격리 없음 — 디바이스가 모든 물리 메모리에 접근 가능
# 개발/테스트 환경에서만 사용!

VFIO 커널 소스 구조

경로역할
drivers/vfio/vfio_main.cVFIO 코어 — Container/Group/Device 관리, ioctl 디스패치
drivers/vfio/vfio_iommu_type1.cType1 IOMMU 백엔드 — DMA 매핑/해제, dirty page tracking
drivers/vfio/pci/vfio_pci_core.cVFIO-PCI 코어 — BAR/Config/IRQ/리셋 처리
drivers/vfio/pci/vfio_pci_config.cPCI Config Space 에뮬레이션 — 읽기/쓰기 필터링
drivers/vfio/pci/vfio_pci_intrs.c인터럽트 처리 — INTx/MSI/MSI-X eventfd 연결
drivers/vfio/pci/vfio_pci_rdwr.cBAR/VGA 영역 read/write 핸들러
drivers/vfio/mdev/mdev 프레임워크 — Mediated Device 라이프사이클 관리
include/linux/vfio.hVFIO 커널 내부 헤더 — 구조체, 콜백 정의
include/uapi/linux/vfio.hVFIO 유저 공간 API — ioctl 번호, 구조체 정의
/* VFIO PCI 드라이버 콜백 구조체 (drivers/vfio/pci/vfio_pci_core.c) */
const struct vfio_device_ops vfio_pci_core_ops = {
    .name          = "vfio-pci",
    .init          = vfio_pci_core_init_dev,
    .release       = vfio_pci_core_release_dev,
    .open_device   = vfio_pci_open_device,
    .close_device  = vfio_pci_core_close_device,
    .ioctl         = vfio_pci_core_ioctl,        /* Device FD ioctl 처리 */
    .read          = vfio_pci_core_read,          /* BAR/Config pread */
    .write         = vfio_pci_core_write,         /* BAR/Config pwrite */
    .mmap          = vfio_pci_core_mmap,          /* BAR mmap */
    .request       = vfio_pci_core_request,
    .match         = vfio_pci_core_match,
};

VFIO 관련 커널 설정

# VFIO 핵심
CONFIG_VFIO=m                    # VFIO 프레임워크
CONFIG_VFIO_PCI=m                # VFIO PCI 드라이버
CONFIG_VFIO_IOMMU_TYPE1=m        # Type1 IOMMU 백엔드
CONFIG_VFIO_NOIOMMU=y            # No-IOMMU 모드 (선택)
CONFIG_VFIO_VIRQFD=m             # eventfd 기반 IRQ

# IOMMU
CONFIG_IOMMU_SUPPORT=y           # IOMMU 서브시스템
CONFIG_INTEL_IOMMU=y             # Intel VT-d
CONFIG_AMD_IOMMU=y               # AMD-Vi (IOMMU v2)
CONFIG_IRQ_REMAP=y               # 인터럽트 리매핑 (보안 필수)
CONFIG_IOMMU_DEFAULT_DMA_LAZY=y  # 지연 IOTLB 무효화 (성능)

# mdev
CONFIG_VFIO_MDEV=m               # Mediated Device 프레임워크

# SR-IOV
CONFIG_PCI_IOV=y                 # SR-IOV 지원
CONFIG_PCI_ATS=y                 # Address Translation Service
CONFIG_PCI_PRI=y                 # Page Request Interface
CONFIG_PCI_PASID=y               # Process Address Space ID

VFIO 트러블슈팅

증상원인해결
Group not viable IOMMU 그룹 내 일부 디바이스가 호스트 드라이버에 바인딩 같은 그룹의 모든 디바이스를 vfio-pci에 바인딩하거나 pci-stub으로 점유
No IOMMU groups IOMMU 비활성화 또는 부트 파라미터 누락 intel_iommu=on / amd_iommu=on 부트 파라미터 추가, BIOS에서 VT-d/AMD-Vi 활성화
Permission denied on /dev/vfio/N 사용자 권한 부족 chown 또는 udev 규칙: SUBSYSTEM=="vfio", OWNER="root", GROUP="kvm", MODE="0660"
Device not resettable FLR/PM Reset 미지원 cat /sys/bus/pci/devices/.../reset_method 확인. Bus Reset 시도 또는 호스트 재부팅
DMAR fault / IOMMU page fault DMA 매핑 누락 또는 IOVA 범위 초과 dmesg | grep DMAR로 fault 주소 확인. DMA 매핑 범위 점검
GPU 패스스루 후 호스트 콘솔 먹통 GPU ROM이 호스트에서 초기화된 상태로 게스트에 전달 GPU ROM 파일 덤프 후 romfile= 옵션 사용, 또는 두 번째 GPU를 호스트용으로 사용
VM 시작 시 수 초 지연 대용량 RAM의 IOMMU 매핑 시간 hugepages 사용 (2MB/1GB 페이지). pre-alloc으로 매핑 시간 단축
IOMMU 그룹이 너무 큼 ACS 미지원 PCIe 브릿지 서버급 플랫폼 사용 (ACS 지원), 또는 pcie_acs_override 패치 (보안 위험)
# VFIO 디버깅 명령 모음

# IOMMU 상태 확인
dmesg | grep -iE "iommu|dmar|amd-vi"

# VFIO 바인딩 상태
ls -la /dev/vfio/
lspci -k -s 0000:03:00.0  # Kernel driver in use 확인

# IOMMU 그룹 내 디바이스와 드라이버
ls /sys/kernel/iommu_groups/15/devices/
readlink /sys/bus/pci/devices/0000:03:00.0/iommu_group

# IOMMU 도메인 정보 (debugfs)
ls /sys/kernel/debug/iommu/
cat /sys/kernel/debug/iommu/intel/dmar0/domain_translation_struct

# vfio-pci 이벤트 추적
echo 1 > /sys/kernel/debug/tracing/events/vfio/enable
cat /sys/kernel/debug/tracing/trace_pipe
VFIO 사용 사례 정리:
  • GPU 패스스루 — 게이밍 VM, ML/AI 워크로드에 NVIDIA/AMD GPU 직접 할당. ROCm/CUDA 가속 활용
  • NIC 패스스루 — SR-IOV VF를 VM에 할당하여 거의 네이티브 네트워크 성능 (10~100GbE)
  • NVMe 패스스루 — 스토리지 I/O 가상화 오버헤드 제거. NVMe namespace를 개별 VM에 할당
  • DPDK — 고성능 패킷 처리를 위한 유저 공간 NIC 제어 (수십 Mpps)
  • SPDK — 유저 공간 NVMe 드라이버. 수백만 IOPS (고성능 스토리지)
  • FPGA/SmartNIC — Xilinx Alveo, Intel PAC 등 가속기를 VM/컨테이너에 할당
  • Confidential Computing — SEV/TDX + VFIO로 암호화된 VM에 디바이스 패스스루