虛擬機中斷


 

 

root@srv6:~# cat /proc/interrupts
           CPU0       CPU1       CPU2       CPU3       CPU4       CPU5       CPU6       CPU7       
  1:          0          0          0          0          0          0          9          0   IO-APIC   1-edge      i8042
  4:        442          0          0          0        319          0          0          0   IO-APIC   4-edge      ttyS0
  6:          0          3          0          0          0          0          0          0   IO-APIC   6-edge      floppy
  8:          0          0          0          0          0          0          0          0   IO-APIC   8-edge      rtc0
  9:          0          0          0          0          0          0          0          0   IO-APIC   9-fasteoi   acpi
 10:          0          4          0          0          0          0          0     317582   IO-APIC  10-fasteoi   virtio2
 11:          0          0          0          0         32          0          0          0   IO-APIC  11-fasteoi   uhci_hcd:usb1
 12:          0          0          0         65          0         16          0          0   IO-APIC  12-edge      i8042
 14:          0          0          0          0          0          0          0          0   IO-APIC  14-edge      ata_piix
 15:          0          0          0          0          0          0          0          0   IO-APIC  15-edge      ata_piix
 24:          0          0          0          0          0          0          0          0   PCI-MSI 49152-edge      virtio0-config
 25:          0          0          0    4891421          0          0          0          0   PCI-MSI 49153-edge      virtio0-input.0
 26:          0          0          0          0          2          1          0          0   PCI-MSI 49154-edge      virtio0-output.0
 27:          0          0          0          0          0          0          0          0   PCI-MSI 65536-edge      virtio1-config
 28:          0          0          0          0          0          0     179869          0   PCI-MSI 65537-edge      virtio1-req.0
NMI:          0          0          0          0          0          0          0          0   Non-maskable interrupts
LOC:   17533085   83660013    3207952    3922778    3037160    2753279    2791938    3814434   Local timer interrupts
SPU:          0          0          0          0          0          0          0          0   Spurious interrupts
PMI:          0          0          0          0          0          0          0          0   Performance monitoring interrupts
IWI:          0          0          0          0          1          0          1          0   IRQ work interrupts
RTR:          0          0          0          0          0          0          0          0   APIC ICR read retries
RES:     450707     460430    1035940    1314558    1113145    1272738    1196648    1030938   Rescheduling interrupts
CAL:      28063      34379      43082      31749      49105      50540      15045      36975   Function call interrupts
TLB:         89         34        234         56        162        113         43         46   TLB shootdowns
TRM:          0          0          0          0          0          0          0          0   Thermal event interrupts
THR:          0          0          0          0          0          0          0          0   Threshold APIC interrupts
DFR:          0          0          0          0          0          0          0          0   Deferred Error APIC interrupts
MCE:          0          0          0          0          0          0          0          0   Machine check exceptions
MCP:      10203      10203      10203      10203      10203      10203      10203      10203   Machine check polls
HYP:          0          0          0          0          0          0          0          0   Hypervisor callback interrupts
ERR:          0
MIS:          0
PIN:          0          0          0          0          0          0          0          0   Posted-interrupt notification event
NPI:          0          0          0          0          0          0          0          0   Nested posted-interrupt event
PIW:          0          0          0          0          0          0          0          0   Posted-interrupt wakeup event
root@srv6:~# 

目前操作系統使用的中斷有io apic,MSI,還有就是NMI,LOC等。 繼續執行cat /proc/ioports | grep pic

root@srv6:~# cat /proc/ioports | grep pic
  0020-0021 : pic1
  00a0-00a1 : pic2

實際上,主板上來連接這兩顆pic設備,也就是兩個i8259,分別是i8259 master和i8259 slave。對它們的訪問使用pio(port io)即可。當guest訪問8259的時候,vm會發生exit,虛擬的i8259響應guest的操作即可。 繼續執行/proc/iomem | grep -i apic

