Skip to contentSkip to conversation

KVM: x86: Split the APIC from the rest of IRQCHIP.

PATCH In review Steve Rutherford 3 participants 11 messages Jul 30, 2015 – Jul 31, 2015

Subsystems (4)
lore
Share & tools
lore
#0 lore raw

PATCH v7 1/4 KVM: x86: Split the APIC from the rest of IRQCHIP.

First patch in a series which enables the relocation of the PIC/IOAPIC to userspace.

Adds capability KVM_CAP_SPLIT_IRQCHIP;

KVM_CAP_SPLIT_IRQCHIP enables the construction of LAPICs without the rest of the irqchip.

Compile tested for x86.

---
 Documentation/virtual/kvm/api.txt | 15 +++++++++++++++
 arch/x86/include/asm/kvm_host.h   |  2 ++
 arch/x86/kvm/i8254.c              |  4 +++-
 arch/x86/kvm/ioapic.h             |  8 ++++++++
 arch/x86/kvm/irq.h                | 11 ++++++++++-
 arch/x86/kvm/irq_comm.c           |  9 ++++++++-
 arch/x86/kvm/lapic.c              |  6 ++++--
 arch/x86/kvm/vmx.c                |  4 ++--
 arch/x86/kvm/x86.c                | 25 +++++++++++++++++++++++--
 include/linux/kvm_host.h          |  1 +
 include/uapi/linux/kvm.h          |  1 +
 11 files changed, 77 insertions(+), 9 deletions(-)
Documentation/virtual/kvm/api.txt +15 -0
index a4ebcb7..b655024 100644
@@ -3620,6 +3620,21 @@ struct {
 
 KVM handlers should exit to userspace with rc = -EREMOTE.
 
+7.5 KVM_SPLIT_IRQCHIP
+
+Architectures: x86
+Parameters: None
+Returns: 0 on success, -1 on error
+
+Create a local apic for each processor in the kernel. With this capability
+enabled, the userspace VMM is expected to emulate the IOAPIC and PIC.
+
+This supersedes KVM_CREATE_IRQCHIP, creating only local APICs, but no in kernel
+IOAPIC or PIC. This also enables in kernel routing of interrupt requests.
+
+Fails if VCPU has already been created, or if the irqchip is already in the
+kernel (i.e. KVM_CREATE_IRQCHIP has already been called).
+
 
 8. Other capabilities.
 ----------------------
arch/x86/include/asm/kvm_host.h +2 -0
index d3e7a53..b4fdf0c 100644
@@ -670,6 +670,8 @@ struct kvm_arch {
 	bool boot_vcpu_runs_old_kvmclock;
 
 	u64 disabled_quirks;
+
+	bool irqchip_split;
 };
 
 struct kvm_vm_stat {
arch/x86/kvm/i8254.c +3 -1
index f588eb7..08116ff 100644
@@ -35,6 +35,7 @@
 #include <linux/kvm_host.h>
 #include <linux/slab.h>
 
+#include "ioapic.h"
 #include "irq.h"
 #include "i8254.h"
 #include "x86.h"
@@ -333,7 +334,8 @@ static void create_pit_timer(struct kvm *kvm, u32 val, int is_period)
 	struct kvm_kpit_state *ps = &kvm->arch.vpit->pit_state;
 	s64 interval;
 
-	if (ps->flags & KVM_PIT_FLAGS_HPET_LEGACY)
+	if (!ioapic_in_kernel(kvm) ||
+	    ps->flags & KVM_PIT_FLAGS_HPET_LEGACY)
 		return;
 
 	interval = muldiv64(val, NSEC_PER_SEC, KVM_PIT_FREQ);
arch/x86/kvm/ioapic.h +8 -0
index bf36d66..a8842c0 100644
@@ -97,6 +97,14 @@ static inline struct kvm_ioapic *ioapic_irqchip(struct kvm *kvm)
 	return kvm->arch.vioapic;
 }
 
+static inline int ioapic_in_kernel(struct kvm *kvm)
+{
+	int ret;
+
+	ret = (ioapic_irqchip(kvm) != NULL);
+	return ret;
+}
+
 void kvm_rtc_eoi_tracking_restore_one(struct kvm_vcpu *vcpu);
 bool kvm_apic_match_dest(struct kvm_vcpu *vcpu, struct kvm_lapic *source,
 		int short_hand, unsigned int dest, int dest_mode);
arch/x86/kvm/irq.h +10 -1
index 3d782a2..72af5a9 100644
@@ -83,13 +83,22 @@ static inline struct kvm_pic *pic_irqchip(struct kvm *kvm)
 	return kvm->arch.vpic;
 }
 
+static inline int irqchip_split(struct kvm *kvm)
+{
+	return kvm->arch.irqchip_split;
+}
+
 static inline int irqchip_in_kernel(struct kvm *kvm)
 {
+	bool ret;
 	struct kvm_pic *vpic = pic_irqchip(kvm);
 
 	/* Read vpic before kvm->irq_routing.  */
 	smp_rmb();
-	return vpic != NULL;
+	ret = (vpic != NULL);
+	ret |= irqchip_split(kvm);
+
+	return ret;
 }
 
 void kvm_pic_reset(struct kvm_kpic_state *s);
arch/x86/kvm/irq_comm.c +8 -1
index 9efff9e..67f6b62 100644
@@ -208,7 +208,7 @@ void kvm_free_irq_source_id(struct kvm *kvm, int irq_source_id)
 		goto unlock;
 	}
 	clear_bit(irq_source_id, &kvm->arch.irq_sources_bitmap);
-	if (!irqchip_in_kernel(kvm))
+	if (!ioapic_in_kernel(kvm))
 		goto unlock;
 
 	kvm_ioapic_clear_all(kvm->arch.vioapic, irq_source_id);
@@ -328,3 +328,10 @@ int kvm_setup_default_irq_routing(struct kvm *kvm)
 	return kvm_set_irq_routing(kvm, default_routing,
 				   ARRAY_SIZE(default_routing), 0);
 }
+
+static const struct kvm_irq_routing_entry empty_routing[] = {};
+
+int kvm_setup_empty_irq_routing(struct kvm *kvm)
+{
+	return kvm_set_irq_routing(kvm, empty_routing, 0, 0);
+}
arch/x86/kvm/lapic.c +4 -2
index c158f46..2f486d8 100644
@@ -209,7 +209,8 @@ out:
 	if (old)
 		kfree_rcu(old, rcu);
 
-	kvm_vcpu_request_scan_ioapic(kvm);
+	if (!irqchip_split(kvm))
+		kvm_vcpu_request_scan_ioapic(kvm);
 }
 
 static inline void apic_set_spiv(struct kvm_lapic *apic, u32 val)
@@ -1845,7 +1846,8 @@ void kvm_apic_post_state_restore(struct kvm_vcpu *vcpu,
 		kvm_x86_ops->hwapic_isr_update(vcpu->kvm,
 				apic_find_highest_isr(apic));
 	kvm_make_request(KVM_REQ_EVENT, vcpu);
-	kvm_rtc_eoi_tracking_restore_one(vcpu);
+	if (!ioapic_in_kernel(vcpu->kvm))
+		kvm_rtc_eoi_tracking_restore_one(vcpu);
 }
 
 void __kvm_migrate_apic_timer(struct kvm_vcpu *vcpu)
arch/x86/kvm/vmx.c +2 -2
index 7323ea8..3a9653f 100644
@@ -948,7 +948,7 @@ static inline bool cpu_has_vmx_tpr_shadow(void)
 
 static inline bool vm_need_tpr_shadow(struct kvm *kvm)
 {
-	return (cpu_has_vmx_tpr_shadow()) && (irqchip_in_kernel(kvm));
+	return (cpu_has_vmx_tpr_shadow()) && irqchip_in_kernel(kvm);
 }
 
 static inline bool cpu_has_secondary_exec_ctrls(void)
