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

Commit c7c9c56c authored by Yang Zhang's avatar Yang Zhang Committed by Gleb Natapov
Browse files

x86, apicv: add virtual interrupt delivery support



Virtual interrupt delivery avoids KVM to inject vAPIC interrupts
manually, which is fully taken care of by the hardware. This needs
some special awareness into existing interrupr injection path:

- for pending interrupt, instead of direct injection, we may need
  update architecture specific indicators before resuming to guest.

- A pending interrupt, which is masked by ISR, should be also
  considered in above update action, since hardware will decide
  when to inject it at right time. Current has_interrupt and
  get_interrupt only returns a valid vector from injection p.o.v.

Reviewed-by: default avatarMarcelo Tosatti <mtosatti@redhat.com>
Signed-off-by: default avatarKevin Tian <kevin.tian@intel.com>
Signed-off-by: default avatarYang Zhang <yang.z.zhang@Intel.com>
Signed-off-by: default avatarGleb Natapov <gleb@redhat.com>
parent 8d14695f
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -27,4 +27,10 @@ int kvm_apic_set_irq(struct kvm_vcpu *vcpu, struct kvm_lapic_irq *irq);
#define kvm_apic_present(x) (true)
#define kvm_lapic_enabled(x) (true)

static inline bool kvm_apic_vid_enabled(void)
{
	/* IA64 has no apicv supporting, do nothing here */
	return false;
}

#endif
+5 −0
Original line number Diff line number Diff line
@@ -699,6 +699,10 @@ struct kvm_x86_ops {
	void (*enable_nmi_window)(struct kvm_vcpu *vcpu);
	void (*enable_irq_window)(struct kvm_vcpu *vcpu);
	void (*update_cr8_intercept)(struct kvm_vcpu *vcpu, int tpr, int irr);
	int (*vm_has_apicv)(struct kvm *kvm);
	void (*hwapic_irr_update)(struct kvm_vcpu *vcpu, int max_irr);
	void (*hwapic_isr_update)(struct kvm *kvm, int isr);
	void (*load_eoi_exitmap)(struct kvm_vcpu *vcpu, u64 *eoi_exit_bitmap);
	void (*set_virtual_x2apic_mode)(struct kvm_vcpu *vcpu, bool set);
	int (*set_tss_addr)(struct kvm *kvm, unsigned int addr);
	int (*get_tdp_level)(void);
@@ -994,6 +998,7 @@ int kvm_age_hva(struct kvm *kvm, unsigned long hva);
int kvm_test_age_hva(struct kvm *kvm, unsigned long hva);
void kvm_set_spte_hva(struct kvm *kvm, unsigned long hva, pte_t pte);
int cpuid_maxphyaddr(struct kvm_vcpu *vcpu);
int kvm_cpu_has_injectable_intr(struct kvm_vcpu *v);
int kvm_cpu_has_interrupt(struct kvm_vcpu *vcpu);
int kvm_arch_interrupt_allowed(struct kvm_vcpu *vcpu);
int kvm_cpu_get_interrupt(struct kvm_vcpu *v);
+11 −0
Original line number Diff line number Diff line
@@ -62,6 +62,7 @@
#define EXIT_REASON_MCE_DURING_VMENTRY  41
#define EXIT_REASON_TPR_BELOW_THRESHOLD 43
#define EXIT_REASON_APIC_ACCESS         44
#define EXIT_REASON_EOI_INDUCED         45
#define EXIT_REASON_EPT_VIOLATION       48
#define EXIT_REASON_EPT_MISCONFIG       49
#define EXIT_REASON_WBINVD              54
@@ -144,6 +145,7 @@
#define SECONDARY_EXEC_WBINVD_EXITING		0x00000040
#define SECONDARY_EXEC_UNRESTRICTED_GUEST	0x00000080
#define SECONDARY_EXEC_APIC_REGISTER_VIRT       0x00000100
#define SECONDARY_EXEC_VIRTUAL_INTR_DELIVERY    0x00000200
#define SECONDARY_EXEC_PAUSE_LOOP_EXITING	0x00000400
#define SECONDARY_EXEC_ENABLE_INVPCID		0x00001000

@@ -181,6 +183,7 @@ enum vmcs_field {
	GUEST_GS_SELECTOR               = 0x0000080a,
	GUEST_LDTR_SELECTOR             = 0x0000080c,
	GUEST_TR_SELECTOR               = 0x0000080e,
	GUEST_INTR_STATUS               = 0x00000810,
	HOST_ES_SELECTOR                = 0x00000c00,
	HOST_CS_SELECTOR                = 0x00000c02,
	HOST_SS_SELECTOR                = 0x00000c04,
@@ -208,6 +211,14 @@ enum vmcs_field {
	APIC_ACCESS_ADDR_HIGH		= 0x00002015,
	EPT_POINTER                     = 0x0000201a,
	EPT_POINTER_HIGH                = 0x0000201b,
	EOI_EXIT_BITMAP0                = 0x0000201c,
	EOI_EXIT_BITMAP0_HIGH           = 0x0000201d,
	EOI_EXIT_BITMAP1                = 0x0000201e,
	EOI_EXIT_BITMAP1_HIGH           = 0x0000201f,
	EOI_EXIT_BITMAP2                = 0x00002020,
	EOI_EXIT_BITMAP2_HIGH           = 0x00002021,
	EOI_EXIT_BITMAP3                = 0x00002022,
	EOI_EXIT_BITMAP3_HIGH           = 0x00002023,
	GUEST_PHYSICAL_ADDRESS          = 0x00002400,
	GUEST_PHYSICAL_ADDRESS_HIGH     = 0x00002401,
	VMCS_LINK_POINTER               = 0x00002800,
+51 −5
Original line number Diff line number Diff line
@@ -37,6 +37,38 @@ int kvm_cpu_has_pending_timer(struct kvm_vcpu *vcpu)
}
EXPORT_SYMBOL(kvm_cpu_has_pending_timer);

/*
 * 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
		return 0;
}

/*
 * check if there is injectable interrupt:
 * when virtual interrupt delivery enabled,
 * interrupt from apic will handled by hardware,
 * we don't need to check it here.
 */
int kvm_cpu_has_injectable_intr(struct kvm_vcpu *v)
{
	if (!irqchip_in_kernel(v->kvm))
		return v->arch.interrupt.pending;

	if (kvm_cpu_has_extint(v))
		return 1;

	if (kvm_apic_vid_enabled(v->kvm))
		return 0;

	return kvm_apic_has_interrupt(v) != -1; /* LAPIC */
}

/*
 * check if there is pending interrupt without
 * intack.
@@ -46,27 +78,41 @@ int kvm_cpu_has_interrupt(struct kvm_vcpu *v)
	if (!irqchip_in_kernel(v->kvm))
		return v->arch.interrupt.pending;

	if (kvm_apic_accept_pic_intr(v) && pic_irqchip(v->kvm)->output)
		return pic_irqchip(v->kvm)->output;	/* PIC */
	if (kvm_cpu_has_extint(v))
		return 1;

	return kvm_apic_has_interrupt(v) != -1;	/* LAPIC */
}
EXPORT_SYMBOL_GPL(kvm_cpu_has_interrupt);

/*
 * Read pending interrupt(from non-APIC source)
 * vector and intack.
 */
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;
}

