[PATCH] rtmutex: Propagate priority settings into PI lock chains
authorThomas Gleixner <tglx@linutronix.de>
Tue, 27 Jun 2006 09:55:02 +0000 (02:55 -0700)
committerLinus Torvalds <torvalds@g5.osdl.org>
Wed, 28 Jun 2006 00:32:48 +0000 (17:32 -0700)
When the priority of a task, which is blocked on a lock, changes we must
propagate this change into the PI lock chain.  Therefor the chain walk code
is changed to get rid of the references to current to avoid false positives
in the deadlock detector, as setscheduler might be called by a task which
holds the lock on which the task whose priority is changed is blocked.

Also add some comments about the get/put_task_struct usage to avoid
confusion.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Cc: Steven Rostedt <rostedt@goodmis.org>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
include/linux/sched.h
kernel/rtmutex.c
kernel/sched.c

index b4e6be7de5ada98a0f5c20433cd88557db0736ab..821f0481ebe190c0b5e171a4e6ab887802b3015c 100644 (file)
@@ -1044,11 +1044,13 @@ extern void sched_idle_next(void);
 #ifdef CONFIG_RT_MUTEXES
 extern int rt_mutex_getprio(task_t *p);
 extern void rt_mutex_setprio(task_t *p, int prio);
+extern void rt_mutex_adjust_pi(task_t *p);
 #else
 static inline int rt_mutex_getprio(task_t *p)
 {
        return p->normal_prio;
 }
+# define rt_mutex_adjust_pi(p)         do { } while (0)
 #endif
 
 extern void set_user_nice(task_t *p, long nice);
index 3fc0f0680ca267fe5d43c7a3f258c920122df239..45d61016da57612ec7690568abaef95a4515c547 100644 (file)
@@ -160,7 +160,8 @@ int max_lock_depth = 1024;
 static int rt_mutex_adjust_prio_chain(task_t *task,
                                      int deadlock_detect,
                                      struct rt_mutex *orig_lock,
-                                     struct rt_mutex_waiter *orig_waiter
+                                     struct rt_mutex_waiter *orig_waiter,
+                                     struct task_struct *top_task
                                      __IP_DECL__)
 {
        struct rt_mutex *lock;
@@ -189,7 +190,7 @@ static int rt_mutex_adjust_prio_chain(task_t *task,
                        prev_max = max_lock_depth;
                        printk(KERN_WARNING "Maximum lock depth %d reached "
                               "task: %s (%d)\n", max_lock_depth,
-                              current->comm, current->pid);
+                              top_task->comm, top_task->pid);
                }
                put_task_struct(task);
 
@@ -229,7 +230,7 @@ static int rt_mutex_adjust_prio_chain(task_t *task,
        }
 
        /* Deadlock detection */
-       if (lock == orig_lock || rt_mutex_owner(lock) == current) {
+       if (lock == orig_lock || rt_mutex_owner(lock) == top_task) {
                debug_rt_mutex_deadlock(deadlock_detect, orig_waiter, lock);
                spin_unlock(&lock->wait_lock);
                ret = deadlock_detect ? -EDEADLK : 0;
@@ -433,6 +434,7 @@ static int task_blocks_on_rt_mutex(struct rt_mutex *lock,
                __rt_mutex_adjust_prio(owner);
                if (owner->pi_blocked_on) {
                        boost = 1;
+                       /* gets dropped in rt_mutex_adjust_prio_chain()! */
                        get_task_struct(owner);
                }
                spin_unlock_irqrestore(&owner->pi_lock, flags);
@@ -441,6 +443,7 @@ static int task_blocks_on_rt_mutex(struct rt_mutex *lock,
                spin_lock_irqsave(&owner->pi_lock, flags);
                if (owner->pi_blocked_on) {
                        boost = 1;
+                       /* gets dropped in rt_mutex_adjust_prio_chain()! */
                        get_task_struct(owner);
                }
                spin_unlock_irqrestore(&owner->pi_lock, flags);
@@ -450,8 +453,8 @@ static int task_blocks_on_rt_mutex(struct rt_mutex *lock,
 
        spin_unlock(&lock->wait_lock);
 
-       res = rt_mutex_adjust_prio_chain(owner, detect_deadlock, lock,
-                                        waiter __IP__);
+       res = rt_mutex_adjust_prio_chain(owner, detect_deadlock, lock, waiter,
+                                        current __IP__);
 
        spin_lock(&lock->wait_lock);
 
@@ -552,6 +555,7 @@ static void remove_waiter(struct rt_mutex *lock,
 
                if (owner->pi_blocked_on) {
                        boost = 1;
+                       /* gets dropped in rt_mutex_adjust_prio_chain()! */
                        get_task_struct(owner);
                }
                spin_unlock_irqrestore(&owner->pi_lock, flags);
@@ -564,11 +568,36 @@ static void remove_waiter(struct rt_mutex *lock,
 
        spin_unlock(&lock->wait_lock);
 
-       rt_mutex_adjust_prio_chain(owner, 0, lock, NULL __IP__);
+       rt_mutex_adjust_prio_chain(owner, 0, lock, NULL, current __IP__);
 
        spin_lock(&lock->wait_lock);
 }
 
+/*
+ * Recheck the pi chain, in case we got a priority setting
+ *
+ * Called from sched_setscheduler
+ */
+void rt_mutex_adjust_pi(struct task_struct *task)
+{
+       struct rt_mutex_waiter *waiter;
+       unsigned long flags;
+
+       spin_lock_irqsave(&task->pi_lock, flags);
+
+       waiter = task->pi_blocked_on;
+       if (!waiter || waiter->list_entry.prio == task->prio) {
+               spin_unlock_irqrestore(&task->pi_lock, flags);
+               return;
+       }
+
+       /* gets dropped in rt_mutex_adjust_prio_chain()! */
+       get_task_struct(task);
+       spin_unlock_irqrestore(&task->pi_lock, flags);
+
+       rt_mutex_adjust_prio_chain(task, 0, NULL, NULL, task __RET_IP__);
+}
+
 /*
  * Slow path lock function:
  */
@@ -636,6 +665,7 @@ rt_mutex_slowlock(struct rt_mutex *lock, int state,
                        if (unlikely(ret))
                                break;
                }
+
                spin_unlock(&lock->wait_lock);
 
                debug_rt_mutex_print_deadlock(&waiter);
index 7a30addfd235f73195fca9943c7e9d80bba617f2..2629c1711fd62be84574153e0ae62077895f3b36 100644 (file)
@@ -4070,6 +4070,8 @@ recheck:
        __task_rq_unlock(rq);
        spin_unlock_irqrestore(&p->pi_lock, flags);
 
+       rt_mutex_adjust_pi(p);
+
        return 0;
 }
 EXPORT_SYMBOL_GPL(sched_setscheduler);