在真實的硬體,外部的中段會由中斷控制如 (PIC/APIC)觸發,而 CPU 接受到中斷請求後將會根據暫存器的配置,查找該引腳對應的中斷向量號,並調轉到目標處理函式。
在 KVM 中,為了讓 Guest 能接收外部中斷,我們可以透過 KVM 建立一組虛擬中斷控制器,然後從 Host 端「拉高/拉低」某條中斷線以觸發中斷事件。
Creates an interrupt controller model in the kernel. On x86, creates a virtual ioapic, a virtual PIC (two PICs, nested), and sets up future vcpus to have a local APIC.
文字擷取自 The Definitive KVM (Kernel-based Virtual Machine) API Documentation
透過 KVM_CREATE_IRQCHIP,KVM 就把「中斷硬體」幫我們接上。
KVM_IRQ_LINE:同步控制引腳電平(1=assert,0=deassert),用來模擬 edge/level。
KVM_IRQFD:把一個 eventfd 綁到 GSI,寫入 eventfd 即觸發中斷(今天先不展開)
struct irq_manager {
int vmfd;
int irqchip_ready;
};
static int create_irqchip(struct irq_manager *mgr)
{
if (!mgr) {
errno = EINVAL;
return -1;
}
if (mgr->irqchip_ready) {
return 0;
}
if (ioctl(mgr->vmfd, KVM_CREATE_IRQCHIP, 0) < 0) {
if (errno == EEXIST) {
mgr->irqchip_ready = 1;
return 0;
}
return -1;
}
mgr->irqchip_ready = 1;
return 0;
}
int irq_manager_init(struct irq_manager *mgr, int vmfd)
{
if (!mgr || vmfd < 0) {
errno = EINVAL;
return -1;
}
memset(mgr, 0, sizeof(*mgr));
mgr->vmfd = vmfd;
if (create_irqchip(mgr) < 0) {
return -1;
}
return 0;
}
在這裡抽象了一個 irq_manager 用於管理 irq 相關的事件。這個結構體 struct
irq_manager 將記錄此 VM 的檔案描述符以及是否建立 irqchip 。而 irq_manager_init 將調用 create_irqchip 建立中斷控制器。
需要注意的是當建立中斷控制器後 HLT 將不會觸發 VM-exit (KVM_EXIT_HLT),這是因為 HLT 是讓 CPU 進入低功耗模式同時等待中斷喚醒。