KVM內核文檔閱讀筆記

KVM在內核中有豐富的文檔,位置在Documentation/virtual/kvm/linux

00-INDEX:整個目錄的索引及介紹文檔。api

api.txt:KVM用戶空間API,所謂的API主要是經過ioctl來實現的。架構

cpuid.txt:KVM的cpuid相關API。electron

devices/:各類平臺相關設備API。ui

hypercalls.txt:KVM的hypercall介紹,介紹了X86和S390的支持的hypercall詳細信息。spa

locking.txt:介紹了KVM用到的鎖、互斥量。線程

mmu.txt:介紹了Guest X86 MMU功能。blog

msr.txt:X86架構下的MSR用途。索引

nested-vmx.txt:使用X86特有的VMX來更簡潔高效的運行Guest OS。進程

ppc-pv.txt和說90-diag.txt:針對PPC和S390架構的特殊用途,忽略。

review-checklist.txt:KVM相關patch的review檢查列表。

timekeeping.txt:X86架構下時間虛擬化設備。

 

關於KVM API定義的文檔(api.txt)

1.概述

KVM API是一系列用來控制VM各方面的ioctl。它包括三個方面:

System ioctls:這些ioctl用來查詢或者設置影響整個KVM子系統的屬性。而且有一個system ioctl用來建立VM。

VM ioctls:這些ioctl用來查詢或者設置影響整個VM的屬性,其中一個VM ioctl用來建立vcpu。這些VM ioctl只能在用來建立此VM的進程中使用。

vcpu ioctls:這些ioctl用來查詢或者設置可以控制單個vcpu的屬性。這些vcpu ioctl只能在建立此vcpu的線程中使用。

從上面描述,能夠清晰看出三者的層級關係:System ioctls(整個KVM子系統) > VM ioctls(單個VM實體) > vcpu ioctls(單個vcpu)

2.文件描述符

KVM API是圍繞文件描述符展開的。從打開/dev/kvm開始,得到操做整個KVM子系統的句柄,這個句柄用來執行System ioctls。基於此System句柄執行KVM_CREATE_VM能夠建立一個VM的文件句柄,VM句柄用來執行VM ioctls。基於VM句柄的KVM_CREATE_VCPU用來建立一個vcpu句柄。vcpu句柄執行系列vcpu ioctls用來控制vcpu。

常見的文件句柄的特性在KVM並不適用,好比fork操做等。KVM裏面只支持一個VM一個進程,一個vcpu一個線程。

3.API描述

每一個API,即ioctl都包含一些信息,好比能力、所屬架構、類型(System、VM、vcpu)、參數和返回值。這些ioctl很是多,而且龐雜,根據類型分爲3類。有一些特殊架構專有的ioctl活在Architecture中列出,若是通用則是all。

下面重點分析Architecture爲all或者x86,Capability爲basic類型的ioctl。

System ioctls

API值 說明
KVM_GET_API_VERSION 目前狀況下返回值,只有12。若是返回12,表示全部能力爲basic的ioctl都可否使用。其餘值是不被容許的。
KVM_CREATE_VM 建立一個VM,返回的句柄能夠用來控制建立的VM。但此時VM尚未vcpu和memory。
KVM_GET_MSR_INDEX_LIST 返回Guest支持的MSRs
KVM_CHECK_EXTENSION  
KVM_GET_VCPU_MMAP_SIZE 返回運行vcpu的KVM_RUN使用的共享Memory Region大小。
   

VM ioctls

API值 說明
KVM_CAP_CHECK_EXTENSION_VM  
KVM_CREATE_VCPU 在VM裏添加一個vcpu,可是總數不會超過max_cpus。vcpu的id在[0, max_vcpu_id)之間。
KVM_GET_DIRTY_LOG  
KVM_SET_MEMORY_ALIAS  
KVM_CREATE_IRQCHIP  
KVM_GET_IRQCHIP  
KVM_SET_IRQCHIP  
KVM_GET_CLOCK  
KVM_SET_CLOCK  
KVM_GET_VCPU_EVENTS  
KVM_SET_VCPU_EVENTS  
KVM_SET_USER_MEMORY_REGION  
KVM_CAP_ENABLE_CAP_VM 不是全部的能力都被默認打開,可使用此ioctl來擴展。
   

vcpu ioctls

API值 說明
KVM_RUN 用於運行一個Guest的vcpu。
KVM_GET_REGS 返回vcpu的通用寄存器值,經過struct kvm_regs結構體返回,根據不一樣架構有所不一樣。
KVM_SET_REGS 設置vcpu的通用寄存器。
KVM_GET_SREGS 讀取不一樣架構vcpu的特殊寄存器。
KVM_SET_SREGS 設置不一樣架構vcpu的特殊寄存器。
KVM_TRANSLATE  
KVM_INTERRUPT  
KVM_DEBUG_GUEST  
KVM_GET_MSRS  
KVM_SET_MSRS  
KVM_SET_CPUID  
KVM_SET_SIGNAL_MASK  
KVM_GET_FPU 獲取vcpu的FPU狀態。
KVM_SET_FPU 設置vcpu的FPU狀態。
KVM_ENABLE_CAP 不是全部的能力都被默認打開,可使用此ioctl來擴展。
   

 

下面一段代碼很好的詮釋了KVM->VM->vcpu之間的關係:

image