@@ -9486,7 +9486,7 @@ static void prepare_vmcs02(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12)
 	/* vmcs12's VM_ENTRY_LOAD_IA32_EFER and VM_ENTRY_IA32E_MODE are
 	 * emulated by vmx_set_efer(), below.
 	 */
-	vm_entry_controls_init(vmx, 
+	vm_entry_controls_init(vmx,
 		(vmcs12->vm_entry_controls & ~VM_ENTRY_LOAD_IA32_EFER &
 			~VM_ENTRY_IA32E_MODE) |
 		(vmcs_config.vmentry_ctrl & ~VM_ENTRY_IA32E_MODE));
arch/x86/kvm/x86.c +23 -2
index c6c6e9c..8e40ddf 100644
@@ -2459,6 +2459,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
 	case KVM_CAP_TSC_DEADLINE_TIMER:
 	case KVM_CAP_ENABLE_CAP_VM:
 	case KVM_CAP_DISABLE_QUIRKS:
+	case KVM_CAP_SPLIT_IRQCHIP:
 #ifdef CONFIG_KVM_DEVICE_ASSIGNMENT
 	case KVM_CAP_ASSIGN_DEV_IRQ:
 	case KVM_CAP_PCI_2_3:
@@ -3566,6 +3567,26 @@ static int kvm_vm_ioctl_enable_cap(struct kvm *kvm,
 		kvm->arch.disabled_quirks = cap->args[0];
 		r = 0;
 		break;
+	case KVM_CAP_SPLIT_IRQCHIP: {
+		mutex_lock(&kvm->lock);
+		r = -EEXIST;
+		if (irqchip_in_kernel(kvm))
+			goto split_irqchip_unlock;
+		if (!irqchip_split(kvm)) {
+			if (atomic_read(&kvm->online_vcpus))
+				goto split_irqchip_unlock;
+			r = kvm_setup_empty_irq_routing(kvm);
+			if (r)
+				goto split_irqchip_unlock;
+			/* Pairs with irqchip_in_kernel. */
+			smp_wmb();
+			kvm->arch.irqchip_split = true;
+		}
+		r = 0;
+split_irqchip_unlock:
+		mutex_unlock(&kvm->lock);
+		break;
+	}
 	default:
 		r = -EINVAL;
 		break;
@@ -3679,7 +3700,7 @@ long kvm_arch_vm_ioctl(struct file *filp,
 		}
 
 		r = -ENXIO;
-		if (!irqchip_in_kernel(kvm))
+		if (!irqchip_in_kernel(kvm) || !ioapic_in_kernel(kvm))
 			goto get_irqchip_out;
 		r = kvm_vm_ioctl_get_irqchip(kvm, chip);
 		if (r)
@@ -3703,7 +3724,7 @@ long kvm_arch_vm_ioctl(struct file *filp,
 		}
 
 		r = -ENXIO;
-		if (!irqchip_in_kernel(kvm))
+		if (!irqchip_in_kernel(kvm) || !ioapic_in_kernel(kvm))
 			goto set_irqchip_out;
 		r = kvm_vm_ioctl_set_irqchip(kvm, chip);
 		if (r)
include/linux/kvm_host.h +1 -0
index 29c0c28..1867b83 100644
@@ -1004,6 +1004,7 @@ static inline int mmu_notifier_retry(struct kvm *kvm, unsigned long mmu_seq)
 #endif
 
 int kvm_setup_default_irq_routing(struct kvm *kvm);
+int kvm_setup_empty_irq_routing(struct kvm *kvm);
 int kvm_set_irq_routing(struct kvm *kvm,
 			const struct kvm_irq_routing_entry *entries,
 			unsigned nr,
include/uapi/linux/kvm.h +1 -0
index 9ef19eb..e4304d0 100644
@@ -818,6 +818,7 @@ struct kvm_ppc_smmu_info {
 #define KVM_CAP_DISABLE_QUIRKS 116
 #define KVM_CAP_X86_SMM 117
 #define KVM_CAP_MULTI_ADDRESS_SPACE 118
+#define KVM_CAP_SPLIT_IRQCHIP 119
 
 #ifdef KVM_CAP_IRQ_ROUTING
 
Signature · 1 line
2.5.0.rc2.392.g76e840b
#1 lore raw

PATCH v7 2/4 KVM: x86: Add KVM exit for IOAPIC EOIs

Adds KVM_EXIT_IOAPIC_EOI which allows the kernel to EOI level-triggered IOAPIC interrupts.

Uses a per VCPU exit bitmap to decide whether or not the IOAPIC needs to be informed (which is identical to the EOI_EXIT_BITMAP field used by modern x86 processors, but can also be used to elide kvm IOAPIC EOI exits on older processors).

[Note: A prototype using ResampleFDs found that decoupling the EOI from the VCPU's thread made it possible for the VCPU to not see a recent EOI after reentering the guest. This does not match real hardware.]

Compile tested for Intel x86.

---
 Documentation/virtual/kvm/api.txt | 12 ++++++++++++
 arch/x86/include/asm/kvm_host.h   |  2 ++
 arch/x86/kvm/lapic.c              | 24 +++++++++++++++++-------
 arch/x86/kvm/x86.c                | 11 +++++++++++
 include/linux/kvm_host.h          |  2 +-
 include/uapi/linux/kvm.h          |  5 +++++
 6 files changed, 48 insertions(+), 8 deletions(-)
Documentation/virtual/kvm/api.txt +12 -0
index b655024..78d0ae8 100644
@@ -3302,6 +3302,18 @@ Valid values for 'type' are:
    to ignore the request, or to gather VM memory core dump and/or
    reset/shutdown of the VM.
 
+		/* KVM_EXIT_IOAPIC_EOI */
+		struct {
+			__u8 vector;
+		} eoi;
+
+Indicates that the VCPU's in-kernel local APIC received an EOI for a
+level-triggered IOAPIC interrupt.  This exit only triggers when the
+IOAPIC is implemented in userspace (i.e. KVM_CAP_SPLIT_IRQCHIP is enabled);
+the userspace IOAPIC should process the EOI and retrigger the interrupt if
+it is still asserted.  Vector is the LAPIC interrupt vector for which the
+EOI was received.
+
 		/* Fix the size of the union. */
 		char padding[256];
 	};
arch/x86/include/asm/kvm_host.h +2 -0
index b4fdf0c..b27f54d 100644
@@ -561,6 +561,8 @@ struct kvm_vcpu_arch {
 	struct {
 		bool pv_unhalted;
 	} pv;
+
+	int pending_ioapic_eoi;
 };
 
 struct kvm_lpage_info {
arch/x86/kvm/lapic.c +17 -7
index 2f486d8..9e69296a 100644
@@ -877,15 +877,25 @@ static bool kvm_ioapic_handles_vector(struct kvm_lapic *apic, int vector)
 
 static void kvm_ioapic_send_eoi(struct kvm_lapic *apic, int vector)
 {
-	if (kvm_ioapic_handles_vector(apic, vector)) {
-		int trigger_mode;
-		if (apic_test_vector(vector, apic->regs + APIC_TMR))
-			trigger_mode = IOAPIC_LEVEL_TRIG;
-		else
-			trigger_mode = IOAPIC_EDGE_TRIG;
+	int trigger_mode;
+
+	/* Eoi the ioapic only if the ioapic doesn't own the vector. */
+	if (!kvm_ioapic_handles_vector(apic, vector))
+		return;
 
-		kvm_ioapic_update_eoi(apic->vcpu, vector, trigger_mode);
+	/* Request a KVM exit to inform the userspace IOAPIC. */
+	if (irqchip_split(apic->vcpu->kvm)) {
+		apic->vcpu->arch.pending_ioapic_eoi = vector;
+		kvm_make_request(KVM_REQ_IOAPIC_EOI_EXIT, apic->vcpu);
+		return;
 	}
+
+	if (apic_test_vector(vector, apic->regs + APIC_TMR))
+		trigger_mode = IOAPIC_LEVEL_TRIG;
+	else
+		trigger_mode = IOAPIC_EDGE_TRIG;
+
+	kvm_ioapic_update_eoi(apic->vcpu, vector, trigger_mode);
 }
 
 static int apic_set_eoi(struct kvm_lapic *apic)
arch/x86/kvm/x86.c +11 -0
index 8e40ddf..4159f29 100644
@@ -6268,6 +6268,17 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu)
 			kvm_pmu_handle_event(vcpu);
 		if (kvm_check_request(KVM_REQ_PMI, vcpu))
 			kvm_pmu_deliver_pmi(vcpu);
+		if (kvm_check_request(KVM_REQ_IOAPIC_EOI_EXIT, vcpu)) {
+			BUG_ON(vcpu->arch.pending_ioapic_eoi > 255);
+			if (test_bit(vcpu->arch.pending_ioapic_eoi,
+				     (void *) vcpu->arch.eoi_exit_bitmap)) {
+				vcpu->run->exit_reason = KVM_EXIT_IOAPIC_EOI;
+				vcpu->run->eoi.vector =
+						vcpu->arch.pending_ioapic_eoi;
+				r = 0;
+				goto out;
+			}
+		}
 		if (kvm_check_request(KVM_REQ_SCAN_IOAPIC, vcpu))
 			vcpu_scan_ioapic(vcpu);
 		if (kvm_check_request(KVM_REQ_APIC_PAGE_RELOAD, vcpu))
include/linux/kvm_host.h +1 -1
index 1867b83..253717c 100644
@@ -140,6 +140,7 @@ static inline bool is_error_page(struct page *page)
 #define KVM_REQ_APIC_PAGE_RELOAD  25
 #define KVM_REQ_SMI               26
 #define KVM_REQ_HV_CRASH          27
+#define KVM_REQ_IOAPIC_EOI_EXIT   28
 
 #define KVM_USERSPACE_IRQ_SOURCE_ID		0
 #define KVM_IRQFD_RESAMPLE_IRQ_SOURCE_ID	1
@@ -1161,4 +1162,3 @@ static inline void kvm_vcpu_set_dy_eligible(struct kvm_vcpu *vcpu, bool val)
 }
 #endif /* CONFIG_HAVE_KVM_CPU_RELAX_INTERCEPT */
 #endif
-
include/uapi/linux/kvm.h +5 -0
index e4304d0..c15f5d9 100644
@@ -183,6 +183,7 @@ struct kvm_s390_skeys {
 #define KVM_EXIT_EPR              23
 #define KVM_EXIT_SYSTEM_EVENT     24
 #define KVM_EXIT_S390_STSI        25
+#define KVM_EXIT_IOAPIC_EOI       26
 
 /* For KVM_EXIT_INTERNAL_ERROR */
 /* Emulate instruction failed. */
@@ -330,6 +331,10 @@ struct kvm_run {
 			__u8 sel1;
 			__u16 sel2;
 		} s390_stsi;
+		/* KVM_EXIT_IOAPIC_EOI */
+		struct {
+			__u8 vector;
+		} eoi;
 		/* Fix the size of the union. */
 		char padding[256];
 	};
Signature · 1 line
2.5.0.rc2.392.g76e840b
#2 lore raw

PATCH v7 3/4 KVM: x86: Add EOI exit bitmap inference

In reply to Steve Rutherford

In order to support a userspace IOAPIC interacting with an in kernel APIC, the EOI exit bitmaps need to be configurable.

If the IOAPIC is in userspace (i.e. the irqchip has been split), the EOI exit bitmaps will be set whenever the GSI Routes are configured. In particular, for the low MSI routes are reservable for userspace IOAPICs. For these MSI routes, the EOI Exit bit corresponding to the destination vector of the route will be set for the destination VCPU.

The intention is for the userspace IOAPICs to use the reservable MSI routes to inject interrupts into the guest.

This is a slight abuse of the notion of an MSI Route, given that MSIs classically bypass the IOAPIC. It might be worthwhile to add an additional route type to improve clarity.

Compile tested for Intel x86.

---
 arch/x86/include/asm/kvm_host.h |  1 +
 arch/x86/kvm/ioapic.h           |  2 ++
 arch/x86/kvm/irq_comm.c         | 42 +++++++++++++++++++++++++++++++++++++++++
 arch/x86/kvm/lapic.c            |  3 +--
 arch/x86/kvm/x86.c              | 11 ++++++++++-
 include/linux/kvm_host.h        | 20 ++++++++++++++++++++
 virt/kvm/irqchip.c              | 12 ++----------
 7 files changed, 78 insertions(+), 13 deletions(-)
arch/x86/include/asm/kvm_host.h +1 -0
index b27f54d..ed896fe 100644
@@ -674,6 +674,7 @@ struct kvm_arch {
 	u64 disabled_quirks;
 
 	bool irqchip_split;
+	u8 nr_reserved_ioapic_pins;
 };
 
 struct kvm_vm_stat {
arch/x86/kvm/ioapic.h +2 -0
index a8842c0..30023ae 100644
@@ -9,6 +9,7 @@ struct kvm;
 struct kvm_vcpu;
 
 #define IOAPIC_NUM_PINS  KVM_IOAPIC_NUM_PINS
+#define MAX_NR_RESERVED_IOAPIC_PINS 48
 #define IOAPIC_VERSION_ID 0x11	/* IOAPIC version */
 #define IOAPIC_EDGE_TRIG  0
 #define IOAPIC_LEVEL_TRIG 1
@@ -122,4 +123,5 @@ int kvm_get_ioapic(struct kvm *kvm, struct kvm_ioapic_state *state);
 int kvm_set_ioapic(struct kvm *kvm, struct kvm_ioapic_state *state);
 void kvm_ioapic_scan_entry(struct kvm_vcpu *vcpu, u64 *eoi_exit_bitmap);
 
+void kvm_scan_ioapic_routes(struct kvm_vcpu *vcpu, u64 *eoi_exit_bitmap);
 #endif
arch/x86/kvm/irq_comm.c +42 -0

45 lines not on this page · raw message

arch/x86/kvm/lapic.c +1 -2
index 9e69296a..e7566f1 100644
@@ -209,8 +209,7 @@ out:
 	if (old)
 		kfree_rcu(old, rcu);
 
-	if (!irqchip_split(kvm))
-		kvm_vcpu_request_scan_ioapic(kvm);
+	kvm_make_scan_ioapic_request(kvm);
 }
 
 static inline void apic_set_spiv(struct kvm_lapic *apic, u32 val)
arch/x86/kvm/x86.c +10 -1
index 4159f29..f9756e7 100644
@@ -3572,6 +3572,11 @@ static int kvm_vm_ioctl_enable_cap(struct kvm *kvm,
 		r = -EEXIST;
 		if (irqchip_in_kernel(kvm))
 			goto split_irqchip_unlock;
+
+		r = -EINVAL;
+		if (cap->args[0] > MAX_NR_RESERVED_IOAPIC_PINS)
+			goto split_irqchip_unlock;
+
 		if (!irqchip_split(kvm)) {
 			if (atomic_read(&kvm->online_vcpus))
 				goto split_irqchip_unlock;
@@ -3582,6 +3587,7 @@ static int kvm_vm_ioctl_enable_cap(struct kvm *kvm,
 			smp_wmb();
 			kvm->arch.irqchip_split = true;
 		}
+		kvm->arch.nr_reserved_ioapic_pins = cap->args[0];
 		r = 0;
 split_irqchip_unlock:
 		mutex_unlock(&kvm->lock);
@@ -6164,7 +6170,10 @@ static void vcpu_scan_ioapic(struct kvm_vcpu *vcpu)
 
 	memset(vcpu->arch.eoi_exit_bitmap, 0, 256 / 8);
 
-	kvm_ioapic_scan_entry(vcpu, vcpu->arch.eoi_exit_bitmap);
+	if (irqchip_split(vcpu->kvm))
+		kvm_scan_ioapic_routes(vcpu, vcpu->arch.eoi_exit_bitmap);
+	else
+		kvm_ioapic_scan_entry(vcpu, vcpu->arch.eoi_exit_bitmap);
 	kvm_x86_ops->load_eoi_exitmap(vcpu);
 }
 
include/linux/kvm_host.h +20 -0
index 253717c..b7483d8 100644
@@ -329,6 +329,17 @@ struct kvm_kernel_irq_routing_entry {
 	struct hlist_node link;
 };
 
+struct kvm_irq_routing_table {
+	int chip[KVM_NR_IRQCHIPS][KVM_IRQCHIP_NUM_PINS];
+	struct kvm_kernel_irq_routing_entry *rt_entries;
+	u32 nr_rt_entries;
+	/*
+	 * Array indexed by gsi. Each entry contains list of irq chips
+	 * the gsi is connected to.
+	 */
+	struct hlist_head map[0];
+};
+
 #ifndef KVM_PRIVATE_MEM_SLOTS
 #define KVM_PRIVATE_MEM_SLOTS 0
 #endif
@@ -458,10 +469,19 @@ void vcpu_put(struct kvm_vcpu *vcpu);
 
 #ifdef __KVM_HAVE_IOAPIC
 void kvm_vcpu_request_scan_ioapic(struct kvm *kvm);
+void kvm_arch_irq_routing_update(struct kvm *kvm);
+u8 kvm_arch_nr_userspace_ioapic_pins(struct kvm *kvm);
 #else
 static inline void kvm_vcpu_request_scan_ioapic(struct kvm *kvm)
 {
 }
+static inline void kvm_arch_irq_routing_update(struct kvm *kvm)
+{
+}
+static inline u8 kvm_arch_nr_userspace_ioapic_pins(struct kvm *kvm)
+{
+	return 0;
+}
 #endif
 
 #ifdef CONFIG_HAVE_KVM_IRQFD
virt/kvm/irqchip.c +2 -10
index 21c1424..4f85c6e 100644
@@ -31,16 +31,6 @@
 #include <trace/events/kvm.h>
 #include "irq.h"
 
-struct kvm_irq_routing_table {
-	int chip[KVM_NR_IRQCHIPS][KVM_IRQCHIP_NUM_PINS];
-	u32 nr_rt_entries;
-	/*
-	 * Array indexed by gsi. Each entry contains list of irq chips
-	 * the gsi is connected to.
-	 */
-	struct hlist_head map[0];
-};
-
 int kvm_irq_map_gsi(struct kvm *kvm,
 		    struct kvm_kernel_irq_routing_entry *entries, int gsi)
 {
@@ -227,6 +217,8 @@ int kvm_set_irq_routing(struct kvm *kvm,
 	kvm_irq_routing_update(kvm);
 	mutex_unlock(&kvm->irq_lock);
 
+	kvm_arch_irq_routing_update(kvm);
+
 	synchronize_srcu_expedited(&kvm->irq_srcu);
 
 	new = old;
Signature · 1 line
2.5.0.rc2.392.g76e840b
#3 lore raw

PATCH v7 4/4 KVM: x86: Add support for local interrupt requests from userspace

In reply to Steve Rutherford

In order to enable userspace PIC support, the userspace PIC needs to be able to inject local interrupts even when the APICs are in the kernel.

KVM_INTERRUPT now supports sending local interrupts to an APIC when APICs are in the kernel.

The ready_for_interrupt_request flag is now only set when the CPU/APIC will immediately accept and inject an interrupt (i.e. APIC has not masked the PIC).

When the PIC wishes to initiate an INTA cycle with, say, CPU0, it kicks CPU0 out of the guest, and renedezvous with CPU0 once it arrives in userspace.

When the CPU/APIC unmasks the PIC, a KVM_EXIT_IRQ_WINDOW_OPEN is triggered, so that userspace has a chance to inject a PIC interrupt if it had been pending.

Overall, this design can lead to a small number of spurious userspace renedezvous. In particular, whenever the PIC transistions from low to high while it is masked and whenever the PIC becomes unmasked while it is low.

Note: this does not buffer more than one local interrupt in the kernel, so the VMM needs to enter the guest in order to complete interrupt injection before injecting an additional interrupt.

Compiles for x86.

Can pass the KVM Unit Tests.

---
 Documentation/virtual/kvm/api.txt | 14 +++++++++----
 arch/x86/include/asm/kvm_host.h   |  1 +
 arch/x86/kvm/irq.c                | 38 +++++++++++++++++++++++++--------
 arch/x86/kvm/irq.h                |  8 +++++++
 arch/x86/kvm/x86.c                | 44 ++++++++++++++++++++++++++++++---------
 5 files changed, 82 insertions(+), 23 deletions(-)
Documentation/virtual/kvm/api.txt +10 -4
index 78d0ae8..4de4286 100644
@@ -401,10 +401,9 @@ Capability: basic
 Architectures: x86, ppc, mips
 Type: vcpu ioctl
 Parameters: struct kvm_interrupt (in)
-Returns: 0 on success, -1 on error
+Returns: 0 on success, negative on failure.
 
-Queues a hardware interrupt vector to be injected.  This is only
-useful if in-kernel local APIC or equivalent is not used.
+Queues a hardware interrupt vector to be injected.
 
 /* for KVM_INTERRUPT */
 struct kvm_interrupt {
@@ -414,7 +413,14 @@ struct kvm_interrupt {
 
 X86:
 
-Note 'irq' is an interrupt vector, not an interrupt pin or line.
+Returns: 0 on success,
+	 -EEXIST if an interrupt is already enqueued
+	 -EINVAL the the irq number is invalid
+	 -ENXIO if the PIC is in the kernel
+	 -EFAULT if the pointer is invalid
+
+Note 'irq' is an interrupt vector, not an interrupt pin or line. This
+ioctl is useful if the in-kernel PIC is not used.
 
 PPC:
 
arch/x86/include/asm/kvm_host.h +1 -0
index ed896fe..33201c6 100644
@@ -563,6 +563,7 @@ struct kvm_vcpu_arch {
 	} pv;
 
 	int pending_ioapic_eoi;
+	int pending_external_vector;
 };
 
 struct kvm_lpage_info {
arch/x86/kvm/irq.c +29 -9
index a1ec6a50..5fa0e6f 100644
@@ -38,14 +38,27 @@ int kvm_cpu_has_pending_timer(struct kvm_vcpu *vcpu)
 EXPORT_SYMBOL(kvm_cpu_has_pending_timer);
 
 /*
+ * check if there is a pending userspace external interrupt
+ */
+static int pending_userspace_extint(struct kvm_vcpu *v)
+{
+	return v->arch.pending_external_vector != -1;
+}
+
+/*
  * check if there is pending interrupt from
  * non-APIC source without intack.
  */
 static int kvm_cpu_has_extint(struct kvm_vcpu *v)
 {
-	if (kvm_apic_accept_pic_intr(v))
-		return pic_irqchip(v->kvm)->output;	/* PIC */
-	else
+	u8 accept = kvm_apic_accept_pic_intr(v);
+
+	if (accept) {
+		if (irqchip_split(v->kvm))
+			return pending_userspace_extint(v);
+		else
+			return pic_irqchip(v->kvm)->output;
+	} else
 		return 0;
 }
 
@@ -57,7 +70,7 @@ static int kvm_cpu_has_extint(struct kvm_vcpu *v)
  */
 int kvm_cpu_has_injectable_intr(struct kvm_vcpu *v)
 {
-	if (!irqchip_in_kernel(v->kvm))
+	if (!pic_in_kernel(v->kvm))
 		return v->arch.interrupt.pending;
 
 	if (kvm_cpu_has_extint(v))
@@ -75,7 +88,7 @@ int kvm_cpu_has_injectable_intr(struct kvm_vcpu *v)
  */
 int kvm_cpu_has_interrupt(struct kvm_vcpu *v)
 {
-	if (!irqchip_in_kernel(v->kvm))
+	if (!pic_in_kernel(v->kvm))
 		return v->arch.interrupt.pending;
 
 	if (kvm_cpu_has_extint(v))
@@ -91,9 +104,16 @@ EXPORT_SYMBOL_GPL(kvm_cpu_has_interrupt);
  */
 static int kvm_cpu_get_extint(struct kvm_vcpu *v)
 {
-	if (kvm_cpu_has_extint(v))
-		return kvm_pic_read_irq(v->kvm); /* PIC */
-	return -1;
+	if (kvm_cpu_has_extint(v)) {
+		if (irqchip_split(v->kvm)) {
+			int vector = v->arch.pending_external_vector;
+
+			v->arch.pending_external_vector = -1;
+			return vector;
+		} else
+			return kvm_pic_read_irq(v->kvm); /* PIC */
+	} else
+		return -1;
 }
 
 /*
@@ -103,7 +123,7 @@ int kvm_cpu_get_interrupt(struct kvm_vcpu *v)
 {
 	int vector;
 
-	if (!irqchip_in_kernel(v->kvm))
+	if (!pic_in_kernel(v->kvm) && v->arch.interrupt.pending)
 		return v->arch.interrupt.nr;
 
 	vector = kvm_cpu_get_extint(v);
arch/x86/kvm/irq.h +8 -0
index 72af5a9..e20581d 100644
@@ -83,6 +83,14 @@ static inline struct kvm_pic *pic_irqchip(struct kvm *kvm)
 	return kvm->arch.vpic;
 }
 
+static inline int pic_in_kernel(struct kvm *kvm)
+{
+	int ret;
+
+	ret = (pic_irqchip(kvm) != NULL);
+	return ret;
+}
+
 static inline int irqchip_split(struct kvm *kvm)
 {
 	return kvm->arch.irqchip_split;
arch/x86/kvm/x86.c +34 -10

82 lines not on this page · raw message

Signature · 1 line
2.5.0.rc2.392.g76e840b
maintainer
#4 lore raw

Re: [PATCH v7 4/4] KVM: x86: Add support for local interrupt requests from userspace

Documentation/virtual/kvm/api.txt Capability: basic · line 404

Quoted 5 lines · Steve Rutherford

Architectures: x86, ppc, mips
Type: vcpu ioctl
Parameters: struct kvm_interrupt (in)

Documentation/virtual/kvm/api.txt +1 -1
-Returns: 0 on success, -1 on error
+Returns: 0 on success, negative on failure.

Really returns -1 because...

Quoted 8 lines
Documentation/virtual/kvm/api.txt +1 -2
-Queues a hardware interrupt vector to be injected.  This is only
-useful if in-kernel local APIC or equivalent is not used.
+Queues a hardware interrupt vector to be injected.
 
 /* for KVM_INTERRUPT */
 struct kvm_interrupt {

Documentation/virtual/kvm/api.txt struct kvm_interrupt { · line 420

Quoted 9 lines
Documentation/virtual/kvm/api.txt +5 -1
@@ -414,7 +413,14 @@ struct kvm_interrupt {
 
 X86:
 
-Note 'irq' is an interrupt vector, not an interrupt pin or line.
+Returns: 0 on success,
+	 -EEXIST if an interrupt is already enqueued
+	 -EINVAL the the irq number is invalid
+	 -ENXIO if the PIC is in the kernel
+	 -EFAULT if the pointer is invalid

... these are errno values. No need to resend.

Paolo

Quoted 2 lines
Documentation/virtual/kvm/api.txt +2 -0
+Note 'irq' is an interrupt vector, not an interrupt pin or line. This
+ioctl is useful if the in-kernel PIC is not used.
maintainer
#5 lore raw

Re: [PATCH v7 4/4] KVM: x86: Add support for local interrupt requests from userspace

In reply to Steve Rutherford
Quoted 18 lines · Steve Rutherford

*/
int kvm_cpu_has_injectable_intr(struct kvm_vcpu *v) {

arch/x86/kvm/irq.c +2 -2
-	if (!irqchip_in_kernel(v->kvm))
+	if (!pic_in_kernel(v->kvm))
 		return v->arch.interrupt.pending;
 
 	if (kvm_cpu_has_extint(v))
@@ -75,7 +88,7 @@ int kvm_cpu_has_injectable_intr(struct kvm_vcpu *v)
  */
 int kvm_cpu_has_interrupt(struct kvm_vcpu *v)
 {
-	if (!irqchip_in_kernel(v->kvm))
+	if (!pic_in_kernel(v->kvm))
 		return v->arch.interrupt.pending;
 
 	if (kvm_cpu_has_extint(v))

arch/x86/kvm/irq.c int kvm_cpu_get_interrupt(struct kvm_vcpu *v) · line 129

Quoted 8 lines
arch/x86/kvm/irq.c +1 -1
@@ -103,7 +123,7 @@ int kvm_cpu_get_interrupt(struct kvm_vcpu *v)
 {
 	int vector;
 
-	if (!irqchip_in_kernel(v->kvm))
+	if (!pic_in_kernel(v->kvm) && v->arch.interrupt.pending)
 		return v->arch.interrupt.nr;
 
 	vector = kvm_cpu_get_extint(v);

I have one more doubt about these three hunks.

v->arch.interrupt should not be used at all with split irqchip. In particular:

- kvm_cpu_has_injectable_intr should go through kvm_cpu_has_extint and query pending_userspace_extint

- same for kvm_cpu_has_interrupt

- kvm_cpu_get_interrupt should go through kvm_cpu_get_extint and return/clear v->arch.pending_external_vector.

So I think !irqchip_in_kernel(v->kvm) is the right test. In particular, with pic_in_kernel, kvm_cpu_has_extint's irqchip_split case is dead. I am then not sure how you reach this code in x86.c:

	/* kvm_cpu_has_injectable_intr doesn't take extints into account? */
        } else if (kvm_cpu_has_injectable_intr(vcpu)) {
                /*
                 * Because interrupts can be injected asynchronously, we are
                 * calling check_nested_events again here to avoid a race condition.
                 * See https://lkml.org/lkml/2014/7/2/60 for discussion about this
                 * proposal and current concerns.  Perhaps we should be setting
                 * KVM_REQ_EVENT only on certain events and not unconditionally?
                 */
                if (is_guest_mode(vcpu) && kvm_x86_ops->check_nested_events) {
                        r = kvm_x86_ops->check_nested_events(vcpu, req_int_win);
                        if (r != 0)
                                return r;
                }
                if (kvm_x86_ops->interrupt_allowed(vcpu)) {
			/*
			 * kvm_cpu_get_interrupt does take extints into account
			 * because of the " && v->arch.interrupt.pending", but
			 * you won't get here unless you have an APIC interrupt
			 * pending!
			 */
                        kvm_queue_interrupt(vcpu, kvm_cpu_get_interrupt(vcpu),
                                            false);
                        kvm_x86_ops->set_irq(vcpu);
                }
        }

Paolo

maintainer
#6 lore raw
In reply to Steve Rutherford
Quoted 119 lines · Steve Rutherford

First patch in a series which enables the relocation of the PIC/IOAPIC to userspace.

Adds capability KVM_CAP_SPLIT_IRQCHIP;

KVM_CAP_SPLIT_IRQCHIP enables the construction of LAPICs without the rest of the irqchip.

Compile tested for x86.

---
 Documentation/virtual/kvm/api.txt | 15 +++++++++++++++
 arch/x86/include/asm/kvm_host.h   |  2 ++
 arch/x86/kvm/i8254.c              |  4 +++-
 arch/x86/kvm/ioapic.h             |  8 ++++++++
 arch/x86/kvm/irq.h                | 11 ++++++++++-
 arch/x86/kvm/irq_comm.c           |  9 ++++++++-
 arch/x86/kvm/lapic.c              |  6 ++++--
 arch/x86/kvm/vmx.c                |  4 ++--
 arch/x86/kvm/x86.c                | 25 +++++++++++++++++++++++--
 include/linux/kvm_host.h          |  1 +
 include/uapi/linux/kvm.h          |  1 +
 11 files changed, 77 insertions(+), 9 deletions(-)
Documentation/virtual/kvm/api.txt +15 -0
index a4ebcb7..b655024 100644
@@ -3620,6 +3620,21 @@ struct {
 
 KVM handlers should exit to userspace with rc = -EREMOTE.
 
+7.5 KVM_SPLIT_IRQCHIP
+
+Architectures: x86
+Parameters: None
+Returns: 0 on success, -1 on error
+
+Create a local apic for each processor in the kernel. With this capability
+enabled, the userspace VMM is expected to emulate the IOAPIC and PIC.
+
+This supersedes KVM_CREATE_IRQCHIP, creating only local APICs, but no in kernel
+IOAPIC or PIC. This also enables in kernel routing of interrupt requests.
+
+Fails if VCPU has already been created, or if the irqchip is already in the
+kernel (i.e. KVM_CREATE_IRQCHIP has already been called).
+
 
 8. Other capabilities.
 ----------------------
arch/x86/include/asm/kvm_host.h +2 -0
index d3e7a53..b4fdf0c 100644
@@ -670,6 +670,8 @@ struct kvm_arch {
 	bool boot_vcpu_runs_old_kvmclock;
 
 	u64 disabled_quirks;
+
+	bool irqchip_split;
 };
 
 struct kvm_vm_stat {
arch/x86/kvm/i8254.c +3 -1
index f588eb7..08116ff 100644
@@ -35,6 +35,7 @@
 #include <linux/kvm_host.h>
 #include <linux/slab.h>
 
+#include "ioapic.h"
 #include "irq.h"
 #include "i8254.h"
 #include "x86.h"
@@ -333,7 +334,8 @@ static void create_pit_timer(struct kvm *kvm, u32 val, int is_period)
 	struct kvm_kpit_state *ps = &kvm->arch.vpit->pit_state;
 	s64 interval;
 
-	if (ps->flags & KVM_PIT_FLAGS_HPET_LEGACY)
+	if (!ioapic_in_kernel(kvm) ||
+	    ps->flags & KVM_PIT_FLAGS_HPET_LEGACY)
 		return;
 
 	interval = muldiv64(val, NSEC_PER_SEC, KVM_PIT_FREQ);
arch/x86/kvm/ioapic.h +8 -0
index bf36d66..a8842c0 100644
@@ -97,6 +97,14 @@ static inline struct kvm_ioapic *ioapic_irqchip(struct kvm *kvm)
 	return kvm->arch.vioapic;
 }
 
+static inline int ioapic_in_kernel(struct kvm *kvm)
+{
+	int ret;
+
+	ret = (ioapic_irqchip(kvm) != NULL);
+	return ret;
+}
+
 void kvm_rtc_eoi_tracking_restore_one(struct kvm_vcpu *vcpu);
 bool kvm_apic_match_dest(struct kvm_vcpu *vcpu, struct kvm_lapic *source,
 		int short_hand, unsigned int dest, int dest_mode);
arch/x86/kvm/irq.h +5 -0
index 3d782a2..72af5a9 100644
@@ -83,13 +83,22 @@ static inline struct kvm_pic *pic_irqchip(struct kvm *kvm)
 	return kvm->arch.vpic;
 }
 
+static inline int irqchip_split(struct kvm *kvm)
+{
+	return kvm->arch.irqchip_split;
+}
+

arch/x86/kvm/irq.h static inline struct kvm_pic *pic_irqchip(struct kvm *kvm) · line 99

Quoted 10 lines
arch/x86/kvm/irq.h +3 -1
 static inline int irqchip_in_kernel(struct kvm *kvm)
 {
+	bool ret;
 	struct kvm_pic *vpic = pic_irqchip(kvm);
 
 	/* Read vpic before kvm->irq_routing.  */
 	smp_rmb();
-	return vpic != NULL;
+	ret = (vpic != NULL);
+	ret |= irqchip_split(kvm);

The memory barrier must be after irqchip_split.

Quoted 34 lines
arch/x86/kvm/irq.h +2 -0
+
+	return ret;
 }
 
 void kvm_pic_reset(struct kvm_kpic_state *s);
arch/x86/kvm/irq_comm.c +8 -1
index 9efff9e..67f6b62 100644
@@ -208,7 +208,7 @@ void kvm_free_irq_source_id(struct kvm *kvm, int irq_source_id)
 		goto unlock;
 	}
 	clear_bit(irq_source_id, &kvm->arch.irq_sources_bitmap);
-	if (!irqchip_in_kernel(kvm))
+	if (!ioapic_in_kernel(kvm))
 		goto unlock;
 
 	kvm_ioapic_clear_all(kvm->arch.vioapic, irq_source_id);
@@ -328,3 +328,10 @@ int kvm_setup_default_irq_routing(struct kvm *kvm)
 	return kvm_set_irq_routing(kvm, default_routing,
 				   ARRAY_SIZE(default_routing), 0);
 }
+
+static const struct kvm_irq_routing_entry empty_routing[] = {};
+
+int kvm_setup_empty_irq_routing(struct kvm *kvm)
+{
+	return kvm_set_irq_routing(kvm, empty_routing, 0, 0);
+}
arch/x86/kvm/lapic.c +0 -0
index c158f46..2f486d8 100644

arch/x86/kvm/lapic.c out: · line 212

Quoted 5 lines
arch/x86/kvm/lapic.c +1 -1
@@ -209,7 +209,8 @@ out:
 	if (old)
 		kfree_rcu(old, rcu);
 
-	kvm_vcpu_request_scan_ioapic(kvm);
+	if (!irqchip_split(kvm))

Might as well use ioapic_in_kernel here.

arch/x86/kvm/lapic.c void kvm_apic_post_state_restore(struct kvm_vcpu *vcpu, · line 1849

Quoted 10 lines
arch/x86/kvm/lapic.c +2 -1
+		kvm_vcpu_request_scan_ioapic(kvm);
 }
 
 static inline void apic_set_spiv(struct kvm_lapic *apic, u32 val)
@@ -1845,7 +1846,8 @@ void kvm_apic_post_state_restore(struct kvm_vcpu *vcpu,
 		kvm_x86_ops->hwapic_isr_update(vcpu->kvm,
 				apic_find_highest_isr(apic));
 	kvm_make_request(KVM_REQ_EVENT, vcpu);
-	kvm_rtc_eoi_tracking_restore_one(vcpu);
+	if (!ioapic_in_kernel(vcpu->kvm))

This should not be negated.

Quoted 39 lines
arch/x86/kvm/lapic.c +1 -0
+		kvm_rtc_eoi_tracking_restore_one(vcpu);
 }
 
 void __kvm_migrate_apic_timer(struct kvm_vcpu *vcpu)
arch/x86/kvm/vmx.c +2 -2
index 7323ea8..3a9653f 100644
@@ -948,7 +948,7 @@ static inline bool cpu_has_vmx_tpr_shadow(void)
 
 static inline bool vm_need_tpr_shadow(struct kvm *kvm)
 {
-	return (cpu_has_vmx_tpr_shadow()) && (irqchip_in_kernel(kvm));
+	return (cpu_has_vmx_tpr_shadow()) && irqchip_in_kernel(kvm);
 }
 
 static inline bool cpu_has_secondary_exec_ctrls(void)
@@ -9486,7 +9486,7 @@ static void prepare_vmcs02(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12)
 	/* vmcs12's VM_ENTRY_LOAD_IA32_EFER and VM_ENTRY_IA32E_MODE are
 	 * emulated by vmx_set_efer(), below.
 	 */
-	vm_entry_controls_init(vmx, 
+	vm_entry_controls_init(vmx,
 		(vmcs12->vm_entry_controls & ~VM_ENTRY_LOAD_IA32_EFER &
 			~VM_ENTRY_IA32E_MODE) |
 		(vmcs_config.vmentry_ctrl & ~VM_ENTRY_IA32E_MODE));
arch/x86/kvm/x86.c +1 -0
index c6c6e9c..8e40ddf 100644
@@ -2459,6 +2459,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
 	case KVM_CAP_TSC_DEADLINE_TIMER:
 	case KVM_CAP_ENABLE_CAP_VM:
 	case KVM_CAP_DISABLE_QUIRKS:
+	case KVM_CAP_SPLIT_IRQCHIP:
 #ifdef CONFIG_KVM_DEVICE_ASSIGNMENT
 	case KVM_CAP_ASSIGN_DEV_IRQ:
 	case KVM_CAP_PCI_2_3:

arch/x86/kvm/x86.c static int kvm_vm_ioctl_enable_cap(struct kvm *kvm, · line 3575

Quoted 9 lines
arch/x86/kvm/x86.c +6 -0
@@ -3566,6 +3567,26 @@ static int kvm_vm_ioctl_enable_cap(struct kvm *kvm,
 		kvm->arch.disabled_quirks = cap->args[0];
 		r = 0;
 		break;
+	case KVM_CAP_SPLIT_IRQCHIP: {
+		mutex_lock(&kvm->lock);
+		r = -EEXIST;
+		if (irqchip_in_kernel(kvm))
+			goto split_irqchip_unlock;
+		if (!irqchip_split(kvm)) {

This "if" must be true.

Quoted 18 lines
arch/x86/kvm/x86.c +14 -0
+			if (atomic_read(&kvm->online_vcpus))
+				goto split_irqchip_unlock;
+			r = kvm_setup_empty_irq_routing(kvm);
+			if (r)
+				goto split_irqchip_unlock;
+			/* Pairs with irqchip_in_kernel. */
+			smp_wmb();
+			kvm->arch.irqchip_split = true;
+		}
+		r = 0;
+split_irqchip_unlock:
+		mutex_unlock(&kvm->lock);
+		break;
+	}
 	default:
 		r = -EINVAL;
 		break;

arch/x86/kvm/x86.c long kvm_arch_vm_ioctl(struct file *filp, · line 3703

Quoted 5 lines
arch/x86/kvm/x86.c +1 -1
@@ -3679,7 +3700,7 @@ long kvm_arch_vm_ioctl(struct file *filp,
 		}
 
 		r = -ENXIO;
-		if (!irqchip_in_kernel(kvm))
+		if (!irqchip_in_kernel(kvm) || !ioapic_in_kernel(kvm))

You're using ioapic_in_kernel for the 8259 too. Better test !irqchip_in_kernel || irqchip_split here.

arch/x86/kvm/x86.c long kvm_arch_vm_ioctl(struct file *filp, · line 3727

Quoted 9 lines
arch/x86/kvm/x86.c +1 -1
 			goto get_irqchip_out;
 		r = kvm_vm_ioctl_get_irqchip(kvm, chip);
 		if (r)
@@ -3703,7 +3724,7 @@ long kvm_arch_vm_ioctl(struct file *filp,
 		}
 
 		r = -ENXIO;
-		if (!irqchip_in_kernel(kvm))
+		if (!irqchip_in_kernel(kvm) || !ioapic_in_kernel(kvm))

Same here.

Quoted 21 lines
arch/x86/kvm/x86.c +0 -0
 			goto set_irqchip_out;
 		r = kvm_vm_ioctl_set_irqchip(kvm, chip);
 		if (r)
include/linux/kvm_host.h +1 -0
index 29c0c28..1867b83 100644
@@ -1004,6 +1004,7 @@ static inline int mmu_notifier_retry(struct kvm *kvm, unsigned long mmu_seq)
 #endif
 
 int kvm_setup_default_irq_routing(struct kvm *kvm);
+int kvm_setup_empty_irq_routing(struct kvm *kvm);
 int kvm_set_irq_routing(struct kvm *kvm,
 			const struct kvm_irq_routing_entry *entries,
 			unsigned nr,
include/uapi/linux/kvm.h +0 -0
index 9ef19eb..e4304d0 100644

include/uapi/linux/kvm.h struct kvm_ppc_smmu_info { · line 824

Quoted 7 lines
include/uapi/linux/kvm.h +1 -0
@@ -818,6 +818,7 @@ struct kvm_ppc_smmu_info {
 #define KVM_CAP_DISABLE_QUIRKS 116
 #define KVM_CAP_X86_SMM 117
 #define KVM_CAP_MULTI_ADDRESS_SPACE 118
+#define KVM_CAP_SPLIT_IRQCHIP 119
 
 #ifdef KVM_CAP_IRQ_ROUTING
 

Please review this diff:

arch/x86/kvm/irq.h +2 -2
index 72af5a989a2e..975cf33ef306 100644
@@ -93,11 +90,11 @@ static inline int irqchip_split(struct kvm *kvm)
 	bool ret;
 	struct kvm_pic *vpic = pic_irqchip(kvm);
 
-	/* Read vpic before kvm->irq_routing.  */
-	smp_rmb();
 	ret = (vpic != NULL);
 	ret |= irqchip_split(kvm);
 
+	/* Read vpic before kvm->irq_routing.  */
+	smp_rmb();
 	return ret;
 }
 
arch/x86/kvm/lapic.c +2 -2
index 2f486d8ecdae..a86324ca9cc3 100644
@@ -209,7 +209,7 @@ out:
 	if (old)
 		kfree_rcu(old, rcu);
 
-	if (!irqchip_split(kvm))
+	if (ioapic_in_kernel(kvm))
 		kvm_vcpu_request_scan_ioapic(kvm);
 }
 
@@ -1846,7 +1846,7 @@ void kvm_apic_post_state_restore(struct kvm_vcpu *vcpu,
 		kvm_x86_ops->hwapic_isr_update(vcpu->kvm,
 				apic_find_highest_isr(apic));
 	kvm_make_request(KVM_REQ_EVENT, vcpu);
-	if (!ioapic_in_kernel(vcpu->kvm))
+	if (ioapic_in_kernel(vcpu->kvm))
 		kvm_rtc_eoi_tracking_restore_one(vcpu);
 }
 
arch/x86/kvm/x86.c +10 -12
index 49a98608e3f6..5d2b8695732c 100644
@@ -3573,16 +3573,14 @@ static int kvm_vm_ioctl_enable_cap(struct kvm *kvm,
 		r = -EEXIST;
 		if (irqchip_in_kernel(kvm))
 			goto split_irqchip_unlock;
-		if (!irqchip_split(kvm)) {
-			if (atomic_read(&kvm->online_vcpus))
-				goto split_irqchip_unlock;
-			r = kvm_setup_empty_irq_routing(kvm);
-			if (r)
-				goto split_irqchip_unlock;
-			/* Pairs with irqchip_in_kernel. */
-			smp_wmb();
-			kvm->arch.irqchip_split = true;
-		}
+		if (atomic_read(&kvm->online_vcpus))
+			goto split_irqchip_unlock;
+		r = kvm_setup_empty_irq_routing(kvm);
+		if (r)
+			goto split_irqchip_unlock;
+		/* Pairs with irqchip_in_kernel. */
+		smp_wmb();
+		kvm->arch.irqchip_split = true;
 		r = 0;
 split_irqchip_unlock:
 		mutex_unlock(&kvm->lock);
@@ -3701,7 +3699,7 @@ long kvm_arch_vm_ioctl(struct file *filp,
 		}
 
 		r = -ENXIO;
-		if (!irqchip_in_kernel(kvm) || !ioapic_in_kernel(kvm))
+		if (!irqchip_in_kernel(kvm) || irqchip_split(kvm))
 			goto get_irqchip_out;
 		r = kvm_vm_ioctl_get_irqchip(kvm, chip);
 		if (r)
@@ -3725,7 +3723,7 @@ long kvm_arch_vm_ioctl(struct file *filp,
 		}
 
 		r = -ENXIO;
-		if (!irqchip_in_kernel(kvm) || !ioapic_in_kernel(kvm))
+		if (!irqchip_in_kernel(kvm) || irqchip_split(kvm))
 			goto set_irqchip_out;
 		r = kvm_vm_ioctl_set_irqchip(kvm, chip);
 		if (r)

No need to resend.

Paolo

#7 lore raw
Quoted 58 lines · Paolo Bonzini

Please review this diff:

arch/x86/kvm/irq.h +2 -2
index 72af5a989a2e..975cf33ef306 100644
@@ -93,11 +90,11 @@ static inline int irqchip_split(struct kvm *kvm)
 	bool ret;
 	struct kvm_pic *vpic = pic_irqchip(kvm);
 
-	/* Read vpic before kvm->irq_routing.  */
-	smp_rmb();
 	ret = (vpic != NULL);
 	ret |= irqchip_split(kvm);
 
+	/* Read vpic before kvm->irq_routing.  */
+	smp_rmb();
 	return ret;
 }
 
arch/x86/kvm/lapic.c +2 -2
index 2f486d8ecdae..a86324ca9cc3 100644
@@ -209,7 +209,7 @@ out:
 	if (old)
 		kfree_rcu(old, rcu);
 
-	if (!irqchip_split(kvm))
+	if (ioapic_in_kernel(kvm))
 		kvm_vcpu_request_scan_ioapic(kvm);
 }
 
@@ -1846,7 +1846,7 @@ void kvm_apic_post_state_restore(struct kvm_vcpu *vcpu,
 		kvm_x86_ops->hwapic_isr_update(vcpu->kvm,
 				apic_find_highest_isr(apic));
 	kvm_make_request(KVM_REQ_EVENT, vcpu);
-	if (!ioapic_in_kernel(vcpu->kvm))
+	if (ioapic_in_kernel(vcpu->kvm))
 		kvm_rtc_eoi_tracking_restore_one(vcpu);
 }
 
arch/x86/kvm/x86.c +0 -8
index 49a98608e3f6..5d2b8695732c 100644
@@ -3573,16 +3573,14 @@ static int kvm_vm_ioctl_enable_cap(struct kvm *kvm,
 		r = -EEXIST;
 		if (irqchip_in_kernel(kvm))
 			goto split_irqchip_unlock;
-		if (!irqchip_split(kvm)) {
-			if (atomic_read(&kvm->online_vcpus))
-				goto split_irqchip_unlock;
-			r = kvm_setup_empty_irq_routing(kvm);
-			if (r)
-				goto split_irqchip_unlock;
-			/* Pairs with irqchip_in_kernel. */
-			smp_wmb();

arch/x86/kvm/x86.c static int kvm_vm_ioctl_enable_cap(struct kvm *kvm, · line 3583

Quoted 10 lines
arch/x86/kvm/x86.c +8 -2
-			kvm->arch.irqchip_split = true;
-		}
+		if (atomic_read(&kvm->online_vcpus))
+			goto split_irqchip_unlock;
+		r = kvm_setup_empty_irq_routing(kvm);
+		if (r)
+			goto split_irqchip_unlock;
+		/* Pairs with irqchip_in_kernel. */
+		smp_wmb();
+		kvm->arch.irqchip_split = true;

This looks a bit non-sensical, but is overprepared for the introduction IOAPIC hotplug, which two patches down the line. Changing it is fine, you'll just need to merge the very same change back.

Quoted 22 lines
arch/x86/kvm/x86.c +2 -2
 		r = 0;
 split_irqchip_unlock:
 		mutex_unlock(&kvm->lock);
@@ -3701,7 +3699,7 @@ long kvm_arch_vm_ioctl(struct file *filp,
 		}
 
 		r = -ENXIO;
-		if (!irqchip_in_kernel(kvm) || !ioapic_in_kernel(kvm))
+		if (!irqchip_in_kernel(kvm) || irqchip_split(kvm))
 			goto get_irqchip_out;
 		r = kvm_vm_ioctl_get_irqchip(kvm, chip);
 		if (r)
@@ -3725,7 +3723,7 @@ long kvm_arch_vm_ioctl(struct file *filp,
 		}
 
 		r = -ENXIO;
-		if (!irqchip_in_kernel(kvm) || !ioapic_in_kernel(kvm))
+		if (!irqchip_in_kernel(kvm) || irqchip_split(kvm))
 			goto set_irqchip_out;
 		r = kvm_vm_ioctl_set_irqchip(kvm, chip);
 		if (r)
Quoted 3 lines

No need to resend.

Paolo

Looks good.

maintainer
#8 lore raw
Quoted 3 lines · Steve Rutherford

This looks a bit non-sensical, but is overprepared for the introduction IOAPIC hotplug, which two patches down the line. Changing it is fine, you'll just need to merge the very same change back.

By "IOAPIC hotplug" you mean changing the number of reserved routes? Is it necessary? You could just reserve a bunch of routes depending on the maximum number of IOAPICs.

And especially, is it documented? :) The docs say "Fails [...] if the irqchip is already in the kernel (i.e. KVM_CREATE_IRQCHIP has already been called)".

As before, no need to resend patches for now. Let's finish discussing all pending points, then I'll push what I have to kvm.git and you can test it with your VMM. There should be time between this week and the next.

Paolo

#9 lore raw
Quoted 10 lines · Paolo Bonzini
Quoted 3 lines · Steve Rutherford

This looks a bit non-sensical, but is overprepared for the introduction IOAPIC hotplug, which two patches down the line. Changing it is fine, you'll just need to merge the very same change back.

By "IOAPIC hotplug" you mean changing the number of reserved routes? Is it necessary? You could just reserve a bunch of routes depending on the maximum number of IOAPICs.

Hmm. Yeah, I think that might be cleaner. Thinking about it, I'm a bit nervous about the idea of the number of reserved routes shrinking. We would have needed to trigger an IOAPIC scan if the number of reserved routes changed.

Jan might have an opinion here.

Quoted 4 lines

And especially, is it documented? :) The docs say "Fails [...] if the irqchip is already in the kernel (i.e. KVM_CREATE_IRQCHIP has already been called)".

The documentation was out of date D:

Quoted 6 lines

As before, no need to resend patches for now. Let's finish discussing all pending points, then I'll push what I have to kvm.git and you can test it with your VMM. There should be time between this week and the next.

Paolo

#10 lore raw
Quoted 8 lines · Steve Rutherford
Quoted 7 lines · Paolo Bonzini
Quoted 3 lines · Steve Rutherford

This looks a bit non-sensical, but is overprepared for the introduction IOAPIC hotplug, which two patches down the line. Changing it is fine, you'll just need to merge the very same change back.

Quoted 8 lines
Quoted 3 lines

By "IOAPIC hotplug" you mean changing the number of reserved routes? Is it necessary? You could just reserve a bunch of routes depending on the maximum number of IOAPICs.

Hmm. Yeah, I think that might be cleaner. Thinking about it, I'm a bit nervous about the idea of the number of reserved routes shrinking. We would have needed to trigger an IOAPIC scan if the number of reserved routes changed.

Jan might have an opinion here.

A static preallocation is likely fine, given reasonable room. I have no idea about a good limit, though. To be safe, we could pull in someone from Intel, maybe the guy who worked on the IOAPIC refactorings in the kernel to enable hotplugging.

Jan

Signature · 2 lines
Siemens AG, Corporate Technology, CT RTC ITP SES-DE
Corporate Competence Center Embedded Linux