Skip to content

Commit

Permalink
KVM: x86: Explicitly track all possibilities for APIC map's logical m…
Browse files Browse the repository at this point in the history
…odes

Track all possibilities for the optimized APIC map's logical modes
instead of overloading the pseudo-bitmap and treating any "unknown" value
as "invalid".

As documented by the now-stale comment above the mode values, the values
did have meaning when the optimized map was originally added.  That
dependent logical was removed by commit e45115b ("KVM: x86: use
physical LAPIC array for logical x2APIC"), but the obfuscated behavior
and its comment were left behind.

Opportunistically rename "mode" to "logical_mode", partly to make it
clear that the "disabled" case applies only to the logical map, but also
to prove that there is no lurking code that expects "mode" to be a bitmap.

Functionally, this is a glorified nop.

Signed-off-by: Sean Christopherson <seanjc@google.com>
Reviewed-by: Maxim Levitsky <mlevitsk@redhat.com>
Message-Id: <20230106011306.85230-19-seanjc@google.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
  • Loading branch information
Sean Christopherson authored and Paolo Bonzini committed Jan 13, 2023
1 parent 6ea567c commit 3536690
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 17 deletions.
29 changes: 20 additions & 9 deletions arch/x86/include/asm/kvm_host.h
Original file line number Diff line number Diff line change
Expand Up @@ -1022,19 +1022,30 @@ struct kvm_arch_memory_slot {
};

/*
* 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.
* Track the mode of the optimized logical map, as the rules for decoding the
* destination vary per mode. Enabling the optimized logical map requires all
* software-enabled local APIs to be in the same mode, each addressable APIC to
* be mapped to only one MDA, and each MDA to map to at most one APIC.
*/
#define KVM_APIC_MODE_XAPIC_CLUSTER 4
#define KVM_APIC_MODE_XAPIC_FLAT 8
#define KVM_APIC_MODE_X2APIC 16
enum kvm_apic_logical_mode {
/* All local APICs are software disabled. */
KVM_APIC_MODE_SW_DISABLED,
/* All software enabled local APICs in xAPIC cluster addressing mode. */
KVM_APIC_MODE_XAPIC_CLUSTER,
/* All software enabled local APICs in xAPIC flat addressing mode. */
KVM_APIC_MODE_XAPIC_FLAT,
/* All software enabled local APICs in x2APIC mode. */
KVM_APIC_MODE_X2APIC,
/*
* Optimized map disabled, e.g. not all local APICs in the same logical
* mode, same logical ID assigned to multiple APICs, etc.
*/
KVM_APIC_MODE_MAP_DISABLED,
};

struct kvm_apic_map {
struct rcu_head rcu;
u8 mode;
enum kvm_apic_logical_mode logical_mode;
u32 max_apic_id;
union {
struct kvm_lapic *xapic_flat_map[8];
Expand Down
40 changes: 32 additions & 8 deletions arch/x86/kvm/lapic.c
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,12 @@ static bool kvm_use_posted_timer_interrupt(struct kvm_vcpu *vcpu)

static inline bool kvm_apic_map_get_logical_dest(struct kvm_apic_map *map,
u32 dest_id, struct kvm_lapic ***cluster, u16 *mask) {
switch (map->mode) {
switch (map->logical_mode) {
case KVM_APIC_MODE_SW_DISABLED:
/* Arbitrarily use the flat map so that @cluster isn't NULL. */
*cluster = map->xapic_flat_map;
*mask = 0;
return true;
case KVM_APIC_MODE_X2APIC: {
u32 offset = (dest_id >> 16) * 16;
u32 max_apic_id = map->max_apic_id;
Expand All @@ -193,8 +198,10 @@ static inline bool kvm_apic_map_get_logical_dest(struct kvm_apic_map *map,
*cluster = map->xapic_cluster_map[(dest_id >> 4) & 0xf];
*mask = dest_id & 0xf;
return true;
case KVM_APIC_MODE_MAP_DISABLED:
return false;
default:
/* Not optimized. */
WARN_ON_ONCE(1);
return false;
}
}
Expand Down Expand Up @@ -256,10 +263,12 @@ void kvm_recalculate_apic_map(struct kvm *kvm)
goto out;

new->max_apic_id = max_id;
new->logical_mode = KVM_APIC_MODE_SW_DISABLED;

kvm_for_each_vcpu(i, vcpu, kvm) {
struct kvm_lapic *apic = vcpu->arch.apic;
struct kvm_lapic **cluster;
enum kvm_apic_logical_mode logical_mode;
u16 mask;
u32 ldr;
u8 xapic_id;
Expand All @@ -282,25 +291,40 @@ void kvm_recalculate_apic_map(struct kvm *kvm)
if (!apic_x2apic_mode(apic) && !new->phys_map[xapic_id])
new->phys_map[xapic_id] = apic;

if (!kvm_apic_sw_enabled(apic))
if (new->logical_mode == KVM_APIC_MODE_MAP_DISABLED ||
!kvm_apic_sw_enabled(apic))
continue;

ldr = kvm_lapic_get_reg(apic, APIC_LDR);
if (!ldr)
continue;

if (apic_x2apic_mode(apic)) {
new->mode |= KVM_APIC_MODE_X2APIC;
logical_mode = KVM_APIC_MODE_X2APIC;
} else {
ldr = GET_APIC_LOGICAL_ID(ldr);
if (kvm_lapic_get_reg(apic, APIC_DFR) == APIC_DFR_FLAT)
new->mode |= KVM_APIC_MODE_XAPIC_FLAT;
logical_mode = KVM_APIC_MODE_XAPIC_FLAT;
else
new->mode |= KVM_APIC_MODE_XAPIC_CLUSTER;
logical_mode = KVM_APIC_MODE_XAPIC_CLUSTER;
}

/*
* To optimize logical mode delivery, all software-enabled APICs must
* be configured for the same mode.
*/
if (new->logical_mode == KVM_APIC_MODE_SW_DISABLED) {
new->logical_mode = logical_mode;
} else if (new->logical_mode != logical_mode) {
new->logical_mode = KVM_APIC_MODE_MAP_DISABLED;
continue;
}

if (!kvm_apic_map_get_logical_dest(new, ldr, &cluster, &mask))
if (WARN_ON_ONCE(!kvm_apic_map_get_logical_dest(new, ldr,
&cluster, &mask))) {
new->logical_mode = KVM_APIC_MODE_MAP_DISABLED;
continue;
}

if (mask)
cluster[ffs(mask) - 1] = apic;
Expand Down Expand Up @@ -953,7 +977,7 @@ static bool kvm_apic_is_broadcast_dest(struct kvm *kvm, struct kvm_lapic **src,
{
if (kvm->arch.x2apic_broadcast_quirk_disabled) {
if ((irq->dest_id == APIC_BROADCAST &&
map->mode != KVM_APIC_MODE_X2APIC))
map->logical_mode != KVM_APIC_MODE_X2APIC))
return true;
if (irq->dest_id == X2APIC_BROADCAST)
return true;
Expand Down

0 comments on commit 3536690

Please sign in to comment.