Skip to content

Commit 4abaffc

Browse files
Wanpeng Libonzini
authored andcommitted
KVM: LAPIC: Recalculate apic map in batch
In the vCPU reset and set APIC_BASE MSR path, the apic map will be recalculated several times, each time it will consume 10+ us observed by ftrace in my non-overcommit environment since the expensive memory allocate/mutex/rcu etc operations. This patch optimizes it by recaluating apic map in batch, I hope this can benefit the serverless scenario which can frequently create/destroy VMs. Before patch: kvm_lapic_reset ~27us After patch: kvm_lapic_reset ~14us Observed by ftrace, improve ~48%. Signed-off-by: Wanpeng Li <[email protected]> Signed-off-by: Paolo Bonzini <[email protected]>
1 parent 49f933d commit 4abaffc

File tree

4 files changed

+39
-10
lines changed

4 files changed

+39
-10
lines changed

arch/x86/include/asm/kvm_host.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -920,6 +920,7 @@ struct kvm_arch {
920920
atomic_t vapics_in_nmi_mode;
921921
struct mutex apic_map_lock;
922922
struct kvm_apic_map *apic_map;
923+
bool apic_map_dirty;
923924

924925
bool apic_access_page_done;
925926
unsigned long apicv_inhibit_reasons;

arch/x86/kvm/lapic.c

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -164,14 +164,28 @@ static void kvm_apic_map_free(struct rcu_head *rcu)
164164
kvfree(map);
165165
}
166166

167-
static void recalculate_apic_map(struct kvm *kvm)
167+
void kvm_recalculate_apic_map(struct kvm *kvm)
168168
{
169169
struct kvm_apic_map *new, *old = NULL;
170170
struct kvm_vcpu *vcpu;
171171
int i;
172172
u32 max_id = 255; /* enough space for any xAPIC ID */
173173

174+
if (!kvm->arch.apic_map_dirty) {
175+
/*
176+
* Read kvm->arch.apic_map_dirty before
177+
* kvm->arch.apic_map
178+
*/
179+
smp_rmb();
180+
return;
181+
}
182+
174183
mutex_lock(&kvm->arch.apic_map_lock);
184+
if (!kvm->arch.apic_map_dirty) {
185+
/* Someone else has updated the map. */
186+
mutex_unlock(&kvm->arch.apic_map_lock);
187+
return;
188+
}
175189

176190
kvm_for_each_vcpu(i, vcpu, kvm)
177191
if (kvm_apic_present(vcpu))
@@ -236,6 +250,12 @@ static void recalculate_apic_map(struct kvm *kvm)
236250
old = rcu_dereference_protected(kvm->arch.apic_map,
237251
lockdep_is_held(&kvm->arch.apic_map_lock));
238252
rcu_assign_pointer(kvm->arch.apic_map, new);
253+
/*
254+
* Write kvm->arch.apic_map before
255+
* clearing apic->apic_map_dirty
256+
*/
257+
smp_wmb();
258+
kvm->arch.apic_map_dirty = false;
239259
mutex_unlock(&kvm->arch.apic_map_lock);
240260

241261
if (old)
@@ -257,20 +277,20 @@ static inline void apic_set_spiv(struct kvm_lapic *apic, u32 val)
257277
else
258278
static_key_slow_inc(&apic_sw_disabled.key);
259279

260-
recalculate_apic_map(apic->vcpu->kvm);
280+
apic->vcpu->kvm->arch.apic_map_dirty = true;
261281
}
262282
}
263283

264284
static inline void kvm_apic_set_xapic_id(struct kvm_lapic *apic, u8 id)
265285
{
266286
kvm_lapic_set_reg(apic, APIC_ID, id << 24);
267-
recalculate_apic_map(apic->vcpu->kvm);
287+
apic->vcpu->kvm->arch.apic_map_dirty = true;
268288
}
269289

270290
static inline void kvm_apic_set_ldr(struct kvm_lapic *apic, u32 id)
271291
{
272292
kvm_lapic_set_reg(apic, APIC_LDR, id);
273-
recalculate_apic_map(apic->vcpu->kvm);
293+
apic->vcpu->kvm->arch.apic_map_dirty = true;
274294
}
275295

276296
static inline u32 kvm_apic_calc_x2apic_ldr(u32 id)
@@ -286,7 +306,7 @@ static inline void kvm_apic_set_x2apic_id(struct kvm_lapic *apic, u32 id)
286306

287307
kvm_lapic_set_reg(apic, APIC_ID, id);
288308
kvm_lapic_set_reg(apic, APIC_LDR, ldr);
289-
recalculate_apic_map(apic->vcpu->kvm);
309+
apic->vcpu->kvm->arch.apic_map_dirty = true;
290310
}
291311

