From 6a1e1c2c48a83be56acbee25f18f5f4fb18d4b43 Mon Sep 17 00:00:00 2001 From: John Baldwin Date: Thu, 29 Aug 2019 18:23:38 +0000 Subject: [PATCH] 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 --- sys/amd64/vmm/io/vlapic.c | 79 ++++++++++++++-------------------- sys/amd64/vmm/io/vlapic.h | 1 - sys/amd64/vmm/io/vlapic_priv.h | 1 - 3 files changed, 32 insertions(+), 49 deletions(-) diff --git a/sys/amd64/vmm/io/vlapic.c b/sys/amd64/vmm/io/vlapic.c index b3aa824c955d..74e6cd967396 100644 --- a/sys/amd64/vmm/io/vlapic.c +++ b/sys/amd64/vmm/io/vlapic.c @@ -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); } diff --git a/sys/amd64/vmm/io/vlapic.h b/sys/amd64/vmm/io/vlapic.h index b2d5db7318f7..2a5f54003253 100644 --- a/sys/amd64/vmm/io/vlapic.h +++ b/sys/amd64/vmm/io/vlapic.h @@ -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); diff --git a/sys/amd64/vmm/io/vlapic_priv.h b/sys/amd64/vmm/io/vlapic_priv.h index 43abd20a0a90..fe7965cb65d7 100644 --- a/sys/amd64/vmm/io/vlapic_priv.h +++ b/sys/amd64/vmm/io/vlapic_priv.h @@ -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 */