Commit b0c5b4f1 authored by Rohan McLure's avatar Rohan McLure Committed by Michael Ellerman
Browse files

powerpc: powernv: Fix KCSAN datarace warnings on idle_state contention



The idle_state entry in the PACA on PowerNV features a bit which is
atomically tested and set through ldarx/stdcx. to be used as a spinlock.
This lock then guards access to other bit fields of idle_state. KCSAN
cannot differentiate between any of these bitfield accesses as they all
are implemented by 8-byte store/load instructions, thus cores contending
on the bit-lock appear to data race with modifications to idle_state.

Separate the bit-lock entry from the data guarded by the lock to avoid
the possibility of data races being detected by KCSAN.

Suggested-by: default avatarNicholas Piggin <npiggin@gmail.com>
Signed-off-by: default avatarRohan McLure <rmclure@linux.ibm.com>
Reviewed-by: default avatarNicholas Piggin <npiggin@gmail.com>
Signed-off-by: default avatarMichael Ellerman <mpe@ellerman.id.au>
Link: https://msgid.link/20230510033117.1395895-7-rmclure@linux.ibm.com
parent be286b86
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -191,6 +191,7 @@ struct paca_struct {
#ifdef CONFIG_PPC_POWERNV
	/* PowerNV idle fields */
	/* PNV_CORE_IDLE_* bits, all siblings work on thread 0 paca */
	unsigned long idle_lock; /* A value of 1 means acquired */
	unsigned long idle_state;
	union {
		/* P7/P8 specific fields */
+9 −7
Original line number Diff line number Diff line
@@ -246,9 +246,9 @@ static inline void atomic_lock_thread_idle(void)
{
	int cpu = raw_smp_processor_id();
	int first = cpu_first_thread_sibling(cpu);
	unsigned long *state = &paca_ptrs[first]->idle_state;
	unsigned long *lock = &paca_ptrs[first]->idle_lock;

	while (unlikely(test_and_set_bit_lock(NR_PNV_CORE_IDLE_LOCK_BIT, state)))
	while (unlikely(test_and_set_bit_lock(NR_PNV_CORE_IDLE_LOCK_BIT, lock)))
		barrier();
}

@@ -258,29 +258,31 @@ static inline void atomic_unlock_and_stop_thread_idle(void)
	int first = cpu_first_thread_sibling(cpu);
	unsigned long thread = 1UL << cpu_thread_in_core(cpu);
	unsigned long *state = &paca_ptrs[first]->idle_state;
	unsigned long *lock = &paca_ptrs[first]->idle_lock;
	u64 s = READ_ONCE(*state);
	u64 new, tmp;

	BUG_ON(!(s & PNV_CORE_IDLE_LOCK_BIT));
	BUG_ON(!(READ_ONCE(*lock) & PNV_CORE_IDLE_LOCK_BIT));
	BUG_ON(s & thread);

again:
	new = (s | thread) & ~PNV_CORE_IDLE_LOCK_BIT;
	new = s | thread;
	tmp = cmpxchg(state, s, new);
	if (unlikely(tmp != s)) {
		s = tmp;
		goto again;
	}
	clear_bit_unlock(NR_PNV_CORE_IDLE_LOCK_BIT, lock);
}

static inline void atomic_unlock_thread_idle(void)
{
	int cpu = raw_smp_processor_id();
	int first = cpu_first_thread_sibling(cpu);
	unsigned long *state = &paca_ptrs[first]->idle_state;
	unsigned long *lock = &paca_ptrs[first]->idle_lock;

	BUG_ON(!test_bit(NR_PNV_CORE_IDLE_LOCK_BIT, state));
	clear_bit_unlock(NR_PNV_CORE_IDLE_LOCK_BIT, state);
	BUG_ON(!test_bit(NR_PNV_CORE_IDLE_LOCK_BIT, lock));
	clear_bit_unlock(NR_PNV_CORE_IDLE_LOCK_BIT, lock);
}

/* P7 and P8 */