Commit 49df6397 authored by Steve Rutherford's avatar Steve Rutherford Committed by Paolo Bonzini

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.
Signed-off-by: default avatarSteve Rutherford <srutherford@google.com>
Suggested-by: default avatarAndrew Honig <ahonig@google.com>
Signed-off-by: default avatarPaolo Bonzini <pbonzini@redhat.com>
parent 4ca7dd8c
...@@ -3627,6 +3627,23 @@ struct { ...@@ -3627,6 +3627,23 @@ struct {
KVM handlers should exit to userspace with rc = -EREMOTE. KVM handlers should exit to userspace with rc = -EREMOTE.
7.5 KVM_CAP_SPLIT_IRQCHIP
Architectures: x86
Parameters: None
Returns: 0 on success, -1 on error
Create a local apic for each processor in the kernel. This can be used
instead of KVM_CREATE_IRQCHIP if the userspace VMM wishes to emulate the
IOAPIC and PIC (and also the PIT, even though this has to be enabled
separately).
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. 8. Other capabilities.
---------------------- ----------------------
......
...@@ -684,6 +684,8 @@ struct kvm_arch { ...@@ -684,6 +684,8 @@ struct kvm_arch {
u32 bsp_vcpu_id; u32 bsp_vcpu_id;
u64 disabled_quirks; u64 disabled_quirks;
bool irqchip_split;
}; };
struct kvm_vm_stat { struct kvm_vm_stat {
......
...@@ -35,6 +35,7 @@ ...@@ -35,6 +35,7 @@
#include <linux/kvm_host.h> #include <linux/kvm_host.h>
#include <linux/slab.h> #include <linux/slab.h>
#include "ioapic.h"
#include "irq.h" #include "irq.h"
#include "i8254.h" #include "i8254.h"
#include "x86.h" #include "x86.h"
...@@ -333,7 +334,8 @@ static void create_pit_timer(struct kvm *kvm, u32 val, int is_period) ...@@ -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; struct kvm_kpit_state *ps = &kvm->arch.vpit->pit_state;
s64 interval; s64 interval;
if (!irqchip_in_kernel(kvm) || ps->flags & KVM_PIT_FLAGS_HPET_LEGACY) if (!ioapic_in_kernel(kvm) ||
ps->flags & KVM_PIT_FLAGS_HPET_LEGACY)
return; return;
interval = muldiv64(val, NSEC_PER_SEC, KVM_PIT_FREQ); interval = muldiv64(val, NSEC_PER_SEC, KVM_PIT_FREQ);
......
...@@ -97,6 +97,14 @@ static inline struct kvm_ioapic *ioapic_irqchip(struct kvm *kvm) ...@@ -97,6 +97,14 @@ static inline struct kvm_ioapic *ioapic_irqchip(struct kvm *kvm)
return kvm->arch.vioapic; 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); void kvm_rtc_eoi_tracking_restore_one(struct kvm_vcpu *vcpu);
bool kvm_apic_match_dest(struct kvm_vcpu *vcpu, struct kvm_lapic *source, bool kvm_apic_match_dest(struct kvm_vcpu *vcpu, struct kvm_lapic *source,
int short_hand, unsigned int dest, int dest_mode); int short_hand, unsigned int dest, int dest_mode);
......
...@@ -83,13 +83,22 @@ static inline struct kvm_pic *pic_irqchip(struct kvm *kvm) ...@@ -83,13 +83,22 @@ static inline struct kvm_pic *pic_irqchip(struct kvm *kvm)
return kvm->arch.vpic; 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) static inline int irqchip_in_kernel(struct kvm *kvm)
{ {
struct kvm_pic *vpic = pic_irqchip(kvm); struct kvm_pic *vpic = pic_irqchip(kvm);
bool ret;
ret = (vpic != NULL);
ret |= irqchip_split(kvm);
/* Read vpic before kvm->irq_routing. */ /* Read vpic before kvm->irq_routing. */
smp_rmb(); smp_rmb();
return vpic != NULL; return ret;
} }
static inline int lapic_in_kernel(struct kvm_vcpu *vcpu) static inline int lapic_in_kernel(struct kvm_vcpu *vcpu)
......
...@@ -208,7 +208,7 @@ void kvm_free_irq_source_id(struct kvm *kvm, int irq_source_id) ...@@ -208,7 +208,7 @@ void kvm_free_irq_source_id(struct kvm *kvm, int irq_source_id)
goto unlock; goto unlock;
} }
clear_bit(irq_source_id, &kvm->arch.irq_sources_bitmap); clear_bit(irq_source_id, &kvm->arch.irq_sources_bitmap);
if (!irqchip_in_kernel(kvm)) if (!ioapic_in_kernel(kvm))
goto unlock; goto unlock;
kvm_ioapic_clear_all(kvm->arch.vioapic, irq_source_id); kvm_ioapic_clear_all(kvm->arch.vioapic, irq_source_id);
...@@ -328,3 +328,10 @@ int kvm_setup_default_irq_routing(struct kvm *kvm) ...@@ -328,3 +328,10 @@ int kvm_setup_default_irq_routing(struct kvm *kvm)
return kvm_set_irq_routing(kvm, default_routing, return kvm_set_irq_routing(kvm, default_routing,
ARRAY_SIZE(default_routing), 0); 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);
}
...@@ -209,7 +209,8 @@ static void recalculate_apic_map(struct kvm *kvm) ...@@ -209,7 +209,8 @@ static void recalculate_apic_map(struct kvm *kvm)
if (old) if (old)
kfree_rcu(old, rcu); kfree_rcu(old, rcu);
kvm_vcpu_request_scan_ioapic(kvm); if (ioapic_in_kernel(kvm))
kvm_vcpu_request_scan_ioapic(kvm);
} }
static inline void apic_set_spiv(struct kvm_lapic *apic, u32 val) 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, ...@@ -1845,7 +1846,8 @@ void kvm_apic_post_state_restore(struct kvm_vcpu *vcpu,
kvm_x86_ops->hwapic_isr_update(vcpu->kvm, kvm_x86_ops->hwapic_isr_update(vcpu->kvm,
apic_find_highest_isr(apic)); apic_find_highest_isr(apic));
kvm_make_request(KVM_REQ_EVENT, vcpu); 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) void __kvm_migrate_apic_timer(struct kvm_vcpu *vcpu)
......
...@@ -2448,6 +2448,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext) ...@@ -2448,6 +2448,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
case KVM_CAP_ENABLE_CAP_VM: case KVM_CAP_ENABLE_CAP_VM:
case KVM_CAP_DISABLE_QUIRKS: case KVM_CAP_DISABLE_QUIRKS:
case KVM_CAP_SET_BOOT_CPU_ID: case KVM_CAP_SET_BOOT_CPU_ID:
case KVM_CAP_SPLIT_IRQCHIP:
#ifdef CONFIG_KVM_DEVICE_ASSIGNMENT #ifdef CONFIG_KVM_DEVICE_ASSIGNMENT
case KVM_CAP_ASSIGN_DEV_IRQ: case KVM_CAP_ASSIGN_DEV_IRQ:
case KVM_CAP_PCI_2_3: case KVM_CAP_PCI_2_3:
...@@ -3555,6 +3556,24 @@ static int kvm_vm_ioctl_enable_cap(struct kvm *kvm, ...@@ -3555,6 +3556,24 @@ static int kvm_vm_ioctl_enable_cap(struct kvm *kvm,
kvm->arch.disabled_quirks = cap->args[0]; kvm->arch.disabled_quirks = cap->args[0];
r = 0; r = 0;
break; break;
case KVM_CAP_SPLIT_IRQCHIP: {
mutex_lock(&kvm->lock);
r = -EEXIST;
if (irqchip_in_kernel(kvm))
goto split_irqchip_unlock;
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: default:
r = -EINVAL; r = -EINVAL;
break; break;
...@@ -3668,7 +3687,7 @@ long kvm_arch_vm_ioctl(struct file *filp, ...@@ -3668,7 +3687,7 @@ long kvm_arch_vm_ioctl(struct file *filp,
} }
r = -ENXIO; r = -ENXIO;
if (!irqchip_in_kernel(kvm)) if (!irqchip_in_kernel(kvm) || irqchip_split(kvm))
goto get_irqchip_out; goto get_irqchip_out;
r = kvm_vm_ioctl_get_irqchip(kvm, chip); r = kvm_vm_ioctl_get_irqchip(kvm, chip);
if (r) if (r)
...@@ -3692,7 +3711,7 @@ long kvm_arch_vm_ioctl(struct file *filp, ...@@ -3692,7 +3711,7 @@ long kvm_arch_vm_ioctl(struct file *filp,
} }
r = -ENXIO; r = -ENXIO;
if (!irqchip_in_kernel(kvm)) if (!irqchip_in_kernel(kvm) || irqchip_split(kvm))
goto set_irqchip_out; goto set_irqchip_out;
r = kvm_vm_ioctl_set_irqchip(kvm, chip); r = kvm_vm_ioctl_set_irqchip(kvm, chip);
if (r) if (r)
......
...@@ -1002,6 +1002,7 @@ static inline int mmu_notifier_retry(struct kvm *kvm, unsigned long mmu_seq) ...@@ -1002,6 +1002,7 @@ static inline int mmu_notifier_retry(struct kvm *kvm, unsigned long mmu_seq)
#endif #endif
int kvm_setup_default_irq_routing(struct kvm *kvm); 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, int kvm_set_irq_routing(struct kvm *kvm,
const struct kvm_irq_routing_entry *entries, const struct kvm_irq_routing_entry *entries,
unsigned nr, unsigned nr,
......
...@@ -824,6 +824,7 @@ struct kvm_ppc_smmu_info { ...@@ -824,6 +824,7 @@ struct kvm_ppc_smmu_info {
#define KVM_CAP_MULTI_ADDRESS_SPACE 118 #define KVM_CAP_MULTI_ADDRESS_SPACE 118
#define KVM_CAP_GUEST_DEBUG_HW_BPS 119 #define KVM_CAP_GUEST_DEBUG_HW_BPS 119
#define KVM_CAP_GUEST_DEBUG_HW_WPS 120 #define KVM_CAP_GUEST_DEBUG_HW_WPS 120
#define KVM_CAP_SPLIT_IRQCHIP 121
#ifdef KVM_CAP_IRQ_ROUTING #ifdef KVM_CAP_IRQ_ROUTING
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment