Kernel: Rework spinlock usage when blocking the current thread

The old SpinLockAsMutex was pretty confusing first of all. It also was
causing the issue thats been around for maybe a year now which is the
only consistently happening kernel panic. I've been pretty confused
about this and finally figured out what was causing this.

The main issue was that we were accidentally enabling interrupts when
blocking a thread that passed SpinLockAsMutex from normally interrupt
enabled context. This led to receiving IPI for thread unblock while
we were actively blocking the thread. I'm very suprized this had't
caused any more serious issues than occasional kernel panics :^)
This commit is contained in:
2026-07-20 04:07:23 +03:00
parent edc95e1c09
commit 5329ec5912
21 changed files with 105 additions and 133 deletions
@@ -0,0 +1,41 @@
#pragma once
#include <kernel/Lock/SpinLock.h>
#include <kernel/Lock/Mutex.h>
namespace Kernel
{
// FIXME: These classes are HACKS to allow passing spinlock
// to unblock functions. Write a better API that either
// allows passing spinlocks or do something cleaner that
// whatever shit this is
template<typename Lock> requires requires (Lock& lock) { lock.lock(); lock.unlock(InterruptState::Disabled); lock.current_processor_has_lock(); }
class BlockableSpinLock : public BaseMutex
{
public:
BlockableSpinLock(Lock& lock)
: m_lock(lock)
{
ASSERT(m_lock.current_processor_has_lock());
}
void lock() override
{
m_lock.lock();
}
void unlock() override
{
m_lock.unlock(InterruptState::Disabled);
}
uint32_t lock_depth() const override { return m_lock.lock_depth(); }
bool is_locked_by_current_thread() const override { return m_lock.current_processor_has_lock(); }
private:
Lock& m_lock;
};
}
+9 -11
View File
@@ -13,12 +13,10 @@ namespace Kernel
{
public:
virtual void lock() = 0;
virtual bool try_lock() = 0;
virtual void unlock() = 0;
virtual pid_t locker() const = 0;
virtual bool is_locked() const = 0;
virtual uint32_t lock_depth() const = 0;
virtual bool is_locked_by_current_thread() const = 0;
};
class Mutex final : public BaseMutex
@@ -51,7 +49,7 @@ namespace Kernel
m_lock_depth++;
}
bool try_lock() override
bool try_lock()
{
const auto tid = Thread::current_tid();
if (tid == m_locker)
@@ -82,10 +80,10 @@ namespace Kernel
}
}
pid_t locker() const override { return m_locker; }
bool is_locked() const override { return m_locker != -1; }
pid_t locker() const { return m_locker; }
bool is_locked() const { return m_locker != -1; }
uint32_t lock_depth() const override { return m_lock_depth; }
bool is_locked_by_current_thread() const { return m_locker == Thread::current_tid(); }
bool is_locked_by_current_thread() const override { return m_locker == Thread::current_tid(); }
private:
BAN::Atomic<pid_t> m_locker { -1 };
@@ -126,7 +124,7 @@ namespace Kernel
m_lock_depth++;
}
bool try_lock() override
bool try_lock()
{
const auto tid = Thread::current_tid();
@@ -164,10 +162,10 @@ namespace Kernel
}
}
pid_t locker() const override { return m_locker; }
bool is_locked() const override { return m_locker != -1; }
pid_t locker() const { return m_locker; }
bool is_locked() const { return m_locker != -1; }
uint32_t lock_depth() const override { return m_lock_depth; }
bool is_locked_by_current_thread() const { return m_locker == Thread::current_tid(); }
bool is_locked_by_current_thread() const override { return m_locker == Thread::current_tid(); }
private:
BAN::Atomic<pid_t> m_locker { -1 };
+5 -5
View File
@@ -1,7 +1,7 @@
#pragma once
#include <kernel/Lock/BlockableSpinLock.h>
#include <kernel/Lock/SpinLock.h>
#include <kernel/Lock/SpinLockAsMutex.h>
namespace Kernel
{
@@ -18,8 +18,8 @@ namespace Kernel
SpinLockGuard _(m_lock);
while (m_writers_waiting > 0 || m_writer != -1)
{
SpinLockGuardAsMutex smutex(_);
m_thread_blocker.block_indefinite(&smutex);
BlockableSpinLock block(m_lock);
m_thread_blocker.block_indefinite(&block);
}
m_readers_active++;
}
@@ -44,8 +44,8 @@ namespace Kernel
m_writers_waiting++;
while (m_readers_active > 0 || m_writer != -1)
{
SpinLockGuardAsMutex smutex(_);
m_thread_blocker.block_indefinite(&smutex);
BlockableSpinLock block(m_lock);
m_thread_blocker.block_indefinite(&block);
}
m_writers_waiting--;
@@ -1,64 +0,0 @@
#pragma once
#include <kernel/Lock/SpinLock.h>
#include <kernel/Lock/Mutex.h>
namespace Kernel
{
// FIXME: These classes are HACKS to allow passing spinlock
// to unblock functions. Write a better API that either
// allows passing spinlocks or do something cleaner that
// whatever shit this is
template<typename Lock>
class SpinLockAsMutex : public BaseMutex
{
public:
SpinLockAsMutex(Lock& lock, InterruptState state)
: m_lock(lock)
, m_lock_depth(lock.lock_depth())
, m_state(state)
, m_locker(Thread::current_tid())
{
ASSERT(m_lock.current_processor_has_lock());
}
void lock() override
{
m_lock.lock();
m_lock_depth++;
}
bool try_lock() override
{
lock();
return true;
}
void unlock() override
{
m_lock.unlock(--m_lock_depth ? InterruptState::Disabled : m_state);
}
pid_t locker() const override { return is_locked() ? m_locker : -1; }
bool is_locked() const override { return m_lock_depth; }
uint32_t lock_depth() const override { return m_lock_depth; }
private:
Lock& m_lock;
uint32_t m_lock_depth { 0 };
InterruptState m_state;
const pid_t m_locker;
};
template<typename Lock>
class SpinLockGuardAsMutex : public SpinLockAsMutex<Lock>
{
public:
SpinLockGuardAsMutex(SpinLockGuard<Lock>& guard)
: SpinLockAsMutex<Lock>(guard.m_lock, guard.m_state)
{}
};
}