diff mbox

[v3,8/9] kvm/x86: Hyper-V synthetic interrupt controller

Message ID 1445530171-20688-1-git-send-email-asmetanin@virtuozzo.com (mailing list archive)
State New, archived
Headers show

Commit Message

Andrey Smetanin Oct. 22, 2015, 4:09 p.m. UTC
SynIC (synthetic interrupt controller) is a lapic extension,
which is controlled via MSRs and maintains for each vCPU
 - 16 synthetic interrupt "lines" (SINT's); each can be configured to
   trigger a specific interrupt vector optionally with auto-EOI
   semantics
 - a message page in the guest memory with 16 256-byte per-SINT message
   slots
 - an event flag page in the guest memory with 16 2048-bit per-SINT
   event flag areas

The host triggers a SINT whenever it delivers a new message to the
corresponding slot or flips an event flag bit in the corresponding area.
The guest informs the host that it can try delivering a message by
explicitly asserting EOI in lapic or writing to End-Of-Message (EOM)
MSR.

The userspace (qemu) triggers interrupts and receives EOM notifications
via irqfd with resampler; for that, a GSI is allocated for each
configured SINT, and irq_routing api is extended to support GSI-SINT
mapping.

Signed-off-by: Andrey Smetanin <asmetanin@virtuozzo.com>
Reviewed-by: Roman Kagan <rkagan@virtuozzo.com>
Signed-off-by: Denis V. Lunev <den@openvz.org>
CC: Vitaly Kuznetsov <vkuznets@redhat.com>
CC: "K. Y. Srinivasan" <kys@microsoft.com>
CC: Gleb Natapov <gleb@kernel.org>
CC: Paolo Bonzini <pbonzini@redhat.com>
CC: Roman Kagan <rkagan@virtuozzo.com>

Changes v3:
* added KVM_CAP_HYPERV_SYNIC and KVM_IRQ_ROUTING_HV_SINT notes into
docs

Changes v2:
* do not use posted interrupts for Hyper-V SynIC AutoEOI vectors
* add Hyper-V SynIC vectors into EOI exit bitmap
* Hyper-V SyniIC SINT msr write logic simplified
---
 Documentation/virtual/kvm/api.txt |  14 ++
 arch/x86/include/asm/kvm_host.h   |  14 ++
 arch/x86/kvm/hyperv.c             | 297 ++++++++++++++++++++++++++++++++++++++
 arch/x86/kvm/hyperv.h             |  21 +++
 arch/x86/kvm/irq_comm.c           |  34 +++++
 arch/x86/kvm/lapic.c              |  18 ++-
 arch/x86/kvm/lapic.h              |   5 +
 arch/x86/kvm/x86.c                |  12 +-
 include/linux/kvm_host.h          |   6 +
 include/uapi/linux/kvm.h          |   8 +
 10 files changed, 421 insertions(+), 8 deletions(-)

Comments

Paolo Bonzini Oct. 28, 2015, 5:41 p.m. UTC | #1
Hi Andrey,

just one question.  Is kvm_arch_set_irq actually needed?  I think
everything should work fine without it.  Can you check?  If so, I can
remove it myself and revert the patch that introduced the hook.

Paolo

On 22/10/2015 18:09, Andrey Smetanin wrote:
> SynIC (synthetic interrupt controller) is a lapic extension,
> which is controlled via MSRs and maintains for each vCPU
>  - 16 synthetic interrupt "lines" (SINT's); each can be configured to
>    trigger a specific interrupt vector optionally with auto-EOI
>    semantics
>  - a message page in the guest memory with 16 256-byte per-SINT message
>    slots
>  - an event flag page in the guest memory with 16 2048-bit per-SINT
>    event flag areas
> 
> The host triggers a SINT whenever it delivers a new message to the
> corresponding slot or flips an event flag bit in the corresponding area.
> The guest informs the host that it can try delivering a message by
> explicitly asserting EOI in lapic or writing to End-Of-Message (EOM)
> MSR.
> 
> The userspace (qemu) triggers interrupts and receives EOM notifications
> via irqfd with resampler; for that, a GSI is allocated for each
> configured SINT, and irq_routing api is extended to support GSI-SINT
> mapping.
> 
> Signed-off-by: Andrey Smetanin <asmetanin@virtuozzo.com>
> Reviewed-by: Roman Kagan <rkagan@virtuozzo.com>
> Signed-off-by: Denis V. Lunev <den@openvz.org>
> CC: Vitaly Kuznetsov <vkuznets@redhat.com>
> CC: "K. Y. Srinivasan" <kys@microsoft.com>
> CC: Gleb Natapov <gleb@kernel.org>
> CC: Paolo Bonzini <pbonzini@redhat.com>
> CC: Roman Kagan <rkagan@virtuozzo.com>
> 
> Changes v3:
> * added KVM_CAP_HYPERV_SYNIC and KVM_IRQ_ROUTING_HV_SINT notes into
> docs
> 
> Changes v2:
> * do not use posted interrupts for Hyper-V SynIC AutoEOI vectors
> * add Hyper-V SynIC vectors into EOI exit bitmap
> * Hyper-V SyniIC SINT msr write logic simplified
> ---
>  Documentation/virtual/kvm/api.txt |  14 ++
>  arch/x86/include/asm/kvm_host.h   |  14 ++
>  arch/x86/kvm/hyperv.c             | 297 ++++++++++++++++++++++++++++++++++++++
>  arch/x86/kvm/hyperv.h             |  21 +++
>  arch/x86/kvm/irq_comm.c           |  34 +++++
>  arch/x86/kvm/lapic.c              |  18 ++-
>  arch/x86/kvm/lapic.h              |   5 +
>  arch/x86/kvm/x86.c                |  12 +-
>  include/linux/kvm_host.h          |   6 +
>  include/uapi/linux/kvm.h          |   8 +
>  10 files changed, 421 insertions(+), 8 deletions(-)
> 
> diff --git a/Documentation/virtual/kvm/api.txt b/Documentation/virtual/kvm/api.txt
> index 092ee9f..8710418 100644
> --- a/Documentation/virtual/kvm/api.txt
> +++ b/Documentation/virtual/kvm/api.txt
> @@ -1451,6 +1451,7 @@ struct kvm_irq_routing_entry {
>  		struct kvm_irq_routing_irqchip irqchip;
>  		struct kvm_irq_routing_msi msi;
>  		struct kvm_irq_routing_s390_adapter adapter;
> +		struct kvm_irq_routing_hv_sint hv_sint;
>  		__u32 pad[8];
>  	} u;
>  };
> @@ -1459,6 +1460,7 @@ struct kvm_irq_routing_entry {
>  #define KVM_IRQ_ROUTING_IRQCHIP 1
>  #define KVM_IRQ_ROUTING_MSI 2
>  #define KVM_IRQ_ROUTING_S390_ADAPTER 3
> +#define KVM_IRQ_ROUTING_HV_SINT 4
>  
>  No flags are specified so far, the corresponding field must be set to zero.
>  
> @@ -1482,6 +1484,10 @@ struct kvm_irq_routing_s390_adapter {
>  	__u32 adapter_id;
>  };
>  
> +struct kvm_irq_routing_hv_sint {
> +	__u32 vcpu;
> +	__u32 sint;
> +};
>  
>  4.53 KVM_ASSIGN_SET_MSIX_NR (deprecated)
>  
> @@ -3685,3 +3691,11 @@ available, means that that the kernel has an implementation of the
>  H_RANDOM hypercall backed by a hardware random-number generator.
>  If present, the kernel H_RANDOM handler can be enabled for guest use
>  with the KVM_CAP_PPC_ENABLE_HCALL capability.
> +
> +8.2 KVM_CAP_HYPERV_SYNIC
> +
> +Architectures: x86
> +This capability, if KVM_CHECK_EXTENSION indicates that it is
> +available, means that that the kernel has an implementation of the
> +Hyper-V Synthetic interrupt controller(SynIC). SynIC is used to
> +support Windows Hyper-V based guest paravirt drivers(VMBus).
> diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
> index 3c6327d..8434f88 100644
> --- a/arch/x86/include/asm/kvm_host.h
> +++ b/arch/x86/include/asm/kvm_host.h
> @@ -25,6 +25,7 @@
>  #include <linux/pvclock_gtod.h>
>  #include <linux/clocksource.h>
>  #include <linux/irqbypass.h>
> +#include <linux/hyperv.h>
>  
>  #include <asm/pvclock-abi.h>
>  #include <asm/desc.h>
> @@ -374,10 +375,23 @@ struct kvm_mtrr {
>  	struct list_head head;
>  };
>  
> +/* Hyper-V synthetic interrupt controller (SynIC)*/
> +struct kvm_vcpu_hv_synic {
> +	u64 version;
> +	u64 control;
> +	u64 msg_page;
> +	u64 evt_page;
> +	atomic64_t sint[HV_SYNIC_SINT_COUNT];
> +	atomic_t sint_to_gsi[HV_SYNIC_SINT_COUNT];
> +	DECLARE_BITMAP(auto_eoi_bitmap, 256);
> +	DECLARE_BITMAP(vec_bitmap, 256);
> +};
> +
>  /* Hyper-V per vcpu emulation context */
>  struct kvm_vcpu_hv {
>  	u64 hv_vapic;
>  	s64 runtime_offset;
> +	struct kvm_vcpu_hv_synic synic;
>  };
>  
>  struct kvm_vcpu_arch {
> diff --git a/arch/x86/kvm/hyperv.c b/arch/x86/kvm/hyperv.c
> index 62cf8c9..8ff71f3 100644
> --- a/arch/x86/kvm/hyperv.c
> +++ b/arch/x86/kvm/hyperv.c
> @@ -23,13 +23,296 @@
>  
>  #include "x86.h"
>  #include "lapic.h"
> +#include "ioapic.h"
>  #include "hyperv.h"
>  
>  #include <linux/kvm_host.h>
> +#include <asm/apicdef.h>
>  #include <trace/events/kvm.h>
>  
>  #include "trace.h"
>  
> +static inline u64 synic_read_sint(struct kvm_vcpu_hv_synic *synic, int sint)
> +{
> +	return atomic64_read(&synic->sint[sint]);
> +}
> +
> +static inline int synic_get_sint_vector(u64 sint_value)
> +{
> +	if (sint_value & HV_SYNIC_SINT_MASKED)
> +		return -1;
> +	return sint_value & HV_SYNIC_SINT_VECTOR_MASK;
> +}
> +
> +static bool synic_has_vector_connected(struct kvm_vcpu_hv_synic *synic,
> +				      int vector)
> +{
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(synic->sint); i++) {
> +		if (synic_get_sint_vector(synic_read_sint(synic, i)) == vector)
> +			return true;
> +	}
> +	return false;
> +}
> +
> +static bool synic_has_vector_auto_eoi(struct kvm_vcpu_hv_synic *synic,
> +				     int vector)
> +{
> +	int i;
> +	u64 sint_value;
> +
> +	for (i = 0; i < ARRAY_SIZE(synic->sint); i++) {
> +		sint_value = synic_read_sint(synic, i);
> +		if (synic_get_sint_vector(sint_value) == vector &&
> +		    sint_value & HV_SYNIC_SINT_AUTO_EOI)
> +			return true;
> +	}
> +	return false;
> +}
> +
> +static int synic_set_sint(struct kvm_vcpu_hv_synic *synic, int sint, u64 data)
> +{
> +	int vector;
> +
> +	vector = data & HV_SYNIC_SINT_VECTOR_MASK;
> +	if (vector < 16)
> +		return 1;
> +	/*
> +	 * Guest may configure multiple SINTs to use the same vector, so
> +	 * we maintain a bitmap of vectors handled by synic, and a
> +	 * bitmap of vectors with auto-eoi behavior.  The bitmaps are
> +	 * updated here, and atomically queried on fast paths.
> +	 */
> +
> +	atomic64_set(&synic->sint[sint], data);
> +
> +	if (synic_has_vector_connected(synic, vector))
> +		__set_bit(vector, synic->vec_bitmap);
> +	else
> +		__clear_bit(vector, synic->vec_bitmap);
> +
> +	if (synic_has_vector_auto_eoi(synic, vector))
> +		__set_bit(vector, synic->auto_eoi_bitmap);
> +	else
> +		__clear_bit(vector, synic->auto_eoi_bitmap);
> +
> +	/* Load SynIC vectors into EOI exit bitmap */
> +	kvm_make_request(KVM_REQ_SCAN_IOAPIC, synic_to_vcpu(synic));
> +	return 0;
> +}
> +
> +static struct kvm_vcpu_hv_synic *synic_get(struct kvm *kvm, u32 vcpu_id)
> +{
> +	struct kvm_vcpu *vcpu;
> +
> +	if (vcpu_id >= atomic_read(&kvm->online_vcpus))
> +		return NULL;
> +	vcpu = kvm_get_vcpu(kvm, vcpu_id);
> +	if (!vcpu)
> +		return NULL;
> +
> +	return vcpu_to_synic(vcpu);
> +}
> +
> +static void kvm_hv_notify_acked_sint(struct kvm_vcpu *vcpu, u32 sint)
> +{
> +	struct kvm *kvm = vcpu->kvm;
> +	int gsi, idx;
> +
> +	vcpu_debug(vcpu, "Hyper-V SynIC acked sint %d\n", sint);
> +
> +	idx = srcu_read_lock(&kvm->irq_srcu);
> +	gsi = atomic_read(&vcpu_to_synic(vcpu)->sint_to_gsi[sint]);
> +	if (gsi != -1)
> +		kvm_notify_acked_gsi(kvm, gsi);
> +	srcu_read_unlock(&kvm->irq_srcu, idx);
> +}
> +
> +static int synic_set_msr(struct kvm_vcpu_hv_synic *synic,
> +			 u32 msr, u64 data, bool host)
> +{
> +	struct kvm_vcpu *vcpu = synic_to_vcpu(synic);
> +	int ret;
> +
> +	vcpu_debug(vcpu, "Hyper-V SynIC set msr 0x%x 0x%llx host %d\n",
> +		   msr, data, host);
> +	ret = 0;
> +	switch (msr) {
> +	case HV_X64_MSR_SCONTROL:
> +		synic->control = data;
> +		break;
> +	case HV_X64_MSR_SVERSION:
> +		if (!host) {
> +			ret = 1;
> +			break;
> +		}
> +		synic->version = data;
> +		break;
> +	case HV_X64_MSR_SIEFP:
> +		if (data & HV_SYNIC_SIEFP_ENABLE)
> +			if (kvm_clear_guest(vcpu->kvm,
> +					    data & PAGE_MASK, PAGE_SIZE)) {
> +				ret = 1;
> +				break;
> +			}
> +		synic->evt_page = data;
> +		break;
> +	case HV_X64_MSR_SIMP:
> +		if (data & HV_SYNIC_SIMP_ENABLE)
> +			if (kvm_clear_guest(vcpu->kvm,
> +					    data & PAGE_MASK, PAGE_SIZE)) {
> +				ret = 1;
> +				break;
> +			}
> +		synic->msg_page = data;
> +		break;
> +	case HV_X64_MSR_EOM: {
> +		int i;
> +
> +		for (i = 0; i < ARRAY_SIZE(synic->sint); i++)
> +			kvm_hv_notify_acked_sint(vcpu, i);
> +		break;
> +	}
> +	case HV_X64_MSR_SINT0 ... HV_X64_MSR_SINT15:
> +		ret = synic_set_sint(synic, msr - HV_X64_MSR_SINT0, data);
> +		break;
> +	default:
> +		ret = 1;
> +		break;
> +	}
> +	return ret;
> +}
> +
> +static int synic_get_msr(struct kvm_vcpu_hv_synic *synic, u32 msr, u64 *pdata)
> +{
> +	int ret;
> +
> +	ret = 0;
> +	switch (msr) {
> +	case HV_X64_MSR_SCONTROL:
> +		*pdata = synic->control;
> +		break;
> +	case HV_X64_MSR_SVERSION:
> +		*pdata = synic->version;
> +		break;
> +	case HV_X64_MSR_SIEFP:
> +		*pdata = synic->evt_page;
> +		break;
> +	case HV_X64_MSR_SIMP:
> +		*pdata = synic->msg_page;
> +		break;
> +	case HV_X64_MSR_EOM:
> +		*pdata = 0;
> +		break;
> +	case HV_X64_MSR_SINT0 ... HV_X64_MSR_SINT15:
> +		*pdata = atomic64_read(&synic->sint[msr - HV_X64_MSR_SINT0]);
> +		break;
> +	default:
> +		ret = 1;
> +		break;
> +	}
> +	return ret;
> +}
> +
> +int synic_set_irq(struct kvm_vcpu_hv_synic *synic, u32 sint)
> +{
> +	struct kvm_vcpu *vcpu = synic_to_vcpu(synic);
> +	struct kvm_lapic_irq irq;
> +	int ret, vector;
> +
> +	if (sint >= ARRAY_SIZE(synic->sint))
> +		return -EINVAL;
> +
> +	vector = synic_get_sint_vector(synic_read_sint(synic, sint));
> +	if (vector < 0)
> +		return -ENOENT;
> +
> +	memset(&irq, 0, sizeof(irq));
> +	irq.dest_id = kvm_apic_id(vcpu->arch.apic);
> +	irq.dest_mode = APIC_DEST_PHYSICAL;
> +	irq.delivery_mode = APIC_DM_FIXED;
> +	irq.vector = vector;
> +	irq.level = 1;
> +
> +	ret = kvm_irq_delivery_to_apic(vcpu->kvm, NULL, &irq, NULL);
> +	vcpu_debug(vcpu, "Hyper-V SynIC set irq ret %d\n", ret);
> +	return ret;
> +}
> +
> +int kvm_hv_synic_set_irq(struct kvm *kvm, u32 vcpu_id, u32 sint)
> +{
> +	struct kvm_vcpu_hv_synic *synic;
> +
> +	synic = synic_get(kvm, vcpu_id);
> +	if (!synic)
> +		return -EINVAL;
> +
> +	return synic_set_irq(synic, sint);
> +}
> +
> +void kvm_hv_synic_send_eoi(struct kvm_vcpu *vcpu, int vector)
> +{
> +	struct kvm_vcpu_hv_synic *synic = vcpu_to_synic(vcpu);
> +	int i;
> +
> +	vcpu_debug(vcpu, "Hyper-V SynIC send eoi vec %d\n", vector);
> +
> +	for (i = 0; i < ARRAY_SIZE(synic->sint); i++)
> +		if (synic_get_sint_vector(synic_read_sint(synic, i)) == vector)
> +			kvm_hv_notify_acked_sint(vcpu, i);
> +}
> +
> +static int kvm_hv_set_sint_gsi(struct kvm *kvm, u32 vcpu_id, u32 sint, int gsi)
> +{
> +	struct kvm_vcpu_hv_synic *synic;
> +
> +	synic = synic_get(kvm, vcpu_id);
> +	if (!synic)
> +		return -EINVAL;
> +
> +	if (sint >= ARRAY_SIZE(synic->sint_to_gsi))
> +		return -EINVAL;
> +
> +	atomic_set(&synic->sint_to_gsi[sint], gsi);
> +	return 0;
> +}
> +
> +void kvm_hv_irq_routing_update(struct kvm *kvm)
> +{
> +	struct kvm_irq_routing_table *irq_rt;
> +	struct kvm_kernel_irq_routing_entry *e;
> +	u32 gsi;
> +
> +	irq_rt = srcu_dereference_check(kvm->irq_routing, &kvm->irq_srcu,
> +					lockdep_is_held(&kvm->irq_lock));
> +
> +	for (gsi = 0; gsi < irq_rt->nr_rt_entries; gsi++) {
> +		hlist_for_each_entry(e, &irq_rt->map[gsi], link) {
> +			if (e->type == KVM_IRQ_ROUTING_HV_SINT)
> +				kvm_hv_set_sint_gsi(kvm, e->hv_sint.vcpu,
> +						    e->hv_sint.sint, gsi);
> +		}
> +	}
> +}
> +
> +static void synic_init(struct kvm_vcpu_hv_synic *synic)
> +{
> +	int i;
> +
> +	memset(synic, 0, sizeof(*synic));
> +	synic->version = HV_SYNIC_VERSION_1;
> +	for (i = 0; i < ARRAY_SIZE(synic->sint); i++) {
> +		atomic64_set(&synic->sint[i], HV_SYNIC_SINT_MASKED);
> +		atomic_set(&synic->sint_to_gsi[i], -1);
> +	}
> +}
> +
> +void kvm_hv_vcpu_init(struct kvm_vcpu *vcpu)
> +{
> +	synic_init(vcpu_to_synic(vcpu));
> +}
> +
>  static bool kvm_hv_msr_partition_wide(u32 msr)
>  {
>  	bool r = false;
> @@ -226,6 +509,13 @@ static int kvm_hv_set_msr(struct kvm_vcpu *vcpu, u32 msr, u64 data, bool host)
>  			return 1;
>  		hv->runtime_offset = data - current_task_runtime_100ns();
>  		break;
> +	case HV_X64_MSR_SCONTROL:
> +	case HV_X64_MSR_SVERSION:
> +	case HV_X64_MSR_SIEFP:
> +	case HV_X64_MSR_SIMP:
> +	case HV_X64_MSR_EOM:
> +	case HV_X64_MSR_SINT0 ... HV_X64_MSR_SINT15:
> +		return synic_set_msr(vcpu_to_synic(vcpu), msr, data, host);
>  	default:
>  		vcpu_unimpl(vcpu, "Hyper-V uhandled wrmsr: 0x%x data 0x%llx\n",
>  			    msr, data);
> @@ -304,6 +594,13 @@ static int kvm_hv_get_msr(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata)
>  	case HV_X64_MSR_VP_RUNTIME:
>  		data = current_task_runtime_100ns() + hv->runtime_offset;
>  		break;
> +	case HV_X64_MSR_SCONTROL:
> +	case HV_X64_MSR_SVERSION:
> +	case HV_X64_MSR_SIEFP:
> +	case HV_X64_MSR_SIMP:
> +	case HV_X64_MSR_EOM:
> +	case HV_X64_MSR_SINT0 ... HV_X64_MSR_SINT15:
> +		return synic_get_msr(vcpu_to_synic(vcpu), msr, pdata);
>  	default:
>  		vcpu_unimpl(vcpu, "Hyper-V unhandled rdmsr: 0x%x\n", msr);
>  		return 1;
> diff --git a/arch/x86/kvm/hyperv.h b/arch/x86/kvm/hyperv.h
> index c7bce55..8668612 100644
> --- a/arch/x86/kvm/hyperv.h
> +++ b/arch/x86/kvm/hyperv.h
> @@ -29,4 +29,25 @@ int kvm_hv_get_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata);
>  bool kvm_hv_hypercall_enabled(struct kvm *kvm);
>  int kvm_hv_hypercall(struct kvm_vcpu *vcpu);
>  
> +int kvm_hv_synic_set_irq(struct kvm *kvm, u32 vcpu_id, u32 sint);
> +void kvm_hv_synic_send_eoi(struct kvm_vcpu *vcpu, int vector);
> +
> +static inline struct kvm_vcpu_hv_synic *vcpu_to_synic(struct kvm_vcpu *vcpu)
> +{
> +	return &vcpu->arch.hyperv.synic;
> +}
> +
> +static inline struct kvm_vcpu *synic_to_vcpu(struct kvm_vcpu_hv_synic *synic)
> +{
> +	struct kvm_vcpu_hv *hv;
> +	struct kvm_vcpu_arch *arch;
> +
> +	hv = container_of(synic, struct kvm_vcpu_hv, synic);
> +	arch = container_of(hv, struct kvm_vcpu_arch, hyperv);
> +	return container_of(arch, struct kvm_vcpu, arch);
> +}
> +void kvm_hv_irq_routing_update(struct kvm *kvm);
> +
> +void kvm_hv_vcpu_init(struct kvm_vcpu *vcpu);
> +
>  #endif
> diff --git a/arch/x86/kvm/irq_comm.c b/arch/x86/kvm/irq_comm.c
> index fe91f72..5e195b9 100644
> --- a/arch/x86/kvm/irq_comm.c
> +++ b/arch/x86/kvm/irq_comm.c
> @@ -33,6 +33,8 @@
>  
>  #include "lapic.h"
>  
> +#include "hyperv.h"
> +
>  static int kvm_set_pic_irq(struct kvm_kernel_irq_routing_entry *e,
>  			   struct kvm *kvm, int irq_source_id, int level,
>  			   bool line_status)
> @@ -251,6 +253,16 @@ void kvm_fire_mask_notifiers(struct kvm *kvm, unsigned irqchip, unsigned pin,
>  	srcu_read_unlock(&kvm->irq_srcu, idx);
>  }
>  
> +static int kvm_hv_set_sint(struct kvm_kernel_irq_routing_entry *e,
> +		    struct kvm *kvm, int irq_source_id, int level,
> +		    bool line_status)
> +{
> +	if (!level)
> +		return -1;
> +
> +	return kvm_hv_synic_set_irq(kvm, e->hv_sint.vcpu, e->hv_sint.sint);
> +}
> +
>  int kvm_set_routing_entry(struct kvm_kernel_irq_routing_entry *e,
>  			  const struct kvm_irq_routing_entry *ue)
>  {
> @@ -289,6 +301,11 @@ int kvm_set_routing_entry(struct kvm_kernel_irq_routing_entry *e,
>  		e->msi.address_hi = ue->u.msi.address_hi;
>  		e->msi.data = ue->u.msi.data;
>  		break;
> +	case KVM_IRQ_ROUTING_HV_SINT:
> +		e->set = kvm_hv_set_sint;
> +		e->hv_sint.vcpu = ue->u.hv_sint.vcpu;
> +		e->hv_sint.sint = ue->u.hv_sint.sint;
> +		break;
>  	default:
>  		goto out;
>  	}
> @@ -405,3 +422,20 @@ void kvm_scan_ioapic_routes(struct kvm_vcpu *vcpu,
>  	}
>  	srcu_read_unlock(&kvm->irq_srcu, idx);
>  }
> +
> +int kvm_arch_set_irq(struct kvm_kernel_irq_routing_entry *irq, struct kvm *kvm,
> +		     int irq_source_id, int level, bool line_status)
> +{
> +	switch (irq->type) {
> +	case KVM_IRQ_ROUTING_HV_SINT:
> +		return kvm_hv_set_sint(irq, kvm, irq_source_id, level,
> +				       line_status);
> +	default:
> +		return -EWOULDBLOCK;
> +	}
> +}
> +
> +void kvm_arch_irq_routing_update(struct kvm *kvm)
> +{
> +	kvm_hv_irq_routing_update(kvm);
> +}
> diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c
> index dc03a01..3132478 100644
> --- a/arch/x86/kvm/lapic.c
> +++ b/arch/x86/kvm/lapic.c
> @@ -41,6 +41,7 @@
>  #include "trace.h"
>  #include "x86.h"
>  #include "cpuid.h"
> +#include "hyperv.h"
>  
>  #ifndef CONFIG_X86_64
>  #define mod_64(x, y) ((x) - (y) * div64_u64(x, y))
> @@ -128,11 +129,6 @@ static inline int apic_enabled(struct kvm_lapic *apic)
>  	(LVT_MASK | APIC_MODE_MASK | APIC_INPUT_POLARITY | \
>  	 APIC_LVT_REMOTE_IRR | APIC_LVT_LEVEL_TRIGGER)
>  
> -static inline int kvm_apic_id(struct kvm_lapic *apic)
> -{
> -	return (kvm_apic_get_reg(apic, APIC_ID) >> 24) & 0xff;
> -}
> -
>  /* The logical map is definitely wrong if we have multiple
>   * modes at the same time.  (Physical map is always right.)
>   */
> @@ -850,7 +846,8 @@ static int __apic_accept_irq(struct kvm_lapic *apic, int delivery_mode,
>  				apic_clear_vector(vector, apic->regs + APIC_TMR);
>  		}
>  
> -		if (kvm_x86_ops->deliver_posted_interrupt)
> +		if (kvm_x86_ops->deliver_posted_interrupt &&
> +		    !test_bit(vector, vcpu_to_synic(vcpu)->auto_eoi_bitmap))
>  			kvm_x86_ops->deliver_posted_interrupt(vcpu, vector);
>  		else {
>  			apic_set_irr(vector, apic);
> @@ -972,6 +969,9 @@ static int apic_set_eoi(struct kvm_lapic *apic)
>  	apic_clear_isr(vector, apic);
>  	apic_update_ppr(apic);
>  
> +	if (test_bit(vector, vcpu_to_synic(apic->vcpu)->vec_bitmap))
> +		kvm_hv_synic_send_eoi(apic->vcpu, vector);
> +
>  	kvm_ioapic_send_eoi(apic, vector);
>  	kvm_make_request(KVM_REQ_EVENT, apic->vcpu);
>  	return vector;
> @@ -1881,6 +1881,12 @@ int kvm_get_apic_interrupt(struct kvm_vcpu *vcpu)
>  	apic_set_isr(vector, apic);
>  	apic_update_ppr(apic);
>  	apic_clear_irr(vector, apic);
> +
> +	if (test_bit(vector, vcpu_to_synic(vcpu)->auto_eoi_bitmap)) {
> +		apic_clear_isr(vector, apic);
> +		apic_update_ppr(apic);
> +	}
> +
>  	return vector;
>  }
>  
> diff --git a/arch/x86/kvm/lapic.h b/arch/x86/kvm/lapic.h
> index fde8e35d..6c64090 100644
> --- a/arch/x86/kvm/lapic.h
> +++ b/arch/x86/kvm/lapic.h
> @@ -164,6 +164,11 @@ static inline int kvm_lapic_latched_init(struct kvm_vcpu *vcpu)
>  	return kvm_vcpu_has_lapic(vcpu) && test_bit(KVM_APIC_INIT, &vcpu->arch.apic->pending_events);
>  }
>  
> +static inline int kvm_apic_id(struct kvm_lapic *apic)
> +{
> +	return (kvm_apic_get_reg(apic, APIC_ID) >> 24) & 0xff;
> +}
> +
>  bool kvm_apic_pending_eoi(struct kvm_vcpu *vcpu, int vector);
>  
>  void wait_lapic_expire(struct kvm_vcpu *vcpu);
> diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
> index fb6cfbf..b853b2df 100644
> --- a/arch/x86/kvm/x86.c
> +++ b/arch/x86/kvm/x86.c
> @@ -959,6 +959,7 @@ static u32 emulated_msrs[] = {
>  	HV_X64_MSR_RESET,
>  	HV_X64_MSR_VP_INDEX,
>  	HV_X64_MSR_VP_RUNTIME,
> +	HV_X64_MSR_SCONTROL,
>  	HV_X64_MSR_APIC_ASSIST_PAGE, MSR_KVM_ASYNC_PF_EN, MSR_KVM_STEAL_TIME,
>  	MSR_KVM_PV_EOI_EN,
>  
> @@ -2441,6 +2442,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
>  	case KVM_CAP_HYPERV:
>  	case KVM_CAP_HYPERV_VAPIC:
>  	case KVM_CAP_HYPERV_SPIN:
> +	case KVM_CAP_HYPERV_SYNIC:
>  	case KVM_CAP_PCI_SEGMENT:
>  	case KVM_CAP_DEBUGREGS:
>  	case KVM_CAP_X86_ROBUST_SINGLESTEP:
> @@ -6195,6 +6197,8 @@ static void process_smi(struct kvm_vcpu *vcpu)
>  
>  static void vcpu_scan_ioapic(struct kvm_vcpu *vcpu)
>  {
> +	u64 eoi_exit_bitmap[4];
> +
>  	if (!kvm_apic_hw_enabled(vcpu->arch.apic))
>  		return;
>  
> @@ -6205,8 +6209,10 @@ static void vcpu_scan_ioapic(struct kvm_vcpu *vcpu)
>  	else
>  		kvm_ioapic_scan_entry(vcpu, vcpu->arch.ioapic_handled_vectors);
>  
> -	kvm_x86_ops->load_eoi_exitmap(vcpu,
> -				      (u64 *)vcpu->arch.ioapic_handled_vectors);
> +	bitmap_or((ulong *)eoi_exit_bitmap, vcpu->arch.ioapic_handled_vectors,
> +		  vcpu_to_synic(vcpu)->vec_bitmap, 256);
> +
> +	kvm_x86_ops->load_eoi_exitmap(vcpu, eoi_exit_bitmap);
>  }
>  
>  static void kvm_vcpu_flush_tlb(struct kvm_vcpu *vcpu)
> @@ -7456,6 +7462,8 @@ int kvm_arch_vcpu_init(struct kvm_vcpu *vcpu)
>  
>  	vcpu->arch.pending_external_vector = -1;
>  
> +	kvm_hv_vcpu_init(vcpu);
> +
>  	return 0;
>  
>  fail_free_mce_banks:
> diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
> index c742e79..43b0141 100644
> --- a/include/linux/kvm_host.h
> +++ b/include/linux/kvm_host.h
> @@ -318,6 +318,11 @@ struct kvm_s390_adapter_int {
>  	u32 adapter_id;
>  };
>  
> +struct kvm_hv_sint {
> +	u32 vcpu;
> +	u32 sint;
> +};
> +
>  struct kvm_kernel_irq_routing_entry {
>  	u32 gsi;
>  	u32 type;
> @@ -331,6 +336,7 @@ struct kvm_kernel_irq_routing_entry {
>  		} irqchip;
>  		struct msi_msg msi;
>  		struct kvm_s390_adapter_int adapter;
> +		struct kvm_hv_sint hv_sint;
>  	};
>  	struct hlist_node link;
>  };
> diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
> index 03f3618..27ce460 100644
> --- a/include/uapi/linux/kvm.h
> +++ b/include/uapi/linux/kvm.h
> @@ -831,6 +831,7 @@ struct kvm_ppc_smmu_info {
>  #define KVM_CAP_GUEST_DEBUG_HW_WPS 120
>  #define KVM_CAP_SPLIT_IRQCHIP 121
>  #define KVM_CAP_IOEVENTFD_ANY_LENGTH 122
> +#define KVM_CAP_HYPERV_SYNIC 123
>  
>  #ifdef KVM_CAP_IRQ_ROUTING
>  
> @@ -854,10 +855,16 @@ struct kvm_irq_routing_s390_adapter {
>  	__u32 adapter_id;
>  };
>  
> +struct kvm_irq_routing_hv_sint {
> +	__u32 vcpu;
> +	__u32 sint;
> +};
> +
>  /* gsi routing entry types */
>  #define KVM_IRQ_ROUTING_IRQCHIP 1
>  #define KVM_IRQ_ROUTING_MSI 2
>  #define KVM_IRQ_ROUTING_S390_ADAPTER 3
> +#define KVM_IRQ_ROUTING_HV_SINT 4
>  
>  struct kvm_irq_routing_entry {
>  	__u32 gsi;
> @@ -868,6 +875,7 @@ struct kvm_irq_routing_entry {
>  		struct kvm_irq_routing_irqchip irqchip;
>  		struct kvm_irq_routing_msi msi;
>  		struct kvm_irq_routing_s390_adapter adapter;
> +		struct kvm_irq_routing_hv_sint hv_sint;
>  		__u32 pad[8];
>  	} u;
>  };
> 
--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Roman Kagan Oct. 29, 2015, 8:45 a.m. UTC | #2
On Wed, Oct 28, 2015 at 06:41:45PM +0100, Paolo Bonzini wrote:
> Hi Andrey,
> 
> just one question.  Is kvm_arch_set_irq actually needed?  I think
> everything should work fine without it.  Can you check?  If so, I can
> remove it myself and revert the patch that introduced the hook.

While Andrey is testing it, I'd like to ask similar question re. MSI:
why is there a "shortcut" for KVM_IRQ_ROUTING_MSI case (which we
basically modelled after) when it would probably get handled through
->set handler in irqfd_inject() too?

Roman.
--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Andrey Smetanin Oct. 29, 2015, 8:50 a.m. UTC | #3
On 10/28/2015 08:41 PM, Paolo Bonzini wrote:
> Hi Andrey,
>
> just one question.  Is kvm_arch_set_irq actually needed?  I think
> everything should work fine without it.  Can you check?  If so, I can
> remove it myself and revert the patch that introduced the hook.
>
Hi Paolo,

I have checked that Hyper-V SynIC unit test and some hand-made tests 
with Windows guest(with enabled SynIC) works fine without 
kvm_arch_set_irq. It will be nice
to remove this function.

Thanks
> Paolo
>
> On 22/10/2015 18:09, Andrey Smetanin wrote:
>> SynIC (synthetic interrupt controller) is a lapic extension,
>> which is controlled via MSRs and maintains for each vCPU
>>   - 16 synthetic interrupt "lines" (SINT's); each can be configured to
>>     trigger a specific interrupt vector optionally with auto-EOI
>>     semantics
>>   - a message page in the guest memory with 16 256-byte per-SINT message
>>     slots
>>   - an event flag page in the guest memory with 16 2048-bit per-SINT
>>     event flag areas
>>
>> The host triggers a SINT whenever it delivers a new message to the
>> corresponding slot or flips an event flag bit in the corresponding area.
>> The guest informs the host that it can try delivering a message by
>> explicitly asserting EOI in lapic or writing to End-Of-Message (EOM)
>> MSR.
>>
>> The userspace (qemu) triggers interrupts and receives EOM notifications
>> via irqfd with resampler; for that, a GSI is allocated for each
>> configured SINT, and irq_routing api is extended to support GSI-SINT
>> mapping.
>>
>> Signed-off-by: Andrey Smetanin <asmetanin@virtuozzo.com>
>> Reviewed-by: Roman Kagan <rkagan@virtuozzo.com>
>> Signed-off-by: Denis V. Lunev <den@openvz.org>
>> CC: Vitaly Kuznetsov <vkuznets@redhat.com>
>> CC: "K. Y. Srinivasan" <kys@microsoft.com>
>> CC: Gleb Natapov <gleb@kernel.org>
>> CC: Paolo Bonzini <pbonzini@redhat.com>
>> CC: Roman Kagan <rkagan@virtuozzo.com>
>>
>> Changes v3:
>> * added KVM_CAP_HYPERV_SYNIC and KVM_IRQ_ROUTING_HV_SINT notes into
>> docs
>>
>> Changes v2:
>> * do not use posted interrupts for Hyper-V SynIC AutoEOI vectors
>> * add Hyper-V SynIC vectors into EOI exit bitmap
>> * Hyper-V SyniIC SINT msr write logic simplified
>> ---
>>   Documentation/virtual/kvm/api.txt |  14 ++
>>   arch/x86/include/asm/kvm_host.h   |  14 ++
>>   arch/x86/kvm/hyperv.c             | 297 ++++++++++++++++++++++++++++++++++++++
>>   arch/x86/kvm/hyperv.h             |  21 +++
>>   arch/x86/kvm/irq_comm.c           |  34 +++++
>>   arch/x86/kvm/lapic.c              |  18 ++-
>>   arch/x86/kvm/lapic.h              |   5 +
>>   arch/x86/kvm/x86.c                |  12 +-
>>   include/linux/kvm_host.h          |   6 +
>>   include/uapi/linux/kvm.h          |   8 +
>>   10 files changed, 421 insertions(+), 8 deletions(-)
>>
>> diff --git a/Documentation/virtual/kvm/api.txt b/Documentation/virtual/kvm/api.txt
>> index 092ee9f..8710418 100644
>> --- a/Documentation/virtual/kvm/api.txt
>> +++ b/Documentation/virtual/kvm/api.txt
>> @@ -1451,6 +1451,7 @@ struct kvm_irq_routing_entry {
>>   		struct kvm_irq_routing_irqchip irqchip;
>>   		struct kvm_irq_routing_msi msi;
>>   		struct kvm_irq_routing_s390_adapter adapter;
>> +		struct kvm_irq_routing_hv_sint hv_sint;
>>   		__u32 pad[8];
>>   	} u;
>>   };
>> @@ -1459,6 +1460,7 @@ struct kvm_irq_routing_entry {
>>   #define KVM_IRQ_ROUTING_IRQCHIP 1
>>   #define KVM_IRQ_ROUTING_MSI 2
>>   #define KVM_IRQ_ROUTING_S390_ADAPTER 3
>> +#define KVM_IRQ_ROUTING_HV_SINT 4
>>
>>   No flags are specified so far, the corresponding field must be set to zero.
>>
>> @@ -1482,6 +1484,10 @@ struct kvm_irq_routing_s390_adapter {
>>   	__u32 adapter_id;
>>   };
>>
>> +struct kvm_irq_routing_hv_sint {
>> +	__u32 vcpu;
>> +	__u32 sint;
>> +};
>>
>>   4.53 KVM_ASSIGN_SET_MSIX_NR (deprecated)
>>
>> @@ -3685,3 +3691,11 @@ available, means that that the kernel has an implementation of the
>>   H_RANDOM hypercall backed by a hardware random-number generator.
>>   If present, the kernel H_RANDOM handler can be enabled for guest use
>>   with the KVM_CAP_PPC_ENABLE_HCALL capability.
>> +
>> +8.2 KVM_CAP_HYPERV_SYNIC
>> +
>> +Architectures: x86
>> +This capability, if KVM_CHECK_EXTENSION indicates that it is
>> +available, means that that the kernel has an implementation of the
>> +Hyper-V Synthetic interrupt controller(SynIC). SynIC is used to
>> +support Windows Hyper-V based guest paravirt drivers(VMBus).
>> diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
>> index 3c6327d..8434f88 100644
>> --- a/arch/x86/include/asm/kvm_host.h
>> +++ b/arch/x86/include/asm/kvm_host.h
>> @@ -25,6 +25,7 @@
>>   #include <linux/pvclock_gtod.h>
>>   #include <linux/clocksource.h>
>>   #include <linux/irqbypass.h>
>> +#include <linux/hyperv.h>
>>
>>   #include <asm/pvclock-abi.h>
>>   #include <asm/desc.h>
>> @@ -374,10 +375,23 @@ struct kvm_mtrr {
>>   	struct list_head head;
>>   };
>>
>> +/* Hyper-V synthetic interrupt controller (SynIC)*/
>> +struct kvm_vcpu_hv_synic {
>> +	u64 version;
>> +	u64 control;
>> +	u64 msg_page;
>> +	u64 evt_page;
>> +	atomic64_t sint[HV_SYNIC_SINT_COUNT];
>> +	atomic_t sint_to_gsi[HV_SYNIC_SINT_COUNT];
>> +	DECLARE_BITMAP(auto_eoi_bitmap, 256);
>> +	DECLARE_BITMAP(vec_bitmap, 256);
>> +};
>> +
>>   /* Hyper-V per vcpu emulation context */
>>   struct kvm_vcpu_hv {
>>   	u64 hv_vapic;
>>   	s64 runtime_offset;
>> +	struct kvm_vcpu_hv_synic synic;
>>   };
>>
>>   struct kvm_vcpu_arch {
>> diff --git a/arch/x86/kvm/hyperv.c b/arch/x86/kvm/hyperv.c
>> index 62cf8c9..8ff71f3 100644
>> --- a/arch/x86/kvm/hyperv.c
>> +++ b/arch/x86/kvm/hyperv.c
>> @@ -23,13 +23,296 @@
>>
>>   #include "x86.h"
>>   #include "lapic.h"
>> +#include "ioapic.h"
>>   #include "hyperv.h"
>>
>>   #include <linux/kvm_host.h>
>> +#include <asm/apicdef.h>
>>   #include <trace/events/kvm.h>
>>
>>   #include "trace.h"
>>
>> +static inline u64 synic_read_sint(struct kvm_vcpu_hv_synic *synic, int sint)
>> +{
>> +	return atomic64_read(&synic->sint[sint]);
>> +}
>> +
>> +static inline int synic_get_sint_vector(u64 sint_value)
>> +{
>> +	if (sint_value & HV_SYNIC_SINT_MASKED)
>> +		return -1;
>> +	return sint_value & HV_SYNIC_SINT_VECTOR_MASK;
>> +}
>> +
>> +static bool synic_has_vector_connected(struct kvm_vcpu_hv_synic *synic,
>> +				      int vector)
>> +{
>> +	int i;
>> +
>> +	for (i = 0; i < ARRAY_SIZE(synic->sint); i++) {
>> +		if (synic_get_sint_vector(synic_read_sint(synic, i)) == vector)
>> +			return true;
>> +	}
>> +	return false;
>> +}
>> +
>> +static bool synic_has_vector_auto_eoi(struct kvm_vcpu_hv_synic *synic,
>> +				     int vector)
>> +{
>> +	int i;
>> +	u64 sint_value;
>> +
>> +	for (i = 0; i < ARRAY_SIZE(synic->sint); i++) {
>> +		sint_value = synic_read_sint(synic, i);
>> +		if (synic_get_sint_vector(sint_value) == vector &&
>> +		    sint_value & HV_SYNIC_SINT_AUTO_EOI)
>> +			return true;
>> +	}
>> +	return false;
>> +}
>> +
>> +static int synic_set_sint(struct kvm_vcpu_hv_synic *synic, int sint, u64 data)
>> +{
>> +	int vector;
>> +
>> +	vector = data & HV_SYNIC_SINT_VECTOR_MASK;
>> +	if (vector < 16)
>> +		return 1;
>> +	/*
>> +	 * Guest may configure multiple SINTs to use the same vector, so
>> +	 * we maintain a bitmap of vectors handled by synic, and a
>> +	 * bitmap of vectors with auto-eoi behavior.  The bitmaps are
>> +	 * updated here, and atomically queried on fast paths.
>> +	 */
>> +
>> +	atomic64_set(&synic->sint[sint], data);
>> +
>> +	if (synic_has_vector_connected(synic, vector))
>> +		__set_bit(vector, synic->vec_bitmap);
>> +	else
>> +		__clear_bit(vector, synic->vec_bitmap);
>> +
>> +	if (synic_has_vector_auto_eoi(synic, vector))
>> +		__set_bit(vector, synic->auto_eoi_bitmap);
>> +	else
>> +		__clear_bit(vector, synic->auto_eoi_bitmap);
>> +
>> +	/* Load SynIC vectors into EOI exit bitmap */
>> +	kvm_make_request(KVM_REQ_SCAN_IOAPIC, synic_to_vcpu(synic));
>> +	return 0;
>> +}
>> +
>> +static struct kvm_vcpu_hv_synic *synic_get(struct kvm *kvm, u32 vcpu_id)
>> +{
>> +	struct kvm_vcpu *vcpu;
>> +
>> +	if (vcpu_id >= atomic_read(&kvm->online_vcpus))
>> +		return NULL;
>> +	vcpu = kvm_get_vcpu(kvm, vcpu_id);
>> +	if (!vcpu)
>> +		return NULL;
>> +
>> +	return vcpu_to_synic(vcpu);
>> +}
>> +
>> +static void kvm_hv_notify_acked_sint(struct kvm_vcpu *vcpu, u32 sint)
>> +{
>> +	struct kvm *kvm = vcpu->kvm;
>> +	int gsi, idx;
>> +
>> +	vcpu_debug(vcpu, "Hyper-V SynIC acked sint %d\n", sint);
>> +
>> +	idx = srcu_read_lock(&kvm->irq_srcu);
>> +	gsi = atomic_read(&vcpu_to_synic(vcpu)->sint_to_gsi[sint]);
>> +	if (gsi != -1)
>> +		kvm_notify_acked_gsi(kvm, gsi);
>> +	srcu_read_unlock(&kvm->irq_srcu, idx);
>> +}
>> +
>> +static int synic_set_msr(struct kvm_vcpu_hv_synic *synic,
>> +			 u32 msr, u64 data, bool host)
>> +{
>> +	struct kvm_vcpu *vcpu = synic_to_vcpu(synic);
>> +	int ret;
>> +
>> +	vcpu_debug(vcpu, "Hyper-V SynIC set msr 0x%x 0x%llx host %d\n",
>> +		   msr, data, host);
>> +	ret = 0;
>> +	switch (msr) {
>> +	case HV_X64_MSR_SCONTROL:
>> +		synic->control = data;
>> +		break;
>> +	case HV_X64_MSR_SVERSION:
>> +		if (!host) {
>> +			ret = 1;
>> +			break;
>> +		}
>> +		synic->version = data;
>> +		break;
>> +	case HV_X64_MSR_SIEFP:
>> +		if (data & HV_SYNIC_SIEFP_ENABLE)
>> +			if (kvm_clear_guest(vcpu->kvm,
>> +					    data & PAGE_MASK, PAGE_SIZE)) {
>> +				ret = 1;
>> +				break;
>> +			}
>> +		synic->evt_page = data;
>> +		break;
>> +	case HV_X64_MSR_SIMP:
>> +		if (data & HV_SYNIC_SIMP_ENABLE)
>> +			if (kvm_clear_guest(vcpu->kvm,
>> +					    data & PAGE_MASK, PAGE_SIZE)) {
>> +				ret = 1;
>> +				break;
>> +			}
>> +		synic->msg_page = data;
>> +		break;
>> +	case HV_X64_MSR_EOM: {
>> +		int i;
>> +
>> +		for (i = 0; i < ARRAY_SIZE(synic->sint); i++)
>> +			kvm_hv_notify_acked_sint(vcpu, i);
>> +		break;
>> +	}
>> +	case HV_X64_MSR_SINT0 ... HV_X64_MSR_SINT15:
>> +		ret = synic_set_sint(synic, msr - HV_X64_MSR_SINT0, data);
>> +		break;
>> +	default:
>> +		ret = 1;
>> +		break;
>> +	}
>> +	return ret;
>> +}
>> +
>> +static int synic_get_msr(struct kvm_vcpu_hv_synic *synic, u32 msr, u64 *pdata)
>> +{
>> +	int ret;
>> +
>> +	ret = 0;
>> +	switch (msr) {
>> +	case HV_X64_MSR_SCONTROL:
>> +		*pdata = synic->control;
>> +		break;
>> +	case HV_X64_MSR_SVERSION:
>> +		*pdata = synic->version;
>> +		break;
>> +	case HV_X64_MSR_SIEFP:
>> +		*pdata = synic->evt_page;
>> +		break;
>> +	case HV_X64_MSR_SIMP:
>> +		*pdata = synic->msg_page;
>> +		break;
>> +	case HV_X64_MSR_EOM:
>> +		*pdata = 0;
>> +		break;
>> +	case HV_X64_MSR_SINT0 ... HV_X64_MSR_SINT15:
>> +		*pdata = atomic64_read(&synic->sint[msr - HV_X64_MSR_SINT0]);
>> +		break;
>> +	default:
>> +		ret = 1;
>> +		break;
>> +	}
>> +	return ret;
>> +}
>> +
>> +int synic_set_irq(struct kvm_vcpu_hv_synic *synic, u32 sint)
>> +{
>> +	struct kvm_vcpu *vcpu = synic_to_vcpu(synic);
>> +	struct kvm_lapic_irq irq;
>> +	int ret, vector;
>> +
>> +	if (sint >= ARRAY_SIZE(synic->sint))
>> +		return -EINVAL;
>> +
>> +	vector = synic_get_sint_vector(synic_read_sint(synic, sint));
>> +	if (vector < 0)
>> +		return -ENOENT;
>> +
>> +	memset(&irq, 0, sizeof(irq));
>> +	irq.dest_id = kvm_apic_id(vcpu->arch.apic);
>> +	irq.dest_mode = APIC_DEST_PHYSICAL;
>> +	irq.delivery_mode = APIC_DM_FIXED;
>> +	irq.vector = vector;
>> +	irq.level = 1;
>> +
>> +	ret = kvm_irq_delivery_to_apic(vcpu->kvm, NULL, &irq, NULL);
>> +	vcpu_debug(vcpu, "Hyper-V SynIC set irq ret %d\n", ret);
>> +	return ret;
>> +}
>> +
>> +int kvm_hv_synic_set_irq(struct kvm *kvm, u32 vcpu_id, u32 sint)
>> +{
>> +	struct kvm_vcpu_hv_synic *synic;
>> +
>> +	synic = synic_get(kvm, vcpu_id);
>> +	if (!synic)
>> +		return -EINVAL;
>> +
>> +	return synic_set_irq(synic, sint);
>> +}
>> +
>> +void kvm_hv_synic_send_eoi(struct kvm_vcpu *vcpu, int vector)
>> +{
>> +	struct kvm_vcpu_hv_synic *synic = vcpu_to_synic(vcpu);
>> +	int i;
>> +
>> +	vcpu_debug(vcpu, "Hyper-V SynIC send eoi vec %d\n", vector);
>> +
>> +	for (i = 0; i < ARRAY_SIZE(synic->sint); i++)
>> +		if (synic_get_sint_vector(synic_read_sint(synic, i)) == vector)
>> +			kvm_hv_notify_acked_sint(vcpu, i);
>> +}
>> +
>> +static int kvm_hv_set_sint_gsi(struct kvm *kvm, u32 vcpu_id, u32 sint, int gsi)
>> +{
>> +	struct kvm_vcpu_hv_synic *synic;
>> +
>> +	synic = synic_get(kvm, vcpu_id);
>> +	if (!synic)
>> +		return -EINVAL;
>> +
>> +	if (sint >= ARRAY_SIZE(synic->sint_to_gsi))
>> +		return -EINVAL;
>> +
>> +	atomic_set(&synic->sint_to_gsi[sint], gsi);
>> +	return 0;
>> +}
>> +
>> +void kvm_hv_irq_routing_update(struct kvm *kvm)
>> +{
>> +	struct kvm_irq_routing_table *irq_rt;
>> +	struct kvm_kernel_irq_routing_entry *e;
>> +	u32 gsi;
>> +
>> +	irq_rt = srcu_dereference_check(kvm->irq_routing, &kvm->irq_srcu,
>> +					lockdep_is_held(&kvm->irq_lock));
>> +
>> +	for (gsi = 0; gsi < irq_rt->nr_rt_entries; gsi++) {
>> +		hlist_for_each_entry(e, &irq_rt->map[gsi], link) {
>> +			if (e->type == KVM_IRQ_ROUTING_HV_SINT)
>> +				kvm_hv_set_sint_gsi(kvm, e->hv_sint.vcpu,
>> +						    e->hv_sint.sint, gsi);
>> +		}
>> +	}
>> +}
>> +
>> +static void synic_init(struct kvm_vcpu_hv_synic *synic)
>> +{
>> +	int i;
>> +
>> +	memset(synic, 0, sizeof(*synic));
>> +	synic->version = HV_SYNIC_VERSION_1;
>> +	for (i = 0; i < ARRAY_SIZE(synic->sint); i++) {
>> +		atomic64_set(&synic->sint[i], HV_SYNIC_SINT_MASKED);
>> +		atomic_set(&synic->sint_to_gsi[i], -1);
>> +	}
>> +}
>> +
>> +void kvm_hv_vcpu_init(struct kvm_vcpu *vcpu)
>> +{
>> +	synic_init(vcpu_to_synic(vcpu));
>> +}
>> +
>>   static bool kvm_hv_msr_partition_wide(u32 msr)
>>   {
>>   	bool r = false;
>> @@ -226,6 +509,13 @@ static int kvm_hv_set_msr(struct kvm_vcpu *vcpu, u32 msr, u64 data, bool host)
>>   			return 1;
>>   		hv->runtime_offset = data - current_task_runtime_100ns();
>>   		break;
>> +	case HV_X64_MSR_SCONTROL:
>> +	case HV_X64_MSR_SVERSION:
>> +	case HV_X64_MSR_SIEFP:
>> +	case HV_X64_MSR_SIMP:
>> +	case HV_X64_MSR_EOM:
>> +	case HV_X64_MSR_SINT0 ... HV_X64_MSR_SINT15:
>> +		return synic_set_msr(vcpu_to_synic(vcpu), msr, data, host);
>>   	default:
>>   		vcpu_unimpl(vcpu, "Hyper-V uhandled wrmsr: 0x%x data 0x%llx\n",
>>   			    msr, data);
>> @@ -304,6 +594,13 @@ static int kvm_hv_get_msr(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata)
>>   	case HV_X64_MSR_VP_RUNTIME:
>>   		data = current_task_runtime_100ns() + hv->runtime_offset;
>>   		break;
>> +	case HV_X64_MSR_SCONTROL:
>> +	case HV_X64_MSR_SVERSION:
>> +	case HV_X64_MSR_SIEFP:
>> +	case HV_X64_MSR_SIMP:
>> +	case HV_X64_MSR_EOM:
>> +	case HV_X64_MSR_SINT0 ... HV_X64_MSR_SINT15:
>> +		return synic_get_msr(vcpu_to_synic(vcpu), msr, pdata);
>>   	default:
>>   		vcpu_unimpl(vcpu, "Hyper-V unhandled rdmsr: 0x%x\n", msr);
>>   		return 1;
>> diff --git a/arch/x86/kvm/hyperv.h b/arch/x86/kvm/hyperv.h
>> index c7bce55..8668612 100644
>> --- a/arch/x86/kvm/hyperv.h
>> +++ b/arch/x86/kvm/hyperv.h
>> @@ -29,4 +29,25 @@ int kvm_hv_get_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata);
>>   bool kvm_hv_hypercall_enabled(struct kvm *kvm);
>>   int kvm_hv_hypercall(struct kvm_vcpu *vcpu);
>>
>> +int kvm_hv_synic_set_irq(struct kvm *kvm, u32 vcpu_id, u32 sint);
>> +void kvm_hv_synic_send_eoi(struct kvm_vcpu *vcpu, int vector);
>> +
>> +static inline struct kvm_vcpu_hv_synic *vcpu_to_synic(struct kvm_vcpu *vcpu)
>> +{
>> +	return &vcpu->arch.hyperv.synic;
>> +}
>> +
>> +static inline struct kvm_vcpu *synic_to_vcpu(struct kvm_vcpu_hv_synic *synic)
>> +{
>> +	struct kvm_vcpu_hv *hv;
>> +	struct kvm_vcpu_arch *arch;
>> +
>> +	hv = container_of(synic, struct kvm_vcpu_hv, synic);
>> +	arch = container_of(hv, struct kvm_vcpu_arch, hyperv);
>> +	return container_of(arch, struct kvm_vcpu, arch);
>> +}
>> +void kvm_hv_irq_routing_update(struct kvm *kvm);
>> +
>> +void kvm_hv_vcpu_init(struct kvm_vcpu *vcpu);
>> +
>>   #endif
>> diff --git a/arch/x86/kvm/irq_comm.c b/arch/x86/kvm/irq_comm.c
>> index fe91f72..5e195b9 100644
>> --- a/arch/x86/kvm/irq_comm.c
>> +++ b/arch/x86/kvm/irq_comm.c
>> @@ -33,6 +33,8 @@
>>
>>   #include "lapic.h"
>>
>> +#include "hyperv.h"
>> +
>>   static int kvm_set_pic_irq(struct kvm_kernel_irq_routing_entry *e,
>>   			   struct kvm *kvm, int irq_source_id, int level,
>>   			   bool line_status)
>> @@ -251,6 +253,16 @@ void kvm_fire_mask_notifiers(struct kvm *kvm, unsigned irqchip, unsigned pin,
>>   	srcu_read_unlock(&kvm->irq_srcu, idx);
>>   }
>>
>> +static int kvm_hv_set_sint(struct kvm_kernel_irq_routing_entry *e,
>> +		    struct kvm *kvm, int irq_source_id, int level,
>> +		    bool line_status)
>> +{
>> +	if (!level)
>> +		return -1;
>> +
>> +	return kvm_hv_synic_set_irq(kvm, e->hv_sint.vcpu, e->hv_sint.sint);
>> +}
>> +
>>   int kvm_set_routing_entry(struct kvm_kernel_irq_routing_entry *e,
>>   			  const struct kvm_irq_routing_entry *ue)
>>   {
>> @@ -289,6 +301,11 @@ int kvm_set_routing_entry(struct kvm_kernel_irq_routing_entry *e,
>>   		e->msi.address_hi = ue->u.msi.address_hi;
>>   		e->msi.data = ue->u.msi.data;
>>   		break;
>> +	case KVM_IRQ_ROUTING_HV_SINT:
>> +		e->set = kvm_hv_set_sint;
>> +		e->hv_sint.vcpu = ue->u.hv_sint.vcpu;
>> +		e->hv_sint.sint = ue->u.hv_sint.sint;
>> +		break;
>>   	default:
>>   		goto out;
>>   	}
>> @@ -405,3 +422,20 @@ void kvm_scan_ioapic_routes(struct kvm_vcpu *vcpu,
>>   	}
>>   	srcu_read_unlock(&kvm->irq_srcu, idx);
>>   }
>> +
>> +int kvm_arch_set_irq(struct kvm_kernel_irq_routing_entry *irq, struct kvm *kvm,
>> +		     int irq_source_id, int level, bool line_status)
>> +{
>> +	switch (irq->type) {
>> +	case KVM_IRQ_ROUTING_HV_SINT:
>> +		return kvm_hv_set_sint(irq, kvm, irq_source_id, level,
>> +				       line_status);
>> +	default:
>> +		return -EWOULDBLOCK;
>> +	}
>> +}
>> +
>> +void kvm_arch_irq_routing_update(struct kvm *kvm)
>> +{
>> +	kvm_hv_irq_routing_update(kvm);
>> +}
>> diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c
>> index dc03a01..3132478 100644
>> --- a/arch/x86/kvm/lapic.c
>> +++ b/arch/x86/kvm/lapic.c
>> @@ -41,6 +41,7 @@
>>   #include "trace.h"
>>   #include "x86.h"
>>   #include "cpuid.h"
>> +#include "hyperv.h"
>>
>>   #ifndef CONFIG_X86_64
>>   #define mod_64(x, y) ((x) - (y) * div64_u64(x, y))
>> @@ -128,11 +129,6 @@ static inline int apic_enabled(struct kvm_lapic *apic)
>>   	(LVT_MASK | APIC_MODE_MASK | APIC_INPUT_POLARITY | \
>>   	 APIC_LVT_REMOTE_IRR | APIC_LVT_LEVEL_TRIGGER)
>>
>> -static inline int kvm_apic_id(struct kvm_lapic *apic)
>> -{
>> -	return (kvm_apic_get_reg(apic, APIC_ID) >> 24) & 0xff;
>> -}
>> -
>>   /* The logical map is definitely wrong if we have multiple
>>    * modes at the same time.  (Physical map is always right.)
>>    */
>> @@ -850,7 +846,8 @@ static int __apic_accept_irq(struct kvm_lapic *apic, int delivery_mode,
>>   				apic_clear_vector(vector, apic->regs + APIC_TMR);
>>   		}
>>
>> -		if (kvm_x86_ops->deliver_posted_interrupt)
>> +		if (kvm_x86_ops->deliver_posted_interrupt &&
>> +		    !test_bit(vector, vcpu_to_synic(vcpu)->auto_eoi_bitmap))
>>   			kvm_x86_ops->deliver_posted_interrupt(vcpu, vector);
>>   		else {
>>   			apic_set_irr(vector, apic);
>> @@ -972,6 +969,9 @@ static int apic_set_eoi(struct kvm_lapic *apic)
>>   	apic_clear_isr(vector, apic);
>>   	apic_update_ppr(apic);
>>
>> +	if (test_bit(vector, vcpu_to_synic(apic->vcpu)->vec_bitmap))
>> +		kvm_hv_synic_send_eoi(apic->vcpu, vector);
>> +
>>   	kvm_ioapic_send_eoi(apic, vector);
>>   	kvm_make_request(KVM_REQ_EVENT, apic->vcpu);
>>   	return vector;
>> @@ -1881,6 +1881,12 @@ int kvm_get_apic_interrupt(struct kvm_vcpu *vcpu)
>>   	apic_set_isr(vector, apic);
>>   	apic_update_ppr(apic);
>>   	apic_clear_irr(vector, apic);
>> +
>> +	if (test_bit(vector, vcpu_to_synic(vcpu)->auto_eoi_bitmap)) {
>> +		apic_clear_isr(vector, apic);
>> +		apic_update_ppr(apic);
>> +	}
>> +
>>   	return vector;
>>   }
>>
>> diff --git a/arch/x86/kvm/lapic.h b/arch/x86/kvm/lapic.h
>> index fde8e35d..6c64090 100644
>> --- a/arch/x86/kvm/lapic.h
>> +++ b/arch/x86/kvm/lapic.h
>> @@ -164,6 +164,11 @@ static inline int kvm_lapic_latched_init(struct kvm_vcpu *vcpu)
>>   	return kvm_vcpu_has_lapic(vcpu) && test_bit(KVM_APIC_INIT, &vcpu->arch.apic->pending_events);
>>   }
>>
>> +static inline int kvm_apic_id(struct kvm_lapic *apic)
>> +{
>> +	return (kvm_apic_get_reg(apic, APIC_ID) >> 24) & 0xff;
>> +}
>> +
>>   bool kvm_apic_pending_eoi(struct kvm_vcpu *vcpu, int vector);
>>
>>   void wait_lapic_expire(struct kvm_vcpu *vcpu);
>> diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
>> index fb6cfbf..b853b2df 100644
>> --- a/arch/x86/kvm/x86.c
>> +++ b/arch/x86/kvm/x86.c
>> @@ -959,6 +959,7 @@ static u32 emulated_msrs[] = {
>>   	HV_X64_MSR_RESET,
>>   	HV_X64_MSR_VP_INDEX,
>>   	HV_X64_MSR_VP_RUNTIME,
>> +	HV_X64_MSR_SCONTROL,
>>   	HV_X64_MSR_APIC_ASSIST_PAGE, MSR_KVM_ASYNC_PF_EN, MSR_KVM_STEAL_TIME,
>>   	MSR_KVM_PV_EOI_EN,
>>
>> @@ -2441,6 +2442,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
>>   	case KVM_CAP_HYPERV:
>>   	case KVM_CAP_HYPERV_VAPIC:
>>   	case KVM_CAP_HYPERV_SPIN:
>> +	case KVM_CAP_HYPERV_SYNIC:
>>   	case KVM_CAP_PCI_SEGMENT:
>>   	case KVM_CAP_DEBUGREGS:
>>   	case KVM_CAP_X86_ROBUST_SINGLESTEP:
>> @@ -6195,6 +6197,8 @@ static void process_smi(struct kvm_vcpu *vcpu)
>>
>>   static void vcpu_scan_ioapic(struct kvm_vcpu *vcpu)
>>   {
>> +	u64 eoi_exit_bitmap[4];
>> +
>>   	if (!kvm_apic_hw_enabled(vcpu->arch.apic))
>>   		return;
>>
>> @@ -6205,8 +6209,10 @@ static void vcpu_scan_ioapic(struct kvm_vcpu *vcpu)
>>   	else
>>   		kvm_ioapic_scan_entry(vcpu, vcpu->arch.ioapic_handled_vectors);
>>
>> -	kvm_x86_ops->load_eoi_exitmap(vcpu,
>> -				      (u64 *)vcpu->arch.ioapic_handled_vectors);
>> +	bitmap_or((ulong *)eoi_exit_bitmap, vcpu->arch.ioapic_handled_vectors,
>> +		  vcpu_to_synic(vcpu)->vec_bitmap, 256);
>> +
>> +	kvm_x86_ops->load_eoi_exitmap(vcpu, eoi_exit_bitmap);
>>   }
>>
>>   static void kvm_vcpu_flush_tlb(struct kvm_vcpu *vcpu)
>> @@ -7456,6 +7462,8 @@ int kvm_arch_vcpu_init(struct kvm_vcpu *vcpu)
>>
>>   	vcpu->arch.pending_external_vector = -1;
>>
>> +	kvm_hv_vcpu_init(vcpu);
>> +
>>   	return 0;
>>
>>   fail_free_mce_banks:
>> diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
>> index c742e79..43b0141 100644
>> --- a/include/linux/kvm_host.h
>> +++ b/include/linux/kvm_host.h
>> @@ -318,6 +318,11 @@ struct kvm_s390_adapter_int {
>>   	u32 adapter_id;
>>   };
>>
>> +struct kvm_hv_sint {
>> +	u32 vcpu;
>> +	u32 sint;
>> +};
>> +
>>   struct kvm_kernel_irq_routing_entry {
>>   	u32 gsi;
>>   	u32 type;
>> @@ -331,6 +336,7 @@ struct kvm_kernel_irq_routing_entry {
>>   		} irqchip;
>>   		struct msi_msg msi;
>>   		struct kvm_s390_adapter_int adapter;
>> +		struct kvm_hv_sint hv_sint;
>>   	};
>>   	struct hlist_node link;
>>   };
>> diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
>> index 03f3618..27ce460 100644
>> --- a/include/uapi/linux/kvm.h
>> +++ b/include/uapi/linux/kvm.h
>> @@ -831,6 +831,7 @@ struct kvm_ppc_smmu_info {
>>   #define KVM_CAP_GUEST_DEBUG_HW_WPS 120
>>   #define KVM_CAP_SPLIT_IRQCHIP 121
>>   #define KVM_CAP_IOEVENTFD_ANY_LENGTH 122
>> +#define KVM_CAP_HYPERV_SYNIC 123
>>
>>   #ifdef KVM_CAP_IRQ_ROUTING
>>
>> @@ -854,10 +855,16 @@ struct kvm_irq_routing_s390_adapter {
>>   	__u32 adapter_id;
>>   };
>>
>> +struct kvm_irq_routing_hv_sint {
>> +	__u32 vcpu;
>> +	__u32 sint;
>> +};
>> +
>>   /* gsi routing entry types */
>>   #define KVM_IRQ_ROUTING_IRQCHIP 1
>>   #define KVM_IRQ_ROUTING_MSI 2
>>   #define KVM_IRQ_ROUTING_S390_ADAPTER 3
>> +#define KVM_IRQ_ROUTING_HV_SINT 4
>>
>>   struct kvm_irq_routing_entry {
>>   	__u32 gsi;
>> @@ -868,6 +875,7 @@ struct kvm_irq_routing_entry {
>>   		struct kvm_irq_routing_irqchip irqchip;
>>   		struct kvm_irq_routing_msi msi;
>>   		struct kvm_irq_routing_s390_adapter adapter;
>> +		struct kvm_irq_routing_hv_sint hv_sint;
>>   		__u32 pad[8];
>>   	} u;
>>   };
>>
--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Paolo Bonzini Oct. 29, 2015, 9:51 a.m. UTC | #4
On 29/10/2015 09:45, Roman Kagan wrote:
> While Andrey is testing it, I'd like to ask similar question re. MSI:
> why is there a "shortcut" for KVM_IRQ_ROUTING_MSI case (which we
> basically modelled after) when it would probably get handled through
> ->set handler in irqfd_inject() too?

Because it's a bit faster that way. :)  By avoiding the schedule_work,
you can improve latency by a few microseconds.  It's nice to have it for
the VFIO case especially, where everything you do takes you further from
hardware performance.

However, that shortcut is badly implemented because it lets you do a
walk over all CPUs while interrupts are disabled.  It should be modified
to use kvm_set_msi_inatomic instead of kvm_set_msi (more precisely, I
would like to remove kvm_set_msi and keep kvm_arch_irq_update; then
kvm_arch_irq_update will call kvm_set_msi_inatomic).

I'll post a patch next Monday.  You can then benchmark the addition of
synthetic interrupts to the atomic-context fast path, and see if it
makes a speed difference.

Paolo
--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/Documentation/virtual/kvm/api.txt b/Documentation/virtual/kvm/api.txt
index 092ee9f..8710418 100644
--- a/Documentation/virtual/kvm/api.txt
+++ b/Documentation/virtual/kvm/api.txt
@@ -1451,6 +1451,7 @@  struct kvm_irq_routing_entry {
 		struct kvm_irq_routing_irqchip irqchip;
 		struct kvm_irq_routing_msi msi;
 		struct kvm_irq_routing_s390_adapter adapter;
+		struct kvm_irq_routing_hv_sint hv_sint;
 		__u32 pad[8];
 	} u;
 };
@@ -1459,6 +1460,7 @@  struct kvm_irq_routing_entry {
 #define KVM_IRQ_ROUTING_IRQCHIP 1
 #define KVM_IRQ_ROUTING_MSI 2
 #define KVM_IRQ_ROUTING_S390_ADAPTER 3
+#define KVM_IRQ_ROUTING_HV_SINT 4
 
 No flags are specified so far, the corresponding field must be set to zero.
 
@@ -1482,6 +1484,10 @@  struct kvm_irq_routing_s390_adapter {
 	__u32 adapter_id;
 };
 
+struct kvm_irq_routing_hv_sint {
+	__u32 vcpu;
+	__u32 sint;
+};
 
 4.53 KVM_ASSIGN_SET_MSIX_NR (deprecated)
 
@@ -3685,3 +3691,11 @@  available, means that that the kernel has an implementation of the
 H_RANDOM hypercall backed by a hardware random-number generator.
 If present, the kernel H_RANDOM handler can be enabled for guest use
 with the KVM_CAP_PPC_ENABLE_HCALL capability.
+
+8.2 KVM_CAP_HYPERV_SYNIC
+
+Architectures: x86
+This capability, if KVM_CHECK_EXTENSION indicates that it is
+available, means that that the kernel has an implementation of the
+Hyper-V Synthetic interrupt controller(SynIC). SynIC is used to
+support Windows Hyper-V based guest paravirt drivers(VMBus).
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index 3c6327d..8434f88 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -25,6 +25,7 @@ 
 #include <linux/pvclock_gtod.h>
 #include <linux/clocksource.h>
 #include <linux/irqbypass.h>
+#include <linux/hyperv.h>
 
 #include <asm/pvclock-abi.h>
 #include <asm/desc.h>
@@ -374,10 +375,23 @@  struct kvm_mtrr {
 	struct list_head head;
 };
 
+/* Hyper-V synthetic interrupt controller (SynIC)*/
+struct kvm_vcpu_hv_synic {
+	u64 version;
+	u64 control;
+	u64 msg_page;
+	u64 evt_page;
+	atomic64_t sint[HV_SYNIC_SINT_COUNT];
+	atomic_t sint_to_gsi[HV_SYNIC_SINT_COUNT];
+	DECLARE_BITMAP(auto_eoi_bitmap, 256);
+	DECLARE_BITMAP(vec_bitmap, 256);
+};
+
 /* Hyper-V per vcpu emulation context */
 struct kvm_vcpu_hv {
 	u64 hv_vapic;
 	s64 runtime_offset;
+	struct kvm_vcpu_hv_synic synic;
 };
 
 struct kvm_vcpu_arch {
diff --git a/arch/x86/kvm/hyperv.c b/arch/x86/kvm/hyperv.c
index 62cf8c9..8ff71f3 100644
--- a/arch/x86/kvm/hyperv.c
+++ b/arch/x86/kvm/hyperv.c
@@ -23,13 +23,296 @@ 
 
 #include "x86.h"
 #include "lapic.h"
+#include "ioapic.h"
 #include "hyperv.h"
 
 #include <linux/kvm_host.h>
+#include <asm/apicdef.h>
 #include <trace/events/kvm.h>
 
 #include "trace.h"
 
+static inline u64 synic_read_sint(struct kvm_vcpu_hv_synic *synic, int sint)
+{
+	return atomic64_read(&synic->sint[sint]);
+}
+
+static inline int synic_get_sint_vector(u64 sint_value)
+{
+	if (sint_value & HV_SYNIC_SINT_MASKED)
+		return -1;
+	return sint_value & HV_SYNIC_SINT_VECTOR_MASK;
+}
+
+static bool synic_has_vector_connected(struct kvm_vcpu_hv_synic *synic,
+				      int vector)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(synic->sint); i++) {
+		if (synic_get_sint_vector(synic_read_sint(synic, i)) == vector)
+			return true;
+	}
+	return false;
+}
+
+static bool synic_has_vector_auto_eoi(struct kvm_vcpu_hv_synic *synic,
+				     int vector)
+{
+	int i;
+	u64 sint_value;
+
+	for (i = 0; i < ARRAY_SIZE(synic->sint); i++) {
+		sint_value = synic_read_sint(synic, i);
+		if (synic_get_sint_vector(sint_value) == vector &&
+		    sint_value & HV_SYNIC_SINT_AUTO_EOI)
+			return true;
+	}
+	return false;
+}
+
+static int synic_set_sint(struct kvm_vcpu_hv_synic *synic, int sint, u64 data)
+{
+	int vector;
+
+	vector = data & HV_SYNIC_SINT_VECTOR_MASK;
+	if (vector < 16)
+		return 1;
+	/*
+	 * Guest may configure multiple SINTs to use the same vector, so
+	 * we maintain a bitmap of vectors handled by synic, and a
+	 * bitmap of vectors with auto-eoi behavior.  The bitmaps are
+	 * updated here, and atomically queried on fast paths.
+	 */
+
+	atomic64_set(&synic->sint[sint], data);
+
+	if (synic_has_vector_connected(synic, vector))
+		__set_bit(vector, synic->vec_bitmap);
+	else
+		__clear_bit(vector, synic->vec_bitmap);
+
+	if (synic_has_vector_auto_eoi(synic, vector))
+		__set_bit(vector, synic->auto_eoi_bitmap);
+	else
+		__clear_bit(vector, synic->auto_eoi_bitmap);
+
+	/* Load SynIC vectors into EOI exit bitmap */
+	kvm_make_request(KVM_REQ_SCAN_IOAPIC, synic_to_vcpu(synic));
+	return 0;
+}
+
+static struct kvm_vcpu_hv_synic *synic_get(struct kvm *kvm, u32 vcpu_id)
+{
+	struct kvm_vcpu *vcpu;
+
+	if (vcpu_id >= atomic_read(&kvm->online_vcpus))
+		return NULL;
+	vcpu = kvm_get_vcpu(kvm, vcpu_id);
+	if (!vcpu)
+		return NULL;
+
+	return vcpu_to_synic(vcpu);
+}
+
+static void kvm_hv_notify_acked_sint(struct kvm_vcpu *vcpu, u32 sint)
+{
+	struct kvm *kvm = vcpu->kvm;
+	int gsi, idx;
+
+	vcpu_debug(vcpu, "Hyper-V SynIC acked sint %d\n", sint);
+
+	idx = srcu_read_lock(&kvm->irq_srcu);
+	gsi = atomic_read(&vcpu_to_synic(vcpu)->sint_to_gsi[sint]);
+	if (gsi != -1)
+		kvm_notify_acked_gsi(kvm, gsi);
+	srcu_read_unlock(&kvm->irq_srcu, idx);
+}
+
+static int synic_set_msr(struct kvm_vcpu_hv_synic *synic,
+			 u32 msr, u64 data, bool host)
+{
+	struct kvm_vcpu *vcpu = synic_to_vcpu(synic);
+	int ret;
+
+	vcpu_debug(vcpu, "Hyper-V SynIC set msr 0x%x 0x%llx host %d\n",
+		   msr, data, host);
+	ret = 0;
+	switch (msr) {
+	case HV_X64_MSR_SCONTROL:
+		synic->control = data;
+		break;
+	case HV_X64_MSR_SVERSION:
+		if (!host) {
+			ret = 1;
+			break;
+		}
+		synic->version = data;
+		break;
+	case HV_X64_MSR_SIEFP:
+		if (data & HV_SYNIC_SIEFP_ENABLE)
+			if (kvm_clear_guest(vcpu->kvm,
+					    data & PAGE_MASK, PAGE_SIZE)) {
+				ret = 1;
+				break;
+			}
+		synic->evt_page = data;
+		break;
+	case HV_X64_MSR_SIMP:
+		if (data & HV_SYNIC_SIMP_ENABLE)
+			if (kvm_clear_guest(vcpu->kvm,
+					    data & PAGE_MASK, PAGE_SIZE)) {
+				ret = 1;
+				break;
+			}
+		synic->msg_page = data;
+		break;
+	case HV_X64_MSR_EOM: {
+		int i;
+
+		for (i = 0; i < ARRAY_SIZE(synic->sint); i++)
+			kvm_hv_notify_acked_sint(vcpu, i);
+		break;
+	}
+	case HV_X64_MSR_SINT0 ... HV_X64_MSR_SINT15:
+		ret = synic_set_sint(synic, msr - HV_X64_MSR_SINT0, data);
+		break;
+	default:
+		ret = 1;
+		break;
+	}
+	return ret;
+}
+
+static int synic_get_msr(struct kvm_vcpu_hv_synic *synic, u32 msr, u64 *pdata)
+{
+	int ret;
+
+	ret = 0;
+	switch (msr) {
+	case HV_X64_MSR_SCONTROL:
+		*pdata = synic->control;
+		break;
+	case HV_X64_MSR_SVERSION:
+		*pdata = synic->version;
+		break;
+	case HV_X64_MSR_SIEFP:
+		*pdata = synic->evt_page;
+		break;
+	case HV_X64_MSR_SIMP:
+		*pdata = synic->msg_page;
+		break;
+	case HV_X64_MSR_EOM:
+		*pdata = 0;
+		break;
+	case HV_X64_MSR_SINT0 ... HV_X64_MSR_SINT15:
+		*pdata = atomic64_read(&synic->sint[msr - HV_X64_MSR_SINT0]);
+		break;
+	default:
+		ret = 1;
+		break;
+	}
+	return ret;
+}
+
+int synic_set_irq(struct kvm_vcpu_hv_synic *synic, u32 sint)
+{
+	struct kvm_vcpu *vcpu = synic_to_vcpu(synic);
+	struct kvm_lapic_irq irq;
+	int ret, vector;
+
+	if (sint >= ARRAY_SIZE(synic->sint))
+		return -EINVAL;
+
+	vector = synic_get_sint_vector(synic_read_sint(synic, sint));
+	if (vector < 0)
+		return -ENOENT;
+
+	memset(&irq, 0, sizeof(irq));
+	irq.dest_id = kvm_apic_id(vcpu->arch.apic);
+	irq.dest_mode = APIC_DEST_PHYSICAL;
+	irq.delivery_mode = APIC_DM_FIXED;
+	irq.vector = vector;
+	irq.level = 1;
+
+	ret = kvm_irq_delivery_to_apic(vcpu->kvm, NULL, &irq, NULL);
+	vcpu_debug(vcpu, "Hyper-V SynIC set irq ret %d\n", ret);
+	return ret;
+}
+
+int kvm_hv_synic_set_irq(struct kvm *kvm, u32 vcpu_id, u32 sint)
+{
+	struct kvm_vcpu_hv_synic *synic;
+
+	synic = synic_get(kvm, vcpu_id);
+	if (!synic)
+		return -EINVAL;
+
+	return synic_set_irq(synic, sint);
+}
+
+void kvm_hv_synic_send_eoi(struct kvm_vcpu *vcpu, int vector)
+{
+	struct kvm_vcpu_hv_synic *synic = vcpu_to_synic(vcpu);
+	int i;
+
+	vcpu_debug(vcpu, "Hyper-V SynIC send eoi vec %d\n", vector);
+
+	for (i = 0; i < ARRAY_SIZE(synic->sint); i++)
+		if (synic_get_sint_vector(synic_read_sint(synic, i)) == vector)
+			kvm_hv_notify_acked_sint(vcpu, i);
+}
+
+static int kvm_hv_set_sint_gsi(struct kvm *kvm, u32 vcpu_id, u32 sint, int gsi)
+{
+	struct kvm_vcpu_hv_synic *synic;
+
+	synic = synic_get(kvm, vcpu_id);
+	if (!synic)
+		return -EINVAL;
+
+	if (sint >= ARRAY_SIZE(synic->sint_to_gsi))
+		return -EINVAL;
+
+	atomic_set(&synic->sint_to_gsi[sint], gsi);
+	return 0;
+}
+
+void kvm_hv_irq_routing_update(struct kvm *kvm)
+{
+	struct kvm_irq_routing_table *irq_rt;
+	struct kvm_kernel_irq_routing_entry *e;
+	u32 gsi;
+
+	irq_rt = srcu_dereference_check(kvm->irq_routing, &kvm->irq_srcu,
+					lockdep_is_held(&kvm->irq_lock));
+
+	for (gsi = 0; gsi < irq_rt->nr_rt_entries; gsi++) {
+		hlist_for_each_entry(e, &irq_rt->map[gsi], link) {
+			if (e->type == KVM_IRQ_ROUTING_HV_SINT)
+				kvm_hv_set_sint_gsi(kvm, e->hv_sint.vcpu,
+						    e->hv_sint.sint, gsi);
+		}
+	}
+}
+
+static void synic_init(struct kvm_vcpu_hv_synic *synic)
+{
+	int i;
+
+	memset(synic, 0, sizeof(*synic));
+	synic->version = HV_SYNIC_VERSION_1;
+	for (i = 0; i < ARRAY_SIZE(synic->sint); i++) {
+		atomic64_set(&synic->sint[i], HV_SYNIC_SINT_MASKED);
+		atomic_set(&synic->sint_to_gsi[i], -1);
+	}
+}
+
+void kvm_hv_vcpu_init(struct kvm_vcpu *vcpu)
+{
+	synic_init(vcpu_to_synic(vcpu));
+}
+
 static bool kvm_hv_msr_partition_wide(u32 msr)
 {
 	bool r = false;
@@ -226,6 +509,13 @@  static int kvm_hv_set_msr(struct kvm_vcpu *vcpu, u32 msr, u64 data, bool host)
 			return 1;
 		hv->runtime_offset = data - current_task_runtime_100ns();
 		break;
+	case HV_X64_MSR_SCONTROL:
+	case HV_X64_MSR_SVERSION:
+	case HV_X64_MSR_SIEFP:
+	case HV_X64_MSR_SIMP:
+	case HV_X64_MSR_EOM:
+	case HV_X64_MSR_SINT0 ... HV_X64_MSR_SINT15:
+		return synic_set_msr(vcpu_to_synic(vcpu), msr, data, host);
 	default:
 		vcpu_unimpl(vcpu, "Hyper-V uhandled wrmsr: 0x%x data 0x%llx\n",
 			    msr, data);
@@ -304,6 +594,13 @@  static int kvm_hv_get_msr(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata)
 	case HV_X64_MSR_VP_RUNTIME:
 		data = current_task_runtime_100ns() + hv->runtime_offset;
 		break;
+	case HV_X64_MSR_SCONTROL:
+	case HV_X64_MSR_SVERSION:
+	case HV_X64_MSR_SIEFP:
+	case HV_X64_MSR_SIMP:
+	case HV_X64_MSR_EOM:
+	case HV_X64_MSR_SINT0 ... HV_X64_MSR_SINT15:
+		return synic_get_msr(vcpu_to_synic(vcpu), msr, pdata);
 	default:
 		vcpu_unimpl(vcpu, "Hyper-V unhandled rdmsr: 0x%x\n", msr);
 		return 1;
diff --git a/arch/x86/kvm/hyperv.h b/arch/x86/kvm/hyperv.h
index c7bce55..8668612 100644
--- a/arch/x86/kvm/hyperv.h
+++ b/arch/x86/kvm/hyperv.h
@@ -29,4 +29,25 @@  int kvm_hv_get_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata);
 bool kvm_hv_hypercall_enabled(struct kvm *kvm);
 int kvm_hv_hypercall(struct kvm_vcpu *vcpu);
 
+int kvm_hv_synic_set_irq(struct kvm *kvm, u32 vcpu_id, u32 sint);
+void kvm_hv_synic_send_eoi(struct kvm_vcpu *vcpu, int vector);
+
+static inline struct kvm_vcpu_hv_synic *vcpu_to_synic(struct kvm_vcpu *vcpu)
+{
+	return &vcpu->arch.hyperv.synic;
+}
+
+static inline struct kvm_vcpu *synic_to_vcpu(struct kvm_vcpu_hv_synic *synic)
+{
+	struct kvm_vcpu_hv *hv;
+	struct kvm_vcpu_arch *arch;
+
+	hv = container_of(synic, struct kvm_vcpu_hv, synic);
+	arch = container_of(hv, struct kvm_vcpu_arch, hyperv);
+	return container_of(arch, struct kvm_vcpu, arch);
+}
+void kvm_hv_irq_routing_update(struct kvm *kvm);
+
+void kvm_hv_vcpu_init(struct kvm_vcpu *vcpu);
+
 #endif
diff --git a/arch/x86/kvm/irq_comm.c b/arch/x86/kvm/irq_comm.c
index fe91f72..5e195b9 100644
--- a/arch/x86/kvm/irq_comm.c
+++ b/arch/x86/kvm/irq_comm.c
@@ -33,6 +33,8 @@ 
 
 #include "lapic.h"
 
+#include "hyperv.h"
+
 static int kvm_set_pic_irq(struct kvm_kernel_irq_routing_entry *e,
 			   struct kvm *kvm, int irq_source_id, int level,
 			   bool line_status)
@@ -251,6 +253,16 @@  void kvm_fire_mask_notifiers(struct kvm *kvm, unsigned irqchip, unsigned pin,
 	srcu_read_unlock(&kvm->irq_srcu, idx);
 }
 
+static int kvm_hv_set_sint(struct kvm_kernel_irq_routing_entry *e,
+		    struct kvm *kvm, int irq_source_id, int level,
+		    bool line_status)
+{
+	if (!level)
+		return -1;
+
+	return kvm_hv_synic_set_irq(kvm, e->hv_sint.vcpu, e->hv_sint.sint);
+}
+
 int kvm_set_routing_entry(struct kvm_kernel_irq_routing_entry *e,
 			  const struct kvm_irq_routing_entry *ue)
 {
@@ -289,6 +301,11 @@  int kvm_set_routing_entry(struct kvm_kernel_irq_routing_entry *e,
 		e->msi.address_hi = ue->u.msi.address_hi;
 		e->msi.data = ue->u.msi.data;
 		break;
+	case KVM_IRQ_ROUTING_HV_SINT:
+		e->set = kvm_hv_set_sint;
+		e->hv_sint.vcpu = ue->u.hv_sint.vcpu;
+		e->hv_sint.sint = ue->u.hv_sint.sint;
+		break;
 	default:
 		goto out;
 	}
@@ -405,3 +422,20 @@  void kvm_scan_ioapic_routes(struct kvm_vcpu *vcpu,
 	}
 	srcu_read_unlock(&kvm->irq_srcu, idx);
 }
+
+int kvm_arch_set_irq(struct kvm_kernel_irq_routing_entry *irq, struct kvm *kvm,
+		     int irq_source_id, int level, bool line_status)
+{
+	switch (irq->type) {
+	case KVM_IRQ_ROUTING_HV_SINT:
+		return kvm_hv_set_sint(irq, kvm, irq_source_id, level,
+				       line_status);
+	default:
+		return -EWOULDBLOCK;
+	}
+}
+
+void kvm_arch_irq_routing_update(struct kvm *kvm)
+{
+	kvm_hv_irq_routing_update(kvm);
+}
diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c
index dc03a01..3132478 100644
--- a/arch/x86/kvm/lapic.c
+++ b/arch/x86/kvm/lapic.c
@@ -41,6 +41,7 @@ 
 #include "trace.h"
 #include "x86.h"
 #include "cpuid.h"
+#include "hyperv.h"
 
 #ifndef CONFIG_X86_64
 #define mod_64(x, y) ((x) - (y) * div64_u64(x, y))
@@ -128,11 +129,6 @@  static inline int apic_enabled(struct kvm_lapic *apic)
 	(LVT_MASK | APIC_MODE_MASK | APIC_INPUT_POLARITY | \
 	 APIC_LVT_REMOTE_IRR | APIC_LVT_LEVEL_TRIGGER)
 
-static inline int kvm_apic_id(struct kvm_lapic *apic)
-{
-	return (kvm_apic_get_reg(apic, APIC_ID) >> 24) & 0xff;
-}
-
 /* The logical map is definitely wrong if we have multiple
  * modes at the same time.  (Physical map is always right.)
  */
@@ -850,7 +846,8 @@  static int __apic_accept_irq(struct kvm_lapic *apic, int delivery_mode,
 				apic_clear_vector(vector, apic->regs + APIC_TMR);
 		}
 
-		if (kvm_x86_ops->deliver_posted_interrupt)
+		if (kvm_x86_ops->deliver_posted_interrupt &&
+		    !test_bit(vector, vcpu_to_synic(vcpu)->auto_eoi_bitmap))
 			kvm_x86_ops->deliver_posted_interrupt(vcpu, vector);
 		else {
 			apic_set_irr(vector, apic);
@@ -972,6 +969,9 @@  static int apic_set_eoi(struct kvm_lapic *apic)
 	apic_clear_isr(vector, apic);
 	apic_update_ppr(apic);
 
+	if (test_bit(vector, vcpu_to_synic(apic->vcpu)->vec_bitmap))
+		kvm_hv_synic_send_eoi(apic->vcpu, vector);
+
 	kvm_ioapic_send_eoi(apic, vector);
 	kvm_make_request(KVM_REQ_EVENT, apic->vcpu);
 	return vector;
@@ -1881,6 +1881,12 @@  int kvm_get_apic_interrupt(struct kvm_vcpu *vcpu)
 	apic_set_isr(vector, apic);
 	apic_update_ppr(apic);
 	apic_clear_irr(vector, apic);
+
+	if (test_bit(vector, vcpu_to_synic(vcpu)->auto_eoi_bitmap)) {
+		apic_clear_isr(vector, apic);
+		apic_update_ppr(apic);
+	}
+
 	return vector;
 }
 
diff --git a/arch/x86/kvm/lapic.h b/arch/x86/kvm/lapic.h
index fde8e35d..6c64090 100644
--- a/arch/x86/kvm/lapic.h
+++ b/arch/x86/kvm/lapic.h
@@ -164,6 +164,11 @@  static inline int kvm_lapic_latched_init(struct kvm_vcpu *vcpu)
 	return kvm_vcpu_has_lapic(vcpu) && test_bit(KVM_APIC_INIT, &vcpu->arch.apic->pending_events);
 }
 
+static inline int kvm_apic_id(struct kvm_lapic *apic)
+{
+	return (kvm_apic_get_reg(apic, APIC_ID) >> 24) & 0xff;
+}
+
 bool kvm_apic_pending_eoi(struct kvm_vcpu *vcpu, int vector);
 
 void wait_lapic_expire(struct kvm_vcpu *vcpu);
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index fb6cfbf..b853b2df 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -959,6 +959,7 @@  static u32 emulated_msrs[] = {
 	HV_X64_MSR_RESET,
 	HV_X64_MSR_VP_INDEX,
 	HV_X64_MSR_VP_RUNTIME,
+	HV_X64_MSR_SCONTROL,
 	HV_X64_MSR_APIC_ASSIST_PAGE, MSR_KVM_ASYNC_PF_EN, MSR_KVM_STEAL_TIME,
 	MSR_KVM_PV_EOI_EN,
 
@@ -2441,6 +2442,7 @@  int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
 	case KVM_CAP_HYPERV:
 	case KVM_CAP_HYPERV_VAPIC:
 	case KVM_CAP_HYPERV_SPIN:
+	case KVM_CAP_HYPERV_SYNIC:
 	case KVM_CAP_PCI_SEGMENT:
 	case KVM_CAP_DEBUGREGS:
 	case KVM_CAP_X86_ROBUST_SINGLESTEP:
@@ -6195,6 +6197,8 @@  static void process_smi(struct kvm_vcpu *vcpu)
 
 static void vcpu_scan_ioapic(struct kvm_vcpu *vcpu)
 {
+	u64 eoi_exit_bitmap[4];
+
 	if (!kvm_apic_hw_enabled(vcpu->arch.apic))
 		return;
 
@@ -6205,8 +6209,10 @@  static void vcpu_scan_ioapic(struct kvm_vcpu *vcpu)
 	else
 		kvm_ioapic_scan_entry(vcpu, vcpu->arch.ioapic_handled_vectors);
 
-	kvm_x86_ops->load_eoi_exitmap(vcpu,
-				      (u64 *)vcpu->arch.ioapic_handled_vectors);
+	bitmap_or((ulong *)eoi_exit_bitmap, vcpu->arch.ioapic_handled_vectors,
+		  vcpu_to_synic(vcpu)->vec_bitmap, 256);
+
+	kvm_x86_ops->load_eoi_exitmap(vcpu, eoi_exit_bitmap);
 }
 
 static void kvm_vcpu_flush_tlb(struct kvm_vcpu *vcpu)
@@ -7456,6 +7462,8 @@  int kvm_arch_vcpu_init(struct kvm_vcpu *vcpu)
 
 	vcpu->arch.pending_external_vector = -1;
 
+	kvm_hv_vcpu_init(vcpu);
+
 	return 0;
 
 fail_free_mce_banks:
diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
index c742e79..43b0141 100644
--- a/include/linux/kvm_host.h
+++ b/include/linux/kvm_host.h
@@ -318,6 +318,11 @@  struct kvm_s390_adapter_int {
 	u32 adapter_id;
 };
 
+struct kvm_hv_sint {
+	u32 vcpu;
+	u32 sint;
+};
+
 struct kvm_kernel_irq_routing_entry {
 	u32 gsi;
 	u32 type;
@@ -331,6 +336,7 @@  struct kvm_kernel_irq_routing_entry {
 		} irqchip;
 		struct msi_msg msi;
 		struct kvm_s390_adapter_int adapter;
+		struct kvm_hv_sint hv_sint;
 	};
 	struct hlist_node link;
 };
diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
index 03f3618..27ce460 100644
--- a/include/uapi/linux/kvm.h
+++ b/include/uapi/linux/kvm.h
@@ -831,6 +831,7 @@  struct kvm_ppc_smmu_info {
 #define KVM_CAP_GUEST_DEBUG_HW_WPS 120
 #define KVM_CAP_SPLIT_IRQCHIP 121
 #define KVM_CAP_IOEVENTFD_ANY_LENGTH 122
+#define KVM_CAP_HYPERV_SYNIC 123
 
 #ifdef KVM_CAP_IRQ_ROUTING
 
@@ -854,10 +855,16 @@  struct kvm_irq_routing_s390_adapter {
 	__u32 adapter_id;
 };
 
+struct kvm_irq_routing_hv_sint {
+	__u32 vcpu;
+	__u32 sint;
+};
+
 /* gsi routing entry types */
 #define KVM_IRQ_ROUTING_IRQCHIP 1
 #define KVM_IRQ_ROUTING_MSI 2
 #define KVM_IRQ_ROUTING_S390_ADAPTER 3
+#define KVM_IRQ_ROUTING_HV_SINT 4
 
 struct kvm_irq_routing_entry {
 	__u32 gsi;
@@ -868,6 +875,7 @@  struct kvm_irq_routing_entry {
 		struct kvm_irq_routing_irqchip irqchip;
 		struct kvm_irq_routing_msi msi;
 		struct kvm_irq_routing_s390_adapter adapter;
+		struct kvm_irq_routing_hv_sint hv_sint;
 		__u32 pad[8];
 	} u;
 };