Răsfoiți Sursa

Merge branch 'kvm-updates/2.6.28' of git://git.kernel.org/pub/scm/linux/kernel/git/avi/kvm

* 'kvm-updates/2.6.28' of git://git.kernel.org/pub/scm/linux/kernel/git/avi/kvm:
  KVM: ia64: Makefile fix for forcing to re-generate asm-offsets.h
  KVM: Future-proof device assignment ABI
  KVM: ia64: Fix halt emulation logic
  KVM: Fix guest shared interrupt with in-kernel irqchip
  KVM: MMU: sync root on paravirt TLB flush
Linus Torvalds 16 ani în urmă
părinte
comite
a186576925

+ 5 - 1
arch/ia64/include/asm/kvm_host.h

@@ -365,7 +365,8 @@ struct kvm_vcpu_arch {
 	long itc_offset;
 	long itc_offset;
 	unsigned long itc_check;
 	unsigned long itc_check;
 	unsigned long timer_check;
 	unsigned long timer_check;
-	unsigned long timer_pending;
+	unsigned int timer_pending;
+	unsigned int timer_fired;
 
 
 	unsigned long vrr[8];
 	unsigned long vrr[8];
 	unsigned long ibr[8];
 	unsigned long ibr[8];
@@ -417,6 +418,9 @@ struct kvm_arch {
 	struct list_head assigned_dev_head;
 	struct list_head assigned_dev_head;
 	struct dmar_domain *intel_iommu_domain;
 	struct dmar_domain *intel_iommu_domain;
 	struct hlist_head irq_ack_notifier_list;
 	struct hlist_head irq_ack_notifier_list;
+
+	unsigned long irq_sources_bitmap;
+	unsigned long irq_states[KVM_IOAPIC_NUM_PINS];
 };
 };
 
 
 union cpuid3_t {
 union cpuid3_t {

+ 6 - 2
arch/ia64/kvm/Makefile

@@ -29,13 +29,18 @@ define cmd_offsets
 	 echo ""; \
 	 echo ""; \
 	 echo "#endif" ) > $@
 	 echo "#endif" ) > $@
 endef
 endef
+
 # We use internal rules to avoid the "is up to date" message from make
 # We use internal rules to avoid the "is up to date" message from make
-arch/ia64/kvm/asm-offsets.s: arch/ia64/kvm/asm-offsets.c
+arch/ia64/kvm/asm-offsets.s: arch/ia64/kvm/asm-offsets.c \
+			$(wildcard $(srctree)/arch/ia64/include/asm/*.h)\
+			$(wildcard $(srctree)/include/linux/*.h)
 	$(call if_changed_dep,cc_s_c)
 	$(call if_changed_dep,cc_s_c)
 
 
 $(obj)/$(offsets-file): arch/ia64/kvm/asm-offsets.s
 $(obj)/$(offsets-file): arch/ia64/kvm/asm-offsets.s
 	$(call cmd,offsets)
 	$(call cmd,offsets)
 
 
+FORCE : $(obj)/$(offsets-file)
+
 #
 #
 # Makefile for Kernel-based Virtual Machine module
 # Makefile for Kernel-based Virtual Machine module
 #
 #
@@ -53,7 +58,6 @@ endif
 kvm-objs := $(common-objs) kvm-ia64.o kvm_fw.o
 kvm-objs := $(common-objs) kvm-ia64.o kvm_fw.o
 obj-$(CONFIG_KVM) += kvm.o
 obj-$(CONFIG_KVM) += kvm.o
 
 
-FORCE : $(obj)/$(offsets-file)
 EXTRA_CFLAGS_vcpu.o += -mfixed-range=f2-f5,f12-f127
 EXTRA_CFLAGS_vcpu.o += -mfixed-range=f2-f5,f12-f127
 kvm-intel-objs = vmm.o vmm_ivt.o trampoline.o vcpu.o optvfault.o mmio.o \
 kvm-intel-objs = vmm.o vmm_ivt.o trampoline.o vcpu.o optvfault.o mmio.o \
 	vtlb.o process.o
 	vtlb.o process.o

+ 41 - 39
arch/ia64/kvm/kvm-ia64.c

@@ -385,6 +385,7 @@ static int handle_global_purge(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
 	struct kvm *kvm = vcpu->kvm;
 	struct kvm *kvm = vcpu->kvm;
 	struct call_data call_data;
 	struct call_data call_data;
 	int i;
 	int i;
+
 	call_data.ptc_g_data = p->u.ptc_g_data;
 	call_data.ptc_g_data = p->u.ptc_g_data;
 
 
 	for (i = 0; i < KVM_MAX_VCPUS; i++) {
 	for (i = 0; i < KVM_MAX_VCPUS; i++) {
@@ -418,33 +419,41 @@ int kvm_emulate_halt(struct kvm_vcpu *vcpu)
 	ktime_t kt;
 	ktime_t kt;
 	long itc_diff;
 	long itc_diff;
 	unsigned long vcpu_now_itc;
 	unsigned long vcpu_now_itc;
-
 	unsigned long expires;
 	unsigned long expires;
 	struct hrtimer *p_ht = &vcpu->arch.hlt_timer;
 	struct hrtimer *p_ht = &vcpu->arch.hlt_timer;
 	unsigned long cyc_per_usec = local_cpu_data->cyc_per_usec;
 	unsigned long cyc_per_usec = local_cpu_data->cyc_per_usec;
 	struct vpd *vpd = to_host(vcpu->kvm, vcpu->arch.vpd);
 	struct vpd *vpd = to_host(vcpu->kvm, vcpu->arch.vpd);
 
 
-	vcpu_now_itc = ia64_getreg(_IA64_REG_AR_ITC) + vcpu->arch.itc_offset;
+	if (irqchip_in_kernel(vcpu->kvm)) {
 
 
-	if (time_after(vcpu_now_itc, vpd->itm)) {
-		vcpu->arch.timer_check = 1;
-		return 1;
-	}
-	itc_diff = vpd->itm - vcpu_now_itc;
-	if (itc_diff < 0)
-		itc_diff = -itc_diff;
+		vcpu_now_itc = ia64_getreg(_IA64_REG_AR_ITC) + vcpu->arch.itc_offset;
 
 
-	expires = div64_u64(itc_diff, cyc_per_usec);
-	kt = ktime_set(0, 1000 * expires);
-	vcpu->arch.ht_active = 1;
-	hrtimer_start(p_ht, kt, HRTIMER_MODE_ABS);
+		if (time_after(vcpu_now_itc, vpd->itm)) {
+			vcpu->arch.timer_check = 1;
+			return 1;
+		}
+		itc_diff = vpd->itm - vcpu_now_itc;
+		if (itc_diff < 0)
+			itc_diff = -itc_diff;
+
+		expires = div64_u64(itc_diff, cyc_per_usec);
+		kt = ktime_set(0, 1000 * expires);
+
+		down_read(&vcpu->kvm->slots_lock);
+		vcpu->arch.ht_active = 1;
+		hrtimer_start(p_ht, kt, HRTIMER_MODE_ABS);
 
 
-	if (irqchip_in_kernel(vcpu->kvm)) {
 		vcpu->arch.mp_state = KVM_MP_STATE_HALTED;
 		vcpu->arch.mp_state = KVM_MP_STATE_HALTED;
 		kvm_vcpu_block(vcpu);
 		kvm_vcpu_block(vcpu);
 		hrtimer_cancel(p_ht);
 		hrtimer_cancel(p_ht);
 		vcpu->arch.ht_active = 0;
 		vcpu->arch.ht_active = 0;
 
 
+		if (test_and_clear_bit(KVM_REQ_UNHALT, &vcpu->requests))
+			if (vcpu->arch.mp_state == KVM_MP_STATE_HALTED)
+				vcpu->arch.mp_state =
+					KVM_MP_STATE_RUNNABLE;
+		up_read(&vcpu->kvm->slots_lock);
+
 		if (vcpu->arch.mp_state != KVM_MP_STATE_RUNNABLE)
 		if (vcpu->arch.mp_state != KVM_MP_STATE_RUNNABLE)
 			return -EINTR;
 			return -EINTR;
 		return 1;
 		return 1;
@@ -484,10 +493,6 @@ static int (*kvm_vti_exit_handlers[])(struct kvm_vcpu *vcpu,
 static const int kvm_vti_max_exit_handlers =
 static const int kvm_vti_max_exit_handlers =
 		sizeof(kvm_vti_exit_handlers)/sizeof(*kvm_vti_exit_handlers);
 		sizeof(kvm_vti_exit_handlers)/sizeof(*kvm_vti_exit_handlers);
 
 
-static void kvm_prepare_guest_switch(struct kvm_vcpu *vcpu)
-{
-}
-
 static uint32_t kvm_get_exit_reason(struct kvm_vcpu *vcpu)
 static uint32_t kvm_get_exit_reason(struct kvm_vcpu *vcpu)
 {
 {
 	struct exit_ctl_data *p_exit_data;
 	struct exit_ctl_data *p_exit_data;
@@ -600,8 +605,6 @@ static int __vcpu_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
 
 
 again:
 again:
 	preempt_disable();
 	preempt_disable();
-
-	kvm_prepare_guest_switch(vcpu);
 	local_irq_disable();
 	local_irq_disable();
 
 
 	if (signal_pending(current)) {
 	if (signal_pending(current)) {
@@ -614,7 +617,7 @@ again:
 
 
 	vcpu->guest_mode = 1;
 	vcpu->guest_mode = 1;
 	kvm_guest_enter();
 	kvm_guest_enter();
-
+	down_read(&vcpu->kvm->slots_lock);
 	r = vti_vcpu_run(vcpu, kvm_run);
 	r = vti_vcpu_run(vcpu, kvm_run);
 	if (r < 0) {
 	if (r < 0) {
 		local_irq_enable();
 		local_irq_enable();
@@ -634,9 +637,8 @@ again:
 	 * But we need to prevent reordering, hence this barrier():
 	 * But we need to prevent reordering, hence this barrier():
 	 */
 	 */
 	barrier();
 	barrier();
-
 	kvm_guest_exit();
 	kvm_guest_exit();
-
+	up_read(&vcpu->kvm->slots_lock);
 	preempt_enable();
 	preempt_enable();
 
 
 	r = kvm_handle_exit(kvm_run, vcpu);
 	r = kvm_handle_exit(kvm_run, vcpu);
@@ -673,6 +675,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
 
 
 	if (unlikely(vcpu->arch.mp_state == KVM_MP_STATE_UNINITIALIZED)) {
 	if (unlikely(vcpu->arch.mp_state == KVM_MP_STATE_UNINITIALIZED)) {
 		kvm_vcpu_block(vcpu);
 		kvm_vcpu_block(vcpu);
+		clear_bit(KVM_REQ_UNHALT, &vcpu->requests);
 		vcpu_put(vcpu);
 		vcpu_put(vcpu);
 		return -EAGAIN;
 		return -EAGAIN;
 	}
 	}
@@ -778,6 +781,9 @@ static void kvm_init_vm(struct kvm *kvm)
 	kvm_build_io_pmt(kvm);
 	kvm_build_io_pmt(kvm);
 
 
 	INIT_LIST_HEAD(&kvm->arch.assigned_dev_head);
 	INIT_LIST_HEAD(&kvm->arch.assigned_dev_head);
+
+	/* Reserve bit 0 of irq_sources_bitmap for userspace irq source */
+	set_bit(KVM_USERSPACE_IRQ_SOURCE_ID, &kvm->arch.irq_sources_bitmap);
 }
 }
 
 
 struct  kvm *kvm_arch_create_vm(void)
 struct  kvm *kvm_arch_create_vm(void)
