ovni/emu_ovni.c

481 lines
9.4 KiB
C
Raw Normal View History

2021-07-28 11:56:35 +02:00
#include "ovni.h"
#include "emu.h"
2021-08-02 10:08:58 +02:00
#include "prv.h"
2021-07-28 11:56:35 +02:00
#include <assert.h>
/* The emulator ovni module provides the execution model by tracking the thread
* state and which threads run in each CPU */
void
update_cpu(struct ovni_emu *emu, struct ovni_cpu *cpu)
{
struct ovni_loom *loom;
loom = emu->cur_loom;
if(loom->nupdated_cpus >= OVNI_MAX_CPU)
abort();
if(cpu->updated)
return;
cpu->updated = 1;
loom->updated_cpu[loom->nupdated_cpus++] = cpu;
}
2021-07-28 19:12:20 +02:00
struct ovni_cpu *
2021-08-02 21:13:03 +02:00
emu_get_cpu(struct ovni_loom *loom, int cpuid)
2021-07-28 19:12:20 +02:00
{
assert(cpuid < OVNI_MAX_CPU);
if(cpuid < 0)
{
2021-08-02 21:13:03 +02:00
return &loom->vcpu;
2021-07-28 19:12:20 +02:00
}
2021-08-02 21:13:03 +02:00
return &loom->cpu[cpuid];
2021-07-28 19:12:20 +02:00
}
int
emu_cpu_find_thread(struct ovni_cpu *cpu, struct ovni_ethread *thread)
{
int i;
for(i=0; i<cpu->nthreads; i++)
if(cpu->thread[i] == thread)
break;
/* Not found */
if(i >= cpu->nthreads)
return -1;
return i;
}
void
emu_cpu_add_thread(struct ovni_emu *emu, struct ovni_cpu *cpu, struct ovni_ethread *thread)
2021-07-28 19:12:20 +02:00
{
/* Found, abort */
if(emu_cpu_find_thread(cpu, thread) >= 0)
abort();
assert(cpu->nthreads < OVNI_MAX_THR);
cpu->thread[cpu->nthreads++] = thread;
update_cpu(emu, cpu);
2021-07-28 19:12:20 +02:00
}
void
emu_cpu_remove_thread(struct ovni_emu *emu, struct ovni_cpu *cpu, struct ovni_ethread *thread)
2021-07-28 19:12:20 +02:00
{
int i, j;
i = emu_cpu_find_thread(cpu, thread);
/* Not found, abort */
if(i < 0)
abort();
for(j=i; j+1 < cpu->nthreads; j++)
{
cpu->thread[i] = cpu->thread[j+1];
}
cpu->nthreads--;
update_cpu(emu, cpu);
2021-07-28 19:12:20 +02:00
}
2021-07-28 11:56:35 +02:00
static void
2021-08-02 21:13:03 +02:00
print_threads_state(struct ovni_loom *loom)
2021-07-28 11:56:35 +02:00
{
struct ovni_cpu *cpu;
int i, j;
2021-08-02 21:13:03 +02:00
for(i=0; i<loom->ncpus; i++)
2021-07-28 11:56:35 +02:00
{
2021-08-02 21:13:03 +02:00
cpu = &loom->cpu[i];
2021-07-28 11:56:35 +02:00
dbg("-- cpu %d runs %lu threads:", i, cpu->nthreads);
2021-07-28 11:56:35 +02:00
for(j=0; j<cpu->nthreads; j++)
{
dbg(" %d", cpu->thread[j]->tid);
}
dbg("\n");
}
dbg("-- vcpu runs %lu threads:", loom->vcpu.nthreads);
2021-08-02 21:13:03 +02:00
for(j=0; j<loom->vcpu.nthreads; j++)
2021-07-28 11:56:35 +02:00
{
2021-08-02 21:13:03 +02:00
dbg(" %d", loom->vcpu.thread[j]->tid);
2021-07-28 11:56:35 +02:00
}
dbg("\n");
}
static void
ev_thread_execute(struct ovni_emu *emu)
{
struct ovni_cpu *cpu;
int cpuid;
/* The thread cannot be already running */
assert(emu->cur_thread->state != TH_ST_RUNNING);
cpuid = emu->cur_ev->payload.i32[0];
2021-07-29 17:46:25 +02:00
//dbg("thread %d runs in cpuid %d\n", emu->cur_thread->tid,
// cpuid);
2021-08-02 21:13:03 +02:00
cpu = emu_get_cpu(emu->cur_loom, cpuid);
2021-07-28 11:56:35 +02:00
emu->cur_thread->state = TH_ST_RUNNING;
emu->cur_thread->cpu = cpu;
emu_cpu_add_thread(emu, cpu, emu->cur_thread);
2021-07-28 11:56:35 +02:00
}
static void
ev_thread_end(struct ovni_emu *emu)
{
assert(emu->cur_thread->state == TH_ST_RUNNING);
assert(emu->cur_thread->cpu);
emu_cpu_remove_thread(emu, emu->cur_thread->cpu, emu->cur_thread);
2021-07-28 11:56:35 +02:00
emu->cur_thread->state = TH_ST_DEAD;
emu->cur_thread->cpu = NULL;
}
static void
ev_thread_pause(struct ovni_emu *emu)
{
assert(emu->cur_thread->state == TH_ST_RUNNING);
assert(emu->cur_thread->cpu);
emu_cpu_remove_thread(emu, emu->cur_thread->cpu, emu->cur_thread);
2021-07-28 11:56:35 +02:00
emu->cur_thread->state = TH_ST_PAUSED;
}
static void
ev_thread_resume(struct ovni_emu *emu)
{
assert(emu->cur_thread->state == TH_ST_PAUSED);
assert(emu->cur_thread->cpu);
emu_cpu_add_thread(emu, emu->cur_thread->cpu, emu->cur_thread);
2021-07-28 11:56:35 +02:00
emu->cur_thread->state = TH_ST_RUNNING;
}
static void
ev_thread(struct ovni_emu *emu)
{
struct ovni_ev *ev;
struct ovni_cpu *cpu;
struct ovni_ethread *thread, *remote_thread;
int i;
2021-07-29 17:46:25 +02:00
//emu_emit(emu);
2021-07-28 11:56:35 +02:00
thread = emu->cur_thread;
cpu = thread->cpu;
ev = emu->cur_ev;
2021-07-30 20:08:40 +02:00
switch(ev->header.value)
2021-07-28 11:56:35 +02:00
{
case 'c': /* create */
dbg("thread %d creates a new thread at cpu=%d with args=%x %x\n",
thread->tid,
ev->payload.u32[0],
ev->payload.u32[1],
ev->payload.u32[2]);
break;
case 'x': ev_thread_execute(emu); break;
case 'e': ev_thread_end(emu); break;
case 'p': ev_thread_pause(emu); break;
case 'r': ev_thread_resume(emu); break;
default:
2021-09-27 17:42:14 +02:00
err("unknown thread event value %c\n",
ev->header.value);
exit(EXIT_FAILURE);
2021-07-28 11:56:35 +02:00
}
2021-09-27 17:42:14 +02:00
/* All but create events change the thread and CPU state: inject two
* virtual events to notify other modules. The order is important. */
if(ev->header.value != 'c')
{
emu_virtual_ev(emu, "*Hc");
emu_virtual_ev(emu, "*Cc");
}
2021-07-28 11:56:35 +02:00
}
static void
ev_affinity_set(struct ovni_emu *emu)
{
int cpuid;
struct ovni_cpu *newcpu;
cpuid = emu->cur_ev->payload.i32[0];
assert(emu->cur_thread->state == TH_ST_RUNNING);
assert(emu->cur_thread->cpu);
/* Migrate current cpu to the one at cpuid */
2021-08-02 21:13:03 +02:00
newcpu = emu_get_cpu(emu->cur_loom, cpuid);
2021-07-28 11:56:35 +02:00
emu_cpu_remove_thread(emu, emu->cur_thread->cpu, emu->cur_thread);
emu_cpu_add_thread(emu, newcpu, emu->cur_thread);
2021-07-28 11:56:35 +02:00
emu->cur_thread->cpu = newcpu;
2021-07-29 17:46:25 +02:00
//dbg("cpu %d now runs %d\n", cpuid, emu->cur_thread->tid);
2021-07-28 11:56:35 +02:00
}
static void
ev_affinity_remote(struct ovni_emu *emu)
{
int cpuid, tid;
struct ovni_cpu *newcpu;
struct ovni_ethread *thread;
cpuid = emu->cur_ev->payload.i32[0];
tid = emu->cur_ev->payload.i32[1];
thread = emu_get_thread(emu->cur_proc, tid);
2021-07-28 11:56:35 +02:00
assert(thread);
/* The thread may still be running */
assert(thread->state == TH_ST_RUNNING ||
thread->state == TH_ST_PAUSED);
/* It must have an assigned CPU */
2021-07-28 11:56:35 +02:00
assert(thread->cpu);
/* Migrate current cpu to the one at cpuid */
2021-08-02 21:13:03 +02:00
newcpu = emu_get_cpu(emu->cur_loom, cpuid);
2021-07-28 11:56:35 +02:00
/* If running, update the CPU thread lists */
if(thread->state == TH_ST_RUNNING)
{
emu_cpu_remove_thread(emu, thread->cpu, thread);
emu_cpu_add_thread(emu, newcpu, thread);
}
else
{
/* Otherwise, ensure that it is not in any CPU list */
assert(emu_cpu_find_thread(thread->cpu, thread) == -1);
assert(emu_cpu_find_thread(newcpu, thread) == -1);
}
2021-07-28 11:56:35 +02:00
/* Always set the assigned CPU in the thread */
2021-07-28 11:56:35 +02:00
thread->cpu = newcpu;
2021-07-29 17:46:25 +02:00
//dbg("thread %d switches to cpu %d by remote petition\n", tid,
// cpuid);
2021-07-28 11:56:35 +02:00
}
static void
ev_affinity(struct ovni_emu *emu)
{
2021-07-29 17:46:25 +02:00
//emu_emit(emu);
2021-07-30 20:08:40 +02:00
switch(emu->cur_ev->header.value)
2021-07-28 11:56:35 +02:00
{
case 's': ev_affinity_set(emu); break;
case 'r': ev_affinity_remote(emu); break;
default:
dbg("unknown affinity event value %c\n",
2021-07-30 20:08:40 +02:00
emu->cur_ev->header.value);
2021-07-28 11:56:35 +02:00
break;
}
}
void
2021-07-28 19:12:20 +02:00
hook_pre_ovni(struct ovni_emu *emu)
2021-07-28 11:56:35 +02:00
{
//emu_emit(emu);
if(emu->cur_ev->header.model != 'O')
return;
2021-07-30 20:08:40 +02:00
switch(emu->cur_ev->header.class)
2021-07-28 11:56:35 +02:00
{
case 'H': ev_thread(emu); break;
case 'A': ev_affinity(emu); break;
2021-08-03 19:56:31 +02:00
case 'B':
//dbg("burst %c\n", emu->cur_ev->header.value);
break;
2021-07-28 11:56:35 +02:00
default:
dbg("unknown ovni event class %c\n",
2021-07-30 20:08:40 +02:00
emu->cur_ev->header.class);
2021-07-28 11:56:35 +02:00
break;
}
2021-07-29 17:46:25 +02:00
//print_threads_state(emu);
2021-07-28 11:56:35 +02:00
}
2021-07-28 19:12:20 +02:00
static void
2021-08-02 10:08:58 +02:00
emit_thread_state(struct ovni_emu *emu)
2021-07-28 19:12:20 +02:00
{
2021-08-02 10:08:58 +02:00
int row, st, tid;
st = emu->cur_thread->state;
row = emu->cur_thread->gindex + 1;
tid = emu->cur_thread->tid;
prv_ev_thread(emu, row, PTT_THREAD_STATE, st);
2021-08-02 10:08:58 +02:00
if(st == TH_ST_RUNNING)
prv_ev_thread(emu, row, PTT_THREAD_TID, tid);
2021-08-02 10:08:58 +02:00
else
prv_ev_thread(emu, row, PTT_THREAD_TID, 0);
2021-08-02 10:08:58 +02:00
}
2021-07-29 17:46:25 +02:00
2021-08-02 10:08:58 +02:00
static void
emit_thread_count(struct ovni_emu *emu)
{
int i, n, row, pid, tid;
2021-08-02 21:13:03 +02:00
struct ovni_loom *loom;
loom = emu->cur_loom;
/* TODO: Use a table to quickly access the updated elements */
2021-07-28 19:12:20 +02:00
/* Check every CPU looking for a change in nthreads */
2021-08-02 21:13:03 +02:00
for(i=0; i<loom->ncpus; i++)
2021-07-28 19:12:20 +02:00
{
2021-08-02 21:13:03 +02:00
if(loom->cpu[i].last_nthreads != loom->cpu[i].nthreads)
2021-07-29 17:46:25 +02:00
{
2021-08-02 10:08:58 +02:00
/* Start at 1 */
row = loom->offset_ncpus + i + 1;
2021-08-02 21:13:03 +02:00
n = loom->cpu[i].nthreads;
prv_ev_cpu(emu, row, PTC_NTHREADS, n);
2021-08-02 10:08:58 +02:00
2021-08-02 21:13:03 +02:00
pid = n == 1 ? loom->cpu[i].thread[0]->proc->pid : 1;
prv_ev_cpu(emu, row, PTC_PROC_PID, pid);
2021-08-02 10:08:58 +02:00
2021-08-02 21:13:03 +02:00
tid = n == 1 ? loom->cpu[i].thread[0]->tid : 1;
prv_ev_cpu(emu, row, PTC_THREAD_TID, tid);
2021-07-29 17:46:25 +02:00
}
2021-07-28 19:12:20 +02:00
}
/* Same with the virtual CPU */
2021-08-02 21:13:03 +02:00
if(loom->vcpu.last_nthreads != loom->vcpu.nthreads)
2021-07-29 17:46:25 +02:00
{
2021-08-02 10:08:58 +02:00
/* Place the virtual CPU after the physical CPUs */
2021-08-02 21:13:03 +02:00
row = loom->ncpus + 1;
n = loom->vcpu.nthreads;
prv_ev_cpu(emu, row, PTC_NTHREADS, n);
2021-07-29 17:46:25 +02:00
2021-08-02 21:13:03 +02:00
pid = n == 1 ? loom->vcpu.thread[0]->proc->pid : 1;
prv_ev_cpu(emu, row, PTC_PROC_PID, pid);
2021-07-29 17:46:25 +02:00
2021-08-02 21:13:03 +02:00
tid = n == 1 ? loom->vcpu.thread[0]->tid : 1;
prv_ev_cpu(emu, row, PTC_THREAD_TID, tid);
2021-08-02 10:08:58 +02:00
}
2021-07-28 19:12:20 +02:00
}
2021-07-30 21:37:25 +02:00
static void
emit_current_pid(struct ovni_emu *emu)
{
if(emu->cur_thread->cpu == NULL)
return;
2021-08-02 10:08:58 +02:00
prv_ev_autocpu(emu, PTC_PROC_PID, emu->cur_proc->pid);
2021-07-30 21:37:25 +02:00
}
2021-07-28 19:12:20 +02:00
void
2021-07-30 21:37:25 +02:00
hook_emit_ovni(struct ovni_emu *emu)
2021-07-28 19:12:20 +02:00
{
if(emu->cur_ev->header.model != 'O')
return;
2021-07-30 20:08:40 +02:00
switch(emu->cur_ev->header.class)
2021-07-28 19:12:20 +02:00
{
case 'H':
2021-08-02 10:08:58 +02:00
emit_thread_state(emu);
/* falltrough */
2021-07-28 19:12:20 +02:00
case 'A':
2021-07-30 21:37:25 +02:00
emit_thread_count(emu);
2021-08-02 10:08:58 +02:00
//emit_current_pid(emu);
2021-07-28 19:12:20 +02:00
break;
default:
break;
}
}
/* Reset thread state */
static void
post_virtual_thread(struct ovni_emu *emu)
2021-07-28 19:12:20 +02:00
{
int i;
2021-08-02 21:13:03 +02:00
struct ovni_loom *loom;
loom = emu->cur_loom;
2021-07-28 19:12:20 +02:00
/* Should be executed *before* we reset the CPUs updated flags */
assert(loom->nupdated_cpus > 0);
2021-07-28 19:12:20 +02:00
/* Update last_nthreads in the CPUs */
2021-08-02 21:13:03 +02:00
for(i=0; i<loom->ncpus; i++)
loom->cpu[i].last_nthreads = loom->cpu[i].nthreads;
2021-07-28 19:12:20 +02:00
/* Fix the virtual CPU as well */
loom->vcpu.last_nthreads = loom->vcpu.nthreads;
}
/* Reset CPU state */
static void
post_virtual_cpu(struct ovni_emu *emu)
{
int i;
struct ovni_loom *loom;
loom = emu->cur_loom;
for(i=0; i<loom->nupdated_cpus; i++)
{
/* Remove the update flags */
assert(loom->updated_cpu[i]->updated == 1);
loom->updated_cpu[i]->updated = 0;
}
/* Fix the virtual CPU as well */
2021-08-02 21:13:03 +02:00
loom->vcpu.last_nthreads = loom->vcpu.nthreads;
/* Restore 0 updated CPUs */
loom->nupdated_cpus = 0;
}
static void
post_virtual(struct ovni_emu *emu)
{
switch(emu->cur_ev->header.class)
{
case 'H':
post_virtual_thread(emu);
break;
case 'C':
post_virtual_cpu(emu);
break;
default:
break;
}
}
void
hook_post_ovni(struct ovni_emu *emu)
{
switch(emu->cur_ev->header.model)
{
case '*':
post_virtual(emu);
break;
default:
break;
}
2021-07-28 19:12:20 +02:00
}