Kernel: Rewrite the whole scheduler and re-architecture SMP handling

Change Semaphore -> ThreadBlocker
  This was not a semaphore, I just named it one because I didn't know
  what semaphore was. I have meant to change this sooner, but it was in
  no way urgent :D

Implement SMP events. Processors can now be sent SMP events through
IPIs. SMP events can be sent either to a single processor or broadcasted
to every processor.

PageTable::{map_page,map_range,unmap_page,unmap_range}() now send SMP
event to invalidate TLB caches for the changed pages.

Scheduler no longer uses a global run queue. Each processor has its own
scheduler that keeps track of the load on the processor. Once every
second schedulers do load balancing. Schedulers have no access to other
processors' schedulers, they just see approximate loads. If scheduler
decides that it has too much load, it will send a thread to another
processor through a SMP event.

Schedulers are currently run using the timer interrupt on BSB. This
should be not the case, and each processor should use its LAPIC timer
for interrupts. There is no reason to broadcast SMP event to all
processors when BSB gets timer interrupt.

Old scheduler only achieved 20% idle load on qemu. That was probably a
very inefficient implementation. This new scheduler seems to average
around 1% idle load. This is much closer to what I would expect. On my
own laptop idle load seems to be only around 0.5% on each processor.
This commit is contained in:
2024-07-22 00:33:50 +03:00
parent 9f90eeab05
commit f8261c60c0
60 changed files with 1559 additions and 715 deletions

View File

@@ -79,7 +79,7 @@ namespace Kernel
if (it != m_arp_table.end())
return it->value;
}
Scheduler::get().yield();
Processor::yield();
}
return BAN::Error::from_errno(ETIMEDOUT);
@@ -150,7 +150,7 @@ namespace Kernel
while (m_pending_packets.empty())
{
m_pending_lock.unlock(state);
m_pending_semaphore.block_indefinite();
m_pending_thread_blocker.block_indefinite();
state = m_pending_lock.lock();
}
auto packet = m_pending_packets.front();
@@ -178,7 +178,7 @@ namespace Kernel
}
m_pending_packets.push({ .interface = interface, .packet = arp_packet });
m_pending_semaphore.unblock();
m_pending_thread_blocker.unblock();
}
}

View File

@@ -308,7 +308,7 @@ namespace Kernel
while (m_pending_packets.empty())
{
m_pending_lock.unlock(state);
m_pending_semaphore.block_indefinite();
m_pending_thread_blocker.block_indefinite();
state = m_pending_lock.lock();
}
auto packet = m_pending_packets.front();
@@ -367,7 +367,7 @@ namespace Kernel
m_pending_total_size += ipv4_header.total_length;
m_pending_packets.push({ .interface = interface });
m_pending_semaphore.unblock();
m_pending_thread_blocker.unblock();
}
}

View File

@@ -75,7 +75,7 @@ namespace Kernel
while (m_pending_connections.empty())
{
LockFreeGuard _(m_mutex);
TRY(Thread::current().block_or_eintr_indefinite(m_semaphore));
TRY(Thread::current().block_or_eintr_indefinite(m_thread_blocker));
}
auto connection = m_pending_connections.front();
@@ -113,7 +113,7 @@ namespace Kernel
if (SystemTimer::get().ms_since_boot() >= wake_time_ms)
return BAN::Error::from_errno(ECONNABORTED);
LockFreeGuard free(m_mutex);
TRY(Thread::current().block_or_eintr_or_waketime(return_inode->m_semaphore, wake_time_ms, true));
TRY(Thread::current().block_or_eintr_or_waketime_ms(return_inode->m_thread_blocker, wake_time_ms, true));
}
if (address)
@@ -170,7 +170,7 @@ namespace Kernel
if (SystemTimer::get().ms_since_boot() >= wake_time_ms)
return BAN::Error::from_errno(ECONNREFUSED);
LockFreeGuard free(m_mutex);
TRY(Thread::current().block_or_eintr_or_waketime(m_semaphore, wake_time_ms, true));
TRY(Thread::current().block_or_eintr_or_waketime_ms(m_thread_blocker, wake_time_ms, true));
}
return {};
@@ -207,7 +207,7 @@ namespace Kernel
if (m_state != State::Established)
return return_with_maybe_zero();
LockFreeGuard free(m_mutex);
TRY(Thread::current().block_or_eintr_indefinite(m_semaphore));
TRY(Thread::current().block_or_eintr_indefinite(m_thread_blocker));
}
const uint32_t to_recv = BAN::Math::min<uint32_t>(buffer.size(), m_recv_window.data_size);
@@ -249,7 +249,7 @@ namespace Kernel
if (m_send_window.data_size + message.size() <= m_send_window.buffer->size())
break;
LockFreeGuard free(m_mutex);
TRY(Thread::current().block_or_eintr_indefinite(m_semaphore));
TRY(Thread::current().block_or_eintr_indefinite(m_thread_blocker));
}
{
@@ -259,14 +259,14 @@ namespace Kernel
}
const uint32_t target_ack = m_send_window.start_seq + m_send_window.data_size;
m_semaphore.unblock();
m_thread_blocker.unblock();
while (m_send_window.current_ack < target_ack)
{
if (m_state != State::Established)
return return_with_maybe_zero();
LockFreeGuard free(m_mutex);
TRY(Thread::current().block_or_eintr_indefinite(m_semaphore));
TRY(Thread::current().block_or_eintr_indefinite(m_thread_blocker));
}
return message.size();
@@ -597,7 +597,7 @@ namespace Kernel
}
}
m_semaphore.unblock();
m_thread_blocker.unblock();
}
void TCPSocket::set_connection_as_closed()
@@ -743,11 +743,11 @@ namespace Kernel
}
}
m_semaphore.unblock();
m_semaphore.block_with_wake_time(current_ms + retransmit_timeout_ms);
m_thread_blocker.unblock();
m_thread_blocker.block_with_wake_time_ms(current_ms + retransmit_timeout_ms);
}
m_semaphore.unblock();
m_thread_blocker.unblock();
}
}

