summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGuEe-GUI <[email protected]>2026-10-20 15:58:07 +0800
committerRbb666 <[email protected]>2026-10-22 17:59:29 +0800
commit2a4e78d2321cff9bc2ad2d41f5c18cfe5e82595b (patch)
tree776e4c104e04b7bd586fa0f614a4b39fcbe5c49b
parentdff75f072fe2b047325a312d6b944dd3acce8676 (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.c48
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();
}