From cf5e6ea72d4f376e9b07fe5c0ad68e818ecb908b Mon Sep 17 00:00:00 2001 From: Sean McBride Date: Fri, 19 Nov 2021 13:28:27 -0500 Subject: [PATCH 01/17] feat: replay deferred sigalrms --- runtime/include/sandbox_set_as_running_user.h | 6 ++++-- runtime/src/software_interrupt.c | 9 +++++++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/runtime/include/sandbox_set_as_running_user.h b/runtime/include/sandbox_set_as_running_user.h index fa8832c..17ce9b6 100644 --- a/runtime/include/sandbox_set_as_running_user.h +++ b/runtime/include/sandbox_set_as_running_user.h @@ -42,9 +42,11 @@ sandbox_set_as_running_user(struct sandbox *sandbox, sandbox_state_t last_state) runtime_sandbox_total_increment(SANDBOX_RUNNING_USER); runtime_sandbox_total_decrement(last_state); - /* WARNING: This state change needs to be at the end of this transition because all code below this assignment - * is preemptable */ + /* WARNING: All code below this assignment is preemptable */ sandbox->state = SANDBOX_RUNNING_USER; + + /* Now that we are preemptable, we can replay deferred sigalrms */ + if (software_interrupt_deferred_sigalrm > 0) raise(SIGALRM); } static inline void diff --git a/runtime/src/software_interrupt.c b/runtime/src/software_interrupt.c index 2c7844f..ecffd02 100644 --- a/runtime/src/software_interrupt.c +++ b/runtime/src/software_interrupt.c @@ -250,11 +250,12 @@ software_interrupt_initialize(void) { struct sigaction signal_action; memset(&signal_action, 0, sizeof(struct sigaction)); + + /* All supported signals trigger the same signal handler */ signal_action.sa_sigaction = software_interrupt_handle_signals; signal_action.sa_flags = SA_SIGINFO | SA_RESTART; - /* all threads created by the calling thread will have signal blocked */ - /* TODO: What does sa_mask do? I have to call software_interrupt_mask_signal below */ + /* Mask SIGALRM and SIGUSR1 while the signal handler executes */ sigemptyset(&signal_action.sa_mask); sigaddset(&signal_action.sa_mask, SIGALRM); sigaddset(&signal_action.sa_mask, SIGUSR1); @@ -264,7 +265,11 @@ software_interrupt_initialize(void) for (int i = 0; i < supported_signals_len; i++) { int signal = supported_signals[i]; + + /* Mask this signal on the listener thread */ software_interrupt_mask_signal(signal); + + /* But register the handler for this signal for the process */ if (sigaction(signal, &signal_action, NULL)) { perror("sigaction"); exit(1); From 43592da62243620acc97bb72c24601d4ba4e4e4b Mon Sep 17 00:00:00 2001 From: Sean McBride Date: Fri, 19 Nov 2021 21:37:13 -0500 Subject: [PATCH 02/17] feat: signal tracking and pretty printing --- .vscode/settings.json | 3 +- runtime/Makefile | 16 +-- runtime/include/pretty_print.h | 41 ++++++ runtime/include/sandbox_perf_log.h | 5 +- runtime/include/sandbox_set_as_running_user.h | 2 +- runtime/include/scheduler.h | 12 +- runtime/include/software_interrupt.h | 35 +++-- runtime/include/software_interrupt_counts.h | 80 ++++++++++++ runtime/src/main.c | 123 ++++++++++-------- runtime/src/runtime.c | 7 +- runtime/src/software_interrupt.c | 120 ++++++----------- runtime/src/software_interrupt_counts.c | 26 ++++ 12 files changed, 300 insertions(+), 170 deletions(-) create mode 100644 runtime/include/pretty_print.h create mode 100644 runtime/include/software_interrupt_counts.h create mode 100644 runtime/src/software_interrupt_counts.c diff --git a/.vscode/settings.json b/.vscode/settings.json index 554a940..444dcd4 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -99,7 +99,8 @@ "sandbox_state_history.h": "c", "sandbox_set_as_running_user.h": "c", "scheduler.h": "c", - "sandbox_set_as_returned.h": "c" + "sandbox_set_as_returned.h": "c", + "software_interrupt_counts.h": "c" }, "files.exclude": { "**/.git": true, diff --git a/runtime/Makefile b/runtime/Makefile index 0a38294..c0c203b 100644 --- a/runtime/Makefile +++ b/runtime/Makefile @@ -49,21 +49,21 @@ BINARY_NAME=sledgert # CFLAGS += -DLOG_TO_FILE # Various Informational Logs for Debugging +# CFLAGS += -DLOG_ADMISSIONS_CONTROL +# CFLAGS += -DLOG_CONTEXT_SWITCHES # CFLAGS += -DLOG_HTTP_PARSER # CFLAGS += -DLOG_LOCK_OVERHEAD -# CFLAGS += -DLOG_CONTEXT_SWITCHES -# CFLAGS += -DLOG_ADMISSIONS_CONTROL -# CFLAGS += -DLOG_REQUEST_ALLOCATION -# CFLAGS += -DLOG_PREEMPTION # CFLAGS += -DLOG_MODULE_LOADING +# CFLAGS += -DLOG_PREEMPTION +# CFLAGS += -DLOG_REQUEST_ALLOCATION + +# Stores and logs extended signal information for each worker +# CFLAGS += -DLOG_SOFTWARE_INTERRUPT_COUNTS # This adds an array of sandbox states to all sandbox structs and appends states at each transition # The history trucates when the number of elements equal SANDBOX_STATE_HISTORY_CAPACITY # CFLAGS += -DLOG_STATE_CHANGES -# Stores the max number of deferred SIGALRMS for each worker -# CFLAGS += -DLOG_DEFERRED_SIGALRM_MAX - # This dumps per module *.csv files containing the cycle a sandbox has been in RUNNING when each # page is allocated. This helps understand the relationship to memory allocation and execution time. # CFLAGS += -DLOG_SANDBOX_MEMORY_PROFILE @@ -119,7 +119,7 @@ all: runtime bin/sledgert: ${CFILES} @echo "Compiling runtime" @mkdir -p bin/ - ${CC} ${INCLUDES} ${CFLAGS} ${LDFLAGS} ${JSMNCFLAGS} -L/usr/lib/ $^ -o bin/sledgert + ${CC} ${CFLAGS} ${LDFLAGS} ${JSMNCFLAGS} ${INCLUDES} -L/usr/lib/ $^ -o bin/sledgert .PHONY: runtime runtime: thirdparty bin/sledgert diff --git a/runtime/include/pretty_print.h b/runtime/include/pretty_print.h new file mode 100644 index 0000000..72ce7db --- /dev/null +++ b/runtime/include/pretty_print.h @@ -0,0 +1,41 @@ +#pragma once + +#include +#include + +#define PRETTY_PRINT_COLOR_CODE_RED "\033[1;31m" +#define PRETTY_COLOR_CODE_GREEN "\033[0;32m" +#define PRETTY_PRINT_COLOR_CODE_RESET "\033[0m" +#define PRETTY_PRINT_GREEN_ENABLED PRETTY_COLOR_CODE_GREEN "Enabled" PRETTY_PRINT_COLOR_CODE_RESET +#define PRETTY_PRINT_RED_DISABLED PRETTY_PRINT_COLOR_CODE_RED "Disabled" PRETTY_PRINT_COLOR_CODE_RESET +#define PRETTY_PRINT_KEY_LEN 30 + + +static inline void +pretty_print_key(char *heading) +{ + printf("\t%-*s", PRETTY_PRINT_KEY_LEN, heading); +} + +static inline void +pretty_print_key_value(char *key, char *fmt, ...) +{ + va_list ap; + pretty_print_key(key); + + va_start(ap, fmt); + vprintf(fmt, ap); + va_end(ap); +} + +static inline void +pretty_print_key_enabled(char *key) +{ + pretty_print_key_value(key, "%s\n", PRETTY_PRINT_GREEN_ENABLED); +} + +static inline void +pretty_print_key_disabled(char *key) +{ + pretty_print_key_value(key, "%s\n", PRETTY_PRINT_RED_DISABLED); +} diff --git a/runtime/include/sandbox_perf_log.h b/runtime/include/sandbox_perf_log.h index dc0b676..2463ba9 100644 --- a/runtime/include/sandbox_perf_log.h +++ b/runtime/include/sandbox_perf_log.h @@ -1,5 +1,6 @@ #pragma once +#include "pretty_print.h" #include "runtime.h" #include "sandbox_types.h" @@ -51,12 +52,12 @@ sandbox_perf_log_init() { char *sandbox_perf_log_path = getenv("SLEDGE_SANDBOX_PERF_LOG"); if (sandbox_perf_log_path != NULL) { - printf("\tSandbox Performance Log: %s\n", sandbox_perf_log_path); + pretty_print_key_value("Sandbox Performance Log", "%s\n", sandbox_perf_log_path); sandbox_perf_log = fopen(sandbox_perf_log_path, "w"); if (sandbox_perf_log == NULL) perror("sandbox_perf_log_init\n"); sandbox_perf_log_print_header(); } else { - printf("\tSandbox Performance Log: Disabled\n"); + pretty_print_key_disabled("Sandbox Performance Log"); } } diff --git a/runtime/include/sandbox_set_as_running_user.h b/runtime/include/sandbox_set_as_running_user.h index 17ce9b6..f177d57 100644 --- a/runtime/include/sandbox_set_as_running_user.h +++ b/runtime/include/sandbox_set_as_running_user.h @@ -46,7 +46,7 @@ sandbox_set_as_running_user(struct sandbox *sandbox, sandbox_state_t last_state) sandbox->state = SANDBOX_RUNNING_USER; /* Now that we are preemptable, we can replay deferred sigalrms */ - if (software_interrupt_deferred_sigalrm > 0) raise(SIGALRM); + software_interrupt_deferred_sigalrm_replay(); } static inline void diff --git a/runtime/include/scheduler.h b/runtime/include/scheduler.h index a4a4cac..16ecaeb 100644 --- a/runtime/include/scheduler.h +++ b/runtime/include/scheduler.h @@ -100,14 +100,6 @@ err: static inline struct sandbox * scheduler_get_next() { -#ifdef LOG_DEFERRED_SIGALRM_MAX - if (unlikely(software_interrupt_deferred_sigalrm - > software_interrupt_deferred_sigalrm_max[worker_thread_idx])) { - software_interrupt_deferred_sigalrm_max[worker_thread_idx] = software_interrupt_deferred_sigalrm; - } -#endif - - atomic_store(&software_interrupt_deferred_sigalrm, 0); switch (scheduler) { case SCHEDULER_EDF: return scheduler_edf_get_next(); @@ -215,6 +207,8 @@ scheduler_preemptive_sched(ucontext_t *interrupted_context) { assert(interrupted_context != NULL); + software_interrupt_deferred_sigalrm_clear(); + /* Process epoll to make sure that all runnable jobs are considered for execution */ scheduler_execute_epoll_loop(); @@ -292,6 +286,8 @@ scheduler_cooperative_sched() /* Assumption: only called by the "base context" */ assert(current_sandbox_get() == NULL); + software_interrupt_deferred_sigalrm_clear(); + /* Try to wakeup sleeping sandboxes */ scheduler_execute_epoll_loop(); diff --git a/runtime/include/software_interrupt.h b/runtime/include/software_interrupt.h index b1f4eac..43d22fb 100644 --- a/runtime/include/software_interrupt.h +++ b/runtime/include/software_interrupt.h @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -12,15 +13,9 @@ #include "debuglog.h" #include "runtime.h" +#include "software_interrupt_counts.h" #include "worker_thread.h" -/************ - * Externs * - ***********/ - -extern _Atomic thread_local volatile sig_atomic_t software_interrupt_deferred_sigalrm; -extern _Atomic volatile sig_atomic_t * software_interrupt_deferred_sigalrm_max; - /************************* * Public Static Inlines * ************************/ @@ -74,13 +69,29 @@ software_interrupt_unmask_signal(int signal) return 0; } +extern thread_local _Atomic volatile sig_atomic_t deferred_sigalrm; + +static inline void +software_interrupt_deferred_sigalrm_replay() +{ + if (deferred_sigalrm > 0) { + software_interrupt_counts_deferred_sigalrm_replay_increment(); + raise(SIGALRM); + } +} + +static inline void +software_interrupt_deferred_sigalrm_clear() +{ + software_interrupt_counts_deferred_sigalrm_max_update(deferred_sigalrm); + atomic_store(&deferred_sigalrm, 0); +} + /************************* - * Exports from module.c * + * Exports from software_interrupt.c * ************************/ -void software_interrupt_initialize(void); void software_interrupt_arm_timer(void); +void software_interrupt_cleanup(void); void software_interrupt_disarm_timer(void); -void software_interrupt_set_interval_duration(uint64_t cycles); -void software_interrupt_deferred_sigalrm_max_free(void); -void software_interrupt_deferred_sigalrm_max_print(void); +void software_interrupt_initialize(void); diff --git a/runtime/include/software_interrupt_counts.h b/runtime/include/software_interrupt_counts.h new file mode 100644 index 0000000..1b19705 --- /dev/null +++ b/runtime/include/software_interrupt_counts.h @@ -0,0 +1,80 @@ +#pragma once + +#include +#include +#include + +#include "worker_thread.h" + +extern _Atomic volatile sig_atomic_t *software_interrupt_counts_deferred_sigalrm_max; +extern _Atomic volatile sig_atomic_t *software_interrupt_counts_deferred_sigalrm_replay; +extern _Atomic volatile sig_atomic_t *software_interrupt_counts_sigalrm_kernel; +extern _Atomic volatile sig_atomic_t *software_interrupt_counts_sigalrm_thread; +extern _Atomic volatile sig_atomic_t *software_interrupt_counts_sigusr; + +static inline void +software_interrupt_counts_alloc() +{ +#ifdef LOG_SOFTWARE_INTERRUPT_COUNTS + software_interrupt_counts_deferred_sigalrm_max = calloc(runtime_worker_threads_count, sizeof(sig_atomic_t)); + software_interrupt_counts_deferred_sigalrm_replay = calloc(runtime_worker_threads_count, sizeof(sig_atomic_t)); + software_interrupt_counts_sigalrm_kernel = calloc(runtime_worker_threads_count, sizeof(sig_atomic_t)); + software_interrupt_counts_sigalrm_thread = calloc(runtime_worker_threads_count, sizeof(sig_atomic_t)); + software_interrupt_counts_sigusr = calloc(runtime_worker_threads_count, sizeof(sig_atomic_t)); +#endif +} + +static inline void +software_interrupt_counts_free() +{ +#ifdef LOG_SOFTWARE_INTERRUPT_COUNTS + free((void *)software_interrupt_counts_deferred_sigalrm_max); + free((void *)software_interrupt_counts_sigalrm_kernel); + free((void *)software_interrupt_counts_sigalrm_thread); + free((void *)software_interrupt_counts_sigusr); +#endif +} + +static inline void +software_interrupt_counts_deferred_sigalrm_max_update(int deferred_sigalrm_count) +{ +#ifdef LOG_SOFTWARE_INTERRUPT_COUNTS + if (unlikely(deferred_sigalrm_count > software_interrupt_counts_deferred_sigalrm_max[worker_thread_idx])) { + software_interrupt_counts_deferred_sigalrm_max[worker_thread_idx] = deferred_sigalrm_count; + } +#endif +} + +static inline void +software_interrupt_counts_deferred_sigalrm_replay_increment() +{ +#ifdef LOG_SOFTWARE_INTERRUPT_COUNTS + atomic_fetch_add(&software_interrupt_counts_deferred_sigalrm_replay[worker_thread_idx], 1); +#endif +} + +static inline void +software_interrupt_counts_sigalrm_kernel_increment() +{ +#ifdef LOG_SOFTWARE_INTERRUPT_COUNTS + atomic_fetch_add(&software_interrupt_counts_sigalrm_kernel[worker_thread_idx], 1); +#endif +} + +static inline void +software_interrupt_counts_sigalrm_thread_increment() +{ +#ifdef LOG_SOFTWARE_INTERRUPT_COUNTS + atomic_fetch_add(&software_interrupt_counts_sigalrm_thread[worker_thread_idx], 1); +#endif +} + +static inline void +software_interrupt_counts_sigusr_increment() +{ +#ifdef LOG_SOFTWARE_INTERRUPT_COUNTS + atomic_fetch_add(&software_interrupt_counts_sigusr[worker_thread_idx], 1); +#endif +} + +extern void software_interrupt_counts_log(); diff --git a/runtime/src/main.c b/runtime/src/main.c index 1e11a24..1faed46 100644 --- a/runtime/src/main.c +++ b/runtime/src/main.c @@ -14,6 +14,7 @@ #include #endif +#include "pretty_print.h" #include "debuglog.h" #include "listener_thread.h" #include "module.h" @@ -58,7 +59,8 @@ runtime_allocate_available_cores() /* Find the number of processors currently online */ runtime_total_online_processors = sysconf(_SC_NPROCESSORS_ONLN); - printf("\tCore Count (Online): %u\n", runtime_total_online_processors); + + pretty_print_key_value("Core Count (Online)", "%u\n", runtime_total_online_processors); /* If more than two cores are available, leave core 0 free to run OS tasks */ if (runtime_total_online_processors > 2) { @@ -84,9 +86,9 @@ runtime_allocate_available_cores() runtime_worker_threads_count = max_possible_workers; } - printf("\tListener core ID: %u\n", LISTENER_THREAD_CORE_ID); - printf("\tFirst Worker core ID: %u\n", runtime_first_worker_processor); - printf("\tWorker core count: %u\n", runtime_worker_threads_count); + pretty_print_key_value("Listener core ID", "%u\n", LISTENER_THREAD_CORE_ID); + pretty_print_key_value("First Worker core ID", "%u\n", runtime_first_worker_processor); + pretty_print_key_value("Worker core count", "%u\n", runtime_worker_threads_count); } /** @@ -184,7 +186,7 @@ runtime_configure() } else { panic("Invalid scheduler policy: %s. Must be {EDF|FIFO}\n", scheduler_policy); } - printf("\tScheduler Policy: %s\n", scheduler_print(scheduler)); + pretty_print_key_value("Scheduler Policy", "%s\n", scheduler_print(scheduler)); /* Sigalrm Handler Technique */ char *sigalrm_policy = getenv("SLEDGE_SIGALRM_HANDLER"); @@ -197,12 +199,13 @@ runtime_configure() } else { panic("Invalid sigalrm policy: %s. Must be {BROADCAST|TRIAGED}\n", sigalrm_policy); } - printf("\tSigalrm Policy: %s\n", runtime_print_sigalrm_handler(runtime_sigalrm_handler)); + pretty_print_key_value("Sigalrm Policy", "%s\n", runtime_print_sigalrm_handler(runtime_sigalrm_handler)); /* Runtime Preemption Toggle */ char *preempt_disable = getenv("SLEDGE_DISABLE_PREEMPTION"); if (preempt_disable != NULL && strcmp(preempt_disable, "false") != 0) runtime_preemption_enabled = false; - printf("\tPreemption: %s\n", runtime_preemption_enabled ? "Enabled" : "Disabled"); + pretty_print_key_value("Preemption", "%s\n", + runtime_preemption_enabled ? PRETTY_PRINT_GREEN_ENABLED : PRETTY_PRINT_RED_DISABLED); /* Runtime Quantum */ char *quantum_raw = getenv("SLEDGE_QUANTUM_US"); @@ -213,7 +216,7 @@ runtime_configure() panic("SLEDGE_QUANTUM_US must be less than 999999 ms, saw %ld\n", quantum); runtime_quantum_us = (uint32_t)quantum; } - printf("\tQuantum: %u us\n", runtime_quantum_us); + pretty_print_key_value("Quantum", "%u us\n", runtime_quantum_us); sandbox_perf_log_init(); } @@ -221,97 +224,113 @@ runtime_configure() void log_compiletime_config() { - printf("Static Compiler Flags:\n"); + /* System Stuff */ + printf("System Flags:\n"); + + pretty_print_key("Architecture"); +#if defined(aarch64) + printf("aarch64\n"); +#elif defined(x86_64) + printf("x86_64\n"); +#endif + + pretty_print_key_value("Page Size", "%lu\n", PAGE_SIZE); + + /* Feature Toggles */ + printf("Static Compiler Flags (Features):\n"); + #ifdef ADMISSIONS_CONTROL - printf("\tAdmissions Control: Enabled\n"); + pretty_print_key_enabled("Admissions Control"); #else - printf("\tAdmissions Control: Disabled\n"); + pretty_print_key_disabled("Admissions Control"); #endif -#ifdef NDEBUG - printf("\tAssertions and Debug Logs: Disabled\n"); + /* Debugging Flags */ + printf("Static Compiler Flags (Debugging):\n"); + +#ifndef NDEBUG + pretty_print_key_enabled("Assertions and Debug Logs"); #else - printf("\tAssertions and Debug Logs: Enabled\n"); + pretty_print_key_disabled("Assertions and Debug Logs"); #endif + pretty_print_key("Logging to"); #ifdef LOG_TO_FILE - printf("\tLogging to: %s\n", RUNTIME_LOG_FILE); + printf("%s\n", RUNTIME_LOG_FILE); #else - printf("\tLogging to: STDOUT and STDERR\n"); + printf("STDOUT and STDERR\n"); #endif -#if defined(aarch64) - printf("\tArchitecture: %s\n", "aarch64"); -#elif defined(x86_64) - printf("\tArchitecture: %s\n", "x86_64"); +#ifdef LOG_ADMISSIONS_CONTROL + pretty_print_key_enabled("Log Admissions Control"); +#else + pretty_print_key_disabled("Log Admissions Control"); #endif - printf("\tPage Size: %lu\n", PAGE_SIZE); - -#ifdef LOG_HTTP_PARSER - printf("\tLog HTTP Parser: Enabled\n"); +#ifdef LOG_CONTEXT_SWITCHES + pretty_print_key_enabled("Log Context Switches"); #else - printf("\tLog HTTP Parser: Disabled\n"); + pretty_print_key_disabled("Log Context Switches"); #endif -#ifdef LOG_STATE_CHANGES - printf("\tLog State Changes: Enabled\n"); +#ifdef LOG_HTTP_PARSER + pretty_print_key_enabled("Log HTTP Parser"); #else - printf("\tLog State Changes: Disabled\n"); + pretty_print_key_disabled("Log HTTP Parser"); #endif #ifdef LOG_LOCK_OVERHEAD - printf("\tLog Lock Overhead: Enabled\n"); + pretty_print_key_enabled("Log Lock Overhead"); #else - printf("\tLog Lock Overhead: Disabled\n"); + pretty_print_key_disabled("Log Lock Overhead"); #endif -#ifdef LOG_CONTEXT_SWITCHES - printf("\tLog Context Switches: Enabled\n"); +#ifdef LOG_MODULE_LOADING + pretty_print_key_enabled("Log Module Loading"); #else - printf("\tLog Context Switches: Disabled\n"); + pretty_print_key_disabled("Log Module Loading"); #endif -#ifdef LOG_ADMISSIONS_CONTROL - printf("\tLog Admissions Control: Enabled\n"); +#ifdef LOG_PREEMPTION + pretty_print_key_enabled("Log Preemption"); #else - printf("\tLog Admissions Control: Disabled\n"); + pretty_print_key_disabled("Log Preemption"); #endif #ifdef LOG_REQUEST_ALLOCATION - printf("\tLog Request Allocation: Enabled\n"); + pretty_print_key_enabled("Log Request Allocation"); #else - printf("\tLog Request Allocation: Disabled\n"); + pretty_print_key_disabled("Log Request Allocation"); #endif -#ifdef LOG_PREEMPTION - printf("\tLog Preemption: Enabled\n"); +#ifdef LOG_SOFTWARE_INTERRUPT_COUNTS + pretty_print_key_enabled("Log Software Interrupt Counts"); #else - printf("\tLog Preemption: Disabled\n"); + pretty_print_key_disabled("Log Software Interrupt Counts"); #endif -#ifdef LOG_MODULE_LOADING - printf("\tLog Module Loading: Enabled\n"); +#ifdef LOG_STATE_CHANGES + pretty_print_key_enabled("Log State Changes"); #else - printf("\tLog Module Loading: Disabled\n"); + pretty_print_key_disabled("Log State Changes"); #endif #ifdef LOG_TOTAL_REQS_RESPS - printf("\tLog Total Reqs/Resps: Enabled\n"); + pretty_print_key_enabled("Log Total Reqs/Resps"); #else - printf("\tLog Total Reqs/Resps: Disabled\n"); + pretty_print_key_disabled("Log Total Reqs/Resps"); #endif #ifdef LOG_SANDBOX_COUNT - printf("\tLog Sandbox Count: Enabled\n"); + pretty_print_key_enabled("Log Sandbox Count"); #else - printf("\tLog Sandbox Count: Disabled\n"); + pretty_print_key_disabled("Log Sandbox Count"); #endif #ifdef LOG_LOCAL_RUNQUEUE - printf("\tLog Local Runqueue: Enabled\n"); + pretty_print_key_enabled("Log Local Runqueue"); #else - printf("\tLog Local Runqueue: Disabled\n"); + pretty_print_key_disabled("Log Local Runqueue"); #endif } @@ -333,9 +352,9 @@ main(int argc, char **argv) runtime_processor_speed_MHz = runtime_get_processor_speed_MHz(); if (unlikely(runtime_processor_speed_MHz == 0)) panic("Failed to detect processor speed\n"); - software_interrupt_set_interval_duration(runtime_quantum_us * runtime_processor_speed_MHz); + int heading_length = 30; - printf("\tProcessor Speed: %u MHz\n", runtime_processor_speed_MHz); + pretty_print_key_value("Processor Speed", "%u MHz\n", runtime_processor_speed_MHz); runtime_set_resource_limits_to_max(); runtime_allocate_available_cores(); diff --git a/runtime/src/runtime.c b/runtime/src/runtime.c index 9226216..b8515be 100644 --- a/runtime/src/runtime.c +++ b/runtime/src/runtime.c @@ -46,8 +46,7 @@ runtime_cleanup() if (runtime_worker_threads_argument) free(runtime_worker_threads_argument); if (runtime_worker_threads) free(runtime_worker_threads); - software_interrupt_deferred_sigalrm_max_print(); - software_interrupt_deferred_sigalrm_max_free(); + software_interrupt_cleanup(); exit(EXIT_SUCCESS); } @@ -81,11 +80,11 @@ runtime_set_resource_limits_to_max() snprintf(max, uint64_t_max_digits, "%lu", limit.rlim_max); } if (limit.rlim_cur == limit.rlim_max) { - printf("\t%s: %s\n", resource_names[i], max); + pretty_print_key_value(resource_names[i], "%s\n", max); } else { limit.rlim_cur = limit.rlim_max; if (setrlimit(resource, &limit) < 0) panic_err(); - printf("\t%s: %s (Increased from %s)\n", resource_names[i], max, lim); + pretty_print_key_value(resource_names[i], "%s (Increased from %s)\n", max, lim); } } } diff --git a/runtime/src/software_interrupt.c b/runtime/src/software_interrupt.c index ecffd02..1202a7c 100644 --- a/runtime/src/software_interrupt.c +++ b/runtime/src/software_interrupt.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -21,52 +22,10 @@ #include "sandbox_types.h" #include "scheduler.h" #include "software_interrupt.h" +#include "software_interrupt_counts.h" -/******************* - * Process Globals * - ******************/ - -static uint64_t software_interrupt_interval_duration_in_cycles; - -/****************** - * Thread Globals * - *****************/ - -thread_local _Atomic static volatile sig_atomic_t software_interrupt_SIGALRM_kernel_count = 0; -thread_local _Atomic static volatile sig_atomic_t software_interrupt_SIGALRM_thread_count = 0; -thread_local _Atomic static volatile sig_atomic_t software_interrupt_SIGUSR_count = 0; -thread_local _Atomic volatile sig_atomic_t software_interrupt_deferred_sigalrm = 0; -thread_local _Atomic volatile sig_atomic_t software_interrupt_signal_depth = 0; - -_Atomic volatile sig_atomic_t *software_interrupt_deferred_sigalrm_max; - -void -software_interrupt_deferred_sigalrm_max_alloc() -{ -#ifdef LOG_DEFERRED_SIGALRM_MAX - software_interrupt_deferred_sigalrm_max = calloc(runtime_worker_threads_count, sizeof(_Atomic(sig_atomic_t))); -#endif -} - -void -software_interrupt_deferred_sigalrm_max_free() -{ -#ifdef LOG_DEFERRED_SIGALRM_MAX - if (software_interrupt_deferred_sigalrm_max) free((void *)software_interrupt_deferred_sigalrm_max); -#endif -} - -void -software_interrupt_deferred_sigalrm_max_print() -{ -#ifdef LOG_DEFERRED_SIGALRM_MAX - printf("Max Deferred Sigalrms\n"); - for (int i = 0; i < runtime_worker_threads_count; i++) { - printf("Worker %d: %d\n", i, software_interrupt_deferred_sigalrm_max[i]); - } - fflush(stdout); -#endif -} +thread_local _Atomic volatile sig_atomic_t handler_depth; +thread_local _Atomic volatile sig_atomic_t deferred_sigalrm; /*************************************** * Externs @@ -78,16 +37,22 @@ extern pthread_t *runtime_worker_threads; * Private Static Inlines * *************************/ +static inline void +defer_sigalrm() +{ + atomic_fetch_add(&deferred_sigalrm, 1); +} + /** * A POSIX signal is delivered to only one thread. * This function broadcasts the sigalarm signal to all other worker threads */ static inline void -sigalrm_propagate_workers(siginfo_t *signal_info) +propagate_sigalrm(siginfo_t *signal_info) { /* Signal was sent directly by the kernel, so forward to other threads */ if (signal_info->si_code == SI_KERNEL) { - atomic_fetch_add(&software_interrupt_SIGALRM_kernel_count, 1); + software_interrupt_counts_sigalrm_kernel_increment(); for (int i = 0; i < runtime_worker_threads_count; i++) { /* All threads should have been initialized */ assert(runtime_worker_threads[i] != 0); @@ -109,23 +74,12 @@ sigalrm_propagate_workers(siginfo_t *signal_info) } } } else { - atomic_fetch_add(&software_interrupt_SIGALRM_thread_count, 1); + software_interrupt_counts_sigalrm_thread_increment(); /* Signal forwarded from another thread. Just confirm it resulted from pthread_kill */ assert(signal_info->si_code == SI_TKILL); } } -/** - * Validates that the thread running the signal handler is a known worker thread - */ -static inline void -software_interrupt_validate_worker() -{ -#ifndef NDEBUG - if (listener_thread_is_running()) panic("The listener thread unexpectedly received a signal!"); -#endif -} - /** * The handler function for Software Interrupts (signals) * SIGALRM is executed periodically by an interval timer, causing preemption of the current sandbox @@ -144,55 +98,57 @@ software_interrupt_handle_signals(int signal_type, siginfo_t *signal_info, void assert(runtime_preemption_enabled); /* Signals should not nest */ - assert(software_interrupt_signal_depth == 0); - atomic_fetch_add(&software_interrupt_signal_depth, 1); + assert(handler_depth == 0); + atomic_fetch_add(&handler_depth, 1); ucontext_t * interrupted_context = (ucontext_t *)interrupted_context_raw; struct sandbox *current_sandbox = current_sandbox_get(); switch (signal_type) { case SIGALRM: { - sigalrm_propagate_workers(signal_info); + propagate_sigalrm(signal_info); - /* Nonpreemptive, so defer */ - if (!sandbox_is_preemptable(current_sandbox)) { - atomic_fetch_add(&software_interrupt_deferred_sigalrm, 1); - goto done; + if (sandbox_is_preemptable(current_sandbox)) { + scheduler_preemptive_sched(interrupted_context); + } else { + defer_sigalrm(); } - scheduler_preemptive_sched(interrupted_context); - - goto done; + break; } case SIGUSR1: { assert(current_sandbox); assert(current_sandbox->state == SANDBOX_PREEMPTED); assert(current_sandbox->ctxt.variant == ARCH_CONTEXT_VARIANT_SLOW); - atomic_fetch_add(&software_interrupt_SIGUSR_count, 1); + software_interrupt_counts_sigusr_increment(); #ifdef LOG_PREEMPTION - debuglog("Total SIGUSR1 Received: %d\n", software_interrupt_SIGUSR_count); + debuglog("Total SIGUSR1 Received: %d\n", sigusr_count); debuglog("Restoring sandbox: %lu, Stack %llu\n", current_sandbox->id, current_sandbox->ctxt.mctx.gregs[REG_RSP]); #endif /* It is the responsibility of the caller to invoke current_sandbox_set before triggering the SIGUSR1 */ scheduler_preemptive_switch_to(interrupted_context, current_sandbox); - goto done; + + break; } default: { + const char *signal_name = strsignal(signal_type); switch (signal_info->si_code) { case SI_TKILL: - panic("Unexpectedly received signal %d from a thread kill, but we have no handler\n", - signal_type); + panic("software_interrupt_handle_signals unexpectedly received signal %s from a thread kill\n", + signal_name); case SI_KERNEL: - panic("Unexpectedly received signal %d from the kernel, but we have no handler\n", signal_type); + panic("software_interrupt_handle_signals unexpectedly received signal %s from the kernel\n", + signal_name); default: - panic("Anomolous Signal\n"); + panic("software_interrupt_handle_signals unexpectedly received signal %s with si_code %d\n", + signal_name, signal_info->si_code); } } } -done: - atomic_fetch_sub(&software_interrupt_signal_depth, 1); + + atomic_fetch_sub(&handler_depth, 1); return; } @@ -240,7 +196,6 @@ software_interrupt_disarm_timer(void) } } - /** * Initialize software Interrupts * Register softint_handler to execute on SIGALRM and SIGUSR1 @@ -276,11 +231,12 @@ software_interrupt_initialize(void) } } - software_interrupt_deferred_sigalrm_max_alloc(); + software_interrupt_counts_alloc(); } void -software_interrupt_set_interval_duration(uint64_t cycles) +software_interrupt_cleanup() { - software_interrupt_interval_duration_in_cycles = cycles; + software_interrupt_counts_deferred_sigalrm_max_update(deferred_sigalrm); + software_interrupt_counts_log(); } diff --git a/runtime/src/software_interrupt_counts.c b/runtime/src/software_interrupt_counts.c new file mode 100644 index 0000000..aa735dc --- /dev/null +++ b/runtime/src/software_interrupt_counts.c @@ -0,0 +1,26 @@ +#include "software_interrupt_counts.h" +#include "pretty_print.h" + +_Atomic volatile sig_atomic_t *software_interrupt_counts_deferred_sigalrm_max; +_Atomic volatile sig_atomic_t *software_interrupt_counts_deferred_sigalrm_replay; +_Atomic volatile sig_atomic_t *software_interrupt_counts_sigalrm_kernel; +_Atomic volatile sig_atomic_t *software_interrupt_counts_sigalrm_thread; +_Atomic volatile sig_atomic_t *software_interrupt_counts_sigusr; + +void +software_interrupt_counts_log() +{ +#ifdef LOG_SOFTWARE_INTERRUPT_COUNTS + for (int i = 0; i < runtime_worker_threads_count; i++) { + printf("Worker %d:\n", i); + pretty_print_key_value("Deferred Sigalrm Max", "%12d\n", + software_interrupt_counts_deferred_sigalrm_max[i]); + pretty_print_key_value("Deferred Sigalrm Replay", "%12d\n", + software_interrupt_counts_deferred_sigalrm_replay[i]); + pretty_print_key_value("Siglarm Kernel Count", "%12d\n", software_interrupt_counts_sigalrm_kernel[i]); + pretty_print_key_value("Siglarm Thread Count", "%12d\n", software_interrupt_counts_sigalrm_thread[i]); + pretty_print_key_value("Sigusr Count", "%12d\n", software_interrupt_counts_sigusr[i]); + } + fflush(stdout); +#endif +} From 132401177c597c3958149a0b7c2c74c8ab475845 Mon Sep 17 00:00:00 2001 From: Sean McBride Date: Sat, 20 Nov 2021 12:42:10 -0500 Subject: [PATCH 03/17] fix: protect preemption changes with mem barriers --- runtime/include/runtime.h | 3 +++ runtime/include/sandbox_set_as_running_sys.h | 8 +++++++- runtime/include/sandbox_set_as_running_user.h | 5 ++++- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/runtime/include/runtime.h b/runtime/include/runtime.h index 75f1a99..c1db761 100644 --- a/runtime/include/runtime.h +++ b/runtime/include/runtime.h @@ -8,6 +8,9 @@ #include "likely.h" #include "types.h" +/* Memory barrier to prevent unexpected code migration due to compiler optimization */ +#define barrier() __asm__ __volatile__("" ::: "memory") + #define RUNTIME_EXPECTED_EXECUTION_US_MAX 3600000000 #define RUNTIME_HTTP_REQUEST_SIZE_MAX 100000000 /* 100 MB */ #define RUNTIME_HTTP_RESPONSE_SIZE_MAX 100000000 /* 100 MB */ diff --git a/runtime/include/sandbox_set_as_running_sys.h b/runtime/include/sandbox_set_as_running_sys.h index e140518..24c2b97 100644 --- a/runtime/include/sandbox_set_as_running_sys.h +++ b/runtime/include/sandbox_set_as_running_sys.h @@ -14,8 +14,14 @@ static inline void sandbox_set_as_running_sys(struct sandbox *sandbox, sandbox_state_t last_state) { assert(sandbox); + + /* WARNING: All code before this barrier is preemptable if transitioning from RUNNING_USER */ + barrier(); sandbox->state = SANDBOX_RUNNING_SYS; - uint64_t now = __getcycles(); + barrier(); + /* WARNING: All code after this barrier is non-preemptable */ + + uint64_t now = __getcycles(); switch (last_state) { case SANDBOX_RUNNING_USER: { diff --git a/runtime/include/sandbox_set_as_running_user.h b/runtime/include/sandbox_set_as_running_user.h index f177d57..f594c53 100644 --- a/runtime/include/sandbox_set_as_running_user.h +++ b/runtime/include/sandbox_set_as_running_user.h @@ -42,8 +42,11 @@ sandbox_set_as_running_user(struct sandbox *sandbox, sandbox_state_t last_state) runtime_sandbox_total_increment(SANDBOX_RUNNING_USER); runtime_sandbox_total_decrement(last_state); - /* WARNING: All code below this assignment is preemptable */ + /* WARNING: All code before this barrier is non-preemptable */ + barrier(); sandbox->state = SANDBOX_RUNNING_USER; + barrier(); + /* WARNING: All code after this barrier is preemptable */ /* Now that we are preemptable, we can replay deferred sigalrms */ software_interrupt_deferred_sigalrm_replay(); From ce132b7bcc8931a2332167761a36d7fee213ee82 Mon Sep 17 00:00:00 2001 From: Sean McBride Date: Sat, 20 Nov 2021 13:26:47 -0500 Subject: [PATCH 04/17] fix: Remove extra memory barriers --- runtime/include/sandbox_set_as_running_sys.h | 4 +--- runtime/include/sandbox_set_as_running_user.h | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/runtime/include/sandbox_set_as_running_sys.h b/runtime/include/sandbox_set_as_running_sys.h index 24c2b97..5fa0a25 100644 --- a/runtime/include/sandbox_set_as_running_sys.h +++ b/runtime/include/sandbox_set_as_running_sys.h @@ -15,11 +15,9 @@ sandbox_set_as_running_sys(struct sandbox *sandbox, sandbox_state_t last_state) { assert(sandbox); - /* WARNING: All code before this barrier is preemptable if transitioning from RUNNING_USER */ - barrier(); + /* WARNING: All code before this assignment is preemptable if transitioning from RUNNING_USER */ sandbox->state = SANDBOX_RUNNING_SYS; barrier(); - /* WARNING: All code after this barrier is non-preemptable */ uint64_t now = __getcycles(); diff --git a/runtime/include/sandbox_set_as_running_user.h b/runtime/include/sandbox_set_as_running_user.h index f594c53..ac0e043 100644 --- a/runtime/include/sandbox_set_as_running_user.h +++ b/runtime/include/sandbox_set_as_running_user.h @@ -42,11 +42,9 @@ sandbox_set_as_running_user(struct sandbox *sandbox, sandbox_state_t last_state) runtime_sandbox_total_increment(SANDBOX_RUNNING_USER); runtime_sandbox_total_decrement(last_state); - /* WARNING: All code before this barrier is non-preemptable */ barrier(); sandbox->state = SANDBOX_RUNNING_USER; - barrier(); - /* WARNING: All code after this barrier is preemptable */ + /* WARNING: All code after this assignment is preemptable */ /* Now that we are preemptable, we can replay deferred sigalrms */ software_interrupt_deferred_sigalrm_replay(); From 72e6c3e043e13b2376a3971d8955330be0b63253 Mon Sep 17 00:00:00 2001 From: Sean McBride Date: Mon, 22 Nov 2021 10:30:17 -0500 Subject: [PATCH 05/17] refactor: Cleanup deferred sigalrm handling --- runtime/include/sandbox_set_as_preempted.h | 1 - runtime/include/sandbox_set_as_running_sys.h | 1 - runtime/include/sandbox_set_as_running_user.h | 1 - runtime/include/scheduler.h | 26 ++++++++++++++----- runtime/include/software_interrupt.h | 15 ++++++----- runtime/src/current_sandbox.c | 14 ++++------ runtime/src/software_interrupt.c | 12 +++++++-- 7 files changed, 42 insertions(+), 28 deletions(-) diff --git a/runtime/include/sandbox_set_as_preempted.h b/runtime/include/sandbox_set_as_preempted.h index 46a9e83..4d999c8 100644 --- a/runtime/include/sandbox_set_as_preempted.h +++ b/runtime/include/sandbox_set_as_preempted.h @@ -27,7 +27,6 @@ sandbox_set_as_preempted(struct sandbox *sandbox, sandbox_state_t last_state) switch (last_state) { case SANDBOX_RUNNING_SYS: { - current_sandbox_set(NULL); break; } default: { diff --git a/runtime/include/sandbox_set_as_running_sys.h b/runtime/include/sandbox_set_as_running_sys.h index 5fa0a25..fd87035 100644 --- a/runtime/include/sandbox_set_as_running_sys.h +++ b/runtime/include/sandbox_set_as_running_sys.h @@ -29,7 +29,6 @@ sandbox_set_as_running_sys(struct sandbox *sandbox, sandbox_state_t last_state) } case SANDBOX_RUNNABLE: { assert(sandbox); - current_sandbox_set(sandbox); /* Does not handle context switch because the caller knows if we need to use fast or slow switched. We * can fix this by breakout out SANDBOX_RUNNABLE and SANDBOX_PREEMPTED */ break; diff --git a/runtime/include/sandbox_set_as_running_user.h b/runtime/include/sandbox_set_as_running_user.h index ac0e043..4ee3372 100644 --- a/runtime/include/sandbox_set_as_running_user.h +++ b/runtime/include/sandbox_set_as_running_user.h @@ -25,7 +25,6 @@ sandbox_set_as_running_user(struct sandbox *sandbox, sandbox_state_t last_state) } case SANDBOX_PREEMPTED: { assert(sandbox); - current_sandbox_set(sandbox); break; } default: { diff --git a/runtime/include/scheduler.h b/runtime/include/scheduler.h index 16ecaeb..da46f69 100644 --- a/runtime/include/scheduler.h +++ b/runtime/include/scheduler.h @@ -180,12 +180,14 @@ scheduler_preemptive_switch_to(ucontext_t *interrupted_context, struct sandbox * case ARCH_CONTEXT_VARIANT_FAST: { assert(next->state == SANDBOX_RUNNABLE); arch_context_restore_fast(&interrupted_context->uc_mcontext, &next->ctxt); + current_sandbox_set(next); sandbox_set_as_running_sys(next, SANDBOX_RUNNABLE); break; } case ARCH_CONTEXT_VARIANT_SLOW: { assert(next->state == SANDBOX_PREEMPTED); arch_context_restore_slow(&interrupted_context->uc_mcontext, &next->ctxt); + current_sandbox_set(next); sandbox_set_as_running_user(next, SANDBOX_PREEMPTED); break; } @@ -207,8 +209,6 @@ scheduler_preemptive_sched(ucontext_t *interrupted_context) { assert(interrupted_context != NULL); - software_interrupt_deferred_sigalrm_clear(); - /* Process epoll to make sure that all runnable jobs are considered for execution */ scheduler_execute_epoll_loop(); @@ -232,12 +232,10 @@ scheduler_preemptive_sched(ucontext_t *interrupted_context) debuglog("Preempting sandbox %lu to run sandbox %lu\n", current->id, next->id); #endif - scheduler_log_sandbox_switch(current, next); - /* Preempt executing sandbox */ + scheduler_log_sandbox_switch(current, next); sandbox_preempt(current); arch_context_save_slow(¤t->ctxt, &interrupted_context->uc_mcontext); - scheduler_preemptive_switch_to(interrupted_context, next); } @@ -259,13 +257,14 @@ scheduler_cooperative_switch_to(struct sandbox *next_sandbox) switch (next_sandbox->state) { case SANDBOX_RUNNABLE: { assert(next_context->variant == ARCH_CONTEXT_VARIANT_FAST); + current_sandbox_set(next_sandbox); sandbox_set_as_running_sys(next_sandbox, SANDBOX_RUNNABLE); break; } case SANDBOX_PREEMPTED: { assert(next_context->variant == ARCH_CONTEXT_VARIANT_SLOW); - /* arch_context_switch triggers a SIGUSR1, which transitions next_sandbox to running_user */ current_sandbox_set(next_sandbox); + /* arch_context_switch triggers a SIGUSR1, which transitions next_sandbox to running_user */ break; } default: { @@ -286,7 +285,8 @@ scheduler_cooperative_sched() /* Assumption: only called by the "base context" */ assert(current_sandbox_get() == NULL); - software_interrupt_deferred_sigalrm_clear(); + /* Deferred signals should have been cleared by this point */ + assert(deferred_sigalrm == 0); /* Try to wakeup sleeping sandboxes */ scheduler_execute_epoll_loop(); @@ -308,3 +308,15 @@ scheduler_worker_would_preempt(int worker_idx) uint64_t global_deadline = global_request_scheduler_peek(); return global_deadline < local_deadline; } + +static inline void +scheduler_switch_to_base_context(struct arch_context *current_context) +{ + /* Clear any deferred sigalrms we hit while cleaning up sandbox. We'll run the scheduler cooperatively + in the base context */ + software_interrupt_deferred_sigalrm_clear(); + + /* Assumption: Base Worker context should never be preempted */ + assert(worker_thread_base_context.variant == ARCH_CONTEXT_VARIANT_FAST); + arch_context_switch(current_context, &worker_thread_base_context); +} diff --git a/runtime/include/software_interrupt.h b/runtime/include/software_interrupt.h index 43d22fb..6dcbcd9 100644 --- a/runtime/include/software_interrupt.h +++ b/runtime/include/software_interrupt.h @@ -71,22 +71,23 @@ software_interrupt_unmask_signal(int signal) extern thread_local _Atomic volatile sig_atomic_t deferred_sigalrm; +static inline void +software_interrupt_deferred_sigalrm_clear() +{ + software_interrupt_counts_deferred_sigalrm_max_update(deferred_sigalrm); + atomic_store(&deferred_sigalrm, 0); +} + static inline void software_interrupt_deferred_sigalrm_replay() { if (deferred_sigalrm > 0) { + software_interrupt_deferred_sigalrm_clear(); software_interrupt_counts_deferred_sigalrm_replay_increment(); raise(SIGALRM); } } -static inline void -software_interrupt_deferred_sigalrm_clear() -{ - software_interrupt_counts_deferred_sigalrm_max_update(deferred_sigalrm); - atomic_store(&deferred_sigalrm, 0); -} - /************************* * Exports from software_interrupt.c * ************************/ diff --git a/runtime/src/current_sandbox.c b/runtime/src/current_sandbox.c index 2a06d9f..aa4ca29 100644 --- a/runtime/src/current_sandbox.c +++ b/runtime/src/current_sandbox.c @@ -34,6 +34,8 @@ void current_sandbox_sleep() { struct sandbox *sandbox = current_sandbox_get(); + current_sandbox_set(NULL); + assert(sandbox != NULL); struct arch_context *current_context = &sandbox->ctxt; @@ -52,11 +54,7 @@ current_sandbox_sleep() sandbox_state_stringify(sandbox->state)); } - current_sandbox_set(NULL); - - /* Assumption: Base Worker context should never be preempted */ - assert(worker_thread_base_context.variant == ARCH_CONTEXT_VARIANT_FAST); - arch_context_switch(current_context, &worker_thread_base_context); + scheduler_switch_to_base_context(current_context); } /** @@ -91,11 +89,9 @@ current_sandbox_exit() } /* Do not access sandbox after this, as it is on the completion queue! */ - /* Assumption: Base Worker context should never be preempted */ - assert(worker_thread_base_context.variant == ARCH_CONTEXT_VARIANT_FAST); - arch_context_switch(current_context, &worker_thread_base_context); + scheduler_switch_to_base_context(current_context); - /* The schduler should never switch back to completed sandboxes */ + /* The scheduler should never switch back to completed sandboxes */ assert(0); } diff --git a/runtime/src/software_interrupt.c b/runtime/src/software_interrupt.c index 1202a7c..a04428a 100644 --- a/runtime/src/software_interrupt.c +++ b/runtime/src/software_interrupt.c @@ -24,8 +24,8 @@ #include "software_interrupt.h" #include "software_interrupt_counts.h" -thread_local _Atomic volatile sig_atomic_t handler_depth; -thread_local _Atomic volatile sig_atomic_t deferred_sigalrm; +thread_local _Atomic volatile sig_atomic_t handler_depth = 0; +thread_local _Atomic volatile sig_atomic_t deferred_sigalrm = 0; /*************************************** * Externs @@ -37,6 +37,12 @@ extern pthread_t *runtime_worker_threads; * Private Static Inlines * *************************/ +static inline bool +worker_thread_is_running_cooperative_scheduler(void) +{ + return current_sandbox_get() == NULL; +} + static inline void defer_sigalrm() { @@ -108,6 +114,8 @@ software_interrupt_handle_signals(int signal_type, siginfo_t *signal_info, void case SIGALRM: { propagate_sigalrm(signal_info); + if (worker_thread_is_running_cooperative_scheduler()) break; + if (sandbox_is_preemptable(current_sandbox)) { scheduler_preemptive_sched(interrupted_context); } else { From 06910736d1d535ddd0ff0737ee2d64a214733085 Mon Sep 17 00:00:00 2001 From: Sean McBride Date: Mon, 22 Nov 2021 15:38:07 -0500 Subject: [PATCH 06/17] feat: Improved sandbox timekeeping --- .vscode/settings.json | 3 +- docs/sledge-states/README.md | 2 + docs/sledge-states/states.dot | 12 +- docs/sledge-states/states.svg | 171 ++++++++++-------- .../experiments/bimodal/edf_preemption.env | 1 + runtime/include/sandbox_functions.h | 6 - runtime/include/sandbox_perf_log.h | 11 +- runtime/include/sandbox_set_as_interrupted.h | 62 +++++++ runtime/include/sandbox_set_as_preempted.h | 6 +- runtime/include/sandbox_set_as_running_sys.h | 6 +- runtime/include/sandbox_set_as_running_user.h | 1 - runtime/include/sandbox_state.h | 1 + runtime/include/scheduler.h | 7 +- runtime/src/current_sandbox.c | 4 +- runtime/src/sandbox_state.c | 1 + runtime/src/software_interrupt.c | 22 ++- 16 files changed, 202 insertions(+), 114 deletions(-) create mode 100644 runtime/include/sandbox_set_as_interrupted.h diff --git a/.vscode/settings.json b/.vscode/settings.json index 444dcd4..ddc6b45 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -100,7 +100,8 @@ "sandbox_set_as_running_user.h": "c", "scheduler.h": "c", "sandbox_set_as_returned.h": "c", - "software_interrupt_counts.h": "c" + "software_interrupt_counts.h": "c", + "sandbox_set_as_running_sys.h": "c" }, "files.exclude": { "**/.git": true, diff --git a/docs/sledge-states/README.md b/docs/sledge-states/README.md index 0d0adbb..366221d 100644 --- a/docs/sledge-states/README.md +++ b/docs/sledge-states/README.md @@ -1 +1,3 @@ This is a state transition diagram of a sandbox. This maps to the state transition functions defined in `runtime/include/sandbox_set_as_*.h` + +Technically, this does not capture all state transitions to or from SANDBOX_INTERRUPTED, as any state can be interrupted by a SIGALRM and this would clutter the diagram. The only transitions shown to or from SANDBOX_INTERRUPTED are those leading to SANDBOX_PREEMPTED, as this reflects actual changes to a sandbox within the scheduler. All other transitions to/from SANDBOX_INTERRUPTED are mostly concerned with preventing scheduler execution time from being counted against sandbox execution times. diff --git a/docs/sledge-states/states.dot b/docs/sledge-states/states.dot index 6e85804..f64f276 100644 --- a/docs/sledge-states/states.dot +++ b/docs/sledge-states/states.dot @@ -2,11 +2,19 @@ digraph { Uninitialized -> Initialized Initialized -> {Runnable Error} Runnable -> Running_Sys - Running_User -> Running_Sys [label="interrupt"] + + Running_User -> Interrupted [label="interrupt"] + Running_User -> Running_Sys [label="syscall"] + + + Interrupted -> Preempted [label="preempt"] + Interrupted -> Running_User + + + Running_Sys -> Asleep [label="sleep"] Running_Sys -> {Error Returned} Running_Sys -> Running_User [label="return"] - Running_Sys -> Preempted [label="preempt"] Preempted -> Running_User Returned -> Complete [label="exit_success"] Asleep -> Runnable [label="wakeup"] diff --git a/docs/sledge-states/states.svg b/docs/sledge-states/states.svg index 7b036f6..2471742 100644 --- a/docs/sledge-states/states.svg +++ b/docs/sledge-states/states.svg @@ -4,154 +4,173 @@ - - + + %3 - + Uninitialized - -Uninitialized + +Uninitialized Initialized - -Initialized + +Initialized Uninitialized->Initialized - - + + Runnable - -Runnable + +Runnable Initialized->Runnable - - + + Error - -Error + +Error Initialized->Error - - + + Running_Sys - -Running_Sys + +Running_Sys Runnable->Running_Sys - - + + - + Running_Sys->Error - - + + Running_User - -Running_User + +Running_User - + Running_Sys->Running_User - - -return + + +return - + Asleep - -Asleep + +Asleep - + Running_Sys->Asleep - - -sleep + + +sleep - + Returned - -Returned + +Returned - + Running_Sys->Returned - - + + + + + +Running_User->Running_Sys + + +syscall + + + +Interrupted + +Interrupted + + + +Running_User->Interrupted + + +interrupt + + + +Interrupted->Running_User + + - + Preempted - -Preempted + +Preempted - - -Running_Sys->Preempted - - -preempt + + +Interrupted->Preempted + + +preempt - - -Running_User->Running_Sys - - -interrupt + + +Preempted->Running_User + + - + Asleep->Runnable - - -wakeup + + +wakeup - + Complete - -Complete + +Complete - + Returned->Complete - - -exit_success - - - -Preempted->Running_User - - + + +exit_success diff --git a/runtime/experiments/bimodal/edf_preemption.env b/runtime/experiments/bimodal/edf_preemption.env index 302a324..8305be6 100644 --- a/runtime/experiments/bimodal/edf_preemption.env +++ b/runtime/experiments/bimodal/edf_preemption.env @@ -1,3 +1,4 @@ SLEDGE_SCHEDULER=EDF SLEDGE_DISABLE_PREEMPTION=false SLEDGE_SIGALRM_HANDLER=TRIAGED +SLEDGE_NWORKERS=4 diff --git a/runtime/include/sandbox_functions.h b/runtime/include/sandbox_functions.h index 6bc0798..002893c 100644 --- a/runtime/include/sandbox_functions.h +++ b/runtime/include/sandbox_functions.h @@ -59,12 +59,6 @@ sandbox_get_priority(void *element) return sandbox->absolute_deadline; }; -static inline bool -sandbox_is_preemptable(struct sandbox *sandbox) -{ - return sandbox && sandbox->state == SANDBOX_RUNNING_USER; -}; - static inline void sandbox_open_http(struct sandbox *sandbox) { diff --git a/runtime/include/sandbox_perf_log.h b/runtime/include/sandbox_perf_log.h index 2463ba9..2bf3b51 100644 --- a/runtime/include/sandbox_perf_log.h +++ b/runtime/include/sandbox_perf_log.h @@ -36,15 +36,16 @@ sandbox_perf_log_print_entry(struct sandbox *sandbox) * becomes more intelligent, then peak linear memory size needs to be tracked * seperately from current linear memory size. */ - fprintf(sandbox_perf_log, "%lu,%s,%d,%s,%lu,%lu,%lu,%lu,%lu,%lu,%lu,%lu,%lu,%lu,%lu,%lu,%lu,%lu,%u,%u\n", + fprintf(sandbox_perf_log, "%lu,%s,%d,%s,%lu,%lu,%lu,,%lu,%lu,%lu,%lu,%lu,%lu,%lu,%lu,%lu,%lu,%lu,%lu,%u,%u\n", sandbox->id, sandbox->module->name, sandbox->module->port, sandbox_state_stringify(sandbox->state), sandbox->module->relative_deadline, sandbox->total_time, queued_duration, sandbox->duration_of_state[SANDBOX_UNINITIALIZED], sandbox->duration_of_state[SANDBOX_ALLOCATED], sandbox->duration_of_state[SANDBOX_INITIALIZED], sandbox->duration_of_state[SANDBOX_RUNNABLE], - sandbox->duration_of_state[SANDBOX_PREEMPTED], sandbox->duration_of_state[SANDBOX_RUNNING_SYS], - sandbox->duration_of_state[SANDBOX_RUNNING_USER], sandbox->duration_of_state[SANDBOX_ASLEEP], - sandbox->duration_of_state[SANDBOX_RETURNED], sandbox->duration_of_state[SANDBOX_COMPLETE], - sandbox->duration_of_state[SANDBOX_ERROR], runtime_processor_speed_MHz, sandbox->memory.size); + sandbox->duration_of_state[SANDBOX_INTERRUPTED], sandbox->duration_of_state[SANDBOX_PREEMPTED], + sandbox->duration_of_state[SANDBOX_RUNNING_SYS], sandbox->duration_of_state[SANDBOX_RUNNING_USER], + sandbox->duration_of_state[SANDBOX_ASLEEP], sandbox->duration_of_state[SANDBOX_RETURNED], + sandbox->duration_of_state[SANDBOX_COMPLETE], sandbox->duration_of_state[SANDBOX_ERROR], + runtime_processor_speed_MHz, sandbox->memory.size); } static inline void diff --git a/runtime/include/sandbox_set_as_interrupted.h b/runtime/include/sandbox_set_as_interrupted.h new file mode 100644 index 0000000..3ee1216 --- /dev/null +++ b/runtime/include/sandbox_set_as_interrupted.h @@ -0,0 +1,62 @@ +#pragma once + +#include +#include + +#include "arch/getcycles.h" +#include "current_sandbox.h" +#include "panic.h" +#include "sandbox_functions.h" +#include "sandbox_state_history.h" +#include "sandbox_types.h" + +static inline void +sandbox_set_as_interrupted(struct sandbox *sandbox, sandbox_state_t last_state) +{ + assert(sandbox); + + /* WARNING: All code before this assignment is preemptable */ + sandbox->state = SANDBOX_INTERRUPTED; + barrier(); + + uint64_t now = __getcycles(); + + /* State Change Bookkeeping */ + 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); +} + +static inline void +sandbox_interrupt(struct sandbox *sandbox) +{ + sandbox_set_as_interrupted(sandbox, sandbox->state); +} + + +/** + * @brief Transition sandbox back to interrupted state + * @param sandbox + * @param interrupted_state - state to return to + */ +static inline void +sandbox_interrupt_return(struct sandbox *sandbox, sandbox_state_t interrupted_state) +{ + assert(sandbox); + assert(interrupted_state != SANDBOX_INTERRUPTED); + + uint64_t now = __getcycles(); + + /* State Change Bookkeeping */ + 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); + + barrier(); + /* WARNING: Code after this assignment may be preemptable */ + sandbox->state = interrupted_state; +} diff --git a/runtime/include/sandbox_set_as_preempted.h b/runtime/include/sandbox_set_as_preempted.h index 4d999c8..8dec5cb 100644 --- a/runtime/include/sandbox_set_as_preempted.h +++ b/runtime/include/sandbox_set_as_preempted.h @@ -26,7 +26,7 @@ sandbox_set_as_preempted(struct sandbox *sandbox, sandbox_state_t last_state) uint64_t now = __getcycles(); switch (last_state) { - case SANDBOX_RUNNING_SYS: { + case SANDBOX_INTERRUPTED: { break; } default: { @@ -46,6 +46,6 @@ sandbox_set_as_preempted(struct sandbox *sandbox, sandbox_state_t last_state) static inline void sandbox_preempt(struct sandbox *sandbox) { - assert(sandbox->state == SANDBOX_RUNNING_SYS); - sandbox_set_as_preempted(sandbox, SANDBOX_RUNNING_SYS); + assert(sandbox->state == SANDBOX_INTERRUPTED); + sandbox_set_as_preempted(sandbox, SANDBOX_INTERRUPTED); } diff --git a/runtime/include/sandbox_set_as_running_sys.h b/runtime/include/sandbox_set_as_running_sys.h index fd87035..67d6159 100644 --- a/runtime/include/sandbox_set_as_running_sys.h +++ b/runtime/include/sandbox_set_as_running_sys.h @@ -29,12 +29,10 @@ sandbox_set_as_running_sys(struct sandbox *sandbox, sandbox_state_t last_state) } case SANDBOX_RUNNABLE: { assert(sandbox); - /* Does not handle context switch because the caller knows if we need to use fast or slow switched. We - * can fix this by breakout out SANDBOX_RUNNABLE and SANDBOX_PREEMPTED */ break; } default: { - panic("Sandbox %lu | Illegal transition from %s to Running Kernel\n", sandbox->id, + panic("Sandbox %lu | Illegal transition from %s to Running Sys\n", sandbox->id, sandbox_state_stringify(last_state)); } } @@ -48,7 +46,7 @@ sandbox_set_as_running_sys(struct sandbox *sandbox, sandbox_state_t last_state) } static inline void -sandbox_interrupt(struct sandbox *sandbox) +sandbox_syscall(struct sandbox *sandbox) { assert(sandbox->state == SANDBOX_RUNNING_USER); sandbox_set_as_running_sys(sandbox, SANDBOX_RUNNING_USER); diff --git a/runtime/include/sandbox_set_as_running_user.h b/runtime/include/sandbox_set_as_running_user.h index 4ee3372..d25ca42 100644 --- a/runtime/include/sandbox_set_as_running_user.h +++ b/runtime/include/sandbox_set_as_running_user.h @@ -24,7 +24,6 @@ sandbox_set_as_running_user(struct sandbox *sandbox, sandbox_state_t last_state) break; } case SANDBOX_PREEMPTED: { - assert(sandbox); break; } default: { diff --git a/runtime/include/sandbox_state.h b/runtime/include/sandbox_state.h index 2dcd693..f107cf7 100644 --- a/runtime/include/sandbox_state.h +++ b/runtime/include/sandbox_state.h @@ -15,6 +15,7 @@ typedef enum SANDBOX_PREEMPTED, SANDBOX_RUNNING_SYS, SANDBOX_RUNNING_USER, + SANDBOX_INTERRUPTED, SANDBOX_ASLEEP, SANDBOX_RETURNED, SANDBOX_COMPLETE, diff --git a/runtime/include/scheduler.h b/runtime/include/scheduler.h index da46f69..19f05f9 100644 --- a/runtime/include/scheduler.h +++ b/runtime/include/scheduler.h @@ -19,6 +19,7 @@ #include "sandbox_set_as_preempted.h" #include "sandbox_set_as_runnable.h" #include "sandbox_set_as_running_sys.h" +#include "sandbox_set_as_interrupted.h" #include "sandbox_set_as_running_user.h" #include "scheduler_execute_epoll_loop.h" @@ -214,9 +215,7 @@ scheduler_preemptive_sched(ucontext_t *interrupted_context) struct sandbox *current = current_sandbox_get(); assert(current != NULL); - assert(current->state == SANDBOX_RUNNING_USER); - - sandbox_interrupt(current); + assert(current->state == SANDBOX_INTERRUPTED); struct sandbox *next = scheduler_get_next(); /* Assumption: the current sandbox is on the runqueue, so the scheduler should always return something */ @@ -224,7 +223,7 @@ scheduler_preemptive_sched(ucontext_t *interrupted_context) /* If current equals next, no switch is necessary, so resume execution */ if (current == next) { - sandbox_return(current); + sandbox_interrupt_return(current, SANDBOX_RUNNING_USER); return; } diff --git a/runtime/src/current_sandbox.c b/runtime/src/current_sandbox.c index aa4ca29..1f4d695 100644 --- a/runtime/src/current_sandbox.c +++ b/runtime/src/current_sandbox.c @@ -35,7 +35,7 @@ current_sandbox_sleep() { struct sandbox *sandbox = current_sandbox_get(); current_sandbox_set(NULL); - + assert(sandbox != NULL); struct arch_context *current_context = &sandbox->ctxt; @@ -139,7 +139,7 @@ current_sandbox_fini() assert(sandbox != NULL); char *error_message = ""; - sandbox_interrupt(sandbox); + sandbox_syscall(sandbox); sandbox->timestamp_of.completion = __getcycles(); diff --git a/runtime/src/sandbox_state.c b/runtime/src/sandbox_state.c index 3af06cf..2c9d160 100644 --- a/runtime/src/sandbox_state.c +++ b/runtime/src/sandbox_state.c @@ -13,6 +13,7 @@ const char *sandbox_state_labels[SANDBOX_STATE_COUNT] = { [SANDBOX_ALLOCATED] = "Allocated", [SANDBOX_INITIALIZED] = "Initialized", [SANDBOX_RUNNABLE] = "Runnable", + [SANDBOX_INTERRUPTED] = "Interrupted", [SANDBOX_PREEMPTED] = "Preempted", [SANDBOX_RUNNING_SYS] = "Running Sys", [SANDBOX_RUNNING_USER] = "Running User", diff --git a/runtime/src/software_interrupt.c b/runtime/src/software_interrupt.c index a04428a..27d47a9 100644 --- a/runtime/src/software_interrupt.c +++ b/runtime/src/software_interrupt.c @@ -19,6 +19,7 @@ #include "panic.h" #include "runtime.h" #include "sandbox_set_as_running_user.h" +#include "sandbox_set_as_interrupted.h" #include "sandbox_types.h" #include "scheduler.h" #include "software_interrupt.h" @@ -43,12 +44,6 @@ worker_thread_is_running_cooperative_scheduler(void) return current_sandbox_get() == NULL; } -static inline void -defer_sigalrm() -{ - atomic_fetch_add(&deferred_sigalrm, 1); -} - /** * A POSIX signal is delivered to only one thread. * This function broadcasts the sigalarm signal to all other worker threads @@ -112,14 +107,21 @@ software_interrupt_handle_signals(int signal_type, siginfo_t *signal_info, void switch (signal_type) { case SIGALRM: { - propagate_sigalrm(signal_info); + if (worker_thread_is_running_cooperative_scheduler()) { + propagate_sigalrm(signal_info); + break; + } - if (worker_thread_is_running_cooperative_scheduler()) break; + sandbox_state_t interrupted_state = current_sandbox->state; + sandbox_interrupt(current_sandbox); + propagate_sigalrm(signal_info); - if (sandbox_is_preemptable(current_sandbox)) { + if (interrupted_state == SANDBOX_RUNNING_USER) { + /* Preemptable, so run scheduler. The scheduler handles outgoing state changes */ scheduler_preemptive_sched(interrupted_context); } else { - defer_sigalrm(); + atomic_fetch_add(&deferred_sigalrm, 1); + sandbox_interrupt_return(current_sandbox, interrupted_state); } break; From 74a715febb7b7c4b6e0d2923a2acf0fcc24d05f5 Mon Sep 17 00:00:00 2001 From: Sean McBride Date: Mon, 22 Nov 2021 15:53:49 -0500 Subject: [PATCH 07/17] fix: Update state assertion in FIFO round robin --- runtime/src/local_runqueue_list.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/runtime/src/local_runqueue_list.c b/runtime/src/local_runqueue_list.c index 1e1fafa..c727e8c 100644 --- a/runtime/src/local_runqueue_list.c +++ b/runtime/src/local_runqueue_list.c @@ -60,8 +60,7 @@ local_runqueue_list_rotate() if (ps_list_head_one_node(&local_runqueue_list)) return; struct sandbox *sandbox_at_head = local_runqueue_list_remove_and_return(); - assert(sandbox_at_head->state == SANDBOX_RUNNING_SYS || sandbox_at_head->state == SANDBOX_RUNNABLE - || sandbox_at_head->state == SANDBOX_PREEMPTED); + assert(sandbox_at_head->state == SANDBOX_INTERRUPTED); local_runqueue_list_append(sandbox_at_head); } From 45e44d51de890ef34ea708430630472b80429fc0 Mon Sep 17 00:00:00 2001 From: Sean McBride Date: Mon, 22 Nov 2021 18:31:17 -0500 Subject: [PATCH 08/17] feat: enable direct fast switch between sandboxes --- .../experiments/bimodal/edf_preemption.env | 1 - runtime/include/arch/x86_64/context.h | 10 +- runtime/include/sandbox_set_as_complete.h | 3 +- runtime/include/sandbox_set_as_error.h | 3 +- runtime/include/scheduler.h | 114 +++++++++++++----- runtime/src/current_sandbox.c | 39 ++---- runtime/src/software_interrupt.c | 1 - runtime/src/worker_thread.c | 5 +- 8 files changed, 101 insertions(+), 75 deletions(-) diff --git a/runtime/experiments/bimodal/edf_preemption.env b/runtime/experiments/bimodal/edf_preemption.env index 8305be6..302a324 100644 --- a/runtime/experiments/bimodal/edf_preemption.env +++ b/runtime/experiments/bimodal/edf_preemption.env @@ -1,4 +1,3 @@ SLEDGE_SCHEDULER=EDF SLEDGE_DISABLE_PREEMPTION=false SLEDGE_SIGALRM_HANDLER=TRIAGED -SLEDGE_NWORKERS=4 diff --git a/runtime/include/arch/x86_64/context.h b/runtime/include/arch/x86_64/context.h index 657e8e1..5c261f7 100644 --- a/runtime/include/arch/x86_64/context.h +++ b/runtime/include/arch/x86_64/context.h @@ -74,22 +74,16 @@ arch_context_restore_fast(mcontext_t *active_context, struct arch_context *sandb * @param b - the registers and context of what we're switching to * @return always returns 0, indicating success * - * NULL in either of these values indicates the "no sandbox to execute" state, - * which defaults to resuming execution of main */ static inline int arch_context_switch(struct arch_context *a, struct arch_context *b) { - /* if both a and b are NULL, there is no state change */ - assert(a != NULL || b != NULL); + assert(a != NULL); + assert(b != NULL); /* Assumption: The caller does not switch to itself */ assert(a != b); - /* Set any NULLs to worker_thread_base_context to resume execution of main */ - if (a == NULL) a = &worker_thread_base_context; - if (b == NULL) b = &worker_thread_base_context; - /* A Transition {Running} -> Fast */ assert(a->variant == ARCH_CONTEXT_VARIANT_RUNNING); diff --git a/runtime/include/sandbox_set_as_complete.h b/runtime/include/sandbox_set_as_complete.h index c797fe6..d7d0937 100644 --- a/runtime/include/sandbox_set_as_complete.h +++ b/runtime/include/sandbox_set_as_complete.h @@ -54,8 +54,7 @@ sandbox_set_as_complete(struct sandbox *sandbox, sandbox_state_t last_state) sandbox_perf_log_print_entry(sandbox); sandbox_summarize_page_allocations(sandbox); - /* Do not touch sandbox state after adding to completion queue to avoid use-after-free bugs */ - local_completion_queue_add(sandbox); + /* Does not add to completion queue until in cooperative scheduler */ } static inline void diff --git a/runtime/include/sandbox_set_as_error.h b/runtime/include/sandbox_set_as_error.h index 22dbdd7..c3e8c7a 100644 --- a/runtime/include/sandbox_set_as_error.h +++ b/runtime/include/sandbox_set_as_error.h @@ -61,8 +61,7 @@ sandbox_set_as_error(struct sandbox *sandbox, sandbox_state_t last_state) sandbox_perf_log_print_entry(sandbox); sandbox_summarize_page_allocations(sandbox); - /* Do not touch sandbox after adding to completion queue to avoid use-after-free bugs */ - local_completion_queue_add(sandbox); + /* Does not add to completion queue until in cooperative scheduler */ } static inline void diff --git a/runtime/include/scheduler.h b/runtime/include/scheduler.h index 19f05f9..c8cdee3 100644 --- a/runtime/include/scheduler.h +++ b/runtime/include/scheduler.h @@ -213,28 +213,28 @@ scheduler_preemptive_sched(ucontext_t *interrupted_context) /* Process epoll to make sure that all runnable jobs are considered for execution */ scheduler_execute_epoll_loop(); - struct sandbox *current = current_sandbox_get(); - assert(current != NULL); - assert(current->state == SANDBOX_INTERRUPTED); + struct sandbox *interrupted_sandbox = current_sandbox_get(); + assert(interrupted_sandbox != NULL); + assert(interrupted_sandbox->state == SANDBOX_INTERRUPTED); struct sandbox *next = scheduler_get_next(); /* Assumption: the current sandbox is on the runqueue, so the scheduler should always return something */ assert(next != NULL); /* If current equals next, no switch is necessary, so resume execution */ - if (current == next) { - sandbox_interrupt_return(current, SANDBOX_RUNNING_USER); + if (interrupted_sandbox == next) { + sandbox_interrupt_return(interrupted_sandbox, SANDBOX_RUNNING_USER); return; } #ifdef LOG_PREEMPTION - debuglog("Preempting sandbox %lu to run sandbox %lu\n", current->id, next->id); + debuglog("Preempting sandbox %lu to run sandbox %lu\n", interrupted_sandbox->id, next->id); #endif /* Preempt executing sandbox */ - scheduler_log_sandbox_switch(current, next); - sandbox_preempt(current); - arch_context_save_slow(¤t->ctxt, &interrupted_context->uc_mcontext); + scheduler_log_sandbox_switch(interrupted_sandbox, next); + sandbox_preempt(interrupted_sandbox); + arch_context_save_slow(&interrupted_sandbox->ctxt, &interrupted_context->uc_mcontext); scheduler_preemptive_switch_to(interrupted_context, next); } @@ -244,14 +244,12 @@ scheduler_preemptive_sched(ucontext_t *interrupted_context) * @param next_sandbox The Sandbox to switch to */ static inline void -scheduler_cooperative_switch_to(struct sandbox *next_sandbox) +scheduler_cooperative_switch_to(struct arch_context *current_context, struct sandbox *next_sandbox) { assert(current_sandbox_get() == NULL); struct arch_context *next_context = &next_sandbox->ctxt; - scheduler_log_sandbox_switch(NULL, next_sandbox); - /* Switch to next sandbox */ switch (next_sandbox->state) { case SANDBOX_RUNNABLE: { @@ -271,17 +269,64 @@ scheduler_cooperative_switch_to(struct sandbox *next_sandbox) sandbox_state_stringify(next_sandbox->state)); } } + arch_context_switch(current_context, next_context); +} - arch_context_switch(&worker_thread_base_context, next_context); +static inline void +scheduler_switch_to_base_context(struct arch_context *current_context) +{ + /* Assumption: Base Worker context should never be preempted */ + assert(worker_thread_base_context.variant == ARCH_CONTEXT_VARIANT_FAST); + arch_context_switch(current_context, &worker_thread_base_context); } -/* A sandbox cannot execute the scheduler directly. It must yield to the base context, and then the context calls this - * within its idle loop + +/* The idle_loop is executed by the base_context. This should not be called directly */ +static inline void +scheduler_idle_loop() +{ + while (true) { + /* Assumption: only called by the "base context" */ + assert(current_sandbox_get() == NULL); + + /* Deferred signals should have been cleared by this point */ + assert(deferred_sigalrm == 0); + + /* Try to wakeup sleeping sandboxes */ + scheduler_execute_epoll_loop(); + + /* Switch to a sandbox if one is ready to run */ + struct sandbox *next_sandbox = scheduler_get_next(); + if (next_sandbox != NULL) { + scheduler_cooperative_switch_to(&worker_thread_base_context, next_sandbox); + } + + /* Clear the completion queue */ + local_completion_queue_free(); + } +} + +/** + * @brief Used to cooperative switch sandboxes when a sandbox sleeps or exits + * Because of use-after-free bugs that interfere with our loggers, when a sandbox exits and switches away never to + * return, the boolean add_to_completion_queue needs to be set to true. Otherwise, we will leak sandboxes. + * @param add_to_completion_queue - Indicates that the sandbox should be added to the completion queue before switching + * away */ static inline void -scheduler_cooperative_sched() +scheduler_cooperative_sched(bool add_to_completion_queue) { - /* Assumption: only called by the "base context" */ + struct sandbox *exiting_sandbox = current_sandbox_get(); + assert(exiting_sandbox != NULL); + + /* Clearing current sandbox indicates we are entering the cooperative scheduler */ + current_sandbox_set(NULL); + barrier(); + software_interrupt_deferred_sigalrm_clear(); + + struct arch_context *exiting_context = &exiting_sandbox->ctxt; + + /* Assumption: Called by an exiting or sleeping sandbox */ assert(current_sandbox_get() == NULL); /* Deferred signals should have been cleared by this point */ @@ -290,12 +335,29 @@ scheduler_cooperative_sched() /* Try to wakeup sleeping sandboxes */ scheduler_execute_epoll_loop(); + /* We have not added ourself to the completion queue, so we can free */ + local_completion_queue_free(); + /* Switch to a sandbox if one is ready to run */ struct sandbox *next_sandbox = scheduler_get_next(); - if (next_sandbox != NULL) scheduler_cooperative_switch_to(next_sandbox); - /* Clear the completion queue */ - local_completion_queue_free(); + /* If our sandbox slept and immediately woke up, we can just return */ + if (next_sandbox == exiting_sandbox) { + sandbox_set_as_running_sys(next_sandbox, SANDBOX_RUNNABLE); + current_sandbox_set(next_sandbox); + return; + } + + scheduler_log_sandbox_switch(exiting_sandbox, next_sandbox); + + if (add_to_completion_queue) local_completion_queue_add(exiting_sandbox); + /* Do not touch sandbox struct after this point! */ + + if (next_sandbox != NULL) { + scheduler_cooperative_switch_to(exiting_context, next_sandbox); + } else { + scheduler_switch_to_base_context(exiting_context); + } } @@ -307,15 +369,3 @@ scheduler_worker_would_preempt(int worker_idx) uint64_t global_deadline = global_request_scheduler_peek(); return global_deadline < local_deadline; } - -static inline void -scheduler_switch_to_base_context(struct arch_context *current_context) -{ - /* Clear any deferred sigalrms we hit while cleaning up sandbox. We'll run the scheduler cooperatively - in the base context */ - software_interrupt_deferred_sigalrm_clear(); - - /* Assumption: Base Worker context should never be preempted */ - assert(worker_thread_base_context.variant == ARCH_CONTEXT_VARIANT_FAST); - arch_context_switch(current_context, &worker_thread_base_context); -} diff --git a/runtime/src/current_sandbox.c b/runtime/src/current_sandbox.c index 1f4d695..b6192cc 100644 --- a/runtime/src/current_sandbox.c +++ b/runtime/src/current_sandbox.c @@ -33,28 +33,22 @@ thread_local struct sandbox_context_cache local_sandbox_context_cache = { void current_sandbox_sleep() { - struct sandbox *sandbox = current_sandbox_get(); - current_sandbox_set(NULL); - - assert(sandbox != NULL); - struct arch_context *current_context = &sandbox->ctxt; + struct sandbox *sleeping_sandbox = current_sandbox_get(); + assert(sleeping_sandbox != NULL); - scheduler_log_sandbox_switch(sandbox, NULL); generic_thread_dump_lock_overhead(); - assert(sandbox != NULL); - - switch (sandbox->state) { + switch (sleeping_sandbox->state) { case SANDBOX_RUNNING_SYS: { - sandbox_sleep(sandbox); + sandbox_sleep(sleeping_sandbox); break; } default: panic("Cooperatively switching from a sandbox in a non-terminal %s state\n", - sandbox_state_stringify(sandbox->state)); + sandbox_state_stringify(sleeping_sandbox->state)); } - scheduler_switch_to_base_context(current_context); + scheduler_cooperative_sched(false); } /** @@ -65,31 +59,24 @@ current_sandbox_sleep() void current_sandbox_exit() { - struct sandbox *sandbox = current_sandbox_get(); - current_sandbox_set(NULL); - - assert(sandbox != NULL); - struct arch_context *current_context = &sandbox->ctxt; + struct sandbox *exiting_sandbox = current_sandbox_get(); + assert(exiting_sandbox != NULL); - scheduler_log_sandbox_switch(sandbox, NULL); generic_thread_dump_lock_overhead(); - assert(sandbox != NULL); - - switch (sandbox->state) { + switch (exiting_sandbox->state) { case SANDBOX_RETURNED: - sandbox_exit_success(sandbox); + sandbox_exit_success(exiting_sandbox); break; case SANDBOX_RUNNING_SYS: - sandbox_exit_error(sandbox); + sandbox_exit_error(exiting_sandbox); break; default: panic("Cooperatively switching from a sandbox in a non-terminal %s state\n", - sandbox_state_stringify(sandbox->state)); + sandbox_state_stringify(exiting_sandbox->state)); } - /* Do not access sandbox after this, as it is on the completion queue! */ - scheduler_switch_to_base_context(current_context); + scheduler_cooperative_sched(true); /* The scheduler should never switch back to completed sandboxes */ assert(0); diff --git a/runtime/src/software_interrupt.c b/runtime/src/software_interrupt.c index 27d47a9..b2c13a1 100644 --- a/runtime/src/software_interrupt.c +++ b/runtime/src/software_interrupt.c @@ -133,7 +133,6 @@ software_interrupt_handle_signals(int signal_type, siginfo_t *signal_info, void software_interrupt_counts_sigusr_increment(); #ifdef LOG_PREEMPTION - debuglog("Total SIGUSR1 Received: %d\n", sigusr_count); debuglog("Restoring sandbox: %lu, Stack %llu\n", current_sandbox->id, current_sandbox->ctxt.mctx.gregs[REG_RSP]); #endif diff --git a/runtime/src/worker_thread.c b/runtime/src/worker_thread.c index 82f86a7..008a4b2 100644 --- a/runtime/src/worker_thread.c +++ b/runtime/src/worker_thread.c @@ -66,8 +66,7 @@ worker_thread_main(void *argument) software_interrupt_unmask_signal(SIGUSR1); } - /* Idle Loop */ - while (true) scheduler_cooperative_sched(); + scheduler_idle_loop(); - panic("Worker Thread unexpectedly completed run loop."); + panic("Worker Thread unexpectedly completed idle loop."); } From c845bcce8e4b8135e2df6bf92803cca67d98ca30 Mon Sep 17 00:00:00 2001 From: Sean McBride Date: Mon, 22 Nov 2021 18:35:25 -0500 Subject: [PATCH 09/17] fix: Correct nit when NDEBUG is set --- runtime/src/current_sandbox.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/runtime/src/current_sandbox.c b/runtime/src/current_sandbox.c index b6192cc..762e4c6 100644 --- a/runtime/src/current_sandbox.c +++ b/runtime/src/current_sandbox.c @@ -117,6 +117,8 @@ err: generic_thread_dump_lock_overhead(); current_sandbox_exit(); assert(0); + + return NULL; } static inline void From 886bdbb97085a394f21cad6748fbb2b54fd1f520 Mon Sep 17 00:00:00 2001 From: Sean McBride Date: Tue, 23 Nov 2021 10:30:52 -0500 Subject: [PATCH 10/17] fix: Add interrupted perf log header --- runtime/include/sandbox_perf_log.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/include/sandbox_perf_log.h b/runtime/include/sandbox_perf_log.h index 2bf3b51..11b729c 100644 --- a/runtime/include/sandbox_perf_log.h +++ b/runtime/include/sandbox_perf_log.h @@ -14,7 +14,7 @@ sandbox_perf_log_print_header() { if (sandbox_perf_log == NULL) { perror("sandbox perf log"); } fprintf(sandbox_perf_log, - "id,module,port,state,deadline,actual,queued,uninitialized,allocated,initialized,runnable,preempted," + "id,module,port,state,deadline,actual,queued,uninitialized,allocated,initialized,runnable,interrupted,preempted," "running_sys,running_user,asleep,returned,complete,error,proc_MHz,memory\n"); } From 84820219e0ffa049ca08a07ffaad391f330b6541 Mon Sep 17 00:00:00 2001 From: Sean McBride Date: Tue, 23 Nov 2021 11:25:50 -0500 Subject: [PATCH 11/17] format: clang-format --- runtime/include/arch/aarch64/context.h | 4 ++-- runtime/include/sandbox_perf_log.h | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/runtime/include/arch/aarch64/context.h b/runtime/include/arch/aarch64/context.h index f58e22c..88950c8 100644 --- a/runtime/include/arch/aarch64/context.h +++ b/runtime/include/arch/aarch64/context.h @@ -100,8 +100,8 @@ arch_context_switch(struct arch_context *a, struct arch_context *b) ".align 8\n\t" "exit%=:\n\t" : - : [a] "r"(a_registers), [b] "r"(b_registers), [av] "r"(&a->variant), [bv] "r"(&b->variant), - [slowpath] "r"(&arch_context_restore_preempted) + : [ a ] "r"(a_registers), [ b ] "r"(b_registers), [ av ] "r"(&a->variant), + [ bv ] "r"(&b->variant), [ slowpath ] "r"(&arch_context_restore_preempted) : "memory", "cc", "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23", "x24", "d8", "d9", "d10", "d11", "d12", "d13", "d14", "d15"); diff --git a/runtime/include/sandbox_perf_log.h b/runtime/include/sandbox_perf_log.h index 11b729c..aa9f640 100644 --- a/runtime/include/sandbox_perf_log.h +++ b/runtime/include/sandbox_perf_log.h @@ -13,9 +13,9 @@ static inline void sandbox_perf_log_print_header() { if (sandbox_perf_log == NULL) { perror("sandbox perf log"); } - fprintf(sandbox_perf_log, - "id,module,port,state,deadline,actual,queued,uninitialized,allocated,initialized,runnable,interrupted,preempted," - "running_sys,running_user,asleep,returned,complete,error,proc_MHz,memory\n"); + fprintf(sandbox_perf_log, "id,module,port,state,deadline,actual,queued,uninitialized,allocated,initialized," + "runnable,interrupted,preempted," + "running_sys,running_user,asleep,returned,complete,error,proc_MHz,memory\n"); } /** From 522809de8d5032b788c7ebab5022f967d0c2e5b2 Mon Sep 17 00:00:00 2001 From: Sean McBride Date: Tue, 23 Nov 2021 11:38:29 -0500 Subject: [PATCH 12/17] chore: pin clang-format 11 --- format.sh | 2 +- runtime/include/arch/aarch64/context.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/format.sh b/format.sh index 197abd7..6585bd1 100755 --- a/format.sh +++ b/format.sh @@ -11,7 +11,7 @@ validate() { declare -i major=0 declare -i minor=0 declare -i patch=0 - declare -i required_major=9 + declare -i required_major=11 declare -i required_minor=0 declare -i required_patch=0 diff --git a/runtime/include/arch/aarch64/context.h b/runtime/include/arch/aarch64/context.h index 88950c8..f58e22c 100644 --- a/runtime/include/arch/aarch64/context.h +++ b/runtime/include/arch/aarch64/context.h @@ -100,8 +100,8 @@ arch_context_switch(struct arch_context *a, struct arch_context *b) ".align 8\n\t" "exit%=:\n\t" : - : [ a ] "r"(a_registers), [ b ] "r"(b_registers), [ av ] "r"(&a->variant), - [ bv ] "r"(&b->variant), [ slowpath ] "r"(&arch_context_restore_preempted) + : [a] "r"(a_registers), [b] "r"(b_registers), [av] "r"(&a->variant), [bv] "r"(&b->variant), + [slowpath] "r"(&arch_context_restore_preempted) : "memory", "cc", "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23", "x24", "d8", "d9", "d10", "d11", "d12", "d13", "d14", "d15"); From 3982c2db5a2e36d6dc194db70b0175e8a9e0b5a0 Mon Sep 17 00:00:00 2001 From: Sean McBride Date: Mon, 29 Nov 2021 09:23:19 -0500 Subject: [PATCH 13/17] build: Revert sledgert command --- runtime/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/Makefile b/runtime/Makefile index c0c203b..0092f2f 100644 --- a/runtime/Makefile +++ b/runtime/Makefile @@ -119,7 +119,7 @@ all: runtime bin/sledgert: ${CFILES} @echo "Compiling runtime" @mkdir -p bin/ - ${CC} ${CFLAGS} ${LDFLAGS} ${JSMNCFLAGS} ${INCLUDES} -L/usr/lib/ $^ -o bin/sledgert + ${CC} ${INCLUDES} ${CFLAGS} ${LDFLAGS} ${JSMNCFLAGS} -L/usr/lib/ $^ -o bin/sledgert .PHONY: runtime runtime: thirdparty bin/sledgert From 098829113b4e9a82f41603cb8545ce3a1fa6515c Mon Sep 17 00:00:00 2001 From: Sean McBride Date: Mon, 29 Nov 2021 09:58:24 -0500 Subject: [PATCH 14/17] docs: Better explain memory barriers --- runtime/include/runtime.h | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/runtime/include/runtime.h b/runtime/include/runtime.h index c1db761..6f32766 100644 --- a/runtime/include/runtime.h +++ b/runtime/include/runtime.h @@ -8,7 +8,17 @@ #include "likely.h" #include "types.h" -/* Memory barrier to prevent unexpected code migration due to compiler optimization */ +/** + * Optimizing compilers and modern CPUs reorder instructions however it sees fit. This means that the resulting + * execution order may differ from the order of our source code. If there is a variable protecting a critical section, + * this means that code may move out of or into the critical section, which could cause bugs. In order to protect + * against this, we need to improve an ordering contraint via a "memory barrier." Inline assembly acts as a such barrier + * that no assembly instructions can be reordered across. An example of how this is used in this code base is in ensure + * that code is either intentionally preemptable or non-preemptable. + * + * Wikipedia: https://en.wikipedia.org/wiki/Memory_barrier + * Linux: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/memory-barriers.txt + */ #define barrier() __asm__ __volatile__("" ::: "memory") #define RUNTIME_EXPECTED_EXECUTION_US_MAX 3600000000 From 9b2f1905a526118d194d51895b9119c7d01d14f2 Mon Sep 17 00:00:00 2001 From: Sean McBride Date: Mon, 29 Nov 2021 10:17:51 -0500 Subject: [PATCH 15/17] docs: high level intro to scheduler --- runtime/include/scheduler.h | 39 +++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/runtime/include/scheduler.h b/runtime/include/scheduler.h index c8cdee3..9ce82f4 100644 --- a/runtime/include/scheduler.h +++ b/runtime/include/scheduler.h @@ -23,6 +23,45 @@ #include "sandbox_set_as_running_user.h" #include "scheduler_execute_epoll_loop.h" + +/** + * This scheduler provides for cooperative and preemptive multitasking in a OS process's userspace. + * + * When executing cooperatively, the scheduler is directly invoked via `scheduler_cooperative_sched`. It runs a single + * time in the existing context in order to try to execute a direct sandbox-to-sandbox switch. When no sandboxes are + * available to execute, the scheduler executes a context switch to `worker_thread_base_context`, which calls + * `scheduler_cooperative_sched` in an infinite idle loop. If the scheduler needs to restore a sandbox that was + * previously preempted, it raises a SIGUSR1 signal to enter the scheduler handler to be able to restore the full + * mcontext structure saved during the last preemption. Otherwise, the cooperative scheduler triggers a "fast switch", + * which only updates the instruction and stack pointer. + * + * Preemptive scheduler is provided by POSIX timers using a set interval defining a scheduling quantum. Our signal + * handler is configured to mask nested signals. Given that POSIX specifies that the kernel only delivers a SIGALRM to a + * single thread, the lucky thread that receives the kernel thread has the responsibility of propagating this signal + * onto all other worker threads. This must occur even when a worker thread is running a sandbox in a nonpreemptable + * state. + * + * When a SIGALRM fires, a worker can be in one of four states: + * + * 1) "Running a signal handler" - We mask signals when we are executing a signal handler, which results in signals + * being ignored. A kernel signal should get delivered to another unmasked worker, so propagation still occurs. + * + * 2) "Running the Cooperative Scheduler" - This is signified by the thread local current_sandbox being set to NULL. We + * propagate the signal and return immediately because we know we're already in the scheduler. We have no sandboxes to + * interrupt, so no sandbox state transitions occur. + * + * 3) "Running a Sandbox in a state other than SANDBOX_RUNNING_USER" - We call sandbox_interrupt on current_sandbox, + * propagate the sigalrms to the other workers, defer the sigalrm locally, and then return. The SANDBOX_INTERRUPTED + * timekeeping data is increased to account for the time needed to propagate the sigalrms. + * + * 4) "Running a Sandbox in the SANDBOX_RUNNING_USER state - We call sandbox_interrupt on current_sandbox, propagate + * the sigalrms to the other workers, and then actually enter the scheduler via scheduler_preemptive_sched. The + * interrupted sandbox may either be preempted or return to depending on the scheduler. If preempted, the interrupted + * mcontext is saved to the sandbox structure. The SANDBOX_INTERRUPTED timekeeping data is increased to account for the + * time needed to propagate the sigalrms, run epoll, query the scheduler data structure, and (potentially) allocate and + * initialize a sandbox. + */ + enum SCHEDULER { SCHEDULER_FIFO = 0, From 9b7cefccd796d1ebc5e6113067866acd4c6acc0c Mon Sep 17 00:00:00 2001 From: Sean McBride Date: Tue, 30 Nov 2021 13:53:01 -0500 Subject: [PATCH 16/17] refactor: Cleanup sigalrm handler --- runtime/src/software_interrupt.c | 34 +++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/runtime/src/software_interrupt.c b/runtime/src/software_interrupt.c index b2c13a1..beb9708 100644 --- a/runtime/src/software_interrupt.c +++ b/runtime/src/software_interrupt.c @@ -38,12 +38,6 @@ extern pthread_t *runtime_worker_threads; * Private Static Inlines * *************************/ -static inline bool -worker_thread_is_running_cooperative_scheduler(void) -{ - return current_sandbox_get() == NULL; -} - /** * A POSIX signal is delivered to only one thread. * This function broadcasts the sigalarm signal to all other worker threads @@ -81,6 +75,19 @@ propagate_sigalrm(siginfo_t *signal_info) } } +static inline bool +running_cooperative_scheduler(void) +{ + return current_sandbox_get() == NULL; +} + + +static inline bool +preemptable(sandbox_state_t state) +{ + return state == SANDBOX_RUNNING_USER; +} + /** * The handler function for Software Interrupts (signals) * SIGALRM is executed periodically by an interval timer, causing preemption of the current sandbox @@ -107,23 +114,28 @@ software_interrupt_handle_signals(int signal_type, siginfo_t *signal_info, void switch (signal_type) { case SIGALRM: { - if (worker_thread_is_running_cooperative_scheduler()) { + /* There is no benefit to deferring SIGALRMs that occur when we are already in the cooperative + * scheduler, so just propagate and return */ + if (running_cooperative_scheduler()) { propagate_sigalrm(signal_info); break; } + /* We transition the sandbox to an interrupted state to exclude time propagating signals and running the + * scheduler from per-sandbox accounting */ sandbox_state_t interrupted_state = current_sandbox->state; sandbox_interrupt(current_sandbox); propagate_sigalrm(signal_info); - if (interrupted_state == SANDBOX_RUNNING_USER) { - /* Preemptable, so run scheduler. The scheduler handles outgoing state changes */ - scheduler_preemptive_sched(interrupted_context); - } else { + /* If our interrupted sandbox is not preempatable, defer the sigalarm and return */ + if (!preemptable(interrupted_state)) { atomic_fetch_add(&deferred_sigalrm, 1); sandbox_interrupt_return(current_sandbox, interrupted_state); + break; } + /* Preemptable, so run scheduler. The scheduler handles outgoing state changes */ + scheduler_preemptive_sched(interrupted_context); break; } case SIGUSR1: { From 8d563a5f53bb926353b40aacc7e3ce4107f43fb5 Mon Sep 17 00:00:00 2001 From: Sean McBride Date: Tue, 30 Nov 2021 14:45:01 -0500 Subject: [PATCH 17/17] refactor: Further sigalrm changes --- runtime/src/software_interrupt.c | 38 +++++++++++++++----------------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/runtime/src/software_interrupt.c b/runtime/src/software_interrupt.c index beb9708..19ac897 100644 --- a/runtime/src/software_interrupt.c +++ b/runtime/src/software_interrupt.c @@ -76,16 +76,17 @@ propagate_sigalrm(siginfo_t *signal_info) } static inline bool -running_cooperative_scheduler(void) +worker_thread_is_running_cooperative_scheduler(void) { return current_sandbox_get() == NULL; } static inline bool -preemptable(sandbox_state_t state) +current_sandbox_is_preemptable() { - return state == SANDBOX_RUNNING_USER; + struct sandbox *sandbox = current_sandbox_get(); + return sandbox != NULL && sandbox->state == SANDBOX_RUNNING_USER; } /** @@ -114,28 +115,25 @@ software_interrupt_handle_signals(int signal_type, siginfo_t *signal_info, void switch (signal_type) { case SIGALRM: { - /* There is no benefit to deferring SIGALRMs that occur when we are already in the cooperative - * scheduler, so just propagate and return */ - if (running_cooperative_scheduler()) { + if (worker_thread_is_running_cooperative_scheduler()) { + /* There is no benefit to deferring SIGALRMs that occur when we are already in the cooperative + * scheduler, so just propagate and return */ + propagate_sigalrm(signal_info); + } else if (current_sandbox_is_preemptable()) { + /* Preemptable, so run scheduler. The scheduler handles outgoing state changes */ + sandbox_interrupt(current_sandbox); + propagate_sigalrm(signal_info); + scheduler_preemptive_sched(interrupted_context); + } else { + /* We transition the sandbox to an interrupted state to exclude time propagating signals and + * running the scheduler from per-sandbox accounting */ + sandbox_state_t interrupted_state = current_sandbox->state; + sandbox_interrupt(current_sandbox); propagate_sigalrm(signal_info); - break; - } - - /* We transition the sandbox to an interrupted state to exclude time propagating signals and running the - * scheduler from per-sandbox accounting */ - sandbox_state_t interrupted_state = current_sandbox->state; - sandbox_interrupt(current_sandbox); - propagate_sigalrm(signal_info); - - /* If our interrupted sandbox is not preempatable, defer the sigalarm and return */ - if (!preemptable(interrupted_state)) { atomic_fetch_add(&deferred_sigalrm, 1); sandbox_interrupt_return(current_sandbox, interrupted_state); - break; } - /* Preemptable, so run scheduler. The scheduler handles outgoing state changes */ - scheduler_preemptive_sched(interrupted_context); break; } case SIGUSR1: {