@@ -941,9 +947,8 @@ long kvm_arch_vm_ioctl(struct file *filp,
 			goto out;
 			goto out;
 		if (irqchip_in_kernel(kvm)) {
 		if (irqchip_in_kernel(kvm)) {
 			mutex_lock(&kvm->lock);
 			mutex_lock(&kvm->lock);
-			kvm_ioapic_set_irq(kvm->arch.vioapic,
-						irq_event.irq,
-						irq_event.level);
+			kvm_set_irq(kvm, KVM_USERSPACE_IRQ_SOURCE_ID,
+				    irq_event.irq, irq_event.level);
 			mutex_unlock(&kvm->lock);
 			mutex_unlock(&kvm->lock);
 			r = 0;
 			r = 0;
 		}
 		}
@@ -1123,15 +1128,16 @@ static enum hrtimer_restart hlt_timer_fn(struct hrtimer *data)
 	wait_queue_head_t *q;
 	wait_queue_head_t *q;
 
 
 	vcpu  = container_of(data, struct kvm_vcpu, arch.hlt_timer);
 	vcpu  = container_of(data, struct kvm_vcpu, arch.hlt_timer);
+	q = &vcpu->wq;
+
 	if (vcpu->arch.mp_state != KVM_MP_STATE_HALTED)
 	if (vcpu->arch.mp_state != KVM_MP_STATE_HALTED)
 		goto out;
 		goto out;
 
 
-	q = &vcpu->wq;
-	if (waitqueue_active(q)) {
-		vcpu->arch.mp_state = KVM_MP_STATE_RUNNABLE;
+	if (waitqueue_active(q))
 		wake_up_interruptible(q);
 		wake_up_interruptible(q);
-	}
+
 out:
 out:
+	vcpu->arch.timer_fired = 1;
 	vcpu->arch.timer_check = 1;
 	vcpu->arch.timer_check = 1;
 	return HRTIMER_NORESTART;
 	return HRTIMER_NORESTART;
 }
 }
