diff options
| author | Gleb Natapov <gleb@redhat.com> | 2012-09-13 10:19:24 -0400 |
|---|---|---|
| committer | Avi Kivity <avi@redhat.com> | 2012-09-20 08:05:26 -0400 |
| commit | 1e08ec4a130e2745d96df169e67c58df98a07311 (patch) | |
| tree | 362647f5b0bac59e7fe93ce66775afac2cd7da79 | |
| parent | 1d86b5cc4c6d9a1be1458be3701ac9c915a9706f (diff) | |
KVM: optimize apic interrupt delivery
Most interrupt are delivered to only one vcpu. Use pre-build tables to
find interrupt destination instead of looping through all vcpus. In case
of logical mode loop only through vcpus in a logical cluster irq is sent
to.
Signed-off-by: Gleb Natapov <gleb@redhat.com>
Acked-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Avi Kivity <avi@redhat.com>
| -rw-r--r-- | arch/x86/include/asm/kvm_host.h | 12 | ||||
| -rw-r--r-- | arch/x86/kvm/lapic.c | 188 | ||||
| -rw-r--r-- | arch/x86/kvm/lapic.h | 3 | ||||
| -rw-r--r-- | arch/x86/kvm/x86.c | 2 | ||||
| -rw-r--r-- | virt/kvm/irq_comm.c | 7 |
5 files changed, 199 insertions, 13 deletions
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index 43aeb9422839..0b902c98f279 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h | |||
| @@ -525,6 +525,16 @@ struct kvm_arch_memory_slot { | |||
| 525 | struct kvm_lpage_info *lpage_info[KVM_NR_PAGE_SIZES - 1]; | 525 | struct kvm_lpage_info *lpage_info[KVM_NR_PAGE_SIZES - 1]; |
| 526 | }; | 526 | }; |
| 527 | 527 | ||
| 528 | struct kvm_apic_map { | ||
| 529 | struct rcu_head rcu; | ||
| 530 | u8 ldr_bits; | ||
| 531 | /* fields bellow are used to decode ldr values in different modes */ | ||
| 532 | u32 cid_shift, cid_mask, lid_mask; | ||
| 533 | struct kvm_lapic *phys_map[256]; | ||
| 534 | /* first index is cluster id second is cpu id in a cluster */ | ||
| 535 | struct kvm_lapic *logical_map[16][16]; | ||
| 536 | }; | ||
| 537 | |||
| 528 | struct kvm_arch { | 538 | struct kvm_arch { |
| 529 | unsigned int n_used_mmu_pages; | 539 | unsigned int n_used_mmu_pages; |
| 530 | unsigned int n_requested_mmu_pages; | 540 | unsigned int n_requested_mmu_pages; |
| @@ -542,6 +552,8 @@ struct kvm_arch { | |||
| 542 | struct kvm_ioapic *vioapic; | 552 | struct kvm_ioapic *vioapic; |
| 543 | struct kvm_pit *vpit; | 553 | struct kvm_pit *vpit; |
| 544 | int vapics_in_nmi_mode; | 554 | int vapics_in_nmi_mode; |
| 555 | struct mutex apic_map_lock; | ||
| 556 | struct kvm_apic_map *apic_map; | ||
| 545 | 557 | ||
| 546 | unsigned int tss_addr; | 558 | unsigned int tss_addr; |
| 547 | struct page *apic_access_page; | 559 | struct page *apic_access_page; |
diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c index 6f9fd633c888..c6e6b721b6ee 100644 --- a/arch/x86/kvm/lapic.c +++ b/arch/x86/kvm/lapic.c | |||
| @@ -140,11 +140,110 @@ static inline int apic_enabled(struct kvm_lapic *apic) | |||
| 140 | (LVT_MASK | APIC_MODE_MASK | APIC_INPUT_POLARITY | \ | 140 | (LVT_MASK | APIC_MODE_MASK | APIC_INPUT_POLARITY | \ |
| 141 | APIC_LVT_REMOTE_IRR | APIC_LVT_LEVEL_TRIGGER) | 141 | APIC_LVT_REMOTE_IRR | APIC_LVT_LEVEL_TRIGGER) |
| 142 | 142 | ||
| 143 | static inline int apic_x2apic_mode(struct kvm_lapic *apic) | ||
| 144 | { | ||
| 145 | return apic->vcpu->arch.apic_base & X2APIC_ENABLE; | ||
| 146 | } | ||
| 147 | |||
| 143 | static inline int kvm_apic_id(struct kvm_lapic *apic) | 148 | static inline int kvm_apic_id(struct kvm_lapic *apic) |
| 144 | { | 149 | { |
| 145 | return (kvm_apic_get_reg(apic, APIC_ID) >> 24) & 0xff; | 150 | return (kvm_apic_get_reg(apic, APIC_ID) >> 24) & 0xff; |
| 146 | } | 151 | } |
| 147 | 152 | ||
| 153 | static inline u16 apic_cluster_id(struct kvm_apic_map *map, u32 ldr) | ||
| 154 | { | ||
| 155 | u16 cid; | ||
| 156 | ldr >>= 32 - map->ldr_bits; | ||
| 157 | cid = (ldr >> map->cid_shift) & map->cid_mask; | ||
| 158 | |||
| 159 | BUG_ON(cid >= ARRAY_SIZE(map->logical_map)); | ||
| 160 | |||
| 161 | return cid; | ||
| 162 | } | ||
| 163 | |||
| 164 | static inline u16 apic_logical_id(struct kvm_apic_map *map, u32 ldr) | ||
| 165 | { | ||
| 166 | ldr >>= (32 - map->ldr_bits); | ||
| 167 | return ldr & map->lid_mask; | ||
| 168 | } | ||
| 169 | |||
| 170 | static void recalculate_apic_map(struct kvm *kvm) | ||
| 171 | { | ||
| 172 | struct kvm_apic_map *new, *old = NULL; | ||
| 173 | struct kvm_vcpu *vcpu; | ||
| 174 | int i; | ||
| 175 | |||
| 176 | new = kzalloc(sizeof(struct kvm_apic_map), GFP_KERNEL); | ||
| 177 | |||
| 178 | mutex_lock(&kvm->arch.apic_map_lock); | ||
| 179 | |||
| 180 | if (!new) | ||
| 181 | goto out; | ||
| 182 | |||
| 183 | new->ldr_bits = 8; | ||
| 184 | /* flat mode is default */ | ||
| 185 | new->cid_shift = 8; | ||
| 186 | new->cid_mask = 0; | ||
| 187 | new->lid_mask = 0xff; | ||
| 188 | |||
| 189 | kvm_for_each_vcpu(i, vcpu, kvm) { | ||
| 190 | struct kvm_lapic *apic = vcpu->arch.apic; | ||
| 191 | u16 cid, lid; | ||
| 192 | u32 ldr; | ||
| 193 | |||
| 194 | if (!kvm_apic_present(vcpu)) | ||
| 195 | continue; | ||
| 196 | |||
| 197 | /* | ||
| 198 | * All APICs have to be configured in the same mode by an OS. | ||
| 199 | * We take advatage of this while building logical id loockup | ||
| 200 | * table. After reset APICs are in xapic/flat mode, so if we | ||
| 201 | * find apic with different setting we assume this is the mode | ||
| 202 | * OS wants all apics to be in; build lookup table accordingly. | ||
| 203 | */ | ||
| 204 | if (apic_x2apic_mode(apic)) { | ||
| 205 | new->ldr_bits = 32; | ||
| 206 | new->cid_shift = 16; | ||
| 207 | new->cid_mask = new->lid_mask = 0xffff; | ||
| 208 | } else if (kvm_apic_sw_enabled(apic) && | ||
| 209 | !new->cid_mask /* flat mode */ && | ||
| 210 | kvm_apic_get_reg(apic, APIC_DFR) == APIC_DFR_CLUSTER) { | ||
| 211 | new->cid_shift = 4; | ||
| 212 | new->cid_mask = 0xf; | ||
| 213 | new->lid_mask = 0xf; | ||
| 214 | } | ||
| 215 | |||
| 216 | new->phys_map[kvm_apic_id(apic)] = apic; | ||
| 217 | |||
| 218 | ldr = kvm_apic_get_reg(apic, APIC_LDR); | ||
| 219 | cid = apic_cluster_id(new, ldr); | ||
| 220 | lid = apic_logical_id(new, ldr); | ||
| 221 | |||
| 222 | if (lid) | ||
| 223 | new->logical_map[cid][ffs(lid) - 1] = apic; | ||
| 224 | } | ||
| 225 | out: | ||
| 226 | old = rcu_dereference_protected(kvm->arch.apic_map, | ||
| 227 | lockdep_is_held(&kvm->arch.apic_map_lock)); | ||
| 228 | rcu_assign_pointer(kvm->arch.apic_map, new); | ||
| 229 | mutex_unlock(&kvm->arch.apic_map_lock); | ||
| 230 | |||
| 231 | if (old) | ||
| 232 | kfree_rcu(old, rcu); | ||
| 233 | } | ||
| 234 | |||
| 235 | static inline void kvm_apic_set_id(struct kvm_lapic *apic, u8 id) | ||
| 236 | { | ||
| 237 | apic_set_reg(apic, APIC_ID, id << 24); | ||
| 238 | recalculate_apic_map(apic->vcpu->kvm); | ||
| 239 | } | ||
| 240 | |||
| 241 | static inline void kvm_apic_set_ldr(struct kvm_lapic *apic, u32 id) | ||
| 242 | { | ||
| 243 | apic_set_reg(apic, APIC_LDR, id); | ||
| 244 | recalculate_apic_map(apic->vcpu->kvm); | ||
| 245 | } | ||
| 246 | |||
| 148 | static inline int apic_lvt_enabled(struct kvm_lapic *apic, int lvt_type) | 247 | static inline int apic_lvt_enabled(struct kvm_lapic *apic, int lvt_type) |
| 149 | { | 248 | { |
| 150 | return !(kvm_apic_get_reg(apic, lvt_type) & APIC_LVT_MASKED); | 249 | return !(kvm_apic_get_reg(apic, lvt_type) & APIC_LVT_MASKED); |
| @@ -194,11 +293,6 @@ void kvm_apic_set_version(struct kvm_vcpu *vcpu) | |||
| 194 | apic_set_reg(apic, APIC_LVR, v); | 293 | apic_set_reg(apic, APIC_LVR, v); |
| 195 | } | 294 | } |
| 196 | 295 | ||
| 197 | static inline int apic_x2apic_mode(struct kvm_lapic *apic) | ||
| 198 | { | ||
| 199 | return apic->vcpu->arch.apic_base & X2APIC_ENABLE; | ||
| 200 | } | ||
| 201 | |||
| 202 | static const unsigned int apic_lvt_mask[APIC_LVT_NUM] = { | 296 | static const unsigned int apic_lvt_mask[APIC_LVT_NUM] = { |
| 203 | LVT_MASK , /* part LVTT mask, timer mode mask added at runtime */ | 297 | LVT_MASK , /* part LVTT mask, timer mode mask added at runtime */ |
| 204 | LVT_MASK | APIC_MODE_MASK, /* LVTTHMR */ | 298 | LVT_MASK | APIC_MODE_MASK, /* LVTTHMR */ |
| @@ -483,6 +577,72 @@ int kvm_apic_match_dest(struct kvm_vcpu *vcpu, struct kvm_lapic *source, | |||
| 483 | return result; | 577 | return result; |
| 484 | } | 578 | } |
| 485 | 579 | ||
| 580 | bool kvm_irq_delivery_to_apic_fast(struct kvm *kvm, struct kvm_lapic *src, | ||
| 581 | struct kvm_lapic_irq *irq, int *r) | ||
| 582 | { | ||
| 583 | struct kvm_apic_map *map; | ||
| 584 | unsigned long bitmap = 1; | ||
| 585 | struct kvm_lapic **dst; | ||
| 586 | int i; | ||
| 587 | bool ret = false; | ||
| 588 | |||
| 589 | *r = -1; | ||
| 590 | |||
| 591 | if (irq->shorthand == APIC_DEST_SELF) { | ||
| 592 | *r = kvm_apic_set_irq(src->vcpu, irq); | ||
| 593 | return true; | ||
| 594 | } | ||
| 595 | |||
| 596 | if (irq->shorthand) | ||
| 597 | return false; | ||
| 598 | |||
| 599 | rcu_read_lock(); | ||
| 600 | map = rcu_dereference(kvm->arch.apic_map); | ||
| 601 | |||
| 602 | if (!map) | ||
| 603 | goto out; | ||
| 604 | |||