/*
 * Read pending interrupt vector and intack.
 */
int kvm_cpu_get_interrupt(struct kvm_vcpu *v)
{
	int vector;

	if (!irqchip_in_kernel(v->kvm))
		return v->arch.interrupt.nr;

	if (kvm_apic_accept_pic_intr(v) && pic_irqchip(v->kvm)->output)
		return kvm_pic_read_irq(v->kvm);	/* PIC */
	vector = kvm_cpu_get_extint(v);

	if (kvm_apic_vid_enabled(v->kvm) || vector != -1)
		return vector;			/* PIC */

	return kvm_get_apic_interrupt(v);	/* APIC */
}
EXPORT_SYMBOL_GPL(kvm_cpu_get_interrupt);

void kvm_inject_pending_timer_irqs(struct kvm_vcpu *vcpu)
{
+83 −23
Original line number Diff line number Diff line
@@ -145,21 +145,51 @@ static inline int kvm_apic_id(struct kvm_lapic *apic)
	return (kvm_apic_get_reg(apic, APIC_ID) >> 24) & 0xff;
}

static inline u16 apic_cluster_id(struct kvm_apic_map *map, u32 ldr)
void kvm_calculate_eoi_exitmap(struct kvm_vcpu *vcpu,
				struct kvm_lapic_irq *irq,
				u64 *eoi_exit_bitmap)
{
	u16 cid;
	ldr >>= 32 - map->ldr_bits;
	cid = (ldr >> map->cid_shift) & map->cid_mask;
	struct kvm_lapic **dst;
	struct kvm_apic_map *map;
	unsigned long bitmap = 1;
	int i;

	BUG_ON(cid >= ARRAY_SIZE(map->logical_map));
	rcu_read_lock();
	map = rcu_dereference(vcpu->kvm->arch.apic_map);

	return cid;
	if (unlikely(!map)) {
		__set_bit(irq->vector, (unsigned long *)eoi_exit_bitmap);
		goto out;
	}

static inline u16 apic_logical_id(struct kvm_apic_map *map, u32 ldr)
{
	ldr >>= (32 - map->ldr_bits);
	return ldr & map->lid_mask;
	if (irq->dest_mode == 0) { /* physical mode */
		if (irq->delivery_mode == APIC_DM_LOWEST ||
				irq->dest_id == 0xff) {
			__set_bit(irq->vector,
				  (unsigned long *)eoi_exit_bitmap);
			goto out;
		}
		dst = &map->phys_map[irq->dest_id & 0xff];
	} else {
		u32 mda = irq->dest_id << (32 - map->ldr_bits);

		dst = map->logical_map[apic_cluster_id(map, mda)];

		bitmap = apic_logical_id(map, mda);
	}

	for_each_set_bit(i, &bitmap, 16) {
		if (!dst[i])
			continue;
		if (dst[i]->vcpu == vcpu) {
			__set_bit(irq->vector,
				  (unsigned long *)eoi_exit_bitmap);
			break;
		}
	}

out:
	rcu_read_unlock();
}

static void recalculate_apic_map(struct kvm *kvm)
@@ -225,6 +255,8 @@ out:

	if (old)
		kfree_rcu(old, rcu);

	kvm_ioapic_make_eoibitmap_request(kvm);
}

