futex: Drop hb->lock before enqueueing on the rtmutex
authorPeter Zijlstra <peterz@infradead.org>
Wed, 22 Mar 2017 10:36:00 +0000 (11:36 +0100)
committerThomas Gleixner <tglx@linutronix.de>
Thu, 23 Mar 2017 18:14:59 +0000 (19:14 +0100)
commit56222b212e8edb1cf51f5dd73ff645809b082b40
tree162dda41c7fd9129019c28e86b873c42925acb17
parentbebe5b514345f09be2c15e414d076b02ecb9cce8
futex: Drop hb->lock before enqueueing on the rtmutex

When PREEMPT_RT_FULL does the spinlock -> rt_mutex substitution the PI
chain code will (falsely) report a deadlock and BUG.

The problem is that it hold hb->lock (now an rt_mutex) while doing
task_blocks_on_rt_mutex on the futex's pi_state::rtmutex. This, when
interleaved just right with futex_unlock_pi() leads it to believe to see an
AB-BA deadlock.

  Task1 (holds rt_mutex, Task2 (does FUTEX_LOCK_PI)
         does FUTEX_UNLOCK_PI)

lock hb->lock
lock rt_mutex (as per start_proxy)
  lock hb->lock

Which is a trivial AB-BA.

It is not an actual deadlock, because it won't be holding hb->lock by the
time it actually blocks on the rt_mutex, but the chainwalk code doesn't
know that and it would be a nightmare to handle this gracefully.

To avoid this problem, do the same as in futex_unlock_pi() and drop
hb->lock after acquiring wait_lock. This still fully serializes against
futex_unlock_pi(), since adding to the wait_list does the very same lock
dance, and removing it holds both locks.

Aside of solving the RT problem this makes the lock and unlock mechanism
symetric and reduces the hb->lock held time.

Reported-and-tested-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
Suggested-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Cc: juri.lelli@arm.com
Cc: xlpang@redhat.com
Cc: rostedt@goodmis.org
Cc: mathieu.desnoyers@efficios.com
Cc: jdesfossez@efficios.com
Cc: dvhart@infradead.org
Cc: bristot@redhat.com
Link: http://lkml.kernel.org/r/20170322104152.161341537@infradead.org
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
kernel/futex.c
kernel/locking/rtmutex.c
kernel/locking/rtmutex_common.h