View File

@@ -70,7 +70,7 @@ namespace Kernel
m_packets.emplace(packet_info);
m_packet_total_size += payload.size();
m_packet_semaphore.unblock();
m_packet_thread_blocker.unblock();
}
BAN::ErrorOr<void> UDPSocket::bind_impl(const sockaddr* address, socklen_t address_len)
@@ -93,7 +93,7 @@ namespace Kernel
while (m_packets.empty())
{
m_packet_lock.unlock(state);
TRY(Thread::current().block_or_eintr_indefinite(m_packet_semaphore));
TRY(Thread::current().block_or_eintr_indefinite(m_packet_thread_blocker));
state = m_packet_lock.lock();
}

View File

@@ -73,7 +73,7 @@ namespace Kernel
return BAN::Error::from_errno(EINVAL);
while (connection_info.pending_connections.empty())
TRY(Thread::current().block_or_eintr_indefinite(connection_info.pending_semaphore));
TRY(Thread::current().block_or_eintr_indefinite(connection_info.pending_thread_blocker));
BAN::RefPtr<UnixDomainSocket> pending;
@@ -81,7 +81,7 @@ namespace Kernel
SpinLockGuard _(connection_info.pending_lock);
pending = connection_info.pending_connections.front();
connection_info.pending_connections.pop();
connection_info.pending_semaphore.unblock();
connection_info.pending_thread_blocker.unblock();
}
BAN::RefPtr<UnixDomainSocket> return_inode;
@@ -162,15 +162,15 @@ namespace Kernel
if (target_info.pending_connections.size() < target_info.pending_connections.capacity())
{
MUST(target_info.pending_connections.push(this));
target_info.pending_semaphore.unblock();
target_info.pending_thread_blocker.unblock();
break;
}
}
TRY(Thread::current().block_or_eintr_indefinite(target_info.pending_semaphore));
TRY(Thread::current().block_or_eintr_indefinite(target_info.pending_thread_blocker));
}
while (!connection_info.connection_done)
Scheduler::get().yield();
Processor::yield();
return {};
}
@@ -241,7 +241,7 @@ namespace Kernel
while (m_packet_sizes.full() || m_packet_size_total + packet.size() > s_packet_buffer_size)
{
m_packet_lock.unlock(state);
TRY(Thread::current().block_or_eintr_indefinite(m_packet_semaphore));
TRY(Thread::current().block_or_eintr_indefinite(m_packet_thread_blocker));
state = m_packet_lock.lock();
}
@@ -252,7 +252,7 @@ namespace Kernel
if (!is_streaming())
m_packet_sizes.push(packet.size());
m_packet_semaphore.unblock();
m_packet_thread_blocker.unblock();
m_packet_lock.unlock(state);
return {};
}
@@ -357,7 +357,7 @@ namespace Kernel
while (m_packet_size_total == 0)
{
m_packet_lock.unlock(state);
TRY(Thread::current().block_or_eintr_indefinite(m_packet_semaphore));
TRY(Thread::current().block_or_eintr_indefinite(m_packet_thread_blocker));
state = m_packet_lock.lock();
}
@@ -376,7 +376,7 @@ namespace Kernel
memmove(packet_buffer, packet_buffer + nread, m_packet_size_total - nread);
m_packet_size_total -= nread;
m_packet_semaphore.unblock();
m_packet_thread_blocker.unblock();
m_packet_lock.unlock(state);
return nread;