KVM: x86: avoid logical_map when it is invalid
authorRadim Krčmář <rkrcmar@redhat.com>
Thu, 12 Feb 2015 18:41:33 +0000 (19:41 +0100)
committerPaolo Bonzini <pbonzini@redhat.com>
Wed, 8 Apr 2015 08:47:01 +0000 (10:47 +0200)
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>
arch/x86/include/asm/kvm_host.h
arch/x86/kvm/lapic.c

index 5b1bc97a258a59619a2423f898939681c7130553..9477b27e8e1e03a395c4de5d412b085bec17175d 100644 (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;
index d45f00ba74402213d57bc89e97efeb6a926722ec..fa4bd89ea381adc7287e34d9a15f08a9f29d53d8 100644 (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;