292312
static inline int apic_lvt_enabled(struct kvm_lapic *apic, int lvt_type)
@@ -1906,7 +1926,7 @@ int kvm_lapic_reg_write(struct kvm_lapic *apic, u32 reg, u32 val)
19061926
case APIC_DFR:
19071927
if (!apic_x2apic_mode(apic)) {
19081928
kvm_lapic_set_reg(apic, APIC_DFR, val | 0x0FFFFFFF);
1909-
recalculate_apic_map(apic->vcpu->kvm);
1929+
apic->vcpu->kvm->arch.apic_map_dirty = true;
19101930
} else
19111931
ret = 1;
19121932
break;
@@ -2012,6 +2032,8 @@ int kvm_lapic_reg_write(struct kvm_lapic *apic, u32 reg, u32 val)
20122032
break;
20132033
}
20142034

2035+
kvm_recalculate_apic_map(apic->vcpu->kvm);
2036+
20152037
return ret;
20162038
}
20172039
EXPORT_SYMBOL_GPL(kvm_lapic_reg_write);
@@ -2160,7 +2182,7 @@ void kvm_lapic_set_base(struct kvm_vcpu *vcpu, u64 value)
21602182
static_key_slow_dec_deferred(&apic_hw_disabled);
21612183
} else {
21622184
static_key_slow_inc(&apic_hw_disabled.key);
2163-
recalculate_apic_map(vcpu->kvm);
2185+
vcpu->kvm->arch.apic_map_dirty = true;
21642186
}
21652187
}
21662188

@@ -2201,6 +2223,7 @@ void kvm_lapic_reset(struct kvm_vcpu *vcpu, bool init_event)
22012223
if (!apic)
22022224
return;
22032225

2226+
vcpu->kvm->arch.apic_map_dirty = false;
22042227
/* Stop the timer in case it's a reset to an active apic */
22052228
hrtimer_cancel(&apic->lapic_timer.timer);
22062229

@@ -2252,6 +2275,8 @@ void kvm_lapic_reset(struct kvm_vcpu *vcpu, bool init_event)
22522275

22532276
vcpu->arch.apic_arb_prio = 0;
22542277
vcpu->arch.apic_attention = 0;
2278+
2279+
kvm_recalculate_apic_map(vcpu->kvm);
22552280
}
22562281

22572282
/*
@@ -2473,17 +2498,18 @@ int kvm_apic_set_state(struct kvm_vcpu *vcpu, struct kvm_lapic_state *s)
24732498
struct kvm_lapic *apic = vcpu->arch.apic;
24742499
int r;
24752500

2476-
24772501
kvm_lapic_set_base(vcpu, vcpu->arch.apic_base);
24782502
/* set SPIV separately to get count of SW disabled APICs right */
24792503
apic_set_spiv(apic, *((u32 *)(s->regs + APIC_SPIV)));
24802504

24812505
r = kvm_apic_state_fixup(vcpu, s, true);
2482-
if (r)
2506+
if (r) {
2507+
kvm_recalculate_apic_map(vcpu->kvm);
24832508
return r;
2509+
}
24842510
memcpy(vcpu->arch.apic->regs, s->regs, sizeof(*s));
24852511

2486-
recalculate_apic_map(vcpu->kvm);
2512+
kvm_recalculate_apic_map(vcpu->kvm);
24872513
kvm_apic_set_version(vcpu);
24882514

24892515
apic_update_ppr(apic);

arch/x86/kvm/lapic.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ void kvm_lapic_set_tpr(struct kvm_vcpu *vcpu, unsigned long cr8);
7878
void kvm_lapic_set_eoi(struct kvm_vcpu *vcpu);
7979
void kvm_lapic_set_base(struct kvm_vcpu *vcpu, u64 value);
8080
u64 kvm_lapic_get_base(struct kvm_vcpu *vcpu);
81+
void kvm_recalculate_apic_map(struct kvm *kvm);
8182
void kvm_apic_set_version(struct kvm_vcpu *vcpu);
8283
int kvm_lapic_reg_write(struct kvm_lapic *apic, u32 reg, u32 val);
8384
int kvm_lapic_reg_read(struct kvm_lapic *apic, u32 offset, int len,

arch/x86/kvm/x86.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,7 @@ int kvm_set_apic_base(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
350350
}
351351

352352
kvm_lapic_set_base(vcpu, msr_info->data);
353+
kvm_recalculate_apic_map(vcpu->kvm);
353354
return 0;
354355
}
355356
EXPORT_SYMBOL_GPL(kvm_set_apic_base);

0 commit comments

Comments
 (0)