@@ -1700,12 +1706,14 @@ static void vcpu_kick_intr(void *info)
 void kvm_vcpu_kick(struct kvm_vcpu *vcpu)
 void kvm_vcpu_kick(struct kvm_vcpu *vcpu)
 {
 {
 	int ipi_pcpu = vcpu->cpu;
 	int ipi_pcpu = vcpu->cpu;
+	int cpu = get_cpu();
 
 
 	if (waitqueue_active(&vcpu->wq))
 	if (waitqueue_active(&vcpu->wq))
 		wake_up_interruptible(&vcpu->wq);
 		wake_up_interruptible(&vcpu->wq);
 
 
-	if (vcpu->guest_mode)
+	if (vcpu->guest_mode && cpu != ipi_pcpu)
 		smp_call_function_single(ipi_pcpu, vcpu_kick_intr, vcpu, 0);
 		smp_call_function_single(ipi_pcpu, vcpu_kick_intr, vcpu, 0);
+	put_cpu();
 }
 }
 
 
 int kvm_apic_set_irq(struct kvm_vcpu *vcpu, u8 vec, u8 trig)
 int kvm_apic_set_irq(struct kvm_vcpu *vcpu, u8 vec, u8 trig)
@@ -1715,13 +1723,7 @@ int kvm_apic_set_irq(struct kvm_vcpu *vcpu, u8 vec, u8 trig)
 
 
 	if (!test_and_set_bit(vec, &vpd->irr[0])) {
 	if (!test_and_set_bit(vec, &vpd->irr[0])) {
 		vcpu->arch.irq_new_pending = 1;
 		vcpu->arch.irq_new_pending = 1;
-		 if (vcpu->arch.mp_state == KVM_MP_STATE_RUNNABLE)
-			kvm_vcpu_kick(vcpu);
-		else if (vcpu->arch.mp_state == KVM_MP_STATE_HALTED) {
-			vcpu->arch.mp_state = KVM_MP_STATE_RUNNABLE;
-			if (waitqueue_active(&vcpu->wq))
-				wake_up_interruptible(&vcpu->wq);
-		}
+		kvm_vcpu_kick(vcpu);
 		return 1;
 		return 1;
 	}
 	}
 	return 0;
 	return 0;
@@ -1791,7 +1793,7 @@ int kvm_cpu_has_interrupt(struct kvm_vcpu *vcpu)
 
 
 int kvm_cpu_has_pending_timer(struct kvm_vcpu *vcpu)
 int kvm_cpu_has_pending_timer(struct kvm_vcpu *vcpu)
 {
 {
-	return 0;
+	return vcpu->arch.timer_fired;
 }
 }
 
 
 gfn_t unalias_gfn(struct kvm *kvm, gfn_t gfn)
 gfn_t unalias_gfn(struct kvm *kvm, gfn_t gfn)

+ 7 - 2
arch/ia64/kvm/kvm_fw.c

@@ -286,6 +286,12 @@ static  u64 kvm_get_pal_call_index(struct kvm_vcpu *vcpu)
 	return index;
 	return index;
 }
 }
 
 
