KVMã®ãªãã¿(KVM internals)
VMMã®é«éåã«ã¤ãã¦å¦ã¶éç¨ã§KVMã®ã³ã¼ããèªãã ã®ã§ã
ã¡ã¢ä»£ããã«å
é¨æ§é ã®è§£èª¬è¨äºãæ¸ãã¾ããã
KVMã¯qemuã¨é£æºãã¦åä½ããããã以åç§ãæ¸ããQEMU internals( http://rkx1209.hatenablog.com/entry/2015/11/15/214404 )
ãåããã¦ãåç
§ãã ãããã¾ãæ¬è¨äºã¯ããç¨åº¦ã·ã¹ãã ããã°ã©ã ã«æ
£ãã¦ããã
VT-xãä»®æ³åã®åºæ¬ã¢ã¼ããã¯ãã£ã¯ç¥ã£ã¦ããç©ã¨ãã¦é²ãã¾ãã
1.qemu-kvm,kvmã®åæå
ã§ã¯æ©éè¦ã¦è¡ãã¾ããããã¾ãã¯KVMã®åæåã®å
¥ãå£ã¨ãªãqemu-kvmãµã¤ãããè¦ã¦ããã¾ãã(ã¡ãªã¿ã«ç¾å¨qemu-kvmã¯qemuæ¬å®¶ã«çµ±åããã¦ããconfigãå¤ãããã¨ã§kvmãæå¹åããä»æ§ã«ãªã£ã¦ãã¾ã)
qemuã¯/dev/kvmãéãã¦KVMã¨ããåããè¡ãã¾ããå ¨ä½çãªã¢ã¼ããã¯ãã£ã¯ä»¥ä¸ã®ãããªæãã§ãã
http://vm360degree.com/2011/09/24/2011-red-hat-forum-%E0%B8%95%E0%B8%AD%E0%B8%99%E0%B8%97%E0%B8%B5%E0%B9%88-1-%E2%80%93-kvm-kernel-base-virtual-machine-%E0%B8%84%E0%B8%B7%E0%B8%AD-%E0%B8%AD%E0%B8%B0%E0%B9%84%E0%B8%A3/ãã
å®éã®åæåã³ã¼ãã¯ä»¥ä¸ã®ããã«ãªãã¾ãã
static int kvm_init(MachineState *ms) { MachineClass *mc = MACHINE_GET_CLASS(ms); ... s = KVM_STATE(ms->accelerator); [*1] ... s->vmfd = -1; s->fd = qemu_open("/dev/kvm", O_RDWR); [*2] ... /* check the vcpu limits */ soft_vcpus_limit = kvm_recommended_vcpus(s); [*3] hard_vcpus_limit = kvm_max_vcpus(s); ... do { ret = kvm_ioctl(s, KVM_CREATE_VM, type); [*4] } while (ret == -EINTR); ... s->vmfd = ret; [*5] ... if (machine_kernel_irqchip_allowed(ms)) { [*25] kvm_irqchip_create(ms, s); [*26] } kvm_state = s; ... return 0; }
[*1]ã§MachineClassããAccelStateã«ã¢ã¯ã»ã¹ãã¦ãã¾ããKVMState(kvm-all.c)ã¯AccelStateãç¶æ¿ãã¦ãããããããªã¢ã¼ãã£ãºã ã§ããã(ããªã¢ã¼ãã£ãºã ã®å®ç¾æ¹æ³ãªã©ã¯qemu internalsã«è¼ã£ã¦ãã¾ã)
ãã¦[*2]ã§/dev/kvmãopenãã¦ãã¾ããããã¯ä¸ã§ãè¿°ã¹ãããã«ãqemuãã«ã¼ãã«ã¢ã¸ã¥ã¼ã«ã§ããkvm.koã¨ããåãããããã®ã¤ã³ã¿ãã§ã¼ã¹ã¨ãã¦ä½¿ç¨ããã¾ãã
[*3]ã§KVMã®å¿ è¦ã¨ããCPUã®æ°ã調ã¹ã¦ãã¾ããä¸èº«ã¯kvm_ioctl(kvm-all.c)ã使ã£ã¦/dev/kvmã«ioctlã§KVM_CHECK_EXTENSIONãªã¯ã¨ã¹ããé£ã°ãã¦ãã¾ããqemu-kvmã¯kvm_ioctlãå©ç¨ãã¦ãã®ãããªãªã¯ã¨ã¹ããé »ç¹ã«éãã¾ããããããªã¯ã¨ã¹ãã¯KVMã®APIã¨ãã¦å®ç¾©ããã¦ãã¾ãã(KVMã®APIã¯Documentation/virtual/kvm/api.txtã«ããã¥ã¢ã«ãããã¾ã)
[*4]ã§KVM_CREATE_VMãçºè¡ãKVMç¨ã®VMãä½æããããä¾é ¼ãã¾ãã
[*5]ã§ã¯ä½æããVMã®fdãvmfdã«ä»£å ¥ãã¦ãã¾ããVMã¯KVMã«ããã¦kvm-vmã¨ããååã®anonymous-inodeã¨ãã¦ç®¡çããã¦ãã[*4]ã§ã¯ãã®fdãè¿ãå¤ã¨ãªã£ã¦ããããã§ãã
ã§ã¯æ¬¡ã«KVMãµã¤ãã®åæåå¦çãè¦ã¦è¡ãã¾ãããã
static int __init vmx_init(void) { int r = kvm_init(&vmx_x86_ops, sizeof(struct vcpu_vmx), __alignof__(struct vcpu_vmx), THIS_MODULE); [*7] if (r) return r; ... return 0; } ... module_init(vmx_init) [*6]
arch/x86/kvm/vmx.cã¯Intelã®VT-Xãæ±ãKVMã¢ã¸ã¥ã¼ã«ã§ã[*6]ã§vmx_initãã«ã¼ãã«ã¢ã¸ã¥ã¼ã«ã®åæåé¢æ°ã¨ãã¦ç»é²ãã¦ãã¾ããã¾ã[*7]ã®kvm_initã®ç¬¬1å¼æ°ã«vmx_x86_opsã®ã¢ãã¬ã¹ã渡ãã¦ãã¾ããvmx_x86_opsã¯kvm_x86_opsæ§é ä½ã§ãããã¯x86ç¹æã®æä½ãé¢æ°ã¨ãã¦ã¾ã¨ããç©ã§ãã(å¾ã§ä½åº¦ãåºã¦ããéè¦ãªæ§é ä½ã§ã)
int kvm_init(void *opaque, unsigned vcpu_size, unsigned vcpu_align, struct module *module) { int r; int cpu; ... kvm_vcpu_cache = kmem_cache_create("kvm_vcpu", vcpu_size, vcpu_align, 0, NULL); ... kvm_chardev_ops.owner = module; kvm_vm_fops.owner = module; kvm_vcpu_fops.owner = module; r = misc_register(&kvm_dev); [*8] ... return 0; ... }
[*8]ã§kvm_devãmiscdeviceã¨ãã¦ç»é²ãã¦ãã¾ããmiscdeviceã¯ã¡ã¸ã£ã¼çªå·ã10ã«åºå®ãããã¤ãã¼çªå·ã§èå¥ããããã£ã©ã¯ã¿ããã¤ã¹ã§ããã«kvmã¨ããååã®ããã¤ã¹ãç»é²ãã¾ãã(ããã/dev/kvmããã¤ã¹ã¨ãªãããã§ã)
kvm_devã¯ä»¥ä¸ã®ããã«å®ç¾©ããã¦ãã¾ãã
(KVM: virt/kvm/kvm_main.c)
static struct file_operations kvm_chardev_ops = { .unlocked_ioctl = kvm_dev_ioctl, .compat_ioctl = kvm_dev_ioctl, .llseek = noop_llseek, }; static struct miscdevice kvm_dev = { KVM_MINOR, "kvm", &kvm_chardev_ops, };
static long kvm_dev_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg) { long r = -EINVAL; switch (ioctl) { case KVM_CREATE_VM: r = kvm_dev_ioctl_create_vm(arg); [*9] break; ... } out: return r; }
[*9]ã®KVM_CREATE_VMãè¦ã¦ãã ãããããã¯/dev/kvmã«KVM_CREATE_VMãioctlã§çºè¡ãããå ´åã«å¦çãããç®æã§ããQEMUãµã¤ãã®åæåã§çºè¡ããã¦ãã¾ããããããã§ã¯kvm_dev_ioctl_create_vmãå¼ã³åºãVMãä½æãã¦ãã¾ãã
static int kvm_dev_ioctl_create_vm(unsigned long type) { int r; struct kvm *kvm; kvm = kvm_create_vm(type); [*10] ... r = anon_inode_getfd("kvm-vm", &kvm_vm_fops, kvm, O_RDWR | O_CLOEXEC); [*11] if (r < 0) kvm_put_kvm(kvm); return r; }
[*10]ã§VMãä½æããå¾[*11]ã§"kvm-vm"ã¨ããååã®anonymous-inodeãä½æãã¦ãã¾ãããããVMã®å®ä½ã§ãããä¸ã§ã説æããããã«qemu-kvmããã¢ã¯ã»ã¹ããéã¯ãã®inodeã®fdãéãã¦è¡ãã¾ããã¾ãkvm_vm_fopsã¯
static struct file_operations kvm_vm_fops = { .release = kvm_vm_release, .unlocked_ioctl = kvm_vm_ioctl, #ifdef CONFIG_KVM_COMPAT .compat_ioctl = kvm_vm_compat_ioctl, #endif .llseek = noop_llseek, };
ã¨ãªã£ã¦ãããVMã«å¯¾ããioctlã¯kvm_vm_ioctlã§å¦çããã¾ãã
ã§ã¯[*10]ã®kvm_create_vmã«ã¤ãã¦è¦ã¦è¡ãã¾ãããã
static struct kvm *kvm_create_vm(unsigned long type) { int r, i; struct kvm *kvm = kvm_arch_alloc_vm(); [*12] ... r = -ENOMEM; for (i = 0; i < KVM_ADDRESS_SPACE_NUM; i++) { kvm->memslots[i] = kvm_alloc_memslots(); [*13] if (!kvm->memslots[i]) goto out_err_no_srcu; } ... for (i = 0; i < KVM_NR_BUSES; i++) { kvm->buses[i] = kzalloc(sizeof(struct kvm_io_bus), GFP_KERNEL); [*14] if (!kvm->buses[i]) goto out_err; } ... r = kvm_init_mmu_notifier(kvm); [*15] ... return kvm; ... }
[*12]ã®kvm_arch_alloc_vm(include/linux/kvm_host.h)ã§æ°ããªkvmæ§é ä½(include/linux/kvm_host.h)ã確ä¿ãã¦ãã¾ããkvmæ§é ä½ã¯ã²ã¹ããã¨ã«ããããä¸ã¤ãã¤åå¨ãã¾ãã
[*13]ã®kvm_alloc_memslots(arch/x86/kvm/kvm_main.c)ã§ã¡ã¢ãªã¹ãããããããã確ä¿ãåæåãã¦ãã¾ããã¾ã[*14]ã§ã¯kvm_io_busãåæåãã¦ãã¾ãã
[*15]ã®kvm_init_mmu_notifier(arch/x86/kvm/kvm_main.c)ã¯MMU notifierããã¯ãã³ãã©ãç»é²ããé¢æ°ã§ããMMU notifierã¯ã²ã¹ãã®ãã¼ã¸ãã¼ãã«ãæããã¼ã¸ãã¼ãã«ã¨ã³ããªããã¹ãOSã«ãã£ã¦ã¹ã¯ãã(ã¤ã³|ã¢ã¦ã)ãããéã«ã²ã¹ãã¸éç¥ããããã«å¼ã°ãã¾ããããã¯ã²ã¹ãOSå´ã®ã«ã¼ãã«ãç¥ããªãéã«ãã¹ãå´ã§pteãã¹ã¯ããã¢ã¦ããããå ´åãã²ã¹ãå´ã®ã¡ã¢ãªç®¡çãç ´ç¶»ãã¦ãã¾ãã®ãé²ãããã§ãã
ã§ã¯æ¬¡ã«kvmã«ãããvcpuã®åæåãè¦ã¦ããã¾ãã
ããã§QEMU internals part1( http://rkx1209.hatenablog.com/entry/2015/11/15/214404 )ã®4.ä»®æ³CPUã«ãããqemu_init_vcpuã®å¦çãè¦ã¦ã¿ã¾ãããã
(QEMU: cpus.c)
static void qemu_kvm_start_vcpu(CPUState *cpu) { char thread_name[VCPU_THREAD_NAME_SIZE]; cpu->thread = g_malloc0(sizeof(QemuThread)); cpu->halt_cond = g_malloc0(sizeof(QemuCond)); qemu_cond_init(cpu->halt_cond); snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "CPU %d/KVM", cpu->cpu_index); qemu_thread_create(cpu->thread, thread_name, qemu_kvm_cpu_thread_fn, cpu, QEMU_THREAD_JOINABLE); [*18] while (!cpu->created) { qemu_cond_wait(&qemu_cpu_cond, &qemu_global_mutex); [*19] } } void qemu_init_vcpu(CPUState *cpu) { cpu->nr_cores = smp_cores; cpu->nr_threads = smp_threads; cpu->stopped = true; if (kvm_enabled()) { qemu_kvm_start_vcpu(cpu); [*17] } else if (tcg_enabled()) { qemu_tcg_init_vcpu(cpu);[*16] } else { qemu_dummy_start_vcpu(cpu); } }
QEMU internalsã§ã¯[*16]ã®qemu_tcg_init_vcpuãè¦ã¦ããã¾ãããã
kvmãæå¹ãªå ´å[*17]ã§qemu_kvm_start_vcpuãå¼ã³åºãããããä»åã¯ãã¡ããè¦ã¾ãã
qemu_kvm_start_vcpuã§ã¯[*18]ã§qemu_kvm_cpu_thread_fnãå®è¡ããvcpuã¹ã¬ãããä½æããvcpuãããã¯ã¤ã³ãããã¾ã§[*19]ã§å¾
ã¡ã¾ãã
ã§ã¯qemu_kvm_cpu_thread_fnãè¦ã¦ã¿ã¾ãããã
(QEMU: cpus.c)
static void *qemu_kvm_cpu_thread_fn(void *arg) { CPUState *cpu = arg; int r; current_cpu = cpu; ... r = kvm_init_vcpu(cpu); [*20] ... qemu_kvm_init_cpu_signals(cpu); /* signal CPU creation */ cpu->created = true; [*23] qemu_cond_signal(&qemu_cpu_cond); while (1) { if (cpu_can_run(cpu)) { r = kvm_cpu_exec(cpu); [*24] if (r == EXCP_DEBUG) { cpu_handle_guest_debug(cpu); } } qemu_kvm_wait_io_event(cpu); } return NULL; }
[*20]ã®kvm_init_vcpuã§vcpuã®åæåãè¡ãã¾ãã
int kvm_init_vcpu(CPUState *cpu) { KVMState *s = kvm_state; long mmap_size; int ret; DPRINTF("kvm_init_vcpu\n"); ret = kvm_vm_ioctl(s, KVM_CREATE_VCPU, (void *)kvm_arch_vcpu_id(cpu)); [*21] cpu->kvm_fd = ret; [*22] cpu->kvm_state = s; cpu->kvm_vcpu_dirty = true; mmap_size = kvm_ioctl(s, KVM_GET_VCPU_MMAP_SIZE, 0); ... cpu->kvm_run = mmap(NULL, mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED, cpu->kvm_fd, 0); ... ret = kvm_arch_init_vcpu(cpu); err: return ret; }
[*21]ã§KVM_CREATE_VCPUãçºè¡ã[*22]ã§CPUStateã®kvm_fdã«ä»£å ¥ãã¦ãã¾ãã
vcpuã®åæåãçµããã°[*23]ã§createdã«trueãè¨å®ãã·ã°ãã«ã§éç¥ãã¾ãã
ããã§KVMã«ããã/dev/kvmããã¤ã¹,vCPU,VMã®åæåãçµããã¾ããã
ã¾ã¨ããã¨
- /dev/kvm(ãã£ã©ã¯ã¿ããã¤ã¹)
KVMèªä½ã®å¶å¾¡ã«ä½¿ãããã¤ã³ã¿ã¼ãã§ã¼ã¹
qemu-kvmå´ããã¯kvm_ioctlã§ã¢ã¯ã»ã¹
- vCPU(anonymous-inode)
KVMã®vCPUã«å¯¾ããå¶å¾¡ãè¡ãã¤ã³ã¿ã¼ãã§ã¼ã¹
qemu-kvmå´ããã¯kvm_vcpu_ioctlã§ã¢ã¯ã»ã¹
ioctlã®ãã³ãã©ã¯KVMå´ã®kvm_vcpu_fopsãç¨ããããã
- VM(anonymous-inode)
KVMèªä½ã®å¶å¾¡ã«ä½¿ãããã¤ã³ã¿ã¼ãã§ã¼ã¹
qemu-kvmå´ããã¯kvm_vm_ioctlã§ã¢ã¯ã»ã¹
ioctlã®ãã³ãã©ã¯KVMå´ã®kvm_vm_fopsãç¨ããããã
ã¨ãªãã¾ãã
ãã¦ã§ã¯ããããã¡ã¤ã³å¦çãè¦ã¦è¡ãã¾ãããã
[*24]ã®kvm_cpu_execãKVMã®ä¸å¿å¦çã§ãã
2.ä»®æ³CPU(vCPU)
kvm_cpu_execã¯VMEnterãã¦ããVMExitããã¾ã§ã®ä¸ååã®å¦çãæ å½ãã¾ãã
int kvm_cpu_exec(CPUState *cpu) { struct kvm_run *run = cpu->kvm_run; int ret, run_ret; ... do { MemTxAttrs attrs; ... run_ret = kvm_vcpu_ioctl(cpu, KVM_RUN, 0); [*1] ... switch (run->exit_reason) { case KVM_EXIT_IO: DPRINTF("handle_io\n"); /* Called outside BQL */ kvm_handle_io(run->io.port, attrs, (uint8_t *)run + run->io.data_offset, run->io.direction, run->io.size, run->io.count); [*14] ret = 0; break; case KVM_EXIT_MMIO: DPRINTF("handle_mmio\n"); /* Called outside BQL */ address_space_rw(&address_space_memory, run->mmio.phys_addr, attrs, run->mmio.data, run->mmio.len, run->mmio.is_write); [*15] ret = 0; break; ... } } while (ret == 0); ... return ret; }
[*1]ã§KVM_RUNãçºè¡ãKVMã«VMEnterãè¡ãããæ示ãã¦ãã¾ãã
vcpuã«å¯¾ãã¦ioctlãçºè¡ãããå ´åãKVMå´ã§ã¯ä»¥ä¸ã®ãããªå¦çãè¡ããã¾ãã
(KVM: virt/kvm/kvm_main.c)
static long kvm_vcpu_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg) { struct kvm_vcpu *vcpu = filp->private_data; ... r = vcpu_load(vcpu); if (r) return r; switch (ioctl) { case KVM_RUN: ... r = kvm_arch_vcpu_ioctl_run(vcpu, vcpu->run); [*2] ... break; ... return r; }
[*2]ãKVM_RUNã®ããã¯é¢æ°ã§ããå®éã®å¦çã¯ãã®kvm_arch_vcpu_ioctl_runã«æãã¦ãã¾ãã
int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run) { struct fpu *fpu = ¤t->thread.fpu; int r; sigset_t sigsaved; ... r = vcpu_run(vcpu); [*3] ... return r; }
[*3]ã®vcpu_runã§å®éã«ã²ã¹ãã¸enterãã¾ãã
static int vcpu_run(struct kvm_vcpu *vcpu) { int r; struct kvm *kvm = vcpu->kvm; vcpu->srcu_idx = srcu_read_lock(&kvm->srcu); for (;;) { if (kvm_vcpu_running(vcpu)) { r = vcpu_enter_guest(vcpu); [*4] } ... } return r; }
[*4]ã®vcpu_enter_guestãã³ã¢é¨åã®å¦çã§ãã
static int vcpu_enter_guest(struct kvm_vcpu *vcpu) { int r; bool req_int_win = dm_request_for_irq_injection(vcpu) && kvm_cpu_accept_dm_intr(vcpu); bool req_immediate_exit = false; if (vcpu->requests) { [*5] if (kvm_check_request(KVM_REQ_MMU_RELOAD, vcpu)) kvm_mmu_unload(vcpu); ... } ... kvm_x86_ops->run(vcpu); [*6] ... /* Interrupt is enabled by handle_external_intr() */ kvm_x86_ops->handle_external_intr(vcpu); ... r = kvm_x86_ops->handle_exit(vcpu); return r; }
[*5]ã§ã¯vcpuã«å±ããåãªã¯ã¨ã¹ããå¦çãã¾ãããªã¯ã¨ã¹ãã¨ã¯VMEnteråã«ããã¹ãå¦çãvcpuã«ä¾é ¼ããæ©æ§ã§ãæåãªä¾ã ã¨kvm-clockãæè¨ã®æ´æ°ãVMEnteråã«è¡ãéãã®ãªã¯ã¨ã¹ãã使ç¨ãã¾ãã
[*6]ã®runã¯arch/x86/kvm/vmx.cå
ã§vmx_vcpu_runã«è¨å®ããã¦ãã¾ãã
static void __noclone vmx_vcpu_run(struct kvm_vcpu *vcpu) { struct vcpu_vmx *vmx = to_vmx(vcpu); unsigned long debugctlmsr, cr4; ... vmx->__launched = vmx->loaded_vmcs->launched; asm( /* Store host registers */ "push %%" _ASM_DX "; push %%" _ASM_BP ";" [*7] "push %%" _ASM_CX " \n\t" /* placeholder for guest rcx */ "push %%" _ASM_CX " \n\t" "cmp %%" _ASM_SP ", %c[host_rsp](%0) \n\t" "je 1f \n\t" "mov %%" _ASM_SP ", %c[host_rsp](%0) \n\t" __ex(ASM_VMX_VMWRITE_RSP_RDX) "\n\t" "1: \n\t" /* Reload cr2 if changed */ "mov %c[cr2](%0), %%" _ASM_AX " \n\t" "mov %%cr2, %%" _ASM_DX " \n\t" "cmp %%" _ASM_AX ", %%" _ASM_DX " \n\t" "je 2f \n\t" "mov %%" _ASM_AX", %%cr2 \n\t" "2: \n\t" /* Check if vmlaunch of vmresume is needed */ "cmpl $0, %c[launched](%0) \n\t" [*8] /* Load guest registers. Don't clobber flags. */ "mov %c[rax](%0), %%" _ASM_AX " \n\t" [*11] "mov %c[rbx](%0), %%" _ASM_BX " \n\t" "mov %c[rdx](%0), %%" _ASM_DX " \n\t" "mov %c[rsi](%0), %%" _ASM_SI " \n\t" "mov %c[rdi](%0), %%" _ASM_DI " \n\t" "mov %c[rbp](%0), %%" _ASM_BP " \n\t" ... "mov %c[rcx](%0), %%" _ASM_CX " \n\t" /* kills %0 (ecx) */ /* Enter guest mode */ "jne 1f \n\t" __ex(ASM_VMX_VMLAUNCH) "\n\t" [*9] "jmp 2f \n\t" "1: " __ex(ASM_VMX_VMRESUME) "\n\t" [*10] "2: " /* Save guest registers, load host registers, keep flags */ "mov %0, %c[wordsize](%%" _ASM_SP ") \n\t" [*12] "pop %0 \n\t" "mov %%" _ASM_AX ", %c[rax](%0) \n\t" "mov %%" _ASM_BX ", %c[rbx](%0) \n\t" __ASM_SIZE(pop) " %c[rcx](%0) \n\t" "mov %%" _ASM_DX ", %c[rdx](%0) \n\t" "mov %%" _ASM_SI ", %c[rsi](%0) \n\t" "mov %%" _ASM_DI ", %c[rdi](%0) \n\t" "mov %%" _ASM_BP ", %c[rbp](%0) \n\t" ... "mov %%cr2, %%" _ASM_AX " \n\t" "mov %%" _ASM_AX ", %c[cr2](%0) \n\t" "pop %%" _ASM_BP "; pop %%" _ASM_DX " \n\t" "setbe %c[fail](%0) \n\t" ".pushsection .rodata \n\t" ".global vmx_return \n\t" "vmx_return: " _ASM_PTR " 2b \n\t" ".popsection" ... ); vmx->exit_reason = vmcs_read32(VM_EXIT_REASON); [*13] ... }
[*7]ã§ãã¹ãã¬ã¸ã¹ã¿ã®ãã¡VT-xã®éé¿å¯¾è±¡ã«ãªã£ã¦ããªãã¬ã¸ã¹ã¿ãã¹ã¿ãã¯ä¸ã«éé¿ãã¦ãã¾ãã
次ã«[*8]ã§ãã§ã«ä¸åº¦VMEnterãããã©ãã確èªããåãã¦ã§ããã°[*9]ã§VMLAUNCHå½ä»¤ããäºåº¦ç®ä»¥éãªã[*10]ã§VMRESUMEå½ä»¤ãå®è¡ãã¦ãã¾ãã[*11]ã§ã²ã¹ãã®ã¬ã¸ã¹ã¿ãvcpu.arch.regsãããã¼ãããå¾ãå¦çãã²ã¹ãã¸ç§»ãã¾ãã
[*12]ã§ã¯VMExitå¾ãã²ã¹ãã®ã¬ã¸ã¹ã¿ãvcpu.arch.regsã«éé¿ããã¹ãã®ã¬ã¸ã¹ã¿ã復帰ãã¦ãã¾ãã
[*13]ã§ã¯VMCSããVM_EXIT_REASONãèªã¿åºãã¾ããããã«ããVM_EXITè¦å ã«åãããã¨ãã¥ã¬ã¼ããè¡ãã¾ãã
ã§ã¯ç¶ãã¦ä»®æ³I/Oå¦çãè¦ã¦ããã¾ãã
[*14],[*15]ã®kvm_handle_io,address_space_rwããI/Oå¦çãçç±ã§VMExitããéã®å¦çã«ãªãã¾ãã
3. ä»®æ³I/O
KVMã«ãããä»®æ³IOã¯qemu-kvmã«å
¨ã¦ä»»ãã¦ãã¾ãããã®ããå¦çã¯é常ã«ç°¡åã§ãã
kvm_handle_ioã¯ä»¥ä¸ã®ããã«ãªã£ã¦ãã¾ãã
static void kvm_handle_io(uint16_t port, MemTxAttrs attrs, void *data, int direction, int size, uint32_t count) { int i; uint8_t *ptr = data; for (i = 0; i < count; i++) { address_space_rw(&address_space_io, port, attrs, ptr, size, direction == KVM_EXIT_IO_OUT); [*1] ptr += size; } }
[*1]ã§address_space_rwãå¼ã³åºãã¦ãã¾ããããã¯qemuã®address_space_ioã¨ããMemoryRegionã使ç¨ããIOå¦çã§ãã(QEMU internalsåç
§)
ã¾ãã®KVM_EXIT_MMIOã®å¦çã§ãaddress_space_memoryãMemoryRegionã¨ããaddress_space_rwãå¼ã°ãã¦ããqemuå´ã«ä¸¸æããã¦ãã¾ãã
ãã®ããã«ä»®æ³IOå¦çã¯qemu-kvmã«ç§»è²ããã¦ãã¾ãã
4. ä»®æ³IRQ(APIC)
KVMã¯é«éåã®ãããä¸é¨ã®ãã¼ãã¦ã§ã¢ãqemu-kvmã§ã¯ãªãç¬èªã«ã¨ãã¥ã¬ã¼ããã¦ãã¾ãã
å
·ä½çã«ã¯PIC,PIT,(I/O|Local)APICã§ãããããã¯qemu-kvmèµ·åæã«-no-kvm-irqchip, -no-kvm-pitãªã©ã®ãªãã·ã§ã³ãã¤ããäºã§qemuã«ã¨ãã¥ã¬ã¼ããä»»ãããã¨ãã§ãã¾ããããã£ãããªã®ã§KVMç¬èªã®ã¨ãã¥ã¬ã¼ã·ã§ã³ã®å®è£
ãè¦ã¦è¡ãã¾ãããã
ã¾ãã¯APICããè¦ã¦ããã¾ãã
1ç« ã®[*25]ãè¦ã¦ãã ãããmachine_kernel_irqchip_allowedã¯kvmã®IRQãããã¨ãã¥ã¬ã¼ã·ã§ã³ãå©ç¨ããå ´åtrueãè¿ãããã®å¾kvm_irqchip_createã«ã¦IRQãããã®åæåãè¡ã£ã¦ãã¾ãã
static void kvm_irqchip_create(MachineState *machine, KVMState *s) { int ret; if (kvm_check_extension(s, KVM_CAP_IRQCHIP)) { ; } ... /* First probe and see if there's a arch-specific hook to create the * in-kernel irqchip for us */ ret = kvm_arch_irqchip_create(s); if (ret == 0) { ret = kvm_vm_ioctl(s, KVM_CREATE_IRQCHIP); [*1] } ... kvm_kernel_irqchip = true; ... kvm_init_irq_routing(s); [*] }
[*1]ã§KVMã«å¯¾ãKVM_CREATE_IRQCHIPãçºè¡ãã¦ãã¾ããããã§KVMå´ã®IRQããããåæåããã¾ãã
long kvm_arch_vm_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg) { struct kvm *kvm = filp->private_data; void __user *argp = (void __user *)arg; int r = -ENOTTY; switch (ioctl) { ... case KVM_CREATE_IRQCHIP: { [*2] struct kvm_pic *vpic; ... if (vpic) { r = kvm_ioapic_init(kvm); [*3] ... } ... r = kvm_setup_default_irq_routing(kvm); [*11] ... } ... } ... }
[*2]ãKVM_CREATE_IRQCHIPãåãåã£ãéã®å¦çã§ããã¾ã[*3]ã®kvm_ioapic_initã§ioapicãä»®æ³ããã¤ã¹ã¨ãã¦åæåãã¦ãã¾ãã
static const struct kvm_io_device_ops ioapic_mmio_ops = { .read = ioapic_mmio_read, .write = ioapic_mmio_write, }; int kvm_ioapic_init(struct kvm *kvm) { struct kvm_ioapic *ioapic; int ret; ioapic = kzalloc(sizeof(struct kvm_ioapic), GFP_KERNEL); .. kvm_ioapic_reset(ioapic); [*4] kvm_iodevice_init(&ioapic->dev, &ioapic_mmio_ops); [*5] ... kvm_vcpu_request_scan_ioapic(kvm); [*10] return ret; }
[*4]ã®kvm_ioapic_resetã§ioapicãåæåãã[*5]ã§ioapic->dev->opsã§è¡¨ãããããã¤ã¹ã®æä½ã¡ã½ãããioapic_mmio_opsã«è¨å®ãã¦ãã¾ãã
/* Caller must hold slots_lock. */ int kvm_io_bus_register_dev(struct kvm *kvm, enum kvm_bus bus_idx, gpa_t addr, int len, struct kvm_io_device *dev) { struct kvm_io_bus *new_bus, *bus; bus = kvm->buses[bus_idx]; [*6] ... new_bus = kmalloc(sizeof(*bus) + ((bus->dev_count + 1) * sizeof(struct kvm_io_range)), GFP_KERNEL); memcpy(new_bus, bus, sizeof(*bus) + (bus->dev_count * sizeof(struct kvm_io_range))); [*7] kvm_io_bus_insert_dev(new_bus, dev, addr, len); [*8] ... return 0; }
[*6]ã§æå®ããããã¹ã«[*7]ã§æ°ãã«ç¢ºä¿ããkvm_io_busãã³ãã¼ãã¦ãã¾ããã¾ã[*8]ã®kvm_io_bus_insert_devã§ã¯æ°ãã«ä½æããkvm_io_busã«ããã¤ã¹ãã¤ãªãã§ãã¾ãã
static int kvm_io_bus_insert_dev(struct kvm_io_bus *bus, struct kvm_io_device *dev, gpa_t addr, int len) { bus->range[bus->dev_count++] = (struct kvm_io_range) { .addr = addr, .len = len, .dev = dev, }; [*9] sort(bus->range, bus->dev_count, sizeof(struct kvm_io_range), kvm_io_bus_sort_cmp, NULL); return 0; }
[*9]ã§kvm_io_rengaeãæ°ãã«è¿½å ãã¦ããäºãåããã¾ãã
ãã¦ããã§APICããã¤ã¹ã®ç»é²ã¯å®äºãã¾ãããæå¾ã«[*10]ã®kvm_vcpu_request_scan_ioapicã§å
¨ã¦ã®vCPUã«å¯¾ãã¦KVM_REQ_SCAN_IOAPICãªã¯ã¨ã¹ããéä¿¡ãã¦ãã¾ãã(ããããªã¯ã¨ã¹ãã®å¦çã¯å¾ã»ã©èª¬æãã¾ã)
ã§ã¯æ»ã£ã¦æ¬¡ã«[*11]ã®kvm_setup_default_irq_routingãè¦ã¦ã¿ã¾ãããã
(KVM: arch/x86/kvm/irq_comm.c)
int kvm_setup_default_irq_routing(struct kvm *kvm) { return kvm_set_irq_routing(kvm, default_routing, ARRAY_SIZE(default_routing), 0); }
int kvm_set_irq_routing(struct kvm *kvm, const struct kvm_irq_routing_entry *ue, unsigned nr, unsigned flags) { struct kvm_irq_routing_table *new, *old; u32 i, j, nr_rt_entries = 0; int r; ... new = kzalloc(sizeof(*new) + (nr_rt_entries * sizeof(struct hlist_head)), GFP_KERNEL); [*12] new->nr_rt_entries = nr_rt_entries; for (i = 0; i < KVM_NR_IRQCHIPS; i++) for (j = 0; j < KVM_IRQCHIP_NUM_PINS; j++) new->chip[i][j] = -1; for (i = 0; i < nr; ++i) { struct kvm_kernel_irq_routing_entry *e; e = kzalloc(sizeof(*e), GFP_KERNEL); [*13] ... r = setup_routing_entry(new, e, ue); [*14] ... ++ue; } ... kvm_arch_irq_routing_update(kvm);[*15] ... }
[*12]ã§kvm_irq_routing_entryã®ãªã¹ãã§ããkvm_irq_routing_tableãä½æãã¦ãã¾ããkvm_irq_routing_entryã¯GSIããIRQã¸ã®å¯¾å¿(ã«ã¼ãã£ã³ã°)ã表ãã¦ããã[*13],[*14]ã§å¼æ°ã¨ãã¦æ¸¡ãããkvm_irq_routing_entryã®ãªã¹ããnewã«è¨å®ãã¦ããã¾ãã
ããã§IRQã«ã¼ãã£ã³ã°ã®ç»é²ã¯å®äºã§ããæå¾ã«[*15]ã§å
¨ã¦ã®vCPUã«KVM_REQ_SCAN_IOAPICãªã¯ã¨ã¹ããéä¿¡ãã¦ãã¾ãã
ãã¦ããã§ä»®æ³APICã®åæåã¯å®äºãã¾ããã
5. ä»®æ³ã¡ã¢ãª(EPT)
ã§ã¯æ¬¡ã«ã¢ãã¬ã¹ç©ºéã®ä»®æ³åæ©æ§ã§ããEPTãKVMã§ã©ã®ããã«æ±ããã¦ãããè¦ã¦ããã¾ããEPTã¯ã²ã¹ãç©çã¢ãã¬ã¹(GPA)ãããã¹ãç©çã¢ãã¬ã¹(HPA)ã¸ã®å¤æãè¡ã4段ãã¼ã¸ãã¼ãã«ã§ãã(以ä¸ãããshadowãã¼ã¸ãã¼ãã«ã¨å¼ã³ã¾ã)
é常ã®ãã¼ã¸ãã¼ãã«æ©æ§ã¨åãããã«ãã¾ã ãã¼ãã«ã«ãããã³ã°ããã¦ããªãã¢ãã¬ã¹ã¸ã¢ã¯ã»ã¹ãçºçããå ´åEPT ViolationãçºçãVMExitããä»çµã¿ã§ãã
http://ytliu.info/blog/2014/11/24/shi-shang-zui-xiang-xi-de-kvm-mmu-pagejie-gou-he-yong-fa-jie-xi/ãã
EPT Violationçºçæã¯handle_ept_violationé¢æ°ãå®è¡ããã¾ãã
static int handle_ept_violation(struct kvm_vcpu *vcpu) { unsigned long exit_qualification; gpa_t gpa; ... gpa = vmcs_read64(GUEST_PHYSICAL_ADDRESS); [*1] ... return kvm_mmu_page_fault(vcpu, gpa, error_code, NULL, 0); [*2] }
[*1]ã§VMCSããGPAãåå¾ããå¾[*2]ã§ãã¼ã¸ãã©ã«ããå¦çãã¾ãã
int kvm_mmu_page_fault(struct kvm_vcpu *vcpu, gva_t cr2, u32 error_code, void *insn, int insn_len) { int r, emulation_type = EMULTYPE_RETRY; enum emulation_result er; r = vcpu->arch.mmu.page_fault(vcpu, cr2, error_code, false); [*3] ... out: return r; }
[*3]ã®arch.mmu.page_faultã«ã¯tdp_page_faultãè¨å®ããã¦ãã¾ãã
static int tdp_page_fault(struct kvm_vcpu *vcpu, gva_t gpa, u32 error_code, bool prefault) { pfn_t pfn; int r; int level; bool force_pt_level; gfn_t gfn = gpa >> PAGE_SHIFT; [*4] ... if (try_async_pf(vcpu, prefault, gfn, gpa, &pfn, write, &map_writable)) [*5] return 0; ... r = __direct_map(vcpu, gpa, write, map_writable, level, gfn, pfn, prefault); [*6] return r; }
EPTã®å¦çã¯åç´ã§[*4]ã§ã¾ãGPAãã²ã¹ããã¼ã¸ãã¬ã¼ã (GFN)ã«å¤æããå¾ã[*5]ã®try_async_pfã§GFNããã¹ããã¼ã¸ãã¬ã¼ã (PFN)ã«å¤æãã¾ããæå¾ã«[*6]ã§EPT table entryãæ°ãã«ä½æãã¦ãã¼ã¸ãã©ã«ãå¦çã¯çµäºã§ãã
ãããã®å¦çã詳細ã«èª¬æãã¦ãè¯ãã®ã§ãããå®ã¯æ¢ã«æ¥æ¬èªè¨äºã§é常ã«è©³ãã説æè¨äº( http://d.hatena.ne.jp/kvm/20110702/1309604602 )ããããããããã§ã¯å¦çæ¦è¦ã ãè¿°ã¹ã¾ãã
(é©æããã¡ãã®ãã¼ã¸ã¨ç §ããåãããªããèªãã§é ããã°ã¨æãã¾ãã決ãã¦ãµãã£ãããã§ã¯(ry )
å¦çæ¦è¦:
try_async_pfã¯Asynchronous page fault(以ä¸APF)ãå®è¡ãã¾ããããããAPFã¨ã¯ã²ã¹ãOSã""ãã¹ãOSã«ãã£ã¦""ã¹ã¯ããã¢ã¦ãããã空éã«ã¢ã¯ã»ã¹ããéãããã¯ã²ã¹ãOSã«ã¨ã£ã¦ééçãªããã²ã¹ãå
ã§ã¹ã¯ããã¤ã³ã®éã®I/Oå¾
ã¡æéããå¥ã®ããã»ã¹ã«å²ãå½ã¦ãããªãã¨ããä¸å¹³çãªç¶æ
ã解æ¶ããããã®æ©æ§ã§ãã
ãã®ãããªç¶æ
ã解æ¶ããããã«APFã§ã¯APFè¦å ã¨ããå¤æ°ããã¹ã-ã²ã¹ãéã®å
±æã¡ã¢ãªã«ç½®ãã¦ã¹ã¯ããã¢ã¦ãããã¦ããäºãã²ã¹ãã«éç¥ããã¹ã¯ããã¤ã³I/Oå¦çã¯workqueueã«ã¾ããã¦åã³VMEnterãã¾ããã¡ãªã¿ã«ãã®ã¡ã¢ãªç©ºéã®éç¥ã«ã¯MSRã使ç¨ãã¾ãã
ã²ã¹ãå´(Linuxã«ã¼ãã«ã®å ´åã§ãã)ã¯APFç¨ã®ãã¼ã¸ãã©ã«ããã³ãã©do_async_page_faultã使ãAPFè¦å ãèªãã å¾ãé©æä»ããã»ã¹ã«CPUãªã½ã¼ã¹ãæã渡ãã¾ãã
ã¤ã¾ãã²ã¹ãã®Linuxã«ã¼ãã«ã«æºä»®æ³åçãªã¢ããã¼ããæ½ãããã¤ãã¼ãã¤ã¶ã¨å調ããäºã§ããã®ãããªããã»ã¹ãã£ã¹ããããè¡ã£ã¦ããããã§ããã
ã¹ã¯ããã¤ã³ã®å¦çã¯ããã§è¯ãã¨ãã¦ãã§ã¯å®éã«ã©ã®ãããªãã¹ãç©çã¢ãã¬ã¹(HPA)ã«å¤æãã¦ãã¼ã¸ãå²ãå½ã¦ãã®ãè¦ã¦è¡ãã¾ãããã
static bool try_async_pf(struct kvm_vcpu *vcpu, bool prefault, gfn_t gfn, gva_t gva, pfn_t *pfn, bool write, bool *writable) { struct kvm_memory_slot *slot; bool async; slot = kvm_vcpu_gfn_to_memslot(vcpu, gfn); [*7] ... *pfn = __gfn_to_pfn_memslot(slot, gfn, false, NULL, write, writable); [*9] return false; }
struct kvm_memory_slot *kvm_vcpu_gfn_to_memslot(struct kvm_vcpu *vcpu, gfn_t gfn) { return __gfn_to_memslot(kvm_vcpu_memslots(vcpu), gfn); }
(KVM: include/linux/kvm_host.h)
__gfn_to_memslot(struct kvm_memslots *slots, gfn_t gfn) { return search_memslots(slots, gfn); [*8] }
ã¢ãã¬ã¹å¤æã¯å
ã»ã©ã®try_async_pfå
ã§è¡ããã¦ãã¾ãã
ã¾ã[*7]ã§GFNãmemslotã«å¤æãã¾ããä¸èº«ã¯[*8]ã®search_memslotsã§ãã
search_memslotsã«ããGFNã«è©²å½ããmemslotsãæ¤ç´¢ãã¾ããkvm_memslotsã¯ã¢ãã¬ã¹å¤æã®ãã¼ã¿æ§é ã§ãkvm_memslots->base_gfnããkvm_memslots->base_gfn + kvm_memslots->npagesãå¤æ対象ã®GFNã§ãããããã®ç¯å²ãkvm_memslots->userspace_addrãéå§ã¢ãã¬ã¹ã¨ããç¯å²ã«ååããäºã§GFNãHVAã«å¤æãã¾ãã(qemuã®softmmuã¨åãã§ãã)
[*9]ã®kvm_memslotsã使ã£ãå®éã®å¤æå¦çãè¦ã¦ã¿ã¾ãããã
static unsigned long __gfn_to_hva_many(struct kvm_memory_slot *slot, gfn_t gfn, gfn_t *nr_pages, bool write) { ... return __gfn_to_hva_memslot(slot, gfn); [*11] } pfn_t __gfn_to_pfn_memslot(struct kvm_memory_slot *slot, gfn_t gfn, bool atomic, bool *async, bool write_fault, bool *writable) { unsigned long addr = __gfn_to_hva_many(slot, gfn, NULL, write_fault); [*10] return hva_to_pfn(addr, atomic, async, write_fault, writable); [*12] }
ã¾ãã¯[*10]ã§GFNãHVAã«å¤æãã¦ãã¾ããå®éã®å¦çã¯[*11]ã®__gfn_to_hva_memslotã§ãã
static inline unsigned long __gfn_to_hva_memslot(struct kvm_memory_slot *slot, gfn_t gfn) { return slot->userspace_addr + (gfn - slot->base_gfn) * PAGE_SIZE; }
è¦ãã¾ã¾ã§ãããä¸ã§è¿°ã¹ãååãè¡ã£ã¦ãã¾ããã
ãã¦ãHVAãåºããã®ã§æ¬¡ã¯[*12]ã®hva_to_pfnã§PFNã«å¤æãã¦å®äºã§ãã
static pfn_t hva_to_pfn(unsigned long addr, bool atomic, bool *async, bool write_fault, bool *writable) { struct vm_area_struct *vma; pfn_t pfn = 0; int npages; if (hva_to_pfn_fast(addr, atomic, async, write_fault, writable, &pfn)) [*13] return pfn; npages = hva_to_pfn_slow(addr, async, write_fault, writable, &pfn); [*14] vma = find_vma_intersection(current->mm, addr, addr + 1); [*15] ... return pfn; }
Linuxã«ã¼ãã«ããã«ã¼ãªæ¹ã«ã¯ã馴æã¿ã®é¢æ°ãããã¾ããããã®PFNã¸ã®å¤æã¯ãã¹ãOSã®å¦çãæ®ã©æµç¨ãã¦ããã ããªããLinuxã«ã¼ãã«å
APIããããã使ããã¦ãã¾ãã
[*13]ã®fastå¦çã¯å
é¨ã§__get_user_pages_fastãå¼ã³åºãã¦ããã ãã§ãã[*14]ã®slowãä¼¼ããããªç©ã§ã[*15]ãaddrãã赤é»æ¨ã辿ã£ã¦VMAãåå¾ããã ãã§ããããã¾ã§KVMã®è§£èª¬ã«å°å¿µãããããã®è¾ºãã®ã«ã¼ãã«å
APIã¯æ¢ç¥ã¨ãã¾ãã
ãã¦ããã§PFNã¸ã®å¤æãå®äºãã¾ããã
ã§ã¯å®éã«ãã®PFNãEPTãã¼ãã«ã«ããããã[*6]ã®__direct_mapã®å¦çãè¦ã¦è¡ãã¾ãããã
static int __direct_map(struct kvm_vcpu *vcpu, gpa_t v, int write, int map_writable, int level, gfn_t gfn, pfn_t pfn, bool prefault) { struct kvm_shadow_walk_iterator iterator; struct kvm_mmu_page *sp; int emulate = 0; gfn_t pseudo_gfn; if (!VALID_PAGE(vcpu->arch.mmu.root_hpa)) return 0; for_each_shadow_entry(vcpu, (u64)gfn << PAGE_SHIFT, iterator) { [*16] if (iterator.level == level) { [*17] mmu_set_spte(vcpu, iterator.sptep, ACC_ALL, write, &emulate, level, gfn, pfn, prefault, map_writable); [*18] direct_pte_prefetch(vcpu, iterator.sptep); ++vcpu->stat.pf_fixed; break; } if (!is_shadow_present_pte(*iterator.sptep)) { [*19] u64 base_addr = iterator.addr; base_addr &= PT64_LVL_ADDR_MASK(iterator.level); pseudo_gfn = base_addr >> PAGE_SHIFT; sp = kvm_mmu_get_page(vcpu, pseudo_gfn, iterator.addr, iterator.level - 1, 1, ACC_ALL, iterator.sptep); [*20] link_shadow_page(iterator.sptep, sp, true); [*21] } } return emulate; }
[*16]ã®for_each_shadow_entryãã¯ãã使ããshadowãã¼ã¸ãã¼ãã«ã®åã¬ãã«ãèµ°æ»ãã¦ããã¾ãã[*17]ã§ç¾å¨è¦ã¦ããã¬ãã«ããããã³ã°ãæå®ãããã¬ãã«ã®å ´åã[*18]ã®mmu_set_spte->set_pteã§pfnãã¨ã³ããªã¼ã«è¨å®ãã¾ãã
[*19]ã§ã¯ãããã³ã°ãæå®ããã¦ããªãã¬ãã«ãã¤ã¾ãããã«ãã¼ãã«(level4~2)ãã¾ã ã¨ã³ããªã¼ä¸ã«ãªãå ´å[*20]ã§æ°ãã«ã¨ã³ããªã¼ãä½æã[*21]ã§ã¤ãªãã§ãã¾ãã
6. ãããã«
vCPUã«å§ã¾ãKVMã®åºæ¬çãªæ§é ãè¦ã¦ãã¾ãããæ¬è¨äºã§èª¬æããé¨å以å¤ã§ãä¾ãã°kvm-clockãªã©ã®æºä»®æ³åã¯ããã¯ãVFIOãªã©ã¾ã ã¾ã KVMã«ã¯æ§ã
ãªæ©æ§ãåãã£ã¦ãã¾ãã
ãããã«ã¤ãã¦ããã¤ãæ¸ãããè¯ããªã¼ã¨æãã¤ã¤ã¨ããããä»åã¯ããã¾ã§ã«ãã¦ããã¾ãã
ãããã«ãå®ã¯ãã®è¨äºã¯Linux Advent Calendar 2015 - Qiita17æ¥ç®ã®è¨äºã§ã....
大é
å»ãã¦ãã¾ãç³ã訳ããã¾ããã§ãã....
Reference
- æ¿ããã KVMã®ä»çµã¿
http://pantora.net/datapool/osc2007-spring/v-tomo_koibana2_kvm.pdf
- Architecture of the Kernel-based Virtual Machine (KVM)
http://www.linux-kongress.org/2010/slides/KVM-Architecture-LK2010.pdf
- A small look inside
http://www.linux-kvm.org/page/Small_look_inside
- ãã¤ãã¼ãã¤ã¶ã®ä½ãæ¹
http://syuu1228.github.io/howto_implement_hypervisor/
- Gerald J. Popek, Robert P. Goldberg
Formal Requirements for Virtualizable Third Generation Architectures
http://www.dc.uba.ar/materias/so/2010/verano/descargas/articulos/VM-requirements.pdf
- Sheng Yang(KVM Forum 2008)
Extending KVM with new Intel Virtualization technology
http://www.linux-kvm.org/images/c/c7/KvmForum2008$kdf2008_11.pdf
- Virtualization Architecture & KVM
http://surriel.com/system/files/KVM-Architecture-Chile-2012.pdf
http://www.slideshare.net/ozax86/linux-kvm
http://www.atmarkit.co.jp/flinux/rensai/watch2008/watchmema.html
- Xiao Guangrong LinuxCon'11
KVM MMU Virtualization
https://events.linuxfoundation.org/slides/2011/linuxcon-japan/lcj2011_guangrong.pdf
http://ytliu.info/blog/2014/11/24/shi-shang-zui-xiang-xi-de-kvm-mmu-pagejie-gou-he-yong-fa-jie-xi/
- KVMå°åç¿»è¯æµç¨åEPT页表ç建ç«è¿ç¨
http://blog.csdn.net/lux_veritas/article/details/9284635
- Jun Nakajima(KVM Forum2012)
Enabling Optimized Interrupt/APIC Virtualization in KVM
http://www.linux-kvm.org/images/7/70/2012-forum-nakajima_apicv.pdf
- KVMæ¥è¨ Asynchronous page fault解æ
http://d.hatena.ne.jp/kvm/20110702/1309604602
- NARKIVE(MALINGLIST ARCHIVE) EPT page fault procedure
http://kvm.vger.kernel.narkive.com/8CNlP9QP/ept-page-fault-procedure