KVM: x86: avoid logical_map when it is invalid

We want to support mixed modes and the easiest solution is to avoid
optimizing those weird and unlikely scenarios.

Signed-off-by: Radim Krčmář <rkrcmar@redhat.com>
Message-Id: <1423766494-26150-4-git-send-email-rkrcmar@redhat.com>
[Add comment above KVM_APIC_MODE_* defines. - Paolo]
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
Radim Krčmář 2015-02-12 19:41:33 +01:00 committed by Paolo Bonzini
parent 9ea369b032
commit 3548a259f6
2 changed files with 35 additions and 1 deletions

View File

@ -548,8 +548,20 @@ struct kvm_arch_memory_slot {
struct kvm_lpage_info *lpage_info[KVM_NR_PAGE_SIZES - 1];
};
/*
* We use as the mode the number of bits allocated in the LDR for the
* logical processor ID. It happens that these are all powers of two.
* This makes it is very easy to detect cases where the APICs are
* configured for multiple modes; in that case, we cannot use the map and
* hence cannot use kvm_irq_delivery_to_apic_fast either.
*/
#define KVM_APIC_MODE_XAPIC_CLUSTER 4
#define KVM_APIC_MODE_XAPIC_FLAT 8
#define KVM_APIC_MODE_X2APIC 16
struct kvm_apic_map {
struct rcu_head rcu;
u8 mode;
u8 ldr_bits;
/* fields bellow are used to decode ldr values in different modes */
u32 cid_shift, cid_mask, lid_mask;

View File

@ -133,6 +133,14 @@ static inline int kvm_apic_id(struct kvm_lapic *apic)
return (kvm_apic_get_reg(apic, APIC_ID) >> 24) & 0xff;
}
/* The logical map is definitely wrong if we have multiple
* modes at the same time. (Physical map is always right.)
*/
static inline bool kvm_apic_logical_map_valid(struct kvm_apic_map *map)
{
return !(map->mode & (map->mode - 1));
}
static void recalculate_apic_map(struct kvm *kvm)
{
struct kvm_apic_map *new, *old = NULL;
@ -162,16 +170,19 @@ static void recalculate_apic_map(struct kvm *kvm)
new->ldr_bits = 32;
new->cid_shift = 16;
new->cid_mask = new->lid_mask = 0xffff;
new->mode |= KVM_APIC_MODE_X2APIC;
} else if (kvm_apic_get_reg(apic, APIC_LDR)) {
if (kvm_apic_get_reg(apic, APIC_DFR) ==
APIC_DFR_CLUSTER) {
new->cid_shift = 4;
new->cid_mask = 0xf;
new->lid_mask = 0xf;
new->mode |= KVM_APIC_MODE_XAPIC_CLUSTER;
} else {
new->cid_shift = 8;
new->cid_mask = 0;
new->lid_mask = 0xff;
new->mode |= KVM_APIC_MODE_XAPIC_FLAT;
}
}
@ -201,6 +212,10 @@ static void recalculate_apic_map(struct kvm *kvm)
if (aid < ARRAY_SIZE(new->phys_map))
new->phys_map[aid] = apic;
if (!kvm_apic_logical_map_valid(new));
continue;
if (lid && cid < ARRAY_SIZE(new->logical_map))
new->logical_map[cid][ffs(lid) - 1] = apic;
}
@ -718,7 +733,14 @@ bool kvm_irq_delivery_to_apic_fast(struct kvm *kvm, struct kvm_lapic *src,
dst = &map->phys_map[irq->dest_id];
} else {
u32 mda = irq->dest_id << (32 - map->ldr_bits);
u16 cid = apic_cluster_id(map, mda);
u16 cid;
if (!kvm_apic_logical_map_valid(map)) {
ret = false;
goto out;
}
cid = apic_cluster_id(map, mda);
if (cid >= ARRAY_SIZE(map->logical_map))
goto out;