static inline void kvm_apic_set_id(struct kvm_lapic *apic, u8 id)
@@ -340,6 +372,10 @@ static inline int apic_find_highest_irr(struct kvm_lapic *apic)
{
	int result;

	/*
	 * Note that irr_pending is just a hint. It will be always
	 * true with virtual interrupt delivery enabled.
	 */
	if (!apic->irr_pending)
		return -1;

@@ -456,6 +492,8 @@ static void pv_eoi_clr_pending(struct kvm_vcpu *vcpu)
static inline int apic_find_highest_isr(struct kvm_lapic *apic)
{
	int result;

	/* Note that isr_count is always 1 with vid enabled */
	if (!apic->isr_count)
		return -1;
	if (likely(apic->highest_isr_cache != -1))
@@ -735,6 +773,19 @@ int kvm_apic_compare_prio(struct kvm_vcpu *vcpu1, struct kvm_vcpu *vcpu2)
	return vcpu1->arch.apic_arb_prio - vcpu2->arch.apic_arb_prio;
}

static void kvm_ioapic_send_eoi(struct kvm_lapic *apic, int vector)
{
	if (!(kvm_apic_get_reg(apic, APIC_SPIV) & APIC_SPIV_DIRECTED_EOI) &&
	    kvm_ioapic_handles_vector(apic->vcpu->kvm, vector)) {
		int trigger_mode;
		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->kvm, vector, trigger_mode);
	}
}

static int apic_set_eoi(struct kvm_lapic *apic)
{
	int vector = apic_find_highest_isr(apic);
@@ -751,19 +802,26 @@ static int apic_set_eoi(struct kvm_lapic *apic)
	apic_clear_isr(vector, apic);
	apic_update_ppr(apic);

	if (!(kvm_apic_get_reg(apic, APIC_SPIV) & APIC_SPIV_DIRECTED_EOI) &&
	    kvm_ioapic_handles_vector(apic->vcpu->kvm, vector)) {
		int trigger_mode;
		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->kvm, vector, trigger_mode);
	}
	kvm_ioapic_send_eoi(apic, vector);
	kvm_make_request(KVM_REQ_EVENT, apic->vcpu);
	return vector;
}

/*
 * this interface assumes a trap-like exit, which has already finished
 * desired side effect including vISR and vPPR update.
 */
void kvm_apic_set_eoi_accelerated(struct kvm_vcpu *vcpu, int vector)
{
	struct kvm_lapic *apic = vcpu->arch.apic;

	trace_kvm_eoi(apic, vector);

	kvm_ioapic_send_eoi(apic, vector);
	kvm_make_request(KVM_REQ_EVENT, apic->vcpu);
}
EXPORT_SYMBOL_GPL(kvm_apic_set_eoi_accelerated);

static void apic_send_ipi(struct kvm_lapic *apic)
{
	u32 icr_low = kvm_apic_get_reg(apic, APIC_ICR);
@@ -1375,8 +1433,8 @@ void kvm_lapic_reset(struct kvm_vcpu *vcpu)
		apic_set_reg(apic, APIC_ISR + 0x10 * i, 0);
		apic_set_reg(apic, APIC_TMR + 0x10 * i, 0);
	}
	apic->irr_pending = false;
	apic->isr_count = 0;
	apic->irr_pending = kvm_apic_vid_enabled(vcpu->kvm);
	apic->isr_count = kvm_apic_vid_enabled(vcpu->kvm);
	apic->highest_isr_cache = -1;
	update_divide_count(apic);
	atomic_set(&apic->lapic_timer.pending, 0);
@@ -1591,8 +1649,10 @@ void kvm_apic_post_state_restore(struct kvm_vcpu *vcpu,
	update_divide_count(apic);
	start_apic_timer(apic);
	apic->irr_pending = true;
	apic->isr_count = count_vectors(apic->regs + APIC_ISR);
	apic->isr_count = kvm_apic_vid_enabled(vcpu->kvm) ?
				1 : count_vectors(apic->regs + APIC_ISR);
	apic->highest_isr_cache = -1;
	kvm_x86_ops->hwapic_isr_update(vcpu->kvm, apic_find_highest_isr(apic));
	kvm_make_request(KVM_REQ_EVENT, vcpu);
}

Loading