vioapic: add pin_state bitmap to set irq

a guest may use per-cpu vector allocation mechanism which may lead to
same vector on different vcpu for different vioapic pins.
when we broadcast EOI from vlapic to vioapic, it could trigger
ptdev_intx_ack for incorrect vioapic pin which will lead to incorrect
vioapic irq deassert.

old implementation is recording assert & deassert by acnt, like:
- assert -> acnt++
- deassert -> acnt--
which means an incorrect deassert may descrease acnt from 0 to -1, it leads
to error.

this patch change the recording method for a pin assert/deassert, it
just use pin_state bit set/clear to indicate line state:
- high -> pin_state bitmap set
- low -> pin_state bitmap clear
and the irq assert will be triggered based on polarity setting during line
state changing.

it can avoid the failure of incorrect deassert on old implementation, and for
ptdev sharing irq, the dev hardware could re-trigger interrupt after deassert.
but for emulated device, there is no mechanism to re-trigger interrupt if
there is irq sharing - for DM, we already has constraint to restrict vGSI
sharing, so there is no problem of it.

Tracked-On: #1269
Signed-off-by: Jason Chen CJ <jason.cj.chen@intel.com>
This commit is contained in:
Jason Chen CJ 2018-09-28 15:11:30 +08:00 committed by Xie, Nanlin
parent ba68bd4190
commit 3b88d3c2ac
2 changed files with 75 additions and 62 deletions

View File

