Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d3457c87 authored by Roman Kagan's avatar Roman Kagan Committed by Radim Krčmář
Browse files

kvm: x86: hyperv: make VP_INDEX managed by userspace



Hyper-V identifies vCPUs by Virtual Processor Index, which can be
queried via HV_X64_MSR_VP_INDEX msr.  It is defined by the spec as a
sequential number which can't exceed the maximum number of vCPUs per VM.
APIC ids can be sparse and thus aren't a valid replacement for VP
indices.

Current KVM uses its internal vcpu index as VP_INDEX.  However, to make
it predictable and persistent across VM migrations, the userspace has to
control the value of VP_INDEX.

This patch achieves that, by storing vp_index explicitly on vcpu, and
allowing HV_X64_MSR_VP_INDEX to be set from the host side.  For
compatibility it's initialized to KVM vcpu index.  Also a few variables
are renamed to make clear distinction betweed this Hyper-V vp_index and
KVM vcpu_id (== APIC id).  Besides, a new capability,
KVM_CAP_HYPERV_VP_INDEX, is added to allow the userspace to skip
attempting msr writes where unsupported, to avoid spamming error logs.

Signed-off-by: default avatarRoman Kagan <rkagan@virtuozzo.com>
Signed-off-by: default avatarRadim Krčmář <rkrcmar@redhat.com>
parent 52a5c155
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -4338,3 +4338,12 @@ This capability enables a newer version of Hyper-V Synthetic interrupt
controller (SynIC).  The only difference with KVM_CAP_HYPERV_SYNIC is that KVM
doesn't clear SynIC message and event flags pages when they are enabled by
writing to the respective MSRs.

8.12 KVM_CAP_HYPERV_VP_INDEX

Architectures: x86

