diff options
Diffstat (limited to 'litmus/servers.c')
-rw-r--r-- | litmus/servers.c | 475 |
1 files changed, 330 insertions, 145 deletions
diff --git a/litmus/servers.c b/litmus/servers.c index a43aad336367..9ee58198b0d6 100644 --- a/litmus/servers.c +++ b/litmus/servers.c | |||
@@ -1,9 +1,13 @@ | |||
1 | /* | ||
2 | * TODO: memory leaks for stopping | ||
3 | */ | ||
1 | #include <linux/hrtimer.h> | 4 | #include <linux/hrtimer.h> |
2 | #include <linux/percpu.h> | 5 | #include <linux/percpu.h> |
3 | #include <linux/sched.h> | 6 | #include <linux/sched.h> |
4 | #include <linux/uaccess.h> | 7 | #include <linux/uaccess.h> |
5 | #include <linux/ctype.h> | 8 | #include <linux/ctype.h> |
6 | 9 | ||
10 | #include <litmus/bheap.h> | ||
7 | #include <litmus/litmus.h> | 11 | #include <litmus/litmus.h> |
8 | #include <litmus/litmus_proc.h> | 12 | #include <litmus/litmus_proc.h> |
9 | #include <litmus/sched_trace.h> | 13 | #include <litmus/sched_trace.h> |
@@ -11,6 +15,9 @@ | |||
11 | 15 | ||
12 | #define DEBUG_SERVERS | 16 | #define DEBUG_SERVERS |
13 | 17 | ||
18 | /* Not working */ | ||
19 | /* #define COMPLETION_ON_MASTER */ | ||
20 | |||
14 | #define TIME(x) \ | 21 | #define TIME(x) \ |
15 | ({lt_t y = x; \ | 22 | ({lt_t y = x; \ |
16 | do_div(y, NSEC_PER_MSEC); \ | 23 | do_div(y, NSEC_PER_MSEC); \ |
@@ -81,6 +88,7 @@ static inline int timer_cancel(struct hrtimer *timer) | |||
81 | 88 | ||
82 | static int completion_timer_arm(server_domain_t* domain, int cpu) | 89 | static int completion_timer_arm(server_domain_t* domain, int cpu) |
83 | { | 90 | { |
91 | int err = 0, on_cpu; | ||
84 | lt_t now = litmus_clock(); | 92 | lt_t now = litmus_clock(); |
85 | server_t *server = domain->running[cpu]; | 93 | server_t *server = domain->running[cpu]; |
86 | lt_t budget_exhausted = now + server->budget; | 94 | lt_t budget_exhausted = now + server->budget; |
@@ -95,13 +103,40 @@ static int completion_timer_arm(server_domain_t* domain, int cpu) | |||
95 | return 0; | 103 | return 0; |
96 | } | 104 | } |
97 | 105 | ||
106 | if (domain->completion_timers[cpu].armed) { | ||
107 | TRACE_SUB(server, "cannot arm completion, waiting for arm"); | ||
108 | return 0; | ||
109 | } | ||
110 | |||
111 | /* This happens when a server is run late enough that it would complete | ||
112 | * after its released. This will almost certainly cause a reschedule, | ||
113 | * so we just force the server to complete here and trust the callback | ||
114 | * to get things right. | ||
115 | */ | ||
116 | if (lt_before_eq(server->deadline, budget_exhausted)) { | ||
117 | budget_exhausted = server->deadline; | ||
118 | } | ||
119 | |||
98 | TRACE_SUB(server, "start time: %llu", server->start_time); | 120 | TRACE_SUB(server, "start time: %llu", server->start_time); |
99 | 121 | ||
122 | #ifdef COMPLETION_ON_MASTER | ||
123 | if (domain->release_master != NO_CPU) | ||
124 | on_cpu = domain->release_master; | ||
125 | else | ||
126 | #endif | ||
127 | on_cpu = cpu; | ||
128 | |||
100 | if (cpu != smp_processor_id()) { | 129 | if (cpu != smp_processor_id()) { |
101 | hrtimer_start_on(cpu, &domain->completion_timers[cpu].info, | 130 | err = hrtimer_start_on(on_cpu, |
102 | &domain->completion_timers[cpu].timer, | 131 | &domain->completion_timers[cpu].info, |
103 | ns_to_ktime(budget_exhausted), | 132 | &domain->completion_timers[cpu].timer, |
104 | HRTIMER_MODE_ABS_PINNED); | 133 | ns_to_ktime(budget_exhausted), |
134 | HRTIMER_MODE_ABS_PINNED); | ||
135 | if (err) { | ||
136 | TRACE_SUB(server, "failed to arm completion"); | ||
137 | } else { | ||
138 | TRACE_SUB(server, "success on P%d!", on_cpu); | ||
139 | } | ||
105 | } else { | 140 | } else { |
106 | __hrtimer_start_range_ns(&domain->completion_timers[cpu].timer, | 141 | __hrtimer_start_range_ns(&domain->completion_timers[cpu].timer, |
107 | ns_to_ktime(budget_exhausted), | 142 | ns_to_ktime(budget_exhausted), |
@@ -132,9 +167,6 @@ void server_run(server_t *server, struct task_struct *task, | |||
132 | server->running = 1; | 167 | server->running = 1; |
133 | server->start_time = litmus_clock(); | 168 | server->start_time = litmus_clock(); |
134 | 169 | ||
135 | /* TODO REMOVE ME */ | ||
136 | server->domain = domain; | ||
137 | |||
138 | domain->running[cpu] = server; | 170 | domain->running[cpu] = server; |
139 | domain->completion_timers[cpu].armed =completion_timer_arm(domain, cpu); | 171 | domain->completion_timers[cpu].armed =completion_timer_arm(domain, cpu); |
140 | 172 | ||
@@ -147,28 +179,26 @@ static enum hrtimer_restart completion_timer_fire(struct hrtimer *timer) | |||
147 | unsigned long flags; | 179 | unsigned long flags; |
148 | enum hrtimer_restart rv; | 180 | enum hrtimer_restart rv; |
149 | struct task_struct *was_running; | 181 | struct task_struct *was_running; |
150 | completion_timer_t *container; | 182 | completion_timer_t *completion_timer; |
151 | server_domain_t *domain; | 183 | server_domain_t *domain; |
152 | server_t *server; | 184 | server_t *server; |
153 | lt_t budget_exhausted; | 185 | lt_t budget_exhausted; |
154 | 186 | ||
155 | rv = HRTIMER_NORESTART; | 187 | rv = HRTIMER_NORESTART; |
156 | cpu = smp_processor_id(); | ||
157 | 188 | ||
158 | 189 | completion_timer = container_of(timer, completion_timer_t, timer); | |
159 | /* Use fancy pointer arithmetic to get the domain */ | 190 | domain = completion_timer->domain; |
160 | container = container_of(timer, completion_timer_t, timer); | 191 | cpu = completion_timer->cpu; |
161 | domain = container->domain; | ||
162 | 192 | ||
163 | raw_spin_lock_irqsave(domain->timer_lock, flags); | 193 | raw_spin_lock_irqsave(domain->timer_lock, flags); |
164 | 194 | ||
165 | server = domain->running[cpu]; | 195 | _TRACE_TIMER("completion timer firing on P%d"); |
166 | TRACE_TIMER(server, "completion timer firing on P%d, remaining budget: %llu", | ||
167 | cpu, server->budget); | ||
168 | 196 | ||
169 | /* We got the lock before someone tried to re-arm. Proceed. */ | 197 | /* We got the lock before someone tried to re-arm. Proceed. */ |
170 | if (domain->completion_timers[cpu].armed) { | 198 | if (completion_timer->armed) { |
199 | server = domain->running[cpu]; | ||
171 | TRACE_SUB(server, "completed"); | 200 | TRACE_SUB(server, "completed"); |
201 | |||
172 | was_running = server->scheduled; | 202 | was_running = server->scheduled; |
173 | 203 | ||
174 | server->budget = 0; | 204 | server->budget = 0; |
@@ -183,13 +213,17 @@ static enum hrtimer_restart completion_timer_fire(struct hrtimer *timer) | |||
183 | /* Someone either beat us to the lock or hooked up a new server | 213 | /* Someone either beat us to the lock or hooked up a new server |
184 | * when we called server_completed. Rearm the timer. | 214 | * when we called server_completed. Rearm the timer. |
185 | */ | 215 | */ |
186 | if (domain->running[cpu] && !domain->completion_timers[cpu].armed) { | 216 | if (domain->running[cpu] && !completion_timer->armed) { |
187 | TRACE_SUB(server, "rearming on P%d", cpu); | ||
188 | server = domain->running[cpu]; | 217 | server = domain->running[cpu]; |
218 | TRACE_SUB(server, "rearming on P%d", cpu); | ||
189 | budget_exhausted = server->start_time + server->budget; | 219 | budget_exhausted = server->start_time + server->budget; |
190 | hrtimer_set_expires(timer, ns_to_ktime(budget_exhausted)); | 220 | hrtimer_set_expires(timer, ns_to_ktime(budget_exhausted)); |
191 | domain->completion_timers[cpu].armed = 1; | 221 | completion_timer->armed = 1; |
192 | rv = HRTIMER_RESTART; | 222 | rv = HRTIMER_RESTART; |
223 | } else { | ||
224 | atomic_set(&completion_timer->info.state, | ||
225 | HRTIMER_START_ON_INACTIVE); | ||
226 | |||
193 | } | 227 | } |
194 | 228 | ||
195 | raw_spin_unlock_irqrestore(domain->timer_lock, flags); | 229 | raw_spin_unlock_irqrestore(domain->timer_lock, flags); |
@@ -197,83 +231,48 @@ static enum hrtimer_restart completion_timer_fire(struct hrtimer *timer) | |||
197 | return rv; | 231 | return rv; |
198 | } | 232 | } |
199 | 233 | ||
200 | static enum hrtimer_restart release_timer_fire(struct hrtimer *timer) | 234 | struct kmem_cache *server_release_cache; /* In litmus.c */ |
201 | { | 235 | static enum hrtimer_restart release_servers_fire(struct hrtimer *timer); |
202 | unsigned long flags; | ||
203 | int was_running; | ||
204 | pserver_t *server = container_of(timer, pserver_t, release_timer); | ||
205 | struct task_struct *was_scheduled; | ||
206 | |||
207 | if (server->server.timer_lock) | ||
208 | raw_spin_lock_irqsave(server->server.timer_lock, flags); | ||
209 | else | ||
210 | local_irq_save(flags); | ||
211 | |||
212 | was_scheduled = server->server.scheduled; | ||
213 | |||
214 | TRACE_TIMER(&server->server, | ||
215 | "release timer firing, remaining budget: %llu", | ||
216 | get_server_budget(server)); | ||
217 | |||
218 | was_running = server->server.running; | ||
219 | |||
220 | /* This would have been armed if the budget would exhaust | ||
221 | * after the server release. | ||
222 | */ | ||
223 | if (was_running) | ||
224 | timer_cancel(&server->server.domain->completion_timers[ | ||
225 | server->server.scheduled->rt_param.linked_on].timer); | ||
226 | |||
227 | |||
228 | pserver_release(server); | ||
229 | server->post_release(server); | ||
230 | |||
231 | /* Need to arm the budget timer if the server continues running | ||
232 | * the same task. | ||
233 | */ | ||
234 | if (was_running && was_scheduled == get_server_scheduled(server)) | ||
235 | completion_timer_arm(server->server.domain, was_scheduled->rt_param.linked_on ); | ||
236 | |||
237 | hrtimer_set_expires(timer, ns_to_ktime(server->deadline)); | ||
238 | |||
239 | if (server->server.timer_lock) | ||
240 | raw_spin_unlock_irqrestore(server->server.timer_lock, flags); | ||
241 | else | ||
242 | local_irq_restore(flags); | ||
243 | 236 | ||
244 | return HRTIMER_RESTART; | 237 | /* |
238 | * Initialize heap. | ||
239 | */ | ||
240 | static server_release_heap_t* release_heap_alloc(int gfp_flags) | ||
241 | { | ||
242 | server_release_heap_t *rh; | ||
243 | rh = kmem_cache_alloc(server_release_cache, gfp_flags); | ||
244 | if (rh) { | ||
245 | hrtimer_init(&rh->timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); | ||
246 | rh->timer.function = release_servers_fire; | ||
247 | } | ||
248 | return rh; | ||
245 | } | 249 | } |
246 | 250 | ||
247 | void server_init(server_t *server, int id, raw_spinlock_t *timer_lock) | 251 | void server_init(server_t *server, int id, lt_t wcet, lt_t period, int grouped) |
248 | { | 252 | { |
249 | server->id = id; | 253 | server->id = id; |
254 | server->wcet = wcet; | ||
255 | server->period = period; | ||
250 | 256 | ||
251 | server->running = 0; | 257 | server->deadline = 0; |
252 | server->budget = 0; | 258 | server->release = 0; |
253 | server->job_no = 1; /* NOT SURE */ | 259 | server->budget = 0; |
254 | server->start_time = 0; | 260 | server->start_time = 0; |
255 | server->scheduled = NULL; | ||
256 | server->timer_lock = timer_lock; | ||
257 | } | ||
258 | 261 | ||
259 | void pserver_init(pserver_t *server, int id, raw_spinlock_t *timer_lock, | 262 | server->running = 0; |
260 | lt_t wcet, lt_t period) | 263 | server->job_no = 0; |
261 | { | 264 | server->scheduled = NULL; |
262 | server_init(&server->server, id, timer_lock); | ||
263 | |||
264 | server->wcet = wcet; | ||
265 | server->period = period; | ||
266 | 265 | ||
267 | server->deadline = 0; | 266 | server->hn = kmalloc(sizeof(struct bheap_node), GFP_ATOMIC); |
268 | server->post_release = NULL; | 267 | bheap_node_init(&server->hn, server); |
268 | INIT_LIST_HEAD(&server->list); | ||
269 | 269 | ||
270 | hrtimer_init(&server->release_timer, | 270 | if (grouped) { |
271 | CLOCK_MONOTONIC, | 271 | server->release_heap = release_heap_alloc(GFP_ATOMIC); |
272 | HRTIMER_MODE_ABS); | 272 | INIT_LIST_HEAD(&server->release_list); |
273 | server->release_timer.function = release_timer_fire; | 273 | } |
274 | } | 274 | } |
275 | 275 | ||
276 | |||
277 | /* | 276 | /* |
278 | * Handles subtraction of lt_t without underflows. | 277 | * Handles subtraction of lt_t without underflows. |
279 | */ | 278 | */ |
@@ -297,75 +296,62 @@ void server_stop(server_t *server, server_domain_t *domain) | |||
297 | } | 296 | } |
298 | 297 | ||
299 | BUG_ON(!server->running); | 298 | BUG_ON(!server->running); |
300 | cpu = server->scheduled->rt_param.linked_on; | ||
301 | |||
302 | TRACE_SUB(server, "stopping server, start: %llu, end: %llu", | 299 | TRACE_SUB(server, "stopping server, start: %llu, end: %llu", |
303 | server->start_time, now); | 300 | server->start_time, now); |
304 | 301 | ||
302 | |||
303 | /* Calculate remaining budget */ | ||
305 | elapsed_time = lt_subtract(now, server->start_time); | 304 | elapsed_time = lt_subtract(now, server->start_time); |
306 | server->budget -= elapsed_time; | 305 | server->budget -= elapsed_time; |
307 | 306 | ||
307 | TRACE_SUB(server, "new budget: %llu", TIME(server->budget)); | ||
308 | |||
309 | /* Set domain state */ | ||
310 | cpu = server->scheduled->rt_param.linked_on; | ||
308 | domain->completion_timers[cpu].armed = 0; | 311 | domain->completion_timers[cpu].armed = 0; |
309 | domain->running[cpu] = NULL; | 312 | domain->running[cpu] = NULL; |
310 | timer_cancel(&domain->completion_timers[cpu].timer); | 313 | timer_cancel(&domain->completion_timers[cpu].timer); |
311 | 314 | ||
312 | TRACE_SUB(server, "new budget: %llu", TIME(server->budget)); | 315 | /* Make server inactive */ |
313 | 316 | server->running = 0; | |
314 | server->running = 0; | 317 | server->scheduled = NULL; |
315 | server->scheduled = NULL; | ||
316 | server->start_time = 0; | 318 | server->start_time = 0; |
317 | } | 319 | } |
318 | 320 | ||
319 | void pserver_start_releasing(pserver_t *server, lt_t time) | 321 | void server_release(server_t *server) |
320 | { | 322 | { |
321 | if (hrtimer_active(&server->release_timer)) | 323 | BUG_ON(!server->deadline); |
322 | timer_cancel(&server->release_timer); | ||
323 | 324 | ||
324 | server->deadline = time; | 325 | server->budget = server->wcet; |
325 | pserver_release(server); | 326 | server->release = server->deadline; |
326 | 327 | server->deadline += server->period; | |
327 | __hrtimer_start_range_ns(&server->release_timer, | 328 | ++server->job_no; |
328 | ns_to_ktime(server->deadline), | ||
329 | 0 /* delta */, | ||
330 | HRTIMER_MODE_ABS_PINNED, | ||
331 | 0 /* no wakeup */); | ||
332 | } | ||
333 | |||
334 | void pserver_start_cpu_releasing(pserver_t *server, lt_t time, int cpu) | ||
335 | { | ||
336 | if (hrtimer_active(&server->release_timer)) | ||
337 | timer_cancel(&server->release_timer); | ||
338 | |||
339 | server->deadline = time; | ||
340 | pserver_release(server); | ||
341 | 329 | ||
342 | hrtimer_start_on_info_init(&per_cpu(server_cpu_infos, cpu)); | 330 | TRACE_SUB(server, "budget: %llu, release: %llu," |
331 | "deadline: %llu, period: %llu, job: %d", | ||
332 | server->budget, server->release, server->deadline, | ||
333 | server->period, server->job_no); | ||
334 | TRACE_SUB(server, "budget: %llu, release: %llu," | ||
335 | "deadline: %llu, period: %llu, job: %d", | ||
336 | TIME(server->budget), TIME(server->release), TIME(server->deadline), | ||
337 | TIME(server->period), server->job_no); | ||
343 | 338 | ||
344 | hrtimer_start_on(cpu, &per_cpu(server_cpu_infos, cpu), | 339 | /* Need to reset for budget calculations */ |
345 | &server->release_timer, | 340 | if (server->running) |
346 | ns_to_ktime(server->deadline), | 341 | server->start_time = litmus_clock(); |
347 | HRTIMER_MODE_ABS_PINNED); | ||
348 | } | 342 | } |
349 | 343 | ||
350 | void pserver_stop_releasing(pserver_t *server) | 344 | void server_release_at(server_t *server, lt_t time) |
351 | { | 345 | { |
352 | if (hrtimer_active(&server->release_timer)) | 346 | server->deadline = time; |
353 | timer_cancel(&server->release_timer); | 347 | server_release(server); |
354 | |||
355 | BUG_ON(get_server_running(server)); | ||
356 | 348 | ||
357 | server->deadline = 0; | 349 | TRACE_SUB(server, "releasing at %llu", time); |
358 | } | 350 | } |
359 | 351 | ||
360 | void server_release(server_t *server, lt_t budget) | 352 | /****************************************************************************** |
361 | { | 353 | * Proc methods |
362 | server->budget = budget; | 354 | ******************************************************************************/ |
363 | ++server->job_no; | ||
364 | |||
365 | /* Need to reset for budget calculations */ | ||
366 | if (server->running) | ||
367 | server->start_time = litmus_clock(); | ||
368 | } | ||
369 | 355 | ||
370 | static int server_proc_read(char* page, char **start, off_t off, | 356 | static int server_proc_read(char* page, char **start, off_t off, |
371 | int count, int *eof, void *data) | 357 | int count, int *eof, void *data) |
@@ -386,25 +372,21 @@ static int server_proc_read(char* page, char **start, off_t off, | |||
386 | return length; | 372 | return length; |
387 | } | 373 | } |
388 | 374 | ||
389 | void server_proc_read_single(pserver_t *server, int cpu, | 375 | void server_proc_read_single(server_t *server, int cpu, |
390 | struct proc_read_args *args) | 376 | struct proc_read_args *args) |
391 | { | 377 | { |
392 | lt_t wcet, period; | ||
393 | wcet = get_server_wcet(server); | ||
394 | period = get_server_period(server); | ||
395 | |||
396 | if (cpu == NO_CPU) { | 378 | if (cpu == NO_CPU) { |
397 | args->length += | 379 | args->length += |
398 | snprintf(args->page + args->length, | 380 | snprintf(args->page + args->length, |
399 | PAGE_SIZE - args->length, | 381 | PAGE_SIZE - args->length, |
400 | "%8llu %8llu\n", | 382 | "%8llu %8llu\n", |
401 | TIME(wcet), TIME(period)); | 383 | server->wcet, server->period); |
402 | } else { | 384 | } else { |
403 | args->length += | 385 | args->length += |
404 | snprintf(args->page + args->length, | 386 | snprintf(args->page + args->length, |
405 | PAGE_SIZE - args->length, | 387 | PAGE_SIZE - args->length, |
406 | "%8llu %8llu %3d\n", | 388 | "%8llu %8llu %3d\n", |
407 | TIME(wcet), TIME(period), cpu); | 389 | server->wcet, server->period, cpu); |
408 | } | 390 | } |
409 | } | 391 | } |
410 | 392 | ||
@@ -509,8 +491,7 @@ static int server_proc_write(struct file *file, const char __user *input, | |||
509 | ret = server_param_check(wcet, period, cpu); | 491 | ret = server_param_check(wcet, period, cpu); |
510 | if (ret) goto loop_end; | 492 | if (ret) goto loop_end; |
511 | 493 | ||
512 | ret = methods->admit_server(wcet * NSEC_PER_MSEC, | 494 | ret = methods->admit_server(wcet, period, cpu); |
513 | period * NSEC_PER_MSEC, cpu); | ||
514 | if (ret) { | 495 | if (ret) { |
515 | printk(KERN_WARNING "Litmus plugin rejects server with " | 496 | printk(KERN_WARNING "Litmus plugin rejects server with " |
516 | "period: %llu, wcet: %llu, cpu: %d\n", | 497 | "period: %llu, wcet: %llu, cpu: %d\n", |
@@ -559,6 +540,10 @@ void server_proc_exit(struct proc_dir_entry *proc_dir, char *file) | |||
559 | remove_proc_entry(file, proc_dir); | 540 | remove_proc_entry(file, proc_dir); |
560 | } | 541 | } |
561 | 542 | ||
543 | /****************************************************************************** | ||
544 | * Domain methods | ||
545 | ******************************************************************************/ | ||
546 | |||
562 | void server_domain_init(server_domain_t *domain, | 547 | void server_domain_init(server_domain_t *domain, |
563 | servers_released_t servers_released, | 548 | servers_released_t servers_released, |
564 | server_completed_t server_completed, | 549 | server_completed_t server_completed, |
@@ -575,7 +560,7 @@ void server_domain_init(server_domain_t *domain, | |||
575 | raw_spin_lock_init(&domain->tobe_lock); | 560 | raw_spin_lock_init(&domain->tobe_lock); |
576 | 561 | ||
577 | 562 | ||
578 | domain->release_master = NO_CPU; | 563 | domain->release_master = release_master; |
579 | domain->timer_lock = timer_lock; | 564 | domain->timer_lock = timer_lock; |
580 | domain->server_completed = server_completed; | 565 | domain->server_completed = server_completed; |
581 | domain->servers_released = servers_released; | 566 | domain->servers_released = servers_released; |
@@ -587,6 +572,7 @@ void server_domain_init(server_domain_t *domain, | |||
587 | for_each_online_cpu(i) { | 572 | for_each_online_cpu(i) { |
588 | domain->running[i] = NULL; | 573 | domain->running[i] = NULL; |
589 | domain->completion_timers[i].armed = 0; | 574 | domain->completion_timers[i].armed = 0; |
575 | domain->completion_timers[i].cpu = i; | ||
590 | hrtimer_init(&domain->completion_timers[i].timer, | 576 | hrtimer_init(&domain->completion_timers[i].timer, |
591 | CLOCK_MONOTONIC, | 577 | CLOCK_MONOTONIC, |
592 | HRTIMER_MODE_ABS); | 578 | HRTIMER_MODE_ABS); |
@@ -603,13 +589,212 @@ void server_domain_exit(server_domain_t *domain) | |||
603 | kfree(domain->running); | 589 | kfree(domain->running); |
604 | } | 590 | } |
605 | 591 | ||
606 | void add_server_release(server_t *server, server_domain_t *server_domain) | 592 | static unsigned int time2slot(lt_t time) |
593 | { | ||
594 | return (unsigned int) time2quanta(time, FLOOR) % | ||
595 | SERVER_RELEASE_QUEUE_SLOTS; | ||
596 | } | ||
597 | |||
598 | /* | ||
599 | * Send a list of servers to a client callback. | ||
600 | */ | ||
601 | static enum hrtimer_restart release_servers_fire(struct hrtimer *timer) | ||
602 | { | ||
603 | unsigned long flags; | ||
604 | server_release_heap_t *rh; | ||
605 | |||
606 | _TRACE_SUB("on_release_timer(0x%p) starts.", timer); | ||
607 | |||
608 | rh = container_of(timer, server_release_heap_t, timer); | ||
609 | |||
610 | raw_spin_lock_irqsave(&rh->domain->release_lock, flags); | ||
611 | _TRACE_SUB("CB has the release_lock"); | ||
612 | |||
613 | /* Remove from release queue */ | ||
614 | list_del(&rh->list); | ||
615 | |||
616 | raw_spin_unlock_irqrestore(&rh->domain->release_lock, flags); | ||
617 | _TRACE_SUB("CB returned release_lock"); | ||
618 | |||
619 | /* Call release callback */ | ||
620 | rh->domain->servers_released(&rh->servers); | ||
621 | /* WARNING: rh can be referenced from other CPUs from now on. */ | ||
622 | |||
623 | _TRACE_SUB("on_release_timer(0x%p) ends.", timer); | ||
624 | |||
625 | return HRTIMER_NORESTART; | ||
626 | } | ||
627 | |||
628 | /* | ||
629 | * Caller must hold release lock. | ||
630 | * Will return heap for given time. If no such heap exists prior to | ||
631 | * the invocation it will be created. | ||
632 | */ | ||
633 | static server_release_heap_t* get_release_heap(server_domain_t *rt, | ||
634 | server_t *server, | ||
635 | int use_server_heap) | ||
636 | { | ||
637 | struct list_head *pos; | ||
638 | server_release_heap_t *heap = NULL; | ||
639 | server_release_heap_t *rh; | ||
640 | lt_t release_time = server->release; | ||
641 | unsigned int slot = time2slot(release_time); | ||
642 | |||
643 | _TRACE_SUB("searching for release time %llu", release_time); | ||
644 | |||
645 | /* Initialize pos for the case that the list is empty */ | ||
646 | pos = rt->release_queue[slot].next; | ||
647 | list_for_each(pos, &rt->release_queue[slot]) { | ||
648 | rh = list_entry(pos, server_release_heap_t, list); | ||
649 | if (release_time == rh->release_time) { | ||
650 | /* Perfect match -- this happens on hyperperiod | ||
651 | * boundaries | ||
652 | */ | ||
653 | heap = rh; | ||
654 | break; | ||
655 | } else if (lt_before(release_time, rh->release_time)) { | ||
656 | /* We need to insert a new node since rh is | ||
657 | * already in the future | ||
658 | */ | ||
659 | break; | ||
660 | } | ||
661 | } | ||
662 | if (!heap && use_server_heap) { | ||
663 | /* Use pre-allocated release heap */ | ||
664 | rh = server->release_heap; | ||
665 | rh->domain = rt; | ||
666 | rh->release_time = release_time; | ||
667 | |||
668 | /* Add to release queue */ | ||
669 | list_add(&rh->list, pos->prev); | ||
670 | heap = rh; | ||
671 | } | ||
672 | return heap; | ||
673 | } | ||
674 | |||
675 | /* | ||
676 | * Prepare a server's release_heap for use. | ||
677 | */ | ||
678 | static int reinit_release_heap(server_t *server) | ||
607 | { | 679 | { |
608 | TRACE_SUB(server, "adding to release"); | 680 | int rv = 0; |
681 | server_release_heap_t* rh; | ||
682 | |||
683 | /* Use pre-allocated release heap */ | ||
684 | rh = server->release_heap; | ||
685 | |||
686 | /* WARNING: If the CPU still holds the release_lock at this point, | ||
687 | * deadlock may occur! | ||
688 | */ | ||
689 | rv = hrtimer_try_to_cancel(&rh->timer); | ||
690 | |||
691 | /* The timer callback is running, it is useless to add | ||
692 | * to the release heap now. | ||
693 | */ | ||
694 | if (rv == -1) { | ||
695 | rv = 0; | ||
696 | goto out; | ||
697 | } | ||
698 | |||
699 | /* Under no cirumstances should the timer have been active | ||
700 | * but not running. | ||
701 | */ | ||
702 | /* TODO: stop living dangerously */ | ||
703 | //BUG_ON(rv == 1); | ||
704 | rv = 1; | ||
705 | |||
706 | /* initialize */ | ||
707 | INIT_LIST_HEAD(&rh->servers); | ||
708 | atomic_set(&rh->info.state, HRTIMER_START_ON_INACTIVE); | ||
709 | out: | ||
710 | return rv; | ||
609 | } | 711 | } |
610 | 712 | ||
611 | void stop_server_releasing(server_domain_t *server_domain) | 713 | /* |
714 | * Arm the release timer for the next set of servers. | ||
715 | */ | ||
716 | static int arm_release_timer(server_domain_t *domain) | ||
612 | { | 717 | { |
718 | int rv = 1; | ||
719 | struct list_head list; | ||
720 | struct list_head *pos, *safe; | ||
721 | server_t *server; | ||
722 | server_release_heap_t *rh; | ||
723 | |||
724 | _TRACE_SUB("arm_release_timer() at %llu", litmus_clock()); | ||
725 | list_replace_init(&domain->tobe_released, &list); | ||
726 | |||
727 | list_for_each_safe(pos, safe, &list) { | ||
728 | /* Pick server from work list */ | ||
729 | server = list_entry(pos, server_t, release_list); | ||
730 | list_del(pos); | ||
731 | |||
732 | /* Put into release heap while holding release_lock */ | ||
733 | raw_spin_lock(&domain->release_lock); | ||
734 | TRACE_SUB(server, "I have the release_lock"); | ||
735 | |||
736 | rh = get_release_heap(domain, server, 0); | ||
737 | if (!rh) { | ||
738 | /* Need to use our own, but drop lock first */ | ||
739 | raw_spin_unlock(&domain->release_lock); | ||
740 | TRACE_SUB(server, "Dropped release_lock"); | ||
741 | |||
742 | rv = reinit_release_heap(server); | ||
743 | |||
744 | /* Bail! We missed the release time */ | ||
745 | if (!rv) { | ||
746 | TRACE_SUB(server, "missed release"); | ||
747 | rv = 0; | ||
748 | goto out; | ||
749 | } | ||
750 | |||
751 | TRACE_SUB(server, "release_heap ready"); | ||
752 | |||
753 | raw_spin_lock(&domain->release_lock); | ||
754 | TRACE_SUB(server, "Re-acquired release_lock"); | ||
613 | 755 | ||
756 | rh = get_release_heap(domain, server, 1); | ||
757 | } | ||
758 | |||
759 | list_add(&server->release_list, &rh->servers); | ||
760 | TRACE_SUB(server, "arm_release_timer(): added to release heap"); | ||
761 | |||
762 | raw_spin_unlock(&domain->release_lock); | ||
763 | TRACE_SUB(server, "Returned the release_lock"); | ||
764 | |||
765 | /* To avoid arming the timer multiple times, we only let the | ||
766 | * owner do the arming (which is the "first" task to reference | ||
767 | * this release_heap anyway). | ||
768 | */ | ||
769 | if (rh == server->release_heap) { | ||
770 | TRACE_SUB(server, "arming timer 0x%p at %llu on P%d", | ||
771 | &rh->timer, rh->release_time, | ||
772 | domain->release_master); | ||
773 | /* We cannot arm the timer using hrtimer_start() | ||
774 | * as it may deadlock on rq->lock | ||
775 | * | ||
776 | * PINNED mode is ok on both local and remote CPU | ||
777 | */ | ||
778 | if (domain->release_master == NO_CPU) { | ||
779 | __hrtimer_start_range_ns(&rh->timer, | ||
780 | ns_to_ktime(rh->release_time), | ||
781 | 0, HRTIMER_MODE_ABS_PINNED, 0); | ||
782 | } else { | ||
783 | hrtimer_start_on(domain->release_master, | ||
784 | &rh->info, &rh->timer, | ||
785 | ns_to_ktime(rh->release_time), | ||
786 | HRTIMER_MODE_ABS_PINNED); | ||
787 | } | ||
788 | } else | ||
789 | TRACE_SUB(server, "0x%p is not my timer", &rh->timer); | ||
790 | } | ||
791 | out: | ||
792 | return rv; | ||
614 | } | 793 | } |
615 | 794 | ||
795 | int add_server_release(server_t *server, server_domain_t *domain) | ||
796 | { | ||
797 | TRACE_SUB(server, "adding to release at %llu", server->release); | ||
798 | list_add(&server->release_list, &domain->tobe_released); | ||
799 | return arm_release_timer(domain); | ||
800 | } | ||