diff options
author | Bjoern B. Brandenburg <bbb@cs.unc.edu> | 2008-04-08 13:15:48 -0400 |
---|---|---|
committer | Bjoern B. Brandenburg <bbb@cs.unc.edu> | 2008-05-01 12:03:10 -0400 |
commit | 03201b0f6d2b3f5ba9e4fe48be6b995ddd58a2d5 (patch) | |
tree | 21ec5e6d3ac6f551fa6e570735daa62d6940ba31 | |
parent | 7ff99e881f4c237e817f51b273c8eeb23660e0e8 (diff) |
SRP: improve robustness
The SRP implementation did not correctly address various
suspension-related scenarios correctly. Now the need for
SRP blocking is tested on each scheduling event. This ensures
mutual exclusion under the SRP even in the face of unexpected
suspensions, for example due to IO.
-rw-r--r-- | include/litmus/litmus.h | 1 | ||||
-rw-r--r-- | include/litmus/rt_param.h | 3 | ||||
-rw-r--r-- | kernel/sched.c | 2 | ||||
-rw-r--r-- | litmus/litmus.c | 15 | ||||
-rw-r--r-- | litmus/litmus_sem.c | 90 |
5 files changed, 65 insertions, 46 deletions
diff --git a/include/litmus/litmus.h b/include/litmus/litmus.h index 512efef341..8fc9f630c8 100644 --- a/include/litmus/litmus.h +++ b/include/litmus/litmus.h | |||
@@ -200,5 +200,6 @@ static inline lt_t litmus_clock(void) | |||
200 | #define make_np(t) do {t->rt_param.kernel_np++;} while(0); | 200 | #define make_np(t) do {t->rt_param.kernel_np++;} while(0); |
201 | #define take_np(t) do {t->rt_param.kernel_np--;} while(0); | 201 | #define take_np(t) do {t->rt_param.kernel_np--;} while(0); |
202 | 202 | ||
203 | void srp_ceiling_block(void); | ||
203 | 204 | ||
204 | #endif | 205 | #endif |
diff --git a/include/litmus/rt_param.h b/include/litmus/rt_param.h index 118e8590fd..933c1961cb 100644 --- a/include/litmus/rt_param.h +++ b/include/litmus/rt_param.h | |||
@@ -73,6 +73,9 @@ struct rt_param { | |||
73 | /* is the task sleeping? */ | 73 | /* is the task sleeping? */ |
74 | unsigned int flags:8; | 74 | unsigned int flags:8; |
75 | 75 | ||
76 | /* do we need to check for srp blocking? */ | ||
77 | unsigned int srp_non_recurse:1; | ||
78 | |||
76 | /* user controlled parameters */ | 79 | /* user controlled parameters */ |
77 | struct rt_task task_params; | 80 | struct rt_task task_params; |
78 | 81 | ||
diff --git a/kernel/sched.c b/kernel/sched.c index 5d31bc9d56..00097dea89 100644 --- a/kernel/sched.c +++ b/kernel/sched.c | |||
@@ -3688,6 +3688,8 @@ need_resched_nonpreemptible: | |||
3688 | preempt_enable_no_resched(); | 3688 | preempt_enable_no_resched(); |
3689 | if (unlikely(test_thread_flag(TIF_NEED_RESCHED))) | 3689 | if (unlikely(test_thread_flag(TIF_NEED_RESCHED))) |
3690 | goto need_resched; | 3690 | goto need_resched; |
3691 | if (srp_active()) | ||
3692 | srp_ceiling_block(); | ||
3691 | } | 3693 | } |
3692 | EXPORT_SYMBOL(schedule); | 3694 | EXPORT_SYMBOL(schedule); |
3693 | 3695 | ||
diff --git a/litmus/litmus.c b/litmus/litmus.c index 2909d5c04e..60aed9dd30 100644 --- a/litmus/litmus.c +++ b/litmus/litmus.c | |||
@@ -153,8 +153,6 @@ asmlinkage long sys_complete_job(void) | |||
153 | * appropriate queue and call schedule | 153 | * appropriate queue and call schedule |
154 | */ | 154 | */ |
155 | retval = litmus->complete_job(); | 155 | retval = litmus->complete_job(); |
156 | if (!retval) | ||
157 | srp_ceiling_block(); | ||
158 | out: | 156 | out: |
159 | return retval; | 157 | return retval; |
160 | } | 158 | } |
@@ -200,15 +198,10 @@ asmlinkage long sys_wait_for_job_release(unsigned int job) | |||
200 | * FIXME: At least problem 2 should be taken care of eventually. | 198 | * FIXME: At least problem 2 should be taken care of eventually. |
201 | */ | 199 | */ |
202 | while (!retval && job > current->rt_param.job_params.job_no) | 200 | while (!retval && job > current->rt_param.job_params.job_no) |
203 | /* If the last job overran then job <= job_no and we | 201 | /* If the last job overran then job <= job_no and we |
204 | * don't send the task to sleep. | 202 | * don't send the task to sleep. |
205 | */ | 203 | */ |
206 | retval = litmus->complete_job(); | 204 | retval = litmus->complete_job(); |
207 | |||
208 | /* We still have to honor the SRP after the actual release. | ||
209 | */ | ||
210 | if (!retval) | ||
211 | srp_ceiling_block(); | ||
212 | out: | 205 | out: |
213 | return retval; | 206 | return retval; |
214 | } | 207 | } |
diff --git a/litmus/litmus_sem.c b/litmus/litmus_sem.c index 60e82a2494..5d51d337d9 100644 --- a/litmus/litmus_sem.c +++ b/litmus/litmus_sem.c | |||
@@ -317,6 +317,24 @@ atomic_t srp_objects_in_use = ATOMIC_INIT(0); | |||
317 | 317 | ||
318 | DEFINE_PER_CPU(struct srp, srp); | 318 | DEFINE_PER_CPU(struct srp, srp); |
319 | 319 | ||
320 | |||
321 | /* Initialize SRP semaphores at boot time. */ | ||
322 | static int __init srp_init(void) | ||
323 | { | ||
324 | int i; | ||
325 | |||
326 | printk("Initializing SRP per-CPU ceilings..."); | ||
327 | for (i = 0; i < NR_CPUS; i++) { | ||
328 | init_waitqueue_head(&per_cpu(srp, i).ceiling_blocked); | ||
329 | INIT_LIST_HEAD(&per_cpu(srp, i).ceiling); | ||
330 | } | ||
331 | printk(" done!\n"); | ||
332 | |||
333 | return 0; | ||
334 | } | ||
335 | module_init(srp_init); | ||
336 | |||
337 | |||
320 | #define system_ceiling(srp) list2prio(srp->ceiling.next) | 338 | #define system_ceiling(srp) list2prio(srp->ceiling.next) |
321 | 339 | ||
322 | 340 | ||
@@ -347,7 +365,7 @@ static void srp_add_prio(struct srp* srp, struct srp_priority* prio) | |||
347 | struct list_head *pos; | 365 | struct list_head *pos; |
348 | if (in_list(&prio->list)) { | 366 | if (in_list(&prio->list)) { |
349 | printk(KERN_CRIT "WARNING: SRP violation detected, prio is already in " | 367 | printk(KERN_CRIT "WARNING: SRP violation detected, prio is already in " |
350 | "ceiling list!\n"); | 368 | "ceiling list! cpu=%d, srp=%p\n", smp_processor_id(), ceiling2sem(prio)); |
351 | return; | 369 | return; |
352 | } | 370 | } |
353 | list_for_each(pos, &srp->ceiling) | 371 | list_for_each(pos, &srp->ceiling) |
@@ -384,6 +402,8 @@ static noinline int open_srp_semaphore(struct od_table_entry* entry, void* __use | |||
384 | struct srp_priority t_prio; | 402 | struct srp_priority t_prio; |
385 | 403 | ||
386 | TRACE("opening SRP semaphore %p, cpu=%d\n", sem, sem->cpu); | 404 | TRACE("opening SRP semaphore %p, cpu=%d\n", sem, sem->cpu); |
405 | if (!srp_active()) | ||
406 | return -EBUSY; | ||
387 | 407 | ||
388 | if (sem->cpu == UNDEF_SEM) | 408 | if (sem->cpu == UNDEF_SEM) |
389 | sem->cpu = get_partition(t); | 409 | sem->cpu = get_partition(t); |
@@ -415,22 +435,6 @@ struct fdso_ops srp_sem_ops = { | |||
415 | .destroy = destroy_srp_semaphore | 435 | .destroy = destroy_srp_semaphore |
416 | }; | 436 | }; |
417 | 437 | ||
418 | /* Initialize SRP semaphores at boot time. */ | ||
419 | static int __init srp_sema_boot_init(void) | ||
420 | { | ||
421 | int i; | ||
422 | |||
423 | printk("Initializing SRP per-CPU ceilings..."); | ||
424 | for (i = 0; i < NR_CPUS; i++) { | ||
425 | init_waitqueue_head(&per_cpu(srp, i).ceiling_blocked); | ||
426 | INIT_LIST_HEAD(&per_cpu(srp, i).ceiling); | ||
427 | } | ||
428 | printk(" done!\n"); | ||
429 | |||
430 | return 0; | ||
431 | } | ||
432 | __initcall(srp_sema_boot_init); | ||
433 | |||
434 | 438 | ||
435 | void do_srp_down(struct srp_semaphore* sem) | 439 | void do_srp_down(struct srp_semaphore* sem) |
436 | { | 440 | { |
@@ -438,18 +442,20 @@ void do_srp_down(struct srp_semaphore* sem) | |||
438 | srp_add_prio(&__get_cpu_var(srp), &sem->ceiling); | 442 | srp_add_prio(&__get_cpu_var(srp), &sem->ceiling); |
439 | WARN_ON(sem->owner != NULL); | 443 | WARN_ON(sem->owner != NULL); |
440 | sem->owner = current; | 444 | sem->owner = current; |
445 | TRACE_CUR("acquired srp 0x%p\n", sem); | ||
441 | } | 446 | } |
442 | 447 | ||
443 | void do_srp_up(struct srp_semaphore* sem) | 448 | void do_srp_up(struct srp_semaphore* sem) |
444 | { | 449 | { |
445 | /* Determine new system priority ceiling for this CPU. */ | 450 | /* Determine new system priority ceiling for this CPU. */ |
446 | WARN_ON(!in_list(&sem->ceiling.list)); | 451 | WARN_ON(!in_list(&sem->ceiling.list)); |
447 | if (!in_list(&sem->ceiling.list)) | 452 | if (in_list(&sem->ceiling.list)) |
448 | list_del(&sem->ceiling.list); | 453 | list_del(&sem->ceiling.list); |
449 | 454 | ||
450 | sem->owner = NULL; | 455 | sem->owner = NULL; |
451 | 456 | ||
452 | /* Wake tasks on this CPU, if they exceed current ceiling. */ | 457 | /* Wake tasks on this CPU, if they exceed current ceiling. */ |
458 | TRACE_CUR("released srp 0x%p\n", sem); | ||
453 | wake_up_all(&__get_cpu_var(srp).ceiling_blocked); | 459 | wake_up_all(&__get_cpu_var(srp).ceiling_blocked); |
454 | } | 460 | } |
455 | 461 | ||
@@ -522,36 +528,50 @@ static int srp_wake_up(wait_queue_t *wait, unsigned mode, int sync, | |||
522 | } | 528 | } |
523 | 529 | ||
524 | 530 | ||
525 | /* Wait for current task priority to exceed system-wide priority ceiling. | 531 | |
526 | * Can be used to determine when it is safe to run a job after its release. | 532 | static void do_ceiling_block(struct task_struct *tsk) |
527 | */ | ||
528 | void srp_ceiling_block(void) | ||
529 | { | 533 | { |
530 | struct task_struct *tsk = current; | ||
531 | wait_queue_t wait = { | 534 | wait_queue_t wait = { |
532 | .private = tsk, | 535 | .private = tsk, |
533 | .func = srp_wake_up, | 536 | .func = srp_wake_up, |
534 | .task_list = {NULL, NULL} | 537 | .task_list = {NULL, NULL} |
535 | }; | 538 | }; |
536 | 539 | ||
537 | /* bail out early if there aren't any SRP resources around */ | 540 | tsk->state = TASK_UNINTERRUPTIBLE; |
538 | if (!atomic_read(&srp_objects_in_use)) | 541 | add_wait_queue(&__get_cpu_var(srp).ceiling_blocked, &wait); |
542 | tsk->rt_param.srp_non_recurse = 1; | ||
543 | preempt_enable_no_resched(); | ||
544 | schedule(); | ||
545 | preempt_disable(); | ||
546 | tsk->rt_param.srp_non_recurse = 0; | ||
547 | remove_wait_queue(&__get_cpu_var(srp).ceiling_blocked, &wait); | ||
548 | } | ||
549 | |||
550 | /* Wait for current task priority to exceed system-wide priority ceiling. | ||
551 | */ | ||
552 | void srp_ceiling_block(void) | ||
553 | { | ||
554 | struct task_struct *tsk = current; | ||
555 | |||
556 | /* Only applies to real-time tasks, but optimize for RT tasks. */ | ||
557 | if (unlikely(!is_realtime(tsk))) | ||
558 | return; | ||
559 | |||
560 | /* Avoid recursive ceiling blocking. */ | ||
561 | if (unlikely(tsk->rt_param.srp_non_recurse)) | ||
562 | return; | ||
563 | |||
564 | /* Bail out early if there aren't any SRP resources around. */ | ||
565 | if (likely(!atomic_read(&srp_objects_in_use))) | ||
539 | return; | 566 | return; |
540 | 567 | ||
541 | preempt_disable(); | 568 | preempt_disable(); |
569 | tsk->rt_param.srp_block = 0; | ||
542 | if (!srp_exceeds_ceiling(tsk, &__get_cpu_var(srp))) { | 570 | if (!srp_exceeds_ceiling(tsk, &__get_cpu_var(srp))) { |
543 | tsk->state = TASK_UNINTERRUPTIBLE; | ||
544 | add_wait_queue(&__get_cpu_var(srp).ceiling_blocked, &wait); | ||
545 | TRACE_CUR("is priority ceiling blocked.\n"); | 571 | TRACE_CUR("is priority ceiling blocked.\n"); |
546 | preempt_enable_no_resched(); | 572 | while (!srp_exceeds_ceiling(tsk, &__get_cpu_var(srp))) |
547 | schedule(); | 573 | do_ceiling_block(tsk); |
548 | /* Access to CPU var must occur with preemptions disabled, | ||
549 | * otherwise Linux debug code complains loudly, even if it is | ||
550 | * ok here. | ||
551 | */ | ||
552 | preempt_disable(); | ||
553 | TRACE_CUR("finally exceeds system ceiling.\n"); | 574 | TRACE_CUR("finally exceeds system ceiling.\n"); |
554 | remove_wait_queue(&__get_cpu_var(srp).ceiling_blocked, &wait); | ||
555 | } else | 575 | } else |
556 | TRACE_CUR("is not priority ceiling blocked\n"); | 576 | TRACE_CUR("is not priority ceiling blocked\n"); |
557 | preempt_enable(); | 577 | preempt_enable(); |