kvm: set irq affinity for assigned devices
v3 cover kvm: set irq affinity for assigned devices
To forward an interrupt to a vcpu that runs on a host cpu different from the current one,
we need an ipi which likely will cost us as much as delivering the interrupt directly to that cpu would.
So it seems to make sense to set irq affinity hint to point there, irq balancer can then take this into accound and balance interrupts accordingly.
This is what this patchset does.
Note: it might make sense to optimize the gsi lookups here for the msi case, using rcu, the way we did for eventfd. But that's a subject for another patch.
Testing shows that with irqbalance running this performs basically the same as manual numa pinning.
This patch applies to 3.2 or linux-next.
Changes from v1:
Address comments by Alex Williamson and Marcelo Tosatti:
- Fold in v2 patch from [email protected]:
fix affinity for lowest priority setups,
- Disable automatic affinity for round robin setups.
- Fix compiler warnings.
- Fix a checkpatch warning.
- Clear hint before free irq for MSI/INTx.
Michael S. Tsirkin (2):
kvm: pass host irq number to set irq calls
kvm: set affinity hint for assigned device msi
arch/x86/kvm/i8254.c | 4 ++-- arch/x86/kvm/lapic.c | 2 +- arch/x86/kvm/x86.c | 2 +- include/linux/kvm_host.h | 7 ++++--- virt/kvm/assigned-dev.c | 14 +++++++++----- virt/kvm/eventfd.c | 7 ++++--- virt/kvm/ioapic.c | 2 +- virt/kvm/ioapic.h | 2 +- virt/kvm/irq_comm.c | 43 ++++++++++++++++++++++++++++++++++--------- 9 files changed, 57 insertions(+), 26 deletions(-)
Signature · 1 line
1.7.8.2.325.g247f9
PATCH v3 1/2 kvm: pass host irq number to set irq calls
Pass host irq number to functions that set guest irq. If unavailable, pass -1.
--- arch/x86/kvm/i8254.c | 4 ++-- arch/x86/kvm/lapic.c | 2 +- arch/x86/kvm/x86.c | 2 +- include/linux/kvm_host.h | 7 ++++--- virt/kvm/assigned-dev.c | 8 ++++---- virt/kvm/eventfd.c | 7 ++++--- virt/kvm/ioapic.c | 2 +- virt/kvm/ioapic.h | 2 +- virt/kvm/irq_comm.c | 18 +++++++++++------- 9 files changed, 29 insertions(+), 23 deletions(-)
arch/x86/kvm/i8254.c +2 -2
@@ -303,8 +303,8 @@ static void pit_do_work(struct work_struct *work) }spin_unlock (&ps->inject_lock);if (inject) { -kvm_set_irq (kvm, kvm->arch.vpit->irq_source_id,0 ,1 ); -kvm_set_irq (kvm, kvm->arch.vpit->irq_source_id,0 ,0 ); +kvm_set_irq (kvm, kvm->arch.vpit->irq_source_id,0 ,1 ,-1 ); +kvm_set_irq (kvm, kvm->arch.vpit->irq_source_id,0 ,0 ,-1 );/* * Provides NMI watchdog support via Virtual Wire mode.
arch/x86/kvm/lapic.c +1 -1
@@ -525,7 +525,7 @@ static void apic_send_ipi(struct kvm_lapic *apic) irq.trig_mode, irq.level, irq.dest_mode, irq.delivery_mode, irq.vector); -kvm_irq_delivery_to_apic (apic->vcpu->kvm, apic, &irq); +kvm_irq_delivery_to_apic (apic->vcpu->kvm, apic, &irq,-1 ); }static u32 apic_get_tmcct (struct kvm_lapic *apic)
arch/x86/kvm/x86.c +1 -1
@@ -3644,7 +3644,7 @@ long kvm_arch_vm_ioctl(struct file *filp,if (irqchip_in_kernel (kvm)) {__s32 status; status =kvm_set_irq (kvm,KVM_USERSPACE_IRQ_SOURCE_ID , - irq_event.irq, irq_event.level); + irq_event.irq, irq_event.level,-1 );if (ioctl ==KVM_IRQ_LINE_STATUS ) { r = -EFAULT ; irq_event.status = status;
include/linux/kvm_host.h +4 -3
@@ -194,7 +194,7 @@ struct kvm_kernel_irq_routing_entry {u32 gsi;u32 type;int (*set)(struct kvm_kernel_irq_routing_entry *e, -struct kvm *kvm,int irq_source_id,int level); +struct kvm *kvm,int irq_source_id,int level,int host_irq); union { struct { unsigned irqchip;
@@ -546,9 +546,10 @@ void kvm_get_intr_delivery_bitmask(struct kvm_ioapic *ioapic,union kvm_ioapic_redirect_entry *entry, unsigned long *deliver_bitmask);#endif -int kvm_set_irq (struct kvm *kvm,int irq_source_id,u32 irq,int level); +int kvm_set_irq (struct kvm *kvm,int irq_source_id,u32 irq,int level, +int host_irq);int kvm_set_msi (struct kvm_kernel_irq_routing_entry *irq_entry,struct kvm *kvm, -int irq_source_id,int level); +int irq_source_id,int level,int host_irq);void kvm_notify_acked_irq (struct kvm *kvm,unsigned irqchip,unsigned pin);void kvm_register_irq_ack_notifier (struct kvm *kvm,struct kvm_irq_ack_notifier *kian);
virt/kvm/assigned-dev.c +4 -4
@@ -69,7 +69,7 @@ static irqreturn_t kvm_assigned_dev_thread(int irq, void *dev_id) }kvm_set_irq (assigned_dev->kvm, assigned_dev->irq_source_id, - assigned_dev->guest_irq,1 ); + assigned_dev->guest_irq,1 , irq);return IRQ_HANDLED ; }
@@ -84,7 +84,7 @@ static irqreturn_t kvm_assigned_dev_thread_msix(int irq, void *dev_id)if (index >=0 ) { vector = assigned_dev->guest_msix_entries[index].vector;kvm_set_irq (assigned_dev->kvm, assigned_dev->irq_source_id, - vector,1 ); + vector,1 , irq); }return IRQ_HANDLED ;
@@ -98,7 +98,7 @@ static void kvm_assigned_dev_ack_irq(struct kvm_irq_ack_notifier *kian) container_of(kian, struct kvm_assigned_dev_kernel, ack_notifier); -kvm_set_irq (dev->kvm, dev->irq_source_id, dev->guest_irq,0 ); +kvm_set_irq (dev->kvm, dev->irq_source_id, dev->guest_irq,0 ,-1 );/* The guest irq may be shared so this ack may be * from another device.
@@ -119,7 +119,7 @@ static void deassign_guest_irq(struct kvm *kvm, &assigned_dev->ack_notifier);kvm_set_irq (assigned_dev->kvm, assigned_dev->irq_source_id, - assigned_dev->guest_irq,0 ); + assigned_dev->guest_irq,0 ,-1 );if (assigned_dev->irq_source_id !=-1 )kvm_free_irq_source_id (kvm, assigned_dev->irq_source_id);
virt/kvm/eventfd.c +4 -3
@@ -67,8 +67,8 @@ irqfd_inject(struct work_struct *work) struct _irqfd *irqfd = container_of(work, struct _irqfd, inject);struct kvm *kvm = irqfd->kvm; -kvm_set_irq (kvm,KVM_USERSPACE_IRQ_SOURCE_ID , irqfd->gsi,1 ); -kvm_set_irq (kvm,KVM_USERSPACE_IRQ_SOURCE_ID , irqfd->gsi,0 ); +kvm_set_irq (kvm,KVM_USERSPACE_IRQ_SOURCE_ID , irqfd->gsi,1 ,-1 ); +kvm_set_irq (kvm,KVM_USERSPACE_IRQ_SOURCE_ID , irqfd->gsi,0 ,-1 ); }/*
@@ -138,7 +138,8 @@ irqfd_wakeup(wait_queue_t *wait, unsigned mode, int sync, void *key) irq =rcu_dereference (irqfd->irq_entry);/* An event has been signaled, inject an interrupt */ if (irq) -kvm_set_msi (irq, kvm,KVM_USERSPACE_IRQ_SOURCE_ID ,1 ); +kvm_set_msi (irq, kvm,KVM_USERSPACE_IRQ_SOURCE_ID ,1 , +-1 );else schedule_work (&irqfd->inject);rcu_read_unlock ();
virt/kvm/ioapic.c +1 -1
@@ -188,7 +188,7 @@ static int ioapic_deliver(struct kvm_ioapic *ioapic, int irq) irqe.dest_id = ioapic->kvm->bsp_vcpu->vcpu_id; }#endif -return kvm_irq_delivery_to_apic (ioapic->kvm,NULL , &irqe); +return kvm_irq_delivery_to_apic (ioapic->kvm,NULL , &irqe,-1 ); }int kvm_ioapic_set_irq (struct kvm_ioapic *ioapic,int irq,int level)
virt/kvm/ioapic.h +1 -1
@@ -76,7 +76,7 @@ void kvm_ioapic_destroy(struct kvm *kvm);int kvm_ioapic_set_irq (struct kvm_ioapic *ioapic,int irq,int level);void kvm_ioapic_reset (struct kvm_ioapic *ioapic);int kvm_irq_delivery_to_apic (struct kvm *kvm,struct kvm_lapic *src, -struct kvm_lapic_irq *irq); +struct kvm_lapic_irq *irq,int host_irq);int kvm_get_ioapic (struct kvm *kvm,struct kvm_ioapic_state *state);int kvm_set_ioapic (struct kvm *kvm,struct kvm_ioapic_state *state);
virt/kvm/irq_comm.c +11 -7
@@ -46,7 +46,8 @@ static inline int kvm_irq_line_state(unsigned long *irq_state, }static int kvm_set_pic_irq (struct kvm_kernel_irq_routing_entry *e, -struct kvm *kvm,int irq_source_id,int level) +struct kvm *kvm,int irq_source_id,int level, +int host_irq) {#ifdef CONFIG_X86 struct kvm_pic *pic =pic_irqchip (kvm);
@@ -59,7 +60,8 @@ static int kvm_set_pic_irq(struct kvm_kernel_irq_routing_entry *e, }static int kvm_set_ioapic_irq (struct kvm_kernel_irq_routing_entry *e, -struct kvm *kvm,int irq_source_id,int level) +struct kvm *kvm,int irq_source_id,int level, +int host_irq) {struct kvm_ioapic *ioapic = kvm->arch.vioapic; level = kvm_irq_line_state(&ioapic->irq_states[e->irqchip.pin],
@@ -79,7 +81,7 @@ inline static bool kvm_is_dm_lowest_prio(struct kvm_lapic_irq *irq) }int kvm_irq_delivery_to_apic (struct kvm *kvm,struct kvm_lapic *src, -struct kvm_lapic_irq *irq) +struct kvm_lapic_irq *irq,int host_irq) {int i, r =-1 ;struct kvm_vcpu *vcpu, *lowest =NULL ;
@@ -115,7 +117,7 @@ int kvm_irq_delivery_to_apic(struct kvm *kvm, struct kvm_lapic *src, }int kvm_set_msi (struct kvm_kernel_irq_routing_entry *e, -struct kvm *kvm,int irq_source_id,int level) +struct kvm *kvm,int irq_source_id,int level,int host_irq) {struct kvm_lapic_irq irq;
@@ -135,7 +137,7 @@ int kvm_set_msi(struct kvm_kernel_irq_routing_entry *e, irq.shorthand =0 ;/* TODO Deal with RH bit of MSI message address */ -return kvm_irq_delivery_to_apic (kvm,NULL , &irq); +return kvm_irq_delivery_to_apic (kvm,NULL , &irq, host_irq); }/*
@@ -144,7 +146,8 @@ int kvm_set_msi(struct kvm_kernel_irq_routing_entry *e,* = 0 Interrupt was coalesced (previous irq is still pending) * > 0 Number of CPUs interrupt was delivered to */ -int kvm_set_irq (struct kvm *kvm,int irq_source_id,u32 irq,int level) +int kvm_set_irq (struct kvm *kvm,int irq_source_id,u32 irq,int level, +int host_irq) {struct kvm_kernel_irq_routing_entry *e, irq_set[KVM_NR_IRQCHIPS ];int ret =-1 , i =0 ;
@@ -166,7 +169,8 @@ int kvm_set_irq(struct kvm *kvm, int irq_source_id, u32 irq, int level)while (i--) {int r; - r = irq_set[i].set (&irq_set[i], kvm, irq_source_id, level); + r = irq_set[i].set (&irq_set[i], kvm, irq_source_id, level, + host_irq);if (r <0 )continue ;
Signature · 1 line
1.7.8.2.325.g247f9
PATCH v3 2/2 kvm: set affinity hint for assigned device msi
In reply to Michael S. TsirkinTo forward an interrupt to a vcpu that runs on a host cpu different from the current one,
we need an ipi which likely will cost us as much as delivering the interrupt directly to that cpu would.
Set irq affinity hint to point there, irq balancer can then take this into accound and balance interrupts accordingly.
--- virt/kvm/assigned-dev.c | 6 +++++- virt/kvm/irq_comm.c | 25 +++++++++++++++++++++++-- 2 files changed, 28 insertions(+), 3 deletions(-)
virt/kvm/assigned-dev.c +5 -1
@@ -146,9 +146,12 @@ static void deassign_host_irq(struct kvm *kvm,for (i =0 ; i < assigned_dev->entries_nr; i++)disable_irq (assigned_dev->host_msix_entries[i].vector); -for (i =0 ; i < assigned_dev->entries_nr; i++) +for (i =0 ; i < assigned_dev->entries_nr; i++) { +u32 vector = assigned_dev->host_msix_entries[i].vector; +irq_set_affinity_hint (vector,NULL );free_irq (assigned_dev->host_msix_entries[i].vector, assigned_dev); + } assigned_dev->entries_nr =0 ;kfree (assigned_dev->host_msix_entries);
@@ -158,6 +161,7 @@ static void deassign_host_irq(struct kvm *kvm,/* Deal with MSI and INTx */ disable_irq (assigned_dev->host_irq); +irq_set_affinity_hint (assigned_dev->host_irq,NULL );free_irq (assigned_dev->host_irq, assigned_dev);if (assigned_dev->irq_requested_type &KVM_DEV_IRQ_HOST_MSI )
virt/kvm/irq_comm.c +23 -2
@@ -22,6 +22,7 @@#include <linux/kvm_host.h> #include <linux/slab.h> +#include <linux/interrupt.h> #include <trace/events/kvm.h> #include <asm/msidef.h>
@@ -80,11 +81,23 @@ inline static bool kvm_is_dm_lowest_prio(struct kvm_lapic_irq *irq)#endif } +static void kvm_vcpu_host_irq_hint (struct kvm_vcpu *vcpu,int host_irq) +{ +const struct cpumask *mask; +/* raw_smp_processor_id() is ok here: if we get preempted we can get a +* wrong value but we don't mind much. */ +if (host_irq >=0 &&unlikely (vcpu->cpu !=raw_smp_processor_id ())) { + mask =get_cpu_mask (vcpu->cpu); +irq_set_affinity_hint (host_irq, mask); + } +} +int kvm_irq_delivery_to_apic (struct kvm *kvm,struct kvm_lapic *src,struct kvm_lapic_irq *irq,int host_irq) {int i, r =-1 ; -struct kvm_vcpu *vcpu, *lowest =NULL ; +int matches =0 ; +struct kvm_vcpu *vcpu, *lowest =NULL , *uninitialized_var (match );if (irq->dest_mode ==0 && irq->dest_id ==0xff &&kvm_is_dm_lowest_prio (irq))
@@ -98,10 +111,13 @@ int kvm_irq_delivery_to_apic(struct kvm *kvm, struct kvm_lapic *src, irq->dest_id, irq->dest_mode))continue ; + ++matches; +if (!kvm_is_dm_lowest_prio (irq)) {if (r <0 ) r =0 ; r +=kvm_apic_set_irq (vcpu, irq); + match = vcpu; }else if (kvm_lapic_enabled (vcpu)) {if (!lowest) lowest = vcpu;
@@ -110,8 +126,13 @@ int kvm_irq_delivery_to_apic(struct kvm *kvm, struct kvm_lapic *src, } } -if (lowest) +if (lowest) { r =kvm_apic_set_irq (lowest, irq); + match = lowest; + } + +if (matches ==1 ) +kvm_vcpu_host_irq_hint (match, host_irq);return r; }
Signature · 1 line
1.7.8.2.325.g247f9