diff options
| author | GuEe-GUI <[email protected]> | 2026-10-20 15:58:07 +0800 |
|---|---|---|
| committer | Rbb666 <[email protected]> | 2026-10-22 17:59:29 +0800 |
| commit | 2a4e78d2321cff9bc2ad2d41f5c18cfe5e82595b (patch) | |
| tree | 776e4c104e04b7bd586fa0f614a4b39fcbe5c49b | |
| parent | dff75f072fe2b047325a312d6b944dd3acce8676 (diff) | |
[libcpu][risc-v] Fix ticket spinlock stalls under SMP contention
Eight-core boot on SpacemiT K1 could stall with one CPU in the
unlock LR/SC loop, two CPUs allocating tickets, and the remaining
CPUs waiting for the scheduler lock.
Replace LR/SC ticket allocation with amoadd.w.aqrl and release the
lock with a halfword owner update. Order critical-section accesses
after acquiring ownership and before releasing it.
Verified eight-core boot on K1 with temporary diagnostics and
reservation-clearing changes removed.
Signed-off-by: GuEe-GUI <[email protected]>
| -rw-r--r-- | libcpu/risc-v/common/cpuport.c | 48 |
1 files changed, 19 insertions, 29 deletions
diff --git a/libcpu/risc-v/common/cpuport.c b/libcpu/risc-v/common/cpuport.c index d06134cf57..1135cdcdc4 100644 --- a/libcpu/risc-v/common/cpuport.c +++ b/libcpu/risc-v/common/cpuport.c @@ -70,22 +70,18 @@ void rt_hw_spin_lock_init(rt_hw_spinlock_t *lock) void rt_hw_spin_lock(rt_hw_spinlock_t *lock) { - unsigned long tmp, new_val, mask; + unsigned long tmp; unsigned short ticket; + /* + * The two 16-bit counters occupy the low word, also on RV64. + * Use an AMO so ticket allocation cannot livelock with an unlock + * retrying LR/SC on the same reservation. Word width wraps next at 16 bits. + */ __asm__ volatile ( - "1:\n\t" - " lr." REG_WIDTH ".aq %0, (%3)\n\t" - " srli %1, %0, 16\n\t" - " addi %1, %1, 1\n\t" - " slli %1, %1, 16\n\t" - " li %2, 0xffff\n\t" - " and %2, %0, %2\n\t" - " or %1, %1, %2\n\t" - " sc." REG_WIDTH ".rl %1, %1, (%3)\n\t" - " bnez %1, 1b" - : "=&r" (tmp), "=&r" (new_val), "=&r" (mask) - : "r" (&lock->slock) + "amoadd.w.aqrl %0, %2, (%1)" + : "=&r" (tmp) + : "r" (&lock->slock), "r" (1UL << 16) : "memory"); ticket = (tmp >> 16) & 0xffff; @@ -94,26 +90,20 @@ void rt_hw_spin_lock(rt_hw_spinlock_t *lock) { __asm__ volatile (OPC_PAUSE_I); } + + /* Order critical-section accesses after observing the owner's release. */ + rt_hw_barrier(); } void rt_hw_spin_unlock(rt_hw_spinlock_t *lock) { - unsigned long tmp, new_val, mask; - - __asm__ volatile ( - "1:\n\t" - " lr." REG_WIDTH ".aq %0, (%3)\n\t" - " addi %1, %0, 1\n\t" - " li %2, 0xffff\n\t" - " and %1, %1, %2\n\t" - " slli %2, %2, 16\n\t" - " and %0, %0, %2\n\t" - " or %1, %0, %1\n\t" - " sc." REG_WIDTH ".rl %1, %1, (%3)\n\t" - " bnez %1, 1b" - : "=&r" (tmp), "=&r" (new_val), "=&r" (mask) - : "r" (&lock->slock) - : "memory"); + /* + * Only the holder writes owner. Publish critical-section accesses before + * advancing it; a halfword store preserves concurrently allocated tickets. + */ + rt_hw_barrier(); + HWREG16(&lock->tickets.owner) = + (unsigned short)(HWREG16(&lock->tickets.owner) + 1); rt_hw_barrier(); } |
