Project homepage Mailing List  Warmcat.com  API Docs  Github Mirror 
    npro  
 Modern all-safe Rust Network Protocol library supporting h1, h2, h3, ws, wt sans-IO and with socket IO + tls
git clone https://npro.rs/repo/npro
 
root / assets / linux-debian-buster.svg
Author[]Andy Green <andy@warmcat.com> 2026-05-17 10:00 UTC
Committer[]Andy Green <andy@warmcat.com> 2026-05-17 10:01 UTC
Tree6d97a3a0ac8a4e43ef0696492b4df9b3868fa71b   Raw Patch
 
sai-virt: phase5
sai-virt: phase5
diff --git a/src/virt/v-http-api.c b/src/virt/v-http-api.c index e76e26a..e0d3ae2 100644 --- a/src/virt/v-http-api.c +++ b/src/virt/v-http-api.c @@ -27,8 +27,34 @@ callback_virt_http(struct lws *wsi, enum lws_callback_reasons reason, if (len > 16 && !strncmp(path, "/auto-power-off/", 16)) { lws_strncpy(vm_id, path + 16, sizeof(vm_id)); lwsl_notice("%s: Received auto-power-off for %s\n", __func__, vm_id); - if (virt.ops) - virt.ops->destroy(&virt, vm_id); + + saiv_vm_t *found_vm = NULL; + lws_start_foreach_dll(struct lws_dll2 *, d, virt.plat_owner.head) { + saiv_plat_t *vp = lws_container_of(d, saiv_plat_t, list); + lws_start_foreach_dll(struct lws_dll2 *, v, vp->vm_owner.head) { + saiv_vm_t *vm = lws_container_of(v, saiv_vm_t, list); + if (!strcmp(vm->name, vm_id)) { + found_vm = vm; + break; + } + } lws_end_foreach_dll(v); + if (found_vm) + break; + } lws_end_foreach_dll(d); + + if (found_vm) { + if (virt.ops) + virt.ops->destroy(&virt, found_vm); + + if (found_vm->plat->starting_vms > 0) + found_vm->plat->starting_vms--; + + virt.running_vms--; + + lws_dll2_remove(&found_vm->list); + lws_sul_cancel(&found_vm->sul_timeout); + free(found_vm); + } lws_return_http_status(wsi, HTTP_STATUS_OK, NULL); return -1; /* hang up */ diff --git a/src/virt/v-libvirt.c b/src/virt/v-libvirt.c index 5cfa876..605d9db 100644 --- a/src/virt/v-libvirt.c +++ b/src/virt/v-libvirt.c @@ -55,32 +55,21 @@ ops_libvirt_init(struct sai_virt *virt) } static int -ops_libvirt_spawn(struct sai_virt *virt, const char *platform) +ops_libvirt_spawn(struct sai_virt *virt, struct saiv_vm *vm) { - /* - * Mock implementation: - * Ideally, we would run: - * virsh virt-clone --original sai-template-<platform> --name sai-ephemeral-<uuid> --auto-clone - * virsh start sai-ephemeral-<uuid> - */ const char * const exec_array[] = { "/usr/bin/virsh", "list", "--all", NULL }; - lwsl_notice("%s: Spawning ephemeral VM for platform: %s\n", __func__, platform); + lwsl_notice("%s: Spawning ephemeral VM %s for platform: %s\n", __func__, vm->name, vm->plat->name); return spawn_virsh_command(virt, exec_array); } static int -ops_libvirt_destroy(struct sai_virt *virt, const char *vm_id) +ops_libvirt_destroy(struct sai_virt *virt, struct saiv_vm *vm) { - /* - * Mock implementation: - * virsh destroy <vm_id> - * virsh undefine <vm_id> --remove-all-storage - */ - lwsl_notice("%s: Destroying ephemeral VM: %s\n", __func__, vm_id); + lwsl_notice("%s: Destroying ephemeral VM: %s\n", __func__, vm->name); return 0; } diff --git a/src/virt/v-private.h b/src/virt/v-private.h index c7fd1fd..273d685 100644 --- a/src/virt/v-private.h +++ b/src/virt/v-private.h @@ -17,23 +17,46 @@ struct sai_virt; +struct saiv_vm; + typedef struct sai_virt_ops { const char *name; int (*init)(struct sai_virt *virt); - int (*spawn)(struct sai_virt *virt, const char *platform); - int (*destroy)(struct sai_virt *virt, const char *vm_id); + int (*spawn)(struct sai_virt *virt, struct saiv_vm *vm); + int (*destroy)(struct sai_virt *virt, struct saiv_vm *vm); } sai_virt_ops_t; +typedef struct saiv_plat { + lws_dll2_t list; + char name[64]; + + int wait_magnification; + int starting_vms; + + lws_dll2_owner_t vm_owner; +} saiv_plat_t; + +typedef struct saiv_vm { + lws_dll2_t list; + saiv_plat_t *plat; + char name[64]; + lws_sorted_usec_list_t sul_timeout; +} saiv_vm_t; + /* * Represents the virt process state */ struct sai_virt { lws_dll2_owner_t sai_server_owner; /* servers we connect to */ + lws_dll2_owner_t plat_owner; /* platforms we can spawn */ struct lws_context *context; struct lws_vhost *vhost; const sai_virt_ops_t *ops; + int running_vms; + int max_vms; + const char *bind; /* listen socket binding */ const char *perms; /* user:group */ const char *port; /* port we listen on */ diff --git a/src/virt/v-sai.c b/src/virt/v-sai.c index 1113272..5b0014f 100644 --- a/src/virt/v-sai.c +++ b/src/virt/v-sai.c @@ -65,6 +65,8 @@ int main(int argc, const char **argv) if (gethostname(virt.hostname, sizeof(virt.hostname) - 1)) lws_strncpy(virt.hostname, "unknown", sizeof(virt.hostname)); + virt.max_vms = 4; + const struct lws_protocols *pprotocols[] = { &virt_protocols[0], NULL @@ -95,6 +97,17 @@ int main(int argc, const char **argv) return 1; } + /* We can spawn mac-m1, windows-10, etc. (Mocked for now) */ + const char *plats[] = {"windows-x86_64", "mac-m1"}; + for (size_t i = 0; i < LWS_ARRAY_SIZE(plats); i++) { + saiv_plat_t *vp = malloc(sizeof(*vp)); + if (vp) { + memset(vp, 0, sizeof(*vp)); + lws_strncpy(vp->name, plats[i], sizeof(vp->name)); + lws_dll2_add_tail(&vp->list, &virt.plat_owner); + } + } + /* We create the server link manually for testing skeleton */ saiv_server_t *srv = malloc(sizeof(*srv)); if (srv) { @@ -119,6 +132,12 @@ int main(int argc, const char **argv) free(s); } lws_end_foreach_dll_safe(d, d1); + lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1, virt.plat_owner.head) { + saiv_plat_t *p = lws_container_of(d, saiv_plat_t, list); + lws_dll2_remove(d); + free(p); + } lws_end_foreach_dll_safe(d, d1); + lws_context_destroy(virt.context); return 0; diff --git a/src/virt/v-ws-server.c b/src/virt/v-ws-server.c index 87dbf8a..a8f7b84 100644 --- a/src/virt/v-ws-server.c +++ b/src/virt/v-ws-server.c @@ -26,6 +26,25 @@ saiv_server_tx(void *userobj, lws_ss_tx_ordinal_t ord, uint8_t *buf, return sai_ss_tx_from_buflist_helper(g->ss, &g->bl_tx, buf, len, flags); } +static void +saiv_vm_timeout_cb(lws_sorted_usec_list_t *sul) +{ + saiv_vm_t *vm = lws_container_of(sul, saiv_vm_t, sul_timeout); + + lwsl_err("%s: VM %s timed out, purging\n", __func__, vm->name); + + if (virt.ops) + virt.ops->destroy(&virt, vm); + + if (vm->plat->starting_vms > 0) + vm->plat->starting_vms--; + + virt.running_vms--; + + lws_dll2_remove(&vm->list); + free(vm); +} + static lws_ss_state_return_t saiv_server_rx(void *userobj, const uint8_t *buf, size_t len, int flags) { @@ -57,13 +76,89 @@ saiv_server_rx(void *userobj, const uint8_t *buf, size_t len, int flags) sai_platform_pending_tasks_t *pt = (sai_platform_pending_tasks_t *)a.dest; lwsl_notice("%s: Pending tasks for pcons: %s\n", __func__, pt->pcons); + int total_wheel_weight = 0; + + /* Step 1: Calculate true demand and populate the wheel */ lws_start_foreach_dll(struct lws_dll2 *, p, pt->tasks.head) { sai_platform_pending_task_t *t = lws_container_of(p, sai_platform_pending_task_t, list); - lwsl_notice(" - %s: %u pending\n", t->plat, t->pending); - if (t->pending > 0 && virt.ops) { - virt.ops->spawn(&virt, t->plat); + + saiv_plat_t *found_vp = NULL; + lws_start_foreach_dll(struct lws_dll2 *, d, virt.plat_owner.head) { + saiv_plat_t *vp = lws_container_of(d, saiv_plat_t, list); + if (!strcmp(vp->name, t->plat)) { + found_vp = vp; + break; + } + } lws_end_foreach_dll(d); + + if (found_vp) { + int true_demand = (int)t->pending - found_vp->starting_vms; + if (true_demand > 0) { + /* Apply wait magnification factor */ + total_wheel_weight += true_demand + found_vp->wait_magnification; + } } } lws_end_foreach_dll(p); + + /* Step 2: Roll the dice if there is demand */ + if (total_wheel_weight > 0 && virt.running_vms < virt.max_vms) { + /* LWS random */ + uint32_t r; + lws_get_random(virt.context, &r, sizeof(r)); + int target = (int)(r % (uint32_t)total_wheel_weight); + + saiv_plat_t *winner = NULL; + + lws_start_foreach_dll(struct lws_dll2 *, p, pt->tasks.head) { + sai_platform_pending_task_t *t = lws_container_of(p, sai_platform_pending_task_t, list); + + saiv_plat_t *found_vp = NULL; + lws_start_foreach_dll(struct lws_dll2 *, d, virt.plat_owner.head) { + saiv_plat_t *vp = lws_container_of(d, saiv_plat_t, list); + if (!strcmp(vp->name, t->plat)) { + found_vp = vp; + break; + } + } lws_end_foreach_dll(d); + + if (found_vp) { + int true_demand = (int)t->pending - found_vp->starting_vms; + if (true_demand > 0) { + target -= (true_demand + found_vp->wait_magnification); + if (target < 0) { + winner = found_vp; + break; + } else { + /* This platform wasn't picked, increase its wait magnification */ + found_vp->wait_magnification++; + } + } + } + } lws_end_foreach_dll(p); + + /* Step 3: Spawn the winner and reset its magnification */ + if (winner && virt.ops) { + lwsl_notice("%s: Wheel picked platform %s (wait factor %d reset)\n", + __func__, winner->name, winner->wait_magnification); + + saiv_vm_t *vm = malloc(sizeof(*vm)); + if (vm) { + memset(vm, 0, sizeof(*vm)); + vm->plat = winner; + lws_snprintf(vm->name, sizeof(vm->name), "sai-vm-%s-%u", winner->name, (unsigned int)lws_now_usecs()); + lws_dll2_add_tail(&vm->list, &winner->vm_owner); + + winner->starting_vms++; + virt.running_vms++; + winner->wait_magnification = 0; + virt.ops->spawn(&virt, vm); + + /* Clean up if it never connects and terminates itself */ + lws_sul_schedule(virt.context, 0, &vm->sul_timeout, + saiv_vm_timeout_cb, 5 * 60 * LWS_US_PER_SEC); /* 5 min */ + } + } + } } lwsac_free(&a.ac); @@ -91,15 +186,14 @@ saiv_server_state(void *userobj, void *sh, lws_ss_constate_t state, lws_strncpy(r.builder_name, virt.hostname, sizeof(r.builder_name)); lws_strncpy(r.power_controller_name, virt.hostname, sizeof(r.power_controller_name)); - /* We can spawn mac-m1, windows-10, etc. (Mocked for now) */ - const char *plats[] = {"windows-x86_64", "mac-m1"}; - for (size_t i = 0; i < LWS_ARRAY_SIZE(plats); i++) { + lws_start_foreach_dll(struct lws_dll2 *, d, virt.plat_owner.head) { + saiv_plat_t *vp = lws_container_of(d, saiv_plat_t, list); sai_builder_platform_t *bp = lwsac_use_zero(&ac, sizeof(*bp), 512); if (bp) { - lws_strncpy(bp->name, plats[i], sizeof(bp->name)); + lws_strncpy(bp->name, vp->name, sizeof(bp->name)); lws_dll2_add_tail(&bp->list, &r.platforms_owner); } - } + } lws_end_foreach_dll(d); sai_ss_serialize_queue_helper(g->ss, &g->bl_tx, lsm_schema_builder_registration,
Page fetched 0s ago, creation time: 3ms (vhost etag hits: 0%, cache hits: 0%)