mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-21 23:20:20 +00:00
Kernel/Net: Implement TCP_NODELAY
This commit is contained in:
parent
38a368c8f6
commit
61ac554a34
Notes:
sideshowbarker
2024-07-17 08:36:27 +09:00
Author: https://github.com/romch007 Commit: https://github.com/SerenityOS/serenity/commit/61ac554a34 Pull-request: https://github.com/SerenityOS/serenity/pull/21771
5 changed files with 84 additions and 12 deletions
18
Kernel/API/POSIX/netinet/tcp.h
Normal file
18
Kernel/API/POSIX/netinet/tcp.h
Normal 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
|
|
@ -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 (Nagle’s 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 (Nagle’s 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());
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -6,5 +6,4 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#define TCP_NODELAY 10
|
||||
#define TCP_MAXSEG 11
|
||||
#include <Kernel/API/POSIX/netinet/tcp.h>
|
||||
|
|
Loading…
Reference in a new issue