RFC: kvm eoi PV using shared memory
Subsystems (10)
Michael S. Tsirkin
maintainerPATCH 1/1 RFC: kvm eoi PV using shared memory
I took a stub at implementing PV EOI using shared memory. This should reduce the number of exits an interrupt causes as much as by half.
A partially complete draft for both host and guest parts is below.
The idea is simple: there's a bit, per APIC, in guest memory, that tells the guest that it does not need EOI.
We set it before injecting an interrupt and clear before injecting a nested one. Guest tests it using a test and clear operation - this is necessary
so that host can detect interrupt nesting -
and if set, it can skip the EOI MSR.
There's a new MSR to set the address of said register in guest memory. Otherwise not much changes:
- Guest EOI is not required
- ISR is automatically cleared before injection
Some things are incomplete: add feature negotiation options, qemu support for said options.
No testing was done beyond compiling the kernel.
I would appreciate early feedback.
--
arch/x86/include/asm/apic.h +5 -0
@@ -457,8 +457,13 @@ static inline u32 safe_apic_wait_icr_idle(void) { return 0; }#endif /* CONFIG_X86_LOCAL_APIC */ +DECLARE_EARLY_PER_CPU(unsigned long, apic_eoi); +static inline void ack_APIC_irq (void ) { +if (__test_and_clear_bit (0 , &__get_cpu_var (apic_eoi))) +return ; +/* * ack_APIC_irq() actually gets compiled as a single instruction * ... yummie.
arch/x86/include/asm/kvm_host.h +6 -0
@@ -481,6 +481,12 @@ struct kvm_vcpu_arch {u64 length;u64 status; } osvw; + +struct { +u64 msr_val; +struct gfn_to_hva_cache data; +int vector; + } eoi; };struct kvm_lpage_info {
arch/x86/include/asm/kvm_para.h +2 -0
@@ -37,6 +37,8 @@#define MSR_KVM_SYSTEM_TIME_NEW 0x4b564d01#define MSR_KVM_ASYNC_PF_EN 0x4b564d02#define MSR_KVM_STEAL_TIME 0x4b564d03 +#define MSR_KVM_EOI_EN 0x4b564d04 +#define MSR_KVM_EOI_ENABLED 0x1struct kvm_steal_time {__u64 steal;
arch/x86/kernel/apic/apic.c +3 -0
@@ -89,6 +89,9 @@ EXPORT_EARLY_PER_CPU_SYMBOL(x86_bios_cpu_apicid);*/ DEFINE_EARLY_PER_CPU (int, x86_cpu_to_logical_apicid,BAD_APICID ); +DEFINE_EARLY_PER_CPU(unsigned long, apic_eoi, 0); +EXPORT_EARLY_PER_CPU_SYMBOL (apic_eoi); +/* * Knob to control our willingness to enable the local APIC. *
arch/x86/kernel/kvm.c +4 -0
@@ -39,6 +39,7 @@#include <asm/desc.h> #include <asm/tlbflush.h> #include <asm/idle.h> +#include <asm/apic.h> static int kvmapf =1 ;
@@ -307,6 +308,9 @@ void __cpuinit kvm_guest_cpu_init(void) smp_processor_id()); } +wrmsrl (MSR_KVM_EOI_EN ,__pa (this_cpu_ptr (apic_eoi)) | +MSR_KVM_EOI_ENABLED ); +if (has_steal_clock)kvm_register_steal_time (); }
arch/x86/kvm/lapic.c +83 -2
113 lines not on this page · raw message
arch/x86/kvm/x86.c +25 -0
@@ -1502,6 +1502,27 @@ static int kvm_pv_enable_async_pf(struct kvm_vcpu *vcpu, u64 data)return 0 ; } +static int kvm_pv_enable_apic_eoi (struct kvm_vcpu *vcpu,u64 data) +{ +gpa_t gpa = data & ~MSR_KVM_EOI_ENABLED ; + +/* Bit 1 is reserved, Should be zero. */ +if (data &0x2 ) +return 1 ; + + vcpu->arch.eoi.msr_val = data; + vcpu->arch.eoi.vector =-1 ; + +if (!(data &MSR_KVM_EOI_ENABLED )) { +return 0 ; + } + +if (kvm_gfn_to_hva_cache_init (vcpu->kvm, &vcpu->arch.eoi.data, gpa)) +return 1 ; + +return 0 ; +} +static void kvmclock_reset (struct kvm_vcpu *vcpu) {if (vcpu->arch.time_page) {
@@ -1627,6 +1648,10 @@ int kvm_set_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 data)if (kvm_pv_enable_async_pf (vcpu, data))return 1 ;break ; +case MSR_KVM_EOI_EN : +if (kvm_pv_enable_apic_eoi (vcpu, data)) +return 1 ; +break ;case MSR_KVM_STEAL_TIME :if (unlikely (!sched_info_on ()))
drivers/block/virtio_blk.c +0 -1
@@ -372,7 +372,6 @@ static int virtblk_name_format(char *prefix, int index, char *buf, int buflen) {const int base ='z' -'a' +1 ;char *begin = buf +strlen (prefix); -char *begin = buf +strlen (prefix);char *end = buf + buflen;char *p;int unit;
net/core/dev.c +5 -5
@@ -2455,6 +2455,11 @@ static inline int __dev_xmit_skb(struct sk_buff *skb, struct Qdisc *q, rc =NET_XMIT_SUCCESS ; }else {skb_dst_force (skb); +/* Orphan the skb - required if we might hang on to it +* for indefinite time. */ +if (unlikely (dev->priv_flags &IFF_TX_CAN_STALL )) +skb_orphan (skb); + rc = q->enqueue (skb, q) &NET_XMIT_MASK ;if (qdisc_run_begin (q)) {if (unlikely (contended)) {
@@ -2517,11 +2522,6 @@ int dev_queue_xmit(struct sk_buff *skb)struct Qdisc *q;int rc = -ENOMEM ; -/* Orphan the skb - required if we might hang on to it -* for indefinite time. */ -if (dev->priv_flags &IFF_TX_CAN_STALL ) -skb_orphan (skb); -/* Disable soft irqs for various locks below. Also * stops preemption for RCU. */
net/sched/sch_generic.c +4 -7
@@ -120,14 +120,11 @@ int sch_direct_xmit(struct sk_buff *skb, struct Qdisc *q,/* And release qdisc */ spin_unlock (root_lock); -/* Orphan the skb - required if we might hang on to it -* for indefinite time. */ -if (dev->priv_flags &IFF_TX_CAN_STALL ) -skb_orphan (skb); -HARD_TX_LOCK (dev, txq,smp_processor_id ()); -if (!netif_xmit_frozen_or_stopped (txq)) +if (likely (!netif_xmit_frozen_or_stopped (txq))) ret =dev_hard_start_xmit (skb, dev, txq); +else if (dev->priv_flags &IFF_TX_CAN_STALL ) +skb_orphan (skb);HARD_TX_UNLOCK (dev, txq);
@@ -695,7 +692,7 @@ static void attach_one_default_qdisc(struct net_device *dev, {struct Qdisc *qdisc = &noqueue_qdisc; -if (dev->tx_queue_len) { +if (dev->tx_queue_len && !(dev->priv_flags &IFF_TX_CAN_STALL )) { qdisc =qdisc_create_dflt (dev_queue, &pfifo_fast_ops,TC_H_ROOT );if (!qdisc) {
Quoted 35 lines · Michael S. Tsirkin
I took a stub at implementing PV EOI using shared memory. This should reduce the number of exits an interrupt causes as much as by half.
A partially complete draft for both host and guest parts is below.
The idea is simple: there's a bit, per APIC, in guest memory, that tells the guest that it does not need EOI.
We set it before injecting an interrupt and clear before injecting a nested one. Guest tests it using a test and clear operation - this is necessary
so that host can detect interrupt nesting -
and if set, it can skip the EOI MSR.There's a new MSR to set the address of said register in guest memory. Otherwise not much changes:
- Guest EOI is not required
- ISR is automatically cleared before injectionSome things are incomplete: add feature negotiation options, qemu support for said options.
No testing was done beyond compiling the kernel.I would appreciate early feedback.
Signed-off-by Michael S. Tsirkin <[email protected]>--
arch/x86/include/asm/apic.h +0
-0
arch/x86/include/asm/apic.h static inline u32 safe_apic_wait_icr_idle(void) { return 0; } · line 466
Quoted 10 lines
arch/x86/include/asm/apic.h +5
-0@@ -457,8 +457,13 @@ static inline u32 safe_apic_wait_icr_idle(void) { return 0; }#endif /* CONFIG_X86_LOCAL_APIC */ +DECLARE_EARLY_PER_CPU(unsigned long, apic_eoi); +static inline void ack_APIC_irq (void ) { +if (__test_and_clear_bit (0 , &__get_cpu_var (apic_eoi))) +return ; +
While __test_and_clear_bit() is implemented in a single instruction, it's not required to be. Better have the instruction there explicitly.
Quoted 8 lines
arch/x86/include/asm/apic.h +0
-0/* * ack_APIC_irq() actually gets compiled as a single instruction * ... yummie. arch/x86/include/asm/kvm_host.h +0
-0
arch/x86/include/asm/kvm_host.h struct kvm_vcpu_arch { · line 490
Quoted 10 lines
arch/x86/include/asm/kvm_host.h +6
-0@@ -481,6 +481,12 @@ struct kvm_vcpu_arch {u64 length;u64 status; } osvw; + +struct { +u64 msr_val; +struct gfn_to_hva_cache data; +int vector; + } eoi; };
Needs to be cleared on INIT.
arch/x86/include/asm/kvm_host.h void __cpuinit kvm_guest_cpu_init(void) · line 313
Quoted 9 lines
arch/x86/include/asm/kvm_host.h +3
-0@@ -307,6 +308,9 @@ void __cpuinit kvm_guest_cpu_init(void) smp_processor_id()); } +wrmsrl (MSR_KVM_EOI_EN ,__pa (this_cpu_ptr (apic_eoi)) | +MSR_KVM_EOI_ENABLED ); +
Clear on kexec.
Quoted 77 lines
arch/x86/include/asm/kvm_host.h +0
-0if (has_steal_clock)kvm_register_steal_time (); }arch/x86/kvm/lapic.c +55
-1@@ -265,7 +265,61 @@ int kvm_apic_set_irq(struct kvm_vcpu *vcpu, struct kvm_lapic_irq *irq) irq->level, irq->trig_mode); } -static inline int apic_find_highest_isr (struct kvm_lapic *apic) +static int eoi_put_user (struct kvm_vcpu *vcpu,u32 val) +{ + +return kvm_write_guest_cached (vcpu->kvm, &vcpu->arch.eoi.data, &val, +sizeof (val)); +} + +static int eoi_get_user (struct kvm_vcpu *vcpu,u32 *val) +{ + +return kvm_read_guest_cached (vcpu->kvm, &vcpu->arch.eoi.data, val, +sizeof (*val)); +} + +static inline bool eoi_enabled (struct kvm_vcpu *vcpu) +{ +return (vcpu->arch.eoi.msr_val &MSR_KVM_EOI_ENABLED ); +} + +static int eoi_get_pending_vector (struct kvm_vcpu *vcpu) +{ +u32 val; +if (eoi_get_user (vcpu, &val) <0 ) +apic_debug ("Can't read EOI MSR value: 0x%llx\n" , + (unsigned long long )vcpi->arch.eoi.msr_val); +if (!(val &0x1 )) + vcpu->arch.eoi.vector =-1 ; +return vcpu->arch.eoi.vector; +} + +static void eoi_set_pending_vector (struct kvm_vcpu *vcpu,int vector) +{ +BUG_ON (vcpu->arch.eoi.vector !=-1 ); +if (eoi_put_user (vcpu,0x1 ) <0 ) { +apic_debug ("Can't set EOI MSR value: 0x%llx\n" , + (unsigned long long )vcpi->arch.eoi.msr_val); +return ; + } + vcpu->arch.eoi.vector = vector; +} + +static int eoi_clr_pending_vector (struct kvm_vcpu *vcpu) +{ +int vector; + vector = vcpu->arch.eoi.vector; +if (vector !=-1 &&eoi_put_user (vcpu,0x0 ) <0 ) { +apic_debug ("Can't clear EOI MSR value: 0x%llx\n" , + (unsigned long long )vcpi->arch.eoi.msr_val); +return -1 ; + } + vcpu->arch.eoi.vector =-1 ; +return vector; +} + +static inline int __apic_find_highest_isr (struct kvm_lapic *apic){
arch/x86/kvm/lapic.c +0
-0int result;@@ -275,6 +329,17 @@ static inline int apic_find_highest_isr(struct kvm_lapic *apic)return result; }
arch/x86/kvm/lapic.c static inline int apic_find_highest_isr(struct kvm_lapic *apic) · line 341
Quoted 10 lines
arch/x86/kvm/lapic.c +10
-0+static inline int apic_find_highest_isr (struct kvm_lapic *apic) +{ +int vector; +if (eoi_enabled (apic->vcpu)) { + vector =eoi_get_pending_vector (apic->vcpu); +if (vector !=-1 ) +return vector; + } +return __apic_find_highest_isr (apic); +}
Why aren't you modifying the ISR unconfitionally?
Quoted 17 lines
arch/x86/kvm/lapic.c +3
-0+static void apic_update_ppr (struct kvm_lapic *apic) {u32 tpr, isrv, ppr, old_ppr;@@ -488,6 +553,8 @@ static void apic_set_eoi(struct kvm_lapic *apic)if (vector ==-1 )return ; +if (eoi_enabled (apic->vcpu)) +eoi_clr_pending_vector (apic->vcpu);apic_clear_vector (vector, apic->regs +APIC_ISR );apic_update_ppr (apic);@@ -1236,11 +1303,25 @@ int kvm_get_apic_interrupt(struct kvm_vcpu *vcpu) {int vector =kvm_apic_has_interrupt (vcpu);struct kvm_lapic *apic = vcpu->arch.apic;
arch/x86/kvm/lapic.c int kvm_get_apic_interrupt(struct kvm_vcpu *vcpu) · line 1314
Quoted 10 lines
arch/x86/kvm/lapic.c +5
-1+bool set_isr = true;if (vector ==-1 )return -1 ; -apic_set_vector (vector, apic->regs +APIC_ISR ); +if (eoi_enabled (vcpu)) { +/* Anything pending? If yes disable eoi optimization. */ +if (unlikely (apic_find_highest_isr (apic) >=0 )) { +int v =eoi_clr_pending_vector (vcpu);
ISR != pending, that's IRR. If ISR vector has lower priority than the new vector, then we don't need to disable eoi avoidance.
arch/x86/kvm/lapic.c int kvm_get_apic_interrupt(struct kvm_vcpu *vcpu) · line 1319
Quoted 5 lines
arch/x86/kvm/lapic.c +5
-0+if (v !=-1 ) +apic_set_vector (v, apic->regs +APIC_ISR ); + }else { +eoi_set_pending_vector (vcpu, vector); + set_isr = false;
Weird. Just set it normally. Remember that reading the ISR needs to return the correct value.
We need to process the avoided EOI before any APIC read/writes, to be sure the guest sees the updated values. Same for IOAPIC, EOI affects remote_irr. That may been we need to sample it after every exit, or perhaps disable the feature for level-triggered interrupts.
Quoted 10 lines
arch/x86/kvm/lapic.c +5
-0+ } + } + +if (set_isr) +apic_set_vector (vector, apic->regs +APIC_ISR );apic_update_ppr (apic);apic_clear_irr (vector, apic);return vector;arch/x86/kvm/x86.c +0
-0
Signature · 1 line
error compiling committee.c: too many arguments to function
Michael S. Tsirkin
maintainerQuoted 62 lines · Avi Kivity
Quoted 35 lines · Michael S. Tsirkin
I took a stub at implementing PV EOI using shared memory. This should reduce the number of exits an interrupt causes as much as by half.
A partially complete draft for both host and guest parts is below.
The idea is simple: there's a bit, per APIC, in guest memory, that tells the guest that it does not need EOI.
We set it before injecting an interrupt and clear before injecting a nested one. Guest tests it using a test and clear operation - this is necessary
so that host can detect interrupt nesting -
and if set, it can skip the EOI MSR.There's a new MSR to set the address of said register in guest memory. Otherwise not much changes:
- Guest EOI is not required
- ISR is automatically cleared before injectionSome things are incomplete: add feature negotiation options, qemu support for said options.
No testing was done beyond compiling the kernel.I would appreciate early feedback.
Signed-off-by Michael S. Tsirkin <[email protected]>--
arch/x86/include/asm/apic.h +0
-0arch/x86/include/asm/apic.h static inline u32 safe_apic_wait_icr_idle(void) { return 0; } · line 466
Quoted 10 lines
arch/x86/include/asm/apic.h +5
-0@@ -457,8 +457,13 @@ static inline u32 safe_apic_wait_icr_idle(void) { return 0; }#endif /* CONFIG_X86_LOCAL_APIC */ +DECLARE_EARLY_PER_CPU(unsigned long, apic_eoi); +static inline void ack_APIC_irq (void ) { +if (__test_and_clear_bit (0 , &__get_cpu_var (apic_eoi))) +return ; +While
__test_and_clear_bit()is implemented in a single instruction, it's not required to be. Better have the instruction there explicitly.Quoted 12 lines
arch/x86/include/asm/apic.h +0
-0/* * ack_APIC_irq() actually gets compiled as a single instruction * ... yummie. arch/x86/include/asm/kvm_host.h +1
-0@@ -481,6 +481,12 @@ struct kvm_vcpu_arch {u64 length;u64 status; } osvw; +
Quoted 8 lines
arch/x86/include/asm/kvm_host.h struct kvm_vcpu_arch { · line 490
Quoted 6 lines
arch/x86/include/asm/kvm_host.h +5
-0+struct { +u64 msr_val; +struct gfn_to_hva_cache data; +int vector; + } eoi; };Needs to be cleared on INIT.
You mean kvm_arch_vcpu_reset?
Quoted 11 lines
arch/x86/include/asm/kvm_host.h void __cpuinit kvm_guest_cpu_init(void) · line 313
Quoted 9 lines
arch/x86/include/asm/kvm_host.h +3
-0@@ -307,6 +308,9 @@ void __cpuinit kvm_guest_cpu_init(void) smp_processor_id()); } +wrmsrl (MSR_KVM_EOI_EN ,__pa (this_cpu_ptr (apic_eoi)) | +MSR_KVM_EOI_ENABLED ); +Clear on kexec.
With register_reboot_notifier?
Quoted 77 lines
Quoted 77 lines
arch/x86/include/asm/kvm_host.h +0
-0if (has_steal_clock)kvm_register_steal_time (); }arch/x86/kvm/lapic.c +55
-1@@ -265,7 +265,61 @@ int kvm_apic_set_irq(struct kvm_vcpu *vcpu, struct kvm_lapic_irq *irq) irq->level, irq->trig_mode); } -static inline int apic_find_highest_isr (struct kvm_lapic *apic) +static int eoi_put_user (struct kvm_vcpu *vcpu,u32 val) +{ + +return kvm_write_guest_cached (vcpu->kvm, &vcpu->arch.eoi.data, &val, +sizeof (val)); +} + +static int eoi_get_user (struct kvm_vcpu *vcpu,u32 *val) +{ + +return kvm_read_guest_cached (vcpu->kvm, &vcpu->arch.eoi.data, val, +sizeof (*val)); +} + +static inline bool eoi_enabled (struct kvm_vcpu *vcpu) +{ +return (vcpu->arch.eoi.msr_val &MSR_KVM_EOI_ENABLED ); +} + +static int eoi_get_pending_vector (struct kvm_vcpu *vcpu) +{ +u32 val; +if (eoi_get_user (vcpu, &val) <0 ) +apic_debug ("Can't read EOI MSR value: 0x%llx\n" , + (unsigned long long )vcpi->arch.eoi.msr_val); +if (!(val &0x1 )) + vcpu->arch.eoi.vector =-1 ; +return vcpu->arch.eoi.vector; +} + +static void eoi_set_pending_vector (struct kvm_vcpu *vcpu,int vector) +{ +BUG_ON (vcpu->arch.eoi.vector !=-1 ); +if (eoi_put_user (vcpu,0x1 ) <0 ) { +apic_debug ("Can't set EOI MSR value: 0x%llx\n" , + (unsigned long long )vcpi->arch.eoi.msr_val); +return ; + } + vcpu->arch.eoi.vector = vector; +} + +static int eoi_clr_pending_vector (struct kvm_vcpu *vcpu) +{ +int vector; + vector = vcpu->arch.eoi.vector; +if (vector !=-1 &&eoi_put_user (vcpu,0x0 ) <0 ) { +apic_debug ("Can't clear EOI MSR value: 0x%llx\n" , + (unsigned long long )vcpi->arch.eoi.msr_val); +return -1 ; + } + vcpu->arch.eoi.vector =-1 ; +return vector; +} + +static inline int __apic_find_highest_isr (struct kvm_lapic *apic){
arch/x86/kvm/lapic.c +0
-0int result;@@ -275,6 +329,17 @@ static inline int apic_find_highest_isr(struct kvm_lapic *apic)return result; }
Quoted 12 lines
arch/x86/kvm/lapic.c static inline int apic_find_highest_isr(struct kvm_lapic *apic) · line 341
Quoted 10 lines
arch/x86/kvm/lapic.c +10
-0+static inline int apic_find_highest_isr (struct kvm_lapic *apic) +{ +int vector; +if (eoi_enabled (apic->vcpu)) { + vector =eoi_get_pending_vector (apic->vcpu); +if (vector !=-1 ) +return vector; + } +return __apic_find_highest_isr (apic); +}Why aren't you modifying the ISR unconfitionally?
ISR is not set if there won't be an EOI
since it's EOI that normally clears it.
Quoted 22 lines
Quoted 22 lines
arch/x86/kvm/lapic.c +4
-0+static void apic_update_ppr (struct kvm_lapic *apic) {u32 tpr, isrv, ppr, old_ppr;@@ -488,6 +553,8 @@ static void apic_set_eoi(struct kvm_lapic *apic)if (vector ==-1 )return ; +if (eoi_enabled (apic->vcpu)) +eoi_clr_pending_vector (apic->vcpu);apic_clear_vector (vector, apic->regs +APIC_ISR );apic_update_ppr (apic);@@ -1236,11 +1303,25 @@ int kvm_get_apic_interrupt(struct kvm_vcpu *vcpu) {int vector =kvm_apic_has_interrupt (vcpu);struct kvm_lapic *apic = vcpu->arch.apic; +bool set_isr = true;if (vector ==-1 )return -1 ;
Quoted 8 lines
arch/x86/kvm/lapic.c int kvm_get_apic_interrupt(struct kvm_vcpu *vcpu) · line 1314
Quoted 5 lines
arch/x86/kvm/lapic.c +4
-1-apic_set_vector (vector, apic->regs +APIC_ISR ); +if (eoi_enabled (vcpu)) { +/* Anything pending? If yes disable eoi optimization. */ +if (unlikely (apic_find_highest_isr (apic) >=0 )) { +int v =eoi_clr_pending_vector (vcpu);ISR != pending, that's IRR. If ISR vector has lower priority than the new vector, then we don't need to disable eoi avoidance.
Yes. But we can and it's easier than figuring out priorities. I am guessing such collisions are rare, right?
I'll add a trace to make sure.
Quoted 8 lines
arch/x86/kvm/lapic.c int kvm_get_apic_interrupt(struct kvm_vcpu *vcpu) · line 1319
Quoted 5 lines
arch/x86/kvm/lapic.c +5
-0+if (v !=-1 ) +apic_set_vector (v, apic->regs +APIC_ISR ); + }else { +eoi_set_pending_vector (vcpu, vector); + set_isr = false;Weird. Just set it normally. Remember that reading the ISR needs to return the correct value.
Marcelo said linux does not normally read ISR - not true? Note this has no effect if the PV optimization is not enabled.
Quoted 4 lines
We need to process the avoided EOI before any APIC read/writes, to be sure the guest sees the updated values. Same for IOAPIC, EOI affects
remote_irr. That may been we need to sample it after every exit, or perhaps disable the feature for level-triggered interrupts.
Disabling would be very sad. Can we sample on remote irr read?
Quoted 13 lines
Quoted 10 lines
arch/x86/kvm/lapic.c +5
-0+ } + } + +if (set_isr) +apic_set_vector (vector, apic->regs +APIC_ISR );apic_update_ppr (apic);apic_clear_irr (vector, apic);return vector;arch/x86/kvm/x86.c +0
-0Signature · 1 line
error compiling committee.c: too many arguments to function
Quoted 12 lines · Michael S. Tsirkin
Quoted 11 lines
arch/x86/include/asm/kvm_host.h struct kvm_vcpu_arch { · line 490
Quoted 9 lines
arch/x86/include/asm/kvm_host.h +6
-0u64 status; } osvw; + +struct { +u64 msr_val; +struct gfn_to_hva_cache data; +int vector; + } eoi; };Needs to be cleared on INIT.
Quoted 1 line
You mean
kvm_arch_vcpu_reset?
Yes, or kvm_lapic_reset().
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
arch/x86/kernel/kvm.c +0
-0@@ -307,6 +308,9 @@ void __cpuinit kvm_guest_cpu_init(void) smp_processor_id()); }
Quoted 7 lines
Quoted 5 lines
arch/x86/kernel/kvm.c void __cpuinit kvm_guest_cpu_init(void) · line 313
Quoted 3 lines
arch/x86/kernel/kvm.c +3
-0+wrmsrl (MSR_KVM_EOI_EN ,__pa (this_cpu_ptr (apic_eoi)) | +MSR_KVM_EOI_ENABLED ); +Clear on kexec.
With
register_reboot_notifier?
Yes, we already clear some kvm msrs there.
Quoted 10 lines
Quoted 9 lines
arch/x86/kvm/lapic.c int kvm_get_apic_interrupt(struct kvm_vcpu *vcpu) · line 1314
Quoted 6 lines
arch/x86/kvm/lapic.c +4
-1-apic_set_vector (vector, apic->regs +APIC_ISR ); +if (eoi_enabled (vcpu)) { +/* Anything pending? If yes disable eoi optimization. */ +if (unlikely (apic_find_highest_isr (apic) >=0 )) { +int v =eoi_clr_pending_vector (vcpu);ISR != pending, that's IRR. If ISR vector has lower priority than the new vector, then we don't need to disable eoi avoidance.
Quoted 2 lines
Yes. But we can and it's easier than figuring out priorities. I am guessing such collisions are rare, right?
It's pretty easy, if there is something in IRR but kvm_lapic_has_interrupt() returns -1, then we need to disable eoi avoidance.
Quoted 11 lines
I'll add a trace to make sure.
Quoted 8 lines
arch/x86/kvm/lapic.c int kvm_get_apic_interrupt(struct kvm_vcpu *vcpu) · line 1319
Quoted 5 lines
arch/x86/kvm/lapic.c +5
-0+if (v !=-1 ) +apic_set_vector (v, apic->regs +APIC_ISR ); + }else { +eoi_set_pending_vector (vcpu, vector); + set_isr = false;Weird. Just set it normally. Remember that reading the ISR needs to return the correct value.
Quoted 1 line
Marcelo said linux does not normally read ISR - not true?
It's true and it's irrelevant. We aren't coding a feature to what linux does now, but for what linux or another guest may do in the future.
Quoted 8 lines
Note this has no effect if the PV optimization is not enabled.
Quoted 4 lines
We need to process the avoided EOI before any APIC read/writes, to be sure the guest sees the updated values. Same for IOAPIC, EOI affects
remote_irr. That may been we need to sample it after every exit, or perhaps disable the feature for level-triggered interrupts.Disabling would be very sad. Can we sample on remote irr read?
That can be done from another vcpu. Why do we care about level-triggered interrupts? Everything uses MSI or edge-triggered IOAPIC interrupts these days.
Signature · 1 line
error compiling committee.c: too many arguments to function
Michael S. Tsirkin
maintainerQuoted 43 lines · Avi Kivity
Quoted 12 lines · Michael S. Tsirkin
Quoted 11 lines
arch/x86/include/asm/kvm_host.h struct kvm_vcpu_arch { · line 490
Quoted 9 lines
arch/x86/include/asm/kvm_host.h +6
-0u64 status; } osvw; + +struct { +u64 msr_val; +struct gfn_to_hva_cache data; +int vector; + } eoi; };Needs to be cleared on INIT.
Quoted 1 line
You mean
kvm_arch_vcpu_reset?Yes, or
kvm_lapic_reset().Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
arch/x86/kernel/kvm.c +0
-0@@ -307,6 +308,9 @@ void __cpuinit kvm_guest_cpu_init(void) smp_processor_id()); }Quoted 7 lines
Quoted 5 lines
arch/x86/kernel/kvm.c void __cpuinit kvm_guest_cpu_init(void) · line 313
Quoted 3 lines
arch/x86/kernel/kvm.c +3
-0+wrmsrl (MSR_KVM_EOI_EN ,__pa (this_cpu_ptr (apic_eoi)) | +MSR_KVM_EOI_ENABLED ); +Clear on kexec.
With
register_reboot_notifier?Yes, we already clear some kvm msrs there.
Quoted 10 lines
Quoted 9 lines
arch/x86/kvm/lapic.c int kvm_get_apic_interrupt(struct kvm_vcpu *vcpu) · line 1314
Quoted 6 lines
arch/x86/kvm/lapic.c +4
-1-apic_set_vector (vector, apic->regs +APIC_ISR ); +if (eoi_enabled (vcpu)) { +/* Anything pending? If yes disable eoi optimization. */ +if (unlikely (apic_find_highest_isr (apic) >=0 )) { +int v =eoi_clr_pending_vector (vcpu);ISR != pending, that's IRR. If ISR vector has lower priority than the new vector, then we don't need to disable eoi avoidance.
Quoted 5 lines
Quoted 2 lines
Yes. But we can and it's easier than figuring out priorities. I am guessing such collisions are rare, right?
It's pretty easy, if there is something in IRR but
kvm_lapic_has_interrupt()returns -1, then we need to disable eoi avoidance.
I only see kvm_apic_has_interrupt - is this what you mean?
Quoted 11 lines
Quoted 11 lines
I'll add a trace to make sure.
Quoted 8 lines
arch/x86/kvm/lapic.c int kvm_get_apic_interrupt(struct kvm_vcpu *vcpu) · line 1319
Quoted 5 lines
arch/x86/kvm/lapic.c +5
-0+if (v !=-1 ) +apic_set_vector (v, apic->regs +APIC_ISR ); + }else { +eoi_set_pending_vector (vcpu, vector); + set_isr = false;Weird. Just set it normally. Remember that reading the ISR needs to return the correct value.
Quoted 4 lines
Quoted 1 line
Marcelo said linux does not normally read ISR - not true?
It's true and it's irrelevant. We aren't coding a feature to what linux does now, but for what linux or another guest may do in the future.
Right. So you think reading ISR has value in combination with PV EOI for future guests? I'm not arguing either way just curious.
Quoted 10 lines
Quoted 8 lines
Note this has no effect if the PV optimization is not enabled.
Quoted 4 lines
We need to process the avoided EOI before any APIC read/writes, to be sure the guest sees the updated values. Same for IOAPIC, EOI affects
remote_irr. That may been we need to sample it after every exit, or perhaps disable the feature for level-triggered interrupts.Disabling would be very sad. Can we sample on remote irr read?
That can be done from another vcpu.
We still can handle it, right? Where's the code that handles that read?
Quoted 3 lines
Why do we care about
level-triggered interrupts? Everything uses MSI or edge-triggered IOAPIC interrupts these days.
Well lots of emulated devices don't yet. They probably should but it's nice to be able to test with e.g. e1000 emulation not just virtio.
Besides, kvm_get_apic_interrupt
simply doesn't know about the triggering mode at the moment.
Signature · 1 line
MST
Quoted 8 lines · Michael S. Tsirkin
Quoted 6 lines
Quoted 3 lines
Yes. But we can and it's easier than figuring out priorities. I am guessing such collisions are rare, right?
It's pretty easy, if there is something in IRR but
kvm_lapic_has_interrupt()returns -1, then we need to disable eoi avoidance.I only see
kvm_apic_has_interrupt- is this what you mean?
Yes, sorry.
It's not clear whether to do the check in kvm_apic_has_interrupt() or kvm_apic_get_interrupt() - the latter is called only after interrupts are enabled, so it looks like a better place (EOIs while interrupts are disabled have no effect). But need to make sure those functions are actually called, since they're protected by KVM_REQ_EVENT.
Quoted 11 lines
Quoted 11 lines
Quoted 11 lines
I'll add a trace to make sure.
Quoted 8 lines
arch/x86/kvm/lapic.c int kvm_get_apic_interrupt(struct kvm_vcpu *vcpu) · line 1319
Quoted 5 lines
arch/x86/kvm/lapic.c +5
-0+if (v !=-1 ) +apic_set_vector (v, apic->regs +APIC_ISR ); + }else { +eoi_set_pending_vector (vcpu, vector); + set_isr = false;Weird. Just set it normally. Remember that reading the ISR needs to return the correct value.
Quoted 8 lines
Quoted 4 lines
Quoted 1 line
Marcelo said linux does not normally read ISR - not true?
It's true and it's irrelevant. We aren't coding a feature to what linux does now, but for what linux or another guest may do in the future.
Right. So you think reading ISR has value in combination with PV EOI for future guests? I'm not arguing either way just curious.
I don't. But we need to preserve the same interface the APIC has presented for thousands of years (well, almost).
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Note this has no effect if the PV optimization is not enabled.
Quoted 4 lines
We need to process the avoided EOI before any APIC read/writes, to be sure the guest sees the updated values. Same for IOAPIC, EOI affects
remote_irr. That may been we need to sample it after every exit, or perhaps disable the feature for level-triggered interrupts.
Quoted 5 lines
Quoted 3 lines
Quoted 1 line
Disabling would be very sad. Can we sample on remote irr read?
That can be done from another vcpu.
We still can handle it, right? Where's the code that handles that read?
Better to keep everything per-cpu. The code is in virt/kvm/ioapic.c
Quoted 8 lines
Quoted 4 lines
Why do we care about
level-triggered interrupts? Everything uses MSI or edge-triggered IOAPIC interrupts these days.Well lots of emulated devices don't yet. They probably should but it's nice to be able to test with e.g. e1000 emulation not just virtio.
e1000 doesn't support msi?
Quoted 4 lines
Besides,
kvm_get_apic_interrupt
simply doesn't know about the triggering mode at the moment.
Signature · 1 line
error compiling committee.c: too many arguments to function
Michael S. Tsirkin
maintainerQuoted 10 lines · Avi Kivity
Quoted 8 lines · Michael S. Tsirkin
Quoted 6 lines
Quoted 3 lines
Yes. But we can and it's easier than figuring out priorities. I am guessing such collisions are rare, right?
It's pretty easy, if there is something in IRR but
kvm_lapic_has_interrupt()returns -1, then we need to disable eoi avoidance.I only see
kvm_apic_has_interrupt- is this what you mean?
Quoted 7 lines
Yes, sorry.
It's not clear whether to do the check in
kvm_apic_has_interrupt()orkvm_apic_get_interrupt()- the latter is called only after interrupts are enabled, so it looks like a better place (EOIs while interrupts are disabled have no effect). But need to make sure those functions are actually called, since they're protected byKVM_REQ_EVENT.
Sorry not sure what you mean by "make sure" - read the code carefully?
Quoted 16 lines
Quoted 16 lines
Quoted 15 lines
Quoted 11 lines
I'll add a trace to make sure.
Quoted 8 lines
arch/x86/kvm/lapic.c int kvm_get_apic_interrupt(struct kvm_vcpu *vcpu) · line 1319
Quoted 5 lines
arch/x86/kvm/lapic.c +5
-0+if (v !=-1 ) +apic_set_vector (v, apic->regs +APIC_ISR ); + }else { +eoi_set_pending_vector (vcpu, vector); + set_isr = false;Weird. Just set it normally. Remember that reading the ISR needs to return the correct value.
Quoted 1 line
Marcelo said linux does not normally read ISR - not true?
It's true and it's irrelevant. We aren't coding a feature to what linux does now, but for what linux or another guest may do in the future.
Quoted 6 lines
Quoted 3 lines
Right. So you think reading ISR has value in combination with PV EOI for future guests? I'm not arguing either way just curious.
I don't. But we need to preserve the same interface the APIC has presented for thousands of years (well, almost).
Talk about overstatements :)
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Note this has no effect if the PV optimization is not enabled.
Quoted 4 lines
We need to process the avoided EOI before any APIC read/writes, to be sure the guest sees the updated values. Same for IOAPIC, EOI affects
remote_irr. That may been we need to sample it after every exit, or perhaps disable the feature for level-triggered interrupts.
Quoted 7 lines
Quoted 5 lines
Quoted 3 lines
Quoted 1 line
Disabling would be very sad. Can we sample on remote irr read?
That can be done from another vcpu.
We still can handle it, right? Where's the code that handles that read?
Better to keep everything per-cpu. The code is in
virt/kvm/ioapic.c
Hmm. Disabling for level handles the ack notifiers issue as well, which I forgot about.
It's a tough call. You think looking at TMR in kvm_get_apic_interrupt is safe?
Quoted 11 lines
Quoted 8 lines
Quoted 4 lines
Why do we care about
level-triggered interrupts? Everything uses MSI or edge-triggered IOAPIC interrupts these days.Well lots of emulated devices don't yet. They probably should but it's nice to be able to test with e.g. e1000 emulation not just virtio.
e1000 doesn't support msi?
qemu emulation doesn't.
Quoted 8 lines
Quoted 4 lines
Besides,
kvm_get_apic_interrupt
simply doesn't know about the triggering mode at the moment.Signature · 1 line
error compiling committee.c: too many arguments to function
Quoted 13 lines · Michael S. Tsirkin
Quoted 12 lines · Avi Kivity
Quoted 8 lines · Michael S. Tsirkin
Quoted 6 lines
Quoted 3 lines
Yes. But we can and it's easier than figuring out priorities. I am guessing such collisions are rare, right?
It's pretty easy, if there is something in IRR but
kvm_lapic_has_interrupt()returns -1, then we need to disable eoi avoidance.I only see
kvm_apic_has_interrupt- is this what you mean?Yes, sorry.
Quoted 7 lines
Quoted 5 lines
It's not clear whether to do the check in
kvm_apic_has_interrupt()orkvm_apic_get_interrupt()- the latter is called only after interrupts are enabled, so it looks like a better place (EOIs while interrupts are disabled have no effect). But need to make sure those functions are actually called, since they're protected byKVM_REQ_EVENT.Sorry not sure what you mean by "make sure" - read the code carefully?
Yes. And I mean, get called at the right time.
Quoted 7 lines
Quoted 2 lines
Better to keep everything per-cpu. The code is in
virt/kvm/ioapic.cHmm. Disabling for level handles the ack notifiers issue as well, which I forgot about.
It's a tough call. You think looking at TMR inkvm_get_apic_interruptis safe?
Yes, it's read only from the guest point of view IIRC.
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
Quoted 4 lines
Why do we care about
level-triggered interrupts? Everything uses MSI or edge-triggered IOAPIC interrupts these days.
Quoted 9 lines
Quoted 6 lines
Quoted 3 lines
Well lots of emulated devices don't yet.
They probably should but it's nice to be able to
test with e.g. e1000 emulation not just virtio.e1000 doesn't support msi?
qemu emulation doesn't.
Can be changed if someone's really interested. But really, avoiding EOIs for e1000 won't help it much.
Signature · 1 line
error compiling committee.c: too many arguments to function
Michael S. Tsirkin
maintainerQuoted 20 lines · Avi Kivity
Quoted 19 lines · Michael S. Tsirkin
Quoted 17 lines · Avi Kivity
Quoted 8 lines · Michael S. Tsirkin
Quoted 6 lines
Quoted 3 lines
Yes. But we can and it's easier than figuring out priorities. I am guessing such collisions are rare, right?
It's pretty easy, if there is something in IRR but
kvm_lapic_has_interrupt()returns -1, then we need to disable eoi avoidance.I only see
kvm_apic_has_interrupt- is this what you mean?Yes, sorry.
It's not clear whether to do the check in
kvm_apic_has_interrupt()orkvm_apic_get_interrupt()- the latter is called only after interrupts are enabled, so it looks like a better place (EOIs while interrupts are disabled have no effect). But need to make sure those functions are actually called, since they're protected byKVM_REQ_EVENT.
Quoted 3 lines
Quoted 1 line
Sorry not sure what you mean by "make sure" - read the code carefully?
Yes. And I mean, get called at the right time.
OK, Review will help here.
Quoted 20 lines
Quoted 7 lines
Quoted 2 lines
Better to keep everything per-cpu. The code is in
virt/kvm/ioapic.cHmm. Disabling for level handles the ack notifiers issue as well, which I forgot about.
It's a tough call. You think looking at TMR inkvm_get_apic_interruptis safe?Yes, it's read only from the guest point of view IIRC.
Quoted 10 lines
Quoted 10 lines
Quoted 8 lines
Quoted 4 lines
Why do we care about
level-triggered interrupts? Everything uses MSI or edge-triggered IOAPIC interrupts these days.Well lots of emulated devices don't yet.
They probably should but it's nice to be able to
test with e.g. e1000 emulation not just virtio.
Quoted 7 lines
Quoted 4 lines
Quoted 1 line
e1000 doesn't support msi?
qemu emulation doesn't.
Can be changed if someone's really interested. But really, avoiding EOIs for e1000 won't help it much.
It will help test EOI avoidance.
Quoted 2 lines
Signature · 1 line
error compiling committee.c: too many arguments to function
Heh, I was working on that too.
Quoted 228 lines · Michael S. Tsirkin
Quoted 62 lines · Avi Kivity
Quoted 35 lines · Michael S. Tsirkin
I took a stub at implementing PV EOI using shared memory. This should reduce the number of exits an interrupt causes as much as by half.
A partially complete draft for both host and guest parts is below.
The idea is simple: there's a bit, per APIC, in guest memory, that tells the guest that it does not need EOI.
We set it before injecting an interrupt and clear before injecting a nested one. Guest tests it using a test and clear operation - this is necessary
so that host can detect interrupt nesting -
and if set, it can skip the EOI MSR.There's a new MSR to set the address of said register in guest memory. Otherwise not much changes:
- Guest EOI is not required
- ISR is automatically cleared before injectionSome things are incomplete: add feature negotiation options, qemu support for said options.
No testing was done beyond compiling the kernel.I would appreciate early feedback.
Signed-off-by Michael S. Tsirkin <[email protected]>--
arch/x86/include/asm/apic.h +0
-0arch/x86/include/asm/apic.h static inline u32 safe_apic_wait_icr_idle(void) { return 0; } · line 466
Quoted 10 lines
arch/x86/include/asm/apic.h +5
-0@@ -457,8 +457,13 @@ static inline u32 safe_apic_wait_icr_idle(void) { return 0; }#endif /* CONFIG_X86_LOCAL_APIC */ +DECLARE_EARLY_PER_CPU(unsigned long, apic_eoi); +static inline void ack_APIC_irq (void ) { +if (__test_and_clear_bit (0 , &__get_cpu_var (apic_eoi))) +return ; +While
__test_and_clear_bit()is implemented in a single instruction, it's not required to be. Better have the instruction there explicitly.Quoted 12 lines
arch/x86/include/asm/apic.h +0
-0/* * ack_APIC_irq() actually gets compiled as a single instruction * ... yummie. arch/x86/include/asm/kvm_host.h +1
-0@@ -481,6 +481,12 @@ struct kvm_vcpu_arch {u64 length;u64 status; } osvw; +Quoted 8 lines
arch/x86/include/asm/kvm_host.h struct kvm_vcpu_arch { · line 490
Quoted 6 lines
arch/x86/include/asm/kvm_host.h +5
-0+struct { +u64 msr_val; +struct gfn_to_hva_cache data; +int vector; + } eoi; };Needs to be cleared on INIT.
You mean
kvm_arch_vcpu_reset?Quoted 11 lines
arch/x86/include/asm/kvm_host.h void __cpuinit kvm_guest_cpu_init(void) · line 313
Quoted 9 lines
arch/x86/include/asm/kvm_host.h +3
-0@@ -307,6 +308,9 @@ void __cpuinit kvm_guest_cpu_init(void) smp_processor_id()); } +wrmsrl (MSR_KVM_EOI_EN ,__pa (this_cpu_ptr (apic_eoi)) | +MSR_KVM_EOI_ENABLED ); +Clear on kexec.
With
register_reboot_notifier?Quoted 77 lines
Quoted 77 lines
arch/x86/include/asm/kvm_host.h +0
-0if (has_steal_clock)kvm_register_steal_time (); }arch/x86/kvm/lapic.c +55
-1@@ -265,7 +265,61 @@ int kvm_apic_set_irq(struct kvm_vcpu *vcpu, struct kvm_lapic_irq *irq) irq->level, irq->trig_mode); } -static inline int apic_find_highest_isr (struct kvm_lapic *apic) +static int eoi_put_user (struct kvm_vcpu *vcpu,u32 val) +{ + +return kvm_write_guest_cached (vcpu->kvm, &vcpu->arch.eoi.data, &val, +sizeof (val)); +} + +static int eoi_get_user (struct kvm_vcpu *vcpu,u32 *val) +{ + +return kvm_read_guest_cached (vcpu->kvm, &vcpu->arch.eoi.data, val, +sizeof (*val)); +} + +static inline bool eoi_enabled (struct kvm_vcpu *vcpu) +{ +return (vcpu->arch.eoi.msr_val &MSR_KVM_EOI_ENABLED ); +} + +static int eoi_get_pending_vector (struct kvm_vcpu *vcpu) +{ +u32 val; +if (eoi_get_user (vcpu, &val) <0 ) +apic_debug ("Can't read EOI MSR value: 0x%llx\n" , + (unsigned long long )vcpi->arch.eoi.msr_val); +if (!(val &0x1 )) + vcpu->arch.eoi.vector =-1 ; +return vcpu->arch.eoi.vector; +} + +static void eoi_set_pending_vector (struct kvm_vcpu *vcpu,int vector) +{ +BUG_ON (vcpu->arch.eoi.vector !=-1 ); +if (eoi_put_user (vcpu,0x1 ) <0 ) { +apic_debug ("Can't set EOI MSR value: 0x%llx\n" , + (unsigned long long )vcpi->arch.eoi.msr_val); +return ; + } + vcpu->arch.eoi.vector = vector; +} + +static int eoi_clr_pending_vector (struct kvm_vcpu *vcpu) +{ +int vector; + vector = vcpu->arch.eoi.vector; +if (vector !=-1 &&eoi_put_user (vcpu,0x0 ) <0 ) { +apic_debug ("Can't clear EOI MSR value: 0x%llx\n" , + (unsigned long long )vcpi->arch.eoi.msr_val); +return -1 ; + } + vcpu->arch.eoi.vector =-1 ; +return vector; +} + +static inline int __apic_find_highest_isr (struct kvm_lapic *apic){
arch/x86/kvm/lapic.c +0
-0int result;@@ -275,6 +329,17 @@ static inline int apic_find_highest_isr(struct kvm_lapic *apic)return result; }Quoted 12 lines
arch/x86/kvm/lapic.c static inline int apic_find_highest_isr(struct kvm_lapic *apic) · line 341
Quoted 10 lines
arch/x86/kvm/lapic.c +10
-0+static inline int apic_find_highest_isr (struct kvm_lapic *apic) +{ +int vector; +if (eoi_enabled (apic->vcpu)) { + vector =eoi_get_pending_vector (apic->vcpu); +if (vector !=-1 ) +return vector; + } +return __apic_find_highest_isr (apic); +}Why aren't you modifying the ISR unconfitionally?
ISR is not set if there won't be an EOI
since it's EOI that normally clears it.Quoted 22 lines
Quoted 22 lines
arch/x86/kvm/lapic.c +4
-0+static void apic_update_ppr (struct kvm_lapic *apic) {u32 tpr, isrv, ppr, old_ppr;@@ -488,6 +553,8 @@ static void apic_set_eoi(struct kvm_lapic *apic)if (vector ==-1 )return ; +if (eoi_enabled (apic->vcpu)) +eoi_clr_pending_vector (apic->vcpu);apic_clear_vector (vector, apic->regs +APIC_ISR );apic_update_ppr (apic);@@ -1236,11 +1303,25 @@ int kvm_get_apic_interrupt(struct kvm_vcpu *vcpu) {int vector =kvm_apic_has_interrupt (vcpu);struct kvm_lapic *apic = vcpu->arch.apic; +bool set_isr = true;if (vector ==-1 )return -1 ;Quoted 8 lines
arch/x86/kvm/lapic.c int kvm_get_apic_interrupt(struct kvm_vcpu *vcpu) · line 1314
Quoted 5 lines
arch/x86/kvm/lapic.c +4
-1-apic_set_vector (vector, apic->regs +APIC_ISR ); +if (eoi_enabled (vcpu)) { +/* Anything pending? If yes disable eoi optimization. */ +if (unlikely (apic_find_highest_isr (apic) >=0 )) { +int v =eoi_clr_pending_vector (vcpu);ISR != pending, that's IRR. If ISR vector has lower priority than the new vector, then we don't need to disable eoi avoidance.
Yes. But we can and it's easier than figuring out priorities. I am guessing such collisions are rare, right?
I'll add a trace to make sure.Quoted 8 lines
arch/x86/kvm/lapic.c int kvm_get_apic_interrupt(struct kvm_vcpu *vcpu) · line 1319
Quoted 5 lines
arch/x86/kvm/lapic.c +5
-0+if (v !=-1 ) +apic_set_vector (v, apic->regs +APIC_ISR ); + }else { +eoi_set_pending_vector (vcpu, vector); + set_isr = false;Weird. Just set it normally. Remember that reading the ISR needs to return the correct value.
Marcelo said linux does not normally read ISR - not true? Note this has no effect if the PV optimization is not enabled.
Quoted 7 lines
Quoted 4 lines
We need to process the avoided EOI before any APIC read/writes, to be sure the guest sees the updated values. Same for IOAPIC, EOI affects
remote_irr. That may been we need to sample it after every exit, or perhaps disable the feature for level-triggered interrupts.Disabling would be very sad. Can we sample on remote irr read?
Nothing sad about it, just correct. MS requires this to be disabled for level triggered interrupts. We have to notify IOAPIC about EOI ASAP. It may hold another interrupt for us that has to be delivered.
I was going to avoid most of the trickery in apic code and just check if avoided EOI should be processed on each exit. This adds one if on exit path instead of couple on interrupt injection path though.
Quoted 13 lines
Quoted 13 lines
Quoted 10 lines
arch/x86/kvm/lapic.c +5
-0+ } + } + +if (set_isr) +apic_set_vector (vector, apic->regs +APIC_ISR );apic_update_ppr (apic);apic_clear_irr (vector, apic);return vector;arch/x86/kvm/x86.c +0
-0Signature · 1 line
error compiling committee.c: too many arguments to function
Quoted 4 lines
--
To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
--
Gleb.
Michael S. Tsirkin
maintainerQuoted 236 lines · Gleb Natapov
Heh, I was working on that too.
Quoted 233 lines · Michael S. Tsirkin
Quoted 62 lines · Avi Kivity
Quoted 35 lines · Michael S. Tsirkin
I took a stub at implementing PV EOI using shared memory. This should reduce the number of exits an interrupt causes as much as by half.
A partially complete draft for both host and guest parts is below.
The idea is simple: there's a bit, per APIC, in guest memory, that tells the guest that it does not need EOI.
We set it before injecting an interrupt and clear before injecting a nested one. Guest tests it using a test and clear operation - this is necessary
so that host can detect interrupt nesting -
and if set, it can skip the EOI MSR.There's a new MSR to set the address of said register in guest memory. Otherwise not much changes:
- Guest EOI is not required
- ISR is automatically cleared before injectionSome things are incomplete: add feature negotiation options, qemu support for said options.
No testing was done beyond compiling the kernel.I would appreciate early feedback.
Signed-off-by Michael S. Tsirkin <[email protected]>--
arch/x86/include/asm/apic.h +0
-0arch/x86/include/asm/apic.h static inline u32 safe_apic_wait_icr_idle(void) { return 0; } · line 466
Quoted 10 lines
arch/x86/include/asm/apic.h +5
-0@@ -457,8 +457,13 @@ static inline u32 safe_apic_wait_icr_idle(void) { return 0; }#endif /* CONFIG_X86_LOCAL_APIC */ +DECLARE_EARLY_PER_CPU(unsigned long, apic_eoi); +static inline void ack_APIC_irq (void ) { +if (__test_and_clear_bit (0 , &__get_cpu_var (apic_eoi))) +return ; +While
__test_and_clear_bit()is implemented in a single instruction, it's not required to be. Better have the instruction there explicitly.Quoted 12 lines
arch/x86/include/asm/apic.h +0
-0/* * ack_APIC_irq() actually gets compiled as a single instruction * ... yummie. arch/x86/include/asm/kvm_host.h +1
-0@@ -481,6 +481,12 @@ struct kvm_vcpu_arch {u64 length;u64 status; } osvw; +Quoted 8 lines
arch/x86/include/asm/kvm_host.h struct kvm_vcpu_arch { · line 490
Quoted 6 lines
arch/x86/include/asm/kvm_host.h +5
-0+struct { +u64 msr_val; +struct gfn_to_hva_cache data; +int vector; + } eoi; };Needs to be cleared on INIT.
You mean
kvm_arch_vcpu_reset?Quoted 11 lines
arch/x86/include/asm/kvm_host.h void __cpuinit kvm_guest_cpu_init(void) · line 313
Quoted 9 lines
arch/x86/include/asm/kvm_host.h +3
-0@@ -307,6 +308,9 @@ void __cpuinit kvm_guest_cpu_init(void) smp_processor_id()); } +wrmsrl (MSR_KVM_EOI_EN ,__pa (this_cpu_ptr (apic_eoi)) | +MSR_KVM_EOI_ENABLED ); +Clear on kexec.
With
register_reboot_notifier?Quoted 77 lines
Quoted 77 lines
arch/x86/include/asm/kvm_host.h +0
-0if (has_steal_clock)kvm_register_steal_time (); }arch/x86/kvm/lapic.c +55
-1@@ -265,7 +265,61 @@ int kvm_apic_set_irq(struct kvm_vcpu *vcpu, struct kvm_lapic_irq *irq) irq->level, irq->trig_mode); } -static inline int apic_find_highest_isr (struct kvm_lapic *apic) +static int eoi_put_user (struct kvm_vcpu *vcpu,u32 val) +{ + +return kvm_write_guest_cached (vcpu->kvm, &vcpu->arch.eoi.data, &val, +sizeof (val)); +} + +static int eoi_get_user (struct kvm_vcpu *vcpu,u32 *val) +{ + +return kvm_read_guest_cached (vcpu->kvm, &vcpu->arch.eoi.data, val, +sizeof (*val)); +} + +static inline bool eoi_enabled (struct kvm_vcpu *vcpu) +{ +return (vcpu->arch.eoi.msr_val &MSR_KVM_EOI_ENABLED ); +} + +static int eoi_get_pending_vector (struct kvm_vcpu *vcpu) +{ +u32 val; +if (eoi_get_user (vcpu, &val) <0 ) +apic_debug ("Can't read EOI MSR value: 0x%llx\n" , + (unsigned long long )vcpi->arch.eoi.msr_val); +if (!(val &0x1 )) + vcpu->arch.eoi.vector =-1 ; +return vcpu->arch.eoi.vector; +} + +static void eoi_set_pending_vector (struct kvm_vcpu *vcpu,int vector) +{ +BUG_ON (vcpu->arch.eoi.vector !=-1 ); +if (eoi_put_user (vcpu,0x1 ) <0 ) { +apic_debug ("Can't set EOI MSR value: 0x%llx\n" , + (unsigned long long )vcpi->arch.eoi.msr_val); +return ; + } + vcpu->arch.eoi.vector = vector; +} + +static int eoi_clr_pending_vector (struct kvm_vcpu *vcpu) +{ +int vector; + vector = vcpu->arch.eoi.vector; +if (vector !=-1 &&eoi_put_user (vcpu,0x0 ) <0 ) { +apic_debug ("Can't clear EOI MSR value: 0x%llx\n" , + (unsigned long long )vcpi->arch.eoi.msr_val); +return -1 ; + } + vcpu->arch.eoi.vector =-1 ; +return vector; +} + +static inline int __apic_find_highest_isr (struct kvm_lapic *apic){
arch/x86/kvm/lapic.c +0
-0int result;@@ -275,6 +329,17 @@ static inline int apic_find_highest_isr(struct kvm_lapic *apic)return result; }Quoted 12 lines
arch/x86/kvm/lapic.c static inline int apic_find_highest_isr(struct kvm_lapic *apic) · line 341
Quoted 10 lines
arch/x86/kvm/lapic.c +10
-0+static inline int apic_find_highest_isr (struct kvm_lapic *apic) +{ +int vector; +if (eoi_enabled (apic->vcpu)) { + vector =eoi_get_pending_vector (apic->vcpu); +if (vector !=-1 ) +return vector; + } +return __apic_find_highest_isr (apic); +}Why aren't you modifying the ISR unconfitionally?
ISR is not set if there won't be an EOI
since it's EOI that normally clears it.Quoted 22 lines
Quoted 22 lines
arch/x86/kvm/lapic.c +4
-0+static void apic_update_ppr (struct kvm_lapic *apic) {u32 tpr, isrv, ppr, old_ppr;@@ -488,6 +553,8 @@ static void apic_set_eoi(struct kvm_lapic *apic)if (vector ==-1 )return ; +if (eoi_enabled (apic->vcpu)) +eoi_clr_pending_vector (apic->vcpu);apic_clear_vector (vector, apic->regs +APIC_ISR );apic_update_ppr (apic);@@ -1236,11 +1303,25 @@ int kvm_get_apic_interrupt(struct kvm_vcpu *vcpu) {int vector =kvm_apic_has_interrupt (vcpu);struct kvm_lapic *apic = vcpu->arch.apic; +bool set_isr = true;if (vector ==-1 )return -1 ;Quoted 8 lines
arch/x86/kvm/lapic.c int kvm_get_apic_interrupt(struct kvm_vcpu *vcpu) · line 1314
Quoted 5 lines
arch/x86/kvm/lapic.c +4
-1-apic_set_vector (vector, apic->regs +APIC_ISR ); +if (eoi_enabled (vcpu)) { +/* Anything pending? If yes disable eoi optimization. */ +if (unlikely (apic_find_highest_isr (apic) >=0 )) { +int v =eoi_clr_pending_vector (vcpu);ISR != pending, that's IRR. If ISR vector has lower priority than the new vector, then we don't need to disable eoi avoidance.
Yes. But we can and it's easier than figuring out priorities. I am guessing such collisions are rare, right?
I'll add a trace to make sure.Quoted 8 lines
arch/x86/kvm/lapic.c int kvm_get_apic_interrupt(struct kvm_vcpu *vcpu) · line 1319
Quoted 5 lines
arch/x86/kvm/lapic.c +5
-0+if (v !=-1 ) +apic_set_vector (v, apic->regs +APIC_ISR ); + }else { +eoi_set_pending_vector (vcpu, vector); + set_isr = false;Weird. Just set it normally. Remember that reading the ISR needs to return the correct value.
Marcelo said linux does not normally read ISR - not true? Note this has no effect if the PV optimization is not enabled.
Quoted 4 lines
We need to process the avoided EOI before any APIC read/writes, to be sure the guest sees the updated values. Same for IOAPIC, EOI affects
remote_irr. That may been we need to sample it after every exit, or perhaps disable the feature for level-triggered interrupts.
Quoted 4 lines
Quoted 2 lines
Disabling would be very sad. Can we sample on remote irr read?
Nothing sad about it, just correct. MS requires this to be disabled for level triggered interrupts.
We don't try to match what HV does 100% anyway.
Quoted 2 lines
We have to notify IOAPIC about EOI ASAP. It
may hold another interrupt for us that has to be delivered.
You mean the ack notifiers? We can skip just for the vectors which have ack notifiers or only if there are no notifiers.
Quoted 24 lines
I was going to avoid most of the trickery in apic code and just check if avoided EOI should be processed on each exit. This adds one if on exit path instead of couple on interrupt injection path though.
Quoted 13 lines
Quoted 13 lines
Quoted 10 lines
arch/x86/kvm/lapic.c +5
-0+ } + } + +if (set_isr) +apic_set_vector (vector, apic->regs +APIC_ISR );apic_update_ppr (apic);apic_clear_irr (vector, apic);return vector;arch/x86/kvm/x86.c +0
-0Signature · 1 line
error compiling committee.c: too many arguments to functionQuoted 4 lines
--
To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html--
Gleb.
Quoted 237 lines · Michael S. Tsirkin
Quoted 236 lines · Gleb Natapov
Heh, I was working on that too.
Quoted 233 lines · Michael S. Tsirkin
Quoted 62 lines · Avi Kivity
Quoted 35 lines · Michael S. Tsirkin
I took a stub at implementing PV EOI using shared memory. This should reduce the number of exits an interrupt causes as much as by half.
A partially complete draft for both host and guest parts is below.
The idea is simple: there's a bit, per APIC, in guest memory, that tells the guest that it does not need EOI.
We set it before injecting an interrupt and clear before injecting a nested one. Guest tests it using a test and clear operation - this is necessary
so that host can detect interrupt nesting -
and if set, it can skip the EOI MSR.There's a new MSR to set the address of said register in guest memory. Otherwise not much changes:
- Guest EOI is not required
- ISR is automatically cleared before injectionSome things are incomplete: add feature negotiation options, qemu support for said options.
No testing was done beyond compiling the kernel.I would appreciate early feedback.
Signed-off-by Michael S. Tsirkin <[email protected]>--
arch/x86/include/asm/apic.h +0
-0arch/x86/include/asm/apic.h static inline u32 safe_apic_wait_icr_idle(void) { return 0; } · line 466
Quoted 10 lines
arch/x86/include/asm/apic.h +5
-0@@ -457,8 +457,13 @@ static inline u32 safe_apic_wait_icr_idle(void) { return 0; }#endif /* CONFIG_X86_LOCAL_APIC */ +DECLARE_EARLY_PER_CPU(unsigned long, apic_eoi); +static inline void ack_APIC_irq (void ) { +if (__test_and_clear_bit (0 , &__get_cpu_var (apic_eoi))) +return ; +While
__test_and_clear_bit()is implemented in a single instruction, it's not required to be. Better have the instruction there explicitly.Quoted 12 lines
arch/x86/include/asm/apic.h +0
-0/* * ack_APIC_irq() actually gets compiled as a single instruction * ... yummie. arch/x86/include/asm/kvm_host.h +1
-0@@ -481,6 +481,12 @@ struct kvm_vcpu_arch {u64 length;u64 status; } osvw; +Quoted 8 lines
arch/x86/include/asm/kvm_host.h struct kvm_vcpu_arch { · line 490
Quoted 6 lines
arch/x86/include/asm/kvm_host.h +5
-0+struct { +u64 msr_val; +struct gfn_to_hva_cache data; +int vector; + } eoi; };Needs to be cleared on INIT.
You mean
kvm_arch_vcpu_reset?Quoted 11 lines
arch/x86/include/asm/kvm_host.h void __cpuinit kvm_guest_cpu_init(void) · line 313
Quoted 9 lines
arch/x86/include/asm/kvm_host.h +3
-0@@ -307,6 +308,9 @@ void __cpuinit kvm_guest_cpu_init(void) smp_processor_id()); } +wrmsrl (MSR_KVM_EOI_EN ,__pa (this_cpu_ptr (apic_eoi)) | +MSR_KVM_EOI_ENABLED ); +Clear on kexec.
With
register_reboot_notifier?Quoted 77 lines
Quoted 77 lines
arch/x86/include/asm/kvm_host.h +0
-0if (has_steal_clock)kvm_register_steal_time (); }arch/x86/kvm/lapic.c +55
-1@@ -265,7 +265,61 @@ int kvm_apic_set_irq(struct kvm_vcpu *vcpu, struct kvm_lapic_irq *irq) irq->level, irq->trig_mode); } -static inline int apic_find_highest_isr (struct kvm_lapic *apic) +static int eoi_put_user (struct kvm_vcpu *vcpu,u32 val) +{ + +return kvm_write_guest_cached (vcpu->kvm, &vcpu->arch.eoi.data, &val, +sizeof (val)); +} + +static int eoi_get_user (struct kvm_vcpu *vcpu,u32 *val) +{ + +return kvm_read_guest_cached (vcpu->kvm, &vcpu->arch.eoi.data, val, +sizeof (*val)); +} + +static inline bool eoi_enabled (struct kvm_vcpu *vcpu) +{ +return (vcpu->arch.eoi.msr_val &MSR_KVM_EOI_ENABLED ); +} + +static int eoi_get_pending_vector (struct kvm_vcpu *vcpu) +{ +u32 val; +if (eoi_get_user (vcpu, &val) <0 ) +apic_debug ("Can't read EOI MSR value: 0x%llx\n" , + (unsigned long long )vcpi->arch.eoi.msr_val); +if (!(val &0x1 )) + vcpu->arch.eoi.vector =-1 ; +return vcpu->arch.eoi.vector; +} + +static void eoi_set_pending_vector (struct kvm_vcpu *vcpu,int vector) +{ +BUG_ON (vcpu->arch.eoi.vector !=-1 ); +if (eoi_put_user (vcpu,0x1 ) <0 ) { +apic_debug ("Can't set EOI MSR value: 0x%llx\n" , + (unsigned long long )vcpi->arch.eoi.msr_val); +return ; + } + vcpu->arch.eoi.vector = vector; +} + +static int eoi_clr_pending_vector (struct kvm_vcpu *vcpu) +{ +int vector; + vector = vcpu->arch.eoi.vector; +if (vector !=-1 &&eoi_put_user (vcpu,0x0 ) <0 ) { +apic_debug ("Can't clear EOI MSR value: 0x%llx\n" , + (unsigned long long )vcpi->arch.eoi.msr_val); +return -1 ; + } + vcpu->arch.eoi.vector =-1 ; +return vector; +} + +static inline int __apic_find_highest_isr (struct kvm_lapic *apic){
arch/x86/kvm/lapic.c +0
-0int result;@@ -275,6 +329,17 @@ static inline int apic_find_highest_isr(struct kvm_lapic *apic)return result; }Quoted 12 lines
arch/x86/kvm/lapic.c static inline int apic_find_highest_isr(struct kvm_lapic *apic) · line 341
Quoted 10 lines
arch/x86/kvm/lapic.c +10
-0+static inline int apic_find_highest_isr (struct kvm_lapic *apic) +{ +int vector; +if (eoi_enabled (apic->vcpu)) { + vector =eoi_get_pending_vector (apic->vcpu); +if (vector !=-1 ) +return vector; + } +return __apic_find_highest_isr (apic); +}Why aren't you modifying the ISR unconfitionally?
ISR is not set if there won't be an EOI
since it's EOI that normally clears it.Quoted 22 lines
Quoted 22 lines
arch/x86/kvm/lapic.c +4
-0+static void apic_update_ppr (struct kvm_lapic *apic) {u32 tpr, isrv, ppr, old_ppr;@@ -488,6 +553,8 @@ static void apic_set_eoi(struct kvm_lapic *apic)if (vector ==-1 )return ; +if (eoi_enabled (apic->vcpu)) +eoi_clr_pending_vector (apic->vcpu);apic_clear_vector (vector, apic->regs +APIC_ISR );apic_update_ppr (apic);@@ -1236,11 +1303,25 @@ int kvm_get_apic_interrupt(struct kvm_vcpu *vcpu) {int vector =kvm_apic_has_interrupt (vcpu);struct kvm_lapic *apic = vcpu->arch.apic; +bool set_isr = true;if (vector ==-1 )return -1 ;Quoted 8 lines
arch/x86/kvm/lapic.c int kvm_get_apic_interrupt(struct kvm_vcpu *vcpu) · line 1314
Quoted 5 lines
arch/x86/kvm/lapic.c +4
-1-apic_set_vector (vector, apic->regs +APIC_ISR ); +if (eoi_enabled (vcpu)) { +/* Anything pending? If yes disable eoi optimization. */ +if (unlikely (apic_find_highest_isr (apic) >=0 )) { +int v =eoi_clr_pending_vector (vcpu);ISR != pending, that's IRR. If ISR vector has lower priority than the new vector, then we don't need to disable eoi avoidance.
Yes. But we can and it's easier than figuring out priorities. I am guessing such collisions are rare, right?
I'll add a trace to make sure.Quoted 8 lines
arch/x86/kvm/lapic.c int kvm_get_apic_interrupt(struct kvm_vcpu *vcpu) · line 1319
Quoted 5 lines
arch/x86/kvm/lapic.c +5
-0+if (v !=-1 ) +apic_set_vector (v, apic->regs +APIC_ISR ); + }else { +eoi_set_pending_vector (vcpu, vector); + set_isr = false;Weird. Just set it normally. Remember that reading the ISR needs to return the correct value.
Marcelo said linux does not normally read ISR - not true? Note this has no effect if the PV optimization is not enabled.
Quoted 4 lines
We need to process the avoided EOI before any APIC read/writes, to be sure the guest sees the updated values. Same for IOAPIC, EOI affects
remote_irr. That may been we need to sample it after every exit, or perhaps disable the feature for level-triggered interrupts.
Quoted 7 lines
Quoted 4 lines
Quoted 2 lines
Disabling would be very sad. Can we sample on remote irr read?
Nothing sad about it, just correct. MS requires this to be disabled for level triggered interrupts.
We don't try to match what HV does 100% anyway.
We should. The same code will be used for HV.
Quoted 6 lines
Quoted 2 lines
We have to notify IOAPIC about EOI ASAP. It
may hold another interrupt for us that has to be delivered.You mean the ack notifiers? We can skip just for the vectors which have ack notifiers or only if there are no notifiers.
No. I mean:
if (!ent->fields.mask && (ioapic->irr & (1 << i)))
ioapic_service(ioapic, i);Quoted 17 lines
Quoted 17 lines
I was going to avoid most of the trickery in apic code and just check if avoided EOI should be processed on each exit. This adds one if on exit path instead of couple on interrupt injection path though.
Quoted 13 lines
Quoted 13 lines
Quoted 10 lines
arch/x86/kvm/lapic.c +5
-0+ } + } + +if (set_isr) +apic_set_vector (vector, apic->regs +APIC_ISR );apic_update_ppr (apic);apic_clear_irr (vector, apic);return vector;arch/x86/kvm/x86.c +0
-0Signature · 1 line
error compiling committee.c: too many arguments to function
Quoted 7 lines
Quoted 7 lines
Quoted 4 lines
--
To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html--
Gleb.
--
Gleb.
Michael S. Tsirkin
maintainerQuoted 3 lines · Gleb Natapov
Quoted 2 lines
We don't try to match what HV does 100% anyway.
We should. The same code will be used for HV.
Only where it makes sense, that is where the functionality is sufficiently similar.
Quoted 10 lines
Quoted 6 lines
Quoted 2 lines
We have to notify IOAPIC about EOI ASAP. It
may hold another interrupt for us that has to be delivered.You mean the ack notifiers? We can skip just for the vectors which have ack notifiers or only if there are no notifiers.
No. I mean:
if (!ent->fields.mask && (ioapic->irr & (1 << i))) ioapic_service(ioapic, i);
Hmm.
Quoted 8 lines · Michael S. Tsirkin
Quoted 3 lines · Gleb Natapov
Quoted 2 lines
We don't try to match what HV does 100% anyway.
We should. The same code will be used for HV.
Only where it makes sense, that is where the functionality is sufficiently similar.
You can sprinkle additional ifs in the code, but I do not see the point.
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 2 lines
We have to notify IOAPIC about EOI ASAP. It
may hold another interrupt for us that has to be delivered.You mean the ack notifiers? We can skip just for the vectors which have ack notifiers or only if there are no notifiers.
Quoted 6 lines
Quoted 4 lines
No. I mean:
if (!ent->fields.mask && (ioapic->irr & (1 << i))) ioapic_service(ioapic, i);Hmm.
--
Gleb.
Michael S. Tsirkin
maintainerPATCH 1/1 RFC: kvm eoi PV using shared memory
In reply to Michael S. TsirkinI got lots of useful feedback from v0 so I thought sending out a brain dump again would be a good idea. This is mainly to show how I'm trying to address the comments I got from the previous round. Flames/feedback are wellcome!
Changes from v0:
- Tweaked setup MSRs a bit
- Keep ISR bit set. Before reading ISR, test EOI in guest memory and clear
- Check priority for nested interrupts, we can
enable optimization if new one is high priority
- Disable optimization for any interrupt handled by ioapic
(This is because ioapic handles notifiers and pic and it generally gets messy.
It's possible that we can optimize some ioapic-handled
edge interrupts - but is it worth it?)
- A less intrusive change for guest apic (0 overhead without kvm)
---
I took a stub at implementing PV EOI using shared memory. This should reduce the number of exits an interrupt causes as much as by half.
A partially complete draft for both host and guest parts is below.
The idea is simple: there's a bit, per APIC, in guest memory, that tells the guest that it does not need EOI.
We set it before injecting an interrupt and clear before injecting a nested one. Guest tests it using a test and clear operation - this is necessary
so that host can detect interrupt nesting -
and if set, it can skip the EOI MSR.
There's a new MSR to set the address of said register in guest memory. Otherwise not much changed:
- Guest EOI is not required
- Register is tested ISR is automatically cleared before injection
qemu support is incomplete - mostly for feature negotiation. need to add some trace points to enable profiling. No testing was done beyond compiling the kernel.
arch/x86/include/asm/bitops.h +4 -2
@@ -26,11 +26,13 @@#if __GNUC__ <4 || (__GNUC__ ==4 && __GNUC_MINOR__ <1 )/* Technically wrong, but this avoids compilation errors on some gcc versions. */ -#define BITOP_ADDR (x) "=m" (*(volatile long *) (x)) +#define BITOP_ADDR_CONSTRAINT "=m"#else -#define BITOP_ADDR (x) "+m" (*(volatile long *) (x)) +#define BITOP_ADDR_CONSTRAINT "+m"#endif +#define BITOP_ADDR (x) BITOP_ADDR_CONSTRAINT (*(volatile long *) (x)) +#define ADDR BITOP_ADDR(addr)/*
arch/x86/include/asm/kvm_host.h +6 -0
@@ -481,6 +481,12 @@ struct kvm_vcpu_arch {u64 length;u64 status; } osvw; + +struct { +u64 msr_val; +struct gfn_to_hva_cache data; +bool pending; + } eoi; };struct kvm_lpage_info {
arch/x86/include/asm/kvm_para.h +3 -0
@@ -22,6 +22,7 @@#define KVM_FEATURE_CLOCKSOURCE2 3#define KVM_FEATURE_ASYNC_PF 4#define KVM_FEATURE_STEAL_TIME 5 +#define KVM_FEATURE_EOI 6/* The last 8 bits are used to indicate how to interpret the flags field * in pvclock structure. If no bits are set, all flags are ignored.
@@ -37,6 +38,8 @@#define MSR_KVM_SYSTEM_TIME_NEW 0x4b564d01#define MSR_KVM_ASYNC_PF_EN 0x4b564d02#define MSR_KVM_STEAL_TIME 0x4b564d03 +#define MSR_KVM_EOI_EN 0x4b564d04 +#define MSR_KVM_EOI_DISABLED 0x0Lstruct kvm_steal_time {__u64 steal;
arch/x86/kernel/kvm.c +47 -3
88 lines not on this page · raw message
arch/x86/kvm/cpuid.c +1 -0
@@ -408,6 +408,7 @@ static int do_cpuid_ent(struct kvm_cpuid_entry2 *entry, u32 function, (1 <<KVM_FEATURE_NOP_IO_DELAY ) | (1 <<KVM_FEATURE_CLOCKSOURCE2 ) | (1 <<KVM_FEATURE_ASYNC_PF ) | + (1 <<KVM_FEATURE_EOI ) | (1 <<KVM_FEATURE_CLOCKSOURCE_STABLE_BIT );if (sched_info_on ())
arch/x86/kvm/irq.c +1 -1
@@ -48,7 +48,7 @@ int kvm_cpu_has_interrupt(struct kvm_vcpu *v)if (!irqchip_in_kernel (v->kvm))return v->arch.interrupt.pending; -if (kvm_apic_has_interrupt (v) ==-1 ) {/* LAPIC */ +if (kvm_apic_has_interrupt (v) <0 ) {/* LAPIC */ if (kvm_apic_accept_pic_intr (v)) { s =pic_irqchip (v->kvm);/* PIC */ return s->output;
arch/x86/kvm/lapic.c +99 -3
156 lines not on this page · raw message
arch/x86/kvm/lapic.h +2 -0
@@ -60,4 +60,6 @@ static inline bool kvm_hv_vapic_assist_page_enabled(struct kvm_vcpu *vcpu) {return vcpu->arch.hv_vapic &HV_X64_MSR_APIC_ASSIST_PAGE_ENABLE ; } + +int kvm_pv_enable_apic_eoi (struct kvm_vcpu *vcpu,u64 data);#endif
arch/x86/kvm/x86.c +4 -0
@@ -1627,6 +1627,10 @@ int kvm_set_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 data)if (kvm_pv_enable_async_pf (vcpu, data))return 1 ;break ; +case MSR_KVM_EOI_EN : +if (kvm_pv_enable_apic_eoi (vcpu, data)) +return 1 ; +break ;case MSR_KVM_STEAL_TIME :if (unlikely (!sched_info_on ()))
Quoted 95 lines · Michael S. Tsirkin
I got lots of useful feedback from v0 so I thought sending out a brain dump again would be a good idea. This is mainly to show how I'm trying to address the comments I got from the previous round. Flames/feedback are wellcome!
Changes from v0:
- Tweaked setup MSRs a bit
- Keep ISR bit set. Before reading ISR, test EOI in guest memory and clear
- Check priority for nested interrupts, we can
enable optimization if new one is high priority
- Disable optimization for any interrupt handled by ioapic
(This is because ioapic handles notifiers and pic and it generally gets messy.
It's possible that we can optimize some ioapic-handled
edge interrupts - but is it worth it?)
- A less intrusive change for guest apic (0 overhead without kvm)---
I took a stub at implementing PV EOI using shared memory. This should reduce the number of exits an interrupt causes as much as by half.
A partially complete draft for both host and guest parts is below.
The idea is simple: there's a bit, per APIC, in guest memory, that tells the guest that it does not need EOI.
We set it before injecting an interrupt and clear before injecting a nested one. Guest tests it using a test and clear operation - this is necessary
so that host can detect interrupt nesting -
and if set, it can skip the EOI MSR.There's a new MSR to set the address of said register in guest memory. Otherwise not much changed:
- Guest EOI is not required
- Register is tested ISR is automatically cleared before injectionqemu support is incomplete - mostly for feature negotiation. need to add some trace points to enable profiling. No testing was done beyond compiling the kernel.
Signed-off-by Michael S. Tsirkin <[email protected]>arch/x86/include/asm/bitops.h +4
-2@@ -26,11 +26,13 @@#if __GNUC__ <4 || (__GNUC__ ==4 && __GNUC_MINOR__ <1 )/* Technically wrong, but this avoids compilation errors on some gcc versions. */ -#define BITOP_ADDR (x) "=m" (*(volatile long *) (x)) +#define BITOP_ADDR_CONSTRAINT "=m"#else -#define BITOP_ADDR (x) "+m" (*(volatile long *) (x)) +#define BITOP_ADDR_CONSTRAINT "+m"#endif +#define BITOP_ADDR (x) BITOP_ADDR_CONSTRAINT (*(volatile long *) (x)) +#define ADDR BITOP_ADDR(addr)/* arch/x86/include/asm/kvm_host.h +6
-0@@ -481,6 +481,12 @@ struct kvm_vcpu_arch {u64 length;u64 status; } osvw; + +struct { +u64 msr_val; +struct gfn_to_hva_cache data; +bool pending; + } eoi; };struct kvm_lpage_info {arch/x86/include/asm/kvm_para.h +1
-0@@ -22,6 +22,7 @@#define KVM_FEATURE_CLOCKSOURCE2 3#define KVM_FEATURE_ASYNC_PF 4#define KVM_FEATURE_STEAL_TIME 5 +#define KVM_FEATURE_EOI 6/* The last 8 bits are used to indicate how to interpret the flags field * in pvclock structure. If no bits are set, all flags are ignored.
arch/x86/include/asm/kvm_para.h line 42
Quoted 5 lines
arch/x86/include/asm/kvm_para.h +2
-0@@ -37,6 +38,8 @@#define MSR_KVM_SYSTEM_TIME_NEW 0x4b564d01#define MSR_KVM_ASYNC_PF_EN 0x4b564d02#define MSR_KVM_STEAL_TIME 0x4b564d03 +#define MSR_KVM_EOI_EN 0x4b564d04 +#define MSR_KVM_EOI_DISABLED 0x0L
This is valid gpa. Follow others MSR example i.e align the address to, lets say dword, and use lsb as enable bit. Other low bits are reserved (#GP if set). And please move defines that not define new MSRs to some other place.
Quoted 50 lines
arch/x86/include/asm/kvm_para.h +0
-0struct kvm_steal_time {__u64 steal;arch/x86/kernel/kvm.c +28
-0@@ -39,6 +39,7 @@#include <asm/desc.h> #include <asm/tlbflush.h> #include <asm/idle.h> +#include <asm/apic.h> static int kvmapf =1 ;@@ -290,6 +291,33 @@ static void kvm_register_steal_time(void) cpu, __pa(st)); } +/* TODO: needs to be early? aligned? */ +static DEFINE_EARLY_PER_CPU(u8, apic_eoi, 0); + +/* Our own copy of __test_and_clear_bit to make sure +* it is done with a single instruction */ +static inline int kvm_test_and_clear_bit (int nr,volatile u8 * addr) +{ +int oldbit; + + asmvolatile ("btr %2,%1\n\t" +"sbb %0,%0" + : "=r" (oldbit), + BITOP_ADDR_CONSTRAINT (*(volatile u8 *) (addr)) + : "Ir" (nr)); +return oldbit; +} + +static void (*kvm_guest_native_apic_write)(u32 reg,u32 val); +static void kvm_guest_apic_write (u32 reg,u32 val) +{ +if (reg ==APIC_EOI && +kvm_test_and_clear_bit (0 , &__get_cpu_var (apic_eoi))) +return ; + +kvm_guest_native_apic_write (reg, val); +} +void __cpuinit kvm_guest_cpu_init (void ) {if (!kvm_para_available ())
arch/x86/kernel/kvm.c void __cpuinit kvm_guest_cpu_init(void) · line 343
Quoted 9 lines
arch/x86/kernel/kvm.c +6
-0@@ -307,11 +335,18 @@ void __cpuinit kvm_guest_cpu_init(void) smp_processor_id()); } + +if (kvm_para_has_feature (KVM_FEATURE_EOI )) { + kvm_guest_native_apic_write = apic->write; + apic->write = kvm_guest_apic_write; +wrmsrl (MSR_KVM_EOI_EN ,__pa (&__get_cpu_var (apic_eoi))); + }
Can happen on more then one cpu. After it happens on two kvm_guest_apic_write() will call to itself.
Quoted 11 lines
arch/x86/kernel/kvm.c +2
-1+if (has_steal_clock)kvm_register_steal_time (); } -static void kvm_pv_disable_apf (void *unused) +static void kvm_pv_disable_apf (void ) {if (!__get_cpu_var (apf_reason).enabled)return ;
arch/x86/kernel/kvm.c static void kvm_pv_disable_apf(void *unused) · line 364
Quoted 7 lines
arch/x86/kernel/kvm.c +4
-0@@ -323,11 +358,18 @@ static void kvm_pv_disable_apf(void *unused) smp_processor_id()); } +static void kvm_pv_guest_cpu_reboot (void *unused) +{ +if (kvm_para_has_feature (KVM_FEATURE_EOI )) +wrmsrl (MSR_KVM_EOI_EN ,MSR_KVM_EOI_DISABLED );
Shouldn't it zero apic_eoi?
Quoted 13 lines
arch/x86/kernel/kvm.c +4
-1+kvm_pv_disable_apf (); +} +static int kvm_pv_reboot_notify (struct notifier_block *nb,unsigned long code,void *unused) {if (code ==SYS_RESTART ) -on_each_cpu (kvm_pv_disable_apf,NULL ,1 ); +on_each_cpu (kvm_pv_guest_cpu_reboot,NULL ,1 );return NOTIFY_DONE ; }
arch/x86/kernel/kvm.c static void __cpuinit kvm_guest_cpu_online(void *dummy) · line 424
Quoted 6 lines
arch/x86/kernel/kvm.c +2
-1@@ -378,7 +420,9 @@ static void __cpuinit kvm_guest_cpu_online(void *dummy)static void kvm_guest_cpu_offline (void *dummy) {kvm_disable_steal_time (); -kvm_pv_disable_apf (NULL ); +if (kvm_para_has_feature (KVM_FEATURE_EOI )) +wrmsrl (MSR_KVM_EOI_EN ,MSR_KVM_EOI_DISABLED );
Same.
Quoted 96 lines
arch/x86/kernel/kvm.c +1
-0+kvm_pv_disable_apf ();apf_task_wake_all (); }arch/x86/kvm/cpuid.c +1
-0@@ -408,6 +408,7 @@ static int do_cpuid_ent(struct kvm_cpuid_entry2 *entry, u32 function, (1 <<KVM_FEATURE_NOP_IO_DELAY ) | (1 <<KVM_FEATURE_CLOCKSOURCE2 ) | (1 <<KVM_FEATURE_ASYNC_PF ) | + (1 <<KVM_FEATURE_EOI ) | (1 <<KVM_FEATURE_CLOCKSOURCE_STABLE_BIT );if (sched_info_on ())arch/x86/kvm/irq.c +1
-1@@ -48,7 +48,7 @@ int kvm_cpu_has_interrupt(struct kvm_vcpu *v)if (!irqchip_in_kernel (v->kvm))return v->arch.interrupt.pending; -if (kvm_apic_has_interrupt (v) ==-1 ) {/* LAPIC */ +if (kvm_apic_has_interrupt (v) <0 ) {/* LAPIC */ if (kvm_apic_accept_pic_intr (v)) { s =pic_irqchip (v->kvm);/* PIC */ return s->output;arch/x86/kvm/lapic.c +52
-0@@ -270,6 +270,54 @@ int kvm_apic_set_irq(struct kvm_vcpu *vcpu, struct kvm_lapic_irq *irq) irq->level, irq->trig_mode); } +static int eoi_put_user (struct kvm_vcpu *vcpu,u8 val) +{ + +return kvm_write_guest_cached (vcpu->kvm, &vcpu->arch.eoi.data, &val, +sizeof (val)); +} + +static int eoi_get_user (struct kvm_vcpu *vcpu,u8 *val) +{ + +return kvm_read_guest_cached (vcpu->kvm, &vcpu->arch.eoi.data, val, +sizeof (*val)); +} + +static inline bool eoi_enabled (struct kvm_vcpu *vcpu) +{ +return vcpu->arch.eoi.msr_val !=MSR_KVM_EOI_DISABLED ; +} + +static bool eoi_get_pending (struct kvm_vcpu *vcpu) +{ +u8 val; +if (eoi_get_user (vcpu, &val) <0 ) +apic_debug ("Can't read EOI MSR value: 0x%llx\n" , + (unsigned long long )vcpi->arch.eoi.msr_val); +return val &0x1 ; +} + +static void eoi_set_pending (struct kvm_vcpu *vcpu) +{ +if (eoi_put_user (vcpu,0x1 ) <0 ) { +apic_debug ("Can't set EOI MSR value: 0x%llx\n" , + (unsigned long long )vcpi->arch.eoi.msr_val); +return ; + } + vcpu->arch.eoi.pending = true; +} + +static void eoi_clr_pending (struct kvm_vcpu *vcpu) +{ +if (eoi_put_user (vcpu,0x0 ) <0 ) { +apic_debug ("Can't clear EOI MSR value: 0x%llx\n" , + (unsigned long long )vcpi->arch.eoi.msr_val); +return ; + } + vcpu->arch.eoi.pending = false; +} +static inline int apic_find_highest_isr (struct kvm_lapic *apic) {int result;@@ -280,6 +328,20 @@ static inline int apic_find_highest_isr(struct kvm_lapic *apic)return result; } +static void apic_update_isr (struct kvm_lapic *apic) +{ +int vector; + if (!eoi_enabled(apic->vcpu) ||
arch/x86/kvm/lapic.c static inline int apic_find_highest_isr(struct kvm_lapic *apic) · line 344
Quoted 10 lines
arch/x86/kvm/lapic.c +10
-0+ !apic->vcpu->arch.eoi.pending || + eoi_get_pending(apic->vcpu)) + return; + apic->vcpu->arch.eoi.pending = false; + vector =apic_find_highest_isr (apic); +if (vector ==-1 ) +return ; +apic_clear_vector (vector, apic->regs +APIC_ISR ); +} +
We should just call apic_set_eoi() on exit if eoi.pending && !eoi_get_pending(). This removes the need for the function and its calls. We already have call to kvm_lapic_sync_from_vapic() on exit path which should be extended to do the above.
Quoted 12 lines
arch/x86/kvm/lapic.c +1
-0static void apic_update_ppr (struct kvm_lapic *apic) {u32 tpr, isrv, ppr, old_ppr;@@ -287,6 +349,7 @@ static void apic_update_ppr(struct kvm_lapic *apic) old_ppr =apic_get_reg (apic,APIC_PROCPRI ); tpr =apic_get_reg (apic,APIC_TASKPRI ); +apic_update_isr (apic); isr =apic_find_highest_isr (apic); isrv = (isr !=-1 ) ? isr :0 ;
arch/x86/kvm/lapic.c static void apic_set_eoi(struct kvm_lapic *apic) · line 559
Quoted 5 lines
arch/x86/kvm/lapic.c +2
-0@@ -492,6 +555,8 @@ static void apic_set_eoi(struct kvm_lapic *apic)if (vector ==-1 )return ; +if (eoi_enabled (apic->vcpu)) +eoi_clr_pending (apic->vcpu);
You have many of those. Just fold eoi_enabled() check into eoi_clr_pending().
Quoted 102 lines
arch/x86/kvm/lapic.c +34
-3apic_clear_vector (vector, apic->regs +APIC_ISR );apic_update_ppr (apic);@@ -1085,6 +1150,8 @@ void kvm_lapic_reset(struct kvm_vcpu *vcpu)atomic_set (&apic->lapic_timer.pending,0 );if (kvm_vcpu_is_bsp (vcpu)) vcpu->arch.apic_base |=MSR_IA32_APICBASE_BSP ; + vcpu->arch.eoi.msr_val =MSR_KVM_EOI_DISABLED ; + vcpu->arch.eoi.pending = false;apic_update_ppr (apic); vcpu->arch.apic_arb_prio =0 ;@@ -1210,9 +1277,10 @@ int kvm_apic_has_interrupt(struct kvm_vcpu *vcpu)apic_update_ppr (apic); highest_irr =apic_find_highest_irr (apic); -if ((highest_irr ==-1 ) || - ((highest_irr &0xF0 ) <=apic_get_reg (apic,APIC_PROCPRI ))) +if (highest_irr ==-1 )return -1 ; +if (((highest_irr &0xF0 ) <=apic_get_reg (apic,APIC_PROCPRI ))) +return -2 ;return highest_irr; }@@ -1244,9 +1312,20 @@ int kvm_get_apic_interrupt(struct kvm_vcpu *vcpu)int vector =kvm_apic_has_interrupt (vcpu);struct kvm_lapic *apic = vcpu->arch.apic; -if (vector ==-1 ) +/* Detect interrupt nesting and disable EOI optimization */ +if (eoi_enabled (vcpu) && vector ==-2 ) +eoi_clr_pending (vcpu); + +if (vector <0 )return -1 ; +if (eoi_enabled (vcpu)) { +if (kvm_ioapic_handles_vector (vcpu->kvm, vector)) +eoi_clr_pending (vcpu); +else +eoi_set_pending (vcpu); + } +apic_set_vector (vector, apic->regs +APIC_ISR );apic_update_ppr (apic);apic_clear_irr (vector, apic);@@ -1261,6 +1340,8 @@ void kvm_apic_post_state_restore(struct kvm_vcpu *vcpu)MSR_IA32_APICBASE_BASE ;kvm_apic_set_version (vcpu); +if (eoi_enabled (apic->vcpu)) + apic->vcpu->arch.eoi.pending =eoi_get_pending (apic->vcpu);apic_update_ppr (apic);hrtimer_cancel (&apic->lapic_timer.timer);update_divide_count (apic);@@ -1392,3 +1473,18 @@ int kvm_hv_vapic_msr_read(struct kvm_vcpu *vcpu, u32 reg, u64 *data)return 0 ; } + +int kvm_pv_enable_apic_eoi (struct kvm_vcpu *vcpu,u64 data) +{ +if (data ==MSR_KVM_EOI_DISABLED ) { +struct kvm_lapic *apic = vcpu->arch.apic; +if (apic &&apic_enabled (apic)) +apic_update_isr (apic); + }else if (kvm_gfn_to_hva_cache_init (vcpu->kvm, &vcpu->arch.eoi.data, + data)) +return 1 ; + + vcpu->arch.eoi.msr_val = data; + vcpu->arch.eoi.pending = false; +return 0 ; +}arch/x86/kvm/lapic.h +2
-0@@ -60,4 +60,6 @@ static inline bool kvm_hv_vapic_assist_page_enabled(struct kvm_vcpu *vcpu) {return vcpu->arch.hv_vapic &HV_X64_MSR_APIC_ASSIST_PAGE_ENABLE ; } + +int kvm_pv_enable_apic_eoi (struct kvm_vcpu *vcpu,u64 data);#endif arch/x86/kvm/x86.c +4
-1@@ -1627,6 +1627,10 @@ int kvm_set_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 data)if (kvm_pv_enable_async_pf (vcpu, data))return 1 ;break ; +case MSR_KVM_EOI_EN : +if (kvm_pv_enable_apic_eoi (vcpu, data)) +return 1 ; +break ;case MSR_KVM_STEAL_TIME :if (unlikely (!sched_info_on ())) --
Quoted 3 lines
To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
--
Gleb.
Michael S. Tsirkin
maintainerThanks very much for the review. I'll address the comments. Some questions on your comments below.
Quoted 8 lines · Gleb Natapov
arch/x86/include/asm/kvm_para.h line 42
Quoted 6 lines
arch/x86/include/asm/kvm_para.h +2
-0@@ -37,6 +38,8 @@#define MSR_KVM_SYSTEM_TIME_NEW 0x4b564d01#define MSR_KVM_ASYNC_PF_EN 0x4b564d02#define MSR_KVM_STEAL_TIME 0x4b564d03 +#define MSR_KVM_EOI_EN 0x4b564d04 +#define MSR_KVM_EOI_DISABLED 0x0LThis is valid gpa. Follow others MSR example i.e align the address to, lets say dword, and use lsb as enable bit.
We only need a single byte, since this is per-CPU - it's better to save the memory, so no alignment is required. An explicit disable msr would also address this, right?
Quoted 8 lines
Quoted 8 lines
arch/x86/kvm/lapic.c +8
-0+static void apic_update_isr (struct kvm_lapic *apic) +{ +int vector; +if (!eoi_enabled (apic->vcpu) || + !apic->vcpu->arch.eoi.pending || +eoi_get_pending (apic->vcpu)) +return ; + apic->vcpu->arch.eoi.pending = false;
Quoted 8 lines
arch/x86/kvm/lapic.c static inline int apic_find_highest_isr(struct kvm_lapic *apic) · line 344
Quoted 6 lines
arch/x86/kvm/lapic.c +6
-0+ vector =apic_find_highest_isr (apic); +if (vector ==-1 ) +return ; +apic_clear_vector (vector, apic->regs +APIC_ISR ); +} +We should just call
apic_set_eoi()on exit if eoi.pending && !eoi_get_pending(). This removes the need for the function and its calls.
It's a bit of a waste: that one does all kind extra things which we know we don't need, some of the atomics. And it's datapath so extra stuff is not free.
Probably a good idea to replace the call on MSR disable - I think apic_update_ppr is a better thing to call there.
Is there anything else that I missed?
Quoted 3 lines
We already have
call tokvm_lapic_sync_from_vapic()on exit path which should be extended to do the above.
It already does this. It calls apic_set_tpr
which calls apic_update_ppr which callsapic_update_isr.
Signature · 1 line
MST
Quoted 13 lines · Michael S. Tsirkin
Thanks very much for the review. I'll address the comments. Some questions on your comments below.
Quoted 8 lines · Gleb Natapov
arch/x86/include/asm/kvm_para.h line 42
Quoted 6 lines
arch/x86/include/asm/kvm_para.h +2
-0@@ -37,6 +38,8 @@#define MSR_KVM_SYSTEM_TIME_NEW 0x4b564d01#define MSR_KVM_ASYNC_PF_EN 0x4b564d02#define MSR_KVM_STEAL_TIME 0x4b564d03 +#define MSR_KVM_EOI_EN 0x4b564d04 +#define MSR_KVM_EOI_DISABLED 0x0LThis is valid gpa. Follow others MSR example i.e align the address to, lets say dword, and use lsb as enable bit.
Quoted 4 lines
We only need a single byte, since this is per-CPU - it's better to save the memory, so no alignment is required. An explicit disable msr would also address this, right?
We do not have shortage of memory. Better make all MSRs works the same way. BTW have you added new MSR to msrs_to_save array? I forgot to checked.
Quoted 17 lines
Quoted 8 lines
Quoted 8 lines
arch/x86/kvm/lapic.c +8
-0+static void apic_update_isr (struct kvm_lapic *apic) +{ +int vector; +if (!eoi_enabled (apic->vcpu) || + !apic->vcpu->arch.eoi.pending || +eoi_get_pending (apic->vcpu)) +return ; + apic->vcpu->arch.eoi.pending = false;Quoted 8 lines
arch/x86/kvm/lapic.c static inline int apic_find_highest_isr(struct kvm_lapic *apic) · line 344
Quoted 6 lines
arch/x86/kvm/lapic.c +6
-0+ vector =apic_find_highest_isr (apic); +if (vector ==-1 ) +return ; +apic_clear_vector (vector, apic->regs +APIC_ISR ); +} +We should just call
apic_set_eoi()on exit if eoi.pending && !eoi_get_pending(). This removes the need for the function and its calls.
Quoted 4 lines
It's a bit of a waste: that one does all kind extra things which we know we don't need, some of the atomics. And it's datapath so extra stuff is not free.
How much time those extra things are taking compared to vmexit you already serving? And there is a good chance you will do them during vmentry anyway while trying to inject (or just check for) new interrupt.
Quoted 4 lines
Probably a good idea to replace the call on MSR disable - I think
apic_update_ppris a better thing to call there.Is there anything else that I missed?
I think that simple things are better then complex things if the end result is the same :) Try it and see how much simpler it is. Have you measured that what you are trying to optimize actually worth optimizing? That you can measure the optimization at all?
Quoted 9 lines
Quoted 4 lines
We already have
call tokvm_lapic_sync_from_vapic()on exit path which should be extended to do the above.It already does this. It calls
apic_set_tpr
which callsapic_update_pprwhich callsapic_update_isr.
It does it only if vapic is in use (and it is usually not). But the if() is already there so we do not need to worry that one additional if() on the exit path will slow KVM to the crawl.
--
Gleb.
Michael S. Tsirkin
maintainerQuoted 14 lines · Gleb Natapov
Quoted 13 lines · Michael S. Tsirkin
Thanks very much for the review. I'll address the comments. Some questions on your comments below.
Quoted 8 lines · Gleb Natapov
arch/x86/include/asm/kvm_para.h line 42
Quoted 6 lines
arch/x86/include/asm/kvm_para.h +2
-0@@ -37,6 +38,8 @@#define MSR_KVM_SYSTEM_TIME_NEW 0x4b564d01#define MSR_KVM_ASYNC_PF_EN 0x4b564d02#define MSR_KVM_STEAL_TIME 0x4b564d03 +#define MSR_KVM_EOI_EN 0x4b564d04 +#define MSR_KVM_EOI_DISABLED 0x0LThis is valid gpa. Follow others MSR example i.e align the address to, lets say dword, and use lsb as enable bit.
Quoted 7 lines
Quoted 4 lines
We only need a single byte, since this is per-CPU - it's better to save the memory, so no alignment is required. An explicit disable msr would also address this, right?
We do not have shortage of memory.
Better make all MSRs works the same
way.
I agree it's nice to have EOI and ASYNC_PF look similar but wasting memory is also bad. I'll ponder this some more.
Quoted 2 lines
BTW have you added new MSR to
msrs_to_savearray? I forgot to checked.
I didn't yet. Trying to understand how will that affect cross-version migration - any input?
Quoted 17 lines
Quoted 17 lines
Quoted 16 lines
Quoted 4 lines
arch/x86/kvm/lapic.c +4
-0+static void apic_update_isr (struct kvm_lapic *apic) +{ +int vector; + if (!eoi_enabled(apic->vcpu) ||arch/x86/kvm/lapic.c static inline int apic_find_highest_isr(struct kvm_lapic *apic) · line 344
Quoted 10 lines
arch/x86/kvm/lapic.c +10
-0+ !apic->vcpu->arch.eoi.pending || + eoi_get_pending(apic->vcpu)) + return; + apic->vcpu->arch.eoi.pending = false; + vector =apic_find_highest_isr (apic); +if (vector ==-1 ) +return ; +apic_clear_vector (vector, apic->regs +APIC_ISR ); +} +We should just call
apic_set_eoi()on exit if eoi.pending && !eoi_get_pending(). This removes the need for the function and its calls.
Quoted 7 lines
Quoted 4 lines
It's a bit of a waste: that one does all kind extra things which we know we don't need, some of the atomics. And it's datapath so extra stuff is not free.
How much time those extra things are taking compared to vmexit you already serving? And there is a good chance you will do them during vmentry anyway while trying to inject (or just check for) new interrupt.
No need to do them twice :)
Quoted 6 lines
Quoted 4 lines
Probably a good idea to replace the call on MSR disable - I think
apic_update_ppris a better thing to call there.Is there anything else that I missed?
I think that simple things are better then complex things if the end result is the same :) Try it and see how much simpler it is.
It doesn't seem to be simpler at all. The common functionality is about 4 lines.
Quoted 3 lines
Have you measured
that what you are trying to optimize actually worth optimizing? That you can measure the optimization at all?
The claim is not that it's measureable. The claim is that it does not scale to keep adding things to do on each entry.
Quoted 10 lines
Quoted 9 lines
Quoted 4 lines
We already have
call tokvm_lapic_sync_from_vapic()on exit path which should be extended to do the above.It already does this. It calls
apic_set_tpr
which callsapic_update_pprwhich callsapic_update_isr.It does it only if vapic is in use (and it is usually not).
When it's not we don't need to update ppr and so
no need to update isr on this exit.
Quoted 3 lines
But the
if()
is already there so we do not need to worry that one additionalif()on the exit path will slow KVM to the crawl.
The number of things we need to do on each entry keeps going up, if we just keep adding stuff it won't end well.
Quoted 2 lines
--
Gleb.
Quoted 19 lines · Michael S. Tsirkin
Quoted 18 lines · Gleb Natapov
Quoted 17 lines · Michael S. Tsirkin
Thanks very much for the review. I'll address the comments. Some questions on your comments below.
Quoted 8 lines · Gleb Natapov
arch/x86/include/asm/kvm_para.h line 42
Quoted 6 lines
arch/x86/include/asm/kvm_para.h +2
-0@@ -37,6 +38,8 @@#define MSR_KVM_SYSTEM_TIME_NEW 0x4b564d01#define MSR_KVM_ASYNC_PF_EN 0x4b564d02#define MSR_KVM_STEAL_TIME 0x4b564d03 +#define MSR_KVM_EOI_EN 0x4b564d04 +#define MSR_KVM_EOI_DISABLED 0x0LThis is valid gpa. Follow others MSR example i.e align the address to, lets say dword, and use lsb as enable bit.
We only need a single byte, since this is per-CPU - it's better to save the memory, so no alignment is required. An explicit disable msr would also address this, right?
Quoted 7 lines
Quoted 3 lines
We do not have shortage of memory.
Better make all MSRs works the same
way.I agree it's nice to have EOI and
ASYNC_PFlook similar but wasting memory is also bad. I'll ponder this some more.
Steal time and kvm clock too and may be others (if anything left at all). I hope you are kidding about wasting of 4 bytes per vcpu.
Quoted 6 lines
Quoted 2 lines
BTW have you added new MSR to
msrs_to_savearray? I forgot to checked.I didn't yet. Trying to understand how will that affect cross-version migration - any input?
Not sure. You need to check what userspace does with them.
Quoted 30 lines
Quoted 17 lines
Quoted 17 lines
Quoted 16 lines
Quoted 4 lines
arch/x86/kvm/lapic.c +4
-0+static void apic_update_isr (struct kvm_lapic *apic) +{ +int vector; + if (!eoi_enabled(apic->vcpu) ||arch/x86/kvm/lapic.c static inline int apic_find_highest_isr(struct kvm_lapic *apic) · line 344
Quoted 10 lines
arch/x86/kvm/lapic.c +10
-0+ !apic->vcpu->arch.eoi.pending || + eoi_get_pending(apic->vcpu)) + return; + apic->vcpu->arch.eoi.pending = false; + vector =apic_find_highest_isr (apic); +if (vector ==-1 ) +return ; +apic_clear_vector (vector, apic->regs +APIC_ISR ); +} +We should just call
apic_set_eoi()on exit if eoi.pending && !eoi_get_pending(). This removes the need for the function and its calls.Quoted 7 lines
Quoted 4 lines
It's a bit of a waste: that one does all kind extra things which we know we don't need, some of the atomics. And it's datapath so extra stuff is not free.
How much time those extra things are taking compared to vmexit you already serving? And there is a good chance you will do them during vmentry anyway while trying to inject (or just check for) new interrupt.
No need to do them twice :)
Quoted 3 lines
Quoted 3 lines
Probably a good idea to replace the call on MSR disable - I think
apic_update_ppris a better thing to call there.
Quoted 6 lines
Quoted 3 lines
Quoted 1 line
Is there anything else that I missed?
I think that simple things are better then complex things if the end result is the same :) Try it and see how much simpler it is.
It doesn't seem to be simpler at all. The common functionality is about 4 lines.
Send patch for us to see. lapic changes should be minimal.
Quoted 8 lines
Quoted 4 lines
Have you measured
that what you are trying to optimize actually worth optimizing? That you can measure the optimization at all?The claim is not that it's measureable. The claim is that it does not scale to keep adding things to do on each entry.
Only if there is something to do. "Premature optimization is the root of all evil". The PV eoi is about not exiting on eoi unnecessary. You are mixing this with trying to avoid calling eoi code for given interrupt at all. Two different optimization, do not try lump them together.
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
Quoted 4 lines
We already have
call tokvm_lapic_sync_from_vapic()on exit path which should be extended to do the above.
Quoted 8 lines
Quoted 5 lines
Quoted 4 lines
It already does this. It calls
apic_set_tpr
which callsapic_update_pprwhich callsapic_update_isr.It does it only if vapic is in use (and it is usually not).
When it's not we don't need to update ppr and so
no need to update isr on this exit.
If there was eoi we need to update both.
Quoted 8 lines
Quoted 4 lines
But the
if()
is already there so we do not need to worry that one additionalif()on the exit path will slow KVM to the crawl.The number of things we need to do on each entry keeps going up, if we just keep adding stuff it won't end well.
You do not add stuff. The if() is already there.
--
Gleb.
Michael S. Tsirkin
maintainerQuoted 24 lines · Gleb Natapov
Quoted 23 lines · Michael S. Tsirkin
Quoted 21 lines · Gleb Natapov
Quoted 13 lines · Michael S. Tsirkin
Thanks very much for the review. I'll address the comments. Some questions on your comments below.
Quoted 8 lines · Gleb Natapov
arch/x86/include/asm/kvm_para.h line 42
Quoted 6 lines
arch/x86/include/asm/kvm_para.h +2
-0@@ -37,6 +38,8 @@#define MSR_KVM_SYSTEM_TIME_NEW 0x4b564d01#define MSR_KVM_ASYNC_PF_EN 0x4b564d02#define MSR_KVM_STEAL_TIME 0x4b564d03 +#define MSR_KVM_EOI_EN 0x4b564d04 +#define MSR_KVM_EOI_DISABLED 0x0LThis is valid gpa. Follow others MSR example i.e align the address to, lets say dword, and use lsb as enable bit.
Quoted 4 lines
We only need a single byte, since this is per-CPU - it's better to save the memory, so no alignment is required. An explicit disable msr would also address this, right?
We do not have shortage of memory.
Better make all MSRs works the same
way.
Quoted 5 lines
Quoted 3 lines
I agree it's nice to have EOI and
ASYNC_PFlook similar but wasting memory is also bad. I'll ponder this some more.Steal time and kvm clock too and may be others (if anything left at all). I hope you are kidding about wasting of 4 bytes per vcpu.
Not vcpu - cpu. It's wasted whenever kernel/kvm.c is built so it has cost on physical machines as well.
Quoted 38 lines
Quoted 6 lines
Quoted 2 lines
BTW have you added new MSR to
msrs_to_savearray? I forgot to checked.I didn't yet. Trying to understand how will that affect cross-version migration - any input?
Not sure. You need to check what userspace does with them.
Quoted 30 lines
Quoted 17 lines
Quoted 17 lines
Quoted 16 lines
Quoted 4 lines
arch/x86/kvm/lapic.c +4
-0+static void apic_update_isr (struct kvm_lapic *apic) +{ +int vector; + if (!eoi_enabled(apic->vcpu) ||arch/x86/kvm/lapic.c static inline int apic_find_highest_isr(struct kvm_lapic *apic) · line 344
Quoted 10 lines
arch/x86/kvm/lapic.c +10
-0+ !apic->vcpu->arch.eoi.pending || + eoi_get_pending(apic->vcpu)) + return; + apic->vcpu->arch.eoi.pending = false; + vector =apic_find_highest_isr (apic); +if (vector ==-1 ) +return ; +apic_clear_vector (vector, apic->regs +APIC_ISR ); +} +We should just call
apic_set_eoi()on exit if eoi.pending && !eoi_get_pending(). This removes the need for the function and its calls.Quoted 7 lines
Quoted 4 lines
It's a bit of a waste: that one does all kind extra things which we know we don't need, some of the atomics. And it's datapath so extra stuff is not free.
How much time those extra things are taking compared to vmexit you already serving? And there is a good chance you will do them during vmentry anyway while trying to inject (or just check for) new interrupt.
No need to do them twice :)
Quoted 3 lines
Quoted 3 lines
Probably a good idea to replace the call on MSR disable - I think
apic_update_ppris a better thing to call there.
Quoted 7 lines
Quoted 6 lines
Quoted 3 lines
Quoted 1 line
Is there anything else that I missed?
I think that simple things are better then complex things if the end result is the same :) Try it and see how much simpler it is.
It doesn't seem to be simpler at all. The common functionality is about 4 lines.
Send patch for us to see.
That's what you are replying to, no?
You can see that it is 4 lines of code.
Quoted 1 line
lapic changes should be minimal.
Exactly my motivation.
Quoted 5 lines
Quoted 5 lines
Quoted 4 lines
Have you measured
that what you are trying to optimize actually worth optimizing? That you can measure the optimization at all?
Quoted 7 lines
Quoted 3 lines
The claim is not that it's measureable. The claim is that it does not scale to keep adding things to do on each entry.
Only if there is something to do. "Premature optimization is the root of all evil". The PV eoi is about not exiting on eoi unnecessary. You are mixing this with trying to avoid calling eoi code for given interrupt at all.
I don't think this is what my patch does. EOI still clears ISR for each interrupt.
Quoted 10 lines
Two different optimization, do not try lump them together.
Quoted 9 lines
Quoted 9 lines
Quoted 9 lines
Quoted 4 lines
We already have
call tokvm_lapic_sync_from_vapic()on exit path which should be extended to do the above.It already does this. It calls
apic_set_tpr
which callsapic_update_pprwhich callsapic_update_isr.
Quoted 5 lines
Quoted 4 lines
Quoted 1 line
It does it only if vapic is in use (and it is usually not).
When it's not we don't need to update ppr and so
no need to update isr on this exit.If there was eoi we need to update both.
By same logic we should call update_ppr on each entry. The overhead is unlikely to be measureable either :).
Quoted 9 lines
Quoted 8 lines
Quoted 4 lines
But the
if()
is already there so we do not need to worry that one additionalif()on the exit path will slow KVM to the crawl.The number of things we need to do on each entry keeps going up, if we just keep adding stuff it won't end well.
You do not add stuff. The
if()is already there.
Your proposal was to check userspace eoi record
each time when eoi is pending, no?
This would certainly add some overhead.
I also find the logic easier to follow as is - it is contained in lapic.c without relying on being called from x86.c as just the right moment.
Quoted 2 lines
--
Gleb.
Quoted 25 lines · Michael S. Tsirkin
Quoted 24 lines · Gleb Natapov
Quoted 23 lines · Michael S. Tsirkin
Quoted 21 lines · Gleb Natapov
Quoted 13 lines · Michael S. Tsirkin
Thanks very much for the review. I'll address the comments. Some questions on your comments below.
Quoted 8 lines · Gleb Natapov
Review line 42
Quoted 6 lines
(quoted diff) +2
-0@@ -37,6 +38,8 @@ #define MSR_KVM_SYSTEM_TIME_NEW 0x4b564d01 #define MSR_KVM_ASYNC_PF_EN 0x4b564d02 #define MSR_KVM_STEAL_TIME 0x4b564d03 +#define MSR_KVM_EOI_EN 0x4b564d04 +#define MSR_KVM_EOI_DISABLED 0x0LThis is valid gpa. Follow others MSR example i.e align the address to, lets say dword, and use lsb as enable bit.
Quoted 4 lines
We only need a single byte, since this is per-CPU - it's better to save the memory, so no alignment is required. An explicit disable msr would also address this, right?
We do not have shortage of memory.
Better make all MSRs works the same
way.
Quoted 9 lines
Quoted 5 lines
Quoted 3 lines
I agree it's nice to have EOI and
ASYNC_PFlook similar but wasting memory is also bad. I'll ponder this some more.Steal time and kvm clock too and may be others (if anything left at all). I hope you are kidding about wasting of 4 bytes per vcpu.
Not vcpu - cpu. It's wasted whenever
kernel/kvm.cis built so it has cost on physical machines as well.
There are less real cpus than vcpus usually :)
Quoted 42 lines
Quoted 42 lines
Quoted 6 lines
Quoted 2 lines
BTW have you added new MSR to
msrs_to_savearray? I forgot to checked.I didn't yet. Trying to understand how will that affect cross-version migration - any input?
Not sure. You need to check what userspace does with them.
Quoted 34 lines
Quoted 17 lines
Quoted 17 lines
Quoted 16 lines
Quoted 4 lines
(quoted diff) +4
-0+static void apic_update_isr(struct kvm_lapic *apic) +{ + int vector; + if (!eoi_enabled(apic->vcpu) ||Review
Quoted 10 lines
(quoted diff) +10
-0+ !apic->vcpu->arch.eoi.pending || + eoi_get_pending(apic->vcpu)) + return; + apic->vcpu->arch.eoi.pending = false; + vector = apic_find_highest_isr(apic); + if (vector == -1) + return; + apic_clear_vector(vector, apic->regs + APIC_ISR); +} +We should just call
apic_set_eoi()on exit if eoi.pending && !eoi_get_pending(). This removes the need for the function and its calls.Quoted 7 lines
Quoted 4 lines
It's a bit of a waste: that one does all kind extra things which we know we don't need, some of the atomics. And it's datapath so extra stuff is not free.
How much time those extra things are taking compared to vmexit you already serving? And there is a good chance you will do them during vmentry anyway while trying to inject (or just check for) new interrupt.
No need to do them twice :)
Quoted 6 lines
Quoted 4 lines
Probably a good idea to replace the call on MSR disable - I think
apic_update_ppris a better thing to call there.Is there anything else that I missed?
I think that simple things are better then complex things if the end result is the same :) Try it and see how much simpler it is.
Quoted 6 lines
Quoted 3 lines
Quoted 2 lines
It doesn't seem to be simpler at all. The common functionality is about 4 lines.
Send patch for us to see.
That's what you are replying to, no?
You can see that it is 4 lines of code.
No. I mean something like patch below. Applies on top of yours. Did not check that it works or even compiles.
Quoted 5 lines
Quoted 2 lines
lapic changes should be minimal.
Exactly my motivation.
My patch removes 13 lines more :)
Quoted 26 lines
Quoted 5 lines
Quoted 5 lines
Quoted 4 lines
Have you measured
that what you are trying to optimize actually worth optimizing? That you can measure the optimization at all?Quoted 7 lines
Quoted 3 lines
The claim is not that it's measureable. The claim is that it does not scale to keep adding things to do on each entry.
Only if there is something to do. "Premature optimization is the root of all evil". The PV eoi is about not exiting on eoi unnecessary. You are mixing this with trying to avoid calling eoi code for given interrupt at all.
I don't think this is what my patch does. EOI still clears ISR for each interrupt.
Quoted 10 lines
Two different optimization, do not try lump them together.
Quoted 9 lines
Quoted 9 lines
Quoted 9 lines
Quoted 4 lines
We already have
call tokvm_lapic_sync_from_vapic()on exit path which should be extended to do the above.It already does this. It calls
apic_set_tpr
which callsapic_update_pprwhich callsapic_update_isr.
Quoted 9 lines
Quoted 5 lines
Quoted 4 lines
Quoted 1 line
It does it only if vapic is in use (and it is usually not).
When it's not we don't need to update ppr and so
no need to update isr on this exit.If there was eoi we need to update both.
By same logic we should call
update_ppron each entry. The overhead is unlikely to be measureable either :).
It is small enough for us to not care about it on RHEL6 where it is called on each entry.
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
Quoted 4 lines
But the
if()
is already there so we do not need to worry that one additionalif()on the exit path will slow KVM to the crawl.
Quoted 8 lines
Quoted 4 lines
Quoted 3 lines
The number of things we need to do on each entry keeps going up, if we just keep adding stuff it won't end well.
You do not add stuff. The
if()is already there.Your proposal was to check userspace eoi record
each time when eoi is pending, no?
Yes.
Quoted 2 lines
This would certainly add some overhead.
Only when eoi is pending. This is rare.
Quoted 4 lines
I also find the logic easier to follow as is - it is contained in
lapic.cwithout relying on being called fromx86.cas just the right moment.
See the patch. It change nothing outside of lapic.c.
arch/x86/kvm/lapic.c +14 -27
@@ -323,20 +323,6 @@ static inline int apic_find_highest_isr(struct kvm_lapic *apic)return result; } -static void apic_update_isr (struct kvm_lapic *apic) -{ -int vector; -if (!eoi_enabled (apic->vcpu) || - !apic->vcpu->arch.eoi.pending || -eoi_get_pending (apic->vcpu)) -return ; - apic->vcpu->arch.eoi.pending = false; - vector =apic_find_highest_isr (apic); -if (vector ==-1 ) -return ; -apic_clear_vector (vector, apic->regs +APIC_ISR ); -} -static void apic_update_ppr (struct kvm_lapic *apic) {u32 tpr, isrv, ppr, old_ppr;
@@ -344,7 +330,6 @@ static void apic_update_ppr(struct kvm_lapic *apic) old_ppr =apic_get_reg (apic,APIC_PROCPRI ); tpr =apic_get_reg (apic,APIC_TASKPRI ); -apic_update_isr (apic); isr =apic_find_highest_isr (apic); isrv = (isr !=-1 ) ? isr :0 ;
@@ -1361,14 +1346,19 @@ void kvm_lapic_sync_from_vapic(struct kvm_vcpu *vcpu)u32 data;void *vapic; -if (!irqchip_in_kernel (vcpu->kvm) || !vcpu->arch.apic->vapic_addr) +if (!irqchip_in_kernel (vcpu->kvm) || !vcpu->arch.apic->vapic_addr || + !apic->vcpu->arch.eoi.pending)return ; - vapic =kmap_atomic (vcpu->arch.apic->vapic_page); - data = *(u32 *)(vapic +offset_in_page (vcpu->arch.apic->vapic_addr)); -kunmap_atomic (vapic); +if (apic->vcpu->arch.eoi.pending && !eoi_get_pending (apic->vcpu)) { +apic_set_eoi (apic); + }else { + vapic =kmap_atomic (vcpu->arch.apic->vapic_page); + data = *(u32 *)(vapic +offset_in_page (vcpu->arch.apic->vapic_addr)); +kunmap_atomic (vapic); -apic_set_tpr (vcpu->arch.apic, data &0xff ); +apic_set_tpr (vcpu->arch.apic, data &0xff ); + } }void kvm_lapic_sync_to_vapic (struct kvm_vcpu *vcpu)
@@ -1469,13 +1459,10 @@ int kvm_hv_vapic_msr_read(struct kvm_vcpu *vcpu, u32 reg, u64 *data)int kvm_pv_enable_apic_eoi (struct kvm_vcpu *vcpu,u64 data) { -if (data ==MSR_KVM_EOI_DISABLED ) { -struct kvm_lapic *apic = vcpu->arch.apic; -if (apic &&apic_enabled (apic)) -apic_update_isr (apic); - }else if (kvm_gfn_to_hva_cache_init (vcpu->kvm, &vcpu->arch.eoi.data, - data)) -return 1 ; +if (data !=MSR_KVM_EOI_DISABLED ) +if (kvm_gfn_to_hva_cache_init (vcpu->kvm, &vcpu->arch.eoi.data, + data)) +return 1 ; vcpu->arch.eoi.msr_val = data; vcpu->arch.eoi.pending = false;
--
Gleb.
Michael S. Tsirkin
maintainerQuoted 5 lines · Gleb Natapov
Quoted 4 lines
Quoted 1 line
lapic changes should be minimal.
Exactly my motivation.
My patch removes 13 lines more :)
Haven't checked whether your patch is correct yet but I see it's checking the eoi register on each exit.
I think it's clear this would make code a bit shorter (not necessarily clearer as we are relying on ) but as I said even though the extra overhead is likely negligeable I have a feeling it's a wrong approach since this won't scale as we add more features.
Let's see what do others think.
Quoted 5 lines
Quoted 4 lines
I also find the logic easier to follow as is - it is contained in
lapic.cwithout relying on being called fromx86.cas just the right moment.See the patch. It change nothing outside of
lapic.c.
True but you rely on x86.c to call kvm_sync_lapic_from_vapic at the right time before injecting interrupts.
I haven't checked whether that is always the case but to me, this makes the code less clear and more fragile.
Again, it appears to be a matter of taste.
Signature · 1 line
MST
Quoted 10 lines · Michael S. Tsirkin
Quoted 5 lines · Gleb Natapov
Quoted 4 lines
Quoted 1 line
lapic changes should be minimal.
Exactly my motivation.
My patch removes 13 lines more :)
Haven't checked whether your patch is correct yet but I see it's checking the eoi register on each exit.
Only if eoi.pending == true on exit.
Quoted 8 lines
I think it's clear this would make code a bit shorter (not necessarily clearer as we are relying on ) but as I said even though the extra overhead is likely negligeable I have a feeling it's a wrong approach since this won't scale as we add more features.
Let's see what do others think.
What I do not like about not calling eoi here is that we have to reason about all the paths into apic and check that we clear isr on all of them. And that is not all. eoi handler set event request, is it OK to skip it? May be, or may be not.
Quoted 8 lines
Quoted 5 lines
Quoted 4 lines
I also find the logic easier to follow as is - it is contained in
lapic.cwithout relying on being called fromx86.cas just the right moment.See the patch. It change nothing outside of
lapic.c.True but you rely on
x86.cto callkvm_sync_lapic_from_vapicat the right time before injecting interrupts.
Not before injecting interrupts, but on vmexit.
Quoted 3 lines
I haven't checked whether that is always the case but to me, this makes the code less clear and more fragile.
We call a lot of apic functions from x86.c. That's were interrupt injection happens.
Quoted 4 lines
Again, it appears to be a matter of taste.
Signature · 1 line
MST
--
Gleb.
Michael S. Tsirkin
maintainerQuoted 4 lines · Gleb Natapov
Quoted 3 lines · Michael S. Tsirkin
Quoted 2 lines · Gleb Natapov
Quoted 2 lines
Quoted 1 line
lapic changes should be minimal.
Quoted 8 lines
Quoted 7 lines
Quoted 3 lines
Quoted 2 lines
Exactly my motivation.
My patch removes 13 lines more :)
Haven't checked whether your patch is correct yet but I see it's checking the eoi register on each exit.
Only if eoi.pending == true on exit.
it's checking eoi.pending always and eoi register when that is true.
Quoted 11 lines
Quoted 8 lines
I think it's clear this would make code a bit shorter (not necessarily clearer as we are relying on ) but as I said even though the extra overhead is likely negligeable I have a feeling it's a wrong approach since this won't scale as we add more features.
Let's see what do others think.
What I do not like about not calling eoi here is that we have to reason about all the paths into apic and check that we clear isr on all of them.
Not at all. Instead, check each time before we read isr or ppr.
Quoted 2 lines
And that is not all. eoi handler
set event request, is it OK to skip it? May be, or may be not.
I think it's for reinjection of lower prio interrupt. Since eoi optimization is disabled in that case we don't need to set event request.
Quoted 9 lines
Quoted 8 lines
Quoted 5 lines
Quoted 4 lines
I also find the logic easier to follow as is - it is contained in
lapic.cwithout relying on being called fromx86.cas just the right moment.See the patch. It change nothing outside of
lapic.c.True but you rely on
x86.cto callkvm_sync_lapic_from_vapicat the right time before injecting interrupts.Not before injecting interrupts, but on vmexit.
sync is called on entry, not on exit, no?
Quoted 13 lines
Quoted 3 lines
I haven't checked whether that is always the case but to me, this makes the code less clear and more fragile.
We call a lot of apic functions from
x86.c. That's were interrupt injection happens.Quoted 4 lines
Again, it appears to be a matter of taste.
Signature · 1 line
MST--
Gleb.
Quoted 9 lines · Michael S. Tsirkin
Quoted 8 lines · Gleb Natapov
Quoted 7 lines · Michael S. Tsirkin
Quoted 5 lines · Gleb Natapov
Quoted 4 lines
Quoted 1 line
lapic changes should be minimal.
Exactly my motivation.
My patch removes 13 lines more :)
Quoted 8 lines
Quoted 4 lines
Quoted 3 lines
Haven't checked whether your patch is correct yet but I see it's checking the eoi register on each exit.
Only if eoi.pending == true on exit.
it's checking eoi.pending always and eoi register when that is true.
We already have if there, so we can reuse it. Instead of checking vapic_addr in kvm_lapic_sync_from_vapic() let it check apic_attention bitmask. vapic_addr and eoi.pending will set bit there and if() will become if(!apic_attention) return. Zero overhead.
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
I think it's clear this would make code a bit shorter (not necessarily clearer as we are relying on ) but as I said even though the extra overhead is likely negligeable I have a feeling it's a wrong approach since this won't scale as we add more features.
Quoted 8 lines
Quoted 5 lines
Quoted 2 lines
Let's see what do others think.
What I do not like about not calling eoi here is that we have to reason about all the paths into apic and check that we clear isr on all of them.
Not at all. Instead, check each time before we read isr or ppr.
Or inject interrupt, or ... Luckily lapic code calls apic_update_ppr() a lot (just in case), so adding check there likely covers any relevant entry into the apic, but this is exactly reasoning I would like to avoid if possible :) I do not see bug per se with your current scheme otherwise I would point it out.
Quoted 5 lines
Quoted 3 lines
And that is not all. eoi handler
set event request, is it OK to skip it? May be, or may be not.I think it's for reinjection of lower prio interrupt.
Me too. But skipping setting that bit should not be taken lightly. We need to know for sure. Current code tries to err on safe side and prefers to set event request when not needed instead of missing it when it is needed. Those rare cases are hard to debug.
Quoted 7 lines
Since eoi optimization is disabled in that case we don't need to set event request.
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
I also find the logic easier to follow as is - it is contained in
lapic.cwithout relying on being called fromx86.cas just the right moment.
Quoted 7 lines
Quoted 5 lines
Quoted 4 lines
Quoted 1 line
See the patch. It change nothing outside of
lapic.c.True but you rely on
x86.cto callkvm_sync_lapic_from_vapicat the right time before injecting interrupts.Not before injecting interrupts, but on vmexit.
sync is called on entry, not on exit, no?
No. Look at the code. Entry is to late.
Quoted 8 lines
Quoted 8 lines
Quoted 5 lines
I haven't checked whether that is always the case but to me, this makes the code less clear and more fragile.
We call a lot of apic functions from
x86.c. That's were interrupt injection happens.
Quoted 7 lines
Quoted 7 lines
Quoted 4 lines
Again, it appears to be a matter of taste.
Signature · 1 line
MST--
Gleb.
--
Gleb.
Michael S. Tsirkin
maintainerQuoted 29 lines · Gleb Natapov
Quoted 28 lines · Michael S. Tsirkin
Quoted 27 lines · Gleb Natapov
Quoted 26 lines · Michael S. Tsirkin
Quoted 14 lines · Gleb Natapov
Quoted 13 lines · Michael S. Tsirkin
Thanks very much for the review. I'll address the comments. Some questions on your comments below.
Quoted 8 lines · Gleb Natapov
Review line 42
Quoted 6 lines
(quoted diff) +2
-0@@ -37,6 +38,8 @@ #define MSR_KVM_SYSTEM_TIME_NEW 0x4b564d01 #define MSR_KVM_ASYNC_PF_EN 0x4b564d02 #define MSR_KVM_STEAL_TIME 0x4b564d03 +#define MSR_KVM_EOI_EN 0x4b564d04 +#define MSR_KVM_EOI_DISABLED 0x0LThis is valid gpa. Follow others MSR example i.e align the address to, lets say dword, and use lsb as enable bit.
Quoted 7 lines
Quoted 4 lines
We only need a single byte, since this is per-CPU - it's better to save the memory, so no alignment is required. An explicit disable msr would also address this, right?
We do not have shortage of memory.
Better make all MSRs works the same
way.I agree it's nice to have EOI and
ASYNC_PFlook similar but wasting memory is also bad. I'll ponder this some more.
Quoted 7 lines
Quoted 6 lines
Quoted 2 lines
Steal time and kvm clock too and may be others (if anything left at all). I hope you are kidding about wasting of 4 bytes per vcpu.
Not vcpu - cpu. It's wasted whenever
kernel/kvm.cis built so it has cost on physical machines as well.There are less real cpus than vcpus usually :)
I'm adding this percpu always. This makes it cheap to access but it means it is allocated on physical cpus - just unused there.
Quoted 52 lines
Quoted 42 lines
Quoted 42 lines
Quoted 6 lines
Quoted 2 lines
BTW have you added new MSR to
msrs_to_savearray? I forgot to checked.I didn't yet. Trying to understand how will that affect cross-version migration - any input?
Not sure. You need to check what userspace does with them.
Quoted 34 lines
Quoted 17 lines
Quoted 17 lines
Quoted 16 lines
Quoted 4 lines
(quoted diff) +4
-0+static void apic_update_isr(struct kvm_lapic *apic) +{ + int vector; + if (!eoi_enabled(apic->vcpu) ||Review
Quoted 10 lines
(quoted diff) +10
-0+ !apic->vcpu->arch.eoi.pending || + eoi_get_pending(apic->vcpu)) + return; + apic->vcpu->arch.eoi.pending = false; + vector = apic_find_highest_isr(apic); + if (vector == -1) + return; + apic_clear_vector(vector, apic->regs + APIC_ISR); +} +We should just call
apic_set_eoi()on exit if eoi.pending && !eoi_get_pending(). This removes the need for the function and its calls.Quoted 7 lines
Quoted 4 lines
It's a bit of a waste: that one does all kind extra things which we know we don't need, some of the atomics. And it's datapath so extra stuff is not free.
How much time those extra things are taking compared to vmexit you already serving? And there is a good chance you will do them during vmentry anyway while trying to inject (or just check for) new interrupt.
No need to do them twice :)
Quoted 6 lines
Quoted 4 lines
Probably a good idea to replace the call on MSR disable - I think
apic_update_ppris a better thing to call there.Is there anything else that I missed?
I think that simple things are better then complex things if the end result is the same :) Try it and see how much simpler it is.
Quoted 6 lines
Quoted 3 lines
Quoted 2 lines
It doesn't seem to be simpler at all. The common functionality is about 4 lines.
Send patch for us to see.
That's what you are replying to, no?
You can see that it is 4 lines of code.No. I mean something like patch below. Applies on top of yours. Did not check that it works or even compiles.
Quoted 1 line
Quoted 5 lines
Quoted 4 lines
Quoted 1 line
lapic changes should be minimal.
Exactly my motivation.
My patch removes 13 lines more :)
I'll take a look, thanks.
Quoted 30 lines
Quoted 30 lines
Quoted 5 lines
Quoted 5 lines
Quoted 4 lines
Have you measured
that what you are trying to optimize actually worth optimizing? That you can measure the optimization at all?Quoted 7 lines
Quoted 3 lines
The claim is not that it's measureable. The claim is that it does not scale to keep adding things to do on each entry.
Only if there is something to do. "Premature optimization is the root of all evil". The PV eoi is about not exiting on eoi unnecessary. You are mixing this with trying to avoid calling eoi code for given interrupt at all.
I don't think this is what my patch does. EOI still clears ISR for each interrupt.
Quoted 14 lines
Two different optimization, do not try lump them together.
Quoted 13 lines
Quoted 10 lines
Quoted 9 lines
Quoted 4 lines
We already have
call tokvm_lapic_sync_from_vapic()on exit path which should be extended to do the above.It already does this. It calls
apic_set_tpr
which callsapic_update_pprwhich callsapic_update_isr.It does it only if vapic is in use (and it is usually not).
When it's not we don't need to update ppr and so
no need to update isr on this exit.
Quoted 7 lines
Quoted 5 lines
Quoted 1 line
If there was eoi we need to update both.
By same logic we should call
update_ppron each entry. The overhead is unlikely to be measureable either :).It is small enough for us to not care about it on RHEL6 where it is called on each entry.
Exactly. So why do not we do it?
Quoted 11 lines
Quoted 11 lines
Quoted 9 lines
Quoted 8 lines
Quoted 4 lines
But the
if()
is already there so we do not need to worry that one additionalif()on the exit path will slow KVM to the crawl.The number of things we need to do on each entry keeps going up, if we just keep adding stuff it won't end well.
You do not add stuff. The
if()is already there.
Quoted 7 lines
Quoted 2 lines
Your proposal was to check userspace eoi record
each time when eoi is pending, no?Yes.
Quoted 2 lines
This would certainly add some overhead.
Only when eoi is pending. This is rare.
This is exactly while guest handles an interrupt. It's not all that rare at all: e.g. device drivers cause an exit from interrupt
handler by doing io.
Quoted 5 lines
Quoted 4 lines
I also find the logic easier to follow as is - it is contained in
lapic.cwithout relying on being called fromx86.cas just the right moment.See the patch. It change nothing outside of
lapic.c.
I'll take a look, thanks.
Quoted 33 lines · Michael S. Tsirkin
Quoted 32 lines · Gleb Natapov
Quoted 31 lines · Michael S. Tsirkin
Quoted 29 lines · Gleb Natapov
Quoted 19 lines · Michael S. Tsirkin
Quoted 18 lines · Gleb Natapov
Quoted 17 lines · Michael S. Tsirkin
Thanks very much for the review. I'll address the comments. Some questions on your comments below.
Quoted 8 lines · Gleb Natapov
Review line 42
Quoted 6 lines
(quoted diff) +2
-0@@ -37,6 +38,8 @@ #define MSR_KVM_SYSTEM_TIME_NEW 0x4b564d01 #define MSR_KVM_ASYNC_PF_EN 0x4b564d02 #define MSR_KVM_STEAL_TIME 0x4b564d03 +#define MSR_KVM_EOI_EN 0x4b564d04 +#define MSR_KVM_EOI_DISABLED 0x0LThis is valid gpa. Follow others MSR example i.e align the address to, lets say dword, and use lsb as enable bit.
We only need a single byte, since this is per-CPU - it's better to save the memory, so no alignment is required. An explicit disable msr would also address this, right?
Quoted 7 lines
Quoted 3 lines
We do not have shortage of memory.
Better make all MSRs works the same
way.I agree it's nice to have EOI and
ASYNC_PFlook similar but wasting memory is also bad. I'll ponder this some more.Steal time and kvm clock too and may be others (if anything left at all). I hope you are kidding about wasting of 4 bytes per vcpu.
Quoted 9 lines
Quoted 4 lines
Quoted 3 lines
Not vcpu - cpu. It's wasted whenever
kernel/kvm.cis built so it has cost on physical machines as well.There are less real cpus than vcpus usually :)
I'm adding this percpu always. This makes it cheap to access but it means it is allocated on physical cpus - just unused there.
I got it. So suppose you have 1024 cpus, so if you'll use dword instead of byte you will spend additional 3072 bytes (which you likely spend anyway due to alignment that will be done to your u8). How much memory do you expect to have with your 1024 cpus to care about 3072 bytes? Are we seriously discussing it?
Quoted 92 lines
Quoted 52 lines
Quoted 42 lines
Quoted 42 lines
Quoted 6 lines
Quoted 2 lines
BTW have you added new MSR to
msrs_to_savearray? I forgot to checked.I didn't yet. Trying to understand how will that affect cross-version migration - any input?
Not sure. You need to check what userspace does with them.
Quoted 34 lines
Quoted 17 lines
Quoted 17 lines
Quoted 16 lines
Quoted 4 lines
(quoted diff) +4
-0+static void apic_update_isr(struct kvm_lapic *apic) +{ + int vector; + if (!eoi_enabled(apic->vcpu) ||Review
Quoted 10 lines
(quoted diff) +10
-0+ !apic->vcpu->arch.eoi.pending || + eoi_get_pending(apic->vcpu)) + return; + apic->vcpu->arch.eoi.pending = false; + vector = apic_find_highest_isr(apic); + if (vector == -1) + return; + apic_clear_vector(vector, apic->regs + APIC_ISR); +} +We should just call
apic_set_eoi()on exit if eoi.pending && !eoi_get_pending(). This removes the need for the function and its calls.Quoted 7 lines
Quoted 4 lines
It's a bit of a waste: that one does all kind extra things which we know we don't need, some of the atomics. And it's datapath so extra stuff is not free.
How much time those extra things are taking compared to vmexit you already serving? And there is a good chance you will do them during vmentry anyway while trying to inject (or just check for) new interrupt.
No need to do them twice :)
Quoted 6 lines
Quoted 4 lines
Probably a good idea to replace the call on MSR disable - I think
apic_update_ppris a better thing to call there.Is there anything else that I missed?
I think that simple things are better then complex things if the end result is the same :) Try it and see how much simpler it is.
Quoted 6 lines
Quoted 3 lines
Quoted 2 lines
It doesn't seem to be simpler at all. The common functionality is about 4 lines.
Send patch for us to see.
That's what you are replying to, no?
You can see that it is 4 lines of code.No. I mean something like patch below. Applies on top of yours. Did not check that it works or even compiles.
Quoted 1 line
Quoted 5 lines
Quoted 4 lines
Quoted 1 line
lapic changes should be minimal.
Exactly my motivation.
My patch removes 13 lines more :)
I'll take a look, thanks.
Quoted 32 lines
Quoted 32 lines
Quoted 5 lines
Quoted 5 lines
Quoted 4 lines
Have you measured
that what you are trying to optimize actually worth optimizing? That you can measure the optimization at all?Quoted 7 lines
Quoted 3 lines
The claim is not that it's measureable. The claim is that it does not scale to keep adding things to do on each entry.
Only if there is something to do. "Premature optimization is the root of all evil". The PV eoi is about not exiting on eoi unnecessary. You are mixing this with trying to avoid calling eoi code for given interrupt at all.
I don't think this is what my patch does. EOI still clears ISR for each interrupt.
Quoted 15 lines
Two different optimization, do not try lump them together.
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
Quoted 4 lines
We already have
call tokvm_lapic_sync_from_vapic()on exit path which should be extended to do the above.Quoted 8 lines
Quoted 5 lines
Quoted 4 lines
It already does this. It calls
apic_set_tpr
which callsapic_update_pprwhich callsapic_update_isr.It does it only if vapic is in use (and it is usually not).
When it's not we don't need to update ppr and so
no need to update isr on this exit.If there was eoi we need to update both.
Quoted 8 lines
Quoted 5 lines
Quoted 3 lines
By same logic we should call
update_ppron each entry. The overhead is unlikely to be measureable either :).It is small enough for us to not care about it on RHEL6 where it is called on each entry.
Exactly. So why do not we do it?
Calling it on each entry is a little bit excessive, calling it on each interrupt it OK. But even when it is called on each entry it does not create performance problems.
Quoted 15 lines
Quoted 15 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
Quoted 4 lines
But the
if()
is already there so we do not need to worry that one additionalif()on the exit path will slow KVM to the crawl.Quoted 8 lines
Quoted 4 lines
Quoted 3 lines
The number of things we need to do on each entry keeps going up, if we just keep adding stuff it won't end well.
You do not add stuff. The
if()is already there.Your proposal was to check userspace eoi record
each time when eoi is pending, no?Yes.
Quoted 8 lines
Quoted 3 lines
Quoted 2 lines
This would certainly add some overhead.
Only when eoi is pending. This is rare.
This is exactly while guest handles an interrupt. It's not all that rare at all: e.g. device drivers cause an exit from interrupt
handler by doing io.
So eoi will be coalesced with io that device driver does. Exactly what we want. But in great scheme of things interrupts are rare :) The optimizations is disabled when interrupts are coming faster that they are served anyway.
Quoted 8 lines
Quoted 6 lines
Quoted 5 lines
I also find the logic easier to follow as is - it is contained in
lapic.cwithout relying on being called fromx86.cas just the right moment.See the patch. It change nothing outside of
lapic.c.I'll take a look, thanks.
--
Gleb.
Michael S. Tsirkin
maintainerQuoted 8 lines · Gleb Natapov
Quoted 6 lines
Quoted 1 line
Only when eoi is pending. This is rare.
This is exactly while guest handles an interrupt. It's not all that rare at all: e.g. device drivers cause an exit from interrupt handler by doing io.
So eoi will be coalesced with io that device driver does. Exactly what we want.
It won't. While we handle interrupts eoi is still set. So there will be a couple of tests + read from userspace Wasted not a huge overhead but it's the principle of the thing.
Signature · 1 line
MST
Quoted 7 lines · Michael S. Tsirkin
Quoted 6 lines · Gleb Natapov
Quoted 6 lines
Quoted 1 line
Only when eoi is pending. This is rare.
This is exactly while guest handles an interrupt. It's not all that rare at all: e.g. device drivers cause an exit from interrupt handler by doing io.
Quoted 7 lines
Quoted 2 lines
So eoi will be coalesced with io that device driver does. Exactly what we want.
It won't. While we handle interrupts eoi is still set. So there will be a couple of tests + read from userspace Wasted not a huge overhead but it's the principle of the thing.
Linux acks irq before calling device handler. Windows for some devices does IO to a device before acking.
--
Gleb.
Quoted 19 lines · Michael S. Tsirkin
Quoted 18 lines · Gleb Natapov
Quoted 17 lines · Michael S. Tsirkin
Thanks very much for the review. I'll address the comments. Some questions on your comments below.
Quoted 8 lines · Gleb Natapov
arch/x86/include/asm/kvm_para.h line 42
Quoted 6 lines
arch/x86/include/asm/kvm_para.h +2
-0@@ -37,6 +38,8 @@#define MSR_KVM_SYSTEM_TIME_NEW 0x4b564d01#define MSR_KVM_ASYNC_PF_EN 0x4b564d02#define MSR_KVM_STEAL_TIME 0x4b564d03 +#define MSR_KVM_EOI_EN 0x4b564d04 +#define MSR_KVM_EOI_DISABLED 0x0LThis is valid gpa. Follow others MSR example i.e align the address to, lets say dword, and use lsb as enable bit.
We only need a single byte, since this is per-CPU - it's better to save the memory, so no alignment is required. An explicit disable msr would also address this, right?
Quoted 6 lines
Quoted 3 lines
We do not have shortage of memory.
Better make all MSRs works the same
way.I agree it's nice to have EOI and
ASYNC_PFlook similar but wasting memory is also bad. I'll ponder this some more.
Wasting three bytes?
Quoted 5 lines
Quoted 2 lines
BTW have you added new MSR to
msrs_to_savearray? I forgot to checked.I didn't yet. Trying to understand how will that affect cross-version migration - any input?
It will just work.
Signature · 1 line
error compiling committee.c: too many arguments to function
Quoted 10 lines · Gleb Natapov
arch/x86/kernel/kvm.c void __cpuinit kvm_guest_cpu_init(void) · line 343
Quoted 8 lines
arch/x86/kernel/kvm.c +6
-0} + +if (kvm_para_has_feature (KVM_FEATURE_EOI )) { + kvm_guest_native_apic_write = apic->write; + apic->write = kvm_guest_apic_write; +wrmsrl (MSR_KVM_EOI_EN ,__pa (&__get_cpu_var (apic_eoi))); + }Can happen on more then one cpu. After it happens on two
kvm_guest_apic_write()will call to itself.
It's also hacky. Try static_cpu_has() in the apic code (and add an eoi method).
Signature · 1 line
error compiling committee.c: too many arguments to function