Skip to content

Commit 44d5271

Browse files
committed
KVM: LAPIC: ensure APIC map is up to date on concurrent update requests
The following race can cause lost map update events: cpu1 cpu2 apic_map_dirty = true ------------------------------------------------------------ kvm_recalculate_apic_map: pass check mutex_lock(&kvm->arch.apic_map_lock); if (!kvm->arch.apic_map_dirty) and in process of updating map ------------------------------------------------------------- other calls to apic_map_dirty = true might be too late for affected cpu ------------------------------------------------------------- apic_map_dirty = false ------------------------------------------------------------- kvm_recalculate_apic_map: bail out on if (!kvm->arch.apic_map_dirty) To fix it, record the beginning of an update of the APIC map in apic_map_dirty. If another APIC map change switches apic_map_dirty back to DIRTY during the update, kvm_recalculate_apic_map should not make it CLEAN, and the other caller will go through the slow path. Reported-by: Igor Mammedov <[email protected]> Signed-off-by: Paolo Bonzini <[email protected]>
1 parent af28dfa commit 44d5271

File tree

2 files changed

+32
-21
lines changed

2 files changed

+32
-21
lines changed

arch/x86/include/asm/kvm_host.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -943,7 +943,7 @@ struct kvm_arch {
943943
atomic_t vapics_in_nmi_mode;
944944
struct mutex apic_map_lock;
945945
struct kvm_apic_map *apic_map;
946-
bool apic_map_dirty;
946+
atomic_t apic_map_dirty;
947947

948948
bool apic_access_page_done;
949949
unsigned long apicv_inhibit_reasons;

arch/x86/kvm/lapic.c

Lines changed: 31 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -169,24 +169,36 @@ static void kvm_apic_map_free(struct rcu_head *rcu)
169169
kvfree(map);
170170
}
171171

172+
/*
173+
* CLEAN -> DIRTY and UPDATE_IN_PROGRESS -> DIRTY changes happen without a lock.
174+
*
175+
* DIRTY -> UPDATE_IN_PROGRESS and UPDATE_IN_PROGRESS -> CLEAN happen with
176+
* apic_map_lock_held.
177+
*/
178+
enum {
179+
CLEAN,
180+
UPDATE_IN_PROGRESS,
181+
DIRTY
182+
};
183+
172184
void kvm_recalculate_apic_map(struct kvm *kvm)
173185
{
174186
struct kvm_apic_map *new, *old = NULL;
175187
struct kvm_vcpu *vcpu;
176188
int i;
177189
u32 max_id = 255; /* enough space for any xAPIC ID */
178190

179-
if (!kvm->arch.apic_map_dirty) {
180-
/*
181-
* Read kvm->arch.apic_map_dirty before
182-
* kvm->arch.apic_map
183-
*/
184-
smp_rmb();
191+
/* Read kvm->arch.apic_map_dirty before kvm->arch.apic_map. */
192+
if (atomic_read_acquire(&kvm->arch.apic_map_dirty) == CLEAN)
185193
return;
186-
}
187194

188195
mutex_lock(&kvm->arch.apic_map_lock);
189-
if (!kvm->arch.apic_map_dirty) {
196+
/*
197+
* Read kvm->arch.apic_map_dirty before kvm->arch.apic_map
198+
* (if clean) or the APIC registers (if dirty).
199+
*/
200+
if (atomic_cmpxchg_acquire(&kvm->arch.apic_map_dirty,
201+
DIRTY, UPDATE_IN_PROGRESS) == CLEAN) {
190202
/* Someone else has updated the map. */
191203
mutex_unlock(&kvm->arch.apic_map_lock);
192204
return;
@@ -256,11 +268,11 @@ void kvm_recalculate_apic_map(struct kvm *kvm)
256268
lockdep_is_held(&kvm->arch.apic_map_lock));
257269
rcu_assign_pointer(kvm->arch.apic_map, new);
258270
/*
259-
* Write kvm->arch.apic_map before
260-
* clearing apic->apic_map_dirty
271+
* Write kvm->arch.apic_map before clearing apic->apic_map_dirty.
272+
* If another update has come in, leave it DIRTY.
261273
*/
262-
smp_wmb();
263-
kvm->arch.apic_map_dirty = false;
274+
atomic_cmpxchg_release(&kvm->arch.apic_map_dirty,
275+
UPDATE_IN_PROGRESS, CLEAN);
264276
mutex_unlock(&kvm->arch.apic_map_lock);
265277

266278
if (old)
@@ -282,20 +294,20 @@ static inline void apic_set_spiv(struct kvm_lapic *apic, u32 val)
282294
else
283295
static_key_slow_inc(&apic_sw_disabled.key);
284296

285-
apic->vcpu->kvm->arch.apic_map_dirty = true;
297+
atomic_set_release(&apic->vcpu->kvm->arch.apic_map_dirty, DIRTY);
286298
}
287299
}
288300

