summaryrefslogtreecommitdiff
path: root/Kernel
diff options
context:
space:
mode:
authorNico Weber <thakis@chromium.org>2020-09-16 12:25:06 -0400
committerAndreas Kling <kling@serenityos.org>2020-09-17 17:23:01 +0200
commit416d470d07744592f186b65de7074f94edf39824 (patch)
tree880865f9037ae074a7e7caadf829af629a3cb33b /Kernel
parentb36a2d66865ae8205b6a7589b8566f27746002f4 (diff)
downloadserenity-416d470d07744592f186b65de7074f94edf39824.zip
Kernel: Plumb packet receive timestamp from NetworkAdapter to Socket::recvfrom
Since the receiving socket isn't yet known at packet receive time, keep timestamps for all packets. This is useful for keeping statistics about in-kernel queue latencies in the future, and it can be used to implement SO_TIMESTAMP.
Diffstat (limited to 'Kernel')
-rw-r--r--Kernel/Net/IPv4Socket.cpp12
-rw-r--r--Kernel/Net/IPv4Socket.h7
-rw-r--r--Kernel/Net/LocalSocket.cpp2
-rw-r--r--Kernel/Net/LocalSocket.h2
-rw-r--r--Kernel/Net/NetworkAdapter.cpp9
-rw-r--r--Kernel/Net/NetworkAdapter.h10
-rw-r--r--Kernel/Net/NetworkTask.cpp39
-rw-r--r--Kernel/Net/Socket.cpp3
-rw-r--r--Kernel/Net/Socket.h2
-rw-r--r--Kernel/Syscalls/socket.cpp3
10 files changed, 52 insertions, 37 deletions
diff --git a/Kernel/Net/IPv4Socket.cpp b/Kernel/Net/IPv4Socket.cpp
index c64c036c4d..2ff4b60001 100644
--- a/Kernel/Net/IPv4Socket.cpp
+++ b/Kernel/Net/IPv4Socket.cpp
@@ -280,7 +280,7 @@ KResultOr<size_t> IPv4Socket::receive_byte_buffered(FileDescription& description
return nreceived;
}
-KResultOr<size_t> IPv4Socket::receive_packet_buffered(FileDescription& description, UserOrKernelBuffer& buffer, size_t buffer_length, int flags, Userspace<sockaddr*> addr, Userspace<socklen_t*> addr_length)
+KResultOr<size_t> IPv4Socket::receive_packet_buffered(FileDescription& description, UserOrKernelBuffer& buffer, size_t buffer_length, int flags, Userspace<sockaddr*> addr, Userspace<socklen_t*> addr_length, timeval& packet_timestamp)
{
Locker locker(lock());
ReceivedPacket packet;
@@ -330,6 +330,8 @@ KResultOr<size_t> IPv4Socket::receive_packet_buffered(FileDescription& descripti
ASSERT(packet.data.has_value());
auto& ipv4_packet = *(const IPv4Packet*)(packet.data.value().data());
+ packet_timestamp = packet.timestamp;
+
if (addr) {
#ifdef IPV4_SOCKET_DEBUG
dbg() << "Incoming packet is from: " << packet.peer_address << ":" << packet.peer_port;
@@ -359,7 +361,7 @@ KResultOr<size_t> IPv4Socket::receive_packet_buffered(FileDescription& descripti
return protocol_receive(packet.data.value(), buffer, buffer_length, flags);
}
-KResultOr<size_t> IPv4Socket::recvfrom(FileDescription& description, UserOrKernelBuffer& buffer, size_t buffer_length, int flags, Userspace<sockaddr*> user_addr, Userspace<socklen_t*> user_addr_length)
+KResultOr<size_t> IPv4Socket::recvfrom(FileDescription& description, UserOrKernelBuffer& buffer, size_t buffer_length, int flags, Userspace<sockaddr*> user_addr, Userspace<socklen_t*> user_addr_length, timeval& packet_timestamp)
{
if (user_addr_length) {
socklen_t addr_length;
@@ -377,14 +379,14 @@ KResultOr<size_t> IPv4Socket::recvfrom(FileDescription& description, UserOrKerne
if (buffer_mode() == BufferMode::Bytes)
nreceived = receive_byte_buffered(description, buffer, buffer_length, flags, user_addr, user_addr_length);
else
- nreceived = receive_packet_buffered(description, buffer, buffer_length, flags, user_addr, user_addr_length);
+ nreceived = receive_packet_buffered(description, buffer, buffer_length, flags, user_addr, user_addr_length, packet_timestamp);
if (!nreceived.is_error())
Thread::current()->did_ipv4_socket_read(nreceived.value());
return nreceived;
}
-bool IPv4Socket::did_receive(const IPv4Address& source_address, u16 source_port, KBuffer&& packet)
+bool IPv4Socket::did_receive(const IPv4Address& source_address, u16 source_port, KBuffer&& packet, const timeval& packet_timestamp)
{
LOCKER(lock());
@@ -413,7 +415,7 @@ bool IPv4Socket::did_receive(const IPv4Address& source_address, u16 source_port,
dbg() << "IPv4Socket(" << this << "): did_receive refusing packet since queue is full.";
return false;
}
- m_receive_queue.append({ source_address, source_port, move(packet) });
+ m_receive_queue.append({ source_address, source_port, packet_timestamp, move(packet) });
m_can_read = true;
}
m_bytes_received += packet_size;
diff --git a/Kernel/Net/IPv4Socket.h b/Kernel/Net/IPv4Socket.h
index 675cfbfcd1..2bee154361 100644
--- a/Kernel/Net/IPv4Socket.h
+++ b/Kernel/Net/IPv4Socket.h
@@ -59,13 +59,13 @@ public:
virtual bool can_read(const FileDescription&, size_t) const override;
virtual bool can_write(const FileDescription&, size_t) const override;
virtual KResultOr<size_t> sendto(FileDescription&, const UserOrKernelBuffer&, size_t, int, Userspace<const sockaddr*>, socklen_t) override;
- virtual KResultOr<size_t> recvfrom(FileDescription&, UserOrKernelBuffer&, size_t, int flags, Userspace<sockaddr*>, Userspace<socklen_t*>) override;
+ virtual KResultOr<size_t> recvfrom(FileDescription&, UserOrKernelBuffer&, size_t, int flags, Userspace<sockaddr*>, Userspace<socklen_t*>, timeval&) override;
virtual KResult setsockopt(int level, int option, Userspace<const void*>, socklen_t) override;
virtual KResult getsockopt(FileDescription&, int level, int option, Userspace<void*>, Userspace<socklen_t*>) override;
virtual int ioctl(FileDescription&, unsigned request, FlatPtr arg) override;
- bool did_receive(const IPv4Address& peer_address, u16 peer_port, KBuffer&&);
+ bool did_receive(const IPv4Address& peer_address, u16 peer_port, KBuffer&&, const timeval&);
const IPv4Address& local_address() const { return m_local_address; }
u16 local_port() const { return m_local_port; }
@@ -111,7 +111,7 @@ private:
virtual bool is_ipv4() const override { return true; }
KResultOr<size_t> receive_byte_buffered(FileDescription&, UserOrKernelBuffer& buffer, size_t buffer_length, int flags, Userspace<sockaddr*>, Userspace<socklen_t*>);
- KResultOr<size_t> receive_packet_buffered(FileDescription&, UserOrKernelBuffer& buffer, size_t buffer_length, int flags, Userspace<sockaddr*>, Userspace<socklen_t*>);
+ KResultOr<size_t> receive_packet_buffered(FileDescription&, UserOrKernelBuffer& buffer, size_t buffer_length, int flags, Userspace<sockaddr*>, Userspace<socklen_t*>, timeval&);
IPv4Address m_local_address;
IPv4Address m_peer_address;
@@ -119,6 +119,7 @@ private:
struct ReceivedPacket {
IPv4Address peer_address;
u16 peer_port;
+ timeval timestamp;
Optional<KBuffer> data;
};
diff --git a/Kernel/Net/LocalSocket.cpp b/Kernel/Net/LocalSocket.cpp
index ee08ca0c26..0b4d4fc658 100644
--- a/Kernel/Net/LocalSocket.cpp
+++ b/Kernel/Net/LocalSocket.cpp
@@ -298,7 +298,7 @@ DoubleBuffer& LocalSocket::send_buffer_for(FileDescription& description)
ASSERT_NOT_REACHED();
}
-KResultOr<size_t> LocalSocket::recvfrom(FileDescription& description, UserOrKernelBuffer& buffer, size_t buffer_size, int, Userspace<sockaddr*>, Userspace<socklen_t*>)
+KResultOr<size_t> LocalSocket::recvfrom(FileDescription& description, UserOrKernelBuffer& buffer, size_t buffer_size, int, Userspace<sockaddr*>, Userspace<socklen_t*>, timeval&)
{
auto& buffer_for_me = receive_buffer_for(description);
if (!description.is_blocking()) {
diff --git a/Kernel/Net/LocalSocket.h b/Kernel/Net/LocalSocket.h
index d39e78d20b..30693283a3 100644
--- a/Kernel/Net/LocalSocket.h
+++ b/Kernel/Net/LocalSocket.h
@@ -61,7 +61,7 @@ public:
virtual bool can_read(const FileDescription&, size_t) const override;
virtual bool can_write(const FileDescription&, size_t) const override;
virtual KResultOr<size_t> sendto(FileDescription&, const UserOrKernelBuffer&, size_t, int, Userspace<const sockaddr*>, socklen_t) override;
- virtual KResultOr<size_t> recvfrom(FileDescription&, UserOrKernelBuffer&, size_t, int flags, Userspace<sockaddr*>, Userspace<socklen_t*>) override;
+ virtual KResultOr<size_t> recvfrom(FileDescription&, UserOrKernelBuffer&, size_t, int flags, Userspace<sockaddr*>, Userspace<socklen_t*>, timeval&) override;
virtual KResult getsockopt(FileDescription&, int level, int option, Userspace<void*>, Userspace<socklen_t*>) override;
virtual KResult chown(FileDescription&, uid_t, gid_t) override;
virtual KResult chmod(FileDescription&, mode_t) override;
diff --git a/Kernel/Net/NetworkAdapter.cpp b/Kernel/Net/NetworkAdapter.cpp
index 4a81fa77af..2ef8228982 100644
--- a/Kernel/Net/NetworkAdapter.cpp
+++ b/Kernel/Net/NetworkAdapter.cpp
@@ -33,6 +33,7 @@
#include <Kernel/Net/EthernetFrameHeader.h>
#include <Kernel/Net/LoopbackAdapter.h>
#include <Kernel/Net/NetworkAdapter.h>
+#include <Kernel/Process.h>
#include <Kernel/Random.h>
#include <Kernel/StdLib.h>
@@ -192,18 +193,20 @@ void NetworkAdapter::did_receive(ReadonlyBytes payload)
}
}
- m_packet_queue.append(buffer.value());
+ m_packet_queue.append({ buffer.value(), kgettimeofday() });
if (on_receive)
on_receive();
}
-size_t NetworkAdapter::dequeue_packet(u8* buffer, size_t buffer_size)
+size_t NetworkAdapter::dequeue_packet(u8* buffer, size_t buffer_size, timeval& packet_timestamp)
{
InterruptDisabler disabler;
if (m_packet_queue.is_empty())
return 0;
- auto packet = m_packet_queue.take_first();
+ auto packet_with_timestamp = m_packet_queue.take_first();
+ packet_timestamp = packet_with_timestamp.timestamp;
+ auto packet = move(packet_with_timestamp.packet);
size_t packet_size = packet.size();
ASSERT(packet_size <= buffer_size);
memcpy(buffer, packet.data(), packet_size);
diff --git a/Kernel/Net/NetworkAdapter.h b/Kernel/Net/NetworkAdapter.h
index 619b25f281..bab8c6eb1d 100644
--- a/Kernel/Net/NetworkAdapter.h
+++ b/Kernel/Net/NetworkAdapter.h
@@ -67,7 +67,7 @@ public:
int send_ipv4(const MACAddress&, const IPv4Address&, IPv4Protocol, const UserOrKernelBuffer& payload, size_t payload_size, u8 ttl);
int send_ipv4_fragmented(const MACAddress&, const IPv4Address&, IPv4Protocol, const UserOrKernelBuffer& payload, size_t payload_size, u8 ttl);
- size_t dequeue_packet(u8* buffer, size_t buffer_size);
+ size_t dequeue_packet(u8* buffer, size_t buffer_size, timeval& packet_timestamp);
bool has_queued_packets() const { return !m_packet_queue.is_empty(); }
@@ -93,7 +93,13 @@ private:
IPv4Address m_ipv4_address;
IPv4Address m_ipv4_netmask;
IPv4Address m_ipv4_gateway;
- SinglyLinkedList<KBuffer> m_packet_queue;
+
+ struct PacketWithTimestamp {
+ KBuffer packet;
+ timeval timestamp;
+ };
+
+ SinglyLinkedList<PacketWithTimestamp> m_packet_queue;
SinglyLinkedList<KBuffer> m_unused_packet_buffers;
size_t m_unused_packet_buffers_count { 0 };
String m_name;
diff --git a/Kernel/Net/NetworkTask.cpp b/Kernel/Net/NetworkTask.cpp
index 0098d686fd..ff2816ea12 100644
--- a/Kernel/Net/NetworkTask.cpp
+++ b/Kernel/Net/NetworkTask.cpp
@@ -52,10 +52,10 @@
namespace Kernel {
static void handle_arp(const EthernetFrameHeader&, size_t frame_size);
-static void handle_ipv4(const EthernetFrameHeader&, size_t frame_size);
-static void handle_icmp(const EthernetFrameHeader&, const IPv4Packet&);
-static void handle_udp(const IPv4Packet&);
-static void handle_tcp(const IPv4Packet&);
+static void handle_ipv4(const EthernetFrameHeader&, size_t frame_size, const timeval& packet_timestamp);
+static void handle_icmp(const EthernetFrameHeader&, const IPv4Packet&, const timeval& packet_timestamp);
+static void handle_udp(const IPv4Packet&, const timeval& packet_timestamp);
+static void handle_tcp(const IPv4Packet&, const timeval& packet_timestamp);
[[noreturn]] static void NetworkTask_main();
@@ -89,14 +89,14 @@ void NetworkTask_main()
};
});
- auto dequeue_packet = [&pending_packets](u8* buffer, size_t buffer_size) -> size_t {
+ auto dequeue_packet = [&pending_packets](u8* buffer, size_t buffer_size, timeval& packet_timestamp) -> size_t {
if (pending_packets == 0)
return 0;
size_t packet_size = 0;
NetworkAdapter::for_each([&](auto& adapter) {
if (packet_size || !adapter.has_queued_packets())
return;
- packet_size = adapter.dequeue_packet(buffer, buffer_size);
+ packet_size = adapter.dequeue_packet(buffer, buffer_size, packet_timestamp);
pending_packets--;
#ifdef NETWORK_TASK_DEBUG
klog() << "NetworkTask: Dequeued packet from " << adapter.name().characters() << " (" << packet_size << " bytes)";
@@ -108,10 +108,11 @@ void NetworkTask_main()
size_t buffer_size = 64 * KiB;
auto buffer_region = MM.allocate_kernel_region(buffer_size, "Kernel Packet Buffer", Region::Access::Read | Region::Access::Write, false, true);
auto buffer = (u8*)buffer_region->vaddr().get();
+ timeval packet_timestamp;
klog() << "NetworkTask: Enter main loop.";
for (;;) {
- size_t packet_size = dequeue_packet(buffer, buffer_size);
+ size_t packet_size = dequeue_packet(buffer, buffer_size, packet_timestamp);
if (!packet_size) {
Thread::current()->wait_on(packet_wait_queue, "NetworkTask");
continue;
@@ -150,7 +151,7 @@ void NetworkTask_main()
handle_arp(eth, packet_size);
break;
case EtherType::IPv4:
- handle_ipv4(eth, packet_size);
+ handle_ipv4(eth, packet_size, packet_timestamp);
break;
case EtherType::IPv6:
// ignore
@@ -213,7 +214,7 @@ void handle_arp(const EthernetFrameHeader& eth, size_t frame_size)
}
}
-void handle_ipv4(const EthernetFrameHeader& eth, size_t frame_size)
+void handle_ipv4(const EthernetFrameHeader& eth, size_t frame_size, const timeval& packet_timestamp)
{
constexpr size_t minimum_ipv4_frame_size = sizeof(EthernetFrameHeader) + sizeof(IPv4Packet);
if (frame_size < minimum_ipv4_frame_size) {
@@ -239,18 +240,18 @@ void handle_ipv4(const EthernetFrameHeader& eth, size_t frame_size)
switch ((IPv4Protocol)packet.protocol()) {
case IPv4Protocol::ICMP:
- return handle_icmp(eth, packet);
+ return handle_icmp(eth, packet, packet_timestamp);
case IPv4Protocol::UDP:
- return handle_udp(packet);
+ return handle_udp(packet, packet_timestamp);
case IPv4Protocol::TCP:
- return handle_tcp(packet);
+ return handle_tcp(packet, packet_timestamp);
default:
klog() << "handle_ipv4: Unhandled protocol " << packet.protocol();
break;
}
}
-void handle_icmp(const EthernetFrameHeader& eth, const IPv4Packet& ipv4_packet)
+void handle_icmp(const EthernetFrameHeader& eth, const IPv4Packet& ipv4_packet, const timeval& packet_timestamp)
{
auto& icmp_header = *static_cast<const ICMPHeader*>(ipv4_packet.payload());
#ifdef ICMP_DEBUG
@@ -263,7 +264,7 @@ void handle_icmp(const EthernetFrameHeader& eth, const IPv4Packet& ipv4_packet)
LOCKER(socket->lock());
if (socket->protocol() != (unsigned)IPv4Protocol::ICMP)
continue;
- socket->did_receive(ipv4_packet.source(), 0, KBuffer::copy(&ipv4_packet, sizeof(IPv4Packet) + ipv4_packet.payload_size()));
+ socket->did_receive(ipv4_packet.source(), 0, KBuffer::copy(&ipv4_packet, sizeof(IPv4Packet) + ipv4_packet.payload_size()), packet_timestamp);
}
}
@@ -290,7 +291,7 @@ void handle_icmp(const EthernetFrameHeader& eth, const IPv4Packet& ipv4_packet)
}
}
-void handle_udp(const IPv4Packet& ipv4_packet)
+void handle_udp(const IPv4Packet& ipv4_packet, const timeval& packet_timestamp)
{
if (ipv4_packet.payload_size() < sizeof(UDPPacket)) {
klog() << "handle_udp: Packet too small (" << ipv4_packet.payload_size() << ", need " << sizeof(UDPPacket) << ")";
@@ -316,10 +317,10 @@ void handle_udp(const IPv4Packet& ipv4_packet)
ASSERT(socket->type() == SOCK_DGRAM);
ASSERT(socket->local_port() == udp_packet.destination_port());
- socket->did_receive(ipv4_packet.source(), udp_packet.source_port(), KBuffer::copy(&ipv4_packet, sizeof(IPv4Packet) + ipv4_packet.payload_size()));
+ socket->did_receive(ipv4_packet.source(), udp_packet.source_port(), KBuffer::copy(&ipv4_packet, sizeof(IPv4Packet) + ipv4_packet.payload_size()), packet_timestamp);
}
-void handle_tcp(const IPv4Packet& ipv4_packet)
+void handle_tcp(const IPv4Packet& ipv4_packet, const timeval& packet_timestamp)
{
if (ipv4_packet.payload_size() < sizeof(TCPPacket)) {
klog() << "handle_tcp: IPv4 payload is too small to be a TCP packet (" << ipv4_packet.payload_size() << ", need " << sizeof(TCPPacket) << ")";
@@ -549,7 +550,7 @@ void handle_tcp(const IPv4Packet& ipv4_packet)
case TCPSocket::State::Established:
if (tcp_packet.has_fin()) {
if (payload_size != 0)
- socket->did_receive(ipv4_packet.source(), tcp_packet.source_port(), KBuffer::copy(&ipv4_packet, sizeof(IPv4Packet) + ipv4_packet.payload_size()));
+ socket->did_receive(ipv4_packet.source(), tcp_packet.source_port(), KBuffer::copy(&ipv4_packet, sizeof(IPv4Packet) + ipv4_packet.payload_size()), packet_timestamp);
socket->set_ack_number(tcp_packet.sequence_number() + payload_size + 1);
(void)socket->send_tcp_packet(TCPFlags::ACK);
@@ -565,7 +566,7 @@ void handle_tcp(const IPv4Packet& ipv4_packet)
#endif
if (payload_size) {
- if (socket->did_receive(ipv4_packet.source(), tcp_packet.source_port(), KBuffer::copy(&ipv4_packet, sizeof(IPv4Packet) + ipv4_packet.payload_size())))
+ if (socket->did_receive(ipv4_packet.source(), tcp_packet.source_port(), KBuffer::copy(&ipv4_packet, sizeof(IPv4Packet) + ipv4_packet.payload_size()), packet_timestamp))
(void)socket->send_tcp_packet(TCPFlags::ACK);
}
}
diff --git a/Kernel/Net/Socket.cpp b/Kernel/Net/Socket.cpp
index 1caa3c5328..701155089b 100644
--- a/Kernel/Net/Socket.cpp
+++ b/Kernel/Net/Socket.cpp
@@ -206,7 +206,8 @@ KResultOr<size_t> Socket::read(FileDescription& description, size_t, UserOrKerne
{
if (is_shut_down_for_reading())
return 0;
- return recvfrom(description, buffer, size, 0, {}, 0);
+ timeval tv;
+ return recvfrom(description, buffer, size, 0, {}, 0, tv);
}
KResultOr<size_t> Socket::write(FileDescription& description, size_t, const UserOrKernelBuffer& data, size_t size)
diff --git a/Kernel/Net/Socket.h b/Kernel/Net/Socket.h
index c768793292..2b178a2ec9 100644
--- a/Kernel/Net/Socket.h
+++ b/Kernel/Net/Socket.h
@@ -108,7 +108,7 @@ public:
virtual void attach(FileDescription&) = 0;
virtual void detach(FileDescription&) = 0;
virtual KResultOr<size_t> sendto(FileDescription&, const UserOrKernelBuffer&, size_t, int flags, Userspace<const sockaddr*>, socklen_t) = 0;
- virtual KResultOr<size_t> recvfrom(FileDescription&, UserOrKernelBuffer&, size_t, int flags, Userspace<sockaddr*>, Userspace<socklen_t*>) = 0;
+ virtual KResultOr<size_t> recvfrom(FileDescription&, UserOrKernelBuffer&, size_t, int flags, Userspace<sockaddr*>, Userspace<socklen_t*>, timeval&) = 0;
virtual KResult setsockopt(int level, int option, Userspace<const void*>, socklen_t);
virtual KResult getsockopt(FileDescription&, int level, int option, Userspace<void*>, Userspace<socklen_t*>);
diff --git a/Kernel/Syscalls/socket.cpp b/Kernel/Syscalls/socket.cpp
index 14c8871869..9d09d70e14 100644
--- a/Kernel/Syscalls/socket.cpp
+++ b/Kernel/Syscalls/socket.cpp
@@ -249,7 +249,8 @@ ssize_t Process::sys$recvmsg(int sockfd, Userspace<struct msghdr*> user_msg, int
auto data_buffer = UserOrKernelBuffer::for_user_buffer((u8*)iovs[0].iov_base, iovs[0].iov_len);
if (!data_buffer.has_value())
return -EFAULT;
- auto result = socket.recvfrom(*description, data_buffer.value(), iovs[0].iov_len, flags, user_addr, user_addr_length);
+ timeval timestamp = { 0, 0 };
+ auto result = socket.recvfrom(*description, data_buffer.value(), iovs[0].iov_len, flags, user_addr, user_addr_length, timestamp);
if (flags & MSG_DONTWAIT)
description->set_blocking(original_blocking);