Commit 5e4823e6 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag 'locking_urgent_for_v5.19' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip

Pull locking fix from Borislav Petkov:

 - Avoid rwsem lockups in certain situations when handling the handoff
   bit

* tag 'locking_urgent_for_v5.19' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
  locking/rwsem: Allow slowpath writer to ignore handoff bit if not set by first waiter
parents cd2715b7 6eebd5fb
Loading
Loading
Loading
Loading
+20 −10
Original line number Diff line number Diff line
@@ -335,8 +335,6 @@ struct rwsem_waiter {
	struct task_struct *task;
	enum rwsem_waiter_type type;
	unsigned long timeout;

	/* Writer only, not initialized in reader */
	bool handoff_set;
};
#define rwsem_first_waiter(sem) \
@@ -459,11 +457,13 @@ static void rwsem_mark_wake(struct rw_semaphore *sem,
			 * to give up the lock), request a HANDOFF to
			 * force the issue.
			 */
			if (!(oldcount & RWSEM_FLAG_HANDOFF) &&
			    time_after(jiffies, waiter->timeout)) {
			if (time_after(jiffies, waiter->timeout)) {
				if (!(oldcount & RWSEM_FLAG_HANDOFF)) {
					adjustment -= RWSEM_FLAG_HANDOFF;
					lockevent_inc(rwsem_rlock_handoff);
				}
				waiter->handoff_set = true;
			}

			atomic_long_add(-adjustment, &sem->count);
			return;
@@ -599,7 +599,7 @@ rwsem_del_wake_waiter(struct rw_semaphore *sem, struct rwsem_waiter *waiter,
static inline bool rwsem_try_write_lock(struct rw_semaphore *sem,
					struct rwsem_waiter *waiter)
{
	bool first = rwsem_first_waiter(sem) == waiter;
	struct rwsem_waiter *first = rwsem_first_waiter(sem);
	long count, new;

	lockdep_assert_held(&sem->wait_lock);
@@ -609,10 +609,19 @@ static inline bool rwsem_try_write_lock(struct rw_semaphore *sem,
		bool has_handoff = !!(count & RWSEM_FLAG_HANDOFF);

		if (has_handoff) {
			if (!first)
			/*
			 * Honor handoff bit and yield only when the first
			 * waiter is the one that set it. Otherwisee, we
			 * still try to acquire the rwsem.
			 */
			if (first->handoff_set && (waiter != first))
				return false;

			/* First waiter inherits a previously set handoff bit */
			/*
			 * First waiter can inherit a previously set handoff
			 * bit and spin on rwsem if lock acquisition fails.
			 */
			if (waiter == first)
				waiter->handoff_set = true;
		}

@@ -1027,6 +1036,7 @@ rwsem_down_read_slowpath(struct rw_semaphore *sem, long count, unsigned int stat
	waiter.task = current;
	waiter.type = RWSEM_WAITING_FOR_READ;
	waiter.timeout = jiffies + RWSEM_WAIT_TIMEOUT;
	waiter.handoff_set = false;

	raw_spin_lock_irq(&sem->wait_lock);
	if (list_empty(&sem->wait_list)) {