+static void prepare_for_halt(struct kvm_vcpu *vcpu)
+{
+	vcpu->arch.timer_pending = 1;
+	vcpu->arch.timer_fired = 0;
+}
+
 int kvm_pal_emul(struct kvm_vcpu *vcpu, struct kvm_run *run)
 int kvm_pal_emul(struct kvm_vcpu *vcpu, struct kvm_run *run)
 {
 {
 
 
@@ -304,11 +310,10 @@ int kvm_pal_emul(struct kvm_vcpu *vcpu, struct kvm_run *run)
 		break;
 		break;
 	case PAL_HALT_LIGHT:
 	case PAL_HALT_LIGHT:
 	{
 	{
-		vcpu->arch.timer_pending = 1;
 		INIT_PAL_STATUS_SUCCESS(result);
 		INIT_PAL_STATUS_SUCCESS(result);
+		prepare_for_halt(vcpu);
 		if (kvm_highest_pending_irq(vcpu) == -1)
 		if (kvm_highest_pending_irq(vcpu) == -1)
 			ret = kvm_emulate_halt(vcpu);
 			ret = kvm_emulate_halt(vcpu);
-
 	}
 	}
 		break;
 		break;
 
 

+ 1 - 1
arch/ia64/kvm/process.c

@@ -713,7 +713,7 @@ void leave_hypervisor_tail(void)
 				if (!(VCPU(v, itv) & (1 << 16))) {
 				if (!(VCPU(v, itv) & (1 << 16))) {
 					vcpu_pend_interrupt(v, VCPU(v, itv)
 					vcpu_pend_interrupt(v, VCPU(v, itv)
 							& 0xff);
 							& 0xff);
-				VMX(v, itc_check) = 0;
+					VMX(v, itc_check) = 0;
 				} else {
 				} else {
 					v->arch.timer_pending = 1;
 					v->arch.timer_pending = 1;
 				}
 				}

+ 3 - 0
arch/x86/include/asm/kvm_host.h

@@ -364,6 +364,9 @@ struct kvm_arch{
 
 
 	struct page *ept_identity_pagetable;
 	struct page *ept_identity_pagetable;
 	bool ept_identity_pagetable_done;
 	bool ept_identity_pagetable_done;
+
+	unsigned long irq_sources_bitmap;
+	unsigned long irq_states[KVM_IOAPIC_NUM_PINS];
 };
 };
 
 
 struct kvm_vm_stat {
 struct kvm_vm_stat {

+ 9 - 2
arch/x86/kvm/i8254.c

@@ -545,6 +545,12 @@ struct kvm_pit *kvm_create_pit(struct kvm *kvm)
 	if (!pit)
 	if (!pit)
 		return NULL;
 		return NULL;
 
 
+	mutex_lock(&kvm->lock);
+	pit->irq_source_id = kvm_request_irq_source_id(kvm);
+	mutex_unlock(&kvm->lock);
+	if (pit->irq_source_id < 0)
+		return NULL;
+
 	mutex_init(&pit->pit_state.lock);
 	mutex_init(&pit->pit_state.lock);
 	mutex_lock(&pit->pit_state.lock);
 	mutex_lock(&pit->pit_state.lock);
 	spin_lock_init(&pit->pit_state.inject_lock);
 	spin_lock_init(&pit->pit_state.inject_lock);
@@ -587,6 +593,7 @@ void kvm_free_pit(struct kvm *kvm)
 		mutex_lock(&kvm->arch.vpit->pit_state.lock);
 		mutex_lock(&kvm->arch.vpit->pit_state.lock);
 		timer = &kvm->arch.vpit->pit_state.pit_timer.timer;
 		timer = &kvm->arch.vpit->pit_state.pit_timer.timer;
 		hrtimer_cancel(timer);
 		hrtimer_cancel(timer);
+		kvm_free_irq_source_id(kvm, kvm->arch.vpit->irq_source_id);
 		mutex_unlock(&kvm->arch.vpit->pit_state.lock);
 		mutex_unlock(&kvm->arch.vpit->pit_state.lock);
 		kfree(kvm->arch.vpit);
 		kfree(kvm->arch.vpit);
 	}
 	}
@@ -595,8 +602,8 @@ void kvm_free_pit(struct kvm *kvm)
 static void __inject_pit_timer_intr(struct kvm *kvm)
 static void __inject_pit_timer_intr(struct kvm *kvm)
 {
 {
 	mutex_lock(&kvm->lock);
 	mutex_lock(&kvm->lock);
-	kvm_set_irq(kvm, 0, 1);
-	kvm_set_irq(kvm, 0, 0);
+	kvm_set_irq(kvm, kvm->arch.vpit->irq_source_id, 0, 1);
+	kvm_set_irq(kvm, kvm->arch.vpit->irq_source_id, 0, 0);
 	mutex_unlock(&kvm->lock);
 	mutex_unlock(&kvm->lock);
 }
 }
 
 

+ 1 - 0
arch/x86/kvm/i8254.h

@@ -44,6 +44,7 @@ struct kvm_pit {
 	struct kvm_io_device speaker_dev;
 	struct kvm_io_device speaker_dev;
 	struct kvm *kvm;
 	struct kvm *kvm;
 	struct kvm_kpit_state pit_state;
 	struct kvm_kpit_state pit_state;
+	int irq_source_id;
 };
 };
 
 
 #define KVM_PIT_BASE_ADDRESS	    0x40
 #define KVM_PIT_BASE_ADDRESS	    0x40

