diff --git a/src/emu/nanos6.c b/src/emu/nanos6.c deleted file mode 100644 index 031e411..0000000 --- a/src/emu/nanos6.c +++ /dev/null @@ -1,658 +0,0 @@ -/* Copyright (c) 2022-2023 Barcelona Supercomputing Center (BSC) - * SPDX-License-Identifier: GPL-3.0-or-later */ - -#include "uthash.h" -#include "utlist.h" - -#include "chan.h" -#include "emu.h" -#include "task.h" -#include "ovni.h" -#include "prv.h" - -void -hook_init_nanos6(struct ovni_emu *emu) -{ - int64_t *clock = &emu->delta_time; - FILE *prv_th = emu->prv_thread; - FILE *prv_cpu = emu->prv_cpu; - - /* Init the channels in all threads */ - for (size_t i = 0; i < emu->total_nthreads; i++) { - struct ovni_ethread *th = emu->global_thread[i]; - int row = th->gindex + 1; - - struct ovni_chan **uth = &emu->th_chan; - - chan_th_init(th, uth, CHAN_NANOS6_TASKID, CHAN_TRACK_TH_RUNNING, 0, 0, 1, row, prv_th, clock); - chan_th_init(th, uth, CHAN_NANOS6_TYPE, CHAN_TRACK_TH_RUNNING, 0, 0, 1, row, prv_th, clock); - chan_th_init(th, uth, CHAN_NANOS6_SUBSYSTEM, CHAN_TRACK_TH_ACTIVE, 0, 0, 1, row, prv_th, clock); - chan_th_init(th, uth, CHAN_NANOS6_RANK, CHAN_TRACK_TH_RUNNING, 0, 0, 1, row, prv_th, clock); - chan_th_init(th, uth, CHAN_NANOS6_THREAD, CHAN_TRACK_NONE, 0, 1, 1, row, prv_th, clock); - } - - /* Init the Nanos6 channels in all cpus */ - for (size_t i = 0; i < emu->total_ncpus; i++) { - struct ovni_cpu *cpu = emu->global_cpu[i]; - int row = cpu->gindex + 1; - struct ovni_chan **ucpu = &emu->cpu_chan; - - chan_cpu_init(cpu, ucpu, CHAN_NANOS6_TASKID, CHAN_TRACK_TH_RUNNING, 0, 0, 1, row, prv_cpu, clock); - chan_cpu_init(cpu, ucpu, CHAN_NANOS6_TYPE, CHAN_TRACK_TH_RUNNING, 0, 0, 1, row, prv_cpu, clock); - chan_cpu_init(cpu, ucpu, CHAN_NANOS6_SUBSYSTEM, CHAN_TRACK_TH_RUNNING, 0, 0, 1, row, prv_cpu, clock); - chan_cpu_init(cpu, ucpu, CHAN_NANOS6_RANK, CHAN_TRACK_TH_RUNNING, 0, 0, 1, row, prv_cpu, clock); - chan_cpu_init(cpu, ucpu, CHAN_NANOS6_THREAD, CHAN_TRACK_TH_RUNNING, 0, 0, 1, row, prv_cpu, clock); - } - - /* Init task stack */ - for (size_t i = 0; i < emu->total_nthreads; i++) { - struct ovni_ethread *th = emu->global_thread[i]; - th->nanos6_task_stack.thread = th; - } -} - -/* --------------------------- pre ------------------------------- */ - -static void -chan_task_stopped(struct ovni_emu *emu) -{ - struct ovni_ethread *th = emu->cur_thread; - - chan_set(&th->chan[CHAN_NANOS6_TASKID], 0); - chan_set(&th->chan[CHAN_NANOS6_TYPE], 0); - - if (emu->cur_loom->rank_enabled) - chan_set(&th->chan[CHAN_NANOS6_RANK], 0); -} - -static void -chan_task_running(struct ovni_emu *emu, struct task *task) -{ - struct ovni_ethread *th = emu->cur_thread; - struct ovni_eproc *proc = emu->cur_proc; - - if (task->id == 0) - edie(emu, "task id cannot be 0\n"); - - if (task->type->gid == 0) - edie(emu, "task type gid cannot be 0\n"); - - if (proc->appid <= 0) - edie(emu, "app id must be positive\n"); - - chan_set(&th->chan[CHAN_NANOS6_TASKID], task->id); - chan_set(&th->chan[CHAN_NANOS6_TYPE], task->type->gid); - - if (emu->cur_loom->rank_enabled) - chan_set(&th->chan[CHAN_NANOS6_RANK], proc->rank + 1); -} - -static void -chan_task_switch(struct ovni_emu *emu, - struct task *prev, struct task *next) -{ - struct ovni_ethread *th = emu->cur_thread; - - if (!prev || !next) - edie(emu, "cannot switch to or from a NULL task\n"); - - if (prev == next) - edie(emu, "cannot switch to the same task\n"); - - if (next->id == 0) - edie(emu, "next task id cannot be 0\n"); - - if (next->type->gid == 0) - edie(emu, "next task type id cannot be 0\n"); - - if (prev->thread != next->thread) - edie(emu, "cannot switch to a task of another thread\n"); - - /* No need to change the rank as we will switch to tasks from - * same thread */ - chan_set(&th->chan[CHAN_NANOS6_TASKID], next->id); - - /* FIXME: We should emit a PRV event even if we are switching to - * the same type event, to mark the end of the current task. For - * now we only emit a new type if we switch to a type with a - * different gid. */ - if (prev->type->gid != next->type->gid) - chan_set(&th->chan[CHAN_NANOS6_TYPE], next->type->gid); -} - -static void -update_task_state(struct ovni_emu *emu) -{ - if (ovni_payload_size(emu->cur_ev) < 4) - edie(emu, "missing task id in payload\n"); - - uint32_t task_id = emu->cur_ev->payload.u32[0]; - - struct ovni_ethread *th = emu->cur_thread; - struct ovni_eproc *proc = emu->cur_proc; - - struct task_info *info = &proc->nanos6_task_info; - struct task_stack *stack = &th->nanos6_task_stack; - - struct task *task = task_find(info->tasks, task_id); - - if (task == NULL) - edie(emu, "cannot find task with id %u\n", task_id); - - switch (emu->cur_ev->header.value) { - case 'x': - task_execute(emu, stack, task); - break; - case 'e': - task_end(emu, stack, task); - break; - case 'p': - task_pause(emu, stack, task); - break; - case 'r': - task_resume(emu, stack, task); - break; - default: - edie(emu, "unexpected Nanos6 task event\n"); - } -} - -static char -expand_transition_value(struct ovni_emu *emu, int was_running, int runs_now) -{ - char tr = emu->cur_ev->header.value; - - /* Ensure we don't clobber the value */ - if (tr == 'X' || tr == 'E') - edie(emu, "unexpected event value %c\n", tr); - - /* Modify the event value to detect nested transitions */ - if (tr == 'x' && was_running) - tr = 'X'; /* Execute a new nested task */ - else if (tr == 'e' && runs_now) - tr = 'E'; /* End a nested task */ - - return tr; -} - -static void -update_task_channels(struct ovni_emu *emu, - char tr, struct task *prev, struct task *next) -{ - switch (tr) { - case 'x': - case 'r': - chan_task_running(emu, next); - break; - case 'e': - case 'p': - chan_task_stopped(emu); - break; - /* Additional nested transitions */ - case 'X': - case 'E': - chan_task_switch(emu, prev, next); - break; - default: - edie(emu, "unexpected transition value %c\n", tr); - } -} - -static void -enforce_task_rules(struct ovni_emu *emu, - char tr, struct task *prev, struct task *next) - -{ - UNUSED(prev); - - /* If a task has just entered the running state, it must show - * the running task body subsystem */ - if (tr == 'x' || tr == 'X') { - if (next->state != TASK_ST_RUNNING) - edie(emu, "a Nanos6 task starts running but not in the running state\n"); - - struct ovni_ethread *th = emu->cur_thread; - struct ovni_chan *sschan = &th->chan[CHAN_NANOS6_SUBSYSTEM]; - int st = chan_get_st(sschan); - - if (st != 0 && st != ST_NANOS6_TASK_BODY) - edie(emu, "a Nanos6 task starts running but not in the \"running body\" subsystem state\n"); - } -} - -static void -update_task(struct ovni_emu *emu) -{ - struct ovni_ethread *th = emu->cur_thread; - struct task_stack *stack = &th->nanos6_task_stack; - - struct task *prev = task_get_running(stack); - - /* Update the emulator state, but don't modify the channels */ - update_task_state(emu); - - struct task *next = task_get_running(stack); - - int was_running = (prev != NULL); - int runs_now = (next != NULL); - char tr = expand_transition_value(emu, was_running, runs_now); - - /* Update the task related channels now */ - update_task_channels(emu, tr, prev, next); - - enforce_task_rules(emu, tr, prev, next); -} - -static void -create_task(struct ovni_emu *emu) -{ - if (ovni_payload_size(emu->cur_ev) != 8) - edie(emu, "cannot create task: unexpected payload size\n"); - - uint32_t task_id = emu->cur_ev->payload.u32[0]; - uint32_t type_id = emu->cur_ev->payload.u32[1]; - - struct task_info *info = &emu->cur_proc->nanos6_task_info; - - task_create(emu, info, type_id, task_id); -} - -static void -pre_task(struct ovni_emu *emu) -{ - switch (emu->cur_ev->header.value) { - case 'c': - create_task(emu); - break; - case 'x': - case 'e': - case 'r': - case 'p': /* Wet floor */ - update_task(emu); - break; - default: - edie(emu, "unexpected Nanos6 task event value\n"); - } -} - -static void -pre_type(struct ovni_emu *emu) -{ - uint8_t value = emu->cur_ev->header.value; - - if (value != 'c') - edie(emu, "unexpected event value %c\n", value); - - if ((emu->cur_ev->header.flags & OVNI_EV_JUMBO) == 0) - edie(emu, "expecting a jumbo event\n"); - - uint8_t *data = &emu->cur_ev->payload.jumbo.data[0]; - uint32_t typeid = *(uint32_t *) data; - data += 4; - - const char *label = (const char *) data; - - struct ovni_eproc *proc = emu->cur_proc; - - task_type_create(&proc->nanos6_task_info, typeid, label); -} - -static void -pre_deps(struct ovni_emu *emu) -{ - struct ovni_ethread *th = emu->cur_thread; - struct ovni_chan *chan_th = &th->chan[CHAN_NANOS6_SUBSYSTEM]; - - switch (emu->cur_ev->header.value) { - case 'r': - chan_push(chan_th, ST_NANOS6_DEP_REG); - break; - case 'R': - chan_pop(chan_th, ST_NANOS6_DEP_REG); - break; - case 'u': - chan_push(chan_th, ST_NANOS6_DEP_UNREG); - break; - case 'U': - chan_pop(chan_th, ST_NANOS6_DEP_UNREG); - break; - default: - edie(emu, "unknown Nanos6 dependency event\n"); - } -} - -static void -pre_blocking(struct ovni_emu *emu) -{ - struct ovni_ethread *th = emu->cur_thread; - struct ovni_chan *chan_th = &th->chan[CHAN_NANOS6_SUBSYSTEM]; - - switch (emu->cur_ev->header.value) { - case 'b': - chan_push(chan_th, ST_NANOS6_BLK_BLOCKING); - break; - case 'B': - chan_pop(chan_th, ST_NANOS6_BLK_BLOCKING); - break; - case 'u': - chan_push(chan_th, ST_NANOS6_BLK_UNBLOCKING); - break; - case 'U': - chan_pop(chan_th, ST_NANOS6_BLK_UNBLOCKING); - break; - case 'w': - chan_push(chan_th, ST_NANOS6_BLK_TASKWAIT); - break; - case 'W': - chan_pop(chan_th, ST_NANOS6_BLK_TASKWAIT); - break; - case 'f': - chan_push(chan_th, ST_NANOS6_BLK_WAITFOR); - break; - case 'F': - chan_pop(chan_th, ST_NANOS6_BLK_WAITFOR); - break; - default: - edie(emu, "unknown Nanos6 blocking event\n"); - } -} - -static void -pre_worker(struct ovni_emu *emu) -{ - struct ovni_ethread *th = emu->cur_thread; - struct ovni_chan *chan_th = &th->chan[CHAN_NANOS6_SUBSYSTEM]; - - switch (emu->cur_ev->header.value) { - case '[': - chan_push(chan_th, ST_NANOS6_WORKER_LOOP); - break; - case ']': - chan_pop(chan_th, ST_NANOS6_WORKER_LOOP); - break; - case 't': - chan_push(chan_th, ST_NANOS6_HANDLING_TASK); - break; - case 'T': - chan_pop(chan_th, ST_NANOS6_HANDLING_TASK); - break; - case 'w': - chan_push(chan_th, ST_NANOS6_SWITCH_TO); - break; - case 'W': - chan_pop(chan_th, ST_NANOS6_SWITCH_TO); - break; - case 'm': - chan_push(chan_th, ST_NANOS6_MIGRATE); - break; - case 'M': - chan_pop(chan_th, ST_NANOS6_MIGRATE); - break; - case 's': - chan_push(chan_th, ST_NANOS6_SUSPEND); - break; - case 'S': - chan_pop(chan_th, ST_NANOS6_SUSPEND); - break; - case 'r': - chan_push(chan_th, ST_NANOS6_RESUME); - break; - case 'R': - chan_pop(chan_th, ST_NANOS6_RESUME); - break; - case '*': - chan_ev(chan_th, EV_NANOS6_SIGNAL); - break; - default: - edie(emu, "unknown Nanos6 worker event\n"); - } -} - -static void -pre_memory(struct ovni_emu *emu) -{ - struct ovni_ethread *th = emu->cur_thread; - struct ovni_chan *chan_th = &th->chan[CHAN_NANOS6_SUBSYSTEM]; - - switch (emu->cur_ev->header.value) { - case 'a': - chan_push(chan_th, ST_NANOS6_ALLOCATING); - break; - case 'A': - chan_pop(chan_th, ST_NANOS6_ALLOCATING); - break; - case 'f': - chan_push(chan_th, ST_NANOS6_FREEING); - break; - case 'F': - chan_pop(chan_th, ST_NANOS6_FREEING); - break; - default: - edie(emu, "unknown Nanos6 memory event\n"); - } -} - -static void -pre_sched(struct ovni_emu *emu) -{ - struct ovni_ethread *th = emu->cur_thread; - struct ovni_chan *chan_th = &th->chan[CHAN_NANOS6_SUBSYSTEM]; - - switch (emu->cur_ev->header.value) { - case '[': - chan_push(chan_th, ST_NANOS6_SCHED_SERVING); - break; - case ']': - chan_pop(chan_th, ST_NANOS6_SCHED_SERVING); - break; - case 'a': - chan_push(chan_th, ST_NANOS6_SCHED_ADDING); - break; - case 'A': - chan_pop(chan_th, ST_NANOS6_SCHED_ADDING); - break; - case 'p': - chan_push(chan_th, ST_NANOS6_SCHED_PROCESSING); - break; - case 'P': - chan_pop(chan_th, ST_NANOS6_SCHED_PROCESSING); - break; - case '@': - chan_ev(chan_th, EV_NANOS6_SCHED_SELF); - break; - case 'r': - chan_ev(chan_th, EV_NANOS6_SCHED_RECV); - break; - case 's': - chan_ev(chan_th, EV_NANOS6_SCHED_SEND); - break; - default: - edie(emu, "unknown Nanos6 scheduler event\n"); - } -} - -static void -pre_thread(struct ovni_emu *emu) -{ - struct ovni_ethread *th = emu->cur_thread; - struct ovni_chan *chan_th = &th->chan[CHAN_NANOS6_THREAD]; - - switch (emu->cur_ev->header.value) { - case 'e': - chan_push(chan_th, ST_NANOS6_TH_EXTERNAL); - break; - case 'E': - chan_pop(chan_th, ST_NANOS6_TH_EXTERNAL); - break; - case 'w': - chan_push(chan_th, ST_NANOS6_TH_WORKER); - break; - case 'W': - chan_pop(chan_th, ST_NANOS6_TH_WORKER); - break; - case 'l': - chan_push(chan_th, ST_NANOS6_TH_LEADER); - break; - case 'L': - chan_pop(chan_th, ST_NANOS6_TH_LEADER); - break; - case 'm': - chan_push(chan_th, ST_NANOS6_TH_MAIN); - break; - case 'M': - chan_pop(chan_th, ST_NANOS6_TH_MAIN); - break; - default: - edie(emu, "unknown Nanos6 thread type event\n"); - } -} - -static void -pre_ss(struct ovni_emu *emu, int st) -{ - struct ovni_ethread *th = emu->cur_thread; - struct ovni_chan *chan_th = &th->chan[CHAN_NANOS6_SUBSYSTEM]; - - dbg("pre_ss chan id %d st=%d\n", chan_th->id, st); - - switch (emu->cur_ev->header.value) { - case '[': - chan_push(chan_th, st); - break; - case ']': - chan_pop(chan_th, st); - break; - default: - edie(emu, "unexpected value '%c' (expecting '[' or ']')\n", - emu->cur_ev->header.value); - } -} - -static void -check_affinity(struct ovni_emu *emu) -{ - struct ovni_ethread *th = emu->cur_thread; - struct ovni_cpu *cpu = th->cpu; - - if (!cpu || cpu->virtual) - return; - - if (cpu->nrunning_threads > 1) { - eerr(emu, "cpu %s has more than one thread running\n", cpu->name); - - /* Only abort in linter mode so we can still see the - * trace to find out what was happening */ - if (emu->enable_linter) - abort(); - } -} - -void -hook_pre_nanos6(struct ovni_emu *emu) -{ - if (emu->cur_ev->header.model != '6') - edie(emu, "hook_pre_nanos6: unexpected event with model %c\n", - emu->cur_ev->header.model); - - if (!emu->cur_thread->is_active) { - edie(emu, "hook_pre_nanos6: current thread %d not active\n", - emu->cur_thread->tid); - } - - switch (emu->cur_ev->header.category) { - case 'T': - pre_task(emu); - break; - case 'C': - pre_ss(emu, ST_NANOS6_TASK_CREATING); - break; - case 'Y': - pre_type(emu); - break; - case 'S': - pre_sched(emu); - break; - case 'U': - pre_ss(emu, ST_NANOS6_TASK_SUBMIT); - break; - case 'F': - pre_ss(emu, ST_NANOS6_TASK_SPAWNING); - break; - case 'O': - pre_ss(emu, ST_NANOS6_TASK_FOR); - break; - case 't': - pre_ss(emu, ST_NANOS6_TASK_BODY); - break; - case 'H': - pre_thread(emu); - break; - case 'D': - pre_deps(emu); - break; - case 'B': - pre_blocking(emu); - break; - case 'W': - pre_worker(emu); - break; - case 'M': - pre_memory(emu); - break; - case 'P': - /* Ignore progress events */ - break; - default: - edie(emu, "unknown Nanos6 event category\n"); - } - - check_affinity(emu); -} - -static void -end_lint(struct ovni_emu *emu) -{ - /* Ensure we run out of subsystem states */ - for (size_t i = 0; i < emu->total_nthreads; i++) { - struct ovni_ethread *th = emu->global_thread[i]; - struct ovni_chan *ch = &th->chan[CHAN_NANOS6_SUBSYSTEM]; - if (ch->n != 1) { - int top = ch->stack[ch->n - 1]; - - struct pcf_value_label *pv; - char *name = "(unknown)"; - for (pv = &nanos6_ss_values[0]; pv->label; pv++) { - if (pv->value == top) { - name = pv->label; - break; - } - } - - die("thread %d ended with %d extra stacked nanos6 subsystems, top=\"%s\"\n", - th->tid, ch->n - 1, name); - } - } -} - -void -hook_end_nanos6(struct ovni_emu *emu) -{ - /* Emit types for all channel types and processes */ - for (enum chan_type ct = 0; ct < CHAN_MAXTYPE; ct++) { - struct pcf_file *pcf = &emu->pcf[ct]; - int typeid = chan_to_prvtype[CHAN_NANOS6_TYPE]; - struct pcf_type *pcftype = pcf_find_type(pcf, typeid); - - for (size_t i = 0; i < emu->trace.nlooms; i++) { - struct ovni_loom *loom = &emu->trace.loom[i]; - for (size_t j = 0; j < loom->nprocs; j++) { - struct ovni_eproc *proc = &loom->proc[j]; - task_create_pcf_types(pcftype, proc->nanos6_task_info.types); - } - } - } - - /* When running in linter mode perform additional checks */ - if (emu->enable_linter) - end_lint(emu); -} diff --git a/src/emu/ovni.c b/src/emu/ovni.c deleted file mode 100644 index 5d53c44..0000000 --- a/src/emu/ovni.c +++ /dev/null @@ -1,620 +0,0 @@ -/* Copyright (c) 2021 Barcelona Supercomputing Center (BSC) - * SPDX-License-Identifier: GPL-3.0-or-later */ - -#include "chan.h" -#include "emu.h" -#include "ovni.h" -#include "prv.h" -#include "utlist.h" - -/* The emulator ovni module provides the execution model by tracking the thread - * state and which threads run in each CPU */ - -/* --------------------------- init ------------------------------- */ - -void -hook_init_ovni(struct ovni_emu *emu) -{ - int64_t *clock = &emu->delta_time; - FILE *prv_th = emu->prv_thread; - FILE *prv_cpu = emu->prv_cpu; - - /* Init the ovni channels in all threads */ - for (size_t i = 0; i < emu->total_nthreads; i++) { - struct ovni_ethread *th = emu->global_thread[i]; - int row = th->gindex + 1; - struct ovni_chan **uth = &emu->th_chan; - - chan_th_init(th, uth, CHAN_OVNI_TID, CHAN_TRACK_TH_RUNNING, th->tid, 0, 1, row, prv_th, clock); - chan_th_init(th, uth, CHAN_OVNI_PID, CHAN_TRACK_TH_RUNNING, th->proc->pid, 0, 1, row, prv_th, clock); - chan_th_init(th, uth, CHAN_OVNI_CPU, CHAN_TRACK_NONE, -1, 0, 1, row, prv_th, clock); - chan_th_init(th, uth, CHAN_OVNI_STATE, CHAN_TRACK_NONE, TH_ST_UNKNOWN, 1, 1, row, prv_th, clock); - chan_th_init(th, uth, CHAN_OVNI_FLUSH, CHAN_TRACK_NONE, ST_NULL, 1, 1, row, prv_th, clock); - } - - /* Init the ovni channels in all cpus */ - for (size_t i = 0; i < emu->total_ncpus; i++) { - struct ovni_cpu *cpu = emu->global_cpu[i]; - int row = cpu->gindex + 1; - struct ovni_chan **ucpu = &emu->cpu_chan; - - chan_cpu_init(cpu, ucpu, CHAN_OVNI_TID, CHAN_TRACK_TH_RUNNING, 0, 1, 1, row, prv_cpu, clock); - chan_cpu_init(cpu, ucpu, CHAN_OVNI_PID, CHAN_TRACK_TH_RUNNING, 0, 1, 1, row, prv_cpu, clock); - chan_cpu_init(cpu, ucpu, CHAN_OVNI_NRTHREADS, CHAN_TRACK_NONE, 0, 1, 1, row, prv_cpu, clock); - chan_cpu_init(cpu, ucpu, CHAN_OVNI_FLUSH, CHAN_TRACK_TH_RUNNING, 0, 0, 1, row, prv_cpu, clock); - } -} - -/* --------------------------- pre ------------------------------- */ - -/* Update the tracking channel if needed */ -static void -chan_tracking_update(struct ovni_chan *chan, struct ovni_ethread *th) -{ - int enabled = 0; - - if (th == NULL) - die("chan_tracking_update: thread is NULL"); - - switch (chan->track) { - case CHAN_TRACK_TH_RUNNING: - enabled = th->is_running; - break; - case CHAN_TRACK_TH_ACTIVE: - enabled = th->is_active; - break; - default: - dbg("ignoring thread %d chan %d with track=%d\n", - th->tid, chan->id, chan->track); - return; - } - - /* The channel is already in the proper state */ - if (chan_is_enabled(chan) == enabled) - return; - - dbg("thread %d changes state to %d: chan %d enabled=%d\n", - th->tid, th->state, chan->id, enabled); - - chan_enable(chan, enabled); -} - -/* Sets the state of the thread and updates the thread tracking channels */ -static void -thread_set_state(struct ovni_ethread *th, enum ethread_state state) -{ - /* The state must be updated when a cpu is set */ - if (th->cpu == NULL) - die("thread_set_state: thread %d doesn't have a CPU\n", - th->tid); - - dbg("thread_set_state: setting thread %d state %d\n", - th->tid, state); - - th->state = state; - - th->is_running = (state == TH_ST_RUNNING) ? 1 : 0; - - th->is_active = (state == TH_ST_RUNNING - || state == TH_ST_COOLING - || state == TH_ST_WARMING) - ? 1 - : 0; - - chan_set(&th->chan[CHAN_OVNI_STATE], th->state); - - /* Enable or disable the thread channels that track the thread state */ - for (int i = 0; i < CHAN_MAX; i++) - chan_tracking_update(&th->chan[i], th); - - dbg("thread_set_state: done\n"); -} - -static void -cpu_update_th_stats(struct ovni_cpu *cpu) -{ - struct ovni_ethread *th = NULL; - struct ovni_ethread *th_running = NULL; - struct ovni_ethread *th_active = NULL; - int active = 0, running = 0; - - DL_FOREACH(cpu->thread, th) - { - if (th->state == TH_ST_RUNNING) { - th_running = th; - running++; - th_active = th; - active++; - } else if (th->state == TH_ST_COOLING || th->state == TH_ST_WARMING) { - th_active = th; - active++; - } - } - - cpu->nrunning_threads = running; - cpu->nactive_threads = active; - cpu->th_running = th_running; - cpu->th_active = th_active; -} - -static void -update_cpu(struct ovni_cpu *cpu) -{ - dbg("updating cpu %s\n", cpu->name); - - /* Update the running and active threads first */ - cpu_update_th_stats(cpu); - - /* From the CPU channels we only need to manually update the number of - * threads running in the CPU */ - if (chan_get_st(&cpu->chan[CHAN_OVNI_NRTHREADS]) != (int) cpu->nrunning_threads) - chan_set(&cpu->chan[CHAN_OVNI_NRTHREADS], (int) cpu->nrunning_threads); - - /* Update all tracking channels */ - for (int i = 0; i < CHAN_MAX; i++) - emu_cpu_update_chan(cpu, &cpu->chan[i]); - - dbg("updating cpu %s complete!\n", cpu->name); -} - -struct ovni_cpu * -emu_get_cpu(struct ovni_loom *loom, int cpuid) -{ - if (cpuid >= (int) loom->ncpus) - die("emu_get_cpu: CPU index out of bounds\n"); - - if (cpuid < 0) - return &loom->vcpu; - - return &loom->cpu[cpuid]; -} - -static struct ovni_ethread * -emu_cpu_find_thread(struct ovni_cpu *cpu, struct ovni_ethread *thread) -{ - struct ovni_ethread *p = NULL; - - DL_FOREACH(cpu->thread, p) - { - if (p == thread) - return p; - } - - return NULL; -} - -/* Add the given thread to the list of threads assigned to the CPU */ -static void -cpu_add_thread(struct ovni_cpu *cpu, struct ovni_ethread *thread) -{ - /* Found, abort */ - if (emu_cpu_find_thread(cpu, thread) != NULL) { - err("The thread %d is already assigned to %s\n", - thread->tid, cpu->name); - abort(); - } - - DL_APPEND(cpu->thread, thread); - cpu->nthreads++; - - update_cpu(cpu); -} - -static void -cpu_remove_thread(struct ovni_cpu *cpu, struct ovni_ethread *thread) -{ - struct ovni_ethread *p = emu_cpu_find_thread(cpu, thread); - - /* Not found, abort */ - if (p == NULL) { - err("cannot remove missing thread %d from cpu %s\n", - thread->tid, cpu->name); - abort(); - } - - DL_DELETE(cpu->thread, thread); - cpu->nthreads--; - - update_cpu(cpu); -} - -static void -cpu_migrate_thread(struct ovni_cpu *cpu, - struct ovni_ethread *thread, - struct ovni_cpu *newcpu) -{ - cpu_remove_thread(cpu, thread); - cpu_add_thread(newcpu, thread); -} - -/* Sets the thread assigned CPU to the given one. - * Precondition: the thread CPU must be null */ -static void -thread_set_cpu(struct ovni_ethread *th, struct ovni_cpu *cpu) -{ - if (th->cpu != NULL) - die("thread_set_cpu: thread %d already has a CPU\n", - th->tid); - - dbg("thread_set_cpu: setting thread %d cpu to %s\n", - th->tid, cpu->name); - th->cpu = cpu; - - chan_enable(&th->chan[CHAN_OVNI_CPU], 1); - chan_set(&th->chan[CHAN_OVNI_CPU], cpu->gindex + 1); -} - -/* Unsets the thread assigned CPU. - * Precondition: the thread CPU must be not null */ -static void -thread_unset_cpu(struct ovni_ethread *th) -{ - if (th->cpu == NULL) - die("thread_unset_cpu: thread %d doesn't have a CPU\n", - th->tid); - - th->cpu = NULL; - - chan_enable(&th->chan[CHAN_OVNI_CPU], 0); -} - -/* Migrates the thread assigned CPU to the given one. - * Precondition: the thread CPU must be not null */ -static void -thread_migrate_cpu(struct ovni_ethread *th, struct ovni_cpu *cpu) -{ - if (th->cpu == NULL) - die("thread_migrate_cpu: thread %d doesn't have a CPU\n", - th->tid); - th->cpu = cpu; - - chan_set(&th->chan[CHAN_OVNI_CPU], cpu->gindex + 1); -} - -static void -pre_thread_execute(struct ovni_emu *emu, struct ovni_ethread *th) -{ - /* The thread cannot be already running */ - if (th->state == TH_ST_RUNNING) - edie(emu, "pre_thread_execute: thread %d already running\n", - th->tid); - - int cpuid = emu->cur_ev->payload.i32[0]; - - struct ovni_cpu *cpu = emu_get_cpu(emu->cur_loom, cpuid); - dbg("pre_thread_execute: thread %d runs in CPU %s\n", th->tid, cpu->name); - - /* First set the CPU in the thread */ - thread_set_cpu(th, cpu); - - /* Then set the thread to running state */ - thread_set_state(th, TH_ST_RUNNING); - - /* And then add the thread to the CPU, so tracking channels see the - * updated thread state */ - cpu_add_thread(cpu, th); -} - -static void -pre_thread_end(struct ovni_ethread *th) -{ - if (th->state != TH_ST_RUNNING && th->state != TH_ST_COOLING) - die("pre_thread_end: thread %d not running or cooling\n", - th->tid); - - if (th->cpu == NULL) - die("pre_thread_end: thread %d doesn't have a CPU\n", - th->tid); - - /* First update the thread state */ - thread_set_state(th, TH_ST_DEAD); - - /* Then remove it from the cpu, so channels are properly updated */ - cpu_remove_thread(th->cpu, th); - - thread_unset_cpu(th); -} - -static void -pre_thread_pause(struct ovni_ethread *th) -{ - if (th->state != TH_ST_RUNNING && th->state != TH_ST_COOLING) - die("pre_thread_pause: thread %d not running or cooling\n", - th->tid); - - if (th->cpu == NULL) - die("pre_thread_pause: thread %d doesn't have a CPU\n", - th->tid); - - thread_set_state(th, TH_ST_PAUSED); - update_cpu(th->cpu); -} - -static void -pre_thread_resume(struct ovni_ethread *th) -{ - if (th->state != TH_ST_PAUSED && th->state != TH_ST_WARMING) - die("pre_thread_resume: thread %d not paused or warming\n", - th->tid); - - if (th->cpu == NULL) - die("pre_thread_resume: thread %d doesn't have a CPU\n", - th->tid); - - thread_set_state(th, TH_ST_RUNNING); - update_cpu(th->cpu); -} - -static void -pre_thread_cool(struct ovni_ethread *th) -{ - if (th->state != TH_ST_RUNNING) - die("pre_thread_cool: thread %d not running\n", - th->tid); - - if (th->cpu == NULL) - die("pre_thread_cool: thread %d doesn't have a CPU\n", - th->tid); - - thread_set_state(th, TH_ST_COOLING); - update_cpu(th->cpu); -} - -static void -pre_thread_warm(struct ovni_ethread *th) -{ - if (th->state != TH_ST_PAUSED) - die("pre_thread_warm: thread %d not paused\n", - th->tid); - - if (th->cpu == NULL) - die("pre_thread_warm: thread %d doesn't have a CPU\n", - th->tid); - - thread_set_state(th, TH_ST_WARMING); - update_cpu(th->cpu); -} - -static void -pre_thread(struct ovni_emu *emu) -{ - struct ovni_ethread *th = emu->cur_thread; - struct ovni_ev *ev = emu->cur_ev; - - switch (ev->header.value) { - case 'C': /* create */ - dbg("thread %d creates a new thread at cpu=%d with args=%x %x\n", - th->tid, - ev->payload.u32[0], - ev->payload.u32[1], - ev->payload.u32[2]); - - break; - case 'x': - pre_thread_execute(emu, th); - break; - case 'e': - pre_thread_end(th); - break; - case 'p': - pre_thread_pause(th); - break; - case 'r': - pre_thread_resume(th); - break; - case 'c': - pre_thread_cool(th); - break; - case 'w': - pre_thread_warm(th); - break; - default: - err("unknown thread event value %c\n", - ev->header.value); - exit(EXIT_FAILURE); - } -} - -static void -pre_affinity_set(struct ovni_emu *emu) -{ - struct ovni_ethread *th = emu->cur_thread; - int cpuid = emu->cur_ev->payload.i32[0]; - - if (th->cpu == NULL) - edie(emu, "pre_affinity_set: thread %d doesn't have a CPU\n", - th->tid); - - if (!th->is_active) - edie(emu, "pre_affinity_set: thread %d is not active\n", - th->tid); - - /* Migrate current cpu to the one at cpuid */ - struct ovni_cpu *newcpu = emu_get_cpu(emu->cur_loom, cpuid); - - /* The CPU is already properly set, return */ - if (th->cpu == newcpu) - return; - - cpu_migrate_thread(th->cpu, th, newcpu); - thread_migrate_cpu(th, newcpu); - - // dbg("cpu %d now runs %d\n", cpuid, th->tid); -} - -static void -pre_affinity_remote(struct ovni_emu *emu) -{ - int32_t cpuid = emu->cur_ev->payload.i32[0]; - int32_t tid = emu->cur_ev->payload.i32[1]; - - struct ovni_ethread *remote_th = emu_get_thread(emu->cur_proc, tid); - - if (remote_th == NULL) { - /* Search the thread in other processes of the loom if - * not found in the current one */ - struct ovni_loom *loom = emu->cur_loom; - - for (size_t i = 0; i < loom->nprocs; i++) { - struct ovni_eproc *proc = &loom->proc[i]; - - /* Skip the current process */ - if (proc == emu->cur_proc) - continue; - - remote_th = emu_get_thread(proc, tid); - - if (remote_th) - break; - } - - if (remote_th == NULL) { - err("thread tid %d not found: cannot set affinity remotely\n", - tid); - abort(); - } - } - - /* The remote_th cannot be in states dead or unknown */ - if (remote_th->state == TH_ST_DEAD) - edie(emu, "pre_affinity_remote: remote thread %d in state DEAD\n", - remote_th->tid); - - if (remote_th->state == TH_ST_UNKNOWN) - edie(emu, "pre_affinity_remote: remote thread %d in state UNKNOWN\n", - remote_th->tid); - - /* It must have an assigned CPU */ - if (remote_th->cpu == NULL) - edie(emu, "pre_affinity_remote: remote thread %d has no CPU\n", - remote_th->tid); - - /* Migrate current cpu to the one at cpuid */ - struct ovni_cpu *newcpu = emu_get_cpu(emu->cur_loom, cpuid); - - cpu_migrate_thread(remote_th->cpu, remote_th, newcpu); - thread_migrate_cpu(remote_th, newcpu); - - // dbg("remote_th %d switches to cpu %d by remote petition\n", tid, - // cpuid); -} - -static void -pre_affinity(struct ovni_emu *emu) -{ - // emu_emit(emu); - switch (emu->cur_ev->header.value) { - case 's': - pre_affinity_set(emu); - break; - case 'r': - pre_affinity_remote(emu); - break; - default: - dbg("unknown affinity event value %c\n", - emu->cur_ev->header.value); - break; - } -} - -static int -compare_int64(const void *a, const void *b) -{ - int64_t aa = *(const int64_t *) a; - int64_t bb = *(const int64_t *) b; - - if (aa < bb) - return -1; - else if (aa > bb) - return +1; - else - return 0; -} - -static void -pre_burst(struct ovni_emu *emu) -{ - int64_t dt = 0; - - UNUSED(dt); - - struct ovni_ethread *th = emu->cur_thread; - - if (th->nbursts >= MAX_BURSTS) { - err("too many bursts: ignored\n"); - return; - } - - th->burst_time[th->nbursts++] = emu->delta_time; - if (th->nbursts == MAX_BURSTS) { - int n = MAX_BURSTS - 1; - int64_t deltas[MAX_BURSTS - 1]; - for (int i = 0; i < n; i++) { - deltas[i] = th->burst_time[i + 1] - th->burst_time[i]; - } - - qsort(deltas, n, sizeof(int64_t), compare_int64); - - double avg = 0.0; - double maxdelta = 0; - for (int i = 0; i < n; i++) { - if (deltas[i] > maxdelta) - maxdelta = deltas[i]; - avg += deltas[i]; - } - - avg /= (double) n; - double median = deltas[n / 2]; - - err("%s burst stats: median %.0f ns, avg %.1f ns, max %.0f ns\n", - emu->cur_loom->dname, median, avg, maxdelta); - - th->nbursts = 0; - } -} - -static void -pre_flush(struct ovni_emu *emu) -{ - struct ovni_ethread *th = emu->cur_thread; - struct ovni_chan *chan_th = &th->chan[CHAN_OVNI_FLUSH]; - - switch (emu->cur_ev->header.value) { - case '[': - chan_push(chan_th, ST_OVNI_FLUSHING); - break; - case ']': - chan_pop(chan_th, ST_OVNI_FLUSHING); - break; - default: - err("unexpected value '%c' (expecting '[' or ']')\n", - emu->cur_ev->header.value); - abort(); - } -} - -void -hook_pre_ovni(struct ovni_emu *emu) -{ - if (emu->cur_ev->header.model != 'O') - return; - - switch (emu->cur_ev->header.category) { - case 'H': - pre_thread(emu); - break; - case 'A': - pre_affinity(emu); - break; - case 'B': - pre_burst(emu); - break; - case 'F': - pre_flush(emu); - break; - default: - dbg("unknown ovni event category %c\n", - emu->cur_ev->header.category); - break; - } -}