This capability indicates that userspace can load HV_X64_MSR_VP_INDEX msr.  Its
value is used to denote the target vcpu for a SynIC interrupt.  For
compatibilty, KVM initializes this msr to KVM's internal vcpu index.  When this
capability is absent, userspace can still query this msr's value.
+1 −0
Original line number Diff line number Diff line
@@ -467,6 +467,7 @@ struct kvm_vcpu_hv_synic {

/* Hyper-V per vcpu emulation context */
struct kvm_vcpu_hv {
	u32 vp_index;
	u64 hv_vapic;
	s64 runtime_offset;
	struct kvm_vcpu_hv_synic synic;
+35 −19
Original line number Diff line number Diff line
@@ -106,14 +106,27 @@ static int synic_set_sint(struct kvm_vcpu_hv_synic *synic, int sint,
	return 0;
}

static struct kvm_vcpu_hv_synic *synic_get(struct kvm *kvm, u32 vcpu_id)
static struct kvm_vcpu *get_vcpu_by_vpidx(struct kvm *kvm, u32 vpidx)
{
	struct kvm_vcpu *vcpu = NULL;
	int i;

	if (vpidx < KVM_MAX_VCPUS)
		vcpu = kvm_get_vcpu(kvm, vpidx);
	if (vcpu && vcpu_to_hv_vcpu(vcpu)->vp_index == vpidx)
		return vcpu;
	kvm_for_each_vcpu(i, vcpu, kvm)
		if (vcpu_to_hv_vcpu(vcpu)->vp_index == vpidx)
			return vcpu;
	return NULL;
}

static struct kvm_vcpu_hv_synic *synic_get(struct kvm *kvm, u32 vpidx)
{
	struct kvm_vcpu *vcpu;
	struct kvm_vcpu_hv_synic *synic;

	if (vcpu_id >= atomic_read(&kvm->online_vcpus))
		return NULL;
	vcpu = kvm_get_vcpu(kvm, vcpu_id);
	vcpu = get_vcpu_by_vpidx(kvm, vpidx);
	if (!vcpu)
		return NULL;
	synic = vcpu_to_synic(vcpu);
@@ -320,11 +333,11 @@ static int synic_set_irq(struct kvm_vcpu_hv_synic *synic, u32 sint)
	return ret;
}

int kvm_hv_synic_set_irq(struct kvm *kvm, u32 vcpu_id, u32 sint)
int kvm_hv_synic_set_irq(struct kvm *kvm, u32 vpidx, u32 sint)
{
	struct kvm_vcpu_hv_synic *synic;

	synic = synic_get(kvm, vcpu_id);
	synic = synic_get(kvm, vpidx);
	if (!synic)
		return -EINVAL;

@@ -343,11 +356,11 @@ void kvm_hv_synic_send_eoi(struct kvm_vcpu *vcpu, int 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)
static int kvm_hv_set_sint_gsi(struct kvm *kvm, u32 vpidx, u32 sint, int gsi)
{
	struct kvm_vcpu_hv_synic *synic;

	synic = synic_get(kvm, vcpu_id);
	synic = synic_get(kvm, vpidx);
	if (!synic)
		return -EINVAL;

@@ -689,6 +702,13 @@ void kvm_hv_vcpu_init(struct kvm_vcpu *vcpu)
		stimer_init(&hv_vcpu->stimer[i], i);
}

void kvm_hv_vcpu_postcreate(struct kvm_vcpu *vcpu)
{
	struct kvm_vcpu_hv *hv_vcpu = vcpu_to_hv_vcpu(vcpu);

	hv_vcpu->vp_index = kvm_vcpu_get_idx(vcpu);
}

int kvm_hv_activate_synic(struct kvm_vcpu *vcpu, bool dont_zero_synic_pages)
{
	struct kvm_vcpu_hv_synic *synic = vcpu_to_synic(vcpu);
@@ -983,6 +1003,11 @@ static int kvm_hv_set_msr(struct kvm_vcpu *vcpu, u32 msr, u64 data, bool host)
	struct kvm_vcpu_hv *hv = &vcpu->arch.hyperv;

	switch (msr) {
	case HV_X64_MSR_VP_INDEX:
		if (!host)
			return 1;
		hv->vp_index = (u32)data;
		break;
	case HV_X64_MSR_APIC_ASSIST_PAGE: {
		u64 gfn;
		unsigned long addr;
@@ -1094,18 +1119,9 @@ static int kvm_hv_get_msr(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata)
	struct kvm_vcpu_hv *hv = &vcpu->arch.hyperv;

	switch (msr) {
	case HV_X64_MSR_VP_INDEX: {
		int r;
		struct kvm_vcpu *v;

		kvm_for_each_vcpu(r, v, vcpu->kvm) {
			if (v == vcpu) {
				data = r;
	case HV_X64_MSR_VP_INDEX:
		data = hv->vp_index;
		break;
			}
		}
		break;
	}
	case HV_X64_MSR_EOI:
		return kvm_hv_vapic_msr_read(vcpu, APIC_EOI, pdata);
	case HV_X64_MSR_ICR:
+1 −0
Original line number Diff line number Diff line
@@ -59,6 +59,7 @@ void kvm_hv_synic_send_eoi(struct kvm_vcpu *vcpu, int vector);
int kvm_hv_activate_synic(struct kvm_vcpu *vcpu, bool dont_zero_synic_pages);

void kvm_hv_vcpu_init(struct kvm_vcpu *vcpu);
void kvm_hv_vcpu_postcreate(struct kvm_vcpu *vcpu);
void kvm_hv_vcpu_uninit(struct kvm_vcpu *vcpu);

static inline struct kvm_vcpu_hv_stimer *vcpu_to_stimer(struct kvm_vcpu *vcpu,
+3 −0
Original line number Diff line number Diff line
@@ -2666,6 +2666,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
	case KVM_CAP_HYPERV_SPIN:
	case KVM_CAP_HYPERV_SYNIC:
	case KVM_CAP_HYPERV_SYNIC2:
	case KVM_CAP_HYPERV_VP_INDEX:
	case KVM_CAP_PCI_SEGMENT:
	case KVM_CAP_DEBUGREGS:
	case KVM_CAP_X86_ROBUST_SINGLESTEP:
@@ -7688,6 +7689,8 @@ void kvm_arch_vcpu_postcreate(struct kvm_vcpu *vcpu)
	struct msr_data msr;
	struct kvm *kvm = vcpu->kvm;

	kvm_hv_vcpu_postcreate(vcpu);

	if (vcpu_load(vcpu))
		return;
	msr.data = 0x0;
Loading