289301
static inline void kvm_apic_set_xapic_id(struct kvm_lapic *apic, u8 id)
290302
{
291303
kvm_lapic_set_reg(apic, APIC_ID, id << 24);
292-
apic->vcpu->kvm->arch.apic_map_dirty = true;
304+
atomic_set_release(&apic->vcpu->kvm->arch.apic_map_dirty, DIRTY);
293305
}
294306

295307
static inline void kvm_apic_set_ldr(struct kvm_lapic *apic, u32 id)
296308
{
297309
kvm_lapic_set_reg(apic, APIC_LDR, id);
298-
apic->vcpu->kvm->arch.apic_map_dirty = true;
310+
atomic_set_release(&apic->vcpu->kvm->arch.apic_map_dirty, DIRTY);
299311
}
300312

301313
static inline u32 kvm_apic_calc_x2apic_ldr(u32 id)
@@ -311,7 +323,7 @@ static inline void kvm_apic_set_x2apic_id(struct kvm_lapic *apic, u32 id)
311323

312324
kvm_lapic_set_reg(apic, APIC_ID, id);
313325
kvm_lapic_set_reg(apic, APIC_LDR, ldr);
314-
apic->vcpu->kvm->arch.apic_map_dirty = true;
326+
atomic_set_release(&apic->vcpu->kvm->arch.apic_map_dirty, DIRTY);
315327
}
316328

317329
static inline int apic_lvt_enabled(struct kvm_lapic *apic, int lvt_type)
@@ -1976,7 +1988,7 @@ int kvm_lapic_reg_write(struct kvm_lapic *apic, u32 reg, u32 val)
19761988
case APIC_DFR:
19771989
if (!apic_x2apic_mode(apic)) {
19781990
kvm_lapic_set_reg(apic, APIC_DFR, val | 0x0FFFFFFF);
1979-
apic->vcpu->kvm->arch.apic_map_dirty = true;
1991+
atomic_set_release(&apic->vcpu->kvm->arch.apic_map_dirty, DIRTY);
19801992
} else
19811993
ret = 1;
19821994
break;
@@ -2232,7 +2244,7 @@ void kvm_lapic_set_base(struct kvm_vcpu *vcpu, u64 value)
22322244
static_key_slow_dec_deferred(&apic_hw_disabled);
22332245
} else {
22342246
static_key_slow_inc(&apic_hw_disabled.key);
2235-
vcpu->kvm->arch.apic_map_dirty = true;
2247+
atomic_set_release(&apic->vcpu->kvm->arch.apic_map_dirty, DIRTY);
22362248
}
22372249
}
22382250

@@ -2273,7 +2285,6 @@ void kvm_lapic_reset(struct kvm_vcpu *vcpu, bool init_event)
22732285
if (!apic)
22742286
return;
22752287

2276-
vcpu->kvm->arch.apic_map_dirty = false;
22772288
/* Stop the timer in case it's a reset to an active apic */
22782289
hrtimer_cancel(&apic->lapic_timer.timer);
22792290

@@ -2567,7 +2578,7 @@ int kvm_apic_set_state(struct kvm_vcpu *vcpu, struct kvm_lapic_state *s)
25672578
}
25682579
memcpy(vcpu->arch.apic->regs, s->regs, sizeof(*s));
25692580

2570-
apic->vcpu->kvm->arch.apic_map_dirty = true;
2581+
atomic_set_release(&apic->vcpu->kvm->arch.apic_map_dirty, DIRTY);
25712582
kvm_recalculate_apic_map(vcpu->kvm);
25722583
kvm_apic_set_version(vcpu);
25732584

0 commit comments

Comments
 (0)