int ret, kvmfd = -1, vmfd = -1, cpufd = -1;

kvmfd = qemu_open("/dev/kvm", O_RDWR);
if (kvmfd < 0) {
    goto err;
}
vmfd = ioctl(kvmfd, KVM_CREATE_VM, 0);
if (vmfd < 0) {
    goto err;
}
cpufd = ioctl(vmfd, KVM_CREATE_VCPU, 0);
if (cpufd < 0) {
    goto err;
}

 

4.kvm_run結構體

KVM Hypercall(hypercalls.txt)

這裏重點關注x86架構下的Hypercall。

KVM hypercall最多支持四個參數,經過rbx、rcx、rdx、rsi。Hypercall調用號經過rax傳遞,返回時rax存放返回值。

通常狀況下,其餘寄存器不參與Hypercall。

適用於X86架構的Hypercall有三個:KVM_HC_VAPIC_POLL_IRQ、KVM_HC_MMU_OP、KVM_HC_KICK_CPU。

Hypercall的定義在include/uapi/linux/kvm_para.h中:

#define KVM_HC_VAPIC_POLL_IRQ        1
#define KVM_HC_MMU_OP            2
#define KVM_HC_FEATURES            3
#define KVM_HC_PPC_MAP_MAGIC_PAGE    4
#define KVM_HC_KICK_CPU            5
#define KVM_HC_MIPS_GET_CLOCK_FREQ    6
#define KVM_HC_MIPS_EXIT_VM        7
#define KVM_HC_MIPS_CONSOLE_OUTPUT    8

KVM_HC_VAPIC_POLL_IRQ:Host檢查關起的中斷。

KVM_HC_MMU_OP:支持MMU操做,好比PTE、flushing TLB、release PT。

KVM_HC_KICK_CPU:喚醒HLT狀態下的vcpu。若是Guest內核模式下一個vcpu等待時間超時而執行HLT指令,另外一個同Guest下vcpu能夠經過觸發KVM_HC_KICK_CPU來喚醒。

這些Hypercall都在kvm_emulate_hypercall中處理:

int kvm_emulate_hypercall(struct kvm_vcpu *vcpu)
{
    unsigned long nr, a0, a1, a2, a3, ret;
    int op_64_bit, r = 1;

    kvm_x86_ops->skip_emulated_instruction(vcpu);

    if (kvm_hv_hypercall_enabled(vcpu->kvm))
        return kvm_hv_hypercall(vcpu);

    nr = kvm_register_read(vcpu, VCPU_REGS_RAX);  Hypercall調用號
    a0 = kvm_register_read(vcpu, VCPU_REGS_RBX);  依次是四個參數
    a1 = kvm_register_read(vcpu, VCPU_REGS_RCX);
    a2 = kvm_register_read(vcpu, VCPU_REGS_RDX);
    a3 = kvm_register_read(vcpu, VCPU_REGS_RSI);

    switch (nr) {
    case KVM_HC_VAPIC_POLL_IRQ:
        ret = 0;
        break;
    case KVM_HC_KICK_CPU:
        kvm_pv_kick_cpu_op(vcpu->kvm, a0, a1);  真正用到的也就是喚醒vcpu,a1表示vcpu的apicid。
        ret = 0;
        break;
    default:
        ret = -KVM_ENOSYS;
        break;
    }
out:
    if (!op_64_bit)
        ret = (u32)ret;
    kvm_register_write(vcpu, VCPU_REGS_RAX, ret);  返回值放在RAX中。
    ++vcpu->stat.hypercalls;
    return r;
}

 

支持Guest的MMU功能(mmu.txt)

X86 KVM的shadow MMU功能提供一個標準的MMU功能給Guest,將Guest的物理地址轉換成Host物理地址。

關於Nested VMX是一種嵌套式虛擬功能,可以使一臺虛擬機具備物理機CPU特性,支持VMX/SVM硬件虛擬化。參考《Nested VMX》。這樣虛擬機可使本身成爲一個Hypervisors,並在其上安裝虛擬機。

所謂Nested就是運行在Guest上的嵌套Guest,Guest做爲Hypervisor。

術語

解釋

PFN

Host page frame number

HPA

Host physical address

HVA

Host virtual address

GFN

Guest page frame number

GPA

Guest physical address

GVA

Guest virtual address

NGPA

Nested guest physical address

NGVA

Nested guest virtual address

PTE

Page table entry

GPTE

Guest page table entry

SPTE

Shadow page table entry

TDP

Two dimensional paging

此處KVM MMU功能主要工做就是配置處理器的MMU以達到將Guest地址轉變到Host。有下面三種不一樣的轉變需求:

-當Guest關閉分頁功能時,將Guest物理地址轉變成Host物理地址。GPA->HPA

-當Guesst使能分頁功能時,將Guest虛擬地址,轉變成Guest物理地址,進而Host物理地址。GVA->GPA->HPA

-當Guest又虛擬化一個嵌套Guest時,將嵌套的Guest虛擬地址轉變成嵌套的物理地址,進而Guest物理地址,最後是Host物理地址。NGVA->NGPA->GPA-HPA

GPA是運行KVM進程的用戶地址空間的一部分。用戶空間定義了Guest地址到用戶空間地址的轉變(GPA->HVA)。兩個不一樣的GPA能夠映射到一個HVA,但反之不成立。

相關文章
相關標籤/搜索