When a notification is bound to an IRQ that arrives on a different core than where the VCPU is running, x86 uses a special IPI to notify the other core about this. (For performance reasons you would try to avoid this setup. Nevertheless, it should work.) When an IpiRemoteCall_VMCheckBoundNotification arrives during a VM exit, the notification reply set by VMCheckBoundNotification() gets overwritten by the handleVmexit() reply, leading to lost notification events. This happens when VMCheckBoundNotification() gets called by the IPI handling code within NODE_LOCK_SYS. As there is no way to postpone the VM exit handling and the IPI code doesn't know whether it races with a VM exit, doing nothing if the current task is the target is the safest choice: Either the IPI itself caused a VM exit, or there was a VM exit happening already. To handle the first case, explicitly call VMCheckBoundNotification() in handleVmexit(). This must be done while holding the kernel lock, as the other core can release the lock any moment after it received our IPI reply. In the latter case, pending notifications will be detected and returned to user space by the next seL4_VMEnter() call. Fix tested by Alessandro Legnani. Resolves issue #1148. Signed-off-by: Indan Zupancic <indan@nul.nu>
119 lines
3.7 KiB
C
119 lines
3.7 KiB
C
/*
|
|
* Copyright 2020, Data61, CSIRO (ABN 41 687 119 230)
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-only
|
|
*/
|
|
|
|
#include <config.h>
|
|
#include <mode/smp/ipi.h>
|
|
#include <smp/ipi.h>
|
|
#include <smp/lock.h>
|
|
|
|
#ifdef ENABLE_SMP_SUPPORT
|
|
|
|
void handleRemoteCall(IpiRemoteCall_t call, word_t arg0, word_t arg1, word_t arg2, bool_t irqPath)
|
|
{
|
|
/* we gets spurious irq_remote_call_ipi calls, e.g. when handling IPI
|
|
* in lock while hardware IPI is pending. Guard against spurious IPIs! */
|
|
if (clh_is_ipi_pending(getCurrentCPUIndex())) {
|
|
switch (call) {
|
|
case IpiRemoteCall_Stall:
|
|
ipiStallCoreCallback(irqPath);
|
|
break;
|
|
|
|
case IpiRemoteCall_InvalidatePageStructureCacheASID:
|
|
invalidateLocalPageStructureCacheASID(arg0, arg1);
|
|
break;
|
|
|
|
case IpiRemoteCall_InvalidateTranslationSingle:
|
|
invalidateLocalTranslationSingle(arg0);
|
|
break;
|
|
|
|
case IpiRemoteCall_InvalidateTranslationSingleASID:
|
|
invalidateLocalTranslationSingleASID(arg0, arg1);
|
|
break;
|
|
|
|
case IpiRemoteCall_InvalidateTranslationAll:
|
|
invalidateLocalTranslationAll();
|
|
break;
|
|
|
|
case IpiRemoteCall_switchFpuOwner:
|
|
switchLocalFpuOwner((tcb_t *)arg0);
|
|
break;
|
|
|
|
#ifdef CONFIG_VTX
|
|
case IpiRemoteCall_ClearCurrentVCPU:
|
|
clearCurrentVCPU();
|
|
break;
|
|
case IpiRemoteCall_VMCheckBoundNotification:
|
|
tcb_t *tcb = (tcb_t *)arg0;
|
|
|
|
/* For a running VM notifications are already checked by handleVmexit */
|
|
if (tcb != NODE_STATE(ksCurThread)) {
|
|
VMCheckBoundNotification();
|
|
}
|
|
break;
|
|
#endif
|
|
default:
|
|
Mode_handleRemoteCall((IpiModeRemoteCall_t)call, arg0, arg1, arg2);
|
|
break;
|
|
}
|
|
|
|
big_kernel_lock.node[getCurrentCPUIndex()].ipi = 0;
|
|
ipi_wait();
|
|
}
|
|
}
|
|
|
|
/* make sure all cpu IDs for number of core fit in bitwise word */
|
|
compile_assert(invalid_number_of_supported_nodes, CONFIG_MAX_NUM_NODES <= wordBits);
|
|
|
|
#ifdef CONFIG_USE_LOGICAL_IDS
|
|
static void x86_ipi_send_mask(interrupt_t ipi, word_t mask, bool_t isBlocking)
|
|
{
|
|
word_t nr_target_clusters = 0;
|
|
word_t target_clusters[CONFIG_MAX_NUM_NODES];
|
|
|
|
do {
|
|
int core = wordBits - 1 - clzl(mask);
|
|
target_clusters[nr_target_clusters] = 0;
|
|
|
|
/* get mask of all cores in bitmask which are in same cluster as 'core' */
|
|
word_t sub_mask = mask & cpu_mapping.other_indexes_in_cluster[core];
|
|
target_clusters[nr_target_clusters] |= cpu_mapping.index_to_logical_id[core];
|
|
if (isBlocking) {
|
|
big_kernel_lock.node[core].ipi = 1;
|
|
}
|
|
|
|
/* check if there is any other core in this cluster */
|
|
while (sub_mask) {
|
|
int index = wordBits - 1 - clzl(sub_mask);
|
|
target_clusters[nr_target_clusters] |= cpu_mapping.index_to_logical_id[index];
|
|
if (isBlocking) {
|
|
big_kernel_lock.node[index].ipi = 1;
|
|
}
|
|
sub_mask &= ~BIT(index);
|
|
}
|
|
|
|
mask &= ~(cpu_mapping.other_indexes_in_cluster[core] | BIT(core));
|
|
nr_target_clusters++;
|
|
} while (mask != 0);
|
|
|
|
/* broadcast IPIs to clusters... */
|
|
IPI_ICR_BARRIER;
|
|
for (int i = 0; i < nr_target_clusters; i++) {
|
|
apic_send_ipi_cluster(ipi, target_clusters[i]);
|
|
}
|
|
}
|
|
#endif /* CONFIG_USE_LOGICAL_IDS */
|
|
|
|
void ipi_send_mask(irq_t ipi, word_t mask, bool_t isBlocking)
|
|
{
|
|
interrupt_t interrupt_ipi = ipi + IRQ_INT_OFFSET;
|
|
|
|
#ifdef CONFIG_USE_LOGICAL_IDS
|
|
x86_ipi_send_mask(interrupt_ipi, mask, isBlocking);
|
|
#else
|
|
generic_ipi_send_mask(interrupt_ipi, mask, isBlocking);
|
|
#endif /* CONFIG_USE_LOGICAL_IDS */
|
|
}
|
|
#endif /* ENABLE_SMP_SUPPORT */
|