1、x86平台主要使用的中斷型別有pic、apic及msi中斷,在多核系統下的apic結構圖如下所示,每個cpu有乙個lapic,外部中斷通過ioapic**到lapic,如果是msi中斷,則繞過了io apic直接發給lapic。
2、kvm初始化過程為每個虛擬機器維護乙個pic主控制器、乙個pic備控制器以及乙個ioapic控制器,每個vcpu維護乙個lapic控制器。同時每個虛擬機器有一張中斷路由表(kvm_irq_routing_table)。中斷路由表裡的chip二維陣列儲存非msi中斷的gsi號,每個中斷都有自己的routing_entry,routing_entry儲存了中斷的型別(pci、ioapic、msi)、中斷號、以及set觸發函式,所有的routing_entry以gsi為索引資訊掛接到route_table的map煉表裡(可能同乙個中斷號會同時關聯pic、ioapic兩種中斷type)。
ioapic裡還維護了一張中斷重對映表(redirtbl),負責為每個ioapic引腳(總共24個引腳)收到的中斷選擇路由到哪個lapic,每個vcpu的lapic控制器則模擬了主要的apic暫存器(irr、isr、eoi)。
3、中斷路由表初始過程
kvm建立好pci、ioapic控制器後,會先使用default_routing(kvm/irq_common.c)安裝預設的中斷路由表。
kvm_arch_vm_ioctl
kvm_create_pic
kvm_ioapic_init
kvm_setup_default_irq_routing
kvm_set_irq_routing
setup_routing_entry
setup_routing_entry的ue引數即為default_routing,以上的流程主要就是將default_routing定義的路由資訊儲存到routing_table裡,default_routing初始化定義了0-24號中斷的基本資訊,如中斷type(都是非msi的irqchip型別,包括pic、ioapic),中斷gsi號等。中斷路由表除了初始化安裝外,還可以通過kvm_set_gsi_routing重新安裝。static int setup_routing_entry(struct kvm *kvm,
struct kvm_irq_routing_table *rt,
struct kvm_kernel_irq_routing_entry *e,
const struct kvm_irq_routing_entry *ue)
int kvm_set_routing_entry(struct kvm *kvm,
struct kvm_kernel_irq_routing_entry *e,
const struct kvm_irq_routing_entry *ue)
e->irqchip.irqchip = ue->u.irqchip.irqchip;
e->irqchip.pin = ue->u.irqchip.pin + delta;
if (e->irqchip.pin >= max_pin)
goto out;
break;
case kvm_irq_routing_msi:
e->set = kvm_set_msi;
e->msi.address_lo = ue->u.msi.address_lo;
e->msi.address_hi = ue->u.msi.address_hi;
e->msi.data = ue->u.msi.data;
if (kvm_msi_route_invalid(kvm, e))
goto out;
break;
default:
goto out;
} r = 0;
out:
return r;
}
4、中斷觸發流程#define ioapic_routing_entry(irq) \
}#define routing_entry1(irq) ioapic_routing_entry(irq)
#define pic_routing_entry(irq) \
}#define routing_entry2(irq) \
ioapic_routing_entry(irq), pic_routing_entry(irq)
static const struct kvm_irq_routing_entry default_routing = ;
當vfio或vhost等後端通過eventfd喚醒kvm中斷處理函式後,會進入irqfd_inject,然後呼叫kvm_set_irq,kvm_set_irq主要是查詢中斷路由表,找到中斷對應的routing_entry,然後呼叫其set觸發函式,如果是ioapic型別的中斷,則會呼叫kvm_set_ioapic_irq,最後進入ioapic_service處理函式。ioapic_service主要是找到中斷的重對映表,然後查詢中斷的目的地資訊並**到對應vcpu的lapic去處理。
lapic收到中斷後,會根據不同的delivery_mode呼叫不同的處理函式,以常見的apic_dm_fixed為例,處理函式還會判斷是否啟用apicv功能,使用apicv和不使用apicv走不同的觸發流程。static int ioapic_service(struct kvm_ioapic *ioapic, int irq, bool line_status)
else
ret = kvm_irq_delivery_to_apic(ioapic->kvm, null, &irqe, null);
if (ret && irqe.trig_mode == ioapic_level_trig)
entry->fields.remote_irr = 1;
return ret;
}
1)、如果使能了apicv,最終呼叫vmx_deliver_posted_interrupt,使用中斷posting的方式來通知vcpu處理中斷。static int __apic_accept_irq(struct kvm_lapic *apic, int delivery_mode,
int vector, int level, int trig_mode,
struct dest_map *dest_map)
//判斷是否使用apicv
if (vcpu->arch.apicv_active)
kvm_x86_ops->deliver_posted_interrupt(vcpu, vector);
else
break;
}
2)、如果沒有使能apicv功能,則標記lapic的irr暫存器,通過kvm_make_request標記vcpu有中斷請求事件,然後觸發vcpu vm-exit。當vcpu重新回到guest模式時,會檢查是否有中斷請求事件,如果有,則設定isr、ppr等暫存器資訊。static void vmx_deliver_posted_interrupt(struct kvm_vcpu *vcpu, int vector)
vcpu_enter_guest
inject_pending_event
kvm_cpu_get_interrupt
kvm_get_apic_interrupt
kvm_queue_interrupt
最後再呼叫vmx_inject_irq將之前儲存在kvm_queued_interrupt的中斷資訊寫到vmcs的vm_entry_intr_info_field,等vcpu執行vm_entry時,就能感知到該中斷的存在。int kvm_get_apic_interrupt(struct kvm_vcpu *vcpu)
static void vmx_inject_irq(struct kvm_vcpu *vcpu)
intr = irq | intr_info_valid_mask;
if (vcpu->arch.interrupt.soft) else
intr |= intr_type_ext_intr;
vmcs_write32(vm_entry_intr_info_field, intr);
linux虛擬化之KVM虛擬化 kvm的安裝
kvm 是基於核心的虛擬機器 kernel based virtual machine 它是linux 的乙個核心模組,核心版本需要在linux 2.6.20以上。kvm虛擬機器是基於硬體輔助虛擬化技術。目前支援這個功能的應用為qume。所以qume和kvm虛擬機器磁碟的指令相關。ps 後面提到這個...
KVM 記憶體虛擬化
除了 cpu 虛擬化,另乙個關鍵是記憶體虛擬化,通過記憶體虛擬化共享物理系統記憶體,動態分配給虛擬機器。虛擬機器的記憶體虛擬化很象現在的作業系統支援的虛擬記憶體方式,應用程式看到鄰近的記憶體位址空間,這個位址空間無需和下面的物理機器記憶體直接對應,作業系統保持著虛擬頁到物理頁的對映。現在所有的 x8...
KVM 記憶體虛擬化
dev kvm 裝置 虛擬化分為軟體虛擬化和硬體虛擬化,而且遵循 intercept 和 virtualize 的規律。記憶體虛擬化也分為基於軟體的記憶體虛擬化和硬體輔助的記憶體虛擬化,其中,常用的基於軟體的記憶體虛擬化技術為 影子頁表 技術,硬體輔助記憶體虛擬化技術為 intel 的 ept ex...