-
Notifications
You must be signed in to change notification settings - Fork 19
Expand file tree
/
Copy pathlocal_runqueue_mtds.c
More file actions
270 lines (227 loc) · 9.83 KB
/
Copy pathlocal_runqueue_mtds.c
File metadata and controls
270 lines (227 loc) · 9.83 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
#include <stdint.h>
#include <threads.h>
#include "arch/context.h"
#include "current_sandbox.h"
#include "debuglog.h"
#include "global_request_scheduler.h"
#include "local_runqueue.h"
#include "local_runqueue_mtds.h"
#include "panic.h"
#include "priority_queue.h"
#include "runtime.h"
#include "sandbox_functions.h"
#include "tenant_functions.h"
thread_local static struct priority_queue *local_runqueue_mtds_guaranteed;
thread_local static struct priority_queue *local_runqueue_mtds_default;
extern __thread struct priority_queue *worker_thread_timeout_queue;
/**
* Get Per-Worker-Tenant priority for Priority Queue ordering
* @param element the PWT
* @returns the priority of the head of the PWT
*/
static inline uint64_t
perworker_tenant_get_priority(void *element)
{
struct perworker_tenant_sandbox_queue *pwt = (struct perworker_tenant_sandbox_queue *)element;
struct sandbox *sandbox = NULL;
priority_queue_top_nolock(pwt->sandboxes, (void **)&sandbox);
return (sandbox) ? sandbox->absolute_deadline : UINT64_MAX;
}
static inline size_t *
perworker_tenant_get_index_ptr(void *element)
{
return &((struct perworker_tenant_sandbox_queue *)element)->pq_idx;
}
/**
* Checks if the run queue is empty
* @returns true if empty. false otherwise
*/
bool
local_runqueue_mtds_is_empty()
{
return priority_queue_length_nolock(local_runqueue_mtds_guaranteed) == 0
&& priority_queue_length_nolock(local_runqueue_mtds_default) == 0;
}
/**
* Adds a sandbox to the run queue
* @param sandbox
* @returns pointer to sandbox added
*/
void
local_runqueue_mtds_add(struct sandbox *sandbox)
{
assert(sandbox != NULL);
struct perworker_tenant_sandbox_queue *pwt = &sandbox->tenant->pwt_sandboxes[worker_thread_idx];
struct priority_queue *destination_queue = pwt->mt_class == MT_GUARANTEED ? local_runqueue_mtds_guaranteed
: local_runqueue_mtds_default;
uint64_t prev_pwt_deadline = priority_queue_peek(pwt->sandboxes);
/* Add the sandbox to the per-worker-tenant (pwt) queue */
int rc = priority_queue_enqueue_nolock(pwt->sandboxes, sandbox);
if (rc == -ENOSPC) panic("Per Worker Tenant queue is full!\n");
// debuglog("Added a sandbox to the PWT of tenant '%s'", sandbox->tenant->name);
/* If the tenant was not in the local runqueue, then since we are the first sandbox for it in this worker, add
* the tenant. */
if (priority_queue_length_nolock(pwt->sandboxes) == 1) {
/* Add sandbox tenant to the worker's timeout queue if guaranteed tenant and only if first sandbox*/
if (tenant_is_paid(sandbox->tenant)) {
pwt->tenant_timeout.timeout = get_next_timeout_of_tenant(sandbox->tenant->replenishment_period);
priority_queue_enqueue_nolock(worker_thread_timeout_queue, &pwt->tenant_timeout);
}
rc = priority_queue_enqueue_nolock(destination_queue, pwt);
return;
}
if (sandbox->absolute_deadline < prev_pwt_deadline) {
/* Maintain the minheap structure by deleting & adding the pwt.
* Do this only when the pwt's priority is updated. */
rc = priority_queue_delete_nolock(destination_queue, pwt);
assert(rc == 0);
rc = priority_queue_enqueue_nolock(destination_queue, pwt);
if (rc == -ENOSPC) panic("Worker Local Runqueue is full!\n");
}
}
/**
* Deletes a sandbox from the runqueue
* @param sandbox to delete
*/
static void
local_runqueue_mtds_delete(struct sandbox *sandbox)
{
assert(sandbox != NULL);
struct perworker_tenant_sandbox_queue *pwt = &sandbox->tenant->pwt_sandboxes[worker_thread_idx];
/* Delete the sandbox from the corresponding Per-Worker-Tenant queue */
if (priority_queue_delete_nolock(pwt->sandboxes, sandbox) == -1) {
panic("Tried to delete sandbox %lu from PWT queue, but was not present\n", sandbox->id);
}
struct priority_queue *destination_queue = local_runqueue_mtds_default;
if (pwt->mt_class == MT_GUARANTEED) { destination_queue = local_runqueue_mtds_guaranteed; }
/* Delete the PWT from the local runqueue completely if pwt is empty, re-add otherwise to heapify */
if (priority_queue_delete_nolock(destination_queue, pwt) == -1) {
// TODO: Apply the composite way of PQ deletion O(logn)
panic("Tried to delete a PWT of %s from local runqueue, but was not present\n", pwt->tenant->name);
}
/* Add the PWT back to the local runqueue if it still has other sandboxes inside */
if (priority_queue_length_nolock(pwt->sandboxes) > 0) {
priority_queue_enqueue_nolock(destination_queue, pwt);
} else if (tenant_is_paid(pwt->tenant)) {
priority_queue_delete_nolock(worker_thread_timeout_queue, &pwt->tenant_timeout);
pwt->mt_class = MT_GUARANTEED;
}
}
/**
* This function determines the next sandbox to run.
* This is the head of the runqueue
*
* Execute the sandbox at the head of the thread local runqueue
* @return the sandbox to execute or NULL if none are available
*/
struct sandbox *
local_runqueue_mtds_get_next()
{
/* Get the deadline of the sandbox at the head of the local request queue */
struct perworker_tenant_sandbox_queue *next_pwt = NULL;
struct priority_queue *dq = local_runqueue_mtds_guaranteed;
/* Check the local guaranteed queue for any potential demotions */
int rc = priority_queue_top_nolock(dq, (void **)&next_pwt);
while (rc != -ENOENT && next_pwt->tenant->remaining_budget <= 0) { // next_pwt->mt_class==MT_DEFAULT){
local_runqueue_mtds_demote(next_pwt);
// debuglog("Demoted '%s' locally in GetNext", next_pwt->tenant->name);
next_pwt->mt_class = MT_DEFAULT;
rc = priority_queue_top_nolock(dq, (void **)&next_pwt);
}
if (rc == -ENOENT) {
dq = local_runqueue_mtds_default;
rc = priority_queue_top_nolock(dq, (void **)&next_pwt);
if (rc == -ENOENT) return NULL;
}
struct sandbox *next_sandbox = NULL;
priority_queue_top_nolock(next_pwt->sandboxes, (void **)&next_sandbox);
assert(next_sandbox);
return next_sandbox;
}
/**
* Registers the PS variant with the polymorphic interface
*/
void
local_runqueue_mtds_initialize()
{
/* Initialize local state */
local_runqueue_mtds_guaranteed = priority_queue_initialize(RUNTIME_MAX_TENANT_COUNT, false,
perworker_tenant_get_priority,
perworker_tenant_get_index_ptr);
local_runqueue_mtds_default = priority_queue_initialize(RUNTIME_MAX_TENANT_COUNT, false,
perworker_tenant_get_priority,
perworker_tenant_get_index_ptr);
/* Register Function Pointers for Abstract Scheduling API */
struct local_runqueue_config config = {.add_fn = local_runqueue_mtds_add,
.is_empty_fn = local_runqueue_mtds_is_empty,
.delete_fn = local_runqueue_mtds_delete,
.get_next_fn = local_runqueue_mtds_get_next};
local_runqueue_initialize(&config);
}
/**
* Promotes the given per-worker-tenant queue, which means deletes from the Default queue
* and adds to the Guaranteed queue.
*/
void
local_runqueue_mtds_promote(struct perworker_tenant_sandbox_queue *pwt)
{
assert(pwt != NULL);
/* Delete the corresponding PWT from the Guaranteed queue */
int rc = priority_queue_delete_nolock(local_runqueue_mtds_default, pwt);
if (rc == -1) {
panic("Tried to delete a non-present PWT of %s from the Local Default queue. Already deleted? , ITS "
"SIZE: %d",
pwt->tenant->name, priority_queue_length_nolock(pwt->sandboxes));
}
/* Add the corresponding PWT to the Default queue */
rc = priority_queue_enqueue_nolock(local_runqueue_mtds_guaranteed, pwt);
if (rc == -ENOSPC) panic("Local Guaranteed queue is full!\n");
}
/**
* Demotes the given per-worker-tenant queue, which means deletes from the Guaranteed queue
* and adds to the Default queue.
*/
void
local_runqueue_mtds_demote(struct perworker_tenant_sandbox_queue *pwt)
{
assert(pwt != NULL);
/* Delete the corresponding PWT from the Guaranteed queue */
int rc = priority_queue_delete_nolock(local_runqueue_mtds_guaranteed, pwt);
if (rc == -1) {
panic("Tried to delete a non-present PWT of %s from the Local Guaranteed queue. Already deleted? , ITS "
"SIZE: %d",
pwt->tenant->name, priority_queue_length_nolock(pwt->sandboxes));
}
/* Add the corresponding PWT to the Default queue */
rc = priority_queue_enqueue_nolock(local_runqueue_mtds_default, pwt);
if (rc == -ENOSPC) panic("Local Default queue is full!\n");
}
/*
* Checks if there are any tenant timers that run out in the LOCAL queue,
* if so promote that tenant.
*/
void
local_timeout_queue_process_promotions()
{
struct tenant_timeout *top_tenant_timeout = NULL;
/* Check the timeout queue for a potential tenant to get PRomoted */
priority_queue_top_nolock(worker_thread_timeout_queue, (void **)&top_tenant_timeout);
if (top_tenant_timeout == NULL) return; // no guaranteed tenants
struct perworker_tenant_sandbox_queue *pwt_to_promote = NULL;
uint64_t now = __getcycles();
while (now >= top_tenant_timeout->timeout) {
pwt_to_promote = top_tenant_timeout->pwt;
assert(priority_queue_length_nolock(pwt_to_promote->sandboxes) > 0);
if (pwt_to_promote->mt_class == MT_DEFAULT) {
local_runqueue_mtds_promote(pwt_to_promote);
pwt_to_promote->mt_class = MT_GUARANTEED;
// debuglog("Promoted '%s' locally", top_tenant_timeout->tenant->name);
}
/* Reheapify the timeout queue with the updated timeout value of the tenant */
priority_queue_delete_nolock(worker_thread_timeout_queue, top_tenant_timeout);
top_tenant_timeout->timeout = get_next_timeout_of_tenant(
top_tenant_timeout->tenant->replenishment_period);
priority_queue_enqueue_nolock(worker_thread_timeout_queue, top_tenant_timeout);
priority_queue_top_nolock(worker_thread_timeout_queue, (void **)&top_tenant_timeout);
}
}