]> rtime.felk.cvut.cz Git - zynq/linux.git/commitdiff
ipc/sem: Rework semaphore wakeups
authorPeter Zijlstra <peterz@infradead.org>
Wed, 14 Sep 2011 09:57:04 +0000 (11:57 +0200)
committerMichal Sojka <sojka@merica.cz>
Sun, 13 Sep 2015 07:47:36 +0000 (09:47 +0200)
Current sysv sems have a weird ass wakeup scheme that involves keeping
preemption disabled over a potential O(n^2) loop and busy waiting on
that on other CPUs.

Kill this and simply wake the task directly from under the sem_lock.

This was discovered by a migrate_disable() debug feature that
disallows:

  spin_lock();
  preempt_disable();
  spin_unlock()
  preempt_enable();

Cc: Manfred Spraul <manfred@colorfullife.com>
Suggested-by: Thomas Gleixner <tglx@linutronix.de>
Reported-by: Mike Galbraith <efault@gmx.de>
Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Manfred Spraul <manfred@colorfullife.com>
Link: http://lkml.kernel.org/r/1315994224.5040.1.camel@twins
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
ipc/sem.c

index 92842113c6a9dfff0ca026f6ec2ff1bc663b2f38..c6dd7dbe181ac5823f0f27cfaa8874afe62faa91 100644 (file)
--- a/ipc/sem.c
+++ b/ipc/sem.c
@@ -680,6 +680,13 @@ undo:
 static void wake_up_sem_queue_prepare(struct list_head *pt,
                                struct sem_queue *q, int error)
 {
+#ifdef CONFIG_PREEMPT_RT_BASE
+       struct task_struct *p = q->sleeper;
+       get_task_struct(p);
+       q->status = error;
+       wake_up_process(p);
+       put_task_struct(p);
+#else
        if (list_empty(pt)) {
                /*
                 * Hold preempt off so that we don't get preempted and have the
@@ -691,6 +698,7 @@ static void wake_up_sem_queue_prepare(struct list_head *pt,
        q->pid = error;
 
        list_add_tail(&q->list, pt);
+#endif
 }
 
 /**
@@ -704,6 +712,7 @@ static void wake_up_sem_queue_prepare(struct list_head *pt,
  */
 static void wake_up_sem_queue_do(struct list_head *pt)
 {
+#ifndef CONFIG_PREEMPT_RT_BASE
        struct sem_queue *q, *t;
        int did_something;
 
@@ -716,6 +725,7 @@ static void wake_up_sem_queue_do(struct list_head *pt)
        }
        if (did_something)
                preempt_enable();
+#endif
 }
 
 static void unlink_queue(struct sem_array *sma, struct sem_queue *q)