linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
To: linux-kernel@vger.kernel.org, stable@vger.kernel.org
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
	Heiko Carstens <heiko.carstens@de.ibm.com>,
	Peter Zijlstra <peterz@infradead.org>,
	Thomas Gleixner <tglx@linutronix.de>,
	Martin Schwidefsky <schwidefsky@de.ibm.com>,
	linux-s390@vger.kernel.org, Stefan Liebler <stli@linux.ibm.com>,
	Sebastian Sewior <bigeasy@linutronix.de>,
	Ben Hutchings <ben@decadent.org.uk>
Subject: [PATCH 4.9 43/53] futex: Handle early deadlock return correctly
Date: Mon, 29 Mar 2021 09:58:18 +0200	[thread overview]
Message-ID: <20210329075608.925964742@linuxfoundation.org> (raw)
In-Reply-To: <20210329075607.561619583@linuxfoundation.org>

From: Thomas Gleixner <tglx@linutronix.de>

commit 1a1fb985f2e2b85ec0d3dc2e519ee48389ec2434 upstream.

commit 56222b212e8e ("futex: Drop hb->lock before enqueueing on the
rtmutex") changed the locking rules in the futex code so that the hash
bucket lock is not longer held while the waiter is enqueued into the
rtmutex wait list. This made the lock and the unlock path symmetric, but
unfortunately the possible early exit from __rt_mutex_proxy_start() due to
a detected deadlock was not updated accordingly. That allows a concurrent
unlocker to observe inconsitent state which triggers the warning in the
unlock path.

futex_lock_pi()                         futex_unlock_pi()
  lock(hb->lock)
  queue(hb_waiter)				lock(hb->lock)
  lock(rtmutex->wait_lock)
  unlock(hb->lock)
                                        // acquired hb->lock
                                        hb_waiter = futex_top_waiter()
                                        lock(rtmutex->wait_lock)
  __rt_mutex_proxy_start()
     ---> fail
          remove(rtmutex_waiter);
     ---> returns -EDEADLOCK
  unlock(rtmutex->wait_lock)
                                        // acquired wait_lock
                                        wake_futex_pi()
                                        rt_mutex_next_owner()
					  --> returns NULL
                                          --> WARN

  lock(hb->lock)
  unqueue(hb_waiter)

The problem is caused by the remove(rtmutex_waiter) in the failure case of
__rt_mutex_proxy_start() as this lets the unlocker observe a waiter in the
hash bucket but no waiter on the rtmutex, i.e. inconsistent state.

The original commit handles this correctly for the other early return cases
(timeout, signal) by delaying the removal of the rtmutex waiter until the
returning task reacquired the hash bucket lock.

Treat the failure case of __rt_mutex_proxy_start() in the same way and let
the existing cleanup code handle the eventual handover of the rtmutex
gracefully. The regular rt_mutex_proxy_start() gains the rtmutex waiter
removal for the failure case, so that the other callsites are still
operating correctly.

Add proper comments to the code so all these details are fully documented.

Thanks to Peter for helping with the analysis and writing the really
valuable code comments.

Fixes: 56222b212e8e ("futex: Drop hb->lock before enqueueing on the rtmutex")
Reported-by: Heiko Carstens <heiko.carstens@de.ibm.com>
Co-developed-by: Peter Zijlstra <peterz@infradead.org>
Signed-off-by: Peter Zijlstra <peterz@infradead.org>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Tested-by: Heiko Carstens <heiko.carstens@de.ibm.com>
Cc: Martin Schwidefsky <schwidefsky@de.ibm.com>
Cc: linux-s390@vger.kernel.org
Cc: Stefan Liebler <stli@linux.ibm.com>
Cc: Sebastian Sewior <bigeasy@linutronix.de>
Cc: stable@vger.kernel.org
Link: https://lkml.kernel.org/r/alpine.DEB.2.21.1901292311410.1950@nanos.tec.linutronix.de
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
 kernel/futex.c           |   28 ++++++++++++++++++----------
 kernel/locking/rtmutex.c |   37 ++++++++++++++++++++++++++++++++-----
 2 files changed, 50 insertions(+), 15 deletions(-)

--- a/kernel/futex.c
+++ b/kernel/futex.c
@@ -2958,35 +2958,39 @@ retry_private:
 	 * and BUG when futex_unlock_pi() interleaves with this.
 	 *
 	 * Therefore acquire wait_lock while holding hb->lock, but drop the
-	 * latter before calling rt_mutex_start_proxy_lock(). This still fully
-	 * serializes against futex_unlock_pi() as that does the exact same
-	 * lock handoff sequence.
+	 * latter before calling __rt_mutex_start_proxy_lock(). This
+	 * interleaves with futex_unlock_pi() -- which does a similar lock
+	 * handoff -- such that the latter can observe the futex_q::pi_state
+	 * before __rt_mutex_start_proxy_lock() is done.
 	 */
 	raw_spin_lock_irq(&q.pi_state->pi_mutex.wait_lock);
 	spin_unlock(q.lock_ptr);
+	/*
+	 * __rt_mutex_start_proxy_lock() unconditionally enqueues the @rt_waiter
+	 * such that futex_unlock_pi() is guaranteed to observe the waiter when
+	 * it sees the futex_q::pi_state.
+	 */
 	ret = __rt_mutex_start_proxy_lock(&q.pi_state->pi_mutex, &rt_waiter, current);
 	raw_spin_unlock_irq(&q.pi_state->pi_mutex.wait_lock);
 
 	if (ret) {
 		if (ret == 1)
 			ret = 0;
-
-		spin_lock(q.lock_ptr);
-		goto no_block;
+		goto cleanup;
 	}
 
-
 	if (unlikely(to))
 		hrtimer_start_expires(&to->timer, HRTIMER_MODE_ABS);
 
 	ret = rt_mutex_wait_proxy_lock(&q.pi_state->pi_mutex, to, &rt_waiter);
 
+cleanup:
 	spin_lock(q.lock_ptr);
 	/*
-	 * If we failed to acquire the lock (signal/timeout), we must
+	 * If we failed to acquire the lock (deadlock/signal/timeout), we must
 	 * first acquire the hb->lock before removing the lock from the
-	 * rt_mutex waitqueue, such that we can keep the hb and rt_mutex
-	 * wait lists consistent.
+	 * rt_mutex waitqueue, such that we can keep the hb and rt_mutex wait
+	 * lists consistent.
 	 *
 	 * In particular; it is important that futex_unlock_pi() can not
 	 * observe this inconsistency.
@@ -3093,6 +3097,10 @@ retry:
 		 * there is no point where we hold neither; and therefore
 		 * wake_futex_pi() must observe a state consistent with what we
 		 * observed.
+		 *
+		 * In particular; this forces __rt_mutex_start_proxy() to
+		 * complete such that we're guaranteed to observe the
+		 * rt_waiter. Also see the WARN in wake_futex_pi().
 		 */
 		raw_spin_lock_irq(&pi_state->pi_mutex.wait_lock);
 		spin_unlock(&hb->lock);
--- a/kernel/locking/rtmutex.c
+++ b/kernel/locking/rtmutex.c
@@ -1695,12 +1695,33 @@ void rt_mutex_proxy_unlock(struct rt_mut
 	rt_mutex_set_owner(lock, NULL);
 }
 
+/**
+ * __rt_mutex_start_proxy_lock() - Start lock acquisition for another task
+ * @lock:		the rt_mutex to take
+ * @waiter:		the pre-initialized rt_mutex_waiter
+ * @task:		the task to prepare
+ *
+ * Starts the rt_mutex acquire; it enqueues the @waiter and does deadlock
+ * detection. It does not wait, see rt_mutex_wait_proxy_lock() for that.
+ *
+ * NOTE: does _NOT_ remove the @waiter on failure; must either call
+ * rt_mutex_wait_proxy_lock() or rt_mutex_cleanup_proxy_lock() after this.
+ *
+ * Returns:
+ *  0 - task blocked on lock
+ *  1 - acquired the lock for task, caller should wake it up
+ * <0 - error
+ *
+ * Special API call for PI-futex support.
+ */
 int __rt_mutex_start_proxy_lock(struct rt_mutex *lock,
 			      struct rt_mutex_waiter *waiter,
 			      struct task_struct *task)
 {
 	int ret;
 
+	lockdep_assert_held(&lock->wait_lock);
+
 	if (try_to_take_rt_mutex(lock, task, NULL))
 		return 1;
 
@@ -1718,9 +1739,6 @@ int __rt_mutex_start_proxy_lock(struct r
 		ret = 0;
 	}
 
-	if (unlikely(ret))
-		remove_waiter(lock, waiter);
-
 	debug_rt_mutex_print_deadlock(waiter);
 
 	return ret;
@@ -1732,12 +1750,18 @@ int __rt_mutex_start_proxy_lock(struct r
  * @waiter:		the pre-initialized rt_mutex_waiter
  * @task:		the task to prepare
  *
+ * Starts the rt_mutex acquire; it enqueues the @waiter and does deadlock
+ * detection. It does not wait, see rt_mutex_wait_proxy_lock() for that.
+ *
+ * NOTE: unlike __rt_mutex_start_proxy_lock this _DOES_ remove the @waiter
+ * on failure.
+ *
  * Returns:
  *  0 - task blocked on lock
  *  1 - acquired the lock for task, caller should wake it up
  * <0 - error
  *
- * Special API call for FUTEX_REQUEUE_PI support.
+ * Special API call for PI-futex support.
  */
 int rt_mutex_start_proxy_lock(struct rt_mutex *lock,
 			      struct rt_mutex_waiter *waiter,
@@ -1747,6 +1771,8 @@ int rt_mutex_start_proxy_lock(struct rt_
 
 	raw_spin_lock_irq(&lock->wait_lock);
 	ret = __rt_mutex_start_proxy_lock(lock, waiter, task);
+	if (unlikely(ret))
+		remove_waiter(lock, waiter);
 	raw_spin_unlock_irq(&lock->wait_lock);
 
 	return ret;
@@ -1814,7 +1840,8 @@ int rt_mutex_wait_proxy_lock(struct rt_m
  * @lock:		the rt_mutex we were woken on
  * @waiter:		the pre-initialized rt_mutex_waiter
  *
- * Attempt to clean up after a failed rt_mutex_wait_proxy_lock().
+ * Attempt to clean up after a failed __rt_mutex_start_proxy_lock() or
+ * rt_mutex_wait_proxy_lock().
  *
  * Unless we acquired the lock; we're still enqueued on the wait-list and can
  * in fact still be granted ownership until we're removed. Therefore we can



  parent reply	other threads:[~2021-03-29  8:05 UTC|newest]

Thread overview: 58+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-03-29  7:57 [PATCH 4.9 00/53] 4.9.264-rc1 review Greg Kroah-Hartman
2021-03-29  7:57 ` [PATCH 4.9 01/53] net: fec: ptp: avoid register access when ipg clock is disabled Greg Kroah-Hartman
2021-03-29  7:57 ` [PATCH 4.9 02/53] powerpc/4xx: Fix build errors from mfdcr() Greg Kroah-Hartman
2021-03-29  7:57 ` [PATCH 4.9 03/53] atm: eni: dont release is never initialized Greg Kroah-Hartman
2021-03-29  7:57 ` [PATCH 4.9 04/53] atm: lanai: dont run lanai_dev_close if not open Greg Kroah-Hartman
2021-03-29  7:57 ` [PATCH 4.9 05/53] ixgbe: Fix memleak in ixgbe_configure_clsu32 Greg Kroah-Hartman
2021-03-29  7:57 ` [PATCH 4.9 06/53] net: tehuti: fix error return code in bdx_probe() Greg Kroah-Hartman
2021-03-29  7:57 ` [PATCH 4.9 07/53] sun/niu: fix wrong RXMAC_BC_FRM_CNT_COUNT count Greg Kroah-Hartman
2021-03-29  7:57 ` [PATCH 4.9 08/53] nfs: fix PNFS_FLEXFILE_LAYOUT Kconfig default Greg Kroah-Hartman
2021-03-29  7:57 ` [PATCH 4.9 09/53] NFS: Correct size calculation for create reply length Greg Kroah-Hartman
2021-03-29  7:57 ` [PATCH 4.9 10/53] net: wan: fix error return code of uhdlc_init() Greg Kroah-Hartman
2021-03-29  7:57 ` [PATCH 4.9 11/53] atm: uPD98402: fix incorrect allocation Greg Kroah-Hartman
2021-03-29  7:57 ` [PATCH 4.9 12/53] atm: idt77252: fix null-ptr-dereference Greg Kroah-Hartman
2021-03-29  7:57 ` [PATCH 4.9 13/53] u64_stats,lockdep: Fix u64_stats_init() vs lockdep Greg Kroah-Hartman
2021-03-29  7:57 ` [PATCH 4.9 14/53] nfs: we dont support removing system.nfs4_acl Greg Kroah-Hartman
2021-03-29  7:57 ` [PATCH 4.9 15/53] ia64: fix ia64_syscall_get_set_arguments() for break-based syscalls Greg Kroah-Hartman
2021-03-29  7:57 ` [PATCH 4.9 16/53] ia64: fix ptrace(PTRACE_SYSCALL_INFO_EXIT) sign Greg Kroah-Hartman
2021-03-29  7:57 ` [PATCH 4.9 17/53] x86/tlb: Flush global mappings when KAISER is disabled Greg Kroah-Hartman
2021-03-29  7:57 ` [PATCH 4.9 18/53] squashfs: fix inode lookup sanity checks Greg Kroah-Hartman
2021-03-29  7:57 ` [PATCH 4.9 19/53] squashfs: fix xattr id and id " Greg Kroah-Hartman
2021-03-29  7:57 ` [PATCH 4.9 20/53] arm64: dts: ls1043a: mark crypto engine dma coherent Greg Kroah-Hartman
2021-03-29  7:57 ` [PATCH 4.9 21/53] bus: omap_l3_noc: mark l3 irqs as IRQF_NO_THREAD Greg Kroah-Hartman
2021-03-29  7:57 ` [PATCH 4.9 22/53] macvlan: macvlan_count_rx() needs to be aware of preemption Greg Kroah-Hartman
2021-03-29  7:57 ` [PATCH 4.9 23/53] net: dsa: bcm_sf2: Qualify phydev->dev_flags based on port Greg Kroah-Hartman
2021-03-29  7:57 ` [PATCH 4.9 24/53] e1000e: add rtnl_lock() to e1000_reset_task Greg Kroah-Hartman
2021-03-29  7:58 ` [PATCH 4.9 25/53] e1000e: Fix error handling in e1000_set_d0_lplu_state_82571 Greg Kroah-Hartman
2021-03-29  7:58 ` [PATCH 4.9 26/53] net/qlcnic: Fix a use after free in qlcnic_83xx_get_minidump_template Greg Kroah-Hartman
2021-03-29  7:58 ` [PATCH 4.9 27/53] can: c_can_pci: c_can_pci_remove(): fix use-after-free Greg Kroah-Hartman
2021-03-29  7:58 ` [PATCH 4.9 28/53] can: c_can: move runtime PM enable/disable to c_can_platform Greg Kroah-Hartman
2021-03-29  7:58 ` [PATCH 4.9 29/53] can: m_can: m_can_do_rx_poll(): fix extraneous msg loss warning Greg Kroah-Hartman
2021-03-29  7:58 ` [PATCH 4.9 30/53] mac80211: fix rate mask reset Greg Kroah-Hartman
2021-03-29  7:58 ` [PATCH 4.9 31/53] net: cdc-phonet: fix data-interface release on probe failure Greg Kroah-Hartman
2021-03-29  7:58 ` [PATCH 4.9 32/53] RDMA/cxgb4: Fix adapter LE hash errors while destroying ipv6 listening server Greg Kroah-Hartman
2021-03-29  7:58 ` [PATCH 4.9 33/53] ACPI: scan: Rearrange memory allocation in acpi_device_add() Greg Kroah-Hartman
2021-03-29  7:58 ` [PATCH 4.9 34/53] ACPI: scan: Use unique number for instance_no Greg Kroah-Hartman
2021-03-29  7:58 ` [PATCH 4.9 35/53] perf auxtrace: Fix auxtrace queue conflict Greg Kroah-Hartman
2021-03-29  7:58 ` [PATCH 4.9 36/53] idr: add ida_is_empty Greg Kroah-Hartman
2021-03-29  7:58 ` [PATCH 4.9 37/53] futex: Use smp_store_release() in mark_wake_futex() Greg Kroah-Hartman
2021-03-29  7:58 ` [PATCH 4.9 38/53] futex,rt_mutex: Introduce rt_mutex_init_waiter() Greg Kroah-Hartman
2021-03-29  7:58 ` [PATCH 4.9 39/53] futex: Rework futex_lock_pi() to use rt_mutex_*_proxy_lock() Greg Kroah-Hartman
2021-03-29  7:58 ` [PATCH 4.9 40/53] futex: Drop hb->lock before enqueueing on the rtmutex Greg Kroah-Hartman
2021-03-29  7:58 ` [PATCH 4.9 41/53] futex: Avoid freeing an active timer Greg Kroah-Hartman
2021-03-29  7:58 ` [PATCH 4.9 42/53] futex,rt_mutex: Fix rt_mutex_cleanup_proxy_lock() Greg Kroah-Hartman
2021-03-29  7:58 ` Greg Kroah-Hartman [this message]
2021-03-29  7:58 ` [PATCH 4.9 44/53] futex: Fix (possible) missed wakeup Greg Kroah-Hartman
2021-03-29  7:58 ` [PATCH 4.9 45/53] locking/futex: Allow low-level atomic operations to return -EAGAIN Greg Kroah-Hartman
2021-03-29  7:58 ` [PATCH 4.9 46/53] arm64: futex: Bound number of LDXR/STXR loops in FUTEX_WAKE_OP Greg Kroah-Hartman
2021-03-29  7:58 ` [PATCH 4.9 47/53] futex: Prevent robust futex exit race Greg Kroah-Hartman
2021-03-29  7:58 ` [PATCH 4.9 48/53] futex: Fix incorrect should_fail_futex() handling Greg Kroah-Hartman
2021-03-29  7:58 ` [PATCH 4.9 49/53] futex: Handle transient "ownerless" rtmutex state correctly Greg Kroah-Hartman
2021-03-29  7:58 ` [PATCH 4.9 50/53] can: dev: Move device back to init netns on owning netns delete Greg Kroah-Hartman
2021-03-29  7:58 ` [PATCH 4.9 51/53] net: sched: validate stab values Greg Kroah-Hartman
2021-03-29  7:58 ` [PATCH 4.9 52/53] net: qrtr: fix a kernel-infoleak in qrtr_recvmsg() Greg Kroah-Hartman
2021-03-29  7:58 ` [PATCH 4.9 53/53] mac80211: fix double free in ibss_leave Greg Kroah-Hartman
2021-03-29 18:45 ` [PATCH 4.9 00/53] 4.9.264-rc1 review Florian Fainelli
2021-03-29 21:32 ` Guenter Roeck
2021-03-30  1:27 ` Shuah Khan
2021-03-30  7:05 ` Naresh Kamboju

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20210329075608.925964742@linuxfoundation.org \
    --to=gregkh@linuxfoundation.org \
    --cc=ben@decadent.org.uk \
    --cc=bigeasy@linutronix.de \
    --cc=heiko.carstens@de.ibm.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-s390@vger.kernel.org \
    --cc=peterz@infradead.org \
    --cc=schwidefsky@de.ibm.com \
    --cc=stable@vger.kernel.org \
    --cc=stli@linux.ibm.com \
    --cc=tglx@linutronix.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).