refactor: Eliminate sandbox request

master
Sean McBride 3 years ago
parent 5e9c7d08d9
commit e888a14e51

@ -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,

@ -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

@ -2,7 +2,7 @@
#include <assert.h>
#include <math.h>
#include "wasm_store.h"
#include "wasm_module_instance.h"
extern thread_local struct wasm_module_instance current_wasm_module_instance;

@ -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;

@ -3,7 +3,7 @@
#include <threads.h>
#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;

@ -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;

@ -2,12 +2,12 @@
#include <stdint.h>
#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);

@ -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();

@ -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 */

@ -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

@ -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;

@ -1,91 +0,0 @@
#pragma once
#include <errno.h>
#include <stdatomic.h>
#include <stdbool.h>
#include <stdint.h>
#include <sys/socket.h>
#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;
}

@ -0,0 +1,30 @@
#pragma once
#include <assert.h>
#include <stddef.h>
#include <stdint.h>
#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);
}

@ -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

@ -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]

@ -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);

@ -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);
}

@ -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 */

@ -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

@ -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);
}

@ -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);
}

@ -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

@ -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;

@ -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
}

@ -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
}

@ -0,0 +1,19 @@
#pragma once
#include <stdatomic.h>
#include <stdint.h>
/* 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);
}

@ -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;

@ -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 *

@ -0,0 +1,37 @@
#pragma once
#include <stdint.h>
#include <stdlib.h>
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);
}

@ -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);
}

@ -0,0 +1,70 @@
#pragma once
#include <stdint.h>
#include <stdlib.h>
#include <sys/mman.h>
#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");
}

@ -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

@ -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 = {

@ -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,

@ -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;
}

@ -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 */

@ -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

@ -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();

@ -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;

@ -1,3 +0,0 @@
#include "sandbox_request.h"
_Atomic uint32_t sandbox_request_count = 0;

@ -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
};

Loading…
Cancel
Save