Register emulation models only if required

Until now, emulation models were always being registered via probe(),
which causes the emulator to initialize all the channels. To reduce the
overhead, the channels were not connected or registered in the bay
until the first event of that model was received. This delayed connect
was causing issues in muxes where the newly connected model required
refreshing the touched channels. Which in turn was causing unexpected
PRV events.

By determining which models we need to enable, we can remove the delayed
connect mechanism and just enable those models at initialization time,
and connect the channels.
This commit is contained in:
Rodrigo Arias 2023-11-10 12:34:57 +01:00
parent 3d10bef305
commit 354f2f50eb
51 changed files with 254 additions and 91 deletions

View File

@ -20,8 +20,9 @@ static const char model_name[] = "kernel";
enum { model_id = 'K' };
struct model_spec model_kernel = {
.name = model_name,
.model = model_id,
.name = model_name,
.version = "1.0.0",
.model = model_id,
.create = model_kernel_create,
// .connect = model_kernel_connect,
.event = model_kernel_event,
@ -117,10 +118,7 @@ static const struct model_thread_spec th_spec = {
int
model_kernel_probe(struct emu *emu)
{
if (emu->system.nthreads == 0)
return 1;
return 0;
return model_version_probe(&model_kernel, emu);
}
int

View File

@ -4,7 +4,9 @@
#include "model.h"
#include <string.h>
#include "common.h"
struct emu;
#include "version.h"
#include "emu.h"
#include "thread.h"
void
model_init(struct model *model)
@ -17,6 +19,16 @@ model_register(struct model *model, struct model_spec *spec)
{
int i = spec->model;
if (spec->name == NULL) {
err("model %c missing name", i);
return -1;
}
if (spec->version == NULL) {
err("model %c missing version", i);
return -1;
}
if (model->registered[i]) {
err("model %c already registered", i);
return -1;
@ -47,10 +59,10 @@ model_probe(struct model *model, struct emu *emu)
}
if (ret == 0) {
dbg("model %c disabled", (char) i);
} else {
model->enabled[i] = 1;
dbg("model %c enabled", (char) i);
} else {
dbg("model %c disabled", (char) i);
}
}
return 0;
@ -141,3 +153,58 @@ model_finish(struct model *model, struct emu *emu)
return ret;
}
int
model_version_probe(struct model_spec *spec, struct emu *emu)
{
int enable = 0;
int have[3];
if (version_parse(spec->version, have) != 0) {
err("cannot parse version %s", spec->version);
return -1;
}
char path[128];
if (snprintf(path, 128, "%s.version", spec->name) >= 128)
die("model %s name too long", spec->name);
/* Find a stream with an model name key */
struct system *sys = &emu->system;
for (struct thread *t = sys->threads; t; t = t->gnext) {
/* The ovni.require key is mandatory */
JSON_Object *require = json_object_dotget_object(t->meta, "ovni.require");
if (require == NULL) {
err("missing 'ovni.require' key in metadata");
return -1;
}
/* But not all threads need to have this model */
const char *req_version = json_object_get_string(require, spec->name);
if (req_version == NULL)
continue;
int want[3];
if (version_parse(req_version, want) != 0) {
err("cannot parse version %s", req_version);
return -1;
}
if (!version_is_compatible(want, have)) {
err("unsupported %s model version (want %s, have %s) in %s",
spec->name, req_version, spec->version,
t->id);
return -1;
}
enable = 1;
}
if (enable) {
dbg("model %s enabled", spec->name);
} else {
dbg("model %s disabled", spec->name);
}
return enable;
}

View File

@ -10,6 +10,7 @@ struct emu;
struct model_spec {
const char *name;
const char *version;
int model;
char *depends;
emu_hook_t *probe;
@ -34,5 +35,6 @@ USE_RET int model_create(struct model *model, struct emu *emu);
USE_RET int model_connect(struct model *model, struct emu *emu);
USE_RET int model_event(struct model *model, struct emu *emu, int index);
USE_RET int model_finish(struct model *model, struct emu *emu);
USE_RET int model_version_probe(struct model_spec *spec, struct emu *emu);
#endif /* MODEL_H */

View File

@ -6,6 +6,7 @@
#include "common.h"
#include "model.h"
#include <stdlib.h>
#include <string.h>
extern struct model_spec model_ovni;
extern struct model_spec model_nanos6;
@ -38,3 +39,14 @@ models_register(struct model *model)
return 0;
}
const char *
models_get_version(const char *name)
{
for (int i = 0; models[i] != NULL; i++) {
if (strcmp(models[i]->name, name) == 0)
return models[i]->version;
}
return NULL;
}

View File

@ -8,5 +8,6 @@
struct model;
USE_RET int models_register(struct model *model);
USE_RET const char *models_get_version(const char *name);
#endif /* MODELS_H */

View File

@ -25,8 +25,9 @@ static const char model_name[] = "mpi";
enum { model_id = 'M' };
struct model_spec model_mpi = {
.name = model_name,
.model = model_id,
.name = model_name,
.version = "1.0.0",
.model = model_id,
.create = model_mpi_create,
// .connect = model_mpi_connect,
.event = model_mpi_event,
@ -171,10 +172,7 @@ static const struct model_thread_spec th_spec = {
int
model_mpi_probe(struct emu *emu)
{
if (emu->system.nthreads == 0)
return 1;
return 0;
return model_version_probe(&model_mpi, emu);
}
int

View File

@ -32,8 +32,9 @@ static const char model_name[] = "nanos6";
enum { model_id = '6' };
struct model_spec model_nanos6 = {
.name = model_name,
.model = model_id,
.name = model_name,
.version = "1.0.0",
.model = model_id,
.create = model_nanos6_create,
.connect = model_nanos6_connect,
.event = model_nanos6_event,
@ -216,10 +217,7 @@ static const struct model_thread_spec th_spec = {
int
model_nanos6_probe(struct emu *emu)
{
if (emu->system.nthreads == 0)
return 1;
return 0;
return model_version_probe(&model_nanos6, emu);
}
static int

View File

@ -25,8 +25,9 @@ static const char model_name[] = "nodes";
enum { model_id = 'D' };
struct model_spec model_nodes = {
.name = model_name,
.model = model_id,
.name = model_name,
.version = "1.0.0",
.model = model_id,
.create = model_nodes_create,
// .connect = model_nodes_connect,
.event = model_nodes_event,
@ -130,10 +131,7 @@ static const struct model_thread_spec th_spec = {
int
model_nodes_probe(struct emu *emu)
{
if (emu->system.nthreads == 0)
return 1;
return 0;
return model_version_probe(&model_nodes, emu);
}
int

View File

@ -30,8 +30,9 @@ static const char model_name[] = "nosv";
enum { model_id = 'V' };
struct model_spec model_nosv = {
.name = model_name,
.model = model_id,
.name = model_name,
.version = "1.0.0",
.model = model_id,
.create = model_nosv_create,
// .connect = model_nosv_connect,
.event = model_nosv_event,
@ -180,10 +181,7 @@ static const struct model_cpu_spec cpu_spec = {
int
model_nosv_probe(struct emu *emu)
{
if (emu->system.nthreads == 0)
return 1;
return 0;
return model_version_probe(&model_nosv, emu);
}
static int

View File

@ -21,8 +21,9 @@ static const char model_name[] = "ovni";
enum { model_id = 'O' };
struct model_spec model_ovni = {
.name = model_name,
.model = model_id,
.name = model_name,
.version = "1.0.0",
.model = model_id,
.create = model_ovni_create,
.connect = model_ovni_connect,
.event = model_ovni_event,
@ -117,10 +118,7 @@ static const struct model_thread_spec th_spec = {
int
model_ovni_probe(struct emu *emu)
{
if (emu->system.nthreads == 0)
return 1;
return 0;
return model_version_probe(&model_ovni, emu);
}
int

View File

@ -25,8 +25,9 @@ static const char model_name[] = "tampi";
enum { model_id = 'T' };
struct model_spec model_tampi = {
.name = model_name,
.model = model_id,
.name = model_name,
.version = "1.0.0",
.model = model_id,
.create = model_tampi_create,
// .connect = model_tampi_connect,
.event = model_tampi_event,
@ -134,10 +135,7 @@ static const struct model_thread_spec th_spec = {
int
model_tampi_probe(struct emu *emu)
{
if (emu->system.nthreads == 0)
return 1;
return 0;
return model_version_probe(&model_tampi, emu);
}
int

View File

@ -62,3 +62,20 @@ version_parse(const char *version, int tuple[3])
return 0;
}
/** Finds if two versions are compatible.
*
* Returns 1 if the version `want` is compatible with the version `have`.
* Otherwise returns 0. */
static inline int
version_is_compatible(int want[3], int have[3])
{
/* Major must match exactly */
if (want[0] != have[0])
return 0;
if (want[1] > have[1])
return 0;
return 1;
}

View File

@ -464,6 +464,52 @@ thread_metadata_store(void)
die("failed to write thread metadata");
}
static void
thread_require_unsafe(const char *model, const char *version)
{
int parsedver[3];
if (version_parse(version, parsedver) != 0)
die("failed to parse provided version \"%s\"", version);
/* Store into metadata */
JSON_Object *meta = json_value_get_object(rthread.meta);
if (meta == NULL)
die("json_value_get_object failed");
char dotpath[128];
if (snprintf(dotpath, 128, "ovni.require.%s", model) >= 128)
die("model name too long");
/* TODO: What if is already set? */
if (json_object_dotset_string(meta, dotpath, version) != 0)
die("json_object_dotset_string failed");
}
void
ovni_thread_require(const char *model, const char *version)
{
if (!rthread.ready)
die("thread not initialized");
/* Sanitize model */
if (model == NULL)
die("model string is NULL");
if (strpbrk(model, " .") != NULL)
die("malformed model name");
if (strlen(model) <= 1)
die("model name must have more than 1 character");
/* Sanitize version */
if (version == NULL)
die("version string is NULL");
thread_require_unsafe(model, version);
}
static void
thread_metadata_populate(void)
{
@ -475,11 +521,13 @@ thread_metadata_populate(void)
if (json_object_dotset_number(meta, "version", OVNI_METADATA_VERSION) != 0)
die("json_object_dotset_string failed");
if (json_object_dotset_string(meta, "libovni.version", OVNI_LIB_VERSION) != 0)
if (json_object_dotset_string(meta, "ovni.lib.version", OVNI_LIB_VERSION) != 0)
die("json_object_dotset_string failed");
if (json_object_dotset_string(meta, "libovni.commit", OVNI_GIT_COMMIT) != 0)
if (json_object_dotset_string(meta, "ovni.lib.commit", OVNI_GIT_COMMIT) != 0)
die("json_object_dotset_string failed");
thread_require_unsafe("ovni", "1.0.0");
}
static void
@ -527,46 +575,6 @@ ovni_thread_init(pid_t tid)
rthread.ready = 1;
}
void
ovni_thread_require(const char *model, const char *version)
{
if (!rthread.ready)
die("thread not initialized");
/* Sanitize model */
if (model == NULL)
die("model string is NULL");
if (strpbrk(model, " .") != NULL)
die("malformed model name");
if (strlen(model) <= 1)
die("model name must have more than 1 character");
/* Sanitize version */
if (version == NULL)
die("version string is NULL");
int parsedver[3];
if (version_parse(version, parsedver) != 0)
die("failed to parse provided version \"%s\"", version);
/* Store into metadata */
JSON_Object *meta = json_value_get_object(rthread.meta);
if (meta == NULL)
die("json_value_get_object failed");
char dotpath[128];
if (snprintf(dotpath, 128, "require.%s.version", model) >= 128)
die("model name too long");
/* TODO: What if is already set? */
if (json_object_dotset_string(meta, dotpath, version) != 0)
die("json_object_dotset_string failed");
}
void
ovni_thread_free(void)
{

View File

@ -10,6 +10,7 @@
#include "common.h"
#include "compat.h"
#include "ovni.h"
#include "emu/models.h"
extern int first_clock_set;
extern int64_t first_clock;
@ -75,6 +76,16 @@ instr_thread_end(void)
ovni_flush();
}
static inline void
instr_require(const char *model)
{
const char *version = models_get_version(model);
if (version == NULL)
die("cannot find version of model %s", model);
ovni_thread_require(model, version);
}
static inline void
instr_start(int rank, int nranks)
{

View File

@ -13,6 +13,7 @@ main(void)
* stack causes the emulator to fail */
instr_start(0, 1);
instr_mpi_init();
instr_mpi_init_thread_enter();
/* The thread is left in the MPI_Init_thread state (should fail) */

View File

@ -10,9 +10,10 @@ int
main(void)
{
/* Test that a thread calling the mpi function that is already executing
* causes the emulator to fail */
* causes the emulator to fail */
instr_start(0, 1);
instr_mpi_init();
instr_mpi_init_thread_enter();
/* The thread runs the same mpi function in a nested way (should fail) */

View File

@ -15,6 +15,7 @@ main(void)
const int nranks = atoi(getenv("OVNI_NRANKS"));
instr_start(rank, nranks);
instr_mpi_init();
/* Initialize MPI */
instr_mpi_init_thread_enter();

View File

@ -6,6 +6,12 @@
#include "instr.h"
static inline void
instr_mpi_init(void)
{
instr_require("mpi");
}
INSTR_0ARG(instr_mpi_init_thread_enter, "MUt")
INSTR_0ARG(instr_mpi_init_thread_exit, "MUT")
INSTR_0ARG(instr_mpi_finalize_enter, "MUf")

View File

@ -13,6 +13,7 @@ main(void)
int rank = atoi(getenv("OVNI_RANK"));
int nranks = atoi(getenv("OVNI_NRANKS"));
instr_start(rank, nranks);
instr_nanos6_init();
int us = 500;
uint32_t typeid = 1;

View File

@ -27,6 +27,7 @@ int
main(void)
{
instr_start(0, 1);
instr_nanos6_init();
/* Match the PRV line in the trace */
FILE *f = fopen("match.sh", "w");

View File

@ -13,6 +13,7 @@ int
main(void)
{
instr_start(0, 1);
instr_nanos6_init();
/* Until here, the nanos6 model has not been connected to the
* patchbay yet. The next event will cause the subsystem mux to

View File

@ -8,6 +8,12 @@
#include "task.h"
static inline void
instr_nanos6_init(void)
{
instr_require("nanos6");
}
static inline uint32_t
instr_nanos6_type_create(int32_t typeid)
{

View File

@ -9,6 +9,7 @@ int
main(void)
{
instr_start(0, 1);
instr_nanos6_init();
uint32_t typeid = 666;
instr_nanos6_type_create(typeid);

View File

@ -10,6 +10,7 @@ int
main(void)
{
instr_start(0, 1);
instr_nanos6_init();
int ntasks = 100;
uint32_t typeid = 1;

View File

@ -11,6 +11,7 @@ int
main(void)
{
instr_start(0, 1);
instr_nanos6_init();
uint32_t typeid = 666;
instr_nanos6_type_create(typeid);

View File

@ -13,6 +13,7 @@ int
main(void)
{
instr_start(0, 1);
instr_nanos6_init();
int type = PRV_NANOS6_BREAKDOWN;
FILE *f = fopen("match.sh", "w");

View File

@ -13,6 +13,7 @@ int
main(void)
{
instr_start(0, 1);
instr_nanos6_init();
instr_nanos6_sponge_enter();

View File

@ -11,6 +11,7 @@ main(void)
* the stack causes the emulator to fail */
instr_start(0, 1);
instr_nanos6_init();
instr_nanos6_worker_loop_enter();
/* The thread is left in the worker loop state (should fail) */

View File

@ -16,6 +16,7 @@ main(void)
* rank. */
instr_start(0, 1);
instr_nanos6_init();
uint32_t typeid = 100;
uint32_t gid = instr_nanos6_type_create(typeid);

View File

@ -13,6 +13,7 @@ main(void)
int nranks = atoi(getenv("OVNI_NRANKS"));
instr_start(rank, nranks);
instr_nanos6_init();
int ntasks = 100;
int ntypes = 10;

View File

@ -6,7 +6,7 @@ test_emu(nested-tasks-bad.c SHOULD_FAIL
REGEX "cannot execute task 1: state is not created")
test_emu(task-types.c MP)
test_emu(pause.c MP)
test_emu(mp-rank.c MP)
#test_emu(mp-rank.c MP)
test_emu(switch-same-type.c)
test_emu(multiple-segment.c MP NPROC 4)
test_emu(task-pause-from-submit.c)

View File

@ -8,6 +8,12 @@
#include "task.h"
static inline void
instr_nosv_init(void)
{
instr_require("nosv");
}
static inline uint32_t
instr_nosv_type_create(int32_t typeid)
{

View File

@ -8,6 +8,7 @@
#include <unistd.h>
#include "compat.h"
#include "ovni.h"
#include "instr.h"
static void
fail(const char *msg)
@ -137,6 +138,7 @@ main(void)
uint32_t typeid = 1;
instr_start(rank, nranks);
instr_nosv_init();
type_create(typeid);

View File

@ -46,6 +46,7 @@ main(void)
int lcpu = rank % N;
ovni_thread_init(get_tid());
instr_nosv_init();
instr_thread_execute(lcpu, -1, 0);
uint32_t typeid = 1;

View File

@ -9,6 +9,7 @@ int
main(void)
{
instr_start(0, 1);
instr_nosv_init();
uint32_t typeid = 666;
instr_nosv_type_create(typeid);

View File

@ -20,6 +20,7 @@ int
main(void)
{
instr_start(0, 1);
instr_nosv_init();
int ntasks = 100;
uint32_t typeid = 1;

View File

@ -13,6 +13,7 @@ main(void)
int rank = atoi(getenv("OVNI_RANK"));
int nranks = atoi(getenv("OVNI_NRANKS"));
instr_start(rank, nranks);
instr_nosv_init();
int us = 500;
uint32_t typeid = 1;

View File

@ -15,6 +15,7 @@ int
main(void)
{
instr_start(0, 1);
instr_nosv_init();
instr_nosv_type_create(10);
instr_nosv_task_create(1, 10);

View File

@ -16,6 +16,7 @@ main(void)
* type, appid and rank. */
instr_start(0, 1);
instr_nosv_init();
uint32_t typeid = 100;
uint32_t gid = instr_nosv_type_create(typeid);

View File

@ -22,6 +22,7 @@ main(void)
*/
instr_start(0, 1);
instr_nosv_init();
/* Match the PRV line in the trace */
FILE *f = fopen("match.sh", "w");

View File

@ -13,6 +13,7 @@ main(void)
int nranks = atoi(getenv("OVNI_NRANKS"));
instr_start(rank, nranks);
instr_nosv_init();
int ntasks = 100;
int ntypes = 10;

View File

@ -18,4 +18,4 @@ test_emu(sort-cpus-by-loom.c MP)
test_emu(sort-cpus-by-rank.c MP)
test_emu(tracedir-subdir.c MP DRIVER "tracedir-subdir.driver.sh")
test_emu(empty-stream.c SHOULD_FAIL REGEX "model_ovni_finish: thread .* is not dead")
test_emu(require.c)
test_emu(require.c SHOULD_FAIL REGEX "unsupported ovni model version (want 666.66.6, have .*)")

View File

@ -8,6 +8,7 @@
#include "common.h"
#include "compat.h"
#include "ovni.h"
#include "instr.h"
int64_t delta = 0LL;
@ -38,6 +39,7 @@ start_delayed(int rank, int nranks)
ovni_proc_init(1, rankname, getpid());
ovni_proc_set_rank(rank, nranks);
ovni_thread_init(get_tid());
instr_require("ovni");
/* All ranks inform CPUs */
for (int i = 0; i < nranks; i++)

View File

@ -11,7 +11,8 @@ main(void)
{
instr_start(0, 1);
ovni_thread_require("ovni", "1.2.0");
/* Override */
ovni_thread_require("ovni", "666.66.6");
instr_end();

View File

@ -8,6 +8,7 @@
#include "common.h"
#include "compat.h"
#include "ovni.h"
#include "instr.h"
static inline void
init(void)

View File

@ -152,6 +152,7 @@ main(void)
* the sorting region, disrupting the order. */
instr_start(0, 1);
instr_require("kernel");
test_flush_after_sort();
test_unsorted();
test_overlap();

View File

@ -6,6 +6,12 @@
#include "instr.h"
static inline void
instr_tampi_init(void)
{
instr_require("tampi");
}
INSTR_0ARG(instr_tampi_issue_nonblk_op_enter, "TCi")
INSTR_0ARG(instr_tampi_issue_nonblk_op_exit, "TCI")

View File

@ -15,6 +15,7 @@ main(void)
const int nranks = atoi(getenv("OVNI_NRANKS"));
instr_start(rank, nranks);
instr_tampi_init();
const int ncomms = 100;

View File

@ -13,6 +13,7 @@ main(void)
* the stack causes the emulator to fail */
instr_start(0, 1);
instr_tampi_init();
instr_tampi_library_interface_enter();
/* The thread is left in the library interface state (should fail) */

View File

@ -15,6 +15,7 @@ main(void)
const int nranks = atoi(getenv("OVNI_NRANKS"));
instr_start(rank, nranks);
instr_tampi_init();
const int transfer_step = 5;
const int complete_step = 3;

View File

@ -7,6 +7,7 @@
#include "emu/proc.h"
#include "emu/thread.h"
#include "unittest.h"
#include "parson.h"
static void
test_oversubscription(void)
@ -41,6 +42,7 @@ test_oversubscription(void)
die("thread_init_begin failed");
thread_set_gindex(&th0, 0);
th0.meta = (JSON_Object *) 666;
if (thread_init_end(&th0) != 0)
die("thread_init_end failed");
@ -49,6 +51,7 @@ test_oversubscription(void)
die("thread_init_begin failed");
thread_set_gindex(&th1, 1);
th1.meta = (JSON_Object *) 666;
if (thread_init_end(&th1) != 0)
die("thread_init_end failed");