aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBjoern B. Brandenburg <bbb@cs.unc.edu>2008-04-08 13:15:48 -0400
committerBjoern B. Brandenburg <bbb@cs.unc.edu>2008-05-01 12:03:10 -0400
commit03201b0f6d2b3f5ba9e4fe48be6b995ddd58a2d5 (patch)
tree21ec5e6d3ac6f551fa6e570735daa62d6940ba31
parent7ff99e881f4c237e817f51b273c8eeb23660e0e8 (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.h1
-rw-r--r--include/litmus/rt_param.h3
-rw-r--r--kernel/sched.c2
-rw-r--r--litmus/litmus.c15
-rw-r--r--litmus/litmus_sem.c90
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
203void 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}
3692EXPORT_SYMBOL(schedule); 3694EXPORT_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
318DEFINE_PER_CPU(struct srp, srp); 318DEFINE_PER_CPU(struct srp, srp);
319 319
320
321/* Initialize SRP semaphores at boot time. */
322static 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}
335module_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. */
419static 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
435void do_srp_down(struct srp_semaphore* sem) 439void 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
443void do_srp_up(struct srp_semaphore* sem) 448void 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. 532static void do_ceiling_block(struct task_struct *tsk)
527 */
528void 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 */
552void 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();