From e888a14e511d8d475177f3ab0b62307bb67ff015 Mon Sep 17 00:00:00 2001 From: Sean McBride Date: Mon, 6 Dec 2021 21:41:28 +0000 Subject: [PATCH] refactor: Eliminate sandbox request --- .vscode/settings.json | 3 +- runtime/Makefile | 7 +- runtime/compiletime/memory_instructions.c | 2 +- runtime/compiletime/table_instructions.c | 2 +- runtime/include/current_sandbox.h | 2 +- .../include/current_sandbox_send_response.h | 8 +- runtime/include/global_request_scheduler.h | 18 +- .../include/global_request_scheduler_deque.h | 4 + runtime/include/runtime.h | 1 - runtime/include/sandbox_functions.h | 23 +- runtime/include/sandbox_receive_request.h | 20 +- runtime/include/sandbox_request.h | 91 -------- runtime/include/sandbox_set_as_allocated.h | 30 +++ runtime/include/sandbox_set_as_asleep.h | 6 +- runtime/include/sandbox_set_as_complete.h | 6 +- runtime/include/sandbox_set_as_error.h | 10 +- runtime/include/sandbox_set_as_initialized.h | 59 ++--- runtime/include/sandbox_set_as_interrupted.h | 8 +- runtime/include/sandbox_set_as_preempted.h | 6 +- runtime/include/sandbox_set_as_returned.h | 7 +- runtime/include/sandbox_set_as_runnable.h | 6 +- runtime/include/sandbox_set_as_running_sys.h | 6 +- runtime/include/sandbox_set_as_running_user.h | 6 +- runtime/include/sandbox_state.h | 24 +-- runtime/include/sandbox_state_history.h | 26 ++- runtime/include/sandbox_total.h | 19 ++ runtime/include/sandbox_types.h | 47 ++-- runtime/include/scheduler.h | 51 ++--- runtime/include/vec_u8.h | 37 ++++ runtime/include/wasm_memory.h | 2 +- .../{wasm_store.h => wasm_module_instance.h} | 0 runtime/include/wasm_stack.h | 70 ++++++ runtime/src/global_request_scheduler.c | 30 +-- runtime/src/global_request_scheduler_deque.c | 32 +-- .../src/global_request_scheduler_minheap.c | 37 ++-- runtime/src/libc/syscall.c | 8 +- runtime/src/listener_thread.c | 18 +- runtime/src/main.c | 8 +- runtime/src/runtime.c | 6 +- runtime/src/sandbox.c | 204 ++++++++++-------- runtime/src/sandbox_request.c | 3 - runtime/src/sandbox_state.c | 29 +-- 42 files changed, 513 insertions(+), 469 deletions(-) delete mode 100644 runtime/include/sandbox_request.h create mode 100644 runtime/include/sandbox_set_as_allocated.h create mode 100644 runtime/include/sandbox_total.h create mode 100644 runtime/include/vec_u8.h rename runtime/include/{wasm_store.h => wasm_module_instance.h} (100%) create mode 100644 runtime/include/wasm_stack.h delete mode 100644 runtime/src/sandbox_request.c diff --git a/.vscode/settings.json b/.vscode/settings.json index 46998be..52765fc 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -80,7 +80,6 @@ "sandbox_set_as_runnable.h": "c", "sandbox_set_as_complete.h": "c", "deque.h": "c", - "sandbox_request.h": "c", "sandbox_send_response.h": "c", "sandbox_setup_arguments.h": "c", "worker_thread.h": "c", @@ -102,7 +101,7 @@ "sandbox_set_as_returned.h": "c", "software_interrupt_counts.h": "c", "sandbox_set_as_running_sys.h": "c", - "wasm_store.h": "c" + "wasm_module_instance.h": "c", }, "files.exclude": { "**/.git": true, diff --git a/runtime/Makefile b/runtime/Makefile index a691738..e73c2c9 100644 --- a/runtime/Makefile +++ b/runtime/Makefile @@ -55,7 +55,7 @@ BINARY_NAME=sledgert # CFLAGS += -DLOG_LOCK_OVERHEAD # CFLAGS += -DLOG_MODULE_LOADING # CFLAGS += -DLOG_PREEMPTION -# CFLAGS += -DLOG_REQUEST_ALLOCATION +# CFLAGS += -DLOG_SANDBOX_ALLOCATION # Stores and logs extended signal information for each worker # CFLAGS += -DLOG_SOFTWARE_INTERRUPT_COUNTS @@ -73,10 +73,9 @@ BINARY_NAME=sledgert # To log, run `call http_total_log()` while in GDB # CFLAGS += -DLOG_TOTAL_REQS_RESPS -# This flag logs the total number of sandboxes in the various states +# This flag tracks the total number of sandboxes in the various states # It is useful to debug if sandboxes are "getting caught" in a particular state -# To log, run `call runtime_log_sandbox_states()` while in GDB -# CFLAGS += -DLOG_SANDBOX_COUNT +# CFLAGS += -DSANDBOX_STATE_TOTALS # This flag enables an per-worker atomic count of sandbox's local runqueue count in thread local storage # Useful to debug if sandboxes are "getting caught" or "leaking" while in a local runqueue diff --git a/runtime/compiletime/memory_instructions.c b/runtime/compiletime/memory_instructions.c index 9d95117..a865444 100644 --- a/runtime/compiletime/memory_instructions.c +++ b/runtime/compiletime/memory_instructions.c @@ -2,7 +2,7 @@ #include #include -#include "wasm_store.h" +#include "wasm_module_instance.h" extern thread_local struct wasm_module_instance current_wasm_module_instance; diff --git a/runtime/compiletime/table_instructions.c b/runtime/compiletime/table_instructions.c index 44a9dcd..d54867e 100644 --- a/runtime/compiletime/table_instructions.c +++ b/runtime/compiletime/table_instructions.c @@ -1,5 +1,5 @@ #include "types.h" -#include "wasm_store.h" +#include "wasm_module_instance.h" extern thread_local struct wasm_module_instance current_wasm_module_instance; diff --git a/runtime/include/current_sandbox.h b/runtime/include/current_sandbox.h index 2793982..eb2b570 100644 --- a/runtime/include/current_sandbox.h +++ b/runtime/include/current_sandbox.h @@ -3,7 +3,7 @@ #include #include "sandbox_types.h" -#include "wasm_store.h" +#include "wasm_module_instance.h" /* current sandbox that is active.. */ extern thread_local struct sandbox *worker_thread_current_sandbox; diff --git a/runtime/include/current_sandbox_send_response.h b/runtime/include/current_sandbox_send_response.h index 5d2a688..bb3d9c6 100644 --- a/runtime/include/current_sandbox_send_response.h +++ b/runtime/include/current_sandbox_send_response.h @@ -25,12 +25,14 @@ current_sandbox_send_response() { struct sandbox *sandbox = current_sandbox_get(); assert(sandbox != NULL); + struct vec_u8 *response = sandbox->response; + assert(response != NULL); int rc; ssize_t sent = 0; /* Determine values to template into our HTTP response */ - ssize_t response_body_size = sandbox->response.length; + ssize_t response_body_size = response->length; char * module_content_type = sandbox->module->response_content_type; const char *content_type = strlen(module_content_type) > 0 ? module_content_type : "text/plain"; @@ -50,8 +52,8 @@ current_sandbox_send_response() current_sandbox_sleep); /* Send HTTP Response Body */ - client_socket_send(sandbox->client_socket_descriptor, sandbox->response.base, response_body_size, - current_sandbox_sleep); + client_socket_send(sandbox->client_socket_descriptor, (const char *)response->buffer, + response_body_size, current_sandbox_sleep); http_total_increment_2xx(); rc = 0; diff --git a/runtime/include/global_request_scheduler.h b/runtime/include/global_request_scheduler.h index 5ec14d8..8e363eb 100644 --- a/runtime/include/global_request_scheduler.h +++ b/runtime/include/global_request_scheduler.h @@ -2,12 +2,12 @@ #include -#include "sandbox_request.h" +#include "sandbox_types.h" /* Returns pointer back if successful, null otherwise */ -typedef struct sandbox_request *(*global_request_scheduler_add_fn_t)(void *); -typedef int (*global_request_scheduler_remove_fn_t)(struct sandbox_request **); -typedef int (*global_request_scheduler_remove_if_earlier_fn_t)(struct sandbox_request **, uint64_t); +typedef struct sandbox *(*global_request_scheduler_add_fn_t)(void *); +typedef int (*global_request_scheduler_remove_fn_t)(struct sandbox **); +typedef int (*global_request_scheduler_remove_if_earlier_fn_t)(struct sandbox **, uint64_t); typedef uint64_t (*global_request_scheduler_peek_fn_t)(void); struct global_request_scheduler_config { @@ -18,8 +18,8 @@ struct global_request_scheduler_config { }; -void global_request_scheduler_initialize(struct global_request_scheduler_config *config); -struct sandbox_request *global_request_scheduler_add(struct sandbox_request *); -int global_request_scheduler_remove(struct sandbox_request **); -int global_request_scheduler_remove_if_earlier(struct sandbox_request **, uint64_t targed_deadline); -uint64_t global_request_scheduler_peek(void); +void global_request_scheduler_initialize(struct global_request_scheduler_config *config); +struct sandbox *global_request_scheduler_add(struct sandbox *); +int global_request_scheduler_remove(struct sandbox **); +int global_request_scheduler_remove_if_earlier(struct sandbox **, uint64_t targed_deadline); +uint64_t global_request_scheduler_peek(void); diff --git a/runtime/include/global_request_scheduler_deque.h b/runtime/include/global_request_scheduler_deque.h index 1dbe4af..6623c77 100644 --- a/runtime/include/global_request_scheduler_deque.h +++ b/runtime/include/global_request_scheduler_deque.h @@ -1,5 +1,9 @@ #pragma once +#include "deque.h" #include "global_request_scheduler.h" +#include "sandbox_types.h" + +DEQUE_PROTOTYPE(sandbox, struct sandbox *) void global_request_scheduler_deque_initialize(); diff --git a/runtime/include/runtime.h b/runtime/include/runtime.h index 6f32766..662ec85 100644 --- a/runtime/include/runtime.h +++ b/runtime/include/runtime.h @@ -26,7 +26,6 @@ #define RUNTIME_HTTP_RESPONSE_SIZE_MAX 100000000 /* 100 MB */ #define RUNTIME_LOG_FILE "sledge.log" #define RUNTIME_MAX_EPOLL_EVENTS 128 -#define RUNTIME_MAX_SANDBOX_REQUEST_COUNT (1 << 19) #define RUNTIME_MAX_WORKER_COUNT 32 /* Static buffer size for per-worker globals */ #define RUNTIME_READ_WRITE_VECTOR_LENGTH 16 #define RUNTIME_RELATIVE_DEADLINE_US_MAX 3600000000 /* One Hour. Fits in uint32_t */ diff --git a/runtime/include/sandbox_functions.h b/runtime/include/sandbox_functions.h index 3a2940f..6b1900d 100644 --- a/runtime/include/sandbox_functions.h +++ b/runtime/include/sandbox_functions.h @@ -6,17 +6,18 @@ #include "client_socket.h" #include "panic.h" -#include "sandbox_request.h" +#include "sandbox_types.h" /*************************** * Public API * **************************/ -struct sandbox *sandbox_new(struct sandbox_request *sandbox_request); +struct sandbox *sandbox_new(struct module *module, int socket_descriptor, const struct sockaddr *socket_address, + uint64_t request_arrival_timestamp, uint64_t admissions_estimate); +int sandbox_prepare_execution_environemnt(struct sandbox *sandbox); void sandbox_free(struct sandbox *sandbox); void sandbox_main(struct sandbox *sandbox); void sandbox_switch_to(struct sandbox *next_sandbox); - static inline void sandbox_close_http(struct sandbox *sandbox) { @@ -39,6 +40,22 @@ sandbox_free_linear_memory(struct sandbox *sandbox) sandbox->memory = NULL; } +/** + * Free Linear Memory, leaving stack in place + * @param sandbox + */ +static inline void +sandbox_free_http_buffers(struct sandbox *sandbox) +{ + assert(sandbox); + assert(sandbox->request); + assert(sandbox->response); + vec_u8_free(sandbox->request); + vec_u8_free(sandbox->response); + sandbox->request = NULL; + sandbox->response = NULL; +} + /** * Given a sandbox, returns the module that sandbox is executing * @param sandbox the sandbox whose module we want diff --git a/runtime/include/sandbox_receive_request.h b/runtime/include/sandbox_receive_request.h index 354bc36..0ab2586 100644 --- a/runtime/include/sandbox_receive_request.h +++ b/runtime/include/sandbox_receive_request.h @@ -24,11 +24,13 @@ static inline int sandbox_receive_request(struct sandbox *sandbox) { assert(sandbox != NULL); - assert(sandbox->module->max_request_size > 0); - assert(sandbox->request.length == 0); int rc = 0; + struct vec_u8 *request = sandbox->request; + assert(request->length == 0); + assert(request->capacity > 0); + while (!sandbox->http_request.message_end) { /* Read from the Socket */ @@ -36,14 +38,16 @@ sandbox_receive_request(struct sandbox *sandbox) http_parser * parser = &sandbox->http_parser; const http_parser_settings *settings = http_parser_settings_get(); - if (sandbox->module->max_request_size <= sandbox->request.length) { + size_t request_length = request->length; + size_t request_capacity = request->capacity; + + if (request_length >= request_capacity) { debuglog("Sandbox %lu: Ran out of Request Buffer before message end\n", sandbox->id); goto err_nobufs; } - ssize_t bytes_received = recv(sandbox->client_socket_descriptor, - &sandbox->request.base[sandbox->request.length], - sandbox->module->max_request_size - sandbox->request.length, 0); + ssize_t bytes_received = recv(sandbox->client_socket_descriptor, &request->buffer[request_length], + request_capacity - request_length, 0); if (bytes_received == -1) { if (errno == EAGAIN) { @@ -75,7 +79,7 @@ sandbox_receive_request(struct sandbox *sandbox) &sandbox->request.base[sandbox->request.length], bytes_received); #endif size_t bytes_parsed = http_parser_execute(parser, settings, - &sandbox->request.base[sandbox->request.length], + (const char *)&request->buffer[request_length], bytes_received); if (bytes_parsed != bytes_received) { @@ -87,7 +91,7 @@ sandbox_receive_request(struct sandbox *sandbox) goto err; } - sandbox->request.length += bytes_parsed; + request->length += bytes_parsed; } rc = 0; diff --git a/runtime/include/sandbox_request.h b/runtime/include/sandbox_request.h deleted file mode 100644 index 421155f..0000000 --- a/runtime/include/sandbox_request.h +++ /dev/null @@ -1,91 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -#include "debuglog.h" -#include "deque.h" -#include "http_total.h" -#include "module.h" -#include "runtime.h" -#include "sandbox_state.h" - -struct sandbox_request { - uint64_t id; - struct module * module; - int socket_descriptor; - struct sockaddr socket_address; - uint64_t request_arrival_timestamp; /* cycles */ - uint64_t absolute_deadline; /* cycles */ - - /* - * Unitless estimate of the instantaneous fraction of system capacity required to run the request - * Calculated by estimated execution time (cycles) * runtime_admissions_granularity / relative deadline (cycles) - */ - uint64_t admissions_estimate; -}; - -DEQUE_PROTOTYPE(sandbox, struct sandbox_request *) - -/* Count of the total number of requests we've ever allocated. Never decrements as it is used to generate IDs */ -extern _Atomic uint32_t sandbox_request_count; - -static inline void -sandbox_request_count_initialize() -{ - atomic_init(&sandbox_request_count, 0); -} - -static inline uint32_t -sandbox_request_count_postfix_increment() -{ - return atomic_fetch_add(&sandbox_request_count, 1); -} - -static inline void -sandbox_request_log_allocation(struct sandbox_request *sandbox_request) -{ -#ifdef LOG_REQUEST_ALLOCATION - debuglog("Sandbox Request %lu: of %s:%d\n", sandbox_request->id, sandbox_request->module->name, - sandbox_request->module->port); -#endif -} - -/** - * Allocates a new Sandbox Request and places it on the Global Deque - * @param module the module we want to request - * @param socket_descriptor - * @param socket_address - * @param request_arrival_timestamp the timestamp of when we receives the request from the network (in cycles) - * @return the new sandbox request - */ -static inline struct sandbox_request * -sandbox_request_allocate(struct module *module, int socket_descriptor, const struct sockaddr *socket_address, - uint64_t request_arrival_timestamp, uint64_t admissions_estimate) -{ - struct sandbox_request *sandbox_request = (struct sandbox_request *)malloc(sizeof(struct sandbox_request)); - assert(sandbox_request); - - /* Sets the ID to the value before the increment */ - sandbox_request->id = sandbox_request_count_postfix_increment(); - - sandbox_request->module = module; - sandbox_request->socket_descriptor = socket_descriptor; - memcpy(&sandbox_request->socket_address, socket_address, sizeof(struct sockaddr)); - sandbox_request->request_arrival_timestamp = request_arrival_timestamp; - sandbox_request->absolute_deadline = request_arrival_timestamp + module->relative_deadline; - - /* - * Admissions Control State - * Assumption: an estimate of 0 should have been interpreted as a rejection - */ - assert(admissions_estimate != 0); - sandbox_request->admissions_estimate = admissions_estimate; - - sandbox_request_log_allocation(sandbox_request); - - return sandbox_request; -} diff --git a/runtime/include/sandbox_set_as_allocated.h b/runtime/include/sandbox_set_as_allocated.h new file mode 100644 index 0000000..2c0cafd --- /dev/null +++ b/runtime/include/sandbox_set_as_allocated.h @@ -0,0 +1,30 @@ +#pragma once + +#include +#include +#include + +#include "arch/context.h" +#include "current_sandbox.h" +#include "ps_list.h" +#include "sandbox_state_history.h" +#include "sandbox_types.h" + +/** + * Transitions a sandbox to the SANDBOX_ALLOCATED state. + * This the is the initial state, so there is no concept of "last state" here + * @param sandbox + */ +static inline void +sandbox_set_as_allocated(struct sandbox *sandbox) +{ + assert(sandbox); + assert(sandbox->state == SANDBOX_UNINITIALIZED); + uint64_t now = __getcycles(); + + /* State Change Bookkeeping */ + sandbox->timestamp_of.last_state_change = now; + sandbox_state_history_init(&sandbox->state_history); + sandbox_state_history_append(&sandbox->state_history, SANDBOX_ALLOCATED); + sandbox_state_totals_increment(SANDBOX_ALLOCATED); +} diff --git a/runtime/include/sandbox_set_as_asleep.h b/runtime/include/sandbox_set_as_asleep.h index 178e8be..f64d079 100644 --- a/runtime/include/sandbox_set_as_asleep.h +++ b/runtime/include/sandbox_set_as_asleep.h @@ -38,9 +38,9 @@ sandbox_set_as_asleep(struct sandbox *sandbox, sandbox_state_t last_state) /* State Change Bookkeeping */ sandbox->duration_of_state[last_state] += (now - sandbox->timestamp_of.last_state_change); sandbox->timestamp_of.last_state_change = now; - sandbox_state_history_append(sandbox, SANDBOX_ASLEEP); - runtime_sandbox_total_increment(SANDBOX_ASLEEP); - runtime_sandbox_total_decrement(last_state); + sandbox_state_history_append(&sandbox->state_history, SANDBOX_ASLEEP); + sandbox_state_totals_increment(SANDBOX_ASLEEP); + sandbox_state_totals_decrement(last_state); } static inline void diff --git a/runtime/include/sandbox_set_as_complete.h b/runtime/include/sandbox_set_as_complete.h index d7d0937..ce94d8f 100644 --- a/runtime/include/sandbox_set_as_complete.h +++ b/runtime/include/sandbox_set_as_complete.h @@ -41,9 +41,9 @@ sandbox_set_as_complete(struct sandbox *sandbox, sandbox_state_t last_state) /* State Change Bookkeeping */ sandbox->duration_of_state[last_state] += (now - sandbox->timestamp_of.last_state_change); sandbox->timestamp_of.last_state_change = now; - sandbox_state_history_append(sandbox, SANDBOX_COMPLETE); - runtime_sandbox_total_increment(SANDBOX_COMPLETE); - runtime_sandbox_total_decrement(last_state); + sandbox_state_history_append(&sandbox->state_history, SANDBOX_COMPLETE); + sandbox_state_totals_increment(SANDBOX_COMPLETE); + sandbox_state_totals_decrement(last_state); /* Admissions Control Post Processing */ admissions_info_update(&sandbox->module->admissions_info, sandbox->duration_of_state[SANDBOX_RUNNING_USER] diff --git a/runtime/include/sandbox_set_as_error.h b/runtime/include/sandbox_set_as_error.h index c3e8c7a..55bcb4c 100644 --- a/runtime/include/sandbox_set_as_error.h +++ b/runtime/include/sandbox_set_as_error.h @@ -33,12 +33,12 @@ sandbox_set_as_error(struct sandbox *sandbox, sandbox_state_t last_state) uint64_t now = __getcycles(); switch (last_state) { - case SANDBOX_UNINITIALIZED: - /* Technically, this is a degenerate sandbox that we generate by hand */ + case SANDBOX_ALLOCATED: break; case SANDBOX_RUNNING_SYS: { local_runqueue_delete(sandbox); sandbox_free_linear_memory(sandbox); + sandbox_free_http_buffers(sandbox); break; } default: { @@ -50,9 +50,9 @@ sandbox_set_as_error(struct sandbox *sandbox, sandbox_state_t last_state) /* State Change Bookkeeping */ uint64_t duration_of_last_state = now - sandbox->timestamp_of.last_state_change; sandbox->duration_of_state[last_state] += duration_of_last_state; - sandbox_state_history_append(sandbox, SANDBOX_ERROR); - runtime_sandbox_total_increment(SANDBOX_ERROR); - runtime_sandbox_total_decrement(last_state); + sandbox_state_history_append(&sandbox->state_history, SANDBOX_ERROR); + sandbox_state_totals_increment(SANDBOX_ERROR); + sandbox_state_totals_decrement(last_state); /* Admissions Control Post Processing */ admissions_control_subtract(sandbox->admissions_estimate); diff --git a/runtime/include/sandbox_set_as_initialized.h b/runtime/include/sandbox_set_as_initialized.h index c229238..5f9fecb 100644 --- a/runtime/include/sandbox_set_as_initialized.h +++ b/runtime/include/sandbox_set_as_initialized.h @@ -7,61 +7,36 @@ #include "arch/context.h" #include "current_sandbox.h" #include "ps_list.h" -#include "sandbox_request.h" #include "sandbox_state_history.h" #include "sandbox_types.h" /** * Transitions a sandbox to the SANDBOX_INITIALIZED state. * The sandbox was already zeroed out during allocation - * @param sandbox an uninitialized sandbox - * @param sandbox_request the request we are initializing the sandbox from - * @param allocation_timestamp timestamp of allocation + * @param sandbox + * @param last_state */ static inline void -sandbox_set_as_initialized(struct sandbox *sandbox, struct sandbox_request *sandbox_request, - uint64_t allocation_timestamp) +sandbox_set_as_initialized(struct sandbox *sandbox, sandbox_state_t last_state) { assert(sandbox); - assert(sandbox->state == SANDBOX_ALLOCATED); - assert(sandbox_request != NULL); - assert(allocation_timestamp > 0); sandbox->state = SANDBOX_INITIALIZED; uint64_t now = __getcycles(); - /* Copy State from Sandbox Request */ - sandbox->id = sandbox_request->id; - sandbox->absolute_deadline = sandbox_request->absolute_deadline; - sandbox->admissions_estimate = sandbox_request->admissions_estimate; - sandbox->client_socket_descriptor = sandbox_request->socket_descriptor; - sandbox->timestamp_of.request_arrival = sandbox_request->request_arrival_timestamp; - /* Copy the socket descriptor and address of the client invocation */ - memcpy(&sandbox->client_address, &sandbox_request->socket_address, sizeof(struct sockaddr)); - - /* Initialize Parsec control structures */ - ps_list_init_d(sandbox); - - /* Allocations require the module to be set */ - sandbox->module = sandbox_request->module; - module_acquire(sandbox->module); + switch (last_state) { + case SANDBOX_ALLOCATED: { + break; + } + default: { + panic("Sandbox %lu | Illegal transition from %s to Preempted\n", sandbox->id, + sandbox_state_stringify(last_state)); + } + } /* State Change Bookkeeping */ - sandbox->duration_of_state[SANDBOX_ALLOCATED] = now - allocation_timestamp; - sandbox->timestamp_of.allocation = allocation_timestamp; - sandbox->timestamp_of.last_state_change = allocation_timestamp; - sandbox_state_history_append(sandbox, SANDBOX_INITIALIZED); - runtime_sandbox_total_increment(SANDBOX_INITIALIZED); - -#ifdef LOG_STATE_CHANGES - sandbox->state_history_count = 0; - sandbox->state_history[sandbox->state_history_count++] = SANDBOX_ALLOCATED; - memset(&sandbox->state_history, 0, SANDBOX_STATE_HISTORY_CAPACITY * sizeof(sandbox_state_t)); -#endif -} - - -static inline void -sandbox_init(struct sandbox *sandbox, struct sandbox_request *sandbox_request, uint64_t allocation_timestamp) -{ - sandbox_set_as_initialized(sandbox, sandbox_request, allocation_timestamp); + sandbox->duration_of_state[last_state] += (now - sandbox->timestamp_of.last_state_change); + sandbox->timestamp_of.last_state_change = now; + sandbox_state_history_append(&sandbox->state_history, SANDBOX_INITIALIZED); + sandbox_state_totals_increment(SANDBOX_INITIALIZED); + sandbox_state_totals_decrement(last_state); } diff --git a/runtime/include/sandbox_set_as_interrupted.h b/runtime/include/sandbox_set_as_interrupted.h index 3ee1216..97d5ef9 100644 --- a/runtime/include/sandbox_set_as_interrupted.h +++ b/runtime/include/sandbox_set_as_interrupted.h @@ -25,8 +25,8 @@ sandbox_set_as_interrupted(struct sandbox *sandbox, sandbox_state_t last_state) sandbox->duration_of_state[last_state] += (now - sandbox->timestamp_of.last_state_change); sandbox->timestamp_of.last_state_change = now; /* We do not append SANDBOX_INTERRUPTED to the sandbox_state_history because it would quickly fill the buffer */ - runtime_sandbox_total_increment(SANDBOX_INTERRUPTED); - runtime_sandbox_total_decrement(last_state); + sandbox_state_totals_increment(SANDBOX_INTERRUPTED); + sandbox_state_totals_decrement(last_state); } static inline void @@ -53,8 +53,8 @@ sandbox_interrupt_return(struct sandbox *sandbox, sandbox_state_t interrupted_st sandbox->duration_of_state[SANDBOX_INTERRUPTED] += (now - sandbox->timestamp_of.last_state_change); sandbox->timestamp_of.last_state_change = now; /* We do not append SANDBOX_INTERRUPTED to the sandbox_state_history because it would quickly fill the buffer */ - runtime_sandbox_total_increment(interrupted_state); - runtime_sandbox_total_decrement(SANDBOX_INTERRUPTED); + sandbox_state_totals_increment(interrupted_state); + sandbox_state_totals_decrement(SANDBOX_INTERRUPTED); barrier(); /* WARNING: Code after this assignment may be preemptable */ diff --git a/runtime/include/sandbox_set_as_preempted.h b/runtime/include/sandbox_set_as_preempted.h index 8dec5cb..ddd0ebe 100644 --- a/runtime/include/sandbox_set_as_preempted.h +++ b/runtime/include/sandbox_set_as_preempted.h @@ -38,9 +38,9 @@ sandbox_set_as_preempted(struct sandbox *sandbox, sandbox_state_t last_state) /* State Change Bookkeeping */ sandbox->duration_of_state[last_state] += (now - sandbox->timestamp_of.last_state_change); sandbox->timestamp_of.last_state_change = now; - sandbox_state_history_append(sandbox, SANDBOX_PREEMPTED); - runtime_sandbox_total_increment(SANDBOX_PREEMPTED); - runtime_sandbox_total_decrement(last_state); + sandbox_state_history_append(&sandbox->state_history, SANDBOX_PREEMPTED); + sandbox_state_totals_increment(SANDBOX_PREEMPTED); + sandbox_state_totals_decrement(last_state); } static inline void diff --git a/runtime/include/sandbox_set_as_returned.h b/runtime/include/sandbox_set_as_returned.h index 0dbcdac..d57801d 100644 --- a/runtime/include/sandbox_set_as_returned.h +++ b/runtime/include/sandbox_set_as_returned.h @@ -33,6 +33,7 @@ sandbox_set_as_returned(struct sandbox *sandbox, sandbox_state_t last_state) sandbox->total_time = now - sandbox->timestamp_of.request_arrival; local_runqueue_delete(sandbox); sandbox_free_linear_memory(sandbox); + sandbox_free_http_buffers(sandbox); break; } default: { @@ -44,7 +45,7 @@ sandbox_set_as_returned(struct sandbox *sandbox, sandbox_state_t last_state) /* State Change Bookkeeping */ sandbox->duration_of_state[last_state] += (now - sandbox->timestamp_of.last_state_change); sandbox->timestamp_of.last_state_change = now; - sandbox_state_history_append(sandbox, SANDBOX_RETURNED); - runtime_sandbox_total_increment(SANDBOX_RETURNED); - runtime_sandbox_total_decrement(last_state); + sandbox_state_history_append(&sandbox->state_history, SANDBOX_RETURNED); + sandbox_state_totals_increment(SANDBOX_RETURNED); + sandbox_state_totals_decrement(last_state); } diff --git a/runtime/include/sandbox_set_as_runnable.h b/runtime/include/sandbox_set_as_runnable.h index edb0eb5..e4414bc 100644 --- a/runtime/include/sandbox_set_as_runnable.h +++ b/runtime/include/sandbox_set_as_runnable.h @@ -45,9 +45,9 @@ sandbox_set_as_runnable(struct sandbox *sandbox, sandbox_state_t last_state) /* State Change Bookkeeping */ sandbox->duration_of_state[last_state] += (now - sandbox->timestamp_of.last_state_change); sandbox->timestamp_of.last_state_change = now; - sandbox_state_history_append(sandbox, SANDBOX_RUNNABLE); - runtime_sandbox_total_increment(SANDBOX_RUNNABLE); - runtime_sandbox_total_decrement(last_state); + sandbox_state_history_append(&sandbox->state_history, SANDBOX_RUNNABLE); + sandbox_state_totals_increment(SANDBOX_RUNNABLE); + sandbox_state_totals_decrement(last_state); } diff --git a/runtime/include/sandbox_set_as_running_sys.h b/runtime/include/sandbox_set_as_running_sys.h index 67d6159..98ecb4d 100644 --- a/runtime/include/sandbox_set_as_running_sys.h +++ b/runtime/include/sandbox_set_as_running_sys.h @@ -40,9 +40,9 @@ sandbox_set_as_running_sys(struct sandbox *sandbox, sandbox_state_t last_state) /* State Change Bookkeeping */ sandbox->duration_of_state[last_state] += (now - sandbox->timestamp_of.last_state_change); sandbox->timestamp_of.last_state_change = now; - sandbox_state_history_append(sandbox, SANDBOX_RUNNING_SYS); - runtime_sandbox_total_increment(SANDBOX_RUNNING_SYS); - runtime_sandbox_total_decrement(last_state); + sandbox_state_history_append(&sandbox->state_history, SANDBOX_RUNNING_SYS); + sandbox_state_totals_increment(SANDBOX_RUNNING_SYS); + sandbox_state_totals_decrement(last_state); } static inline void diff --git a/runtime/include/sandbox_set_as_running_user.h b/runtime/include/sandbox_set_as_running_user.h index d25ca42..3cab443 100644 --- a/runtime/include/sandbox_set_as_running_user.h +++ b/runtime/include/sandbox_set_as_running_user.h @@ -36,9 +36,9 @@ sandbox_set_as_running_user(struct sandbox *sandbox, sandbox_state_t last_state) /* State Change Bookkeeping */ sandbox->duration_of_state[last_state] += (now - sandbox->timestamp_of.last_state_change); sandbox->timestamp_of.last_state_change = now; - sandbox_state_history_append(sandbox, SANDBOX_RUNNING_USER); - runtime_sandbox_total_increment(SANDBOX_RUNNING_USER); - runtime_sandbox_total_decrement(last_state); + sandbox_state_history_append(&sandbox->state_history, SANDBOX_RUNNING_USER); + sandbox_state_totals_increment(SANDBOX_RUNNING_USER); + sandbox_state_totals_decrement(last_state); barrier(); sandbox->state = SANDBOX_RUNNING_USER; diff --git a/runtime/include/sandbox_state.h b/runtime/include/sandbox_state.h index f107cf7..d74069a 100644 --- a/runtime/include/sandbox_state.h +++ b/runtime/include/sandbox_state.h @@ -32,31 +32,31 @@ sandbox_state_stringify(sandbox_state_t state) return sandbox_state_labels[state]; } -#ifdef LOG_SANDBOX_COUNT -extern _Atomic uint32_t sandbox_state_count[SANDBOX_STATE_COUNT]; +#ifdef SANDBOX_STATE_TOTALS +extern _Atomic uint32_t sandbox_state_totals[SANDBOX_STATE_COUNT]; #endif static inline void -sandbox_count_initialize() +sandbox_state_totals_initialize() { -#ifdef LOG_SANDBOX_COUNT - for (int i = 0; i < SANDBOX_STATE_COUNT; i++) atomic_init(&sandbox_state_count[i], 0); +#ifdef SANDBOX_STATE_TOTALS + for (int i = 0; i < SANDBOX_STATE_COUNT; i++) atomic_init(&sandbox_state_totals[i], 0); #endif } static inline void -runtime_sandbox_total_increment(sandbox_state_t state) +sandbox_state_totals_increment(sandbox_state_t state) { -#ifdef LOG_SANDBOX_COUNT - atomic_fetch_add(&sandbox_state_count[state], 1); +#ifdef SANDBOX_STATE_TOTALS + atomic_fetch_add(&sandbox_state_totals[state], 1); #endif } static inline void -runtime_sandbox_total_decrement(sandbox_state_t state) +sandbox_state_totals_decrement(sandbox_state_t state) { -#ifdef LOG_SANDBOX_COUNT - if (atomic_load(&sandbox_state_count[state]) == 0) panic("Underflow of %s\n", sandbox_state_stringify(state)); - atomic_fetch_sub(&sandbox_state_count[state], 1); +#ifdef SANDBOX_STATE_TOTALS + if (atomic_load(&sandbox_state_totals[state]) == 0) panic("Underflow of %s\n", sandbox_state_stringify(state)); + atomic_fetch_sub(&sandbox_state_totals[state], 1); #endif } diff --git a/runtime/include/sandbox_state_history.h b/runtime/include/sandbox_state_history.h index 11e2afd..fa2e1d3 100644 --- a/runtime/include/sandbox_state_history.h +++ b/runtime/include/sandbox_state_history.h @@ -3,12 +3,30 @@ #include "sandbox_state.h" #include "sandbox_types.h" +#ifdef LOG_STATE_CHANGES +#define SANDBOX_STATE_HISTORY_CAPACITY 100 +#else +#define SANDBOX_STATE_HISTORY_CAPACITY 0 +#endif + +struct sandbox_state_history { + uint16_t size; + sandbox_state_t buffer[SANDBOX_STATE_HISTORY_CAPACITY]; +}; + +static inline void +sandbox_state_history_init(struct sandbox_state_history *self) +{ +#ifdef LOG_STATE_CHANGES + memset(self, 0, + sizeof(struct sandbox_state_history) + SANDBOX_STATE_HISTORY_CAPACITY * sizeof(sandbox_state_t)); +#endif +} + static inline void -sandbox_state_history_append(struct sandbox *sandbox, sandbox_state_t state) +sandbox_state_history_append(struct sandbox_state_history *self, sandbox_state_t state) { #ifdef LOG_STATE_CHANGES - if (likely(sandbox->state_history_count < SANDBOX_STATE_HISTORY_CAPACITY)) { - sandbox->state_history[sandbox->state_history_count++] = state; - } + if (likely(self->size < SANDBOX_STATE_HISTORY_CAPACITY)) { self->buffer[self->size++] = state; } #endif } diff --git a/runtime/include/sandbox_total.h b/runtime/include/sandbox_total.h new file mode 100644 index 0000000..6adf746 --- /dev/null +++ b/runtime/include/sandbox_total.h @@ -0,0 +1,19 @@ +#pragma once + +#include +#include + +/* Count of the total number of requests we've ever allocated. Never decrements as it is used to generate IDs */ +extern _Atomic uint32_t sandbox_total; + +static inline void +sandbox_total_initialize() +{ + atomic_init(&sandbox_total, 0); +} + +static inline uint32_t +sandbox_total_postfix_increment() +{ + return atomic_fetch_add(&sandbox_total, 1); +} diff --git a/runtime/include/sandbox_types.h b/runtime/include/sandbox_types.h index 87b1fcb..f489dd1 100644 --- a/runtime/include/sandbox_types.h +++ b/runtime/include/sandbox_types.h @@ -12,26 +12,20 @@ #include "module.h" #include "ps_list.h" #include "sandbox_state.h" +#include "sandbox_state_history.h" +#include "vec_u8.h" #include "wasm_memory.h" #include "wasm_types.h" +#include "wasm_stack.h" #ifdef LOG_SANDBOX_MEMORY_PROFILE #define SANDBOX_PAGE_ALLOCATION_TIMESTAMP_COUNT 1024 #endif -#ifdef LOG_STATE_CHANGES -#define SANDBOX_STATE_HISTORY_CAPACITY 100 -#endif - /********************* * Structs and Types * ********************/ -struct sandbox_stack { - void * start; /* points to the bottom of the usable stack */ - uint32_t size; -}; - struct sandbox_timestamps { uint64_t last_state_change; /* Used for bookkeeping of actual execution time */ uint64_t request_arrival; /* Timestamp when request is received */ @@ -44,38 +38,29 @@ struct sandbox_timestamps { #endif }; -struct sandbox_buffer { - char * base; - size_t length; -}; - struct sandbox { - uint64_t id; - sandbox_state_t state; - -#ifdef LOG_STATE_CHANGES - sandbox_state_t state_history[SANDBOX_STATE_HISTORY_CAPACITY]; - uint16_t state_history_count; -#endif + uint64_t id; + sandbox_state_t state; + struct sandbox_state_history state_history; struct ps_list list; /* used by ps_list's default name-based MACROS for the scheduling runqueue */ /* HTTP State */ - struct sockaddr client_address; /* client requesting connection! */ - int client_socket_descriptor; - http_parser http_parser; - struct http_request http_request; - ssize_t http_request_length; /* TODO: Get rid of me */ - struct sandbox_buffer request; - struct sandbox_buffer response; + struct sockaddr client_address; /* client requesting connection! */ + int client_socket_descriptor; + http_parser http_parser; + struct http_request http_request; + ssize_t http_request_length; /* TODO: Get rid of me */ + struct vec_u8 * request; + struct vec_u8 * response; /* WebAssembly Module State */ struct module *module; /* the module this is an instance of */ /* WebAssembly Instance State */ - struct arch_context ctxt; - struct sandbox_stack stack; - struct wasm_memory * memory; + struct arch_context ctxt; + struct wasm_stack stack; + struct wasm_memory *memory; /* Scheduling and Temporal State */ struct sandbox_timestamps timestamp_of; diff --git a/runtime/include/scheduler.h b/runtime/include/scheduler.h index 9f2c129..b4812d9 100644 --- a/runtime/include/scheduler.h +++ b/runtime/include/scheduler.h @@ -13,7 +13,6 @@ #include "local_runqueue_minheap.h" #include "local_runqueue_list.h" #include "panic.h" -#include "sandbox_request.h" #include "sandbox_functions.h" #include "sandbox_types.h" #include "sandbox_set_as_preempted.h" @@ -74,67 +73,49 @@ static inline struct sandbox * scheduler_edf_get_next() { /* Get the deadline of the sandbox at the head of the local request queue */ - struct sandbox * local = local_runqueue_get_next(); - uint64_t local_deadline = local == NULL ? UINT64_MAX : local->absolute_deadline; - struct sandbox_request *request = NULL; + struct sandbox *local = local_runqueue_get_next(); + uint64_t local_deadline = local == NULL ? UINT64_MAX : local->absolute_deadline; + struct sandbox *global = NULL; uint64_t global_deadline = global_request_scheduler_peek(); /* Try to pull and allocate from the global queue if earlier * This will be placed at the head of the local runqueue */ if (global_deadline < local_deadline) { - if (global_request_scheduler_remove_if_earlier(&request, local_deadline) == 0) { - assert(request != NULL); - assert(request->absolute_deadline < local_deadline); - struct sandbox *global = sandbox_new(request); - if (!global) goto err_allocate; - + if (global_request_scheduler_remove_if_earlier(&global, local_deadline) == 0) { + assert(global != NULL); + assert(global->absolute_deadline < local_deadline); + sandbox_prepare_execution_environemnt(global); assert(global->state == SANDBOX_INITIALIZED); sandbox_set_as_runnable(global, SANDBOX_INITIALIZED); } } -/* Return what is at the head of the local runqueue or NULL if empty */ -done: + /* Return what is at the head of the local runqueue or NULL if empty */ return local_runqueue_get_next(); -err_allocate: - client_socket_send_oneshot(request->socket_descriptor, http_header_build(503), http_header_len(503)); - client_socket_close(request->socket_descriptor, &request->socket_address); - free(request); - goto done; } static inline struct sandbox * scheduler_fifo_get_next() { - struct sandbox *sandbox = local_runqueue_get_next(); + struct sandbox *local = local_runqueue_get_next(); - struct sandbox_request *sandbox_request = NULL; + struct sandbox *global = NULL; - if (sandbox == NULL) { + if (local == NULL) { /* If the local runqueue is empty, pull from global request scheduler */ - if (global_request_scheduler_remove(&sandbox_request) < 0) goto err; + if (global_request_scheduler_remove(&global) < 0) goto done; - sandbox = sandbox_new(sandbox_request); - if (!sandbox) goto err_allocate; - - sandbox_set_as_runnable(sandbox, SANDBOX_INITIALIZED); - } else if (sandbox == current_sandbox_get()) { + sandbox_prepare_execution_environemnt(global); + sandbox_set_as_runnable(global, SANDBOX_INITIALIZED); + } else if (local == current_sandbox_get()) { /* Execute Round Robin Scheduling Logic if the head is the current sandbox */ local_runqueue_list_rotate(); - sandbox = local_runqueue_get_next(); } done: - return sandbox; -err_allocate: - client_socket_send_oneshot(sandbox_request->socket_descriptor, http_header_build(503), http_header_len(503)); - client_socket_close(sandbox_request->socket_descriptor, &sandbox->client_address); - free(sandbox_request); -err: - sandbox = NULL; - goto done; + return local_runqueue_get_next(); } static inline struct sandbox * diff --git a/runtime/include/vec_u8.h b/runtime/include/vec_u8.h new file mode 100644 index 0000000..8ac47df --- /dev/null +++ b/runtime/include/vec_u8.h @@ -0,0 +1,37 @@ +#pragma once + +#include +#include + +struct vec_u8 { + size_t length; + size_t capacity; + uint8_t buffer[]; +}; + +static inline struct vec_u8 * +vec_u8_alloc(size_t capacity) +{ + return (struct vec_u8 *)calloc(1, sizeof(struct vec_u8) + capacity * sizeof(uint8_t)); +} + +static inline void +vec_u8_init(struct vec_u8 *self, size_t capacity) +{ + self->length = 0; + self->capacity = capacity; +} + +static inline struct vec_u8 * +vec_u8_new(size_t capacity) +{ + struct vec_u8 *self = vec_u8_alloc(capacity); + vec_u8_init(self, capacity); + return self; +} + +static inline void +vec_u8_free(struct vec_u8 *self) +{ + free(self); +} diff --git a/runtime/include/wasm_memory.h b/runtime/include/wasm_memory.h index d5a6fd5..4715b31 100644 --- a/runtime/include/wasm_memory.h +++ b/runtime/include/wasm_memory.h @@ -57,7 +57,7 @@ wasm_memory_allocate(size_t initial, size_t max) static inline void wasm_memory_free(struct wasm_memory *self) { - size_t size_to_free = sizeof(struct wasm_memory) + self->max + /* guard page */ PAGE_SIZE; + size_t size_to_free = sizeof(struct wasm_memory) + WASM_MEMORY_MAX + /* guard page */ PAGE_SIZE; munmap(self, size_to_free); } diff --git a/runtime/include/wasm_store.h b/runtime/include/wasm_module_instance.h similarity index 100% rename from runtime/include/wasm_store.h rename to runtime/include/wasm_module_instance.h diff --git a/runtime/include/wasm_stack.h b/runtime/include/wasm_stack.h new file mode 100644 index 0000000..2f3a662 --- /dev/null +++ b/runtime/include/wasm_stack.h @@ -0,0 +1,70 @@ +#pragma once + +#include +#include +#include + +#include "sandbox_types.h" + +struct wasm_stack { + size_t capacity; /* Usable capacity. Excludes size of guard page that we need to free */ + uint8_t *high; /* The highest address of the stack. Grows down from here */ + uint8_t *low; /* The address of the lowest usabe address. Above guard page */ + uint8_t *buffer; /* Points to Guard Page */ +}; + +/** + * Allocates a static sized stack for a sandbox with a guard page underneath + * Because a stack grows down, this protects against stack overflow + * TODO: Should this use MAP_GROWSDOWN to enable demand paging for the stack? + * @param sandbox sandbox that we want to allocate a stack for + * @returns 0 on success, -1 on error + */ +static inline int +wasm_stack_allocate(struct wasm_stack *stack, size_t capacity) +{ + assert(stack); + + int rc = 0; + + char *addr, *addr_rw; + + stack->buffer = (uint8_t *)mmap(NULL, /* guard page */ PAGE_SIZE + capacity, PROT_NONE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (unlikely(stack->buffer == MAP_FAILED)) { + perror("sandbox allocate stack"); + goto err_stack_allocation_failed; + } + + stack->low = (uint8_t *)mmap(stack->buffer + /* guard page */ PAGE_SIZE, capacity, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0); + if (unlikely(addr_rw == MAP_FAILED)) { + perror("sandbox set stack read/write"); + goto err_stack_prot_failed; + } + + stack->capacity = capacity; + stack->high = stack->low + capacity; + + rc = 0; +done: + return rc; +err_stack_prot_failed: + rc = munmap(stack->buffer, PAGE_SIZE + capacity); + if (rc == -1) perror("munmap"); +err_stack_allocation_failed: + stack->buffer = NULL; + rc = -1; + goto done; +} + +static inline void +wasm_stack_free(struct wasm_stack *stack) +{ + assert(stack != NULL); + assert(stack->buffer != NULL); + /* The stack start is the bottom of the usable stack, but we allocated a guard page below this */ + int rc = munmap(stack->buffer, stack->capacity + PAGE_SIZE); + stack->buffer = NULL; + if (unlikely(rc == -1)) perror("munmap"); +} diff --git a/runtime/src/global_request_scheduler.c b/runtime/src/global_request_scheduler.c index f88e4e9..459eef9 100644 --- a/runtime/src/global_request_scheduler.c +++ b/runtime/src/global_request_scheduler.c @@ -4,14 +4,14 @@ #include "panic.h" /* Default uninitialized implementations of the polymorphic interface */ -noreturn static struct sandbox_request * +noreturn static struct sandbox * uninitialized_add(void *arg) { panic("Global Request Scheduler Add was called before initialization\n"); } noreturn static int -uninitialized_remove(struct sandbox_request **arg) +uninitialized_remove(struct sandbox **arg) { panic("Global Request Scheduler Remove was called before initialization\n"); } @@ -41,44 +41,44 @@ global_request_scheduler_initialize(struct global_request_scheduler_config *conf /** - * Adds a sandbox request to the request scheduler - * @param sandbox_request + * Adds a sandbox to the request scheduler + * @param sandbox */ -struct sandbox_request * -global_request_scheduler_add(struct sandbox_request *sandbox_request) +struct sandbox * +global_request_scheduler_add(struct sandbox *sandbox) { - assert(sandbox_request != NULL); - return global_request_scheduler.add_fn(sandbox_request); + assert(sandbox != NULL); + return global_request_scheduler.add_fn(sandbox); } /** - * Removes a sandbox request according to the scheduling policy of the variant + * Removes a sandbox according to the scheduling policy of the variant * @param removed_sandbox where to write the adddress of the removed sandbox - * @returns 0 if successfully returned a sandbox request, -ENOENT if empty, -EAGAIN if atomic operation unsuccessful + * @returns 0 if successfully returned a sandbox, -ENOENT if empty, -EAGAIN if atomic operation unsuccessful */ int -global_request_scheduler_remove(struct sandbox_request **removed_sandbox) +global_request_scheduler_remove(struct sandbox **removed_sandbox) { assert(removed_sandbox != NULL); return global_request_scheduler.remove_fn(removed_sandbox); } /** - * Removes a sandbox request according to the scheduling policy of the variant + * Removes a sandbox according to the scheduling policy of the variant * @param removed_sandbox where to write the adddress of the removed sandbox * @param target_deadline the deadline that must be validated before dequeuing - * @returns 0 if successfully returned a sandbox request, -ENOENT if empty or if no element meets target_deadline, + * @returns 0 if successfully returned a sandbox, -ENOENT if empty or if no element meets target_deadline, * -EAGAIN if atomic operation unsuccessful */ int -global_request_scheduler_remove_if_earlier(struct sandbox_request **removed_sandbox, uint64_t target_deadline) +global_request_scheduler_remove_if_earlier(struct sandbox **removed_sandbox, uint64_t target_deadline) { assert(removed_sandbox != NULL); return global_request_scheduler.remove_if_earlier_fn(removed_sandbox, target_deadline); } /** - * Peeks at the priority of the highest priority sandbox request + * Peeks at the priority of the highest priority sandbox * @returns highest priority */ uint64_t diff --git a/runtime/src/global_request_scheduler_deque.c b/runtime/src/global_request_scheduler_deque.c index 69f415a..d850ffd 100644 --- a/runtime/src/global_request_scheduler_deque.c +++ b/runtime/src/global_request_scheduler_deque.c @@ -1,46 +1,48 @@ #include "global_request_scheduler.h" +#include "global_request_scheduler_deque.h" #include "runtime.h" +#define GLOBAL_REQUEST_SCHEDULER_DEQUE_CAPACITY (1 << 19) + static struct deque_sandbox *global_request_scheduler_deque; /* TODO: Should this be used??? */ static pthread_mutex_t global_request_scheduler_deque_mutex = PTHREAD_MUTEX_INITIALIZER; /** - * Pushes a sandbox request to the global deque - * @param sandbox_request + * Pushes a sandbox to the global deque + * @param sandbox_raw * @returns pointer to request if added. NULL otherwise */ -static struct sandbox_request * -global_request_scheduler_deque_add(void *sandbox_request_raw) +static struct sandbox * +global_request_scheduler_deque_add(void *sandbox_raw) { - struct sandbox_request *sandbox_request = (struct sandbox_request *)sandbox_request_raw; - int return_code = 1; + struct sandbox *sandbox = (struct sandbox *)sandbox_raw; + int return_code = 1; - return_code = deque_push_sandbox(global_request_scheduler_deque, &sandbox_request); + return_code = deque_push_sandbox(global_request_scheduler_deque, &sandbox); if (return_code != 0) return NULL; - return sandbox_request_raw; + return sandbox_raw; } /** * Stealing from the dequeue is a lock-free, cross-core "pop", which removes the element from the end opposite to - * "pop". Because the producer and consumer (the core stealine the sandbox request) modify different ends, + * "pop". Because the producer and consumer (the core stealine the sandbox) modify different ends, * no locks are required, and coordination is achieved by instead retrying on inconsistent indices. * * Relevant Read: https://www.dre.vanderbilt.edu/~schmidt/PDF/work-stealing-dequeue.pdf * - * @returns 0 if successfully returned a sandbox request, -ENOENT if empty, -EAGAIN if atomic instruction unsuccessful + * @returns 0 if successfully returned a sandbox, -ENOENT if empty, -EAGAIN if atomic instruction unsuccessful */ static int -global_request_scheduler_deque_remove(struct sandbox_request **removed_sandbox_request) +global_request_scheduler_deque_remove(struct sandbox **removed_sandbox) { - return deque_steal_sandbox(global_request_scheduler_deque, removed_sandbox_request); + return deque_steal_sandbox(global_request_scheduler_deque, removed_sandbox); } static int -global_request_scheduler_deque_remove_if_earlier(struct sandbox_request **removed_sandbox_request, - uint64_t target_deadline) +global_request_scheduler_deque_remove_if_earlier(struct sandbox **removed_sandbox, uint64_t target_deadline) { panic("Deque variant does not support this call\n"); return -1; @@ -53,7 +55,7 @@ global_request_scheduler_deque_initialize() global_request_scheduler_deque = (struct deque_sandbox *)malloc(sizeof(struct deque_sandbox)); assert(global_request_scheduler_deque); /* Note: Below is a Macro */ - deque_init_sandbox(global_request_scheduler_deque, RUNTIME_MAX_SANDBOX_REQUEST_COUNT); + deque_init_sandbox(global_request_scheduler_deque, GLOBAL_REQUEST_SCHEDULER_DEQUE_CAPACITY); /* Register Function Pointers for Abstract Scheduling API */ struct global_request_scheduler_config config = { diff --git a/runtime/src/global_request_scheduler_minheap.c b/runtime/src/global_request_scheduler_minheap.c index dc054ef..3d8facf 100644 --- a/runtime/src/global_request_scheduler_minheap.c +++ b/runtime/src/global_request_scheduler_minheap.c @@ -10,43 +10,42 @@ static struct priority_queue *global_request_scheduler_minheap; /** - * Pushes a sandbox request to the global deque - * @param sandbox_request - * @returns pointer to request if added. NULL otherwise + * Pushes a sandbox to the global deque + * @param sandbox + * @returns pointer to request if added. Panics runtime otherwise */ -static struct sandbox_request * -global_request_scheduler_minheap_add(void *sandbox_request) +static struct sandbox * +global_request_scheduler_minheap_add(void *sandbox_raw) { - assert(sandbox_request); + assert(sandbox_raw); assert(global_request_scheduler_minheap); if (unlikely(!listener_thread_is_running())) panic("%s is only callable by the listener thread\n", __func__); - int return_code = priority_queue_enqueue(global_request_scheduler_minheap, sandbox_request); + int return_code = priority_queue_enqueue(global_request_scheduler_minheap, sandbox_raw); /* TODO: Propagate -1 to caller. Issue #91 */ if (return_code == -ENOSPC) panic("Request Queue is full\n"); - return sandbox_request; + return (struct sandbox *)sandbox_raw; } /** - * @param pointer to the pointer that we want to set to the address of the removed sandbox request + * @param pointer to the pointer that we want to set to the address of the removed sandbox * @returns 0 if successful, -ENOENT if empty */ int -global_request_scheduler_minheap_remove(struct sandbox_request **removed_sandbox_request) +global_request_scheduler_minheap_remove(struct sandbox **removed_sandbox) { - return priority_queue_dequeue(global_request_scheduler_minheap, (void **)removed_sandbox_request); + return priority_queue_dequeue(global_request_scheduler_minheap, (void **)removed_sandbox); } /** - * @param removed_sandbox_request pointer to set to removed sandbox request + * @param removed_sandbox pointer to set to removed sandbox * @param target_deadline the deadline that the request must be earlier than to dequeue * @returns 0 if successful, -ENOENT if empty or if request isn't earlier than target_deadline */ int -global_request_scheduler_minheap_remove_if_earlier(struct sandbox_request **removed_sandbox_request, - uint64_t target_deadline) +global_request_scheduler_minheap_remove_if_earlier(struct sandbox **removed_sandbox, uint64_t target_deadline) { - return priority_queue_dequeue_if_earlier(global_request_scheduler_minheap, (void **)removed_sandbox_request, + return priority_queue_dequeue_if_earlier(global_request_scheduler_minheap, (void **)removed_sandbox, target_deadline); } @@ -63,10 +62,10 @@ global_request_scheduler_minheap_peek(void) } uint64_t -sandbox_request_get_priority_fn(void *element) +sandbox_get_priority_fn(void *element) { - struct sandbox_request *sandbox_request = (struct sandbox_request *)element; - return sandbox_request->absolute_deadline; + struct sandbox *sandbox = (struct sandbox *)element; + return sandbox->absolute_deadline; }; @@ -76,7 +75,7 @@ sandbox_request_get_priority_fn(void *element) void global_request_scheduler_minheap_initialize() { - global_request_scheduler_minheap = priority_queue_initialize(4096, true, sandbox_request_get_priority_fn); + global_request_scheduler_minheap = priority_queue_initialize(4096, true, sandbox_get_priority_fn); struct global_request_scheduler_config config = { .add_fn = global_request_scheduler_minheap_add, diff --git a/runtime/src/libc/syscall.c b/runtime/src/libc/syscall.c index 04fddf7..8e71b1b 100644 --- a/runtime/src/libc/syscall.c +++ b/runtime/src/libc/syscall.c @@ -14,7 +14,7 @@ #include "scheduler.h" #include "sandbox_functions.h" #include "worker_thread.h" -#include "wasm_store.h" +#include "wasm_module_instance.h" // What should we tell the child program its UID and GID are? #define UID 0xFF @@ -141,12 +141,12 @@ wasm_write(int32_t fd, int32_t buf_offset, int32_t buf_size) if (fd == STDERR_FILENO) { write(STDERR_FILENO, buffer, buf_size); } if (fd == STDOUT_FILENO) { - int buffer_remaining = s->module->max_response_size - s->response.length; + int buffer_remaining = s->response->capacity - s->response->length; int to_write = buffer_remaining > buf_size ? buf_size : buffer_remaining; if (to_write == 0) return 0; - memcpy(&s->response.base[s->response.length], buffer, to_write); - s->response.length += to_write; + memcpy(&s->response->buffer[s->response->length], buffer, to_write); + s->response->length += to_write; return to_write; } diff --git a/runtime/src/listener_thread.c b/runtime/src/listener_thread.c index 4752dce..e47470c 100644 --- a/runtime/src/listener_thread.c +++ b/runtime/src/listener_thread.c @@ -7,6 +7,7 @@ #include "generic_thread.h" #include "listener_thread.h" #include "runtime.h" +#include "sandbox_functions.h" /* * Descriptor of the epoll instance used to monitor the socket descriptors of registered @@ -177,14 +178,19 @@ listener_thread_main(void *dummy) continue; } - /* Allocate a Sandbox Request */ - struct sandbox_request *sandbox_request = - sandbox_request_allocate(module, client_socket, - (const struct sockaddr *)&client_address, - request_arrival_timestamp, work_admitted); + /* Allocate a Sandbox */ + struct sandbox *sandbox = sandbox_new(module, client_socket, + (const struct sockaddr *)&client_address, + request_arrival_timestamp, work_admitted); + if (unlikely(sandbox == NULL)) { + client_socket_send_oneshot(sandbox->client_socket_descriptor, + http_header_build(503), http_header_len(503)); + client_socket_close(sandbox->client_socket_descriptor, + &sandbox->client_address); + } /* Add to the Global Sandbox Request Scheduler */ - global_request_scheduler_add(sandbox_request); + global_request_scheduler_add(sandbox); } /* while true */ } /* for loop */ diff --git a/runtime/src/main.c b/runtime/src/main.c index 1faed46..7819b2c 100644 --- a/runtime/src/main.c +++ b/runtime/src/main.c @@ -297,7 +297,7 @@ log_compiletime_config() pretty_print_key_disabled("Log Preemption"); #endif -#ifdef LOG_REQUEST_ALLOCATION +#ifdef LOG_SANDBOX_ALLOCATION pretty_print_key_enabled("Log Request Allocation"); #else pretty_print_key_disabled("Log Request Allocation"); @@ -321,10 +321,10 @@ log_compiletime_config() pretty_print_key_disabled("Log Total Reqs/Resps"); #endif -#ifdef LOG_SANDBOX_COUNT - pretty_print_key_enabled("Log Sandbox Count"); +#ifdef SANDBOX_STATE_TOTALS + pretty_print_key_enabled("Log Sandbox State Count"); #else - pretty_print_key_disabled("Log Sandbox Count"); + pretty_print_key_disabled("Log Sandbox State Count"); #endif #ifdef LOG_LOCAL_RUNQUEUE diff --git a/runtime/src/runtime.c b/runtime/src/runtime.c index b8515be..11ac861 100644 --- a/runtime/src/runtime.c +++ b/runtime/src/runtime.c @@ -20,7 +20,7 @@ #include "listener_thread.h" #include "module.h" #include "runtime.h" -#include "sandbox_request.h" +#include "sandbox_total.h" #include "scheduler.h" #include "software_interrupt.h" @@ -101,8 +101,8 @@ runtime_initialize(void) memset(runtime_worker_threads_deadline, UINT8_MAX, runtime_worker_threads_count * sizeof(uint64_t)); http_total_init(); - sandbox_request_count_initialize(); - sandbox_count_initialize(); + sandbox_total_initialize(); + sandbox_state_totals_initialize(); /* Setup Scheduler */ scheduler_initialize(); diff --git a/runtime/src/sandbox.c b/runtime/src/sandbox.c index 15684d6..c160a42 100644 --- a/runtime/src/sandbox.c +++ b/runtime/src/sandbox.c @@ -5,16 +5,30 @@ #include "current_sandbox.h" #include "debuglog.h" #include "panic.h" +#include "runtime.h" #include "sandbox_functions.h" #include "sandbox_set_as_error.h" #include "sandbox_set_as_initialized.h" +#include "sandbox_set_as_allocated.h" +#include "sandbox_total.h" #include "wasm_memory.h" +#include "wasm_stack.h" + +_Atomic uint32_t sandbox_total = 0; + +static inline void +sandbox_log_allocation(struct sandbox *sandbox) +{ +#ifdef LOG_SANDBOX_ALLOCATION + debuglog("Sandbox %lu: of %s:%d\n", sandbox->id, sandbox->module->name, sandbox->module->port); +#endif +} /** - * Allocates a WebAssembly sandbox represented by the following layout - * struct sandbox | HTTP Req Buffer | HTTP Resp Buffer | 4GB of Wasm Linear Memory | Guard Page - * @param module the module that we want to run - * @returns the resulting sandbox or NULL if mmap failed + * Allocates a WebAssembly linear memory for a sandbox based on the starting_pages and max_pages globals present in + * the associated *.so module + * @param sandbox sandbox that we want to allocate a linear memory for + * @returns 0 on success, -1 on error */ static inline int sandbox_allocate_linear_memory(struct sandbox *sandbox) @@ -23,8 +37,8 @@ sandbox_allocate_linear_memory(struct sandbox *sandbox) char *error_message = NULL; - size_t initial = (size_t)WASM_MEMORY_PAGES_INITIAL * WASM_PAGE_SIZE; - size_t max = (size_t)WASM_MEMORY_PAGES_MAX * WASM_PAGE_SIZE; + size_t initial = (size_t)sandbox->module->abi.starting_pages * WASM_PAGE_SIZE; + size_t max = (size_t)sandbox->module->abi.max_pages * WASM_PAGE_SIZE; assert(initial <= (size_t)UINT32_MAX + 1); assert(max <= (size_t)UINT32_MAX + 1); @@ -41,48 +55,33 @@ sandbox_allocate_stack(struct sandbox *sandbox) assert(sandbox); assert(sandbox->module); - int rc = 0; - - char *addr = mmap(NULL, /* guard page */ PAGE_SIZE + sandbox->module->stack_size, PROT_NONE, - MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); - if (unlikely(addr == MAP_FAILED)) { - perror("sandbox allocate stack"); - goto err_stack_allocation_failed; - } - - /* Set the struct sandbox, HTTP Req/Resp buffer, and the initial Wasm Pages as read/write */ - char *addr_rw = mmap(addr + /* guard page */ PAGE_SIZE, sandbox->module->stack_size, PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0); - if (unlikely(addr_rw == MAP_FAILED)) { - perror("sandbox set stack read/write"); - goto err_stack_allocation_failed; - } + return wasm_stack_allocate(&sandbox->stack, sandbox->module->stack_size); +} - sandbox->stack.start = addr_rw; - sandbox->stack.size = sandbox->module->stack_size; +static inline void +sandbox_free_stack(struct sandbox *sandbox) +{ + assert(sandbox); - rc = 0; -done: - return rc; -err_stack_prot_failed: - rc = munmap(addr, sandbox->stack.size + PAGE_SIZE); - if (rc == -1) perror("munmap"); -err_stack_allocation_failed: - sandbox->stack.start = NULL; - sandbox->stack.size = 0; - goto done; + return wasm_stack_free(&sandbox->stack); } +/** + * Allocate http request and response buffers for a sandbox + * @param sandbox sandbox that we want to allocate HTTP buffers for + * @returns 0 on success, -1 on error + */ static inline int sandbox_allocate_http_buffers(struct sandbox *self) { - self->request.base = calloc(1, self->module->max_request_size); - if (self->request.base == NULL) return -1; - self->request.length = 0; + self->request = vec_u8_new(self->module->max_request_size); + if (self->request == NULL) return -1; - self->response.base = calloc(1, self->module->max_response_size); - if (self->response.base == NULL) return -1; - self->response.length = 0; + self->response = vec_u8_new(self->module->max_response_size); + if (self->response == NULL) { + vec_u8_free(self->request); + return -1; + } return 0; } @@ -93,35 +92,25 @@ sandbox_allocate(void) struct sandbox *sandbox = NULL; size_t page_aligned_sandbox_size = round_up_to_page(sizeof(struct sandbox)); sandbox = calloc(1, page_aligned_sandbox_size); - sandbox->state = SANDBOX_ALLOCATED; + sandbox_set_as_allocated(sandbox); return sandbox; } - /** - * Allocates a new sandbox from a sandbox request - * Frees the sandbox request on success - * @param sandbox_request request being allocated - * @returns sandbox * on success, NULL on error + * Allocates HTTP buffers and performs our approximation of "WebAssembly instantiation" + * @param sandbox + * @returns 0 on success, -1 on error */ -struct sandbox * -sandbox_new(struct sandbox_request *sandbox_request) +int +sandbox_prepare_execution_environemnt(struct sandbox *sandbox) { - /* Validate Arguments */ - assert(sandbox_request != NULL); + assert(sandbox != NULL); char * error_message = ""; uint64_t now = __getcycles(); int rc; - struct sandbox *sandbox = sandbox_allocate(); - if (sandbox == NULL) goto err_struct_allocation_failed; - - sandbox_init(sandbox, sandbox_request, now); - - free(sandbox_request); - if (sandbox_allocate_http_buffers(sandbox)) { error_message = "failed to allocate http buffers"; goto err_http_allocation_failed; @@ -140,30 +129,74 @@ sandbox_new(struct sandbox_request *sandbox_request) } /* Initialize the sandbox's context, stack, and instruction pointer */ - /* stack.start points to the bottom of the usable stack, so add stack_size to get to top */ - arch_context_init(&sandbox->ctxt, (reg_t)current_sandbox_start, - (reg_t)sandbox->stack.start + sandbox->stack.size); + /* stack grows down, so set to high address */ + arch_context_init(&sandbox->ctxt, (reg_t)current_sandbox_start, (reg_t)sandbox->stack.high); + rc = 0; done: - return sandbox; + return rc; err_stack_allocation_failed: - /* - * This is a degenerate sandbox that never successfully completed initialization, so we need to - * hand jam some things to be able to cleanly transition to ERROR state - */ - sandbox->state = SANDBOX_UNINITIALIZED; - sandbox->timestamp_of.last_state_change = now; - - ps_list_init_d(sandbox); err_memory_allocation_failed: err_http_allocation_failed: - sandbox_set_as_error(sandbox, SANDBOX_UNINITIALIZED); + client_socket_send_oneshot(sandbox->client_socket_descriptor, http_header_build(503), http_header_len(503)); + client_socket_close(sandbox->client_socket_descriptor, &sandbox->client_address); + sandbox_set_as_error(sandbox, SANDBOX_ALLOCATED); perror(error_message); -err_struct_allocation_failed: - sandbox = NULL; + rc = -1; goto done; } +void +sandbox_init(struct sandbox *sandbox, struct module *module, int socket_descriptor, + const struct sockaddr *socket_address, uint64_t request_arrival_timestamp, uint64_t admissions_estimate) +{ + /* Sets the ID to the value before the increment */ + sandbox->id = sandbox_total_postfix_increment(); + sandbox->module = module; + module_acquire(sandbox->module); + + /* Initialize Parsec control structures */ + ps_list_init_d(sandbox); + + sandbox->client_socket_descriptor = socket_descriptor; + memcpy(&sandbox->client_address, socket_address, sizeof(struct sockaddr)); + sandbox->timestamp_of.request_arrival = request_arrival_timestamp; + sandbox->absolute_deadline = request_arrival_timestamp + module->relative_deadline; + + /* + * Admissions Control State + * Assumption: an estimate of 0 should have been interpreted as a rejection + */ + assert(admissions_estimate != 0); + sandbox->admissions_estimate = admissions_estimate; + + sandbox_log_allocation(sandbox); + sandbox_set_as_initialized(sandbox, SANDBOX_ALLOCATED); +} + +/** + * Allocates a new Sandbox Request and places it on the Global Deque + * @param module the module we want to request + * @param socket_descriptor + * @param socket_address + * @param request_arrival_timestamp the timestamp of when we receives the request from the network (in cycles) + * @param admissions_estimate the timestamp of when we receives the request from the network (in cycles) + * @return the new sandbox request + */ +struct sandbox * +sandbox_new(struct module *module, int socket_descriptor, const struct sockaddr *socket_address, + uint64_t request_arrival_timestamp, uint64_t admissions_estimate) +{ + struct sandbox *sandbox = sandbox_allocate(); + assert(sandbox); + + sandbox_init(sandbox, module, socket_descriptor, socket_address, request_arrival_timestamp, + admissions_estimate); + + + return sandbox; +} + /** * Free stack and heap resources.. also any I/O handles. @@ -180,31 +213,14 @@ sandbox_free(struct sandbox *sandbox) module_release(sandbox->module); - /* Free Sandbox Stack if initial allocation was successful */ - if (likely(sandbox->stack.size > 0)) { - assert(sandbox->stack.start != NULL); - /* The stack start is the bottom of the usable stack, but we allocated a guard page below this */ - rc = munmap((char *)sandbox->stack.start - PAGE_SIZE, sandbox->stack.size + PAGE_SIZE); - if (unlikely(rc == -1)) { - debuglog("Failed to unmap stack of Sandbox %lu\n", sandbox->id); - goto err_free_stack_failed; - }; - } - - - /* Free Sandbox Struct and HTTP Request and Response Buffers - * The linear memory was already freed during the transition from running to error|complete - * struct sandbox | HTTP Request Buffer | HTTP Response Buffer | 4GB of Wasm Linear Memory | Guard Page - * Allocated | Allocated | Allocated | Freed | Freed - */ - /* Linear Memory and Guard Page should already have been munmaped and set to NULL */ assert(sandbox->memory == NULL); - errno = 0; - unsigned long size_to_unmap = round_up_to_page(sizeof(struct sandbox)) + sandbox->module->max_request_size - + sandbox->module->max_response_size; - munmap(sandbox, size_to_unmap); + /* Free Sandbox Struct and HTTP Request and Response Buffers */ + + if (likely(sandbox->stack.buffer != NULL)) sandbox_free_stack(sandbox); + free(sandbox); + if (rc == -1) { debuglog("Failed to unmap Sandbox %lu\n", sandbox->id); goto err_free_sandbox_failed; diff --git a/runtime/src/sandbox_request.c b/runtime/src/sandbox_request.c deleted file mode 100644 index f19f7f4..0000000 --- a/runtime/src/sandbox_request.c +++ /dev/null @@ -1,3 +0,0 @@ -#include "sandbox_request.h" - -_Atomic uint32_t sandbox_request_count = 0; diff --git a/runtime/src/sandbox_state.c b/runtime/src/sandbox_state.c index 2c9d160..ab0eda5 100644 --- a/runtime/src/sandbox_state.c +++ b/runtime/src/sandbox_state.c @@ -23,31 +23,6 @@ const char *sandbox_state_labels[SANDBOX_STATE_COUNT] = { [SANDBOX_ERROR] = "Error" }; -#ifdef LOG_SANDBOX_COUNT -_Atomic uint32_t sandbox_state_count[SANDBOX_STATE_COUNT]; +#ifdef SANDBOX_STATE_TOTALS +_Atomic uint32_t sandbox_state_totals[SANDBOX_STATE_COUNT]; #endif - -/* - * Function intended to be interactively run in a debugger to look at sandbox totals - * via `call runtime_log_sandbox_states()` - */ -void -runtime_log_sandbox_states() -{ -#ifdef LOG_SANDBOX_COUNT - const size_t buffer_size = 1000; - char buffer[buffer_size] = ""; - for (int i = 0; i < SANDBOX_STATE_COUNT; i++) { - const size_t tiny_buffer_size = 50; - char tiny_buffer[tiny_buffer_size] = ""; - snprintf(tiny_buffer, tiny_buffer_size - 1, "%s: %u\n\t", sandbox_state_stringify(i), - atomic_load(&sandbox_state_count[i])); - strncat(buffer, tiny_buffer, buffer_size - 1 - strlen(buffer)); - } - - debuglog("%s", buffer); - -#else - debuglog("Must compile with LOG_SANDBOX_COUNT for this functionality!\n"); -#endif -};