Simplify bhyve vlapic ESR logic.
The bhyve virtual local APIC uses an instance-global flag to indicate when an error LVT is being delivered to prevent infinite recursion. Use a function argument instead to reduce the amount of instance-global state. This was inspired by reviewing the bhyve save/restore work, which saves a copy of the instance-global state for each vlapic. Smart OS bug: https://smartos.org/bugview/OS-7777 Submitted by: Patrick Mooney Reviewed by: markj, rgrimes Obtained from: SmartOS / Joyent Differential Revision: https://reviews.freebsd.org/D20365
This commit is contained in:
parent
755c53463b
commit
6abfa282d2
@ -3,6 +3,7 @@
|
||||
*
|
||||
* Copyright (c) 2011 NetApp, Inc.
|
||||
* All rights reserved.
|
||||
* Copyright (c) 2019 Joyent, Inc.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
@ -78,6 +79,8 @@ __FBSDID("$FreeBSD$");
|
||||
*/
|
||||
#define VLAPIC_BUS_FREQ (128 * 1024 * 1024)
|
||||
|
||||
static void vlapic_set_error(struct vlapic *, uint32_t, bool);
|
||||
|
||||
static __inline uint32_t
|
||||
vlapic_get_id(struct vlapic *vlapic)
|
||||
{
|
||||
@ -275,7 +278,8 @@ vlapic_set_intr_ready(struct vlapic *vlapic, int vector, bool level)
|
||||
}
|
||||
|
||||
if (vector < 16) {
|
||||
vlapic_set_error(vlapic, APIC_ESR_RECEIVE_ILLEGAL_VECTOR);
|
||||
vlapic_set_error(vlapic, APIC_ESR_RECEIVE_ILLEGAL_VECTOR,
|
||||
false);
|
||||
VLAPIC_CTR1(vlapic, "vlapic ignoring interrupt to vector %d",
|
||||
vector);
|
||||
return (1);
|
||||
@ -432,20 +436,22 @@ vlapic_mask_lvts(struct vlapic *vlapic)
|
||||
}
|
||||
|
||||
static int
|
||||
vlapic_fire_lvt(struct vlapic *vlapic, uint32_t lvt)
|
||||
vlapic_fire_lvt(struct vlapic *vlapic, u_int lvt)
|
||||
{
|
||||
uint32_t vec, mode;
|
||||
uint32_t mode, reg, vec;
|
||||
|
||||
if (lvt & APIC_LVT_M)
|
||||
reg = atomic_load_acq_32(&vlapic->lvt_last[lvt]);
|
||||
|
||||
if (reg & APIC_LVT_M)
|
||||
return (0);
|
||||
|
||||
vec = lvt & APIC_LVT_VECTOR;
|
||||
mode = lvt & APIC_LVT_DM;
|
||||
vec = reg & APIC_LVT_VECTOR;
|
||||
mode = reg & APIC_LVT_DM;
|
||||
|
||||
switch (mode) {
|
||||
case APIC_LVT_DM_FIXED:
|
||||
if (vec < 16) {
|
||||
vlapic_set_error(vlapic, APIC_ESR_SEND_ILLEGAL_VECTOR);
|
||||
vlapic_set_error(vlapic, APIC_ESR_SEND_ILLEGAL_VECTOR,
|
||||
lvt == APIC_LVT_ERROR);
|
||||
return (0);
|
||||
}
|
||||
if (vlapic_set_intr_ready(vlapic, vec, false))
|
||||
@ -606,22 +612,22 @@ vlapic_periodic_timer(struct vlapic *vlapic)
|
||||
|
||||
static VMM_STAT(VLAPIC_INTR_ERROR, "error interrupts generated by vlapic");
|
||||
|
||||
void
|
||||
vlapic_set_error(struct vlapic *vlapic, uint32_t mask)
|
||||
static void
|
||||
vlapic_set_error(struct vlapic *vlapic, uint32_t mask, bool lvt_error)
|
||||
{
|
||||
uint32_t lvt;
|
||||
|
||||
vlapic->esr_pending |= mask;
|
||||
if (vlapic->esr_firing)
|
||||
return;
|
||||
vlapic->esr_firing = 1;
|
||||
|
||||
// The error LVT always uses the fixed delivery mode.
|
||||
lvt = vlapic_get_lvt(vlapic, APIC_OFFSET_ERROR_LVT);
|
||||
if (vlapic_fire_lvt(vlapic, lvt | APIC_LVT_DM_FIXED)) {
|
||||
/*
|
||||
* Avoid infinite recursion if the error LVT itself is configured with
|
||||
* an illegal vector.
|
||||
*/
|
||||
if (lvt_error)
|
||||
return;
|
||||
|
||||
if (vlapic_fire_lvt(vlapic, APIC_LVT_ERROR)) {
|
||||
vmm_stat_incr(vlapic->vm, vlapic->vcpuid, VLAPIC_INTR_ERROR, 1);
|
||||
}
|
||||
vlapic->esr_firing = 0;
|
||||
}
|
||||
|
||||
static VMM_STAT(VLAPIC_INTR_TIMER, "timer interrupts generated by vlapic");
|
||||
@ -629,13 +635,10 @@ static VMM_STAT(VLAPIC_INTR_TIMER, "timer interrupts generated by vlapic");
|
||||
static void
|
||||
vlapic_fire_timer(struct vlapic *vlapic)
|
||||
{
|
||||
uint32_t lvt;
|
||||
|
||||
KASSERT(VLAPIC_TIMER_LOCKED(vlapic), ("vlapic_fire_timer not locked"));
|
||||
|
||||
// The timer LVT always uses the fixed delivery mode.
|
||||
lvt = vlapic_get_lvt(vlapic, APIC_OFFSET_TIMER_LVT);
|
||||
if (vlapic_fire_lvt(vlapic, lvt | APIC_LVT_DM_FIXED)) {
|
||||
|
||||
if (vlapic_fire_lvt(vlapic, APIC_LVT_TIMER)) {
|
||||
VLAPIC_CTR0(vlapic, "vlapic timer fired");
|
||||
vmm_stat_incr(vlapic->vm, vlapic->vcpuid, VLAPIC_INTR_TIMER, 1);
|
||||
}
|
||||
@ -647,10 +650,8 @@ static VMM_STAT(VLAPIC_INTR_CMC,
|
||||
void
|
||||
vlapic_fire_cmci(struct vlapic *vlapic)
|
||||
{
|
||||
uint32_t lvt;
|
||||
|
||||
lvt = vlapic_get_lvt(vlapic, APIC_OFFSET_CMCI_LVT);
|
||||
if (vlapic_fire_lvt(vlapic, lvt)) {
|
||||
if (vlapic_fire_lvt(vlapic, APIC_LVT_CMCI)) {
|
||||
vmm_stat_incr(vlapic->vm, vlapic->vcpuid, VLAPIC_INTR_CMC, 1);
|
||||
}
|
||||
}
|
||||
@ -661,7 +662,6 @@ static VMM_STAT_ARRAY(LVTS_TRIGGERRED, VLAPIC_MAXLVT_INDEX + 1,
|
||||
int
|
||||
vlapic_trigger_lvt(struct vlapic *vlapic, int vector)
|
||||
{
|
||||
uint32_t lvt;
|
||||
|
||||
if (vlapic_enabled(vlapic) == false) {
|
||||
/*
|
||||
@ -684,35 +684,20 @@ vlapic_trigger_lvt(struct vlapic *vlapic, int vector)
|
||||
|
||||
switch (vector) {
|
||||
case APIC_LVT_LINT0:
|
||||
lvt = vlapic_get_lvt(vlapic, APIC_OFFSET_LINT0_LVT);
|
||||
break;
|
||||
case APIC_LVT_LINT1:
|
||||
lvt = vlapic_get_lvt(vlapic, APIC_OFFSET_LINT1_LVT);
|
||||
break;
|
||||
case APIC_LVT_TIMER:
|
||||
lvt = vlapic_get_lvt(vlapic, APIC_OFFSET_TIMER_LVT);
|
||||
lvt |= APIC_LVT_DM_FIXED;
|
||||
break;
|
||||
case APIC_LVT_ERROR:
|
||||
lvt = vlapic_get_lvt(vlapic, APIC_OFFSET_ERROR_LVT);
|
||||
lvt |= APIC_LVT_DM_FIXED;
|
||||
break;
|
||||
case APIC_LVT_PMC:
|
||||
lvt = vlapic_get_lvt(vlapic, APIC_OFFSET_PERF_LVT);
|
||||
break;
|
||||
case APIC_LVT_THERMAL:
|
||||
lvt = vlapic_get_lvt(vlapic, APIC_OFFSET_THERM_LVT);
|
||||
break;
|
||||
case APIC_LVT_CMCI:
|
||||
lvt = vlapic_get_lvt(vlapic, APIC_OFFSET_CMCI_LVT);
|
||||
if (vlapic_fire_lvt(vlapic, vector)) {
|
||||
vmm_stat_array_incr(vlapic->vm, vlapic->vcpuid,
|
||||
LVTS_TRIGGERRED, vector, 1);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return (EINVAL);
|
||||
}
|
||||
if (vlapic_fire_lvt(vlapic, lvt)) {
|
||||
vmm_stat_array_incr(vlapic->vm, vlapic->vcpuid,
|
||||
LVTS_TRIGGERRED, vector, 1);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
@ -980,7 +965,7 @@ vlapic_icrlo_write_handler(struct vlapic *vlapic, bool *retu)
|
||||
mode = icrval & APIC_DELMODE_MASK;
|
||||
|
||||
if (mode == APIC_DELMODE_FIXED && vec < 16) {
|
||||
vlapic_set_error(vlapic, APIC_ESR_SEND_ILLEGAL_VECTOR);
|
||||
vlapic_set_error(vlapic, APIC_ESR_SEND_ILLEGAL_VECTOR, false);
|
||||
VLAPIC_CTR1(vlapic, "Ignoring invalid IPI %d", vec);
|
||||
return (0);
|
||||
}
|
||||
|
@ -71,7 +71,6 @@ int vlapic_set_intr_ready(struct vlapic *vlapic, int vector, bool level);
|
||||
*/
|
||||
void vlapic_post_intr(struct vlapic *vlapic, int hostcpu, int ipinum);
|
||||
|
||||
void vlapic_set_error(struct vlapic *vlapic, uint32_t mask);
|
||||
void vlapic_fire_cmci(struct vlapic *vlapic);
|
||||
int vlapic_trigger_lvt(struct vlapic *vlapic, int vector);
|
||||
|
||||
|
@ -156,7 +156,6 @@ struct vlapic {
|
||||
struct vlapic_ops ops;
|
||||
|
||||
uint32_t esr_pending;
|
||||
int esr_firing;
|
||||
|
||||
struct callout callout; /* vlapic timer */
|
||||
struct bintime timer_fire_bt; /* callout expiry time */
|
||||
|
Loading…
x
Reference in New Issue
Block a user