Kernel/Net: Implement TCP_NODELAY

This commit is contained in:
Romain Chardiny 2023-11-04 15:08:25 +01:00 committed by Andreas Kling
parent 38a368c8f6
commit 61ac554a34
Notes: sideshowbarker 2024-07-17 08:36:27 +09:00
5 changed files with 84 additions and 12 deletions

View file

@ -0,0 +1,18 @@
/*
* Copyright (c) 2023, Romain Chardiny <romain.chardiny@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#define TCP_NODELAY 10
#define TCP_MAXSEG 11
#ifdef __cplusplus
}
#endif

View file

@ -211,16 +211,17 @@ ErrorOr<size_t> TCPSocket::protocol_send(UserOrKernelBuffer const& data, size_t
return set_so_error(EHOSTUNREACH);
size_t mss = routing_decision.adapter->mtu() - sizeof(IPv4Packet) - sizeof(TCPPacket);
// RFC 896 (Nagles algorithm): https://www.ietf.org/rfc/rfc0896
// "The solution is to inhibit the sending of new TCP segments when
// new outgoing data arrives from the user if any previously
// transmitted data on the connection remains unacknowledged. This
// inhibition is to be unconditional; no timers, tests for size of
// data received, or other conditions are required."
// FIXME: Make this configurable via TCP_NODELAY.
auto has_unacked_data = m_unacked_packets.with_shared([&](auto const& packets) { return packets.size > 0; });
if (has_unacked_data && data_length < mss)
return set_so_error(EAGAIN);
if (!m_no_delay) {
// RFC 896 (Nagles algorithm): https://www.ietf.org/rfc/rfc0896
// "The solution is to inhibit the sending of new TCP segments when
// new outgoing data arrives from the user if any previously
// transmitted data on the connection remains unacknowledged. This
// inhibition is to be unconditional; no timers, tests for size of
// data received, or other conditions are required."
auto has_unacked_data = m_unacked_packets.with_shared([&](auto const& packets) { return packets.size > 0; });
if (has_unacked_data && data_length < mss)
return set_so_error(EAGAIN);
}
data_length = min(data_length, mss);
TRY(send_tcp_packet(TCPFlags::PSH | TCPFlags::ACK, &data, data_length, &routing_decision));
@ -427,6 +428,54 @@ NetworkOrdered<u16> TCPSocket::compute_tcp_checksum(IPv4Address const& source, I
return ~(checksum & 0xffff);
}
ErrorOr<void> TCPSocket::setsockopt(int level, int option, Userspace<void const*> user_value, socklen_t user_value_size)
{
if (level != IPPROTO_TCP)
return IPv4Socket::setsockopt(level, option, user_value, user_value_size);
MutexLocker locker(mutex());
switch (option) {
case TCP_NODELAY:
if (user_value_size < sizeof(int))
return EINVAL;
int value;
TRY(copy_from_user(&value, static_ptr_cast<int const*>(user_value)));
if (value != 0 && value != 1)
return EINVAL;
m_no_delay = value;
return {};
default:
dbgln("setsockopt({}) at IPPROTO_TCP not implemented.", option);
return ENOPROTOOPT;
}
}
ErrorOr<void> TCPSocket::getsockopt(OpenFileDescription& description, int level, int option, Userspace<void*> value, Userspace<socklen_t*> value_size)
{
if (level != IPPROTO_TCP)
return IPv4Socket::getsockopt(description, level, option, value, value_size);
MutexLocker locker(mutex());
socklen_t size;
TRY(copy_from_user(&size, value_size.unsafe_userspace_ptr()));
switch (option) {
case TCP_NODELAY: {
int nodelay = m_no_delay ? 1 : 0;
if (size < sizeof(nodelay))
return EINVAL;
TRY(copy_to_user(static_ptr_cast<int*>(value), &nodelay));
size = sizeof(nodelay);
return copy_to_user(value_size, &size);
}
default:
dbgln("getsockopt({}) at IPPROTO_TCP not implemented.", option);
return ENOPROTOOPT;
}
}
ErrorOr<void> TCPSocket::protocol_bind()
{
dbgln_if(TCP_SOCKET_DEBUG, "TCPSocket::protocol_bind(), local_port() is {}", local_port());

View file

@ -165,6 +165,9 @@ public:
static NetworkOrdered<u16> compute_tcp_checksum(IPv4Address const& source, IPv4Address const& destination, TCPPacket const&, u16 payload_size);
virtual ErrorOr<void> setsockopt(int level, int option, Userspace<void const*>, socklen_t) override;
virtual ErrorOr<void> getsockopt(OpenFileDescription&, int level, int option, Userspace<void*>, Userspace<socklen_t*>) override;
protected:
void set_direction(Direction direction) { m_direction = direction; }
@ -227,6 +230,8 @@ private:
// peer's advertised window size.
u32 m_send_window_size { 64 * KiB };
bool m_no_delay { false };
IntrusiveListNode<TCPSocket> m_retransmit_list_node;
public:

View file

@ -13,6 +13,7 @@
#include <Kernel/API/POSIX/net/if_arp.h>
#include <Kernel/API/POSIX/net/route.h>
#include <Kernel/API/POSIX/netinet/in.h>
#include <Kernel/API/POSIX/netinet/tcp.h>
#include <Kernel/API/POSIX/poll.h>
#include <Kernel/API/POSIX/sched.h>
#include <Kernel/API/POSIX/serenity.h>

View file

@ -6,5 +6,4 @@
#pragma once
#define TCP_NODELAY 10
#define TCP_MAXSEG 11
#include <Kernel/API/POSIX/netinet/tcp.h>