+ 1 - 0
arch/x86/kvm/mmu.c

@@ -2634,6 +2634,7 @@ static int kvm_pv_mmu_write(struct kvm_vcpu *vcpu,
 static int kvm_pv_mmu_flush_tlb(struct kvm_vcpu *vcpu)
 static int kvm_pv_mmu_flush_tlb(struct kvm_vcpu *vcpu)
 {
 {
 	kvm_x86_ops->tlb_flush(vcpu);
 	kvm_x86_ops->tlb_flush(vcpu);
+	set_bit(KVM_REQ_MMU_SYNC, &vcpu->requests);
 	return 1;
 	return 1;
 }
 }
 
 

+ 5 - 1
arch/x86/kvm/x86.c

@@ -1742,7 +1742,8 @@ long kvm_arch_vm_ioctl(struct file *filp,
 			goto out;
 			goto out;
 		if (irqchip_in_kernel(kvm)) {
 		if (irqchip_in_kernel(kvm)) {
 			mutex_lock(&kvm->lock);
 			mutex_lock(&kvm->lock);
-			kvm_set_irq(kvm, irq_event.irq, irq_event.level);
+			kvm_set_irq(kvm, KVM_USERSPACE_IRQ_SOURCE_ID,
+				    irq_event.irq, irq_event.level);
 			mutex_unlock(&kvm->lock);
 			mutex_unlock(&kvm->lock);
 			r = 0;
 			r = 0;
 		}
 		}
@@ -4013,6 +4014,9 @@ struct  kvm *kvm_arch_create_vm(void)
 	INIT_LIST_HEAD(&kvm->arch.active_mmu_pages);
 	INIT_LIST_HEAD(&kvm->arch.active_mmu_pages);
 	INIT_LIST_HEAD(&kvm->arch.assigned_dev_head);
 	INIT_LIST_HEAD(&kvm->arch.assigned_dev_head);
 
 
+	/* Reserve bit 0 of irq_sources_bitmap for userspace irq source */
+	set_bit(KVM_USERSPACE_IRQ_SOURCE_ID, &kvm->arch.irq_sources_bitmap);
+
 	return kvm;
 	return kvm;
 }
 }
 
 

+ 6 - 0
include/linux/kvm.h

@@ -489,6 +489,9 @@ struct kvm_assigned_pci_dev {
 	__u32 busnr;
 	__u32 busnr;
 	__u32 devfn;
 	__u32 devfn;
 	__u32 flags;
 	__u32 flags;
+	union {
+		__u32 reserved[12];
+	};
 };
 };
 
 
 struct kvm_assigned_irq {
 struct kvm_assigned_irq {
@@ -496,6 +499,9 @@ struct kvm_assigned_irq {
 	__u32 host_irq;
 	__u32 host_irq;
 	__u32 guest_irq;
 	__u32 guest_irq;
 	__u32 flags;
 	__u32 flags;
+	union {
+		__u32 reserved[12];
+	};
 };
 };
 
 
 #define KVM_DEV_ASSIGN_ENABLE_IOMMU	(1 << 0)
 #define KVM_DEV_ASSIGN_ENABLE_IOMMU	(1 << 0)

+ 6 - 1
include/linux/kvm_host.h

@@ -37,6 +37,8 @@
 #define KVM_REQ_UNHALT             6
 #define KVM_REQ_UNHALT             6
 #define KVM_REQ_MMU_SYNC           7
 #define KVM_REQ_MMU_SYNC           7
 
 
+#define KVM_USERSPACE_IRQ_SOURCE_ID	0
+
 struct kvm_vcpu;
 struct kvm_vcpu;
 extern struct kmem_cache *kvm_vcpu_cache;
 extern struct kmem_cache *kvm_vcpu_cache;
 
 
@@ -306,15 +308,18 @@ struct kvm_assigned_dev_kernel {
 	int host_irq;
 	int host_irq;
 	int guest_irq;
 	int guest_irq;
 	int irq_requested;
 	int irq_requested;
+	int irq_source_id;
 	struct pci_dev *dev;
 	struct pci_dev *dev;
 	struct kvm *kvm;
 	struct kvm *kvm;
 };
 };
