hv: vlapic_timer: refine vlapic tscdeadline timer

Add vlapic_create_timer/vlapic_reset_timer to setup/reset a timer.
Add vlapic_update_lvtt to disarm timer when mode changes.

Signed-off-by: Li, Fei1 <fei1.li@intel.com>
Acked-by: Eddie Dong <eddie.dong@intel.com>
This commit is contained in:
Li, Fei1 2018-05-04 10:22:51 +08:00 committed by Jack Ren
parent ea54216116
commit 9dd7d27737
4 changed files with 132 additions and 44 deletions

View File

@ -107,6 +107,8 @@ apicv_batch_set_tmr(struct vlapic *vlapic);
*/
static void vlapic_set_error(struct vlapic *vlapic, uint32_t mask);
static int vlapic_timer_expired(void *data);
static struct vlapic *
vm_lapic_from_vcpu_id(struct vm *vm, int vcpu_id)
{
@ -255,6 +257,58 @@ vlapic_lvtt_masked(struct vlapic *vlapic)
return !!(vlapic->apic_page->lvt_timer & APIC_LVTT_M);
}
static void vlapic_create_timer(struct vlapic *vlapic)
{
struct vlapic_timer *vlapic_timer;
if (vlapic == NULL)
return;
vlapic_timer = &vlapic->vlapic_timer;
memset(vlapic_timer, 0, sizeof(struct vlapic_timer));
initialize_timer(&vlapic_timer->timer,
vlapic_timer_expired, vlapic->vcpu,
0, 0, 0);
}
static void vlapic_reset_timer(struct vlapic *vlapic)
{
struct timer *timer;
if (vlapic == NULL)
return;
timer = &vlapic->vlapic_timer.timer;
del_timer(timer);
timer->mode = 0;
timer->fire_tsc = 0;
timer->period_in_cycle = 0;
}
static void vlapic_update_lvtt(struct vlapic *vlapic,
uint32_t val)
{
uint32_t timer_mode = val & APIC_LVTT_TM;
struct vlapic_timer *vlapic_timer = &vlapic->vlapic_timer;
if (vlapic_timer->mode != timer_mode) {
struct timer *timer = &vlapic_timer->timer;
/*
* A write to the LVT Timer Register that changes
* the timer mode disarms the local APIC timer.
*/
del_timer(timer);
timer->mode = (timer_mode == APIC_LVTT_TM_PERIODIC) ?
TICK_MODE_PERIODIC: TICK_MODE_ONESHOT;
timer->fire_tsc = 0;
timer->period_in_cycle = 0;
vlapic_timer->mode = timer_mode;
}
}
static uint32_t vlapic_get_ccr(__unused struct vlapic *vlapic)
{
return 0;
@ -264,6 +318,46 @@ static void vlapic_dcr_write_handler(__unused struct vlapic *vlapic)
{
}
static void vlapic_icrtmr_write_handler(__unused struct vlapic *vlapic)
{
}
static uint64_t vlapic_get_tsc_deadline_msr(struct vlapic *vlapic)
{
if (!vlapic_lvtt_tsc_deadline(vlapic))
return 0;
return (vlapic->vlapic_timer.timer.fire_tsc == 0) ? 0 :
vlapic->vcpu->guest_msrs[IDX_TSC_DEADLINE];
}
static void vlapic_set_tsc_deadline_msr(struct vlapic *vlapic,
uint64_t val)
{
struct timer *timer;
if (!vlapic_lvtt_tsc_deadline(vlapic))
return;
vlapic->vcpu->guest_msrs[IDX_TSC_DEADLINE] = val;
timer = &vlapic->vlapic_timer.timer;
del_timer(timer);
if (val != 0UL) {
struct vcpu_arch *arch = &vlapic->vcpu->arch_vcpu;
/* transfer guest tsc to host tsc */
val -= arch->contexts[arch->cur_context].tsc_offset;
timer->fire_tsc = val;
add_timer(timer);
} else
timer->fire_tsc = 0;
}
static void
vlapic_esr_write_handler(struct vlapic *vlapic)
{
@ -451,7 +545,9 @@ vlapic_lvt_write_handler(struct vlapic *vlapic, uint32_t offset)
"vpic wire mode -> NULL");
}
}
}
} else if (offset == APIC_OFFSET_TIMER_LVT)
vlapic_update_lvtt(vlapic, val);
*lvtptr = val;
atomic_store_rel_32(&vlapic->lvt_last[idx], val);
}
@ -703,10 +799,6 @@ vlapic_trigger_lvt(struct vlapic *vlapic, int vector)
return 0;
}
static void vlapic_icrtmr_write_handler(__unused struct vlapic *vlapic)
{
}
/*
* This function populates 'dmask' with the set of vcpus that match the
* addressing specified by the (dest, phys, lowprio) tuple.
@ -1332,12 +1424,11 @@ vlapic_reset(struct vlapic *vlapic)
vlapic_mask_lvts(vlapic);
vlapic_reset_tmr(vlapic);
lapic->icr_timer = 0;
lapic->dcr_timer = 0;
vlapic_dcr_write_handler(vlapic);
vlapic_reset_timer(vlapic);
vlapic->svr_last = lapic->svr;
initialize_timer(&vlapic->timer, NULL, NULL, 0, 0, 0);
}
void
@ -1359,6 +1450,8 @@ vlapic_init(struct vlapic *vlapic)
if (vlapic->vcpu->vcpu_id == 0)
vlapic->msr_apicbase |= APICBASE_BSP;
vlapic_create_timer(vlapic);
vlapic_reset(vlapic);
}
@ -1655,7 +1748,7 @@ vlapic_msr(uint32_t msr)
}
/* interrupt context */
static int tsc_periodic_time(void *data)
static int vlapic_timer_expired(void *data)
{
struct vcpu *vcpu = (struct vcpu *)data;
struct vlapic *vlapic;
@ -1668,6 +1761,9 @@ static int tsc_periodic_time(void *data)
if (!vlapic_lvtt_masked(vlapic))
vlapic_intr_edge(vcpu, lapic->lvt_timer & APIC_LVTT_VECTOR);
if (!vlapic_lvtt_period(vlapic))
vlapic->vlapic_timer.timer.fire_tsc = 0;
return 0;
}
@ -1686,6 +1782,10 @@ vlapic_rdmsr(struct vcpu *vcpu, uint32_t msr, uint64_t *rval)
*rval = vlapic_get_apicbase(vlapic);
break;
case MSR_IA32_TSC_DEADLINE:
*rval = vlapic_get_tsc_deadline_msr(vlapic);
break;
default:
offset = x2apic_msr_to_regoff(msr);
error = vlapic_read(vlapic, 0, offset, rval);
@ -1698,7 +1798,7 @@ vlapic_rdmsr(struct vcpu *vcpu, uint32_t msr, uint64_t *rval)
int
vlapic_wrmsr(struct vcpu *vcpu, uint32_t msr, uint64_t val)
{
int error;
int error = 0;
uint32_t offset;
struct vlapic *vlapic;
@ -1710,26 +1810,7 @@ vlapic_wrmsr(struct vcpu *vcpu, uint32_t msr, uint64_t val)
break;
case MSR_IA32_TSC_DEADLINE:
error = 0;
if (!vlapic_lvtt_tsc_deadline(vlapic))
return error;
del_timer(&vlapic->timer);
if (val != 0UL) {
/* transfer guest tsc to host tsc */
val -= vcpu->arch_vcpu.contexts[vcpu->
arch_vcpu.cur_context].tsc_offset;
initialize_timer(&vlapic->timer,
tsc_periodic_time, (void *)vcpu,
val, TICK_MODE_ONESHOT, 0);
if (add_timer(&vlapic->timer) != 0) {
pr_err("failed to add timer on VM %d",
vcpu->vm->attr.id);
error = -1;
}
}
vlapic_set_tsc_deadline_msr(vlapic, val);
break;
default:
@ -1881,7 +1962,7 @@ void vlapic_free(struct vcpu *vcpu)
if (vlapic == NULL)
return;
del_timer(&vlapic->timer);
del_timer(&vlapic->vlapic_timer.timer);
if (!is_vapic_supported()) {
unregister_mmio_emulation_handler(vcpu->vm,

View File

@ -108,6 +108,13 @@ struct vlapic_ops {
void (*enable_x2apic_mode)(struct vlapic *vlapic);
};
struct vlapic_timer {
struct timer timer;
uint32_t mode;
uint32_t tmicr;
uint32_t divisor;
};
struct vlapic {
struct vm *vm;
struct vcpu *vcpu;
@ -118,7 +125,7 @@ struct vlapic {
uint32_t esr_pending;
int esr_firing;
struct timer timer;
struct vlapic_timer vlapic_timer;
/*
* The 'isrvec_stk' is a stack of vectors injected by the local apic.

View File

@ -51,16 +51,6 @@ static const uint32_t emulated_msrs[] = {
*/
};
/* the index is matched with emulated msrs array*/
enum {
IDX_TSC_DEADLINE,
IDX_BIOS_UPDT_TRIG,
IDX_BIOS_SIGN_ID,
IDX_TSC,
IDX_MAX_MSR
};
static void enable_msr_interception(uint8_t *bitmap, uint32_t msr)
{
uint8_t *read_map;
@ -185,7 +175,7 @@ int rdmsr_vmexit_handler(struct vcpu *vcpu)
switch (msr) {
case MSR_IA32_TSC_DEADLINE:
{
v = vcpu->guest_msrs[IDX_TSC_DEADLINE];
vlapic_rdmsr(vcpu, msr, &v);
break;
}
case MSR_IA32_TIME_STAMP_COUNTER:
@ -280,7 +270,6 @@ int wrmsr_vmexit_handler(struct vcpu *vcpu)
case MSR_IA32_TSC_DEADLINE:
{
vlapic_wrmsr(vcpu, msr, v);
vcpu->guest_msrs[IDX_TSC_DEADLINE] = v;
break;
}
case MSR_IA32_TIME_STAMP_COUNTER:

View File

@ -45,6 +45,17 @@
(idx < vm->hw.num_vcpus) & (vcpu != NULL); \
idx++, vcpu = vm->hw.vcpu_array[idx])
/* the index is matched with emulated msrs array*/
enum {
IDX_TSC_DEADLINE,
IDX_BIOS_UPDT_TRIG,
IDX_BIOS_SIGN_ID,
IDX_TSC,
IDX_MAX_MSR
};
struct vhm_request;
/*