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: Nicholas Piggin <npiggin@gmail.com>
Signed-off-by: Rohan McLure <rmclure@linux.ibm.com>
Reviewed-by: Nicholas Piggin <npiggin@gmail.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Link: https://msgid.link/20230510033117.1395895-7-rmclure@linux.ibm.com
This commit is contained in:
Rohan McLure 2023-05-10 13:31:12 +10:00 committed by Michael Ellerman
parent be286b8637
commit b0c5b4f1ee
2 changed files with 10 additions and 7 deletions

View File

@ -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 */

View File

@ -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 */