summaryrefslogtreecommitdiff
path: root/Userland/ntpquery.cpp
diff options
context:
space:
mode:
authorNico Weber <thakis@chromium.org>2020-09-16 12:35:13 -0400
committerAndreas Kling <kling@serenityos.org>2020-09-17 17:23:01 +0200
commit8212ba467ce630b2517262fec19ca9d13218dcb0 (patch)
tree83c1b4d0afa51b8ce3185a5331f2ae6281801237 /Userland/ntpquery.cpp
parent47b3e98af8bfbf469391458fdb4e4b4783562b0c (diff)
downloadserenity-8212ba467ce630b2517262fec19ca9d13218dcb0.zip
ntpquery: Use SO_TIMESTAMP to get a more accurate destination_timestamp
We can now see at which time a packet was received by the network adapter, instead of having to measure user time after receiving the packet in user space. This means the destination timestamp is no longer affected by in-kernel queuing delays, which can be tens of milliseconds when the system is under load. It also means that if ntpquery grows a message queue that waits on replies from several requests, the time used processing one response won't be incorrectly included in the destination timestamp of the next response (in case two responses arrive at the network adapter at roughly the same time). NTP's calculations work better if send and receive latency are about equal, and this only removes in-kernel queue delays and context switch delays for the receiving packet. But the two latencies aren't very equal anyways because $network. Also, maybe we can add another API for setting the send time in the outgoing packet in kernel space right before (or when) hitting the network adapter and use that here too. So this still seems like progress.
Diffstat (limited to 'Userland/ntpquery.cpp')
-rw-r--r--Userland/ntpquery.cpp32
1 files changed, 27 insertions, 5 deletions
diff --git a/Userland/ntpquery.cpp b/Userland/ntpquery.cpp
index f5cf9b12fb..e24333bd2d 100644
--- a/Userland/ntpquery.cpp
+++ b/Userland/ntpquery.cpp
@@ -35,6 +35,7 @@
#include <string.h>
#include <sys/socket.h>
#include <sys/time.h>
+#include <sys/uio.h>
// An NtpTimestamp is a 64-bit integer that's a 32.32 binary-fixed point number.
// The integral part in the upper 32 bits represents seconds since 1900-01-01.
@@ -147,6 +148,12 @@ int main(int argc, char** argv)
return 1;
}
+ int enable = 1;
+ if (setsockopt(fd, SOL_SOCKET, SO_TIMESTAMP, &enable, sizeof(enable)) < 0) {
+ perror("setsockopt");
+ return 1;
+ }
+
sockaddr_in peer_address;
memset(&peer_address, 0, sizeof(peer_address));
peer_address.sin_family = AF_INET;
@@ -173,22 +180,33 @@ int main(int argc, char** argv)
return 1;
}
- socklen_t peer_address_size = sizeof(peer_address);
- rc = recvfrom(fd, &packet, sizeof(packet), 0, (struct sockaddr*)&peer_address, &peer_address_size);
- gettimeofday(&t, nullptr);
+ iovec iov { &packet, sizeof(packet) };
+ char control_message_buffer[CMSG_SPACE(sizeof(timeval))];
+ msghdr msg = { &peer_address, sizeof(peer_address), &iov, 1, control_message_buffer, sizeof(control_message_buffer), 0};
+ rc = recvmsg(fd, &msg, 0);
if (rc < 0) {
- perror("recvfrom");
+ perror("recvmsg");
return 1;
}
+ gettimeofday(&t, nullptr);
if ((size_t)rc < sizeof(packet)) {
fprintf(stderr, "incomplete packet recv\n");
return 1;
}
+ cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
+ ASSERT(cmsg->cmsg_level == SOL_SOCKET);
+ ASSERT(cmsg->cmsg_type == SCM_TIMESTAMP);
+ ASSERT(!CMSG_NXTHDR(&msg, cmsg));
+ timeval packet_t;
+ memcpy(&packet_t, CMSG_DATA(cmsg), sizeof(packet_t));
+
NtpTimestamp origin_timestamp = be64toh(packet.origin_timestamp);
NtpTimestamp receive_timestamp = be64toh(packet.receive_timestamp);
NtpTimestamp transmit_timestamp = be64toh(packet.transmit_timestamp);
- NtpTimestamp destination_timestamp = ntp_timestamp_from_timeval(t);
+ NtpTimestamp destination_timestamp = ntp_timestamp_from_timeval(packet_t);
+
+ timersub(&t, &packet_t, &t);
if (set_time) {
// FIXME: Do all the time filtering described in 5905, or at least correct for time of flight.
@@ -215,6 +233,10 @@ int main(int argc, char** argv)
printf("Transmit timestamp: %#016llx (%s)\n", transmit_timestamp, format_ntp_timestamp(transmit_timestamp).characters());
printf("Destination timestamp: %#016llx (%s)\n", destination_timestamp, format_ntp_timestamp(destination_timestamp).characters());
+ // When the system isn't under load, user-space t and packet_t are identical. If a shell with `yes` is running, it can be as high as 30ms in this program,
+ // which gets user-space time immediately after the recvmsg() call. In programs that have an event loop reading from multiple sockets, it could be higher.
+ printf("Receive latency: %lld.%06d s\n", t.tv_sec, t.tv_usec);
+
// Parts of the "Clock Filter" computations, https://tools.ietf.org/html/rfc5905#section-10
NtpTimestamp T1 = origin_timestamp;
NtpTimestamp T2 = receive_timestamp;