-void kvm_set_irq(struct kvm *kvm, int irq, int level);
+void kvm_set_irq(struct kvm *kvm, int irq_source_id, int irq, int level);
 void kvm_notify_acked_irq(struct kvm *kvm, unsigned gsi);
 void kvm_notify_acked_irq(struct kvm *kvm, unsigned gsi);
 void kvm_register_irq_ack_notifier(struct kvm *kvm,
 void kvm_register_irq_ack_notifier(struct kvm *kvm,
 				   struct kvm_irq_ack_notifier *kian);
 				   struct kvm_irq_ack_notifier *kian);
 void kvm_unregister_irq_ack_notifier(struct kvm *kvm,
 void kvm_unregister_irq_ack_notifier(struct kvm *kvm,
 				     struct kvm_irq_ack_notifier *kian);
 				     struct kvm_irq_ack_notifier *kian);
+int kvm_request_irq_source_id(struct kvm *kvm);
+void kvm_free_irq_source_id(struct kvm *kvm, int irq_source_id);
 
 
 #ifdef CONFIG_DMAR
 #ifdef CONFIG_DMAR
 int kvm_iommu_map_pages(struct kvm *kvm, gfn_t base_gfn,
 int kvm_iommu_map_pages(struct kvm *kvm, gfn_t base_gfn,

+ 39 - 3
virt/kvm/irq_comm.c

@@ -25,15 +25,23 @@
 #include "ioapic.h"
 #include "ioapic.h"
 
 
 /* This should be called with the kvm->lock mutex held */
 /* This should be called with the kvm->lock mutex held */
-void kvm_set_irq(struct kvm *kvm, int irq, int level)
+void kvm_set_irq(struct kvm *kvm, int irq_source_id, int irq, int level)
 {
 {
+	unsigned long *irq_state = (unsigned long *)&kvm->arch.irq_states[irq];
+
+	/* Logical OR for level trig interrupt */
+	if (level)
+		set_bit(irq_source_id, irq_state);
+	else
+		clear_bit(irq_source_id, irq_state);
+
 	/* Not possible to detect if the guest uses the PIC or the
 	/* Not possible to detect if the guest uses the PIC or the
 	 * IOAPIC.  So set the bit in both. The guest will ignore
 	 * IOAPIC.  So set the bit in both. The guest will ignore
 	 * writes to the unused one.
 	 * writes to the unused one.
 	 */
 	 */
-	kvm_ioapic_set_irq(kvm->arch.vioapic, irq, level);
+	kvm_ioapic_set_irq(kvm->arch.vioapic, irq, !!(*irq_state));
 #ifdef CONFIG_X86
 #ifdef CONFIG_X86
-	kvm_pic_set_irq(pic_irqchip(kvm), irq, level);
+	kvm_pic_set_irq(pic_irqchip(kvm), irq, !!(*irq_state));
 #endif
 #endif
 }
 }
 
 
@@ -58,3 +66,31 @@ void kvm_unregister_irq_ack_notifier(struct kvm *kvm,
 {
 {
 	hlist_del(&kian->link);
 	hlist_del(&kian->link);
 }
 }
+
+/* The caller must hold kvm->lock mutex */
+int kvm_request_irq_source_id(struct kvm *kvm)
+{
+	unsigned long *bitmap = &kvm->arch.irq_sources_bitmap;
+	int irq_source_id = find_first_zero_bit(bitmap,
+				sizeof(kvm->arch.irq_sources_bitmap));
+	if (irq_source_id >= sizeof(kvm->arch.irq_sources_bitmap)) {
+		printk(KERN_WARNING "kvm: exhaust allocatable IRQ sources!\n");
+		irq_source_id = -EFAULT;
+	} else
+		set_bit(irq_source_id, bitmap);
+	return irq_source_id;
+}
+
+void kvm_free_irq_source_id(struct kvm *kvm, int irq_source_id)
+{
+	int i;
+
+	if (irq_source_id <= 0 ||
+	    irq_source_id >= sizeof(kvm->arch.irq_sources_bitmap)) {
+		printk(KERN_ERR "kvm: IRQ source ID out of range!\n");
+		return;
+	}
+	for (i = 0; i < KVM_IOAPIC_NUM_PINS; i++)
+		clear_bit(irq_source_id, &kvm->arch.irq_states[i]);
+	clear_bit(irq_source_id, &kvm->arch.irq_sources_bitmap);
+}

+ 8 - 4
virt/kvm/kvm_main.c

@@ -105,14 +105,12 @@ static void kvm_assigned_dev_interrupt_work_handler(struct work_struct *work)
 	 */
 	 */
 	mutex_lock(&assigned_dev->kvm->lock);
 	mutex_lock(&assigned_dev->kvm->lock);
 	kvm_set_irq(assigned_dev->kvm,
 	kvm_set_irq(assigned_dev->kvm,
+		    assigned_dev->irq_source_id,
 		    assigned_dev->guest_irq, 1);
 		    assigned_dev->guest_irq, 1);
 	mutex_unlock(&assigned_dev->kvm->lock);
 	mutex_unlock(&assigned_dev->kvm->lock);
 	kvm_put_kvm(assigned_dev->kvm);
 	kvm_put_kvm(assigned_dev->kvm);
 }
 }
 
 
-/* FIXME: Implement the OR logic needed to make shared interrupts on
- * this line behave properly
- */
 static irqreturn_t kvm_assigned_dev_intr(int irq, void *dev_id)
 static irqreturn_t kvm_assigned_dev_intr(int irq, void *dev_id)
 {
 {
 	struct kvm_assigned_dev_kernel *assigned_dev =
 	struct kvm_assigned_dev_kernel *assigned_dev =
@@ -134,7 +132,7 @@ static void kvm_assigned_dev_ack_irq(struct kvm_irq_ack_notifier *kian)
 
 
 	dev = container_of(kian, struct kvm_assigned_dev_kernel,
 	dev = container_of(kian, struct kvm_assigned_dev_kernel,
 			   ack_notifier);
 			   ack_notifier);
-	kvm_set_irq(dev->kvm, dev->guest_irq, 0);
+	kvm_set_irq(dev->kvm, dev->irq_source_id, dev->guest_irq, 0);
 	enable_irq(dev->host_irq);
 	enable_irq(dev->host_irq);
 }
 }
 
 
@@ -146,6 +144,7 @@ static void kvm_free_assigned_device(struct kvm *kvm,
 		free_irq(assigned_dev->host_irq, (void *)assigned_dev);
 		free_irq(assigned_dev->host_irq, (void *)assigned_dev);
 
 
 	kvm_unregister_irq_ack_notifier(kvm, &assigned_dev->ack_notifier);
 	kvm_unregister_irq_ack_notifier(kvm, &assigned_dev->ack_notifier);
+	kvm_free_irq_source_id(kvm, assigned_dev->irq_source_id);
 
 
 	if (cancel_work_sync(&assigned_dev->interrupt_work))
 	if (cancel_work_sync(&assigned_dev->interrupt_work))
 		/* We had pending work. That means we will have to take
 		/* We had pending work. That means we will have to take
@@ -215,6 +214,11 @@ static int kvm_vm_ioctl_assign_irq(struct kvm *kvm,
 		match->ack_notifier.gsi = assigned_irq->guest_irq;
 		match->ack_notifier.gsi = assigned_irq->guest_irq;
 		match->ack_notifier.irq_acked = kvm_assigned_dev_ack_irq;
 		match->ack_notifier.irq_acked = kvm_assigned_dev_ack_irq;
 		kvm_register_irq_ack_notifier(kvm, &match->ack_notifier);
 		kvm_register_irq_ack_notifier(kvm, &match->ack_notifier);
+		r = kvm_request_irq_source_id(kvm);
+		if (r < 0)
+			goto out_release;
+		else
+			match->irq_source_id = r;
 
 
 		/* Even though this is PCI, we don't want to use shared
 		/* Even though this is PCI, we don't want to use shared
 		 * interrupts. Sharing host devices with guest-assigned devices
 		 * interrupts. Sharing host devices with guest-assigned devices