#include <config.h>
#include <mode/kernel/ipi.h>
#include <smp/ipi.h>
#include <smp/lock.h>
#if CONFIG_MAX_NUM_NODES > 1
static volatile struct {
word_t count;
word_t globalsense;
PAD_TO_NEXT_CACHE_LN(sizeof(word_t) + sizeof(word_t));
} ipiSyncBarrier = {0};
static volatile word_t totalCoreBarrier;
static IpiModeRemoteCall_t remoteCall;
static word_t ipi_args[MAX_IPI_ARGS];
static inline word_t get_ipi_arg(word_t n)
{
assert(n < MAX_IPI_ARGS);
return ipi_args[n];
}
static inline void init_ipi_args(IpiModeRemoteCall_t func,
word_t data1, word_t data2, word_t data3,
word_t mask)
{
remoteCall = func;
ipi_args[0] = data1;
ipi_args[1] = data2;
ipi_args[2] = data3;
totalCoreBarrier = popcountl(mask);
}
static inline void ipi_wait(word_t cores)
{
word_t localsense = ipiSyncBarrier.globalsense;
if (__atomic_fetch_add(&ipiSyncBarrier.count, 1, __ATOMIC_ACQ_REL) == cores) {
ipiSyncBarrier.count = 0;
ipiSyncBarrier.globalsense =
~ipiSyncBarrier.globalsense;
}
while (localsense == ipiSyncBarrier.globalsense) {
asm volatile("pause");
}
}
static void ipiStallCoreCallback(bool_t irqPath)
{
if (clh_is_self_in_queue() && !irqPath) {
if (thread_state_ptr_get_tsType(&NODE_STATE(ksCurThread)->tcbState) == ThreadState_Running) {
setThreadState(NODE_STATE(ksCurThread), ThreadState_Restart);
}
SCHED_ENQUEUE_CURRENT_TCB;
switchToIdleThread();
NODE_STATE(ksSchedulerAction) = SchedulerAction_ResumeCurrentThread;
big_kernel_lock.node_owners[getCurrentCPUIndex()].ipi = 0;
ipi_wait(totalCoreBarrier);
while (big_kernel_lock.node_owners[getCurrentCPUIndex()].next->value != CLHState_Granted) {
if (clh_is_ipi_pending(getCurrentCPUIndex())) {
assert((IpiRemoteCall_t)remoteCall != IpiRemoteCall_Stall);
Arch_handleIPI(irq_remote_call_ipi, irqPath);
}
asm volatile("pause");
}
asm volatile("" ::: "memory");
activateThread();
restore_user_context();
} else {
SCHED_ENQUEUE_CURRENT_TCB;
switchToIdleThread();
NODE_STATE(ksSchedulerAction) = SchedulerAction_ResumeCurrentThread;
}
}
static void handleRemoteCall(IpiModeRemoteCall_t call, word_t arg0,
word_t arg1, word_t arg2, bool_t irqPath)
{
if (clh_is_ipi_pending(getCurrentCPUIndex())) {
switch ((IpiRemoteCall_t)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((user_fpu_state_t *)arg0);
break;
#ifdef CONFIG_VTX
case IpiRemoteCall_ClearCurrentVCPU:
clearCurrentVCPU();
break;
case IpiRemoteCall_VMCheckBoundNotification:
VMCheckBoundNotification((tcb_t*)arg0);
break;
#endif
default:
Mode_handleRemoteCall(call, arg0, arg1, arg2);
break;
}
big_kernel_lock.node_owners[getCurrentCPUIndex()].ipi = 0;
ipi_wait(totalCoreBarrier);
}
}
static void handleReschedule(void)
{
rescheduleRequired();
}
void Arch_handleIPI(irq_t irq, bool_t irqPath)
{
if (irq == irq_remote_call_ipi) {
handleRemoteCall(remoteCall, get_ipi_arg(0), get_ipi_arg(1), get_ipi_arg(2), irqPath);
} else if (irq == irq_reschedule_ipi) {
handleReschedule();
} else {
fail("Invalid IPI");
}
}
compile_assert(invalid_number_of_supported_nodes, CONFIG_MAX_NUM_NODES <= wordBits);
#ifdef CONFIG_USE_LOGICAL_IDS
static void ipi_send_mask(irq_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;
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_owners[core].ipi = 1;
}
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_owners[index].ipi = 1;
}
sub_mask &= ~BIT(index);
}
mask &= ~(cpu_mapping.other_indexes_in_cluster[core] | BIT(core));
nr_target_clusters++;
} while (mask != 0);
IPI_ICR_BARRIER;
for (int i = 0; i < nr_target_clusters; i++) {
apic_send_ipi_cluster(ipi, target_clusters[i]);
}
}
#else
static void ipi_send_mask(irq_t ipi, word_t mask, bool_t isBlocking)
{
word_t nr_target_cores = 0;
uint16_t target_cores[CONFIG_MAX_NUM_NODES];
while (mask) {
int index = wordBits - 1 - clzl(mask);
if (isBlocking) {
big_kernel_lock.node_owners[index].ipi = 1;
target_cores[nr_target_cores] = index;
nr_target_cores++;
} else {
apic_send_ipi_core(ipi, cpuIndexToID(index));
}
mask &= ~BIT(index);
}
if (nr_target_cores > 0) {
IPI_ICR_BARRIER;
for (int i = 0; i < nr_target_cores; i++) {
apic_send_ipi_core(ipi, cpuIndexToID(target_cores[i]));
}
}
}
#endif
void doRemoteMaskOp(IpiRemoteCall_t func, word_t data1, word_t data2, word_t data3, word_t mask)
{
mask &= ~BIT(getCurrentCPUIndex());
if (mask != 0) {
init_ipi_args(func, data1, data2, data3, mask);
asm volatile("" ::: "memory");
ipi_send_mask(int_remote_call_ipi, mask, true);
ipi_wait(totalCoreBarrier);
}
}
void doMaskReschedule(word_t mask)
{
mask &= ~BIT(getCurrentCPUIndex());
if (mask != 0) {
ipi_send_mask(int_reschedule_ipi, mask, false);
}
}
#endif