From 4d764cb005283dd034b40c56c48d3e97eb8fa497 Mon Sep 17 00:00:00 2001 From: Dmitrii Kuvaiskii Date: Thu, 11 Jun 2020 19:00:45 +0000 Subject: [PATCH] [Pal/Linux-SGX] Exitless: let untrusted RPC threads sleep Previously, the Exitless feature forced all untrusted RPC threads to spin forever while waiting for new syscall requests from enclave threads. This led to constant high CPU utilization even on idle workloads. This commit adds simple logic for RPC threads to first spin for a while and then perform short nanosleeps. This heuristic significantly reduces CPU utilization at the penalty of increased latency in case when syscall request arrives when RPC thread is sleeping ("warm-up" of RPC thread). --- Pal/src/host/Linux-SGX/sgx_enclave.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/Pal/src/host/Linux-SGX/sgx_enclave.c b/Pal/src/host/Linux-SGX/sgx_enclave.c index cb529c95..4be41a9e 100644 --- a/Pal/src/host/Linux-SGX/sgx_enclave.c +++ b/Pal/src/host/Linux-SGX/sgx_enclave.c @@ -724,13 +724,34 @@ static int rpc_thread_loop(void* arg) { g_rpc_queue->rpc_threads_cnt++; spinlock_unlock(&g_rpc_queue->lock); + static const uint64_t SPIN_ATTEMPTS_MAX = 10000; /* rather arbitrary */ + static const uint64_t SLEEP_TIME_MAX = 100000000; /* nanoseconds (0.1 seconds) */ + static const uint64_t SLEEP_TIME_STEP = SLEEP_TIME_MAX / 100; /* 100 steps before capped */ + + /* no races possible since vars are thread-local and RPC threads don't receive signals */ + uint64_t spin_attempts = 0; + uint64_t sleep_time = 0; + while (1) { rpc_request_t* req = rpc_dequeue(g_rpc_queue); if (!req) { - cpu_pause(); + if (spin_attempts == SPIN_ATTEMPTS_MAX) { + if (sleep_time < SLEEP_TIME_MAX) + sleep_time += SLEEP_TIME_STEP; + + struct timespec tv = {.tv_sec = 0, .tv_nsec = sleep_time}; + (void)INLINE_SYSCALL(nanosleep, 2, &tv, /*rem=*/NULL); + } else { + spin_attempts++; + cpu_pause(); + } continue; } + /* new request came, reset spin/sleep heuristics */ + spin_attempts = 0; + sleep_time = 0; + /* call actual function and notify awaiting enclave thread when done */ sgx_ocall_fn_t f = ocall_table[req->ocall_index]; req->result = f(req->buffer);