@ -42,11 +42,6 @@
#define MASK_ALL_INTERRUPTS 0x0001000000010000UL
#define IOAPIC_RTE_LOW_INTVEC ((uint32_t)IOAPIC_RTE_INTVEC)
static inline const char *pinstate_str(bool asserted)
{
return (asserted) ? "asserted" : "deasserted";
}
/**
* @pre pin < vioapic_pincount(vm)
*/
@ -87,36 +82,67 @@ vioapic_send_intr(struct acrn_vioapic *vioapic, uint32_t pin)
* @pre pin < vioapic_pincount(vm)
*/
static void
vioapic_set_pinstate(struct acrn_vioapic *vioapic, uint32_t pin, bool newstate)
vioapic_set_pinstate(struct acrn_vioapic *vioapic, uint16_t pin, uint32_t level)
{
int oldcnt, newcnt;
bool needintr;
uint32_t old_lvl;
union ioapic_rte rte = vioapic->rtbl[pin];
oldcnt = vioapic->acnt[pin];
if (newstate) {
vioapic->acnt[pin]++;
old_lvl = (uint32_t)bitmap_test(pin & 0x3FU, &vioapic->pin_state[pin >> 6U]);
if (level == 0U) {
/* clear pin_state and deliver interrupt according to polarity */
bitmap_clear_nolock(pin & 0x3FU,
&vioapic->pin_state[pin >> 6U]);
if (((rte.full & IOAPIC_RTE_INTPOL) != 0UL)
&& old_lvl != level) {
vioapic_send_intr(vioapic, pin);
}
} else {
vioapic->acnt[pin]--;
/* set pin_state and deliver intrrupt according to polarity */
bitmap_set_nolock(pin & 0x3FU, &vioapic->pin_state[pin >> 6U]);
if (((rte.full & IOAPIC_RTE_INTPOL) == 0UL)
&& old_lvl != level) {
vioapic_send_intr(vioapic, pin);
}
}
newcnt = vioapic->acnt[pin];
}
if (newcnt < 0) {
pr_err("ioapic pin%hhu: bad acnt %d", pin, newcnt);
}
/**
* @pre irq < vioapic_pincount(vm)
* @pre operation value shall be one of the folllowing values:
* GSI_SET_HIGH
* GSI_SET_LOW
* GSI_RAISING_PULSE
* GSI_FALLING_PULSE
* @pre call with vioapic lock
*/
void
vioapic_set_irq_nolock(struct vm *vm, uint32_t irq, uint32_t operation)
{
struct acrn_vioapic *vioapic;
uint16_t pin = (uint16_t)irq;
needintr = false;
if ((oldcnt == 0) && (newcnt == 1)) {
needintr = true;
dev_dbg(ACRN_DBG_IOAPIC, "ioapic pin%hhu: asserted", pin);
} else if ((oldcnt == 1) && (newcnt == 0)) {
dev_dbg(ACRN_DBG_IOAPIC, "ioapic pin%hhu: deasserted", pin);
} else {
dev_dbg(ACRN_DBG_IOAPIC, "ioapic pin%hhu: %s, ignored, acnt %d",
pin, pinstate_str(newstate), newcnt);
}
vioapic = vm_ioapic(vm);
if (needintr) {
vioapic_send_intr(vioapic, pin);
switch (operation) {
case GSI_SET_HIGH:
vioapic_set_pinstate(vioapic, pin, 1U);
break;
case GSI_SET_LOW:
vioapic_set_pinstate(vioapic, pin, 0U);
break;
case GSI_RAISING_PULSE:
vioapic_set_pinstate(vioapic, pin, 1U);
vioapic_set_pinstate(vioapic, pin, 0U);
break;
case GSI_FALLING_PULSE:
vioapic_set_pinstate(vioapic, pin, 0U);
vioapic_set_pinstate(vioapic, pin, 1U);
break;
default:
/*
* The function caller could guarantee the pre condition.
*/
break;
}
}
@ -131,33 +157,10 @@ vioapic_set_pinstate(struct acrn_vioapic *vioapic, uint32_t pin, bool newstate)
void
vioapic_set_irq(struct vm *vm, uint32_t irq, uint32_t operation)
{
struct acrn_vioapic *vioapic;
uint32_t pin = irq;
vioapic = vm_ioapic(vm);
struct acrn_vioapic *vioapic = vm_ioapic(vm);
spinlock_obtain(&(vioapic->mtx));
switch (operation) {
case GSI_SET_HIGH:
vioapic_set_pinstate(vioapic, pin, true);
break;
case GSI_SET_LOW:
vioapic_set_pinstate(vioapic, pin, false);
break;
case GSI_RAISING_PULSE:
vioapic_set_pinstate(vioapic, pin, true);
vioapic_set_pinstate(vioapic, pin, false);
break;
case GSI_FALLING_PULSE:
vioapic_set_pinstate(vioapic, pin, false);
vioapic_set_pinstate(vioapic, pin, true);
break;
default:
/*
* The function caller could guarantee the pre condition.
*/
break;
}
vioapic_set_irq_nolock(vm, irq, operation);
spinlock_release(&(vioapic->mtx));
}
@ -240,6 +243,16 @@ vioapic_indirect_read(struct acrn_vioapic *vioapic, uint32_t addr)
return 0;
}
static inline bool vioapic_need_intr(struct acrn_vioapic *vioapic, uint16_t pin)
{
uint32_t lvl =(uint32_t)bitmap_test(pin & 0x3FU,
&vioapic->pin_state[pin >> 6U]);
union ioapic_rte rte = vioapic->rtbl[pin];
return !!((((rte.full & IOAPIC_RTE_INTPOL) != 0UL) && lvl == 0U) ||
(((rte.full & IOAPIC_RTE_INTPOL) == 0UL) && lvl != 0U));
}
/*
* Due to the race between vcpus, ensure to do spinlock_obtain(&(vioapic->mtx))
* & spinlock_release(&(vioapic->mtx)) by caller.
@ -356,11 +369,10 @@ vioapic_indirect_write(struct acrn_vioapic *vioapic, uint32_t addr,
*/
if (((vioapic->rtbl[pin].full & IOAPIC_RTE_INTMASK) ==
IOAPIC_RTE_INTMCLR) &&
((vioapic->rtbl[pin].full & IOAPIC_RTE_REM_IRR) == 0UL) &&
(vioapic->acnt[pin] > 0)) {
((vioapic->rtbl[pin].full & IOAPIC_RTE_REM_IRR) == 0UL)
&& vioapic_need_intr(vioapic, (uint16_t)pin)) {
dev_dbg(ACRN_DBG_IOAPIC,
"ioapic pin%hhu: asserted at rtbl write, acnt %d",
pin, vioapic->acnt[pin]);
"ioapic pin%hhu: asserted at rtbl write", pin);
vioapic_send_intr(vioapic, pin);
}
@ -453,10 +465,9 @@ vioapic_process_eoi(struct vm *vm, uint32_t vector)
}
vioapic->rtbl[pin].full &= (~IOAPIC_RTE_REM_IRR);
if (vioapic->acnt[pin] > 0) {
if (vioapic_need_intr(vioapic, (uint16_t)pin)) {
dev_dbg(ACRN_DBG_IOAPIC,
"ioapic pin%hhu: asserted at eoi, acnt %d",
pin, vioapic->acnt[pin]);
"ioapic pin%hhu: asserted at eoi", pin);
vioapic_send_intr(vioapic, pin);
}
}

View File

@ -37,6 +37,7 @@
#define VIOAPIC_SIZE 4096UL
#define REDIR_ENTRIES_HW 120U /* SOS align with native ioapic */
#define STATE_BITMAP_SIZE INT_DIV_ROUNDUP(REDIR_ENTRIES_HW, 64U)
struct acrn_vioapic {
struct vm *vm;
@ -44,8 +45,8 @@ struct acrn_vioapic {
uint32_t id;
uint32_t ioregsel;
union ioapic_rte rtbl[REDIR_ENTRIES_HW];
/* sum of pin asserts (+1) and deasserts (-1) */
int32_t acnt[REDIR_ENTRIES_HW];
/* pin_state status bitmap: 1 - high, 0 - low */
uint64_t pin_state[STATE_BITMAP_SIZE];
};
void vioapic_init(struct vm *vm);
@ -53,6 +54,7 @@ void vioapic_cleanup(struct acrn_vioapic *vioapic);
void vioapic_reset(struct acrn_vioapic *vioapic);
void vioapic_set_irq(struct vm *vm, uint32_t irq, uint32_t operation);
void vioapic_set_irq_nolock(struct vm *vm, uint32_t irq, uint32_t operation);
void vioapic_update_tmr(struct vcpu *vcpu);
uint32_t vioapic_pincount(struct vm *vm);