Kernel: Optimize networking code

Remove buffering from network layer and rework loopback interface.
loopback now has a separate recieve thread to allow concurrent sends and
prevent deadlocks
This commit is contained in:
2026-02-27 19:08:08 +02:00
parent ff378e4538
commit 9ddf19f605
20 changed files with 563 additions and 476 deletions

View File

@@ -7,6 +7,9 @@
namespace Kernel
{
// each buffer is 7440 bytes + padding = 8192
constexpr size_t s_buffer_size = 8192;
bool RTL8169::probe(PCI::Device& pci_device)
{
if (pci_device.vendor_id() != 0x10ec)
@@ -68,9 +71,28 @@ namespace Kernel
// lock config registers
m_io_bar_region->write8(RTL8169_IO_9346CR, RTL8169_9346CR_MODE_NORMAL);
auto* thread = TRY(Thread::create_kernel([](void* rtl8169_ptr) {
static_cast<RTL8169*>(rtl8169_ptr)->receive_thread();
}, this));
if (auto ret = Processor::scheduler().add_thread(thread); ret.is_error())
{
delete thread;
return ret.release_error();
}
m_thread_is_dead = false;
return {};
}
RTL8169::~RTL8169()
{
m_thread_should_die = true;
m_thread_blocker.unblock();
while (!m_thread_is_dead)
Processor::yield();
}
BAN::ErrorOr<void> RTL8169::reset()
{
m_io_bar_region->write8(RTL8169_IO_CR, RTL8169_CR_RST);
@@ -85,15 +107,12 @@ namespace Kernel
BAN::ErrorOr<void> RTL8169::initialize_rx()
{
// each buffer is 7440 bytes + padding = 8192
constexpr size_t buffer_size = 2 * PAGE_SIZE;
m_rx_buffer_region = TRY(DMARegion::create(m_rx_descriptor_count * buffer_size));
m_rx_buffer_region = TRY(DMARegion::create(m_rx_descriptor_count * s_buffer_size));
m_rx_descriptor_region = TRY(DMARegion::create(m_rx_descriptor_count * sizeof(RTL8169Descriptor)));
for (size_t i = 0; i < m_rx_descriptor_count; i++)
{
const paddr_t rx_buffer_paddr = m_rx_buffer_region->paddr() + i * buffer_size;
const paddr_t rx_buffer_paddr = m_rx_buffer_region->paddr() + i * s_buffer_size;
uint32_t command = 0x1FF8 | RTL8169_DESC_CMD_OWN;
if (i == m_rx_descriptor_count - 1)
@@ -120,21 +139,17 @@ namespace Kernel
// configure max rx packet size
m_io_bar_region->write16(RTL8169_IO_RMS, RTL8169_RMS_MAX);
return {};
}
BAN::ErrorOr<void> RTL8169::initialize_tx()
{
// each buffer is 7440 bytes + padding = 8192
constexpr size_t buffer_size = 2 * PAGE_SIZE;
m_tx_buffer_region = TRY(DMARegion::create(m_tx_descriptor_count * buffer_size));
m_tx_buffer_region = TRY(DMARegion::create(m_tx_descriptor_count * s_buffer_size));
m_tx_descriptor_region = TRY(DMARegion::create(m_tx_descriptor_count * sizeof(RTL8169Descriptor)));
for (size_t i = 0; i < m_tx_descriptor_count; i++)
{
const paddr_t tx_buffer_paddr = m_tx_buffer_region->paddr() + i * buffer_size;
const paddr_t tx_buffer_paddr = m_tx_buffer_region->paddr() + i * s_buffer_size;
uint32_t command = 0;
if (i == m_tx_descriptor_count - 1)
@@ -194,14 +209,8 @@ namespace Kernel
return 0;
}
BAN::ErrorOr<void> RTL8169::send_bytes(BAN::MACAddress destination, EtherType protocol, BAN::ConstByteSpan buffer)
BAN::ErrorOr<void> RTL8169::send_bytes(BAN::MACAddress destination, EtherType protocol, BAN::Span<const BAN::ConstByteSpan> payload)
{
constexpr size_t buffer_size = 8192;
const uint16_t packet_size = sizeof(EthernetHeader) + buffer.size();
if (packet_size > buffer_size)
return BAN::Error::from_errno(EINVAL);
if (!link_up())
return BAN::Error::from_errno(EADDRNOTAVAIL);
@@ -219,14 +228,20 @@ namespace Kernel
m_lock.unlock(state);
auto* tx_buffer = reinterpret_cast<uint8_t*>(m_tx_buffer_region->vaddr() + tx_current * buffer_size);
auto* tx_buffer = reinterpret_cast<uint8_t*>(m_tx_buffer_region->vaddr() + tx_current * s_buffer_size);
// write packet
auto& ethernet_header = *reinterpret_cast<EthernetHeader*>(tx_buffer);
ethernet_header.dst_mac = destination;
ethernet_header.src_mac = get_mac_address();
ethernet_header.ether_type = protocol;
memcpy(tx_buffer + sizeof(EthernetHeader), buffer.data(), buffer.size());
size_t packet_size = sizeof(EthernetHeader);
for (const auto& buffer : payload)
{
memcpy(tx_buffer + packet_size, buffer.data(), buffer.size());
packet_size += buffer.size();
}
// give packet ownership to NIC
uint32_t command = packet_size | RTL8169_DESC_CMD_OWN | RTL8169_DESC_CMD_LS | RTL8169_DESC_CMD_FS;
@@ -240,6 +255,50 @@ namespace Kernel
return {};
}
void RTL8169::receive_thread()
{
SpinLockGuard _(m_lock);
while (!m_thread_should_die)
{
for (;;)
{
auto& descriptor = reinterpret_cast<volatile RTL8169Descriptor*>(m_rx_descriptor_region->vaddr())[m_rx_current];
if (descriptor.command & RTL8169_DESC_CMD_OWN)
break;
// packet buffer can only hold single packet, so we should not receive any multi-descriptor packets
ASSERT((descriptor.command & RTL8169_DESC_CMD_LS) && (descriptor.command & RTL8169_DESC_CMD_FS));
const uint16_t packet_length = descriptor.command & 0x3FFF;
if (packet_length > s_buffer_size)
dwarnln("Got {} bytes to {} byte buffer", packet_length, s_buffer_size);
else if (descriptor.command & (1u << 21))
; // descriptor has an error
else
{
m_lock.unlock(InterruptState::Enabled);
NetworkManager::get().on_receive(*this, BAN::ConstByteSpan {
reinterpret_cast<const uint8_t*>(m_rx_buffer_region->vaddr() + m_rx_current * s_buffer_size),
packet_length
});
m_lock.lock();
}
m_rx_current = (m_rx_current + 1) % m_rx_descriptor_count;
descriptor.command = descriptor.command | RTL8169_DESC_CMD_OWN;
}
SpinLockAsMutex smutex(m_lock, InterruptState::Enabled);
m_thread_blocker.block_indefinite(&smutex);
}
m_thread_is_dead = true;
}
void RTL8169::handle_irq()
{
const uint16_t interrupt_status = m_io_bar_region->read16(RTL8169_IO_ISR);
@@ -251,7 +310,7 @@ namespace Kernel
dprintln("link status -> {}", m_link_up.load());
}
if (interrupt_status & RTL8169_IR_TOK)
if (interrupt_status & (RTL8169_IR_TOK | RTL8169_IR_ROK))
{
SpinLockGuard _(m_lock);
m_thread_blocker.unblock();
@@ -266,38 +325,6 @@ namespace Kernel
if (interrupt_status & RTL8169_IR_FVOW)
dwarnln("Rx FIFO overflow");
// dont log TDU is sent after each sent packet
if (!(interrupt_status & RTL8169_IR_ROK))
return;
constexpr size_t buffer_size = 8192;
for (;;)
{
auto& descriptor = reinterpret_cast<volatile RTL8169Descriptor*>(m_rx_descriptor_region->vaddr())[m_rx_current];
if (descriptor.command & RTL8169_DESC_CMD_OWN)
break;
// packet buffer can only hold single packet, so we should not receive any multi-descriptor packets
ASSERT((descriptor.command & RTL8169_DESC_CMD_LS) && (descriptor.command & RTL8169_DESC_CMD_FS));
const uint16_t packet_length = descriptor.command & 0x3FFF;
if (packet_length > buffer_size)
dwarnln("Got {} bytes to {} byte buffer", packet_length, buffer_size);
else if (descriptor.command & (1u << 21))
; // descriptor has an error
else
{
NetworkManager::get().on_receive(*this, BAN::ConstByteSpan {
reinterpret_cast<const uint8_t*>(m_rx_buffer_region->vaddr() + m_rx_current * buffer_size),
packet_length
});
}
m_rx_current = (m_rx_current + 1) % m_rx_descriptor_count;
descriptor.command = descriptor.command | RTL8169_DESC_CMD_OWN;
}
}
}