読者です 読者をやめる 読者になる 読者になる

BHyVeビルドめも25

DragonFlyBSD BHyVe

φ(・ω・ )かきかき
vmm_ipi.cのipiの初期化、終了処理あたりを片付けました。
FreeBSDのidtを"FreeBSDのidtめも"で調べたところ、すべてのcpuはidtとしてidt0を参照するようになっていたのですが、DragonFly BSDは一応cpu毎に違う変数を使っているのを考慮しつつこんな感じにしました。

diff --git a/sys/platform/pc64/vmm/vmm_ipi.c b/sys/platform/pc64/vmm/vmm_ipi.c
index c8cf4ee..becccf7 100644
--- a/sys/platform/pc64/vmm/vmm_ipi.c
+++ b/sys/platform/pc64/vmm/vmm_ipi.c
@@ -45,15 +45,23 @@
 
 extern inthand_t IDTVEC(rsvd), IDTVEC(justreturn);
 
+/* 
+ * DragonFly BSD dosen't have IPI_AST, APIC_IPI_INTS and
+ * APIC_SPURIOUS_INT so define them here.
+ */
+#define IPI_AST 0
+#define APIC_IPI_INTS 243
+#define APIC_SPURIOUS_INT 255
+
 /*
  * The default is to use the IPI_AST to interrupt a vcpu.
  */
-static int ipinum = IPI_AST;
+static int ipinum[MAXCPU] = { IPI_AST };
 
 CTASSERT(APIC_SPURIOUS_INT == 255);
 
-void
-vmm_ipi_init(void)
+static void
+vmm_ipi_init2(int cpu)
 {
 	int idx;
 	uintptr_t func;
@@ -70,27 +78,42 @@ vmm_ipi_init(void)
 	 */
 	idx = APIC_SPURIOUS_INT;
 	while (--idx >= APIC_IPI_INTS) {
-		ip = &idt[idx];
+		ip = &idt_arr[cpu][idx];
 		func = ((long)ip->gd_hioffset << 16 | ip->gd_looffset);
 		if (func == (uintptr_t)&IDTVEC(rsvd)) {
-			ipinum = idx;
-			setidt(ipinum, IDTVEC(justreturn), SDT_SYSIGT,
-			       SEL_KPL, 0);
+			ipinum[cpu] = idx;
+			setidt(ipinum[cpu], IDTVEC(justreturn), SDT_SYSIGT,
+			       SEL_KPL, 0, cpu);
 			break;
 		}
 	}
 	
-	if (ipinum != IPI_AST && bootverbose) {
+	if (ipinum[cpu] != IPI_AST && bootverbose) {
 		kprintf("vmm_ipi_init: installing ipi handler to interrupt "
-		       "vcpus at vector %d\n", ipinum);
+		       "vcpus at vector %d\n", ipinum[cpu]);
+	}
+}
+
+void
+vmm_ipi_init(void)
+{
+	int cpu;
+
+	/* ncpus come from sys/ststm.h */
+	for (cpu = 0; cpu < ncpus; cpu++) {
+		vmm_ipi_init2(cpu);
 	}
 }
 
 void
 vmm_ipi_cleanup(void)
 {
-	if (ipinum != IPI_AST)
-		setidt(ipinum, IDTVEC(rsvd), SDT_SYSIGT, SEL_KPL, 0);
+	int cpu;
+	
+	for (cpu = 0; cpu < ncpus; cpu++) {
+		if (ipinum[cpu] != IPI_AST)
+			setidt(ipinum[cpu], IDTVEC(rsvd), SDT_SYSIGT, SEL_KPL, 0, cpu);
+	}
 }
 
 void

まずはipinumをMAXCPU個の配列にしてしまいます。これはsys/cpu/x86_64/include/param.hで定義されててSMP環境だと63です。

#define SMP_MAXCPU	63
#ifdef SMP
#define MAXCPU		SMP_MAXCPU
#else
#define MAXCPU		1
#endif /* SMP */

ホントはこんなにいらなくて実際のcpu数(ncpus)あれば良いのですが、これはsmpとかの初期化が終わらないと分からない数なので配列としてはMAXCPU個だけど、実際に使うのはncpus個分だけです。

vmm_ipi_init2()は元vmm_ipi_init()でこれはidtをidtの配列のidt_arrに変えて、cpu番目のidtから開いてるところを探して~と言う風に変更してます。新しく作ったvmm_ipi_init()はncpus個分のループをしつつvmm_ipi_init2()を呼ぶ感じです。
別に新しく関数作る理由は特にないのですが、ループ1個分のインデントが深くなるのもアレかなという程度の気持ちで関数追加を選んでます。

そして残すは実際にcpu間で割り込みする部分のipi_cpu()はdfbsdだとどんな関数を使うのかとかを調べて何とかすれば良いわけですね!

void
vm_interrupt_hostcpu(struct vm *vm, int vcpu)
{
	int hostcpu;

	if (vcpu_is_running(vm, vcpu, &hostcpu) && hostcpu != mycpuid)
		ipi_cpu(hostcpu, ipinum);
}