root@srv6:~# cat /proc/iomem | grep -i apic
fec00000-fec003ff : IOAPIC 0
fee00000-fee00fff : Local APIC
root@srv6:~# 

 

apic設備的訪問可以使用mmio(memory mapped io)的方式進行。同時,如果支持x2apic也可以使用msr訪問apic。 那么中斷設備的關系如下:

i8259每個pic可以產生8中irq,所以master產生的irq是從0-7,slave是8-15。 以i8042 kbd為例,在qemu-kvm的場景下,它會產生irq 1,所以它是由i8259 master產生的。 I8042 mouse,產生的irq是12,所以是i8259 slave產生的。 vda是virtio-blk,它是一個pci設備,它的中斷是msi信號。 net0也是pci設備,中斷也是msi信號。 2,irq routing 物理環境上,irq是由硬件產生的。在虛擬化下,卻是由虛擬出來的。 kvm提供了set irq line這ioctl給user-mode調用,也提供了kvm_vm_ioctl_irq_line這樣的函數在kernel-mode使用。 以鍵盤i8042 kbd為例,如果模擬一個鍵盤中斷,即set irq 1.

虛擬觸發了irq 1,那么需要經過irq routing: irq 1在0-7的范圍內,所以會路由到i8259 master,隨后i8259 master會向vCPU注入中斷。 同時,irq 1也會路由到io apic一份,io apic也會向lapic繼續delivery。lapic繼續向vCPU注入中斷。 linux在啟動階段,檢查到io apic后,會選擇使用io apic。盡管經過irq routing產生了i8259 master和io apic兩個中斷,但是Linux選擇io apic上的中斷。 同理,如果是virtio-blk產生了中斷,則路由到msi處理。

3,apicv vcpu在setup階段,如果支持apicv技術,將會配置EOI exit bitmap和posted interrupt等寄存器。在posted interrupt技術下,可以在不發生vm exit的情況注入中斷,提高虛擬機的性能。 相關文檔在intel的開發文檔中《29.6 POSTED-INTERRUPT PROCESSING》中,對應的代碼在linux-4.4/arch/x86/kvm/vmx.c中。

4,pv eoi 經過posted interrupt技術優化后,注入irq不發生vm exit。但是guest在應答irq的時候,還是要發生vm exit。 所以有了pv eoi技術: a,guest和host通過msr寄存器,協商出來一個地址,用作eoi使用。前提是host和guest都支持pv eoi。 b,guest的pa,和host的va,通過映射計算,就是在操作同一塊內存。 那么就不用發生vm exit的情況下,達到guest和host內外通信的目的。同樣原理的還有kvm clock、steal time等。

5,i8254 i8254就是irq 0的timer。在qemu-kvm中,可以通過用戶態qemu實現,也可以通過內核態實現。默認使用內核態的實現。在host上執行ps,就可以看到[kvm-pit/1234]類似的進程,后面的數字是qemu進程的pid。kvm-pit線程是一個kthread worker線程,周期性的執行set irq 0,就可以在guest中周期性的發生中斷。 Linux如果檢測到LOC timer,就會選擇停止i8254。停止i8254,kvm-pit停止周期性的注入中斷,但是並不會退出。所以看到kvm-pit內核線程,但是它並不一定是在工作的。 如果在內核中加log,就會發現有趣的現象:在vm啟動階段,會有大量的注入中斷的log;linux啟動過程中,log就停止了。

 

 

 

 

 

 

qemu通過kvm的ioctl命令KVM_CREATE_IRQCHIP調用到kvm內核模塊中,在內核模塊中創建和初始化PIC/IOAPIC設備(創建設備對應的數據結構並將設備注冊到總線上)。

kvm_arch_vm_ioctl(s, KVM_CREATE_IRQCHIP)
    |--> kvm_pic_init                    /* i8259 初始化 */
    |--> kvm_ioapic_init                 /* ioapic 初始化 */
    |--> kvm_setup_default_irq_routing   /* 初始化缺省的IRE */

