Kernel: Make TTY write lock a spinlock

I don't really like this but its needed with the current architecture to
allow printing while interrupts are disabled. I was hitting a dead lock
on few real machines
This commit is contained in:
2026-08-23 14:21:27 +03:00
parent 315d8895a7
commit 74d7a48c4b
5 changed files with 71 additions and 65 deletions
+2 -2
View File
@@ -100,9 +100,9 @@ namespace Kernel
termios m_termios; termios m_termios;
protected: protected:
Mutex m_mutex; Mutex m_input_mutex;
Mutex m_write_lock; RecursiveSpinLock m_write_lock;
ThreadBlocker m_write_blocker; ThreadBlocker m_write_blocker;
}; };
@@ -21,6 +21,8 @@ namespace Kernel
void clear() override; void clear() override;
BAN::ErrorOr<void> set_terminal_driver(BAN::RefPtr<TerminalDriver>);
protected: protected:
BAN::StringView name() const override { return m_name; } BAN::StringView name() const override { return m_name; }
bool putchar_impl(uint8_t ch) override; bool putchar_impl(uint8_t ch) override;
+1 -1
View File
@@ -146,7 +146,7 @@ namespace Kernel
auto slave = m_slave.lock(); auto slave = m_slave.lock();
if (!slave) if (!slave)
return BAN::Error::from_errno(EIO); return BAN::Error::from_errno(EIO);
LockGuard _(slave->m_mutex); LockGuard _(slave->m_input_mutex);
for (size_t i = 0; i < buffer.size(); i++) for (size_t i = 0; i < buffer.size(); i++)
slave->handle_input_byte(buffer[i]); slave->handle_input_byte(buffer[i]);
return buffer.size(); return buffer.size();
+17 -15
View File
@@ -6,6 +6,7 @@
#include <kernel/FS/DevFS/FileSystem.h> #include <kernel/FS/DevFS/FileSystem.h>
#include <kernel/FS/VirtualFileSystem.h> #include <kernel/FS/VirtualFileSystem.h>
#include <kernel/Input/InputDevice.h> #include <kernel/Input/InputDevice.h>
#include <kernel/Lock/BlockableSpinLock.h>
#include <kernel/Lock/LockGuard.h> #include <kernel/Lock/LockGuard.h>
#include <kernel/Process.h> #include <kernel/Process.h>
#include <kernel/Terminal/TTY.h> #include <kernel/Terminal/TTY.h>
@@ -220,7 +221,7 @@ namespace Kernel
if (ansi_c_str == nullptr) if (ansi_c_str == nullptr)
return; return;
LockGuard _(m_mutex); LockGuard _(m_input_mutex);
while (*ansi_c_str) while (*ansi_c_str)
handle_input_byte(*ansi_c_str++); handle_input_byte(*ansi_c_str++);
after_write(); after_write();
@@ -269,7 +270,7 @@ namespace Kernel
bool should_flush = false; bool should_flush = false;
bool force_echo = false; bool force_echo = false;
LockGuard _(m_mutex); LockGuard _(m_input_mutex);
if (!(termios.c_lflag & ICANON)) if (!(termios.c_lflag & ICANON))
should_flush = true; should_flush = true;
@@ -377,7 +378,8 @@ namespace Kernel
const auto termios = get_termios(); const auto termios = get_termios();
LockGuard _(m_write_lock); SpinLockGuard _(m_write_lock);
if (termios.c_oflag & OPOST) if (termios.c_oflag & OPOST)
{ {
if ((termios.c_oflag & ONLCR) && ch == NL) if ((termios.c_oflag & ONLCR) && ch == NL)
@@ -385,14 +387,16 @@ namespace Kernel
if ((termios.c_oflag & OCRNL) && ch == CR) if ((termios.c_oflag & OCRNL) && ch == CR)
return putchar_impl(NL); return putchar_impl(NL);
} }
return putchar_impl(ch); return putchar_impl(ch);
} }
BAN::ErrorOr<size_t> TTY::read_impl(off_t, BAN::ByteSpan buffer) BAN::ErrorOr<size_t> TTY::read_impl(off_t, BAN::ByteSpan buffer)
{ {
LockGuard _(m_mutex); LockGuard _(m_input_mutex);
while (!m_output.flush) while (!m_output.flush)
TRY(Thread::current().block_or_eintr_indefinite(m_output.thread_blocker, &m_mutex)); TRY(Thread::current().block_or_eintr_indefinite(m_output.thread_blocker, &m_input_mutex));
if (m_output.buffer->empty()) if (m_output.buffer->empty())
{ {
@@ -424,13 +428,14 @@ namespace Kernel
BAN::ErrorOr<size_t> TTY::write_impl(off_t, BAN::ConstByteSpan buffer) BAN::ErrorOr<size_t> TTY::write_impl(off_t, BAN::ConstByteSpan buffer)
{ {
LockGuard write_guard(m_write_lock); SpinLockGuard _(m_write_lock);
while (!can_write()) while (!can_write())
{ {
if (master_has_closed()) if (master_has_closed())
return BAN::Error::from_errno(EIO); return BAN::Error::from_errno(EIO);
TRY(Thread::current().block_or_eintr_indefinite(m_write_blocker, &m_write_lock)); BlockableSpinLock block(m_write_lock);
TRY(Thread::current().block_or_eintr_indefinite(m_write_blocker, &block));
} }
size_t written = 0; size_t written = 0;
@@ -447,15 +452,12 @@ namespace Kernel
void TTY::putchar_current(uint8_t ch) void TTY::putchar_current(uint8_t ch)
{ {
ASSERT(s_tty); auto tty = s_tty;
ASSERT(tty);
while (!s_tty->m_write_lock.try_lock()) SpinLockGuard _(tty->m_write_lock);
Processor::pause(); tty->putchar(ch);
tty->after_write();
s_tty->putchar(ch);
s_tty->after_write();
s_tty->m_write_lock.unlock();
} }
bool TTY::is_initialized() bool TTY::is_initialized()
+49 -47
View File
@@ -50,17 +50,48 @@ namespace Kernel
, m_foreground(driver->palette()[15]) , m_foreground(driver->palette()[15])
, m_background(driver->palette()[0]) , m_background(driver->palette()[0])
{ {
m_width = m_terminal_driver->width(); MUST(set_terminal_driver(driver));
m_height = m_terminal_driver->height(); }
update_winsize(m_width, m_height);
m_buffer = new Cell[m_width * m_height]; BAN::ErrorOr<void> VirtualTTY::set_terminal_driver(BAN::RefPtr<TerminalDriver> terminal_driver)
ASSERT(m_buffer); {
const uint32_t new_width = terminal_driver->width();
const uint32_t new_height = terminal_driver->height();
SpinLockGuard _(m_write_lock);
if (m_width != new_width || m_height != new_height)
{
Cell* new_buffer = new Cell[new_width * new_height];
ASSERT(new_buffer);
for (uint32_t i = 0; i < new_width * m_height; i++)
new_buffer[i] = { .foreground = m_foreground, .background = m_background, .codepoint = ' ' };
for (uint32_t y = 0; y < BAN::Math::min<uint32_t>(m_height, new_height); y++)
for (uint32_t x = 0; x < BAN::Math::min<uint32_t>(m_width, new_width); x++)
new_buffer[y * new_width + x] = m_buffer[y * m_width + x];
delete[] m_buffer;
m_buffer = new_buffer;
m_width = new_width;
m_height = new_height;
}
m_terminal_driver = terminal_driver;
for (uint32_t y = 0; y < m_height; y++)
for (uint32_t x = 0; x < m_width; x++)
render_from_buffer(x, y);
update_winsize(new_width, new_height);
return {};
} }
void VirtualTTY::clear() void VirtualTTY::clear()
{ {
LockGuard _(m_write_lock); SpinLockGuard _(m_write_lock);
for (uint32_t i = 0; i < m_width * m_height; i++) for (uint32_t i = 0; i < m_width * m_height; i++)
m_buffer[i] = { .foreground = m_foreground, .background = m_background, .codepoint = ' ' }; m_buffer[i] = { .foreground = m_foreground, .background = m_background, .codepoint = ' ' };
m_terminal_driver->clear(m_background); m_terminal_driver->clear(m_background);
@@ -70,46 +101,14 @@ namespace Kernel
{ {
if (!m_terminal_driver->has_font()) if (!m_terminal_driver->has_font())
return BAN::Error::from_errno(EINVAL); return BAN::Error::from_errno(EINVAL);
TRY(m_terminal_driver->set_font(BAN::move(font)));
{ MUST(set_terminal_driver(m_terminal_driver));
LockGuard _(m_write_lock);
TRY(m_terminal_driver->set_font(BAN::move(font)));
uint32_t new_width = m_terminal_driver->width();
uint32_t new_height = m_terminal_driver->height();
if (m_width != new_width || m_height != new_height)
{
Cell* new_buffer = new Cell[new_width * new_height];
ASSERT(new_buffer);
for (uint32_t i = 0; i < new_width * m_height; i++)
new_buffer[i] = { .foreground = m_foreground, .background = m_background, .codepoint = ' ' };
for (uint32_t y = 0; y < BAN::Math::min<uint32_t>(m_height, new_height); y++)
for (uint32_t x = 0; x < BAN::Math::min<uint32_t>(m_width, new_width); x++)
new_buffer[y * new_width + x] = m_buffer[y * m_width + x];
delete[] m_buffer;
m_buffer = new_buffer;
m_width = new_width;
m_height = new_height;
}
for (uint32_t y = 0; y < m_height; y++)
for (uint32_t x = 0; x < m_width; x++)
render_from_buffer(x, y);
}
update_winsize(m_width, m_height);
return {}; return {};
} }
void VirtualTTY::reset_ansi() void VirtualTTY::reset_ansi()
{ {
ASSERT(m_write_lock.is_locked_by_current_thread()); ASSERT(m_write_lock.current_processor_has_lock());
m_ansi_state = { m_ansi_state = {
.nums = { -1, -1, -1, -1, -1 }, .nums = { -1, -1, -1, -1, -1 },
.index = 0, .index = 0,
@@ -120,7 +119,7 @@ namespace Kernel
void VirtualTTY::handle_ansi_csi_color(uint8_t value) void VirtualTTY::handle_ansi_csi_color(uint8_t value)
{ {
ASSERT(m_write_lock.is_locked_by_current_thread()); ASSERT(m_write_lock.current_processor_has_lock());
auto& palette = m_terminal_driver->palette(); auto& palette = m_terminal_driver->palette();
@@ -203,7 +202,8 @@ namespace Kernel
void VirtualTTY::handle_ansi_csi(uint8_t ch) void VirtualTTY::handle_ansi_csi(uint8_t ch)
{ {
ASSERT(m_write_lock.is_locked_by_current_thread()); ASSERT(m_write_lock.current_processor_has_lock());
switch (ch) switch (ch)
{ {
case '0' ... '9': case '0' ... '9':
@@ -445,7 +445,7 @@ namespace Kernel
void VirtualTTY::render_from_buffer(uint32_t x, uint32_t y) void VirtualTTY::render_from_buffer(uint32_t x, uint32_t y)
{ {
ASSERT(m_write_lock.is_locked_by_current_thread()); ASSERT(m_write_lock.current_processor_has_lock());
ASSERT(x < m_width && y < m_height); ASSERT(x < m_width && y < m_height);
const auto& cell = m_buffer[y * m_width + x]; const auto& cell = m_buffer[y * m_width + x];
m_terminal_driver->putchar_at(cell.codepoint, x, y, cell.foreground, cell.background); m_terminal_driver->putchar_at(cell.codepoint, x, y, cell.foreground, cell.background);
@@ -453,7 +453,7 @@ namespace Kernel
void VirtualTTY::putchar_at(uint32_t codepoint, uint32_t x, uint32_t y) void VirtualTTY::putchar_at(uint32_t codepoint, uint32_t x, uint32_t y)
{ {
ASSERT(m_write_lock.is_locked_by_current_thread()); ASSERT(m_write_lock.current_processor_has_lock());
ASSERT(x < m_width && y < m_height); ASSERT(x < m_width && y < m_height);
auto& cell = m_buffer[y * m_width + x]; auto& cell = m_buffer[y * m_width + x];
cell.codepoint = codepoint; cell.codepoint = codepoint;
@@ -464,6 +464,8 @@ namespace Kernel
void VirtualTTY::scroll_if_needed() void VirtualTTY::scroll_if_needed()
{ {
ASSERT(m_write_lock.current_processor_has_lock());
while (m_row >= m_height) while (m_row >= m_height)
{ {
memmove(m_buffer, m_buffer + m_width, m_width * (m_height - 1) * sizeof(Cell)); memmove(m_buffer, m_buffer + m_width, m_width * (m_height - 1) * sizeof(Cell));
@@ -487,7 +489,7 @@ namespace Kernel
void VirtualTTY::putcodepoint(uint32_t codepoint) void VirtualTTY::putcodepoint(uint32_t codepoint)
{ {
ASSERT(m_write_lock.is_locked_by_current_thread()); ASSERT(m_write_lock.current_processor_has_lock());
switch (codepoint) switch (codepoint)
{ {
@@ -533,7 +535,7 @@ namespace Kernel
bool VirtualTTY::putchar_impl(uint8_t ch) bool VirtualTTY::putchar_impl(uint8_t ch)
{ {
ASSERT(m_write_lock.is_locked_by_current_thread()); ASSERT(m_write_lock.current_processor_has_lock());
uint32_t codepoint = ch; uint32_t codepoint = ch;