feat(arch timer): handle spurious host timer interrupt
Whenever a spurious interrupt occurs due to S-EL2 host timer, simply
resume the preempted vCPU. One such scenario is described here:
SPMC on CPUx could be tracking a pending timer deadline of a SP's
vCPU. If NWd driver allocates CPU cycles to migrate this vCPU to
CPUy, the host timer on CPUx will still cause the interrupt to
trigger spuriously on CPUx. In this case, simply resume whichever
vCPU was preempted on CPUx. The S-EL2 host timer on that CPUy will
take care of signaling the timer virtual eventually.
Change-Id: I7118c4f0036d9eae95cc3512a54c98c4b6fd5959
Signed-off-by: Madhukar Pappireddy <madhukar.pappireddy@arm.com>
diff --git a/src/arch/aarch64/plat/ffa/spmc.c b/src/arch/aarch64/plat/ffa/spmc.c
index c164064..f1f66cb 100644
--- a/src/arch/aarch64/plat/ffa/spmc.c
+++ b/src/arch/aarch64/plat/ffa/spmc.c
@@ -1835,8 +1835,15 @@
/* Disable the S-EL2 physical timer */
host_timer_disable();
target_vcpu = timer_find_target_vcpu(current);
- v_intid = HF_VIRTUAL_TIMER_INTID;
- break;
+
+ if (target_vcpu != NULL) {
+ v_intid = HF_VIRTUAL_TIMER_INTID;
+ break;
+ }
+ /*
+ * It is possible for target_vcpu to be NULL in case of spurious
+ * timer interrupt. Fall through.
+ */
case SPURIOUS_INTID_OTHER_WORLD:
/*
* Spurious interrupt ID indicating that there are no pending
diff --git a/src/timer_mgmt.c b/src/timer_mgmt.c
index df26a8a..1714b18 100644
--- a/src/timer_mgmt.c
+++ b/src/timer_mgmt.c
@@ -128,8 +128,38 @@
{
struct vcpu *target_vcpu;
+ /*
+ * There are three possible scenarios here when execution was brought
+ * back from NWd to SPMC as soon as host timer expired:
+ * 1. The vCPU that was being tracked by SPMC on this CPUx has expired
+ * timer. This will be the target vCPU.
+ *
+ * However, it is likely that this vCPU could have been migrated by
+ * NWd driver to another CPU(lets say CPUy). The S-EL2 host timer on
+ * CPUy will take care of signaling the virtual timer interrupt
+ * eventually.
+ *
+ * 2. If there is another vCPU with expired timer in the list maintained
+ * by SPMC on present CPUx, SPMC will pick that vCPU to be
+ * target_vcpu.
+ * 3. If none of the vCPUs have expired timer, simply resume the normal
+ * world i.e., target_vcpu will be NULL.
+ */
if (current->vm->id == HF_OTHER_WORLD_ID) {
target_vcpu = timer_find_vcpu_nearest_deadline(current->cpu);
+ if (target_vcpu != NULL) {
+ if (arch_timer_remaining_ns(&target_vcpu->regs) == 0) {
+ /*
+ * SPMC will either signal or queue the virtual
+ * timer interrupt to the target vCPU. No
+ * need to track this vcpu anymore.
+ */
+ timer_list_remove_vcpu(current->cpu,
+ target_vcpu);
+ } else {
+ target_vcpu = NULL;
+ }
+ }
} else {
target_vcpu = current;
}