qemu在kvm內核中創建完成PIC和IOAPIC后將全局變量kvm_kernel_irqchip置為true,kvm模塊則將kvm->arch.irqchip_mode 賦值為 KVM_IRQCHIP_KERNEL,這樣后面的kvm_irqchip_in_kernel返回true表示pic芯片放到kvm內核模塊中實現,kvm_ioapic_in_kernel也返回true表示ioapic放到kvm中來模擬。

中斷處理的邏輯放在kvm內核模塊中進行實現,但設備的模擬呈現還是需要qemu設備模擬器來搞定,最后qemu和kvm一起配合完成快速中斷處理的流程。

i8259的設備創建流程(pic還是傳統的isa設備,中斷是邊沿觸發的,master的i/o port為0x20,0x21 slave的i/o port為0xa0,0xa1):

machine_run_board_init
    |--> pc_init1
        |--> if (kvm_pic_in_kernel())
            |--> kvm_i8259_init
                |--> isadev = isa_create(bus, name)

ioapic的設備創建流程:

machine_run_board_init
    |--> pc_init1
        |--> if (pcmc->pci_enabled)
            |--> ioapic_init_gsi(gsi_state, "i440fx")
                |--> if kvm_ioapic_in_kernel()
                    |--> dev = qdev_create(NULL, "kvm-ioapic")

PIC由2個i8259進行“級聯”,一個為master一個為slave,每個i8259有8個PIN(salve的INT輸出線連接到master的IRQ2引腳上,所以實際可用的IRQ數目為15)。目前kvm只為虛擬機創建一個ioapic設備(現在多路服務器可能有多個ioapic設備),ioapic設備提供24個PIN給外部中斷使用。在IRQ路由上 0-15號GSI為PIC和IOAPIC共用的,16-23號GSI則都分配給ioapic。

幾個概念要理清楚:IRQ號,中斷向量和GSI。

  • IRQ號是PIC時代引入的概念,由於ISA設備通常是直接連接到到固定的引腳,所以對於IRQ號描述了設備連接到了PIC的哪個引腳上,同IRQ號直接和中斷優先級相關,例如IRQ0比IRQ3的中斷優先級更高。
  • GSI號是ACPI引入的概念,全稱是Global System Interrupt,用於為系統中每個中斷源指定一個唯一的中斷編號。注:ACPI Spec規定PIC的IRQ號必須對應到GSI0-GSI15上。kvm默認支持最大1024個GSI。
  • 中斷向量是針對邏輯CPU的概念,用來表示中斷在IDT表的索引號,每個IRQ(或者GSI)最后都會被定向到某個Vecotor上。對於PIC上的中斷,中斷向量 = 32(start vector) + IRQ號。在IOAPIC上的中斷被分配的中斷向量則是由操作系統分配。

PIC主要針對與傳統的單核處理器體系結構,在SMP系統上則是通過IOAPIC和每個CPU內部的LAPIC來構成整個中斷系統的。

如上圖所描述,IOAPIC 負責接受中斷並將中斷格式化化成中斷消息,並按照一定規則轉發給LAPIC。LAPIC內部有IRR(Interrupt Reguest Register)和ISR(Interrupt Service Register)等2個重要寄存器。系統在處理一個vector的同時緩存着一個相同的vector,vector通過2個256-bit寄存器標志,對應位置位則表示上報了vector請求或者正在處理中。另外LAPIC提供了TPR(Task Priority Register),PPR(Processor Priority Register)來設置LAPIC的task優先級和CPU的優先級,當IOAPIC轉發的終端vector優先級小於LAPIC設置的TPR時,此中斷不能打斷當前cpu上運行的task;當中斷vector的優先級小於LAPIC設置的PPR時此cpu不處理這個中斷。操作系統通過動態設置TPR和PPR來實現系統的實時性需求和中斷負載均衡。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM