Fix very subtle spinlock race condition due to atomics misuse

This commit is contained in:
2025-11-02 02:17:03 +01:00
parent 9ccc6751e3
commit e52b33671b

View File

@ -5,17 +5,15 @@
#include "hal/hal.h" #include "hal/hal.h"
#include "kprintf.h" #include "kprintf.h"
#define SPINLOCK_IRQ_DBG 1
#define SPINLOCK_HINT() asm volatile("pause") #define SPINLOCK_HINT() asm volatile("pause")
struct { struct {
atomic_uint64_t irq_flags; uint64_t irq_flags;
atomic_int irq_nest; atomic_int irq_nest;
} IRQ_CTX; } IRQ_CTX = {0};
void spinlock_init(SpinLock *sl) { void spinlock_init(SpinLock *sl) {
atomic_store(&sl->lock, false); atomic_clear_flag_explicit(&sl->lock, memory_order_relaxed);
} }
uint64_t irqsave(void) { uint64_t irqsave(void) {
@ -25,37 +23,22 @@ uint64_t irqsave(void) {
} }
void irqrestore(uint64_t flags) { void irqrestore(uint64_t flags) {
if (flags & (1<<9)) { if (flags & (1ULL << 9)) {
asm volatile("sti" ::: "memory", "cc"); asm volatile("sti" ::: "memory", "cc");
} }
} }
void irqsave_nested(void) { void irqsave_nested(void) {
if (atomic_load(&IRQ_CTX.irq_nest) == 0) { int prev = atomic_fetch_add_explicit(&IRQ_CTX.irq_nest, 1, memory_order_acq_rel);
atomic_store(&IRQ_CTX.irq_flags, irqsave()); if (prev == 0) {
IRQ_CTX.irq_flags = irqsave();
} }
atomic_inc(&IRQ_CTX.irq_nest);
#if SPINLOCK_IRQ_DBG
if (atomic_load(&IRQ_CTX.irq_nest) < 0) {
kprintf("IRQ_CTX.irq_nest underflow\n");
hal_hang();
}
#endif
} }
void irqrestore_nested(void) { void irqrestore_nested(void) {
#if SPINLOCK_IRQ_DBG int prev = atomic_fetch_sub_explicit(&IRQ_CTX.irq_nest, 1, memory_order_acq_rel);
int irq_nest = atomic_load(&IRQ_CTX.irq_nest); if (prev == 1) {
if (irq_nest <= 0) { irqrestore(IRQ_CTX.irq_flags);
kprintf("spinlock: irqs restored too many times: %d\n", irq_nest);
hal_hang();
}
#endif
atomic_dec(&IRQ_CTX.irq_nest);
if (atomic_load(&IRQ_CTX.irq_nest) == 0) {
irqrestore(atomic_load(&IRQ_CTX.irq_flags));
} }
} }